[openssl-commits] [openssl] master update

paul.dale at oracle.com paul.dale at oracle.com
Sun Dec 17 22:29:57 UTC 2017


The branch master has been updated
       via  efb8128ad56924736f50c01cde94b1716560aec7 (commit)
       via  933033b6928e9f01b99e7f2125f840bc052c5d96 (commit)
       via  08a65d9686b131cb4193feaaf1d5cef941fa349c (commit)
       via  a93ba405b0327db9106f8f224112a2b64fb264e7 (commit)
      from  56d362881eb9173d74f89aa8c4c4b42bc3397a17 (commit)


- Log -----------------------------------------------------------------
commit efb8128ad56924736f50c01cde94b1716560aec7
Author: Dr. Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
Date:   Mon Nov 20 23:27:23 2017 +0100

    Make DRBG uninstantiate() and instantiate() methods inverse to each other
    
    Previously, the RAND_DRBG_uninstantiate() call was not exactly inverse to
    RAND_DRBG_instantiate(), because some important member values of the
    drbg->ctr member where cleared. Now these values are restored internally.
    
    Signed-off-by: Dr. Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    Reviewed-by: Kurt Roeckx <kurt at roeckx.be>
    (Merged from https://github.com/openssl/openssl/pull/4402)

commit 933033b6928e9f01b99e7f2125f840bc052c5d96
Author: Dr. Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
Date:   Mon Nov 6 02:29:15 2017 +0100

    Allocate the three shared DRBGs on the secure heap
    
    Signed-off-by: Dr. Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    Reviewed-by: Kurt Roeckx <kurt at roeckx.be>
    (Merged from https://github.com/openssl/openssl/pull/4402)

commit 08a65d9686b131cb4193feaaf1d5cef941fa349c
Author: Dr. Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
Date:   Fri Nov 24 15:24:51 2017 +0100

    Implement automatic reseeding of DRBG after a specified time interval
    
    Every DRBG now supports automatic reseeding not only after a given
    number of generate requests, but also after a specified time interval.
    
    Signed-off-by: Dr. Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    Reviewed-by: Kurt Roeckx <kurt at roeckx.be>
    (Merged from https://github.com/openssl/openssl/pull/4402)

commit a93ba405b0327db9106f8f224112a2b64fb264e7
Author: Dr. Matthias St. Pierre <Matthias.St.Pierre at ncp-e.com>
Date:   Fri Nov 24 14:59:58 2017 +0100

    Add master DRBG for reseeding
    
    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 at ncp-e.com>
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    Reviewed-by: Kurt Roeckx <kurt at roeckx.be>
    (Merged from https://github.com/openssl/openssl/pull/4402)

-----------------------------------------------------------------------

Summary of changes:
 crypto/rand/drbg_lib.c  | 305 +++++++++++++++++++++++++++++++++++++-----------
 crypto/rand/drbg_rand.c |   6 +-
 crypto/rand/rand_lcl.h  |  40 ++++++-
 crypto/rand/rand_lib.c  |   6 +-
 include/internal/rand.h |   9 +-
 ssl/ssl_lib.c           |   2 +-
 test/drbgtest.c         | 297 +++++++++++++++++++++++++++++++++++++++++++---
 util/libcrypto.num      |   6 +-
 8 files changed, 574 insertions(+), 97 deletions(-)

diff --git a/crypto/rand/drbg_lib.c b/crypto/rand/drbg_lib.c
index c471b6e..e33877e 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,14 +27,84 @@ 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 one of the following two conditions holds:
+ *
+ * - the number of generate requests since the last reseeding exceeds a
+ *   certain threshold, the so called |reseed_interval|. This behaviour
+ *   can be disabled by setting the |reseed_interval| to 0.
+ *
+ * - the time elapsed since the last reseeding exceeds a certain time
+ *   interval, the so called |reseed_time_interval|. This behaviour
+ *   can be disabled by setting the |reseed_time_interval| to 0.
+ *
+ * MANUAL RESEEDING
+ *
+ * For the three shared DRBGs (and only for these) there is another way to
+ * reseed them manually by calling RAND_seed() (or RAND_add() with a positive
+ * |randomness| argument). This will immediately reseed the <master> DRBG.
+ * The <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 RAND_DRBG *drbg_setup(const char *name, RAND_DRBG *parent);
+static void drbg_cleanup(RAND_DRBG *drbg);
 
 /*
  * Set/initialize |drbg| to be of type |nid|, with optional |flags|.
- * Return -2 if the type is not supported, 1 on success and -1 on
- * failure.
+ *
+ * Returns 1 on success, 0 on failure.
  */
 int RAND_DRBG_set(RAND_DRBG *drbg, int nid, unsigned int flags)
 {
@@ -53,7 +117,7 @@ int RAND_DRBG_set(RAND_DRBG *drbg, int nid, unsigned int flags)
     switch (nid) {
     default:
         RANDerr(RAND_F_RAND_DRBG_SET, RAND_R_UNSUPPORTED_DRBG_TYPE);
-        return -2;
+        return 0;
     case 0:
         /* Uninitialized; that's okay. */
         return 1;
@@ -64,7 +128,7 @@ int RAND_DRBG_set(RAND_DRBG *drbg, int nid, unsigned int flags)
         break;
     }
 
-    if (ret < 0)
+    if (ret == 0)
         RANDerr(RAND_F_RAND_DRBG_SET, RAND_R_ERROR_INITIALISING_DRBG);
     return ret;
 }
@@ -72,6 +136,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)
 {
@@ -83,15 +149,13 @@ RAND_DRBG *RAND_DRBG_new(int type, unsigned int flags, RAND_DRBG *parent)
     }
     drbg->fork_count = rand_fork_count;
     drbg->parent = parent;
-    if (RAND_DRBG_set(drbg, type, flags) < 0)
+    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;
 
@@ -118,6 +182,8 @@ void RAND_DRBG_free(RAND_DRBG *drbg)
  * |perslen| as prediction-resistance input.
  *
  * Requires that drbg->lock is already locked for write, if non-null.
+ *
+ * Returns 1 on success, 0 on failure.
  */
 int RAND_DRBG_instantiate(RAND_DRBG *drbg,
                           const unsigned char *pers, size_t perslen)
@@ -164,7 +230,14 @@ int RAND_DRBG_instantiate(RAND_DRBG *drbg,
     }
 
     drbg->state = DRBG_READY;
-    drbg->reseed_counter = 1;
+    drbg->generate_counter = 0;
+    drbg->reseed_time = time(NULL);
+    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)
@@ -189,20 +262,25 @@ end:
  * Uninstantiate |drbg|. Must be instantiated before it can be used.
  *
  * Requires that drbg->lock is already locked for write, if non-null.
+ *
+ * Returns 1 on success, 0 on failure.
  */
 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;
+    /* Clear the entire drbg->ctr struct, then reset some important
+     * members of the drbg->ctr struct (e.g. keysize, df_ks) to their
+     * initial values.
+     */
+    ctr_uninstantiate(drbg);
+    return RAND_DRBG_set(drbg, drbg->nid, drbg->flags);
 }
 
 /*
  * Reseed |drbg|, mixing in the specified data
  *
  * Requires that drbg->lock is already locked for write, if non-null.
+ *
+ * Returns 1 on success, 0 on failure.
  */
 int RAND_DRBG_reseed(RAND_DRBG *drbg,
                      const unsigned char *adin, size_t adinlen)
@@ -238,8 +316,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;
+    drbg->reseed_time = time(NULL);
+    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)
@@ -315,7 +401,10 @@ int rand_drbg_restart(RAND_DRBG *drbg,
 
     /* 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 +483,20 @@ 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_time_interval > 0) {
+        time_t now = time(NULL);
+        if (now < drbg->reseed_time
+            || now - drbg->reseed_time >= drbg->reseed_time_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 +513,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,16 +573,40 @@ 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 feature 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;
 }
 
 /*
+ * Set the reseed time interval.
+ *
+ * The drbg will reseed automatically whenever the time elapsed since
+ * the last reseeding exceeds the given reseed time interval. For safety,
+ * a reseeding will also occur if the clock has been reset to a smaller
+ * value.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+int RAND_DRBG_set_reseed_time_interval(RAND_DRBG *drbg, time_t interval)
+{
+    if (interval > MAX_RESEED_TIME_INTERVAL)
+        return 0;
+    drbg->reseed_time_interval = interval;
+    return 1;
+}
+
+/*
  * Get and set the EXDATA
  */
 int RAND_DRBG_set_ex_data(RAND_DRBG *drbg, int idx, void *arg)
@@ -501,31 +626,50 @@ 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
+ * Allocates a new global DRBG on the secure heap (if enabled) and
+ * initializes it 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 RAND_DRBG *drbg_setup(const char *name, RAND_DRBG *parent)
 {
-    int ret = 1;
+    RAND_DRBG *drbg;
 
-    if (name != NULL) {
-        if (drbg->lock != NULL) {
-            RANDerr(RAND_F_DRBG_SETUP, ERR_R_INTERNAL_ERROR);
-            return 0;
-        }
+    if (name == NULL) {
+        RANDerr(RAND_F_DRBG_SETUP, ERR_R_INTERNAL_ERROR);
+        return NULL;
+    }
 
-        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 = OPENSSL_secure_zalloc(sizeof(RAND_DRBG));
+    if (drbg == NULL)
+        return NULL;
+
+    drbg->lock = CRYPTO_THREAD_glock_new(name);
+    if (drbg->lock == NULL) {
+        RANDerr(RAND_F_DRBG_SETUP, RAND_R_FAILED_TO_CREATE_LOCK);
+        goto err;
     }
 
-    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 (RAND_DRBG_set(drbg,
+                      RAND_DRBG_NID, RAND_DRBG_FLAG_CTR_USE_DF) != 1)
+        goto err;
+    if (RAND_DRBG_set_callbacks(drbg, rand_drbg_get_entropy,
+                                rand_drbg_cleanup_entropy, NULL, NULL) != 1)
+        goto err;
+
+    if (parent == NULL) {
+        drbg->reseed_interval = MASTER_RESEED_INTERVAL;
+        drbg->reseed_time_interval = MASTER_RESEED_TIME_INTERVAL;
+    } else {
+        drbg->parent = parent;
+        drbg->reseed_interval = SLAVE_RESEED_INTERVAL;
+        drbg->reseed_time_interval = SLAVE_RESEED_TIME_INTERVAL;
+    }
+
+    /* enable seed propagation */
+    drbg->reseed_counter = 1;
+
     /*
      * Ignore instantiation error so support just-in-time instantiation.
      *
@@ -535,7 +679,11 @@ static int drbg_setup(RAND_DRBG *drbg, const char *name)
     RAND_DRBG_instantiate(drbg,
                           (const unsigned char *) ossl_pers_string,
                           sizeof(ossl_pers_string) - 1);
-    return ret;
+    return drbg;
+
+err:
+    drbg_cleanup(drbg);
+    return NULL;
 }
 
 /*
@@ -544,26 +692,34 @@ static int drbg_setup(RAND_DRBG *drbg, const char *name)
  */
 DEFINE_RUN_ONCE_STATIC(do_rand_drbg_init)
 {
-    int ret = 1;
+    drbg_master = drbg_setup("drbg_master", NULL);
+    drbg_public = drbg_setup("drbg_public", drbg_master);
+    drbg_private = drbg_setup("drbg_private", drbg_master);
 
-    ret &= drbg_setup(&rand_drbg, "rand_drbg");
-    ret &= drbg_setup(&priv_drbg, "priv_drbg");
+    if (drbg_master == NULL || drbg_public == NULL || drbg_private == NULL)
+        return 0;
 
-    return ret;
+    return 1;
 }
 
 /* Cleans up the given global DRBG  */
 static void drbg_cleanup(RAND_DRBG *drbg)
 {
-    CRYPTO_THREAD_lock_free(drbg->lock);
-    RAND_DRBG_uninstantiate(drbg);
+    if (drbg != NULL) {
+        RAND_DRBG_uninstantiate(drbg);
+        CRYPTO_THREAD_lock_free(drbg->lock);
+        OPENSSL_secure_clear_free(drbg, sizeof(RAND_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);
+
+    drbg_private = drbg_public = drbg_master = NULL;
 }
 
 /* Implements the default OpenSSL RAND_bytes() method */
@@ -571,7 +727,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 +755,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 +792,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 +804,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 f45ef14..bb3acc8 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;
 }
 
@@ -313,7 +313,7 @@ int ctr_init(RAND_DRBG *drbg)
     switch (drbg->nid) {
     default:
         /* This can't happen, but silence the compiler warning. */
-        return -1;
+        return 0;
     case NID_aes_128_ctr:
         keylen = 16;
         break;
@@ -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 5e319d8..9044981 100644
--- a/crypto/rand/rand_lcl.h
+++ b/crypto/rand/rand_lcl.h
@@ -20,8 +20,17 @@
 /* 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 intervals */
+# define MAX_RESEED_INTERVAL                     (1 << 24)
+# define MAX_RESEED_TIME_INTERVAL                (1 << 20) /* approx. 12 days */
+
+/* Default reseed intervals */
+# define MASTER_RESEED_INTERVAL                  (1 << 8)
+# define SLAVE_RESEED_INTERVAL                   (1 << 16)
+# define MASTER_RESEED_TIME_INTERVAL             (60*60)   /* 1 hour */
+# define SLAVE_RESEED_TIME_INTERVAL              (7*60)    /* 7 minutes */
+
+
 
 /* Max size of additional input and personalization string. */
 # define DRBG_MAX_LENGTH                4096
@@ -106,8 +115,33 @@ 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;
+    /* Stores the time when the last reseeding occurred */
+    time_t reseed_time;
+    /*
+     * Specifies the maximum time interval (in seconds) between reseeds.
+     * This value is ignored if it is zero.
+     */
+    time_t reseed_time_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 a290e5c..07f5427 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 feda9be..2f9a801 100644
--- a/include/internal/rand.h
+++ b/include/internal/rand.h
@@ -42,9 +42,12 @@ 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);
+int RAND_DRBG_set_reseed_time_interval(RAND_DRBG *drbg, time_t 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 37e83bd..fb113ba 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 25920d6..68c1697 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,288 @@ 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];
+    time_t before_reseed, after_reseed;
+    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 */
+    before_reseed = expect_master_reseed == 1 ? time(NULL) : 0;
+    if (!TEST_int_eq(RAND_bytes(buf, sizeof(buf)), expect_success)
+        || !TEST_int_eq(RAND_priv_bytes(buf, sizeof(buf)), expect_success))
+        return 0;
+    after_reseed = time(NULL);
+
+
+    /*
+     * 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;
+
+        /* Test whether reseed time of master DRBG is set correctly */
+        if (!TEST_time_t_le(before_reseed, master->reseed_time)
+            || !TEST_time_t_le(master->reseed_time, after_reseed))
+            return 0;
+
+        /* Test whether reseed times of child DRBGs are synchronized with master */
+        if (!TEST_time_t_ge(public->reseed_time, master->reseed_time)
+            || !TEST_time_t_ge(private->reseed_time, master->reseed_time))
+            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 +764,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 10ffa2c..0b52a44 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,5 @@ 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:
+RAND_DRBG_set_reseed_time_interval      4395	1_1_1	EXIST::FUNCTION:


More information about the openssl-commits mailing list