/*
 ---------------------------------------------------------------------------
 Copyright (c) 2003, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
 All rights reserved.

 LICENSE TERMS

 The free distribution and use of this software in both source and binary
 form is allowed (with or without changes) provided that:

   1. distributions of this source code include the above copyright
      notice, this list of conditions and the following disclaimer;

   2. distributions in binary form include the above copyright
      notice, this list of conditions and the following disclaimer
      in the documentation and/or other associated materials;

   3. the copyright holder's name is not used to endorse products
      built using this software without specific written permission.

 ALTERNATIVELY, provided that this notice is retained in full, this product
 may be distributed under the terms of the GNU General Public License (GPL),
 in which case the provisions of the GPL apply INSTEAD OF those given above.

 DISCLAIMER

 This software is provided 'as is' with no explicit or implied warranties
 in respect of its properties, including, but not limited to, correctness
 and/or fitness for purpose.
 ---------------------------------------------------------------------------
 Issue Date: 1/05/2003

 This code implements the EAX combined encryption and authentication mode 
 specified M. Bellare, P. Rogaway and D. Wagner.

 This is a byte oriented version in which the nonce is of limited length
*/

#include <memory.h>

#include "eax-aes.h"

#define BLOCK_SIZE	AES_BLOCK_SIZE

void eax_initialise(	
			const unsigned char key[],		/* the encryption key				*/
			unsigned long key_len,			/* key length (bytes)				*/
			const unsigned char nonce[],	/* the nonce data					*/
			unsigned long nonce_len,		/* nonce data length (bytes)		*/
			unsigned long auth_tag_length,  /* authentication tag length (bytes)*/
			eax_ctx ctx[1])                 /* the CCM context					*/
{	unsigned int i = 0, n_pos = 0;
	unsigned char t, *p;
	static unsigned char x_t[4] = { 0x00, 0x87, 0x0e, 0x87 ^ 0x0e };

	/* set the context to all zeroes			*/
	memset(ctx, 0, sizeof(eax_ctx));
	ctx->tag_len = auth_tag_length;
	
	/* set the AES key							*/ 
	aes_encrypt_key(key, key_len, ctx->aes);

	/* compute E(0) (needed for the pad values)	*/
	aes_encrypt(ctx->pad_xvv, ctx->pad_xvv, ctx->aes);

	/* if the nonce length is zero we can use	*/
	/* the encrypted zero buffer for the nonce 	*/
	if(nonce_len == 0)
		memcpy(ctx->nce_cbc, ctx->pad_xvv, BLOCK_SIZE);
	else
		n_pos = 16;

	/* compute {02} * {E(0)} and {04} * {E(0)}	*/
	/* GF(2^128) mod x^128 + x^7 + x^2 + x + 1	*/
	for(i = 0, p = ctx->pad_xvv, t = *p >> 6; i < BLOCK_SIZE - 1; ++i, ++p)
	{
		*(p + 16) = (*p << 2) | (*(p + 1) >> 6);
		*p = (*p << 1) | (*(p + 1) >> 7);
	}
	*(p + 16) = (*p << 2) ^ x_t[t];
	*(p + 15) ^= (t >>= 1);
	*p = (*p << 1) ^ x_t[t];

	/* set the header CBC start value			*/
	ctx->hdr_cbc[BLOCK_SIZE - 1] = 1;
	ctx->hdr_cnt = 16;

	/* set the ciphertext CBC start value		*/
	ctx->txt_cbc[BLOCK_SIZE - 1] = 2;
	ctx->txte_cnt = 16;	/* encryption count		*/
	ctx->txta_cnt = 16;	/* authentication count	*/

	/* compile the OMAC value for the nonce		*/
	i = 0;
	while(i < nonce_len)
	{
		if(n_pos == BLOCK_SIZE)
		{
			aes_encrypt(ctx->nce_cbc, ctx->nce_cbc, ctx->aes);
			n_pos = 0;
		}
		ctx->nce_cbc[n_pos++] ^= nonce[i++];
	}

	/* do the OMAC padding for the nonce		*/
	p = ctx->pad_xvv;
	if(n_pos < BLOCK_SIZE)
	{
		ctx->nce_cbc[n_pos] ^= 0x80; p += 16;
	}

	for(i = 0; i < BLOCK_SIZE; ++i)
		ctx->nce_cbc[i] ^= p[i];

	/* compute the OMAC*(nonce) value			*/
	aes_encrypt(ctx->nce_cbc, ctx->nce_cbc, ctx->aes);

	/* copy value into counter for CTR			*/
	memcpy(ctx->ctr_val, ctx->nce_cbc, BLOCK_SIZE);
}

void eax_authenticate_header(
			unsigned char hbuf[],			/* the header input buffer			*/
			unsigned long len,				/* the length of this block (bytes) */
			eax_ctx ctx[1]) 				/* the eax context                  */
{	unsigned int i = 0, j = ctx->hdr_cnt;

	while(i < len)
	{
		if(j == BLOCK_SIZE)
		{
			aes_encrypt(ctx->hdr_cbc, ctx->hdr_cbc, ctx->aes);
			j = 0;
		}
		ctx->hdr_cbc[j++] ^= hbuf[i++];
	}

	ctx->hdr_cnt = j;
}

void eax_encrypt_data(
			unsigned char buf[],			/* the data buffer					*/
			unsigned long len,				/* the length of this block (bytes) */
			eax_ctx ctx[1]) 				/* the eax context                  */
{	unsigned int i = 0, j = ctx->txte_cnt;

	while(i < len)
	{
		if(j == BLOCK_SIZE)
		{
			aes_encrypt(ctx->txt_cbc, ctx->txt_cbc, ctx->aes);
			aes_encrypt(ctx->ctr_val, ctx->enc_ctr, ctx->aes);
			while(j-- > 0 && !++(ctx->ctr_val[j]))
				;
			j = 0;
		}
		buf[i] ^= ctx->enc_ctr[j];
		ctx->txt_cbc[j++] ^= buf[i++];
	}

	ctx->txta_cnt = ctx->txte_cnt = j;
}

void eax_authenticate_data(
			unsigned char buf[],			/* the data input buffer			*/
			unsigned long len,				/* the length of this block (bytes) */
			eax_ctx ctx[1]) 				/* the eax context                  */
{	unsigned int i = 0, j = ctx->txta_cnt;

	while(i < len)
	{
		if(j == BLOCK_SIZE)
		{
			aes_encrypt(ctx->txt_cbc, ctx->txt_cbc, ctx->aes);
			j = 0;
		}
		ctx->txt_cbc[j++] ^= buf[i++];
	}

	ctx->txta_cnt = j;
}

void eax_decrypt_data(
			unsigned char buf[],			/* the data buffer					*/
			unsigned long len,				/* the length of this block (bytes) */
			eax_ctx ctx[1])					/* the eax context                  */
{	unsigned int i = 0, j = ctx->txte_cnt;

	while(i < len)
	{
		if(j == BLOCK_SIZE)
		{
			aes_encrypt(ctx->ctr_val, ctx->enc_ctr, ctx->aes);
			while(j-- > 0 && !++(ctx->ctr_val[j]))
				;
			j = 0;
		}
		buf[i++] ^= ctx->enc_ctr[j++];
	}

	ctx->txte_cnt = j;
}

void eax_authenticate_and_decrypt_data(
			unsigned char buf[],			/* the data buffer					*/
			unsigned long len,				/* the length of this block (bytes) */
			eax_ctx ctx[1]) 				/* the eax context                  */
{	unsigned int i = 0, j = ctx->txte_cnt;

	while(i < len)
	{
		if(j == BLOCK_SIZE)
		{
			aes_encrypt(ctx->txt_cbc, ctx->txt_cbc, ctx->aes);
			aes_encrypt(ctx->ctr_val, ctx->enc_ctr, ctx->aes);
			while(j-- > 0 && !++(ctx->ctr_val[j]))
				;
			j = 0;
		}
		ctx->txt_cbc[j] ^= buf[i];
		buf[i++] ^= ctx->enc_ctr[j++];
	}

	ctx->txta_cnt = ctx->txte_cnt = j;
}

void eax_compute_tag(	
			unsigned char auth_tag[],		/* the encryption key				*/
			eax_ctx ctx[1])	                /* the EAX context					*/
{	unsigned int i;
	unsigned char	*p;

	/* complete OMAC* for header value		*/
	p = ctx->pad_xvv;
	if(ctx->hdr_cnt < BLOCK_SIZE)
	{
		ctx->hdr_cbc[ctx->hdr_cnt] ^= 0x80;
		p += 16;
	}

	for(i = 0; i < BLOCK_SIZE; ++i)
		ctx->hdr_cbc[i] ^= p[i];

	aes_encrypt(ctx->hdr_cbc, ctx->hdr_cbc, ctx->aes);

	/* complete OMAC* for ciphertext value	*/
	p = ctx->pad_xvv;
	if(ctx->txta_cnt < BLOCK_SIZE)
	{
		ctx->txt_cbc[ctx->txta_cnt] ^= 0x80;
		p += 16;
	}

	for(i = 0; i < BLOCK_SIZE; ++i)
		ctx->txt_cbc[i] ^= p[i];

	aes_encrypt(ctx->txt_cbc, ctx->txt_cbc, ctx->aes);

	/* compute final authentication tag		*/
	for(i = 0; i < ctx->tag_len; ++i)
		auth_tag[i] = ctx->nce_cbc[i] ^ ctx->txt_cbc[i] ^ ctx->hdr_cbc[i];
}

void eax_end(eax_ctx ctx[1])
{
	memset(ctx, 0, sizeof(eax_ctx));
}
