#include "stdafx.h" #include "MD5Checksum.h" #include "MD5ChecksumDefines.h" extern HANDLE g_EventCheckMd5; #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif /***************************************************************************************** *****************************************************************************************/ BYTE CMD5Checksum::GetMD5(LPCTSTR sFilePath, CString &sMd5) { try { CFile file; if(file.Open(sFilePath,CFile::modeRead)==0) return 1; CMD5Checksum MD5Checksum; //checksum object int nLength = 0; //number of bytes read from the file const int nBufferSize = 500*1024; //checksum the file in blocks of 1024 bytes BYTE Buffer[nBufferSize]; //buffer for data read from the file BYTE btRet = 0; //checksum the file in blocks of 1024 bytes while ((nLength = file.Read( Buffer, nBufferSize )) > 0 ) { if(WaitForSingleObject(g_EventCheckMd5,0) == WAIT_OBJECT_0) { btRet = 2; break; } MD5Checksum.Update( Buffer, nLength ); } file.Close(); //finalise the checksum and return it sMd5 = MD5Checksum.Final(); return btRet; } //report any file exceptions in debug mode only catch (CFileException* e ) { TRACE0("CMD5Checksum::GetMD5: CFileException caught"); throw e; } return 1; } /***************************************************************************************** FUNCTION: CMD5Checksum::RotateLeft DETAILS: private DESCRIPTION: Rotates the bits in a 32 bit DWORD left by a specified amount RETURNS: The rotated DWORD ARGUMENTS: DWORD x : the value to be rotated int n : the number of bits to rotate by *****************************************************************************************/ DWORD CMD5Checksum::RotateLeft(DWORD x, int n) { //check that DWORD is 4 bytes long - true in Visual C++ 6 and 32 bit Windows ASSERT( sizeof(x) == 4 ); //rotate and return x return (x << n) | (x >> (32-n)); } /***************************************************************************************** FUNCTION: CMD5Checksum::FF DETAILS: protected DESCRIPTION: Implementation of basic MD5 transformation algorithm RETURNS: none ARGUMENTS: DWORD &A, B, C, D : Current (partial) checksum DWORD X : Input data DWORD S : MD5_SXX Transformation constant DWORD T : MD5_TXX Transformation constant NOTES: None *****************************************************************************************/ void CMD5Checksum::FF( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T) { DWORD F = (B & C) | (~B & D); A += F + X + T; A = RotateLeft(A, S); A += B; } /***************************************************************************************** FUNCTION: CMD5Checksum::GG DETAILS: protected DESCRIPTION: Implementation of basic MD5 transformation algorithm RETURNS: none ARGUMENTS: DWORD &A, B, C, D : Current (partial) checksum DWORD X : Input data DWORD S : MD5_SXX Transformation constant DWORD T : MD5_TXX Transformation constant NOTES: None *****************************************************************************************/ void CMD5Checksum::GG( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T) { DWORD G = (B & D) | (C & ~D); A += G + X + T; A = RotateLeft(A, S); A += B; } /***************************************************************************************** FUNCTION: CMD5Checksum::HH DETAILS: protected DESCRIPTION: Implementation of basic MD5 transformation algorithm RETURNS: none ARGUMENTS: DWORD &A, B, C, D : Current (partial) checksum DWORD X : Input data DWORD S : MD5_SXX Transformation constant DWORD T : MD5_TXX Transformation constant NOTES: None *****************************************************************************************/ void CMD5Checksum::HH( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T) { DWORD H = (B ^ C ^ D); A += H + X + T; A = RotateLeft(A, S); A += B; } /***************************************************************************************** FUNCTION: CMD5Checksum::II DETAILS: protected DESCRIPTION: Implementation of basic MD5 transformation algorithm RETURNS: none ARGUMENTS: DWORD &A, B, C, D : Current (partial) checksum DWORD X : Input data DWORD S : MD5_SXX Transformation constant DWORD T : MD5_TXX Transformation constant NOTES: None *****************************************************************************************/ void CMD5Checksum::II( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T) { DWORD I = (C ^ (B | ~D)); A += I + X + T; A = RotateLeft(A, S); A += B; } /***************************************************************************************** FUNCTION: CMD5Checksum::ByteToDWord DETAILS: private DESCRIPTION: Transfers the data in an 8 bit array to a 32 bit array RETURNS: void ARGUMENTS: DWORD* Output : the 32 bit (unsigned long) destination array BYTE* Input : the 8 bit (unsigned char) source array UINT nLength : the number of 8 bit data items in the source array NOTES: Four BYTES from the input array are transferred to each DWORD entry of the output array. The first BYTE is transferred to the bits (0-7) of the output DWORD, the second BYTE to bits 8-15 etc. The algorithm assumes that the input array is a multiple of 4 bytes long so that there is a perfect fit into the array of 32 bit words. *****************************************************************************************/ void CMD5Checksum::ByteToDWord(DWORD* Output, BYTE* Input, UINT nLength) { //entry invariants ASSERT( nLength % 4 == 0 ); ASSERT( AfxIsValidAddress(Output, nLength/4, TRUE) ); ASSERT( AfxIsValidAddress(Input, nLength, FALSE) ); //initialisations UINT i=0; //index to Output array UINT j=0; //index to Input array //transfer the data by shifting and copying for ( ; j < nLength; i++, j += 4) { Output[i] = (ULONG)Input[j] | (ULONG)Input[j+1] << 8 | (ULONG)Input[j+2] << 16 | (ULONG)Input[j+3] << 24; } } /***************************************************************************************** FUNCTION: CMD5Checksum::Transform DETAILS: protected DESCRIPTION: MD5 basic transformation algorithm; transforms 'm_lMD5' RETURNS: void ARGUMENTS: BYTE Block[64] NOTES: An MD5 checksum is calculated by four rounds of 'Transformation'. The MD5 checksum currently held in m_lMD5 is merged by the transformation process with data passed in 'Block'. *****************************************************************************************/ void CMD5Checksum::Transform(BYTE Block[64]) { //initialise local data with current checksum ULONG a = m_lMD5[0]; ULONG b = m_lMD5[1]; ULONG c = m_lMD5[2]; ULONG d = m_lMD5[3]; //copy BYTES from input 'Block' to an array of ULONGS 'X' ULONG X[16]; ByteToDWord( X, Block, 64 ); //Perform Round 1 of the transformation FF (a, b, c, d, X[ 0], MD5_S11, MD5_T01); FF (d, a, b, c, X[ 1], MD5_S12, MD5_T02); FF (c, d, a, b, X[ 2], MD5_S13, MD5_T03); FF (b, c, d, a, X[ 3], MD5_S14, MD5_T04); FF (a, b, c, d, X[ 4], MD5_S11, MD5_T05); FF (d, a, b, c, X[ 5], MD5_S12, MD5_T06); FF (c, d, a, b, X[ 6], MD5_S13, MD5_T07); FF (b, c, d, a, X[ 7], MD5_S14, MD5_T08); FF (a, b, c, d, X[ 8], MD5_S11, MD5_T09); FF (d, a, b, c, X[ 9], MD5_S12, MD5_T10); FF (c, d, a, b, X[10], MD5_S13, MD5_T11); FF (b, c, d, a, X[11], MD5_S14, MD5_T12); FF (a, b, c, d, X[12], MD5_S11, MD5_T13); FF (d, a, b, c, X[13], MD5_S12, MD5_T14); FF (c, d, a, b, X[14], MD5_S13, MD5_T15); FF (b, c, d, a, X[15], MD5_S14, MD5_T16); //Perform Round 2 of the transformation GG (a, b, c, d, X[ 1], MD5_S21, MD5_T17); GG (d, a, b, c, X[ 6], MD5_S22, MD5_T18); GG (c, d, a, b, X[11], MD5_S23, MD5_T19); GG (b, c, d, a, X[ 0], MD5_S24, MD5_T20); GG (a, b, c, d, X[ 5], MD5_S21, MD5_T21); GG (d, a, b, c, X[10], MD5_S22, MD5_T22); GG (c, d, a, b, X[15], MD5_S23, MD5_T23); GG (b, c, d, a, X[ 4], MD5_S24, MD5_T24); GG (a, b, c, d, X[ 9], MD5_S21, MD5_T25); GG (d, a, b, c, X[14], MD5_S22, MD5_T26); GG (c, d, a, b, X[ 3], MD5_S23, MD5_T27); GG (b, c, d, a, X[ 8], MD5_S24, MD5_T28); GG (a, b, c, d, X[13], MD5_S21, MD5_T29); GG (d, a, b, c, X[ 2], MD5_S22, MD5_T30); GG (c, d, a, b, X[ 7], MD5_S23, MD5_T31); GG (b, c, d, a, X[12], MD5_S24, MD5_T32); //Perform Round 3 of the transformation HH (a, b, c, d, X[ 5], MD5_S31, MD5_T33); HH (d, a, b, c, X[ 8], MD5_S32, MD5_T34); HH (c, d, a, b, X[11], MD5_S33, MD5_T35); HH (b, c, d, a, X[14], MD5_S34, MD5_T36); HH (a, b, c, d, X[ 1], MD5_S31, MD5_T37); HH (d, a, b, c, X[ 4], MD5_S32, MD5_T38); HH (c, d, a, b, X[ 7], MD5_S33, MD5_T39); HH (b, c, d, a, X[10], MD5_S34, MD5_T40); HH (a, b, c, d, X[13], MD5_S31, MD5_T41); HH (d, a, b, c, X[ 0], MD5_S32, MD5_T42); HH (c, d, a, b, X[ 3], MD5_S33, MD5_T43); HH (b, c, d, a, X[ 6], MD5_S34, MD5_T44); HH (a, b, c, d, X[ 9], MD5_S31, MD5_T45); HH (d, a, b, c, X[12], MD5_S32, MD5_T46); HH (c, d, a, b, X[15], MD5_S33, MD5_T47); HH (b, c, d, a, X[ 2], MD5_S34, MD5_T48); //Perform Round 4 of the transformation II (a, b, c, d, X[ 0], MD5_S41, MD5_T49); II (d, a, b, c, X[ 7], MD5_S42, MD5_T50); II (c, d, a, b, X[14], MD5_S43, MD5_T51); II (b, c, d, a, X[ 5], MD5_S44, MD5_T52); II (a, b, c, d, X[12], MD5_S41, MD5_T53); II (d, a, b, c, X[ 3], MD5_S42, MD5_T54); II (c, d, a, b, X[10], MD5_S43, MD5_T55); II (b, c, d, a, X[ 1], MD5_S44, MD5_T56); II (a, b, c, d, X[ 8], MD5_S41, MD5_T57); II (d, a, b, c, X[15], MD5_S42, MD5_T58); II (c, d, a, b, X[ 6], MD5_S43, MD5_T59); II (b, c, d, a, X[13], MD5_S44, MD5_T60); II (a, b, c, d, X[ 4], MD5_S41, MD5_T61); II (d, a, b, c, X[11], MD5_S42, MD5_T62); II (c, d, a, b, X[ 2], MD5_S43, MD5_T63); II (b, c, d, a, X[ 9], MD5_S44, MD5_T64); //add the transformed values to the current checksum m_lMD5[0] += a; m_lMD5[1] += b; m_lMD5[2] += c; m_lMD5[3] += d; } /***************************************************************************************** CONSTRUCTOR: CMD5Checksum DESCRIPTION: Initialises member data ARGUMENTS: None NOTES: None *****************************************************************************************/ CMD5Checksum::CMD5Checksum() { // zero members memset( m_lpszBuffer, 0, 64 ); m_nCount[0] = m_nCount[1] = 0; // Load magic state initialization constants m_lMD5[0] = MD5_INIT_STATE_0; m_lMD5[1] = MD5_INIT_STATE_1; m_lMD5[2] = MD5_INIT_STATE_2; m_lMD5[3] = MD5_INIT_STATE_3; } /***************************************************************************************** FUNCTION: CMD5Checksum::DWordToByte DETAILS: private DESCRIPTION: Transfers the data in an 32 bit array to a 8 bit array RETURNS: void ARGUMENTS: BYTE* Output : the 8 bit destination array DWORD* Input : the 32 bit source array UINT nLength : the number of 8 bit data items in the source array NOTES: One DWORD from the input array is transferred into four BYTES in the output array. The first (0-7) bits of the first DWORD are transferred to the first output BYTE, bits bits 8-15 are transferred from the second BYTE etc. The algorithm assumes that the output array is a multiple of 4 bytes long so that there is a perfect fit of 8 bit BYTES into the 32 bit DWORDs. *****************************************************************************************/ void CMD5Checksum::DWordToByte(BYTE* Output, DWORD* Input, UINT nLength ) { //entry invariants ASSERT( nLength % 4 == 0 ); ASSERT( AfxIsValidAddress(Output, nLength, TRUE) ); ASSERT( AfxIsValidAddress(Input, nLength/4, FALSE) ); //transfer the data by shifting and copying UINT i = 0; UINT j = 0; for ( ; j < nLength; i++, j += 4) { Output[j] = (UCHAR)(Input[i] & 0xff ); Output[j+1] = (UCHAR)((Input[i] >> 8) & 0xff); Output[j+2] = (UCHAR)((Input[i] >> 16) & 0xff); Output[j+3] = (UCHAR)((Input[i] >> 24) & 0xff); } } /***************************************************************************************** FUNCTION: CMD5Checksum::Final DETAILS: protected DESCRIPTION: Implementation of main MD5 checksum algorithm; ends the checksum calculation. RETURNS: CString : the final hexadecimal MD5 checksum result ARGUMENTS: None NOTES: Performs the final MD5 checksum calculation ('Update' does most of the work, this function just finishes the calculation.) *****************************************************************************************/ CString CMD5Checksum::Final() { //Save number of bits BYTE Bits[8]; DWordToByte( Bits, m_nCount, 8 ); //Pad out to 56 mod 64. UINT nIndex = (UINT)((m_nCount[0] >> 3) & 0x3f); UINT nPadLen = (nIndex < 56) ? (56 - nIndex) : (120 - nIndex); Update( PADDING, nPadLen ); //Append length (before padding) Update( Bits, 8 ); //Store final state in 'lpszMD5' const int nMD5Size = 16; unsigned char lpszMD5[ nMD5Size ]; DWordToByte( lpszMD5, m_lMD5, nMD5Size ); //Convert the hexadecimal checksum to a CString CString strMD5; for ( int i=0; i < nMD5Size; i++) { CString Str; if (lpszMD5[i] == 0) { Str = CString("00"); } else if (lpszMD5[i] <= 15) { Str.Format(_T("0%X"),lpszMD5[i]); } else { Str.Format(_T("%X"),lpszMD5[i]); } ASSERT( Str.GetLength() == 2 ); strMD5 += Str; } ASSERT( strMD5.GetLength() == 32 ); /* char szMd5[MAX_DESC] = {0}; sprintf_s(szMd5, sizeof(szMd5), "%s", strMD5.GetBuffer()); strMD5.ReleaseBuffer(); return szMd5; */ return strMD5; } /***************************************************************************************** FUNCTION: CMD5Checksum::Update DETAILS: protected DESCRIPTION: Implementation of main MD5 checksum algorithm RETURNS: void ARGUMENTS: BYTE* Input : input block UINT nInputLen : length of input block NOTES: Computes the partial MD5 checksum for 'nInputLen' bytes of data in 'Input' *****************************************************************************************/ void CMD5Checksum::Update( BYTE* Input, ULONG nInputLen ) { //Compute number of bytes mod 64 UINT nIndex = (UINT)((m_nCount[0] >> 3) & 0x3F); //Update number of bits if ( ( m_nCount[0] += nInputLen << 3 ) < ( nInputLen << 3) ) { m_nCount[1]++; } m_nCount[1] += (nInputLen >> 29); //Transform as many times as possible. UINT i=0; UINT nPartLen = 64 - nIndex; if (nInputLen >= nPartLen) { memcpy( &m_lpszBuffer[nIndex], Input, nPartLen ); Transform( m_lpszBuffer ); for (i = nPartLen; i + 63 < nInputLen; i += 64) { Transform( &Input[i] ); } nIndex = 0; } else { i = 0; } // Buffer remaining input memcpy( &m_lpszBuffer[nIndex], &Input[i], nInputLen-i); } CString CMD5Checksum::GetMD5OfString(CString strString) { try { CMD5Checksum MD5Checksum; //checksum object int nLength = strString.GetLength(); //number of bytes read from the file //const int nBufferSize = 1024; //checksum the file in blocks of 1024 bytes BYTE *Buffer; //buffer for data read from the file Buffer=(BYTE*)(strString.GetBuffer(nLength)); //checksum the file in blocks of 1024 bytes //while ((nLength = File.Read( Buffer, nBufferSize )) > 0 ) //{ MD5Checksum.Update( Buffer, nLength ); //} //finalise the checksum and return it return MD5Checksum.Final(); } //report any file exceptions in debug mode only catch (CFileException* e ) { TRACE0("CMD5Checksum::GetMD5: CFileException caught"); throw e; } }