
/* ****************************************************************************

 * eID Middleware Project.
 * Copyright (C) 2008-2010 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see
 * http://www.gnu.org/licenses/.

**************************************************************************** */
#include "hash.h"
#include "mwexception.h"
#include "eiderrors.h"

using namespace eIDMW;

CHash::CHash()
{
	m_bInitialized = false;
}

unsigned long CHash::GetHashLength(tHashAlgo algo)
{
	switch (algo)
	{
		case ALGO_MD5:
			return 16;
		case ALGO_SHA1:
			return 20;
		case ALGO_MD5_SHA1:
			return 36;
		case ALGO_SHA256:
			return 32;
		case ALGO_SHA384:
			return 48;
		case ALGO_SHA512:
			return 64;
		case ALGO_RIPEMD160:
			return 20;
		default:
			throw CMWEXCEPTION(EIDMW_ERR_PARAM_BAD);
	}

}

CByteArray CHash::Hash(tHashAlgo algo, const CByteArray & data)
{
	return Hash(algo, data, 0, data.Size());
}

CByteArray CHash::Hash(tHashAlgo algo, const CByteArray & data,
		       unsigned long ulOffset, unsigned long ulLen)
{
	Init(algo);
	Update(data, ulOffset, ulLen);
	return GetHash();
}

void CHash::Init(tHashAlgo algo)
{
	switch (algo)
	{
		case ALGO_MD5:
			md5_init(&m_md1);
			break;
		case ALGO_SHA1:
			sha1_init(&m_md1);
			break;
		case ALGO_MD5_SHA1:
			md5_init(&m_md1);
			sha1_init(&m_md2);
			break;
		case ALGO_SHA256:
			sha256_init(&m_md1);
			break;
		case ALGO_SHA384:
			sha384_init(&m_md1);
			break;
		case ALGO_SHA512:
			sha512_init(&m_md1);
			break;
		case ALGO_RIPEMD160:
			rmd160_init(&m_md1);
			break;
		default:
			throw CMWEXCEPTION(EIDMW_ERR_PARAM_BAD);
	}

	m_Algo = algo;
	m_bInitialized = true;
}

void CHash::Update(const CByteArray & data)
{
	Update(data, 0, data.Size());
}

void CHash::Update(const CByteArray & data, unsigned long ulOffset,
		   unsigned long ulLen)
{
	if (!m_bInitialized)
		throw CMWEXCEPTION(EIDMW_ERR_PARAM_BAD);

	if (ulLen != 0)
	{
		const unsigned char *pucData = data.GetBytes() + ulOffset;

		switch (m_Algo)
		{
			case ALGO_MD5:
				md5_process(&m_md1, pucData, ulLen);
				break;
			case ALGO_SHA1:
				sha1_process(&m_md1, pucData, ulLen);
				break;
			case ALGO_MD5_SHA1:
				md5_process(&m_md1, pucData, ulLen);
				sha1_process(&m_md2, pucData, ulLen);
				break;
			case ALGO_SHA256:
				sha256_process(&m_md1, pucData, ulLen);
				break;
			case ALGO_SHA384:
				sha384_process(&m_md1, pucData, ulLen);
				break;
			case ALGO_SHA512:
				sha512_process(&m_md1, pucData, ulLen);
				break;
			case ALGO_RIPEMD160:
				rmd160_process(&m_md1, pucData, ulLen);
				break;
			default:
				throw CMWEXCEPTION(EIDMW_ERR_PARAM_BAD);
		}
	}
}

CByteArray CHash::GetHash()
{
	if (!m_bInitialized)
		throw CMWEXCEPTION(EIDMW_ERR_PARAM_BAD);

	m_bInitialized = false;

	// hash result
	unsigned char tucHash[64];	// make sure this is enough if other hashes are added!!!

	switch (m_Algo)
	{
		case ALGO_MD5:
			md5_done(&m_md1, tucHash);
			break;
		case ALGO_SHA1:
			sha1_done(&m_md1, tucHash);
			break;
		case ALGO_MD5_SHA1:
			md5_done(&m_md1, tucHash);
			sha1_done(&m_md2, tucHash + 16);
			break;
		case ALGO_SHA256:
			sha256_done(&m_md1, tucHash);
			break;
		case ALGO_SHA384:
			sha384_done(&m_md1, tucHash);
			break;
		case ALGO_SHA512:
			sha512_done(&m_md1, tucHash);
			break;
		case ALGO_RIPEMD160:
			rmd160_done(&m_md1, tucHash);
			break;
		default:
			throw CMWEXCEPTION(EIDMW_ERR_PARAM_BAD);
	}

	return CByteArray(tucHash, GetHashLength(m_Algo));
}
