Commit 5f9b4da9dd for openssl.org
commit 5f9b4da9ddf835aa7c532202d8d84eab02a07067
Author: Jakub Zelenka <jakub.zelenka@openssl.foundation>
Date: Tue Jul 14 00:27:25 2026 +0200
rand: add seed_strict option enforcing the configured seed source
When a provider requests seeding material via the get_user_entropy or
get_user_nonce core callbacks before the primary DRBG was created, the
request silently falls back to the operating system entropy sources,
because the seed source EVP_RAND_CTX only comes into existence as a
side effect of creating the primary DRBG. Whether a configured seed
source is used therefore depends on operation order: for example with
the FIPS provider, genrsa happened to use the configured source while
ecparam -genkey did not.
This fallback is acceptable for most setups, but not when the seed
source selection is meant to be a guarantee, for example for compliance
reasons. Add a seed_strict option to the [random] configuration
section which makes the callbacks instantiate the seed source on
demand and turns an unusable seed source into an error rather than a
silent fallback. The option is off by default with two exceptions:
selecting the JITTER seed source, at runtime or at build time via
-DOPENSSL_DEFAULT_SEED_SRC, seeds strictly unless the option disables
it, and enable-fips-jitter builds always seed strictly.
The boolean value parsing is shared with the provider section by
factoring the existing parser out into ossl_conf_parse_bool(). A build
time default property query can now be set with
-DOPENSSL_DEFAULT_SEED_PROPQ.
Add a regression test covering the on-demand creation and the no
fallback behaviour on the strict provider seeding path, a test for the
default non-strict fallback behaviour including the JITTER strictness
default, and [random] seed configuration coverage in the rand config
test recipe.
Fixes #25941
Assisted-by: Claude:claude-fable-5
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Merge-date: Tue Sep 1 12:07:39 2026
Merged-from: https://github.com/openssl/openssl/pull/31939
diff --git a/CHANGES.md b/CHANGES.md
index b3daa8cf18..2e8ce6daa5 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -32,6 +32,25 @@ OpenSSL 4.1
### Changes between 4.0 and 4.1 [xx XXX xxxx]
+ * Added a `seed_strict` option to the `random` configuration section
+ which makes the configured random seed source strictly enforced when
+ a provider (such as the FIPS provider) requests entropy or a nonce.
+
+ When a provider requests seeding material before the primary DRBG has
+ been created, the request falls back to the operating system entropy
+ sources, because the seed source only comes into existence as a side
+ effect of creating the primary DRBG. Whether a configured seed source
+ is used therefore depends on operation order. With `seed_strict`
+ enabled, the seed source is instead instantiated on demand and an
+ error is reported if it cannot be used. The option is off by default
+ with two exceptions: the `JITTER` seed source seeds strictly unless
+ the option disables it and `enable-fips-jitter` builds always seed
+ strictly. Additionally, the property query used to fetch the default
+ seed source can now be set at build time with
+ `-DOPENSSL_DEFAULT_SEED_PROPQ`.
+
+ *Jakub Zelenka*
+
* Fixed a bug where a TLS 1.3 session ticket could retain a stale ALPN
protocol from an earlier connection after a resumption negotiated a
different protocol (or none), on both the server and the client,
diff --git a/Configure b/Configure
index 0af6e7c3c2..1d9c890a4a 100755
--- a/Configure
+++ b/Configure
@@ -1418,7 +1418,8 @@ application.
Instead of manually seeding, a different random generator can be set
at runtime in openssl.cnf or configured at build time with
--DOPENSSL_DEFAULT_SEED_SRC.
+-DOPENSSL_DEFAULT_SEED_SRC. The property query used to fetch it can
+be set with -DOPENSSL_DEFAULT_SEED_PROPQ if needed.
Please read the 'Note on random number generation' section in the
INSTALL.md instructions for more details.
diff --git a/crypto/conf/conf_mod.c b/crypto/conf/conf_mod.c
index 9070f2967e..f20bc6ab46 100644
--- a/crypto/conf/conf_mod.c
+++ b/crypto/conf/conf_mod.c
@@ -723,3 +723,35 @@ int CONF_parse_list(const char *list_, int sep, int nospc,
lstart = p + 1;
}
}
+
+/*
+ * Parse a boolean configuration value: 1, yes, true or on (in lower or
+ * uppercase) enables, 0, no, false or off disables. Returns 0 when the
+ * value is missing or not recognised, without raising an error.
+ */
+int ossl_conf_parse_bool(const char *value, int *result)
+{
+ if (value == NULL)
+ return 0;
+ if (strcmp(value, "1") == 0
+ || strcmp(value, "yes") == 0
+ || strcmp(value, "YES") == 0
+ || strcmp(value, "true") == 0
+ || strcmp(value, "TRUE") == 0
+ || strcmp(value, "on") == 0
+ || strcmp(value, "ON") == 0) {
+ *result = 1;
+ return 1;
+ }
+ if (strcmp(value, "0") == 0
+ || strcmp(value, "no") == 0
+ || strcmp(value, "NO") == 0
+ || strcmp(value, "false") == 0
+ || strcmp(value, "FALSE") == 0
+ || strcmp(value, "off") == 0
+ || strcmp(value, "OFF") == 0) {
+ *result = 0;
+ return 1;
+ }
+ return 0;
+}
diff --git a/crypto/provider_conf.c b/crypto/provider_conf.c
index f2e76ac402..3b62622939 100644
--- a/crypto/provider_conf.c
+++ b/crypto/provider_conf.c
@@ -15,6 +15,7 @@
#include <openssl/provider.h>
#include "internal/provider.h"
#include "internal/cryptlib.h"
+#include "internal/conf.h"
#include "provider_local.h"
#include "crypto/context.h"
@@ -275,30 +276,7 @@ static int provider_conf_activate(OSSL_LIB_CTX *libctx, const char *name,
static int provider_conf_parse_bool_setting(const char *confname,
const char *confvalue, int *val)
{
-
- if (confvalue == NULL) {
- ERR_raise_data(ERR_LIB_CRYPTO, CRYPTO_R_PROVIDER_SECTION_ERROR,
- "directive %s set to unrecognized value",
- confname);
- return 0;
- }
- if ((strcmp(confvalue, "1") == 0)
- || (strcmp(confvalue, "yes") == 0)
- || (strcmp(confvalue, "YES") == 0)
- || (strcmp(confvalue, "true") == 0)
- || (strcmp(confvalue, "TRUE") == 0)
- || (strcmp(confvalue, "on") == 0)
- || (strcmp(confvalue, "ON") == 0)) {
- *val = 1;
- } else if ((strcmp(confvalue, "0") == 0)
- || (strcmp(confvalue, "no") == 0)
- || (strcmp(confvalue, "NO") == 0)
- || (strcmp(confvalue, "false") == 0)
- || (strcmp(confvalue, "FALSE") == 0)
- || (strcmp(confvalue, "off") == 0)
- || (strcmp(confvalue, "OFF") == 0)) {
- *val = 0;
- } else {
+ if (!ossl_conf_parse_bool(confvalue, val)) {
ERR_raise_data(ERR_LIB_CRYPTO, CRYPTO_R_PROVIDER_SECTION_ERROR,
"directive %s set to unrecognized value",
confname);
diff --git a/crypto/rand/prov_seed.c b/crypto/rand/prov_seed.c
index 8466ded8ab..8b640855e3 100644
--- a/crypto/rand/prov_seed.c
+++ b/crypto/rand/prov_seed.c
@@ -45,13 +45,27 @@ size_t ossl_rand_get_user_entropy(OSSL_LIB_CTX *ctx,
unsigned char **pout, int entropy,
size_t min_len, size_t max_len)
{
- EVP_RAND_CTX *rng = ossl_rand_get0_seed_noncreating(ctx);
-
- if (rng != NULL && evp_rand_can_seed(rng))
- return evp_rand_get_seed(rng, pout, entropy, min_len, max_len,
- 0, NULL, 0);
- else
- return ossl_rand_get_entropy(ctx, pout, entropy, min_len, max_len);
+ EVP_RAND_CTX *rng;
+
+ if (ossl_rand_seed_source_strict(ctx)) {
+ /*
+ * With strict seeding the seed source must be used, even when the
+ * request arrives before anything instantiated it: create it now
+ * and fail instead of silently substituting the operating system
+ * entropy sources.
+ */
+ rng = ossl_rand_get0_seed(ctx);
+ if (rng == NULL || !evp_rand_can_seed(rng)) {
+ ERR_raise(ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_ENTROPY);
+ return 0;
+ }
+ } else {
+ rng = ossl_rand_get0_seed_noncreating(ctx);
+ if (rng == NULL || !evp_rand_can_seed(rng))
+ return ossl_rand_get_entropy(ctx, pout, entropy, min_len, max_len);
+ }
+ return evp_rand_get_seed(rng, pout, entropy, min_len, max_len,
+ 0, NULL, 0);
}
void ossl_rand_cleanup_entropy(ossl_unused OSSL_LIB_CTX *ctx,
@@ -103,10 +117,21 @@ size_t ossl_rand_get_user_nonce(OSSL_LIB_CTX *ctx,
const void *salt, size_t salt_len)
{
unsigned char *buf;
- EVP_RAND_CTX *rng = ossl_rand_get0_seed_noncreating(ctx);
-
- if (rng == NULL)
- return ossl_rand_get_nonce(ctx, pout, min_len, max_len, salt, salt_len);
+ EVP_RAND_CTX *rng;
+
+ if (ossl_rand_seed_source_strict(ctx)) {
+ /* See ossl_rand_get_user_entropy() */
+ rng = ossl_rand_get0_seed(ctx);
+ if (rng == NULL) {
+ ERR_raise(ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_NONCE);
+ return 0;
+ }
+ } else {
+ rng = ossl_rand_get0_seed_noncreating(ctx);
+ if (rng == NULL)
+ return ossl_rand_get_nonce(ctx, pout, min_len, max_len,
+ salt, salt_len);
+ }
if ((buf = OPENSSL_malloc(min_len)) == NULL)
return 0;
diff --git a/crypto/rand/rand_lib.c b/crypto/rand/rand_lib.c
index a58b0bb9ef..d4a8203851 100644
--- a/crypto/rand/rand_lib.c
+++ b/crypto/rand/rand_lib.c
@@ -68,6 +68,12 @@ typedef struct rand_global_st {
/* Allow the randomness source to be changed */
char *seed_name;
char *seed_propq;
+
+ /*
+ * Whether the seed source may never fall back to the OS entropy:
+ * 1 strict, 0 not strict, -1 unset (strict only for JITTER)
+ */
+ int seed_strict;
} RAND_GLOBAL;
static EVP_RAND_CTX *rand_get0_primary(OSSL_LIB_CTX *ctx, RAND_GLOBAL *dgbl);
@@ -85,6 +91,7 @@ static RAND_GLOBAL *rand_get_global(OSSL_LIB_CTX *libctx)
#include <limits.h>
#include <openssl/conf.h>
#include <openssl/trace.h>
+#include "internal/conf.h"
#include "crypto/rand_pool.h"
#include "prov/seeding.h"
#include "internal/e_os.h"
@@ -451,6 +458,8 @@ void *ossl_rand_ctx_new(OSSL_LIB_CTX *libctx)
if (dgbl == NULL)
return NULL;
+ dgbl->seed_strict = -1;
+
#ifndef FIPS_MODULE
/*
* We need to ensure that base libcrypto thread handling has been
@@ -522,6 +531,27 @@ static void rand_delete_thread_state(void *arg)
}
#if !defined(FIPS_MODULE) || !defined(OPENSSL_NO_FIPS_JITTER)
+/*
+ * Return 1 if the seed source must always be used and never be silently
+ * substituted by the operating system entropy sources: requested via the
+ * seed_strict option of the [random] configuration section, defaulting
+ * to strict for the JITTER seed source or implied by an
+ * enable-fips-jitter build which hard-wires the JITTER seed source.
+ */
+static int rand_seed_source_strict(ossl_unused RAND_GLOBAL *dgbl)
+{
+#ifdef OPENSSL_NO_FIPS_JITTER
+ const char *name;
+
+ if (dgbl->seed_strict >= 0)
+ return dgbl->seed_strict;
+ name = dgbl->seed_name != NULL ? dgbl->seed_name : OPENSSL_SEED_SRC_NAME;
+ return OPENSSL_strcasecmp(name, "JITTER") == 0;
+#else /* !OPENSSL_NO_FIPS_JITTER */
+ return 1;
+#endif /* OPENSSL_NO_FIPS_JITTER */
+}
+
static EVP_RAND_CTX *rand_new_seed(OSSL_LIB_CTX *libctx)
{
EVP_RAND *rand;
@@ -535,10 +565,14 @@ static EVP_RAND_CTX *rand_new_seed(OSSL_LIB_CTX *libctx)
if (dgbl == NULL)
return NULL;
propq = dgbl->seed_propq;
+#ifdef OPENSSL_DEFAULT_SEED_PROPQ
+ if (propq == NULL)
+ propq = OPENSSL_MSTR(OPENSSL_DEFAULT_SEED_PROPQ);
+#endif /* OPENSSL_DEFAULT_SEED_PROPQ */
if (dgbl->seed_name != NULL) {
name = dgbl->seed_name;
} else {
- fallback = 1;
+ fallback = !rand_seed_source_strict(dgbl);
name = OPENSSL_SEED_SRC_NAME;
}
#else /* !OPENSSL_NO_FIPS_JITTER */
@@ -621,6 +655,22 @@ EVP_RAND_CTX *ossl_rand_get0_seed_noncreating(OSSL_LIB_CTX *ctx)
CRYPTO_THREAD_unlock(dgbl->lock);
return ret;
}
+
+EVP_RAND_CTX *ossl_rand_get0_seed(OSSL_LIB_CTX *ctx)
+{
+ RAND_GLOBAL *dgbl = rand_get_global(ctx);
+
+ if (dgbl == NULL)
+ return NULL;
+ return rand_get0_seed(ctx, dgbl);
+}
+
+int ossl_rand_seed_source_strict(OSSL_LIB_CTX *ctx)
+{
+ RAND_GLOBAL *dgbl = rand_get_global(ctx);
+
+ return dgbl != NULL && rand_seed_source_strict(dgbl);
+}
#endif /* !FIPS_MODULE */
static EVP_RAND_CTX *rand_new_drbg(OSSL_LIB_CTX *libctx, EVP_RAND_CTX *parent,
@@ -934,6 +984,16 @@ static int random_set_string(char **p, const char *s)
return 1;
}
+static int random_set_bool(int *p, const CONF_VALUE *cval)
+{
+ if (!ossl_conf_parse_bool(cval->value, p)) {
+ ERR_raise_data(ERR_LIB_CRYPTO, CRYPTO_R_RANDOM_SECTION_ERROR,
+ "name=%s, value=%s", cval->name, cval->value);
+ return 0;
+ }
+ return 1;
+}
+
/*
* Load the DRBG definitions from a configuration file.
*/
@@ -978,6 +1038,9 @@ static int random_conf_init(CONF_IMODULE *md, const CONF *cnf)
} else if (OPENSSL_strcasecmp(cval->name, "seed_properties") == 0) {
if (!random_set_string(&dgbl->seed_propq, cval->value))
return 0;
+ } else if (OPENSSL_strcasecmp(cval->name, "seed_strict") == 0) {
+ if (!random_set_bool(&dgbl->seed_strict, cval))
+ return 0;
} else if (OPENSSL_strcasecmp(cval->name, "random_provider") == 0) {
#ifndef FIPS_MODULE
OSSL_PROVIDER *prov = ossl_provider_find(libctx, cval->value, 0);
diff --git a/doc/internal/man3/ossl_rand_get_entropy.pod b/doc/internal/man3/ossl_rand_get_entropy.pod
index be39369f2b..0adb09404c 100644
--- a/doc/internal/man3/ossl_rand_get_entropy.pod
+++ b/doc/internal/man3/ossl_rand_get_entropy.pod
@@ -44,7 +44,15 @@ returned to the caller.
ossl_rand_get_user_entropy() is the same as ossl_rand_get_entropy()
except that it retrieves the seeding material from the library context's
DRBG seed source. By default this is the operating system but it can
-be changed by calling L<RAND_set_seed_source_type(3)>.
+be changed by calling L<RAND_set_seed_source_type(3)>, via the B<random>
+configuration section or at build time by overriding
+B<OPENSSL_DEFAULT_SEED_SRC>. With strict seeding, enabled via the
+B<seed_strict> option of the B<random> configuration section, used by
+default for the B<JITTER> seed source or implied by an
+B<enable-fips-jitter> build, the seed source is instantiated on first
+use and an error is returned when it cannot be used. Otherwise the
+operating system entropy sources are used as a fallback while the seed
+source has not been instantiated yet.
ossl_rand_cleanup_entropy() cleanses and frees any storage allocated by
ossl_rand_get_entropy(). The entropy buffer is pointed to by I<buf>
@@ -64,8 +72,9 @@ buffer length returned to the caller.
ossl_rand_get_user_nonce() is the same as ossl_rand_get_nonce() except
that it retrieves the seeding material from the library context's DRBG
-seed source. By default this is the operating system but it can be
-changed by calling L<RAND_set_seed_source_type(3)>.
+seed source. The seed source is selected and instantiated in the same
+way as for ossl_rand_get_user_entropy(), including the strict seeding
+behaviour.
ossl_rand_cleanup_nonce() cleanses and frees any storage allocated by
ossl_rand_get_nonce() or ossl_rand_get_user_nonce(). The nonce buffer
diff --git a/doc/man3/RAND_set_DRBG_type.pod b/doc/man3/RAND_set_DRBG_type.pod
index 4d26029c2f..bf40838064 100644
--- a/doc/man3/RAND_set_DRBG_type.pod
+++ b/doc/man3/RAND_set_DRBG_type.pod
@@ -39,11 +39,19 @@ These functions must be called before the random bit generators are first
created in the library context. They will return an error if the call
is made too late.
+Note that with strict seeding, enabled via the B<seed_strict> option of
+the B<random> configuration section, used by default for the B<JITTER>
+seed source or implied by an B<enable-fips-jitter> build, a provider
+(for example the FIPS provider) requesting entropy or a nonce
+instantiates the seed source before the application's first use of it.
+RAND_set_seed_source_type() fails once this happens.
+
The default DRBG is "CTR-DRBG" using the "AES-256-CTR" cipher.
The default seed source can be configured when OpenSSL is compiled by
setting B<-DOPENSSL_DEFAULT_SEED_SRC=SEED-SRC>. If not set then
-"SEED-SRC" is used.
+"SEED-SRC" is used. The property query used when fetching the default
+seed source can similarly be set with B<-DOPENSSL_DEFAULT_SEED_PROPQ>.
=head1 EXAMPLES
diff --git a/doc/man5/config.pod b/doc/man5/config.pod
index 2e455ea78c..3da360b7e7 100644
--- a/doc/man5/config.pod
+++ b/doc/man5/config.pod
@@ -416,6 +416,18 @@ to access the same randomness sources from outside the validated boundary.
This sets the property query used when fetching the randomness source.
+=item B<seed_strict>
+
+This enables strict seeding. The randomness source is then instantiated
+on first use, even for a provider entropy or nonce request arriving before
+the DRBG setup, and an unusable randomness source is an error instead of
+a silent fallback to the operating system entropy sources. Recognised
+values are B<yes>, B<on>, B<true>, B<1> to enable and B<no>, B<off>,
+B<false>, B<0> to disable, in lower or uppercase. The default is B<no>,
+except for the B<JITTER> randomness source, which seeds strictly unless
+this option disables it. Builds configured with B<enable-fips-jitter>
+always seed strictly.
+
=item B<random_provider>
This sets the provider to use for the L<RAND_bytes(3)> calls instead of the built-in
diff --git a/include/crypto/rand.h b/include/crypto/rand.h
index 357ce885b7..b77f009fde 100644
--- a/include/crypto/rand.h
+++ b/include/crypto/rand.h
@@ -153,6 +153,8 @@ int ossl_pool_add_nonce_data(RAND_POOL *pool);
EVP_RAND_CTX *ossl_rand_get0_private_noncreating(OSSL_LIB_CTX *ctx);
#else
EVP_RAND_CTX *ossl_rand_get0_seed_noncreating(OSSL_LIB_CTX *ctx);
+EVP_RAND_CTX *ossl_rand_get0_seed(OSSL_LIB_CTX *ctx);
+int ossl_rand_seed_source_strict(OSSL_LIB_CTX *ctx);
#endif
/* Generate a uniformly distributed random integer in the interval [0, upper) */
diff --git a/include/internal/conf.h b/include/internal/conf.h
index bf0ca15f74..5e27f9d36c 100644
--- a/include/internal/conf.h
+++ b/include/internal/conf.h
@@ -62,5 +62,6 @@ struct conf_imodule_st {
int ossl_config_int(const OPENSSL_INIT_SETTINGS *);
void ossl_no_config_int(void);
void ossl_config_modules_free(void);
+int ossl_conf_parse_bool(const char *value, int *result);
#endif
diff --git a/test/rand_strict.cnf b/test/rand_strict.cnf
new file mode 100644
index 0000000000..4ac3458354
--- /dev/null
+++ b/test/rand_strict.cnf
@@ -0,0 +1,8 @@
+openssl_conf = openssl_init
+
+[openssl_init]
+random = random_section
+
+[random_section]
+seed = TEST-RAND
+seed_strict = yes
diff --git a/test/rand_test.c b/test/rand_test.c
index afbd746fc3..22e72be51b 100644
--- a/test/rand_test.c
+++ b/test/rand_test.c
@@ -16,6 +16,7 @@
#include "testutil.h"
static char *configfile;
+static char *strictconfigfile;
static int test_rand(void)
{
@@ -319,6 +320,166 @@ err:
return res;
}
+/*
+ * Create a parentless DRBG in a provider: instantiating it requests
+ * seeding material through the core's get_user_entropy and
+ * get_user_nonce callbacks, the same path the FIPS provider uses.
+ */
+static EVP_RAND_CTX *provider_side_drbg(OSSL_LIB_CTX *ctx)
+{
+ EVP_RAND *rand;
+ EVP_RAND_CTX *rctx;
+
+ if (!TEST_ptr(rand = EVP_RAND_fetch(ctx, "CTR-DRBG", NULL)))
+ return NULL;
+ rctx = EVP_RAND_CTX_new(rand, NULL);
+ EVP_RAND_free(rand);
+ return rctx;
+}
+
+static int provider_side_drbg_instantiate(EVP_RAND_CTX *rctx)
+{
+ OSSL_PARAM params[2];
+
+ params[0] = OSSL_PARAM_construct_utf8_string(OSSL_DRBG_PARAM_CIPHER,
+ (char *)"AES-256-CTR", 0);
+ params[1] = OSSL_PARAM_construct_end();
+ return EVP_RAND_instantiate(rctx, 0, 0, NULL, 0, params);
+}
+
+/*
+ * Regression test for #25941: with strict seeding the configured seed
+ * source must be instantiated on demand and used when a provider
+ * requests seeding material before anything else created it, instead of
+ * being silently replaced by the operating system entropy sources.
+ */
+static int test_rand_seed_source_strict(void)
+{
+#ifndef OPENSSL_NO_FIPS_JITTER
+ TEST_info("skipped: enable-fips-jitter forces the JITTER seed source");
+ return 1;
+#else
+ OSSL_LIB_CTX *ctx = NULL;
+ EVP_RAND_CTX *drbg = NULL, *seed;
+ unsigned char entropy[64], buf[16];
+ OSSL_PARAM params[3];
+ int generate = 1, res = 0;
+ size_t i;
+
+ for (i = 0; i < sizeof(entropy); i++)
+ entropy[i] = 0xff & (i + 1);
+
+ /* The config configures TEST-RAND as the seed source and seed_strict */
+ if (!TEST_ptr(ctx = OSSL_LIB_CTX_new())
+ || !TEST_true(OSSL_LIB_CTX_load_config(ctx, strictconfigfile)))
+ goto err;
+
+ /*
+ * The first seeding request must fail: the configured TEST-RAND has
+ * no entropy to hand out yet and falling back to the operating
+ * system sources would defeat the configuration.
+ */
+ if (!TEST_ptr(drbg = provider_side_drbg(ctx)))
+ goto err;
+ ERR_set_mark();
+ if (!TEST_false(provider_side_drbg_instantiate(drbg))) {
+ ERR_clear_last_mark();
+ goto err;
+ }
+ ERR_pop_to_mark();
+ EVP_RAND_CTX_free(drbg);
+ drbg = NULL;
+
+ /* The request must have instantiated the configured seed source */
+ if (!TEST_ptr(seed = ossl_rand_get0_seed_noncreating(ctx))
+ || !TEST_str_eq(EVP_RAND_get0_name(EVP_RAND_CTX_get0_rand(seed)),
+ "TEST-RAND"))
+ goto err;
+
+ /* Provision the seed source and check that it feeds the DRBG */
+ params[0] = OSSL_PARAM_construct_octet_string(OSSL_RAND_PARAM_TEST_ENTROPY,
+ entropy, sizeof(entropy));
+ params[1] = OSSL_PARAM_construct_int(OSSL_RAND_PARAM_GENERATE, &generate);
+ params[2] = OSSL_PARAM_construct_end();
+ if (!TEST_true(EVP_RAND_CTX_set_params(seed, params))
+ || !TEST_ptr(drbg = provider_side_drbg(ctx))
+ || !TEST_true(provider_side_drbg_instantiate(drbg))
+ || !TEST_true(EVP_RAND_generate(drbg, buf, sizeof(buf), 0, 0,
+ NULL, 0)))
+ goto err;
+
+ res = 1;
+err:
+ EVP_RAND_CTX_free(drbg);
+ OSSL_LIB_CTX_free(ctx);
+ return res;
+#endif /* OPENSSL_NO_FIPS_JITTER */
+}
+
+/*
+ * Verify that a provider requesting seeding material keeps the fallback
+ * behaviour without strict seeding: the request falls back to the
+ * operating system sources without instantiating the seed source, even
+ * when one was configured, so a later RAND_set_seed_source_type() call
+ * still succeeds. In enable-fips-jitter builds seeding is always
+ * strict and the request instantiates the seed source instead.
+ */
+static int test_rand_seed_source_nonstrict(void)
+{
+ OSSL_LIB_CTX *ctx = NULL;
+ EVP_RAND_CTX *drbg = NULL;
+ int ok, res = 0;
+
+ if (!TEST_ptr(ctx = OSSL_LIB_CTX_new()))
+ goto err;
+
+ if (ossl_rand_seed_source_strict(ctx)) {
+ /* enable-fips-jitter build: the JITTER seed source is hard-wired */
+ if (!TEST_ptr(drbg = provider_side_drbg(ctx)))
+ goto err;
+ ERR_set_mark();
+ ok = provider_side_drbg_instantiate(drbg);
+ ERR_pop_to_mark();
+ /* The seed source may be unusable in this configuration */
+ if (ok
+ && (!TEST_ptr(ossl_rand_get0_seed_noncreating(ctx))
+ || !TEST_false(RAND_set_seed_source_type(ctx, "TEST-RAND",
+ NULL))))
+ goto err;
+ } else {
+#ifdef OPENSSL_RAND_SEED_NONE
+ TEST_info("skipped: no operating system entropy sources");
+#else
+ /* Strict seeding is implied when the JITTER source is selected */
+ if (!TEST_true(RAND_set_seed_source_type(ctx, "JITTER", NULL))
+ || !TEST_true(ossl_rand_seed_source_strict(ctx))
+ || !TEST_true(RAND_set_seed_source_type(ctx, NULL, NULL))
+ || !TEST_false(ossl_rand_seed_source_strict(ctx)))
+ goto err;
+
+ if (!TEST_ptr(drbg = provider_side_drbg(ctx))
+ || !TEST_true(provider_side_drbg_instantiate(drbg))
+ || !TEST_ptr_null(ossl_rand_get0_seed_noncreating(ctx))
+ || !TEST_true(RAND_set_seed_source_type(ctx, "TEST-RAND", NULL)))
+ goto err;
+ EVP_RAND_CTX_free(drbg);
+ drbg = NULL;
+
+ /* A configured but non-strict seed source still falls back */
+ if (!TEST_ptr(drbg = provider_side_drbg(ctx))
+ || !TEST_true(provider_side_drbg_instantiate(drbg))
+ || !TEST_ptr_null(ossl_rand_get0_seed_noncreating(ctx)))
+ goto err;
+#endif /* OPENSSL_RAND_SEED_NONE */
+ }
+
+ res = 1;
+err:
+ EVP_RAND_CTX_free(drbg);
+ OSSL_LIB_CTX_free(ctx);
+ return res;
+}
+
/* Warm up the DRBG cipher fetch caches outside the mfail injection window */
static int rand_drbg_fetch_warmup(EVP_RAND *drbg_alg)
{
@@ -463,6 +624,7 @@ int setup_tests(void)
}
if (!TEST_ptr(configfile = test_get_argument(0))
+ || !TEST_ptr(strictconfigfile = test_get_argument(1))
|| !TEST_true(RAND_set_DRBG_type(NULL, "TEST-RAND", "fips=no",
NULL, NULL))
|| (fips_provider_version_ge(NULL, 3, 0, 8)
@@ -483,6 +645,9 @@ int setup_tests(void)
|| fips_provider_version_ge(NULL, 3, 5, 1))
ADD_TEST(test_rand_get0_primary);
+ ADD_TEST(test_rand_seed_source_strict);
+ ADD_TEST(test_rand_seed_source_nonstrict);
+
ADD_MFAIL_ALL_TESTS(test_rand_bytes_mfail, 2);
ADD_MFAIL_TEST(test_rand_seed_src_mfail);
ADD_MFAIL_TEST(test_rand_drbg_mfail);
diff --git a/test/recipes/05-test_rand.t b/test/recipes/05-test_rand.t
index d70b65c649..69a1887bb0 100644
--- a/test/recipes/05-test_rand.t
+++ b/test/recipes/05-test_rand.t
@@ -16,12 +16,14 @@ use Cwd qw(abs_path);
plan tests => 6;
setup("test_rand");
-ok(run(test(["rand_test", srctop_file("test", "default.cnf")])));
+ok(run(test(["rand_test", srctop_file("test", "default.cnf"),
+ srctop_file("test", "rand_strict.cnf")])));
SKIP: {
skip "Skipping FIPS test in this build", 1 if disabled('fips');
- ok(run(test(["rand_test", srctop_file("test", "fips.cnf")])));
+ ok(run(test(["rand_test", srctop_file("test", "fips.cnf"),
+ srctop_file("test", "rand_strict.cnf")])));
}
ok(run(test(["drbgtest"])));
diff --git a/test/recipes/20-test_rand_config.t b/test/recipes/20-test_rand_config.t
index 1db541ffe0..34bcf38782 100644
--- a/test/recipes/20-test_rand_config.t
+++ b/test/recipes/20-test_rand_config.t
@@ -57,7 +57,33 @@ my @aria_tests = (
push @rand_tests, @aria_tests unless disabled("aria");
-plan tests => scalar @rand_tests * 2;
+# Configured seed sources must be honoured: an available one is used and
+# an unavailable one is an error rather than a silent fallback to the
+# operating system entropy sources. Not applicable to enable-fips-jitter
+# builds, which hard-wire the JITTER seed source.
+my $rand_seed_none =
+ grep { $_ eq 'OPENSSL_RAND_SEED_NONE' }
+ @{ config('openssl_feature_defines') // [] };
+my @seed_tests;
+if (disabled("fips-jitter")) {
+ push @seed_tests,
+ { seed => 'SEED-SRC',
+ expected_ok => 1,
+ desc => 'configured SEED-SRC seed source works' }
+ unless $rand_seed_none;
+ push @seed_tests,
+ { seed => 'SEED-SRC',
+ strict => 'yes',
+ expected_ok => 1,
+ desc => 'strictly configured SEED-SRC seed source works' }
+ unless $rand_seed_none;
+ push @seed_tests,
+ { seed => 'NONEXISTENT-SEED-SOURCE',
+ expected_ok => 0,
+ desc => 'unavailable configured seed source fails, no fallback' };
+}
+
+plan tests => scalar @rand_tests * 2 + scalar @seed_tests;
my $contents =<<'CONFIGEND';
openssl_conf = openssl_init
@@ -91,6 +117,20 @@ foreach (@rand_tests) {
ok(run(app(["openssl", "rand", "-writerand", "$result_dir/$tmpfile.bin"])));
}
+foreach (@seed_tests) {
+ my $tmpfile = 'rand_seed_config.cfg';
+ open(my $cfg, '>', $tmpfile) or die "Could not open file";
+ print $cfg $contents;
+ print $cfg "seed = $_->{seed}\n";
+ print $cfg "seed_strict = $_->{strict}\n" if defined $_->{strict};
+ close $cfg;
+
+ $ENV{OPENSSL_CONF} = $tmpfile;
+
+ my $ok = run(app(["openssl", "rand", "-hex", "16"]));
+ ok(!$ok == !$_->{expected_ok}, $_->{desc});
+}
+
# Check that the stdout output contains the expected values.
sub comparelines {
my @lines = run(app(["openssl", "list", "--random-instances"]),