Add unit tests that check for common RNG failure modes

Check that crypto_rand doesn't return all zeroes, identical values,
or incrementing values (OpenSSL's rand_predictable feature).
This commit is contained in:
teor (Tim Wilson-Brown) 2015-11-26 21:25:31 +11:00
parent 943369f927
commit 155fa2dbdb
2 changed files with 110 additions and 0 deletions

View File

@ -0,0 +1,5 @@
o Minor features (unit tests, random number generation):
- Add unit tests that check for common RNG failure modes, such as
returning all zeroes, identical values, or incrementing values
(OpenSSL's rand_predictable feature).
Patch by "teor".

View File

@ -1803,6 +1803,110 @@ test_crypto_siphash(void *arg)
; ;
} }
/* We want the likelihood that the random buffer exhibits any regular pattern
* to be far less than the memory bit error rate in the int return value.
* Using 2048 bits provides a failure rate of 1/(3 * 10^616), and we call
* 3 functions, leading to an overall error rate of 1/10^616.
* This is comparable with the 1/10^603 failure rate of test_crypto_rng_range.
*/
#define FAILURE_MODE_BUFFER_SIZE (2048/8)
/** Check crypto_rand for a failure mode where it does nothing to the buffer,
* or it sets the buffer to all zeroes. Return 0 when the check passes,
* or -1 when it fails. */
static int
crypto_rand_check_failure_mode_zero(void)
{
char buf[FAILURE_MODE_BUFFER_SIZE];
memset(buf, 0, FAILURE_MODE_BUFFER_SIZE);
crypto_rand(buf, FAILURE_MODE_BUFFER_SIZE);
for (size_t i = 0; i < FAILURE_MODE_BUFFER_SIZE; i++) {
if (buf[i] != 0) {
return 0;
}
}
return -1;
}
/** Check crypto_rand for a failure mode where every int64_t in the buffer is
* the same. Return 0 when the check passes, or -1 when it fails. */
static int
crypto_rand_check_failure_mode_identical(void)
{
/* just in case the buffer size isn't a multiple of sizeof(int64_t) */
#define FAILURE_MODE_BUFFER_SIZE_I64 \
(FAILURE_MODE_BUFFER_SIZE/SIZEOF_INT64_T)
#define FAILURE_MODE_BUFFER_SIZE_I64_BYTES \
(FAILURE_MODE_BUFFER_SIZE_I64*SIZEOF_INT64_T)
#if FAILURE_MODE_BUFFER_SIZE_I64 < 2
#error FAILURE_MODE_BUFFER_SIZE needs to be at least 2*SIZEOF_INT64_T
#endif
int64_t buf[FAILURE_MODE_BUFFER_SIZE_I64];
memset(buf, 0, FAILURE_MODE_BUFFER_SIZE_I64_BYTES);
crypto_rand((char *)buf, FAILURE_MODE_BUFFER_SIZE_I64_BYTES);
for (size_t i = 1; i < FAILURE_MODE_BUFFER_SIZE_I64; i++) {
if (buf[i] != buf[i-1]) {
return 0;
}
}
return -1;
}
/** Check crypto_rand for a failure mode where it increments the "random"
* value by 1 for every byte in the buffer. (This is OpenSSL's PREDICT mode.)
* Return 0 when the check passes, or -1 when it fails. */
static int
crypto_rand_check_failure_mode_predict(void)
{
unsigned char buf[FAILURE_MODE_BUFFER_SIZE];
memset(buf, 0, FAILURE_MODE_BUFFER_SIZE);
crypto_rand((char *)buf, FAILURE_MODE_BUFFER_SIZE);
for (size_t i = 1; i < FAILURE_MODE_BUFFER_SIZE; i++) {
/* check if the last byte was incremented by 1, including integer
* wrapping */
if (buf[i] - buf[i-1] != 1 && buf[i-1] - buf[i] != 255) {
return 0;
}
}
return -1;
}
#undef FAILURE_MODE_BUFFER_SIZE
static void
test_crypto_failure_modes(void *arg)
{
int rv = 0;
(void)arg;
rv = crypto_early_init();
tt_assert(rv == 0);
/* Check random works */
rv = crypto_rand_check_failure_mode_zero();
tt_assert(rv == 0);
rv = crypto_rand_check_failure_mode_identical();
tt_assert(rv == 0);
rv = crypto_rand_check_failure_mode_predict();
tt_assert(rv == 0);
done:
;
}
#define CRYPTO_LEGACY(name) \ #define CRYPTO_LEGACY(name) \
{ #name, test_crypto_ ## name , 0, NULL, NULL } { #name, test_crypto_ ## name , 0, NULL, NULL }
@ -1841,6 +1945,7 @@ struct testcase_t crypto_tests[] = {
{ "ed25519_fuzz_donna", test_crypto_ed25519_fuzz_donna, TT_FORK, NULL, { "ed25519_fuzz_donna", test_crypto_ed25519_fuzz_donna, TT_FORK, NULL,
NULL }, NULL },
{ "siphash", test_crypto_siphash, 0, NULL, NULL }, { "siphash", test_crypto_siphash, 0, NULL, NULL },
{ "failure_modes", test_crypto_failure_modes, TT_FORK, NULL, NULL },
END_OF_TESTCASES END_OF_TESTCASES
}; };