/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
 * All rights reserved.
 *
 * This package is an SSL implementation written
 * by Eric Young (eay@cryptsoft.com).
 * The implementation was written so as to conform with Netscapes SSL.
 *
 * This library is free for commercial and non-commercial use as long as
 * the following conditions are adhered to.  The following conditions
 * apply to all code found in this distribution, be it the RC4, RSA,
 * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
 * included with this distribution is covered by the same copyright terms
 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
 *
 * Copyright remains Eric Young's, and as such any Copyright notices in
 * the code are not to be removed.
 * If this package is used in a product, Eric Young should be given attribution
 * as the author of the parts of the library used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    "This product includes cryptographic software written by
 *     Eric Young (eay@cryptsoft.com)"
 *    The word 'cryptographic' can be left out if the routines from the library
 *    being used are not cryptographic related :-).
 * 4. If you include any Windows specific code (or a derivative thereof) from
 *    the apps directory (application code) you must include an acknowledgement:
 *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
 *
 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * The licence and distribution terms for any publicly available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */
/*****************************************************************************/
/**
*
* @file md5.c
*
* Contains code to calculate checksum using md5 algorithm
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver	Who	Date		Changes
* ----- ---- -------- -------------------------------------------------------
* 5.00a sgd	05/17/13 Initial release
*
*
* </pre>
*
* @note
*
******************************************************************************/
/****************************** Include Files *********************************/

#include "md5.h"

/******************************************************************************/
/**
*
* This function sets the memory
*
* @param	dest
*
* @param	ch
*
* @param	count
*
* @return	None
*
* @note		None
*
****************************************************************************/
inline void * MD5Memset( void *dest, int	ch, u32	count )
{
	register char *dst8 = (char*)dest;

	while( count-- )
		*dst8++ = ch;

	return dest;
}

/******************************************************************************/
/**
*
* This function copy the memory
*
* @param	dest
*
* @param	ch
*
* @param	count
*
* @return	None
*
* @note		None
*
****************************************************************************/
inline void * MD5Memcpy( void *dest, const void *src,
		 	 u32 count, boolean	doByteSwap )
{
	register char * dst8 = (char*)dest;
	register char * src8 = (char*)src;
	
	if( doByteSwap == FALSE ) {
		while( count-- )
			*dst8++ = *src8++;
	} else {
		count /= sizeof( u32 );
		
		while( count-- ) {
			dst8[ 0 ] = src8[ 3 ];
			dst8[ 1 ] = src8[ 2 ];
			dst8[ 2 ] = src8[ 1 ];
			dst8[ 3 ] = src8[ 0 ];
			
			dst8 += 4;
			src8 += 4;
		}
	}
	
	return dest;
}

/******************************************************************************/
/**
*
* This function is the core of the MD5 algorithm,
* this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*
* Use binary integer part of the sine of integers (Radians) as constants.
* Calculated as:
*
* for( i = 0; i < 63; i++ )
*     k[ i ] := floor( abs( sin( i + 1 ) ) × pow( 2, 32 ) )
*
* Following number is the per-round shift amount.
*
* @param	dest
*
* @param	ch
*
* @param	count
*
* @return	None
*
* @note		None
*
****************************************************************************/
void MD5Transform( u32 *buffer, u32 *intermediate )
{
	register u32 a, b, c, d;
	
	a = buffer[ 0 ];
	b = buffer[ 1 ];
	c = buffer[ 2 ];
	d = buffer[ 3 ];

	MD5_STEP( F1, a, b, c, d, intermediate[  0 ] + 0xd76aa478,  7 );
	MD5_STEP( F1, d, a, b, c, intermediate[  1 ] + 0xe8c7b756, 12 );
	MD5_STEP( F1, c, d, a, b, intermediate[  2 ] + 0x242070db, 17 );
	MD5_STEP( F1, b, c, d, a, intermediate[  3 ] + 0xc1bdceee, 22 );
	MD5_STEP( F1, a, b, c, d, intermediate[  4 ] + 0xf57c0faf,  7 );
	MD5_STEP( F1, d, a, b, c, intermediate[  5 ] + 0x4787c62a, 12 );
	MD5_STEP( F1, c, d, a, b, intermediate[  6 ] + 0xa8304613, 17 );
	MD5_STEP( F1, b, c, d, a, intermediate[  7 ] + 0xfd469501, 22 );
	MD5_STEP( F1, a, b, c, d, intermediate[  8 ] + 0x698098d8,  7 );
	MD5_STEP( F1, d, a, b, c, intermediate[  9 ] + 0x8b44f7af, 12 );
	MD5_STEP( F1, c, d, a, b, intermediate[ 10 ] + 0xffff5bb1, 17 );
	MD5_STEP( F1, b, c, d, a, intermediate[ 11 ] + 0x895cd7be, 22 );
	MD5_STEP( F1, a, b, c, d, intermediate[ 12 ] + 0x6b901122,  7 );
	MD5_STEP( F1, d, a, b, c, intermediate[ 13 ] + 0xfd987193, 12 );
	MD5_STEP( F1, c, d, a, b, intermediate[ 14 ] + 0xa679438e, 17 );
	MD5_STEP( F1, b, c, d, a, intermediate[ 15 ] + 0x49b40821, 22 );
	
	MD5_STEP( F2, a, b, c, d, intermediate[  1 ] + 0xf61e2562,  5 );
	MD5_STEP( F2, d, a, b, c, intermediate[  6 ] + 0xc040b340,  9 );
	MD5_STEP( F2, c, d, a, b, intermediate[ 11 ] + 0x265e5a51, 14 );
	MD5_STEP( F2, b, c, d, a, intermediate[  0 ] + 0xe9b6c7aa, 20 );
	MD5_STEP( F2, a, b, c, d, intermediate[  5 ] + 0xd62f105d,  5 );
	MD5_STEP( F2, d, a, b, c, intermediate[ 10 ] + 0x02441453,  9 );
	MD5_STEP( F2, c, d, a, b, intermediate[ 15 ] + 0xd8a1e681, 14 );
	MD5_STEP( F2, b, c, d, a, intermediate[  4 ] + 0xe7d3fbc8, 20 );
	MD5_STEP( F2, a, b, c, d, intermediate[  9 ] + 0x21e1cde6,  5 );
	MD5_STEP( F2, d, a, b, c, intermediate[ 14 ] + 0xc33707d6,  9 );
	MD5_STEP( F2, c, d, a, b, intermediate[  3 ] + 0xf4d50d87, 14 );
	MD5_STEP( F2, b, c, d, a, intermediate[  8 ] + 0x455a14ed, 20 );
	MD5_STEP( F2, a, b, c, d, intermediate[ 13 ] + 0xa9e3e905,  5 );
	MD5_STEP( F2, d, a, b, c, intermediate[  2 ] + 0xfcefa3f8,  9 );
	MD5_STEP( F2, c, d, a, b, intermediate[  7 ] + 0x676f02d9, 14 );
	MD5_STEP( F2, b, c, d, a, intermediate[ 12 ] + 0x8d2a4c8a, 20 );
	
	MD5_STEP( F3, a, b, c, d, intermediate[  5 ] + 0xfffa3942,  4 );
	MD5_STEP( F3, d, a, b, c, intermediate[  8 ] + 0x8771f681, 11 );
	MD5_STEP( F3, c, d, a, b, intermediate[ 11 ] + 0x6d9d6122, 16 );
	MD5_STEP( F3, b, c, d, a, intermediate[ 14 ] + 0xfde5380c, 23 );
	MD5_STEP( F3, a, b, c, d, intermediate[  1 ] + 0xa4beea44,  4 );
	MD5_STEP( F3, d, a, b, c, intermediate[  4 ] + 0x4bdecfa9, 11 );
	MD5_STEP( F3, c, d, a, b, intermediate[  7 ] + 0xf6bb4b60, 16 );
	MD5_STEP( F3, b, c, d, a, intermediate[ 10 ] + 0xbebfbc70, 23 );
	MD5_STEP( F3, a, b, c, d, intermediate[ 13 ] + 0x289b7ec6,  4 );
	MD5_STEP( F3, d, a, b, c, intermediate[  0 ] + 0xeaa127fa, 11 );
	MD5_STEP( F3, c, d, a, b, intermediate[  3 ] + 0xd4ef3085, 16 );
	MD5_STEP( F3, b, c, d, a, intermediate[  6 ] + 0x04881d05, 23 );
	MD5_STEP( F3, a, b, c, d, intermediate[  9 ] + 0xd9d4d039,  4 );
	MD5_STEP( F3, d, a, b, c, intermediate[ 12 ] + 0xe6db99e5, 11 );
	MD5_STEP( F3, c, d, a, b, intermediate[ 15 ] + 0x1fa27cf8, 16 );
	MD5_STEP( F3, b, c, d, a, intermediate[  2 ] + 0xc4ac5665, 23 );
	
	MD5_STEP( F4, a, b, c, d, intermediate[  0 ] + 0xf4292244,  6 );
	MD5_STEP( F4, d, a, b, c, intermediate[  7 ] + 0x432aff97, 10 );
	MD5_STEP( F4, c, d, a, b, intermediate[ 14 ] + 0xab9423a7, 15 );
	MD5_STEP( F4, b, c, d, a, intermediate[  5 ] + 0xfc93a039, 21 );
	MD5_STEP( F4, a, b, c, d, intermediate[ 12 ] + 0x655b59c3,  6 );
	MD5_STEP( F4, d, a, b, c, intermediate[  3 ] + 0x8f0ccc92, 10 );
	MD5_STEP( F4, c, d, a, b, intermediate[ 10 ] + 0xffeff47d, 15 );
	MD5_STEP( F4, b, c, d, a, intermediate[  1 ] + 0x85845dd1, 21 );
	MD5_STEP( F4, a, b, c, d, intermediate[  8 ] + 0x6fa87e4f,  6 );
	MD5_STEP( F4, d, a, b, c, intermediate[ 15 ] + 0xfe2ce6e0, 10 );
	MD5_STEP( F4, c, d, a, b, intermediate[  6 ] + 0xa3014314, 15 );
	MD5_STEP( F4, b, c, d, a, intermediate[ 13 ] + 0x4e0811a1, 21 );
	MD5_STEP( F4, a, b, c, d, intermediate[  4 ] + 0xf7537e82,  6 );
	MD5_STEP( F4, d, a, b, c, intermediate[ 11 ] + 0xbd3af235, 10 );
	MD5_STEP( F4, c, d, a, b, intermediate[  2 ] + 0x2ad7d2bb, 15 );
	MD5_STEP( F4, b, c, d, a, intermediate[  9 ] + 0xeb86d391, 21 );

	buffer[ 0 ] += a;
	buffer[ 1 ] += b;
	buffer[ 2 ] += c;
	buffer[ 3 ] += d;
	
}

/******************************************************************************/
/**
*
* This function Start MD5 accumulation
* Set bit count to 0 and buffer to mysterious initialization constants
*
* @param
*
* @return	None
*
* @note		None
*
****************************************************************************/
inline void MD5Init( MD5Context *context )
{
	
	context->buffer[ 0 ] = 0x67452301;
	context->buffer[ 1 ] = 0xefcdab89;
	context->buffer[ 2 ] = 0x98badcfe;
	context->buffer[ 3 ] = 0x10325476;

	context->bits[ 0 ] = 0;
	context->bits[ 1 ] = 0;
	
}


/******************************************************************************/
/**
*
* This function updates context to reflect the concatenation of another
* buffer full of bytes
*
* @param
*
* @param
*
* @param
*
* @param
*
* @return	None
*
* @note		None
*
****************************************************************************/
inline void MD5Update( MD5Context *context, u8 *buffer,
		   u32 len, boolean	doByteSwap )
{
	register u32	temp;
	register u8 *	p;
	
	/*
	 * Update bitcount
	 */

	temp = context->bits[ 0 ];
	
	if( ( context->bits[ 0 ] = temp + ( (u32)len << 3 ) ) < temp ) {
		/*
		 * Carry from low to high
		 */
		context->bits[ 1 ]++;
	}
		
	context->bits[ 1 ] += len >> 29;
	
	/*
	 * Bytes already in shsInfo->data
	 */
	
	temp = ( temp >> 3 ) & 0x3f;

	/*
	 * Handle any leading odd-sized chunks
	 */

	if( temp ) {
		p = (u8 *)context->intermediate + temp;

		temp = MD5_SIGNATURE_BYTE_SIZE - temp;
		
		if( len < temp ) {
			MD5Memcpy( p, buffer, len, doByteSwap );
			return;
		}
		
		MD5Memcpy( p, buffer, temp, doByteSwap );
		
		MD5Transform( context->buffer, (u32 *)context->intermediate );
		
		buffer += temp;
		len    -= temp;
		
	}
		
	/*
	 * Process data in 64-byte, 512 bit, chunks
	 */

	while( len >= MD5_SIGNATURE_BYTE_SIZE ) {
		MD5Memcpy( context->intermediate, buffer, MD5_SIGNATURE_BYTE_SIZE,
				 doByteSwap );
		
		MD5Transform( context->buffer, (u32 *)context->intermediate );
		
		buffer += MD5_SIGNATURE_BYTE_SIZE;
		len    -= MD5_SIGNATURE_BYTE_SIZE;
		
	}

	/*
	 * Handle any remaining bytes of data
	 */
	MD5Memcpy( context->intermediate, buffer, len, doByteSwap );
	
}

/******************************************************************************/
/**
*
* This function final wrap-up - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first
*
* @param
*
* @param
*
* @param
*
* @param
*
* @return	None
*
* @note		None
*
****************************************************************************/
inline void MD5Final( MD5Context *context, u8 *digest,
		  boolean doByteSwap )
{
	u32		count;
	u8 *	p;
	
	/*
	 * Compute number of bytes mod 64
	 */
	count = ( context->bits[ 0 ] >> 3 ) & 0x3F;

	/*
	 * Set the first char of padding to 0x80. This is safe since there is
	 * always at least one byte free
	 */
	p = context->intermediate + count;
	*p++ = 0x80;

	/*
	 * Bytes of padding needed to make 64 bytes
	 */
	count = MD5_SIGNATURE_BYTE_SIZE - 1 - count;

	/*
	 * Pad out to 56 mod 64
	 */
	if( count < 8 ) {
		/*
		 * Two lots of padding: Pad the first block to 64 bytes
		 */
		MD5Memset( p, 0, count );
		
		MD5Transform( context->buffer, (u32 *)context->intermediate );

		/*
		 * Now fill the next block with 56 bytes
		 */
		MD5Memset( context->intermediate, 0, 56 );
	} else {
		/*
		 * Pad block to 56 bytes
		 */
		MD5Memset( p, 0, count - 8 );
	}

	/*
	 * Append length in bits and transform
	 */
	( (u32 *)context->intermediate )[ 14 ] = context->bits[ 0 ];
	( (u32 *)context->intermediate )[ 15 ] = context->bits[ 1 ];

	MD5Transform( context->buffer, (u32 *)context->intermediate );
	
	/*
	 * Now return the digest
	 */
	MD5Memcpy( digest, context->buffer, 16, doByteSwap );
}

/******************************************************************************/
/**
*
* This function calculate and store in 'digest' the MD5 digest of 'len' bytes at
* 'input'. 'digest' must have enough space to hold 16 bytes
*
* @param
*
* @param
*
* @param
*
* @param
*
* @return	None
*
* @note		None
*
****************************************************************************/
void md5( u8 *input, u32 len, u8 *digest, boolean doByteSwap )
{
	MD5Context context;

	MD5Init( &context );
	
	MD5Update( &context, input, len, doByteSwap );
	
	MD5Final( &context, digest, doByteSwap );
}
