/*
 * TweGIFT-64_LOTUS-AEAD
 * 
 * 
 * TweGIFT-64_LOTUS-AEAD ia a nonce-based AEAD based on the LOTUS-AEAD
 * mode of operation and TweGIFT-64 tweakable block cipher.
 * 
 * Test Vector (in little endian format):
 * Key		: 0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00
 * PT 		:
 * AD		: 
 * CT		: e8 8d f3 3f b8 eb f3 37
 * 
 */

#include "crypto_aead.h"
#include "api.h"
#include "lotus.h"

/**********************************************************************
 * 
 * @name	:	xor_bytes
 * 
 * @note	:	XORs "num" many bytes of "src" to "dest".
 * 
 **********************************************************************/		
void xor_bytes(u8 *dest, const u8 *src, u8 num)
{
	for(u8 i=0; i < num; i++)
	{
		dest[i] ^= src[i];
	}
}

/**********************************************************************
 * 
 * @name	:	mult_by_alpha
 * 
 * @note	:	Multiplies given field element in "src" with \alpha,
 * 				the primitive element corresponding to the primitive
 * 				polynomial p(x) as defined in PRIM_POLY_MOD_128, and
 * 				stores the result in "dest".
 * 
 **********************************************************************/	
void mult_by_alpha(u8 *dest, u8 *src)
{
	u8 mask = 0x00;
	if(src[CRYPTO_KEYBYTES-1] & 0x80){
		mask = PRIM_POLY_MOD_128;
	}
	for(u8 i=CRYPTO_KEYBYTES-1; i>0; i--){
		dest[i] = src[i]<<1 | src[i-1]>>7;
	}
	dest[0] = src[0]<<1;
	dest[0] ^= mask;
}

/**********************************************************************
 * 
 * @name	:	memcpy_and_zero_one_pad
 * 
 * @note	:	Copies src bytes to dest and pads with 10* to create
 * 				CRYPTO_BLOCKBYTES-oriented data.
 * 
 **********************************************************************/
void memcpy_and_zero_one_pad(u8* dest, const u8 *src, u8 len)
{
	memset(dest, 0, CRYPTO_BLOCKBYTES);
	memcpy(dest, src, len);
	dest[len] ^= 0x01;
}

/**********************************************************************
 * 
 * @name	:	init
 * 
 * @note	:	Derives nonce-dependent key and mask.
 * 
 **********************************************************************/
void init(u8 *nonced_key, u8 *nonced_mask, const u8 *key, const u8 *nonce)
{
	u8 twk;
	
	u8 zero[CRYPTO_BLOCKBYTES] = { 0 };
	
	u8 enc_zero[CRYPTO_BLOCKBYTES];
	
	// set control bits to 0000.
	twk = 0x00;
	
	// encrypt zero with the master key.
	twegift_enc(enc_zero, key, &twk, zero);
	
	// compute K_N = K + N
	memcpy(nonced_key, key, CRYPTO_KEYBYTES);
	xor_bytes(nonced_key, nonce, CRYPTO_NPUBBYTES);
	
	// set control bits to 0001.
	twk = 0x01;
	
	//compute \Delta_N = E^1_{K_N}(E^0_K(0))
	twegift_enc(nonced_mask, nonced_key, &twk, enc_zero);
}

/**********************************************************************
 * 
 * @name	:	proc_ad
 * 
 * @note	:	Processes associated data to generate intermediate
 * 				checksum.
 * 
 **********************************************************************/
void proc_ad(u8 *nonced_key, u8 *vxor, u8 *nonced_mask, const u8 *ad, u64 a, u64 adlen)
{
	u8 twk;
	
	u8 u[CRYPTO_BLOCKBYTES];
	
	u8 v[CRYPTO_BLOCKBYTES];
	
	// set control bits to 0010
	twk = 0x02;
	
	// L_0 = K_N \odot \alpha
	mult_by_alpha(nonced_key, nonced_key);
	
	for(u64 i=0; i < a-1; i++)
	{
		// compute U_i = A_i + \Delta_N
		memcpy(&u[0],&ad[i*CRYPTO_BLOCKBYTES],CRYPTO_BLOCKBYTES);
		xor_bytes(u, nonced_mask, CRYPTO_BLOCKBYTES);
		
		// compute V_i = E^2_{L_i}(U_i)
		twegift_enc(v, nonced_key, &twk, u);
		
		// V_\xor = V_\xor + V_i
		xor_bytes(vxor, v, CRYPTO_BLOCKBYTES);
		
		// L_{i+1} = L_i \odot \alpha
		mult_by_alpha(nonced_key, nonced_key);
	}
	if(adlen%CRYPTO_BLOCKBYTES != 0)
	{
		// partial block processing
		
		// set control bits to 011
		twk = 0x03;
		
		// compute U_{a-1} = 0^*1||A_{a-1} + \Delta_N
		memcpy_and_zero_one_pad(&u[0], &ad[(a-1)*CRYPTO_BLOCKBYTES], PARTIAL_BLOCK_LEN(a,adlen));
		xor_bytes(u, nonced_mask, CRYPTO_BLOCKBYTES);
	}
	else
	{
		// full block processing
		
		// compute U_{a-1} = A_{a-1} + \Delta_N
		memcpy(&u[0], &ad[(a-1)*CRYPTO_BLOCKBYTES], CRYPTO_BLOCKBYTES);
		xor_bytes(u, nonced_mask, CRYPTO_BLOCKBYTES);
	}
	
	// compute V_{a-1} = E^2/3_{L_{a-1}}(U_{a-1})
	twegift_enc(v, nonced_key, &twk, u);
	
	// V_\xor = V_\xor + V_i
	xor_bytes(vxor, v, CRYPTO_BLOCKBYTES);
}

/**********************************************************************
 * 
 * @name	:	proc_pt
 * 
 * @note	:	Generates ciphertext by encrypting plaintext.
 * 
 **********************************************************************/
void proc_pt(u8 *nonced_key, u8 *wxor, u8 *ct, u64 *ctlen, u8 *nonced_mask, const u8 *pt, u64 m, u64 ptlen)
{
	u8 twk;
	
	u8 x0[CRYPTO_BLOCKBYTES];
	u8 x1[CRYPTO_BLOCKBYTES];
	
	u8 w0[CRYPTO_BLOCKBYTES];
	u8 w1[CRYPTO_BLOCKBYTES];
	
	u8 y0[CRYPTO_BLOCKBYTES];
	u8 y1[CRYPTO_BLOCKBYTES];
	
	*ctlen = 0;
	
	// L_a = K_N \odot \alpha
	mult_by_alpha(nonced_key, nonced_key);
	
	u64 d = m%2 ? ((m/2)+1) : (m/2);
	
	for(u64 j,i=0; i < d-1; i++)
	{
		j = 2*i;	
			
		// set control bits to 0100
		twk = 0x04;

		// compute X_{j} = M_j + \Delta_N
		memcpy(&x0[0], &pt[j*CRYPTO_BLOCKBYTES], CRYPTO_BLOCKBYTES);
		xor_bytes(x0, nonced_mask, CRYPTO_BLOCKBYTES);
		
		// compute W_{j} = E^4_{L_{a+j}}(X_{j})
		twegift_enc(w0, nonced_key, &twk, x0);
		
		// compute Y_{j} = E^4_{L_{a+j}}(W_{j})
		twegift_enc(y0, nonced_key, &twk, w0);
				
		// set control bits to 0101
		twk = 0x05;

		// compute X_{j+1} = Y_{j} + M_{j+1}
		memcpy(&x1[0], &pt[(j+1)*CRYPTO_BLOCKBYTES], CRYPTO_BLOCKBYTES);
		xor_bytes(x1, y0, CRYPTO_BLOCKBYTES);
		
		// compute W_{j+1} = E^5_{L_{a+j}}(X_{j+1})
		twegift_enc(w1, nonced_key, &twk, x1);
		
		// compute Y_{j+1} = E^5_{L_{a+j}}(W_{j+1})
		twegift_enc(y1, nonced_key, &twk, w1);
		
		// W_\xor = W_\xor + W_{j} + W_{j+1}
		xor_bytes(wxor, w0, CRYPTO_BLOCKBYTES);
		xor_bytes(wxor, w1, CRYPTO_BLOCKBYTES);
		
		// compute C_{j} = X_{j+1} + \Delta_N
		xor_bytes(x1, nonced_mask, CRYPTO_BLOCKBYTES);
		memcpy(&ct[j*CRYPTO_BLOCKBYTES], &x1[0], CRYPTO_BLOCKBYTES);
		
		// compute C_{j+1} = X_{j} + Y_{j+1}
		xor_bytes(x0, y1, CRYPTO_BLOCKBYTES);
		memcpy(&ct[(j+1)*CRYPTO_BLOCKBYTES], &x0[0], CRYPTO_BLOCKBYTES);
		
		*ctlen += 2*CRYPTO_BLOCKBYTES;

		// L_{a+j+2} = L_{a+j} \odot \alpha
		// as L_{a+j+1} = L_{a+j}
		mult_by_alpha(nonced_key, nonced_key);
	}
	// set control bits to 1100
	twk = 0x0c;
	
	// compute X_{2d-2} = \Delta_N + <|M|-2(d-1)n>_n
	memcpy(x0, nonced_mask, CRYPTO_BLOCKBYTES);
	x0[0] ^= PARTIAL_DIBLOCK_LEN(d, ptlen);
	
	// compute W_{2d-2} = E^c_{L_{a+2d-2}}(X_{2d-2})
	twegift_enc(w0, nonced_key, &twk, x0);
	
	// compute Y_{2d-2} = E^c_{L_{a+2d-2}}(W_{2d-2})
	twegift_enc(y0, nonced_key, &twk, w0);
	
	// W_\xor = W_\xor + W_{2d-2}
	xor_bytes(wxor, w0, CRYPTO_BLOCKBYTES);
	
	if(m == 2*d)
	{
		// M is diblock oriented (last diblock is full).
		// process last diblock.
		
		// compute X_{2d-1} = Y_{2d-2} + M_{2d-2}
		memcpy(&x1[0], &y0[0], CRYPTO_BLOCKBYTES);
		xor_bytes(x1, &pt[(2*d-2)*CRYPTO_BLOCKBYTES], CRYPTO_BLOCKBYTES);
		
		//compute C_{2d-2} = X_{2d-1} + \Delta_N
		memcpy(&ct[(2*d-2)*CRYPTO_BLOCKBYTES], &x1[0], CRYPTO_BLOCKBYTES);
		xor_bytes(&ct[(2*d-2)*CRYPTO_BLOCKBYTES], nonced_mask, CRYPTO_BLOCKBYTES);
		
		// set control bits to 1101
		twk = 0x0d;
		
		// compute W_{2d-1} = E^d_{L_{a+2d-2}}(X_{2d-1})
		twegift_enc(w1, nonced_key, &twk, x1);
		
		// compute Y_{2d-1} = E^d_{L_{a+2d-2}}(W_{2d-1})
		twegift_enc(y1, nonced_key, &twk, w1);
		
		// W_\xor = W_\xor + W_{2d-1}
		xor_bytes(wxor, w1, CRYPTO_BLOCKBYTES);
		
		// compute C_{2d-1} = chop(X_{2d-2} + Y_{2d-1}) + M_{2d-1}
		// this block could be partial
		memcpy(&ct[(2*d-1)*CRYPTO_BLOCKBYTES], &pt[(2*d-1)*CRYPTO_BLOCKBYTES], PARTIAL_BLOCK_LEN(m, ptlen));
		xor_bytes(&ct[(2*d-1)*CRYPTO_BLOCKBYTES], x0, PARTIAL_BLOCK_LEN(m, ptlen));
		xor_bytes(&ct[(2*d-1)*CRYPTO_BLOCKBYTES], y1, PARTIAL_BLOCK_LEN(m, ptlen));
		
		*ctlen += PARTIAL_DIBLOCK_LEN(d, ptlen);
	}
	else
	{
		// M is not diblock oriented (last diblock is only half filled).
		// process the last block (could be partial)

		//compute C_{2d-2} = chop(Y_{2d-2} + \Delta_N) + M_{2d-2}
		memcpy(&ct[(2*d-2)*CRYPTO_BLOCKBYTES], &pt[(2*d-2)*CRYPTO_BLOCKBYTES], PARTIAL_BLOCK_LEN(m, ptlen));
		xor_bytes(&ct[(2*d-2)*CRYPTO_BLOCKBYTES], y0, PARTIAL_BLOCK_LEN(m, ptlen));
		xor_bytes(&ct[(2*d-2)*CRYPTO_BLOCKBYTES], nonced_mask, PARTIAL_BLOCK_LEN(m, ptlen));
		
		*ctlen += PARTIAL_BLOCK_LEN(m, ptlen);
	}
	// W_\xor = W_\xor + M_{m-1}
	xor_bytes(wxor, &pt[(m-1)*CRYPTO_BLOCKBYTES], PARTIAL_BLOCK_LEN(m, ptlen));
}

/**********************************************************************
 * 
 * @name	:	proc_ct
 * 
 * @note	:	Generates plaintext by decrypting ciphertext.
 * 
 **********************************************************************/
void proc_ct(u8 *nonced_key, u8 *wxor, u8 *pt, u64 *ptlen, u8 *nonced_mask, const u8 *ct, u64 m, u64 ctlen)
{
	u8 twk;
	
	u8 x0[CRYPTO_BLOCKBYTES];
	u8 x1[CRYPTO_BLOCKBYTES];
	
	u8 w0[CRYPTO_BLOCKBYTES];
	u8 w1[CRYPTO_BLOCKBYTES];
	
	u8 y0[CRYPTO_BLOCKBYTES];
	u8 y1[CRYPTO_BLOCKBYTES];
	
	*ptlen = 0;
	
	// L_a = K_N \odot \alpha
	mult_by_alpha(nonced_key, nonced_key);
	
	u64 d = m%2 ? ((m/2)+1) : (m/2);
	
	for(u64 j,i=0; i < d-1; i++)
	{
		j = 2*i;
		
		// set control bits to 0101
		twk = 0x05;
	
		// compute X_{j+1} = C_j + \Delta_N
		memcpy(&x1[0], &ct[j*CRYPTO_BLOCKBYTES], CRYPTO_BLOCKBYTES);
		xor_bytes(x1, nonced_mask, CRYPTO_BLOCKBYTES);
		
		// compute W_{j+1} = E^5_{L_{a+j}}(X_{j+1})
		twegift_enc(w1, nonced_key, &twk, x1);
		
		// compute Y_{j+1} = E^5_{L_{a+j}}(W_{j+1})
		twegift_enc(y1, nonced_key, &twk, w1);
		
		// set control bits to 0100
		twk = 0x04;
		
		// compute X_{j} = C_{j+1} + Y_{j+1}
		memcpy(&x0[0], &ct[(j+1)*CRYPTO_BLOCKBYTES], CRYPTO_BLOCKBYTES);
		xor_bytes(x0, y1, CRYPTO_BLOCKBYTES);
		
		// compute W_{j} = E^4_{L_{a+j}}(X_{j})
		twegift_enc(w0, nonced_key, &twk, x0);
		
		// compute Y_{j} = E^4_{L_{a+j}}(W_{j})
		twegift_enc(y0, nonced_key, &twk, w0);
		
		// W_\xor = W_\xor + W_{j} + W_{j+1}
		xor_bytes(wxor, w0, CRYPTO_BLOCKBYTES);
		xor_bytes(wxor, w1, CRYPTO_BLOCKBYTES);
		
		// compute M_{j} = X_{j} + \Delta_N
		xor_bytes(x0, nonced_mask, CRYPTO_BLOCKBYTES);
		memcpy(&pt[j*CRYPTO_BLOCKBYTES], &x0[0], CRYPTO_BLOCKBYTES);
		
		// compute M_{j+1} = Y_{j} + X_{j+1}
		xor_bytes(x1, y0, CRYPTO_BLOCKBYTES);
		memcpy(&pt[(j+1)*CRYPTO_BLOCKBYTES], &x1[0], CRYPTO_BLOCKBYTES);
		
		*ptlen += 2*CRYPTO_BLOCKBYTES;

		// L_{a+j+2} = L_{a+j} \odot \alpha
		// as L_{a+j+1} = L_{a+j}
		mult_by_alpha(nonced_key, nonced_key);
	}
	
	// set control bits to 1100
	twk = 0x0c;
	
	// compute X_{2d-2} = \Delta_N + <|M|-2(d-1)n>_n
	memcpy(x0, nonced_mask, CRYPTO_BLOCKBYTES);
	x0[0] ^= PARTIAL_DIBLOCK_LEN(d, ctlen);
	
	// compute W_{2d-2} = E^c_{L_{a+2d-2}}(X_{2d-2})
	twegift_enc(w0, nonced_key, &twk, x0);
	
	// compute Y_{2d-2} = E^c_{L_{a+2d-2}}(W_{2d-2})
	twegift_enc(y0, nonced_key, &twk, w0);
	
	// W_\xor = W_\xor + W_{2d-2}
	xor_bytes(wxor, w0, CRYPTO_BLOCKBYTES);
	
	if(m == 2*d)
	{
		// C is diblock oriented (last diblock is full).
		// process last diblock.
		
		//compute M_{2d-2} = \Delta_N + Y_{2d-2} + C_{2d-2}
		memcpy(&pt[(2*d-2)*CRYPTO_BLOCKBYTES], &ct[(2*d-2)*CRYPTO_BLOCKBYTES], CRYPTO_BLOCKBYTES);
		xor_bytes(&pt[(2*d-2)*CRYPTO_BLOCKBYTES], nonced_mask, CRYPTO_BLOCKBYTES);
		xor_bytes(&pt[(2*d-2)*CRYPTO_BLOCKBYTES], &y0[0], CRYPTO_BLOCKBYTES);
		
		// compute X_{2d-1} = Y_{2d-2} + M_{2d-2}
		memcpy(&x1[0], &y0[0], CRYPTO_BLOCKBYTES);
		xor_bytes(x1, &pt[(2*d-2)*CRYPTO_BLOCKBYTES], CRYPTO_BLOCKBYTES);

		// set control bits to 1101
		twk = 0x0d;
		
		// compute W_{2d-1} = E^d_{L_{a+2d-2}}(X_{2d-1})
		twegift_enc(w1, nonced_key, &twk, x1);
		
		// compute Y_{2d-1} = E^d_{L_{a+2d-2}}(W_{2d-1})
		twegift_enc(y1, nonced_key, &twk, w1);
		
		// W_\xor = W_\xor + W_{2d-1}
		xor_bytes(wxor, w1, CRYPTO_BLOCKBYTES);
		
		//compute M_{2d-2} = chop(X_{2d-2} + Y_{2d-1}) + C_{2d-1}
		// this block could be partial
		memcpy(&pt[(2*d-1)*CRYPTO_BLOCKBYTES], &ct[(2*d-1)*CRYPTO_BLOCKBYTES], PARTIAL_BLOCK_LEN(m, ctlen));
		xor_bytes(&pt[(2*d-1)*CRYPTO_BLOCKBYTES], x0, PARTIAL_BLOCK_LEN(m, ctlen));
		xor_bytes(&pt[(2*d-1)*CRYPTO_BLOCKBYTES], y1, PARTIAL_BLOCK_LEN(m, ctlen));
		
		*ptlen += PARTIAL_DIBLOCK_LEN(d, ctlen);
	}
	else
	{
		// M is not diblock oriented (last diblock is half).
		// process the last block (could be partial)
		
		//compute M_{2d-2} = chop(\Delta_N + Y_{2d-2}) + C_{2d-2}
		memcpy(&pt[(2*d-2)*CRYPTO_BLOCKBYTES], &ct[(2*d-2)*CRYPTO_BLOCKBYTES], PARTIAL_BLOCK_LEN(m, ctlen));
		xor_bytes(&pt[(2*d-2)*CRYPTO_BLOCKBYTES], nonced_mask, PARTIAL_BLOCK_LEN(m, ctlen));
		xor_bytes(&pt[(2*d-2)*CRYPTO_BLOCKBYTES], &y0[0], PARTIAL_BLOCK_LEN(m, ctlen));
		
		*ptlen += PARTIAL_BLOCK_LEN(m, ctlen);
	}
	// W_\xor = W_\xor + M_{m-1}
	xor_bytes(wxor, &pt[(m-1)*CRYPTO_BLOCKBYTES], PARTIAL_BLOCK_LEN(m, ctlen));
}

/**********************************************************************
 * 
 * @name	:	proc_tg
 * 
 * @note	:	Tag generator.
 * 
 **********************************************************************/
void proc_tg(u8 *tag, u8 *nonced_key, u8 *nonced_mask, u8 *vxor, u8 *wxor)
{
	u8 twk = 0;
	
	// set control bits to 0110
	twk = 0x06;
	
	// L_{a+m} = K_N \odot \alpha
	mult_by_alpha(nonced_key, nonced_key);
	
	// compute T = E^6_{L_{a+m}}(V_\xor + W_\xor + \Delta_N) + \Delta_N
	xor_bytes(vxor, wxor, CRYPTO_BLOCKBYTES);
	xor_bytes(vxor, nonced_mask, CRYPTO_BLOCKBYTES);
	twegift_enc(tag, nonced_key, &twk, vxor);
	xor_bytes(tag, nonced_mask, CRYPTO_BLOCKBYTES);
}

/**********************************************************************
 * 
 * @name	:	crypto_aead_encrypt
 * 
 * @note	:	Main encryption function.
 * 
 **********************************************************************/
int crypto_aead_encrypt(
	unsigned char *ct, unsigned long long *ctlen,
	const unsigned char *pt, unsigned long long ptlen,
	const unsigned char *ad, unsigned long long adlen,
	const unsigned char *nsec,
	const unsigned char *npub,
	const unsigned char *k
)
{
	// to bypass unused warning on nsec
	nsec = nsec;
	
	u8 nonced_key[CRYPTO_KEYBYTES];
	u8 nonced_mask[CRYPTO_NPUBBYTES];
	
	u8 tag[CRYPTO_ABYTES];
	
	u8 wxor[CRYPTO_BLOCKBYTES] = { 0 };
	u8 vxor[CRYPTO_BLOCKBYTES] = { 0 };
	
	// initialize and derive nonce-based key and mask
	u64 pt_blocks = ptlen%CRYPTO_BLOCKBYTES ? ((ptlen/CRYPTO_BLOCKBYTES)+1) : (ptlen/CRYPTO_BLOCKBYTES);
	u64 ad_blocks = adlen%CRYPTO_BLOCKBYTES ? ((adlen/CRYPTO_BLOCKBYTES)+1) : (adlen/CRYPTO_BLOCKBYTES);
	
	init(nonced_key, nonced_mask, k, npub);
	
	// process AD, if non-empty
	if(ad_blocks != 0)
	{
		proc_ad(nonced_key, vxor, nonced_mask, ad, ad_blocks, adlen);
	}
	
	// process PT, if non-empty
	if(pt_blocks != 0)
	{
		proc_pt(nonced_key, wxor, ct, ctlen, nonced_mask, pt, pt_blocks, ptlen);
	}
	else
	{
		*ctlen = 0;
	}
	
	// generate tag and append to ciphertext
	proc_tg(tag, nonced_key, nonced_mask, vxor, wxor);
	memcpy(&ct[*ctlen],&tag[0],CRYPTO_ABYTES);
	*ctlen += CRYPTO_ABYTES;
	
	return 0;
}

/**********************************************************************
 * 
 * @name	:	crypto_aead_decrypt
 * 
 * @note	:	Main decryption function.
 * 
 **********************************************************************/
int crypto_aead_decrypt(
	unsigned char *pt, unsigned long long *ptlen,
	unsigned char *nsec,
	const unsigned char *ct, unsigned long long ctlen,
	const unsigned char *ad, unsigned long long adlen,
	const unsigned char *npub,
	const unsigned char *k
)
{
	// to bypass unused warning on nsec
	nsec = nsec;
	
	ctlen = ctlen - CRYPTO_ABYTES;
	
	int pass;
	
	u8 nonced_key[CRYPTO_KEYBYTES];
	u8 nonced_mask[CRYPTO_NPUBBYTES];
	
	u8 tag[CRYPTO_ABYTES];
	
	u8 wxor[CRYPTO_BLOCKBYTES] = { 0 };
	u8 vxor[CRYPTO_BLOCKBYTES] = { 0 };
	
	// initialize and derive nonce-based key and mask
	u64 ct_blocks = ctlen%CRYPTO_BLOCKBYTES ? ((ctlen/CRYPTO_BLOCKBYTES)+1) : (ctlen/CRYPTO_BLOCKBYTES);
	u64 ad_blocks = adlen%CRYPTO_BLOCKBYTES ? ((adlen/CRYPTO_BLOCKBYTES)+1) : (adlen/CRYPTO_BLOCKBYTES);
	
	init(nonced_key, nonced_mask, k, npub);
	
	// process AD, if non-empty
	if(ad_blocks != 0)
	{
		proc_ad(nonced_key, vxor, nonced_mask, ad, ad_blocks, adlen);
	}
	
	// process CT, if non-empty
	if(ct_blocks != 0)
	{
		proc_ct(nonced_key, wxor, pt, ptlen, nonced_mask, ct, ct_blocks, ctlen);
	}
	else
	{
		*ptlen = 0;
	}
	
	// generate tag
	proc_tg(tag, nonced_key, nonced_mask, vxor, wxor);
	
	// check computed tag =? received tag (0 if equal)
	pass = memcmp(tag, &ct[*ptlen], CRYPTO_ABYTES);
	
	if(!pass)
	{
		return pass;
	}
	else
	{
		return -1;
	}
}