From 0d6dbbae9387f9a96f2866c20f5ac01140217ac4 Mon Sep 17 00:00:00 2001 From: "atul.jha" Date: Wed, 13 Jan 2021 10:55:08 +0100 Subject: [PATCH] Keygen working --- trial4/KeyGen.c | 171 +++++++++++++++++++++++++++++++++++++------ trial4/KeyGen.h | 19 ++++- trial4/RANDFILE | Bin 1024 -> 0 bytes trial4/ROMprotocol.c | 42 +++++------ trial4/defines.h | 64 ++++++++-------- trial4/makefile | 8 ++ trial4/out/main | Bin 18560 -> 23064 bytes 7 files changed, 229 insertions(+), 75 deletions(-) delete mode 100644 trial4/RANDFILE diff --git a/trial4/KeyGen.c b/trial4/KeyGen.c index 0958bd0..c287564 100644 --- a/trial4/KeyGen.c +++ b/trial4/KeyGen.c @@ -37,6 +37,8 @@ Every key derivation should start with a new KD contxt **/ /* + +/////////////////////////////////////TODO//////////////////// void cleanup() { mbedtls_entropy_free(&entropyCtx); @@ -133,19 +135,8 @@ DIMASTATUS AsymmKeyGen(KeyDrv_context * KD_ctx) if(DEBUG) printf("PASS 1\n"); - mbedtls_pk_context key_ctx; - mbedtls_pk_init(&key_ctx); -/* - mbedtls_mpi N, P, Q, D, E, DP, DQ, QP; - mbedtls_mpi_init( &N ); - mbedtls_mpi_init( &P ); - mbedtls_mpi_init( &Q ); - mbedtls_mpi_init( &D ); - mbedtls_mpi_init( &E ); - mbedtls_mpi_init( &DP ); - mbedtls_mpi_init( &DQ ); - mbedtls_mpi_init( &QP ); -*/ + mbedtls_pk_context pkey_ctx; + mbedtls_pk_init(&pkey_ctx); //assign entropy source and derive seed for key gen @@ -207,7 +198,7 @@ if(DEBUG) if(KD_ctx->PKC_MODE == isECC) { - ret = mbedtls_pk_setup(&key_ctx,mbedtls_pk_info_from_type((mbedtls_pk_type_t)MBEDTLS_PK_ECKEY)); + ret = mbedtls_pk_setup(&pkey_ctx,mbedtls_pk_info_from_type((mbedtls_pk_type_t)MBEDTLS_PK_ECKEY)); if(ret < DIMASUCCESS) { perror("DIMAPKFAILURE\n"); @@ -215,7 +206,7 @@ if(DEBUG) exit(DIMAPKFAILURE); } - ret = mbedtls_ecp_gen_key( (mbedtls_ecp_group_id) ECC_CURVE, mbedtls_pk_ec( key_ctx ), + ret = mbedtls_ecp_gen_key( (mbedtls_ecp_group_id) ECC_CURVE, mbedtls_pk_ec( pkey_ctx ), mbedtls_ctr_drbg_random, &drbgCtx ); if(ret < DIMASUCCESS) { @@ -224,9 +215,9 @@ if(DEBUG) exit(DIMAECCFAILURE); } - if( mbedtls_pk_get_type( &key_ctx ) == MBEDTLS_PK_ECKEY ) + if( mbedtls_pk_get_type( &pkey_ctx ) == MBEDTLS_PK_ECKEY ) { - mbedtls_ecp_keypair *ecp = mbedtls_pk_ec( key_ctx ); + mbedtls_ecp_keypair *ecp = mbedtls_pk_ec( pkey_ctx ); if(DEBUG) { printf( "curve: %s\n", mbedtls_ecp_curve_info_from_grp_id( ecp->grp.id )->name ); @@ -259,7 +250,7 @@ if(DEBUG) mbedtls_mpi_init( &DQ ); mbedtls_mpi_init( &QP ); - ret = mbedtls_pk_setup(&key_ctx, mbedtls_pk_info_from_type((mbedtls_pk_type_t) MBEDTLS_PK_RSA)); + ret = mbedtls_pk_setup(&pkey_ctx, mbedtls_pk_info_from_type((mbedtls_pk_type_t) MBEDTLS_PK_RSA)); if(ret < DIMASUCCESS) { perror("DIMAPKFAILURE\n"); @@ -267,7 +258,7 @@ if(DEBUG) exit(DIMAPKFAILURE); } - ret = mbedtls_rsa_gen_key( mbedtls_pk_rsa( key_ctx ), mbedtls_ctr_drbg_random, &drbgCtx, RSA_SIZE, RSA_EXP ); + ret = mbedtls_rsa_gen_key( mbedtls_pk_rsa( pkey_ctx ), mbedtls_ctr_drbg_random, &drbgCtx, RSA_SIZE, RSA_EXP ); if(ret < DIMASUCCESS) { perror("DIMARSAFAILURE\n"); @@ -275,9 +266,9 @@ if(DEBUG) exit(DIMARSAFAILURE); } - if( mbedtls_pk_get_type( &key_ctx ) == MBEDTLS_PK_RSA ) + if( mbedtls_pk_get_type( &pkey_ctx ) == MBEDTLS_PK_RSA ) { - mbedtls_rsa_context *rsa = mbedtls_pk_rsa( key_ctx ); + mbedtls_rsa_context *rsa = mbedtls_pk_rsa( pkey_ctx ); if( ( ret = mbedtls_rsa_export ( rsa, &N, &P, &Q, &D, &E ) ) != 0 || ( ret = mbedtls_rsa_export_crt( rsa, &DP, &DQ, &QP ) ) != 0 ) @@ -306,12 +297,150 @@ if(DEBUG) exit(DIMAINVALIDSTATE); } + ret = WritePrivKey(KD_ctx,&pkey_ctx); + ret = WritePubKey(KD_ctx, &pkey_ctx); + ////////////////////////////TODO/////////////////////////// //free block return DIMASUCCESS; } +//#define DFL_PUB "keys/DID_pub." DFL_FORM +//#define DFL_PRIV "SecureStorage/DID_priv" DFL_FORM +DIMASTATUS WritePrivKey(KeyDrv_context * KD_ctx, mbedtls_pk_context * pkey_ctx) +{ + DIMASTATUS ret = 0; + int i = 0; + size_t len; + FILE *fp; + unsigned char dest_file[50]; + unsigned char * outbuf = calloc(1,sizeof(unsigned char)*KEY_BUF_SIZE); + + + if(strcmp(KD_ctx -> phrase, IDENTITY)) + { + if(DEBUG) + { + printf("Private DID key should be stored only inside secure storage.\n"); + } + //codeblock to check existance of SS. + //return with warning otherwise + //exit? + //////////////////////////////TODO/////////////////// + + + strcpy(dest_file,"SecureStorage/"); + } + else + { + strcpy(dest_file,"keys/"); + } + +printf("%s,%s\n",KD_ctx->phrase,IDENTITY ); + strcat(dest_file, KD_ctx->phrase); + strcat(dest_file, "_priv"); + + if(KD_ctx->KEY_FORM == PEM) + { + strcat(dest_file, ".pem"); + //write pem does not return no of bytes written....stupid + len = mbedtls_pk_write_key_pem(pkey_ctx, outbuf, KEY_BUF_SIZE); + if(len < 0) + { + perror("DIMAOUTPUTERROR:"); + return(DIMAOUTPUTERROR); + } + } + else if(KD_ctx->KEY_FORM == DER) + { + strcat(dest_file, ".der"); + len = mbedtls_pk_write_key_der(pkey_ctx, outbuf, KEY_BUF_SIZE); + if(len < 0) + { + perror("DIMAOUTPUTERROR:"); + return(DIMAOUTPUTERROR); + } + } + else + { + perror("DIMAINVALIDSTATE : Check key format in Key Derivation struct\n"); + //cleanup(); + exit(DIMAINVALIDSTATE); + } + + len = strlen( (char *) outbuf ); + + if( ( fp = fopen( dest_file, "w" ) ) == NULL ) + { + perror("DIMAOUTPUTERROR: "); + return(DIMAOUTPUTERROR); + } + + fwrite( outbuf, 1, len, fp ); + fclose( fp ); + + return(DIMASUCCESS); + +} + + +DIMASTATUS WritePubKey(KeyDrv_context * KD_ctx, mbedtls_pk_context * pkey_ctx) +{ + DIMASTATUS ret = 0; + int i = 0; + size_t len; + FILE * fp; + unsigned char dest_file[50]; + unsigned char * outbuf = calloc(1,sizeof(unsigned char)*KEY_BUF_SIZE); + + strcpy(dest_file,"keys/"); + strcat(dest_file, KD_ctx->phrase); + strcat(dest_file, "_pub"); + + + if(KD_ctx->KEY_FORM == PEM) + { + strcat(dest_file, ".pem"); + //write pem does not return no of bytes written....stupid + len = mbedtls_pk_write_pubkey_pem(pkey_ctx, outbuf, KEY_BUF_SIZE); + if(len < 0) + { + perror("DIMAOUTPUTERROR:"); + return(DIMAOUTPUTERROR); + } + } + else if(KD_ctx->KEY_FORM == DER) + { + strcat(dest_file, ".der"); + len = mbedtls_pk_write_pubkey_der(pkey_ctx, outbuf, KEY_BUF_SIZE); + if(len < 0) + { + perror("DIMAOUTPUTERROR:"); + return(DIMAOUTPUTERROR); + } + } + else + { + perror("DIMAINVALIDSTATE : Check key format in Key Derivation struct\n"); + //cleanup(); + exit(DIMAINVALIDSTATE); + } + + len = strlen( (char *) outbuf ); + + if( ( fp = fopen( dest_file, "w" ) ) == NULL ) + { + perror("DIMAOUTPUTERROR: "); + return(DIMAOUTPUTERROR); + } + + fwrite( outbuf, 1, len, fp ); + fclose( fp ); + + return(DIMASUCCESS); + +} diff --git a/trial4/KeyGen.h b/trial4/KeyGen.h index fb4975c..c031a55 100644 --- a/trial4/KeyGen.h +++ b/trial4/KeyGen.h @@ -29,4 +29,21 @@ DIMASTATUS use_dev_random(void *data, unsigned char *output, size_t len, size_t DIMASTATUS seedRNGSource(void *data, unsigned char *output, size_t len); */ -DIMASTATUS AsymmKeyGen(KeyDrv_context * KD_ctx); \ No newline at end of file + + //Create Asymmetric device key from CDI key + + //Create KD contxt, fill in information required for the asymm key derivation + //call KeyGen with KD ctx + //export Pub key? + //export Pub cert? + //what else can we do? key chains? + + + +DIMASTATUS AsymmKeyGen(KeyDrv_context * KD_ctx); + + +//If KD->phrase is IDENTITY, do not write priv key file outside SS. issue warning. +DIMASTATUS WritePrivKey(KeyDrv_context * KD_ctx, mbedtls_pk_context * key_ctx); +DIMASTATUS WritePubKey(KeyDrv_context * KD_ctx, mbedtls_pk_context * key_ctx); + diff --git a/trial4/RANDFILE b/trial4/RANDFILE deleted file mode 100644 index fd3981be19fb821cee53a8f8969c2cc21a46a686..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1024 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 literal 18560 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