diff options
author | Pauli <paul.dale@oracle.com> | 2019-05-29 09:54:29 +1000 |
---|---|---|
committer | Pauli <paul.dale@oracle.com> | 2019-05-29 09:54:29 +1000 |
commit | 5d2f3e4a6c3e62677cdf3b33d8b9b08ff7d52dc7 (patch) | |
tree | 36c697b0b6bc6552ff3471e26c8c49bcf5410d9e /test/bntest.c | |
parent | d4e2d5db621a512eff8a0ab07d3ac44d7122428a (diff) | |
download | openssl-new-5d2f3e4a6c3e62677cdf3b33d8b9b08ff7d52dc7.tar.gz |
Test of uniformity of BN_rand_range output.
Rework the test so that it fails far less often.
A number of independent tests are executed and 5% are expected to fail.
The number of such failures follows a binomial distribution which permits
a statistical test a 0.01% expected failure rate.
There is a command line option to enable the stochastic range checking.
It is off by default.
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/8830)
Diffstat (limited to 'test/bntest.c')
-rw-r--r-- | test/bntest.c | 84 |
1 files changed, 60 insertions, 24 deletions
diff --git a/test/bntest.c b/test/bntest.c index 2043e43e27..8df6e0f34c 100644 --- a/test/bntest.c +++ b/test/bntest.c @@ -1956,25 +1956,27 @@ static int test_rand(void) /* * Run some statistical tests to provide a degree confidence that the - * BN_rand_range() function works as expected. The critical value - * is computed using the R statistical suite: + * BN_rand_range() function works as expected. The test cases and + * critical values are generated by the bn_rand_range script. * - * qchisq(alpha, df=iterations - 1) + * Each individual test is a Chi^2 goodness of fit for a specified number + * of samples and range. The samples are assumed to be independent and + * that they are from a discrete uniform distribution. * - * where alpha is the significance level (0.95 is used here) and iterations - * is the number of samples being drawn. + * Some of these individual tests are expected to fail, the success/failure + * of each is an independent Bernoulli trial. The number of such successes + * will form a binomial distribution. The count of the successes is compared + * against a precomputed critical value to determine the overall outcome. */ -static const struct { +struct rand_range_case { unsigned int range; unsigned int iterations; double critical; -} rand_range_cases[] = { - { 2, 100, 123.2252 /* = qchisq(.95, df=99) */ }, - { 12, 1000, 1073.643 /* = qchisq(.95, df=999) */ }, - { 1023, 100000, 100735.7 /* = qchisq(.95, df=99999) */ }, }; -static int test_rand_range(int n) +#include "bn_rand_range.h" + +static int test_rand_range_single(size_t n) { const unsigned int range = rand_range_cases[n].range; const unsigned int iterations = rand_range_cases[n].iterations; @@ -1998,22 +2000,20 @@ static int test_rand_range(int n) counts[v]++; } - TEST_note("range %u iterations %u critical %.4f", range, iterations, - critical); - if (range < 20) { - TEST_note("frequencies (expected %.2f)", expected); - for (i = 0; i < range; i++) - TEST_note(" %2u %6zu", i, counts[i]); - } for (i = 0; i < range; i++) { const double delta = counts[i] - expected; sum += delta * delta; } sum /= expected; - TEST_note("test statistic %.4f", sum); - if (TEST_double_lt(sum, critical)) - res = 1; + if (sum > critical) { + TEST_info("Chi^2 test negative %.4f > %4.f", sum, critical); + TEST_note("test case %zu range %u iterations %u", n + 1, range, + iterations); + goto err; + } + + res = 1; err: BN_free(rng); BN_free(val); @@ -2021,6 +2021,19 @@ err: return res; } +static int test_rand_range(void) +{ + int n_success = 0; + size_t i; + + for (i = 0; i < OSSL_NELEM(rand_range_cases); i++) + n_success += test_rand_range_single(i); + if (TEST_int_ge(n_success, binomial_critical)) + return 1; + TEST_note("This test is expeced to fail by chance 0.01%% of the time."); + return 0; +} + static int test_negzero(void) { BIGNUM *a = NULL, *b = NULL, *c = NULL, *d = NULL; @@ -2448,11 +2461,18 @@ static int run_file_tests(int i) return c == 0; } +typedef enum OPTION_choice { + OPT_ERR = -1, + OPT_EOF = 0, + OPT_STOCHASTIC_TESTS, + OPT_TEST_ENUM +} OPTION_CHOICE; + const OPTIONS *test_get_options(void) { - enum { OPT_TEST_ENUM }; static const OPTIONS test_options[] = { OPT_TEST_OPTIONS_WITH_EXTRA_USAGE("[file...]\n"), + { "stochastic", OPT_STOCHASTIC_TESTS, '-', "Run stochastic tests" }, { OPT_HELP_STR, 1, '-', "file\tFile to run tests on. Normal tests are not run\n" }, { NULL } @@ -2462,7 +2482,22 @@ const OPTIONS *test_get_options(void) int setup_tests(void) { - int n = test_get_argument_count(); + OPTION_CHOICE o; + int n, stochastic = 0; + + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_STOCHASTIC_TESTS: + stochastic = 1; + break; + case OPT_TEST_CASES: + break; + default: + case OPT_ERR: + return 0; + } + } + n = test_get_argument_count(); if (!TEST_ptr(ctx = BN_CTX_new())) return 0; @@ -2499,7 +2534,8 @@ int setup_tests(void) #endif ADD_ALL_TESTS(test_is_prime, (int)OSSL_NELEM(primes)); ADD_ALL_TESTS(test_not_prime, (int)OSSL_NELEM(not_primes)); - ADD_ALL_TESTS(test_rand_range, OSSL_NELEM(rand_range_cases)); + if (stochastic) + ADD_TEST(test_rand_range); } else { ADD_ALL_TESTS(run_file_tests, n); } |