summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crypto/rand/drbg_lib.c215
-rw-r--r--crypto/rand/drbg_rand.c4
-rw-r--r--crypto/rand/rand_lcl.h28
-rw-r--r--crypto/rand/rand_lib.c6
-rw-r--r--include/internal/rand.h8
-rw-r--r--ssl/ssl_lib.c2
-rw-r--r--test/drbgtest.c284
-rw-r--r--util/libcrypto.num5
8 files changed, 474 insertions, 78 deletions
diff --git a/crypto/rand/drbg_lib.c b/crypto/rand/drbg_lib.c
index c471b6eda4..67a79d4efe 100644
--- a/crypto/rand/drbg_lib.c
+++ b/crypto/rand/drbg_lib.c
@@ -15,12 +15,6 @@
#include "internal/thread_once.h"
#include "internal/rand_int.h"
-static RAND_DRBG rand_drbg; /* The default global DRBG. */
-static RAND_DRBG priv_drbg; /* The global private-key DRBG. */
-
-/* NIST SP 800-90A DRBG recommends the use of a personalization string. */
-static const char ossl_pers_string[] = "OpenSSL NIST SP 800-90A DRBG";
-
/*
* Support framework for NIST SP 800-90A DRBG, AES-CTR mode.
* The RAND_DRBG is OpenSSL's pointer to an instance of the DRBG.
@@ -33,9 +27,72 @@ static const char ossl_pers_string[] = "OpenSSL NIST SP 800-90A DRBG";
* a much bigger deal than just re-setting an allocated resource.)
*/
+/*
+ * THE THREE SHARED DRBGs
+ *
+ * There are three shared DRBGs (master, public and private), which are
+ * accessed concurrently by all threads.
+ *
+ * THE MASTER DRBG
+ *
+ * Not used directly by the application, only for reseeding the two other
+ * DRBGs. It reseeds itself by pulling either randomness from os entropy
+ * sources or by consuming randomnes which was added by RAND_add()
+ */
+static RAND_DRBG drbg_master;
+/*
+ * THE PUBLIC DRBG
+ *
+ * Used by default for generating random bytes using RAND_bytes().
+ */
+static RAND_DRBG drbg_public;
+/*
+ * THE PRIVATE DRBG
+ *
+ * Used by default for generating private keys using RAND_priv_bytes()
+ */
+static RAND_DRBG drbg_private;
+/*+
+ * DRBG HIERARCHY
+ *
+ * In addition there are DRBGs, which are not shared, but used only by a
+ * single thread at every time, for example the DRBGs which are owned by
+ * an SSL context. All DRBGs are organized in a hierarchical fashion
+ * with the <master> DRBG as root.
+ *
+ * This gives the following overall picture:
+ *
+ * <os entropy sources>
+ * |
+ * RAND_add() ==> <master> \
+ * / \ | shared DRBGs (with locking)
+ * <public> <private> /
+ * |
+ * <ssl> owned by an SSL context
+ *
+ * AUTOMATIC RESEEDING
+ *
+ * Before satisfying a generate request, a DRBG reseeds itself automatically
+ * if the number of generate requests since the last reseeding exceeds a
+ * certain threshold, the so called |reseed_interval|. This automatic
+ * reseeding can be disabled by setting the |reseed_interval| to 0.
+ *
+ * MANUAL RESEEDING
+ *
+ * For the three shared DRBGs (and only for these) there is a way to reseed
+ * them manually by calling RAND_seed() (or RAND_add() with a positive
+ * |randomness| argument). This will immediately reseed the <master> DRBG.
+ * Its immediate children (<public> and <private> DRBG) will detect this
+ * on their next generate call and reseed, pulling randomness from <master>.
+ */
+
+
+/* NIST SP 800-90A DRBG recommends the use of a personalization string. */
+static const char ossl_pers_string[] = "OpenSSL NIST SP 800-90A DRBG";
+
static CRYPTO_ONCE rand_drbg_init = CRYPTO_ONCE_STATIC_INIT;
-static int drbg_setup(RAND_DRBG *drbg, const char *name);
+static int drbg_setup(RAND_DRBG *drbg, const char *name, RAND_DRBG *parent);
/*
* Set/initialize |drbg| to be of type |nid|, with optional |flags|.
@@ -72,6 +129,8 @@ int RAND_DRBG_set(RAND_DRBG *drbg, int nid, unsigned int flags)
/*
* Allocate memory and initialize a new DRBG. The |parent|, if not
* NULL, will be used to auto-seed this RAND_DRBG as needed.
+ *
+ * Returns a pointer to the new DRBG instance on success, NULL on failure.
*/
RAND_DRBG *RAND_DRBG_new(int type, unsigned int flags, RAND_DRBG *parent)
{
@@ -86,12 +145,10 @@ RAND_DRBG *RAND_DRBG_new(int type, unsigned int flags, RAND_DRBG *parent)
if (RAND_DRBG_set(drbg, type, flags) < 0)
goto err;
- if (parent != NULL) {
- if (!RAND_DRBG_set_callbacks(drbg, rand_drbg_get_entropy,
- rand_drbg_cleanup_entropy,
- NULL, NULL))
- goto err;
- }
+ if (!RAND_DRBG_set_callbacks(drbg, rand_drbg_get_entropy,
+ rand_drbg_cleanup_entropy,
+ NULL, NULL))
+ goto err;
return drbg;
@@ -164,7 +221,14 @@ int RAND_DRBG_instantiate(RAND_DRBG *drbg,
}
drbg->state = DRBG_READY;
- drbg->reseed_counter = 1;
+ drbg->generate_counter = 0;
+
+ if (drbg->reseed_counter > 0) {
+ if (drbg->parent == NULL)
+ drbg->reseed_counter++;
+ else
+ drbg->reseed_counter = drbg->parent->reseed_counter;
+ }
end:
if (entropy != NULL && drbg->cleanup_entropy != NULL)
@@ -194,7 +258,6 @@ int RAND_DRBG_uninstantiate(RAND_DRBG *drbg)
{
int ret = ctr_uninstantiate(drbg);
- OPENSSL_cleanse(&drbg->ctr, sizeof(drbg->ctr));
drbg->state = DRBG_UNINITIALISED;
return ret;
}
@@ -238,8 +301,16 @@ int RAND_DRBG_reseed(RAND_DRBG *drbg,
if (!ctr_reseed(drbg, entropy, entropylen, adin, adinlen))
goto end;
+
drbg->state = DRBG_READY;
- drbg->reseed_counter = 1;
+ drbg->generate_counter = 0;
+
+ if (drbg->reseed_counter > 0) {
+ if (drbg->parent == NULL)
+ drbg->reseed_counter++;
+ else
+ drbg->reseed_counter = drbg->parent->reseed_counter;
+ }
end:
if (entropy != NULL && drbg->cleanup_entropy != NULL)
@@ -310,12 +381,18 @@ int rand_drbg_restart(RAND_DRBG *drbg,
}
/* repair error state */
- if (drbg->state == DRBG_ERROR)
+ if (drbg->state == DRBG_ERROR) {
RAND_DRBG_uninstantiate(drbg);
+ /* The drbg->ctr member needs to be reinitialized before reinstantiation */
+ RAND_DRBG_set(drbg, drbg->nid, drbg->flags);
+ }
/* repair uninitialized state */
if (drbg->state == DRBG_UNINITIALISED) {
- drbg_setup(drbg, NULL);
+ /* reinstantiate drbg */
+ RAND_DRBG_instantiate(drbg,
+ (const unsigned char *) ossl_pers_string,
+ sizeof(ossl_pers_string) - 1);
/* already reseeded. prevent second reseeding below */
reseeded = (drbg->state == DRBG_READY);
}
@@ -394,8 +471,15 @@ int RAND_DRBG_generate(RAND_DRBG *drbg, unsigned char *out, size_t outlen,
reseed_required = 1;
}
- if (drbg->reseed_counter >= drbg->reseed_interval)
- reseed_required = 1;
+ if (drbg->reseed_interval > 0) {
+ if (drbg->generate_counter >= drbg->reseed_interval)
+ reseed_required = 1;
+ }
+
+ if (drbg->reseed_counter > 0 && drbg->parent != NULL) {
+ if (drbg->reseed_counter != drbg->parent->reseed_counter)
+ reseed_required = 1;
+ }
if (reseed_required || prediction_resistance) {
if (!RAND_DRBG_reseed(drbg, adin, adinlen)) {
@@ -412,7 +496,7 @@ int RAND_DRBG_generate(RAND_DRBG *drbg, unsigned char *out, size_t outlen,
return 0;
}
- drbg->reseed_counter++;
+ drbg->generate_counter++;
return 1;
}
@@ -472,10 +556,16 @@ int RAND_DRBG_set_callbacks(RAND_DRBG *drbg,
/*
* Set the reseed interval.
+ *
+ * The drbg will reseed automatically whenever the number of generate
+ * requests exceeds the given reseed interval. If the reseed interval
+ * is 0, then this automatic reseeding is disabled.
+ *
+ * Returns 1 on success, 0 on failure.
*/
-int RAND_DRBG_set_reseed_interval(RAND_DRBG *drbg, int interval)
+int RAND_DRBG_set_reseed_interval(RAND_DRBG *drbg, unsigned int interval)
{
- if (interval < 0 || interval > MAX_RESEED)
+ if (interval > MAX_RESEED_INTERVAL)
return 0;
drbg->reseed_interval = interval;
return 1;
@@ -501,31 +591,41 @@ void *RAND_DRBG_get_ex_data(const RAND_DRBG *drbg, int idx)
*/
/*
- * Initializes the DRBG with default settings.
- * For global DRBGs a global lock is created with the given name
- * Returns 1 on success, 0 on failure
+ * Initializes the given global DRBG with default settings.
+ * A global lock for the DRBG is created with the given name.
+ *
+ * Returns a pointer to the new DRBG instance on success, NULL on failure.
*/
-static int drbg_setup(RAND_DRBG *drbg, const char *name)
+static int drbg_setup(RAND_DRBG *drbg, const char *name, RAND_DRBG *parent)
{
int ret = 1;
- if (name != NULL) {
- if (drbg->lock != NULL) {
- RANDerr(RAND_F_DRBG_SETUP, ERR_R_INTERNAL_ERROR);
- return 0;
- }
+ if (name == NULL || drbg->lock != NULL) {
+ RANDerr(RAND_F_DRBG_SETUP, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
- drbg->lock = CRYPTO_THREAD_glock_new(name);
- if (drbg->lock == NULL) {
- RANDerr(RAND_F_DRBG_SETUP, RAND_R_FAILED_TO_CREATE_LOCK);
- return 0;
- }
+ drbg->lock = CRYPTO_THREAD_glock_new(name);
+ if (drbg->lock == NULL) {
+ RANDerr(RAND_F_DRBG_SETUP, RAND_R_FAILED_TO_CREATE_LOCK);
+ return 0;
}
ret &= RAND_DRBG_set(drbg,
RAND_DRBG_NID, RAND_DRBG_FLAG_CTR_USE_DF) == 1;
ret &= RAND_DRBG_set_callbacks(drbg, rand_drbg_get_entropy,
rand_drbg_cleanup_entropy, NULL, NULL) == 1;
+
+ if (parent == NULL)
+ drbg->reseed_interval = MASTER_RESEED_INTERVAL;
+ else {
+ drbg->parent = parent;
+ drbg->reseed_interval = SLAVE_RESEED_INTERVAL;
+ }
+
+ /* enable seed propagation */
+ drbg->reseed_counter = 1;
+
/*
* Ignore instantiation error so support just-in-time instantiation.
*
@@ -546,8 +646,9 @@ DEFINE_RUN_ONCE_STATIC(do_rand_drbg_init)
{
int ret = 1;
- ret &= drbg_setup(&rand_drbg, "rand_drbg");
- ret &= drbg_setup(&priv_drbg, "priv_drbg");
+ ret &= drbg_setup(&drbg_master, "drbg_master", NULL);
+ ret &= drbg_setup(&drbg_public, "drbg_public", &drbg_master);
+ ret &= drbg_setup(&drbg_private, "drbg_private", &drbg_master);
return ret;
}
@@ -562,8 +663,9 @@ static void drbg_cleanup(RAND_DRBG *drbg)
/* Clean up the global DRBGs before exit */
void rand_drbg_cleanup_int(void)
{
- drbg_cleanup(&rand_drbg);
- drbg_cleanup(&priv_drbg);
+ drbg_cleanup(&drbg_private);
+ drbg_cleanup(&drbg_public);
+ drbg_cleanup(&drbg_master);
}
/* Implements the default OpenSSL RAND_bytes() method */
@@ -571,7 +673,7 @@ static int drbg_bytes(unsigned char *out, int count)
{
int ret = 0;
size_t chunk;
- RAND_DRBG *drbg = RAND_DRBG_get0_global();
+ RAND_DRBG *drbg = RAND_DRBG_get0_public();
if (drbg == NULL)
return 0;
@@ -599,7 +701,7 @@ err:
static int drbg_add(const void *buf, int num, double randomness)
{
int ret = 0;
- RAND_DRBG *drbg = RAND_DRBG_get0_global();
+ RAND_DRBG *drbg = RAND_DRBG_get0_master();
if (drbg == NULL)
return 0;
@@ -636,7 +738,7 @@ static int drbg_seed(const void *buf, int num)
static int drbg_status(void)
{
int ret;
- RAND_DRBG *drbg = RAND_DRBG_get0_global();
+ RAND_DRBG *drbg = RAND_DRBG_get0_master();
if (drbg == NULL)
return 0;
@@ -648,27 +750,40 @@ static int drbg_status(void)
}
/*
- * Get the global public DRBG.
+ * Get the master DRBG.
+ * Returns pointer to the DRBG on success, NULL on failure.
+ *
+ */
+RAND_DRBG *RAND_DRBG_get0_master(void)
+{
+ if (!RUN_ONCE(&rand_drbg_init, do_rand_drbg_init))
+ return NULL;
+
+ return &drbg_master;
+}
+
+/*
+ * Get the public DRBG.
* Returns pointer to the DRBG on success, NULL on failure.
*/
-RAND_DRBG *RAND_DRBG_get0_global(void)
+RAND_DRBG *RAND_DRBG_get0_public(void)
{
if (!RUN_ONCE(&rand_drbg_init, do_rand_drbg_init))
return NULL;
- return &rand_drbg;
+ return &drbg_public;
}
/*
- * Get the global private DRBG.
+ * Get the private DRBG.
* Returns pointer to the DRBG on success, NULL on failure.
*/
-RAND_DRBG *RAND_DRBG_get0_priv_global(void)
+RAND_DRBG *RAND_DRBG_get0_private(void)
{
if (!RUN_ONCE(&rand_drbg_init, do_rand_drbg_init))
return NULL;
- return &priv_drbg;
+ return &drbg_private;
}
RAND_METHOD rand_meth = {
diff --git a/crypto/rand/drbg_rand.c b/crypto/rand/drbg_rand.c
index f45ef1424c..9d10aaa8be 100644
--- a/crypto/rand/drbg_rand.c
+++ b/crypto/rand/drbg_rand.c
@@ -301,7 +301,7 @@ int ctr_generate(RAND_DRBG *drbg,
int ctr_uninstantiate(RAND_DRBG *drbg)
{
- memset(&drbg->ctr, 0, sizeof(drbg->ctr));
+ OPENSSL_cleanse(&drbg->ctr, sizeof(drbg->ctr));
return 1;
}
@@ -357,6 +357,6 @@ int ctr_init(RAND_DRBG *drbg)
}
drbg->max_request = 1 << 16;
- drbg->reseed_interval = MAX_RESEED;
+ drbg->reseed_interval = MAX_RESEED_INTERVAL;
return 1;
}
diff --git a/crypto/rand/rand_lcl.h b/crypto/rand/rand_lcl.h
index 5e319d8c99..ad79434ec4 100644
--- a/crypto/rand/rand_lcl.h
+++ b/crypto/rand/rand_lcl.h
@@ -20,8 +20,12 @@
/* How many times to read the TSC as a randomness source. */
# define TSC_READ_COUNT 4
-/* Maximum count allowed in reseeding */
-# define MAX_RESEED (1 << 24)
+/* Maximum reseed interval */
+# define MAX_RESEED_INTERVAL (1 << 24)
+
+/* Default reseed intervals */
+# define MASTER_RESEED_INTERVAL (1 << 8)
+# define SLAVE_RESEED_INTERVAL (1 << 16)
/* Max size of additional input and personalization string. */
# define DRBG_MAX_LENGTH 4096
@@ -106,8 +110,26 @@ struct rand_drbg_st {
size_t min_entropylen, max_entropylen;
size_t min_noncelen, max_noncelen;
size_t max_perslen, max_adinlen;
- unsigned int reseed_counter;
+
+ /* Counts the number of generate requests since the last reseed. */
+ unsigned int generate_counter;
+ /*
+ * Maximum number of generate requests until a reseed is required.
+ * This value is ignored if it is zero.
+ */
unsigned int reseed_interval;
+ /*
+ * Counts the number of reseeds since instantiation.
+ * This value is ignored if it is zero.
+ *
+ * This counter is used only for seed propagation from the <master> DRBG
+ * to its two children, the <public> and <private> DRBG. This feature is
+ * very special and its sole purpose is to ensure that any randomness which
+ * is added by RAND_add() or RAND_seed() will have an immediate effect on
+ * the output of RAND_bytes() resp. RAND_priv_bytes().
+ */
+ unsigned int reseed_counter;
+
size_t seedlen;
DRBG_STATUS state;
diff --git a/crypto/rand/rand_lib.c b/crypto/rand/rand_lib.c
index a290e5c1a2..07f54276d3 100644
--- a/crypto/rand/rand_lib.c
+++ b/crypto/rand/rand_lib.c
@@ -246,8 +246,8 @@ int RAND_poll(void)
const RAND_METHOD *meth = RAND_get_rand_method();
if (meth == RAND_OpenSSL()) {
- /* fill random pool and seed the default DRBG */
- RAND_DRBG *drbg = RAND_DRBG_get0_global();
+ /* fill random pool and seed the master DRBG */
+ RAND_DRBG *drbg = RAND_DRBG_get0_master();
if (drbg == NULL)
return 0;
@@ -639,7 +639,7 @@ int RAND_priv_bytes(unsigned char *buf, int num)
if (meth != RAND_OpenSSL())
return RAND_bytes(buf, num);
- drbg = RAND_DRBG_get0_priv_global();
+ drbg = RAND_DRBG_get0_private();
if (drbg == NULL)
return 0;
diff --git a/include/internal/rand.h b/include/internal/rand.h
index feda9beaa1..26d6c38f45 100644
--- a/include/internal/rand.h
+++ b/include/internal/rand.h
@@ -42,9 +42,11 @@ int RAND_DRBG_reseed(RAND_DRBG *drbg,
int RAND_DRBG_generate(RAND_DRBG *drbg, unsigned char *out, size_t outlen,
int prediction_resistance,
const unsigned char *adin, size_t adinlen);
-int RAND_DRBG_set_reseed_interval(RAND_DRBG *drbg, int interval);
-RAND_DRBG *RAND_DRBG_get0_global(void);
-RAND_DRBG *RAND_DRBG_get0_priv_global(void);
+int RAND_DRBG_set_reseed_interval(RAND_DRBG *drbg, unsigned int interval);
+
+RAND_DRBG *RAND_DRBG_get0_master(void);
+RAND_DRBG *RAND_DRBG_get0_public(void);
+RAND_DRBG *RAND_DRBG_get0_private(void);
/*
* EXDATA
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 37e83bddaf..fb113bab6d 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -691,7 +691,7 @@ SSL *SSL_new(SSL_CTX *ctx)
if (RAND_get_rand_method() == RAND_OpenSSL()) {
s->drbg =
RAND_DRBG_new(RAND_DRBG_NID, RAND_DRBG_FLAG_CTR_USE_DF,
- RAND_DRBG_get0_global());
+ RAND_DRBG_get0_public());
if (s->drbg == NULL
|| RAND_DRBG_instantiate(s->drbg,
(const unsigned char *) SSL_version_str,
diff --git a/test/drbgtest.c b/test/drbgtest.c
index 25920d6e40..b1fc751cd9 100644
--- a/test/drbgtest.c
+++ b/test/drbgtest.c
@@ -274,7 +274,7 @@ static int error_check(DRBG_SELFTEST_DATA *td)
RAND_DRBG *drbg = NULL;
TEST_CTX t;
unsigned char buff[1024];
- unsigned int reseed_counter_tmp;
+ unsigned int generate_counter_tmp;
int ret = 0;
if (!TEST_ptr(drbg = RAND_DRBG_new(0, 0, NULL)))
@@ -369,15 +369,15 @@ static int error_check(DRBG_SELFTEST_DATA *td)
/* Instantiate again with valid data */
if (!instantiate(drbg, td, &t))
goto err;
- reseed_counter_tmp = drbg->reseed_counter;
- drbg->reseed_counter = drbg->reseed_interval;
+ generate_counter_tmp = drbg->generate_counter;
+ drbg->generate_counter = drbg->reseed_interval;
/* Generate output and check entropy has been requested for reseed */
t.entropycnt = 0;
if (!TEST_true(RAND_DRBG_generate(drbg, buff, td->exlen, 0,
td->adin, td->adinlen))
|| !TEST_int_eq(t.entropycnt, 1)
- || !TEST_int_eq(drbg->reseed_counter, reseed_counter_tmp + 1)
+ || !TEST_int_eq(drbg->generate_counter, generate_counter_tmp + 1)
|| !uninstantiate(drbg))
goto err;
@@ -394,15 +394,15 @@ static int error_check(DRBG_SELFTEST_DATA *td)
/* Test reseed counter works */
if (!instantiate(drbg, td, &t))
goto err;
- reseed_counter_tmp = drbg->reseed_counter;
- drbg->reseed_counter = drbg->reseed_interval;
+ generate_counter_tmp = drbg->generate_counter;
+ drbg->generate_counter = drbg->reseed_interval;
/* Generate output and check entropy has been requested for reseed */
t.entropycnt = 0;
if (!TEST_true(RAND_DRBG_generate(drbg, buff, td->exlen, 0,
td->adin, td->adinlen))
|| !TEST_int_eq(t.entropycnt, 1)
- || !TEST_int_eq(drbg->reseed_counter, reseed_counter_tmp + 1)
+ || !TEST_int_eq(drbg->generate_counter, generate_counter_tmp + 1)
|| !uninstantiate(drbg))
goto err;
@@ -475,19 +475,275 @@ err:
return rv;
}
-#define RAND_ADD_SIZE 500
+/*
+ * Hook context data, attached as EXDATA to the RAND_DRBG
+ */
+typedef struct hook_ctx_st {
+ RAND_DRBG *drbg;
+ /*
+ * Currently, all DRBGs use the same get_entropy() callback.
+ * The tests however, don't assume this and store
+ * the original callback for every DRBG separately.
+ */
+ RAND_DRBG_get_entropy_fn get_entropy;
+ /* forces a failure of the get_entropy() call if nonzero */
+ int fail;
+ /* counts successful reseeds */
+ int reseed_count;
+} HOOK_CTX;
+
+static HOOK_CTX master_ctx, public_ctx, private_ctx;
+
+static HOOK_CTX *get_hook_ctx(RAND_DRBG *drbg)
+{
+ return (HOOK_CTX *)RAND_DRBG_get_ex_data(drbg, app_data_index);
+}
+
+/* Intercepts and counts calls to the get_entropy() callback */
+static size_t get_entropy_hook(RAND_DRBG *drbg, unsigned char **pout,
+ int entropy, size_t min_len, size_t max_len)
+{
+ size_t ret;
+ HOOK_CTX *ctx = get_hook_ctx(drbg);
+
+ if (ctx->fail != 0)
+ return 0;
+
+ ret = ctx->get_entropy(
+ drbg, pout, entropy, min_len, max_len);
+
+ if (ret != 0)
+ ctx->reseed_count++;
+ return ret;
+}
+
+/* Installs a hook for the get_entropy() callback of the given drbg */
+static void hook_drbg(RAND_DRBG *drbg, HOOK_CTX *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->drbg = drbg;
+ ctx->get_entropy = drbg->get_entropy;
+ drbg->get_entropy = get_entropy_hook;
+ RAND_DRBG_set_ex_data(drbg, app_data_index, ctx);
+}
+
+/* Installs the hook for the get_entropy() callback of the given drbg */
+static void unhook_drbg(RAND_DRBG *drbg)
+{
+ HOOK_CTX *ctx = get_hook_ctx(drbg);
+
+ drbg->get_entropy = ctx->get_entropy;
+ CRYPTO_free_ex_data(CRYPTO_EX_INDEX_DRBG, drbg, &drbg->ex_data);
+}
-static int test_rand_add(void)
+/* Resets the given hook context */
+static void reset_hook_ctx(HOOK_CTX *ctx)
{
- char *p;
+ ctx->fail = 0;
+ ctx->reseed_count = 0;
+}
+
+/* Resets all drbg hook contexts */
+static void reset_drbg_hook_ctx()
+{
+ reset_hook_ctx(&master_ctx);
+ reset_hook_ctx(&public_ctx);
+ reset_hook_ctx(&private_ctx);
+}
+
+/*
+ * Generates random output using RAND_bytes() and RAND_priv_bytes()
+ * and checks whether the three shared DRBGs were reseeded as
+ * expected.
+ *
+ * |expect_success|: expected outcome (as reported by RAND_status())
+ * |master|, |public|, |private|: pointers to the three shared DRBGs
+ * |expect_xxx_reseed| =
+ * 1: it is expected that the specified DRBG is reseeded
+ * 0: it is expected that the specified DRBG is not reseeded
+ * -1: don't check whether the specified DRBG was reseeded or not
+ */
+static int test_drbg_reseed(int expect_success,
+ RAND_DRBG *master,
+ RAND_DRBG *public,
+ RAND_DRBG *private,
+ int expect_master_reseed,
+ int expect_public_reseed,
+ int expect_private_reseed
+ )
+{
+ unsigned char buf[32];
+ int expected_state = (expect_success ? DRBG_READY : DRBG_ERROR);
+
+ /*
+ * step 1: check preconditions
+ */
+
+ /* Test whether seed propagation is enabled */
+ if (!TEST_int_ne(master->reseed_counter, 0)
+ || !TEST_int_ne(public->reseed_counter, 0)
+ || !TEST_int_ne(private->reseed_counter, 0))
+ return 0;
+
+ /* Check whether the master DRBG's reseed counter is the largest one */
+ if (!TEST_int_le(public->reseed_counter, master->reseed_counter)
+ || !TEST_int_le(private->reseed_counter, master->reseed_counter))
+ return 0;
+
+ /*
+ * step 2: generate random output
+ */
+
+ /* Generate random output from the public and private DRBG */
+ if (!TEST_int_eq(RAND_bytes(buf, sizeof(buf)), expect_success)
+ || !TEST_int_eq(RAND_priv_bytes(buf, sizeof(buf)), expect_success))
+ return 0;
+
+
+ /*
+ * step 3: check postconditions
+ */
- if (!TEST_ptr(p = calloc(RAND_ADD_SIZE, 1)))
+ /* Test whether reseeding succeeded as expected */
+ if (!TEST_int_eq(master->state, expected_state)
+ || !TEST_int_eq(public->state, expected_state)
+ || !TEST_int_eq(private->state, expected_state))
return 0;
- RAND_add(p, RAND_ADD_SIZE, RAND_ADD_SIZE);
- free(p);
+
+ if (expect_master_reseed >= 0) {
+ /* Test whether master DRBG was reseeded as expected */
+ if (!TEST_int_eq(master_ctx.reseed_count, expect_master_reseed))
+ return 0;
+ }
+
+ if (expect_public_reseed >= 0) {
+ /* Test whether public DRBG was reseeded as expected */
+ if (!TEST_int_eq(public_ctx.reseed_count, expect_public_reseed))
+ return 0;
+ }
+
+ if (expect_private_reseed >= 0) {
+ /* Test whether public DRBG was reseeded as expected */
+ if (!TEST_int_eq(private_ctx.reseed_count, expect_private_reseed))
+ return 0;
+ }
+
+ if (expect_success == 1) {
+ /* Test whether all three reseed counters are synchronized */
+ if (!TEST_int_eq(public->reseed_counter, master->reseed_counter)
+ || !TEST_int_eq(private->reseed_counter, master->reseed_counter))
+ return 0;
+ } else {
+ ERR_clear_error();
+ }
+
return 1;
}
+/*
+ * Test whether the default rand_method (RAND_OpenSSL()) is
+ * setup correctly, in particular whether reseeding works
+ * as designed.
+ */
+static int test_rand_reseed(void)
+{
+ RAND_DRBG *master, *public, *private;
+ unsigned char rand_add_buf[256];
+ int rv=0;
+
+ /* Check whether RAND_OpenSSL() is the default method */
+ if (!TEST_ptr_eq(RAND_get_rand_method(), RAND_OpenSSL()))
+ return 0;
+
+ /* All three DRBGs should be non-null */
+ if (!TEST_ptr(master = RAND_DRBG_get0_master())
+ || !TEST_ptr(public = RAND_DRBG_get0_public())
+ || !TEST_ptr(private = RAND_DRBG_get0_private()))
+ return 0;
+
+ /* There should be three distinct DRBGs, two of them chained to master */
+ if (!TEST_ptr_ne(public, private)
+ || !TEST_ptr_ne(public, master)
+ || !TEST_ptr_ne(private, master)
+ || !TEST_ptr_eq(public->parent, master)
+ || !TEST_ptr_eq(private->parent, master))
+ return 0;
+
+ /* Install hooks for the following tests */
+ hook_drbg(master, &master_ctx);
+ hook_drbg(public, &public_ctx);
+ hook_drbg(private, &private_ctx);
+
+ /*
+ * Test initial state of shared DRBs
+ */
+ if (!TEST_true(test_drbg_reseed(1, master, public, private, 0, 0, 0)))
+ goto error;
+ reset_drbg_hook_ctx();
+
+ /*
+ * Test whether the public and private DRBG are both reseeded when their
+ * reseed counters differ from the master's reseed counter.
+ */
+ master->reseed_counter++;
+ if (!TEST_true(test_drbg_reseed(1, master, public, private, 0, 1, 1)))
+ goto error;
+ reset_drbg_hook_ctx();
+
+ /*
+ * Test whether the public DRBG is reseeded when its reseed counter differs
+ * from the master's reseed counter.
+ */
+ master->reseed_counter++;
+ private->reseed_counter++;
+ if (!TEST_true(test_drbg_reseed(1, master, public, private, 0, 1, 0)))
+ goto error;
+ reset_drbg_hook_ctx();
+
+ /*
+ * Test whether the private DRBG is reseeded when its reseed counter differs
+ * from the master's reseed counter.
+ */
+ master->reseed_counter++;
+ public->reseed_counter++;
+ if (!TEST_true(test_drbg_reseed(1, master, public, private, 0, 0, 1)))
+ goto error;
+ reset_drbg_hook_ctx();
+
+
+ /* fill 'randomness' buffer with some arbitrary data */
+ memset(rand_add_buf, 'r', sizeof(rand_add_buf));
+
+ /*
+ * Test whether all three DRBGs are reseeded by RAND_add()
+ */
+ RAND_add(rand_add_buf, sizeof(rand_add_buf), sizeof(rand_add_buf));
+ if (!TEST_true(test_drbg_reseed(1, master, public, private, 1, 1, 1)))
+ goto error;
+ reset_drbg_hook_ctx();
+
+
+ /*
+ * Test whether none of the DRBGs is reseed if the master fails to reseed
+ */
+ master_ctx.fail = 1;
+ master->reseed_counter++;
+ RAND_add(rand_add_buf, sizeof(rand_add_buf), sizeof(rand_add_buf));
+ if (!TEST_true(test_drbg_reseed(0, master, public, private, 0, 0, 0)))
+ goto error;
+ reset_drbg_hook_ctx();
+
+ rv = 1;
+
+error:
+ /* Remove hooks */
+ unhook_drbg(master);
+ unhook_drbg(public);
+ unhook_drbg(private);
+
+ return rv;
+}
+
int setup_tests(void)
{
@@ -495,6 +751,6 @@ int setup_tests(void)
ADD_ALL_TESTS(test_kats, OSSL_NELEM(drbg_test));
ADD_ALL_TESTS(test_error_checks, OSSL_NELEM(drbg_test));
- ADD_TEST(test_rand_add);
+ ADD_TEST(test_rand_reseed);
return 1;
}
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 10ffa2c3d5..7bfa6015d0 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -4371,7 +4371,7 @@ SCRYPT_PARAMS_it 4314 1_1_1 EXIST:EXPORT_VAR_AS_FUNCTION:
CRYPTO_secure_clear_free 4315 1_1_0g EXIST::FUNCTION:
EVP_PKEY_meth_get0 4316 1_1_1 EXIST::FUNCTION:
EVP_PKEY_meth_get_count 4317 1_1_1 EXIST::FUNCTION:
-RAND_DRBG_get0_global 4319 1_1_1 EXIST::FUNCTION:
+RAND_DRBG_get0_public 4319 1_1_1 EXIST::FUNCTION:
RAND_priv_bytes 4320 1_1_1 EXIST::FUNCTION:
BN_priv_rand 4321 1_1_1 EXIST::FUNCTION:
BN_priv_rand_range 4322 1_1_1 EXIST::FUNCTION:
@@ -4381,7 +4381,7 @@ ASN1_TIME_compare 4325 1_1_1 EXIST::FUNCTION:
EVP_PKEY_CTX_ctrl_uint64 4326 1_1_1 EXIST::FUNCTION:
EVP_DigestFinalXOF 4327 1_1_1 EXIST::FUNCTION:
ERR_clear_last_mark 4328 1_1_1 EXIST::FUNCTION:
-RAND_DRBG_get0_priv_global 4329 1_1_1 EXIST::FUNCTION:
+RAND_DRBG_get0_private 4329 1_1_1 EXIST::FUNCTION:
EVP_aria_192_ccm 4330 1_1_1 EXIST::FUNCTION:ARIA
EVP_aria_256_gcm 4331 1_1_1 EXIST::FUNCTION:ARIA
EVP_aria_256_ccm 4332 1_1_1 EXIST::FUNCTION:ARIA
@@ -4446,3 +4446,4 @@ RSA_set0_multi_prime_params 4390 1_1_1 EXIST::FUNCTION:RSA
RSA_get_version 4391 1_1_1 EXIST::FUNCTION:RSA
RSA_meth_get_multi_prime_keygen 4392 1_1_1 EXIST::FUNCTION:RSA
RSA_meth_set_multi_prime_keygen 4393 1_1_1 EXIST::FUNCTION:RSA
+RAND_DRBG_get0_master 4394 1_1_1 EXIST::FUNCTION: