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

 * eID Middleware Project.
 * Copyright (C) 2008-2009 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 "UnitTest++/src/UnitTest++.h"
#include "../common/Hash.h"

using namespace eIDMW;

#define ERR_LOG(msg) {printf("  - ERR in %s() on line %d: %s\n", __FUNCTION__, __LINE__, msg); errors++;}

static int Test1Hash(const tHashAlgo algo, const CByteArray & data, const CByteArray & res)
{
    int errors = 0;

    CHash oHash;

	// GetHashLength()
	CByteArray h1 = oHash.Hash(algo, data);
	if (h1.Size() != res.Size())
		ERR_LOG("CHash::GetHashLength() returns a wrong length");

	// Hash()
	if (!h1.Equals(res))
		ERR_LOG("CHash::Hash() returns a wrong value");

	// Update()
	oHash.Init(algo);
	oHash.Update(data);
	CByteArray h2 = oHash.GetHash();
	if (!h2.Equals(res))
		ERR_LOG("CHash::Update() returns a wrong value");

	if (data.Size() >= 20)
	{
		// multiple Update()
		oHash.Init(algo);
		oHash.Update(data, 0, 2);
		oHash.Update(data, 2, 18);
		oHash.Update(data, 20, data.Size() - 20);
		CByteArray h3 = oHash.GetHash();
		if (!h3.Equals(res))
			ERR_LOG("doing multiple CHash::Update() returns a wrong value");
	}

	if (data.Size() >= 1)
	{
		// Negative test
		CByteArray h4  = oHash.Hash(algo, data, 0, data.Size() - 1);
		if (h4.Equals(res))
			ERR_LOG("hasing a wrong value returns the correct result !?!?");
	}

	return errors;
}

TEST(Hash)
{
	CByteArray data1((const unsigned char *)
		"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffffgggggggggghhhhhhhhhhiiiiiiiiiijjjjjjjjjj", 100);
	CByteArray data2("abc");

	// For MD5
	unsigned char tucMD5[] = {
		0x90,0x86,0xd9,0x79,0x80,0xe4,0x92,0xed,0x03,0x7d,0xba,0xb6,0xc6,0xd3,0x38,0x20};
	CByteArray md5Res(tucMD5, sizeof(tucMD5));
	int md5_errors = Test1Hash(ALGO_MD5, data1, md5Res);
	CHECK_EQUAL(0, md5_errors);

	unsigned char tuc2MD5[] = {
		0x90,0x01,0x50,0x98,0x3c,0xd2,0x4f,0xb0,0xd6,0x96,0x3f,0x7d,0x28,0xe1,0x7f,0x72};
	CByteArray md5Res2(tuc2MD5, sizeof(tuc2MD5));
	md5_errors = Test1Hash(ALGO_MD5, data2, md5Res2);
	CHECK_EQUAL(0, md5_errors);

	// For SHA1
	unsigned char tucSHA1[] = {
		0x35,0x08,0xf9,0xef,0x99,0xb1,0x30,0xaa,0xdf,0x6e,0x48,0x25,0x4c,0x46,0x72,0x94,0x20,0x2b,0x9a,0xc5};
	CByteArray sha1Res(tucSHA1, sizeof(tucSHA1));
	int sha1_errors = Test1Hash(ALGO_SHA1, data1, sha1Res);
	CHECK_EQUAL(0, sha1_errors);

	unsigned char tuc2SHA1[] = {
		0xa9,0x99,0x3e,0x36,0x47,0x06,0x81,0x6a,0xba,0x3e,0x25,0x71,0x78,0x50,0xc2,0x6c,0x9c,0xd0,0xd8,0x9d};
	CByteArray sha1Res2(tuc2SHA1, sizeof(tuc2SHA1));
	sha1_errors = Test1Hash(ALGO_SHA1, data2, sha1Res2);
	CHECK_EQUAL(0, sha1_errors);

	// For MD5_SHA1
	CByteArray md5sha1Res = md5Res;
	md5sha1Res.Append(sha1Res);
	int md5sha1_errors = Test1Hash(ALGO_MD5_SHA1, data1, md5sha1Res);
	CHECK_EQUAL(0, md5sha1_errors);

	CByteArray md5sha1Res2 = md5Res2;
	md5sha1Res2.Append(sha1Res2);
	md5sha1_errors = Test1Hash(ALGO_MD5_SHA1, data2, md5sha1Res2);
	CHECK_EQUAL(0, md5sha1_errors);

	// For SHA256
	unsigned char tucSHA256[] = {
		0xdf,0x07,0x80,0x15,0xed,0xf3,0x66,0x1c,0x5b,0xfb,0xe4,0xfc,0x79,0xae,0x72,0x2c,
		0xb3,0xeb,0xc2,0xfa,0x05,0x0b,0xff,0x66,0xa3,0x20,0x2b,0xb4,0xaa,0x69,0xda,0xc1};
	CByteArray sha256Res(tucSHA256, sizeof(tucSHA256));
	int sha256_errors = Test1Hash(ALGO_SHA256, data1, sha256Res);
	CHECK_EQUAL(0, sha256_errors);

	unsigned char tuc2SHA256[] = {
		0xba,0x78,0x16,0xbf,0x8f,0x01,0xcf,0xea,0x41,0x41,0x40,0xde,0x5d,0xae,0x22,0x23,
		0xb0,0x03,0x61,0xa3,0x96,0x17,0x7a,0x9c,0xb4,0x10,0xff,0x61,0xf2,0x00,0x15,0xad};
	CByteArray sha256Res2(tuc2SHA256, sizeof(tuc2SHA256));
	sha256_errors = Test1Hash(ALGO_SHA256, data2, sha256Res2);
	CHECK_EQUAL(0, sha256_errors);

	// For SHA384
	unsigned char tucSHA384[] = {
		0x88,0xef,0x7a,0x83,0x08,0xa0,0x63,0xbb,0xd0,0x3d,0x52,0x41,0x31,0xfb,0x32,0xe4,
		0x54,0x38,0xe8,0xe2,0x9f,0x14,0x10,0xf2,0x87,0x08,0x69,0x6a,0x99,0x1e,0xa9,0x1f,
		0x13,0x0e,0x7f,0xfe,0xee,0xab,0x9b,0x36,0x19,0xaf,0x83,0xf6,0xa7,0x14,0x2b,0xda};
	CByteArray sha384Res(tucSHA384, sizeof(tucSHA384));
	int sha384_errors = Test1Hash(ALGO_SHA384, data1, sha384Res);
	CHECK_EQUAL(0, sha384_errors);

	unsigned char tuc2SHA384[] = {
		0xcb,0x00,0x75,0x3f,0x45,0xa3,0x5e,0x8b,0xb5,0xa0,0x3d,0x69,0x9a,0xc6,0x50,0x07,
		0x27,0x2c,0x32,0xab,0x0e,0xde,0xd1,0x63,0x1a,0x8b,0x60,0x5a,0x43,0xff,0x5b,0xed,
		0x80,0x86,0x07,0x2b,0xa1,0xe7,0xcc,0x23,0x58,0xba,0xec,0xa1,0x34,0xc8,0x25,0xa7};
	CByteArray sha384Res2(tuc2SHA384, sizeof(tuc2SHA384));
	sha384_errors = Test1Hash(ALGO_SHA384, data2, sha384Res2);
	CHECK_EQUAL(0, sha384_errors);

	// For SHA512
	unsigned char tucSHA512[] = {
		0xd3,0x40,0x04,0xa6,0x4f,0xce,0x09,0x54,0x35,0x12,0xed,0x82,0x5d,0xc7,0x65,0x95,
		0x84,0xf9,0x1d,0x9a,0x43,0xa2,0x57,0xbd,0x86,0xe6,0xf7,0xd9,0xd5,0x07,0xaa,0x17,
		0x37,0xf2,0x67,0x81,0xb9,0xe5,0x5a,0x50,0x66,0x24,0x42,0x43,0x68,0xfa,0x03,0x34,
		0x85,0x1a,0xff,0xc2,0xe3,0xfd,0xac,0x5b,0x18,0x5b,0x48,0x67,0xd8,0x35,0x57,0x88};
	CByteArray sha512Res(tucSHA512, sizeof(tucSHA512));
	int sha512_errors = Test1Hash(ALGO_SHA512, data1, sha512Res);
	CHECK_EQUAL(0, sha512_errors);

	unsigned char tuc2SHA512[] = {
		0xdd,0xaf,0x35,0xa1,0x93,0x61,0x7a,0xba,0xcc,0x41,0x73,0x49,0xae,0x20,0x41,0x31,
		0x12,0xe6,0xfa,0x4e,0x89,0xa9,0x7e,0xa2,0x0a,0x9e,0xee,0xe6,0x4b,0x55,0xd3,0x9a,
		0x21,0x92,0x99,0x2a,0x27,0x4f,0xc1,0xa8,0x36,0xba,0x3c,0x23,0xa3,0xfe,0xeb,0xbd,
		0x45,0x4d,0x44,0x23,0x64,0x3c,0xe8,0x0e,0x2a,0x9a,0xc9,0x4f,0xa5,0x4c,0xa4,0x9f};
	CByteArray sha512Res2(tuc2SHA512, sizeof(tuc2SHA512));
	sha512_errors = Test1Hash(ALGO_SHA512, data2, sha512Res2);
	CHECK_EQUAL(0, sha512_errors);

	// For RIPEMD160
	unsigned char tucRIPEMD160[] = {
		0x00,0x5e,0x4a,0x45,0xd3,0x72,0x0f,0x93,0xfe,0x3a,0x50,0x95,0x47,0x05,0x0e,0xa3,
		0xb7,0xd2,0x6b,0xf6};
	CByteArray ripemd160Res(tucRIPEMD160, sizeof(tucRIPEMD160));
	int ripemd160_errors = Test1Hash(ALGO_RIPEMD160, data1, ripemd160Res);
	CHECK_EQUAL(0, ripemd160_errors);

	unsigned char tuc2RIPEMD160[] = {
		0x8e,0xb2,0x08,0xf7,0xe0,0x5d,0x98,0x7a,0x9b,0x04,0x4a,0x8e,0x98,0xc6,0xb0,0x87,
		0xf1,0x5a,0x0b,0xfc};
	CByteArray ripemd160Res2(tuc2RIPEMD160, sizeof(tuc2RIPEMD160));
	ripemd160_errors = Test1Hash(ALGO_RIPEMD160, data2, ripemd160Res2);
	CHECK_EQUAL(0, ripemd160_errors);
}

/*
static int Test1Hash(const tHashAlgo algo, const CByteArray & data, const CByteArray & res)
{
	MWLOG(LEV_WARN, MOD_TEST, "HashTest, entry ...\n");

	CHash oHash;

	// GetHashLength()
	CByteArray h1 = oHash.Hash(algo, data);
	CHECK_EQUAL(h1.Size(), res.Size());

	// Hash()
	TEST(h1.Equals(res));

	// Update()
	oHash.Init(algo);
	oHash.Update(data);
	CByteArray h2 = oHash.GetHash();
	TEST(h2.Equals(res));

	// multiple Update()
	oHash.Init(algo);
	oHash.Update(data, 0, 2);
	oHash.Update(data, 2, 18);
	oHash.Update(data, 20, data.Size() - 20);
	CByteArray h3 = oHash.GetHash();
	TEST(h3.Equals(res));

	// Negative test
	CByteArray h4  = oHash.Hash(algo, data, 0, data.Size() - 1);

	TEST(!h4.Equals(res));
}

CByteArray data = CByteArray((const unsigned char *)
	"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffffgggggggggghhhhhhhhhhiiiiiiiiiijjjjjjjjjj", 100);

TEST(MD5)
{
	// For MD5
	unsigned char tucMD5[] = {
		0x90,0x86,0xd9,0x79,0x80,0xe4,0x92,0xed,0x03,0x7d,0xba,0xb6,0xc6,0xd3,0x38,0x20};
	CByteArray md5Res(tucMD5, sizeof(tucMD5));

	Test1Hash(ALGO_MD5, data, md5Res);
}
*/
