Branch data Line data Source code
1 : : /*
2 : : * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License").
5 : : * You may not use this file except in compliance with the License.
6 : : * A copy of the License is located at
7 : : *
8 : : * http://aws.amazon.com/apache2.0
9 : : *
10 : : * or in the "license" file accompanying this file. This file is distributed
11 : : * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 : : * express or implied. See the License for the specific language governing
13 : : * permissions and limitations under the License.
14 : : */
15 : :
16 : : /*
17 : : * _XOPEN_SOURCE is needed for resolving the constant O_CLOEXEC in some
18 : : * environments. We use _XOPEN_SOURCE instead of _GNU_SOURCE because
19 : : * _GNU_SOURCE is not portable and breaks when attempting to build rust
20 : : * bindings on MacOS.
21 : : *
22 : : * https://man7.org/linux/man-pages/man2/open.2.html
23 : : * The O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW flags are not
24 : : * specified in POSIX.1-2001, but are specified in POSIX.1-2008.
25 : : * Since glibc 2.12, one can obtain their definitions by defining
26 : : * either _POSIX_C_SOURCE with a value greater than or equal to
27 : : * 200809L or _XOPEN_SOURCE with a value greater than or equal to
28 : : * 700. In glibc 2.11 and earlier, one obtains the definitions by
29 : : * defining _GNU_SOURCE.
30 : : *
31 : : * We use two feature probes to detect the need to perform this workaround.
32 : : * It is only applied if we can't get CLOEXEC without it and the build doesn't
33 : : * fail with _XOPEN_SOURCE being defined.
34 : : *
35 : : * # Relevent Links
36 : : *
37 : : * - POSIX.1-2017: https://pubs.opengroup.org/onlinepubs/9699919799
38 : : * - https://stackoverflow.com/a/5724485
39 : : * - https://stackoverflow.com/a/5583764
40 : : */
41 : : #if !defined(S2N_CLOEXEC_SUPPORTED) && defined(S2N_CLOEXEC_XOPEN_SUPPORTED) && !defined(_XOPEN_SOURCE)
42 : : #define _XOPEN_SOURCE 700
43 : : #include <fcntl.h>
44 : : #undef _XOPEN_SOURCE
45 : : #else
46 : : #include <fcntl.h>
47 : : #endif
48 : : #include <errno.h>
49 : : #include <limits.h>
50 : : #if S2N_LIBCRYPTO_SUPPORTS_ENGINE
51 : : #include <openssl/engine.h>
52 : : #endif
53 : : /* LibreSSL requires <openssl/rand.h> include.
54 : : * https://github.com/aws/s2n-tls/issues/153#issuecomment-129651643
55 : : */
56 : : #include <openssl/rand.h>
57 : : #include <pthread.h>
58 : : #include <stdint.h>
59 : : #include <stdlib.h>
60 : : #include <string.h>
61 : : #include <sys/param.h>
62 : : #include <sys/stat.h>
63 : : #include <sys/types.h>
64 : : #include <time.h>
65 : : #include <unistd.h>
66 : :
67 : : #if defined(S2N_CPUID_AVAILABLE)
68 : : #include <cpuid.h>
69 : : #endif
70 : :
71 : : #include "api/s2n.h"
72 : : #include "crypto/s2n_drbg.h"
73 : : #include "crypto/s2n_fips.h"
74 : : #include "crypto/s2n_libcrypto.h"
75 : : #include "error/s2n_errno.h"
76 : : #include "s2n_io.h"
77 : : #include "stuffer/s2n_stuffer.h"
78 : : #include "utils/s2n_fork_detection.h"
79 : : #include "utils/s2n_init.h"
80 : : #include "utils/s2n_mem.h"
81 : : #include "utils/s2n_random.h"
82 : : #include "utils/s2n_result.h"
83 : : #include "utils/s2n_safety.h"
84 : :
85 : : const char s2n_rand_engine_id[] = "s2n_rand";
86 : :
87 : : #if defined(O_CLOEXEC)
88 : : #define ENTROPY_FLAGS O_RDONLY | O_CLOEXEC
89 : : #else
90 : : #define ENTROPY_FLAGS O_RDONLY
91 : : #endif
92 : :
93 : : /* See https://en.wikipedia.org/wiki/CPUID */
94 : 545 : #define RDRAND_ECX_FLAG 0x40000000
95 : :
96 : : /* One second in nanoseconds */
97 : : #define ONE_S INT64_C(1000000000)
98 : :
99 : : /* Placeholder value for an uninitialized entropy file descriptor */
100 : 1641 : #define UNINITIALIZED_ENTROPY_FD -1
101 : :
102 : : static struct s2n_rand_device s2n_dev_urandom = {
103 : : .source = "/dev/urandom",
104 : : .fd = UNINITIALIZED_ENTROPY_FD,
105 : : };
106 : :
107 : : struct s2n_rand_state {
108 : : uint64_t cached_fork_generation_number;
109 : : struct s2n_drbg public_drbg;
110 : : struct s2n_drbg private_drbg;
111 : : bool drbgs_initialized;
112 : : };
113 : :
114 : : /* Key which will control per-thread freeing of drbg memory */
115 : : static pthread_key_t s2n_per_thread_rand_state_key;
116 : : /* Needed to ensure key is initialized only once */
117 : : static pthread_once_t s2n_per_thread_rand_state_key_once = PTHREAD_ONCE_INIT;
118 : : /* Tracks if call to pthread_key_create failed */
119 : : static int pthread_key_create_result;
120 : :
121 : : static __thread struct s2n_rand_state s2n_per_thread_rand_state = {
122 : : .cached_fork_generation_number = 0,
123 : : .public_drbg = { 0 },
124 : : .private_drbg = { 0 },
125 : : .drbgs_initialized = false
126 : : };
127 : :
128 : : static int s2n_rand_init_cb_impl(void);
129 : : static int s2n_rand_cleanup_cb_impl(void);
130 : : static int s2n_rand_get_entropy_from_urandom(void *ptr, uint32_t size);
131 : : static int s2n_rand_get_entropy_from_rdrand(void *ptr, uint32_t size);
132 : :
133 : : static s2n_rand_init_callback s2n_rand_init_cb = s2n_rand_init_cb_impl;
134 : : static s2n_rand_cleanup_callback s2n_rand_cleanup_cb = s2n_rand_cleanup_cb_impl;
135 : : static s2n_rand_seed_callback s2n_rand_seed_cb = s2n_rand_get_entropy_from_urandom;
136 : : static s2n_rand_mix_callback s2n_rand_mix_cb = s2n_rand_get_entropy_from_urandom;
137 : :
138 : : static int s2n_rand_entropy_fd_close_ptr(int *fd)
139 : 548 : {
140 [ + - ][ - + ]: 548 : if (fd && *fd != UNINITIALIZED_ENTROPY_FD) {
141 : 0 : close(*fd);
142 : 0 : }
143 : 548 : return S2N_SUCCESS;
144 : 548 : }
145 : :
146 : : /* non-static for SAW proof */
147 : : bool s2n_cpu_supports_rdrand()
148 : 545 : {
149 : 545 : #if defined(S2N_CPUID_AVAILABLE)
150 : 545 : uint32_t eax, ebx, ecx, edx;
151 [ - + ]: 545 : if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
152 : 0 : return false;
153 : 0 : }
154 : :
155 [ + - ]: 545 : if (ecx & RDRAND_ECX_FLAG) {
156 : 545 : return true;
157 : 545 : }
158 : 0 : #endif
159 : 0 : return false;
160 : 545 : }
161 : :
162 : : int s2n_rand_set_callbacks(s2n_rand_init_callback rand_init_callback,
163 : : s2n_rand_cleanup_callback rand_cleanup_callback,
164 : : s2n_rand_seed_callback rand_seed_callback,
165 : : s2n_rand_mix_callback rand_mix_callback)
166 : 309 : {
167 [ + + ][ + - ]: 309 : POSIX_ENSURE_REF(rand_init_callback);
168 [ + - ][ + + ]: 239 : POSIX_ENSURE_REF(rand_cleanup_callback);
169 [ + - ][ + + ]: 169 : POSIX_ENSURE_REF(rand_seed_callback);
170 [ + + ][ + - ]: 99 : POSIX_ENSURE_REF(rand_mix_callback);
171 : 29 : s2n_rand_init_cb = rand_init_callback;
172 : 29 : s2n_rand_cleanup_cb = rand_cleanup_callback;
173 : 29 : s2n_rand_seed_cb = rand_seed_callback;
174 : 29 : s2n_rand_mix_cb = rand_mix_callback;
175 : :
176 : 29 : return S2N_SUCCESS;
177 : 99 : }
178 : :
179 : : S2N_RESULT s2n_get_seed_entropy(struct s2n_blob *blob)
180 : 6776 : {
181 [ - + ][ # # ]: 6776 : RESULT_ENSURE_REF(blob);
182 : :
183 [ - + ][ # # ]: 6776 : RESULT_ENSURE(s2n_rand_seed_cb(blob->data, blob->size) >= S2N_SUCCESS, S2N_ERR_CANCELLED);
184 : :
185 : 6776 : return S2N_RESULT_OK;
186 : 6776 : }
187 : :
188 : : S2N_RESULT s2n_get_mix_entropy(struct s2n_blob *blob)
189 : 1749424 : {
190 [ - + ][ # # ]: 1749424 : RESULT_ENSURE_REF(blob);
191 : :
192 [ - + ]: 1749424 : RESULT_GUARD_POSIX(s2n_rand_mix_cb(blob->data, blob->size));
193 : :
194 : 1749424 : return S2N_RESULT_OK;
195 : 1749424 : }
196 : :
197 : : /* Deletes pthread key at process-exit */
198 : : static void __attribute__((destructor)) s2n_drbg_rand_state_key_cleanup(void)
199 : 522 : {
200 [ + + ]: 522 : if (s2n_is_initialized()) {
201 : 4 : pthread_key_delete(s2n_per_thread_rand_state_key);
202 : 4 : }
203 : 522 : }
204 : :
205 : : static void s2n_drbg_destructor(void *_unused_argument)
206 : 1 : {
207 : 1 : (void) _unused_argument;
208 : :
209 : 1 : s2n_result_ignore(s2n_rand_cleanup_thread());
210 : 1 : }
211 : :
212 : : static void s2n_drbg_make_rand_state_key(void)
213 : 516 : {
214 : : /* We can't return the output of pthread_key_create due to the parameter constraints of pthread_once.
215 : : * Here we store the result in a global variable that will be error checked later. */
216 : 516 : pthread_key_create_result = pthread_key_create(&s2n_per_thread_rand_state_key, s2n_drbg_destructor);
217 : 516 : }
218 : :
219 : : static S2N_RESULT s2n_init_drbgs(void)
220 : 3449 : {
221 : 3449 : uint8_t s2n_public_drbg[] = "s2n public drbg";
222 : 3449 : uint8_t s2n_private_drbg[] = "s2n private drbg";
223 : 3449 : struct s2n_blob public = { 0 };
224 [ - + ]: 3449 : RESULT_GUARD_POSIX(s2n_blob_init(&public, s2n_public_drbg, sizeof(s2n_public_drbg)));
225 : 3449 : struct s2n_blob private = { 0 };
226 [ - + ]: 3449 : RESULT_GUARD_POSIX(s2n_blob_init(&private, s2n_private_drbg, sizeof(s2n_private_drbg)));
227 : :
228 [ - + ][ # # ]: 3449 : RESULT_ENSURE(pthread_once(&s2n_per_thread_rand_state_key_once, s2n_drbg_make_rand_state_key) == 0, S2N_ERR_DRBG);
229 [ # # ][ - + ]: 3449 : RESULT_ENSURE_EQ(pthread_key_create_result, 0);
230 : :
231 [ - + ]: 3449 : RESULT_GUARD(s2n_drbg_instantiate(&s2n_per_thread_rand_state.public_drbg, &public, S2N_AES_128_CTR_NO_DF_PR));
232 [ - + ]: 3449 : RESULT_GUARD(s2n_drbg_instantiate(&s2n_per_thread_rand_state.private_drbg, &private, S2N_AES_256_CTR_NO_DF_PR));
233 : :
234 [ - + ][ # # ]: 3449 : RESULT_ENSURE(pthread_setspecific(s2n_per_thread_rand_state_key, &s2n_per_thread_rand_state) == 0, S2N_ERR_DRBG);
235 : :
236 : 3449 : s2n_per_thread_rand_state.drbgs_initialized = true;
237 : :
238 : 3449 : return S2N_RESULT_OK;
239 : 3449 : }
240 : :
241 : : static S2N_RESULT s2n_ensure_initialized_drbgs(void)
242 : 1222773 : {
243 [ + + ]: 1222773 : if (s2n_per_thread_rand_state.drbgs_initialized == false) {
244 [ - + ]: 3450 : RESULT_GUARD(s2n_init_drbgs());
245 : :
246 : : /* Then cache the fork generation number. We just initialized the drbg
247 : : * states with new entropy and forking is not an external event.
248 : : */
249 : 3450 : uint64_t returned_fork_generation_number = 0;
250 [ - + ]: 3450 : RESULT_GUARD(s2n_get_fork_generation_number(&returned_fork_generation_number));
251 : 3450 : s2n_per_thread_rand_state.cached_fork_generation_number = returned_fork_generation_number;
252 : 3450 : }
253 : :
254 : 1222773 : return S2N_RESULT_OK;
255 : 1222773 : }
256 : :
257 : : /* s2n_ensure_uniqueness() implements defenses against uniqueness
258 : : * breaking events that might cause duplicated drbg states. Currently, only
259 : : * implements fork detection.
260 : : */
261 : : static S2N_RESULT s2n_ensure_uniqueness(void)
262 : 1221955 : {
263 : 1221955 : uint64_t returned_fork_generation_number = 0;
264 [ - + ]: 1221955 : RESULT_GUARD(s2n_get_fork_generation_number(&returned_fork_generation_number));
265 : :
266 [ + + ]: 1221955 : if (returned_fork_generation_number != s2n_per_thread_rand_state.cached_fork_generation_number) {
267 : : /* This assumes that s2n_rand_cleanup_thread() doesn't mutate any other
268 : : * state than the drbg states and it resets the drbg initialization
269 : : * boolean to false. s2n_ensure_initialized_drbgs() will cache the new
270 : : * fork generation number in the per thread state.
271 : : */
272 [ - + ]: 201 : RESULT_GUARD(s2n_rand_cleanup_thread());
273 [ - + ]: 201 : RESULT_GUARD(s2n_ensure_initialized_drbgs());
274 : 201 : }
275 : :
276 : 1221955 : return S2N_RESULT_OK;
277 : 1221955 : }
278 : :
279 : : static S2N_RESULT s2n_get_libcrypto_private_random_data(struct s2n_blob *out_blob)
280 : 0 : {
281 [ # # ]: 0 : RESULT_GUARD_PTR(out_blob);
282 : 0 : #if S2N_LIBCRYPTO_SUPPORTS_PRIVATE_RAND
283 [ # # ][ # # ]: 0 : RESULT_GUARD_OSSL(RAND_priv_bytes(out_blob->data, out_blob->size), S2N_ERR_DRBG);
284 : : #else
285 : : RESULT_GUARD_OSSL(RAND_bytes(out_blob->data, out_blob->size), S2N_ERR_DRBG);
286 : : #endif
287 : 0 : return S2N_RESULT_OK;
288 : 0 : }
289 : :
290 : : static S2N_RESULT s2n_get_libcrypto_random_data(struct s2n_blob *out_blob)
291 : 0 : {
292 [ # # ]: 0 : RESULT_GUARD_PTR(out_blob);
293 [ # # ][ # # ]: 0 : RESULT_GUARD_OSSL(RAND_bytes(out_blob->data, out_blob->size), S2N_ERR_DRBG);
294 : 0 : return S2N_RESULT_OK;
295 : 0 : }
296 : :
297 : : static S2N_RESULT s2n_get_custom_random_data(struct s2n_blob *out_blob, struct s2n_drbg *drbg_state)
298 : 1222000 : {
299 [ - + ]: 1222000 : RESULT_GUARD_PTR(out_blob);
300 [ - + ]: 1222000 : RESULT_GUARD_PTR(drbg_state);
301 : :
302 [ # # ][ - + ]: 1222000 : RESULT_ENSURE(!s2n_is_in_fips_mode(), S2N_ERR_DRBG);
303 [ - + ]: 1222000 : RESULT_GUARD(s2n_ensure_initialized_drbgs());
304 [ - + ]: 1222000 : RESULT_GUARD(s2n_ensure_uniqueness());
305 : :
306 : 1222000 : uint32_t offset = 0;
307 : 1222000 : uint32_t remaining = out_blob->size;
308 : :
309 [ + + ]: 2480171 : while (remaining) {
310 : 1258171 : struct s2n_blob slice = { 0 };
311 : :
312 [ - + ][ + + ]: 1258171 : RESULT_GUARD_POSIX(s2n_blob_slice(out_blob, &slice, offset, MIN(remaining, S2N_DRBG_GENERATE_LIMIT)));
313 [ - + ]: 1258171 : RESULT_GUARD(s2n_drbg_generate(drbg_state, &slice));
314 : :
315 : 1258171 : remaining -= slice.size;
316 : 1258171 : offset += slice.size;
317 : 1258171 : }
318 : :
319 : 1222000 : return S2N_RESULT_OK;
320 : 1222000 : }
321 : :
322 : : S2N_RESULT s2n_get_public_random_data(struct s2n_blob *blob)
323 : 815024 : {
324 [ - + ]: 815024 : if (s2n_is_in_fips_mode()) {
325 [ # # ]: 0 : RESULT_GUARD(s2n_get_libcrypto_random_data(blob));
326 : 815024 : } else {
327 [ - + ]: 815024 : RESULT_GUARD(s2n_get_custom_random_data(blob, &s2n_per_thread_rand_state.public_drbg));
328 : 815024 : }
329 : 815024 : return S2N_RESULT_OK;
330 : 815024 : }
331 : :
332 : : S2N_RESULT s2n_get_private_random_data(struct s2n_blob *blob)
333 : 406971 : {
334 [ - + ]: 406971 : if (s2n_is_in_fips_mode()) {
335 [ # # ]: 0 : RESULT_GUARD(s2n_get_libcrypto_private_random_data(blob));
336 : 406971 : } else {
337 [ - + ]: 406971 : RESULT_GUARD(s2n_get_custom_random_data(blob, &s2n_per_thread_rand_state.private_drbg));
338 : 406971 : }
339 : 406971 : return S2N_RESULT_OK;
340 : 406971 : }
341 : :
342 : : S2N_RESULT s2n_get_public_random_bytes_used(uint64_t *bytes_used)
343 : 112 : {
344 [ - + ]: 112 : RESULT_GUARD(s2n_drbg_bytes_used(&s2n_per_thread_rand_state.public_drbg, bytes_used));
345 : 112 : return S2N_RESULT_OK;
346 : 112 : }
347 : :
348 : : S2N_RESULT s2n_get_private_random_bytes_used(uint64_t *bytes_used)
349 : 114 : {
350 [ - + ]: 114 : RESULT_GUARD(s2n_drbg_bytes_used(&s2n_per_thread_rand_state.private_drbg, bytes_used));
351 : 114 : return S2N_RESULT_OK;
352 : 114 : }
353 : :
354 : : S2N_RESULT s2n_rand_get_urandom_for_test(struct s2n_rand_device **device)
355 : 1 : {
356 [ # # ][ - + ]: 1 : RESULT_ENSURE_REF(device);
357 [ - + ][ # # ]: 1 : RESULT_ENSURE(s2n_in_unit_test(), S2N_ERR_NOT_IN_UNIT_TEST);
358 : 1 : *device = &s2n_dev_urandom;
359 : 1 : return S2N_RESULT_OK;
360 : 1 : }
361 : :
362 : : static S2N_RESULT s2n_rand_device_open(struct s2n_rand_device *device)
363 : 548 : {
364 [ - + ][ # # ]: 548 : RESULT_ENSURE_REF(device);
365 [ - + ][ # # ]: 548 : RESULT_ENSURE_REF(device->source);
366 : :
367 : 548 : DEFER_CLEANUP(int fd = -1, s2n_rand_entropy_fd_close_ptr);
368 [ - + ][ # # ]: 548 : S2N_IO_RETRY_EINTR(fd, open(device->source, ENTROPY_FLAGS));
369 [ - + ][ # # ]: 548 : RESULT_ENSURE(fd >= 0, S2N_ERR_OPEN_RANDOM);
370 : :
371 : 548 : struct stat st = { 0 };
372 [ # # ][ - + ]: 548 : RESULT_ENSURE(fstat(fd, &st) == 0, S2N_ERR_OPEN_RANDOM);
373 : 548 : device->dev = st.st_dev;
374 : 548 : device->ino = st.st_ino;
375 : 548 : device->mode = st.st_mode;
376 : 548 : device->rdev = st.st_rdev;
377 : :
378 : 548 : device->fd = fd;
379 : :
380 : : /* Disable closing the file descriptor with defer cleanup */
381 : 548 : fd = UNINITIALIZED_ENTROPY_FD;
382 : :
383 : 548 : return S2N_RESULT_OK;
384 : 548 : }
385 : :
386 : : S2N_RESULT s2n_rand_device_validate(struct s2n_rand_device *device)
387 : 7400 : {
388 [ # # ][ - + ]: 7400 : RESULT_ENSURE_REF(device);
389 [ + + ][ + - ]: 7400 : RESULT_ENSURE_NE(device->fd, UNINITIALIZED_ENTROPY_FD);
390 : :
391 : : /* Ensure that the random device is still valid by comparing it to the current file descriptor
392 : : * status. From:
393 : : * https://github.com/openssl/openssl/blob/260d97229c467d17934ca3e2e0455b1b5c0994a6/providers/implementations/rands/seeding/rand_unix.c#L513
394 : : */
395 : 7398 : struct stat st = { 0 };
396 [ + - ][ + + ]: 7398 : RESULT_ENSURE(fstat(device->fd, &st) == 0, S2N_ERR_OPEN_RANDOM);
397 [ + - ][ + + ]: 7395 : RESULT_ENSURE_EQ(device->dev, st.st_dev);
398 [ - + ][ # # ]: 7393 : RESULT_ENSURE_EQ(device->ino, st.st_ino);
399 [ - + ][ # # ]: 7393 : RESULT_ENSURE_EQ(device->rdev, st.st_rdev);
400 : :
401 : : /* Ensure that the mode is the same (equal to 0 when xor'd), but don't check the permission bits. */
402 : 7393 : mode_t permission_mask = ~(S_IRWXU | S_IRWXG | S_IRWXO);
403 [ # # ][ - + ]: 7393 : RESULT_ENSURE_EQ((device->mode ^ st.st_mode) & permission_mask, 0);
404 : :
405 : 7393 : return S2N_RESULT_OK;
406 : 7393 : }
407 : :
408 : : static int s2n_rand_get_entropy_from_urandom(void *ptr, uint32_t size)
409 : 6890 : {
410 [ - + ][ # # ]: 6890 : POSIX_ENSURE_REF(ptr);
411 [ - + ][ # # ]: 6890 : POSIX_ENSURE(s2n_dev_urandom.fd != UNINITIALIZED_ENTROPY_FD, S2N_ERR_NOT_INITIALIZED);
412 : :
413 : : /* It's possible that the file descriptor pointing to /dev/urandom was closed or changed from
414 : : * when it was last opened. Ensure that the file descriptor is still valid, and if it isn't,
415 : : * re-open it before getting entropy.
416 : : *
417 : : * If the file descriptor is invalid and the process doesn't have access to /dev/urandom (as is
418 : : * the case within a chroot tree), an error is raised here before attempting to indefinitely
419 : : * read.
420 : : */
421 [ + + ]: 6890 : if (s2n_result_is_error(s2n_rand_device_validate(&s2n_dev_urandom))) {
422 [ - + ]: 3 : POSIX_GUARD_RESULT(s2n_rand_device_open(&s2n_dev_urandom));
423 : 3 : }
424 : :
425 : 6890 : uint8_t *data = ptr;
426 : 6890 : uint32_t n = size;
427 : 6890 : struct timespec sleep_time = { .tv_sec = 0, .tv_nsec = 0 };
428 : 6890 : long backoff = 1;
429 : :
430 [ + + ]: 13586 : while (n) {
431 : 6696 : errno = 0;
432 : 6696 : int r = read(s2n_dev_urandom.fd, data, n);
433 [ - + ]: 6696 : if (r <= 0) {
434 : : /*
435 : : * A non-blocking read() on /dev/urandom should "never" fail,
436 : : * except for EINTR. If it does, briefly pause and use
437 : : * exponential backoff to avoid creating a tight spinning loop.
438 : : *
439 : : * iteration delay
440 : : * --------- -----------------
441 : : * 1 10 nsec
442 : : * 2 100 nsec
443 : : * 3 1,000 nsec
444 : : * 4 10,000 nsec
445 : : * 5 100,000 nsec
446 : : * 6 1,000,000 nsec
447 : : * 7 10,000,000 nsec
448 : : * 8 99,999,999 nsec
449 : : * 9 99,999,999 nsec
450 : : * ...
451 : : */
452 [ # # ]: 0 : if (errno != EINTR) {
453 : 0 : backoff = MIN(backoff * 10, ONE_S - 1);
454 : 0 : sleep_time.tv_nsec = backoff;
455 : 0 : do {
456 : 0 : r = nanosleep(&sleep_time, &sleep_time);
457 [ # # ]: 0 : } while (r != 0);
458 : 0 : }
459 : :
460 : 0 : continue;
461 : 0 : }
462 : :
463 : 6696 : data += r;
464 : 6696 : n -= r;
465 : 6696 : }
466 : :
467 : 6890 : return S2N_SUCCESS;
468 : 6890 : }
469 : :
470 : : /*
471 : : * Return a random number in the range [0, bound)
472 : : */
473 : : S2N_RESULT s2n_public_random(int64_t bound, uint64_t *output)
474 : 119345 : {
475 : 119345 : uint64_t r = 0;
476 : :
477 [ + - ][ + + ]: 119345 : RESULT_ENSURE_GT(bound, 0);
478 : :
479 : 133095 : while (1) {
480 : 133095 : struct s2n_blob blob = { 0 };
481 [ - + ]: 133095 : RESULT_GUARD_POSIX(s2n_blob_init(&blob, (void *) &r, sizeof(r)));
482 [ - + ]: 133095 : RESULT_GUARD(s2n_get_public_random_data(&blob));
483 : :
484 : : /* Imagine an int was one byte and UINT_MAX was 256. If the
485 : : * caller asked for s2n_random(129, ...) we'd end up in
486 : : * trouble. Each number in the range 0...127 would be twice
487 : : * as likely as 128. That's because r == 0 % 129 -> 0, and
488 : : * r == 129 % 129 -> 0, but only r == 128 returns 128,
489 : : * r == 257 is out of range.
490 : : *
491 : : * To de-bias the dice, we discard values of r that are higher
492 : : * that the highest multiple of 'bound' an int can support. If
493 : : * bound is a uint, then in the worst case we discard 50% - 1 r's.
494 : : * But since 'bound' is an int and INT_MAX is <= UINT_MAX / 2,
495 : : * in the worst case we discard 25% - 1 r's.
496 : : */
497 [ + + ]: 133095 : if (r < (UINT64_MAX - (UINT64_MAX % bound))) {
498 : 119290 : *output = r % bound;
499 : 119290 : return S2N_RESULT_OK;
500 : 119290 : }
501 : 133095 : }
502 : 119290 : }
503 : :
504 : : int s2n_openssl_compat_rand(unsigned char *buf, int num)
505 : 39953 : {
506 : 39953 : struct s2n_blob out = { 0 };
507 [ - + ]: 39953 : POSIX_GUARD(s2n_blob_init(&out, buf, num));
508 : :
509 [ - + ]: 39953 : if (s2n_result_is_error(s2n_get_private_random_data(&out))) {
510 : 0 : return 0;
511 : 0 : }
512 : 39953 : return 1;
513 : 39953 : }
514 : :
515 : : int s2n_openssl_compat_status(void)
516 : 0 : {
517 : 0 : return 1;
518 : 0 : }
519 : :
520 : : #if S2N_LIBCRYPTO_SUPPORTS_ENGINE
521 : : int s2n_openssl_compat_init(ENGINE *unused)
522 : 574 : {
523 : 574 : return 1;
524 : 574 : }
525 : :
526 : : /* RAND_METHOD is gated behind S2N_LIBCRYPTO_SUPPORTS_ENGINE because AWS-LC has
527 : : * a different signature for RAND_METHOD and fails to compile.
528 : : *
529 : : * - AWS-LC: https://github.com/aws/aws-lc/blob/main/include/openssl/rand.h#L124
530 : : * - OpenSSL: https://github.com/openssl/openssl/blob/master/include/openssl/rand.h#L42
531 : : */
532 : : RAND_METHOD s2n_openssl_rand_method = {
533 : : .seed = NULL,
534 : : .bytes = s2n_openssl_compat_rand,
535 : : .cleanup = NULL,
536 : : .add = NULL,
537 : : .pseudorand = s2n_openssl_compat_rand,
538 : : .status = s2n_openssl_compat_status
539 : : };
540 : : #endif
541 : :
542 : : static int s2n_rand_init_cb_impl(void)
543 : 545 : {
544 : : /* Currently, s2n-tls may mix in entropy from urandom into every generation of random data. The
545 : : * file descriptor is opened on initialization for better performance reading from urandom, and
546 : : * to ensure that urandom is accessible from within a chroot tree.
547 : : */
548 [ - + ]: 545 : POSIX_GUARD_RESULT(s2n_rand_device_open(&s2n_dev_urandom));
549 : :
550 [ + - ]: 545 : if (s2n_cpu_supports_rdrand()) {
551 : 545 : s2n_rand_mix_cb = s2n_rand_get_entropy_from_rdrand;
552 : 545 : }
553 : :
554 : 545 : return S2N_SUCCESS;
555 : 545 : }
556 : :
557 : : bool s2n_supports_custom_rand(void)
558 : 1327 : {
559 : : #if !defined(S2N_LIBCRYPTO_SUPPORTS_ENGINE) || defined(OPENSSL_FIPS)
560 : : /* OpenSSL 1.0.2-fips is excluded to match historical behavior */
561 : : /* OPENSSL_FIPS is only defined for 1.0.2-fips, not 3.x-fips */
562 : : return false;
563 : : #elif defined(S2N_DISABLE_RAND_ENGINE_OVERRIDE)
564 : : return false;
565 : : #else
566 [ + - ][ + - ]: 1327 : return s2n_libcrypto_is_openssl() && !s2n_is_in_fips_mode();
567 : 1327 : #endif
568 : 1327 : }
569 : :
570 : : S2N_RESULT s2n_rand_init(void)
571 : 574 : {
572 [ - + ][ # # ]: 574 : RESULT_ENSURE(s2n_rand_init_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
573 : :
574 [ - + ]: 574 : RESULT_GUARD(s2n_ensure_initialized_drbgs());
575 : :
576 : 574 : #if S2N_LIBCRYPTO_SUPPORTS_ENGINE
577 [ + - ]: 574 : if (s2n_supports_custom_rand()) {
578 : : /* Unset any existing random engine */
579 [ # # ][ - + ]: 574 : RESULT_GUARD_OSSL(RAND_set_rand_engine(NULL), S2N_ERR_OPEN_RANDOM);
580 : :
581 : : /* Create an engine */
582 : 574 : ENGINE *e = ENGINE_new();
583 : :
584 : : /* Initialize the engine */
585 [ - + ][ # # ]: 574 : RESULT_ENSURE(e != NULL, S2N_ERR_OPEN_RANDOM);
586 [ - + ][ # # ]: 574 : RESULT_GUARD_OSSL(ENGINE_set_id(e, s2n_rand_engine_id), S2N_ERR_OPEN_RANDOM);
587 [ - + ][ # # ]: 574 : RESULT_GUARD_OSSL(ENGINE_set_name(e, "s2n entropy generator"), S2N_ERR_OPEN_RANDOM);
588 [ - + ][ # # ]: 574 : RESULT_GUARD_OSSL(ENGINE_set_flags(e, ENGINE_FLAGS_NO_REGISTER_ALL), S2N_ERR_OPEN_RANDOM);
589 [ - + ][ # # ]: 574 : RESULT_GUARD_OSSL(ENGINE_set_init_function(e, s2n_openssl_compat_init), S2N_ERR_OPEN_RANDOM);
590 [ - + ][ # # ]: 574 : RESULT_GUARD_OSSL(ENGINE_set_RAND(e, &s2n_openssl_rand_method), S2N_ERR_OPEN_RANDOM);
591 [ - + ][ # # ]: 574 : RESULT_GUARD_OSSL(ENGINE_add(e), S2N_ERR_OPEN_RANDOM);
592 [ - + ][ # # ]: 574 : RESULT_GUARD_OSSL(ENGINE_free(e), S2N_ERR_OPEN_RANDOM);
593 : :
594 : : /* Use that engine for rand() */
595 : 574 : e = ENGINE_by_id(s2n_rand_engine_id);
596 [ - + ][ # # ]: 574 : RESULT_ENSURE(e != NULL, S2N_ERR_OPEN_RANDOM);
597 [ # # ][ - + ]: 574 : RESULT_GUARD_OSSL(ENGINE_init(e), S2N_ERR_OPEN_RANDOM);
598 [ - + ][ # # ]: 574 : RESULT_GUARD_OSSL(ENGINE_set_default(e, ENGINE_METHOD_RAND), S2N_ERR_OPEN_RANDOM);
599 [ - + ][ # # ]: 574 : RESULT_GUARD_OSSL(ENGINE_free(e), S2N_ERR_OPEN_RANDOM);
600 : 574 : }
601 : 574 : #endif
602 : :
603 : 574 : return S2N_RESULT_OK;
604 : 574 : }
605 : :
606 : : static int s2n_rand_cleanup_cb_impl(void)
607 : 576 : {
608 [ + + ][ + - ]: 576 : POSIX_ENSURE(s2n_dev_urandom.fd != UNINITIALIZED_ENTROPY_FD, S2N_ERR_NOT_INITIALIZED);
609 : :
610 [ + - ]: 545 : if (s2n_result_is_ok(s2n_rand_device_validate(&s2n_dev_urandom))) {
611 [ - + ]: 545 : POSIX_GUARD(close(s2n_dev_urandom.fd));
612 : 545 : }
613 : 545 : s2n_dev_urandom.fd = UNINITIALIZED_ENTROPY_FD;
614 : :
615 : 545 : return S2N_SUCCESS;
616 : 545 : }
617 : :
618 : : S2N_RESULT s2n_rand_cleanup(void)
619 : 605 : {
620 [ + - ][ + + ]: 605 : RESULT_ENSURE(s2n_rand_cleanup_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
621 : :
622 : 574 : #if S2N_LIBCRYPTO_SUPPORTS_ENGINE
623 [ + - ]: 574 : if (s2n_supports_custom_rand()) {
624 : : /* Cleanup our rand ENGINE in libcrypto */
625 : 574 : ENGINE *rand_engine = ENGINE_by_id(s2n_rand_engine_id);
626 [ + - ]: 574 : if (rand_engine) {
627 : 574 : ENGINE_remove(rand_engine);
628 : 574 : ENGINE_finish(rand_engine);
629 : 574 : ENGINE_unregister_RAND(rand_engine);
630 : 574 : ENGINE_free(rand_engine);
631 : 574 : ENGINE_cleanup();
632 : 574 : RAND_set_rand_engine(NULL);
633 : 574 : RAND_set_rand_method(NULL);
634 : 574 : }
635 : 574 : }
636 : 574 : #endif
637 : :
638 : 574 : s2n_rand_init_cb = s2n_rand_init_cb_impl;
639 : 574 : s2n_rand_cleanup_cb = s2n_rand_cleanup_cb_impl;
640 : 574 : s2n_rand_seed_cb = s2n_rand_get_entropy_from_urandom;
641 : 574 : s2n_rand_mix_cb = s2n_rand_get_entropy_from_urandom;
642 : :
643 : 574 : return S2N_RESULT_OK;
644 : 605 : }
645 : :
646 : : S2N_RESULT s2n_rand_cleanup_thread(void)
647 : 3604 : {
648 : : /* Currently, it is only safe for this function to mutate the drbg states
649 : : * in the per thread rand state. See s2n_ensure_uniqueness().
650 : : */
651 [ - + ]: 3604 : RESULT_GUARD(s2n_drbg_wipe(&s2n_per_thread_rand_state.private_drbg));
652 [ - + ]: 3604 : RESULT_GUARD(s2n_drbg_wipe(&s2n_per_thread_rand_state.public_drbg));
653 : :
654 : 3604 : s2n_per_thread_rand_state.drbgs_initialized = false;
655 : :
656 : : /* Unset the thread-local destructor */
657 [ + + ]: 3617 : if (s2n_is_initialized()) {
658 : 3617 : pthread_setspecific(s2n_per_thread_rand_state_key, NULL);
659 : 3617 : }
660 : :
661 : 3604 : return S2N_RESULT_OK;
662 : 3604 : }
663 : :
664 : : /* This must only be used for unit tests. Any real use is dangerous and will be
665 : : * overwritten in s2n_ensure_uniqueness() if it is forked. This was added to
666 : : * support known answer tests that use OpenSSL and s2n_get_private_random_data
667 : : * directly.
668 : : */
669 : : S2N_RESULT s2n_set_private_drbg_for_test(struct s2n_drbg drbg)
670 : 1 : {
671 [ # # ][ - + ]: 1 : RESULT_ENSURE(s2n_in_unit_test(), S2N_ERR_NOT_IN_UNIT_TEST);
672 [ - + ]: 1 : RESULT_GUARD(s2n_drbg_wipe(&s2n_per_thread_rand_state.private_drbg));
673 : :
674 : 1 : s2n_per_thread_rand_state.private_drbg = drbg;
675 : :
676 : 1 : return S2N_RESULT_OK;
677 : 1 : }
678 : :
679 : : S2N_RESULT s2n_rand_set_urandom_for_test()
680 : 2 : {
681 [ # # ][ - + ]: 2 : RESULT_ENSURE(s2n_in_unit_test(), S2N_ERR_NOT_IN_UNIT_TEST);
682 : 2 : s2n_rand_mix_cb = s2n_rand_get_entropy_from_urandom;
683 : 2 : return S2N_RESULT_OK;
684 : 2 : }
685 : :
686 : : /*
687 : : * volatile is important to prevent the compiler from
688 : : * re-ordering or optimizing the use of RDRAND.
689 : : */
690 : : static int s2n_rand_get_entropy_from_rdrand(void *data, uint32_t size)
691 : 1749364 : {
692 : 1749364 : #if defined(__x86_64__) || defined(__i386__)
693 : 1749364 : struct s2n_blob out = { 0 };
694 [ - + ]: 1749364 : POSIX_GUARD(s2n_blob_init(&out, data, size));
695 : 1749364 : size_t space_remaining = 0;
696 : 1749364 : struct s2n_stuffer stuffer = { 0 };
697 : 1749364 : union {
698 : 1749364 : uint64_t u64;
699 : : #if defined(__i386__)
700 : : struct {
701 : : /* since we check first that we're on intel, we can safely assume little endian. */
702 : : uint32_t u_low;
703 : : uint32_t u_high;
704 : : } i386_fields;
705 : : #endif /* defined(__i386__) */
706 : 1749364 : uint8_t u8[8];
707 : 1749364 : } output;
708 : :
709 [ - + ]: 1749364 : POSIX_GUARD(s2n_stuffer_init(&stuffer, &out));
710 [ + + ]: 10124464 : while ((space_remaining = s2n_stuffer_space_remaining(&stuffer))) {
711 : 8375100 : unsigned char success = 0;
712 : 8375100 : output.u64 = 0;
713 : :
714 [ + + ]:>1844*10^16 : for (int tries = 0; tries < 10; tries++) {
715 : : #if defined(__i386__)
716 : : /* execute the rdrand instruction, store the result in a general purpose register (it's assigned to
717 : : * output.i386_fields.u_low). Check the carry bit, which will be set on success. Then clober the register and reset
718 : : * the carry bit. Due to needing to support an ancient assembler we use the opcode syntax.
719 : : * the %b1 is to force compilers to use c1 instead of ecx.
720 : : * Here's a description of how the opcode is encoded:
721 : : * 0x0fc7 (rdrand)
722 : : * 0xf0 (store the result in eax).
723 : : */
724 : : unsigned char success_high = 0, success_low = 0;
725 : : __asm__ __volatile__(
726 : : ".byte 0x0f, 0xc7, 0xf0;\n"
727 : : "setc %b1;\n"
728 : : : "=&a"(output.i386_fields.u_low), "=qm"(success_low)
729 : : :
730 : : : "cc");
731 : :
732 : : __asm__ __volatile__(
733 : : ".byte 0x0f, 0xc7, 0xf0;\n"
734 : : "setc %b1;\n"
735 : : : "=&a"(output.i386_fields.u_high), "=qm"(success_high)
736 : : :
737 : : : "cc");
738 : : /* cppcheck-suppress knownConditionTrueFalse */
739 : : success = success_high & success_low;
740 : :
741 : : /* Treat either all 1 or all 0 bits in either the high or low order
742 : : * bits as failure */
743 : : if (output.i386_fields.u_low == 0 || output.i386_fields.u_low == UINT32_MAX
744 : : || output.i386_fields.u_high == 0 || output.i386_fields.u_high == UINT32_MAX) {
745 : : success = 0;
746 : : }
747 : : #else
748 : : /* execute the rdrand instruction, store the result in a general purpose register (it's assigned to
749 : : * output.u64). Check the carry bit, which will be set on success. Then clober the carry bit.
750 : : * Due to needing to support an ancient assembler we use the opcode syntax.
751 : : * the %b1 is to force compilers to use c1 instead of ecx.
752 : : * Here's a description of how the opcode is encoded:
753 : : * 0x48 (pick a 64-bit register it does more too, but that's all that matters there)
754 : : * 0x0fc7 (rdrand)
755 : : * 0xf0 (store the result in rax). */
756 : 8375154 : __asm__ __volatile__(
757 : 8375154 : ".byte 0x48, 0x0f, 0xc7, 0xf0;\n"
758 : 8375154 : "setc %b1;\n"
759 : 8375154 : : "=&a"(output.u64), "=qm"(success)
760 : 8375154 : :
761 : 8375154 : : "cc");
762 : 8375154 : #endif /* defined(__i386__) */
763 : :
764 : : /* Some AMD CPUs will find that RDRAND "sticks" on all 1s but still reports success.
765 : : * Some other very old CPUs use all 0s as an error condition while still reporting success.
766 : : * If we encounter either of these suspicious values (a 1/2^63 chance) we'll treat them as
767 : : * a failure and generate a new value.
768 : : *
769 : : * In the future we could add CPUID checks to detect processors with these known bugs,
770 : : * however it does not appear worth it. The entropy loss is negligible and the
771 : : * corresponding likelihood that a healthy CPU generates either of these values is also
772 : : * negligible (1/2^63). Finally, adding processor specific logic would greatly
773 : : * increase the complexity and would cause us to "miss" any unknown processors with
774 : : * similar bugs. */
775 [ + + ][ - + ]: 8375224 : if (output.u64 == UINT64_MAX || output.u64 == 0) {
776 : 0 : success = 0;
777 : 0 : }
778 : :
779 [ + + ]: 8375224 : if (success) {
780 : 8375224 : break;
781 : 8375224 : }
782 : 8375154 : }
783 : :
784 [ - + ][ # # ]: 8375100 : POSIX_ENSURE(success, S2N_ERR_RDRAND_FAILED);
785 : :
786 : 8375100 : size_t data_to_fill = MIN(sizeof(output), space_remaining);
787 : :
788 [ - + ]: 8375100 : POSIX_GUARD(s2n_stuffer_write_bytes(&stuffer, output.u8, data_to_fill));
789 : 8375100 : }
790 : :
791 : 1749364 : return S2N_SUCCESS;
792 : : #else
793 : : POSIX_BAIL(S2N_ERR_UNSUPPORTED_CPU);
794 : : #endif
795 : 1749364 : }
|