#include <string.h>
#include "crypto_onetimeauth.h"

#define poly1305_maa44_g8 CRYPTO_SHARED_NAMESPACE(poly1305_maa44_g8)
#define poly1305_maa44_g8_key CRYPTO_SHARED_NAMESPACE(poly1305_maa44_g8_key)
#define poly1305_maa44_g8_keypowers CRYPTO_SHARED_NAMESPACE(poly1305_maa44_g8_keypowers)

extern void poly1305_maa44_g8(unsigned long long *,unsigned long long *,unsigned long long *,unsigned long long,unsigned long long,unsigned long long);
extern void poly1305_maa44_g8_key(unsigned long long *);
extern void poly1305_maa44_g8_keypowers(unsigned long long *,const unsigned long long);

void crypto_onetimeauth(unsigned char *out,const unsigned char *in,long long inlen,const unsigned char *k) {

	unsigned long long s,t,l,*p;
	unsigned long long h[2],r[4],kp[26];

  	memcpy(r,k,32);
  	
  	/* if the message is empty */
  	if (inlen == 0) {
  		
  		p = (unsigned long long *)out; 
  		p[0] = r[2]; p[1] = r[3];
  		return;
  	}
  	
  	/* clamp key */
  	r[0] = r[0] & 0x0ffffffc0fffffff;
  	r[1] = r[1] & 0x0ffffffc0ffffffc;
  	
	p = (unsigned long long *)in;
	l = inlen;
	
	/* s = #blocks, t = #leftover-bytes */
	s = l / 16; t = l % 16;
	if (t > 0) s = s + 1;
	
	/* t = #leftover-bits */
	t = 8*t;	
	
	/* initialize key powers array with first 16 bytes of the key */
	kp[0]  = r[0]; kp[1]  = r[1]; kp[2] = 0; 

	/* copy last 16 bytes of the key */
	kp[24] = r[2]; kp[25] = r[3];
	
	/* get the key in base 2^44 */
	
	poly1305_maa44_g8_key(kp);	

	/* compute key powers if there are more than two blocks */
      	if (s > 2) poly1305_maa44_g8_keypowers(kp,s);	
	
	/* 
	 * h  : output
	 * p  : input
	 * kp : key-powers 
	 * s  : number of blocks in the message
	 * t  : number of bits in the last block
	 *      t = 0 indicates last block is full
	 *      t > 0 indicates last block is partial	 
	 * l  : number of bytes in the message;
	 *      passing this to optimize the computation 
	 *      for short messages up to 15 bytes   
	 */
	poly1305_maa44_g8(h,p,kp,s,t,l);
	
	p = (unsigned long long *)out; 
	p[0] = h[0]; p[1] = h[1];
}