Improve test coverage of our strongest-rng code.

This commit is contained in:
Nick Mathewson 2016-05-03 13:33:33 -04:00
parent 148f0004e1
commit 7a5f15b6e0
3 changed files with 90 additions and 2 deletions

View File

@ -2571,6 +2571,11 @@ crypto_seed_weak_rng(tor_weak_rng_t *rng)
tor_init_weak_random(rng, seed); tor_init_weak_random(rng, seed);
} }
#ifdef TOR_UNIT_TESTS
int break_strongest_rng_syscall = 0;
int break_strongest_rng_fallback = 0;
#endif
/** Try to get <b>out_len</b> bytes of the strongest entropy we can generate, /** Try to get <b>out_len</b> bytes of the strongest entropy we can generate,
* via system calls, storing it into <b>out</b>. Return 0 on success, -1 on * via system calls, storing it into <b>out</b>. Return 0 on success, -1 on
* failure. A maximum request size of 256 bytes is imposed. * failure. A maximum request size of 256 bytes is imposed.
@ -2580,6 +2585,11 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
{ {
tor_assert(out_len <= MAX_STRONGEST_RAND_SIZE); tor_assert(out_len <= MAX_STRONGEST_RAND_SIZE);
#ifdef TOR_UNIT_TESTS
if (break_strongest_rng_syscall)
return -1;
#endif
#if defined(_WIN32) #if defined(_WIN32)
static int provider_set = 0; static int provider_set = 0;
static HCRYPTPROV provider; static HCRYPTPROV provider;
@ -2664,6 +2674,11 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
static int static int
crypto_strongest_rand_fallback(uint8_t *out, size_t out_len) crypto_strongest_rand_fallback(uint8_t *out, size_t out_len)
{ {
#ifdef TOR_UNIT_TESTS
if (break_strongest_rng_fallback)
return -1;
#endif
#ifdef _WIN32 #ifdef _WIN32
/* Windows exclusively uses crypto_strongest_rand_syscall(). */ /* Windows exclusively uses crypto_strongest_rand_syscall(). */
(void)out; (void)out;
@ -2701,7 +2716,7 @@ crypto_strongest_rand_fallback(uint8_t *out, size_t out_len)
* storing it into <b>out</b>. Return 0 on success, -1 on failure. A maximum * storing it into <b>out</b>. Return 0 on success, -1 on failure. A maximum
* request size of 256 bytes is imposed. * request size of 256 bytes is imposed.
*/ */
static int STATIC int
crypto_strongest_rand_raw(uint8_t *out, size_t out_len) crypto_strongest_rand_raw(uint8_t *out, size_t out_len)
{ {
static const size_t sanity_min_size = 16; static const size_t sanity_min_size = 16;
@ -2735,13 +2750,17 @@ crypto_strongest_rand_raw(uint8_t *out, size_t out_len)
return 0; return 0;
} }
/* We tried max_attempts times to fill a buffer >= 128 bits long, /* LCOV_EXCL_START
*
* We tried max_attempts times to fill a buffer >= 128 bits long,
* and each time it returned all '0's. Either the system entropy * and each time it returned all '0's. Either the system entropy
* source is busted, or the user should go out and buy a ticket to * source is busted, or the user should go out and buy a ticket to
* every lottery on the planet. * every lottery on the planet.
*/ */
log_warn(LD_CRYPTO, "Strong OS entropy returned all zero buffer."); log_warn(LD_CRYPTO, "Strong OS entropy returned all zero buffer.");
return -1; return -1;
/* LCOV_EXCL_STOP */
} }
/** Try to get <b>out_len</b> bytes of the strongest entropy we can generate, /** Try to get <b>out_len</b> bytes of the strongest entropy we can generate,

View File

@ -317,6 +317,7 @@ void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
#ifdef CRYPTO_PRIVATE #ifdef CRYPTO_PRIVATE
STATIC int crypto_force_rand_ssleay(void); STATIC int crypto_force_rand_ssleay(void);
STATIC int crypto_strongest_rand_raw(uint8_t *out, size_t out_len);
#endif #endif
#endif #endif

View File

@ -190,6 +190,67 @@ test_crypto_rng_range(void *arg)
; ;
} }
extern int break_strongest_rng_fallback;
extern int break_strongest_rng_syscall;
static void
test_crypto_rng_strongest(void *arg)
{
const char *how = arg;
int broken = 0;
if (how == NULL) {
;
} else if (!strcmp(how, "nosyscall")) {
break_strongest_rng_syscall = 1;
} else if (!strcmp(how, "nofallback")) {
break_strongest_rng_fallback = 1;
} else if (!strcmp(how, "broken")) {
broken = break_strongest_rng_syscall = break_strongest_rng_fallback = 1;
}
#define N 128
uint8_t combine_and[N];
uint8_t combine_or[N];
int i, j;
memset(combine_and, 0xff, N);
memset(combine_or, 0, N);
for (i = 0; i < 100; ++i) { /* 2^-100 chances just don't happen. */
uint8_t output[N];
memset(output, 0, N);
if (how == NULL) {
/* this one can't fail. */
crypto_strongest_rand(output, sizeof(output));
} else {
int r = crypto_strongest_rand_raw(output, sizeof(output));
if (r == -1) {
if (broken) {
goto done; /* we're fine. */
}
/* This function is allowed to break, but only if it always breaks. */
tt_int_op(i, OP_EQ, 0);
tt_skip();
} else {
tt_assert(! broken);
}
}
for (j = 0; j < N; ++j) {
combine_and[j] &= output[j];
combine_or[j] |= output[j];
}
}
for (j = 0; j < N; ++j) {
tt_int_op(combine_and[j], OP_EQ, 0);
tt_int_op(combine_or[j], OP_EQ, 0xff);
}
done:
;
#undef N
}
/* Test for rectifying openssl RAND engine. */ /* Test for rectifying openssl RAND engine. */
static void static void
test_crypto_rng_engine(void *arg) test_crypto_rng_engine(void *arg)
@ -2750,6 +2811,13 @@ struct testcase_t crypto_tests[] = {
CRYPTO_LEGACY(rng), CRYPTO_LEGACY(rng),
{ "rng_range", test_crypto_rng_range, 0, NULL, NULL }, { "rng_range", test_crypto_rng_range, 0, NULL, NULL },
{ "rng_engine", test_crypto_rng_engine, TT_FORK, NULL, NULL }, { "rng_engine", test_crypto_rng_engine, TT_FORK, NULL, NULL },
{ "rng_strongest", test_crypto_rng_strongest, TT_FORK, NULL, NULL },
{ "rng_strongest_nosyscall", test_crypto_rng_strongest, TT_FORK,
&passthrough_setup, (void*)"nosyscall" },
{ "rng_strongest_nofallback", test_crypto_rng_strongest, TT_FORK,
&passthrough_setup, (void*)"nofallback" },
{ "rng_strongest_broken", test_crypto_rng_strongest, TT_FORK,
&passthrough_setup, (void*)"broken" },
{ "openssl_version", test_crypto_openssl_version, TT_FORK, NULL, NULL }, { "openssl_version", test_crypto_openssl_version, TT_FORK, NULL, NULL },
{ "aes_AES", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"aes" }, { "aes_AES", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"aes" },
{ "aes_EVP", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"evp" }, { "aes_EVP", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"evp" },