From bc2a5c55ff00043ffa656e6213309fbcaedab524 Mon Sep 17 00:00:00 2001 From: Torben Hansen <50673096+torben-hansen@users.noreply.github.com> Date: Fri, 1 Nov 2024 07:04:39 -0700 Subject: [PATCH] More words --- .../rand/entropy/tree_drbg_jitter_entropy.c | 118 ++++++++++++++---- 1 file changed, 96 insertions(+), 22 deletions(-) diff --git a/crypto/fipsmodule/rand/entropy/tree_drbg_jitter_entropy.c b/crypto/fipsmodule/rand/entropy/tree_drbg_jitter_entropy.c index 8b6d3027eb8..44408361df1 100644 --- a/crypto/fipsmodule/rand/entropy/tree_drbg_jitter_entropy.c +++ b/crypto/fipsmodule/rand/entropy/tree_drbg_jitter_entropy.c @@ -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 | -| @@ -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 @@ -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], @@ -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) { @@ -144,12 +171,19 @@ 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) { @@ -157,7 +191,9 @@ static void tree_jitter_drbg_derive_seed( 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) { @@ -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]) { @@ -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); @@ -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(); } @@ -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()); @@ -304,7 +361,6 @@ 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; @@ -312,24 +368,24 @@ static void tree_jitter_free_global_drbg(void) { 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 @@ -337,6 +393,19 @@ void tree_jitter_free_thread_drbg(struct entropy_source_t *entropy_source) { // 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) { @@ -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()); @@ -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);