// Piglet1-Test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <stdio.h>
#include <iostream>
#include <iomanip>  
#include  "alg.h"
#include <time.h>

#pragma warning(disable : 4996)

#define TESTLENGTH  1024

typedef unsigned long long ULONGLONG;
typedef unsigned char UCHAR;
typedef unsigned int UINT;

/**********************************************************
* 函数：OutputBuff
* 功能：格式化输出缓冲区
*
***********************************************************/
void OutputBuff(long len, const UCHAR* buff, int leading=0,int bytes_per_line=16)
{
	std::cout << "\n";
	int count = 0;
	
	//防止错误参数
	if (bytes_per_line < 1) bytes_per_line = 16;

	std::cout << std::setbase(16);
	for (long i = 0; i < len; i++) {
		if (count == 0)
		{
			for (int j = 0; j < leading; j++)
			{
				std::cout << " ";
			}
		}
		else if (count == bytes_per_line)
		{
			std::cout << "\n";
			count = 0;
			for (int j = 0; j < leading; j++)
			{
				std::cout << " ";
			}
		}
		count++;
		std::cout <<  std::setfill('0') << std::setw(2)<<(UINT) buff[i] <<" ";
	}
	std::cout << std::setbase(10);
	std::cout << "\n\n";
}

//Write buffer to file
void write_buff(FILE *fp, unsigned char *buff, long len)
{
	unsigned char len_buff[4];
	len_buff[0] = (unsigned char)(len >> 24);
	len_buff[1] = (unsigned char)(len >> 16);
	len_buff[2] = (unsigned char)(len >> 8);
	len_buff[3] = (unsigned char)(len);
	fwrite(len_buff, 1, 4, fp);
	fwrite(buff, 1, len, fp);
}


int main(int argc, char *argv[])
{
	UCHAR Random_Seed[TESTLENGTH];
	UCHAR Random_Bytes[TESTLENGTH];
	long SeedLen=0;

	//把随机种子设置为 0,1,2,3,4,...,255,0,1,2,...
	for (int i = 0; i < TESTLENGTH; i++)
	{
		Random_Seed[i] = (UCHAR)i;
	}
    std::cout << "===========================================================================\n"; 
	std::cout << "                         Piglet1-Q128算法测试\n";
	std::cout << "===========================================================================\n\n";
	std::cout << "1、随机数部分\n\n";
	SeedLen = (long) rand_get_sd_byts();
	std::cout << "rand_get_sd_byts函数测试\n";
	std::cout << "	返回值: "  << SeedLen << "\n";
	std::cout << "rand_init函数测试:\n";
	std::cout << "	长度参数过长返回: " << rand_init(Random_Seed, SeedLen + 1) << "\n";
	std::cout << "	长度参数过短返回: " << rand_init(Random_Seed, SeedLen - 1) << "\n";
	std::cout << "	长度参数正确返回: " << rand_init(Random_Seed, SeedLen ) << "\n";

	OutputBuff(SeedLen , Random_Seed, 8);
	ULONGLONG ll= SeedLen;
	UCHAR Get_Seed[TESTLENGTH];
	memset(Get_Seed, 0, TESTLENGTH);

	std::cout << "rand_init_get函数测试:\n";
	rand_init_get(Get_Seed, &ll);
	OutputBuff(SeedLen, Get_Seed, 8);

	std::cout << "rand_byts函数测试:\n";
	rand_byts(TESTLENGTH, Random_Bytes);
	OutputBuff(TESTLENGTH, Random_Bytes,8);

	std::cout << "2、算法基本参数\n\n";
	std::cout << "kem_get_pk_byts-获取密钥封装算法声称公钥（字节）长度函数测试:\n";
	ULONGLONG pk_len = kem_get_pk_byts();
	std::cout << "	返回值: " <<pk_len<< "\n\n";

	std::cout << "kem_get_sk_byts-获取密钥封装算法声称私钥（字节）长度函数测试:\n";
	ULONGLONG sk_len = kem_get_sk_byts();
	std::cout << "	返回值: " << sk_len << "\n\n";

	std::cout << "kem_get_ss_byts-获取密钥封装算法声称共享秘密（字节）长度函数测试:\n";
	ULONGLONG ss_len = kem_get_ss_byts();
	std::cout << "	返回值: " << ss_len << "\n\n";

	std::cout << "kem_get_ct_byts-获取密钥封装算法声称密文（字节）长度函数测试:\n";
	ULONGLONG ct_len = kem_get_ct_byts();
	std::cout << "	返回值: " << ct_len << "\n\n";


	std::cout << "==算法基本参数(长度单位为字节数）：\n";
	
	std::cout << "	公钥种子长度: " << SeedLen << "\n";
	std::cout << "	    公钥长度: " << pk_len << "\n";
	std::cout << "	    私钥长度: " << sk_len << "\n";
	std::cout << "	共享密钥长度: " << ss_len << "\n";
	std::cout << "	    密文长度: " << ct_len << "\n\n";

	UCHAR *pk = new UCHAR[pk_len];
	UCHAR *sk = new UCHAR[sk_len];
	UCHAR *ss = new UCHAR[ss_len];
	UCHAR *ss_dec = new UCHAR[ss_len];
	UCHAR *ct = new UCHAR[ct_len];

	std::cout << "3、密钥封装与解封装测试\n\n";

	ULONGLONG pk_len_get=0;
	ULONGLONG sk_len_get = 0;
	ULONGLONG ss_len_get = 0;
	ULONGLONG ct_len_get = 0;

	std::cout << "kem_keygen-密钥封装算法密钥生成函数测试:\n";
	std::cout << "	  返回值: " << kem_keygen(pk,&pk_len_get,sk, &sk_len_get) << "\n";
	std::cout << "	公钥长度: " << pk_len_get << (pk_len== pk_len_get)?"(正确)\n":"(错误)\n";
	std::cout << "	公钥内容: \n";
	OutputBuff((long)pk_len, pk, 8,32);

	std::cout << "	私钥长度: " << sk_len_get << "\n";
	std::cout << "	私钥内容: \n";
	OutputBuff((long)sk_len, sk, 8,32);
	
	std::cout << "\nkem_enc-密钥封装函数测试:\n";
	std::cout << "          返回值: " << kem_enc(pk, pk_len, ss, &ss_len_get, ct, &ct_len_get) << "\n";
	std::cout << "    共享密钥长度: " << ss_len_get << ((ss_len == ss_len_get) ? "(正确)\n" : "(错误)\n");
	std::cout << "    共享密钥内容: \n";
	OutputBuff((long)ss_len, ss, 8, 32);

	std::cout << "        密文长度: " << ct_len_get << ((ct_len == ct_len_get) ? "(正确)\n" : "(错误)\n");
	std::cout << "        密文内容: \n";
	OutputBuff((long)ct_len, ct, 8, 32);

	std::cout << "\nkem_dec-密钥解封装函数测试:\n";
	std::cout << "          返回值: " << kem_dec(sk, sk_len, ct, ct_len, ss_dec, &ss_len_get)<< "\n";
	std::cout << "    共享密钥长度: " << ss_len_get << ((ss_len == ss_len_get) ? "(正确)\n" : "(错误)\n");
	std::cout << "    共享密钥内容: \n";
	OutputBuff((long)ss_len, ss_dec, 8, 32);

	std::cout << "3、密钥封装计时测试\n\n";
	unsigned int experiment = 1;
	clock_t start[3], end[3];
	unsigned long total_time[3] = { 0,0,0 };
	FILE *fpVector;

	//创建测试向量文件
	char file_path[1024] = "";
	errno_t err;
	strcpy(file_path, argv[0]);
	//获取exe执行文件名字(去掉后缀".exe")
	*strrchr(file_path, '\\') = '\0';   // 从最左边开始最后一次出现"."
	strcpy(file_path + strlen(file_path), "\\KEM_VEC_INFO.dat");
	err = fopen_s(&fpVector,file_path, "wb");
	if (err != 0) fpVector = NULL;
	if (!fpVector) {
		std::cout << "\nError-测试向量文件创建失败,测试过程中无法生成测试向量!\n";
	}

	setbuf(stdin, NULL);
	std::cout << "\n\n请输入封装与解封装测试次数: ";
	scanf_s("%d", &experiment);
	if(experiment < 1) experiment = 1;
	std::cout << "\n正在进行封装与解封测试....("<< experiment <<"次)\n";
	for (unsigned int i = 0; i < experiment; i++) {
		// 密钥生成，保存在 pk and sk.
		start[0] = clock();
		kem_keygen(pk, &pk_len_get, sk, &sk_len_get);
		end[0] = clock();
		total_time[0] = total_time[0] + end[0] - start[0];
		// 保存测试向量,只保存第一次的，内容依次是：公钥种子、私钥、公钥、密文、共享密钥
		//每个部分的格式是 Big endian的长度、信息内容
		if (i == 0 && fpVector) {
			write_buff(fpVector, pk + pk_len_get - SeedLen, SeedLen);
			write_buff(fpVector, sk, (long)sk_len_get);
			write_buff(fpVector, pk, (long)pk_len_get);
		}

		//封装
		start[1] = clock();
		kem_enc(pk, pk_len, ss, &ss_len_get, ct, &ct_len_get);
		end[1] = clock();
		total_time[1] = total_time[1] + end[1] - start[1];

		// 保存测试向量,只保存第一次的，内容依次是：公钥种子、私钥、公钥、密文、共享密钥
		//每个部分的格式是 Big endian的长度、信息内容
		if (i == 0 && fpVector) {
			write_buff(fpVector, ct, (long)ct_len_get);
			write_buff(fpVector, ss, (long)ss_len_get);
		}
		// 解封装
		start[2] = clock();
		kem_dec(sk, sk_len, ct, ct_len, ss_dec, &ss_len_get);
		end[2] = clock();
		total_time[2] = total_time[2] + end[2] - start[2];
		std::cout<<". ";
		//比较结果，算法确保了成功，结果不做比较
		long c = 0;
		for (c = 0; c < ss_len; c++)
		{
			if (ss[c] != ss_dec[c])
			{
				break;
			}
		}
		if (c < ss_len)
		{
			std::cout << "\n发生错误，算法测试终止!\n";
			break;
		}
	}

	printf("\n封装与解封装次数: %d\n\n", experiment);
	printf("    封装平均时间: %ums\n", total_time[1] / experiment);
	printf("  解封装平均时间:  %ums\n", total_time[2] / experiment);//CLOCKS_PER_SEC

	//关闭测试向量文件
	if (fpVector) {
		fclose(fpVector);
	}

	setbuf(stdin, NULL);

	if (pk) delete[] pk;
	if (sk) delete[] sk;
	if (ss) delete[] ss;
	if (ss_dec) delete[] ss_dec;
	if (ct) delete[] ct;

	getchar();
}
