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 e70e42b5706f82ae02b22fdc458b6ae9e3c49935..11c80b762f1efb5190959647c8f116dfc97cb299 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 a298b7515be2225eb0a73a0ec64db96a80afe8e6..2b77960529eeb925a775f94faeb94f204790429a 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 0066c86e01bd72b03f5174ea3070517f5a41cec0..09903f8ca9dc39551dbfe5bb249d081d82c54e1f 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 ca08a39bb5dd5894454005361d9a9af76fba5c23..c4d2671ba5a558c9af595ac59b378f50054ff240 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 9a1d15723996534cefabf386d245e5098bb532fa..4734b0729577d5e22e62746d8024605ded1dfd14 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 f3fa605ee6f32eed3e485a4e1606437ec6e5a992..48d4ace09c86caa1a495a30d668e46631cfd681c 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 ca45a8fd5bee5167bc34e33c17d7ef2648dc4438..bf4c723c779ce11526113e89d07a6df9236231ea 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)