Add prediction resistance capability to the DRBG reseeding process.
authorPauli <paul.dale@oracle.com>
Fri, 12 Apr 2019 08:16:20 +0000 (18:16 +1000)
committerPauli <paul.dale@oracle.com>
Fri, 12 Apr 2019 08:16:20 +0000 (18:16 +1000)
Refer to NIST SP 800-90C section 5.4 "Prediction Resistance.l"

This requires the seed sources to be approved as entropy sources, after
which they should be considered live sources as per section 5.3.2 "Live
Entropy Source Availability."

Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
(Merged from https://github.com/openssl/openssl/pull/8647)

CHANGES
crypto/rand/rand_lib.c
doc/man3/RAND_DRBG_generate.pod
doc/man3/RAND_DRBG_reseed.pod
doc/man3/RAND_DRBG_set_callbacks.pod
doc/man7/RAND_DRBG.pod
test/drbgtest.c

diff --git a/CHANGES b/CHANGES
index e70e42b..11c80b7 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -9,8 +9,12 @@
 
  Changes between 1.1.1 and 3.0.0 [xx XXX xxxx]
 
+  *) Add prediction resistance to the DRBG reseeding process.
+     [Paul Dale]
+
   *) Limit the number of blocks in a data unit for AES-XTS to 2^20 as
      mandated by IEEE Std 1619-2018.
+     [Paul Dale]
 
   *) Added newline escaping functionality to a filename when using openssl dgst.
      This output format is to replicate the output format found in the '*sum'
index a298b75..2b77960 100644 (file)
@@ -183,17 +183,6 @@ size_t rand_drbg_get_entropy(RAND_DRBG *drbg,
         }
 
     } else {
-        if (prediction_resistance) {
-            /*
-             * We don't have any entropy sources that comply with the NIST
-             * standard to provide prediction resistance (see NIST SP 800-90C,
-             * Section 5.4).
-             */
-            RANDerr(RAND_F_RAND_DRBG_GET_ENTROPY,
-                    RAND_R_PREDICTION_RESISTANCE_NOT_SUPPORTED);
-            goto err;
-        }
-
         /* Get entropy by polling system entropy sources. */
         entropy_available = rand_pool_acquire_entropy(pool);
     }
@@ -203,7 +192,6 @@ size_t rand_drbg_get_entropy(RAND_DRBG *drbg,
         *pout = rand_pool_detach(pool);
     }
 
- err:
     if (drbg->seed_pool == NULL)
         rand_pool_free(pool);
     return ret;
index 0066c86..09903f8 100644 (file)
@@ -29,7 +29,9 @@ number of generate requests (I<reseed interval>) or the maximum timespan
 (I<reseed time interval>) since its last seeding have been reached.
 If this is the case, the DRBG reseeds automatically.
 Additionally, an immediate reseeding can be requested by setting the
-B<prediction_resistance> flag to 1. See NOTES section for more details.
+B<prediction_resistance> flag to 1.
+Requesting prediction resistance is a relative expensive operation.
+See NOTES section for more details.
 
 The caller can optionally provide additional data to be used for reseeding
 by passing a pointer B<adin> to a buffer of length B<adinlen>.
@@ -59,16 +61,16 @@ If necessary, they can be changed using L<RAND_DRBG_set_reseed_interval(3)>
 and L<RAND_DRBG_set_reseed_time_interval(3)>, respectively.
 
 A request for prediction resistance can only be satisfied by pulling fresh
-entropy from one of the approved entropy sources listed in section 5.5.2 of
-[NIST SP 800-90C].
-Since the default DRBG implementation does not have access to such an approved
-entropy source, a request for prediction resistance will always fail.
-In other words, prediction resistance is currently not supported yet by the DRBG.
+entropy from a live entropy source (section 5.5.2 of [NIST SP 800-90C]).
+It is up to the user to ensure that a live entropy source is configured
+and is being used.
 
 =head1 HISTORY
 
 The RAND_DRBG functions were added in OpenSSL 1.1.1.
 
+Prediction resistance is supported from OpenSSL 3.0.0.
+
 =head1 SEE ALSO
 
 L<RAND_bytes(3)>,
index ca08a39..c4d2671 100644 (file)
@@ -13,7 +13,8 @@ RAND_DRBG_set_reseed_defaults
  #include <openssl/rand_drbg.h>
 
  int RAND_DRBG_reseed(RAND_DRBG *drbg,
-                      const unsigned char *adin, size_t adinlen);
+                      const unsigned char *adin, size_t adinlen,
+                      int prediction_resistance);
 
  int RAND_DRBG_set_reseed_interval(RAND_DRBG *drbg,
                                    unsigned int interval);
@@ -37,6 +38,10 @@ and mixing in the specified additional data provided in the buffer B<adin>
 of length B<adinlen>.
 The additional data can be omitted by setting B<adin> to NULL and B<adinlen>
 to 0.
+An immediate reseeding can be requested by setting the
+B<prediction_resistance> flag to 1.
+Requesting prediction resistance is a relative expensive operation.
+See NOTES section for more details.
 
 RAND_DRBG_set_reseed_interval()
 sets the reseed interval of the B<drbg>, which is the maximum allowed number
@@ -88,10 +93,17 @@ To ensure that they are applied to the global and thread-local DRBG instances
 RAND_DRBG_set_reseed_defaults() before creating any thread and before calling any
  cryptographic routines that obtain random data directly or indirectly.
 
+A request for prediction resistance can only be satisfied by pulling fresh
+entropy from a live entropy source (section 5.5.2 of [NIST SP 800-90C]).
+It is up to the user to ensure that a live entropy source is configured
+and is being used.
+
 =head1 HISTORY
 
 The RAND_DRBG functions were added in OpenSSL 1.1.1.
 
+Prediction resistance is supported from OpenSSL 3.0.0.
+
 =head1 SEE ALSO
 
 L<RAND_DRBG_generate(3)>,
index 9a1d157..4734b07 100644 (file)
@@ -104,12 +104,9 @@ contents safely before freeing it, in order not to leave sensitive information
 about the DRBG's state in memory.
 
 A request for prediction resistance can only be satisfied by pulling fresh
-entropy from one of the approved entropy sources listed in section 5.5.2 of
-[NIST SP 800-90C].
-Since the default implementation of the get_entropy callback does not have access
-to such an approved entropy source, a request for prediction resistance will
-always fail.
-In other words, prediction resistance is currently not supported yet by the DRBG.
+entropy from a live entropy source (section 5.5.2 of [NIST SP 800-90C]).
+It is up to the user to ensure that a live entropy source is configured
+and is being used.
 
 The derivation function is disabled during initialization by calling the
 RAND_DRBG_set() function with the RAND_DRBG_FLAG_CTR_NO_DF flag.
index f3fa605..48d4ace 100644 (file)
@@ -192,9 +192,10 @@ I<prediction resistance> parameter to 1 when calling L<RAND_DRBG_generate(3)>.
 The document [NIST SP 800-90C] describes prediction resistance requests
 in detail and imposes strict conditions on the entropy sources that are
 approved for providing prediction resistance.
-Since the default DRBG implementation does not have access to such an approved
-entropy source, a request for prediction resistance will currently always fail.
-In other words, prediction resistance is currently not supported yet by the DRBG.
+A request for prediction resistance can only be satisfied by pulling fresh
+entropy from a live entropy source (section 5.5.2 of [NIST SP 800-90C]).
+It is up to the user to ensure that a live entropy source is configured
+and is being used.
 
 
 For the three shared DRBGs (and only for these) there is another way to
index ca45a8f..bf4c723 100644 (file)
@@ -1012,6 +1012,83 @@ static int test_rand_add(void)
     return 1;
 }
 
+static int test_rand_drbg_prediction_resistance(void)
+{
+    RAND_DRBG *m = NULL, *i = NULL, *s = NULL;
+    unsigned char buf1[51], buf2[sizeof(buf1)];
+    int ret = 0, mreseed, ireseed, sreseed;
+
+    /* Initialise a three long DRBG chain */
+    if (!TEST_ptr(m = RAND_DRBG_new(0, 0, NULL))
+        || !TEST_true(disable_crngt(m))
+        || !TEST_true(RAND_DRBG_instantiate(m, NULL, 0))
+        || !TEST_ptr(i = RAND_DRBG_new(0, 0, m))
+        || !TEST_true(RAND_DRBG_instantiate(i, NULL, 0))
+        || !TEST_ptr(s = RAND_DRBG_new(0, 0, i))
+        || !TEST_true(RAND_DRBG_instantiate(s, NULL, 0)))
+        goto err;
+
+    /* During a normal reseed, only the slave DRBG should be reseed */
+    mreseed = ++m->reseed_prop_counter;
+    ireseed = ++i->reseed_prop_counter;
+    sreseed = s->reseed_prop_counter;
+    if (!TEST_true(RAND_DRBG_reseed(s, NULL, 0, 0))
+        || !TEST_int_eq(m->reseed_prop_counter, mreseed)
+        || !TEST_int_eq(i->reseed_prop_counter, ireseed)
+        || !TEST_int_gt(s->reseed_prop_counter, sreseed))
+        goto err;
+
+    /*
+     * When prediction resistance is requested, the request should be
+     * propagated to the master, so that the entire DRBG chain reseeds.
+     */
+    sreseed = s->reseed_prop_counter;
+    if (!TEST_true(RAND_DRBG_reseed(s, NULL, 0, 1))
+        || !TEST_int_gt(m->reseed_prop_counter, mreseed)
+        || !TEST_int_gt(i->reseed_prop_counter, ireseed)
+        || !TEST_int_gt(s->reseed_prop_counter, sreseed))
+        goto err;
+
+    /* During a normal generate, only the slave DRBG should be reseed */
+    mreseed = ++m->reseed_prop_counter;
+    ireseed = ++i->reseed_prop_counter;
+    sreseed = s->reseed_prop_counter;
+    if (!TEST_true(RAND_DRBG_generate(s, buf1, sizeof(buf1), 0, NULL, 0))
+        || !TEST_int_eq(m->reseed_prop_counter, mreseed)
+        || !TEST_int_eq(i->reseed_prop_counter, ireseed)
+        || !TEST_int_gt(s->reseed_prop_counter, sreseed))
+        goto err;
+
+    /*
+     * When a prediction resistant generate is requested, the request
+     * should be propagated to the master, reseeding the entire DRBG chain.
+     */
+    sreseed = s->reseed_prop_counter;
+    if (!TEST_true(RAND_DRBG_generate(s, buf2, sizeof(buf2), 1, NULL, 0))
+        || !TEST_int_gt(m->reseed_prop_counter, mreseed)
+        || !TEST_int_gt(i->reseed_prop_counter, ireseed)
+        || !TEST_int_gt(s->reseed_prop_counter, sreseed)
+        || !TEST_mem_ne(buf1, sizeof(buf1), buf2, sizeof(buf2)))
+        goto err;
+
+    /* Verify that a normal reseed still only reseeds the slave DRBG */
+    mreseed = ++m->reseed_prop_counter;
+    ireseed = ++i->reseed_prop_counter;
+    sreseed = s->reseed_prop_counter;
+    if (!TEST_true(RAND_DRBG_reseed(s, NULL, 0, 0))
+        || !TEST_int_eq(m->reseed_prop_counter, mreseed)
+        || !TEST_int_eq(i->reseed_prop_counter, ireseed)
+        || !TEST_int_gt(s->reseed_prop_counter, sreseed))
+        goto err;
+
+    ret = 1;
+err:
+    RAND_DRBG_free(s);
+    RAND_DRBG_free(i);
+    RAND_DRBG_free(m);
+    return ret;
+}
+
 static int test_multi_set(void)
 {
     int rv = 0;
@@ -1252,6 +1329,7 @@ int setup_tests(void)
     ADD_TEST(test_rand_drbg_reseed);
     ADD_TEST(test_rand_seed);
     ADD_TEST(test_rand_add);
+    ADD_TEST(test_rand_drbg_prediction_resistance);
     ADD_TEST(test_multi_set);
     ADD_TEST(test_set_defaults);
 #if defined(OPENSSL_THREADS)