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

Fix non-uniform random generation in a range #4611

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
cce0601
mbedtls_ecp_gen_privkey: minor refactoring
gilles-peskine-arm Mar 23, 2021
72fcc98
mbedtls_ecp_gen_privkey: create subfunctions for each curve type
gilles-peskine-arm Mar 23, 2021
55c4604
mbedtls_ecp_gen_privkey_mx: rename n_bits to high_bit
gilles-peskine-arm Mar 24, 2021
0b1b0ab
Update references in some test function documentation
gilles-peskine-arm Mar 23, 2021
ecacc3c
Make the fallback behavior of mbedtls_test_rnd_buffer_rand optional
gilles-peskine-arm Mar 23, 2021
6ff8a01
Add unit tests for mbedtls_ecp_gen_privkey_mx
gilles-peskine-arm Mar 24, 2021
96449ce
mbedtls_ecp_gen_privkey_mx: remove the exception for all-zero
gilles-peskine-arm Mar 24, 2021
67986d0
mbedtls_ecp_gen_privkey_mx: make bit manipulations unconditional
gilles-peskine-arm Mar 24, 2021
61f1f5f
mbedtls_ecp_gen_privkey_mx: simplify the size calculation logic
gilles-peskine-arm Mar 24, 2021
7888073
mbedtls_ecp_gen_privkey_sw: range and coverage tests
gilles-peskine-arm Mar 29, 2021
8cfffb3
mbedtls_ecp_gen_privkey_sw: generalize to mbedtls_mpi_random
gilles-peskine-arm Mar 29, 2021
02ac93a
Move mbedtls_mpi_random to the bignum module
gilles-peskine-arm Mar 29, 2021
1e918f4
mbedtls_mpi_random: check for invalid arguments
gilles-peskine-arm Mar 29, 2021
fdc58c1
Changelog entry for adding mbedtls_mpi_random()
gilles-peskine-arm Mar 31, 2021
60d8b98
Preserve MBEDTLS_ERR_ECP_RANDOM_FAILED in case of a hostile RNG
gilles-peskine-arm Mar 29, 2021
5921517
ECP: use mbedtls_mpi_random for blinding
gilles-peskine-arm Mar 29, 2021
02db8f4
Test range and format of dhm_make_params output
gilles-peskine-arm Mar 30, 2021
b27db0a
Repeat a few DH tests
gilles-peskine-arm Mar 30, 2021
2baf2b0
Test mbedtls_dhm_make_params with different x_size
gilles-peskine-arm Mar 30, 2021
cb660f2
DHM refactoring: unify mbedtls_dhm_make_{params,public}
gilles-peskine-arm Mar 31, 2021
7b2b66e
DHM blinding: don't accept P-1 as a blinding value
gilles-peskine-arm Mar 31, 2021
17f1a26
DHM refactoring: use dhm_random_below in dhm_make_common
gilles-peskine-arm Mar 31, 2021
8e38acc
dhm_check_range: microoptimization
gilles-peskine-arm Mar 31, 2021
da7ee01
DHM: use mbedtls_mpi_random for blinding and key generation
gilles-peskine-arm Mar 31, 2021
9367f4b
Add changelog entry for non-uniform MPI random generation
gilles-peskine-arm Mar 31, 2021
1a7df4e
Fix mbedtls_mpi_random when N has leading zeros
gilles-peskine-arm Apr 1, 2021
422e867
MPI random: add unit tests with a previously nonzero value
gilles-peskine-arm Apr 1, 2021
eedefa5
Better document and slightly simplify >>2^n heuristic
gilles-peskine-arm Apr 13, 2021
ee966c4
Contextualize comment about mbedtls_mpi_random retries
gilles-peskine-arm Apr 13, 2021
fbb9009
Fix copypasta in test case description
gilles-peskine-arm Apr 13, 2021
951b569
MPI random test: Add a few more small-range tests
gilles-peskine-arm Apr 13, 2021
d463edf
MPI random test: fix small-range test stats check when min > 1
gilles-peskine-arm Apr 13, 2021
0ad640a
MPI random test: Add test cases with lower_bound > upper_bound
gilles-peskine-arm Apr 13, 2021
e538168
MPI random test: use more iterations for small numbers
gilles-peskine-arm Apr 13, 2021
7ed7c5a
mbedtls_mpi_random: document MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
gilles-peskine-arm Apr 13, 2021
0cb493d
Note that the "0 limb in ..." tests rely on undocumented behavior
gilles-peskine-arm Apr 13, 2021
ebe9b6a
mpi_fill_random_internal: remove spurious grow() call
gilles-peskine-arm Apr 13, 2021
03299dc
DHM: add notes about leading zeros
gilles-peskine-arm Apr 13, 2021
19e3620
DHM tests: add some explanations
gilles-peskine-arm Apr 13, 2021
3270b14
DHM: add test case with x_size < 0
gilles-peskine-arm Apr 13, 2021
e842e58
Correct some comments about ECC in mbedtls_mpi_random
gilles-peskine-arm Apr 15, 2021
c7eeeb1
Fix long-standing obsolete comment
gilles-peskine-arm Jun 2, 2021
87823d7
Use ternary operator with the most common case first
gilles-peskine-arm Jun 2, 2021
9077e43
Fix mistakes in test case descriptions
gilles-peskine-arm Jun 2, 2021
ceefe5d
Lift function call out of inner loop
gilles-peskine-arm Jun 2, 2021
ed32b57
New internal function mbedtls_mpi_resize_clear
gilles-peskine-arm Jun 2, 2021
405b091
Use MBEDTLS_MPI_CHK where warranted
gilles-peskine-arm Jun 3, 2021
afb2bd2
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