Skip to content

Commit

Permalink
fips selftest: avoid relying on a real RNG for self tests
Browse files Browse the repository at this point in the history
Rather than instantiate the private and primary DRBGs during the
selftest, instead use a test RNG.  This leaves the DRBG setup
pristine and permits later replacement of the seed source despite
the very early running power up self tests.

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #22210)
  • Loading branch information
paulidale committed Oct 3, 2023
1 parent 3b804c5 commit bc347a3
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 71 deletions.
20 changes: 16 additions & 4 deletions crypto/rand/rand_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@ int RAND_priv_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
}
#endif

if (num < 0)
return 0;
rand = RAND_get0_private(ctx);
if (rand != NULL)
return EVP_RAND_generate(rand, buf, num, strength, 0, NULL, 0);
Expand All @@ -347,8 +349,6 @@ int RAND_priv_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,

int RAND_priv_bytes(unsigned char *buf, int num)
{
if (num < 0)
return 0;
return RAND_priv_bytes_ex(NULL, buf, (size_t)num, 0);
}

Expand All @@ -367,6 +367,8 @@ int RAND_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
}
#endif

if (num < 0)
return 0;
rand = RAND_get0_public(ctx);
if (rand != NULL)
return EVP_RAND_generate(rand, buf, num, strength, 0, NULL, 0);
Expand All @@ -376,8 +378,6 @@ int RAND_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,

int RAND_bytes(unsigned char *buf, int num)
{
if (num < 0)
return 0;
return RAND_bytes_ex(NULL, buf, (size_t)num, 0);
}

Expand Down Expand Up @@ -727,6 +727,18 @@ EVP_RAND_CTX *RAND_get0_private(OSSL_LIB_CTX *ctx)
return rand;
}

#ifdef FIPS_MODULE
EVP_RAND_CTX *ossl_rand_get0_private_noncreating(OSSL_LIB_CTX *ctx)
{
RAND_GLOBAL *dgbl = rand_get_global(ctx);

if (dgbl == NULL)
return NULL;

return CRYPTO_THREAD_get_local(&dgbl->private);
}
#endif

int RAND_set0_public(OSSL_LIB_CTX *ctx, EVP_RAND_CTX *rand)
{
RAND_GLOBAL *dgbl = rand_get_global(ctx);
Expand Down
4 changes: 4 additions & 0 deletions include/crypto/rand.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,8 @@ void ossl_rand_cleanup_nonce(ossl_unused const OSSL_CORE_HANDLE *handle,
size_t ossl_pool_acquire_entropy(RAND_POOL *pool);
int ossl_pool_add_nonce_data(RAND_POOL *pool);

# ifdef FIPS_MODULE
EVP_RAND_CTX *ossl_rand_get0_private_noncreating(OSSL_LIB_CTX *ctx);
# endif

#endif
1 change: 1 addition & 0 deletions include/openssl/core_names.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ extern "C" {
#define OSSL_RAND_PARAM_MAX_REQUEST "max_request"
#define OSSL_RAND_PARAM_TEST_ENTROPY "test_entropy"
#define OSSL_RAND_PARAM_TEST_NONCE "test_nonce"
#define OSSL_RAND_PARAM_GENERATE "generate"

/* RAND/DRBG names */
#define OSSL_DRBG_PARAM_RESEED_REQUESTS "reseed_requests"
Expand Down
17 changes: 9 additions & 8 deletions providers/fips/self_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "internal/e_os.h"
#include "internal/tsan_assist.h"
#include "prov/providercommon.h"
#include "crypto/rand.h"

/*
* We're cheating here. Normally we don't allow RUN_ONCE usage inside the FIPS
Expand Down Expand Up @@ -406,14 +407,14 @@ int SELF_TEST_post(SELF_TEST_POST_PARAMS *st, int on_demand_test)
}

/* Verify that the RNG has been restored properly */
testrand = EVP_RAND_fetch(st->libctx, "TEST-RAND", NULL);
if (testrand == NULL
|| (rng = RAND_get0_private(st->libctx)) == NULL
|| strcmp(EVP_RAND_get0_name(EVP_RAND_CTX_get0_rand(rng)),
EVP_RAND_get0_name(testrand)) == 0) {
ERR_raise(ERR_LIB_PROV, PROV_R_SELF_TEST_KAT_FAILURE);
goto end;
}
rng = ossl_rand_get0_private_noncreating(st->libctx);
if (rng != NULL)
if ((testrand = EVP_RAND_fetch(st->libctx, "TEST-RAND", NULL)) == NULL
|| strcmp(EVP_RAND_get0_name(EVP_RAND_CTX_get0_rand(rng)),
EVP_RAND_get0_name(testrand)) == 0) {
ERR_raise(ERR_LIB_PROV, PROV_R_SELF_TEST_KAT_FAILURE);
goto end;
}

ok = 1;
end:
Expand Down
136 changes: 84 additions & 52 deletions providers/fips/self_test_kats.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <openssl/core_names.h>
#include <openssl/param_build.h>
#include <openssl/rand.h>
#include "crypto/rand.h"
#include "internal/cryptlib.h"
#include "internal/nelem.h"
#include "self_test.h"
Expand All @@ -22,7 +23,7 @@ static int set_kat_drbg(OSSL_LIB_CTX *ctx,
const unsigned char *entropy, size_t entropy_len,
const unsigned char *nonce, size_t nonce_len,
const unsigned char *persstr, size_t persstr_len);
static int reset_original_drbg(OSSL_LIB_CTX *ctx);
static int reset_main_drbg(OSSL_LIB_CTX *ctx);

static int self_test_digest(const ST_KAT_DIGEST *t, OSSL_SELF_TEST *st,
OSSL_LIB_CTX *libctx)
Expand Down Expand Up @@ -701,39 +702,12 @@ static int self_test_signatures(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx)
return 0;
if (!self_test_sign(t, st, libctx))
ret = 0;
if (!reset_original_drbg(libctx))
if (!reset_main_drbg(libctx))
ret = 0;
}
return ret;
}

/*
* Run the algorithm KAT's.
* Return 1 is successful, otherwise return 0.
* This runs all the tests regardless of if any fail.
*/
int SELF_TEST_kats(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx)
{
int ret = 1;

if (!self_test_digests(st, libctx))
ret = 0;
if (!self_test_ciphers(st, libctx))
ret = 0;
if (!self_test_signatures(st, libctx))
ret = 0;
if (!self_test_kdfs(st, libctx))
ret = 0;
if (!self_test_drbgs(st, libctx))
ret = 0;
if (!self_test_kas(st, libctx))
ret = 0;
if (!self_test_asym_ciphers(st, libctx))
ret = 0;

return ret;
}

/*
* Swap the library context DRBG for KAT testing
*
Expand All @@ -745,13 +719,12 @@ int SELF_TEST_kats(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx)
*/

/*
* The default private DRBG of the library context, saved for the duration
* of KAT testing.
* Replacement "random" sources
* main_rand is used for most tests and it's set to generate mode.
* kat_rand is used for KATs where specific input is mandated.
*/
static EVP_RAND_CTX *saved_rand = NULL;

/* Replacement "random" source */
static EVP_RAND_CTX *kat_rand = NULL;
static EVP_RAND_CTX *main_rand = NULL;

static int set_kat_drbg(OSSL_LIB_CTX *ctx,
const unsigned char *entropy, size_t entropy_len,
Expand All @@ -765,7 +738,7 @@ static int set_kat_drbg(OSSL_LIB_CTX *ctx,
};

/* If not NULL, we didn't cleanup from last call: BAD */
if (kat_rand != NULL || saved_rand != NULL)
if (kat_rand != NULL)
return 0;

rand = EVP_RAND_fetch(ctx, "TEST-RAND", NULL);
Expand All @@ -777,7 +750,8 @@ static int set_kat_drbg(OSSL_LIB_CTX *ctx,
if (parent_rand == NULL)
goto err;

drbg_params[0] = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_STRENGTH, &strength);
drbg_params[0] = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_STRENGTH,
&strength);
if (!EVP_RAND_CTX_set_params(parent_rand, drbg_params))
goto err;

Expand Down Expand Up @@ -810,37 +784,30 @@ static int set_kat_drbg(OSSL_LIB_CTX *ctx,
if (!EVP_RAND_instantiate(kat_rand, strength, 0, persstr, persstr_len, NULL))
goto err;

/* When we set the new private generator this one is freed, so upref it */
if (!EVP_RAND_CTX_up_ref(main_rand))
goto err;

/* Update the library context DRBG */
if ((saved_rand = RAND_get0_private(ctx)) != NULL)
/* Avoid freeing this since we replace it */
if (!EVP_RAND_CTX_up_ref(saved_rand)) {
saved_rand = NULL;
goto err;
}
if (RAND_set0_private(ctx, kat_rand) > 0) {
/* Keeping a copy to verify zeroization */
if (EVP_RAND_CTX_up_ref(kat_rand))
return 1;
if (saved_rand != NULL)
RAND_set0_private(ctx, saved_rand);
RAND_set0_private(ctx, main_rand);
}

err:
EVP_RAND_CTX_free(parent_rand);
EVP_RAND_CTX_free(saved_rand);
EVP_RAND_CTX_free(kat_rand);
kat_rand = saved_rand = NULL;
kat_rand = NULL;
return 0;
}

static int reset_original_drbg(OSSL_LIB_CTX *ctx) {
static int reset_main_drbg(OSSL_LIB_CTX *ctx) {
int ret = 1;

if (saved_rand != NULL) {
if (!RAND_set0_private(ctx, saved_rand))
ret = 0;
saved_rand = NULL;
}
if (!RAND_set0_private(ctx, main_rand))
ret = 0;
if (kat_rand != NULL) {
if (!EVP_RAND_uninstantiate(kat_rand)
|| !EVP_RAND_verify_zeroization(kat_rand))
Expand All @@ -851,3 +818,68 @@ static int reset_original_drbg(OSSL_LIB_CTX *ctx) {
return ret;
}

static int setup_main_random(OSSL_LIB_CTX *libctx)
{
OSSL_PARAM drbg_params[3] = {
OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END
};
unsigned int strength = 256, generate = 1;
EVP_RAND *rand;

rand = EVP_RAND_fetch(libctx, "TEST-RAND", NULL);
if (rand == NULL)
return 0;

main_rand = EVP_RAND_CTX_new(rand, NULL);
EVP_RAND_free(rand);
if (main_rand == NULL)
goto err;

drbg_params[0] = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_GENERATE,
&generate);
drbg_params[1] = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_STRENGTH,
&strength);

if (!EVP_RAND_instantiate(main_rand, strength, 0, NULL, 0, drbg_params))
goto err;
return 1;
err:
EVP_RAND_CTX_free(main_rand);
return 0;
}

/*
* Run the algorithm KAT's.
* Return 1 is successful, otherwise return 0.
* This runs all the tests regardless of if any fail.
*/
int SELF_TEST_kats(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx)
{
EVP_RAND_CTX *saved_rand = ossl_rand_get0_private_noncreating(libctx);
int ret = 1;

if (!setup_main_random(libctx)
|| !RAND_set0_private(libctx, main_rand)) {
EVP_RAND_CTX_free(main_rand);
return 0;
}

if (!self_test_digests(st, libctx))
ret = 0;
if (!self_test_ciphers(st, libctx))
ret = 0;
if (!self_test_signatures(st, libctx))
ret = 0;
if (!self_test_kdfs(st, libctx))
ret = 0;
if (!self_test_drbgs(st, libctx))
ret = 0;
if (!self_test_kas(st, libctx))
ret = 0;
if (!self_test_asym_ciphers(st, libctx))
ret = 0;

RAND_set0_private(libctx, saved_rand);
return ret;
}

0 comments on commit bc347a3

Please sign in to comment.