Fix non-constant-time comparison in mbedtls_mpi_random

Calling mbedtls_mpi_cmp_int reveals the number of leading zero limbs
to an adversary who is capable of very fine-grained timing
measurements. This is very little information, but could be practical
with secp521r1 (1/512 chance of the leading limb being 0) if the
adversary can measure the precise timing of a large number of
signature operations.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
This commit is contained in:
Gilles Peskine 2021-04-13 21:09:10 +02:00
parent 0c1a42a147
commit 5b0589e9ab
2 changed files with 17 additions and 3 deletions

View file

@ -0,0 +1,7 @@
Security
* Fix a potential side channel vulnerability in ECDSA ephemeral key generation.
An adversary who is capable of very precise timing measurements could
learn partial information about the leading bits of the nonce used for the
signature, allowing the recovery of the private key after observing a
large number of signature operations. This completes a partial fix in
Mbed TLS 2.20.0.

View file

@ -2466,9 +2466,10 @@ int mbedtls_mpi_random( mbedtls_mpi *X,
{ {
int ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; int ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
int count; int count;
unsigned cmp = 0; unsigned lt_lower = 1, lt_upper = 0;
size_t n_bits = mbedtls_mpi_bitlen( N ); size_t n_bits = mbedtls_mpi_bitlen( N );
size_t n_bytes = ( n_bits + 7 ) / 8; size_t n_bytes = ( n_bits + 7 ) / 8;
mbedtls_mpi lower_bound;
if( min < 0 ) if( min < 0 )
return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
@ -2494,10 +2495,14 @@ int mbedtls_mpi_random( mbedtls_mpi *X,
*/ */
count = ( n_bytes > 4 ? 30 : 250 ); count = ( n_bytes > 4 ? 30 : 250 );
mbedtls_mpi_init( &lower_bound );
/* Ensure that target MPI has exactly the same number of limbs /* Ensure that target MPI has exactly the same number of limbs
* as the upper bound, even if the upper bound has leading zeros. * as the upper bound, even if the upper bound has leading zeros.
* This is necessary for the mbedtls_mpi_lt_mpi_ct() check. */ * This is necessary for the mbedtls_mpi_lt_mpi_ct() check. */
MBEDTLS_MPI_CHK( mbedtls_mpi_resize_clear( X, N->n ) ); MBEDTLS_MPI_CHK( mbedtls_mpi_resize_clear( X, N->n ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &lower_bound, N->n ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &lower_bound, min ) );
/* /*
* Match the procedure given in RFC 6979 §3.3 (deterministic ECDSA) * Match the procedure given in RFC 6979 §3.3 (deterministic ECDSA)
@ -2518,11 +2523,13 @@ int mbedtls_mpi_random( mbedtls_mpi *X,
goto cleanup; goto cleanup;
} }
MBEDTLS_MPI_CHK( mbedtls_mpi_lt_mpi_ct( X, N, &cmp ) ); MBEDTLS_MPI_CHK( mbedtls_mpi_lt_mpi_ct( X, &lower_bound, &lt_lower ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_lt_mpi_ct( X, N, &lt_upper ) );
} }
while( mbedtls_mpi_cmp_int( X, min ) < 0 || cmp != 1 ); while( lt_lower != 0 || lt_upper == 0 );
cleanup: cleanup:
mbedtls_mpi_free( &lower_bound );
return( ret ); return( ret );
} }