#include "layer1.h"


int readUDS(uint8_t* UDSdigest)
{
  //Read 8 bytes from RANDFILE
	//calculate sha256  	
  //print digest

  FILE *fp = NULL;
  fp = fopen("RANDFILE","rb");
  if (!fp)
  {  
    perror("File open failed\n");
    return -1;    //FILENOTFOUND
  }
  //uint8_t UDSbuf[UDS_SIZE] = {0};	//{0Xe3,0xc5,0x58,0xaa,0x2f,0xd2,0x19,0x25};
  uint8_t *UDSbuf = calloc(1, sizeof(uint8_t)*UDS_SIZE);
  fread(UDSbuf,UDS_SIZE,1,fp);
  fclose(fp);

  uint8_t UDS_ID[UDS_DGST_SIZE] = {0};

  //mbedtls_sha256_ret( &UDSbuf,sizeof(UDSbuf),UDS_ID,0 );
  mbedtls_sha256_ret( UDSbuf,UDS_SIZE,UDSdigest,0 );


	for (int i = 0; i < UDS_SIZE; i++)
  		printf("%hhx", UDSbuf[i]);
  printf(" :	fuse secret\n" );  	

  for (int i = 0; i < UDS_DGST_SIZE; i++)
  		printf("%hhx", UDSdigest[i]);
  printf(" :  UDS ID\n" );

  free(UDSbuf);
	return RIOTSUCCESS;

}


int readFWID(uint8_t* FW_M)
{

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

  FILE *fp = NULL;  
  fp = fopen("layer1.c", "r");
  if (!fp)
  {  
    perror("File open failed\n");
    return -1;    //FILENOTFOUND
  }

  fseek(fp, 0L, SEEK_END);
  long FW_size = ftell(fp);
  rewind(fp);

  //uint8_t *source = calloc(1,sizeof(uint8_t)*FW_size);
  uint8_t source[FW_size];

  if (fread(&source, FW_size, 1, fp) < 0)
  {
    perror("File read failed");
    return -1;
  }
  fclose(fp);
  mbedtls_sha256_ret(source,FW_size,FW_M,0 );

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

  //printf("File contnts : %s\n", source);

  for(int i = 0; i < FW_DGST_SIZE; i++)
    printf("%hhx",FW_M[i]);
  printf(" :  FW digest\n");
  
  //free(source);
  return RIOTSUCCESS;

}

int _calcCDID(uint8_t * CDID)
{
  //function should not expose any internal measurements
  //ONLY CDI should be accessible outside layer 0

  //call readUDS, store measurement
  //call readFWID, store measurement
  //calculate Composite Device Identity
  //return CDI

  uint8_t* UDS_ID = calloc(1,sizeof(uint8_t)*UDS_DGST_SIZE);
  readUDS(UDS_ID);

  uint8_t* FW_ID = calloc(1,sizeof(uint8_t)*FW_DGST_SIZE);
  readFWID(FW_ID);

  mbedtls_sha256_context sha_ctx;
  mbedtls_sha256_init(&sha_ctx);
  if(mbedtls_sha256_starts_ret(&sha_ctx,0) < 0 )
  {
    perror("SHA session failed to start\n");
    mbedtls_sha256_free(&sha_ctx);
    return -1;
  }

  if(mbedtls_sha256_update_ret(&sha_ctx, UDS_ID, UDS_DGST_SIZE) < 0)
  {
    perror("SHA session update failed\n");
    mbedtls_sha256_free(&sha_ctx);
    return -1;  
  }

  if(mbedtls_sha256_update_ret(&sha_ctx, FW_ID, FW_DGST_SIZE) < 0)
  {
    perror("SHA session update failed\n");
    mbedtls_sha256_free(&sha_ctx);
    return -1;  
  }  

  if(mbedtls_sha256_finish_ret(&sha_ctx,CDID) < 0)
  {
    perror("SHA session update failed\n");
    mbedtls_sha256_free(&sha_ctx);
    return -1;  
  }

    for(int i = 0; i < CDI_DGST_SIZE; i++)
      printf("%hhx",CDID[i]);
    printf(" :  CDID\n");
  
  free(UDS_ID);
  free(FW_ID);
  mbedtls_sha256_free(&sha_ctx);
  return RIOTSUCCESS;

}

/*int mbedtls_hkdf( const mbedtls_md_info_t *md, 
                  const unsigned char *salt, size_t salt_len, 
                  const unsigned char *ikm, size_t ikm_len,
                  const unsigned char *info, size_t info_len,
                  unsigned char *okm, size_t okm_len ); */

int _calcCDIKEY(uint8_t * CDIKEY)
{
  uint8_t* KEYIN = calloc(1,sizeof(uint8_t)*CDI_DGST_SIZE);
  _calcCDID(KEYIN);

  for(int i = 0; i < SHA256_DGST_SIZE; i++)
      printf("%hhx",KEYIN[i]);
  printf(" :  CDID main\n");

  const mbedtls_md_info_t * md_info;
  if(!(md_info = mbedtls_md_info_from_type(HKDF_ALG)))
  {
    perror("MD alg type def failed\n");
    return RIOTFAILURE;
  }

  uint8_t salt[32] = {0x30,0xe2,0x3e,0xcc,0x28,0xc5,0x7b,0xbb,0x38,0x7d,0xe6,0x66,0xbb,
    0xbe,0x67,0x0a,0xf8,0xf3,0x92,0x0e,0xba,0x68,0xd1,0x56,0xea,0x34,0x3f,0xbc,0x4f,
    0xf1,0xd9,0x1e};

  mbedtls_hkdf(md_info, salt, sizeof(salt), KEYIN, CDI_DGST_SIZE,
              IDENTITY, sizeof(IDENTITY), CDIKEY, CDI_KEY_SIZE);

  //mbedtls_hkdf_extract( md_info, salt, sizeof(salt),KEYIN, CDI_DGST_SIZE, CDIKEY);

  for(int i = 0; i < CDI_KEY_SIZE; i++)
      printf("%hhx",CDIKEY[i]);
  printf(" :  CDIKEY\n");

  free(KEYIN);

  return RIOTSUCCESS;
}



  //firt generate ECC/RSA key. - Done ECC
  //check for deterministic consistency	- inconsistent
  //seed RNGs with CDI
  //let's see how it goes

  //add entropy source?
  //seed RNG
  //create ctx
  //init
  //gen keypair


/* To use HW TRNG /dev/random as the source of entropy add source to entropy contxt -
*/

int use_dev_random(void *data, unsigned char *output,
                             size_t len, size_t *olen )
{
    FILE *file;
    size_t ret, left = len;
    unsigned char *p = output;
    ((void) data);

    *olen = 0;

    file = fopen( "/dev/random", "rb" );
    if( file == NULL )
        return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED );

    while( left > 0 )
    {
        /* /dev/random can return much less than requested. If so, try again */
        ret = fread( p, 1, left, file );
        if( ret == 0 && ferror( file ) )
        {
            fclose( file );
            return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED );
        }

        p += ret;
        left -= ret;
        sleep( 1 );
    }
    fclose( file );
    *olen = len;

    return( 0 );
}


int seedRNGSource(void *data, unsigned char *output, size_t len)
{
  //srand(); lib fun call //https://stackoverflow.com/questions/55927662/generate-every-time-same-rsa-key-with-c
  //ctr_drbg    //programs/test/benchmark.c:705
  //hmac_drbg

	//Fill entropy accum with CDI and pass to DRBG


  mbedtls_entropy_context * p_ent = data;
  printf("manual update entropy with CDI\n");
      
  if(memcpy(output, p_ent -> accumulator.buffer + ACCUM_BUFF_OFFSET , ENTROPY_LEN) < 0)
  	return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED );

  len = ENTROPY_LEN;


  return 0;

  //  ((void) data);
  //  printf("Direct return CDI to drbg\n");
  
  // len = sizeof(CDI);
  // memcpy(output, CDI, len);
  //      for(int i = 0; i < len; i++)
  //   printf("0x%hhx,",output[i]);
  //  printf(" :  CDIKEY\n"); 
  //  printf("%d\n", (int)len);
  // return 0;

}




int deriveECCKeyPair(KeyDrv_context * KD_ctx)
{

	printf("inside deriveECCKeyPair layer1\n\n");

	int ret = 0;
  unsigned char pubkeybuf[100];
  size_t pubkeysize;
  char privkeybuf[100];
  size_t privkeysize;
  char privkeybuf2[100];
  size_t privkey2size;


  mbedtls_ecp_group ecpGrp;
  mbedtls_ecp_group_init(&ecpGrp);
  mbedtls_ecp_group_load(&ecpGrp, ECC_CURVE);

  mbedtls_mpi secret;
  mbedtls_mpi_init(&secret);

  mbedtls_ecp_point Public;
  mbedtls_ecp_point_init(&Public);

  mbedtls_entropy_context entropyCtx;
  mbedtls_entropy_init(&entropyCtx);

  mbedtls_ctr_drbg_context drbgCtx;
  mbedtls_ctr_drbg_init(&drbgCtx);

 
  if (KD_ctx->ENT_MODE == HW_TRNG)		//HW RNG
  {
  	printf("using /dev/random.... this may take a moment\n");
  	mbedtls_entropy_add_source( &entropyCtx, use_dev_random,
                               NULL, ENTROPY_LEN, MBEDTLS_ENTROPY_SOURCE_STRONG );
  	
  	mbedtls_ctr_drbg_seed(&drbgCtx, mbedtls_entropy_func, &entropyCtx,
                             (const unsigned char *) KD_ctx->phrase, strlen(KD_ctx->phrase) );
  }

  else if (KD_ctx->ENT_MODE == DETERM)			// Deterministic derviation with seed
  {	
  	printf("Seeding entropy accumulator....\n");
    if(mbedtls_entropy_update_manual(&entropyCtx, KD_ctx->seed, ENTROPY_LEN) < 0)
    {
    	perror("Accumulator seed failed\n");
    	return RIOTFAILURE;
    }

    if(mbedtls_ctr_drbg_seed(&drbgCtx, seedRNGSource, &entropyCtx,
    				(const unsigned char *) KD_ctx->phrase, sizeof(&KD_ctx->phrase)) < 0)
    {
    	perror("drbg seed failed\v");
    	return RIOTFAILURE;
    }
	}

	else 						//regular SW accumulator used for key derivation
	{
		printf("Accumulating entropy ...\n");
		mbedtls_entropy_update_manual(&entropyCtx, KD_ctx->seed, ENTROPY_LEN);
		mbedtls_ctr_drbg_seed(&drbgCtx, mbedtls_entropy_func, &entropyCtx,
                              (const unsigned char *) KD_ctx->phrase, strlen(KD_ctx->phrase) );
	}

  if(mbedtls_ecp_gen_keypair(&ecpGrp, &secret, &Public, 
  				mbedtls_ctr_drbg_random, &drbgCtx) <0)
  {
  	perror("ECP gen keypair failed\n");
  	return RIOTFAILURE;
  }

  ret = mbedtls_ecp_tls_write_point(&ecpGrp, &Public, MBEDTLS_ECP_PF_UNCOMPRESSED,
   				&pubkeysize, pubkeybuf, sizeof(pubkeybuf));
  if(ret < 0)
  {
  	perror("ECP write point failure\n");
  	return RIOTFAILURE;
  }

	//printf("%zu : pubkeysize\n", pubkeysize );
	for(int i = 0; i < pubkeysize; i++)
		printf("%hhx",pubkeybuf[i]);
	printf(" :  PubKey\n");


  ret = mbedtls_mpi_write_string(&secret, 16, privkeybuf, sizeof(privkeybuf), &privkeysize);
  if(ret < 0)
  {
  	printf("%d\n", ret);
  	perror("MPI write point to string failure\n");
  	return RIOTFAILURE;
  }

	printf("%s :  PrivKey\n",privkeybuf);

	//copy keys to parent function
  mbedtls_ecp_copy(&KD_ctx->Public, &Public);
  mbedtls_mpi_copy(&KD_ctx->secret, &secret);		/* Make SK NULL for Identitiy key generation */



    //what now? how to obtain the keys in PEM/DER/bin format?

    //following experiment better documented in ECC_trial/PKtoPEM.c
    /**
    mbedtls_pk_context ec_pkCtx;
    mbedtls_pk_init(&ec_pkCtx);

    mbedtls_pk_setup(&ec_pkCtx, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
    mbedtls_ecp_keypair *pk_ecp = mbedtls_pk_ec( ec_pkCtx );

	mbedtls_ecp_copy(&pk_ecp->Q, &Public);
	mbedtls_mpi_copy(&pk_ecp->d, &secret);    

	mbedtls_mpi_write_string(&pk_ecp->d, 16, privkeybuf2, sizeof(privkeybuf2), &privkey2size);
	printf("%s :  wrapped PrivKey\n",privkeybuf2);



	ret = 0;
	unsigned char privBuf[16000];
    unsigned char * d_b = privBuf;
    memset(privBuf, 0, 16000);
    unsigned char pubBuf[16000];
    unsigned char * Q_b = pubBuf;
    memset(pubBuf, 0, 16000);
    ret = mbedtls_pk_write_key_pem(&ec_pkCtx, privBuf, 16000);
    printf("write priv pem ret value = %d\n", ret);

    printf("%s\n", privBuf);

    ret = mbedtls_pk_write_pubkey_pem(&ec_pkCtx, pubBuf, 16000);
    printf("write pub pem ret value = %d\n", ret);

    printf("%s\n", pubBuf);    

	**/


    mbedtls_mpi_free(&secret);
    mbedtls_ecp_point_free(&Public);
    mbedtls_ecp_group_free(&ecpGrp);
    mbedtls_entropy_free(&entropyCtx);
   	mbedtls_ctr_drbg_free(&drbgCtx);

   	printf("leaving deriveECCKeyPair layer1\n\n");

    return 0;
}

int deriveRSAKeyPair(void)
{	
	printf("inside deriveRSAKeyPair layer1\n\n");

	mbedtls_rsa_context rsaCtx;
	mbedtls_rsa_init(&rsaCtx,MBEDTLS_RSA_PKCS_V21, RSA_HASH_ID);

	mbedtls_entropy_context entropyCtx;
  mbedtls_entropy_init(&entropyCtx);

  mbedtls_ctr_drbg_context drbgCtx;
  mbedtls_ctr_drbg_init(&drbgCtx);

    //Seed drbg with secret data now?
    //move "private" string to n param
  int ret = mbedtls_ctr_drbg_seed(&drbgCtx, mbedtls_entropy_func, &entropyCtx,
    				(const unsigned char *) "Private", sizeof("Private"));

	mbedtls_rsa_gen_key(&rsaCtx,mbedtls_ctr_drbg_random, &drbgCtx,
					(unsigned int) 256, 65537);

	//is the key generated and stored in the contxt struct?
    //what now? how to obtain the keys in PEM/DER/bin format?

	printf("leaving deriveRSAKeyPair layer1\n\n");

	return 0;
}
