Clear PRNG states in FIPS mode.
FIPS requires that the CTR-DRBG state be zeroed on process exit, however destructors for thread-local data aren't called when the process exits. This change maintains a linked-list of thread-local state which is walked on exit to zero each thread's PRNG state. Any concurrently running threads block until the process finishes exiting. Change-Id: Ie5dc18e1bb2941a569d8b309411cf20c9bdf52ef Reviewed-on: https://boringssl-review.googlesource.com/16764 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
This commit is contained in:
parent
d79bc9d397
commit
0ffc795efb
@ -18,12 +18,17 @@
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(BORINGSSL_FIPS)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/chacha.h>
|
||||
#include <openssl/cpu.h>
|
||||
#include <openssl/mem.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "../../internal.h"
|
||||
#include "../delocate.h"
|
||||
|
||||
|
||||
/* It's assumed that the operating system always has an unfailing source of
|
||||
@ -55,22 +60,66 @@ struct rand_thread_state {
|
||||
/* calls is the number of generate calls made on |drbg| since it was last
|
||||
* (re)seeded. This is bound by |kReseedInterval|. */
|
||||
unsigned calls;
|
||||
/* last_block contains the previous block from |CRYPTO_sysrand|. */
|
||||
uint8_t last_block[CRNGT_BLOCK_SIZE];
|
||||
/* last_block_valid is non-zero iff |last_block| contains data from
|
||||
* |CRYPTO_sysrand|. */
|
||||
int last_block_valid;
|
||||
|
||||
#if defined(BORINGSSL_FIPS)
|
||||
/* last_block contains the previous block from |CRYPTO_sysrand|. */
|
||||
uint8_t last_block[CRNGT_BLOCK_SIZE];
|
||||
/* next and prev form a NULL-terminated, double-linked list of all states in
|
||||
* a process. */
|
||||
struct rand_thread_state *next, *prev;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(BORINGSSL_FIPS)
|
||||
/* thread_states_list is the head of a linked-list of all |rand_thread_state|
|
||||
* objects in the process, one per thread. This is needed because FIPS requires
|
||||
* that they be zeroed on process exit, but thread-local destructors aren't
|
||||
* called when the whole process is exiting. */
|
||||
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_STATIC_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) {
|
||||
CTR_DRBG_clear(&cur->drbg);
|
||||
}
|
||||
/* |thread_states_list_lock is deliberately left locked so that any threads
|
||||
* that are still running will hang if they try to call |RAND_bytes|. */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* rand_thread_state_free frees a |rand_thread_state|. This is called when a
|
||||
* thread exits. */
|
||||
static void rand_thread_state_free(void *state_in) {
|
||||
struct rand_thread_state *state = state_in;
|
||||
|
||||
if (state_in == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct rand_thread_state *state = state_in;
|
||||
#if defined(BORINGSSL_FIPS)
|
||||
CRYPTO_STATIC_MUTEX_lock_write(thread_states_list_lock_bss_get());
|
||||
|
||||
if (state->prev != NULL) {
|
||||
state->prev->next = state->next;
|
||||
} else {
|
||||
*thread_states_list_bss_get() = state->next;
|
||||
}
|
||||
|
||||
if (state->next != NULL) {
|
||||
state->next->prev = state->prev;
|
||||
}
|
||||
|
||||
CRYPTO_STATIC_MUTEX_unlock_write(thread_states_list_lock_bss_get());
|
||||
|
||||
CTR_DRBG_clear(&state->drbg);
|
||||
#endif
|
||||
|
||||
OPENSSL_free(state);
|
||||
}
|
||||
|
||||
@ -202,8 +251,26 @@ void RAND_bytes_with_additional_data(uint8_t *out, size_t out_len,
|
||||
abort();
|
||||
}
|
||||
state->calls = 0;
|
||||
|
||||
#if defined(BORINGSSL_FIPS)
|
||||
if (state != &stack_state) {
|
||||
CRYPTO_STATIC_MUTEX_lock_write(thread_states_list_lock_bss_get());
|
||||
struct rand_thread_state **states_list = thread_states_list_bss_get();
|
||||
state->next = *states_list;
|
||||
if (state->next != NULL) {
|
||||
state->next->prev = state;
|
||||
}
|
||||
state->prev = NULL;
|
||||
*states_list = state;
|
||||
CRYPTO_STATIC_MUTEX_unlock_write(thread_states_list_lock_bss_get());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(BORINGSSL_FIPS)
|
||||
CRYPTO_STATIC_MUTEX_lock_read(thread_states_list_lock_bss_get());
|
||||
#endif
|
||||
|
||||
if (state->calls >= kReseedInterval) {
|
||||
uint8_t seed[CTR_DRBG_ENTROPY_LEN];
|
||||
rand_get_seed(state, seed);
|
||||
@ -256,6 +323,10 @@ void RAND_bytes_with_additional_data(uint8_t *out, size_t out_len,
|
||||
CTR_DRBG_clear(&state->drbg);
|
||||
}
|
||||
|
||||
#if defined(BORINGSSL_FIPS)
|
||||
CRYPTO_STATIC_MUTEX_unlock_read(thread_states_list_lock_bss_get());
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "internal.h"
|
||||
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
@ -232,9 +233,32 @@ static int test_thread_local(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void rand_state_test_thread(void) {
|
||||
uint8_t buf[1];
|
||||
RAND_bytes(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static int test_rand_state(void) {
|
||||
/* In FIPS mode, rand.c maintains a linked-list of thread-local data because
|
||||
* we're required to clear it on process exit. This test exercises removing a
|
||||
* value from that list. */
|
||||
uint8_t buf[1];
|
||||
RAND_bytes(buf, sizeof(buf));
|
||||
|
||||
thread_t thread;
|
||||
if (!run_thread(&thread, rand_state_test_thread) ||
|
||||
!wait_for_thread(thread)) {
|
||||
fprintf(stderr, "thread failed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (!test_once() ||
|
||||
!test_thread_local()) {
|
||||
!test_thread_local() ||
|
||||
!test_rand_state()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user