LCOV - code coverage report
Current view: top level - test - rng_test_helpers.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 77 94 81.9 %
Date: 2021-11-24 03:28:48 Functions: 9 11 81.8 %

          Line data    Source code
       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           1 : mock_crypto_strongest_rand(uint8_t *out, size_t len)
      49             : {
      50           1 :   crypto_rand((char *)out, len);
      51           1 : }
      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           0 : testing_dump_reproducible_rng_seed(void)
      63             : {
      64           0 :   printf("\n"
      65             :          "Seed: %s\n",
      66             :          hex_str((const char*)rng_seed, sizeof(rng_seed)));
      67           0 : }
      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        4239 : crypto_rand_deterministic(char *out, size_t n)
      77             : {
      78        4239 :   tor_assert(rng_xof);
      79        4239 :   tor_mutex_acquire(rng_mutex);
      80        4239 :   crypto_xof_squeeze_bytes(rng_xof, (uint8_t*)out, n);
      81        4239 :   tor_mutex_release(rng_mutex);
      82        4239 : }
      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          20 : enable_deterministic_rng_impl(const uint8_t *seed, size_t seed_len)
      91             : {
      92          20 :   tor_assert(!rng_is_replaced);
      93          20 :   tor_assert(crypto_rand == crypto_rand__real);
      94             : 
      95          20 :   memset(rng_seed, 0, sizeof(rng_seed));
      96          20 :   memcpy(rng_seed, seed, MIN(seed_len, sizeof(rng_seed)));
      97             : 
      98          20 :   rng_mutex = tor_mutex_new();
      99             : 
     100          20 :   crypto_xof_free(rng_xof);
     101          20 :   rng_xof = crypto_xof_new();
     102          20 :   crypto_xof_add_bytes(rng_xof, rng_seed, sizeof(rng_seed));
     103          20 :   MOCK(crypto_rand, crypto_rand_deterministic);
     104          20 :   MOCK(crypto_strongest_rand_, mock_crypto_strongest_rand);
     105             : 
     106          20 :   uint8_t fast_rng_seed[CRYPTO_FAST_RNG_SEED_LEN];
     107          20 :   memset(fast_rng_seed, 0xff, sizeof(fast_rng_seed));
     108          20 :   memcpy(fast_rng_seed, rng_seed, MIN(sizeof(rng_seed),
     109             :                                       sizeof(fast_rng_seed)));
     110          20 :   crypto_fast_rng_t *fast_rng = crypto_fast_rng_new_from_seed(fast_rng_seed);
     111          20 :   crypto_fast_rng_disable_reseed(fast_rng);
     112          20 :   stored_fast_rng = crypto_replace_thread_fast_rng(fast_rng);
     113             : 
     114          20 :   rng_is_replaced = true;
     115          20 : }
     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          19 : testing_enable_reproducible_rng(void)
     126             : {
     127          19 :   const char *provided_seed = getenv("TOR_TEST_RNG_SEED");
     128          19 :   if (provided_seed) {
     129           0 :     size_t hexlen = strlen(provided_seed);
     130           0 :     size_t seedlen = hexlen / 2;
     131           0 :     uint8_t *seed = tor_malloc(hexlen / 2);
     132           0 :     if (base16_decode((char*)seed, seedlen, provided_seed, hexlen) < 0) {
     133           0 :       puts("Cannot decode value in TOR_TEST_RNG_SEED");
     134           0 :       exit(1);
     135             :     }
     136           0 :     enable_deterministic_rng_impl(seed, seedlen);
     137           0 :     tor_free(seed);
     138             :   } else {
     139          19 :     uint8_t seed[16];
     140          19 :     crypto_rand((char*)seed, sizeof(seed));
     141          19 :     enable_deterministic_rng_impl(seed, sizeof(seed));
     142             :   }
     143          19 : }
     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           1 : testing_enable_deterministic_rng(void)
     157             : {
     158           1 :   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           1 :   enable_deterministic_rng_impl(quotation, sizeof(quotation));
     162           1 : }
     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        1023 : crypto_rand_prefilled(char *out, size_t n)
     173             : {
     174        1023 :   tor_mutex_acquire(rng_mutex);
     175        2061 :   while (n) {
     176        1038 :     size_t n_to_copy = MIN(prefilled_rng_buflen - prefilled_rng_idx, n);
     177        1038 :     memcpy(out, prefilled_rng_buffer + prefilled_rng_idx, n_to_copy);
     178        1038 :     out += n_to_copy;
     179        1038 :     n -= n_to_copy;
     180        1038 :     prefilled_rng_idx += n_to_copy;
     181             : 
     182        1038 :     if (prefilled_rng_idx == prefilled_rng_buflen) {
     183          25 :       prefilled_rng_idx = 0;
     184             :     }
     185             :   }
     186        1023 :   tor_mutex_release(rng_mutex);
     187        1023 : }
     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          11 : testing_enable_prefilled_rng(const void *buffer, size_t buflen)
     199             : {
     200          11 :   tor_assert(buflen > 0);
     201          11 :   tor_assert(!rng_mutex);
     202          11 :   rng_mutex = tor_mutex_new();
     203             : 
     204          11 :   tor_mutex_acquire(rng_mutex);
     205             : 
     206          11 :   prefilled_rng_buffer = tor_memdup(buffer, buflen);
     207          11 :   prefilled_rng_buflen = buflen;
     208          11 :   prefilled_rng_idx = 0;
     209             : 
     210          11 :   tor_mutex_release(rng_mutex);
     211             : 
     212          11 :   MOCK(crypto_rand, crypto_rand_prefilled);
     213          11 :   MOCK(crypto_strongest_rand_, mock_crypto_strongest_rand);
     214          11 : }
     215             : 
     216             : /**
     217             :  * Reset the position in the prefilled RNG buffer to the start.
     218             :  */
     219             : void
     220           0 : testing_prefilled_rng_reset(void)
     221             : {
     222           0 :   tor_mutex_acquire(rng_mutex);
     223           0 :   prefilled_rng_idx = 0;
     224           0 :   tor_mutex_release(rng_mutex);
     225           0 : }
     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          32 : testing_disable_rng_override(void)
     235             : {
     236          32 :   crypto_xof_free(rng_xof);
     237          32 :   tor_free(prefilled_rng_buffer);
     238          32 :   UNMOCK(crypto_rand);
     239          32 :   UNMOCK(crypto_strongest_rand_);
     240          32 :   tor_mutex_free(rng_mutex);
     241             : 
     242          32 :   crypto_fast_rng_t *rng = crypto_replace_thread_fast_rng(stored_fast_rng);
     243          32 :   crypto_fast_rng_free(rng);
     244             : 
     245          32 :   rng_is_replaced = false;
     246          32 : }
     247             : 
     248             : /**
     249             :  * As testing_disable_rng_override(), but dump the seed if the current
     250             :  * test has failed.
     251             :  */
     252             : void
     253          19 : testing_disable_reproducible_rng(void)
     254             : {
     255          19 :   if (tinytest_cur_test_has_failed()) {
     256           0 :     testing_dump_reproducible_rng_seed();
     257             :   }
     258          19 :   testing_disable_rng_override();
     259          19 : }

Generated by: LCOV version 1.14