package org.bouncycastle.crypto.engines;


/**
 * A class that provides CAST6 key encryption operations,
 * such as encoding data and generating keys.
 *
 * All the algorithms herein are from the Internet RFC
 *
 * RFC2612 - CAST6 (128bit block, 128-256bit key)
 *
 * and implement a simplified cryptography interface.
 */
public final class CAST6Engine extends CAST5Engine
{
    //====================================
    // Useful constants
    //====================================

    protected static final int    ROUNDS = 12;

    protected static final int    BLOCK_SIZE = 16;  // bytes = 128 bits

	/*
	 * Put the round and mask keys into an array.
	 * Kr0[i] => _Kr[i*4 + 0]
	 */
	protected int _Kr[] = new int[ROUNDS*4]; // the rotating round key(s)
	protected int _Km[] = new int[ROUNDS*4]; // the masking round key(s)

	/*
	 * Key setup
	 */
	protected int _Tr[] = new int[24 * 8];
	protected int _Tm[] = new int[24 * 8];

    private int[] _workingKey = new int[8];

    public CAST6Engine()
    {
    }

    public String getAlgorithmName()
    {
        return "CAST6";
    }

    public void reset()
    {
    }

    public int getBlockSize()
    {
        return BLOCK_SIZE;
    }

    //==================================
    // Private Implementation
    //==================================

	/*
	 * Creates the subkeys using the same nomenclature
	 * as described in RFC2612.
	 *
	 * See section 2.4
	 */
    protected void setKey(byte[] key)
    {
		int Cm = 0x5a827999;
		int Mm = 0x6ed9eba1;
		int Cr = 19;
		int Mr = 17;

		/* 
		 * Determine the key size here, if required
		 *
		 * if keysize < 256 bytes, pad with 0
		 *
		 * Typical key sizes => 128, 160, 192, 224, 256
		 */
		for (int i=0; i< 24; i++)
		{
			for (int j=0; j< 8; j++)
			{
				_Tm[i*8 + j] = Cm;
				Cm = (Cm + Mm) & 0xffffffff;	// mod 2^32;

				_Tr[i*8 + j] = Cr;
				Cr = (Cr + Mr) & 0x1f;			// mod 32
			}
		}

		byte[] tmpKey = new byte[64];
		int length = key.length;
		for (int i=0; i < length ; i++)
		{
			tmpKey[i] = key[i];
		}

		// now create ABCDEFGH
		for (int i=0; i< 8; i++)
		{
			_workingKey[i] = BytesTo32bits(tmpKey, i*4);
		}

		// Generate the key schedule
		int i2 = 0;
		for (int i=0; i< 12; i++)
		{
			// KAPPA <- W2i(KAPPA)
			i2 = i*2 *8;
			_workingKey[6] ^= F1(_workingKey[7], _Tm[i2+0], _Tr[i2+0]);
			_workingKey[5] ^= F2(_workingKey[6], _Tm[i2+1], _Tr[i2+1]);
			_workingKey[4] ^= F3(_workingKey[5], _Tm[i2+2], _Tr[i2+2]);
			_workingKey[3] ^= F1(_workingKey[4], _Tm[i2+3], _Tr[i2+3]);
			_workingKey[2] ^= F2(_workingKey[3], _Tm[i2+4], _Tr[i2+4]);
			_workingKey[1] ^= F3(_workingKey[2], _Tm[i2+5], _Tr[i2+5]);
			_workingKey[0] ^= F1(_workingKey[1], _Tm[i2+6], _Tr[i2+6]);
			_workingKey[7] ^= F2(_workingKey[0], _Tm[i2+7], _Tr[i2+7]);

			// KAPPA <- W2i+1(KAPPA)
			i2 = (i*2 + 1)*8;
			_workingKey[6] ^= F1(_workingKey[7], _Tm[i2+0], _Tr[i2+0]);
			_workingKey[5] ^= F2(_workingKey[6], _Tm[i2+1], _Tr[i2+1]);
			_workingKey[4] ^= F3(_workingKey[5], _Tm[i2+2], _Tr[i2+2]);
			_workingKey[3] ^= F1(_workingKey[4], _Tm[i2+3], _Tr[i2+3]);
			_workingKey[2] ^= F2(_workingKey[3], _Tm[i2+4], _Tr[i2+4]);
			_workingKey[1] ^= F3(_workingKey[2], _Tm[i2+5], _Tr[i2+5]);
			_workingKey[0] ^= F1(_workingKey[1], _Tm[i2+6], _Tr[i2+6]);
			_workingKey[7] ^= F2(_workingKey[0], _Tm[i2+7], _Tr[i2+7]);

			// Kr_(i) <- KAPPA
			_Kr[i*4 + 0] = _workingKey[0] & 0x1f;
			_Kr[i*4 + 1] = _workingKey[2] & 0x1f;
			_Kr[i*4 + 2] = _workingKey[4] & 0x1f;
			_Kr[i*4 + 3] = _workingKey[6] & 0x1f;


			// Km_(i) <- KAPPA
			_Km[i*4 + 0] = _workingKey[7];
			_Km[i*4 + 1] = _workingKey[5];
			_Km[i*4 + 2] = _workingKey[3];
			_Km[i*4 + 3] = _workingKey[1];
		}
		
    }

    /**
     * Encrypt the given input starting at the given offset and place
     * the result in the provided buffer starting at the given offset.
	 *
	 * @param src		The plaintext buffer
	 * @param srcIndex	An offset into src
	 * @param dst		The ciphertext buffer
	 * @param dstIndex	An offset into dst
     */
    protected int encryptBlock(
        byte[] src, 
        int srcIndex,
        byte[] dst,
        int dstIndex)
    {

        int  result[] = new int[4];

        // process the input block 
        // batch the units up into 4x32 bit chunks and go for it

        int A = BytesTo32bits(src, srcIndex);
        int B = BytesTo32bits(src, srcIndex + 4);
        int C = BytesTo32bits(src, srcIndex + 8);
        int D = BytesTo32bits(src, srcIndex + 12);

        CAST_Encipher(A, B, C, D, result);

        // now stuff them into the destination block
        Bits32ToBytes(result[0], dst, dstIndex);
        Bits32ToBytes(result[1], dst, dstIndex + 4);
        Bits32ToBytes(result[2], dst, dstIndex + 8);
        Bits32ToBytes(result[3], dst, dstIndex + 12);

        return BLOCK_SIZE;
    }

    /**
     * Decrypt the given input starting at the given offset and place
     * the result in the provided buffer starting at the given offset.
	 *
	 * @param src		The plaintext buffer
	 * @param srcIndex	An offset into src
	 * @param dst		The ciphertext buffer
	 * @param dstIndex	An offset into dst
     */
    protected int decryptBlock(
        byte[] src, 
        int srcIndex,
        byte[] dst,
        int dstIndex)
    {
        int  result[] = new int[4];

        // process the input block
        // batch the units up into 4x32 bit chunks and go for it
        int A = BytesTo32bits(src, srcIndex);
        int B = BytesTo32bits(src, srcIndex + 4);
        int C = BytesTo32bits(src, srcIndex + 8);
        int D = BytesTo32bits(src, srcIndex + 12);

        CAST_Decipher(A, B, C, D, result);

        // now stuff them into the destination block
        Bits32ToBytes(result[0], dst, dstIndex);
        Bits32ToBytes(result[1], dst, dstIndex + 4);
        Bits32ToBytes(result[2], dst, dstIndex + 8);
        Bits32ToBytes(result[3], dst, dstIndex + 12);

        return BLOCK_SIZE;
    }

	/**
	 * Does the 12 quad rounds rounds to encrypt the block.
	 * 
	 * @param A	the 00-31  bits of the plaintext block
	 * @param B	the 32-63  bits of the plaintext block
	 * @param C	the 64-95  bits of the plaintext block
	 * @param D	the 96-127 bits of the plaintext block
	 * @param result the resulting ciphertext
	 */
    protected final void CAST_Encipher(int A, int B, int C, int D,int result[])
    {
		int x;
		for (int i=0; i< 6; i++)
		{
			x = i*4;
			// BETA <- Qi(BETA)
			C ^= F1(D, _Km[x + 0], _Kr[x + 0]);
			B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
			A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
			D ^= F1(A, _Km[x + 3], _Kr[x + 3]);

		}

		for (int i=6; i<12; i++)
		{
			x = i*4;
			// BETA <- QBARi(BETA)
			D ^= F1(A, _Km[x + 3], _Kr[x + 3]);
			A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
			B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
			C ^= F1(D, _Km[x + 0], _Kr[x + 0]);

		}

		result[0] = A;
		result[1] = B;
		result[2] = C;
		result[3] = D;

        return;
    }

	/**
	 * Does the 12 quad rounds rounds to decrypt the block.
	 * 
	 * @param A	the 00-31  bits of the ciphertext block
	 * @param B	the 32-63  bits of the ciphertext block
	 * @param C	the 64-95  bits of the ciphertext block
	 * @param D	the 96-127 bits of the ciphertext block
	 * @param result the resulting plaintext
	 */
    protected final void CAST_Decipher(int A, int B, int C, int D,int result[])
    {
		int x;
		for (int i=0; i< 6; i++)
		{
			x = (11-i)*4;
			// BETA <- Qi(BETA)
			C ^= F1(D, _Km[x + 0], _Kr[x + 0]);
			B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
			A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
			D ^= F1(A, _Km[x + 3], _Kr[x + 3]);

		}

		for (int i=6; i<12; i++)
		{
			x = (11-i)*4;
			// BETA <- QBARi(BETA)
			D ^= F1(A, _Km[x + 3], _Kr[x + 3]);
			A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
			B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
			C ^= F1(D, _Km[x + 0], _Kr[x + 0]);

		}

		result[0] = A;
		result[1] = B;
		result[2] = C;
		result[3] = D;

        return;
    }

}
