123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683 |
- /*
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
- *
- * http://aws.amazon.com/apache2.0
- *
- * or in the "license" file accompanying this file. This file is distributed
- * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
- /*
- * _XOPEN_SOURCE is needed for resolving the constant O_CLOEXEC in some
- * environments. We use _XOPEN_SOURCE instead of _GNU_SOURCE because
- * _GNU_SOURCE is not portable and breaks when attempting to build rust
- * bindings on MacOS.
- *
- * https://man7.org/linux/man-pages/man2/open.2.html
- * The O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW flags are not
- * specified in POSIX.1-2001, but are specified in POSIX.1-2008.
- * Since glibc 2.12, one can obtain their definitions by defining
- * either _POSIX_C_SOURCE with a value greater than or equal to
- * 200809L or _XOPEN_SOURCE with a value greater than or equal to
- * 700. In glibc 2.11 and earlier, one obtains the definitions by
- * defining _GNU_SOURCE.
- *
- * We use two feature probes to detect the need to perform this workaround.
- * It is only applied if we can't get CLOEXEC without it and the build doesn't
- * fail with _XOPEN_SOURCE being defined.
- *
- * # Relevent Links
- *
- * - POSIX.1-2017: https://pubs.opengroup.org/onlinepubs/9699919799
- * - https://stackoverflow.com/a/5724485
- * - https://stackoverflow.com/a/5583764
- */
- #if !defined(S2N_CLOEXEC_SUPPORTED) && defined(S2N_CLOEXEC_XOPEN_SUPPORTED) && !defined(_XOPEN_SOURCE)
- #define _XOPEN_SOURCE 700
- #include <fcntl.h>
- #undef _XOPEN_SOURCE
- #else
- #include <fcntl.h>
- #endif
- #include <errno.h>
- #include <limits.h>
- #include <openssl/engine.h>
- #include <openssl/rand.h>
- #include <pthread.h>
- #include <stdint.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/param.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <time.h>
- #include <unistd.h>
- #if defined(S2N_CPUID_AVAILABLE)
- #include <cpuid.h>
- #endif
- #include "api/s2n.h"
- #include "crypto/s2n_drbg.h"
- #include "crypto/s2n_fips.h"
- #include "error/s2n_errno.h"
- #include "stuffer/s2n_stuffer.h"
- #include "utils/s2n_fork_detection.h"
- #include "utils/s2n_init.h"
- #include "utils/s2n_mem.h"
- #include "utils/s2n_random.h"
- #include "utils/s2n_result.h"
- #include "utils/s2n_safety.h"
- #define ENTROPY_SOURCE "/dev/urandom"
- #if defined(O_CLOEXEC)
- #define ENTROPY_FLAGS O_RDONLY | O_CLOEXEC
- #else
- #define ENTROPY_FLAGS O_RDONLY
- #endif
- /* See https://en.wikipedia.org/wiki/CPUID */
- #define RDRAND_ECX_FLAG 0x40000000
- /* One second in nanoseconds */
- #define ONE_S INT64_C(1000000000)
- /* Placeholder value for an uninitialized entropy file descriptor */
- #define UNINITIALIZED_ENTROPY_FD -1
- static int entropy_fd = UNINITIALIZED_ENTROPY_FD;
- struct s2n_rand_state {
- uint64_t cached_fork_generation_number;
- struct s2n_drbg public_drbg;
- struct s2n_drbg private_drbg;
- bool drbgs_initialized;
- };
- /* Key which will control per-thread freeing of drbg memory */
- static pthread_key_t s2n_per_thread_rand_state_key;
- /* Needed to ensure key is initialized only once */
- static pthread_once_t s2n_per_thread_rand_state_key_once = PTHREAD_ONCE_INIT;
- /* Tracks if call to pthread_key_create failed */
- static int pthread_key_create_result;
- static __thread struct s2n_rand_state s2n_per_thread_rand_state = {
- .cached_fork_generation_number = 0,
- .public_drbg = { 0 },
- .private_drbg = { 0 },
- .drbgs_initialized = false
- };
- static int s2n_rand_init_impl(void);
- static int s2n_rand_cleanup_impl(void);
- static int s2n_rand_urandom_impl(void *ptr, uint32_t size);
- static int s2n_rand_rdrand_impl(void *ptr, uint32_t size);
- static s2n_rand_init_callback s2n_rand_init_cb = s2n_rand_init_impl;
- static s2n_rand_cleanup_callback s2n_rand_cleanup_cb = s2n_rand_cleanup_impl;
- static s2n_rand_seed_callback s2n_rand_seed_cb = s2n_rand_urandom_impl;
- static s2n_rand_mix_callback s2n_rand_mix_cb = s2n_rand_urandom_impl;
- /* non-static for SAW proof */
- bool s2n_cpu_supports_rdrand()
- {
- #if defined(S2N_CPUID_AVAILABLE)
- uint32_t eax, ebx, ecx, edx;
- if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
- return false;
- }
- if (ecx & RDRAND_ECX_FLAG) {
- return true;
- }
- #endif
- return false;
- }
- int s2n_rand_set_callbacks(s2n_rand_init_callback rand_init_callback,
- s2n_rand_cleanup_callback rand_cleanup_callback,
- s2n_rand_seed_callback rand_seed_callback,
- s2n_rand_mix_callback rand_mix_callback)
- {
- POSIX_ENSURE_REF(rand_init_callback);
- POSIX_ENSURE_REF(rand_cleanup_callback);
- POSIX_ENSURE_REF(rand_seed_callback);
- POSIX_ENSURE_REF(rand_mix_callback);
- s2n_rand_init_cb = rand_init_callback;
- s2n_rand_cleanup_cb = rand_cleanup_callback;
- s2n_rand_seed_cb = rand_seed_callback;
- s2n_rand_mix_cb = rand_mix_callback;
- return S2N_SUCCESS;
- }
- S2N_RESULT s2n_get_seed_entropy(struct s2n_blob *blob)
- {
- RESULT_ENSURE_REF(blob);
- RESULT_ENSURE(s2n_rand_seed_cb(blob->data, blob->size) >= S2N_SUCCESS, S2N_ERR_CANCELLED);
- return S2N_RESULT_OK;
- }
- S2N_RESULT s2n_get_mix_entropy(struct s2n_blob *blob)
- {
- RESULT_ENSURE_REF(blob);
- RESULT_GUARD_POSIX(s2n_rand_mix_cb(blob->data, blob->size));
- return S2N_RESULT_OK;
- }
- /* Deletes pthread key at process-exit */
- static void __attribute__((destructor)) s2n_drbg_rand_state_key_cleanup(void)
- {
- if (s2n_is_initialized()) {
- pthread_key_delete(s2n_per_thread_rand_state_key);
- }
- }
- static void s2n_drbg_destructor(void *_unused_argument)
- {
- (void) _unused_argument;
- s2n_result_ignore(s2n_rand_cleanup_thread());
- }
- static void s2n_drbg_make_rand_state_key(void)
- {
- /* We can't return the output of pthread_key_create due to the parameter constraints of pthread_once.
- * Here we store the result in a global variable that will be error checked later. */
- pthread_key_create_result = pthread_key_create(&s2n_per_thread_rand_state_key, s2n_drbg_destructor);
- }
- static S2N_RESULT s2n_init_drbgs(void)
- {
- uint8_t s2n_public_drbg[] = "s2n public drbg";
- uint8_t s2n_private_drbg[] = "s2n private drbg";
- struct s2n_blob public = { 0 };
- RESULT_GUARD_POSIX(s2n_blob_init(&public, s2n_public_drbg, sizeof(s2n_public_drbg)));
- struct s2n_blob private = { 0 };
- RESULT_GUARD_POSIX(s2n_blob_init(&private, s2n_private_drbg, sizeof(s2n_private_drbg)));
- RESULT_ENSURE(pthread_once(&s2n_per_thread_rand_state_key_once, s2n_drbg_make_rand_state_key) == 0, S2N_ERR_DRBG);
- RESULT_ENSURE_EQ(pthread_key_create_result, 0);
- RESULT_GUARD(s2n_drbg_instantiate(&s2n_per_thread_rand_state.public_drbg, &public, S2N_AES_128_CTR_NO_DF_PR));
- RESULT_GUARD(s2n_drbg_instantiate(&s2n_per_thread_rand_state.private_drbg, &private, S2N_AES_256_CTR_NO_DF_PR));
- RESULT_ENSURE(pthread_setspecific(s2n_per_thread_rand_state_key, &s2n_per_thread_rand_state) == 0, S2N_ERR_DRBG);
- s2n_per_thread_rand_state.drbgs_initialized = true;
- return S2N_RESULT_OK;
- }
- static S2N_RESULT s2n_ensure_initialized_drbgs(void)
- {
- if (s2n_per_thread_rand_state.drbgs_initialized == false) {
- RESULT_GUARD(s2n_init_drbgs());
- /* Then cache the fork generation number. We just initialized the drbg
- * states with new entropy and forking is not an external event.
- */
- uint64_t returned_fork_generation_number = 0;
- RESULT_GUARD(s2n_get_fork_generation_number(&returned_fork_generation_number));
- s2n_per_thread_rand_state.cached_fork_generation_number = returned_fork_generation_number;
- }
- return S2N_RESULT_OK;
- }
- /* s2n_ensure_uniqueness() implements defenses against uniqueness
- * breaking events that might cause duplicated drbg states. Currently, only
- * implements fork detection.
- */
- static S2N_RESULT s2n_ensure_uniqueness(void)
- {
- uint64_t returned_fork_generation_number = 0;
- RESULT_GUARD(s2n_get_fork_generation_number(&returned_fork_generation_number));
- if (returned_fork_generation_number != s2n_per_thread_rand_state.cached_fork_generation_number) {
- /* This assumes that s2n_rand_cleanup_thread() doesn't mutate any other
- * state than the drbg states and it resets the drbg initialization
- * boolean to false. s2n_ensure_initialized_drbgs() will cache the new
- * fork generation number in the per thread state.
- */
- RESULT_GUARD(s2n_rand_cleanup_thread());
- RESULT_GUARD(s2n_ensure_initialized_drbgs());
- }
- return S2N_RESULT_OK;
- }
- static S2N_RESULT s2n_get_libcrypto_random_data(struct s2n_blob *out_blob)
- {
- RESULT_GUARD_PTR(out_blob);
- RESULT_GUARD_OSSL(RAND_bytes(out_blob->data, out_blob->size), S2N_ERR_DRBG);
- return S2N_RESULT_OK;
- }
- static S2N_RESULT s2n_get_custom_random_data(struct s2n_blob *out_blob, struct s2n_drbg *drbg_state)
- {
- RESULT_GUARD_PTR(out_blob);
- RESULT_GUARD_PTR(drbg_state);
- RESULT_ENSURE(!s2n_is_in_fips_mode(), S2N_ERR_DRBG);
- RESULT_GUARD(s2n_ensure_initialized_drbgs());
- RESULT_GUARD(s2n_ensure_uniqueness());
- uint32_t offset = 0;
- uint32_t remaining = out_blob->size;
- while (remaining) {
- struct s2n_blob slice = { 0 };
- RESULT_GUARD_POSIX(s2n_blob_slice(out_blob, &slice, offset, MIN(remaining, S2N_DRBG_GENERATE_LIMIT)));
- RESULT_GUARD(s2n_drbg_generate(drbg_state, &slice));
- remaining -= slice.size;
- offset += slice.size;
- }
- return S2N_RESULT_OK;
- }
- static S2N_RESULT s2n_get_random_data(struct s2n_blob *blob, struct s2n_drbg *drbg_state)
- {
- /* By default, s2n-tls uses a custom random implementation to generate random data for the TLS
- * handshake. When operating in FIPS mode, the FIPS-validated libcrypto implementation is used
- * instead.
- */
- if (s2n_is_in_fips_mode()) {
- RESULT_GUARD(s2n_get_libcrypto_random_data(blob));
- return S2N_RESULT_OK;
- }
- RESULT_GUARD(s2n_get_custom_random_data(blob, drbg_state));
- return S2N_RESULT_OK;
- }
- S2N_RESULT s2n_get_public_random_data(struct s2n_blob *blob)
- {
- RESULT_GUARD(s2n_get_random_data(blob, &s2n_per_thread_rand_state.public_drbg));
- return S2N_RESULT_OK;
- }
- S2N_RESULT s2n_get_private_random_data(struct s2n_blob *blob)
- {
- RESULT_GUARD(s2n_get_random_data(blob, &s2n_per_thread_rand_state.private_drbg));
- return S2N_RESULT_OK;
- }
- S2N_RESULT s2n_get_public_random_bytes_used(uint64_t *bytes_used)
- {
- RESULT_GUARD(s2n_drbg_bytes_used(&s2n_per_thread_rand_state.public_drbg, bytes_used));
- return S2N_RESULT_OK;
- }
- S2N_RESULT s2n_get_private_random_bytes_used(uint64_t *bytes_used)
- {
- RESULT_GUARD(s2n_drbg_bytes_used(&s2n_per_thread_rand_state.private_drbg, bytes_used));
- return S2N_RESULT_OK;
- }
- static int s2n_rand_urandom_impl(void *ptr, uint32_t size)
- {
- POSIX_ENSURE(entropy_fd != UNINITIALIZED_ENTROPY_FD, S2N_ERR_NOT_INITIALIZED);
- uint8_t *data = ptr;
- uint32_t n = size;
- struct timespec sleep_time = { .tv_sec = 0, .tv_nsec = 0 };
- long backoff = 1;
- while (n) {
- errno = 0;
- int r = read(entropy_fd, data, n);
- if (r <= 0) {
- /*
- * A non-blocking read() on /dev/urandom should "never" fail,
- * except for EINTR. If it does, briefly pause and use
- * exponential backoff to avoid creating a tight spinning loop.
- *
- * iteration delay
- * --------- -----------------
- * 1 10 nsec
- * 2 100 nsec
- * 3 1,000 nsec
- * 4 10,000 nsec
- * 5 100,000 nsec
- * 6 1,000,000 nsec
- * 7 10,000,000 nsec
- * 8 99,999,999 nsec
- * 9 99,999,999 nsec
- * ...
- */
- if (errno != EINTR) {
- backoff = MIN(backoff * 10, ONE_S - 1);
- sleep_time.tv_nsec = backoff;
- do {
- r = nanosleep(&sleep_time, &sleep_time);
- } while (r != 0);
- }
- continue;
- }
- data += r;
- n -= r;
- }
- return S2N_SUCCESS;
- }
- /*
- * Return a random number in the range [0, bound)
- */
- S2N_RESULT s2n_public_random(int64_t bound, uint64_t *output)
- {
- uint64_t r;
- RESULT_ENSURE_GT(bound, 0);
- while (1) {
- struct s2n_blob blob = { 0 };
- RESULT_GUARD_POSIX(s2n_blob_init(&blob, (void *) &r, sizeof(r)));
- RESULT_GUARD(s2n_get_public_random_data(&blob));
- /* Imagine an int was one byte and UINT_MAX was 256. If the
- * caller asked for s2n_random(129, ...) we'd end up in
- * trouble. Each number in the range 0...127 would be twice
- * as likely as 128. That's because r == 0 % 129 -> 0, and
- * r == 129 % 129 -> 0, but only r == 128 returns 128,
- * r == 257 is out of range.
- *
- * To de-bias the dice, we discard values of r that are higher
- * that the highest multiple of 'bound' an int can support. If
- * bound is a uint, then in the worst case we discard 50% - 1 r's.
- * But since 'bound' is an int and INT_MAX is <= UINT_MAX / 2,
- * in the worst case we discard 25% - 1 r's.
- */
- if (r < (UINT64_MAX - (UINT64_MAX % bound))) {
- *output = r % bound;
- return S2N_RESULT_OK;
- }
- }
- }
- #if S2N_LIBCRYPTO_SUPPORTS_CUSTOM_RAND
- #define S2N_RAND_ENGINE_ID "s2n_rand"
- int s2n_openssl_compat_rand(unsigned char *buf, int num)
- {
- struct s2n_blob out = { 0 };
- POSIX_GUARD(s2n_blob_init(&out, buf, num));
- if (s2n_result_is_error(s2n_get_private_random_data(&out))) {
- return 0;
- }
- return 1;
- }
- int s2n_openssl_compat_status(void)
- {
- return 1;
- }
- int s2n_openssl_compat_init(ENGINE *unused)
- {
- return 1;
- }
- RAND_METHOD s2n_openssl_rand_method = {
- .seed = NULL,
- .bytes = s2n_openssl_compat_rand,
- .cleanup = NULL,
- .add = NULL,
- .pseudorand = s2n_openssl_compat_rand,
- .status = s2n_openssl_compat_status
- };
- #endif
- static int s2n_rand_init_impl(void)
- {
- OPEN:
- entropy_fd = open(ENTROPY_SOURCE, ENTROPY_FLAGS);
- if (entropy_fd == -1) {
- if (errno == EINTR) {
- goto OPEN;
- }
- POSIX_BAIL(S2N_ERR_OPEN_RANDOM);
- }
- if (s2n_cpu_supports_rdrand()) {
- s2n_rand_mix_cb = s2n_rand_rdrand_impl;
- }
- return S2N_SUCCESS;
- }
- S2N_RESULT s2n_rand_init(void)
- {
- RESULT_ENSURE(s2n_rand_init_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
- RESULT_GUARD(s2n_ensure_initialized_drbgs());
- #if S2N_LIBCRYPTO_SUPPORTS_CUSTOM_RAND
- if (s2n_is_in_fips_mode()) {
- return S2N_RESULT_OK;
- }
- /* Create an engine */
- ENGINE *e = ENGINE_new();
- RESULT_ENSURE(e != NULL, S2N_ERR_OPEN_RANDOM);
- RESULT_GUARD_OSSL(ENGINE_set_id(e, S2N_RAND_ENGINE_ID), S2N_ERR_OPEN_RANDOM);
- RESULT_GUARD_OSSL(ENGINE_set_name(e, "s2n entropy generator"), S2N_ERR_OPEN_RANDOM);
- RESULT_GUARD_OSSL(ENGINE_set_flags(e, ENGINE_FLAGS_NO_REGISTER_ALL), S2N_ERR_OPEN_RANDOM);
- RESULT_GUARD_OSSL(ENGINE_set_init_function(e, s2n_openssl_compat_init), S2N_ERR_OPEN_RANDOM);
- RESULT_GUARD_OSSL(ENGINE_set_RAND(e, &s2n_openssl_rand_method), S2N_ERR_OPEN_RANDOM);
- RESULT_GUARD_OSSL(ENGINE_add(e), S2N_ERR_OPEN_RANDOM);
- RESULT_GUARD_OSSL(ENGINE_free(e), S2N_ERR_OPEN_RANDOM);
- /* Use that engine for rand() */
- e = ENGINE_by_id(S2N_RAND_ENGINE_ID);
- RESULT_ENSURE(e != NULL, S2N_ERR_OPEN_RANDOM);
- RESULT_GUARD_OSSL(ENGINE_init(e), S2N_ERR_OPEN_RANDOM);
- RESULT_GUARD_OSSL(ENGINE_set_default(e, ENGINE_METHOD_RAND), S2N_ERR_OPEN_RANDOM);
- RESULT_GUARD_OSSL(ENGINE_free(e), S2N_ERR_OPEN_RANDOM);
- #endif
- return S2N_RESULT_OK;
- }
- static int s2n_rand_cleanup_impl(void)
- {
- POSIX_ENSURE(entropy_fd != UNINITIALIZED_ENTROPY_FD, S2N_ERR_NOT_INITIALIZED);
- POSIX_GUARD(close(entropy_fd));
- entropy_fd = UNINITIALIZED_ENTROPY_FD;
- return S2N_SUCCESS;
- }
- S2N_RESULT s2n_rand_cleanup(void)
- {
- RESULT_ENSURE(s2n_rand_cleanup_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
- #if S2N_LIBCRYPTO_SUPPORTS_CUSTOM_RAND
- /* Cleanup our rand ENGINE in libcrypto */
- ENGINE *rand_engine = ENGINE_by_id(S2N_RAND_ENGINE_ID);
- if (rand_engine) {
- ENGINE_remove(rand_engine);
- ENGINE_finish(rand_engine);
- ENGINE_unregister_RAND(rand_engine);
- ENGINE_free(rand_engine);
- ENGINE_cleanup();
- RAND_set_rand_engine(NULL);
- RAND_set_rand_method(NULL);
- }
- #endif
- s2n_rand_init_cb = s2n_rand_init_impl;
- s2n_rand_cleanup_cb = s2n_rand_cleanup_impl;
- s2n_rand_seed_cb = s2n_rand_urandom_impl;
- s2n_rand_mix_cb = s2n_rand_urandom_impl;
- return S2N_RESULT_OK;
- }
- S2N_RESULT s2n_rand_cleanup_thread(void)
- {
- /* Currently, it is only safe for this function to mutate the drbg states
- * in the per thread rand state. See s2n_ensure_uniqueness().
- */
- RESULT_GUARD(s2n_drbg_wipe(&s2n_per_thread_rand_state.private_drbg));
- RESULT_GUARD(s2n_drbg_wipe(&s2n_per_thread_rand_state.public_drbg));
- s2n_per_thread_rand_state.drbgs_initialized = false;
- /* Unset the thread-local destructor */
- if (s2n_is_initialized()) {
- pthread_setspecific(s2n_per_thread_rand_state_key, NULL);
- }
- return S2N_RESULT_OK;
- }
- /* This must only be used for unit tests. Any real use is dangerous and will be
- * overwritten in s2n_ensure_uniqueness() if it is forked. This was added to
- * support known answer tests that use OpenSSL and s2n_get_private_random_data
- * directly.
- */
- S2N_RESULT s2n_set_private_drbg_for_test(struct s2n_drbg drbg)
- {
- RESULT_ENSURE(s2n_in_unit_test(), S2N_ERR_NOT_IN_UNIT_TEST);
- RESULT_GUARD(s2n_drbg_wipe(&s2n_per_thread_rand_state.private_drbg));
- s2n_per_thread_rand_state.private_drbg = drbg;
- return S2N_RESULT_OK;
- }
- /*
- * volatile is important to prevent the compiler from
- * re-ordering or optimizing the use of RDRAND.
- */
- static int s2n_rand_rdrand_impl(void *data, uint32_t size)
- {
- #if defined(__x86_64__) || defined(__i386__)
- struct s2n_blob out = { 0 };
- POSIX_GUARD(s2n_blob_init(&out, data, size));
- size_t space_remaining = 0;
- struct s2n_stuffer stuffer = { 0 };
- union {
- uint64_t u64;
- #if defined(__i386__)
- struct {
- /* since we check first that we're on intel, we can safely assume little endian. */
- uint32_t u_low;
- uint32_t u_high;
- } i386_fields;
- #endif /* defined(__i386__) */
- uint8_t u8[8];
- } output;
- POSIX_GUARD(s2n_stuffer_init(&stuffer, &out));
- while ((space_remaining = s2n_stuffer_space_remaining(&stuffer))) {
- unsigned char success = 0;
- output.u64 = 0;
- for (int tries = 0; tries < 10; tries++) {
- #if defined(__i386__)
- /* execute the rdrand instruction, store the result in a general purpose register (it's assigned to
- * output.i386_fields.u_low). Check the carry bit, which will be set on success. Then clober the register and reset
- * the carry bit. Due to needing to support an ancient assembler we use the opcode syntax.
- * the %b1 is to force compilers to use c1 instead of ecx.
- * Here's a description of how the opcode is encoded:
- * 0x0fc7 (rdrand)
- * 0xf0 (store the result in eax).
- */
- unsigned char success_high = 0, success_low = 0;
- __asm__ __volatile__(
- ".byte 0x0f, 0xc7, 0xf0;\n"
- "setc %b1;\n"
- : "=a"(output.i386_fields.u_low), "=qm"(success_low)
- :
- : "cc");
- __asm__ __volatile__(
- ".byte 0x0f, 0xc7, 0xf0;\n"
- "setc %b1;\n"
- : "=a"(output.i386_fields.u_high), "=qm"(success_high)
- :
- : "cc");
- /* cppcheck-suppress knownConditionTrueFalse */
- success = success_high & success_low;
- /* Treat either all 1 or all 0 bits in either the high or low order
- * bits as failure */
- if (output.i386_fields.u_low == 0 || output.i386_fields.u_low == UINT32_MAX
- || output.i386_fields.u_high == 0 || output.i386_fields.u_high == UINT32_MAX) {
- success = 0;
- }
- #else
- /* execute the rdrand instruction, store the result in a general purpose register (it's assigned to
- * output.u64). Check the carry bit, which will be set on success. Then clober the carry bit.
- * Due to needing to support an ancient assembler we use the opcode syntax.
- * the %b1 is to force compilers to use c1 instead of ecx.
- * Here's a description of how the opcode is encoded:
- * 0x48 (pick a 64-bit register it does more too, but that's all that matters there)
- * 0x0fc7 (rdrand)
- * 0xf0 (store the result in rax). */
- __asm__ __volatile__(
- ".byte 0x48, 0x0f, 0xc7, 0xf0;\n"
- "setc %b1;\n"
- : "=a"(output.u64), "=qm"(success)
- :
- : "cc");
- #endif /* defined(__i386__) */
- /* Some AMD CPUs will find that RDRAND "sticks" on all 1s but still reports success.
- * Some other very old CPUs use all 0s as an error condition while still reporting success.
- * If we encounter either of these suspicious values (a 1/2^63 chance) we'll treat them as
- * a failure and generate a new value.
- *
- * In the future we could add CPUID checks to detect processors with these known bugs,
- * however it does not appear worth it. The entropy loss is negligible and the
- * corresponding likelihood that a healthy CPU generates either of these values is also
- * negligible (1/2^63). Finally, adding processor specific logic would greatly
- * increase the complexity and would cause us to "miss" any unknown processors with
- * similar bugs. */
- if (output.u64 == UINT64_MAX || output.u64 == 0) {
- success = 0;
- }
- if (success) {
- break;
- }
- }
- POSIX_ENSURE(success, S2N_ERR_RDRAND_FAILED);
- size_t data_to_fill = MIN(sizeof(output), space_remaining);
- POSIX_GUARD(s2n_stuffer_write_bytes(&stuffer, output.u8, data_to_fill));
- }
- return S2N_SUCCESS;
- #else
- POSIX_BAIL(S2N_ERR_UNSUPPORTED_CPU);
- #endif
- }
|