/**********************************************************************
 *                          gosthash.c                                *
 *             Copyright (c) 2005-2006 Cryptocom LTD                  *
 *         This file is distributed under the same license as OpenSSL *
 *                                                                    *
 *    Implementation of GOST R 34.11-94 hash function                 *
 *       uses on gost89.c and gost89.h Doesn't need OpenSSL           *
 **********************************************************************/
#include <string.h>

#include "gost89.h"
#include "gosthash.h"


/* Use OPENSSL_malloc for memory allocation if compiled with 
 * -DOPENSSL_BUILD, and libc malloc otherwise
 */
#ifndef MYALLOC
# ifdef OPENSSL_BUILD
#  include <openssl/crypto.h>
#  define MYALLOC(size) OPENSSL_malloc(size)
#  define MYFREE(ptr) OPENSSL_free(ptr)
# else
#  define MYALLOC(size) malloc(size)
#  define MYFREE(ptr) free(ptr)
# endif
#endif
/* Following functions are various bit meshing routines used in
 * GOST R 34.11-94 algorithms */
static void swap_bytes (byte *w, byte *k) 
	{
	int i,j;
	for (i=0;i<4;i++)	
		for (j=0;j<8;j++) 
			k[i+4*j]=w[8*i+j];

	}

/* was A_A */
static void circle_xor8 (const byte *w, byte *k) 
	{
	byte buf[8];
	int i;
	memcpy(buf,w,8);
	memmove(k,w+8,24);
	for(i=0;i<8;i++) 
		k[i+24]=buf[i]^k[i];
	}

/* was R_R */
static void transform_3 (byte *data) 
	{
	unsigned short int acc;
	acc=(data[0]^data[2]^data[4]^data[6]^data[24]^data[30])|
		((data[1]^data[3]^data[5]^data[7]^data[25]^data[31])<<8);
	memmove(data,data+2,30);
	data[30]=acc&0xff;
	data[31]=acc>>8;
	}

/* Adds blocks of N bytes modulo 2**(8*n). Returns carry*/
static int add_blocks(int n,byte *left, const byte *right) 
	{
	int i;
	int carry=0;
	int sum;
	for (i=0;i<n;i++) 
		{
	   	sum=(int)left[i]+(int)right[i]+carry;
		left[i]=sum & 0xff;
		carry=sum>>8;
		}
	return carry;
	} 

/* Xor two sequences of bytes */
static void xor_blocks (byte *result,const byte *a,const byte *b,size_t len)
	{
	size_t i;
	for (i=0;i<len;i++) result[i]=a[i]^b[i];
	}	

/* 
 * 	Calculate H(i+1) = Hash(Hi,Mi) 
 * 	Where H and M are 32 bytes long
 */
static int hash_step(gost_ctx *c,byte *H,const byte *M) 
	{
	byte U[32],W[32],V[32],S[32],Key[32];
	int i;
	/* Compute first key */
	xor_blocks(W,H,M,32);
	swap_bytes(W,Key);
	/* Encrypt first 8 bytes of H with first key*/
	gost_enc_with_key(c,Key,H,S);
	/* Compute second key*/
	circle_xor8(H,U);
	circle_xor8(M,V);
	circle_xor8(V,V);
	xor_blocks(W,U,V,32);
	swap_bytes(W,Key);
	/* encrypt second 8 bytes of H with second key*/
	gost_enc_with_key(c,Key,H+8,S+8);
	/* compute third key */
	circle_xor8(U,U);
	U[31]=~U[31]; U[29]=~U[29]; U[28]=~U[28]; U[24]=~U[24];
	U[23]=~U[23]; U[20]=~U[20]; U[18]=~U[18]; U[17]=~U[17];
	U[14]=~U[14]; U[12]=~U[12]; U[10]=~U[10]; U[ 8]=~U[ 8];
	U[ 7]=~U[ 7]; U[ 5]=~U[ 5]; U[ 3]=~U[ 3]; U[ 1]=~U[ 1];
	circle_xor8(V,V);
	circle_xor8(V,V);
	xor_blocks(W,U,V,32);
	swap_bytes(W,Key);
	/* encrypt third 8 bytes of H with third key*/
	gost_enc_with_key(c,Key,H+16,S+16);
	/* Compute fourth key */
	circle_xor8(U,U);
	circle_xor8(V,V);
	circle_xor8(V,V);
	xor_blocks(W,U,V,32);
	swap_bytes(W,Key);
	/* Encrypt last 8 bytes with fourth key */
	gost_enc_with_key(c,Key,H+24,S+24);
	for (i=0;i<12;i++) 
		transform_3(S);
	xor_blocks(S,S,M,32);
	transform_3(S);
	xor_blocks(S,S,H,32);
	for (i=0;i<61;i++) 
		transform_3(S);
	memcpy(H,S,32);
	return 1;
	}

/* Initialize gost_hash ctx - cleans up temporary structures and
 * set up substitution blocks
 */
int init_gost_hash_ctx(gost_hash_ctx *ctx, const gost_subst_block *subst_block)
	{	
	memset(ctx,0,sizeof(gost_hash_ctx));
	ctx->cipher_ctx = (gost_ctx *)MYALLOC(sizeof(gost_ctx));
	if (!ctx->cipher_ctx)
		{
		return 0;
		}		
	gost_init(ctx->cipher_ctx,subst_block);
	return 1;
	}

/*
 * Free cipher CTX if it is dynamically allocated. Do not use
 * if cipher ctx is statically allocated as in OpenSSL implementation of
 * GOST hash algroritm
 *
 */ 
void done_gost_hash_ctx(gost_hash_ctx *ctx) 
	{
	/* No need to use gost_destroy, because cipher keys are not really
	 * secret when hashing */
	MYFREE(ctx->cipher_ctx);
	}

/*
 * reset state of hash context to begin hashing new message
 */
int start_hash(gost_hash_ctx *ctx)
	{
	if (!ctx->cipher_ctx) return 0;
	memset(&(ctx->H),0,32);
	memset(&(ctx->S),0,32);
	ctx->len = 0L;
	ctx->left=0;
	return 1;
	}

/*
 * Hash block of arbitrary length
 *
 *
 */
int hash_block(gost_hash_ctx *ctx,const byte *block, size_t length)
	{
	if (ctx->left)
		{
		/*There are some bytes from previous step*/
		unsigned int add_bytes = 32-ctx->left;
		if (add_bytes>length)
			{
			add_bytes = length;
			}	
		memcpy(&(ctx->remainder[ctx->left]),block,add_bytes);
		ctx->left+=add_bytes;
		if (ctx->left<32)
			{
			return 1;
			}	
		block+=add_bytes;
		length-=add_bytes;
		hash_step(ctx->cipher_ctx,ctx->H,ctx->remainder);
		add_blocks(32,ctx->S,ctx->remainder);
		ctx->len+=32;
		ctx->left=0;
		}
	while (length>=32)
		{	
		hash_step(ctx->cipher_ctx,ctx->H,block);
			
		add_blocks(32,ctx->S,block);
		ctx->len+=32;
		block+=32;
		length-=32;
		}	
	if (length)
		{
		memcpy(ctx->remainder,block,ctx->left=length);
		}	
	return 1;	
	}

/*
 * Compute hash value from current state of ctx
 * state of hash ctx becomes invalid and cannot be used for further
 * hashing.
 */ 
int finish_hash(gost_hash_ctx *ctx,byte *hashval)
	{
	byte buf[32];
	byte H[32];
	byte S[32];
	ghosthash_len fin_len=ctx->len;
	byte *bptr;
	memcpy(H,ctx->H,32);
	memcpy(S,ctx->S,32);
	if (ctx->left)
		{
		memset(buf,0,32);
		memcpy(buf,ctx->remainder,ctx->left);
		hash_step(ctx->cipher_ctx,H,buf);
		add_blocks(32,S,buf);
		fin_len+=ctx->left;
		}
	memset(buf,0,32);
	bptr=buf;
	fin_len<<=3; /* Hash length in BITS!!*/
	while(fin_len>0)
		{
		*(bptr++)=(byte)(fin_len&0xFF);
		fin_len>>=8;
		};
	hash_step(ctx->cipher_ctx,H,buf);
	hash_step(ctx->cipher_ctx,H,S);
	memcpy(hashval,H,32);
	return 1;
	}
