Add master DRBG for reseeding
authorDr. Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
Fri, 24 Nov 2017 13:59:58 +0000 (14:59 +0100)
committerDr. Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
Sun, 17 Dec 2017 22:12:10 +0000 (23:12 +0100)
A third shared DRBG is added, the so called master DRBG. Its sole purpose
is to reseed the two other shared DRBGs, the public and the private DRBG.
The randomness for the master DRBG is either pulled from the os entropy
sources, or added by the application using the RAND_add() call.

The master DRBG reseeds itself automatically after a given number of generate
requests, but can also be reseeded using RAND_seed() or RAND_add().
A reseeding of the master DRBG is automatically propagated to the public
and private DRBG. This construction fixes the problem, that up to now
the randomness provided by RAND_add() was added only to the public and
not to the private DRBG.

Signed-off-by: Dr. Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Kurt Roeckx <kurt@roeckx.be>
(Merged from https://github.com/openssl/openssl/pull/4402)

crypto/rand/drbg_lib.c
crypto/rand/drbg_rand.c
crypto/rand/rand_lcl.h
crypto/rand/rand_lib.c
include/internal/rand.h
ssl/ssl_lib.c
test/drbgtest.c
util/libcrypto.num

index c471b6e..67a79d4 100644 (file)
 #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 = {
index f45ef14..9d10aaa 100644 (file)
@@ -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;
 }
index 5e319d8..ad79434 100644 (file)
 /* 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;
 
index a290e5c..07f5427 100644 (file)
@@ -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;
 
index feda9be..26d6c38 100644 (file)
@@ -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
index 37e83bd..fb113ba 100644 (file)
@@ -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,
index 25920d6..b1fc751 100644 (file)
@@ -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;
 }
index 10ffa2c..7bfa601 100644 (file)
@@ -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: