Opportunistically read entropy from the OS in FIPS mode.

Even if RDRAND works, still mix in /dev/urandom or
getrandom(GRND_NONBLOCK) in the likely case that the entropy pool has
been initialized.

Change-Id: Ia61fc6eb07e90ae725a1781311c0ecc2fdabca87
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/37664
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/fipsmodule/rand/internal.h b/crypto/fipsmodule/rand/internal.h
index ad75823..c7ed74d 100644
--- a/crypto/fipsmodule/rand/internal.h
+++ b/crypto/fipsmodule/rand/internal.h
@@ -26,6 +26,11 @@
 #endif
 
 
+#if !defined(OPENSSL_WINDOWS) && !defined(OPENSSL_FUCHSIA) && \
+    !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE) && !defined(OPENSSL_TRUSTY)
+#define OPENSSL_URANDOM
+#endif
+
 // RAND_bytes_with_additional_data samples from the RNG after mixing 32 bytes
 // from |user_additional_data| in.
 void RAND_bytes_with_additional_data(uint8_t *out, size_t out_len,
@@ -35,6 +40,14 @@
 // system.
 void CRYPTO_sysrand(uint8_t *buf, size_t len);
 
+#if defined(OPENSSL_URANDOM) && defined(BORINGSSL_FIPS)
+// CRYPTO_sysrand_if_available fills |len| bytes at |buf| with entropy from the
+// operating system, if the entropy pool is initialized. If it is uninitialized,
+// it will not block and will instead fill |buf| with all zeros or early
+// /dev/urandom output.
+void CRYPTO_sysrand_if_available(uint8_t *buf, size_t len);
+#endif
+
 // rand_fork_unsafe_buffering_enabled returns whether fork-unsafe buffering has
 // been enabled via |RAND_enable_fork_unsafe_buffering|.
 int rand_fork_unsafe_buffering_enabled(void);
diff --git a/crypto/fipsmodule/rand/rand.c b/crypto/fipsmodule/rand/rand.c
index a8ef458..60e92c5 100644
--- a/crypto/fipsmodule/rand/rand.c
+++ b/crypto/fipsmodule/rand/rand.c
@@ -179,7 +179,8 @@
 #define FIPS_OVERREAD 10
   uint8_t entropy[CTR_DRBG_ENTROPY_LEN * FIPS_OVERREAD];
 
-  if (!hwrand(entropy, sizeof(entropy))) {
+  int used_hwrand = hwrand(entropy, sizeof(entropy));
+  if (!used_hwrand) {
     CRYPTO_sysrand(entropy, sizeof(entropy));
   }
 
@@ -210,6 +211,17 @@
       seed[j] ^= entropy[CTR_DRBG_ENTROPY_LEN * i + j];
     }
   }
+
+#if defined(OPENSSL_URANDOM)
+  // If we used RDRAND, also opportunistically read from the system. This avoids
+  // solely relying on the hardware once the entropy pool has been initialized.
+  if (used_hwrand) {
+    CRYPTO_sysrand_if_available(entropy, CTR_DRBG_ENTROPY_LEN);
+    for (size_t i = 0; i < CTR_DRBG_ENTROPY_LEN; i++) {
+      seed[i] ^= entropy[i];
+    }
+  }
+#endif
 }
 
 #else
diff --git a/crypto/fipsmodule/rand/urandom.c b/crypto/fipsmodule/rand/urandom.c
index f63857f..23413ff 100644
--- a/crypto/fipsmodule/rand/urandom.c
+++ b/crypto/fipsmodule/rand/urandom.c
@@ -18,8 +18,7 @@
 
 #include <openssl/rand.h>
 
-#if !defined(OPENSSL_WINDOWS) && !defined(OPENSSL_FUCHSIA) && \
-    !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE) && !defined(OPENSSL_TRUSTY)
+#if defined(OPENSSL_URANDOM)
 
 #include <assert.h>
 #include <errno.h>
@@ -133,6 +132,12 @@
 // urandom_fd is a file descriptor to /dev/urandom. It's protected by |once|.
 DEFINE_BSS_GET(int, urandom_fd)
 
+#if defined(USE_NR_getrandom)
+// getrandom_ready is one if |getrandom| had been initialized by the time
+// |init_once| was called and zero otherwise.
+DEFINE_BSS_GET(int, getrandom_ready)
+#endif
+
 DEFINE_STATIC_ONCE(rand_once)
 
 // init_once initializes the state of this module to values previously
@@ -145,42 +150,29 @@
   CRYPTO_STATIC_MUTEX_unlock_read(rand_lock_bss_get());
 
 #if defined(USE_NR_getrandom)
+  int have_getrandom;
   uint8_t dummy;
   ssize_t getrandom_ret =
       boringssl_getrandom(&dummy, sizeof(dummy), GRND_NONBLOCK);
-
-  if (getrandom_ret == -1 && errno == EAGAIN) {
-    // Attempt to get the path of the current process to aid in debugging when
-    // something blocks.
-    const char *current_process = "<unknown>";
-#if defined(OPENSSL_HAS_GETAUXVAL)
-    const unsigned long getauxval_ret = getauxval(AT_EXECFN);
-    if (getauxval_ret != 0) {
-      current_process = (const char *)getauxval_ret;
-    }
-#endif
-
-    fprintf(stderr,
-            "%s: getrandom indicates that the entropy pool has not been "
-            "initialized. Rather than continue with poor entropy, this process "
-            "will block until entropy is available.\n",
-            current_process);
-
-    getrandom_ret =
-        boringssl_getrandom(&dummy, sizeof(dummy), 0 /* no flags */);
-  }
-
   if (getrandom_ret == 1) {
-    *urandom_fd_bss_get() = kHaveGetrandom;
-    return;
-  }
-
-  // Ignore ENOSYS and fallthrough to using /dev/urandom, below. Otherwise it's
-  // a fatal error.
-  if (getrandom_ret != -1 || errno != ENOSYS) {
+    *getrandom_ready_bss_get() = 1;
+    have_getrandom = 1;
+  } else if (getrandom_ret == -1 && errno == EAGAIN) {
+    // We have getrandom, but the entropy pool has not been initialized yet.
+    have_getrandom = 1;
+  } else if (getrandom_ret == -1 && errno == ENOSYS) {
+    // Fallthrough to using /dev/urandom, below.
+    have_getrandom = 0;
+  } else {
+    // Other errors are fatal.
     perror("getrandom");
     abort();
   }
+
+  if (have_getrandom) {
+    *urandom_fd_bss_get() = kHaveGetrandom;
+    return;
+  }
 #endif  // USE_NR_getrandom
 
   // Android FIPS builds must support getrandom.
@@ -214,6 +206,71 @@
     }
   }
 
+  int flags = fcntl(fd, F_GETFD);
+  if (flags == -1) {
+    // Native Client doesn't implement |fcntl|.
+    if (errno != ENOSYS) {
+      perror("failed to get flags from urandom fd");
+      abort();
+    }
+  } else {
+    flags |= FD_CLOEXEC;
+    if (fcntl(fd, F_SETFD, flags) == -1) {
+      perror("failed to set FD_CLOEXEC on urandom fd");
+      abort();
+    }
+  }
+  *urandom_fd_bss_get() = fd;
+}
+
+DEFINE_STATIC_ONCE(wait_for_entropy_once)
+
+static void wait_for_entropy(void) {
+  int fd = *urandom_fd_bss_get();
+  if (fd == kHaveGetrandom) {
+#if defined(USE_NR_getrandom)
+    if (*getrandom_ready_bss_get()) {
+      // The entropy pool was already initialized in |init_once|.
+      return;
+    }
+
+    uint8_t dummy;
+    ssize_t getrandom_ret =
+        boringssl_getrandom(&dummy, sizeof(dummy), GRND_NONBLOCK);
+    if (getrandom_ret == -1 && errno == EAGAIN) {
+      // Attempt to get the path of the current process to aid in debugging when
+      // something blocks.
+      const char *current_process = "<unknown>";
+#if defined(OPENSSL_HAS_GETAUXVAL)
+      const unsigned long getauxval_ret = getauxval(AT_EXECFN);
+      if (getauxval_ret != 0) {
+        current_process = (const char *)getauxval_ret;
+      }
+#endif
+
+      fprintf(
+          stderr,
+          "%s: getrandom indicates that the entropy pool has not been "
+          "initialized. Rather than continue with poor entropy, this process "
+          "will block until entropy is available.\n",
+          current_process);
+
+      getrandom_ret =
+          boringssl_getrandom(&dummy, sizeof(dummy), 0 /* no flags */);
+    }
+
+    if (getrandom_ret == 1) {
+      return;
+    }
+
+    perror("getrandom");
+    abort();
+#else
+    fprintf(stderr, "urandom fd corrupt.\n");
+    abort();
+#endif  // USE_NR_getrandom
+  }
+
 #if defined(BORINGSSL_FIPS)
   // In FIPS mode we ensure that the kernel has sufficient entropy before
   // continuing. This is automatically handled by getrandom, which requires
@@ -235,23 +292,7 @@
 
     usleep(250000);
   }
-#endif
-
-  int flags = fcntl(fd, F_GETFD);
-  if (flags == -1) {
-    // Native Client doesn't implement |fcntl|.
-    if (errno != ENOSYS) {
-      perror("failed to get flags from urandom fd");
-      abort();
-    }
-  } else {
-    flags |= FD_CLOEXEC;
-    if (fcntl(fd, F_SETFD, flags) == -1) {
-      perror("failed to set FD_CLOEXEC on urandom fd");
-      abort();
-    }
-  }
-  *urandom_fd_bss_get() = fd;
+#endif  // BORINGSSL_FIPS
 }
 
 void RAND_set_urandom_fd(int fd) {
@@ -289,14 +330,28 @@
 }
 
 // fill_with_entropy writes |len| bytes of entropy into |out|. It returns one
-// on success and zero on error.
-static char fill_with_entropy(uint8_t *out, size_t len) {
+// on success and zero on error. If |block| is one, this function will block
+// until the entropy pool is initialized. Otherwise, this function may fail,
+// setting |errno| to |EAGAIN| if the entropy pool has not yet been initialized.
+static int fill_with_entropy(uint8_t *out, size_t len, int block) {
+  if (len == 0) {
+    return 1;
+  }
+
+  CRYPTO_once(rand_once_bss_get(), init_once);
+  if (block) {
+    CRYPTO_once(wait_for_entropy_once_bss_get(), wait_for_entropy);
+  }
+
+  // Clear |errno| so it has defined value if |read| or |getrandom|
+  // "successfully" returns zero.
+  errno = 0;
   while (len > 0) {
     ssize_t r;
 
     if (*urandom_fd_bss_get() == kHaveGetrandom) {
 #if defined(USE_NR_getrandom)
-      r = boringssl_getrandom(out, len, 0 /* no flags */);
+      r = boringssl_getrandom(out, len, block ? 0 : GRND_NONBLOCK);
 #else  // USE_NR_getrandom
       fprintf(stderr, "urandom fd corrupt.\n");
       abort();
@@ -319,13 +374,7 @@
 
 // CRYPTO_sysrand puts |requested| random bytes into |out|.
 void CRYPTO_sysrand(uint8_t *out, size_t requested) {
-  if (requested == 0) {
-    return;
-  }
-
-  CRYPTO_once(rand_once_bss_get(), init_once);
-
-  if (!fill_with_entropy(out, requested)) {
+  if (!fill_with_entropy(out, requested, /*block=*/1)) {
     perror("entropy fill failed");
     abort();
   }
@@ -337,5 +386,17 @@
 #endif
 }
 
-#endif /* !OPENSSL_WINDOWS && !defined(OPENSSL_FUCHSIA) && \
-          !BORINGSSL_UNSAFE_DETERMINISTIC_MODE && !OPENSSL_TRUSTY */
+#if defined(BORINGSSL_FIPS)
+void CRYPTO_sysrand_if_available(uint8_t *out, size_t requested) {
+  // Return all zeros if |fill_with_entropy| fails.
+  OPENSSL_memset(out, 0, requested);
+
+  if (!fill_with_entropy(out, requested, /*block=*/0) &&
+      errno != EAGAIN) {
+    perror("opportunistic entropy fill failed");
+    abort();
+  }
+}
+#endif  // BORINGSSL_FIPS
+
+#endif  // OPENSSL_URANDOM