#include "common.h"

#define ShiftLeftBytes(r) ((r << 1) & 0xfefefefe)
#define CycleBits(r)      ((r >> 7) & 0x01010101)
#define Mult02(r) ShiftLeftBytes(r) ^ (CycleBits(r) * 0x1b)


/* Loading a state */

#define Load(r,in)                                      \
    {                                                   \
        r##0 = MkW((in)[0],(in)[4],(in)[8] ,(in)[12]);  \
        r##1 = MkW((in)[1],(in)[5],(in)[9] ,(in)[13]);  \
        r##2 = MkW((in)[2],(in)[6],(in)[10],(in)[14]);  \
        r##3 = MkW((in)[3],(in)[7],(in)[11],(in)[15]);  \
    }

/* n = r */

#define Copy(n,r)						\
    { n##0 = r##0; n##1 = r##1; n##2 = r##2; n##3 = r##3; }

/* n ^= r */

#define XOR(n,r)						\
	{ n##0 ^= r##0; n##1 ^= r##1; n##2 ^= r##2; n##3 ^= r##3; }

#define Store(r,out)              \
    {                             \
	(out)[0]  = B3(r##0);	  \
	(out)[4]  = B2(r##0);	  \
	(out)[8]  = B1(r##0);	  \
	(out)[12] = B0(r##0);	  \
                                  \
	(out)[1]  = B3(r##1);	  \
	(out)[5]  = B2(r##1);	  \
	(out)[9]  = B1(r##1);	  \
	(out)[13] = B0(r##1);	  \
                                  \
	(out)[2]  = B3(r##2);	  \
	(out)[6]  = B2(r##2);	  \
	(out)[10] = B1(r##2);	  \
	(out)[14] = B0(r##2);	  \
                                  \
	(out)[3]  = B3(r##3);	  \
	(out)[7]  = B2(r##3);	  \
	(out)[11] = B1(r##3);	  \
	(out)[15] = B0(r##3);	  \
    }

#define AddRoundKey(r,s)        \
    {                           \
        r##0 ^= s[0];           \
        r##1 ^= s[1];           \
        r##2 ^= s[2];           \
        r##3 ^= s[3];           \
    }


#define AddRoundKeyAssign(n,r,key)      \
    {                                   \
        n##0 = r##0 ^ key[0];		\
        n##1 = r##1 ^ key[1];		\
        n##2 = r##2 ^ key[2];		\
        n##3 = r##3 ^ key[3];		\
    }

#define MixColumns(n,r)                \
    {                                  \
        n##0 = r##1 ^ r##2 ^ r##3 ;    \
        n##1 = r##2 ^ r##3 ^ r##0 ;    \
        n##2 = r##3 ^ r##0 ^ r##1 ;    \
        n##3 = r##0 ^ r##1 ^ r##2 ;    \
                                       \
        r##0 = Mult02(r##0);           \
        r##1 = Mult02(r##1);           \
        r##2 = Mult02(r##2);           \
        r##3 = Mult02(r##3);           \
				       \
        n##0 ^= r##0 ^ r##1;           \
        n##1 ^= r##1 ^ r##2;           \
        n##2 ^= r##2 ^ r##3;           \
        n##3 ^= r##3 ^ r##0;           \
    }


#define InvMixColumns(n,r)                        \
        {                                         \
        MixColumns(n,r)                           \
                                                  \
        r##0 ^= r##2 ;                            \
        r##1 ^= r##3 ;                            \
						  \
        r##0 = Mult02(r##0);                      \
        r##1 = Mult02(r##1);                      \
						  \
        n##0 ^= r##0;                             \
        n##1 ^= r##1;                             \
        n##2 ^= r##0;                             \
        n##3 ^= r##1;                             \
						  \
        r##0 = Mult02(r##0);                      \
        r##1 = Mult02(r##1);                      \
        r##0 ^= r##1;                             \
						  \
        n##0 ^= r##0;                             \
        n##1 ^= r##0;                             \
        n##2 ^= r##0;                             \
        n##3 ^= r##0;                             \
        }

#define DECL_MATRIX_REGISTER(r)  \
    register Row r##0;           \
    register Row r##1;		 \
    register Row r##2;		 \
    register Row r##3;

#define DECL_MATRIX(r) \
    Row r##0;          \
    Row r##1;          \
    Row r##2;          \
    Row r##3;


/* The encryption macro

   Uses variables state, temp, eKey, r and nRounds

   If state contained the block that needs to be encrypted then by the
   end of ENCRYPT state will contain the encrypted block.

 */

#define ENCRYPT	{				\
    AddRoundKey(state, eKey[0]);                \
    for(r = 1; r < nRounds; ++r)		\
    {						\
        SubBytesAndShift(state);		\
        MixColumns(temp,state);                 \
	AddRoundKeyAssign(state,temp, eKey[r]); \
    }                                           \
    SubBytesAndShift(state);                    \
    AddRoundKey(state,eKey[nRounds]);           \
}

/* The decryption macro

   Uses variables state, temp, eKey, r and nRounds

   If state contained the block that needs to be encrypted then by the
   end of DECRYPT the variable state will contain the decrypted block.

 */


#define DECRYPT {                               \
    AddRoundKey(state,eKey[nRounds]);           \
    for(r = nRounds - 1; r > 0; --r)		\
    {                                           \
	InvSubBytesAndShift(state);             \
	AddRoundKeyAssign(temp,state, eKey[r]); \
	InvMixColumns(state,temp);              \
    }                                           \
    InvSubBytesAndShift(state);                 \
    AddRoundKey(state,eKey[0]);			\
}

void raazAESCBCEncryptCPortable(
    Block *inp, int nBlocks,
    int nRounds, RMatrix *eKey,
    RMatrix iv)
{
    int r;
    DECL_MATRIX_REGISTER(state);
    DECL_MATRIX_REGISTER(temp);

    state0 = iv[0];
    state1 = iv[1];
    state2 = iv[2];
    state3 = iv[3];

    /* Invariant: State contains the iv for the current block */
    while( nBlocks )
    {
	/* Load the actual block into temp */
	Load(temp, *inp);

	/* XOR with the iv that is in state and store it in state */

	XOR(state, temp);

	ENCRYPT; /* now state contains the encrypted block which is
		    also the iv for the next block.
		 */

	Store(state, *inp);

	--nBlocks;
	++inp;
    }

    iv[0] = state0;
    iv[1] = state1;
    iv[2] = state2;
    iv[3] = state3;

}


void raazAESCBCDecryptCPortable(
    Block *inp, int nBlocks,
    int nRounds, RMatrix *eKey,
    RMatrix iv)
{
    int cursor, r;

    DECL_MATRIX(endIV)
    DECL_MATRIX_REGISTER(state);
    DECL_MATRIX_REGISTER(temp);


    Load(state, inp[nBlocks - 1]); /* Start from the last block */

    /* The last encrypted block is also the IV for the subsequent
       blocks. So keep track of it.
    */

    Copy(endIV, state);

    /*
      The invariant kept track of is that the variable state contains
      the current block that is to be decrypted.
    */

    for(cursor = nBlocks - 1; cursor > 0 ; --cursor)
    {

	DECRYPT;

	/* Load the IV for the current block into temp */
	Load(temp, inp[cursor - 1]);

	/* Recover the actual block */
	XOR(state,temp)

	/* Store the decrypted block */
	Store(state, inp[cursor]);

	/* Maintain the invariant by moving stuff in temp to state */
	Copy(state, temp);

    }

    /* For the first block */
    DECRYPT;

    state0 ^= iv[0];
    state1 ^= iv[1];
    state2 ^= iv[2];
    state3 ^= iv[3];

    Store(state, inp[0]);

    iv[0] = endIV0;
    iv[1] = endIV1;
    iv[2] = endIV2;
    iv[3] = endIV3;

}
