diff --git a/include/polarssl/dhm.h b/include/polarssl/dhm.h index 4d7bd8a7a..4874bc8df 100644 --- a/include/polarssl/dhm.h +++ b/include/polarssl/dhm.h @@ -147,6 +147,9 @@ typedef struct mpi GY; /*!< peer = G^Y mod P */ mpi K; /*!< key = GY^X mod P */ mpi RP; /*!< cached R^2 mod P */ + mpi Vi; /*!< blinding value */ + mpi Vf; /*!< un-blinding value */ + mpi _X; /*!< previous X */ } dhm_context; @@ -223,6 +226,9 @@ int dhm_make_public( dhm_context *ctx, int x_size, * \param p_rng RNG parameter * * \return 0 if successful, or an POLARSSL_ERR_DHM_XXX error code + * + * \note If f_rng is not NULL, it is used to blind the input as + * countermeasure against timing attacks. */ int dhm_calc_secret( dhm_context *ctx, unsigned char *output, size_t *olen, diff --git a/library/dhm.c b/library/dhm.c index a5c3e9095..11eee2a7a 100644 --- a/library/dhm.c +++ b/library/dhm.c @@ -245,6 +245,60 @@ cleanup: return( 0 ); } +/* + * Use the blinding method and optimisation suggested in section 10 of: + * KOCHER, Paul C. Timing attacks on implementations of Diffie-Hellman, RSA, + * DSS, and other systems. In : Advances in Cryptology—CRYPTO’96. Springer + * Berlin Heidelberg, 1996. p. 104-113. + */ +static int dhm_update_blinding( dhm_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + int ret, count; + + /* + * We can just update the previous values (by squaring them) if: + * - the values are initialized, and + * - our secret exponent did not change. + */ + if( ctx->Vi.p != NULL && + mpi_cmp_mpi( &ctx->X, &ctx->_X ) == 0 ) + { + MPI_CHK( mpi_mul_mpi( &ctx->Vf, &ctx->Vf, &ctx->Vf ) ); + MPI_CHK( mpi_mul_mpi( &ctx->Vi, &ctx->Vi, &ctx->Vi ) ); + + return( 0 ); + } + + /* + * Otherwise, we need to generate new values from scratch for this secret + */ + + /* Vi = random( 2, P-1 ) */ + count = 0; + do + { + mpi_fill_random( &ctx->Vi, mpi_size( &ctx->P ), f_rng, p_rng ); + + while( mpi_cmp_mpi( &ctx->Vi, &ctx->P ) >= 0 ) + mpi_shift_r( &ctx->Vi, 1 ); + + if( count++ > 10 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + } + while( mpi_cmp_int( &ctx->Vi, 1 ) <= 0 ); + + /* Vf = Vi^-X mod P */ + MPI_CHK( mpi_inv_mod( &ctx->Vf, &ctx->Vi, &ctx->P ) ); + MPI_CHK( mpi_exp_mod( &ctx->Vf, &ctx->Vf, &ctx->X, &ctx->P, &ctx->RP ) ); + + /* Remember secret associated with Vi and Vf */ + MPI_CHK( mpi_copy( &ctx->_X, &ctx->X ) );; + +cleanup: + return( ret ); +} + /* * Derive and export the shared secret (G^Y)^X mod P */ @@ -254,24 +308,43 @@ int dhm_calc_secret( dhm_context *ctx, void *p_rng ) { int ret; - - (void) f_rng; - (void) p_rng; + mpi GYb; if( ctx == NULL || *olen < ctx->len ) return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); - MPI_CHK( mpi_exp_mod( &ctx->K, &ctx->GY, &ctx->X, - &ctx->P, &ctx->RP ) ); - if( ( ret = dhm_check_range( &ctx->GY, &ctx->P ) ) != 0 ) return( ret ); + mpi_init( &GYb ); + + /* Blind peer's value */ + if( f_rng != 0 ) + { + MPI_CHK( dhm_update_blinding( ctx, f_rng, p_rng ) ); + MPI_CHK( mpi_mul_mpi( &GYb, &ctx->GY, &ctx->Vi ) ); + MPI_CHK( mpi_mod_mpi( &GYb, &GYb, &ctx->P ) ); + } + else + MPI_CHK( mpi_copy( &GYb, &ctx->GY ) ); + + /* Do modular exponentiation */ + MPI_CHK( mpi_exp_mod( &ctx->K, &GYb, &ctx->X, + &ctx->P, &ctx->RP ) ); + + /* Unblind secret value */ + if( f_rng != 0 ) + { + MPI_CHK( mpi_mul_mpi( &ctx->K, &ctx->K, &ctx->Vf ) ); + MPI_CHK( mpi_mod_mpi( &ctx->K, &ctx->K, &ctx->P ) ); + } + *olen = mpi_size( &ctx->K ); MPI_CHK( mpi_write_binary( &ctx->K, output, *olen ) ); cleanup: + mpi_free( &GYb ); if( ret != 0 ) return( POLARSSL_ERR_DHM_CALC_SECRET_FAILED + ret ); @@ -284,6 +357,7 @@ cleanup: */ void dhm_free( dhm_context *ctx ) { + mpi_free( &ctx->Vi ); mpi_free( &ctx->Vf ); mpi_free( &ctx->RP ); mpi_free( &ctx->K ); mpi_free( &ctx->GY ); mpi_free( &ctx->GX ); mpi_free( &ctx->X ); mpi_free( &ctx->G ); mpi_free( &ctx->P ); diff --git a/tests/suites/test_suite_dhm.data b/tests/suites/test_suite_dhm.data index 99f44826d..0c0dce867 100644 --- a/tests/suites/test_suite_dhm.data +++ b/tests/suites/test_suite_dhm.data @@ -4,5 +4,5 @@ dhm_do_dhm:1024:10:"23":10:"5" Diffie-Hellman full exchange #2 dhm_do_dhm:1024:10:"93450983094850938450983409623":10:"9345098304850938450983409622" -Diffie-Hellman full exchange #2 +Diffie-Hellman full exchange #3 dhm_do_dhm:1024:10:"93450983094850938450983409623982317398171298719873918739182739712938719287391879381271":10:"9345098309485093845098340962223981329819812792137312973297123912791271" diff --git a/tests/suites/test_suite_dhm.function b/tests/suites/test_suite_dhm.function index e6fadfb2f..31a9004c3 100644 --- a/tests/suites/test_suite_dhm.function +++ b/tests/suites/test_suite_dhm.function @@ -35,18 +35,53 @@ void dhm_do_dhm( int NOTUSED, int radix_P, char *input_P, memset( sec_cli, 0x00, 1000 ); memset( &rnd_info, 0x00, sizeof( rnd_pseudo_info ) ); + /* + * Set params + */ TEST_ASSERT( mpi_read_string( &ctx_srv.P, radix_P, input_P ) == 0 ); TEST_ASSERT( mpi_read_string( &ctx_srv.G, radix_G, input_G ) == 0 ); x_size = mpi_size( &ctx_srv.P ); + pub_cli_len = x_size; + + /* + * First key exchange + */ + TEST_ASSERT( dhm_make_params( &ctx_srv, x_size, ske, &ske_len, &rnd_pseudo_rand, &rnd_info ) == 0 ); + ske[ske_len++] = 0; + ske[ske_len++] = 0; + TEST_ASSERT( dhm_read_params( &ctx_cli, &p, ske + ske_len ) == 0 ); + + TEST_ASSERT( dhm_make_public( &ctx_cli, x_size, pub_cli, pub_cli_len, &rnd_pseudo_rand, &rnd_info ) == 0 ); + TEST_ASSERT( dhm_read_public( &ctx_srv, pub_cli, pub_cli_len ) == 0 ); + + TEST_ASSERT( dhm_calc_secret( &ctx_srv, sec_srv, &sec_srv_len, &rnd_pseudo_rand, &rnd_info ) == 0 ); + TEST_ASSERT( dhm_calc_secret( &ctx_cli, sec_cli, &sec_cli_len, NULL, NULL ) == 0 ); + + TEST_ASSERT( sec_srv_len == sec_cli_len ); + TEST_ASSERT( sec_srv_len != 0 ); + TEST_ASSERT( memcmp( sec_srv, sec_cli, sec_srv_len ) == 0 ); + + /* Re-do calc_secret on server to test update of blinding values */ + sec_srv_len = 1000; + TEST_ASSERT( dhm_calc_secret( &ctx_srv, sec_srv, &sec_srv_len, &rnd_pseudo_rand, &rnd_info ) == 0 ); + + TEST_ASSERT( sec_srv_len == sec_cli_len ); + TEST_ASSERT( sec_srv_len != 0 ); + TEST_ASSERT( memcmp( sec_srv, sec_cli, sec_srv_len ) == 0 ); + + /* + * Second key exchange to test change of blinding values on server + */ + sec_cli_len = 1000; + sec_srv_len = 1000; + p = ske; TEST_ASSERT( dhm_make_params( &ctx_srv, x_size, ske, &ske_len, &rnd_pseudo_rand, &rnd_info ) == 0 ); ske[ske_len++] = 0; ske[ske_len++] = 0; TEST_ASSERT( dhm_read_params( &ctx_cli, &p, ske + ske_len ) == 0 ); - pub_cli_len = x_size; TEST_ASSERT( dhm_make_public( &ctx_cli, x_size, pub_cli, pub_cli_len, &rnd_pseudo_rand, &rnd_info ) == 0 ); - TEST_ASSERT( dhm_read_public( &ctx_srv, pub_cli, pub_cli_len ) == 0 ); TEST_ASSERT( dhm_calc_secret( &ctx_srv, sec_srv, &sec_srv_len, &rnd_pseudo_rand, &rnd_info ) == 0 );