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

Integrate ube for rand engine #1897

Merged
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
87 changes: 77 additions & 10 deletions crypto/fipsmodule/rand/new_rand.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "new_rand_internal.h"
#include "internal.h"
#include "../../internal.h"
#include "../../ube/internal.h"

#include "new_rand_prefix.h"
#include "entropy/internal.h"
Expand All @@ -22,6 +23,13 @@ struct rand_thread_local_state {
// since it was last (re)seeded. Must be bounded by |kReseedInterval|.
uint64_t generate_calls_since_seed;

// reseed_calls_since_initialization is the number of reseed calls made of
// |drbg| since its initialization.
uint64_t reseed_calls_since_initialization;

// generation_number caches the UBE generation number.
uint64_t generation_number;

// Entropy source. UBE volatile state.
const struct entropy_source *entropy_source;
};
Expand All @@ -44,18 +52,48 @@ static void rand_thread_local_state_free(void *state_in) {
OPENSSL_free(state);
}

// TODO
// Ensure snapsafe is in valid state
static int rand_ensure_valid_state(void) {
// rand_ensure_valid_state determines whether |state| is in a valid state. The
// reasons are documented with inline comments in the function.
//
// Returns 1 if |state| is in a valid state and 0 otherwise.
static int rand_ensure_valid_state(struct rand_thread_local_state *state) {

// We do not allow the UBE generation number to change while executing AWS-LC
// randomness generation code e.g. while |RAND_bytes| executes. One way to hit
// this error is if snapshotting the address space while executing
// |RAND_bytes| and while snapsafe is active.
uint64_t current_generation_number = 0;
if (CRYPTO_get_ube_generation_number(&current_generation_number) == 1 &&
current_generation_number != state->generation_number) {
return 0;
}

return 1;
}

// TODO
// For UBE.
static int rand_ensure_ctr_drbg_uniquness(struct rand_thread_local_state *state,
size_t out_len) {
// rand_ensure_ctr_drbg_uniqueness computes whether |state| must be randomized
// to ensure uniqueness.
//
// Note: If |rand_ensure_ctr_drbg_uniqueness| returns 1 it does not necessarily
// imply that an UBE occurred. It can also mean that no UBE detection is
// supported or that UBE detection failed. In these cases, |state| must also be
// randomized to ensure uniqueness. Any special future cases can be handled in
// this function.
//
// Return 1 if |state| must be randomized. 0 otherwise.
static int rand_ensure_ctr_drbg_uniqueness(struct rand_thread_local_state *state) {

uint64_t current_generation_number = 0;
if (CRYPTO_get_ube_generation_number(&current_generation_number) != 1) {
return 1;
}

return 1;
if (current_generation_number != state->generation_number) {
state->generation_number = current_generation_number;
return 1;
}

return 0;
}

// rand_maybe_get_ctr_drbg_pred_resistance maybe fills |pred_resistance| with
Expand Down Expand Up @@ -136,6 +174,7 @@ static void rand_ctr_drbg_reseed(struct rand_thread_local_state *state) {
abort();
}

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

OPENSSL_cleanse(seed, CTR_DRBG_ENTROPY_LEN);
Expand Down Expand Up @@ -167,7 +206,9 @@ static void rand_state_initialize(struct rand_thread_local_state *state) {
abort();
}

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

OPENSSL_cleanse(seed, CTR_DRBG_ENTROPY_LEN);
OPENSSL_cleanse(personalization_string, CTR_DRBG_ENTROPY_LEN);
Expand All @@ -191,7 +232,7 @@ static void RAND_bytes_core(
GUARD_PTR_ABORT(out);

// Ensure the CTR-DRBG state is unique.
if (rand_ensure_ctr_drbg_uniquness(state, out_len) != 1) {
if (rand_ensure_ctr_drbg_uniqueness(state) == 1) {
rand_ctr_drbg_reseed(state);
}

Expand Down Expand Up @@ -252,7 +293,7 @@ static void RAND_bytes_core(

OPENSSL_cleanse(pred_resistance, RAND_PRED_RESISTANCE_LEN);

if (rand_ensure_valid_state() != 1) {
if (rand_ensure_valid_state(state) != 1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NP: in a future PR can you add a comment here explaining why we need to abort. I get that it means a UBE occurred and if that happened after line 235 and before line 242 you could end up with duplicate state, we might just forget/think this looks weird in the future.

abort();
}
}
Expand Down Expand Up @@ -316,3 +357,29 @@ int NR_PREFIX(RAND_priv_bytes)(uint8_t *out, size_t out_len) {
int NR_PREFIX(RAND_pseudo_bytes)(uint8_t *out, size_t out_len) {
return NR_PREFIX(RAND_bytes)(out, out_len);
}

// Returns the number of generate calls made on the thread-local state since
// last seed/reseed. Returns 0 if thread-local state has not been initialized.
uint64_t get_thread_generate_calls_since_seed(void) {

struct rand_thread_local_state *state =
CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_PRIVATE_RAND);
if (state == NULL) {
return 0;
}

return state->generate_calls_since_seed;
}

// Returns the number of reseed calls made on the thread-local state since
// initialization. Returns 0 if thread-local state has not been initialized.
uint64_t get_thread_reseed_calls_since_initialization(void) {

struct rand_thread_local_state *state =
CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_PRIVATE_RAND);
if (state == NULL) {
return 0;
}

return state->reseed_calls_since_initialization;
}
3 changes: 3 additions & 0 deletions crypto/fipsmodule/rand/new_rand_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ OPENSSL_EXPORT int NR_PREFIX(RAND_pseudo_bytes)(uint8_t *out, size_t out_len);
OPENSSL_EXPORT int RAND_bytes_with_user_prediction_resistance(uint8_t *out,
size_t out_len, const uint8_t user_pred_resistance[RAND_PRED_RESISTANCE_LEN]);

OPENSSL_EXPORT uint64_t get_thread_generate_calls_since_seed(void);
OPENSSL_EXPORT uint64_t get_thread_reseed_calls_since_initialization(void);

#if defined(__cplusplus)
} // extern C
#endif
Expand Down
46 changes: 45 additions & 1 deletion crypto/fipsmodule/rand/new_rand_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <openssl/ctrdrbg.h>

#include "new_rand_internal.h"

#include "../../ube/internal.h"

// TODO
// Remove when promoting to default
Expand Down Expand Up @@ -39,5 +39,49 @@ TEST(NewRand, Basic) {
}
}

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

const size_t request_size_one_generate = 10;
const size_t request_size_two_generate = CTR_DRBG_MAX_GENERATE_LENGTH + 1;
uint64_t current_reseed_calls = 0;
uint8_t randomness[MAX_REQUEST_SIZE] = {0};

// Make sure things are initialized and at default values. Cache
// current_reseed_calls last in case RAND_bytes() invokes a reseed.
set_detection_method_gn(1);
ASSERT_TRUE(RAND_bytes(randomness, request_size_one_generate));
current_reseed_calls = get_thread_reseed_calls_since_initialization();

// Bump fork generation number and expect one reseed. In addition, expect one
// generate call since request size is less than CTR_DRBG_MAX_GENERATE_LENGTH.
set_detection_method_gn(2);
ASSERT_TRUE(RAND_bytes(randomness, request_size_one_generate));
ASSERT_EQ(get_thread_reseed_calls_since_initialization(), current_reseed_calls + 1ULL);
ASSERT_EQ(get_thread_generate_calls_since_seed(), 1ULL);

// Bump fork generation number again and expect one reseed. In addition,
// expect two generate call since request size is higher than
// CTR_DRBG_MAX_GENERATE_LENGTH.
set_detection_method_gn(3);
ASSERT_TRUE(RAND_bytes(randomness, request_size_two_generate));
ASSERT_EQ(get_thread_reseed_calls_since_initialization(), current_reseed_calls + 2ULL);
ASSERT_EQ(get_thread_generate_calls_since_seed(), 2ULL);
}

TEST(NewRand, UbeDetectionForkMocked) {
MockedUbeDetection(
[](uint64_t gn) {
set_fork_generation_number_FOR_TESTING(gn);
}
);
}

TEST(NewRand, UbeDetectionSnapsafeMocked) {
MockedUbeDetection(
[](uint64_t gn) {
set_snapsafe_generation_number_FOR_TESTING(static_cast<uint32_t>(gn));
}
);
}

#endif
2 changes: 1 addition & 1 deletion crypto/ube/ube.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ int CRYPTO_get_ube_generation_number(uint64_t *current_generation_number) {
// synchronize an update to the UBE generation number. To avoid redundant
// reseeds, we must ensure the generation number is only incremented once for
// all UBE's that might have happened. Therefore, first take a write lock but
// before mutation the state, check for an UBE again. Checking again ensures
// before mutating the state, check for an UBE again. Checking again ensures
// that only one thread increments the UBE generation number, because the
// cached detection method generation numbers have been updated by the thread
// that had the first entry.
Expand Down
Loading