diff --git a/tests/suites/test_suite_ecp.data b/tests/suites/test_suite_ecp.data index 5f92ca459..0c344561b 100644 --- a/tests/suites/test_suite_ecp.data +++ b/tests/suites/test_suite_ecp.data @@ -312,6 +312,96 @@ genkey_mx_known_answer:447:"ffffffffffffffffffffffffffffffffffffffffffffffffffff ECP generate Montgomery key: Curve448, not enough entropy genkey_mx_known_answer:447:"4f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233343536":"" +ECP generate in range: 4 +genkey_sw_many:"04":1000 + +ECP generate in range: 5 +genkey_sw_many:"05":1000 + +ECP generate in range: 6 +genkey_sw_many:"06":1000 + +ECP generate in range: 7 +genkey_sw_many:"07":1000 + +ECP generate in range: 8 +genkey_sw_many:"08":1000 + +ECP generate in range: 9 +genkey_sw_many:"09":1000 + +ECP generate in range: 10 +genkey_sw_many:"0a":1000 + +ECP generate in range: 11 +genkey_sw_many:"0b":1000 + +ECP generate in range: 12 +genkey_sw_many:"0c":1000 + +ECP generate in range: 255 +genkey_sw_many:"ff":100 + +ECP generate in range: 256 +genkey_sw_many:"0100":100 + +ECP generate in range: 257 +genkey_sw_many:"0101":100 + +ECP generate in range: 272 +genkey_sw_many:"0110":100 + +ECP generate in range: 2^64-1 +genkey_sw_many:"ffffffffffffffff":100 + +ECP generate in range: 2^64 +genkey_sw_many:"010000000000000000":100 + +ECP generate in range: 2^64+1 +genkey_sw_many:"010000000000000001":100 + +ECP generate in range: 2^64+2^63 +genkey_sw_many:"018000000000000000":100 + +ECP generate in range: 2^65-1 +genkey_sw_many:"01ffffffffffffffff":100 + +ECP generate in range: 2^65 +genkey_sw_many:"020000000000000000":100 + +ECP generate in range: 2^65+1 +genkey_sw_many:"020000000000000001":100 + +ECP generate in range: 2^65+2^64 +genkey_sw_many:"030000000000000000":100 + +ECP generate in range: 2^66+2^65 +genkey_sw_many:"060000000000000000":100 + +ECP generate in range: 2^71-1 +genkey_sw_many:"7fffffffffffffffff":100 + +ECP generate in range: 2^71 +genkey_sw_many:"800000000000000000":100 + +ECP generate in range: 2^71+1 +genkey_sw_many:"800000000000000001":100 + +ECP generate in range: 2^71+2^63 +genkey_sw_many:"c00000000000000000":100 + +ECP generate in range: 2^72-1 +genkey_sw_many:"ffffffffffffffffff":100 + +ECP generate in range: 2^72 +genkey_sw_many:"01000000000000000000":100 + +ECP generate in range: 2^72+1 +genkey_sw_many:"01000000000000000001":100 + +ECP generate in range: 2^72+2^63 +genkey_sw_many:"01800000000000000000":100 + ECP read key #1 (short weierstrass, too small) depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED mbedtls_ecp_read_key:MBEDTLS_ECP_DP_SECP192R1:"00":MBEDTLS_ERR_ECP_INVALID_KEY:0 diff --git a/tests/suites/test_suite_ecp.function b/tests/suites/test_suite_ecp.function index 1492b9531..1049c96a1 100644 --- a/tests/suites/test_suite_ecp.function +++ b/tests/suites/test_suite_ecp.function @@ -15,6 +15,43 @@ #define ECP_PT_RESET( x ) \ mbedtls_ecp_point_free( x ); \ mbedtls_ecp_point_init( x ); + +#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) +/* Test whether bytes represents (in big-endian base 256) a number B that + * is "significantly" above a power of 2, which is defined as follows. + * Let n be the integer such that 2^n <= B < 2^{n+1}. B is significantly + * above a power of 2 if (B - 2^n) / 2^n is not negligible. "Negligible" + * is defined as having a negligible chance that if you draw an integer + * in the range [1, B-1] K times, the number will always be less than 2^n, + * where K is the iteration count passed to genkey_sw_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; + while( len > 0 && p[0] == 0 ) + { + ++p; + --len; + } + if( len == 0 ) + return( 0 ); + else if( len == 1 ) + x = p[0]; + else + x = ( p[0] << 8 ) | p[1]; + + if( x <= 4 ) + return( 0 ); + + while( ( x & 0x8000 ) == 0 ) + x <<= 1; + x &= 0x7fff; + return( x >= 0x1000 ); +} +#endif + /* END_HEADER */ /* BEGIN_DEPENDENCIES @@ -1286,6 +1323,114 @@ exit: } /* END_CASE */ +/* BEGIN_CASE depends_on:MBEDTLS_TEST_HOOKS:MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ +void genkey_sw_many( data_t *bound_bytes, 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. + */ + + mbedtls_mpi bound; + size_t n_bits; + mbedtls_mpi result; + size_t b; + /* If 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; + + mbedtls_mpi_init( &bound ); + mbedtls_mpi_init( &result ); + + TEST_EQUAL( 0, mbedtls_mpi_read_binary( &bound, + bound_bytes->x, bound_bytes->len ) ); + n_bits = mbedtls_mpi_bitlen( &bound ); + /* 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 = bound_bytes->x[bound_bytes->len - 1]; + } + 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_ecp_gen_privkey_sw( + &bound, n_bits, &result, + mbedtls_test_rnd_std_rand, NULL ) ); + + TEST_ASSERT( mbedtls_mpi_cmp_mpi( &result, &bound ) < 0 ); + TEST_ASSERT( mbedtls_mpi_cmp_int( &result, 1 ) >= 0 ); + if( full_stats ) + { + uint8_t value; + TEST_EQUAL( 0, mbedtls_mpi_write_binary( &result, &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 ); + } + } + + if( full_stats ) + { + for( b = 1; 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 + { + 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( b != n_bits - 1 || + is_significantly_above_a_power_of_2( bound_bytes ) ) + { + TEST_ASSERT( stats[b] > 0 ); + } + TEST_ASSERT( stats[b] < (size_t) iterations ); + } + } + +exit: + mbedtls_mpi_free( &bound ); + mbedtls_mpi_free( &result ); + mbedtls_free( stats ); +} +/* END_CASE */ + /* BEGIN_CASE depends_on:MBEDTLS_SELF_TEST */ void ecp_selftest( ) {