#include <assert.h>
#include <limits.h>
#include <string.h>
#if defined(BORINGSSL_FIPS)
#include <unistd.h>
#endif
#include <openssl/chacha.h>
#include <openssl/ctrdrbg.h>
#include <openssl/mem.h>
#include "../../bcm_support.h"
#include "../bcm_interface.h"
#include "../delocate.h"
#include "internal.h"
static const unsigned kReseedInterval = …;
#define CRNGT_BLOCK_SIZE …
struct rand_thread_state { … };
#if defined(BORINGSSL_FIPS)
DEFINE_BSS_GET(struct rand_thread_state *, thread_states_list);
DEFINE_STATIC_MUTEX(thread_states_list_lock);
static void rand_thread_state_clear_all(void) __attribute__((destructor));
static void rand_thread_state_clear_all(void) {
CRYPTO_MUTEX_lock_write(thread_states_list_lock_bss_get());
for (struct rand_thread_state *cur = *thread_states_list_bss_get();
cur != NULL; cur = cur->next) {
CRYPTO_MUTEX_lock_write(&cur->clear_drbg_lock);
CTR_DRBG_clear(&cur->drbg);
}
}
#endif
static void rand_thread_state_free(void *state_in) { … }
#if defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM) && \
!defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
static int rdrand(uint8_t *buf, const size_t len) { … }
#else
static int rdrand(uint8_t *buf, size_t len) { return 0; }
#endif
bcm_status BCM_rand_bytes_hwrng(uint8_t *buf, const size_t len) { … }
#if defined(BORINGSSL_FIPS)
struct entropy_buffer {
uint8_t
bytes[CRNGT_BLOCK_SIZE + CTR_DRBG_ENTROPY_LEN * BORINGSSL_FIPS_OVERREAD];
size_t bytes_valid;
int want_additional_input;
};
DEFINE_BSS_GET(struct entropy_buffer, entropy_buffer);
DEFINE_STATIC_MUTEX(entropy_buffer_lock);
bcm_infallible BCM_rand_load_entropy(const uint8_t *entropy, size_t entropy_len,
int want_additional_input) {
struct entropy_buffer *const buffer = entropy_buffer_bss_get();
CRYPTO_MUTEX_lock_write(entropy_buffer_lock_bss_get());
const size_t space = sizeof(buffer->bytes) - buffer->bytes_valid;
if (entropy_len > space) {
entropy_len = space;
}
OPENSSL_memcpy(&buffer->bytes[buffer->bytes_valid], entropy, entropy_len);
buffer->bytes_valid += entropy_len;
buffer->want_additional_input |= want_additional_input && (entropy_len != 0);
CRYPTO_MUTEX_unlock_write(entropy_buffer_lock_bss_get());
return bcm_infallible_not_approved;
}
static void get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len,
int *out_want_additional_input) {
struct entropy_buffer *const buffer = entropy_buffer_bss_get();
if (out_entropy_len > sizeof(buffer->bytes)) {
abort();
}
CRYPTO_MUTEX_lock_write(entropy_buffer_lock_bss_get());
while (buffer->bytes_valid < out_entropy_len) {
CRYPTO_MUTEX_unlock_write(entropy_buffer_lock_bss_get());
RAND_need_entropy(out_entropy_len - buffer->bytes_valid);
CRYPTO_MUTEX_lock_write(entropy_buffer_lock_bss_get());
}
*out_want_additional_input = buffer->want_additional_input;
OPENSSL_memcpy(out_entropy, buffer->bytes, out_entropy_len);
OPENSSL_memmove(buffer->bytes, &buffer->bytes[out_entropy_len],
buffer->bytes_valid - out_entropy_len);
buffer->bytes_valid -= out_entropy_len;
if (buffer->bytes_valid == 0) {
buffer->want_additional_input = 0;
}
CRYPTO_MUTEX_unlock_write(entropy_buffer_lock_bss_get());
}
static void rand_get_seed(struct rand_thread_state *state,
uint8_t seed[CTR_DRBG_ENTROPY_LEN],
uint8_t additional_input[CTR_DRBG_ENTROPY_LEN],
size_t *out_additional_input_len) {
uint8_t entropy_bytes[sizeof(state->last_block) +
CTR_DRBG_ENTROPY_LEN * BORINGSSL_FIPS_OVERREAD];
uint8_t *entropy = entropy_bytes;
size_t entropy_len = sizeof(entropy_bytes);
if (state->last_block_valid) {
entropy += sizeof(state->last_block);
entropy_len -= sizeof(state->last_block);
}
int want_additional_input;
get_seed_entropy(entropy, entropy_len, &want_additional_input);
if (!state->last_block_valid) {
OPENSSL_memcpy(state->last_block, entropy, sizeof(state->last_block));
entropy += sizeof(state->last_block);
entropy_len -= sizeof(state->last_block);
}
if (CRYPTO_memcmp(state->last_block, entropy, sizeof(state->last_block)) ==
0) {
fprintf(stderr, "CRNGT failed.\n");
BORINGSSL_FIPS_abort();
}
assert(entropy_len % CRNGT_BLOCK_SIZE == 0);
for (size_t i = CRNGT_BLOCK_SIZE; i < entropy_len; i += CRNGT_BLOCK_SIZE) {
if (CRYPTO_memcmp(entropy + i - CRNGT_BLOCK_SIZE, entropy + i,
CRNGT_BLOCK_SIZE) == 0) {
fprintf(stderr, "CRNGT failed.\n");
BORINGSSL_FIPS_abort();
}
}
OPENSSL_memcpy(state->last_block, entropy + entropy_len - CRNGT_BLOCK_SIZE,
CRNGT_BLOCK_SIZE);
assert(entropy_len == BORINGSSL_FIPS_OVERREAD * CTR_DRBG_ENTROPY_LEN);
OPENSSL_memcpy(seed, entropy, CTR_DRBG_ENTROPY_LEN);
for (size_t i = 1; i < BORINGSSL_FIPS_OVERREAD; i++) {
for (size_t j = 0; j < CTR_DRBG_ENTROPY_LEN; j++) {
seed[j] ^= entropy[CTR_DRBG_ENTROPY_LEN * i + j];
}
}
*out_additional_input_len = 0;
if (want_additional_input &&
CRYPTO_sysrand_if_available(additional_input, CTR_DRBG_ENTROPY_LEN)) {
*out_additional_input_len = CTR_DRBG_ENTROPY_LEN;
}
}
#else
static void rand_get_seed(struct rand_thread_state *state,
uint8_t seed[CTR_DRBG_ENTROPY_LEN],
uint8_t additional_input[CTR_DRBG_ENTROPY_LEN],
size_t *out_additional_input_len) { … }
#endif
bcm_infallible BCM_rand_bytes_with_additional_data(
uint8_t *out, size_t out_len, const uint8_t user_additional_data[32]) { … }
bcm_infallible BCM_rand_bytes(uint8_t *out, size_t out_len) { … }