From 11eaed66bbc2105942c26a2dc9e502ce77e08b69 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 18 Mar 2019 09:37:13 -0400 Subject: [PATCH] Add support for deterministic override of crypto_rand() in tests We had this previously, but we did it differently in different places. This implementation is pulled from test_prob_distr.c --- src/test/include.am | 3 + src/test/rng_test_helpers.c | 121 ++++++++++++++++++++++++++++++++++++ src/test/rng_test_helpers.h | 21 +++++++ 3 files changed, 145 insertions(+) create mode 100644 src/test/rng_test_helpers.c create mode 100644 src/test/rng_test_helpers.h diff --git a/src/test/include.am b/src/test/include.am index 497aa320a4..022cdbe035 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -89,6 +89,7 @@ src_test_test_SOURCES += \ src/test/log_test_helpers.c \ src/test/hs_test_helpers.c \ src/test/rend_test_helpers.c \ + src/test/rng_test_helpers.c \ src/test/test.c \ src/test/test_accounting.c \ src/test/test_addr.c \ @@ -211,6 +212,7 @@ endif src_test_test_slow_SOURCES = if UNITTESTS_ENABLED src_test_test_slow_SOURCES += \ + src/test/rng_test_helpers.c \ src/test/test_slow.c \ src/test/test_crypto_slow.c \ src/test/test_process_slow.c \ @@ -319,6 +321,7 @@ noinst_HEADERS+= \ src/test/hs_test_helpers.h \ src/test/log_test_helpers.h \ src/test/rend_test_helpers.h \ + src/test/rng_test_helpers.h \ src/test/test.h \ src/test/ptr_helpers.h \ src/test/test_helpers.h \ diff --git a/src/test/rng_test_helpers.c b/src/test/rng_test_helpers.c new file mode 100644 index 0000000000..4384d2bb37 --- /dev/null +++ b/src/test/rng_test_helpers.c @@ -0,0 +1,121 @@ +/* Copyright (c) 2018-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file rng_test_helpers.c + * \brief Helpers for overriding PRNGs during unit tests. + * + * We define two PRNG overrides: a "reproducible PRNG" where the seed is + * chosen randomly but the stream can be replayed later on in case a bug is + * found, and a "deterministic PRNG" where the seed is fixed in the unit + * tests. + * + * Obviously, this code is testing-only. + */ + +#include "orconfig.h" +#include "core/or/or.h" + +#include "lib/crypt_ops/crypto_rand.h" + +#include "test/rng_test_helpers.h" + +#ifndef TOR_UNIT_TESTS +#error "No. Never link this code into Tor proper." +#endif + +/** + * Mutex to protect deterministic prng. + * + * Note that if you actually _use_ the prng from two threads at the same time, + * the results will probably be nondeterministic anyway. + */ +static tor_mutex_t *rng_mutex = NULL; + +/* This is the seed of the deterministic randomness. */ +static uint8_t rng_seed[16]; +static crypto_xof_t *rng_xof = NULL; + +/** + * Print the seed for our PRNG to stdout. We use this when we're + **/ +void +testing_dump_reproducible_rng_seed(void) +{ + printf("\n" + "Seed: %s\n", + hex_str((const char*)rng_seed, sizeof(rng_seed))); +} + +/** Produce deterministic randomness for the stochastic tests using the global + * rng_xof output. + * + * This function produces deterministic data over multiple calls iff it's + * called in the same call order with the same 'n' parameter. + * If not, outputs will deviate. */ +static void +crypto_rand_deterministic(char *out, size_t n) +{ + tor_assert(rng_xof); + tor_mutex_acquire(rng_mutex); + crypto_xof_squeeze_bytes(rng_xof, (uint8_t*)out, n); + tor_mutex_release(rng_mutex); +} + +/** + * Implementation helper: override our crypto_rand() PRNG with a given seed of + * length seed_len. Overlong seeds are truncated; short ones are + * padded. + **/ +static void +enable_deterministic_rng_impl(const uint8_t *seed, size_t seed_len) +{ + memset(rng_seed, 0, sizeof(rng_seed)); + memcpy(rng_seed, seed, MIN(seed_len, sizeof(rng_seed))); + + rng_mutex = tor_mutex_new(); + + crypto_xof_free(rng_xof); + rng_xof = crypto_xof_new(); + crypto_xof_add_bytes(rng_xof, rng_seed, sizeof(rng_seed)); + MOCK(crypto_rand, crypto_rand_deterministic); +} + +/** + * Replace our crypto_rand() prng with a variant that generates all of its + * output deterministically from a randomly chosen seed. In the event of an + * error, you can log the seed later on with + * testing_dump_reproducible_rng_seed. + **/ +void +testing_enable_reproducible_rng(void) +{ + uint8_t seed[16]; + crypto_rand((char*)seed, sizeof(seed)); + enable_deterministic_rng_impl(seed, sizeof(seed)); +} + +/** + * Replace our crypto_rand() prng with a variant that generates all of its + * output deterministically from a fixed seed. This variant is mainly useful + * for cases when we don't want coverage to change between runs. + **/ +void +testing_enable_deterministic_rng(void) +{ + static const uint8_t quotation[] = + "What will it be? A tree? A weed? " + "Each one is started from a seed."; // -- Mary Ann Hoberman + enable_deterministic_rng_impl(quotation, sizeof(quotation)); +} + +/** + * Undo the overrides for our PRNG. To be used at the end of testing. + **/ +void +testing_disable_rng_override(void) +{ + crypto_xof_free(rng_xof); + UNMOCK(crypto_rand); + tor_mutex_free(rng_mutex); +} diff --git a/src/test/rng_test_helpers.h b/src/test/rng_test_helpers.h new file mode 100644 index 0000000000..be95ec9d1d --- /dev/null +++ b/src/test/rng_test_helpers.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2017-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_RNG_TEST_HELPERS_H +#define TOR_RNG_TEST_HELPERS_H + +#include "core/or/or.h" + +void testing_enable_deterministic_rng(void); +void testing_enable_reproducible_rng(void); + +void testing_disable_rng_override(void); + +#define testing_disable_reproducible_rng() \ + testing_disable_rng_override() +#define testing_disable_deterministic_rng() \ + testing_disable_rng_override() + +void testing_dump_reproducible_rng_seed(void); + +#endif /* !defined(TOR_RNG_TEST_HELPERS_H) */