/*
//	file:			md5.c
//	discription:	md5 algorithm function define
//	date:			2004-3-23
//	copyright:		Beijing Senselock corp
*/

#include "md5.h"
#include "string.h"

/*
// constant table of MD5
*/
code DWORD ac[] =
{
	0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
	0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
	0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
	0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
	0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
	0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
	0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
	0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
};

code BYTE s[] =
{
	0x07, 0x0C, 0x11, 0x16, 0x05, 0x09, 0x0E, 0x14, 0x04, 0x0B, 0x10, 0x17, 0x06, 0x0A, 0x0F, 0x15
};

/* define the non-linear fuction of MD5					*/
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))


/* define the macro function for MD5 calculate			*/
#define FF(a, b, c, d, x, s, ac)	{											\
										(a) += F((b), (c), (d)) + (x) + (ac);	\
										(a) = hash_rl((a), (s));				\
										(a) += (b);								\
									}

#define GG(a, b, c, d, x, s, ac) 	{											\
										(a) += G((b), (c), (d)) + (x) + (ac);	\
										(a) = hash_rl((a), (s));				\
										(a) += (b);								\
									}

#define HH(a, b, c, d, x, s, ac) 	{											\
										(a) += H((b), (c), (d)) + (x) + (ac);	\
										(a) = hash_rl((a), (s));				\
										(a) += (b);								\
									}

#define II(a, b, c, d, x, s, ac) 	{											\
										(a) += I((b), (c), (d)) + (x) + (ac);	\
										(a) = hash_rl((a), (s));				\
										(a) += (b);								\
									}

/*
// C prorotype	:	void MD5Decode(DWORD *pdwData, BYTE bLen)
// parameter(s)	:	[IN/OUT]	pdwData		- input the DWORD array, return the decode DWORD array
//					[IN]		bLen		- the DWORD array length
// return value	:	no return value
// memory used	:	none
// remarks		:	finish one block MD5 calculation
*/
void MD5Decode(DWORD *pdwData, BYTE bLen)
{
	BYTE i;
	for(i=0; i<bLen; i++)
	{
		LE32_TO_CC(&pdwData[i]);
	}
}

/*
// C prorotype	:	void MD5Encode(DWORD *pdwData, BYTE bLen)
// parameter(s)	:	[IN/OUT]	pdwData		- input the DWORD array, return the decode DWORD array
//					[IN]		bLen		- the DWORD array length
// return value	:	no return value
// memory used	:	none
// remarks		:	finish one block MD5 calculation
*/
void MD5Encode(DWORD *pdwData, BYTE bLen)
{
	BYTE i;
	for(i=0; i<bLen; i++)
	{
		CC_TO_LE32(&pdwData[i]);
	}
}

/*
// C prorotype	:	void MD5Transmit(DWORD* pdwS, DWORD *pdwM)
// parameter(s)	:	[IN/OUT]	pdwS		- input the result of last block, and return 
//											the result of this block
//					[IN]		pdwM		- the input DWORD message array
// return value	:	no return value
// memory used	:	none
// remarks		:	finish one block MD5 calculation
*/
void MD5Transmit(DWORD* pdwS, DWORD *pdwM)
{
	BYTE i, j;
	DWORD a[4];
	a[0] = pdwS[0];
	a[1] = pdwS[1];
	a[2] = pdwS[2];
	a[3] = pdwS[3];
	
	for(i=0; i<64; i++)
	{
		switch(i)
		{
		case 0:
			j = 0; break;
		case 16:
			j = 1; break;
		case 32:
			j = 5; break;
		case 48:
			j = 0; break;
		default:
			break;
		}
		if(i<16)
		{
			FF(a[(0-i)&3], a[(1-i)&3], a[(2-i)&3], a[(3-i)&3], pdwM[j], s[i&3], ac[i]);
			j++;
		}
		else if(i<32)
		{
			GG(a[(0-i)&3], a[(1-i)&3], a[(2-i)&3], a[(3-i)&3], pdwM[j], s[4+(i&3)], ac[i]);
			j = (j+5) & 0x0F;
		}
		else if(i<48)
		{
			HH(a[(0-i)&3], a[(1-i)&3], a[(2-i)&3], a[(3-i)&3], pdwM[j], s[8+(i&3)], ac[i]);
			j = (j+3) & 0x0F;
		}
		else
		{
			II(a[(0-i)&3], a[(1-i)&3], a[(2-i)&3], a[(3-i)&3], pdwM[j], s[12+(i&3)], ac[i]);
			j = (j+7) & 0x0F;
		}
	}

	pdwS[0] += a[0];
	pdwS[1] += a[1];
	pdwS[2] += a[2];
	pdwS[3] += a[3];
}

/*
// C prorotype	:	void MD5Init(MD5_HASH_CONTEXT* pCtx)
// parameter(s)	:	[IN/OUT]	pbCtx		- input the hash context
// return value	:	none
// memory used	:	none
// remarks		:	init hash context for MD5
*/
void MD5Init(MD5_HASH_CONTEXT* pCtx)
{
	memset(pCtx, 0x00, ECC_HASH_CONTEXT_LENGTH);
	
	pCtx->pdwState[0] = 0x67452301;
	pCtx->pdwState[1] = 0xEFCDAB89;
	pCtx->pdwState[2] = 0x98BADCFE;
	pCtx->pdwState[3] = 0x10325476;
}

/*
// C prorotype	:	void MD5Update(MD5_HASH_CONTEXT* pCtx, BYTE* pbIn, BYTE bLen)
// parameter(s)	:	[IN/OUT]	pCtx		- input the hash context pointer
//					[IN]		pbIn		- the buffer of data to be hash
//					[IN]		bLen		- length of input data
// return value	:	no return value
// memory used	:	none
// remarks		:	hash the input data using MD5 algorithm
*/
void MD5Update(MD5_HASH_CONTEXT* pCtx, BYTE* pbIn, BYTE bLen)
{
	BYTE bReLen = (pCtx->pdwCount[0] >> 3) & 0x3F;
	MD5_HASH_DATA_BLOCK* pDataBlock = (MD5_HASH_DATA_BLOCK*)pCtx->pbBuffer;
	BYTE ret = 0;

	pCtx->pdwCount[0] += (DWORD)bLen << 3;
	if(pCtx->pdwCount[0] < ((DWORD)bLen<<3))
		pCtx->pdwCount[1]++;

	while((WORD)bReLen+bLen > 0x3F)
	{
		memcpy(pCtx->pbBuffer+bReLen, pbIn, bLen);
		MD5Decode(pDataBlock->pdwBlock, 0x10);
		MD5Transmit(pCtx->pdwState, pDataBlock->pdwBlock);
		bLen -= 0x40 - bReLen;
		pbIn += 0x40 - bReLen;
		bReLen = 0;
	}
	if(0 != bLen)
	{
		ret = _mem_copy(pCtx->pbBuffer+bReLen, pbIn, bLen);
	}
	return;
}

/*
// C prorotype	:	void MD5Final(MD5_HASH_CONTEXT* pCtx, BYTE* pbOut)
// parameter(s)	:	[IN/OUT]	pCtx		- input the hash context pointer
//					[IN]		pbOut		- return the result of hash
// return value	:	no return value
// memory used	:	none
// remarks		:	finish the MD5 calculate, and get the result
*/
void MD5Final(MD5_HASH_CONTEXT* pCtx, BYTE* pbOut)
{
	BYTE bReLen = (pCtx->pdwCount[0] >> 3) & 0x3F;
	MD5_HASH_DATA_BLOCK* pDataBlock = (MD5_HASH_DATA_BLOCK*)pCtx->pbBuffer;

	pCtx->pbBuffer[bReLen] = 0x80;
	memset(pCtx->pbBuffer+bReLen+1, 0x00, 0x40-bReLen-1);
	MD5Decode(pDataBlock->pdwBlock, 0x10);
	if(bReLen < 56)
	{
		memcpy(pCtx->pbBuffer+56, pCtx->pdwCount, 8);
	}
	MD5Transmit(pCtx->pdwState, pDataBlock->pdwBlock);
	if(bReLen >= 56)
	{
		memset(pCtx->pbBuffer, 0x00, 0x40);
		MD5Decode(pDataBlock->pdwBlock, 0x10);
		memcpy(pCtx->pbBuffer+56, pCtx->pdwCount, 8);
		MD5Transmit(pCtx->pdwState, pDataBlock->pdwBlock);
	}
	MD5Encode(pCtx->pdwState, 0x4);
	memcpy(pbOut, pCtx->pdwState, 0x10);
}
