Require getrandom in all FIPS builds.
It is now 2022. See if we can assume getrandom in this configuration.
Update-Note: The /dev/urandom fallback is no longer available in FIPS
builds. This fallback relied on RNGGETENTCNT and was quite flaky.
Change-Id: Icf6d29f6d5952fb6c5656c9039a4cfaf1de2d724
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/54127
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/fipsmodule/rand/urandom.c b/crypto/fipsmodule/rand/urandom.c
index 11f1b5b..77dad74 100644
--- a/crypto/fipsmodule/rand/urandom.c
+++ b/crypto/fipsmodule/rand/urandom.c
@@ -62,14 +62,11 @@
#include <sys/random.h>
#endif
-#if defined(OPENSSL_FREEBSD)
-#define URANDOM_BLOCKS_FOR_ENTROPY
-#if __FreeBSD__ >= 12
+#if defined(OPENSSL_FREEBSD) && __FreeBSD__ >= 12
// getrandom is supported in FreeBSD 12 and up.
#define FREEBSD_GETRANDOM
#include <sys/random.h>
#endif
-#endif
#include <openssl/thread.h>
#include <openssl/mem.h>
@@ -190,8 +187,14 @@
return;
#endif
- // Android FIPS builds must support getrandom.
-#if defined(BORINGSSL_FIPS) && defined(OPENSSL_ANDROID)
+ // FIPS builds must support getrandom.
+ //
+ // Historically, only Android FIPS builds required getrandom, while Linux FIPS
+ // builds had a /dev/urandom fallback which used RNDGETENTCNT as a poor
+ // approximation for getrandom's blocking behavior. This is now removed, but
+ // avoid making assumptions on this removal until March 2023, in case it needs
+ // to be restored. This comment can be deleted after March 2023.
+#if defined(BORINGSSL_FIPS)
perror("getrandom not found");
abort();
#endif
@@ -255,29 +258,6 @@
#endif // USE_NR_getrandom
return;
}
-
-#if defined(BORINGSSL_FIPS) && !defined(URANDOM_BLOCKS_FOR_ENTROPY)
- // In FIPS mode on platforms where urandom doesn't block at startup, we ensure
- // that the kernel has sufficient entropy before continuing. This is
- // automatically handled by getrandom, which requires that the entropy pool
- // has been initialised, but for urandom we have to poll.
- for (;;) {
- int entropy_bits;
- if (ioctl(fd, RNDGETENTCNT, &entropy_bits)) {
- fprintf(stderr,
- "RNDGETENTCNT on /dev/urandom failed. We cannot continue in this "
- "case when in FIPS mode.\n");
- abort();
- }
-
- static const int kBitsNeeded = 256;
- if (entropy_bits >= kBitsNeeded) {
- break;
- }
-
- usleep(250000);
- }
-#endif // BORINGSSL_FIPS && !URANDOM_BLOCKS_FOR_ENTROPY
}
// fill_with_entropy writes |len| bytes of entropy into |out|. It returns one
diff --git a/crypto/fipsmodule/rand/urandom_test.cc b/crypto/fipsmodule/rand/urandom_test.cc
index dabaf39..13ac610 100644
--- a/crypto/fipsmodule/rand/urandom_test.cc
+++ b/crypto/fipsmodule/rand/urandom_test.cc
@@ -45,7 +45,6 @@
kGetRandom,
kOpen,
kUrandomRead,
- kUrandomIoctl,
kAbort,
};
@@ -77,11 +76,6 @@
return e;
}
- static Event UrandomIoctl() {
- Event e(Syscall::kUrandomIoctl);
- return e;
- }
-
static Event Abort() {
Event e(Syscall::kAbort);
return e;
@@ -103,9 +97,6 @@
snprintf(buf, sizeof(buf), "read(urandom_fd, _, %zu)", length);
break;
- case Syscall::kUrandomIoctl:
- return "ioctl(urandom_fd, RNDGETENTCNT, _)";
-
case Syscall::kAbort:
return "abort()";
}
@@ -139,14 +130,11 @@
static const unsigned NO_URANDOM = 2;
// getrandom always returns |EAGAIN| if given |GRNG_NONBLOCK|.
static const unsigned GETRANDOM_NOT_READY = 4;
-// The ioctl on urandom returns only 255 bits of entropy the first time that
-// it's called.
-static const unsigned URANDOM_NOT_READY = 8;
// getrandom gives |EINVAL| unless |NO_GETRANDOM| is set.
-static const unsigned GETRANDOM_ERROR = 16;
+static const unsigned GETRANDOM_ERROR = 8;
// Reading from /dev/urandom gives |EINVAL|.
-static const unsigned URANDOM_ERROR = 32;
-static const unsigned NEXT_FLAG = 64;
+static const unsigned URANDOM_ERROR = 16;
+static const unsigned NEXT_FLAG = 32;
// GetTrace runs |thunk| in a forked process and observes the resulting system
// calls using ptrace. It simulates a variety of failures based on the contents
@@ -204,8 +192,6 @@
const auto syscall_number = regs.orig_rax;
bool is_opening_urandom = false;
- bool is_urandom_ioctl = false;
- uintptr_t ioctl_output_addr = 0;
// inject_error is zero to indicate that the system call should run
// normally. Otherwise it's, e.g. -EINVAL, to indicate that the system call
// should not run and that error should be injected on return.
@@ -251,16 +237,6 @@
}
break;
}
-
- case __NR_ioctl: {
- const int ioctl_fd = regs.rdi;
- if (urandom_fd >= 0 && ioctl_fd == urandom_fd &&
- regs.rsi == RNDGETENTCNT) {
- out_trace->push_back(Event::UrandomIoctl());
- is_urandom_ioctl = true;
- ioctl_output_addr = regs.rdx;
- }
- }
}
if (inject_error) {
@@ -294,33 +270,6 @@
} else if (is_opening_urandom) {
ASSERT_EQ(0, ptrace(PTRACE_GETREGS, child_pid, nullptr, ®s));
urandom_fd = regs.rax;
- } else if (is_urandom_ioctl) {
- // The result is the number of bits of entropy that the kernel currently
- // believes that it has. urandom.c waits until 256 bits are ready.
- int result = 256;
-
- // If we are simulating urandom not being ready then we have the ioctl
- // indicate one too few bits of entropy the first time it's queried.
- if (flags & URANDOM_NOT_READY) {
- result--;
- flags &= ~URANDOM_NOT_READY;
- }
-
- // ptrace always works with ill-defined "words", which appear to be 64-bit
- // on x86-64. Since the ioctl result is a 32-bit int, do a
- // read-modify-write to inject the answer.
- const uintptr_t aligned_addr = ioctl_output_addr & ~7;
- const uintptr_t offset = ioctl_output_addr - aligned_addr;
- union {
- uint64_t word;
- uint8_t bytes[8];
- } u;
- u.word = ptrace(PTRACE_PEEKDATA, child_pid,
- reinterpret_cast<void *>(aligned_addr), nullptr);
- memcpy(&u.bytes[offset], &result, sizeof(result));
- ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, child_pid,
- reinterpret_cast<void *>(aligned_addr),
- reinterpret_cast<void *>(u.word)));
}
}
}
@@ -347,7 +296,6 @@
#endif
std::vector<Event> ret;
- bool urandom_probed = false;
bool getrandom_ready = false;
// Probe for getrandom support
@@ -356,32 +304,19 @@
std::function<bool(bool, size_t)> sysrand;
if (flags & NO_GETRANDOM) {
+ if (is_fips) {
+ // FIPS builds require getrandom.
+ ret.push_back(Event::Abort());
+ return ret;
+ }
+
ret.push_back(Event::Open("/dev/urandom"));
if (flags & NO_URANDOM) {
ret.push_back(Event::Abort());
return ret;
}
- wait_for_entropy = [&ret, &urandom_probed, flags] {
- if (!is_fips || urandom_probed) {
- return;
- }
-
- // Probe urandom for entropy.
- ret.push_back(Event::UrandomIoctl());
- if (flags & URANDOM_NOT_READY) {
- // If the first attempt doesn't report enough entropy, probe
- // repeatedly until it does, which will happen with the second attempt.
- ret.push_back(Event::UrandomIoctl());
- }
-
- urandom_probed = true;
- };
-
- sysrand = [&ret, &wait_for_entropy, flags](bool block, size_t len) {
- if (block) {
- wait_for_entropy();
- }
+ sysrand = [&ret, flags](bool block, size_t len) {
ret.push_back(Event::UrandomRead(len));
if (flags & URANDOM_ERROR) {
ret.push_back(Event::Abort());
@@ -457,11 +392,6 @@
}
break;
- case Event::Syscall::kUrandomIoctl:
- ADD_FAILURE() << "Urandom polling found with RDRAND: "
- << ToString(events);
- break;
-
default:
break;
}
@@ -483,7 +413,6 @@
TRACE_FLAG(NO_GETRANDOM);
TRACE_FLAG(NO_URANDOM);
TRACE_FLAG(GETRANDOM_NOT_READY);
- TRACE_FLAG(URANDOM_NOT_READY);
TRACE_FLAG(GETRANDOM_ERROR);
TRACE_FLAG(URANDOM_ERROR);