Skip to content

Commit

Permalink
Avoid double seed for non-UBE support runtimes
Browse files Browse the repository at this point in the history
  • Loading branch information
torben-hansen committed Mar 9, 2025
1 parent 1b0a250 commit f4f9a34
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 21 deletions.
58 changes: 44 additions & 14 deletions crypto/fipsmodule/rand/new_rand_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,39 +18,59 @@

#define MAX_REQUEST_SIZE (CTR_DRBG_MAX_GENERATE_LENGTH * 2 + 1)

class newRandTest : public ubeTest {};
static const size_t global_request_len = 64;
static const size_t number_of_threads = 8;

static void test_all_exported_functions(size_t request_len, uint8_t *out_buf,
uint8_t user_pred_res[RAND_PRED_RESISTANCE_LEN]) {
ASSERT_TRUE(RAND_bytes(out_buf, request_len));
ASSERT_TRUE(RAND_priv_bytes(out_buf, request_len));
ASSERT_TRUE(RAND_pseudo_bytes(out_buf, request_len));
ASSERT_TRUE(RAND_bytes_with_user_prediction_resistance(out_buf, request_len, user_pred_res));
}

class newRandTest : public ubeTest {
public:
void SetUp() override {
ubeTest::SetUp();

static void randBasicTests(bool *returnFlag) {
// Ensure randomness generation state is initialized.
uint8_t *randomness = (uint8_t *) OPENSSL_zalloc(MAX_REQUEST_SIZE);
bssl::UniquePtr<uint8_t> deleter(randomness);
uint8_t user_prediction_resistance[RAND_PRED_RESISTANCE_LEN] = {0};
test_all_exported_functions(global_request_len, randomness, user_prediction_resistance);
}
};

static void randBasicTestsNewThread(bool *returnFlag) {
// Do not use stack arrays for these. For example, Alpine OS has too low
// default thread stack size limit to accommodate.
uint8_t *randomness = (uint8_t *) OPENSSL_zalloc(MAX_REQUEST_SIZE);
bssl::UniquePtr<uint8_t> deleter(randomness);
uint8_t user_personalization_string[RAND_PRED_RESISTANCE_LEN] = {0};
uint8_t user_prediction_resistance[RAND_PRED_RESISTANCE_LEN] = {0};

for (size_t i = 0; i < 65; i++) {
ASSERT_TRUE(RAND_bytes(randomness, i));
ASSERT_TRUE(RAND_priv_bytes(randomness, i));
ASSERT_TRUE(RAND_pseudo_bytes(randomness, i));
ASSERT_TRUE(RAND_bytes_with_user_prediction_resistance(randomness, i, user_personalization_string));
test_all_exported_functions(i, randomness, user_prediction_resistance);
}

for (size_t i : {CTR_DRBG_MAX_GENERATE_LENGTH-1, CTR_DRBG_MAX_GENERATE_LENGTH, CTR_DRBG_MAX_GENERATE_LENGTH + 1, CTR_DRBG_MAX_GENERATE_LENGTH * 2}) {
ASSERT_TRUE(RAND_bytes(randomness, i));
ASSERT_TRUE(RAND_priv_bytes(randomness, i));
ASSERT_TRUE(RAND_pseudo_bytes(randomness, i));
ASSERT_TRUE(RAND_bytes_with_user_prediction_resistance(randomness, i, user_personalization_string));
test_all_exported_functions(i, randomness, user_prediction_resistance);
}

*returnFlag = true;
}

TEST_F(newRandTest, Basic) {
ASSERT_TRUE(threadTest(10, randBasicTests));
ASSERT_TRUE(threadTest(number_of_threads, randBasicTestsNewThread));
}

static void randReseedIntervalUbeIsSupportedTests(bool *returnFlag) {
uint8_t *randomness = (uint8_t *) OPENSSL_zalloc(CTR_DRBG_MAX_GENERATE_LENGTH * 5 + 1);
bssl::UniquePtr<uint8_t> deleter(randomness);

// If in a new thread, this will initialize the state.
ASSERT_TRUE(RAND_bytes(randomness, global_request_len));

uint64_t reseed_calls_since_initialization = get_thread_reseed_calls_since_initialization();
uint64_t generate_calls_since_seed = get_thread_generate_calls_since_seed();

Expand Down Expand Up @@ -93,12 +113,19 @@ TEST_F(newRandTest, ReseedIntervalWhenUbeIsSupported) {
if (!UbeIsSupported()) {
GTEST_SKIP() << "UBE detection is not supported";
}
ASSERT_TRUE(threadTest(10, randReseedIntervalUbeIsSupportedTests));
bool returnFlag = false;
randReseedIntervalUbeIsSupportedTests(&returnFlag);
ASSERT_TRUE(returnFlag);
ASSERT_TRUE(threadTest(number_of_threads, randReseedIntervalUbeIsSupportedTests));
}

static void randReseedIntervalUbeNotSupportedTests(bool *returnFlag) {
uint8_t *randomness = (uint8_t *) OPENSSL_zalloc(CTR_DRBG_MAX_GENERATE_LENGTH);
bssl::UniquePtr<uint8_t> deleter(randomness);

// If in a new thread, this will initialize the state.
ASSERT_TRUE(RAND_bytes(randomness, global_request_len));

uint64_t generate_calls_since_seed = get_thread_generate_calls_since_seed();
uint64_t reseed_calls_since_initialization = get_thread_reseed_calls_since_initialization();

Expand Down Expand Up @@ -126,7 +153,10 @@ TEST_F(newRandTest, ReseedIntervalWhenUbeNotSupported) {
if (UbeIsSupported()) {
GTEST_SKIP() << "UBE detection is supported";
}
ASSERT_TRUE(threadTest(10, randReseedIntervalUbeNotSupportedTests));
bool returnFlag = false;
randReseedIntervalUbeNotSupportedTests(&returnFlag);
ASSERT_TRUE(returnFlag);
ASSERT_TRUE(threadTest(number_of_threads, randReseedIntervalUbeNotSupportedTests));
}

static void MockedUbeDetection(std::function<void(uint64_t)> set_detection_method_gn) {
Expand Down
24 changes: 18 additions & 6 deletions crypto/fipsmodule/rand/rand.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ 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
// reseed_calls_since_initialization is the number of reseed calls made on
// |drbg| since its initialization.
uint64_t reseed_calls_since_initialization;

Expand Down Expand Up @@ -357,11 +357,13 @@ static void rand_state_initialize(struct rand_thread_local_state *state) {
// |RAND_USE_USER_PRED_RESISTANCE| or |RAND_NO_USER_PRED_RESISTANCE|. The former
// cause the content of |user_pred_resistance| to be mixed in as prediction
// resistance. The latter ensures that |user_pred_resistance| is not used.
//
// If the state has just been initialized, then |ctr_drbg_state_is_fresh| is 1.
static void rand_bytes_core(
struct rand_thread_local_state *state,
uint8_t *out, size_t out_len,
const uint8_t user_pred_resistance[RAND_PRED_RESISTANCE_LEN],
int use_user_pred_resistance) {
int use_user_pred_resistance, int ctr_drbg_state_is_fresh) {

GUARD_PTR_ABORT(state);
GUARD_PTR_ABORT(out);
Expand All @@ -370,8 +372,10 @@ static void rand_bytes_core(
// CTR-DRBG generate function CTR_DRBG_generate().
int must_reseed_before_generate = 0;

// Ensure that the CTR-DRBG state is unique.
if (rand_check_ctr_drbg_uniqueness(state) != 1) {
// Ensure that the CTR-DRBG state is unique. If the state is fresh then
// uniqueness is guaranteed.
if (rand_check_ctr_drbg_uniqueness(state) != 1 &&
ctr_drbg_state_is_fresh != 1) {
must_reseed_before_generate = 1;
}

Expand Down Expand Up @@ -479,6 +483,8 @@ static void rand_bytes_private(uint8_t *out, size_t out_len,
struct rand_thread_local_state *state =
CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_PRIVATE_RAND);

int ctr_drbg_state_is_fresh = 0;

if (state == NULL) {
state = OPENSSL_zalloc(sizeof(struct rand_thread_local_state));
if (state == NULL ||
Expand All @@ -489,18 +495,24 @@ static void rand_bytes_private(uint8_t *out, size_t out_len,

rand_state_initialize(state);
thread_local_list_add_node(state);

ctr_drbg_state_is_fresh = 0;
}

rand_bytes_core(state, out, out_len, user_pred_resistance,
use_user_pred_resistance);
use_user_pred_resistance, ctr_drbg_state_is_fresh);

FIPS_service_indicator_unlock_state();
FIPS_service_indicator_update_state();
}

int RAND_bytes_with_user_prediction_resistance(uint8_t *out, size_t out_len,
const uint8_t user_pred_resistance[RAND_PRED_RESISTANCE_LEN]) {


if (user_pred_resistance == NULL) {
abort();
}

rand_bytes_private(out, out_len, user_pred_resistance,
RAND_USE_USER_PRED_RESISTANCE);
return 1;
Expand Down
3 changes: 2 additions & 1 deletion include/openssl/rand.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ OPENSSL_EXPORT int RAND_priv_bytes(uint8_t *buf, size_t len);
// RAND_bytes_with_user_prediction_resistance is functionally equivalent to
// |RAND_bytes| but also provides a way for the caller to inject prediction
// resistance material using the argument |user_pred_resistance|.
// |user_pred_resistance| must be filled with |RAND_PRED_RESISTANCE_LEN| bytes.
// |user_pred_resistance| must not be NULL and |user_pred_resistance| must be
// filled with |RAND_PRED_RESISTANCE_LEN| bytes.
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]);

Expand Down

0 comments on commit f4f9a34

Please sign in to comment.