// CHash.cpp : implementation file
//



#include "stdafx.h"
#include "CHash.h"



#if SUPPORT_CRC32
	#include "crc32.h" 
#endif
#if SUPPORT_GOSTHASH 
	#include "gosthash.h" 
#endif
#if SUPPORT_MD2 
	#include "md2.h" 
#endif
#if SUPPORT_MD4 
	#include "md4.h" 
#endif
#if SUPPORT_MD5 
	#include "md5.h" 
#endif
#if SUPPORT_SHA1
	#include "sha1.h" 
#endif
#if SUPPORT_SHA2
	#include "sha2.h" 
#endif



/////////////////////////////////////////////////////////////////////////////
// CHash class



CHash::CHash()
{
	// Set some variables for defaultness
	hashAlgo = SHA1;
	hashFile = "";
	hashOperation = 1;
	hashString = "";
	sha2Strength = 256;
	tempHash.Empty();
}



// Do/Retrieve the hash
//
//
CString CHash::DoHash()
{
	CString retHash = "";

	tempHash.Empty();

	// See what hash was selected and initiate it

	// If CRC32 don't bother calling a seperate function, do it here
	if (hashAlgo == CRC32)
	{
#if SUPPORT_CRC32
		DWORD crc32r;
		CCrc32Static crc32;

		if (hashOperation == STRING_HASH)
		{
			crc32.StringCrc32((LPCSTR)hashString, crc32r);
		}
		if (hashOperation == FILE_HASH)
		{
			crc32.FileCrc32Streams((LPCSTR)hashFile, crc32r);
		}

		retHash.Format(_T("%08x"), crc32r);
#endif
	}

	if (hashAlgo == GOSTHASH)
		retHash = GOSTHash();

	if (hashAlgo == MD2)
		retHash = MD2Hash();

	if (hashAlgo == MD4)
		retHash = MD4Hash();

	if (hashAlgo == MD5)
		retHash = MD5Hash();

	if (hashAlgo == SHA1)
		retHash = SHA1Hash();

	if (hashAlgo == SHA2)
		retHash = SHA2Hash();

	// Do the styling
	if (hashFormatting == LOWERCASE_SPACES || hashFormatting == UPPERCASE_SPACES)
	{
		// Add spacing
		bool isChar = false;
		int hashLen = retHash.GetLength();

		for (int i = 0; i < hashLen; i++)
		{
			if (!isChar)
			{
				tempHash += retHash.GetAt(i);
				isChar = true;
			}
			else
			{
				tempHash += retHash.GetAt(i);
				tempHash += " ";
				isChar = false;
			}
		}

		retHash = tempHash;
	}

	if (hashFormatting == UPPERCASE_NOSPACES || hashFormatting == UPPERCASE_SPACES)
	{
		retHash.MakeUpper();
	}

	return retHash;
}
// End DoHash ///////////////////////////////////////////////////



// Get the current algorithm
//
//
int CHash::GetHashAlgorithm()
{
	return hashAlgo;
}
// End GetHashAlgorithm /////////////////////////////////////////



// Get the current operation
//
//
int CHash::GetHashOperation()
{
	return hashOperation;
}
// End GetHashOperation /////////////////////////////////////////



// Get the input string
//
//
CString CHash::GetHashString()
{
	return hashString;
}
// End GetHashString ////////////////////////////////////////////



// Get the hash file
//
//
CString CHash::GetHashFile()
{
	return hashFile;
}
// End GetHashFile //////////////////////////////////////////////



// Get the hash format
//
//
int CHash::GetHashFormat()
{
	return hashFormatting;
}
// End GetHashFormat ////////////////////////////////////////////



// Get the strength of the SHA-2 hash (256, 384 or 512)
//
//
int CHash::GetSHA2Strength()
{
	return sha2Strength;
}
// End GetSHA2Strength //////////////////////////////////////////



// Hash with GOST-Hash
//
//
CString CHash::GOSTHash()
{
#if SUPPORT_GOSTHASH
	// Common variables
	FILE *fileToHash = NULL;
	unsigned char fileBuf[SIZE_OF_BUFFER];
	unsigned long lenRead = 0;

	// The outputted hash
	CString outHash;

	// Temporary working buffers
	unsigned char* tempOut = new unsigned char[256];

	// GOST-Hash object to work with
	GostHashCtx m_gost;

	// Check what operation we're doing
	if (hashOperation == STRING_HASH)
	{
		// Hashing a string, reset the object, give it the text, and finalise it
		gosthash_reset(&m_gost);
		gosthash_update(&m_gost, reinterpret_cast<unsigned char *>(hashString.GetBuffer(hashString.GetLength())), hashString.GetLength());
		gosthash_final(&m_gost, tempOut);
	}

	// Hash a file
	if (hashOperation == FILE_HASH)
	{
		if (hashFile.IsEmpty()) return "";
		gosthash_reset(&m_gost);

		fileToHash = fopen(hashFile, "rb");
	
		do
		{
			lenRead = fread(fileBuf, 1, SIZE_OF_BUFFER, fileToHash);
			if(lenRead != 0)
			{
				gosthash_update(&m_gost, fileBuf, lenRead);
			}
		} while (lenRead == SIZE_OF_BUFFER);

		fclose(fileToHash); fileToHash = NULL;

		gosthash_final(&m_gost, tempOut);
	}

	for (int i = 0 ; i < 32 ; i++)
	{
		char tmp[3];
		_itoa(tempOut[i], tmp , 16);

		if (strlen(tmp) == 1)
		{
			tmp[1] = tmp[0];
			tmp[0] = '0';
			tmp[2] = '\0';
		}
		tempHash += tmp;
	}

	delete[] tempOut;

	outHash = tempHash;
#endif
	return outHash;
}
// End GOSTHash /////////////////////////////////////////////////



// Hash with MD2
//
//
CString CHash::MD2Hash()
{
#if SUPPORT_MD2
	// Common variables
	FILE *fileToHash = NULL;
	unsigned char fileBuf[SIZE_OF_BUFFER];
	unsigned long lenRead = 0;

	// The outputted hash
	CString outHash;

	// Temporary working buffers
	unsigned char* tempOut = new unsigned char[256];

	// MD2 object to work with
	CMD2 m_md2;

	// Check what operation we're doing
	if (hashOperation == STRING_HASH)
	{
		// Hashing a string, open the object, give it the text, and finalise it
		m_md2.Init();
		m_md2.Update(reinterpret_cast<unsigned char *>(hashString.GetBuffer(hashString.GetLength())), hashString.GetLength());
		m_md2.TruncatedFinal(tempOut, 16);
	}

	// Hash a file
	if (hashOperation == FILE_HASH)
	{
		if (hashFile.IsEmpty()) return "";
		m_md2.Init();

		fileToHash = fopen(hashFile, "rb");
	
		do
		{
			lenRead = fread(fileBuf, 1, SIZE_OF_BUFFER, fileToHash);
			if(lenRead != 0)
			{
				m_md2.Update(fileBuf, lenRead);
			}
		} while (lenRead == SIZE_OF_BUFFER);

		fclose(fileToHash); fileToHash = NULL;

		m_md2.TruncatedFinal(tempOut, 16);
	}

	for (int i = 0 ; i < 16 ; i++)
	{
		char tmp[3];
		_itoa(tempOut[i], tmp, 16);
		if (strlen(tmp) == 1)
		{
			tmp[1] = tmp[0];
			tmp[0] = '0';
			tmp[2] = '\0';
		}
		tempHash += tmp;	
	}

	delete[] tempOut;

	outHash = tempHash;
#endif
	return outHash;
}
// End MD2Hash //////////////////////////////////////////////////



// Hash with MD4
//
//
CString CHash::MD4Hash()
{
#if SUPPORT_MD4
	// Common variables
	FILE *fileToHash = NULL;
	unsigned char fileBuf[SIZE_OF_BUFFER];
	unsigned long lenRead = 0;

	// The outputted hash
	CString outHash;

	// Temporary working buffers
	unsigned char* tempOut = new unsigned char[256];

	// MD4 object to work with
	MD4_CTX m_md4;

	// Check what operation we're doing
	if (hashOperation == STRING_HASH)
	{
		// Hashing a string, open the object, give it the text, and finalise it
		MD4Init(&m_md4);
		MD4Update(&m_md4, reinterpret_cast<unsigned char *>(hashString.GetBuffer(hashString.GetLength())), hashString.GetLength());
		MD4Final(tempOut, &m_md4);
	}

	// Hash a file
	if (hashOperation == FILE_HASH)
	{
		if (hashFile.IsEmpty()) return "";
		MD4Init(&m_md4);

		fileToHash = fopen(hashFile, "rb");
	
		do
		{
			lenRead = fread(fileBuf, 1, SIZE_OF_BUFFER, fileToHash);
			if(lenRead != 0)
			{
				MD4Update(&m_md4, fileBuf, lenRead);
			}
		} while (lenRead == SIZE_OF_BUFFER);

		fclose(fileToHash); fileToHash = NULL;

		MD4Final(tempOut, &m_md4);
	}

	for (int i = 0 ; i < 16 ; i++)
	{
		char tmp[3];
		_itoa(tempOut[i], tmp, 16);
		if (strlen(tmp) == 1)
		{
			tmp[1] = tmp[0];
			tmp[0] = '0';
			tmp[2] = '\0';
		}

		tempHash += tmp;	
	}

	delete[] tempOut;

	outHash = tempHash;
#endif
	return outHash;
}
// End MD4Hash //////////////////////////////////////////////////



// Hash with MD5
//
//
CString CHash::MD5Hash()
{
#if SUPPORT_MD5
	// Common variables
	FILE *fileToHash = NULL;
	unsigned long lenRead = 0;

	// The outputted hash
	CString outHash;

	// Temporary working buffers
	unsigned char* tempOut = new unsigned char[256];
	CString tempHash = "";

	// MD5 object to work with
	MD5_CTX m_md5;

	// Check what operation we're doing
	if (hashOperation == STRING_HASH)
	{
		// Hashing a string, open the object, give it the text, and finalise it
		MD5Init(&m_md5);
		MD5Update(&m_md5, reinterpret_cast<unsigned char *>(hashString.GetBuffer(hashString.GetLength())), hashString.GetLength());
		MD5Final(&m_md5);

		// Get each chunk of the hash and append it to the output
		for ( int i = 0 ; i < 16 ; i++ )
		{
			char tmp[3];
			_itoa(m_md5.digest[i], tmp , 16);

			if (strlen(tmp) == 1)
			{
				tmp[1] = tmp[0];
				tmp[0] = '0';
				tmp[2] = '\0';
			}
			tempHash += tmp;
		}
		// Copy the temporary hash to the final buffer
		outHash = tempHash;
	}

	// Hash a file
	if (hashOperation == FILE_HASH)
	{
		if (hashFile.IsEmpty()) return "";

		if (md5file(hashFile.GetBuffer(MAX_PATH), 0, &m_md5))
		{
			for (int i = 0; i < 16; i++)
			{
				char tmp[3];
				_itoa(m_md5.digest[i], tmp, 16);
				if (strlen(tmp) == 1)
				{
					tmp[1] = tmp[0];
					tmp[0] = '0';
					tmp[2] = '\0';
				}
				tempHash += tmp;		
			}
		}
		outHash = tempHash;
	}

	delete[] tempOut;

#endif
	return outHash;
}
// End MD5Hash //////////////////////////////////////////////////



// Hash with SHA-1
//
//
CString CHash::SHA1Hash()
{
#if SUPPORT_SHA1
	// Common variables
	FILE *fileToHash = NULL;
	unsigned char fileBuf[SIZE_OF_BUFFER];
	unsigned long lenRead = 0;

	// The outputted hash
	CString outHash;

	// Temporary working buffers
	unsigned char* tempOut = new unsigned char[256];

	// SHA-1 object
	sha1_ctx m_sha1;

	// Check what operation we're doing
	if (hashOperation == STRING_HASH)
	{
		// Hashing a string, open the object, give it the text, and finalise it
		sha1_begin(&m_sha1);
		sha1_hash(reinterpret_cast<unsigned char *>(hashString.GetBuffer(hashString.GetLength())), hashString.GetLength(), &m_sha1);
		sha1_end(tempOut, &m_sha1);
	}

	// Hash a file
	if (hashOperation == FILE_HASH)
	{
		if (hashFile.IsEmpty()) return "";
		sha1_begin(&m_sha1);

		fileToHash = fopen(hashFile, "rb");
	
		do
		{
			lenRead = fread(fileBuf, 1, SIZE_OF_BUFFER, fileToHash);
			if(lenRead != 0)
			{
				sha1_hash(fileBuf, lenRead, &m_sha1);
			}
		} while (lenRead == SIZE_OF_BUFFER);

		fclose(fileToHash); fileToHash = NULL;

		sha1_end(tempOut, &m_sha1);
	}

	for (int i = 0 ; i < 20 ; i++)
	{
		char tmp[3];
		_itoa(tempOut[i], tmp, 16);
		if (strlen(tmp) == 1)
		{
			tmp[1] = tmp[0];
			tmp[0] = '0';
			tmp[2] = '\0';
		}
		tempHash += tmp;	

	}

	delete[] tempOut;

	outHash = tempHash;
#endif
	return outHash;
}
// End SHA1Hash /////////////////////////////////////////////////



// Hash with SHA-2
//
//
CString CHash::SHA2Hash()
{
#if SUPPORT_SHA2
	// Common variables
	FILE *fileToHash = NULL;
	unsigned char fileBuf[SIZE_OF_BUFFER];
	unsigned long lenRead = 0;

	// The outputted hash
	CString outHash;

	// Temporary working buffers
	unsigned char* tempOut = new unsigned char[256];

	sha2_ctx sha;

	// Work out hash length
	int hashLength = sha2Strength / 8;

	if (hashOperation == STRING_HASH)
	{
		// Hashing a string, open the object, give it the text, and finalise it
		sha2_begin(sha2Strength, &sha);
		sha2_hash(reinterpret_cast<unsigned char *>(hashString.GetBuffer(hashString.GetLength())), hashString.GetLength(), &sha);
		sha2_end(tempOut, &sha);
	}

	// Hash a file
	if (hashOperation == FILE_HASH)
	{
		if (hashFile.IsEmpty()) return "";
		sha2_begin(sha2Strength, &sha);

		fileToHash = fopen(hashFile, "rb");
	
		do
		{
			lenRead = fread(fileBuf, 1, SIZE_OF_BUFFER, fileToHash);
			if(lenRead != 0)
			{
				sha2_hash(fileBuf, lenRead, &sha);
			}
		} while (lenRead == SIZE_OF_BUFFER);

		fclose(fileToHash); fileToHash = NULL;

		sha2_end(tempOut, &sha);
	}

	for (int i = 0; i < hashLength; i++)
	{
		char tmp[3];
		_itoa(tempOut[i], tmp, 16);
		if (strlen(tmp) == 1)
		{
			tmp[1] = tmp[0];
			tmp[0] = '0';
			tmp[2] = '\0';
		}
		tempHash += tmp;		
	}

	delete[] tempOut;

	outHash = tempHash;
#endif
	return outHash;
}
// End SHA2Hash /////////////////////////////////////////////////



// Set the hash algorithm
//
//
void CHash::SetHashAlgorithm(int Algo)
{
	hashAlgo = Algo;
}
// End SetHashAlgorithm /////////////////////////////////////////



// Set the formatting style of the output hash
//
//
void CHash::SetHashFormat(int Style)
{
	if (Style == 1 || Style == 2 || Style == 3 || Style == 4)
	{
		hashFormatting = Style;
	}
	else
	{
		hashFormatting = 1;
	}
}
// End SetHashFormat ////////////////////////////////////////////



// Set the hash operation
// Available operations:
//
// STRING_HASH
// FILE_HASH
//
//
void CHash::SetHashOperation(int Style)
{
	// Check the style is legit
	if (Style == 1 || Style == 2)
	{
		hashOperation = Style;
	}
	else
	{
		hashOperation = 1;
	}
}
// End SetHashOperation /////////////////////////////////////////



// Set the hash file
//
//
void CHash::SetHashFile(LPCSTR File)
{
	hashFile = File;
}
// End SetHashFile //////////////////////////////////////////////



// Set the SHA-2 hash strength
//
// 256, 384 or 512
//
//
void CHash::SetSHA2Strength(int Strength)
{
	// Check the strength is legit
	if (Strength == 256 || Strength == 384 || Strength == 512)
	{
		sha2Strength = Strength;
	}
	else
	{
		sha2Strength = 256;
	}
}
// End SetSHA2Strength///////////////////////////////////////////



// Set the input string
//
//
void CHash::SetHashString(LPCSTR Hash)
{
	hashString.Empty();
	hashString = Hash;
}
// End SetHashString ////////////////////////////////////////////