diff -uNr a/eucrypt/smg_rsa/include/smg_rsa.h b/eucrypt/smg_rsa/include/smg_rsa.h --- a/eucrypt/smg_rsa/include/smg_rsa.h d3df108b121830777634fa225dd26165762168ee632c81906b9feb22b43017360f77fae03ce53f4eeec2b81eb28249bcc6542d50ae0fec9f3f1c0d33eb6b3c20 +++ b/eucrypt/smg_rsa/include/smg_rsa.h f0d2ac0848e2e2b0ef2826f5e5d747a4e9a8fc20b79c2fccdb02b390c14f2b1b59902024f294e28710844f6b3512537c23f9b46b887015f4556c8c31262ccff4 @@ -15,6 +15,21 @@ */ static const int KEY_LENGTH_OCTETS = 512; +typedef struct { + MPI n; /* modulus */ + MPI e; /* public exponent */ +} RSA_public_key; + +typedef struct { + MPI n; /* public modulus */ + MPI e; /* public exponent */ + MPI d; /* private exponent: e*d=1 mod phi */ + MPI p; /* prime p */ + MPI q; /* prime q */ + MPI u; /* inverse of p mod q */ +} RSA_secret_key; + + /*********truerandom.c*********/ /* @@ -86,6 +101,68 @@ */ void gen_random_prime( unsigned int noctets, MPI output); +/*********rsa.c*********/ +/* + * Generates a pair of public+private RSA keys using directly the entropy source + * specified in eucrypt/smg_rsa/include/knobs.h + * + * ALL RSA keys are 4096 bits out of 2 2048 bits primes, as per TMSR spec. + * + * @param sk a fully-allocated structure to hold the generated keypair (secret +key structure holds all the elements anyway, public key is a subset of this) + * + * NB: this procedure does NOT allocate memory for components in sk! + * caller should ALLOCATE enough memory for all the MPIs in sk + * Precondition: + * MPIs in sk have known allocated memory for the nlimbs fitting their TMSR size + */ +void gen_keypair( RSA_secret_key *sk ); + +/**************** + * Public key operation. Encrypt input with pk and store result into output. + * + * output = input^e mod n , where e,n are elements of pkey. + * NB: caller should allocate *sufficient* memory for output to hold the result. + * NB: NO checks are made on input! + * + * @param output MPI with enough allocated memory to hold result of encryption + * @param input MPI containing content to encrypt; it *has to be* different from +output! + * @param pk the public key that will be used to encrypt input + * + * Precondition: + * output != input + * Output and input have to be two distinct MPIs because of the sorry state of +the underlying mpi lib that can't handle properly the case when those are the +same. + */ +void public_rsa( MPI output, MPI input, RSA_public_key *pk ); + + +/**************** + * Secret key operation. Decrypt input with sk and store result in output. + * + * output = input^d mod n , where d, n are elements of skey. + * + * This implementation uses the Chinese Remainder Theorem (CRT): + * + * out1 = input ^ (d mod (p-1)) mod p + * out2 = input ^ (d mod (q-1)) mod q + * h = u * (out2 - out1) mod q + * output = out1 + h * p + * + * where out1, out2 and h are intermediate values, d,n,p,q,u are elements of +skey. By using CRT, encryption is *faster*. Decide for yourself if this fits +your needs though! + * NB: it is the caller's responsibility to allocate memory for output! + * NB: NO checks are made on input! + * + * @param output MPI with enough allocated memory to hold result of decryption + * @param input MPI containing content to decrypt + * @param sk the secret key that will be used to decrypt input + */ +void secret_rsa( MPI output, MPI input, RSA_secret_key *sk ); + #endif /*SMG_RSA*/ diff -uNr a/eucrypt/smg_rsa/rsa.c b/eucrypt/smg_rsa/rsa.c --- a/eucrypt/smg_rsa/rsa.c false +++ b/eucrypt/smg_rsa/rsa.c 99e516c7c6b48c92437207404d2605637575722df4d49f11c35d9b9e90a93c26ee6eb0dd5472eeaf364eccc49f66f48a6c966fb8ddf3cd1dae459450d573efa9 @@ -0,0 +1,138 @@ +/* + * An implementation of TMSR RSA + * S.MG, 2018 + */ + +#include "smg_rsa.h" +#include + +void gen_keypair( RSA_secret_key *sk ) { + /* precondition: sk is not null */ + assert(sk != NULL); + + /* precondition: enough memory allocated, corresponding to key size */ + int noctets_pq = KEY_LENGTH_OCTETS / 2; + unsigned int nlimbs_pq = mpi_nlimb_hint_from_nbytes( noctets_pq); + unsigned int nlimbs_n = mpi_nlimb_hint_from_nbytes( KEY_LENGTH_OCTETS); + assert( mpi_get_alloced( sk->n) >= nlimbs_n); + assert( mpi_get_alloced( sk->p) >= nlimbs_pq); + assert( mpi_get_alloced( sk->q) >= nlimbs_pq); + + /* helper variables for calculating Euler's totient phi=(p-1)*(q-1) */ + MPI p_minus1 = mpi_alloc(nlimbs_pq); + MPI q_minus1 = mpi_alloc(nlimbs_pq); + MPI phi = mpi_alloc(nlimbs_n); + + /* generate 2 random primes, p and q*/ + /* gen_random_prime sets top 2 bits to 1 so p*q will have KEY_LENGTH bits */ + /* in the extremely unlikely case that p = q, discard and generate again */ + do { + gen_random_prime( noctets_pq, sk->p); + gen_random_prime( noctets_pq, sk->q); + } while ( mpi_cmp( sk->p, sk->q) == 0); + + /* swap if needed, to ensure p < q for calculating u */ + if ( mpi_cmp( sk->p, sk->q) > 0) + mpi_swap( sk->p, sk->q); + + /* calculate helper for Chinese Remainder Theorem: + u = p ^ -1 ( mod q ) + this is used to speed-up decryption. + */ + mpi_invm( sk->u, sk->p, sk->q); + + /* calculate modulus n = p * q */ + mpi_mul( sk->n, sk->p, sk->q); + + /* calculate Euler totient: phi = (p-1)*(q-1) */ + mpi_sub_ui( p_minus1, sk->p, 1); + mpi_sub_ui( q_minus1, sk->q, 1); + mpi_mul( phi, p_minus1, q_minus1); + + /* choose random prime e, public exponent, with 3 < e < phi */ + /* because e is prime, gcd(e, phi) is always 1 so no need to check it */ + do { + gen_random_prime( noctets_pq, sk->e); + } while ( (mpi_cmp_ui(sk->e, 3) < 0) || (mpi_cmp(sk->e, phi) > 0)); + + /* calculate private exponent d, 1 < d < phi, where e * d = 1 mod phi */ + mpi_invm( sk->d, sk->e, phi); + + /* tidy up: free locally allocated memory for helper variables */ + mpi_free(phi); + mpi_free(p_minus1); + mpi_free(q_minus1); +} + +void public_rsa( MPI output, MPI input, RSA_public_key *pk ) { + + /* mpi_powm can't handle output and input being same */ + assert (output != input); + + mpi_powm( output, input, pk->e, pk->n ); +} + +void secret_rsa( MPI output, MPI input, RSA_secret_key *skey ) { + /* at its simplest, this would be input ^ d (mod n), hence: + * mpi_powm( output, input, skey->d, skey->n ); + * for faster decryption though, we'll use CRT and Garner's algorithm, hence: + * u = p ^ (-1) (mod q) , already calculated and stored in skey + * dp = d mod (p-1) + * dq = d mod (q-1) + * m1 = input ^ dp (mod p) + * m2 = input ^ dq (mod q) + * h = u * (m2 - m1) mod q + * output = m1 + h * p + * Note that same CRT speed up isn't available for encryption because at +encryption time not enough information is available (only e and n are known). + */ + + /* allocate memory for all local, helper MPIs */ + MPI p_minus1 = mpi_alloc( mpi_get_nlimbs( skey->p) ); + MPI q_minus1 = mpi_alloc( mpi_get_nlimbs( skey->q) ); + int nlimbs = mpi_get_nlimbs( skey->n ) + 1; + MPI dp = mpi_alloc( nlimbs ); + MPI dq = mpi_alloc( nlimbs ); + MPI m1 = mpi_alloc( nlimbs ); + MPI m2 = mpi_alloc( nlimbs ); + MPI h = mpi_alloc( nlimbs ); + + /* p_minus1 = p - 1 */ + mpi_sub_ui( p_minus1, skey->p, 1 ); + + /* dp = d mod (p - 1) aka remainder of d / (p - 1) */ + mpi_fdiv_r( dp, skey->d, p_minus1 ); + + /* m1 = input ^ dp (mod p) */ + mpi_powm( m1, input, dp, skey->p ); + + /* q_minus1 = q - 1 */ + mpi_sub_ui( q_minus1, skey->q, 1 ); + + /* dq = d mod (q - 1) aka remainder of d / (q - 1) */ + mpi_fdiv_r( dq, skey->d, q_minus1 ); + + /* m2 = input ^ dq (mod q) */ + mpi_powm( m2, input, dq, skey->q ); + + /* h = u * ( m2 - m1 ) mod q */ + mpi_sub( h, m2, m1 ); + if ( mpi_is_neg( h ) ) + mpi_add ( h, h, skey->q ); + mpi_mulm( h, skey->u, h, skey->q ); + + /* output = m1 + h * p */ + mpi_mul ( h, h, skey->p ); + mpi_add ( output, m1, h ); + + /* tidy up */ + mpi_free ( p_minus1 ); + mpi_free ( q_minus1 ); + mpi_free ( dp ); + mpi_free ( dq ); + mpi_free ( m1 ); + mpi_free ( m2 ); + mpi_free ( h ); + +} + diff -uNr a/eucrypt/smg_rsa/tests/tests.c b/eucrypt/smg_rsa/tests/tests.c --- a/eucrypt/smg_rsa/tests/tests.c 25364b546a9ec5983239a7c783b316f704c814ebee5778cfa595f14f802e64930b7234ee536e0f69e519bf36b558bea432d62a85d670d0ff62556cc12c6a1dc6 +++ b/eucrypt/smg_rsa/tests/tests.c 8293de5fe325f0499d3fc0b882e94b3e8e297911d2c9aa83ecdcbcaa13914273db14ed6a077ca24f75b34d9258224e72806f917def17d5cf7762b06d5a54f008 @@ -184,10 +184,234 @@ close(entropy_source); } +/* Test encryption+decryption on noctets of random data, using sk + * Output is written to file. + */ +void test_rsa_keys( RSA_secret_key *sk, unsigned int noctets, FILE *file ) { + RSA_public_key pk; + MPI test = mpi_alloc ( mpi_nlimb_hint_from_nbytes (noctets) ); + MPI out1 = mpi_alloc ( mpi_nlimb_hint_from_nbytes (noctets) ); + MPI out2 = mpi_alloc ( mpi_nlimb_hint_from_nbytes (noctets) ); + + pk.n = mpi_copy(sk->n); + pk.e = mpi_copy(sk->e); + unsigned char *p; + p = xmalloc(noctets); + + fprintf(file, "TEST encrypt/decrypt on %d octets of random data\n", noctets); + fflush(file); + if (get_random_octets( noctets, p) == noctets) { + mpi_set_buffer( test, p, noctets, 0 ); + + fprintf(file, "TEST data:\n"); + mpi_print(file, test, 1); + fprintf(file, "\n"); + fflush(file); + + public_rsa( out1, test, &pk ); + secret_rsa( out2, out1, sk ); + + fprintf(file, "ENCRYPTED with PUBLIC key data:\n"); + mpi_print(file, out1, 1); + fprintf(file, "\n"); + fflush(file); + + fprintf(file, "DECRYPTED with SECRET key:\n"); + mpi_print(file, out2, 1); + fprintf(file, "\n"); + fflush(file); + + if( mpi_cmp( test, out2 ) ) + fprintf(file, "FAILED: RSA operation: public(secret) failed\n"); + else + fprintf(file, "PASSED: RSA operation: public(secret) passed\n"); + fflush(file); + + secret_rsa( out1, test, sk ); + public_rsa( out2, out1, &pk ); + if( mpi_cmp( test, out2 ) ) + fprintf(file, "FAILED: RSA operation: secret(public) failed\n"); + else + fprintf(file, "PASSED: RSA operation: secret(public) passed\n"); + } + else + fprintf(file, "FAILED: not enough bits returned from entropy source\n"); + + fflush(file); + xfree(p); + mpi_free( pk.n); + mpi_free( pk.e); + + mpi_free( test ); + mpi_free( out1 ); + mpi_free( out2 ); +} + +void test_rsa( int nruns, FILE *fkeys, FILE *fout) { + RSA_secret_key sk; + int noctets = KEY_LENGTH_OCTETS; + int noctets_pq = noctets / 2; + int nlimbs = mpi_nlimb_hint_from_nbytes(noctets); + int nlimbs_pq = mpi_nlimb_hint_from_nbytes(noctets_pq); + int i; + + sk.n = mpi_alloc(nlimbs); + sk.e = mpi_alloc(nlimbs); + sk.d = mpi_alloc(nlimbs); + sk.p = mpi_alloc(nlimbs_pq); + sk.q = mpi_alloc(nlimbs_pq); + sk.u = mpi_alloc(nlimbs_pq); + + printf("TEST RSA key generation and use with %d runs\n", nruns); + fflush(stdout); + + for (i = 0;i < nruns; i++) { + gen_keypair(&sk); + printf("."); + fflush(stdout); + + mpi_print(fkeys, sk.n, 1); + fwrite("\n", sizeof(char), 1, fkeys); + + mpi_print(fkeys, sk.e, 1); + fwrite("\n", sizeof(char), 1, fkeys); + + mpi_print(fkeys, sk.d, 1); + fwrite("\n", sizeof(char), 1, fkeys); + + mpi_print(fkeys, sk.p, 1); + fwrite("\n", sizeof(char), 1, fkeys); + + mpi_print(fkeys, sk.q, 1); + fwrite("\n", sizeof(char), 1, fkeys); + + mpi_print(fkeys, sk.u, 1); + fwrite("\n", sizeof(char), 1, fkeys); + + test_rsa_keys(&sk, noctets_pq, fout); + printf("*"); + fflush(stdout); + } + + mpi_free(sk.n); + mpi_free(sk.e); + mpi_free(sk.d); + mpi_free(sk.p); + mpi_free(sk.q); + mpi_free(sk.u); + +} + +void test_rsa_exp() { + MPI msg = mpi_alloc(0); + MPI expected = mpi_alloc(0); + MPI result; + + RSA_public_key pk; + pk.n = mpi_alloc(0); + pk.e = mpi_alloc(0); + + printf("TEST verify of rsa exponentiation on input data: \n"); + + mpi_fromstr(msg, "0x\ +5B6A8A0ACF4F4DB3F82EAC2D20255E4DF3E4B7C799603210766F26EF87C8980E737579\ +EC08E6505A51D19654C26D806BAF1B62F9C032E0B13D02AF99F7313BFCFD68DA46836E\ +CA529D7360948550F982C6476C054A97FD01635AB44BFBDBE2A90BE06F7984AC8534C3\ +8613747F340C18176E6D5F0C10246A2FCE3A668EACB6165C2052497CA2EE483F4FD8D0\ +6A9911BD97E9B6720521D872BD08FF8DA11A1B8DB147F252E4E69AE6201D3B374B171D\ +F445EF2BF509D468FD57CEB5840349B14C6E2AAA194D9531D238B85B8F0DD352D1E596\ +71539B429849E5D965E438BF9EFFC338DF9AADF304C4130D5A05E006ED855F37A06242\ +28097EF92F6E78CAE0CB97"); + + mpi_fromstr(expected, "0x\ +1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\ +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\ +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\ +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\ +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003051300\ +D0609608648016503040203050004406255509399A3AF322C486C770C5F7F6E05E18FC\ +3E2219A03CA56C7501426A597187468B2F71B4A198C807171B73D0E7DBC3EEF6EA6AFF\ +693DE58E18FF84395BE"); + result = mpi_alloc( mpi_get_nlimbs(expected) ); + + mpi_fromstr(pk.n, "0x\ +CDD49A674BAF76D3B73E25BC6DF66EF3ABEDDCA461D3CCB6416793E3437C7806562694\ +73C2212D5FD5EED17AA067FEC001D8E76EC901EDEDF960304F891BD3CAD7F9A335D1A2\ +EC37EABEFF3FBE6D3C726DC68E599EBFE5456EF19813398CD7D548D746A30AA47D4293\ +968BFBAFCBF65A90DFFC87816FEE2A01E1DC699F4DDABB84965514C0D909D54FDA7062\ +A2037B50B771C153D5429BA4BA335EAB840F9551E9CD9DF8BB4A6DC3ED1318FF3969F7\ +B99D9FB90CAB968813F8AD4F9A069C9639A74D70A659C69C29692567CE863B88E191CC\ +9535B91B417D0AF14BE09C78B53AF9C5F494BCF2C60349FFA93C81E817AC682F0055A6\ +07BB56D6A281C1A04CEFE1"); + + mpi_fromstr( pk.e, "0x10001"); + + mpi_print( stdout, msg, 1); + printf("\n"); + + public_rsa( result, msg, &pk); + if ( mpi_cmp( result, expected) != 0 ) + printf( "FAILED\n"); + else + printf( "PASSED\n"); + + printf("Expected:\n"); + mpi_print( stdout, expected, 1); + printf("\n"); + + printf("Obtained:\n"); + mpi_print( stdout, result, 1); + printf("\n"); + + mpi_free( pk.n ); + mpi_free( pk.e ); + mpi_free( msg ); + mpi_free( expected ); + mpi_free( result ); +} + +void time_rsa_gen( int nruns ) { + struct timespec tstart, tend; + long int diff; + int i; + + RSA_secret_key sk; + int noctets = KEY_LENGTH_OCTETS; + int noctets_pq = noctets / 2; + int nlimbs = mpi_nlimb_hint_from_nbytes(noctets); + int nlimbs_pq = mpi_nlimb_hint_from_nbytes(noctets_pq); + sk.n = mpi_alloc(nlimbs); + sk.e = mpi_alloc(nlimbs); + sk.d = mpi_alloc(nlimbs); + sk.p = mpi_alloc(nlimbs_pq); + sk.q = mpi_alloc(nlimbs_pq); + sk.u = mpi_alloc(nlimbs_pq); + + clock_gettime(CLOCK_MONOTONIC, &tstart); + for (i = 0;i < nruns; i++) { + gen_keypair(&sk); + } + clock_gettime(CLOCK_MONOTONIC, &tend); + + diff = tend.tv_sec-tstart.tv_sec; + + printf("TOTAL: %ld seconds for generating %d key pairs\n", diff, nruns); + printf("Average (%d runs): %f seconds per TMSR RSA key pair.\n", + nruns, diff / (1.0*nruns)); + mpi_free(sk.n); + mpi_free(sk.e); + mpi_free(sk.d); + mpi_free(sk.p); + mpi_free(sk.q); + mpi_free(sk.u); +} + int main(int ac, char **av) { int nruns; int id; + FILE *fk; + FILE *fout; if (ac<2) { printf("Usage: %s number_of_runs/octets [testID]\n", av[0]); @@ -236,6 +460,25 @@ case 5: time_rpng(nruns); break; + case 6: + fk = fopen("keys.asc", "a"); + if ( fk == NULL ) + err("Failed to open file keys.asc!"); + fout = fopen("check_keys.asc", "a"); + if ( fout == NULL ) { + fclose(fk); + err("Failed to open file keys_check.asc!"); + } + test_rsa(nruns, fk, fout); + fclose(fk); + fclose(fout); + break; + case 7: + test_rsa_exp(); + break; + case 8: + time_rsa_gen(nruns); + break; default: printf("Current test ids:\n"); printf("0 for timing entropy source\n"); @@ -244,6 +487,10 @@ printf("3 for timing Miller-Rabin\n"); printf("4 for random prime number generator test\n"); printf("5 for timing random prime number generator\n"); + printf("6 for testing rsa key pair generation and use; \ +writes to keys.asc and check_keys.asc\n"); + printf("7 for testing rsa exponentiation (fixed data)\n"); + printf("8 for timing rsa key pair generator\n"); } return 0;