Switch away from being an entropy-injected FIPS module.

BoringCrypto has, historically, been an "entropy injected" module. That
means that entropy is passed in from outside of the module and thus the
module isn't responsible for it.

This change switches the source of entropy to being internal. This is
done on non-Android platforms by jittering the CPU and, on Android
platforms, by contacting a system service.

Change-Id: Ib6fca6fff4d36825912076131c82f83216b3a3a1
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/91347
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
diff --git a/build.json b/build.json
index a2021c2..358a8c3 100644
--- a/build.json
+++ b/build.json
@@ -80,6 +80,7 @@
             "crypto/fipsmodule/mldsa/mldsa.cc.inc",
             "crypto/fipsmodule/mlkem/fips_known_values.inc",
             "crypto/fipsmodule/mlkem/mlkem.cc.inc",
+            "crypto/fipsmodule/rand/android_entropy_client.cc.inc",
             "crypto/fipsmodule/rand/ctrdrbg.cc.inc",
             "crypto/fipsmodule/rand/rand.cc.inc",
             "crypto/fipsmodule/rsa/padding.cc.inc",
@@ -309,7 +310,6 @@
             "crypto/rand/forkunsafe.cc",
             "crypto/rand/getentropy.cc",
             "crypto/rand/ios.cc",
-            "crypto/rand/passive.cc",
             "crypto/rand/rand.cc",
             "crypto/rand/trusty.cc",
             "crypto/rand/urandom.cc",
diff --git a/crypto/bcm_support.h b/crypto/bcm_support.h
index 20e6cef..7e39637 100644
--- a/crypto/bcm_support.h
+++ b/crypto/bcm_support.h
@@ -32,10 +32,6 @@
 // system.
 void CRYPTO_sysrand(uint8_t *buf, size_t len);
 
-// RAND_need_entropy is called whenever the BCM module has stopped because it
-// has run out of entropy.
-void RAND_need_entropy(size_t bytes_needed);
-
 // crypto_get_fork_generation returns the fork generation number for the current
 // process, or zero if not supported on the platform. The fork generation number
 // is a non-zero, strictly-monotonic counter with the property that, if queried
diff --git a/crypto/fipsmodule/FIPS.md b/crypto/fipsmodule/FIPS.md
index c665daf..35425bc 100644
--- a/crypto/fipsmodule/FIPS.md
+++ b/crypto/fipsmodule/FIPS.md
@@ -19,6 +19,10 @@
 1. 2024-04-07: certificate [#5104](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/5104).
 1. 2024-08-05: certificate [#5244](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/5244).
 
+Entropy validations:
+
+1. 2025-07-25: certificate [#E321](https://csrc.nist.gov/projects/cryptographic-module-validation-program/entropy-validations/certificate/321).
+
 The following validations are active:
 
 | Version | Status | CAVP |
@@ -46,7 +50,7 @@
 
 The latest stable versions of Clang, Go, Ninja, and CMake should be used.
 
-On the upstream stream, `FIPS_version` will return zero to indicate that it is not the validated module stream.
+On the update stream, `FIPS_version` will return zero to indicate that it is not the validated module stream.
 
 ## Running ACVP tests
 
@@ -60,7 +64,6 @@
 
 1. `RSA_PWCT`
 1. `ECDSA_PWCT`
-1. `CRNG`
 
 ## Breaking the integrity test
 
@@ -68,19 +71,17 @@
 
 ## RNG design
 
-FIPS 140-2 requires that one of its PRNGs be used (which they call DRBGs). In BoringCrypto, we use CTR-DRBG with AES-256 exclusively and `RAND_bytes` (the primary interface for the rest of the system to get random data) takes its output from there.
+FIPS 140-3 requires that one of its PRNGs be used (which they call DRBGs). In BoringCrypto, we use CTR-DRBG with AES-256 exclusively and `RAND_bytes` (the primary interface for the rest of the system to get random data) takes its output from there.
 
-The DRBG state is kept in a thread-local structure and is seeded from one of the following entropy sources in preference order: RDRAND (on Intel chips) and `getrandom`.
+For performance, each thread gets a thread-local DRBG from which it can generate random bytes. Those DRBGs are seeded from a compliant entropy source. For Linux platforms, that means a jitter source implemented within BoringCrypto (ESV \#E321). On Android, BoringCrypto makes a UNIX domain socket connection to a local entropy daemon, which provides entropy from a certified hardware source.
 
-In FIPS mode, each of those entropy sources is subject to a 10× overread. That is, when *n* bytes of entropy are needed, *10n* bytes will be read from the entropy source and XORed down to *n* bytes. Reads from the entropy source are also processed in blocks of 16 bytes and if two consecutive chunks are equal the process will abort.
+The jitter source takes a little time to generate and so it is wrapped in its own DRBG, which is process-wide. That DRBG is only reseeded at the rate that NIST requires. Since we use CTR-DRBG with AES-256, 2⁴⁸ calls are allowed before reseeding and thus most processes will never reseed it. Therefore any jittering delay can be considered a one-time cost and brought forward by calling `RAND_bytes` to generate a single byte at startup. In order to be more uniform, Android also uses this process-wide DRBG and thus only seeds from the daemon once.
 
-In the case that the seed is taken from RDRAND, getrandom will also be queried to obtain additional entropy from the operating system.
+The FIPS DRBGs allow “additional input” to be fed into a given call. We use this feature to be as robust as possible to state duplication from process forks and VM copies:
 
-The CTR-DRBG is reseeded every 4096 calls to `RAND_bytes`. Thus the process will randomly crash about every 2¹³⁵ calls.
+The thread-local DRBGs are seeded from the entropy source as described above, but OS entropy will also be used as additional input when seeding. Also, for every call we read 32 bytes of “additional data” from the OS or RDRAND, unless there's no fast RDRAND and the OS provides a signal when the address space forks (or if the process has promised not to fork). This is called “prediction resistance” by FIPS, but we do _not_ claim this property in a FIPS context because we don't implement it the way they want.
 
-The FIPS PRNGs allow “additional input” to be fed into a given call. We use this feature to be as robust as possible to state duplication from process forks and VM copies: for every call we read 32 bytes of “additional data” from the entropy source (without overread) which means that cloned states will diverge at the next call to `RAND_bytes`. This is called “prediction resistance” by FIPS, but we do *not* claim this property in a FIPS context because we don't implement it the way they want.
-
-There is a second interface to the RNG which allows the caller to supply bytes that will be XORed into the generated additional data (`RAND_bytes_with_additional_data`). This is used in the ECDSA code to include the message and private key in the generation of *k*, the ECDSA nonce. This allows ECDSA to be robust to entropy failures while still following the FIPS rules.
+There is a second interface to the RNG which allows the caller to supply bytes that will be XORed into the generated additional data (`RAND_bytes_with_additional_data`). This is used in the ECDSA code to include the message and private key in the generation of _k_, the ECDSA nonce. This allows ECDSA to be robust to entropy failures while still following the FIPS rules.
 
 FIPS requires that RNG state be zeroed when the process exits. In order to implement this, all per-thread RNG states are tracked in a linked list and a destructor function is included which clears them. In order for this to be safe in the presence of threads, a lock is used to stop all other threads from using the RNG once this process has begun. Thus the main thread exiting may cause other threads to deadlock, and drawing on entropy in a destructor function may also deadlock.
 
@@ -140,11 +141,11 @@
 
 The script performs a number of other transformations which are worth noting but do not warrant their own discussions:
 
-1.  It duplicates each global symbol with a local symbol that has `_local_target` appended to the name. References to the global symbols are rewritten to use these duplicates instead. Otherwise, although the generated code uses IP-relative references, relocations are emitted for global symbols in case they are overridden by a different object file during the link.
-1.  Various sections, notably `.rodata`, are moved to the `.text` section, inside the module, so module code may reference it without relocations.
-1.  For each BSS symbol, it generates a function named after that symbol but with `_bss_get` appended, which returns its address.
-1.  It inserts the labels that delimit the module's code and data (called `module_start` and `module_end` in the diagram above).
-1.  It adds a 64-byte, read-only array outside of the module to contain the known-good HMAC value.
+1. It duplicates each global symbol with a local symbol that has `_local_target` appended to the name. References to the global symbols are rewritten to use these duplicates instead. Otherwise, although the generated code uses IP-relative references, relocations are emitted for global symbols in case they are overridden by a different object file during the link.
+1. Various sections, notably `.rodata`, are moved to the `.text` section, inside the module, so module code may reference it without relocations.
+1. For each BSS symbol, it generates a function named after that symbol but with `_bss_get` appended, which returns its address.
+1. It inserts the labels that delimit the module's code and data (called `module_start` and `module_end` in the diagram above).
+1. It adds a 64-byte, read-only array outside of the module to contain the known-good HMAC value.
 
 ##### Integrity testing
 
@@ -160,16 +161,16 @@
 
 OpenSSL's solution to this problem is very similar to our shared build, with just a few differences:
 
-1.  OpenSSL deals with run-time relocations by not hashing parts of the module's data.
-1.  OpenSSL uses `ld -r` (the partial linking mode) to merge a number of object files into their `fipscanister.o`. For BoringCrypto's static build, we merge all the C source files by building a single C file that #includes all the others, and we merge the assembly sources by appending them to the assembly output from the C compiler.
-1.  OpenSSL depends on the link order and inserts two object files, `fips_start.o` and `fips_end.o`, in order to establish the `module_start` and `module_end` values. BoringCrypto adds labels at the correct places in the assembly for the static build, or uses a linker script for the shared build.
-1.  OpenSSL calculates the hash after the final link and either injects it into the binary or recompiles with the value of the hash passed in as a #define. BoringCrypto calculates it prior to the final link and injects it into the object file.
-1.  OpenSSL references read-write data directly, since it can know the offsets to it. BoringCrypto indirects these loads and stores.
-1.  OpenSSL doesn't run the power-on test until `FIPS_module_mode_set` is called. BoringCrypto does it in a constructor function. Failure of the test is non-fatal in OpenSSL, BoringCrypto will crash.
-1.  Since the contents of OpenSSL's module change between compilation and use, OpenSSL generates `fipscanister.o.sha1` to check that the compiled object doesn't change before linking. Since BoringCrypto's module is fixed after compilation (in the static case), the final integrity check is unbroken through the linking process.
+1. OpenSSL deals with run-time relocations by not hashing parts of the module's data.
+1. OpenSSL uses `ld -r` (the partial linking mode) to merge a number of object files into their `fipscanister.o`. For BoringCrypto's static build, we merge all the C source files by building a single C file that #includes all the others, and we merge the assembly sources by appending them to the assembly output from the C compiler.
+1. OpenSSL depends on the link order and inserts two object files, `fips_start.o` and `fips_end.o`, in order to establish the `module_start` and `module_end` values. BoringCrypto adds labels at the correct places in the assembly for the static build, or uses a linker script for the shared build.
+1. OpenSSL calculates the hash after the final link and either injects it into the binary or recompiles with the value of the hash passed in as a #define. BoringCrypto calculates it prior to the final link and injects it into the object file.
+1. OpenSSL references read-write data directly, since it can know the offsets to it. BoringCrypto indirects these loads and stores.
+1. OpenSSL doesn't run the power-on test until `FIPS_module_mode_set` is called. BoringCrypto does it in a constructor function. Failure of the test is non-fatal in OpenSSL, BoringCrypto will crash.
+1. Since the contents of OpenSSL's module change between compilation and use, OpenSSL generates `fipscanister.o.sha1` to check that the compiled object doesn't change before linking. Since BoringCrypto's module is fixed after compilation (in the static case), the final integrity check is unbroken through the linking process.
 
 Some of the similarities are worth noting:
 
-1.  OpenSSL has all out-calls from the module indirecting via the PLT, which is equivalent to the redirector functions described above.
+1. OpenSSL has all out-calls from the module indirecting via the PLT, which is equivalent to the redirector functions described above.
 
 ![OpenSSL build process](./intcheck3.png)
diff --git a/crypto/fipsmodule/bcm.cc b/crypto/fipsmodule/bcm.cc
index 1a5a6fd..95d9f92 100644
--- a/crypto/fipsmodule/bcm.cc
+++ b/crypto/fipsmodule/bcm.cc
@@ -102,6 +102,7 @@
 #include "keccak/keccak.cc.inc"
 #include "mldsa/mldsa.cc.inc"
 #include "mlkem/mlkem.cc.inc"
+#include "rand/android_entropy_client.cc.inc"
 #include "rand/ctrdrbg.cc.inc"
 #include "rand/rand.cc.inc"
 #include "rsa/padding.cc.inc"
@@ -181,8 +182,8 @@
 
 #endif  // !ASAN
 
-static void
-    __attribute__((constructor)) BORINGSSL_bcm_power_on_self_test(void) {
+static void __attribute__((constructor)) BORINGSSL_bcm_power_on_self_test(
+    void) {
 #if !defined(OPENSSL_ASAN)
   // Integrity tests cannot run under ASAN because it involves reading the full
   // .text section, which triggers the global-buffer overflow detection.
diff --git a/crypto/fipsmodule/bcm_interface.h b/crypto/fipsmodule/bcm_interface.h
index d4ea95d..db44e06 100644
--- a/crypto/fipsmodule/bcm_interface.h
+++ b/crypto/fipsmodule/bcm_interface.h
@@ -73,12 +73,6 @@
 
 #endif  // BORINGSSL_FIPS
 
-// BCM_rand_load_entropy supplies |entropy_len| bytes of entropy to the BCM
-// module. The |want_additional_input| parameter is true iff the entropy was
-// obtained from a source other than the system, e.g. directly from the CPU.
-bcm_infallible BCM_rand_load_entropy(const uint8_t *entropy, size_t entropy_len,
-                                     int want_additional_input);
-
 // BCM_rand_bytes is the same as the public |RAND_bytes| function, other
 // than returning a bcm_infallible status indicator.
 bcm_infallible BCM_rand_bytes(uint8_t *out, size_t out_len);
diff --git a/crypto/fipsmodule/rand/android_entropy_client.cc.inc b/crypto/fipsmodule/rand/android_entropy_client.cc.inc
new file mode 100644
index 0000000..c31ebfb
--- /dev/null
+++ b/crypto/fipsmodule/rand/android_entropy_client.cc.inc
@@ -0,0 +1,82 @@
+// Copyright 2020 The BoringSSL Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <openssl/base.h>
+
+#if defined(BORINGSSL_FIPS) && defined(OPENSSL_ANDROID)
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "internal.h"
+
+BSSL_NAMESPACE_BEGIN
+
+int bssl_get_seed_from_daemon(uint8_t *out_entropy, size_t *inout_entropy_len) {
+  const int sock = socket(AF_UNIX, SOCK_STREAM, 0);
+  if (sock < 0) {
+    return 0;
+  }
+
+  struct sockaddr_un sun;
+  memset(&sun, 0, sizeof(sun));
+  sun.sun_family = AF_UNIX;
+  static const char kSocketPath[] = "/dev/socket/prng_seeder";
+  static_assert(sizeof(kSocketPath) <= UNIX_PATH_MAX, "kSocketPath too long");
+  OPENSSL_memcpy(sun.sun_path, kSocketPath, sizeof(kSocketPath));
+
+  int ret = 0;
+  size_t done = 0;
+  if (connect(sock, (struct sockaddr *)&sun, sizeof(sun))) {
+    goto out;
+  }
+
+  uint8_t buffer[BSSL_ENTROPY_DAEMON_RESPONSE_LEN];
+  while (done < sizeof(buffer)) {
+    ssize_t n;
+    do {
+      n = read(sock, buffer + done, sizeof(buffer) - done);
+    } while (n == -1 && errno == EINTR);
+
+    if (n < 1) {
+      goto out;
+    }
+    done += n;
+  }
+
+  if (done != BSSL_ENTROPY_DAEMON_RESPONSE_LEN) {
+    // The daemon should always write |BSSL_ENTROPY_DAEMON_RESPONSE_LEN| bytes
+    // on every connection.
+    goto out;
+  }
+
+  if (*inout_entropy_len > sizeof(buffer)) {
+    *inout_entropy_len = sizeof(buffer);
+  }
+  OPENSSL_memcpy(out_entropy, buffer, *inout_entropy_len);
+  ret = 1;
+
+out:
+  if (sock >= 0) {
+    close(sock);
+  }
+  return ret;
+}
+
+BSSL_NAMESPACE_END
+
+#endif  // FIPS && ANDROID
diff --git a/crypto/fipsmodule/rand/internal.h b/crypto/fipsmodule/rand/internal.h
index 0913f00..cd1fc62 100644
--- a/crypto/fipsmodule/rand/internal.h
+++ b/crypto/fipsmodule/rand/internal.h
@@ -56,6 +56,16 @@
                                  const uint8_t *personalization,
                                  size_t personalization_len);
 
+// BSSL_ENTROPY_DAEMON_RESPONSE_LEN is the number of bytes that the Android
+// entropy daemon replies with.
+#define BSSL_ENTROPY_DAEMON_RESPONSE_LEN (48 * 10 + 16)
+
+// bssl_get_seed_from_daemon fetches up to `*inout_entropy_len` bytes of
+// entropy from the Android entropy daemon, writing them to `*out_entropy`.
+// On successful exit, `*inout_entropy_len` contains the number of bytes
+// written. Returns one on success and zero on error.
+int bssl_get_seed_from_daemon(uint8_t *out_entropy, size_t *inout_entropy_len);
+
 #if defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM)
 
 inline int have_rdrand() { return CRYPTO_is_RDRAND_capable(); }
diff --git a/crypto/fipsmodule/rand/rand.cc.inc b/crypto/fipsmodule/rand/rand.cc.inc
index 9b29441..dad1f2b 100644
--- a/crypto/fipsmodule/rand/rand.cc.inc
+++ b/crypto/fipsmodule/rand/rand.cc.inc
@@ -24,6 +24,7 @@
 #include <openssl/ctrdrbg.h>
 #include <openssl/mem.h>
 #include <openssl/rand.h>
+#include <openssl/sha.h>
 
 #include "../../bcm_support.h"
 #include "../../mem_internal.h"
@@ -31,33 +32,157 @@
 #include "../delocate.h"
 #include "internal.h"
 
+#if defined(BORINGSSL_FIPS)
+#include "../entropy/internal.h"
+#endif
+
 
 using namespace bssl;
 
-// It's assumed that the operating system always has an unfailing source of
-// entropy which is accessed via |CRYPTO_sysrand|. (If the operating system
-// entropy source fails, it's up to |CRYPTO_sysrand| to abort the process—we
-// don't try to handle it.)
+// # Entropy design
 //
-// In addition, the hardware may provide a low-latency RNG. Intel's rdrand
-// instruction is the canonical example of this. When a hardware RNG is
-// available we don't need to worry about an RNG failure arising from fork()ing
-// the process or moving a VM, so we can keep thread-local RNG state and use it
-// as an additional-data input to CTR-DRBG.
+// Each thread gets its own, thread-local DRBG. These are `rand_thread_state`
+// objects. In non-FIPS mode these are seeded from the operating system. It's
+// assumed that the operating system always has an unfailing source of entropy
+// which is accessed via |CRYPTO_sysrand|. (If the operating system entropy
+// source fails, it's up to |CRYPTO_sysrand| to abort the process—we don't try
+// to handle it.)
+//
+// If running in FIPS mode, a compliant entropy source must be used to seed the
+// thread-local DRBGs instead. That is either the BoringCrypto jitter source or,
+// on Android, a source provided by the OS which is accessed via a UNIX domain
+// socket. When seeding from these sources, OS entropy is still used via the
+// "additional data" input to the DRBG. But, from a compliance perspective,
+// this input is irrelevent.
+//
+// Since the jitter source takes some time to run, there is a single jitter
+// source per address space. It's used to seed a DRBG (`core_drbg`) which, in
+// turn, seeds the thread-local DRBGs. Android also uses this core DRBG design
+// in order to keep things more uniform across platforms.
+//
+// The core DRBG will be reseeded in accordance with NIST's reseeding
+// requirements. However, since our DRBG configuration allows for 2**48
+// generate calls before reseeding, it's unlikely that any process would
+// observe a reseed of that DRBG.
+//
+// The thread-local DRBGs mean that we have to worry about the process fork()ing
+// or the underlying VM getting cloned. Thus we try to mix entropy into each
+// generation from the thread-local DRBGs. The hardware may provide a
+// low-latency RNG. (Intel's rdrand instruction is the canonical example of
+// this.) If available, that'll be used for this additional data. Otherwise
+// we'll draw from the OS if it doesn't provide other fork-detection APIs.
 //
 // (We assume that the OS entropy is safe from fork()ing and VM duplication.
 // This might be a bit of a leap of faith, esp on Windows, but there's nothing
 // that we can do about it.)
 
-// kReseedInterval is the number of generate calls made to CTR-DRBG before
-// reseeding.
-static const unsigned kReseedInterval = 4096;
+#if defined(BORINGSSL_FIPS)
+// This #if-block needs to be outside of the anonymous namespace:
 
-// CRNGT_BLOCK_SIZE is the number of bytes in a “block” for the purposes of the
-// continuous random number generator test in FIPS 140-2, section 4.9.2.
-#define CRNGT_BLOCK_SIZE 16
+struct core_drbg {
+  CTR_DRBG_STATE drbg;
+  // calls is the number of generate calls made on |drbg| since it was last
+  // (re)seeded. This is bound by |kCoreReseedInterval|.
+  uint64_t calls = 0;
+};
+
+DEFINE_BSS_GET(struct core_drbg *, g_core_drbg, = nullptr)
+DEFINE_STATIC_MUTEX(g_core_drbg_lock)
+#endif  // FIPS
 
 namespace {
+
+#if defined(BORINGSSL_FIPS)
+
+// The number of times we'll generate from the core DRBG before reseeding.
+// This is within the permitted bounds but so high that this effectively means
+// that we'll never reseed it. See SP 800-90Ar1 table 3.
+constexpr uint64_t kCoreReseedInterval = (UINT64_C(1) << 48) - 1;
+constexpr size_t kCoreDRBGEntropySize = 48;
+
+#if defined(OPENSSL_ANDROID)
+// Android seeds from a system daemon which is responsible for getting
+// entropy from a validated source.
+void core_drbg_get_entropy(uint8_t entropy[kCoreDRBGEntropySize]) {
+  uint8_t daemon_buffer[BSSL_ENTROPY_DAEMON_RESPONSE_LEN];
+  size_t daemon_buffer_len = sizeof(daemon_buffer);
+  if (!bssl_get_seed_from_daemon(daemon_buffer, &daemon_buffer_len)) {
+    // If the daemon isn't running then fallback to system entropy.
+    CRYPTO_sysrand(entropy, kCoreDRBGEntropySize);
+    return;
+  }
+  SHA384(daemon_buffer, daemon_buffer_len, entropy);
+}
+#else
+// Non-Android platforms use the jitter source.
+void core_drbg_get_entropy(uint8_t entropy[kCoreDRBGEntropySize]) {
+  // This runs the jitter source and takes some milliseconds.
+  for (unsigned i = 0; i < 100; i++) {
+    // The internal health checks of the jitter source may cause spurious
+    // failures, therefore we retry a number of times because failure
+    // to get entropy is fatal.
+    if (entropy::GetSeed(entropy)) {
+      return;
+    }
+  }
+  fprintf(stderr, "Persistent jitter source failure.\n");
+  BORINGSSL_FIPS_abort();
+}
+#endif
+
+struct core_drbg *core_drbg_get_locked() {
+  struct core_drbg **core = g_core_drbg_bss_get();
+  if (*core != nullptr) {
+    return *core;
+  }
+
+  uint8_t entropy[kCoreDRBGEntropySize];
+  core_drbg_get_entropy(entropy);
+
+  *core = New<core_drbg>();
+  if (!*core) {
+    BORINGSSL_FIPS_abort();
+  }
+
+  uint8_t nonce[CTR_DRBG_NONCE_LEN] = {0};
+  static constexpr char kPersonalization[] = "BoringSSL";
+  if (!CTR_DRBG_init(&(*core)->drbg, /*df=*/1, entropy, sizeof(entropy), nonce,
+                     reinterpret_cast<const uint8_t *>(kPersonalization),
+                     sizeof(kPersonalization))) {
+    BORINGSSL_FIPS_abort();
+  }
+
+  return *core;
+}
+
+// Draw a seed for a thread-local DRBG from the core DRBG.
+void core_drbg_draw_seed(uint8_t *out_seed, size_t len) {
+  MutexWriteLock lock(g_core_drbg_lock_bss_get());
+
+  struct core_drbg *const core = core_drbg_get_locked();
+  if (core->calls > kCoreReseedInterval) {
+    uint8_t reseed_entropy[kCoreDRBGEntropySize];
+    core_drbg_get_entropy(reseed_entropy);
+    if (!CTR_DRBG_reseed(&core->drbg, reseed_entropy,
+                         /*additional_data=*/nullptr, 0)) {
+      BORINGSSL_FIPS_abort();
+    }
+    core->calls = 0;
+  }
+
+  if (!CTR_DRBG_generate(&core->drbg, out_seed, len,
+                         /*additional_data=*/nullptr, 0)) {
+    BORINGSSL_FIPS_abort();
+  }
+  core->calls++;
+}
+
+#endif  // BORINGSSL_FIPS
+
+// kReseedInterval is the number of generate calls made to a thread-local
+// CTR-DRBG before reseeding.
+static const unsigned kReseedInterval = 4096;
+
 // rand_thread_state contains the per-thread state for the RNG.
 struct rand_thread_state {
   CTR_DRBG_STATE drbg;
@@ -65,16 +190,11 @@
   // calls is the number of generate calls made on |drbg| since it was last
   // (re)seeded. This is bound by |kReseedInterval|.
   unsigned calls;
-  // last_block_valid is non-zero iff |last_block| contains data from
-  // |get_seed_entropy|.
-  int last_block_valid;
   // fork_unsafe_buffering is non-zero iff, when |drbg| was last (re)seeded,
   // fork-unsafe buffering was enabled.
   int fork_unsafe_buffering;
 
 #if defined(BORINGSSL_FIPS)
-  // last_block contains the previous block from |get_seed_entropy|.
-  uint8_t last_block[CRNGT_BLOCK_SIZE];
   // next and prev form a nullptr-terminated, double-linked list of all states
   // in a process.
   struct rand_thread_state *next, *prev;
@@ -84,13 +204,14 @@
   Mutex clear_drbg_lock;
 #endif
 };
+
 }  // namespace
 
 #if defined(BORINGSSL_FIPS)
 // thread_states_list is the head of a linked-list of all |rand_thread_state|
 // objects in the process, one per thread. This is needed because FIPS requires
-// that they be zeroed on process exit, but thread-local destructors aren't
-// called when the whole process is exiting.
+// the ability to zero them on demand (AS09.28). BoringSSL triggers this with a
+// destructor function.
 DEFINE_BSS_GET(struct rand_thread_state *, thread_states_list, = nullptr)
 DEFINE_STATIC_MUTEX(thread_states_list_lock)
 
@@ -102,6 +223,13 @@
     cur->clear_drbg_lock.LockWrite();
     CTR_DRBG_clear(&cur->drbg);
   }
+
+  g_core_drbg_lock_bss_get()->LockWrite();
+  struct core_drbg **core = g_core_drbg_bss_get();
+  if (*core) {
+    CTR_DRBG_clear(&(*core)->drbg);
+  }
+
   // The locks are deliberately left locked so that any threads that are still
   // running will hang if they try to call |BCM_rand_bytes|. It also ensures
   // |rand_thread_state_free| cannot free any thread state while we've taken the
@@ -185,146 +313,26 @@
 
 #if defined(BORINGSSL_FIPS)
 
-// In passive entropy mode, entropy is supplied from outside of the module via
-// |BCM_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[CRNGT_BLOCK_SIZE + CTR_DRBG_SEED_LEN * BORINGSSL_FIPS_OVERREAD];
-  // bytes_valid indicates the number of bytes of |bytes| that contain valid
-  // data.
-  size_t bytes_valid;
-  // want_additional_input is true if any of the contents of |bytes| were
-  // obtained via a method other than from the kernel. In these cases entropy
-  // from the kernel is also provided via an additional input to the DRBG.
-  int want_additional_input;
-};
-
-DEFINE_BSS_GET(struct entropy_buffer, entropy_buffer, = {})
-DEFINE_STATIC_MUTEX(entropy_buffer_lock)
-
-bcm_infallible bssl::BCM_rand_load_entropy(const uint8_t *entropy,
-                                           size_t entropy_len,
-                                           int want_additional_input) {
-  struct entropy_buffer *const buffer = entropy_buffer_bss_get();
-
-  MutexWriteLock lock(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->want_additional_input |= want_additional_input && (entropy_len != 0);
-  return bcm_infallible::not_approved;
-}
-
-// 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_want_additional_input) {
-  struct entropy_buffer *const buffer = entropy_buffer_bss_get();
-  if (out_entropy_len > sizeof(buffer->bytes)) {
-    abort();
-  }
-
-  MutexWriteLock lock(entropy_buffer_lock_bss_get());
-  while (buffer->bytes_valid < out_entropy_len) {
-    MutexWriteUnlock unlock(entropy_buffer_lock_bss_get());
-    RAND_need_entropy(out_entropy_len - buffer->bytes_valid);
-  }
-
-  *out_want_additional_input = buffer->want_additional_input;
-  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->want_additional_input = 0;
-  }
-}
-
-// rand_get_seed fills |seed| with entropy. In some cases, it will additionally
-// fill |additional_input| with entropy to supplement |seed|. It sets
-// |*out_additional_input_len| to the number of extra bytes.
+// rand_get_seed fills |seed| with entropy. Since, in FIPS mode, this entropy
+// comes from the jitter source / system daemon, `additional_input` will also be
+// filled with system entropy.
 static void rand_get_seed(struct rand_thread_state *state,
-                          uint8_t seed[CTR_DRBG_SEED_LEN],
+                          uint8_t seed[CTR_DRBG_ENTROPY_LEN],
                           uint8_t additional_input[CTR_DRBG_SEED_LEN],
                           size_t *out_additional_input_len) {
-  uint8_t entropy_bytes[sizeof(state->last_block) +
-                        CTR_DRBG_SEED_LEN * BORINGSSL_FIPS_OVERREAD];
-  uint8_t *entropy = entropy_bytes;
-  size_t entropy_len = sizeof(entropy_bytes);
-
-  if (state->last_block_valid) {
-    // No need to fill |state->last_block| with entropy from the read.
-    entropy += sizeof(state->last_block);
-    entropy_len -= sizeof(state->last_block);
-  }
-
-  int want_additional_input;
-  get_seed_entropy(entropy, entropy_len, &want_additional_input);
-
-  if (!state->last_block_valid) {
-    OPENSSL_memcpy(state->last_block, entropy, sizeof(state->last_block));
-    entropy += sizeof(state->last_block);
-    entropy_len -= sizeof(state->last_block);
-  }
-
-  // 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
-  // rate of failure is small enough not to be a problem in practice.
-  if (CRYPTO_memcmp(state->last_block, entropy, sizeof(state->last_block)) ==
-      0) {
-    fprintf(CRYPTO_get_stderr(), "CRNGT failed.\n");
-    BORINGSSL_FIPS_abort();
-  }
-
-  assert(entropy_len % CRNGT_BLOCK_SIZE == 0);
-  for (size_t i = CRNGT_BLOCK_SIZE; i < entropy_len; i += CRNGT_BLOCK_SIZE) {
-    if (CRYPTO_memcmp(entropy + i - CRNGT_BLOCK_SIZE, entropy + i,
-                      CRNGT_BLOCK_SIZE) == 0) {
-      fprintf(CRYPTO_get_stderr(), "CRNGT failed.\n");
-      BORINGSSL_FIPS_abort();
-    }
-  }
-  OPENSSL_memcpy(state->last_block, entropy + entropy_len - CRNGT_BLOCK_SIZE,
-                 CRNGT_BLOCK_SIZE);
-
-  assert(entropy_len == BORINGSSL_FIPS_OVERREAD * CTR_DRBG_SEED_LEN);
-  OPENSSL_memcpy(seed, entropy, CTR_DRBG_SEED_LEN);
-
-  for (size_t i = 1; i < BORINGSSL_FIPS_OVERREAD; i++) {
-    for (size_t j = 0; j < CTR_DRBG_SEED_LEN; j++) {
-      seed[j] ^= entropy[CTR_DRBG_SEED_LEN * i + j];
-    }
-  }
-
-  // If we used something other than system entropy then also read from the
-  // system. This avoids solely relying on the hardware.
-  // TODO(crbug.com/446280903): Once this change sticks, switch
-  // |get_seed_entropy| to draw from the OS instead of RDRAND.
-  *out_additional_input_len = 0;
-  if (want_additional_input) {
-    CRYPTO_sysrand(additional_input, CTR_DRBG_SEED_LEN);
-    *out_additional_input_len = CTR_DRBG_SEED_LEN;
-  }
+  core_drbg_draw_seed(seed, CTR_DRBG_ENTROPY_LEN);
+  CRYPTO_sysrand(additional_input, CTR_DRBG_SEED_LEN);
+  *out_additional_input_len = CTR_DRBG_SEED_LEN;
 }
 
 #else
 
-// rand_get_seed fills |seed| with entropy. In some cases, it will additionally
-// fill |additional_input| with entropy to supplement |seed|. It sets
-// |*out_additional_input_len| to the number of extra bytes.
+// rand_get_seed fills |seed| with system entropy in a non-FIPS build.
 static void rand_get_seed(struct rand_thread_state *state,
-                          uint8_t seed[CTR_DRBG_SEED_LEN],
+                          uint8_t seed[CTR_DRBG_ENTROPY_LEN],
                           uint8_t additional_input[CTR_DRBG_SEED_LEN],
                           size_t *out_additional_input_len) {
-  // 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_SEED_LEN);
+  CRYPTO_sysrand(seed, CTR_DRBG_ENTROPY_LEN);
   *out_additional_input_len = 0;
 }
 
@@ -374,16 +382,18 @@
                                  rand_thread_state_free)) {
       // If the system is out of memory, use an ephemeral state on the
       // stack.
+      Delete(state);
       state = &stack_state;
     }
 
-    state->last_block_valid = 0;
-    uint8_t seed[CTR_DRBG_SEED_LEN];
+    uint8_t seed[CTR_DRBG_ENTROPY_LEN];
     uint8_t personalization[CTR_DRBG_SEED_LEN] = {0};
     size_t personalization_len = 0;
     rand_get_seed(state, seed, personalization, &personalization_len);
 
-    if (!CTR_DRBG_init(&state->drbg, /*df=*/true, seed, 32u, seed + 32,
+    if (!CTR_DRBG_init(&state->drbg, /*df=*/true, seed,
+                       sizeof(seed) - CTR_DRBG_NONCE_LEN,
+                       seed + sizeof(seed) - CTR_DRBG_NONCE_LEN,
                        personalization, personalization_len)) {
       abort();
     }
@@ -414,7 +424,7 @@
       // safety. The children must reseed to avoid working from the same PRNG
       // state.
       state->fork_unsafe_buffering != fork_unsafe_buffering) {
-    uint8_t seed[CTR_DRBG_SEED_LEN];
+    uint8_t seed[CTR_DRBG_ENTROPY_LEN];
     uint8_t reseed_additional_data[CTR_DRBG_SEED_LEN] = {0};
     size_t reseed_additional_data_len = 0;
     rand_get_seed(state, seed, reseed_additional_data,
@@ -474,9 +484,3 @@
   BCM_rand_bytes_with_additional_data(out, out_len, kZeroAdditionalData);
   return bcm_infallible::approved;
 }
-
-int RAND_maybe_reseed() {
-  // Currently does nothing since we don't use jitter entropy yet and so the
-  // reseeding is quick.
-  return 0;
-}
diff --git a/crypto/rand/passive.cc b/crypto/rand/passive.cc
index a791ec0..e69de29 100644
--- a/crypto/rand/passive.cc
+++ b/crypto/rand/passive.cc
@@ -1,180 +0,0 @@
-// Copyright 2020 The BoringSSL Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <openssl/ctrdrbg.h>
-
-#include "../bcm_support.h"
-#include "../fipsmodule/bcm_interface.h"
-#include "../internal.h"
-
-#if defined(BORINGSSL_FIPS)
-
-#include <atomic>
-
-using namespace bssl;
-
-// passive_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
-static void passive_get_seed_entropy(uint8_t *out_entropy,
-                                     size_t out_entropy_len,
-                                     int *out_want_additional_input) {
-  *out_want_additional_input = 0;
-  if (bcm_success(BCM_rand_bytes_hwrng(out_entropy, out_entropy_len))) {
-    *out_want_additional_input = 1;
-  } else {
-    CRYPTO_sysrand(out_entropy, out_entropy_len);
-  }
-}
-
-#define ENTROPY_READ_LEN \
-  (/* last_block size */ 16 + CTR_DRBG_SEED_LEN * BORINGSSL_FIPS_OVERREAD)
-
-#if defined(OPENSSL_ANDROID)
-
-#include <errno.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-// socket_history_t enumerates whether the entropy daemon should be contacted
-// for a given entropy request. Values other than socket_not_yet_attempted are
-// sticky so if the first attempt to read from the daemon fails it's assumed
-// that the daemon is not present and no more attempts will be made. If the
-// first attempt is successful then attempts will be made forever more.
-enum class socket_history_t {
-  // initial value, no connections to the entropy daemon have been made yet.
-  socket_not_yet_attempted = 0,
-  // reading from the entropy daemon was successful
-  socket_success,
-  // reading from the entropy daemon failed.
-  socket_failed,
-};
-
-static std::atomic<socket_history_t> g_socket_history{
-    socket_history_t::socket_not_yet_attempted};
-
-// DAEMON_RESPONSE_LEN is the number of bytes that the entropy daemon replies
-// with.
-#define DAEMON_RESPONSE_LEN 496
-
-static_assert(ENTROPY_READ_LEN == DAEMON_RESPONSE_LEN,
-              "entropy daemon response length mismatch");
-
-static int get_seed_from_daemon(uint8_t *out_entropy, size_t out_entropy_len) {
-  // |RAND_need_entropy| should never call this function for more than
-  // |DAEMON_RESPONSE_LEN| bytes.
-  if (out_entropy_len > DAEMON_RESPONSE_LEN) {
-    abort();
-  }
-
-  const socket_history_t socket_history =
-      g_socket_history.load(std::memory_order_acquire);
-  if (socket_history == socket_history_t::socket_failed) {
-    return 0;
-  }
-
-  int ret = 0;
-  static const char kSocketPath[] = "/dev/socket/prng_seeder";
-  struct sockaddr_un sun;
-  uint8_t buffer[DAEMON_RESPONSE_LEN];
-  size_t done = 0;
-  const int sock = socket(AF_UNIX, SOCK_STREAM, 0);
-  if (sock < 0) {
-    goto out;
-  }
-
-  memset(&sun, 0, sizeof(sun));
-  sun.sun_family = AF_UNIX;
-  static_assert(sizeof(kSocketPath) <= UNIX_PATH_MAX, "kSocketPath too long");
-  OPENSSL_memcpy(sun.sun_path, kSocketPath, sizeof(kSocketPath));
-
-  if (connect(sock, (struct sockaddr *)&sun, sizeof(sun))) {
-    goto out;
-  }
-
-  while (done < sizeof(buffer)) {
-    ssize_t n;
-    do {
-      n = read(sock, buffer + done, sizeof(buffer) - done);
-    } while (n == -1 && errno == EINTR);
-
-    if (n < 1) {
-      goto out;
-    }
-    done += n;
-  }
-
-  if (done != DAEMON_RESPONSE_LEN) {
-    // The daemon should always write |DAEMON_RESPONSE_LEN| bytes on every
-    // connection.
-    goto out;
-  }
-
-  assert(out_entropy_len <= DAEMON_RESPONSE_LEN);
-  OPENSSL_memcpy(out_entropy, buffer, out_entropy_len);
-  ret = 1;
-
-out:
-  if (socket_history == socket_history_t::socket_not_yet_attempted) {
-    socket_history_t expected = socket_history_t::socket_not_yet_attempted;
-    // If another thread has already updated |g_socket_history| then we defer
-    // to their value.
-    g_socket_history.compare_exchange_strong(
-        expected,
-        (ret == 0) ? socket_history_t::socket_failed
-                   : socket_history_t::socket_success,
-        std::memory_order_release, std::memory_order_relaxed);
-  }
-
-  close(sock);
-  return ret;
-}
-
-#else
-
-static int get_seed_from_daemon(uint8_t *out_entropy, size_t out_entropy_len) {
-  return 0;
-}
-
-#endif  // OPENSSL_ANDROID
-
-// 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 bssl::RAND_need_entropy(size_t bytes_needed) {
-  uint8_t buf[ENTROPY_READ_LEN];
-  size_t todo = sizeof(buf);
-  if (todo > bytes_needed) {
-    todo = bytes_needed;
-  }
-
-  int want_additional_input;
-  if (get_seed_from_daemon(buf, todo)) {
-    want_additional_input = 1;
-  } else {
-    passive_get_seed_entropy(buf, todo, &want_additional_input);
-  }
-
-  if (boringssl_fips_break_test("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, todo);
-  }
-
-  BCM_rand_load_entropy(buf, todo, want_additional_input);
-}
-
-#endif  // FIPS
diff --git a/crypto/rand/urandom_test.cc b/crypto/rand/urandom_test.cc
index f4044bf..5e9cdea 100644
--- a/crypto/rand/urandom_test.cc
+++ b/crypto/rand/urandom_test.cc
@@ -640,10 +640,10 @@
 // |GetTrace| will observe the real code making.
 static std::vector<Event> TestFunctionPRNGModel(unsigned flags) {
   std::vector<Event> ret;
-  bool used_daemon = false;
 
-  if (have_fork_detection()) {
-    used_daemon = kUsesDaemon && AppendDaemonEvents(&ret, flags);
+  bool daemon_failed = false;
+  if (have_fork_detection() && kUsesDaemon) {
+    daemon_failed = !AppendDaemonEvents(&ret, flags);
   }
 
   // Probe for getrandom support
@@ -683,8 +683,10 @@
     };
   }
 
-  const size_t kSeedLength =
-      CTR_DRBG_SEED_LEN * (kIsFIPS ? 10 : 1) + (kIsFIPS ? 16 : 0);
+  if (daemon_failed && !sysrand(48)) {
+    return ret;
+  }
+
   const size_t kAdditionalDataLength = 32;
 
   if (!have_rdrand()) {
@@ -692,12 +694,16 @@
       if (!sysrand(kAdditionalDataLength)) {
         return ret;
       }
-      used_daemon = kUsesDaemon && AppendDaemonEvents(&ret, flags);
+      if (kUsesDaemon && !AppendDaemonEvents(&ret, flags) &&
+          !sysrand(48)) {
+        return ret;
+      }
     }
-    if (// Initialise CRNGT. If the daemon is used, the OS is used for the
-        // personalisation data of length |CTR_DRBG_SEED_LEN|. Otherwise, it
-        // used for the seed itself, including FIPS overread.
-        !sysrand(used_daemon ? CTR_DRBG_SEED_LEN : kSeedLength) ||
+    if (  // Initialise thread-local DRBG. In FIPS mode, the jitter source
+          // will be used, but we cannot observe that. Either way,
+          // CTR_DRBG_SEED_LEN will be drawn from sysrand, either as the
+          // additional data in FIPS mode, or as the seed otherwise.
+        !sysrand(CTR_DRBG_SEED_LEN) ||
         // Second additional data, when other fork-safety measures have failed.
         (!have_fork_detection() && !sysrand(kAdditionalDataLength))) {
       return ret;
diff --git a/gen/sources.bzl b/gen/sources.bzl
index f2f2b34..8b57aa8 100644
--- a/gen/sources.bzl
+++ b/gen/sources.bzl
@@ -83,6 +83,7 @@
     "crypto/fipsmodule/mldsa/mldsa.cc.inc",
     "crypto/fipsmodule/mlkem/fips_known_values.inc",
     "crypto/fipsmodule/mlkem/mlkem.cc.inc",
+    "crypto/fipsmodule/rand/android_entropy_client.cc.inc",
     "crypto/fipsmodule/rand/ctrdrbg.cc.inc",
     "crypto/fipsmodule/rand/rand.cc.inc",
     "crypto/fipsmodule/rsa/padding.cc.inc",
@@ -436,7 +437,6 @@
     "crypto/rand/forkunsafe.cc",
     "crypto/rand/getentropy.cc",
     "crypto/rand/ios.cc",
-    "crypto/rand/passive.cc",
     "crypto/rand/rand.cc",
     "crypto/rand/trusty.cc",
     "crypto/rand/urandom.cc",
diff --git a/gen/sources.cmake b/gen/sources.cmake
index 4b886f6..9acba30 100644
--- a/gen/sources.cmake
+++ b/gen/sources.cmake
@@ -87,6 +87,7 @@
   crypto/fipsmodule/mldsa/mldsa.cc.inc
   crypto/fipsmodule/mlkem/fips_known_values.inc
   crypto/fipsmodule/mlkem/mlkem.cc.inc
+  crypto/fipsmodule/rand/android_entropy_client.cc.inc
   crypto/fipsmodule/rand/ctrdrbg.cc.inc
   crypto/fipsmodule/rand/rand.cc.inc
   crypto/fipsmodule/rsa/padding.cc.inc
@@ -454,7 +455,6 @@
   crypto/rand/forkunsafe.cc
   crypto/rand/getentropy.cc
   crypto/rand/ios.cc
-  crypto/rand/passive.cc
   crypto/rand/rand.cc
   crypto/rand/trusty.cc
   crypto/rand/urandom.cc
diff --git a/gen/sources.gni b/gen/sources.gni
index 3cd5a70..8b13bb5 100644
--- a/gen/sources.gni
+++ b/gen/sources.gni
@@ -83,6 +83,7 @@
   "crypto/fipsmodule/mldsa/mldsa.cc.inc",
   "crypto/fipsmodule/mlkem/fips_known_values.inc",
   "crypto/fipsmodule/mlkem/mlkem.cc.inc",
+  "crypto/fipsmodule/rand/android_entropy_client.cc.inc",
   "crypto/fipsmodule/rand/ctrdrbg.cc.inc",
   "crypto/fipsmodule/rand/rand.cc.inc",
   "crypto/fipsmodule/rsa/padding.cc.inc",
@@ -436,7 +437,6 @@
   "crypto/rand/forkunsafe.cc",
   "crypto/rand/getentropy.cc",
   "crypto/rand/ios.cc",
-  "crypto/rand/passive.cc",
   "crypto/rand/rand.cc",
   "crypto/rand/trusty.cc",
   "crypto/rand/urandom.cc",
diff --git a/gen/sources.json b/gen/sources.json
index b5c9697..92e375e 100644
--- a/gen/sources.json
+++ b/gen/sources.json
@@ -68,6 +68,7 @@
       "crypto/fipsmodule/mldsa/mldsa.cc.inc",
       "crypto/fipsmodule/mlkem/fips_known_values.inc",
       "crypto/fipsmodule/mlkem/mlkem.cc.inc",
+      "crypto/fipsmodule/rand/android_entropy_client.cc.inc",
       "crypto/fipsmodule/rand/ctrdrbg.cc.inc",
       "crypto/fipsmodule/rand/rand.cc.inc",
       "crypto/fipsmodule/rsa/padding.cc.inc",
@@ -420,7 +421,6 @@
       "crypto/rand/forkunsafe.cc",
       "crypto/rand/getentropy.cc",
       "crypto/rand/ios.cc",
-      "crypto/rand/passive.cc",
       "crypto/rand/rand.cc",
       "crypto/rand/trusty.cc",
       "crypto/rand/urandom.cc",
diff --git a/gen/sources.mk b/gen/sources.mk
index 3e85ae6..fa0b051 100644
--- a/gen/sources.mk
+++ b/gen/sources.mk
@@ -82,6 +82,7 @@
   crypto/fipsmodule/mldsa/mldsa.cc.inc \
   crypto/fipsmodule/mlkem/fips_known_values.inc \
   crypto/fipsmodule/mlkem/mlkem.cc.inc \
+  crypto/fipsmodule/rand/android_entropy_client.cc.inc \
   crypto/fipsmodule/rand/ctrdrbg.cc.inc \
   crypto/fipsmodule/rand/rand.cc.inc \
   crypto/fipsmodule/rsa/padding.cc.inc \
@@ -428,7 +429,6 @@
   crypto/rand/forkunsafe.cc \
   crypto/rand/getentropy.cc \
   crypto/rand/ios.cc \
-  crypto/rand/passive.cc \
   crypto/rand/rand.cc \
   crypto/rand/trusty.cc \
   crypto/rand/urandom.cc \
diff --git a/include/openssl/prefix_symbols.h b/include/openssl/prefix_symbols.h
index 4d970a3..5b4bace 100644
--- a/include/openssl/prefix_symbols.h
+++ b/include/openssl/prefix_symbols.h
@@ -1715,7 +1715,6 @@
 #pragma redefine_extname RAND_get_rand_method BORINGSSL_ADD_USER_LABEL_AND_PREFIX(RAND_get_rand_method)
 #pragma redefine_extname RAND_get_system_entropy_for_custom_prng BORINGSSL_ADD_USER_LABEL_AND_PREFIX(RAND_get_system_entropy_for_custom_prng)
 #pragma redefine_extname RAND_load_file BORINGSSL_ADD_USER_LABEL_AND_PREFIX(RAND_load_file)
-#pragma redefine_extname RAND_maybe_reseed BORINGSSL_ADD_USER_LABEL_AND_PREFIX(RAND_maybe_reseed)
 #pragma redefine_extname RAND_poll BORINGSSL_ADD_USER_LABEL_AND_PREFIX(RAND_poll)
 #pragma redefine_extname RAND_pseudo_bytes BORINGSSL_ADD_USER_LABEL_AND_PREFIX(RAND_pseudo_bytes)
 #pragma redefine_extname RAND_reset_for_fuzzing BORINGSSL_ADD_USER_LABEL_AND_PREFIX(RAND_reset_for_fuzzing)
@@ -4830,7 +4829,6 @@
 #define RAND_get_rand_method BORINGSSL_ADD_PREFIX(RAND_get_rand_method)
 #define RAND_get_system_entropy_for_custom_prng BORINGSSL_ADD_PREFIX(RAND_get_system_entropy_for_custom_prng)
 #define RAND_load_file BORINGSSL_ADD_PREFIX(RAND_load_file)
-#define RAND_maybe_reseed BORINGSSL_ADD_PREFIX(RAND_maybe_reseed)
 #define RAND_poll BORINGSSL_ADD_PREFIX(RAND_poll)
 #define RAND_pseudo_bytes BORINGSSL_ADD_PREFIX(RAND_pseudo_bytes)
 #define RAND_reset_for_fuzzing BORINGSSL_ADD_PREFIX(RAND_reset_for_fuzzing)
diff --git a/include/openssl/rand.h b/include/openssl/rand.h
index 9d66a89..1cea36d 100644
--- a/include/openssl/rand.h
+++ b/include/openssl/rand.h
@@ -75,17 +75,6 @@
 OPENSSL_EXPORT void RAND_get_system_entropy_for_custom_prng(uint8_t *buf,
                                                             size_t len);
 
-// RAND_maybe_reseed might reseed the PRNG if it's getting close to the reseed
-// limit. If it does so, it may briefly block other threads that are
-// concurrently calling `RAND_bytes`, but only for ~microseconds. Applications
-// may wish to periodically call this function to avoid hitting a reseed while
-// servicing a `RAND_bytes` call, which could happen from anywhere and take
-// milliseconds or more in FIPS configurations. _Most_ applications, however,
-// should ignore this and it only makes a difference in FIPS builds.
-//
-// Returns one if a reseed was performed and zero otherwise.
-OPENSSL_EXPORT int RAND_maybe_reseed(void);
-
 
 // Deprecated functions
 
diff --git a/util/fipstools/break-kat.go b/util/fipstools/break-kat.go
index 3304e9e..170f3f1 100644
--- a/util/fipstools/break-kat.go
+++ b/util/fipstools/break-kat.go
@@ -118,7 +118,7 @@
 		os.Remove(outFile)
 	}
 
-	for _, test := range []string{"ECDSA_PWCT", "RSA_PWCT", "MLDSA_PWCT", "MLKEM_PWCT", "SLHDSA_PWCT", "CRNG"} {
+	for _, test := range []string{"ECDSA_PWCT", "RSA_PWCT", "MLDSA_PWCT", "MLKEM_PWCT", "SLHDSA_PWCT"} {
 		fmt.Printf("\n### Running test for %q\n\n", test)
 
 		cmd := exec.Command("./" + inPath)
diff --git a/util/fipstools/break-tests.sh b/util/fipstools/break-tests.sh
index 111f120..ea7a1a0 100644
--- a/util/fipstools/break-tests.sh
+++ b/util/fipstools/break-tests.sh
@@ -192,7 +192,7 @@
 
 if [ "$MODE" = "local" ]; then
   # TODO(prb): add support for Android devices.
-  for runtime_test in ECDSA_PWCT RSA_PWCT CRNG; do
+  for runtime_test in ECDSA_PWCT RSA_PWCT; do
     echo
     echo -e "\033[1m${runtime_test} failure\033[0m"
     $RUNTIME_BREAK_TEST ${runtime_test}