Skip to content

Commit

Permalink
Default entropy source (#1872)
Browse files Browse the repository at this point in the history
This PR implements a default entropy source object for the randomness generation. This is done by defining the function table that is already assumed by the randomness generation code in new_rand.c. The configuration is similar to the one already being used but simplified. It's now assumed that an entropy source can never not be initialised which seems reasonable...

Since the entropy source method table is a const object, get_entropy_source() is changed to return a reference to it. Related code is also changed to take this into account.

Finally, the entropy source object "cleanup" function is changed to not return anything. It's called at thread exit and if it fails there is no way to recover anyway.
  • Loading branch information
torben-hansen committed Sep 27, 2024
1 parent bc7aeff commit a11fc48
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 34 deletions.
63 changes: 47 additions & 16 deletions crypto/fipsmodule/rand/entropy/entropy_sources.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,65 @@
#include <openssl/base.h>

#include "internal.h"
#include "../internal.h"
#include "../../delocate.h"

static int fake_void(void) {
static int entropy_default_initialize(void) {
return 1;
}

static int fake_rand(uint8_t a[CTR_DRBG_ENTROPY_LEN]) {
OPENSSL_cleanse(a, CTR_DRBG_ENTROPY_LEN);
static void entropy_default_cleanup(void) {
}

static int entropy_default_get_seed(uint8_t seed[CTR_DRBG_ENTROPY_LEN]) {
CRYPTO_sysrand_for_seed(seed, CTR_DRBG_ENTROPY_LEN);
return 1;
}

static int fake_rand_(uint8_t a[RAND_PRED_RESISTANCE_LEN]) {
OPENSSL_cleanse(a, RAND_PRED_RESISTANCE_LEN);
static int entropy_default_get_prediction_resistance(
uint8_t pred_resistance[RAND_PRED_RESISTANCE_LEN]) {
if (have_fast_rdrand() == 1 &&
rdrand(pred_resistance, RAND_PRED_RESISTANCE_LEN) != 1) {
return 0;
}
return 1;
}

int get_entropy_source(struct entropy_source *entropy_source) {
static int entropy_default_randomize(void) {
return 1;
}

// In the future this function will lazily initialise a global entropy source.
// The default entropy source configuration using
// - OS randomness source for seeding.
// - Doesn't have a personalization string source.
// - If run-time is on an Intel CPU and it supports rdrand, use it as a source
// for prediction resistance. Otherwise, no source.
DEFINE_LOCAL_DATA(struct entropy_source, default_entropy_source) {
out->initialize = entropy_default_initialize;
out->cleanup = entropy_default_cleanup;
out->get_seed = entropy_default_get_seed;
out->get_personalization_string = NULL;
if (have_fast_rdrand() == 1) {
out->get_prediction_resistance = entropy_default_get_prediction_resistance;
} else {
out->get_prediction_resistance = NULL;
}
out->randomize = entropy_default_randomize;
}

GUARD_PTR(entropy_source);
const struct entropy_source * get_entropy_source(void) {
const struct entropy_source *ent_source = default_entropy_source();

entropy_source->is_initialized = 1;
entropy_source->initialize = fake_void;
entropy_source->cleanup = fake_void;
entropy_source->get_seed = fake_rand;
entropy_source->get_personalization_string = fake_rand;
entropy_source->get_prediction_resistance = fake_rand_;
entropy_source->randomize = fake_void;
// Make sure that the function table contains the minimal number of callbacks
// that we expect. Also make sure that the entropy source is initialized such
// that calling code can assume that.
if (ent_source->cleanup == NULL ||
ent_source->get_seed == NULL ||
ent_source->randomize == NULL ||
ent_source->initialize == NULL ||
ent_source->initialize() != 1) {
return NULL;
}

return 1;
return ent_source;
}
7 changes: 3 additions & 4 deletions crypto/fipsmodule/rand/entropy/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,16 @@ extern "C" {

// I could make these array types!
struct entropy_source {
int is_initialized;
int (*initialize)(void);
int (*cleanup)(void);
void (*cleanup)(void);
int (*get_seed)(uint8_t seed[CTR_DRBG_ENTROPY_LEN]);
int (*get_personalization_string)(uint8_t personalization_string[CTR_DRBG_ENTROPY_LEN]);
int (*get_prediction_resistance)(uint8_t pred_resistance[RAND_PRED_RESISTANCE_LEN]);
int (*randomize)(void);
};

// get_entropy_source will configure an entropy source in |entropy_source|.
int get_entropy_source(struct entropy_source *entropy_source);
// get_entropy_source will return an entropy source configured for the platform.
const struct entropy_source * get_entropy_source(void);

#if defined(__cplusplus)
} // extern C
Expand Down
2 changes: 2 additions & 0 deletions crypto/fipsmodule/rand/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ OPENSSL_EXPORT int CTR_DRBG_init(CTR_DRBG_STATE *drbg,
const uint8_t *personalization,
size_t personalization_len);

int rdrand(uint8_t *buf, const size_t len);

#if defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM)

OPENSSL_INLINE int have_rdrand(void) {
Expand Down
32 changes: 20 additions & 12 deletions crypto/fipsmodule/rand/new_rand.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct rand_thread_local_state {
uint64_t generate_calls_since_seed;

// Entropy source. UBE volatile state.
struct entropy_source entropy_source;
const struct entropy_source *entropy_source;
};

// rand_thread_local_state frees a |rand_thread_local_state|. This is called
Expand All @@ -35,6 +35,12 @@ static void rand_thread_local_state_free(void *state_in) {
return;
}

// Potentially, something could kill the thread before an entropy source has
// been associated to the thread-local randomness generator object.
if (state->entropy_source != NULL) {
state->entropy_source->cleanup();
}

OPENSSL_free(state);
}

Expand All @@ -60,7 +66,7 @@ static int rand_ensure_ctr_drbg_uniquness(struct rand_thread_local_state *state,
// |*pred_resistance_len| is set to 0 if no prediction resistance source is
// available and |RAND_PRED_RESISTANCE_LEN| otherwise.
static void rand_maybe_get_ctr_drbg_pred_resistance(
struct entropy_source *entropy_source,
const struct entropy_source *entropy_source,
uint8_t pred_resistance[RAND_PRED_RESISTANCE_LEN],
size_t *pred_resistance_len) {

Expand All @@ -70,7 +76,9 @@ static void rand_maybe_get_ctr_drbg_pred_resistance(
*pred_resistance_len = 0;

if (entropy_source->get_prediction_resistance != NULL) {
entropy_source->get_prediction_resistance(pred_resistance);
if (entropy_source->get_prediction_resistance(pred_resistance) != 1) {
abort();
}
*pred_resistance_len = RAND_PRED_RESISTANCE_LEN;
}
}
Expand All @@ -83,7 +91,8 @@ static void rand_maybe_get_ctr_drbg_pred_resistance(
//
// |*personalization_string_len| is set to 0 if no personalization string source
// is available and |CTR_DRBG_ENTROPY_LEN| otherwise.
static void rand_get_ctr_drbg_seed_entropy(struct entropy_source *entropy_source,
static void rand_get_ctr_drbg_seed_entropy(
const struct entropy_source *entropy_source,
uint8_t seed[CTR_DRBG_ENTROPY_LEN],
uint8_t personalization_string[CTR_DRBG_ENTROPY_LEN],
size_t *personalization_string_len) {
Expand All @@ -93,10 +102,8 @@ static void rand_get_ctr_drbg_seed_entropy(struct entropy_source *entropy_source

*personalization_string_len = 0;

// If not initialized or the seed source is missing it is impossible to source
// any entropy.
if (entropy_source->is_initialized == 0 ||
entropy_source->get_seed(seed) != 1) {
// If the seed source is missing it is impossible to source any entropy.
if (entropy_source->get_seed(seed) != 1) {
abort();
}

Expand All @@ -118,7 +125,7 @@ static void rand_ctr_drbg_reseed(struct rand_thread_local_state *state) {
uint8_t seed[CTR_DRBG_ENTROPY_LEN];
uint8_t personalization_string[CTR_DRBG_ENTROPY_LEN];
size_t personalization_string_len = 0;
rand_get_ctr_drbg_seed_entropy(&(state->entropy_source), seed,
rand_get_ctr_drbg_seed_entropy(state->entropy_source, seed,
personalization_string, &personalization_string_len);

assert(personalization_string_len == 0 ||
Expand All @@ -141,14 +148,15 @@ static void rand_state_initialize(struct rand_thread_local_state *state) {

GUARD_PTR_ABORT(state);

if (get_entropy_source(&(state->entropy_source)) != 1) {
state->entropy_source = get_entropy_source();
if (state->entropy_source == NULL) {
abort();
}

uint8_t seed[CTR_DRBG_ENTROPY_LEN];
uint8_t personalization_string[CTR_DRBG_ENTROPY_LEN];
size_t personalization_string_len = 0;
rand_get_ctr_drbg_seed_entropy(&(state->entropy_source), seed,
rand_get_ctr_drbg_seed_entropy(state->entropy_source, seed,
personalization_string, &personalization_string_len);

assert(personalization_string_len == 0 ||
Expand Down Expand Up @@ -192,7 +200,7 @@ static void RAND_bytes_core(
// ensuring that its state is randomized before generating output.
size_t first_pred_resistance_len = 0;
uint8_t pred_resistance[RAND_PRED_RESISTANCE_LEN] = {0};
rand_maybe_get_ctr_drbg_pred_resistance(&(state->entropy_source),
rand_maybe_get_ctr_drbg_pred_resistance(state->entropy_source,
pred_resistance, &first_pred_resistance_len);

// If caller input user-controlled prediction resistance, use it.
Expand Down
4 changes: 2 additions & 2 deletions crypto/fipsmodule/rand/rand.c
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ OPENSSL_STATIC_ASSERT(RDRAND_MAX_RETRIES > 0, rdrand_max_retries_must_be_positiv

// rdrand should only be called if either |have_rdrand| or |have_fast_rdrand|
// returned true.
static int rdrand(uint8_t *buf, const size_t len) {
int rdrand(uint8_t *buf, const size_t len) {
const size_t len_multiple8 = len & ~7;
CALL_RDRAND_WITH_RETRY(CRYPTO_rdrand_multiple8_buf(buf, len_multiple8), 0)
const size_t remainder = len - len_multiple8;
Expand All @@ -319,7 +319,7 @@ static int rdrand(uint8_t *buf, const size_t len) {

#else

static int rdrand(uint8_t *buf, size_t len) {
int rdrand(uint8_t *buf, const size_t len) {
return 0;
}

Expand Down

0 comments on commit a11fc48

Please sign in to comment.