#include "layer1.h"


//ROM functions

#define UDSFILE "./out/RANDFILE"
#define UDSsize 8						//bytes
#define FW_file "layer2.c"				//use RANDFILE if testing FW_M for consistency
#define FW_size 1000		
////need to find a way to determine FW file size using BIO tools


int createUDS()
{
	int ret = RAND_write_file(UDSFILE);
	if (ret == -1)
		perror("rand write to file failed\n");
	printf("\n %d bytes written to file\n", ret);
	//readUDS();
	return 0;
}

int readUDS(uint8_t* UDS_M)
{
	//1. Create RANDFILE if it does not exist. skip if it exists
	//2. Load RANDFILE
	//3. Read 64 bits to UDSbuf and compute hash into input arg.
	//4. free UDSbuf

	BIO *fp, *out;
	int i;

	//uint8_t UDSbuf[UDSsize] = {0};
	//uint8_t UDS_M[SHA256_DGST_SIZE] = {0};

	uint8_t* UDSbuf = calloc(1,sizeof(uint8_t)*UDSsize);
	//uint8_t* UDS_M = calloc(1,sizeof(uint8_t)*SHA256_DGST_SIZE);


	out = BIO_new_fp(stdout, BIO_NOCLOSE);

	int ret = RAND_load_file(UDSFILE, 8);
	while (ret < 0)
	{
		perror("Could not load seed file\n");
		BIO_printf(out,"Creating new seed file\n");
		createUDS();
		ret = RAND_load_file(UDSFILE, 8);
	}


	fp = BIO_new_file(UDSFILE, "r");
	if(!fp)
		perror("Opening seed file to read failed\n");
	
	if (BIO_read(fp,UDSbuf,UDSsize) < 0)		//Suspecting a ENDian issue in reading. data is half byte reversed
		perror("BIO read failed\n");

	//Compute hash of UDS
	if(SHA256(UDSbuf, UDSsize, UDS_M) == NULL)
		perror("UDS measurement failed\n");

	//Print block. delete later
	for(i = 0; i < UDSsize; i++)
	 	BIO_printf(out,"%x,",UDSbuf[i]);
	BIO_printf(out, "\n");

	BIO_printf(out,"UDS digest\t: ");
	for(i = 0; i < SHA256_DGST_SIZE; i++)
	 	BIO_printf(out,"%x",UDS_M[i]);
	BIO_printf(out, "\n");

	free(UDSbuf);
	BIO_free(fp);
	BIO_free(out);
	return 1;

}


int readFWID(uint8_t* FW_M)
{

// 	//1. Read layer1.c into memory
// 	//2. Calcualte hash into arg

	BIO *fp, *out;
	int i;

// 	//uint8_t * source;
// 	//FW_size shoudl not be static.
// 	//Use indefinite array or determine FW_size
	uint8_t* source = calloc(1,sizeof(uint8_t)*FW_size);
	out = BIO_new_fp(stdout, BIO_NOCLOSE);

	fp = BIO_new_file(FW_file, "r");
	if(!fp)
		perror("Opening firmware bin to read failed\n");
	
	if (BIO_read(fp,source,FW_size) < 0)		//Suspecting a ENDian issue in reading. data is half byte reversed
		perror("BIO read failed\n");

	//Compute hash of FW
	if(SHA256(source, FW_size, FW_M) == NULL)
		perror("FW measurement failed\n");

	//Print block. delete later
	 // for(i = 0; i < 100; i++)
	 //  	BIO_printf(out,"%x,",source[i]);
	 // BIO_printf(out, "\n");

	BIO_printf(out,"FW digest\t: ");
	for(i = 0; i < SHA256_DGST_SIZE; i++)
	 	BIO_printf(out,"%x",FW_M[i]);
	BIO_printf(out, "\n");

	free(source);
	BIO_free(fp);
	BIO_free(out);
	return 1;

}



int calcCDID(uint8_t * UDS_M, uint8_t * FW_M, uint8_t * CDID)
{
	//0. internally call readUDS and readFWID? abstraction of UDS against layer2

	//1. create sha256 context
	//2. add UDS hash
	//3. add FW hash
	//4. calc composite hash into CDID arg

	BIO * out = BIO_new_fp(stdout, BIO_NOCLOSE);

	SHA256_CTX *ctx;
	if(!SHA256_Init(ctx))
		perror("SHA init failed\n");

	if(!SHA256_Update(ctx, UDS_M, UDSsize))
		perror("SHA update failed\n");
	if(!SHA256_Update(ctx, FW_M, FW_size))
		perror("SHA update2 failed\n");

 	if(!SHA256_Final(CDID, ctx))
 		perror("SHA close failed\n");

 	//print block

 // 	BIO_printf(out,"UDID_M : ");
	// for(int i = 0; i < SHA256_DGST_SIZE; i++)
	//  	BIO_printf(out,"%x",UDS_M[i]);
	// BIO_printf(out, "\n");

	// BIO_printf(out,"FWID_M : ");
	// for(int i = 0; i < SHA256_DGST_SIZE; i++)
	//  	BIO_printf(out,"%x",FW_M[i]);
	// BIO_printf(out, "\n");

	BIO_printf(out,"CDI\t\t: ");
	for(int i = 0; i < SHA256_DGST_SIZE; i++)
	 	BIO_printf(out,"%x",CDID[i]);
	BIO_printf(out, "\n");
	BIO_free(out);

	return 1;
}

int _calcCDID(uint8_t * _CDID)
{

	/* 
	***BUG*** : 
	CDI hash value returned is not correct or consistent.
	hash generated by readUDS() and readFWID() are correct and consistent but 
	caclCDID() is broken. 

	Do not use this wrapper function unless fixed
	*/

	BIO * out = BIO_new_fp(stdout, BIO_NOCLOSE);
	BIO_printf(out, "\n");BIO_printf(out, "\n");BIO_printf(out, "\n");

	//step 1 : Derive Device ID
	uint8_t* UDS_ID = calloc(1,sizeof(uint8_t)*SHA256_DGST_SIZE);
	readUDS(UDS_ID);

	//step 2 : Derive Firmware ID
	uint8_t* FW_ID = calloc(1,sizeof(uint8_t)*SHA256_DGST_SIZE);
	readFWID(FW_ID);

	////step3 : call calcCDID
	calcCDID(UDS_ID,FW_ID,_CDID);
	BIO_printf(out,"_CDI : ");
	for(int i = 0; i < SHA256_DGST_SIZE; i++)
	 	BIO_printf(out,"%x",_CDID[i]);
	BIO_printf(out, " : wrap \n\n\n");


	//attempt 2 : use SHA256 here
	BIO_printf(out,"_UDID : ");
	for(int i = 0; i < SHA256_DGST_SIZE; i++)
	 	BIO_printf(out,"%x",UDS_ID[i]);
	BIO_printf(out, "\n");

	BIO_printf(out,"_FWID : ");
	for(int i = 0; i < SHA256_DGST_SIZE; i++)
	 	BIO_printf(out,"%x",FW_ID[i]);
	BIO_printf(out, "\n");

	memset(_CDID, 0, SHA256_DGST_SIZE);

	BIO_printf(out,"_CDI 0 : ");
	for(int i = 0; i < SHA256_DGST_SIZE; i++)
	 	BIO_printf(out,"%x",_CDID[i]);
	BIO_printf(out, "\n");

	SHA256_CTX *ctx;
	if(!SHA256_Init(ctx))
		perror("SHA init failed\n");

	if(!SHA256_Update(ctx, UDS_ID, UDSsize))
		perror("SHA update failed\n");
	if(!SHA256_Update(ctx, FW_ID, FW_size))
		perror("SHA update2 failed\n");

 	if(!SHA256_Final(_CDID, ctx))
  		perror("SHA close failed\n");


	
	BIO_printf(out,"_UDID : ");
	for(int i = 0; i < SHA256_DGST_SIZE; i++)
	 	BIO_printf(out,"%x",UDS_ID[i]);
	BIO_printf(out, "\n");

	BIO_printf(out,"_FWID : ");
	for(int i = 0; i < SHA256_DGST_SIZE; i++)
	 	BIO_printf(out,"%x",FW_ID[i]);
	BIO_printf(out, "\n");


	BIO_printf(out,"_CDI : ");
	for(int i = 0; i < SHA256_DGST_SIZE; i++)
	 	BIO_printf(out,"%x",_CDID[i]);
	BIO_printf(out, "\n");
	BIO_free(out);

	return 1;
}
