package org.bouncycastle.crypto.params;

import org.bouncycastle.util.Arrays;

public class DESParameters
    extends KeyParameter
{
    public DESParameters(
        byte[]  key)
    {
        super(key);

        if (isWeakKey(key, 0))
        {
            throw new IllegalArgumentException("attempt to create weak DES key");
        }
    }

    /*
     * DES Key length in bytes.
     */
    public static final int DES_KEY_LENGTH = 8;

    /*
     * Table of weak and semi-weak keys taken from Schneier pp281
     */
    private static final int N_DES_WEAK_KEYS = 16;

    private static byte[] DES_weak_keys =
    {
        /* weak keys */
        (byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01, (byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01,
        (byte)0x1f,(byte)0x1f,(byte)0x1f,(byte)0x1f, (byte)0x0e,(byte)0x0e,(byte)0x0e,(byte)0x0e,
        (byte)0xe0,(byte)0xe0,(byte)0xe0,(byte)0xe0, (byte)0xf1,(byte)0xf1,(byte)0xf1,(byte)0xf1,
        (byte)0xfe,(byte)0xfe,(byte)0xfe,(byte)0xfe, (byte)0xfe,(byte)0xfe,(byte)0xfe,(byte)0xfe,

        /* semi-weak keys */
        (byte)0x01,(byte)0xfe,(byte)0x01,(byte)0xfe, (byte)0x01,(byte)0xfe,(byte)0x01,(byte)0xfe,
        (byte)0x1f,(byte)0xe0,(byte)0x1f,(byte)0xe0, (byte)0x0e,(byte)0xf1,(byte)0x0e,(byte)0xf1,
        (byte)0x01,(byte)0xe0,(byte)0x01,(byte)0xe0, (byte)0x01,(byte)0xf1,(byte)0x01,(byte)0xf1,
        (byte)0x1f,(byte)0xfe,(byte)0x1f,(byte)0xfe, (byte)0x0e,(byte)0xfe,(byte)0x0e,(byte)0xfe,
        (byte)0x01,(byte)0x1f,(byte)0x01,(byte)0x1f, (byte)0x01,(byte)0x0e,(byte)0x01,(byte)0x0e,
        (byte)0xe0,(byte)0xfe,(byte)0xe0,(byte)0xfe, (byte)0xf1,(byte)0xfe,(byte)0xf1,(byte)0xfe,
        (byte)0xfe,(byte)0x01,(byte)0xfe,(byte)0x01, (byte)0xfe,(byte)0x01,(byte)0xfe,(byte)0x01,
        (byte)0xe0,(byte)0x1f,(byte)0xe0,(byte)0x1f, (byte)0xf1,(byte)0x0e,(byte)0xf1,(byte)0x0e,
        (byte)0xe0,(byte)0x01,(byte)0xe0,(byte)0x01, (byte)0xf1,(byte)0x01,(byte)0xf1,(byte)0x01,
        (byte)0xfe,(byte)0x1f,(byte)0xfe,(byte)0x1f, (byte)0xfe,(byte)0x0e,(byte)0xfe,(byte)0x0e,
        (byte)0x1f,(byte)0x01,(byte)0x1f,(byte)0x01, (byte)0x0e,(byte)0x01,(byte)0x0e,(byte)0x01,
        (byte)0xfe,(byte)0xe0,(byte)0xfe,(byte)0xe0, (byte)0xfe,(byte)0xf1,(byte)0xfe,(byte)0xf1
    };

    /**
     * DES has 16 weak keys.  This method will check
     * if the given DES key material is weak or semi-weak.
     * Key material that is too short is regarded as weak.
     * <p>
     * See <a href="https://www.counterpane.com/applied.html">"Applied
     * Cryptography"</a> by Bruce Schneier for more information.
     *
     * @return true if the given DES key material is weak or semi-weak,
     *     false otherwise.
     */
    public static boolean isWeakKey(byte[] key, int offset)
    {
        if (offset > (key.length - DES_KEY_LENGTH))
        {
            throw new IllegalArgumentException("key material too short.");
        }

        for (int i = 0; i < N_DES_WEAK_KEYS; i++)
        {
            if (Arrays.constantTimeAreEqual(DES_KEY_LENGTH, key, offset, DES_weak_keys, i * DES_KEY_LENGTH))
            {
                return true;
            }
        }

        return false;
    }

    /**
     * DES Keys use the LSB as the odd parity bit.  This can
     * be used to check for corrupt keys.
     *
     * @param bytes the byte array to set the parity on.
     */
    public static void setOddParity(
        byte[] bytes)
    {
        for (int i = 0; i < bytes.length; i++)
        {
            int b = bytes[i];
            bytes[i] = (byte)((b & 0xfe) |
                            ((((b >> 1) ^
                            (b >> 2) ^
                            (b >> 3) ^
                            (b >> 4) ^
                            (b >> 5) ^
                            (b >> 6) ^
                            (b >> 7)) ^ 0x01) & 0x01));
        }
    }
}
