#include "common.h"

/* The main chacha20 block transform for a complete block of data.
 *
 * Maximum bytes that should be encoded is 2^32 * 64 = 256GB.  The
 * counter repeats after that.
 *
 */



/* Warnings all macros are unprotected use with care */

# define R(x,i) ((x << i) | (x >> (32 - i)))

# define QROUND(a,b,c,d)			\
    {						\
	a += b; d ^= a; d = R(d,16);		\
	c += d; b ^= c; b = R(b,12);		\
	a += b; d ^= a; d = R(d,8);		\
	c += d; b ^= c; b = R(b,7);		\
    }						\



# define ROUND			  \
    {				  \
	QROUND(x0, x4, x8,  x12); \
	QROUND(x1, x5, x9,  x13); \
	QROUND(x2, x6, x10, x14); \
	QROUND(x3, x7, x11, x15); \
	QROUND(x0, x5, x10, x15); \
	QROUND(x1, x6, x11, x12); \
	QROUND(x2, x7, x8,  x13); \
	QROUND(x3, x4, x9,  x14); \
    }


# define XOR(i,a)    (*msg)[i] ^= raaz_tole32(a)
# define EMIT(i,a)   (*msg)[i]  = a

/*

Some function for debugging.

# define PR(i)        printf("%8x  ", x##i)
# define PRM(i)       printf("%8x  ", (*msg)[i])
# define NEWLINE      printf("\n")
# define PRINTSTATE				\
    {						\
    PR(0); PR(1);   PR(2);    PR(3); NEWLINE;	\
    PR(4); PR(5);   PR(6);    PR(7); NEWLINE;	\
    PR(8); PR(9);   PR(10);   PR(11); NEWLINE;	\
    PR(12); PR(13); PR(14); PR(15); NEWLINE;	\
    }

# define PRINTMESG				\
    {						\
    PRM(0); PRM(1);   PRM(2);    PRM(3); NEWLINE;	\
    PRM(4); PRM(5);   PRM(6);    PRM(7); NEWLINE;	\
    PRM(8); PRM(9);   PRM(10);   PRM(11); NEWLINE;	\
    PRM(12); PRM(13); PRM(14); PRM(15); NEWLINE;	\
    }

*/



# pragma GCC optimize "tree-vectorize"

void raazChaCha20Block(Block * msg, int nblocks, const Key key, const IV iv, Counter  *ctr)
{
    register Word x0,  x1,  x2, x3;
    register Word x4,  x5,  x6, x7;
    register Word x8,  x9,  x10, x11;
    register Word x12, x13, x14, x15;
    register Word valCtr; /* value of the ctr */

    valCtr = *ctr;
    while( nblocks > 0){


	x0  = C0     ; x1  = C1     ; x2  = C2     ; x3  = C3     ;
	x4  = key[0] ; x5  = key[1] ; x6  = key[2] ; x7  = key[3] ;
	x8  = key[4] ; x9  = key[5] ; x10 = key[6] ; x11 = key[7] ;
	x12 = valCtr ; x13 = iv[0]  ; x14 = iv[1]  ; x15 = iv[2]  ;


	ROUND; /* 0,1   */
	ROUND; /* 2,3   */
	ROUND; /* 4,5   */
	ROUND; /* 6,7   */
	ROUND; /* 8,9   */
	ROUND; /* 10,11 */
	ROUND; /* 12,13 */
	ROUND; /* 14,15 */
	ROUND; /* 16,17 */
	ROUND; /* 18,19 */



	x0  += C0     ; x1  += C1     ; x2  += C2     ; x3  += C3     ;
	x4  += key[0] ; x5  += key[1] ; x6  += key[2] ; x7  += key[3] ;
	x8  += key[4] ; x9  += key[5] ; x10 += key[6] ; x11 += key[7] ;
	x12 += valCtr ; x13 += iv[0]  ; x14 += iv[1]  ; x15 += iv[2]  ;


	XOR(0,x0)   ; XOR(1, x1) ; XOR(2, x2)   ; XOR(3,  x3)  ;
	XOR(4,x4)   ; XOR(5, x5) ; XOR(6, x6)   ; XOR(7,  x7)  ;
	XOR(8,x8)   ; XOR(9, x9) ; XOR(10, x10) ; XOR(11, x11) ;
	XOR(12,x12) ; XOR(13,x13); XOR(14, x14) ; XOR(15, x15) ;


	++ valCtr;
	--nblocks; ++msg; /* move to the next block */
    }
    *ctr = valCtr;         /* increment counter      */
    return;
}
