/* This program is released under the Common Public License V1.0
 *
 * You should have received a copy of Common Public License V1.0 along with
 * with this program.
 */

/* Copyright IBM Corp. 2016 */
/* (C) COPYRIGHT International Business Machines Corp. 2016          */
#include <fcntl.h>
#include <sys/errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "ica_api.h"
#include "testcase.h"

#define NUM_FIPS_TESTS 3

/**
 * The SHAKE algo has a variable output length, so we cannot use a static
 * value as for the other SHA algos. However, a known answer test requires
 * having pre-calculated results, so let's use a fixed big output length of
 * 2048 bits (256 bytes) for this test. This output length exceeds the
 * size of the KIMD/KLMD parmblock, so the output is directly written by
 * KIMD/KLMD to the provided output buffer.
 */
#define SHAKE256_256_HASH_LENGTH  256

unsigned char FIPS_TEST_DATA[NUM_FIPS_TESTS][64] = {
  // Test 0: "abc"
  { 0x61,0x62,0x63 },
  // Test 1: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
  {
0x61,0x62,0x63,0x64,0x62,0x63,0x64,0x65,0x63,0x64,0x65,0x66,0x64,0x65,0x66,0x67,
0x65,0x66,0x67,0x68,0x66,0x67,0x68,0x69,0x67,0x68,0x69,0x6a,0x68,0x69,0x6a,0x6b,
0x69,0x6a,0x6b,0x6c,0x6a,0x6b,0x6c,0x6d,0x6b,0x6c,0x6d,0x6e,0x6c,0x6d,0x6e,0x6f,
0x6d,0x6e,0x6f,0x70,0x6e,0x6f,0x70,0x71,
  },
  // Test 2: 1,000,000 'a' -- don't actually use this... see the special case
  // in the loop below.
  {
0x61,
  },
};

unsigned int FIPS_TEST_DATA_SIZE[NUM_FIPS_TESTS] = {
  // Test 0: "abc"
  3,
  // Test 1: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
  56,
  // Test 2: 1,000,000 'a'
  1000000,
};

unsigned char FIPS_TEST_RESULT[NUM_FIPS_TESTS][SHAKE256_256_HASH_LENGTH] =
{
  // SHAKE_256(2048) Hash for test 0: "abc"
  {
0x48,0x33,0x66,0x60,0x13,0x60,0xa8,0x77,0x1c,0x68,0x63,0x08,0x0c,0xc4,0x11,0x4d,
0x8d,0xb4,0x45,0x30,0xf8,0xf1,0xe1,0xee,0x4f,0x94,0xea,0x37,0xe7,0x8b,0x57,0x39,
0xd5,0xa1,0x5b,0xef,0x18,0x6a,0x53,0x86,0xc7,0x57,0x44,0xc0,0x52,0x7e,0x1f,0xaa,
0x9f,0x87,0x26,0xe4,0x62,0xa1,0x2a,0x4f,0xeb,0x06,0xbd,0x88,0x01,0xe7,0x51,0xe4,
0x13,0x85,0x14,0x12,0x04,0xf3,0x29,0x97,0x9f,0xd3,0x04,0x7a,0x13,0xc5,0x65,0x77,
0x24,0xad,0xa6,0x4d,0x24,0x70,0x15,0x7b,0x3c,0xdc,0x28,0x86,0x20,0x94,0x4d,0x78,
0xdb,0xcd,0xdb,0xd9,0x12,0x99,0x3f,0x09,0x13,0xf1,0x64,0xfb,0x2c,0xe9,0x51,0x31,
0xa2,0xd0,0x9a,0x3e,0x6d,0x51,0xcb,0xfc,0x62,0x27,0x20,0xd7,0xa7,0x5c,0x63,0x34,
0xe8,0xa2,0xd7,0xec,0x71,0xa7,0xcc,0x29,0xcf,0x0e,0xa6,0x10,0xee,0xff,0x1a,0x58,
0x82,0x90,0xa5,0x30,0x00,0xfa,0xa7,0x99,0x32,0xbe,0xce,0xc0,0xbd,0x3c,0xd0,0xb3,
0x3a,0x7e,0x5d,0x39,0x7f,0xed,0x1a,0xda,0x94,0x42,0xb9,0x99,0x03,0xf4,0xdc,0xfd,
0x85,0x59,0xed,0x39,0x50,0xfa,0xf4,0x0f,0xe6,0xf3,0xb5,0xd7,0x10,0xed,0x3b,0x67,
0x75,0x13,0x77,0x1a,0xf6,0xbf,0xe1,0x19,0x34,0x81,0x7e,0x87,0x62,0xd9,0x89,0x6b,
0xa5,0x79,0xd8,0x8d,0x84,0xba,0x7a,0xa3,0xcd,0xc7,0x05,0x5f,0x67,0x96,0xf1,0x95,
0xbd,0x9a,0xe7,0x88,0xf2,0xf5,0xbb,0x96,0x10,0x0d,0x6b,0xba,0xff,0x7f,0xbc,0x6e,
0xea,0x24,0xd4,0x44,0x9a,0x24,0x77,0xd1,0x72,0xa5,0x50,0x7d,0xcc,0x93,0x14,0x12,
  },
  // SHAKE_256(2048) Hash for test 1: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
  {
0x4d,0x8c,0x2d,0xd2,0x43,0x5a,0x01,0x28,0xee,0xfb,0xb8,0xc3,0x6f,0x6f,0x87,0x13,
0x3a,0x79,0x11,0xe1,0x8d,0x97,0x9e,0xe1,0xae,0x6b,0xe5,0xd4,0xfd,0x2e,0x33,0x29,
0x40,0xd8,0x68,0x8a,0x4e,0x6a,0x59,0xaa,0x80,0x60,0xf1,0xf9,0xbc,0x99,0x6c,0x05,
0xac,0xa3,0xc6,0x96,0xa8,0xb6,0x62,0x79,0xdc,0x67,0x2c,0x74,0x0b,0xb2,0x24,0xec,
0x37,0xa9,0x2b,0x65,0xdb,0x05,0x39,0xc0,0x20,0x34,0x55,0xf5,0x1d,0x97,0xcc,0xe4,
0xcf,0xc4,0x91,0x27,0xd7,0x26,0x0a,0xfc,0x67,0x3a,0xf2,0x08,0xba,0xf1,0x9b,0xe2,
0x12,0x33,0xf3,0xde,0xbe,0x78,0xd0,0x67,0x60,0xcf,0xa5,0x51,0xee,0x1e,0x07,0x91,
0x41,0xd4,0x9d,0xd3,0xef,0x7e,0x18,0x2b,0x15,0x24,0xdf,0x82,0xea,0x1c,0xef,0xe1,
0xc6,0xc3,0x96,0x61,0x75,0xf0,0x22,0x8d,0x35,0x88,0x7c,0xd9,0xf0,0x9b,0x05,0x45,
0x7f,0x6d,0x95,0x2f,0x9b,0x3b,0x32,0x46,0x4e,0x0b,0x3c,0x54,0xdc,0xc1,0x3e,0xfd,
0xb4,0xc5,0x4e,0x29,0xcd,0xb4,0x08,0x8f,0xaf,0x48,0x2c,0xdd,0xd0,0xa5,0xe6,0xb8,
0x22,0xf5,0xa8,0x0d,0x0c,0xc7,0x8d,0x4c,0xc9,0x01,0x31,0x90,0x6f,0xd5,0x15,0x9e,
0xb5,0x14,0x2e,0x15,0x50,0x24,0xb6,0x24,0x02,0xeb,0x00,0x17,0xf9,0x86,0xc9,0x63,
0x8b,0xa6,0x19,0x70,0xe9,0x08,0x6d,0xd9,0x48,0x84,0x27,0x5f,0x48,0x4d,0x3c,0x3b,
0x84,0x22,0x11,0x0e,0xd6,0x4f,0x07,0x9a,0xb2,0xc9,0xac,0xff,0x78,0xe8,0xbd,0x49,
0x51,0x92,0x3f,0x75,0xf0,0xa2,0xf1,0x8c,0x43,0x80,0x6c,0xe5,0xde,0x92,0x38,0x6f,
  },
  // SHAKE_256(2048) Hash for test 2: 1,000,000 'a'
  {
0x35,0x78,0xa7,0xa4,0xca,0x91,0x37,0x56,0x9c,0xdf,0x76,0xed,0x61,0x7d,0x31,0xbb,
0x99,0x4f,0xca,0x9c,0x1b,0xbf,0x8b,0x18,0x40,0x13,0xde,0x82,0x34,0xdf,0xd1,0x3a,
0x3f,0xd1,0x24,0xd4,0xdf,0x76,0xc0,0xa5,0x39,0xee,0x7d,0xd2,0xf6,0xe1,0xec,0x34,
0x61,0x24,0xc8,0x15,0xd9,0x41,0x0e,0x14,0x5e,0xb5,0x61,0xbc,0xd9,0x7b,0x18,0xab,
0x6c,0xe8,0xd5,0x55,0x3e,0x0e,0xab,0x3d,0x1f,0x7d,0xfb,0x8f,0x9d,0xee,0xfe,0x16,
0x84,0x7e,0x21,0x92,0xf6,0xf6,0x1f,0xb8,0x2f,0xb9,0x0d,0xde,0x60,0xb1,0x90,0x63,
0xc5,0x6a,0x4c,0x55,0xcd,0xd7,0xb6,0x72,0xb7,0x5b,0xf5,0x15,0xad,0xbf,0xe2,0x04,
0x90,0x3c,0x8c,0x00,0x36,0xde,0x54,0xa2,0x99,0x9a,0x92,0x0d,0xe9,0x0f,0x66,0xd7,
0xff,0x6e,0xc8,0xe4,0xc9,0x3d,0x24,0xae,0x34,0x6f,0xdc,0xb3,0xa5,0xa5,0xbd,0x57,
0x39,0xec,0x15,0xa6,0xed,0xdb,0x5c,0xe5,0xb0,0x2d,0xa5,0x30,0x39,0xfa,0xc6,0x3e,
0x19,0x55,0x5f,0xaa,0x2e,0xdd,0xc6,0x93,0xb1,0xf0,0xc2,0xa6,0xfc,0xbe,0x7c,0x0a,
0x0a,0x09,0x1d,0x0e,0xe7,0x00,0xd7,0x32,0x2e,0x4b,0x0f,0xf0,0x95,0x90,0xde,0x16,
0x64,0x22,0xf9,0xea,0xd5,0xda,0x4c,0x99,0x3d,0x60,0x5f,0xe4,0xd9,0xc6,0x34,0x84,
0x3a,0xa1,0x78,0xb1,0x76,0x72,0xc6,0x56,0x8c,0x8a,0x2e,0x62,0xab,0xeb,0xea,0x2c,
0x21,0xc3,0x02,0xbd,0x36,0x6a,0xd6,0x98,0x95,0x9e,0x1f,0x6e,0x43,0x4a,0xf1,0x55,
0x56,0x8b,0x27,0x34,0xd8,0x37,0x9f,0xcd,0x3f,0xfe,0x64,0x89,0xba,0xff,0xa6,0xd7,
  },
};

int new_api_shake_256_test(void)
{
	shake_256_context_t shake_256_context;
	int rc = 0;
	int i = 0;
	unsigned char input_data[1000000];
	unsigned int  output_hash_length = SHAKE256_256_HASH_LENGTH;
	unsigned char output_hash[SHAKE256_256_HASH_LENGTH];
	unsigned int errors = 0;

	for (i = 0; i < NUM_FIPS_TESTS; i++) {
		// Test 2 is a special one, because we want to keep the size of the
		// executable down, so we build it special, instead of using a static
		if (i != 2)
			memcpy(input_data, FIPS_TEST_DATA[i], FIPS_TEST_DATA_SIZE[i]);
		else
			memset(input_data, 'a', FIPS_TEST_DATA_SIZE[i]);

		VV_(printf("\nOriginal data for test %d:\n", i));
		if (i != 2)
			dump_array(input_data, FIPS_TEST_DATA_SIZE[i]);
		else
			VV_(printf("Data suppressed (1.000.000 'a'), too much output.\n"));

		rc = ica_shake_256(SHA_MSG_PART_ONLY, FIPS_TEST_DATA_SIZE[i], input_data,
				&shake_256_context, output_hash, SHAKE256_256_HASH_LENGTH);

		if (rc != 0) {
			V_(printf("icaShake_256 failed with errno %d (0x%x).\n", rc, rc));
			return TEST_FAIL;
		}

		VV_(printf("\nOutput hash for test %d:\n", i));
		dump_array(output_hash, output_hash_length);
		if (memcmp(output_hash, FIPS_TEST_RESULT[i], SHAKE256_256_HASH_LENGTH) != 0) {
			VV_(printf("This does NOT match the known result.\n"));
			errors++;
		} else {
			VV_(printf("Yep, it's what it should be.\n"));
		}
	}

	// This test is the same as test 2, except that we use the SHA256_CONTEXT and
	// break it into calls of 1088 bytes each (which is 8 * 136, where 136 is the
	// SHAKE-256 block length.
	V_(printf("\nOriginal data for test 2(chunks = 1088) is calls of 1088 'a's at a time\n"));
	i = FIPS_TEST_DATA_SIZE[2];
	while (i > 0) {
		unsigned int sha_message_part;
		memset(input_data, 'a', 1088);

		if (i == (int)FIPS_TEST_DATA_SIZE[2])
			sha_message_part = SHA_MSG_PART_FIRST;
		else if (i <= 1088)
			sha_message_part = SHA_MSG_PART_FINAL;
		else
			sha_message_part = SHA_MSG_PART_MIDDLE;

		rc = ica_shake_256(sha_message_part, (i < 1088) ? i : 1088,
				input_data, &shake_256_context, output_hash,
				SHAKE256_256_HASH_LENGTH);

		if (rc != 0) {
			V_(printf("ica_shake_256 failed with errno %d (0x%x) on iteration %d.\n",
					rc, rc, i));
			return TEST_FAIL;
		}
		i -= 1088;
	}

	VV_(printf("\nOutput hash for test 2 (chunks = 1088):\n"));
	dump_array(output_hash, output_hash_length);
	if (memcmp(output_hash, FIPS_TEST_RESULT[2], SHAKE256_256_HASH_LENGTH) != 0) {
		VV_(printf("This does NOT match the known result.\n"));
		errors++;
	} else {
		VV_(printf("Yep, it's what it should be.\n"));
	}

	// This test is the same as test 2, except that we use the
	// SHA256_CONTEXT and break it into calls of 136 bytes each.
	V_(printf("\nOriginal data for test 2 (chunks = 136) is calls of 136 'a's at a time\n"));
	i = FIPS_TEST_DATA_SIZE[2];
	while (i > 0) {
		unsigned int sha_message_part;
		memset(input_data, 'a', 136);

		if (i == (int)FIPS_TEST_DATA_SIZE[2])
			sha_message_part = SHA_MSG_PART_FIRST;
		else if (i <= 136)
			sha_message_part = SHA_MSG_PART_FINAL;
		else
			sha_message_part = SHA_MSG_PART_MIDDLE;

		rc = ica_shake_256(sha_message_part, (i < 136) ? i : 136,
				input_data, &shake_256_context, output_hash,
				SHAKE256_256_HASH_LENGTH);

		if (rc != 0) {
			V_(printf("ica_shake_256 failed with errno %d (0x%x) on iteration %d.\n",
					rc, rc, i));
			return TEST_FAIL;
		}
		i -= 136;
	}

	VV_(printf("\nOutput hash for test 2 (chunks = 136):\n"));
	dump_array(output_hash, output_hash_length);
	if (memcmp(output_hash, FIPS_TEST_RESULT[2], SHAKE256_256_HASH_LENGTH) != 0) {
		VV_(printf("This does NOT match the known result.\n"));
		errors++;
	} else {
		VV_(printf("Yep, it's what it should be.\n"));
	}

	if (errors) {
		printf("%d out of %d SHAKE_256 tests did not return the expected result.\n",
				errors, (NUM_FIPS_TESTS+2));
		return TEST_FAIL;
	} else {
		printf("All SHAKE_256 tests passed.\n");
		return TEST_SUCC;
	}
}

int main(int argc, char **argv)
{
#ifdef NO_CPACF
	UNUSED(argc);
	UNUSED(argv);
	printf("Skipping SHAKE-256 test, because CPACF support disabled via config option.\n");
	return TEST_SKIP;
#else
	int rc = 0;

	set_verbosity(argc, argv);

	if (!sha3_available()) {
		printf("Skipping SHAKE-256 test, because SHA3/SHAKE not available on this machine.\n");
		return TEST_SKIP;
	}

	rc = new_api_shake_256_test();
	if (rc) {
		printf("new_api_shake_256_test: returned rc = %i\n", rc);
		return TEST_FAIL;
	}

	return TEST_SUCC;
#endif /* NO_CPACF */
}
