From 6f949ea67b20c1ca63813345a1d70191777d15d5 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 20 Sep 2022 18:38:35 +0200 Subject: [PATCH 01/14] New constant-flow function mbedtls_mpi_core_uint_le_mpi Compare a single-limb MPI with a multi-limb MPI. This is rather ad hoc, but will be useful for mbedtls_mpi_core_random. Signed-off-by: Gilles Peskine --- library/bignum_core.c | 21 +++++++ library/bignum_core.h | 16 +++++ tests/suites/test_suite_bignum_core.function | 50 +++++++++++++++ tests/suites/test_suite_bignum_core.misc.data | 63 +++++++++++++++++++ 4 files changed, 150 insertions(+) diff --git a/library/bignum_core.c b/library/bignum_core.c index 663535107c..08158fa78d 100644 --- a/library/bignum_core.c +++ b/library/bignum_core.c @@ -154,6 +154,27 @@ void mbedtls_mpi_core_bigendian_to_host( mbedtls_mpi_uint *A, } } +/* Whether min <= A, in constant time. + * A_limbs must be at least 1. */ +unsigned mbedtls_mpi_core_uint_le_mpi( mbedtls_mpi_uint min, + const mbedtls_mpi_uint *A, + size_t A_limbs ) +{ + /* min <= least significant limb? */ + unsigned min_le_lsl = 1 ^ mbedtls_ct_mpi_uint_lt( A[0], min ); + + /* most significant limbs (excluding 1) are all zero? */ + mbedtls_mpi_uint msll_mask = 0; + for( size_t i = 1; i < A_limbs; i++ ) + msll_mask |= A[i]; + /* The most significant limbs of A are not all zero iff msll_mask != 0. */ + unsigned msll_nonzero = mbedtls_ct_mpi_uint_mask( msll_mask ) & 1; + + /* min <= A iff the lowest limb of A is >= min or the other limbs + * are not all zero. */ + return( min_le_lsl | msll_nonzero ); +} + void mbedtls_mpi_core_cond_assign( mbedtls_mpi_uint *X, const mbedtls_mpi_uint *A, size_t limbs, diff --git a/library/bignum_core.h b/library/bignum_core.h index 24559c6e97..120fa184c6 100644 --- a/library/bignum_core.h +++ b/library/bignum_core.h @@ -129,6 +129,22 @@ size_t mbedtls_mpi_core_bitlen( const mbedtls_mpi_uint *A, size_t A_limbs ); void mbedtls_mpi_core_bigendian_to_host( mbedtls_mpi_uint *A, size_t A_limbs ); +/** \brief Compare a machine integer with an MPI. + * + * This function operates in constant time with respect + * to the values of \p min and \p A. + * + * \param min A machine integer. + * \param[in] A An MPI. + * \param A_limbs The number of limbs of \p A. + * This must be at least 1. + * + * \return 1 if \p min is less than or equal to \p A, otherwise 0. + */ +unsigned mbedtls_mpi_core_uint_le_mpi( mbedtls_mpi_uint min, + const mbedtls_mpi_uint *A, + size_t A_limbs ); + /** * \brief Perform a safe conditional copy of an MPI which doesn't reveal * whether assignment was done or not. diff --git a/tests/suites/test_suite_bignum_core.function b/tests/suites/test_suite_bignum_core.function index 078239fdc1..d842c496c2 100644 --- a/tests/suites/test_suite_bignum_core.function +++ b/tests/suites/test_suite_bignum_core.function @@ -344,6 +344,56 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void mpi_core_uint_le_mpi( char *input_A ) +{ + mbedtls_mpi_uint *A = NULL; + size_t A_limbs = 0; + + TEST_EQUAL( mbedtls_test_read_mpi_core( &A, &A_limbs, input_A ), 0 ); + + int is_large = 0; /* nonzero limbs beyond the lowest-order one? */ + for( size_t i = 1; i < A_limbs; i++ ) + { + if( A[i] != 0 ) + { + is_large = 1; + break; + } + } + + TEST_CF_SECRET( A, A_limbs * sizeof( *A ) ); + + TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( 0, A, A_limbs ), 1 ); + TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( A[0], A, A_limbs ), 1 ); + + if( is_large ) + { + TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( A[0] + 1, + A, A_limbs ), 1 ); + TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( (mbedtls_mpi_uint)( -1 ) >> 1, + A, A_limbs ), 1 ); + TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( (mbedtls_mpi_uint)( -1 ), + A, A_limbs ), 1 ); + } + else + { + TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( A[0] + 1, + A, A_limbs ), + A[0] + 1 <= A[0] ); + TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( (mbedtls_mpi_uint)( -1 ) >> 1, + A, A_limbs ), + (mbedtls_mpi_uint)( -1 ) >> 1 <= A[0] ); + TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( (mbedtls_mpi_uint)( -1 ), + A, A_limbs ), + (mbedtls_mpi_uint)( -1 ) <= A[0] ); + } + +exit: + mbedtls_free( A ); +} +/* END_CASE */ + /* BEGIN_CASE */ void mpi_core_cond_assign( char * input_X, char * input_Y, diff --git a/tests/suites/test_suite_bignum_core.misc.data b/tests/suites/test_suite_bignum_core.misc.data index 62480e47f7..81a767a0c4 100644 --- a/tests/suites/test_suite_bignum_core.misc.data +++ b/tests/suites/test_suite_bignum_core.misc.data @@ -242,6 +242,69 @@ mpi_core_lt_ct:"11FFFFFFFFFFFFFFFF":"FF1111111111111111":1 mbedtls_mpi_core_lt_ct: x>y (alternating limbs) mpi_core_lt_ct:"FF1111111111111111":"11FFFFFFFFFFFFFFFF":0 +Test mbedtls_mpi_core_uint_le_mpi: 0 (1 limb) +mpi_core_uint_le_mpi:"00" + +Test mbedtls_mpi_core_uint_le_mpi: 0 (>=2 limbs) +mpi_core_uint_le_mpi:"000000000000000000" + +Test mbedtls_mpi_core_uint_le_mpi: 1 (1 limb) +mpi_core_uint_le_mpi:"01" + +Test mbedtls_mpi_core_uint_le_mpi: 1 (>=2 limbs) +mpi_core_uint_le_mpi:"000000000000000001" + +Test mbedtls_mpi_core_uint_le_mpi: 42 (1 limb) +mpi_core_uint_le_mpi:"2a" + +Test mbedtls_mpi_core_uint_le_mpi: 42 (>=2 limbs) +mpi_core_uint_le_mpi:"000000000000000042" + +Test mbedtls_mpi_core_uint_le_mpi: 2^31-1 +mpi_core_uint_le_mpi:"7fffffff" + +Test mbedtls_mpi_core_uint_le_mpi: 2^31-1 with leading zero limb +mpi_core_uint_le_mpi:"00000000007fffffff" + +Test mbedtls_mpi_core_uint_le_mpi: 2^32-1 +mpi_core_uint_le_mpi:"ffffffff" + +Test mbedtls_mpi_core_uint_le_mpi: 2^32-1 with leading zero limb +mpi_core_uint_le_mpi:"0000000000ffffffff" + +Test mbedtls_mpi_core_uint_le_mpi: 2^32 +mpi_core_uint_le_mpi:"10000000" + +Test mbedtls_mpi_core_uint_le_mpi: 2^32 with leading zero limb +mpi_core_uint_le_mpi:"000000000010000000" + +Test mbedtls_mpi_core_uint_le_mpi: 2^32+1 +mpi_core_uint_le_mpi:"10000001" + +Test mbedtls_mpi_core_uint_le_mpi: 2^32+1 with leading zero limb +mpi_core_uint_le_mpi:"000000000010000001" + +Test mbedtls_mpi_core_uint_le_mpi: 2^63-1 +mpi_core_uint_le_mpi:"7fffffffffffffff" + +Test mbedtls_mpi_core_uint_le_mpi: 2^63-1 with leading zero limb +mpi_core_uint_le_mpi:"007fffffffffffffff" + +Test mbedtls_mpi_core_uint_le_mpi: 2^64-1 +mpi_core_uint_le_mpi:"ffffffffffffffff" + +Test mbedtls_mpi_core_uint_le_mpi: 2^64-1 with leading zero limb +mpi_core_uint_le_mpi:"00ffffffffffffffff" + +Test mbedtls_mpi_core_uint_le_mpi: 2^64 +mpi_core_uint_le_mpi:"010000000000000000" + +Test mbedtls_mpi_core_uint_le_mpi: 2^64+1 +mpi_core_uint_le_mpi:"010000000000000001" + +Test mbedtls_mpi_core_uint_le_mpi: 2^64+2 +mpi_core_uint_le_mpi:"010000000000000002" + mbedtls_mpi_core_cond_assign: 1 limb mpi_core_cond_assign:"FFFFFFFF":"11111111":4 From 8a32a75aa213a228594574e5035e40233bd00312 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 9 Sep 2022 23:19:04 +0200 Subject: [PATCH 02/14] mbedtls_mpi_random: avoid local allocation Rewrite the minimum bound comparison to avoid a local allocation. This costs a bit of code size, but saves RAM. This is in preparation for moving the bulk of the function to the bignum_core module where allocation is not permitted. Signed-off-by: Gilles Peskine --- library/bignum.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/library/bignum.c b/library/bignum.c index a68957a534..98d2442bbe 100644 --- a/library/bignum.c +++ b/library/bignum.c @@ -1968,10 +1968,9 @@ int mbedtls_mpi_random( mbedtls_mpi *X, { int ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; int count; - unsigned lt_lower = 1, lt_upper = 0; + unsigned ge_lower = 1, lt_upper = 0; size_t n_bits = mbedtls_mpi_bitlen( N ); size_t n_bytes = ( n_bits + 7 ) / 8; - mbedtls_mpi lower_bound; if( min < 0 ) return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); @@ -1997,14 +1996,10 @@ int mbedtls_mpi_random( mbedtls_mpi *X, */ count = ( n_bytes > 4 ? 30 : 250 ); - mbedtls_mpi_init( &lower_bound ); - /* 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 ) ); - 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) @@ -2027,13 +2022,12 @@ int mbedtls_mpi_random( mbedtls_mpi *X, goto cleanup; } - MBEDTLS_MPI_CHK( mbedtls_mpi_lt_mpi_ct( X, &lower_bound, <_lower ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_lt_mpi_ct( X, N, <_upper ) ); + ge_lower = mbedtls_mpi_core_uint_le_mpi( min, X->p, X->n ); + lt_upper = mbedtls_mpi_core_lt_ct( X->p, N->p, N->n ); } - while( lt_lower != 0 || lt_upper == 0 ); + while( ge_lower == 0 || lt_upper == 0 ); cleanup: - mbedtls_mpi_free( &lower_bound ); return( ret ); } From 26be89b3f6f183ef5da089bbf88b7eccbfc0c7f1 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 21 Sep 2022 13:43:30 +0200 Subject: [PATCH 03/14] Bignum core: random: prepare to break out the core function Shuffle things around a bit inside mbedtls_mpi_random() in preparation for breaking out mbedtls_mpi_core_random(). Signed-off-by: Gilles Peskine --- library/bignum.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/library/bignum.c b/library/bignum.c index 98d2442bbe..82e47b7d8c 100644 --- a/library/bignum.c +++ b/library/bignum.c @@ -1966,17 +1966,24 @@ int mbedtls_mpi_random( mbedtls_mpi *X, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) { - int ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; - int count; - unsigned ge_lower = 1, lt_upper = 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 ); + /* 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. */ + int ret = mbedtls_mpi_resize_clear( X, N->n ); + if( ret != 0 ) + return( ret ); + + unsigned ge_lower = 1, lt_upper = 0; + size_t n_bits = mbedtls_mpi_bitlen( N ); + size_t n_bytes = ( n_bits + 7 ) / 8; + + ret = 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 @@ -1994,12 +2001,7 @@ int mbedtls_mpi_random( mbedtls_mpi *X, * 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 ) ); + int count = ( n_bytes > 4 ? 30 : 250 ); /* * Match the procedure given in RFC 6979 §3.3 (deterministic ECDSA) From 4a8c5cdfbf9e1aef65a9a745d7ce84e6ce3ac3ee Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 18 Oct 2022 18:15:01 +0200 Subject: [PATCH 04/14] Bignum core: random: prototype Signed-off-by: Gilles Peskine --- library/bignum_core.h | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/library/bignum_core.h b/library/bignum_core.h index 120fa184c6..eeca41eb4f 100644 --- a/library/bignum_core.h +++ b/library/bignum_core.h @@ -512,6 +512,43 @@ int mbedtls_mpi_core_fill_random( mbedtls_mpi_uint *X, size_t X_limbs, 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, with \p limbs limbs. + * It must not be aliased with \p N or otherwise overlap it. + * \param min The minimum value to return. + * \param N The upper bound of the range, exclusive, with \p limbs limbs. + * In other words, this is one plus the maximum value to return. + * \p N must be strictly larger than \p min. + * \param limbs The number of limbs of \p N and \p X. + * This must not be 0. + * \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_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. + */ +int mbedtls_mpi_core_random( mbedtls_mpi_uint *X, + mbedtls_mpi_uint min, + const mbedtls_mpi_uint *N, + size_t limbs, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + /* BEGIN MERGE SLOT 1 */ /** From 78cf3bbf2251b9bd773f848abfc308298b901402 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 21 Sep 2022 15:46:04 +0200 Subject: [PATCH 05/14] Bignum core: break mbedtls_mpi_core_random out of mbedtls_mpi_random Signed-off-by: Gilles Peskine --- library/bignum.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/library/bignum.c b/library/bignum.c index 82e47b7d8c..060d88abf1 100644 --- a/library/bignum.c +++ b/library/bignum.c @@ -1978,11 +1978,20 @@ int mbedtls_mpi_random( mbedtls_mpi *X, if( ret != 0 ) return( ret ); - unsigned ge_lower = 1, lt_upper = 0; - size_t n_bits = mbedtls_mpi_bitlen( N ); - size_t n_bytes = ( n_bits + 7 ) / 8; + return( mbedtls_mpi_core_random( X->p, min, N->p, X->n, f_rng, p_rng ) ); +} - ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; +int mbedtls_mpi_core_random( mbedtls_mpi_uint *X, + mbedtls_mpi_uint min, + const mbedtls_mpi_uint *N, + size_t limbs, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + unsigned ge_lower = 1, lt_upper = 0; + size_t n_bits = mbedtls_mpi_core_bitlen( N, limbs ); + size_t n_bytes = ( n_bits + 7 ) / 8; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; /* * When min == 0, each try has at worst a probability 1/2 of failing @@ -2013,10 +2022,10 @@ int mbedtls_mpi_random( mbedtls_mpi *X, */ do { - MBEDTLS_MPI_CHK( mbedtls_mpi_core_fill_random( X->p, X->n, + MBEDTLS_MPI_CHK( mbedtls_mpi_core_fill_random( X, limbs, n_bytes, f_rng, p_rng ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( X, 8 * n_bytes - n_bits ) ); + mbedtls_mpi_core_shift_r( X, limbs, 8 * n_bytes - n_bits ); if( --count == 0 ) { @@ -2024,8 +2033,8 @@ int mbedtls_mpi_random( mbedtls_mpi *X, goto cleanup; } - ge_lower = mbedtls_mpi_core_uint_le_mpi( min, X->p, X->n ); - lt_upper = mbedtls_mpi_core_lt_ct( X->p, N->p, N->n ); + ge_lower = mbedtls_mpi_core_uint_le_mpi( min, X, limbs ); + lt_upper = mbedtls_mpi_core_lt_ct( X, N, limbs ); } while( ge_lower == 0 || lt_upper == 0 ); From 70375b2028b964b78aab109354f8241316d7894f Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 21 Sep 2022 15:47:23 +0200 Subject: [PATCH 06/14] Move mbedtls_mpi_core_random to the proper source file Signed-off-by: Gilles Peskine --- library/bignum.c | 61 ------------------------------------------- library/bignum_core.c | 61 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/library/bignum.c b/library/bignum.c index 060d88abf1..142c4c6602 100644 --- a/library/bignum.c +++ b/library/bignum.c @@ -1981,67 +1981,6 @@ int mbedtls_mpi_random( mbedtls_mpi *X, return( mbedtls_mpi_core_random( X->p, min, N->p, X->n, f_rng, p_rng ) ); } -int mbedtls_mpi_core_random( mbedtls_mpi_uint *X, - mbedtls_mpi_uint min, - const mbedtls_mpi_uint *N, - size_t limbs, - int (*f_rng)(void *, unsigned char *, size_t), - void *p_rng ) -{ - unsigned ge_lower = 1, lt_upper = 0; - size_t n_bits = mbedtls_mpi_core_bitlen( N, limbs ); - size_t n_bytes = ( n_bits + 7 ) / 8; - int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - - /* - * 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. - */ - int count = ( n_bytes > 4 ? 30 : 250 ); - - /* - * 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_CHK( mbedtls_mpi_core_fill_random( X, limbs, - n_bytes, - f_rng, p_rng ) ); - mbedtls_mpi_core_shift_r( X, limbs, 8 * n_bytes - n_bits ); - - if( --count == 0 ) - { - ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; - goto cleanup; - } - - ge_lower = mbedtls_mpi_core_uint_le_mpi( min, X, limbs ); - lt_upper = mbedtls_mpi_core_lt_ct( X, N, limbs ); - } - while( ge_lower == 0 || lt_upper == 0 ); - -cleanup: - return( ret ); -} - /* * Modular inverse: X = A^-1 mod N (HAC 14.61 / 14.64) */ diff --git a/library/bignum_core.c b/library/bignum_core.c index 08158fa78d..a432c2b21a 100644 --- a/library/bignum_core.c +++ b/library/bignum_core.c @@ -602,6 +602,67 @@ cleanup: return( ret ); } +int mbedtls_mpi_core_random( mbedtls_mpi_uint *X, + mbedtls_mpi_uint min, + const mbedtls_mpi_uint *N, + size_t limbs, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + unsigned ge_lower = 1, lt_upper = 0; + size_t n_bits = mbedtls_mpi_core_bitlen( N, limbs ); + size_t n_bytes = ( n_bits + 7 ) / 8; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* + * 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. + */ + int count = ( n_bytes > 4 ? 30 : 250 ); + + /* + * 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_CHK( mbedtls_mpi_core_fill_random( X, limbs, + n_bytes, + f_rng, p_rng ) ); + mbedtls_mpi_core_shift_r( X, limbs, 8 * n_bytes - n_bits ); + + if( --count == 0 ) + { + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; + } + + ge_lower = mbedtls_mpi_core_uint_le_mpi( min, X, limbs ); + lt_upper = mbedtls_mpi_core_lt_ct( X, N, limbs ); + } + while( ge_lower == 0 || lt_upper == 0 ); + +cleanup: + return( ret ); +} + /* BEGIN MERGE SLOT 1 */ static size_t exp_mod_get_window_size( size_t Ebits ) From 34e8a2c2875f94f863c260d5f64ee7751873f97b Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 27 Sep 2022 22:04:51 +0200 Subject: [PATCH 07/14] Test mbedtls_mpi_random differentially from mbedtls_mpi_core_random For good cases, test that mbedtls_mpi_random() produces the same output as mbedtls_mpi_core_random(). Signed-off-by: Gilles Peskine --- tests/suites/test_suite_bignum.function | 53 +++++++++++++++++++++++- tests/suites/test_suite_bignum.misc.data | 35 ++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/tests/suites/test_suite_bignum.function b/tests/suites/test_suite_bignum.function index 55bb2f58d2..cf54920583 100644 --- a/tests/suites/test_suite_bignum.function +++ b/tests/suites/test_suite_bignum.function @@ -2,6 +2,7 @@ #include "mbedtls/bignum.h" #include "mbedtls/entropy.h" #include "constant_time_internal.h" +#include "bignum_core.h" #include "test/constant_flow.h" #if MBEDTLS_MPI_MAX_BITS > 792 @@ -1294,6 +1295,57 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void mpi_random_values( int min, char *max_hex ) +{ + mbedtls_test_rnd_pseudo_info rnd_core = { + {'T', 'h', 'i', 's', ' ', 'i', ',', 'a', + 's', 'e', 'e', 'd', '!', 0}, + 0, 0}; + mbedtls_test_rnd_pseudo_info rnd_legacy; + memcpy( &rnd_legacy, &rnd_core, sizeof( rnd_core ) ); + mbedtls_mpi max_legacy; + mbedtls_mpi_init( &max_legacy ); + mbedtls_mpi_uint *R_core = NULL; + mbedtls_mpi R_legacy; + mbedtls_mpi_init( &R_legacy ); + + TEST_EQUAL( 0, mbedtls_test_read_mpi( &max_legacy, max_hex ) ); + size_t limbs = max_legacy.n; + ASSERT_ALLOC( R_core, limbs * ciL ); + + /* Call the legacy function and the core function with the same random + * stream. */ + int core_ret = mbedtls_mpi_core_random( R_core, min, max_legacy.p, limbs, + mbedtls_test_rnd_pseudo_rand, + &rnd_core ); + int legacy_ret = mbedtls_mpi_random( &R_legacy, min, &max_legacy, + mbedtls_test_rnd_pseudo_rand, + &rnd_legacy ); + + /* They must return the same status, and, on success, output the + * same number, with the same limb count. */ + TEST_EQUAL( core_ret, legacy_ret ); + if( core_ret == 0 ) + { + ASSERT_COMPARE( R_core, limbs * ciL, + R_legacy.p, R_legacy.n * ciL ); + } + + /* Also check that they have consumed the RNG in the same way. */ + /* This may theoretically fail on rare platforms with padding in + * the structure! If this is a problem in practice, change to a + * field-by-field comparison. */ + ASSERT_COMPARE( &rnd_core, sizeof( rnd_core ), + &rnd_legacy, sizeof( rnd_legacy ) ); + +exit: + mbedtls_mpi_free( &max_legacy ); + mbedtls_free( R_core ); + mbedtls_mpi_free( &R_legacy ); +} +/* END_CASE */ + /* BEGIN_CASE */ void mpi_random_many( int min, data_t *bound_bytes, int iterations ) { @@ -1481,7 +1533,6 @@ void most_negative_mpi_sint( ) mbedtls_mpi_init( &R ); mbedtls_mpi_init( &X ); - const size_t biL = 8 * sizeof( mbedtls_mpi_sint ); mbedtls_mpi_uint most_positive_plus_1 = (mbedtls_mpi_uint) 1 << ( biL - 1 ); const mbedtls_mpi_sint most_positive = most_positive_plus_1 - 1; const mbedtls_mpi_sint most_negative = - most_positive - 1; diff --git a/tests/suites/test_suite_bignum.misc.data b/tests/suites/test_suite_bignum.misc.data index dc6830e8fa..bc659c1af5 100644 --- a/tests/suites/test_suite_bignum.misc.data +++ b/tests/suites/test_suite_bignum.misc.data @@ -1958,6 +1958,41 @@ mpi_random_fail:2:"01":MBEDTLS_ERR_MPI_BAD_INPUT_DATA MPI random bad arguments: min > N = 1, 0 limb in upper bound mpi_random_fail:2:"000000000000000001":MBEDTLS_ERR_MPI_BAD_INPUT_DATA +MPI random legacy=core: 0..1 +mpi_random_values:0:"01" + +MPI random legacy=core: 0..2 +mpi_random_values:0:"02" + +MPI random legacy=core: 1..2 +mpi_random_values:1:"02" + +MPI random legacy=core: 2^30..2^31 +mpi_random_values:0x40000000:"80000000" + +MPI random legacy=core: 2^31-1..2^32-1 +mpi_random_values:0x7fffffff:"ffffffff" + +MPI random legacy=core: 0..2^256 +mpi_random_values:0:"010000000000000000000000000000000000000000000000000000000000000000" + +MPI random legacy=core: 0..2^256+1 +mpi_random_values:0:"010000000000000000000000000000000000000000000000000000000000000001" + +# The following test cases return MPI_NOT_ACCEPTABLE +# (verified at the time of writing, not enforced at runtime) +MPI random legacy=core: 2^28-1..2^28 (improbable) +mpi_random_values:0x0fffffff:"10000000" + +MPI random legacy=core: 2^29-1..2^29 (improbable) +mpi_random_values:0x1fffffff:"20000000" + +MPI random legacy=core: 2^30-1..2^30 (improbable) +mpi_random_values:0x3fffffff:"40000000" + +MPI random legacy=core: 2^31-1..2^31 (improbable) +mpi_random_values:0x7fffffff:"80000000" + Most negative mbedtls_mpi_sint most_negative_mpi_sint: From e3a1b81cb9bfa44b1db98b9a227cd4df3d96873e Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 27 Sep 2022 22:18:17 +0200 Subject: [PATCH 08/14] Switch mpi_random_many test function to testing core The test function mpi_random_many() is the main function for testing the get-random-in-range function. It validates that the random generator's output is within the desired range, and performs some basic statistical checks including checking that small ranges are covered exhaustively. Switch this function from testing mbedtls_mpi_random() to testing mbedtls_mpi_core_random(). This does not reduce the test coverage of mbedtls_mpi_random() because the same properties are now validated indirectly via mpi_random_values() which checks that mbedtls_mpi_random() and mbedtls_mpi_core_random() produce identical values for identical inputs. As of this commit, mpi_random_many() still uses some legacy mpi functions internally because the corresponding functions don't exist yet in core. Signed-off-by: Gilles Peskine --- tests/suites/test_suite_bignum.function | 49 +++++++++++++++---------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/tests/suites/test_suite_bignum.function b/tests/suites/test_suite_bignum.function index cf54920583..6fd0b1b5fe 100644 --- a/tests/suites/test_suite_bignum.function +++ b/tests/suites/test_suite_bignum.function @@ -1347,7 +1347,7 @@ exit: /* END_CASE */ /* BEGIN_CASE */ -void mpi_random_many( int min, data_t *bound_bytes, int iterations ) +void mpi_random_many( int min, char *bound_hex, int iterations ) { /* Generate numbers in the range 1..bound-1. Do it iterations times. * This function assumes that the value of bound is at least 2 and @@ -1355,9 +1355,11 @@ void mpi_random_many( int min, data_t *bound_bytes, int iterations ) * effectively never occurs. */ - mbedtls_mpi upper_bound; + data_t bound_bytes = {NULL, 0}; + mbedtls_mpi_uint *upper_bound = NULL; + size_t limbs; size_t n_bits; - mbedtls_mpi result; + mbedtls_mpi_uint *result = NULL; size_t b; /* If upper_bound is small, stats[b] is the number of times the value b * has been generated. Otherwise stats[b] is the number of times a @@ -1367,12 +1369,11 @@ void mpi_random_many( int min, data_t *bound_bytes, int iterations ) int full_stats; size_t i; - mbedtls_mpi_init( &upper_bound ); - mbedtls_mpi_init( &result ); + TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &upper_bound, &limbs, + bound_hex ) ); + ASSERT_ALLOC( result, limbs * ciL ); - TEST_EQUAL( 0, mbedtls_mpi_read_binary( &upper_bound, - bound_bytes->x, bound_bytes->len ) ); - n_bits = mbedtls_mpi_bitlen( &upper_bound ); + n_bits = mbedtls_mpi_core_bitlen( upper_bound, limbs ); /* Consider a bound "small" if it's less than 2^5. This value is chosen * to be small enough that the probability of missing one value is * negligible given the number of iterations. It must be less than @@ -1381,7 +1382,7 @@ void mpi_random_many( int min, data_t *bound_bytes, int iterations ) if( n_bits <= 5 ) { full_stats = 1; - stats_len = bound_bytes->x[bound_bytes->len - 1]; + stats_len = (uint8_t) upper_bound[0]; } else { @@ -1393,23 +1394,28 @@ void mpi_random_many( int min, data_t *bound_bytes, int iterations ) for( i = 0; i < (size_t) iterations; i++ ) { mbedtls_test_set_step( i ); - TEST_EQUAL( 0, mbedtls_mpi_random( &result, min, &upper_bound, - mbedtls_test_rnd_std_rand, NULL ) ); + TEST_EQUAL( 0, mbedtls_mpi_core_random( result, + min, upper_bound, limbs, + mbedtls_test_rnd_std_rand, NULL ) ); - TEST_ASSERT( sign_is_valid( &result ) ); - TEST_ASSERT( mbedtls_mpi_cmp_mpi( &result, &upper_bound ) < 0 ); - TEST_ASSERT( mbedtls_mpi_cmp_int( &result, min ) >= 0 ); + /* Temporarily use a legacy MPI for analysis, because the + * necessary auxiliary functions don't exist yet in core. */ + mbedtls_mpi B = {1, limbs, upper_bound}; + mbedtls_mpi R = {1, limbs, result}; + + TEST_ASSERT( mbedtls_mpi_cmp_mpi( &R, &B ) < 0 ); + TEST_ASSERT( mbedtls_mpi_cmp_int( &R, min ) >= 0 ); if( full_stats ) { uint8_t value; - TEST_EQUAL( 0, mbedtls_mpi_write_binary( &result, &value, 1 ) ); + TEST_EQUAL( 0, mbedtls_mpi_write_binary( &R, &value, 1 ) ); TEST_ASSERT( value < stats_len ); ++stats[value]; } else { for( b = 0; b < n_bits; b++ ) - stats[b] += mbedtls_mpi_get_bit( &result, b ); + stats[b] += mbedtls_mpi_get_bit( &R, b ); } } @@ -1427,8 +1433,12 @@ void mpi_random_many( int min, data_t *bound_bytes, int iterations ) } else { + bound_bytes.len = limbs * sizeof( mbedtls_mpi_uint ); + ASSERT_ALLOC( bound_bytes.x, bound_bytes.len ); + mbedtls_mpi_core_write_be( upper_bound, limbs, + bound_bytes.x, bound_bytes.len ); int statistically_safe_all_the_way = - is_significantly_above_a_power_of_2( bound_bytes ); + is_significantly_above_a_power_of_2( &bound_bytes ); for( b = 0; b < n_bits; b++ ) { mbedtls_test_set_step( 1000000 + b ); @@ -1449,8 +1459,9 @@ void mpi_random_many( int min, data_t *bound_bytes, int iterations ) } exit: - mbedtls_mpi_free( &upper_bound ); - mbedtls_mpi_free( &result ); + mbedtls_free( bound_bytes.x ); + mbedtls_free( upper_bound ); + mbedtls_free( result ); mbedtls_free( stats ); } /* END_CASE */ From da9746e8cec20b043459ccdc85de567eb7611a36 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 27 Sep 2022 22:38:38 +0200 Subject: [PATCH 09/14] Test NOT_ACCEPTABLE cases for MPI get-random Test some cases where mbedtls_mpi_core_random() or mbedtls_mpi_random() should return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE. These test cases use a very small range that makes the NOT_ACCEPTABLE case likely. The test code uses a deterministic RNG whose implementation is in the test framework, so we know that the tests will pass reproducibly unless the implementation the test framework changes. Signed-off-by: Gilles Peskine --- tests/suites/test_suite_bignum.function | 37 +++++++++++++++++++ tests/suites/test_suite_bignum.misc.data | 45 ++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/tests/suites/test_suite_bignum.function b/tests/suites/test_suite_bignum.function index 6fd0b1b5fe..9dc5e595df 100644 --- a/tests/suites/test_suite_bignum.function +++ b/tests/suites/test_suite_bignum.function @@ -1295,9 +1295,46 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void mpi_core_random_basic( int min, char *bound_bytes, int expected_ret ) +{ + /* Same RNG as in mpi_random_values */ + mbedtls_test_rnd_pseudo_info rnd = { + {'T', 'h', 'i', 's', ' ', 'i', ',', 'a', + 's', 'e', 'e', 'd', '!', 0}, + 0, 0}; + size_t limbs; + mbedtls_mpi_uint *lower_bound = NULL; + mbedtls_mpi_uint *upper_bound = NULL; + mbedtls_mpi_uint *result = NULL; + + TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &upper_bound, &limbs, + bound_bytes ) ); + ASSERT_ALLOC( lower_bound, limbs * ciL ); + lower_bound[0] = min; + ASSERT_ALLOC( result, limbs * ciL ); + + TEST_EQUAL( expected_ret, + mbedtls_mpi_core_random( result, min, upper_bound, limbs, + mbedtls_test_rnd_pseudo_rand, &rnd ) ); + + if( expected_ret == 0 ) + { + TEST_EQUAL( 0, mbedtls_mpi_core_lt_ct( result, lower_bound, limbs ) ); + TEST_EQUAL( 1, mbedtls_mpi_core_lt_ct( result, upper_bound, limbs ) ); + } + +exit: + mbedtls_free( lower_bound ); + mbedtls_free( upper_bound ); + mbedtls_free( result ); +} +/* END_CASE */ + /* BEGIN_CASE */ void mpi_random_values( int min, char *max_hex ) { + /* Same RNG as in mpi_core_random_basic */ mbedtls_test_rnd_pseudo_info rnd_core = { {'T', 'h', 'i', 's', ' ', 'i', ',', 'a', 's', 'e', 'e', 'd', '!', 0}, diff --git a/tests/suites/test_suite_bignum.misc.data b/tests/suites/test_suite_bignum.misc.data index bc659c1af5..fa4c5bce41 100644 --- a/tests/suites/test_suite_bignum.misc.data +++ b/tests/suites/test_suite_bignum.misc.data @@ -1788,6 +1788,51 @@ mpi_fill_random:16:15:0:MBEDTLS_ERR_ENTROPY_SOURCE_FAILED Fill random: MAX_SIZE bytes, RNG failure after MAX_SIZE-1 bytes mpi_fill_random:MBEDTLS_MPI_MAX_SIZE:MBEDTLS_MPI_MAX_SIZE-1:0:MBEDTLS_ERR_ENTROPY_SOURCE_FAILED +MPI core random basic: 0..1 +mpi_core_random_basic:0:"01":0 + +MPI core random basic: 0..2 +mpi_core_random_basic:0:"02":0 + +MPI core random basic: 1..2 +mpi_core_random_basic:1:"02":0 + +MPI core random basic: 2^30..2^31 +mpi_core_random_basic:0x40000000:"80000000":0 + +MPI core random basic: 0..2^128 +mpi_core_random_basic:0x40000000:"0100000000000000000000000000000000":0 + +MPI core random basic: 2^30..2^129 +mpi_core_random_basic:0x40000000:"0200000000000000000000000000000000":0 + +# Use the same data values for mpi_core_random_basic->NOT_ACCEPTABLE +# and for mpi_random_values where we want to return NOT_ACCEPTABLE but +# this isn't checked at runtime. +MPI core random basic: 2^28-1..2^28 (NOT_ACCEPTABLE) +mpi_core_random_basic:0x0fffffff:"10000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE + +MPI random legacy=core: 2^28-1..2^28 (NOT_ACCEPTABLE) +mpi_random_values:0x0fffffff:"10000000" + +MPI core random basic: 2^29-1..2^29 (NOT_ACCEPTABLE) +mpi_core_random_basic:0x1fffffff:"20000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE + +MPI random legacy=core: 2^29-1..2^29 (NOT_ACCEPTABLE) +mpi_random_values:0x1fffffff:"20000000" + +MPI core random basic: 2^30-1..2^30 (NOT_ACCEPTABLE) +mpi_core_random_basic:0x3fffffff:"40000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE + +MPI random legacy=core: 2^30-1..2^30 (NOT_ACCEPTABLE) +mpi_random_values:0x3fffffff:"40000000" + +MPI core random basic: 2^31-1..2^31 (NOT_ACCEPTABLE) +mpi_core_random_basic:0x7fffffff:"80000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE + +MPI random legacy=core: 2^31-1..2^31 (NOT_ACCEPTABLE) +mpi_random_values:0x7fffffff:"80000000" + MPI random in range: 1..2 mpi_random_many:1:"02":1000 From de09ddd64ee7801b5caa21aebb373f80deee72fc Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 6 Dec 2022 13:20:55 +0100 Subject: [PATCH 10/14] Move tests of random-in-range functions to their own suite The random-in-nrange test code has auxiliary functions that are common to all the interfaces (core, mod_raw (upcoming), mod (upcoming), legacy), and does some differential testing to check that all the layers consume the RNG in the saame way. Test them all in the same test suite. Signed-off-by: Gilles Peskine --- tests/suites/test_suite_bignum.function | 307 ---------------- tests/suites/test_suite_bignum.misc.data | 250 ------------- tests/suites/test_suite_bignum_random.data | 249 +++++++++++++ .../suites/test_suite_bignum_random.function | 331 ++++++++++++++++++ 4 files changed, 580 insertions(+), 557 deletions(-) create mode 100644 tests/suites/test_suite_bignum_random.data create mode 100644 tests/suites/test_suite_bignum_random.function diff --git a/tests/suites/test_suite_bignum.function b/tests/suites/test_suite_bignum.function index 9dc5e595df..01af2ffc06 100644 --- a/tests/suites/test_suite_bignum.function +++ b/tests/suites/test_suite_bignum.function @@ -90,50 +90,6 @@ static int f_rng_bytes_left( void *state, unsigned char *buf, size_t len ) return( 0 ); } -/* Test whether bytes represents (in big-endian base 256) a number b that - * is significantly above a power of 2. That is, b must not have a long run - * of unset bits after the most significant bit. - * - * Let n be the bit-size of b, i.e. the integer such that 2^n <= b < 2^{n+1}. - * This function returns 1 if, when drawing a number between 0 and b, - * the probability that this number is at least 2^n is not negligible. - * This probability is (b - 2^n) / b and this function checks that this - * number is above some threshold A. The threshold value is heuristic and - * based on the needs of mpi_random_many(). - */ -static int is_significantly_above_a_power_of_2( data_t *bytes ) -{ - const uint8_t *p = bytes->x; - size_t len = bytes->len; - unsigned x; - - /* Skip leading null bytes */ - while( len > 0 && p[0] == 0 ) - { - ++p; - --len; - } - /* 0 is not significantly above a power of 2 */ - if( len == 0 ) - return( 0 ); - /* Extract the (up to) 2 most significant bytes */ - if( len == 1 ) - x = p[0]; - else - x = ( p[0] << 8 ) | p[1]; - - /* Shift the most significant bit of x to position 8 and mask it out */ - while( ( x & 0xfe00 ) != 0 ) - x >>= 1; - x &= 0x00ff; - - /* At this point, x = floor((b - 2^n) / 2^(n-8)). b is significantly above - * a power of 2 iff x is significantly above 0 compared to 2^8. - * Testing x >= 2^4 amounts to picking A = 1/16 in the function - * description above. */ - return( x >= 0x10 ); -} - /* END_HEADER */ /* BEGIN_DEPENDENCIES @@ -1295,269 +1251,6 @@ exit: } /* END_CASE */ -/* BEGIN_CASE */ -void mpi_core_random_basic( int min, char *bound_bytes, int expected_ret ) -{ - /* Same RNG as in mpi_random_values */ - mbedtls_test_rnd_pseudo_info rnd = { - {'T', 'h', 'i', 's', ' ', 'i', ',', 'a', - 's', 'e', 'e', 'd', '!', 0}, - 0, 0}; - size_t limbs; - mbedtls_mpi_uint *lower_bound = NULL; - mbedtls_mpi_uint *upper_bound = NULL; - mbedtls_mpi_uint *result = NULL; - - TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &upper_bound, &limbs, - bound_bytes ) ); - ASSERT_ALLOC( lower_bound, limbs * ciL ); - lower_bound[0] = min; - ASSERT_ALLOC( result, limbs * ciL ); - - TEST_EQUAL( expected_ret, - mbedtls_mpi_core_random( result, min, upper_bound, limbs, - mbedtls_test_rnd_pseudo_rand, &rnd ) ); - - if( expected_ret == 0 ) - { - TEST_EQUAL( 0, mbedtls_mpi_core_lt_ct( result, lower_bound, limbs ) ); - TEST_EQUAL( 1, mbedtls_mpi_core_lt_ct( result, upper_bound, limbs ) ); - } - -exit: - mbedtls_free( lower_bound ); - mbedtls_free( upper_bound ); - mbedtls_free( result ); -} -/* END_CASE */ - -/* BEGIN_CASE */ -void mpi_random_values( int min, char *max_hex ) -{ - /* Same RNG as in mpi_core_random_basic */ - mbedtls_test_rnd_pseudo_info rnd_core = { - {'T', 'h', 'i', 's', ' ', 'i', ',', 'a', - 's', 'e', 'e', 'd', '!', 0}, - 0, 0}; - mbedtls_test_rnd_pseudo_info rnd_legacy; - memcpy( &rnd_legacy, &rnd_core, sizeof( rnd_core ) ); - mbedtls_mpi max_legacy; - mbedtls_mpi_init( &max_legacy ); - mbedtls_mpi_uint *R_core = NULL; - mbedtls_mpi R_legacy; - mbedtls_mpi_init( &R_legacy ); - - TEST_EQUAL( 0, mbedtls_test_read_mpi( &max_legacy, max_hex ) ); - size_t limbs = max_legacy.n; - ASSERT_ALLOC( R_core, limbs * ciL ); - - /* Call the legacy function and the core function with the same random - * stream. */ - int core_ret = mbedtls_mpi_core_random( R_core, min, max_legacy.p, limbs, - mbedtls_test_rnd_pseudo_rand, - &rnd_core ); - int legacy_ret = mbedtls_mpi_random( &R_legacy, min, &max_legacy, - mbedtls_test_rnd_pseudo_rand, - &rnd_legacy ); - - /* They must return the same status, and, on success, output the - * same number, with the same limb count. */ - TEST_EQUAL( core_ret, legacy_ret ); - if( core_ret == 0 ) - { - ASSERT_COMPARE( R_core, limbs * ciL, - R_legacy.p, R_legacy.n * ciL ); - } - - /* Also check that they have consumed the RNG in the same way. */ - /* This may theoretically fail on rare platforms with padding in - * the structure! If this is a problem in practice, change to a - * field-by-field comparison. */ - ASSERT_COMPARE( &rnd_core, sizeof( rnd_core ), - &rnd_legacy, sizeof( rnd_legacy ) ); - -exit: - mbedtls_mpi_free( &max_legacy ); - mbedtls_free( R_core ); - mbedtls_mpi_free( &R_legacy ); -} -/* END_CASE */ - -/* BEGIN_CASE */ -void mpi_random_many( int min, char *bound_hex, int iterations ) -{ - /* Generate numbers in the range 1..bound-1. Do it iterations times. - * This function assumes that the value of bound is at least 2 and - * that iterations is large enough that a one-in-2^iterations chance - * effectively never occurs. - */ - - data_t bound_bytes = {NULL, 0}; - mbedtls_mpi_uint *upper_bound = NULL; - size_t limbs; - size_t n_bits; - mbedtls_mpi_uint *result = NULL; - size_t b; - /* If upper_bound is small, stats[b] is the number of times the value b - * has been generated. Otherwise stats[b] is the number of times a - * value with bit b set has been generated. */ - size_t *stats = NULL; - size_t stats_len; - int full_stats; - size_t i; - - TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &upper_bound, &limbs, - bound_hex ) ); - ASSERT_ALLOC( result, limbs * ciL ); - - n_bits = mbedtls_mpi_core_bitlen( upper_bound, limbs ); - /* Consider a bound "small" if it's less than 2^5. This value is chosen - * to be small enough that the probability of missing one value is - * negligible given the number of iterations. It must be less than - * 256 because some of the code below assumes that "small" values - * fit in a byte. */ - if( n_bits <= 5 ) - { - full_stats = 1; - stats_len = (uint8_t) upper_bound[0]; - } - else - { - full_stats = 0; - stats_len = n_bits; - } - ASSERT_ALLOC( stats, stats_len ); - - for( i = 0; i < (size_t) iterations; i++ ) - { - mbedtls_test_set_step( i ); - TEST_EQUAL( 0, mbedtls_mpi_core_random( result, - min, upper_bound, limbs, - mbedtls_test_rnd_std_rand, NULL ) ); - - /* Temporarily use a legacy MPI for analysis, because the - * necessary auxiliary functions don't exist yet in core. */ - mbedtls_mpi B = {1, limbs, upper_bound}; - mbedtls_mpi R = {1, limbs, result}; - - TEST_ASSERT( mbedtls_mpi_cmp_mpi( &R, &B ) < 0 ); - TEST_ASSERT( mbedtls_mpi_cmp_int( &R, min ) >= 0 ); - if( full_stats ) - { - uint8_t value; - TEST_EQUAL( 0, mbedtls_mpi_write_binary( &R, &value, 1 ) ); - TEST_ASSERT( value < stats_len ); - ++stats[value]; - } - else - { - for( b = 0; b < n_bits; b++ ) - stats[b] += mbedtls_mpi_get_bit( &R, b ); - } - } - - if( full_stats ) - { - for( b = min; b < stats_len; b++ ) - { - mbedtls_test_set_step( 1000000 + b ); - /* Assert that each value has been reached at least once. - * This is almost guaranteed if the iteration count is large - * enough. This is a very crude way of checking the distribution. - */ - TEST_ASSERT( stats[b] > 0 ); - } - } - else - { - bound_bytes.len = limbs * sizeof( mbedtls_mpi_uint ); - ASSERT_ALLOC( bound_bytes.x, bound_bytes.len ); - mbedtls_mpi_core_write_be( upper_bound, limbs, - bound_bytes.x, bound_bytes.len ); - int statistically_safe_all_the_way = - is_significantly_above_a_power_of_2( &bound_bytes ); - for( b = 0; b < n_bits; b++ ) - { - mbedtls_test_set_step( 1000000 + b ); - /* Assert that each bit has been set in at least one result and - * clear in at least one result. Provided that iterations is not - * too small, it would be extremely unlikely for this not to be - * the case if the results are uniformly distributed. - * - * As an exception, the top bit may legitimately never be set - * if bound is a power of 2 or only slightly above. - */ - if( statistically_safe_all_the_way || b != n_bits - 1 ) - { - TEST_ASSERT( stats[b] > 0 ); - } - TEST_ASSERT( stats[b] < (size_t) iterations ); - } - } - -exit: - mbedtls_free( bound_bytes.x ); - mbedtls_free( upper_bound ); - mbedtls_free( result ); - mbedtls_free( stats ); -} -/* END_CASE */ - -/* BEGIN_CASE */ -void mpi_random_sizes( int min, data_t *bound_bytes, int nlimbs, int before ) -{ - mbedtls_mpi upper_bound; - mbedtls_mpi result; - - mbedtls_mpi_init( &upper_bound ); - mbedtls_mpi_init( &result ); - - if( before != 0 ) - { - /* Set result to sign(before) * 2^(|before|-1) */ - TEST_ASSERT( mbedtls_mpi_lset( &result, before > 0 ? 1 : -1 ) == 0 ); - if( before < 0 ) - before = - before; - TEST_ASSERT( mbedtls_mpi_shift_l( &result, before - 1 ) == 0 ); - } - - TEST_EQUAL( 0, mbedtls_mpi_grow( &result, nlimbs ) ); - TEST_EQUAL( 0, mbedtls_mpi_read_binary( &upper_bound, - bound_bytes->x, bound_bytes->len ) ); - TEST_EQUAL( 0, mbedtls_mpi_random( &result, min, &upper_bound, - mbedtls_test_rnd_std_rand, NULL ) ); - TEST_ASSERT( sign_is_valid( &result ) ); - TEST_ASSERT( mbedtls_mpi_cmp_mpi( &result, &upper_bound ) < 0 ); - TEST_ASSERT( mbedtls_mpi_cmp_int( &result, min ) >= 0 ); - -exit: - mbedtls_mpi_free( &upper_bound ); - mbedtls_mpi_free( &result ); -} -/* END_CASE */ - -/* BEGIN_CASE */ -void mpi_random_fail( int min, data_t *bound_bytes, int expected_ret ) -{ - mbedtls_mpi upper_bound; - mbedtls_mpi result; - int actual_ret; - - mbedtls_mpi_init( &upper_bound ); - mbedtls_mpi_init( &result ); - - TEST_EQUAL( 0, mbedtls_mpi_read_binary( &upper_bound, - bound_bytes->x, bound_bytes->len ) ); - actual_ret = mbedtls_mpi_random( &result, min, &upper_bound, - mbedtls_test_rnd_std_rand, NULL ); - TEST_EQUAL( expected_ret, actual_ret ); - -exit: - mbedtls_mpi_free( &upper_bound ); - mbedtls_mpi_free( &result ); -} -/* END_CASE */ - /* BEGIN_CASE */ void most_negative_mpi_sint( ) { diff --git a/tests/suites/test_suite_bignum.misc.data b/tests/suites/test_suite_bignum.misc.data index fa4c5bce41..5eda4c11ad 100644 --- a/tests/suites/test_suite_bignum.misc.data +++ b/tests/suites/test_suite_bignum.misc.data @@ -1788,256 +1788,6 @@ mpi_fill_random:16:15:0:MBEDTLS_ERR_ENTROPY_SOURCE_FAILED Fill random: MAX_SIZE bytes, RNG failure after MAX_SIZE-1 bytes mpi_fill_random:MBEDTLS_MPI_MAX_SIZE:MBEDTLS_MPI_MAX_SIZE-1:0:MBEDTLS_ERR_ENTROPY_SOURCE_FAILED -MPI core random basic: 0..1 -mpi_core_random_basic:0:"01":0 - -MPI core random basic: 0..2 -mpi_core_random_basic:0:"02":0 - -MPI core random basic: 1..2 -mpi_core_random_basic:1:"02":0 - -MPI core random basic: 2^30..2^31 -mpi_core_random_basic:0x40000000:"80000000":0 - -MPI core random basic: 0..2^128 -mpi_core_random_basic:0x40000000:"0100000000000000000000000000000000":0 - -MPI core random basic: 2^30..2^129 -mpi_core_random_basic:0x40000000:"0200000000000000000000000000000000":0 - -# Use the same data values for mpi_core_random_basic->NOT_ACCEPTABLE -# and for mpi_random_values where we want to return NOT_ACCEPTABLE but -# this isn't checked at runtime. -MPI core random basic: 2^28-1..2^28 (NOT_ACCEPTABLE) -mpi_core_random_basic:0x0fffffff:"10000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE - -MPI random legacy=core: 2^28-1..2^28 (NOT_ACCEPTABLE) -mpi_random_values:0x0fffffff:"10000000" - -MPI core random basic: 2^29-1..2^29 (NOT_ACCEPTABLE) -mpi_core_random_basic:0x1fffffff:"20000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE - -MPI random legacy=core: 2^29-1..2^29 (NOT_ACCEPTABLE) -mpi_random_values:0x1fffffff:"20000000" - -MPI core random basic: 2^30-1..2^30 (NOT_ACCEPTABLE) -mpi_core_random_basic:0x3fffffff:"40000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE - -MPI random legacy=core: 2^30-1..2^30 (NOT_ACCEPTABLE) -mpi_random_values:0x3fffffff:"40000000" - -MPI core random basic: 2^31-1..2^31 (NOT_ACCEPTABLE) -mpi_core_random_basic:0x7fffffff:"80000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE - -MPI random legacy=core: 2^31-1..2^31 (NOT_ACCEPTABLE) -mpi_random_values:0x7fffffff:"80000000" - -MPI random in range: 1..2 -mpi_random_many:1:"02":1000 - -MPI random in range: 1..3 -mpi_random_many:1:"03":1000 - -MPI random in range: 1..4 -mpi_random_many:1:"04":1000 - -MPI random in range: 1..5 -mpi_random_many:1:"05":1000 - -MPI random in range: 1..6 -mpi_random_many:1:"06":1000 - -MPI random in range: 1..7 -mpi_random_many:1:"07":1000 - -MPI random in range: 1..8 -mpi_random_many:1:"08":1000 - -MPI random in range: 1..9 -mpi_random_many:1:"09":1000 - -MPI random in range: 1..10 -mpi_random_many:1:"0a":1000 - -MPI random in range: 1..11 -mpi_random_many:1:"0b":1000 - -MPI random in range: 1..12 -mpi_random_many:1:"0c":1000 - -MPI random in range: 1..255 -mpi_random_many:1:"ff":200 - -MPI random in range: 1..256 -mpi_random_many:1:"0100":200 - -MPI random in range: 1..257 -mpi_random_many:1:"0101":200 - -MPI random in range: 1..272 -mpi_random_many:1:"0110":200 - -MPI random in range: 1..2^64-1 -mpi_random_many:1:"ffffffffffffffff":100 - -MPI random in range: 1..2^64 -mpi_random_many:1:"010000000000000000":100 - -MPI random in range: 1..2^64+1 -mpi_random_many:1:"010000000000000001":100 - -MPI random in range: 1..2^64+2^63 -mpi_random_many:1:"018000000000000000":100 - -MPI random in range: 1..2^65-1 -mpi_random_many:1:"01ffffffffffffffff":100 - -MPI random in range: 1..2^65 -mpi_random_many:1:"020000000000000000":100 - -MPI random in range: 1..2^65+1 -mpi_random_many:1:"020000000000000001":100 - -MPI random in range: 1..2^65+2^64 -mpi_random_many:1:"030000000000000000":100 - -MPI random in range: 1..2^66+2^65 -mpi_random_many:1:"060000000000000000":100 - -MPI random in range: 1..2^71-1 -mpi_random_many:1:"7fffffffffffffffff":100 - -MPI random in range: 1..2^71 -mpi_random_many:1:"800000000000000000":100 - -MPI random in range: 1..2^71+1 -mpi_random_many:1:"800000000000000001":100 - -MPI random in range: 1..2^71+2^70 -mpi_random_many:1:"c00000000000000000":100 - -MPI random in range: 1..2^72-1 -mpi_random_many:1:"ffffffffffffffffff":100 - -MPI random in range: 1..2^72 -mpi_random_many:1:"01000000000000000000":100 - -MPI random in range: 1..2^72+1 -mpi_random_many:1:"01000000000000000001":100 - -MPI random in range: 1..2^72+2^71 -mpi_random_many:1:"01800000000000000000":100 - -MPI random in range: 0..1 -mpi_random_many:0:"04":10000 - -MPI random in range: 0..4 -mpi_random_many:0:"04":10000 - -MPI random in range: 2..4 -mpi_random_many:2:"04":10000 - -MPI random in range: 3..4 -mpi_random_many:3:"04":10000 - -MPI random in range: smaller result -mpi_random_sizes:1:"aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb":1:0 - -MPI random in range: same size result (32-bit limbs) -mpi_random_sizes:1:"aaaaaaaaaaaaaaaa":2:0 - -MPI random in range: same size result (64-bit limbs) -mpi_random_sizes:1:"aaaaaaaaaaaaaaaa":1:0 - -MPI random in range: larger result -mpi_random_sizes:1:"aaaaaaaaaaaaaaaa":3:0 - -## The "0 limb in upper bound" tests rely on the fact that -## mbedtls_mpi_read_binary() bases the size of the MPI on the size of -## the input, without first checking for leading zeros. If this was -## not the case, the tests would still pass, but would not exercise -## the advertised behavior. -MPI random in range: leading 0 limb in upper bound #0 -mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":0:0 - -MPI random in range: leading 0 limb in upper bound #1 -mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":1:0 - -MPI random in range: leading 0 limb in upper bound #2 -mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":2:0 - -MPI random in range: leading 0 limb in upper bound #3 -mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":3:0 - -MPI random in range: leading 0 limb in upper bound #4 -mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":4:0 - -MPI random in range: previously small >0 -mpi_random_sizes:1:"1234567890":4:1 - -MPI random in range: previously small <0 -mpi_random_sizes:1:"1234567890":4:-1 - -MPI random in range: previously large >0 -mpi_random_sizes:1:"1234":4:65 - -MPI random in range: previously large <0 -mpi_random_sizes:1:"1234":4:-65 - -MPI random bad arguments: min < 0 -mpi_random_fail:-1:"04":MBEDTLS_ERR_MPI_BAD_INPUT_DATA - -MPI random bad arguments: min = N = 0 -mpi_random_fail:0:"00":MBEDTLS_ERR_MPI_BAD_INPUT_DATA - -MPI random bad arguments: min = N = 1 -mpi_random_fail:1:"01":MBEDTLS_ERR_MPI_BAD_INPUT_DATA - -MPI random bad arguments: min > N = 0 -mpi_random_fail:1:"00":MBEDTLS_ERR_MPI_BAD_INPUT_DATA - -MPI random bad arguments: min > N = 1 -mpi_random_fail:2:"01":MBEDTLS_ERR_MPI_BAD_INPUT_DATA - -MPI random bad arguments: min > N = 1, 0 limb in upper bound -mpi_random_fail:2:"000000000000000001":MBEDTLS_ERR_MPI_BAD_INPUT_DATA - -MPI random legacy=core: 0..1 -mpi_random_values:0:"01" - -MPI random legacy=core: 0..2 -mpi_random_values:0:"02" - -MPI random legacy=core: 1..2 -mpi_random_values:1:"02" - -MPI random legacy=core: 2^30..2^31 -mpi_random_values:0x40000000:"80000000" - -MPI random legacy=core: 2^31-1..2^32-1 -mpi_random_values:0x7fffffff:"ffffffff" - -MPI random legacy=core: 0..2^256 -mpi_random_values:0:"010000000000000000000000000000000000000000000000000000000000000000" - -MPI random legacy=core: 0..2^256+1 -mpi_random_values:0:"010000000000000000000000000000000000000000000000000000000000000001" - -# The following test cases return MPI_NOT_ACCEPTABLE -# (verified at the time of writing, not enforced at runtime) -MPI random legacy=core: 2^28-1..2^28 (improbable) -mpi_random_values:0x0fffffff:"10000000" - -MPI random legacy=core: 2^29-1..2^29 (improbable) -mpi_random_values:0x1fffffff:"20000000" - -MPI random legacy=core: 2^30-1..2^30 (improbable) -mpi_random_values:0x3fffffff:"40000000" - -MPI random legacy=core: 2^31-1..2^31 (improbable) -mpi_random_values:0x7fffffff:"80000000" - Most negative mbedtls_mpi_sint most_negative_mpi_sint: diff --git a/tests/suites/test_suite_bignum_random.data b/tests/suites/test_suite_bignum_random.data new file mode 100644 index 0000000000..c511af5337 --- /dev/null +++ b/tests/suites/test_suite_bignum_random.data @@ -0,0 +1,249 @@ +MPI core random basic: 0..1 +mpi_core_random_basic:0:"01":0 + +MPI core random basic: 0..2 +mpi_core_random_basic:0:"02":0 + +MPI core random basic: 1..2 +mpi_core_random_basic:1:"02":0 + +MPI core random basic: 2^30..2^31 +mpi_core_random_basic:0x40000000:"80000000":0 + +MPI core random basic: 0..2^128 +mpi_core_random_basic:0x40000000:"0100000000000000000000000000000000":0 + +MPI core random basic: 2^30..2^129 +mpi_core_random_basic:0x40000000:"0200000000000000000000000000000000":0 + +# Use the same data values for mpi_core_random_basic->NOT_ACCEPTABLE +# and for mpi_random_values where we want to return NOT_ACCEPTABLE but +# this isn't checked at runtime. +MPI core random basic: 2^28-1..2^28 (NOT_ACCEPTABLE) +mpi_core_random_basic:0x0fffffff:"10000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE + +MPI random legacy=core: 2^28-1..2^28 (NOT_ACCEPTABLE) +mpi_random_values:0x0fffffff:"10000000" + +MPI core random basic: 2^29-1..2^29 (NOT_ACCEPTABLE) +mpi_core_random_basic:0x1fffffff:"20000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE + +MPI random legacy=core: 2^29-1..2^29 (NOT_ACCEPTABLE) +mpi_random_values:0x1fffffff:"20000000" + +MPI core random basic: 2^30-1..2^30 (NOT_ACCEPTABLE) +mpi_core_random_basic:0x3fffffff:"40000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE + +MPI random legacy=core: 2^30-1..2^30 (NOT_ACCEPTABLE) +mpi_random_values:0x3fffffff:"40000000" + +MPI core random basic: 2^31-1..2^31 (NOT_ACCEPTABLE) +mpi_core_random_basic:0x7fffffff:"80000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE + +MPI random legacy=core: 2^31-1..2^31 (NOT_ACCEPTABLE) +mpi_random_values:0x7fffffff:"80000000" + +MPI random in range: 1..2 +mpi_random_many:1:"02":1000 + +MPI random in range: 1..3 +mpi_random_many:1:"03":1000 + +MPI random in range: 1..4 +mpi_random_many:1:"04":1000 + +MPI random in range: 1..5 +mpi_random_many:1:"05":1000 + +MPI random in range: 1..6 +mpi_random_many:1:"06":1000 + +MPI random in range: 1..7 +mpi_random_many:1:"07":1000 + +MPI random in range: 1..8 +mpi_random_many:1:"08":1000 + +MPI random in range: 1..9 +mpi_random_many:1:"09":1000 + +MPI random in range: 1..10 +mpi_random_many:1:"0a":1000 + +MPI random in range: 1..11 +mpi_random_many:1:"0b":1000 + +MPI random in range: 1..12 +mpi_random_many:1:"0c":1000 + +MPI random in range: 1..255 +mpi_random_many:1:"ff":200 + +MPI random in range: 1..256 +mpi_random_many:1:"0100":200 + +MPI random in range: 1..257 +mpi_random_many:1:"0101":200 + +MPI random in range: 1..272 +mpi_random_many:1:"0110":200 + +MPI random in range: 1..2^64-1 +mpi_random_many:1:"ffffffffffffffff":100 + +MPI random in range: 1..2^64 +mpi_random_many:1:"010000000000000000":100 + +MPI random in range: 1..2^64+1 +mpi_random_many:1:"010000000000000001":100 + +MPI random in range: 1..2^64+2^63 +mpi_random_many:1:"018000000000000000":100 + +MPI random in range: 1..2^65-1 +mpi_random_many:1:"01ffffffffffffffff":100 + +MPI random in range: 1..2^65 +mpi_random_many:1:"020000000000000000":100 + +MPI random in range: 1..2^65+1 +mpi_random_many:1:"020000000000000001":100 + +MPI random in range: 1..2^65+2^64 +mpi_random_many:1:"030000000000000000":100 + +MPI random in range: 1..2^66+2^65 +mpi_random_many:1:"060000000000000000":100 + +MPI random in range: 1..2^71-1 +mpi_random_many:1:"7fffffffffffffffff":100 + +MPI random in range: 1..2^71 +mpi_random_many:1:"800000000000000000":100 + +MPI random in range: 1..2^71+1 +mpi_random_many:1:"800000000000000001":100 + +MPI random in range: 1..2^71+2^70 +mpi_random_many:1:"c00000000000000000":100 + +MPI random in range: 1..2^72-1 +mpi_random_many:1:"ffffffffffffffffff":100 + +MPI random in range: 1..2^72 +mpi_random_many:1:"01000000000000000000":100 + +MPI random in range: 1..2^72+1 +mpi_random_many:1:"01000000000000000001":100 + +MPI random in range: 1..2^72+2^71 +mpi_random_many:1:"01800000000000000000":100 + +MPI random in range: 0..1 +mpi_random_many:0:"04":10000 + +MPI random in range: 0..4 +mpi_random_many:0:"04":10000 + +MPI random in range: 2..4 +mpi_random_many:2:"04":10000 + +MPI random in range: 3..4 +mpi_random_many:3:"04":10000 + +MPI random in range: smaller result +mpi_random_sizes:1:"aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb":1:0 + +MPI random in range: same size result (32-bit limbs) +mpi_random_sizes:1:"aaaaaaaaaaaaaaaa":2:0 + +MPI random in range: same size result (64-bit limbs) +mpi_random_sizes:1:"aaaaaaaaaaaaaaaa":1:0 + +MPI random in range: larger result +mpi_random_sizes:1:"aaaaaaaaaaaaaaaa":3:0 + +## The "0 limb in upper bound" tests rely on the fact that +## mbedtls_mpi_read_binary() bases the size of the MPI on the size of +## the input, without first checking for leading zeros. If this was +## not the case, the tests would still pass, but would not exercise +## the advertised behavior. +MPI random in range: leading 0 limb in upper bound #0 +mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":0:0 + +MPI random in range: leading 0 limb in upper bound #1 +mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":1:0 + +MPI random in range: leading 0 limb in upper bound #2 +mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":2:0 + +MPI random in range: leading 0 limb in upper bound #3 +mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":3:0 + +MPI random in range: leading 0 limb in upper bound #4 +mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":4:0 + +MPI random in range: previously small >0 +mpi_random_sizes:1:"1234567890":4:1 + +MPI random in range: previously small <0 +mpi_random_sizes:1:"1234567890":4:-1 + +MPI random in range: previously large >0 +mpi_random_sizes:1:"1234":4:65 + +MPI random in range: previously large <0 +mpi_random_sizes:1:"1234":4:-65 + +MPI random bad arguments: min < 0 +mpi_random_fail:-1:"04":MBEDTLS_ERR_MPI_BAD_INPUT_DATA + +MPI random bad arguments: min = N = 0 +mpi_random_fail:0:"00":MBEDTLS_ERR_MPI_BAD_INPUT_DATA + +MPI random bad arguments: min = N = 1 +mpi_random_fail:1:"01":MBEDTLS_ERR_MPI_BAD_INPUT_DATA + +MPI random bad arguments: min > N = 0 +mpi_random_fail:1:"00":MBEDTLS_ERR_MPI_BAD_INPUT_DATA + +MPI random bad arguments: min > N = 1 +mpi_random_fail:2:"01":MBEDTLS_ERR_MPI_BAD_INPUT_DATA + +MPI random bad arguments: min > N = 1, 0 limb in upper bound +mpi_random_fail:2:"000000000000000001":MBEDTLS_ERR_MPI_BAD_INPUT_DATA + +MPI random legacy=core: 0..1 +mpi_random_values:0:"01" + +MPI random legacy=core: 0..2 +mpi_random_values:0:"02" + +MPI random legacy=core: 1..2 +mpi_random_values:1:"02" + +MPI random legacy=core: 2^30..2^31 +mpi_random_values:0x40000000:"80000000" + +MPI random legacy=core: 2^31-1..2^32-1 +mpi_random_values:0x7fffffff:"ffffffff" + +MPI random legacy=core: 0..2^256 +mpi_random_values:0:"010000000000000000000000000000000000000000000000000000000000000000" + +MPI random legacy=core: 0..2^256+1 +mpi_random_values:0:"010000000000000000000000000000000000000000000000000000000000000001" + +# The following test cases return MPI_NOT_ACCEPTABLE +# (verified at the time of writing, not enforced at runtime) +MPI random legacy=core: 2^28-1..2^28 (improbable) +mpi_random_values:0x0fffffff:"10000000" + +MPI random legacy=core: 2^29-1..2^29 (improbable) +mpi_random_values:0x1fffffff:"20000000" + +MPI random legacy=core: 2^30-1..2^30 (improbable) +mpi_random_values:0x3fffffff:"40000000" + +MPI random legacy=core: 2^31-1..2^31 (improbable) +mpi_random_values:0x7fffffff:"80000000" diff --git a/tests/suites/test_suite_bignum_random.function b/tests/suites/test_suite_bignum_random.function new file mode 100644 index 0000000000..7a66073281 --- /dev/null +++ b/tests/suites/test_suite_bignum_random.function @@ -0,0 +1,331 @@ +/* BEGIN_HEADER */ +/* Dedicated test suite for mbedtls_mpi_core_random() and the upper-layer + * functions. Due to the complexity of how these functions are tested, + * we test all the layers in a single test suite, unlike the way other + * functions are tested with each layer in its own test suite. + */ + +#include "mbedtls/bignum.h" +#include "mbedtls/entropy.h" +#include "bignum_core.h" +#include "constant_time_internal.h" + +/* This test suite only manipulates non-negative bignums. */ +static int sign_is_valid( const mbedtls_mpi *X ) +{ + return( X->s == 1 ); +} + +/* Test whether bytes represents (in big-endian base 256) a number b that + * is significantly above a power of 2. That is, b must not have a long run + * of unset bits after the most significant bit. + * + * Let n be the bit-size of b, i.e. the integer such that 2^n <= b < 2^{n+1}. + * This function returns 1 if, when drawing a number between 0 and b, + * the probability that this number is at least 2^n is not negligible. + * This probability is (b - 2^n) / b and this function checks that this + * number is above some threshold A. The threshold value is heuristic and + * based on the needs of mpi_random_many(). + */ +static int is_significantly_above_a_power_of_2( data_t *bytes ) +{ + const uint8_t *p = bytes->x; + size_t len = bytes->len; + unsigned x; + + /* Skip leading null bytes */ + while( len > 0 && p[0] == 0 ) + { + ++p; + --len; + } + /* 0 is not significantly above a power of 2 */ + if( len == 0 ) + return( 0 ); + /* Extract the (up to) 2 most significant bytes */ + if( len == 1 ) + x = p[0]; + else + x = ( p[0] << 8 ) | p[1]; + + /* Shift the most significant bit of x to position 8 and mask it out */ + while( ( x & 0xfe00 ) != 0 ) + x >>= 1; + x &= 0x00ff; + + /* At this point, x = floor((b - 2^n) / 2^(n-8)). b is significantly above + * a power of 2 iff x is significantly above 0 compared to 2^8. + * Testing x >= 2^4 amounts to picking A = 1/16 in the function + * description above. */ + return( x >= 0x10 ); +} + +/* END_HEADER */ + +/* BEGIN_DEPENDENCIES + * depends_on:MBEDTLS_BIGNUM_C + * END_DEPENDENCIES + */ + +/* BEGIN_CASE */ +void mpi_core_random_basic( int min, char *bound_bytes, int expected_ret ) +{ + /* Same RNG as in mpi_random_values */ + mbedtls_test_rnd_pseudo_info rnd = { + {'T', 'h', 'i', 's', ' ', 'i', ',', 'a', + 's', 'e', 'e', 'd', '!', 0}, + 0, 0}; + size_t limbs; + mbedtls_mpi_uint *lower_bound = NULL; + mbedtls_mpi_uint *upper_bound = NULL; + mbedtls_mpi_uint *result = NULL; + + TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &upper_bound, &limbs, + bound_bytes ) ); + ASSERT_ALLOC( lower_bound, limbs * ciL ); + lower_bound[0] = min; + ASSERT_ALLOC( result, limbs * ciL ); + + TEST_EQUAL( expected_ret, + mbedtls_mpi_core_random( result, min, upper_bound, limbs, + mbedtls_test_rnd_pseudo_rand, &rnd ) ); + + if( expected_ret == 0 ) + { + TEST_EQUAL( 0, mbedtls_mpi_core_lt_ct( result, lower_bound, limbs ) ); + TEST_EQUAL( 1, mbedtls_mpi_core_lt_ct( result, upper_bound, limbs ) ); + } + +exit: + mbedtls_free( lower_bound ); + mbedtls_free( upper_bound ); + mbedtls_free( result ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void mpi_random_values( int min, char *max_hex ) +{ + /* Same RNG as in mpi_core_random_basic */ + mbedtls_test_rnd_pseudo_info rnd_core = { + {'T', 'h', 'i', 's', ' ', 'i', ',', 'a', + 's', 'e', 'e', 'd', '!', 0}, + 0, 0}; + mbedtls_test_rnd_pseudo_info rnd_legacy; + memcpy( &rnd_legacy, &rnd_core, sizeof( rnd_core ) ); + mbedtls_mpi max_legacy; + mbedtls_mpi_init( &max_legacy ); + mbedtls_mpi_uint *R_core = NULL; + mbedtls_mpi R_legacy; + mbedtls_mpi_init( &R_legacy ); + + TEST_EQUAL( 0, mbedtls_test_read_mpi( &max_legacy, max_hex ) ); + size_t limbs = max_legacy.n; + ASSERT_ALLOC( R_core, limbs * ciL ); + + /* Call the legacy function and the core function with the same random + * stream. */ + int core_ret = mbedtls_mpi_core_random( R_core, min, max_legacy.p, limbs, + mbedtls_test_rnd_pseudo_rand, + &rnd_core ); + int legacy_ret = mbedtls_mpi_random( &R_legacy, min, &max_legacy, + mbedtls_test_rnd_pseudo_rand, + &rnd_legacy ); + + /* They must return the same status, and, on success, output the + * same number, with the same limb count. */ + TEST_EQUAL( core_ret, legacy_ret ); + if( core_ret == 0 ) + { + ASSERT_COMPARE( R_core, limbs * ciL, + R_legacy.p, R_legacy.n * ciL ); + } + + /* Also check that they have consumed the RNG in the same way. */ + /* This may theoretically fail on rare platforms with padding in + * the structure! If this is a problem in practice, change to a + * field-by-field comparison. */ + ASSERT_COMPARE( &rnd_core, sizeof( rnd_core ), + &rnd_legacy, sizeof( rnd_legacy ) ); + +exit: + mbedtls_mpi_free( &max_legacy ); + mbedtls_free( R_core ); + mbedtls_mpi_free( &R_legacy ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void mpi_random_many( int min, char *bound_hex, int iterations ) +{ + /* Generate numbers in the range 1..bound-1. Do it iterations times. + * This function assumes that the value of bound is at least 2 and + * that iterations is large enough that a one-in-2^iterations chance + * effectively never occurs. + */ + + data_t bound_bytes = {NULL, 0}; + mbedtls_mpi_uint *upper_bound = NULL; + size_t limbs; + size_t n_bits; + mbedtls_mpi_uint *result = NULL; + size_t b; + /* If upper_bound is small, stats[b] is the number of times the value b + * has been generated. Otherwise stats[b] is the number of times a + * value with bit b set has been generated. */ + size_t *stats = NULL; + size_t stats_len; + int full_stats; + size_t i; + + TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &upper_bound, &limbs, + bound_hex ) ); + ASSERT_ALLOC( result, limbs * ciL ); + + n_bits = mbedtls_mpi_core_bitlen( upper_bound, limbs ); + /* Consider a bound "small" if it's less than 2^5. This value is chosen + * to be small enough that the probability of missing one value is + * negligible given the number of iterations. It must be less than + * 256 because some of the code below assumes that "small" values + * fit in a byte. */ + if( n_bits <= 5 ) + { + full_stats = 1; + stats_len = (uint8_t) upper_bound[0]; + } + else + { + full_stats = 0; + stats_len = n_bits; + } + ASSERT_ALLOC( stats, stats_len ); + + for( i = 0; i < (size_t) iterations; i++ ) + { + mbedtls_test_set_step( i ); + TEST_EQUAL( 0, mbedtls_mpi_core_random( result, + min, upper_bound, limbs, + mbedtls_test_rnd_std_rand, NULL ) ); + + /* Temporarily use a legacy MPI for analysis, because the + * necessary auxiliary functions don't exist yet in core. */ + mbedtls_mpi B = {1, limbs, upper_bound}; + mbedtls_mpi R = {1, limbs, result}; + + TEST_ASSERT( mbedtls_mpi_cmp_mpi( &R, &B ) < 0 ); + TEST_ASSERT( mbedtls_mpi_cmp_int( &R, min ) >= 0 ); + if( full_stats ) + { + uint8_t value; + TEST_EQUAL( 0, mbedtls_mpi_write_binary( &R, &value, 1 ) ); + TEST_ASSERT( value < stats_len ); + ++stats[value]; + } + else + { + for( b = 0; b < n_bits; b++ ) + stats[b] += mbedtls_mpi_get_bit( &R, b ); + } + } + + if( full_stats ) + { + for( b = min; b < stats_len; b++ ) + { + mbedtls_test_set_step( 1000000 + b ); + /* Assert that each value has been reached at least once. + * This is almost guaranteed if the iteration count is large + * enough. This is a very crude way of checking the distribution. + */ + TEST_ASSERT( stats[b] > 0 ); + } + } + else + { + bound_bytes.len = limbs * sizeof( mbedtls_mpi_uint ); + ASSERT_ALLOC( bound_bytes.x, bound_bytes.len ); + mbedtls_mpi_core_write_be( upper_bound, limbs, + bound_bytes.x, bound_bytes.len ); + int statistically_safe_all_the_way = + is_significantly_above_a_power_of_2( &bound_bytes ); + for( b = 0; b < n_bits; b++ ) + { + mbedtls_test_set_step( 1000000 + b ); + /* Assert that each bit has been set in at least one result and + * clear in at least one result. Provided that iterations is not + * too small, it would be extremely unlikely for this not to be + * the case if the results are uniformly distributed. + * + * As an exception, the top bit may legitimately never be set + * if bound is a power of 2 or only slightly above. + */ + if( statistically_safe_all_the_way || b != n_bits - 1 ) + { + TEST_ASSERT( stats[b] > 0 ); + } + TEST_ASSERT( stats[b] < (size_t) iterations ); + } + } + +exit: + mbedtls_free( bound_bytes.x ); + mbedtls_free( upper_bound ); + mbedtls_free( result ); + mbedtls_free( stats ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void mpi_random_sizes( int min, data_t *bound_bytes, int nlimbs, int before ) +{ + mbedtls_mpi upper_bound; + mbedtls_mpi result; + + mbedtls_mpi_init( &upper_bound ); + mbedtls_mpi_init( &result ); + + if( before != 0 ) + { + /* Set result to sign(before) * 2^(|before|-1) */ + TEST_ASSERT( mbedtls_mpi_lset( &result, before > 0 ? 1 : -1 ) == 0 ); + if( before < 0 ) + before = - before; + TEST_ASSERT( mbedtls_mpi_shift_l( &result, before - 1 ) == 0 ); + } + + TEST_EQUAL( 0, mbedtls_mpi_grow( &result, nlimbs ) ); + TEST_EQUAL( 0, mbedtls_mpi_read_binary( &upper_bound, + bound_bytes->x, bound_bytes->len ) ); + TEST_EQUAL( 0, mbedtls_mpi_random( &result, min, &upper_bound, + mbedtls_test_rnd_std_rand, NULL ) ); + TEST_ASSERT( sign_is_valid( &result ) ); + TEST_ASSERT( mbedtls_mpi_cmp_mpi( &result, &upper_bound ) < 0 ); + TEST_ASSERT( mbedtls_mpi_cmp_int( &result, min ) >= 0 ); + +exit: + mbedtls_mpi_free( &upper_bound ); + mbedtls_mpi_free( &result ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void mpi_random_fail( int min, data_t *bound_bytes, int expected_ret ) +{ + mbedtls_mpi upper_bound; + mbedtls_mpi result; + int actual_ret; + + mbedtls_mpi_init( &upper_bound ); + mbedtls_mpi_init( &result ); + + TEST_EQUAL( 0, mbedtls_mpi_read_binary( &upper_bound, + bound_bytes->x, bound_bytes->len ) ); + actual_ret = mbedtls_mpi_random( &result, min, &upper_bound, + mbedtls_test_rnd_std_rand, NULL ); + TEST_EQUAL( expected_ret, actual_ret ); + +exit: + mbedtls_mpi_free( &upper_bound ); + mbedtls_mpi_free( &result ); +} +/* END_CASE */ From 8781dd0e6185b1d82951e313029e0ab40f1936ad Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 6 Dec 2022 23:05:06 +0100 Subject: [PATCH 11/14] Fix overly large allocations of MPIs The second argument of ASSERT_ALLOC is a number of array elements, not a number of bytes. Signed-off-by: Gilles Peskine --- tests/suites/test_suite_bignum_random.function | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/suites/test_suite_bignum_random.function b/tests/suites/test_suite_bignum_random.function index 7a66073281..c8bca6e7c7 100644 --- a/tests/suites/test_suite_bignum_random.function +++ b/tests/suites/test_suite_bignum_random.function @@ -82,9 +82,9 @@ void mpi_core_random_basic( int min, char *bound_bytes, int expected_ret ) TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &upper_bound, &limbs, bound_bytes ) ); - ASSERT_ALLOC( lower_bound, limbs * ciL ); + ASSERT_ALLOC( lower_bound, limbs ); lower_bound[0] = min; - ASSERT_ALLOC( result, limbs * ciL ); + ASSERT_ALLOC( result, limbs ); TEST_EQUAL( expected_ret, mbedtls_mpi_core_random( result, min, upper_bound, limbs, @@ -121,7 +121,7 @@ void mpi_random_values( int min, char *max_hex ) TEST_EQUAL( 0, mbedtls_test_read_mpi( &max_legacy, max_hex ) ); size_t limbs = max_legacy.n; - ASSERT_ALLOC( R_core, limbs * ciL ); + ASSERT_ALLOC( R_core, limbs ); /* Call the legacy function and the core function with the same random * stream. */ @@ -180,7 +180,7 @@ void mpi_random_many( int min, char *bound_hex, int iterations ) TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &upper_bound, &limbs, bound_hex ) ); - ASSERT_ALLOC( result, limbs * ciL ); + ASSERT_ALLOC( result, limbs ); n_bits = mbedtls_mpi_core_bitlen( upper_bound, limbs ); /* Consider a bound "small" if it's less than 2^5. This value is chosen From 3e5d56e7d4ba1c67a1e8f6aea84c189e2fbcad81 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 7 Dec 2022 22:57:54 +0100 Subject: [PATCH 12/14] Remove duplicated test cases Signed-off-by: Gilles Peskine --- tests/suites/test_suite_bignum_random.data | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tests/suites/test_suite_bignum_random.data b/tests/suites/test_suite_bignum_random.data index c511af5337..fe290531a9 100644 --- a/tests/suites/test_suite_bignum_random.data +++ b/tests/suites/test_suite_bignum_random.data @@ -233,17 +233,3 @@ mpi_random_values:0:"01000000000000000000000000000000000000000000000000000000000 MPI random legacy=core: 0..2^256+1 mpi_random_values:0:"010000000000000000000000000000000000000000000000000000000000000001" - -# The following test cases return MPI_NOT_ACCEPTABLE -# (verified at the time of writing, not enforced at runtime) -MPI random legacy=core: 2^28-1..2^28 (improbable) -mpi_random_values:0x0fffffff:"10000000" - -MPI random legacy=core: 2^29-1..2^29 (improbable) -mpi_random_values:0x1fffffff:"20000000" - -MPI random legacy=core: 2^30-1..2^30 (improbable) -mpi_random_values:0x3fffffff:"40000000" - -MPI random legacy=core: 2^31-1..2^31 (improbable) -mpi_random_values:0x7fffffff:"80000000" From 6b7ce968d2b535ea99cdd9c86dc05c4bf103314c Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 15 Dec 2022 15:04:33 +0100 Subject: [PATCH 13/14] Clarify some comments Signed-off-by: Gilles Peskine --- library/bignum.c | 2 +- library/bignum_core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/bignum.c b/library/bignum.c index 142c4c6602..043bd6a0bc 100644 --- a/library/bignum.c +++ b/library/bignum.c @@ -1973,7 +1973,7 @@ int mbedtls_mpi_random( mbedtls_mpi *X, /* 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. */ + * This is necessary for mbedtls_mpi_core_random. */ int ret = mbedtls_mpi_resize_clear( X, N->n ); if( ret != 0 ) return( ret ); diff --git a/library/bignum_core.c b/library/bignum_core.c index a432c2b21a..770734c4a0 100644 --- a/library/bignum_core.c +++ b/library/bignum_core.c @@ -163,7 +163,7 @@ unsigned mbedtls_mpi_core_uint_le_mpi( mbedtls_mpi_uint min, /* min <= least significant limb? */ unsigned min_le_lsl = 1 ^ mbedtls_ct_mpi_uint_lt( A[0], min ); - /* most significant limbs (excluding 1) are all zero? */ + /* limbs other than the least significant one are all zero? */ mbedtls_mpi_uint msll_mask = 0; for( size_t i = 1; i < A_limbs; i++ ) msll_mask |= A[i]; From acdefdd51ac8defc0637b7b996d8976229a1463b Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 15 Dec 2022 15:10:36 +0100 Subject: [PATCH 14/14] Unify RNG initialization that must be unified mpi_core_random_basic and mpi_random_values must generate the same random sequences in order to get the expected test coverage (where we know we'll hit certain numbers of retries). Facilitate this by defining the RNG seed only once. Fix the seed to explicitly list all 16 words of the key. This isn't strictly required (missing initializer fields get the value zero), but it's clearer. Signed-off-by: Gilles Peskine --- .../suites/test_suite_bignum_random.function | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/suites/test_suite_bignum_random.function b/tests/suites/test_suite_bignum_random.function index c8bca6e7c7..184de5a405 100644 --- a/tests/suites/test_suite_bignum_random.function +++ b/tests/suites/test_suite_bignum_random.function @@ -16,6 +16,15 @@ static int sign_is_valid( const mbedtls_mpi *X ) return( X->s == 1 ); } +/* A common initializer for test functions that should generate the same + * sequences for reproducibility and good coverage. */ +const mbedtls_test_rnd_pseudo_info rnd_pseudo_seed = { + /* 16-word key */ + {'T', 'h', 'i', 's', ' ', 'i', 's', ' ', + 'a', ' ', 's', 'e', 'e', 'd', '!', 0}, + /* 2-word initial state, should be zero */ + 0, 0}; + /* Test whether bytes represents (in big-endian base 256) a number b that * is significantly above a power of 2. That is, b must not have a long run * of unset bits after the most significant bit. @@ -71,10 +80,7 @@ static int is_significantly_above_a_power_of_2( data_t *bytes ) void mpi_core_random_basic( int min, char *bound_bytes, int expected_ret ) { /* Same RNG as in mpi_random_values */ - mbedtls_test_rnd_pseudo_info rnd = { - {'T', 'h', 'i', 's', ' ', 'i', ',', 'a', - 's', 'e', 'e', 'd', '!', 0}, - 0, 0}; + mbedtls_test_rnd_pseudo_info rnd = rnd_pseudo_seed; size_t limbs; mbedtls_mpi_uint *lower_bound = NULL; mbedtls_mpi_uint *upper_bound = NULL; @@ -107,10 +113,7 @@ exit: void mpi_random_values( int min, char *max_hex ) { /* Same RNG as in mpi_core_random_basic */ - mbedtls_test_rnd_pseudo_info rnd_core = { - {'T', 'h', 'i', 's', ' ', 'i', ',', 'a', - 's', 'e', 'e', 'd', '!', 0}, - 0, 0}; + mbedtls_test_rnd_pseudo_info rnd_core = rnd_pseudo_seed; mbedtls_test_rnd_pseudo_info rnd_legacy; memcpy( &rnd_legacy, &rnd_core, sizeof( rnd_core ) ); mbedtls_mpi max_legacy;