Switch to passive entropy collection for Android FIPS.

Rather than the FIPS module actively collecting entropy from the CPU or
OS, this change configures Android FIPS to passively receive entropy.

See FIPS IG 7.14 section two.

Change-Id: Ibfc5c5042e560718474b89970199d35b67c21296
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/44305
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
index 2771768..8368a21 100644
--- a/crypto/CMakeLists.txt
+++ b/crypto/CMakeLists.txt
@@ -325,6 +325,7 @@
   rand_extra/deterministic.c
   rand_extra/forkunsafe.c
   rand_extra/fuchsia.c
+  rand_extra/passive.c
   rand_extra/rand_extra.c
   rand_extra/windows.c
   rc4/rc4.c
diff --git a/crypto/fipsmodule/rand/internal.h b/crypto/fipsmodule/rand/internal.h
index db81c33..598a17b 100644
--- a/crypto/fipsmodule/rand/internal.h
+++ b/crypto/fipsmodule/rand/internal.h
@@ -36,6 +36,34 @@
 void RAND_bytes_with_additional_data(uint8_t *out, size_t out_len,
                                      const uint8_t user_additional_data[32]);
 
+#if defined(BORINGSSL_FIPS)
+
+// We overread from /dev/urandom or RDRAND by a factor of 10 and XOR to whiten.
+#define BORINGSSL_FIPS_OVERREAD 10
+
+// CRYPTO_get_seed_entropy writes |out_entropy_len| bytes of entropy, suitable
+// for seeding a DRBG, to |out_entropy|. It sets |*out_used_cpu| to one if the
+// entropy came directly from the CPU and zero if it came from the OS. It
+// actively obtains entropy from the CPU/OS and so should not be called from
+// within the FIPS module if |BORINGSSL_FIPS_PASSIVE_ENTROPY| is defined.
+void CRYPTO_get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len,
+                             int *out_used_cpu);
+
+#if defined(BORINGSSL_FIPS_PASSIVE_ENTROPY)
+
+// RAND_load_entropy supplies |entropy_len| bytes of entropy to the module. The
+// |from_cpu| parameter is true iff the entropy was obtained directly from the
+// CPU.
+void RAND_load_entropy(const uint8_t *entropy, size_t entropy_len,
+                       int from_cpu);
+
+// RAND_need_entropy is implemented outside of the FIPS module and is called
+// when the module has stopped because it has run out of entropy.
+void RAND_need_entropy(size_t bytes_needed);
+
+#endif  // BORINGSSL_FIPS_PASSIVE_ENTROPY
+#endif  // BORINGSSL_FIPS
+
 // CRYPTO_sysrand fills |len| bytes at |buf| with entropy from the operating
 // system.
 void CRYPTO_sysrand(uint8_t *buf, size_t len);
diff --git a/crypto/fipsmodule/rand/rand.c b/crypto/fipsmodule/rand/rand.c
index 05d6a29..aa0f05b 100644
--- a/crypto/fipsmodule/rand/rand.c
+++ b/crypto/fipsmodule/rand/rand.c
@@ -25,6 +25,7 @@
 #include <openssl/chacha.h>
 #include <openssl/cpu.h>
 #include <openssl/mem.h>
+#include <openssl/type_check.h>
 
 #include "internal.h"
 #include "fork_detect.h"
@@ -63,11 +64,11 @@
   // (re)seeded. This is bound by |kReseedInterval|.
   unsigned calls;
   // last_block_valid is non-zero iff |last_block| contains data from
-  // |CRYPTO_sysrand_for_seed|.
+  // |get_seed_entropy|.
   int last_block_valid;
 
 #if defined(BORINGSSL_FIPS)
-  // last_block contains the previous block from |CRYPTO_sysrand_for_seed|.
+  // last_block contains the previous block from |get_seed_entropy|.
   uint8_t last_block[CRNGT_BLOCK_SIZE];
   // next and prev form a NULL-terminated, double-linked list of all states in
   // a process.
@@ -146,12 +147,6 @@
     OPENSSL_memcpy(buf + len_multiple8, rand_buf, remainder);
   }
 
-#if defined(BORINGSSL_FIPS_BREAK_CRNG)
-  // This breaks the "continuous random number generator test" defined in FIPS
-  // 140-2, section 4.9.2, and implemented in rand_get_seed().
-  OPENSSL_memset(buf, 0, len);
-#endif
-
   return 1;
 }
 
@@ -165,25 +160,110 @@
 
 #if defined(BORINGSSL_FIPS)
 
+void CRYPTO_get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len,
+                             int *out_used_cpu) {
+  *out_used_cpu = 0;
+  if (have_rdrand() && rdrand(out_entropy, out_entropy_len)) {
+    *out_used_cpu = 1;
+  } else {
+    CRYPTO_sysrand_for_seed(out_entropy, out_entropy_len);
+  }
+
+#if defined(BORINGSSL_FIPS_BREAK_CRNG)
+  // This breaks the "continuous random number generator test" defined in FIPS
+  // 140-2, section 4.9.2, and implemented in |rand_get_seed|.
+  OPENSSL_memset(out_entropy, 0, out_entropy_len);
+#endif
+}
+
+#if defined(BORINGSSL_FIPS_PASSIVE_ENTROPY)
+
+// In passive entropy mode, entropy is supplied from outside of the module via
+// |RAND_load_entropy| and is stored in global instance of the following
+// structure.
+
+struct entropy_buffer {
+  // bytes contains entropy suitable for seeding a DRBG.
+  uint8_t bytes[CTR_DRBG_ENTROPY_LEN * BORINGSSL_FIPS_OVERREAD];
+  // bytes_valid indicates the number of bytes of |bytes| that contain valid
+  // data.
+  size_t bytes_valid;
+  // from_cpu is true if any of the contents of |bytes| were obtained directly
+  // from the CPU.
+  int from_cpu;
+};
+
+DEFINE_BSS_GET(struct entropy_buffer, entropy_buffer);
+DEFINE_STATIC_MUTEX(entropy_buffer_lock);
+
+void RAND_load_entropy(const uint8_t *entropy, size_t entropy_len,
+                       int from_cpu) {
+  struct entropy_buffer *const buffer = entropy_buffer_bss_get();
+
+  CRYPTO_STATIC_MUTEX_lock_write(entropy_buffer_lock_bss_get());
+  const size_t space = sizeof(buffer->bytes) - buffer->bytes_valid;
+  if (entropy_len > space) {
+    entropy_len = space;
+  }
+
+  OPENSSL_memcpy(&buffer->bytes[buffer->bytes_valid], entropy, entropy_len);
+  buffer->bytes_valid += entropy_len;
+  buffer->from_cpu |= from_cpu && (entropy_len != 0);
+  CRYPTO_STATIC_MUTEX_unlock_write(entropy_buffer_lock_bss_get());
+}
+
+// get_seed_entropy fills |out_entropy_len| bytes of |out_entropy| from the
+// global |entropy_buffer|.
+static void get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len,
+                             int *out_used_cpu) {
+  struct entropy_buffer *const buffer = entropy_buffer_bss_get();
+  if (out_entropy_len > sizeof(buffer->bytes)) {
+    abort();
+  }
+
+  CRYPTO_STATIC_MUTEX_lock_write(entropy_buffer_lock_bss_get());
+  while (buffer->bytes_valid < out_entropy_len) {
+    CRYPTO_STATIC_MUTEX_unlock_write(entropy_buffer_lock_bss_get());
+    RAND_need_entropy(out_entropy_len - buffer->bytes_valid);
+    CRYPTO_STATIC_MUTEX_lock_write(entropy_buffer_lock_bss_get());
+  }
+
+  *out_used_cpu = buffer->from_cpu;
+  OPENSSL_memcpy(out_entropy, buffer->bytes, out_entropy_len);
+  OPENSSL_memmove(buffer->bytes, &buffer->bytes[out_entropy_len],
+                  buffer->bytes_valid - out_entropy_len);
+  buffer->bytes_valid -= out_entropy_len;
+  if (buffer->bytes_valid == 0) {
+    buffer->from_cpu = 0;
+  }
+
+  CRYPTO_STATIC_MUTEX_unlock_write(entropy_buffer_lock_bss_get());
+}
+
+#else
+
+// In the active case, |get_seed_entropy| simply calls |CRYPTO_get_seed_entropy|
+// in order to obtain entropy from the CPU or OS.
+static void get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len,
+                            int *out_used_cpu) {
+  CRYPTO_get_seed_entropy(out_entropy, out_entropy_len, out_used_cpu);
+}
+
+#endif  // !BORINGSSL_FIPS_PASSIVE_ENTROPY
+
+// rand_get_seed fills |seed| with entropy and sets |*out_used_cpu| to one if
+// that entropy came directly from the CPU and zero otherwise.
 static void rand_get_seed(struct rand_thread_state *state,
-                          uint8_t seed[CTR_DRBG_ENTROPY_LEN]) {
+                          uint8_t seed[CTR_DRBG_ENTROPY_LEN],
+                          int *out_used_cpu) {
   if (!state->last_block_valid) {
-    if (!have_rdrand() ||
-        !rdrand(state->last_block, sizeof(state->last_block))) {
-      CRYPTO_sysrand_for_seed(state->last_block, sizeof(state->last_block));
-    }
+    int unused;
+    get_seed_entropy(state->last_block, sizeof(state->last_block), &unused);
     state->last_block_valid = 1;
   }
 
-  // We overread from /dev/urandom or RDRAND by a factor of 10 and XOR to
-  // whiten.
-#define FIPS_OVERREAD 10
-  uint8_t entropy[CTR_DRBG_ENTROPY_LEN * FIPS_OVERREAD];
-
-  int used_rdrand = have_rdrand() && rdrand(entropy, sizeof(entropy));
-  if (!used_rdrand) {
-    CRYPTO_sysrand_for_seed(entropy, sizeof(entropy));
-  }
+  uint8_t entropy[CTR_DRBG_ENTROPY_LEN * BORINGSSL_FIPS_OVERREAD];
+  get_seed_entropy(entropy, sizeof(entropy), out_used_cpu);
 
   // See FIPS 140-2, section 4.9.2. This is the “continuous random number
   // generator test” which causes the program to randomly abort. Hopefully the
@@ -193,6 +273,7 @@
     BORINGSSL_FIPS_abort();
   }
 
+  OPENSSL_STATIC_ASSERT(sizeof(entropy) % CRNGT_BLOCK_SIZE == 0, "");
   for (size_t i = CRNGT_BLOCK_SIZE; i < sizeof(entropy);
        i += CRNGT_BLOCK_SIZE) {
     if (CRYPTO_memcmp(entropy + i - CRNGT_BLOCK_SIZE, entropy + i,
@@ -207,31 +288,24 @@
 
   OPENSSL_memcpy(seed, entropy, CTR_DRBG_ENTROPY_LEN);
 
-  for (size_t i = 1; i < FIPS_OVERREAD; i++) {
+  for (size_t i = 1; i < BORINGSSL_FIPS_OVERREAD; i++) {
     for (size_t j = 0; j < CTR_DRBG_ENTROPY_LEN; j++) {
       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_rdrand) {
-    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
 
+// rand_get_seed fills |seed| with entropy and sets |*out_used_cpu| to one if
+// that entropy came directly from the CPU and zero otherwise.
 static void rand_get_seed(struct rand_thread_state *state,
-                          uint8_t seed[CTR_DRBG_ENTROPY_LEN]) {
+                          uint8_t seed[CTR_DRBG_ENTROPY_LEN],
+                          int *out_used_cpu) {
   // If not in FIPS mode, we don't overread from the system entropy source and
   // we don't depend only on the hardware RDRAND.
   CRYPTO_sysrand(seed, CTR_DRBG_ENTROPY_LEN);
+  *out_used_cpu = 0;
 }
 
 #endif
@@ -290,8 +364,23 @@
 
     state->last_block_valid = 0;
     uint8_t seed[CTR_DRBG_ENTROPY_LEN];
-    rand_get_seed(state, seed);
-    if (!CTR_DRBG_init(&state->drbg, seed, NULL, 0)) {
+    int used_cpu;
+    rand_get_seed(state, seed, &used_cpu);
+
+    uint8_t personalization[CTR_DRBG_ENTROPY_LEN];
+    size_t personalization_len = 0;
+#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_cpu &&
+        CRYPTO_sysrand_if_available(personalization, sizeof(personalization))) {
+      personalization_len = sizeof(personalization);
+    }
+#endif
+
+    if (!CTR_DRBG_init(&state->drbg, seed, personalization,
+                       personalization_len)) {
       abort();
     }
     state->calls = 0;
@@ -315,7 +404,8 @@
   if (state->calls >= kReseedInterval ||
       state->fork_generation != fork_generation) {
     uint8_t seed[CTR_DRBG_ENTROPY_LEN];
-    rand_get_seed(state, seed);
+    int used_cpu;
+    rand_get_seed(state, seed, &used_cpu);
 #if defined(BORINGSSL_FIPS)
     // Take a read lock around accesses to |state->drbg|. This is needed to
     // avoid returning bad entropy if we race with
diff --git a/crypto/fipsmodule/rand/urandom.c b/crypto/fipsmodule/rand/urandom.c
index bae3fc3..3def3aa 100644
--- a/crypto/fipsmodule/rand/urandom.c
+++ b/crypto/fipsmodule/rand/urandom.c
@@ -366,14 +366,7 @@
     perror("entropy fill failed");
     abort();
   }
-
-#if defined(BORINGSSL_FIPS_BREAK_CRNG)
-  // This breaks the "continuous random number generator test" defined in FIPS
-  // 140-2, section 4.9.2, and implemented in rand_get_seed().
-  OPENSSL_memset(out, 0, requested);
-#endif
 }
-
 #endif  // BORINGSSL_FIPS
 
 int CRYPTO_sysrand_if_available(uint8_t *out, size_t requested) {
diff --git a/crypto/rand_extra/passive.c b/crypto/rand_extra/passive.c
new file mode 100644
index 0000000..a8c2487
--- /dev/null
+++ b/crypto/rand_extra/passive.c
@@ -0,0 +1,34 @@
+/* Copyright (c) 2020, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/base.h>
+#include "../fipsmodule/rand/internal.h"
+
+#if defined(BORINGSSL_FIPS_PASSIVE_ENTROPY)
+
+// RAND_need_entropy is called by the FIPS module when it has blocked because of
+// a lack of entropy. This signal is used as an indication to feed it more.
+void RAND_need_entropy(size_t bytes_needed) {
+  uint8_t buf[CTR_DRBG_ENTROPY_LEN * BORINGSSL_FIPS_OVERREAD];
+  size_t todo = sizeof(buf);
+  if (todo > bytes_needed) {
+    todo = bytes_needed;
+  }
+
+  int used_cpu;
+  CRYPTO_get_seed_entropy(buf, todo, &used_cpu);
+  RAND_load_entropy(buf, todo, used_cpu);
+}
+
+#endif  // BORINGSSL_FIPS_PASSIVE_ENTROPY
diff --git a/include/openssl/base.h b/include/openssl/base.h
index 8e8cc15..60c7e87 100644
--- a/include/openssl/base.h
+++ b/include/openssl/base.h
@@ -156,6 +156,10 @@
 
 #if defined(__ANDROID_API__)
 #define OPENSSL_ANDROID
+#if defined(BORINGSSL_FIPS)
+// The FIPS module on Android passively receives entropy.
+#define BORINGSSL_FIPS_PASSIVE_ENTROPY
+#endif
 #endif
 
 // BoringSSL requires platform's locking APIs to make internal global state