drbg_get_entropy: force a reseed before calling ssleay_rand_bytes()
authorDr. Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
Mon, 17 Sep 2018 15:50:54 +0000 (17:50 +0200)
committerDr. Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
Thu, 20 Sep 2018 16:27:27 +0000 (18:27 +0200)
Fixes #7240

In FIPS mode, the default FIPS DRBG uses the drbg_get_entropy()
callback to reseed itself, which is provided by the wrapping
libcrypto library. This callback in turn uses ssleay_rand_bytes()
to generate random bytes.

Now ssleay_rand_bytes() calls RAND_poll() once on first call to
seed itself, but RAND_poll() is never called again (unless the
application calls RAND_poll() explicitely). This implies that
whenever the DRBG reseeds itself (which happens every 2^14
generate requests) this happens without obtaining fresh random
data from the operating system's entropy sources.

This patch forces a reseed from system entropy sources on every
call to drbg_get_entropy(). In contrary to the automatic reseeding
of the DRBG in master, this reseeding does not break applications
running in a chroot() environment (see c7504aeb640a), because the
SSLEAY PRNG does not maintain an error state. (It does not even
check the return value of RAND_poll() on its instantiation.)

In the worst case, if no random device is available for reseeding,
no fresh entropy will be added to the SSLEAY PRNG but it will happily
continue to generate random bytes as 'entropy' input for the DRBG's
reseeding, which is just as good (or bad) as before this patch.

To prevent ssleay_rand_bytes_from_system() (and hence RAND_poll())
from being called twice during instantiation, a separate
drbg_get_nonce() callback has been introduced, which is identical
with the previous implementation of drbg_get_entropy().

Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Ben Kaduk <kaduk@mit.edu>
(Merged from https://github.com/openssl/openssl/pull/7259)

crypto/rand/md_rand.c
crypto/rand/rand_lcl.h
crypto/rand/rand_lib.c

index a7af9f9d8671bad10c4b73c479b55095ffe3cf53..abca70f23ae110e1319f89a9ed8e727cd5fbd940 100644 (file)
@@ -555,6 +555,18 @@ int ssleay_rand_bytes(unsigned char *buf, int num, int pseudo, int lock)
     return (0);
 }
 
+/*
+ * Returns ssleay_rand_bytes(), enforcing a reseeding from the
+ * system entropy sources using RAND_poll() before generating
+`* the random bytes.
+ */
+
+int ssleay_rand_bytes_from_system(unsigned char *buf, int num)
+{
+    initialized = 0;
+    return ssleay_rand_bytes(buf, num, 0, 0);
+}
+
 static int ssleay_rand_nopseudo_bytes(unsigned char *buf, int num)
 {
     return ssleay_rand_bytes(buf, num, 0, 1);
index f9fda3eb89c9e814a9816f5fb5c2a7ebce830edc..10ccdf0c20daad6ba68c39dd6454d55535eba6bd 100644 (file)
 # endif
 
 int ssleay_rand_bytes(unsigned char *buf, int num, int pseudo, int lock);
-
+int ssleay_rand_bytes_from_system(unsigned char *buf, int num);
 #endif
index 88a78d350656ae02a30ef72a53d09f4513635deb..6094c83e407451952f3f909b8504e36c7aa60f45 100644 (file)
@@ -185,11 +185,29 @@ int RAND_status(void)
 
 /*
  * Entropy gatherer: use standard OpenSSL PRNG to seed (this will gather
- * entropy internally through RAND_poll().
+ * entropy internally through RAND_poll()).
  */
 
 static size_t drbg_get_entropy(DRBG_CTX *ctx, unsigned char **pout,
                                int entropy, size_t min_len, size_t max_len)
+{
+    /* Round up request to multiple of block size */
+    min_len = ((min_len + 19) / 20) * 20;
+    *pout = OPENSSL_malloc(min_len);
+    if (!*pout)
+        return 0;
+
+    /* Enforces a reseed of the SSLEAY PRNG before generating random bytes */
+    if (ssleay_rand_bytes_from_system(*pout, min_len) <= 0) {
+        OPENSSL_free(*pout);
+        *pout = NULL;
+        return 0;
+    }
+    return min_len;
+}
+
+static size_t drbg_get_nonce(DRBG_CTX *ctx, unsigned char **pout,
+                               int entropy, size_t min_len, size_t max_len)
 {
     /* Round up request to multiple of block size */
     min_len = ((min_len + 19) / 20) * 20;
@@ -281,7 +299,7 @@ int RAND_init_fips(void)
 
     FIPS_drbg_set_callbacks(dctx,
                             drbg_get_entropy, drbg_free_entropy, 20,
-                            drbg_get_entropy, drbg_free_entropy);
+                            drbg_get_nonce, drbg_free_entropy);
     FIPS_drbg_set_rand_callbacks(dctx, drbg_get_adin, 0,
                                  drbg_rand_seed, drbg_rand_add);
     /* Personalisation string: a string followed by date time vector */