tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

rng_test_helpers.c (7290B)


      1 /* Copyright (c) 2018-2021, The Tor Project, Inc. */
      2 /* See LICENSE for licensing information */
      3 
      4 /**
      5 * \file rng_test_helpers.c
      6 * \brief Helpers for overriding PRNGs during unit tests.
      7 *
      8 * We define two PRNG overrides: a "reproducible PRNG" where the seed is
      9 * chosen randomly but the stream can be replayed later on in case a bug is
     10 * found, and a "deterministic PRNG" where the seed is fixed in the unit
     11 * tests.
     12 *
     13 * Obviously, this code is testing-only.
     14 */
     15 
     16 #include "orconfig.h"
     17 #include "core/or/or.h"
     18 
     19 #include "lib/crypt_ops/crypto_rand.h"
     20 #include "ext/tinytest.h"
     21 
     22 #include "test/rng_test_helpers.h"
     23 
     24 #ifndef TOR_UNIT_TESTS
     25 #error "No. Never link this code into Tor proper."
     26 #endif
     27 
     28 /**
     29 * True iff the RNG is currently replaced.  Prevents double-replacement.
     30 **/
     31 static bool rng_is_replaced = false;
     32 
     33 /**
     34 * Mutex to protect deterministic prng.
     35 *
     36 * Note that if you actually _use_ the prng from two threads at the same time,
     37 * the results will probably be nondeterministic anyway.
     38 */
     39 static tor_mutex_t *rng_mutex = NULL;
     40 
     41 /**
     42 * Cached old value for the thread prng.
     43 **/
     44 static crypto_fast_rng_t *stored_fast_rng = NULL;
     45 
     46 /** replacement for crypto_strongest_rand that delegates to crypto_rand. */
     47 static void
     48 mock_crypto_strongest_rand(uint8_t *out, size_t len)
     49 {
     50  crypto_rand((char *)out, len);
     51 }
     52 
     53 /* This is the seed of the deterministic randomness. */
     54 static uint8_t rng_seed[16];
     55 static crypto_xof_t *rng_xof = NULL;
     56 
     57 /**
     58 * Print the seed for our PRNG to stdout.  We use this when we're failed
     59 * test that had a reproducible RNG set.
     60 **/
     61 void
     62 testing_dump_reproducible_rng_seed(void)
     63 {
     64  printf("\n"
     65         "Seed: %s\n",
     66         hex_str((const char*)rng_seed, sizeof(rng_seed)));
     67 }
     68 
     69 /** Produce deterministic randomness for the stochastic tests using the global
     70 * rng_xof output.
     71 *
     72 * This function produces deterministic data over multiple calls iff it's
     73 * called in the same call order with the same 'n' parameter.
     74 * If not, outputs will deviate. */
     75 static void
     76 crypto_rand_deterministic(char *out, size_t n)
     77 {
     78  tor_assert(rng_xof);
     79  tor_mutex_acquire(rng_mutex);
     80  crypto_xof_squeeze_bytes(rng_xof, (uint8_t*)out, n);
     81  tor_mutex_release(rng_mutex);
     82 }
     83 
     84 /**
     85 * Implementation helper: override our crypto_rand() PRNG with a given seed of
     86 * length <b>seed_len</b>.  Overlong seeds are truncated; short ones are
     87 * padded.
     88 **/
     89 static void
     90 enable_deterministic_rng_impl(const uint8_t *seed, size_t seed_len)
     91 {
     92  tor_assert(!rng_is_replaced);
     93  tor_assert(crypto_rand == crypto_rand__real);
     94 
     95  memset(rng_seed, 0, sizeof(rng_seed));
     96  memcpy(rng_seed, seed, MIN(seed_len, sizeof(rng_seed)));
     97 
     98  rng_mutex = tor_mutex_new();
     99 
    100  crypto_xof_free(rng_xof);
    101  rng_xof = crypto_xof_new();
    102  crypto_xof_add_bytes(rng_xof, rng_seed, sizeof(rng_seed));
    103  MOCK(crypto_rand, crypto_rand_deterministic);
    104  MOCK(crypto_strongest_rand_, mock_crypto_strongest_rand);
    105 
    106  uint8_t fast_rng_seed[CRYPTO_FAST_RNG_SEED_LEN];
    107  memset(fast_rng_seed, 0xff, sizeof(fast_rng_seed));
    108  memcpy(fast_rng_seed, rng_seed, MIN(sizeof(rng_seed),
    109                                      sizeof(fast_rng_seed)));
    110  crypto_fast_rng_t *fast_rng = crypto_fast_rng_new_from_seed(fast_rng_seed);
    111  crypto_fast_rng_disable_reseed(fast_rng);
    112  stored_fast_rng = crypto_replace_thread_fast_rng(fast_rng);
    113 
    114  rng_is_replaced = true;
    115 }
    116 
    117 /**
    118 * Replace our get_thread_fast_rng(), crypto_rand() and
    119 * crypto_strongest_rand() prngs with a variant that generates all of its
    120 * output deterministically from a randomly chosen seed.  In the event of an
    121 * error, you can log the seed later on with
    122 * testing_dump_reproducible_rng_seed.
    123 **/
    124 void
    125 testing_enable_reproducible_rng(void)
    126 {
    127  const char *provided_seed = getenv("TOR_TEST_RNG_SEED");
    128  if (provided_seed) {
    129    size_t hexlen = strlen(provided_seed);
    130    size_t seedlen = hexlen / 2;
    131    uint8_t *seed = tor_malloc(hexlen / 2);
    132    if (base16_decode((char*)seed, seedlen, provided_seed, hexlen) < 0) {
    133      puts("Cannot decode value in TOR_TEST_RNG_SEED");
    134      exit(1);
    135    }
    136    enable_deterministic_rng_impl(seed, seedlen);
    137    tor_free(seed);
    138  } else {
    139    uint8_t seed[16];
    140    crypto_rand((char*)seed, sizeof(seed));
    141    enable_deterministic_rng_impl(seed, sizeof(seed));
    142  }
    143 }
    144 
    145 /**
    146 * Replace our get_thread_fast_rng(), crypto_rand() and
    147 * crypto_strongest_rand() prngs with a variant that generates all of its
    148 * output deterministically from a fixed seed.  This variant is mainly useful
    149 * for cases when we don't want coverage to change between runs.
    150 *
    151 * USAGE NOTE: Test correctness SHOULD NOT depend on the specific output of
    152 * this "rng".  If you need a specific output, use
    153 * testing_enable_prefilled_rng() instead.
    154 **/
    155 void
    156 testing_enable_deterministic_rng(void)
    157 {
    158  static const uint8_t quotation[] =
    159    "What will it be? A tree? A weed? "
    160    "Each one is started from a seed."; // -- Mary Ann Hoberman
    161  enable_deterministic_rng_impl(quotation, sizeof(quotation));
    162 }
    163 
    164 static uint8_t *prefilled_rng_buffer = NULL;
    165 static size_t prefilled_rng_buflen;
    166 static size_t prefilled_rng_idx;
    167 
    168 /**
    169 * crypto_rand() replacement that returns canned data.
    170 **/
    171 static void
    172 crypto_rand_prefilled(char *out, size_t n)
    173 {
    174  tor_mutex_acquire(rng_mutex);
    175  while (n) {
    176    size_t n_to_copy = MIN(prefilled_rng_buflen - prefilled_rng_idx, n);
    177    memcpy(out, prefilled_rng_buffer + prefilled_rng_idx, n_to_copy);
    178    out += n_to_copy;
    179    n -= n_to_copy;
    180    prefilled_rng_idx += n_to_copy;
    181 
    182    if (prefilled_rng_idx == prefilled_rng_buflen) {
    183      prefilled_rng_idx = 0;
    184    }
    185  }
    186  tor_mutex_release(rng_mutex);
    187 }
    188 
    189 /**
    190 * Replace our crypto_rand() and crypto_strongest_rand() prngs with a variant
    191 * that yields output from a buffer.  If it reaches the end of the buffer, it
    192 * starts over.
    193 *
    194 * Note: the get_thread_fast_rng() prng is not replaced by this; we'll need
    195 * more code to support that.
    196 **/
    197 void
    198 testing_enable_prefilled_rng(const void *buffer, size_t buflen)
    199 {
    200  tor_assert(buflen > 0);
    201  tor_assert(!rng_mutex);
    202  rng_mutex = tor_mutex_new();
    203 
    204  tor_mutex_acquire(rng_mutex);
    205 
    206  prefilled_rng_buffer = tor_memdup(buffer, buflen);
    207  prefilled_rng_buflen = buflen;
    208  prefilled_rng_idx = 0;
    209 
    210  tor_mutex_release(rng_mutex);
    211 
    212  MOCK(crypto_rand, crypto_rand_prefilled);
    213  MOCK(crypto_strongest_rand_, mock_crypto_strongest_rand);
    214 }
    215 
    216 /**
    217 * Reset the position in the prefilled RNG buffer to the start.
    218 */
    219 void
    220 testing_prefilled_rng_reset(void)
    221 {
    222  tor_mutex_acquire(rng_mutex);
    223  prefilled_rng_idx = 0;
    224  tor_mutex_release(rng_mutex);
    225 }
    226 
    227 /**
    228 * Undo the overrides for our PRNG.  To be used at the end of testing.
    229 *
    230 * Note that this function should be safe to call even if the rng has not
    231 * yet been replaced.
    232 **/
    233 void
    234 testing_disable_rng_override(void)
    235 {
    236  crypto_xof_free(rng_xof);
    237  tor_free(prefilled_rng_buffer);
    238  UNMOCK(crypto_rand);
    239  UNMOCK(crypto_strongest_rand_);
    240  tor_mutex_free(rng_mutex);
    241 
    242  crypto_fast_rng_t *rng = crypto_replace_thread_fast_rng(stored_fast_rng);
    243  crypto_fast_rng_free(rng);
    244 
    245  rng_is_replaced = false;
    246 }
    247 
    248 /**
    249 * As testing_disable_rng_override(), but dump the seed if the current
    250 * test has failed.
    251 */
    252 void
    253 testing_disable_reproducible_rng(void)
    254 {
    255  if (tinytest_cur_test_has_failed()) {
    256    testing_dump_reproducible_rng_seed();
    257  }
    258  testing_disable_rng_override();
    259 }