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 : }
|