Skip to content

Commit

Permalink
More words
Browse files Browse the repository at this point in the history
  • Loading branch information
torben-hansen committed Nov 1, 2024
1 parent 3327b7a commit bc2a5c5
Showing 1 changed file with 96 additions and 22 deletions.
118 changes: 96 additions & 22 deletions crypto/fipsmodule/rand/entropy/tree_drbg_jitter_entropy.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,20 @@

#include "../../../../third_party/jitterentropy/jitterentropy.h"

// Implements a tree-DRBG with the following characteristics:
// - A per-thread "seed"-DRBG that serves seed requests for a thread-local
// "frontend"-DRBG.
// - A global "seed"-DRBG that serves seed requests from the thread-local seed
// Randomness generation implements thread-local "frontend" DRBGs that serve
// requests for randomness from consumers through exported functions such as
// RAND_bytes(). This file implements a tree-DRBG from SP800-90C as a seed
// entropy source for the frontend DRBGs. The implemented tree-DRBG has the
// following characteristics:
// - A per-thread seed DRBG that serves seed requests for a thread-local
// frontend DRBG.
// - A global seed DRBG that serves seed requests from the thread-local seed
// DRBGs.
// - A root seed source that serves seed requests from the global seed DRBG.
// The root seed source is a global instance of Jitter Entropy.
//
//
// The dependency tree looks as follows:
//
// per-thread
// +-----------+
// | CTR-DRBG | -|
Expand All @@ -33,8 +39,19 @@
// | CTR-DRBG |-|
// +-----------+
//
// Memory life-cycle notes:
// TODOOOOO
// Memory life-cycle: The thread-local DRBGs has the same storage duration as
// their corresponding thread-local frontend DRBGs. The per-process DRBG and
// Jitter Entropy instance has a storage duration that extends to the duration
// of AWS-LC being loaded into the process. The per-process memory is lazily
// allocated.

// To serve seed requests from the frontend DRBGs the following
// |struct entropy_source_methods| interface functions are implemented:
// - tree_jitter_initialize
// - tree_jitter_zeroize_thread_drbg
// - tree_jitter_free_thread_drbg
// - tree_jitter_get_seed


// TREE_JITTER_GLOBAL_DRBG_MAX_GENERATE = 2^24
#define TREE_JITTER_GLOBAL_DRBG_MAX_GENERATE 0xFFFFFF
Expand Down Expand Up @@ -115,6 +132,12 @@ static void tree_jitter_get_root_seed(
}
}

// tree_jitter_drbg_maybe_get_pred_resistance generates RAND_PRED_RESISTANCE_LEN
// bytes for prediction resistance and returns them in |pred_resistance|.
// However, it only generate bytes if |tree_jitter_drbg| meets the conditions:
// 1) is not the global seed DRBG 2) is not protected from UBEs. If bytes are
// generated, |pre_resistance_len| is set to RAND_PRED_RESISTANCE_LEN and is
// otherwise not mutated.
static void tree_jitter_drbg_maybe_get_pred_resistance(
struct tree_jitter_drbg_t *tree_jitter_drbg,
uint8_t pred_resistance[RAND_PRED_RESISTANCE_LEN],
Expand All @@ -127,6 +150,10 @@ static void tree_jitter_drbg_maybe_get_pred_resistance(
}
}

// tree_jitter_check_drbg_must_reseed computes whether |state| must be
// randomized to ensure uniqueness.
//
// Return 1 if |state| must be randomized. 0 otherwise.
static int tree_jitter_check_drbg_must_reseed(
struct tree_jitter_drbg_t *tree_jitter_drbg) {

Expand All @@ -144,20 +171,29 @@ static int tree_jitter_check_drbg_must_reseed(
return 0;
}

// tree_jitter_drbg_derive_seed generates a CTR_DRBG_ENTROPY_LEN byte seed from
// the DRBG configured in |tree_jitter_drbg|. The generated bytes are returned
// in |seed_out|.
//
// |tree_jitter_drbg_derive_seed| automatically handles reseeding the
// associated DRBG if required. In addition, if UBE detection is not supported
// prediction resistance is used to ensure bytes are generated safely.
static void tree_jitter_drbg_derive_seed(
struct tree_jitter_drbg_t *tree_jitter_drbg,
uint8_t seed_out[CTR_DRBG_ENTROPY_LEN]) {

if (tree_jitter_drbg->is_global == 1) {
CRYPTO_STATIC_MUTEX_lock_write(global_seed_drbg_lock_bss_get());
if (tree_jitter_drbg == NULL) {
abort();
}

if (tree_jitter_check_drbg_must_reseed(tree_jitter_drbg) == 1) {
uint8_t seed_drbg[CTR_DRBG_ENTROPY_LEN];
if (tree_jitter_drbg->is_global == 1) {
tree_jitter_get_root_seed(tree_jitter_drbg, seed_drbg);
} else {
CRYPTO_STATIC_MUTEX_lock_write(global_seed_drbg_lock_bss_get());
tree_jitter_drbg_derive_seed(*global_seed_drbg_bss_get(), seed_drbg);
CRYPTO_STATIC_MUTEX_unlock_write(global_seed_drbg_lock_bss_get());
}

if (CTR_DRBG_reseed(&(tree_jitter_drbg->drbg), seed_drbg, NULL, 0) != 1) {
Expand All @@ -182,12 +218,14 @@ static void tree_jitter_drbg_derive_seed(
}
OPENSSL_cleanse(pred_resistance, RAND_PRED_RESISTANCE_LEN);
tree_jitter_drbg->generate_calls_since_seed += 1;

if (tree_jitter_drbg->is_global == 1) {
CRYPTO_STATIC_MUTEX_unlock_write(global_seed_drbg_lock_bss_get());
}
}

// tree_jitter_drbg_derive_seed generates a CTR_DRBG_ENTROPY_LEN byte seed from
// the DRBG configured in |entropy_source|. The generated bytes are returned
// in |seed_out|. This function is the entry point for generating output from
// the tree DRBG.
//
// Return 1 on success and 0 otherwise.
int tree_jitter_get_seed(const struct entropy_source_t *entropy_source,
uint8_t seed_out[CTR_DRBG_ENTROPY_LEN]) {

Expand Down Expand Up @@ -238,6 +276,11 @@ static void tree_jitter_initialize_once(void) {
*global_seed_drbg_bss_get() = tree_jitter_drbg_global;
}

// tree_jitter_initialize initalizes a thread-local seed DRBG as configures
// it in |entropy_source|. If the global seed DRBG has not been initialized yet
// it's also initialized.
//
// Returns 1 on success and 0 otherwise.
int tree_jitter_initialize(struct entropy_source_t *entropy_source) {

GUARD_PTR_ABORT(entropy_source);
Expand All @@ -254,7 +297,10 @@ int tree_jitter_initialize(struct entropy_source_t *entropy_source) {

// Initialize the per-thread seed DRBG.
uint8_t seed_drbg[CTR_DRBG_ENTROPY_LEN];
CRYPTO_STATIC_MUTEX_lock_write(global_seed_drbg_lock_bss_get());
tree_jitter_drbg_derive_seed(*global_seed_drbg_bss_get(), seed_drbg);
CRYPTO_STATIC_MUTEX_unlock_write(global_seed_drbg_lock_bss_get());

if (!CTR_DRBG_init(&(tree_jitter_drbg->drbg), seed_drbg, NULL, 0)) {
abort();
}
Expand Down Expand Up @@ -289,6 +335,17 @@ __declspec(allocate(".CRT$XCU")) void(*tree_jitter_drbg_destructor)(void) =
static void tree_jitter_free_global_drbg(void) __attribute__ ((destructor));
#endif

// The memory life-time for thread-local seed DRBGs is handled differently
// compared to the global seed DRBG (and Jitter Entropy instance). The frontend
// DRBG thread-local destuctors will invoke |tree_jitter_free_thread_drbg| using
// their reference to it. The global seed DRBG and Jitter Entropy instance will
// be released by a destructor. This ensures that the global seed DRBG life-time
// extends to the entire process life-time if the lazy initialization happened.
// Obviously, any dlclose on AWS-LC will release the memory early but that's
// correct behaviour.

// tree_jitter_free_global_drbg frees the memory allocated for the global seed
// DRBG and Jitter Entropy instance.
static void tree_jitter_free_global_drbg(void) {

CRYPTO_STATIC_MUTEX_lock_write(global_seed_drbg_lock_bss_get());
Expand All @@ -304,39 +361,51 @@ static void tree_jitter_free_global_drbg(void) {
}

jent_entropy_collector_free(global_tree_jitter_drbg->jitter_ec);
OPENSSL_cleanse(&global_tree_jitter_drbg->drbg, sizeof(CTR_DRBG_STATE));
OPENSSL_free(global_tree_jitter_drbg);

*global_seed_drbg_bss_get() = NULL;

CRYPTO_STATIC_MUTEX_unlock_write(global_seed_drbg_lock_bss_get());
}

// tree_jitter_zeroize_thread_drbg frees the thread-local seed DRBG
// associated with the entropy source |entropy_source|.
void tree_jitter_free_thread_drbg(struct entropy_source_t *entropy_source) {

GUARD_PTR_ABORT(entropy_source);

struct tree_jitter_drbg_t *tree_jitter_drbg =
(struct tree_jitter_drbg_t *) entropy_source->state;

// First cleanse is redundant but there to ensure we definitely zeroize the
// DRBG state if we later swap from a flat struct field to a pointer.
if (tree_jitter_drbg != NULL) {
OPENSSL_cleanse(&tree_jitter_drbg->drbg, sizeof(CTR_DRBG_STATE));
OPENSSL_free(tree_jitter_drbg);
entropy_source->state = NULL;
if (tree_jitter_drbg == NULL) {
return;
}

OPENSSL_free(tree_jitter_drbg);
entropy_source->state = NULL;
}

// Per ISO/IEC 19790-2012 7.9.7 "zeroization" can be random data just not other
// SSP/CSP's. The Jitter Entropy instance doesn't have any practical state, it's
// SSP/CSP's. The Jitter Entropy instance doesn't have any practical state; it's
// a live entropy source. The zeroization strategy used for the DRBG's is to
// reseed with random data, that in turn, will override all states in the tree
// with random data. The zeroization of the tree DRBG executes after the
// frontend DRBGs have been locked - they can't release any generated output.
// Therefore, the randomness generation layer ensures that no output from an the
// tree DRBG is used to generate any output that is later released. Randomizing
// the tree DRBG states therefore effectively "zeroize" the state.
//
// If there aren't any threads running, the zeroizer for the global seed DRBG
// won't execute. But the destructor responsible for releasing the memory
// allocated for the global seed DRBG and Jitter Entropy instance, will still
// execute, in turn, zeroize it.
//
// One could override the DRBG states with zero's. However, doing the small
// extra work to use random data (from the OS source) ensures that even if some
// output where to escape from the randomness generation, it will still be sound
// practically.

// tree_jitter_zeroize_drbg zeroizes the DRBG state configured in
// |tree_jitter_drbg|.
static void tree_jitter_zeroize_drbg(
struct tree_jitter_drbg_t *tree_jitter_drbg) {

Expand All @@ -351,6 +420,8 @@ static void tree_jitter_zeroize_drbg(
tree_jitter_drbg->generate_calls_since_seed = 0;
}

// tree_jitter_zeroize_thread_drbg is similar to but also handles
// synchronizing access to the global seed DRBG
static void tree_jitter_zeroize_global_drbg(void) {

CRYPTO_STATIC_MUTEX_lock_write(global_seed_drbg_lock_bss_get());
Expand All @@ -370,6 +441,9 @@ static void tree_jitter_zeroize_global_drbg(void) {
CRYPTO_STATIC_MUTEX_unlock_write(global_seed_drbg_lock_bss_get());
}

// tree_jitter_zeroize_thread_drbg zeroizes the thread-local seed DRBG
// associated with the entropy source |entropy_source|. It also executes
// zeroization of the global seed DRBG if applicable.
void tree_jitter_zeroize_thread_drbg(struct entropy_source_t *entropy_source) {

GUARD_PTR_ABORT(entropy_source);
Expand Down

0 comments on commit bc2a5c5

Please sign in to comment.