Merge pull request #4628 from ronald-cron-arm/dhm-key-generation-bias

dhm: Fix bias in private key generation
This commit is contained in:
Manuel Pégourié-Gonnard
2021-06-16 13:13:34 +02:00
committed by GitHub
5 changed files with 247 additions and 116 deletions

View File

@@ -0,0 +1,4 @@
Security
* Fix a bias in the generation of finite-field Diffie-Hellman-Merkle (DHM)
private keys and of blinding values for DHM and elliptic curves (ECP)
computations. Reported by FlorianF89 in #4245.

View File

@@ -130,22 +130,21 @@ static int dhm_read_bignum( mbedtls_mpi *X,
*/
static int dhm_check_range( const mbedtls_mpi *param, const mbedtls_mpi *P )
{
mbedtls_mpi L, U;
mbedtls_mpi U;
int ret = 0;
mbedtls_mpi_init( &L ); mbedtls_mpi_init( &U );
mbedtls_mpi_init( &U );
MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &L, 2 ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( &U, P, 2 ) );
if( mbedtls_mpi_cmp_mpi( param, &L ) < 0 ||
if( mbedtls_mpi_cmp_int( param, 2 ) < 0 ||
mbedtls_mpi_cmp_mpi( param, &U ) > 0 )
{
ret = MBEDTLS_ERR_DHM_BAD_INPUT_DATA;
}
cleanup:
mbedtls_mpi_free( &L ); mbedtls_mpi_free( &U );
mbedtls_mpi_free( &U );
return( ret );
}
@@ -181,38 +180,54 @@ int mbedtls_dhm_read_params( mbedtls_dhm_context *ctx,
}
/*
* Setup and write the ServerKeyExchange parameters
* Pick a random R in the range [2, M-2] for blinding or key generation.
*/
int mbedtls_dhm_make_params( mbedtls_dhm_context *ctx, int x_size,
unsigned char *output, size_t *olen,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng )
static int dhm_random_below( mbedtls_mpi *R, const mbedtls_mpi *M,
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng )
{
int ret, count = 0;
size_t n1, n2, n3;
unsigned char *p;
DHM_VALIDATE_RET( ctx != NULL );
DHM_VALIDATE_RET( output != NULL );
DHM_VALIDATE_RET( olen != NULL );
DHM_VALIDATE_RET( f_rng != NULL );
int ret, count;
size_t m_size = mbedtls_mpi_size( M );
size_t m_bitlen = mbedtls_mpi_bitlen( M );
count = 0;
do
{
if( count++ > 30 )
return( MBEDTLS_ERR_MPI_NOT_ACCEPTABLE );
MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( R, m_size, f_rng, p_rng ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( R, ( m_size * 8 ) - m_bitlen ) );
}
while( dhm_check_range( R, M ) != 0 );
cleanup:
return( ret );
}
static int dhm_make_common( mbedtls_dhm_context *ctx, int x_size,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng )
{
int ret = 0;
if( mbedtls_mpi_cmp_int( &ctx->P, 0 ) == 0 )
return( MBEDTLS_ERR_DHM_BAD_INPUT_DATA );
if( x_size < 0 )
return( MBEDTLS_ERR_DHM_BAD_INPUT_DATA );
/*
* Generate X as large as possible ( < P )
*/
do
if( (unsigned) x_size < mbedtls_mpi_size( &ctx->P ) )
{
MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( &ctx->X, x_size, f_rng, p_rng ) );
while( mbedtls_mpi_cmp_mpi( &ctx->X, &ctx->P ) >= 0 )
MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &ctx->X, 1 ) );
if( count++ > 10 )
return( MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED );
}
while( dhm_check_range( &ctx->X, &ctx->P ) != 0 );
else
{
/* Generate X as large as possible ( <= P - 2 ) */
ret = dhm_random_below( &ctx->X, &ctx->P, f_rng, p_rng );
if( ret == MBEDTLS_ERR_MPI_NOT_ACCEPTABLE )
return( MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED );
if( ret != 0 )
return( ret );
}
/*
* Calculate GX = G^X mod P
@@ -223,8 +238,33 @@ int mbedtls_dhm_make_params( mbedtls_dhm_context *ctx, int x_size,
if( ( ret = dhm_check_range( &ctx->GX, &ctx->P ) ) != 0 )
return( ret );
cleanup:
return( ret );
}
/*
* Setup and write the ServerKeyExchange parameters
*/
int mbedtls_dhm_make_params( mbedtls_dhm_context *ctx, int x_size,
unsigned char *output, size_t *olen,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng )
{
int ret;
size_t n1, n2, n3;
unsigned char *p;
DHM_VALIDATE_RET( ctx != NULL );
DHM_VALIDATE_RET( output != NULL );
DHM_VALIDATE_RET( olen != NULL );
DHM_VALIDATE_RET( f_rng != NULL );
ret = dhm_make_common( ctx, x_size, f_rng, p_rng );
if( ret != 0 )
goto cleanup;
/*
* export P, G, GX
* Export P, G, GX. RFC 5246 §4.4 states that "leading zero octets are
* not required". We omit leading zeros for compactness.
*/
#define DHM_MPI_EXPORT( X, n ) \
do { \
@@ -250,11 +290,9 @@ int mbedtls_dhm_make_params( mbedtls_dhm_context *ctx, int x_size,
ctx->len = n1;
cleanup:
if( ret != 0 )
if( ret != 0 && ret > -128 )
return( MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED + ret );
return( 0 );
return( ret );
}
/*
@@ -306,7 +344,7 @@ int mbedtls_dhm_make_public( mbedtls_dhm_context *ctx, int x_size,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng )
{
int ret, count = 0;
int ret;
DHM_VALIDATE_RET( ctx != NULL );
DHM_VALIDATE_RET( output != NULL );
DHM_VALIDATE_RET( f_rng != NULL );
@@ -314,62 +352,18 @@ int mbedtls_dhm_make_public( mbedtls_dhm_context *ctx, int x_size,
if( olen < 1 || olen > ctx->len )
return( MBEDTLS_ERR_DHM_BAD_INPUT_DATA );
if( mbedtls_mpi_cmp_int( &ctx->P, 0 ) == 0 )
return( MBEDTLS_ERR_DHM_BAD_INPUT_DATA );
/*
* generate X and calculate GX = G^X mod P
*/
do
{
MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( &ctx->X, x_size, f_rng, p_rng ) );
while( mbedtls_mpi_cmp_mpi( &ctx->X, &ctx->P ) >= 0 )
MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &ctx->X, 1 ) );
if( count++ > 10 )
return( MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED );
}
while( dhm_check_range( &ctx->X, &ctx->P ) != 0 );
MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &ctx->GX, &ctx->G, &ctx->X,
&ctx->P , &ctx->RP ) );
if( ( ret = dhm_check_range( &ctx->GX, &ctx->P ) ) != 0 )
return( ret );
ret = dhm_make_common( ctx, x_size, f_rng, p_rng );
if( ret == MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED )
return( MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED );
if( ret != 0 )
goto cleanup;
MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( &ctx->GX, output, olen ) );
cleanup:
if( ret != 0 )
if( ret != 0 && ret > -128 )
return( MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED + ret );
return( 0 );
}
/*
* Pick a random R in the range [2, M) for blinding purposes
*/
static int dhm_random_below( mbedtls_mpi *R, const mbedtls_mpi *M,
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng )
{
int ret, count;
count = 0;
do
{
MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( R, mbedtls_mpi_size( M ), f_rng, p_rng ) );
while( mbedtls_mpi_cmp_mpi( R, M ) >= 0 )
MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( R, 1 ) );
if( count++ > 10 )
return( MBEDTLS_ERR_MPI_NOT_ACCEPTABLE );
}
while( mbedtls_mpi_cmp_int( R, 1 ) <= 0 );
cleanup:
return( ret );
}
@@ -420,7 +414,7 @@ static int dhm_update_blinding( mbedtls_dhm_context *ctx,
* We need to generate blinding values from scratch
*/
/* Vi = random( 2, P-1 ) */
/* Vi = random( 2, P-2 ) */
MBEDTLS_MPI_CHK( dhm_random_below( &ctx->Vi, &ctx->P, f_rng, p_rng ) );
/* Vf = Vi^-X mod P
@@ -484,8 +478,9 @@ int mbedtls_dhm_calc_secret( mbedtls_dhm_context *ctx,
MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &ctx->K, &ctx->K, &ctx->P ) );
}
/* Output the secret without any leading zero byte. This is mandatory
* for TLS per RFC 5246 §8.1.2. */
*olen = mbedtls_mpi_size( &ctx->K );
MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( &ctx->K, output, *olen ) );
cleanup:

View File

@@ -1738,18 +1738,17 @@ static int ecp_randomize_jac( const mbedtls_ecp_group *grp, mbedtls_ecp_point *p
/* Generate l such that 1 < l < p */
do
{
MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( &l, p_size, f_rng, p_rng ) );
while( mbedtls_mpi_cmp_mpi( &l, &grp->P ) >= 0 )
MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &l, 1 ) );
if( count++ > 10 )
if( count++ > 30 )
{
ret = MBEDTLS_ERR_ECP_RANDOM_FAILED;
goto cleanup;
}
MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( &l, p_size, f_rng, p_rng ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &l, ( p_size * 8 ) - grp->pbits ) );
}
while( mbedtls_mpi_cmp_int( &l, 1 ) <= 0 );
while( ( mbedtls_mpi_cmp_int( &l, 1 ) <= 0 ) ||
( mbedtls_mpi_cmp_mpi( &l, &grp->P ) >= 0 ) );
/* Z = l * Z */
MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &pt->Z, &pt->Z, &l ) ); MOD_MUL( pt->Z );
@@ -2514,18 +2513,17 @@ static int ecp_randomize_mxz( const mbedtls_ecp_group *grp, mbedtls_ecp_point *P
/* Generate l such that 1 < l < p */
do
{
MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( &l, p_size, f_rng, p_rng ) );
while( mbedtls_mpi_cmp_mpi( &l, &grp->P ) >= 0 )
MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &l, 1 ) );
if( count++ > 10 )
if( count++ > 30 )
{
ret = MBEDTLS_ERR_ECP_RANDOM_FAILED;
goto cleanup;
}
MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( &l, p_size, f_rng, p_rng ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &l, ( p_size * 8 ) - grp->pbits ) );
}
while( mbedtls_mpi_cmp_int( &l, 1 ) <= 0 );
while( ( mbedtls_mpi_cmp_int( &l, 1 ) <= 0 ) ||
( mbedtls_mpi_cmp_mpi( &l, &grp->P ) >= 0 ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &P->X, &P->X, &l ) ); MOD_MUL( P->X );
MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &P->Z, &P->Z, &l ) ); MOD_MUL( P->Z );

View File

@@ -1,26 +1,92 @@
Diffie-Hellman full exchange: tiny x_size
dhm_do_dhm:10:"93450983094850938450983409623":1:10:"9345098304850938450983409622":0
Diffie-Hellman parameter validation
dhm_invalid_params:
Diffie-Hellman full exchange #1
dhm_do_dhm:10:"23":10:"5":0
Diffie-Hellman full exchange: 5-bit, x_size=3
dhm_do_dhm:10:"23":3:10:"5":0
Diffie-Hellman full exchange #2
dhm_do_dhm:10:"93450983094850938450983409623":10:"9345098304850938450983409622":0
Diffie-Hellman full exchange: 5-bit, x_size=2
dhm_do_dhm:10:"23":2:10:"5":0
Diffie-Hellman full exchange #3
dhm_do_dhm:10:"93450983094850938450983409623982317398171298719873918739182739712938719287391879381271":10:"9345098309485093845098340962223981329819812792137312973297123912791271":0
## Repeat this test case and a few similar ones several times. The RNG state
## changes, so we get to exercise the code with a few different values.
Diffie-Hellman full exchange: 5-bit #1
dhm_do_dhm:10:"23":1:10:"5":0
Diffie-Hellman full exchange: 5-bit #2
dhm_do_dhm:10:"23":1:10:"5":0
Diffie-Hellman full exchange: 5-bit #3
dhm_do_dhm:10:"23":1:10:"5":0
Diffie-Hellman full exchange: 5-bit #4
dhm_do_dhm:10:"23":1:10:"5":0
Diffie-Hellman full exchange: 5-bit #5
dhm_do_dhm:10:"23":1:10:"5":0
## This is x_size = P_size + 1. Arguably x_size > P_size makes no sense,
## but it's the current undocumented behavior to treat it the same as when
## x_size = P_size. If this behavior changes in the future, change the expected
## return status from 0 to MBEDTLS_ERR_DHM_BAD_INPUT_DATA.
Diffie-Hellman full exchange: 97-bit, x_size=14
dhm_do_dhm:10:"93450983094850938450983409623":14:10:"9345098304850938450983409622":0
Diffie-Hellman full exchange: 97-bit #1
dhm_do_dhm:10:"93450983094850938450983409623":13:10:"9345098304850938450983409622":0
Diffie-Hellman full exchange: 97-bit #2
dhm_do_dhm:10:"93450983094850938450983409623":13:10:"9345098304850938450983409622":0
Diffie-Hellman full exchange: 97-bit #3
dhm_do_dhm:10:"93450983094850938450983409623":13:10:"9345098304850938450983409622":0
Diffie-Hellman full exchange: 97-bit #4
dhm_do_dhm:10:"93450983094850938450983409623":13:10:"9345098304850938450983409622":0
Diffie-Hellman full exchange: 97-bit #5
dhm_do_dhm:10:"93450983094850938450983409623":13:10:"9345098304850938450983409622":0
Diffie-Hellman full exchange: 97-bit, x_size=12
dhm_do_dhm:10:"93450983094850938450983409623":12:10:"9345098304850938450983409622":0
Diffie-Hellman full exchange: 97-bit, x_size=11
dhm_do_dhm:10:"93450983094850938450983409623":11:10:"9345098304850938450983409622":0
Diffie-Hellman full exchange: 97-bit, x_size=1 #1
dhm_do_dhm:10:"93450983094850938450983409623":1:10:"9345098304850938450983409622":0
Diffie-Hellman full exchange: 97-bit, x_size=1 #2
dhm_do_dhm:10:"93450983094850938450983409623":1:10:"9345098304850938450983409622":0
Diffie-Hellman full exchange: 97-bit, x_size=1 #3
dhm_do_dhm:10:"93450983094850938450983409623":1:10:"9345098304850938450983409622":0
Diffie-Hellman full exchange: 97-bit, x_size=1 #4
dhm_do_dhm:10:"93450983094850938450983409623":1:10:"9345098304850938450983409622":0
Diffie-Hellman full exchange: 97-bit, x_size=1 #5
dhm_do_dhm:10:"93450983094850938450983409623":1:10:"9345098304850938450983409622":0
Diffie-Hellman full exchange: 286-bit
dhm_do_dhm:10:"93450983094850938450983409623982317398171298719873918739182739712938719287391879381271":36:10:"9345098309485093845098340962223981329819812792137312973297123912791271":0
Diffie-Hellman trivial subgroup #1
dhm_do_dhm:10:"23":10:"1":MBEDTLS_ERR_DHM_BAD_INPUT_DATA
dhm_do_dhm:10:"23":1:10:"1":MBEDTLS_ERR_DHM_BAD_INPUT_DATA
Diffie-Hellman trivial subgroup #2
dhm_do_dhm:10:"23":10:"-1":MBEDTLS_ERR_DHM_BAD_INPUT_DATA
dhm_do_dhm:10:"23":1:10:"-1":MBEDTLS_ERR_DHM_BAD_INPUT_DATA
Diffie-Hellman small modulus
dhm_do_dhm:10:"3":10:"5":MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED
dhm_do_dhm:10:"3":1:10:"5":MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED
Diffie-Hellman zero modulus
dhm_do_dhm:10:"0":10:"5":MBEDTLS_ERR_DHM_BAD_INPUT_DATA
dhm_do_dhm:10:"0":1:10:"5":MBEDTLS_ERR_DHM_BAD_INPUT_DATA
Diffie-Hellman: x_size < 0
dhm_do_dhm:10:"93450983094850938450983409623":-1:10:"9345098304850938450983409622":MBEDTLS_ERR_DHM_BAD_INPUT_DATA
Diffie-Hellman MPI_MAX_SIZE modulus
dhm_make_public:MBEDTLS_MPI_MAX_SIZE:10:"5":0

View File

@@ -1,5 +1,66 @@
/* BEGIN_HEADER */
#include "mbedtls/dhm.h"
/* Sanity checks on a Diffie-Hellman parameter: check the length-value
* syntax and check that the value is the expected one (taken from the
* DHM context by the caller). */
static int check_dhm_param_output( const mbedtls_mpi *expected,
const unsigned char *buffer,
size_t size,
size_t *offset )
{
size_t n;
mbedtls_mpi actual;
int ok = 0;
mbedtls_mpi_init( &actual );
TEST_ASSERT( size >= *offset + 2 );
n = ( buffer[*offset] << 8 ) | buffer[*offset + 1];
*offset += 2;
/* The DHM param output from Mbed TLS has leading zeros stripped, as
* permitted but not required by RFC 5246 \S4.4. */
TEST_EQUAL( n, mbedtls_mpi_size( expected ) );
TEST_ASSERT( size >= *offset + n );
TEST_EQUAL( 0, mbedtls_mpi_read_binary( &actual, buffer + *offset, n ) );
TEST_EQUAL( 0, mbedtls_mpi_cmp_mpi( expected, &actual ) );
*offset += n;
ok = 1;
exit:
mbedtls_mpi_free( &actual );
return( ok );
}
/* Sanity checks on Diffie-Hellman parameters: syntax, range, and comparison
* against the context. */
static int check_dhm_params( const mbedtls_dhm_context *ctx,
size_t x_size,
const unsigned char *ske, size_t ske_len )
{
size_t offset = 0;
/* Check that ctx->X and ctx->GX are within range. */
TEST_ASSERT( mbedtls_mpi_cmp_int( &ctx->X, 1 ) > 0 );
TEST_ASSERT( mbedtls_mpi_cmp_mpi( &ctx->X, &ctx->P ) < 0 );
TEST_ASSERT( mbedtls_mpi_size( &ctx->X ) <= x_size );
TEST_ASSERT( mbedtls_mpi_cmp_int( &ctx->GX, 1 ) > 0 );
TEST_ASSERT( mbedtls_mpi_cmp_mpi( &ctx->GX, &ctx->P ) < 0 );
/* Check ske: it must contain P, G and G^X, each prefixed with a
* 2-byte size. */
if( !check_dhm_param_output( &ctx->P, ske, ske_len, &offset ) )
goto exit;
if( !check_dhm_param_output( &ctx->G, ske, ske_len, &offset ) )
goto exit;
if( !check_dhm_param_output( &ctx->GX, ske, ske_len, &offset ) )
goto exit;
TEST_EQUAL( offset, ske_len );
return( 1 );
exit:
return( 0 );
}
/* END_HEADER */
/* BEGIN_DEPENDENCIES
@@ -115,7 +176,7 @@ exit:
/* END_CASE */
/* BEGIN_CASE */
void dhm_do_dhm( int radix_P, char *input_P,
void dhm_do_dhm( int radix_P, char *input_P, int x_size,
int radix_G, char *input_G, int result )
{
mbedtls_dhm_context ctx_srv;
@@ -129,7 +190,7 @@ void dhm_do_dhm( int radix_P, char *input_P,
size_t pub_cli_len = 0;
size_t sec_srv_len;
size_t sec_cli_len;
int x_size, i;
int i;
rnd_pseudo_info rnd_info;
mbedtls_dhm_init( &ctx_srv );
@@ -145,15 +206,18 @@ void dhm_do_dhm( int radix_P, char *input_P,
*/
TEST_ASSERT( mbedtls_mpi_read_string( &ctx_srv.P, radix_P, input_P ) == 0 );
TEST_ASSERT( mbedtls_mpi_read_string( &ctx_srv.G, radix_G, input_G ) == 0 );
x_size = mbedtls_mpi_size( &ctx_srv.P );
pub_cli_len = x_size;
pub_cli_len = mbedtls_mpi_size( &ctx_srv.P );
/*
* First key exchange
*/
TEST_ASSERT( mbedtls_dhm_make_params( &ctx_srv, x_size, ske, &ske_len, &rnd_pseudo_rand, &rnd_info ) == result );
TEST_ASSERT( mbedtls_dhm_make_params( &ctx_srv, x_size, ske, &ske_len,
&rnd_pseudo_rand,
&rnd_info ) == result );
if ( result != 0 )
goto exit;
if( !check_dhm_params( &ctx_srv, x_size, ske, ske_len ) )
goto exit;
ske[ske_len++] = 0;
ske[ske_len++] = 0;
@@ -185,7 +249,11 @@ void dhm_do_dhm( int radix_P, char *input_P,
*/
p = ske;
TEST_ASSERT( mbedtls_dhm_make_params( &ctx_srv, x_size, ske, &ske_len, &rnd_pseudo_rand, &rnd_info ) == 0 );
TEST_ASSERT( mbedtls_dhm_make_params( &ctx_srv, x_size, ske, &ske_len,
&rnd_pseudo_rand,
&rnd_info ) == 0 );
if( !check_dhm_params( &ctx_srv, x_size, ske, ske_len ) )
goto exit;
ske[ske_len++] = 0;
ske[ske_len++] = 0;
TEST_ASSERT( mbedtls_dhm_read_params( &ctx_cli, &p, ske + ske_len ) == 0 );