DRBG: fix reseeding via RAND_add()/RAND_seed() with large input
authorDr. Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
Tue, 9 Oct 2018 23:53:29 +0000 (01:53 +0200)
committerDr. Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
Tue, 16 Oct 2018 20:15:43 +0000 (22:15 +0200)
In pull request #4328 the seeding of the DRBG via RAND_add()/RAND_seed()
was implemented by buffering the data in a random pool where it is
picked up later by the rand_drbg_get_entropy() callback. This buffer
was limited to the size of 4096 bytes.

When a larger input was added via RAND_add() or RAND_seed() to the DRBG,
the reseeding failed, but the error returned by the DRBG was ignored
by the two calling functions, which both don't return an error code.
As a consequence, the data provided by the application was effectively
ignored.

This commit fixes the problem by a more efficient implementation which
does not copy the data in memory and by raising the buffer the size limit
to INT32_MAX (2 gigabytes). This is less than the NIST limit of 2^35 bits
but it was chosen intentionally to avoid platform dependent problems
like integer sizes and/or signed/unsigned conversion.

Additionally, the DRBG is now less permissive on errors: In addition to
pushing a message to the openssl error stack, it enters the error state,
which forces a reinstantiation on next call.

Thanks go to Dr. Falko Strenzke for reporting this issue to the
openssl-security mailing list. After internal discussion the issue
has been categorized as not being security relevant, because the DRBG
reseeds automatically and is fully functional even without additional
randomness provided by the application.

Fixes #7381

Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/7382)

13 files changed:
CHANGES
crypto/err/openssl.txt
crypto/include/internal/rand_int.h
crypto/rand/drbg_ctr.c
crypto/rand/drbg_hash.c
crypto/rand/drbg_hmac.c
crypto/rand/drbg_lib.c
crypto/rand/rand_err.c
crypto/rand/rand_lcl.h
crypto/rand/rand_lib.c
crypto/rand/rand_unix.c
include/openssl/randerr.h
test/drbgtest.c

diff --git a/CHANGES b/CHANGES
index a1fa57c8019db70cae188f332bac7ef3d900d6a7..aa00369eda7ba0bbfd7d57e8c98f4fa16fe6e639 100644 (file)
--- a/CHANGES
+++ b/CHANGES
      list of built in objects, i.e. OIDs with names.
      [Richard Levitte]
 
+ Changes between 1.1.1 and 1.1.1a [xx XXX xxxx]
+
+  *) Fixed the issue that RAND_add()/RAND_seed() silently discards random input
+     if its length exceeds 4096 bytes. The limit has been raised to a buffer size
+     of two gigabytes and the error handling improved.
+
+     This issue was reported to OpenSSL by Dr. Falko Strenzke. It has been
+     categorized as a normal bug, not a security issue, because the DRBG reseeds
+     automatically and is fully functional even without additional randomness
+     provided by the application.
+
  Changes between 1.1.0i and 1.1.1 [11 Sep 2018]
 
   *) Add a new ClientHello callback. Provides a callback interface that gives
@@ -13121,4 +13132,3 @@ des-cbc           3624.96k     5258.21k     5530.91k     5624.30k     5628.26k
   *) A minor bug in ssl/s3_clnt.c where there would always be 4 0
      bytes sent in the client random.
      [Edward Bishop <ebishop@spyglass.com>]
-
index 84e639305b9baf7c30a60afe44b5dba6ba6b548e..489ccc098623d7c9e5c687f32d1b26c819796684 100644 (file)
@@ -1016,6 +1016,7 @@ RAND_F_RAND_POOL_ACQUIRE_ENTROPY:122:rand_pool_acquire_entropy
 RAND_F_RAND_POOL_ADD:103:rand_pool_add
 RAND_F_RAND_POOL_ADD_BEGIN:113:rand_pool_add_begin
 RAND_F_RAND_POOL_ADD_END:114:rand_pool_add_end
+RAND_F_RAND_POOL_ATTACH:124:rand_pool_attach
 RAND_F_RAND_POOL_BYTES_NEEDED:115:rand_pool_bytes_needed
 RAND_F_RAND_POOL_NEW:116:rand_pool_new
 RAND_F_RAND_WRITE_FILE:112:RAND_write_file
index d91ee4c9342cbfac5e5dd9e712e9f1f3a9b3194a..3c966ab96ef62a8cdc1e33c94cb440306f959b9e 100644 (file)
@@ -53,6 +53,8 @@ void rand_drbg_cleanup_additional_data(unsigned char *out, size_t outlen);
  * RAND_POOL functions
  */
 RAND_POOL *rand_pool_new(int entropy_requested, size_t min_len, size_t max_len);
+RAND_POOL *rand_pool_attach(const unsigned char *buffer, size_t len,
+                            size_t entropy);
 void rand_pool_free(RAND_POOL *pool);
 
 const unsigned char *rand_pool_buffer(RAND_POOL *pool);
index 0548237e7d70ff2ab662dcae8a4703cfbaa0c66a..894c77d6eb86f335593ea9e80af76ef0b39d1e1a 100644 (file)
@@ -416,9 +416,9 @@ int drbg_ctr_init(RAND_DRBG *drbg)
             return 0;
 
         drbg->min_entropylen = ctr->keylen;
-        drbg->max_entropylen = DRBG_MINMAX_FACTOR * drbg->min_entropylen;
+        drbg->max_entropylen = DRBG_MAX_LENGTH;
         drbg->min_noncelen = drbg->min_entropylen / 2;
-        drbg->max_noncelen = DRBG_MINMAX_FACTOR * drbg->min_noncelen;
+        drbg->max_noncelen = DRBG_MAX_LENGTH;
         drbg->max_perslen = DRBG_MAX_LENGTH;
         drbg->max_adinlen = DRBG_MAX_LENGTH;
     } else {
index 9caf5b27bec64e5ae43202bf3026f6f3ea93dcdc..cae567beb438bf1bb1127a67918bf6921e8b9f22 100644 (file)
@@ -332,10 +332,10 @@ int drbg_hash_init(RAND_DRBG *drbg)
         drbg->seedlen = HASH_PRNG_SMALL_SEEDLEN;
 
     drbg->min_entropylen = drbg->strength / 8;
-    drbg->max_entropylen = DRBG_MINMAX_FACTOR * drbg->min_entropylen;
+    drbg->max_entropylen = DRBG_MAX_LENGTH;
 
     drbg->min_noncelen = drbg->min_entropylen / 2;
-    drbg->max_noncelen = DRBG_MINMAX_FACTOR * drbg->min_noncelen;
+    drbg->max_noncelen = DRBG_MAX_LENGTH;
 
     drbg->max_perslen = DRBG_MAX_LENGTH;
     drbg->max_adinlen = DRBG_MAX_LENGTH;
index 25c5b0301cbb3a4269408665b15ae37d87fff454..424c88cb26831c74540ebc27bf0e7f8c87465af2 100644 (file)
@@ -223,10 +223,10 @@ int drbg_hmac_init(RAND_DRBG *drbg)
     drbg->seedlen = hmac->blocklen;
 
     drbg->min_entropylen = drbg->strength / 8;
-    drbg->max_entropylen = DRBG_MINMAX_FACTOR * drbg->min_entropylen;
+    drbg->max_entropylen = DRBG_MAX_LENGTH;
 
     drbg->min_noncelen = drbg->min_entropylen / 2;
-    drbg->max_noncelen = DRBG_MINMAX_FACTOR * drbg->min_noncelen;
+    drbg->max_noncelen = DRBG_MAX_LENGTH;
 
     drbg->max_perslen = DRBG_MAX_LENGTH;
     drbg->max_adinlen = DRBG_MAX_LENGTH;
index c59f3f0026e237910d06d5e6ab8a20b5c15f92c6..36b20c9398ac2cd7811cf6614ea6ebd23e2072bd 100644 (file)
@@ -556,8 +556,10 @@ int rand_drbg_restart(RAND_DRBG *drbg,
 
     if (drbg->pool != NULL) {
         RANDerr(RAND_F_RAND_DRBG_RESTART, ERR_R_INTERNAL_ERROR);
+        drbg->state = DRBG_ERROR;
         rand_pool_free(drbg->pool);
         drbg->pool = NULL;
+        return 0;
     }
 
     if (buffer != NULL) {
@@ -565,24 +567,25 @@ int rand_drbg_restart(RAND_DRBG *drbg,
             if (drbg->max_entropylen < len) {
                 RANDerr(RAND_F_RAND_DRBG_RESTART,
                     RAND_R_ENTROPY_INPUT_TOO_LONG);
+                drbg->state = DRBG_ERROR;
                 return 0;
             }
 
             if (entropy > 8 * len) {
                 RANDerr(RAND_F_RAND_DRBG_RESTART, RAND_R_ENTROPY_OUT_OF_RANGE);
+                drbg->state = DRBG_ERROR;
                 return 0;
             }
 
             /* will be picked up by the rand_drbg_get_entropy() callback */
-            drbg->pool = rand_pool_new(entropy, len, len);
+            drbg->pool = rand_pool_attach(buffer, len, entropy);
             if (drbg->pool == NULL)
                 return 0;
-
-            rand_pool_add(drbg->pool, buffer, len, entropy);
         } else {
             if (drbg->max_adinlen < len) {
                 RANDerr(RAND_F_RAND_DRBG_RESTART,
                         RAND_R_ADDITIONAL_INPUT_TOO_LONG);
+                drbg->state = DRBG_ERROR;
                 return 0;
             }
             adin = buffer;
@@ -1040,14 +1043,16 @@ static int drbg_add(const void *buf, int num, double randomness)
     if (num < 0 || randomness < 0.0)
         return 0;
 
-    if (randomness > (double)drbg->max_entropylen) {
+    if (randomness > (double)RAND_DRBG_STRENGTH) {
         /*
          * The purpose of this check is to bound |randomness| by a
          * relatively small value in order to prevent an integer
          * overflow when multiplying by 8 in the rand_drbg_restart()
-         * call below.
+         * call below. Note that randomness is measured in bytes,
+         * not bits, so this value corresponds to eight times the
+         * security strength.
          */
-        return 0;
+        randomness = (double)RAND_DRBG_STRENGTH;
     }
 
     rand_drbg_lock(drbg);
index 31480a6828381183a992b9495d28476c0caeda2a..6a870455d50a8ac0b1a7cf33668e923ef4a624cc 100644 (file)
@@ -44,6 +44,7 @@ static const ERR_STRING_DATA RAND_str_functs[] = {
     {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_POOL_ADD_BEGIN, 0),
      "rand_pool_add_begin"},
     {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_POOL_ADD_END, 0), "rand_pool_add_end"},
+    {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_POOL_ATTACH, 0), "rand_pool_attach"},
     {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_POOL_BYTES_NEEDED, 0),
      "rand_pool_bytes_needed"},
     {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_POOL_NEW, 0), "rand_pool_new"},
index 7b1c74d4b38453eabc873ba89298cc3d66dc2147..13f8cc823951f23cb9d860efa3e1cdfe93dfbb87 100644 (file)
 
 
 
-/* Max size of additional input and personalization string. */
-# define DRBG_MAX_LENGTH                4096
+/*
+ * Maximum input size for the DRBG (entropy, nonce, personalization string)
+ *
+ * NIST SP800 90Ar1 allows a maximum of (1 << 35) bits i.e., (1 << 32) bytes.
+ *
+ * We lower it to 'only' INT32_MAX bytes, which is equivalent to 2 gigabytes.
+ */
+# define DRBG_MAX_LENGTH                         INT32_MAX
+
+
 
 /*
- * The quotient between max_{entropy,nonce}len and min_{entropy,nonce}len
+ * Maximum allocation size for RANDOM_POOL buffers
  *
- * The current factor is large enough that the RAND_POOL can store a
- * random input which has a lousy entropy rate of 0.0625 bits per byte.
- * This input will be sent through the derivation function which 'compresses'
- * the low quality input into a high quality output.
+ * The max_len value for the buffer provided to the rand_drbg_get_entropy()
+ * callback is currently 2^31 bytes (2 gigabytes), if a derivation function
+ * is used. Since this is much too large to be allocated, the rand_pool_new()
+ * function chooses more modest values as default pool length, bounded
+ * by RAND_POOL_MIN_LENGTH and RAND_POOL_MAX_LENGTH
+ *
+ * The choice of the RAND_POOL_FACTOR is large enough such that the
+ * RAND_POOL can store a random input which has a lousy entropy rate of
+ * 8/256 (= 0.03125) bits per byte. This input will be sent through the
+ * derivation function which 'compresses' the low quality input into a
+ * high quality output.
+ *
+ * The factor 1.5 below is the pessimistic estimate for the extra amount
+ * of entropy required when no get_nonce() callback is defined.
+ */
+# define RAND_POOL_FACTOR        256
+# define RAND_POOL_MAX_LENGTH    (RAND_POOL_FACTOR * \
+                                  3 * (RAND_DRBG_STRENGTH / 16))
+/*
+ *                             = (RAND_POOL_FACTOR * \
+ *                                1.5 * (RAND_DRBG_STRENGTH / 8))
  */
-# define DRBG_MINMAX_FACTOR              128
 
 
 /* DRBG status values */
@@ -142,10 +166,12 @@ struct rand_pool_st {
     unsigned char *buffer;  /* points to the beginning of the random pool */
     size_t len; /* current number of random bytes contained in the pool */
 
+    int attached;  /* true pool was attached to existing buffer */
+
     size_t min_len; /* minimum number of random bytes requested */
     size_t max_len; /* maximum number of random bytes (allocated buffer size) */
     size_t entropy; /* current entropy count in bits */
-    size_t requested_entropy; /* requested entropy count in bits */
+    size_t entropy_requested; /* requested entropy count in bits */
 };
 
 /*
@@ -167,7 +193,7 @@ struct rand_drbg_st {
     unsigned short flags; /* various external flags */
 
     /*
-     * The random pool is used by RAND_add()/drbg_add() to attach random
+     * The random_data is used by RAND_add()/drbg_add() to attach random
      * data to the global drbg, such that the rand_drbg_get_entropy() callback
      * can pull it during instantiation and reseeding. This is necessary to
      * reconcile the different philosophies of the RAND and the RAND_DRBG
index e9bc9522101cad322cd882a523ec28a6ec593a82..f40c513ce87683a13341cd2c5d3311982a9664fc 100644 (file)
@@ -146,17 +146,11 @@ size_t rand_drbg_get_entropy(RAND_DRBG *drbg,
         return 0;
     }
 
-    pool = rand_pool_new(entropy, min_len, max_len);
-    if (pool == NULL)
-        return 0;
-
-    if (drbg->pool) {
-        rand_pool_add(pool,
-                      rand_pool_buffer(drbg->pool),
-                      rand_pool_length(drbg->pool),
-                      rand_pool_entropy(drbg->pool));
-        rand_pool_free(drbg->pool);
-        drbg->pool = NULL;
+    if (drbg->pool != NULL) {
+        pool = drbg->pool;
+        pool->entropy_requested = entropy;
+    } else {
+        pool = rand_pool_new(entropy, min_len, max_len);
     }
 
     if (drbg->parent) {
@@ -217,7 +211,10 @@ size_t rand_drbg_get_entropy(RAND_DRBG *drbg,
 void rand_drbg_cleanup_entropy(RAND_DRBG *drbg,
                                unsigned char *out, size_t outlen)
 {
-    OPENSSL_secure_clear_free(out, outlen);
+    if (drbg->pool == NULL)
+        OPENSSL_secure_clear_free(out, outlen);
+    else
+        drbg->pool = NULL;
 }
 
 
@@ -405,7 +402,7 @@ int RAND_poll(void)
         /* fill random pool and seed the current legacy RNG */
         pool = rand_pool_new(RAND_DRBG_STRENGTH,
                              RAND_DRBG_STRENGTH / 8,
-                             DRBG_MINMAX_FACTOR * (RAND_DRBG_STRENGTH / 8));
+                             RAND_POOL_MAX_LENGTH);
         if (pool == NULL)
             return 0;
 
@@ -430,17 +427,18 @@ err:
  * Allocate memory and initialize a new random pool
  */
 
-RAND_POOL *rand_pool_new(int entropy, size_t min_len, size_t max_len)
+RAND_POOL *rand_pool_new(int entropy_requested, size_t min_len, size_t max_len)
 {
     RAND_POOL *pool = OPENSSL_zalloc(sizeof(*pool));
 
     if (pool == NULL) {
         RANDerr(RAND_F_RAND_POOL_NEW, ERR_R_MALLOC_FAILURE);
-        goto err;
+        return NULL;
     }
 
     pool->min_len = min_len;
-    pool->max_len = max_len;
+    pool->max_len = (max_len > RAND_POOL_MAX_LENGTH) ?
+        RAND_POOL_MAX_LENGTH : max_len;
 
     pool->buffer = OPENSSL_secure_zalloc(pool->max_len);
     if (pool->buffer == NULL) {
@@ -448,7 +446,7 @@ RAND_POOL *rand_pool_new(int entropy, size_t min_len, size_t max_len)
         goto err;
     }
 
-    pool->requested_entropy = entropy;
+    pool->entropy_requested = entropy_requested;
 
     return pool;
 
@@ -457,6 +455,38 @@ err:
     return NULL;
 }
 
+/*
+ * Attach new random pool to the given buffer
+ *
+ * This function is intended to be used only for feeding random data
+ * provided by RAND_add() and RAND_seed() into the <master> DRBG.
+ */
+RAND_POOL *rand_pool_attach(const unsigned char *buffer, size_t len,
+                            size_t entropy)
+{
+    RAND_POOL *pool = OPENSSL_zalloc(sizeof(*pool));
+
+    if (pool == NULL) {
+        RANDerr(RAND_F_RAND_POOL_ATTACH, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    /*
+     * The const needs to be cast away, but attached buffers will not be
+     * modified (in contrary to allocated buffers which are zeroed and
+     * freed in the end).
+     */
+    pool->buffer = (unsigned char *) buffer;
+    pool->len = len;
+
+    pool->attached = 1;
+
+    pool->min_len = pool->max_len = pool->len;
+    pool->entropy = entropy;
+
+    return pool;
+}
+
 /*
  * Free |pool|, securely erasing its buffer.
  */
@@ -465,7 +495,14 @@ void rand_pool_free(RAND_POOL *pool)
     if (pool == NULL)
         return;
 
-    OPENSSL_secure_clear_free(pool->buffer, pool->max_len);
+    /*
+     * Although it would be advisable from a cryptographical viewpoint,
+     * we are not allowed to clear attached buffers, since they are passed
+     * to rand_pool_attach() as `const unsigned char*`.
+     * (see corresponding comment in rand_pool_attach()).
+     */
+    if (!pool->attached)
+        OPENSSL_secure_clear_free(pool->buffer, pool->max_len);
     OPENSSL_free(pool);
 }
 
@@ -524,7 +561,7 @@ unsigned char *rand_pool_detach(RAND_POOL *pool)
  */
 size_t rand_pool_entropy_available(RAND_POOL *pool)
 {
-    if (pool->entropy < pool->requested_entropy)
+    if (pool->entropy < pool->entropy_requested)
         return 0;
 
     if (pool->len < pool->min_len)
@@ -540,8 +577,8 @@ size_t rand_pool_entropy_available(RAND_POOL *pool)
 
 size_t rand_pool_entropy_needed(RAND_POOL *pool)
 {
-    if (pool->entropy < pool->requested_entropy)
-        return pool->requested_entropy - pool->entropy;
+    if (pool->entropy < pool->entropy_requested)
+        return pool->entropy_requested - pool->entropy;
 
     return 0;
 }
index e53b876dd346e8c36286c5eee58962c40ecb7411..cb3a6b24ab04c3280f2cd3c9c75eda4c828b0818 100644 (file)
@@ -264,7 +264,7 @@ static ssize_t syscall_random(void *buf, size_t buflen)
      * Note: 'buflen' equals the size of the buffer which is used by the
      * get_entropy() callback of the RAND_DRBG. It is roughly bounded by
      *
-     *   2 * DRBG_MINMAX_FACTOR * (RAND_DRBG_STRENGTH / 8) = 2^13
+     *   2 * RAND_POOL_FACTOR * (RAND_DRBG_STRENGTH / 8) = 2^14
      *
      * which is way below the OSSL_SSIZE_MAX limit. Therefore sign conversion
      * between size_t and ssize_t is safe even without a range check.
index 128f4dea751c6444b8c179d593ffad744a9be884..599a2a18d41ff0a121258dac26468e50ab5576fe 100644 (file)
@@ -40,6 +40,7 @@ int ERR_load_RAND_strings(void);
 # define RAND_F_RAND_POOL_ADD                             103
 # define RAND_F_RAND_POOL_ADD_BEGIN                       113
 # define RAND_F_RAND_POOL_ADD_END                         114
+# define RAND_F_RAND_POOL_ATTACH                          124
 # define RAND_F_RAND_POOL_BYTES_NEEDED                    115
 # define RAND_F_RAND_POOL_NEW                             116
 # define RAND_F_RAND_WRITE_FILE                           112
index f2054a8ac5e65ab086d23ed373ddc702a5d3fd35..bb51e0121036a67249553329033aee89307cc2c9 100644 (file)
@@ -911,7 +911,6 @@ static size_t get_pool_entropy(RAND_DRBG *drbg,
  */
 static void cleanup_pool_entropy(RAND_DRBG *drbg, unsigned char *out, size_t outlen)
 {
-    OPENSSL_secure_clear_free(drbg->pool->buffer, drbg->pool->max_len);
     OPENSSL_free(drbg->pool);
     drbg->pool = NULL;
 }