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 */