/*
 * Copyright (c) 2010 SURFnet bv
 * All rights reserved.
 *
 * 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 above 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
 */

/*****************************************************************************
 MacTests.cpp

 Contains test cases to test the MAC implementations
 *****************************************************************************/

#include <stdlib.h>
#include <cppunit/extensions/HelperMacros.h>
#include "MacTests.h"
#include "CryptoFactory.h"
#include <stdio.h>
#include "MacAlgorithm.h"
#include "RNG.h"

CPPUNIT_TEST_SUITE_REGISTRATION(MacTests);

void MacTests::setUp()
{
	mac = NULL;
	rng = NULL;
}

void MacTests::tearDown()
{
	if (mac != NULL)
	{
		CryptoFactory::i()->recycleMacAlgorithm(mac);
	}

	fflush(stdout);
}

#ifndef WITH_FIPS
void MacTests::testHMACMD5()
{
	char testData[4096] = "B64CCFF0DC038F4B4B6C77677B1E89774028CFB6F94EE920ABFABC8C389F7DE287D558664FD05836E854458940A486A367C9B771A7938BE7DFFF2C695BB99CB96F946707FB405A0FA94116FC4002FAD47F813C4DAF6F39B579A2C1E607AF0F80E55FC6742ABB46CF25EFBAEF820DACAE925532E0F2AAAD64D23D0E5682DC38FA47F230EAA299C4D87EB76D45D4B4A08BB47AC873A428708F9D236CF9B11831BC713DB5C8F58A4A4DD228B7A370154CCDB92420B01D0B4141B2CBF05E51D364F7A4D7EE20F1299A697AEAE7341EB6C2F5C458D8AA9A33CE6B2C87B42A9C8EABB3A3311E3828935B8743604895A37D0E9717266872B51CBAB50E9399A0E54457F88DB022CD1859D704FE07C2B530BE14A11072133A9D3A8CD94FCA2B22A320EA08D0292DEADAC4BF705B2CDF71CDC13EC72918F2BD8697BA42AA0AAD62E2D549AC639095EFD2EF606414207CD3770A14FA14AAD7EED197A1A61EBF1FCA89CFE2B69E709C98F4299CF6E1DCF34A98F3C89C6357B5DCDCB335C98EA8DBBA64700EFF9B79CCB875FA49C0C8FA8AA98D6B7B83C3818C4078A8433EEEC71423D9B476029C190ECF1552CF59E1BB2ACDB08663FA792D806E5FF5A6D3E4E09C3F6276663A09D5730A9AAB456D4863F2EDEFEF156A7809AA16D3202AA03C64359BAC628EA1D9ABE2D99999C2891ED49C6081DEA6907C93C5873B4D7880DE271D6016075AFF330FDF221790258A99564F1B51B979DE7997F5FD6676F679B4B14222753547C06960ADCC287EDC29E627FB88BD0E73EBC4E631A58DCA425FADFFEE9DBA177EC182CABA803EEE16636599A88C39A828A5FB206F6DDFD6023F560A421FF3D93B6C4A2A27B78283280C2FD5D249C35270EAE8F8947FA07189BB3A03184C1D8DDB12038184C467EE7DA6CD62F1775F316BE3C2FAB947D49DA19B480E8E4CB4C7818D7769351107386311E5EE411DAC5136869C147DA8E782FA60ED69B13C2DE18CF11FD75CE5F8F16993A026FC3441EE3B23DD6002B8F015E5CC336B5559A427864338C098F4857AA40399916614E061BB176AE4457CA72625A37F08F179A14C39B065CF3283E9425355B6504784C0F4FC1D7932F5C14B43A9CE3604935DE695CDB60B1ED58BD71AB540EEDDF9337B0743D8E624E1A932A69FB1FCA21CFE7F6D2FFDA78F2B8D5BCE01C59BC0A3981BB3BBECF345F43ACD571432B742F80491B490FD71F947480FF9D215EC21237F5AF28C31608DA7A6230CAD24EA799506C8C0B1298E9FD09DA496C6B63710920CC0DC14C00944A7D9B9B751D741A828AB35A5926D3653D45531A4D233DE198439D1946633FF6B91DF0744073CC6E3EC3B177414D8ED2AF30515D8688914667F507667776634A2A11BB68F65B363BD56E8CCF957EB4BB0147862D4C17268AFCE5685C8346E1917B8E8618D3888355CE401AAC5D2DF";
	char testResult[512] = "1026862877813E17E4371095271E1B56";

	// Get a HMAC-MD5 instance
	CPPUNIT_ASSERT((mac = CryptoFactory::i()->getMacAlgorithm(MacAlgo::HMAC_MD5)) != NULL);

	// Key
	char pk[] = "a_key_for_HMAC-MD5_test";
	ByteString k((unsigned char *)pk, sizeof(pk));
	SymmetricKey key;
	CPPUNIT_ASSERT(key.setKeyBits(k));

	ByteString b(testData);
	ByteString osslMac(testResult), shsmMac;

	// Now recreate the MAC using our implementation in a single operation
	CPPUNIT_ASSERT(mac->signInit(&key));
	CPPUNIT_ASSERT(mac->signUpdate(b));
	CPPUNIT_ASSERT(mac->signFinal(shsmMac));

	CPPUNIT_ASSERT(osslMac == shsmMac);

	// Now recreate the MAC in a multiple part operation
	shsmMac.wipe();

	CPPUNIT_ASSERT(mac->signInit(&key));
	CPPUNIT_ASSERT(mac->signUpdate(b.substr(0, 567)));
	CPPUNIT_ASSERT(mac->signUpdate(b.substr(567, 989)));
	CPPUNIT_ASSERT(mac->signUpdate(b.substr(567 + 989)));
	CPPUNIT_ASSERT(mac->signFinal(shsmMac));

	CPPUNIT_ASSERT(osslMac == shsmMac);

	CryptoFactory::i()->recycleMacAlgorithm(mac);

	mac = NULL;
	rng = NULL;
}
#endif

void MacTests::testHMACSHA1()
{
	char testData[4096] = "EA9E200DE69522895F516F1380536406D205D72B31ACB902CC582B12696579826B5AF47892C4831D8AEBBAE039D87F82096364782B49E2E896AB8077E773AB61E05A8223BF17CBF37AB1BDDA70C9B10D9943AF4109571ECFB029156889249B309EC45A34E2D701F1F4093610A7064B69E7FAE5FB39E39EEF3CAA9D64E50DA8822DF4C8DFA064B165ECA4C9CD2A6AF1A251A1F5D4004123B67A7CC3FAB7462AD43535018479BDFD24A2C4A1C4271F907E40E2057A6271BB2BC9740F753CA3692E7ACACE547E64B9FAE79A96DF3DDFCDA7FDCCC7612E41B0B9BBFA3BDE2102F322E8EC42DA2679531782920FD2E7385F6AC887D578B9D844F6A7288EDF613F3663EB538DBFF14EA29AC8D2F58C703303CD370F55BAF02532A0BCB2F80673F6D84B04A5C3EF9E05B640B4CFFD903272089B9960B54197CB5D10C23BAFEA72EFF79C65E0EABEC628EEB5850AC4A23DC80E857EBA5EC5EDB9DD0A5F391B7F8BD354EC976881C157BD5735AD894F020164A9DF214150C5788BC1CF73C15437DBF21D791C75074DF4B51B5C0ABDC79063AB7B7CEDEDC6412F9FB1F6CA171BE6814E033F32CF1F08236A3DD657DB96CCF7A35434C4D0ACFCA81546B4138A6EF6987A874111946010F78074E69BA83E3F395D1C239A122B259F48140E4B80157738A5D443E1762C55EBB67791D5297B3E900B0B53CA7433FB61D2D03C3B34772C5DF77711437003C6DFF4B03DC0B713491F526F9AE3260F903EF9A1BED641E1B262103D395ACAC3FE6F6F1436F0CB3A13AC69C60101EC394DAFFCAD8EED23893C4BC30485FC68854447A9930C0F4FB9AC5B72EF6512278F09F5A024C7CF5C5617461751CE193E54051AE93DF136725642E1DB98842BAA8B8F6231181F0BAA8393FC2EDB7CF0832BA1FDB76A46C8059732B7492711AD1A12BA584D49885E263789E27664B3893EF2DE5C010C093DE1A735D806E4D48C36A0995EADF011FB50D0CA97AFF26B4698524B9F75386897940434A0A38EAF513845BF0F024014001180C651F76B9E0ECA6499F177A1079395C1785B856D5762550EAD2B47B15AF0A6BFBB1B597E72B9E5E6F61769C27AFC29E4D8A523C89D1D2E5E59B57DE89BE04BDC1DCDA0476A157BCB4DA2F7AC0CAF9351772652E2E5B6261501BDF42C23EA9726CB6C258EE9511684CFEECA1C0598D372808002E250EDD4019F85630FD499C35016B8A99C6BF78E69053B4AE7BE6B84009B505594603B363037212DAFCC669F7482995C74A7CC225245DC2E7D4CACC8A59F67513E1D788F0218DA902F440CD2FEB6C2A90A653B0C2DBE45CC67B47863C1090F3F41AFEA9C53CE5C61A7E6B7212EE3BBE67ED91EB1BA006A931055512046779935F84D7CA306C7F9F3894F91F51EFF6005F5A8BD9DD8828BCEA1C4CC6E5F22CE7F14C0A942243C1205FD7697F09C333AE0B263";
	char testResult[512] = "C2FFE4BF83A6FE299CA4A187157F2442EC1527CE";

	// Get a HMAC-SHA1 instance
	CPPUNIT_ASSERT((mac = CryptoFactory::i()->getMacAlgorithm(MacAlgo::HMAC_SHA1)) != NULL);

	// Key
	char pk[] = "a_key_for_HMAC-SHA1_test";
	ByteString k((unsigned char *)pk, sizeof(pk));
	SymmetricKey key;
	CPPUNIT_ASSERT(key.setKeyBits(k));

	ByteString b(testData);
	ByteString osslMac(testResult), shsmMac;

	// Now verify the MAC using our implementation in a single operation
	CPPUNIT_ASSERT(mac->verifyInit(&key));
	CPPUNIT_ASSERT(mac->verifyUpdate(b));
	CPPUNIT_ASSERT(mac->verifyFinal(osslMac));

	// Now recreate the MAC in a multiple part operation
	CPPUNIT_ASSERT(mac->signInit(&key));
	CPPUNIT_ASSERT(mac->signUpdate(b.substr(0, 567)));
	CPPUNIT_ASSERT(mac->signUpdate(b.substr(567, 989)));
	CPPUNIT_ASSERT(mac->signUpdate(b.substr(567 + 989)));
	CPPUNIT_ASSERT(mac->signFinal(shsmMac));

	CPPUNIT_ASSERT(osslMac == shsmMac);

	// Now recreate a wrong MAC
	b[5] ^= 0x28;
	CPPUNIT_ASSERT(mac->signInit(&key));
	CPPUNIT_ASSERT(mac->signUpdate(b.substr(0, 567)));
	CPPUNIT_ASSERT(mac->signUpdate(b.substr(567, 989)));
	CPPUNIT_ASSERT(mac->signUpdate(b.substr(567 + 989)));
	CPPUNIT_ASSERT(mac->signFinal(shsmMac));

	CPPUNIT_ASSERT(osslMac != shsmMac);

	CryptoFactory::i()->recycleMacAlgorithm(mac);

	mac = NULL;
	rng = NULL;
}

void MacTests::testHMACSHA224()
{
	char testData[4096] = "83E369A9F34D645B7E1F2F7BB676A4DFD36C411D5472A9AC6F3945EF5C9A561BB040C38F2F321C582882820C7F44D48CDC5BB84F41818F4AEB226F63078CE131481D26F26C61EE52BD1BFB60EDEDE1E03A8656E87D5662EE0C0AE085E56E488325512467DAC43E99484A16FA419D6704922C1ADE06F7D1188DBEC88384CDBFDCD2E74787AE247A8027EF60383B3B0A7D0D3ADA95BB3AA5F0821AF050A9709C75673F3926CF9AE029158D684F470DE3EE6B00BBC90F85CD6E634E86B67E4D8EB4251B472B02D167790C6E6C38BA9FE39880544EA66EB4C0BFF8AFEB9AFC40ADC24DF191744482F70793CB4A802CF1AB58562CC26D1CAA2E80375BA45507C3E9F7D99223E0E7FE93CAC58B7B0C69231162D2D7DA75EEFD59452642CDC5AA4A118B6D4AD00E8368A44988201C6286CAA8612BCFFF714855DE1E053AFD2EEED9737459540E45AEAB26999C0951228716AF02F0D35264E3411B03D222F331A4695D6DF4E9EE35D5B1015FF0BE46081D7CEC9137824217A711F015639BE76223845F1C2A25A10D29B637C5124CF50AB0CAA1E33D75843D00EAE69C3A189D463377731C3197BF44523936C4F84F143759E3D58891F7B3B51C858EE29BC1DF214AF09C93148172E842A7FC0078D4E106324AAF8862B845F290FE831037B2EFEF2528DF070DD7B1ACD67762CF1071B96FB95C5AA14F7AE13103AF45A1CF3C42C5D53CB5B954F97BA223E70E0098E224BEF8F5430D027B510DFBBC35EE5F9170E4A43BDDFCBAE8B82240DB870B6C7C7E21E21234EFA62F1582A9D150CCE1B8822BD77ED8288B20883AAEEB5BDF9D0EBB8D3FF47DD51B99E9BDB8B8A87D0536CC25D4939ECB13F7B4F7DF5F0BE8231CF3F53CE52D16A29825739B26BA4082975583967180F787ECF98AD956A9CE53759E20960752938C142DF80E57DDCA236A1F596031942016442002683865EDB210073797547D83CE77D3D6C39E2B9034E685BD28D365992E821BECAFC6DF2B60EAE9777FEE7879B176CC602501BA0B0BCB434DFA5517F8D6172647364F235B3C9BA0B1B90FE0FD67CF6650C2D8D2BC08D127DF0AB887F69CDD81D03B4CC7F44A4362C90BB38556D081E51EABD9CA3AA6C877C42FD1B001C030D0B281590696B5BB9C6A78CBE356F7AC72F525300FD13E24755294712DDF48D1AD19F844120306DC99D8CC18516A23BD022CF9DC9CACC168ADDE1C15337F15B3FBEDE4BFA498F2F963E14B7E66CD737A5485227BA1BAA7668D97C58DCE40EE7A843A5E6EB591FF91D6A6292C8A3E95A0B23C1F0B8815BE526EE7C49B5153264BC1207013EA85E9DA37F19BD50DDC9F0A5B9AB4FCFFAD2840B5A8856882E8DF95362DCA13C15328137A2A8318884FCF4D05236CEE9985DB1BA873A9AF5B33E317FA2C0CB94E7C18E46744A374C19D8C9B2788FE50C9E4D237D290555E1077";
	char testResult[512] = "4B089658FF932CA07BF7C42E6EC46BFF560BCAF295826D9D3C0BAE1C";

	// Get a HMAC-SHA224 instance
	CPPUNIT_ASSERT((mac = CryptoFactory::i()->getMacAlgorithm(MacAlgo::HMAC_SHA224)) != NULL);

	// Key
	char pk[] = "a_key_for_HMAC-SHA224_test";
	ByteString k((unsigned char *)pk, sizeof(pk));
	SymmetricKey key;
	CPPUNIT_ASSERT(key.setKeyBits(k));

	ByteString b(testData);
	ByteString osslMac(testResult), shsmMac;

	// Now recreate the MAC using our implementation in a single operation
	CPPUNIT_ASSERT(mac->signInit(&key));
	CPPUNIT_ASSERT(mac->signUpdate(b));
	CPPUNIT_ASSERT(mac->signFinal(shsmMac));

	CPPUNIT_ASSERT(osslMac == shsmMac);

	// Now verify the MAC in a multiple part operation
	CPPUNIT_ASSERT(mac->verifyInit(&key));
	CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(0, 567)));
	CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(567, 989)));
	CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(567 + 989)));
	CPPUNIT_ASSERT(mac->verifyFinal(osslMac));

	// Now don't verify a MAC with different input
	b[600] ^= 0xff;
	CPPUNIT_ASSERT(mac->verifyInit(&key));
	CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(0, 567)));
	CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(567, 989)));
	CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(567 + 989)));
	CPPUNIT_ASSERT(!mac->verifyFinal(osslMac));

	CryptoFactory::i()->recycleMacAlgorithm(mac);

	mac = NULL;
	rng = NULL;
}

void MacTests::testHMACSHA256()
{
	char testData[4096] = "FF7E669D7764FABF6E3550E192E660DF1EE237B49F9473D757DEFA1400534F57C81E8B0E99458F74E6F57A5BB71A50D1EF61EDB3B3FB4BCA57896DFBA50C6C55B04D4C8C804DB8EB2A273E25F49AE2742D2AE430BE9A3A07715AF6816E818B63BB1FF8C876A921CF931F693E624CDAD4B54F3ABBEE4A09BF39E0FE3A313B20CD3A36B0EBD677D9D4D7C576B5EA244CF0436110D9D66110C95BE187D6EDA1E81A4456231BE32A1B5F4CDC02B2DA679322AEBAE0B795763888F07F0222ADBF824F31AEC7B80625DC36DA1F0C3DD20C6E8F79A7FA2D286119894E611EE550364428723E9C33A573F18D02F9DC90ED215EF08C95DB1CED68DA864732BC9A56B8B191815BCC12075641E3E87F46EFD3726BC1D6F47AF7BC573C554279C041C7FD5E924093241855507EE2A837E2CE62E7A9898712EDF593217B6425B3EB855D53D31F38E5A0736E34844AFCAD58A15B06D357697DC921C2FCA77091BFC207B0B4F1FA94F923B05817211462104287B43CA72986D9E6E393C9A075C267B2C1706CAD136E9F549A83E58315DE8D0B67852006EFE8B6E0773519FE6DB316EA596E1998B79C19152AADDDF8E025E7EDCB16B5CCBA2C9F990605A6969D3C667D6EDA1DA5AFF476661349712CD73F707BB0FD6F8BF0C13A90DE9C1C5CE9FB696C22849C1205C8435C818E2EE03F2235B502E0C24068B8687AF9ED065910535072DABB83F651FC60E005BEF909F6EE308B37E43DA01366C062168007D733D5EA4A610D1307BA087E8F883864140BBB02E523997A0D83E9BF289B8EC9F83F57FF553485409B0D82E77F32BD2D06A130103B9F3915730EE8BC3FB099B74AE8B32C478A83D0BAD2281C279D6B802F42A80E179F4ABB69AD485BBAE348C6F774285870F53699500CEC045B9521CF22826FAFFBC4EABE9D856B28DDBC6D2A0F318AE9475E82DE2CA6704B743397EFE45F825019B58D9E671181512D780B1BA34D059BFBF8B9B232E337335C1045682635CEF8D3CB1C744D3A7791F5DB323EF8DE768C63931266EAB0DFACAB0F99BC9C5DA234FE48ECDB23A5FDA2E92EC10AF7476B07C4BB85BDD0600B49F41585F9E0DB2B276BBE0FA7C5792B65012B6564561E4814E8D1EE706935A45F969C9AA110A630E354C7243E21436A180046605FE5D31008045A5C4AB4FF5C23585D77D6C839875CC2E960436A3D90F9664D5522EAB08867B95F9641DF0C81D59DCA8ECE8709A3F521532041BDCF7A7535F940BD3569FD2145A1B34E955E04F66E0CA809BA3FA6A347FD0CB69DADBA93DC03CDF547A96CD3159ACA61EFE20F9A566316BB9C746EB851CE51F04823667CDA0D898253BB10C1C5220B2C30461A49C5FF0DEC38264786C6125720B3768629ADABCF694E01795F618FBD0ADE5C91CB5A981BBA581A9664F51DF758D7300E081E57CE20C8A7210BC3558AD240B7";
	char testResult[512] = "90A49F8EB80D8ED405EFC8D658FCB9102314598939DCAA090756668056B0228A";

	// Get a HMAC-SHA256 instance
	CPPUNIT_ASSERT((mac = CryptoFactory::i()->getMacAlgorithm(MacAlgo::HMAC_SHA256)) != NULL);

	// Key
	char pk[] = "a_key_for_HMAC-SHA256_test";
	ByteString k((unsigned char *)pk, sizeof(pk));
	SymmetricKey key;
	CPPUNIT_ASSERT(key.setKeyBits(k));

	ByteString b(testData);
	ByteString osslMac(testResult);

	// Now verify the MAC using our implementation in a single operation
	CPPUNIT_ASSERT(mac->verifyInit(&key));
	CPPUNIT_ASSERT(mac->verifyUpdate(b));
	CPPUNIT_ASSERT(mac->verifyFinal(osslMac));

	// Now verify the MAC in a multiple part operation
	CPPUNIT_ASSERT(mac->verifyInit(&key));
	CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(0, 567)));
	CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(567, 989)));
	CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(567 + 989)));
	CPPUNIT_ASSERT(mac->verifyFinal(osslMac));

	// Check if bad key is refused
	osslMac[10] ^= 0x11;
	CPPUNIT_ASSERT(mac->verifyInit(&key));
	CPPUNIT_ASSERT(mac->verifyUpdate(b));
	CPPUNIT_ASSERT(!mac->verifyFinal(osslMac));

	CryptoFactory::i()->recycleMacAlgorithm(mac);

	mac = NULL;
	rng = NULL;
}

void MacTests::testHMACSHA384()
{
	char testData[4096] = "7D4C4274CFAFABE3605DE7E35B43F10251A441A6F4B03EE142B9EAE3DEC2E30E1A47FBE779D716427B108CCB2A2C2D7561B879B1C021E00E0498D91859BA3E7851EF3FC25A1BC6EADA8524EAEEDCC0E752AD90DFD0EA2F04B71B8ABF56DA85E4D070C91EC4B7723A02C3B52DAE31EEAF8E397CC3E92FA31E5F3292D8C9F45D459D8D1105E539D3979ADC01EB4E0C90BC33351295628BD0C2E1B4B13764EDD66BE9AD67AA7B4A7780264B0A3DBE099B190D25649B692401E0B758AB2A281B2E1AC9B4F7EE051C1B98BE92463F314A86A7FD40A4683A82457699F514563C70F1231C0B667C7B8CAD5910334CF894A9810D7C7C6DFC1E904D8512813A84911580C2AC56BB005A4FFB512A1290501AE795C832C0BF7DEBCD9B1D84CC0BB5B5F19D02FDBAB01F7B7CA47C99F3CB7D4F0D35622C267EBDADB2D089D0DA73FEA15A25C873C27EF72A84F99EC4E6980695BA6468B34B5AEC0746219468FEE88277739123FB51CEBF264140FFF27B94D6F751F26F0BC27387A2EFD038EEE43013AFC8FE94BE8E272B28ABFE2160BA81416113CBC2A1ADD96DD23F7107F4771CF2016501A8918A9A71C476CEDC10BB355BC144C818FD620301793CB16372A80881C5DFE387619ED651626CF44C8EDA8DA5BA8A6895148EE36037D4ADDBC13C5681935EDC5BD9B35F8E91B84C1F593B3E89614879CAE4DF4413E13655079F3CE21449CCA19E0963C925A75BC294F343016518D2F8FCB13ED38D574232602AD2416E94ED7735680439CE042145552F352156FCA722B31BFAA289C3711F170A845143ADFEF49CA53278358A7FECBD605AE9899192990D728CA37218B65DD9978EC4345100E174F3D04A0C8A5611263DABF7DA0BC6CE50BCD338830688B697FEB71909D1A2E09B62C5C4A8C1069C50C8DD6A432ED2B429F6BBFC304D0F0A35167BBDDCBC7F78959058393BC812820AF4917C56EA9233A1567608534F93857EA783B537CE294B4778E4B5D30D5836AE1F0D25B663A62512D2488E959D7FF000F8A70F31CC4CABB51D6F57A842AEABD707122935690F78F1F8BD7EC8544F1E70B9D6D53C50632F4DF5F0BC75630B427B28BAF2249C72F5864A3CE17627BA4F16F8FF13FA19DDF83736B171C024C95EFBC5D90615C495C2B02685C24C6E64E19C804B1DC138B5BC52A6969C4332F6113ED8C38F03A0107513C611A6FF013D2FC0B00859A6A5DFB8626002EA0126D477B5F9C7B5BEAB61A13AA8841A4C11E9DFB6A7164BBC2D40E940D9D10C56D58FBC76B92EA68425C2FF48BAE0CA2AF6F1CFBFFBBD855688C3960538E2D87088472CD73AB6D0FB86D9411310F5311EFCA3B89949A87D408B86B96C29C89A0180AF8957944E2A385A221081870CECC995FFDDD6994B0856ED0DC6303F7FDE5072F0B3D68BB132B0C5D3113939D614677720186052F4F90DD60A1CFD";
	char testResult[512] = "AC387A4BB1E4E078C43C69087C206F49F56EC63CE244A429DA56B8EC3CAFBC987090DA7A8F6470874CA6049D20AC5154";

	// Get a HMAC-SHA384 instance
	CPPUNIT_ASSERT((mac = CryptoFactory::i()->getMacAlgorithm(MacAlgo::HMAC_SHA384)) != NULL);

	// Key
	char pk[] = "a_key_for_HMAC-SHA384_test";
	ByteString k((unsigned char *)pk, sizeof(pk));
	SymmetricKey key;
	CPPUNIT_ASSERT(key.setKeyBits(k));

	ByteString b(testData);
	ByteString osslMac(testResult), shsmMac;

	// Now recreate the MAC using our implementation in a single operation
	CPPUNIT_ASSERT(mac->signInit(&key));
	CPPUNIT_ASSERT(mac->signUpdate(b));
	CPPUNIT_ASSERT(mac->signFinal(shsmMac));

	CPPUNIT_ASSERT(osslMac == shsmMac);

	// Now recreate the MAC in a multiple part operation
	shsmMac.wipe();

	CPPUNIT_ASSERT(mac->signInit(&key));
	CPPUNIT_ASSERT(mac->signUpdate(b.substr(0, 567)));
	CPPUNIT_ASSERT(mac->signUpdate(b.substr(567, 989)));
	CPPUNIT_ASSERT(mac->signUpdate(b.substr(567 + 989)));
	CPPUNIT_ASSERT(mac->signFinal(shsmMac));

	CPPUNIT_ASSERT(osslMac == shsmMac);

	// Now recreate a different MAC
	b[100] ^= 0x42;
	CPPUNIT_ASSERT(mac->signInit(&key));
	CPPUNIT_ASSERT(mac->signUpdate(b));
	CPPUNIT_ASSERT(mac->signFinal(shsmMac));

	CPPUNIT_ASSERT(osslMac != shsmMac);

	CryptoFactory::i()->recycleMacAlgorithm(mac);

	mac = NULL;
	rng = NULL;
}

void MacTests::testHMACSHA512()
{
	char testData[4096] = "A7A3B0B40CBAAB9B58F6946D9D15870B56E6832DECB708949EBF98DC4EC9C0AF477216C542A98A3646B6ABB72D89E6B3F18201CC75E93B37C2E8EADAD6827FBBFFD220642A92CABB587B27590ED204F8B2C926777C3DC9E5273CBB38582C103905C074002DAA2D8D16DD907BEFB4C51B49C464FF22B93269274291D1254889E3F677FBA6929AF472AECC02DECF822EABB2F05311E0BA8600B58075D4E49837F81C9565E37EAC96B9C2F3C08E3D818DD507D0CC8064C8CC7553D5A17204F4B92B74E5A441CF9142ED059DB1ADA76C799B10178FB3E76E4E3CAC6B1AD8648D522DCA6C93200E706D947E15422E1CC47742CCBF8E2C1E50492AFFA49BE22D5E34B0ACA62744626E2EF7014EAB17D8E538324D9A764501428A53593D23E3785831C1262F220883E2DEEFC66584A3AEC1F35998F842C5298A5E28653CF40F21C6E2383086C266E2902272198D45B38308904F5D2F614A6857E027D133C5455E75B16B9DB124755ADF06B16F04872D74D1A90F36709D8263B1190D625849F017930763AC1EA548DEC56BFD46D09F32D6ABAE96A778B39FBF22B1D6B3CA43F8E7ED32DB2254018FB1999ED0C4C3CDE710899CB20AB5E1CA706DA3E4E8E65253782379B49B7DEFE4E26DD498A935057E91B8FFB8B110336A554A6C61C26C2DDEEE8631926F8A706171B309B99E3B24030C17609340EBB82C4129B172B64CE6249FED00F05DCD624969BC1E4A29ED028C9821A812CDC310A974B959B29E30888887D9A8688E92F85E2A1683C5E993BCC584979F0B3F7DBE34295E2D852B6444EC4152A0C40F3113B402027E323231FC2EDD8078D6661F598588F68FB83DE9C2CA1491107E1F7B676BE16AA6C62B2FF86056905CFCE1FA57065B20A87B15BF1EADAC5FD94F5368451CE8CC2D363BC184A6E87D3D088CBCDE259845E4046D314660B1C1D4AFC7CCC37C7B1B8BBC7A54B63FE61FCD082966425AC3795F08BE7AFD6F9C66F2C3C50DD1EB6B8F8FC3C1A6A298A442EB6FC14AA906A7DA7854B53205E0A8F91C8D6CC3C41F4795FA70E14BA876548022733E61972F0351BC4187FFDE18E776BD68C5E947D2F0B19206D34770BA8ED63874639BBCEAA8D59771A02AEE654D20C69ACD8D16C42BAED6BD1AC51134F49C2A1D685B045527D4A86A7656439A2587F48F5AC43C5094DC680ECE19EBDD53B39F3B84F36D363D8CE464A20DE7ED50C6E32D5C7A9C3868D9823DF0E0A5FD824D239F67C41BC5AD3615AB51EA8B1A2C50B6D4C8F196F1278D9747B8C0B941509850052E63467950EBCE7B56D5E5C481D39E3958C51AE1267C075D8E62EC7F15400A5B4B91B15789015FBCCCC8186DC888CC0BC70326F1800FF56B01BC39DD1C2421BE6E142627B24822790DA36AD9CEA0661FAA03074FAA6F9EBC243DDCB249334B13D31DC19C0F03C6923CBFFEC6FAA29255";
	char testResult[512] = "93E3A4336693965FEEC902F3BDDB064DD63D83EA1E46AA13DA209F8F000C15F366D3F9BE4F8AF189EA96D191D0BDE4CF0FC6C462C214B55ABF78F33BD6DF3DD0";

	// Get a HMAC-SHA512 instance
	CPPUNIT_ASSERT((mac = CryptoFactory::i()->getMacAlgorithm(MacAlgo::HMAC_SHA512)) != NULL);

	// Key
	char pk[] = "a_key_for_HMAC-SHA512_test";
	ByteString k((unsigned char *)pk, sizeof(pk));
	SymmetricKey key;
	CPPUNIT_ASSERT(key.setKeyBits(k));

	ByteString b(testData);
	ByteString osslMac(testResult);

	// Now verify the MAC using our implementation in a single operation
	CPPUNIT_ASSERT(mac->verifyInit(&key));
	CPPUNIT_ASSERT(mac->verifyUpdate(b));
	CPPUNIT_ASSERT(mac->verifyFinal(osslMac));

	// Now verify the MAC in a multiple part operation
	CPPUNIT_ASSERT(mac->verifyInit(&key));
	CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(0, 567)));
	CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(567, 989)));
	CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(567 + 989)));
	CPPUNIT_ASSERT(mac->verifyFinal(osslMac));

	CryptoFactory::i()->recycleMacAlgorithm(mac);

	mac = NULL;
	rng = NULL;
}

void MacTests::testCMACDES2()
{
	// Test vectors from NIST SP 800-38B
	char pk[33] = "4cf15134a2850dd58a3d10ba80570d38";
	char testData[4][2][256] = {
		{
			"",
			"bd2ebf9a3ba00361"
		},
		{
			"6bc1bee22e409f96",
			"4ff2ab813c53ce83"
		},
		{
			"6bc1bee22e409f96e93d7e117393172aae2d8a57",
			"62dd1b471902bd4e"
		},
		{
			"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51",
			"31b1e431dabc4eb8"
		}
	};

	// Get a CMAC-DES instance
	CPPUNIT_ASSERT((mac = CryptoFactory::i()->getMacAlgorithm(MacAlgo::CMAC_DES)) != NULL);

	// Key
	ByteString k(pk);
	SymmetricKey key;
	CPPUNIT_ASSERT(key.setKeyBits(k));
	key.setBitLen(112);

	for (int i = 0; i < 4; i++)
	{
		ByteString b(testData[i][0]);
		ByteString nistMac(testData[i][1]);
		ByteString shsmMac;
		size_t size, part1, part2;

		// Now recreate the MAC using our implementation in a single operation
		CPPUNIT_ASSERT(mac->signInit(&key));
		CPPUNIT_ASSERT(mac->signUpdate(b));
		CPPUNIT_ASSERT(mac->signFinal(shsmMac));
		CPPUNIT_ASSERT(nistMac == shsmMac);

		// Now verify the MAC in a single operation
		CPPUNIT_ASSERT(mac->verifyInit(&key));
		CPPUNIT_ASSERT(mac->verifyUpdate(b));
		CPPUNIT_ASSERT(mac->verifyFinal(nistMac));

		// Now sign the MAC in a multiple part operation
		shsmMac.wipe();
		size = b.size();
		part1 = size / 2;
		part2 = size - part1;
		CPPUNIT_ASSERT(mac->signInit(&key));
		CPPUNIT_ASSERT(mac->signUpdate(b.substr(0, part1)));
		CPPUNIT_ASSERT(mac->signUpdate(b.substr(part2)));
		CPPUNIT_ASSERT(mac->signFinal(shsmMac));
		CPPUNIT_ASSERT(nistMac == shsmMac);

		// Now verify the MAC in a multiple part operation
		CPPUNIT_ASSERT(mac->verifyInit(&key));
		CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(0, part1)));
		CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(part2)));
		CPPUNIT_ASSERT(mac->verifyFinal(nistMac));
	}

	CryptoFactory::i()->recycleMacAlgorithm(mac);

	mac = NULL;
	rng = NULL;
}

void MacTests::testCMACDES3()
{
	// Test vectors from NIST SP 800-38B
	char pk[49] = "8aa83bf8cbda10620bc1bf19fbb6cd58bc313d4a371ca8b5";
	char testData[4][2][256] = {
		{
			"",
			"b7a688e122ffaf95"
		},
		{
			"6bc1bee22e409f96",
			"8e8f293136283797"
		},
		{
			"6bc1bee22e409f96e93d7e117393172aae2d8a57",
			"743ddbe0ce2dc2ed"
		},
		{
			"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51",
			"33e6b1092400eae5"
		}
	};

	// Get a CMAC-DES instance
	CPPUNIT_ASSERT((mac = CryptoFactory::i()->getMacAlgorithm(MacAlgo::CMAC_DES)) != NULL);

	// Key
	ByteString k(pk);
	SymmetricKey key;
	CPPUNIT_ASSERT(key.setKeyBits(k));
	key.setBitLen(168);

	for (int i = 0; i < 4; i++)
	{
		ByteString b(testData[i][0]);
		ByteString nistMac(testData[i][1]);
		ByteString shsmMac;
		size_t size, part1, part2;

		// Now recreate the MAC using our implementation in a single operation
		CPPUNIT_ASSERT(mac->signInit(&key));
		CPPUNIT_ASSERT(mac->signUpdate(b));
		CPPUNIT_ASSERT(mac->signFinal(shsmMac));
		CPPUNIT_ASSERT(nistMac == shsmMac);

		// Now verify the MAC in a single operation
		CPPUNIT_ASSERT(mac->verifyInit(&key));
		CPPUNIT_ASSERT(mac->verifyUpdate(b));
		CPPUNIT_ASSERT(mac->verifyFinal(nistMac));

		// Now sign the MAC in a multiple part operation
		shsmMac.wipe();
		size = b.size();
		part1 = size / 2;
		part2 = size - part1;
		CPPUNIT_ASSERT(mac->signInit(&key));
		CPPUNIT_ASSERT(mac->signUpdate(b.substr(0, part1)));
		CPPUNIT_ASSERT(mac->signUpdate(b.substr(part2)));
		CPPUNIT_ASSERT(mac->signFinal(shsmMac));
		CPPUNIT_ASSERT(nistMac == shsmMac);

		// Now verify the MAC in a multiple part operation
		CPPUNIT_ASSERT(mac->verifyInit(&key));
		CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(0, part1)));
		CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(part2)));
		CPPUNIT_ASSERT(mac->verifyFinal(nistMac));
	}

	CryptoFactory::i()->recycleMacAlgorithm(mac);

	mac = NULL;
	rng = NULL;
}

void MacTests::testCMACAES128()
{
	// Test vectors from NIST SP 800-38B
	char pk[33] = "2b7e151628aed2a6abf7158809cf4f3c";
	char testData[4][2][256] = {
		{
			"",
			"bb1d6929e95937287fa37d129b756746"
		},
		{
			"6bc1bee22e409f96e93d7e117393172a",
			"070a16b46b4d4144f79bdd9dd04a287c"
		},
		{
			"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411",
			"dfa66747de9ae63030ca32611497c827"
		},
		{
			"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
			"51f0bebf7e3b9d92fc49741779363cfe"
		}
	};

	// Get a CMAC-AES instance
	CPPUNIT_ASSERT((mac = CryptoFactory::i()->getMacAlgorithm(MacAlgo::CMAC_AES)) != NULL);

	// Key
	ByteString k(pk);
	SymmetricKey key;
	CPPUNIT_ASSERT(key.setKeyBits(k));
	key.setBitLen(128);

	for (int i = 0; i < 4; i++)
	{
		ByteString b(testData[i][0]);
		ByteString nistMac(testData[i][1]);
		ByteString shsmMac;
		size_t size, part1, part2;

		// Now recreate the MAC using our implementation in a single operation
		CPPUNIT_ASSERT(mac->signInit(&key));
		CPPUNIT_ASSERT(mac->signUpdate(b));
		CPPUNIT_ASSERT(mac->signFinal(shsmMac));
		CPPUNIT_ASSERT(nistMac == shsmMac);

		// Now verify the MAC in a single operation
		CPPUNIT_ASSERT(mac->verifyInit(&key));
		CPPUNIT_ASSERT(mac->verifyUpdate(b));
		CPPUNIT_ASSERT(mac->verifyFinal(nistMac));

		// Now sign the MAC in a multiple part operation
		shsmMac.wipe();
		size = b.size();
		part1 = size / 2;
		part2 = size - part1;
		CPPUNIT_ASSERT(mac->signInit(&key));
		CPPUNIT_ASSERT(mac->signUpdate(b.substr(0, part1)));
		CPPUNIT_ASSERT(mac->signUpdate(b.substr(part2)));
		CPPUNIT_ASSERT(mac->signFinal(shsmMac));
		CPPUNIT_ASSERT(nistMac == shsmMac);

		// Now verify the MAC in a multiple part operation
		CPPUNIT_ASSERT(mac->verifyInit(&key));
		CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(0, part1)));
		CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(part2)));
		CPPUNIT_ASSERT(mac->verifyFinal(nistMac));
	}

	CryptoFactory::i()->recycleMacAlgorithm(mac);

	mac = NULL;
	rng = NULL;
}

void MacTests::testCMACAES192()
{
	// Test vectors from NIST SP 800-38B
	char pk[49] = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b";
	char testData[4][2][256] = {
		{
			"",
			"d17ddf46adaacde531cac483de7a9367"
		},
		{
			"6bc1bee22e409f96e93d7e117393172a",
			"9e99a7bf31e710900662f65e617c5184"
		},
		{
			"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411",
			"8a1de5be2eb31aad089a82e6ee908b0e"
		},
		{
			"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
			"a1d5df0eed790f794d77589659f39a11"
		}
	};

	// Get a CMAC-AES instance
	CPPUNIT_ASSERT((mac = CryptoFactory::i()->getMacAlgorithm(MacAlgo::CMAC_AES)) != NULL);

	// Key
	ByteString k(pk);
	SymmetricKey key;
	CPPUNIT_ASSERT(key.setKeyBits(k));
	key.setBitLen(192);

	for (int i = 0; i < 4; i++)
	{
		ByteString b(testData[i][0]);
		ByteString nistMac(testData[i][1]);
		ByteString shsmMac;
		size_t size, part1, part2;

		// Now recreate the MAC using our implementation in a single operation
		CPPUNIT_ASSERT(mac->signInit(&key));
		CPPUNIT_ASSERT(mac->signUpdate(b));
		CPPUNIT_ASSERT(mac->signFinal(shsmMac));
		CPPUNIT_ASSERT(nistMac == shsmMac);

		// Now verify the MAC in a single operation
		CPPUNIT_ASSERT(mac->verifyInit(&key));
		CPPUNIT_ASSERT(mac->verifyUpdate(b));
		CPPUNIT_ASSERT(mac->verifyFinal(nistMac));

		// Now sign the MAC in a multiple part operation
		shsmMac.wipe();
		size = b.size();
		part1 = size / 2;
		part2 = size - part1;
		CPPUNIT_ASSERT(mac->signInit(&key));
		CPPUNIT_ASSERT(mac->signUpdate(b.substr(0, part1)));
		CPPUNIT_ASSERT(mac->signUpdate(b.substr(part2)));
		CPPUNIT_ASSERT(mac->signFinal(shsmMac));
		CPPUNIT_ASSERT(nistMac == shsmMac);

		// Now verify the MAC in a multiple part operation
		CPPUNIT_ASSERT(mac->verifyInit(&key));
		CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(0, part1)));
		CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(part2)));
		CPPUNIT_ASSERT(mac->verifyFinal(nistMac));
	}

	CryptoFactory::i()->recycleMacAlgorithm(mac);

	mac = NULL;
	rng = NULL;
}

void MacTests::testCMACAES256()
{
	// Test vectors from NIST SP 800-38B
	char pk[65] = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4";
	char testData[4][2][256] = {
		{
			"",
			"028962f61b7bf89efc6b551f4667d983"
		},
		{
			"6bc1bee22e409f96e93d7e117393172a",
			"28a7023f452e8f82bd4bf28d8c37c35c"
		},
		{
			"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411",
			"aaf3d8f1de5640c232f5b169b9c911e6"
		},
		{
			"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
			"e1992190549f6ed5696a2c056c315410"
		}
	};

	// Get a CMAC-AES instance
	CPPUNIT_ASSERT((mac = CryptoFactory::i()->getMacAlgorithm(MacAlgo::CMAC_AES)) != NULL);

	// Key
	ByteString k(pk);
	SymmetricKey key;
	CPPUNIT_ASSERT(key.setKeyBits(k));
	key.setBitLen(256);

	for (int i = 0; i < 4; i++)
	{
		ByteString b(testData[i][0]);
		ByteString nistMac(testData[i][1]);
		ByteString shsmMac;
		size_t size, part1, part2;

		// Now recreate the MAC using our implementation in a single operation
		CPPUNIT_ASSERT(mac->signInit(&key));
		CPPUNIT_ASSERT(mac->signUpdate(b));
		CPPUNIT_ASSERT(mac->signFinal(shsmMac));
		CPPUNIT_ASSERT(nistMac == shsmMac);

		// Now verify the MAC in a single operation
		CPPUNIT_ASSERT(mac->verifyInit(&key));
		CPPUNIT_ASSERT(mac->verifyUpdate(b));
		CPPUNIT_ASSERT(mac->verifyFinal(nistMac));

		// Now sign the MAC in a multiple part operation
		shsmMac.wipe();
		size = b.size();
		part1 = size / 2;
		part2 = size - part1;
		CPPUNIT_ASSERT(mac->signInit(&key));
		CPPUNIT_ASSERT(mac->signUpdate(b.substr(0, part1)));
		CPPUNIT_ASSERT(mac->signUpdate(b.substr(part2)));
		CPPUNIT_ASSERT(mac->signFinal(shsmMac));
		CPPUNIT_ASSERT(nistMac == shsmMac);

		// Now verify the MAC in a multiple part operation
		CPPUNIT_ASSERT(mac->verifyInit(&key));
		CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(0, part1)));
		CPPUNIT_ASSERT(mac->verifyUpdate(b.substr(part2)));
		CPPUNIT_ASSERT(mac->verifyFinal(nistMac));
	}

	CryptoFactory::i()->recycleMacAlgorithm(mac);

	mac = NULL;
	rng = NULL;
}
