Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reorganise reseeding #1941

Open
wants to merge 4 commits into
base: randomness_generation
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 33 additions & 27 deletions crypto/fipsmodule/rand/new_rand.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,29 +156,20 @@ static void rand_get_ctr_drbg_seed_entropy(
}

// rand_ctr_drbg_reseed reseeds the CTR-DRBG state in |state|.
static void rand_ctr_drbg_reseed(struct rand_thread_local_state *state) {
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) {

GUARD_PTR_ABORT(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,
personalization_string, &personalization_string_len);

assert(personalization_string_len == 0 ||
personalization_string_len == CTR_DRBG_ENTROPY_LEN);

if (CTR_DRBG_reseed(&(state->drbg), seed, personalization_string,
personalization_string_len) != 1) {
abort();
}

state->reseed_calls_since_initialization++;
state->generate_calls_since_seed = 0;

OPENSSL_cleanse(seed, CTR_DRBG_ENTROPY_LEN);
OPENSSL_cleanse(personalization_string, CTR_DRBG_ENTROPY_LEN);
}

// rand_state_initialize initializes the thread-local state |state|. In
Expand Down Expand Up @@ -231,9 +222,13 @@ static void RAND_bytes_core(
GUARD_PTR_ABORT(state);
GUARD_PTR_ABORT(out);

// Ensure the CTR-DRBG state is unique.
// must_reseed_before_generate is 1 if we must reseed before invoking the
// CTR-DRBG generate function CTR_DRBG_generate().
int must_reseed_before_generate = 0;

// Ensure the CTR-DRBG state is safe to use.
if (rand_ensure_ctr_drbg_uniqueness(state) == 1) {
rand_ctr_drbg_reseed(state);
must_reseed_before_generate = 1;
}

// If a prediction resistance source is available, use it.
Expand All @@ -255,7 +250,9 @@ static void RAND_bytes_core(
assert(first_pred_resistance_len == 0 ||
first_pred_resistance_len == RAND_PRED_RESISTANCE_LEN);

// Iterate CTR-DRBG generate until we |out_len| bytes of randomness have been
// TODO: lock here

// Iterate CTR-DRBG generate until |out_len| bytes of randomness have been
// generated. CTR_DRBG_generate can maximally generate
// |CTR_DRBG_MAX_GENERATE_LENGTH| bytes per usage of its state see
// SP800-90A Rev 1 Table 3. If user requests more, we most generate output in
Expand All @@ -267,17 +264,24 @@ static void RAND_bytes_core(
todo = CTR_DRBG_MAX_GENERATE_LENGTH;
}

// Each reseed interval can generate up to
// |CTR_DRBG_MAX_GENERATE_LENGTH*kCtrDrbgReseedInterval| bytes.
// Determining the time(s) to reseed prior to entering the CTR-DRBG generate
// loop is a doable strategy. But tracking reseed times adds unnecessary
// complexity. Instead our strategy is optimizing for simplicity.
// |out_len < CTR_DRBG_MAX_GENERATE_LENGTH| will be the majority case
// (by far) and requires a single check in either strategy.
// Note if we already reseeded through |rand_ctr_drbg_reseed()|, we won't
// reseed again here.
if( state->generate_calls_since_seed + 1 >= kCtrDrbgReseedInterval) {
rand_ctr_drbg_reseed(state);
if (must_reseed_before_generate == 1 ||
(state->generate_calls_since_seed + 1) > kCtrDrbgReseedInterval) {

must_reseed_before_generate = 0;

// TODO: unlock here
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,
personalization_string, &personalization_string_len);

// TODO: lock here
rand_ctr_drbg_reseed(state, seed, personalization_string,
personalization_string_len);

OPENSSL_cleanse(seed, CTR_DRBG_ENTROPY_LEN);
OPENSSL_cleanse(personalization_string, CTR_DRBG_ENTROPY_LEN);
}

if (!CTR_DRBG_generate(&(state->drbg), out, todo, pred_resistance,
Expand All @@ -296,6 +300,8 @@ static void RAND_bytes_core(
if (rand_ensure_valid_state(state) != 1) {
abort();
}

// TODO: unlock here
}

static void RAND_bytes_private(uint8_t *out, size_t out_len,
Expand Down
38 changes: 38 additions & 0 deletions crypto/fipsmodule/rand/new_rand_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,44 @@ TEST(NewRand, Basic) {
}
}

TEST(NewRand, ReseedInterval) {
uint8_t randomness[CTR_DRBG_MAX_GENERATE_LENGTH * 5 + 1] = {0};
uint64_t reseed_calls_since_initialization = get_thread_reseed_calls_since_initialization();
uint64_t generate_calls_since_seed = get_thread_generate_calls_since_seed();

// First check that we can predict when a reseed happens based on the current
// number of invoked generate calls. After the loop, we expect to be one
// invoke generate call from a reseed.
for(size_t i = 0; i < (kCtrDrbgReseedInterval - generate_calls_since_seed); i++) {
ASSERT_TRUE(RAND_bytes(randomness, 1));
ASSERT_EQ(get_thread_reseed_calls_since_initialization(), reseed_calls_since_initialization);
}
ASSERT_TRUE(RAND_bytes(randomness, 1));
ASSERT_EQ(get_thread_reseed_calls_since_initialization(), reseed_calls_since_initialization + 1);
ASSERT_EQ(get_thread_generate_calls_since_seed(), 1ULL);

ASSERT_TRUE(RAND_bytes(randomness, 1));
ASSERT_EQ(get_thread_reseed_calls_since_initialization(), reseed_calls_since_initialization + 1);
ASSERT_EQ(get_thread_generate_calls_since_seed(), 2ULL);

// Should be able to perform kCtrDrbgReseedInterval-2 generate calls before a
// reseed is emitted. Requesting
// CTR_DRBG_MAX_GENERATE_LENGTH * (kCtrDrbgReseedInterval-2) + 1 would require
// quite a large buffer. Instead iterate until we need
// 5 iterations and request 5 * CTR_DRBG_MAX_GENERATE_LENGTH+1, which is a
// much smaller buffer.
for(size_t i = 0; i < (kCtrDrbgReseedInterval - 7); i++) {
ASSERT_TRUE(RAND_bytes(randomness, 1));
ASSERT_EQ(get_thread_reseed_calls_since_initialization(), reseed_calls_since_initialization + 1);
ASSERT_EQ(get_thread_generate_calls_since_seed(), 2 + (i + 1));
}
ASSERT_EQ(get_thread_generate_calls_since_seed(), kCtrDrbgReseedInterval - 5);
size_t request_len_new_reseed = CTR_DRBG_MAX_GENERATE_LENGTH * 5 + 1;
ASSERT_TRUE(RAND_bytes(randomness, request_len_new_reseed));
ASSERT_EQ(get_thread_reseed_calls_since_initialization(), reseed_calls_since_initialization + 2);
ASSERT_EQ(get_thread_generate_calls_since_seed(), 1ULL);
}

static void MockedUbeDetection(std::function<void(uint64_t)> set_detection_method_gn) {

const size_t request_size_one_generate = 10;
Expand Down
Loading