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 }