Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dhm: Fix bias in private key generation #4628

Merged
merged 13 commits into from
Jun 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ChangeLog.d/random-range.txt
Original file line number Diff line number Diff line change
@@ -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.
165 changes: 80 additions & 85 deletions library/dhm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
}

Expand Down Expand Up @@ -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 )
}
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 );
}
while( dhm_check_range( &ctx->X, &ctx->P ) != 0 );

/*
* Calculate GX = G^X mod P
Expand All @@ -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 { \
Expand All @@ -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 );
}

/*
Expand Down Expand Up @@ -306,70 +344,26 @@ 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 );

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 );
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
26 changes: 12 additions & 14 deletions library/ecp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down Expand Up @@ -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 );
Expand Down
Loading