// WMMD5.cpp: implementation of the WMMD5 class.
//
//////////////////////////////////////////////////////////////////////
//
// NO WARRANTY: This software is provided AS IS, without warranty of any kind.
//
//////////////////////////////////////////////////////////////////////
//
// Class used to implement MD5 digest algorithm (RFC 1321).
// Author: Robert Cunnings
// Incorporates code written by Eric Young and modified by Wei Dai and
// published as 'md5.cpp' - Copyright 1995 Eric Young.
// 
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "WMMD5.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

WMMD5::WMMD5()
{
	Initialize();
}

void WMMD5::Initialize()
{
	m_ulDigest[0] = 0x67452301L;
	m_ulDigest[1] = 0xefcdab89L;
	m_ulDigest[2] = 0x98badcfeL;
	m_ulDigest[3] = 0x10325476L;

	m_ulCountLoWord = 0;
	m_ulCountHiWord = 0;
}


WMMD5::~WMMD5()
{

}

void WMMD5::Update(const unsigned char *pInput, unsigned long ulLength)
{
	unsigned long ulTemp = m_ulCountLoWord;
	// add number of bits in input
	m_ulCountLoWord = ulTemp + (ulLength << 3);
	if (m_ulCountLoWord < ulTemp)
	{
		// LoWord overflow so carry (increment HiWord)
		m_ulCountHiWord++;
	}
	// calculate adjustment to HiWord
	// add (length * 8) / 2^32
	m_ulCountHiWord += ulLength >> ((8 * sizeof(unsigned long)) - 3);
	// calculate number of residual bytes left in buffer
	unsigned long ulRemainder = (ulTemp >> 3) % DATA_BLOCKSIZE;

	if (ulRemainder != 0)
	{
		if ((ulRemainder + ulLength) >= DATA_BLOCKSIZE)
		{
			// enough to process so clear out buffer
			memcpy((unsigned char *)m_ulBuffer + ulRemainder, pInput, DATA_BLOCKSIZE - ulRemainder);
			ProcessBlock();
			pInput += (DATA_BLOCKSIZE - ulRemainder);
			ulLength -= (DATA_BLOCKSIZE - ulRemainder);
		}
		else
		{
			// not enough data to process so append to buffer and return
			memcpy((unsigned char *)m_ulBuffer + ulRemainder, pInput, ulLength);
			return;
		}
	}
	// process in chunks of DATA_BLOCKSIZE bytes
	if (ulLength >= DATA_BLOCKSIZE)
	{
		do
		{
			memcpy((unsigned char *)m_ulBuffer, pInput, DATA_BLOCKSIZE);
			ProcessBlock();
			pInput += DATA_BLOCKSIZE;
			ulLength -= DATA_BLOCKSIZE;

		} while (ulLength >= DATA_BLOCKSIZE);
	}
	// any remainder copy into buffer
	memcpy((unsigned char *)m_ulBuffer, pInput, ulLength);
}

void WMMD5::Finalize(unsigned long *pDigest)
{
	// calculate number of residual bytes left in buffer
	unsigned long ulRemainder = (m_ulCountLoWord >> 3) % DATA_BLOCKSIZE;
	// add terminal "1" bit
	((unsigned char *)m_ulBuffer)[ulRemainder++] = 0x80;
	if (ulRemainder <= LAST_DATA_BLOCKSIZE)
	{
		// pad out with "0"
		memset((unsigned char *)m_ulBuffer + ulRemainder, 0, LAST_DATA_BLOCKSIZE - ulRemainder);
	}
	else
	{
		// not enough room left for length words so pad with "0" and process
		memset((unsigned char *)m_ulBuffer + ulRemainder, 0, DATA_BLOCKSIZE - ulRemainder);
		ProcessBlock();
		// pad out with "0"
		memset((unsigned char *)m_ulBuffer, 0, LAST_DATA_BLOCKSIZE);
	}

	m_ulBuffer[LEN_LOWORD_OFFSET] = m_ulCountLoWord;
	m_ulBuffer[LEN_HIWORD_OFFSET] = m_ulCountHiWord;

	ProcessBlock();
	memcpy(pDigest, m_ulDigest, DIGEST_SIZE);

	Initialize();
}

static char *pszHexChars = "0123456789ABCDEF";

void WMMD5::FinalizeHexEncoded(string& strDigest)
{
	unsigned char ucDigest[16];
	Finalize((unsigned long *)ucDigest);
	strDigest.erase();
	for (int i = 0; i < 16; i++)
	{
		unsigned char c = ucDigest[i];
		strDigest += pszHexChars[c >> 4];
		strDigest += pszHexChars[c & 0x0f];
	}
}

inline unsigned long RotateLeft(unsigned long x, int nShift)
{
	return nShift ? _lrotl(x, nShift) : x;
}

void WMMD5::ProcessBlock()
{

#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 Subround(f,a,b,c,d,k,s,t)			\
{											\
	a += (k + t + f(b,c,d));				\
	a = RotateLeft(a, s);					\
	a += b;									\
}

	unsigned long A,B,C,D;

	A = m_ulDigest[0];
	B = m_ulDigest[1];
	C = m_ulDigest[2];
	D = m_ulDigest[3];

	/* Round 0 */
	Subround(F,A,B,C,D,m_ulBuffer[ 0], 7,0xd76aa478);
	Subround(F,D,A,B,C,m_ulBuffer[ 1],12,0xe8c7b756);
	Subround(F,C,D,A,B,m_ulBuffer[ 2],17,0x242070db);
	Subround(F,B,C,D,A,m_ulBuffer[ 3],22,0xc1bdceee);
	Subround(F,A,B,C,D,m_ulBuffer[ 4], 7,0xf57c0faf);
	Subround(F,D,A,B,C,m_ulBuffer[ 5],12,0x4787c62a);
	Subround(F,C,D,A,B,m_ulBuffer[ 6],17,0xa8304613);
	Subround(F,B,C,D,A,m_ulBuffer[ 7],22,0xfd469501);
	Subround(F,A,B,C,D,m_ulBuffer[ 8], 7,0x698098d8);
	Subround(F,D,A,B,C,m_ulBuffer[ 9],12,0x8b44f7af);
	Subround(F,C,D,A,B,m_ulBuffer[10],17,0xffff5bb1);
	Subround(F,B,C,D,A,m_ulBuffer[11],22,0x895cd7be);
	Subround(F,A,B,C,D,m_ulBuffer[12], 7,0x6b901122);
	Subround(F,D,A,B,C,m_ulBuffer[13],12,0xfd987193);
	Subround(F,C,D,A,B,m_ulBuffer[14],17,0xa679438e);
	Subround(F,B,C,D,A,m_ulBuffer[15],22,0x49b40821);
	/* Round 1 */
	Subround(G,A,B,C,D,m_ulBuffer[ 1], 5,0xf61e2562);
	Subround(G,D,A,B,C,m_ulBuffer[ 6], 9,0xc040b340);
	Subround(G,C,D,A,B,m_ulBuffer[11],14,0x265e5a51);
	Subround(G,B,C,D,A,m_ulBuffer[ 0],20,0xe9b6c7aa);
	Subround(G,A,B,C,D,m_ulBuffer[ 5], 5,0xd62f105d);
	Subround(G,D,A,B,C,m_ulBuffer[10], 9,0x02441453);
	Subround(G,C,D,A,B,m_ulBuffer[15],14,0xd8a1e681);
	Subround(G,B,C,D,A,m_ulBuffer[ 4],20,0xe7d3fbc8);
	Subround(G,A,B,C,D,m_ulBuffer[ 9], 5,0x21e1cde6);
	Subround(G,D,A,B,C,m_ulBuffer[14], 9,0xc33707d6);
	Subround(G,C,D,A,B,m_ulBuffer[ 3],14,0xf4d50d87);
	Subround(G,B,C,D,A,m_ulBuffer[ 8],20,0x455a14ed);
	Subround(G,A,B,C,D,m_ulBuffer[13], 5,0xa9e3e905);
	Subround(G,D,A,B,C,m_ulBuffer[ 2], 9,0xfcefa3f8);
	Subround(G,C,D,A,B,m_ulBuffer[ 7],14,0x676f02d9);
	Subround(G,B,C,D,A,m_ulBuffer[12],20,0x8d2a4c8a);
	/* Round 2 */
	Subround(H,A,B,C,D,m_ulBuffer[ 5], 4,0xfffa3942);
	Subround(H,D,A,B,C,m_ulBuffer[ 8],11,0x8771f681);
	Subround(H,C,D,A,B,m_ulBuffer[11],16,0x6d9d6122);
	Subround(H,B,C,D,A,m_ulBuffer[14],23,0xfde5380c);
	Subround(H,A,B,C,D,m_ulBuffer[ 1], 4,0xa4beea44);
	Subround(H,D,A,B,C,m_ulBuffer[ 4],11,0x4bdecfa9);
	Subround(H,C,D,A,B,m_ulBuffer[ 7],16,0xf6bb4b60);
	Subround(H,B,C,D,A,m_ulBuffer[10],23,0xbebfbc70);
	Subround(H,A,B,C,D,m_ulBuffer[13], 4,0x289b7ec6);
	Subround(H,D,A,B,C,m_ulBuffer[ 0],11,0xeaa127fa);
	Subround(H,C,D,A,B,m_ulBuffer[ 3],16,0xd4ef3085);
	Subround(H,B,C,D,A,m_ulBuffer[ 6],23,0x04881d05);
	Subround(H,A,B,C,D,m_ulBuffer[ 9], 4,0xd9d4d039);
	Subround(H,D,A,B,C,m_ulBuffer[12],11,0xe6db99e5);
	Subround(H,C,D,A,B,m_ulBuffer[15],16,0x1fa27cf8);
	Subround(H,B,C,D,A,m_ulBuffer[ 2],23,0xc4ac5665);
	/* Round 3 */
	Subround(I,A,B,C,D,m_ulBuffer[ 0], 6,0xf4292244);
	Subround(I,D,A,B,C,m_ulBuffer[ 7],10,0x432aff97);
	Subround(I,C,D,A,B,m_ulBuffer[14],15,0xab9423a7);
	Subround(I,B,C,D,A,m_ulBuffer[ 5],21,0xfc93a039);
	Subround(I,A,B,C,D,m_ulBuffer[12], 6,0x655b59c3);
	Subround(I,D,A,B,C,m_ulBuffer[ 3],10,0x8f0ccc92);
	Subround(I,C,D,A,B,m_ulBuffer[10],15,0xffeff47d);
	Subround(I,B,C,D,A,m_ulBuffer[ 1],21,0x85845dd1);
	Subround(I,A,B,C,D,m_ulBuffer[ 8], 6,0x6fa87e4f);
	Subround(I,D,A,B,C,m_ulBuffer[15],10,0xfe2ce6e0);
	Subround(I,C,D,A,B,m_ulBuffer[ 6],15,0xa3014314);
	Subround(I,B,C,D,A,m_ulBuffer[13],21,0x4e0811a1);
	Subround(I,A,B,C,D,m_ulBuffer[ 4], 6,0xf7537e82);
	Subround(I,D,A,B,C,m_ulBuffer[11],10,0xbd3af235);
	Subround(I,C,D,A,B,m_ulBuffer[ 2],15,0x2ad7d2bb);
	Subround(I,B,C,D,A,m_ulBuffer[ 9],21,0xeb86d391);

	m_ulDigest[0] += A;
	m_ulDigest[1] += B;
	m_ulDigest[2] += C;
	m_ulDigest[3] += D;
}
