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

Backport 2.x: Fix non-uniform random generation in a range #4276

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
48f052f
mbedtls_ecp_gen_privkey: minor refactoring
gilles-peskine-arm Mar 23, 2021
de33213
mbedtls_ecp_gen_privkey: create subfunctions for each curve type
gilles-peskine-arm Mar 23, 2021
3838f28
mbedtls_ecp_gen_privkey_mx: rename n_bits to high_bit
gilles-peskine-arm Mar 24, 2021
ebf3a4b
Update references in some test function documentation
gilles-peskine-arm Mar 23, 2021
bef3019
Make the fallback behavior of mbedtls_test_rnd_buffer_rand optional
gilles-peskine-arm Mar 23, 2021
1888285
Add unit tests for mbedtls_ecp_gen_privkey_mx
gilles-peskine-arm Mar 24, 2021
6acfc9c
mbedtls_ecp_gen_privkey_mx: remove the exception for all-zero
gilles-peskine-arm Mar 24, 2021
4f77674
mbedtls_ecp_gen_privkey_mx: make bit manipulations unconditional
gilles-peskine-arm Mar 24, 2021
eadf31d
mbedtls_ecp_gen_privkey_mx: simplify the size calculation logic
gilles-peskine-arm Mar 24, 2021
6373fab
mbedtls_ecp_gen_privkey_sw: range and coverage tests
gilles-peskine-arm Mar 29, 2021
7967ec5
mbedtls_ecp_gen_privkey_sw: generalize to mbedtls_mpi_random
gilles-peskine-arm Mar 29, 2021
4699fa4
Move mbedtls_mpi_random to the bignum module
gilles-peskine-arm Mar 29, 2021
9312ba5
mbedtls_mpi_random: check for invalid arguments
gilles-peskine-arm Mar 29, 2021
cba4b35
Changelog entry for adding mbedtls_mpi_random()
gilles-peskine-arm Mar 31, 2021
aeab0fb
Preserve MBEDTLS_ERR_ECP_RANDOM_FAILED in case of a hostile RNG
gilles-peskine-arm Mar 29, 2021
6466d34
ECP: use mbedtls_mpi_random for blinding
gilles-peskine-arm Mar 29, 2021
dc0b6e4
Test range and format of dhm_make_params output
gilles-peskine-arm Mar 30, 2021
a2ce04e
Repeat a few DH tests
gilles-peskine-arm Mar 30, 2021
33ec863
Test mbedtls_dhm_make_params with different x_size
gilles-peskine-arm Mar 30, 2021
0853bb2
DHM refactoring: unify mbedtls_dhm_make_{params,public}
gilles-peskine-arm Mar 31, 2021
b4e815f
DHM blinding: don't accept P-1 as a blinding value
gilles-peskine-arm Mar 31, 2021
87fdb1f
DHM refactoring: use dhm_random_below in dhm_make_common
gilles-peskine-arm Mar 31, 2021
58df4c9
dhm_check_range: microoptimization
gilles-peskine-arm Mar 31, 2021
16e3668
DHM: use mbedtls_mpi_random for blinding and key generation
gilles-peskine-arm Mar 31, 2021
be4b5dd
Add changelog entry for non-uniform MPI random generation
gilles-peskine-arm Mar 31, 2021
8f45470
Fix mbedtls_mpi_random when N has leading zeros
gilles-peskine-arm Apr 1, 2021
f467e1a
MPI random: add unit tests with a previously nonzero value
gilles-peskine-arm Apr 1, 2021
3b05615
Better document and slightly simplify >>2^n heuristic
gilles-peskine-arm Apr 13, 2021
ef13251
Contextualize comment about mbedtls_mpi_random retries
gilles-peskine-arm Apr 13, 2021
b66cc7d
Fix copypasta in test case description
gilles-peskine-arm Apr 13, 2021
8190d31
MPI random test: Add a few more small-range tests
gilles-peskine-arm Apr 13, 2021
c520d7a
MPI random test: fix small-range test stats check when min > 1
gilles-peskine-arm Apr 13, 2021
38de7ee
MPI random test: Add test cases with lower_bound > upper_bound
gilles-peskine-arm Apr 13, 2021
e39ee8e
MPI random test: use more iterations for small numbers
gilles-peskine-arm Apr 13, 2021
33701a6
mbedtls_mpi_random: document MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
gilles-peskine-arm Apr 13, 2021
3d60ece
Note that the "0 limb in ..." tests rely on undocumented behavior
gilles-peskine-arm Apr 13, 2021
a16001e
mpi_fill_random_internal: remove spurious grow() call
gilles-peskine-arm Apr 13, 2021
104eb82
DHM: add notes about leading zeros
gilles-peskine-arm Apr 13, 2021
9e96679
DHM tests: add some explanations
gilles-peskine-arm Apr 13, 2021
346d20d
DHM: add test case with x_size < 0
gilles-peskine-arm Apr 13, 2021
3f61363
Correct some comments about ECC in mbedtls_mpi_random
gilles-peskine-arm Apr 15, 2021
b72b7e6
Fix long-standing obsolete comment
gilles-peskine-arm Jun 2, 2021
1177907
Use ternary operator with the most common case first
gilles-peskine-arm Jun 2, 2021
f37b9f7
Fix mistakes in test case descriptions
gilles-peskine-arm Jun 2, 2021
e4f937f
Lift function call out of inner loop
gilles-peskine-arm Jun 2, 2021
3130ce2
New internal function mbedtls_mpi_resize_clear
gilles-peskine-arm Jun 2, 2021
c0b68bf
Use MBEDTLS_MPI_CHK where warranted
gilles-peskine-arm Jun 3, 2021
23422e4
Note that the byte order in mpi_fill_random_internal() is deliberate
gilles-peskine-arm Jun 3, 2021
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
3 changes: 3 additions & 0 deletions ChangeLog.d/mpi_random.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Features
* The new function mbedtls_mpi_random() generates a random value in a
given range uniformly.
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.
38 changes: 38 additions & 0 deletions include/mbedtls/bignum.h
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,44 @@ int mbedtls_mpi_fill_random( mbedtls_mpi *X, size_t size,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng );

/** Generate a random number uniformly in a range.
*
* This function generates a random number between \p min inclusive and
* \p N exclusive.
*
* The procedure complies with RFC 6979 §3.3 (deterministic ECDSA)
* when the RNG is a suitably parametrized instance of HMAC_DRBG
* and \p min is \c 1.
*
* \note There are `N - min` possible outputs. The lower bound
* \p min can be reached, but the upper bound \p N cannot.
*
* \param X The destination MPI. This must point to an initialized MPI.
* \param min The minimum value to return.
* It must be nonnegative.
* \param N The upper bound of the range, exclusive.
* In other words, this is one plus the maximum value to return.
* \p N must be strictly larger than \p min.
* \param f_rng The RNG function to use. This must not be \c NULL.
* \param p_rng The RNG parameter to be passed to \p f_rng.
*
* \return \c 0 if successful.
* \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed.
* \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p min or \p N is invalid
* or if they are incompatible.
* \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if the implementation was
* unable to find a suitable value within a limited number
* of attempts. This has a negligible probability if \p N
* is significantly larger than \p min, which is the case
* for all usual cryptographic applications.
* \return Another negative error code on failure.
*/
int mbedtls_mpi_random( mbedtls_mpi *X,
mbedtls_mpi_sint min,
const mbedtls_mpi *N,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng );

/**
* \brief Compute the greatest common divisor: G = gcd(A, B)
*
Expand Down
150 changes: 122 additions & 28 deletions library/bignum.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,27 @@ int mbedtls_mpi_shrink( mbedtls_mpi *X, size_t nblimbs )
return( 0 );
}

/* Resize X to have exactly n limbs and set it to 0. */
static int mbedtls_mpi_resize_clear( mbedtls_mpi *X, size_t limbs )
{
if( limbs == 0 )
{
mbedtls_mpi_free( X );
return( 0 );
}
else if( X->n == limbs )
{
memset( X->p, 0, limbs * ciL );
X->s = 1;
return( 0 );
}
else
{
mbedtls_mpi_free( X );
return( mbedtls_mpi_grow( X, limbs ) );
}
}

/*
* Copy the contents of Y into X
*/
Expand Down Expand Up @@ -838,14 +859,7 @@ int mbedtls_mpi_read_binary_le( mbedtls_mpi *X,
size_t const limbs = CHARS_TO_LIMBS( buflen );

/* Ensure that target MPI has exactly the necessary number of limbs */
if( X->n != limbs )
{
mbedtls_mpi_free( X );
mbedtls_mpi_init( X );
MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, limbs ) );
}

MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_resize_clear( X, limbs ) );

for( i = 0; i < buflen; i++ )
X->p[i / ciL] |= ((mbedtls_mpi_uint) buf[i]) << ((i % ciL) << 3);
Expand Down Expand Up @@ -874,17 +888,11 @@ int mbedtls_mpi_read_binary( mbedtls_mpi *X, const unsigned char *buf, size_t bu
MPI_VALIDATE_RET( buflen == 0 || buf != NULL );

/* Ensure that target MPI has exactly the necessary number of limbs */
if( X->n != limbs )
{
mbedtls_mpi_free( X );
mbedtls_mpi_init( X );
MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, limbs ) );
}
MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_resize_clear( X, limbs ) );

/* Avoid calling `memcpy` with NULL source argument,
/* Avoid calling `memcpy` with NULL source or destination argument,
* even if buflen is 0. */
if( buf != NULL )
if( buflen != 0 )
{
Xp = (unsigned char*) X->p;
memcpy( Xp + overhead, buf, buflen );
Expand Down Expand Up @@ -2395,6 +2403,33 @@ int mbedtls_mpi_gcd( mbedtls_mpi *G, const mbedtls_mpi *A, const mbedtls_mpi *B
return( ret );
}

/* Fill X with n_bytes random bytes.
* X must already have room for those bytes.
* The ordering of the bytes returned from the RNG is suitable for
* deterministic ECDSA (see RFC 6979 §3.3 and mbedtls_mpi_random()).
* The size and sign of X are unchanged.
* n_bytes must not be 0.
*/
static int mpi_fill_random_internal(
mbedtls_mpi *X, size_t n_bytes,
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng )
{
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
const size_t limbs = CHARS_TO_LIMBS( n_bytes );
const size_t overhead = ( limbs * ciL ) - n_bytes;

if( X->n < limbs )
return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );

memset( X->p, 0, overhead );
memset( (unsigned char *) X->p + limbs * ciL, 0, ( X->n - limbs ) * ciL );
MBEDTLS_MPI_CHK( f_rng( p_rng, (unsigned char *) X->p + overhead, n_bytes ) );
mpi_bigendian_to_host( X->p, limbs );

cleanup:
return( ret );
}

/*
* Fill X with size bytes of random.
*
Expand All @@ -2408,25 +2443,84 @@ int mbedtls_mpi_fill_random( mbedtls_mpi *X, size_t size,
{
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
size_t const limbs = CHARS_TO_LIMBS( size );
size_t const overhead = ( limbs * ciL ) - size;
unsigned char *Xp;

MPI_VALIDATE_RET( X != NULL );
MPI_VALIDATE_RET( f_rng != NULL );

/* Ensure that target MPI has exactly the necessary number of limbs */
if( X->n != limbs )
MBEDTLS_MPI_CHK( mbedtls_mpi_resize_clear( X, limbs ) );
if( size == 0 )
return( 0 );

ret = mpi_fill_random_internal( X, size, f_rng, p_rng );

cleanup:
return( ret );
}

int mbedtls_mpi_random( mbedtls_mpi *X,
mbedtls_mpi_sint min,
const mbedtls_mpi *N,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng )
{
int ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
int count;
unsigned cmp = 0;
size_t n_bits = mbedtls_mpi_bitlen( N );
size_t n_bytes = ( n_bits + 7 ) / 8;

if( min < 0 )
return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
if( mbedtls_mpi_cmp_int( N, min ) <= 0 )
return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );

/*
* When min == 0, each try has at worst a probability 1/2 of failing
* (the msb has a probability 1/2 of being 0, and then the result will
* be < N), so after 30 tries failure probability is a most 2**(-30).
*
* When N is just below a power of 2, as is the case when generating
* a random scalar on most elliptic curves, 1 try is enough with
* overwhelming probability. When N is just above a power of 2,
* as when generating a random scalar on secp224k1, each try has
* a probability of failing that is almost 1/2.
*
* The probabilities are almost the same if min is nonzero but negligible
* compared to N. This is always the case when N is crypto-sized, but
* it's convenient to support small N for testing purposes. When N
* is small, use a higher repeat count, otherwise the probability of
* failure is macroscopic.
*/
count = ( n_bytes > 4 ? 30 : 250 );

/* Ensure that target MPI has exactly the same number of limbs
* as the upper bound, even if the upper bound has leading zeros.
* This is necessary for the mbedtls_mpi_lt_mpi_ct() check. */
MBEDTLS_MPI_CHK( mbedtls_mpi_resize_clear( X, N->n ) );

/*
* Match the procedure given in RFC 6979 §3.3 (deterministic ECDSA)
* when f_rng is a suitably parametrized instance of HMAC_DRBG:
* - use the same byte ordering;
* - keep the leftmost n_bits bits of the generated octet string;
* - try until result is in the desired range.
* This also avoids any bias, which is especially important for ECDSA.
*/
do
{
mbedtls_mpi_free( X );
mbedtls_mpi_init( X );
MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, limbs ) );
}
MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) );
MBEDTLS_MPI_CHK( mpi_fill_random_internal( X, n_bytes, f_rng, p_rng ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( X, 8 * n_bytes - n_bits ) );

Xp = (unsigned char*) X->p;
MBEDTLS_MPI_CHK( f_rng( p_rng, Xp + overhead, size ) );
if( --count == 0 )
{
ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE;
goto cleanup;
}

mpi_bigendian_to_host( X->p, limbs );
MBEDTLS_MPI_CHK( mbedtls_mpi_lt_mpi_ct( X, N, &cmp ) );
}
while( mbedtls_mpi_cmp_int( X, min ) < 0 || cmp != 1 );

cleanup:
return( ret );
Expand Down
Loading