summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Thierer <tobiast@google.com>2019-10-19 13:25:11 -0700
committerandroid-build-merger <android-build-merger@google.com>2019-10-19 13:25:11 -0700
commitaff937c24d3ee49d29b0804038f874d80a938979 (patch)
tree1b54a52ae8096564b711849bb27210da83c8eb6e
parent002011a51c152b350a2dd22e343f8e32771a4c8a (diff)
parent6204b54fdd49dfcf1f8e19161e00fb556ee78c4d (diff)
downloadboringssl-aff937c24d3ee49d29b0804038f874d80a938979.tar.gz
external/boringssl: Sync to da8caf5b1029b93d482702759058ac993a39bcc5.
am: 6204b54fdd Change-Id: I7cfe610ced26c166e513c418d2a014598fb514b9
-rw-r--r--Android.bp23
-rw-r--r--BORINGSSL_REVISION2
-rw-r--r--src/crypto/CMakeLists.txt14
-rw-r--r--src/crypto/cpu-intel.c10
-rw-r--r--src/crypto/dsa/dsa.c4
-rw-r--r--src/crypto/fipsmodule/bcm.c38
-rw-r--r--src/crypto/fipsmodule/bn/prime.c90
-rw-r--r--src/crypto/fipsmodule/fips_shared.lds6
-rw-r--r--src/crypto/fipsmodule/rand/urandom_test.cc471
-rw-r--r--src/ssl/tls13_client.cc12
-rw-r--r--src/ssl/tls13_server.cc6
-rw-r--r--src/third_party/sike/utils.h2
-rw-r--r--src/util/fipstools/break-hash.go6
-rw-r--r--src/util/fipstools/delocate/delocate.go29
-rw-r--r--src/util/fipstools/delocate/testdata/x86_64-GOTRewrite/in.s3
-rw-r--r--src/util/fipstools/delocate/testdata/x86_64-GOTRewrite/out.s15
-rw-r--r--src/util/generate_build_files.py13
17 files changed, 639 insertions, 105 deletions
diff --git a/Android.bp b/Android.bp
index e9095c40..adb238fb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -91,6 +91,7 @@ cc_object {
],
sanitize: {
address: false,
+ hwaddress: false,
},
target: {
android: {
@@ -336,15 +337,15 @@ cc_test {
// Utility binary for CMVP on-site testing.
cc_binary {
- name: "test_fips",
- host_supported: false,
- defaults: [
- "boringssl_flags",
- ],
- shared_libs: [
- "libcrypto",
- ],
- srcs: [
- "src/util/fipstools/cavp/test_fips.c",
- ],
+ name: "test_fips",
+ host_supported: false,
+ defaults: [
+ "boringssl_flags",
+ ],
+ shared_libs: [
+ "libcrypto",
+ ],
+ srcs: [
+ "src/util/fipstools/cavp/test_fips.c",
+ ],
}
diff --git a/BORINGSSL_REVISION b/BORINGSSL_REVISION
index 6661f8e4..3b00600a 100644
--- a/BORINGSSL_REVISION
+++ b/BORINGSSL_REVISION
@@ -1 +1 @@
-a7a75f208caea8a303615724d4cc5f4e8dfb9695
+da8caf5b1029b93d482702759058ac993a39bcc5
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index d84e5457..f3e2ca47 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -462,6 +462,20 @@ if(USE_CUSTOM_LIBCXX)
target_link_libraries(crypto libcxx)
endif()
+# urandom_test is a separate binary because it needs to be able to observe the
+# PRNG initialisation, which means that it can't have other tests running before
+# it does.
+add_executable(
+ urandom_test
+
+ fipsmodule/rand/urandom_test.cc
+)
+
+target_link_libraries(urandom_test test_support_lib boringssl_gtest crypto)
+
+add_dependencies(urandom_test global_target)
+add_dependencies(all_tests urandom_test)
+
add_executable(
crypto_test
diff --git a/src/crypto/cpu-intel.c b/src/crypto/cpu-intel.c
index 832e9d6d..cc41fc47 100644
--- a/src/crypto/cpu-intel.c
+++ b/src/crypto/cpu-intel.c
@@ -123,9 +123,17 @@ static uint64_t OPENSSL_xgetbv(uint32_t xcr) {
// and |out[1]|. See the comment in |OPENSSL_cpuid_setup| about this.
static void handle_cpu_env(uint32_t *out, const char *in) {
const int invert = in[0] == '~';
+ const int hex = in[invert] == '0' && in[invert+1] == 'x';
+
+ int sscanf_result;
uint64_t v;
+ if (hex) {
+ sscanf_result = sscanf(in + invert + 2, "%" PRIx64, &v);
+ } else {
+ sscanf_result = sscanf(in + invert, "%" PRIu64, &v);
+ }
- if (!sscanf(in + invert, "%" PRIu64, &v)) {
+ if (!sscanf_result) {
return;
}
diff --git a/src/crypto/dsa/dsa.c b/src/crypto/dsa/dsa.c
index cb419c81..cc98225d 100644
--- a/src/crypto/dsa/dsa.c
+++ b/src/crypto/dsa/dsa.c
@@ -256,7 +256,7 @@ int DSA_generate_parameters_ex(DSA *dsa, unsigned bits, const uint8_t *seed_in,
// Find q.
for (;;) {
// step 1
- if (!BN_GENCB_call(cb, 0, m++)) {
+ if (!BN_GENCB_call(cb, BN_GENCB_GENERATED, m++)) {
goto err;
}
@@ -319,7 +319,7 @@ int DSA_generate_parameters_ex(DSA *dsa, unsigned bits, const uint8_t *seed_in,
n = (bits - 1) / 160;
for (;;) {
- if ((counter != 0) && !BN_GENCB_call(cb, 0, counter)) {
+ if ((counter != 0) && !BN_GENCB_call(cb, BN_GENCB_GENERATED, counter)) {
goto err;
}
diff --git a/src/crypto/fipsmodule/bcm.c b/src/crypto/fipsmodule/bcm.c
index f3f66f4b..91b91701 100644
--- a/src/crypto/fipsmodule/bcm.c
+++ b/src/crypto/fipsmodule/bcm.c
@@ -115,6 +115,26 @@ extern const uint8_t BORINGSSL_bcm_rodata_start[];
extern const uint8_t BORINGSSL_bcm_rodata_end[];
#endif
+// assert_within is used to sanity check that certain symbols are within the
+// bounds of the integrity check. It checks that start <= symbol < end and
+// aborts otherwise.
+static void assert_within(const void *start, const void *symbol,
+ const void *end) {
+ const uintptr_t start_val = (uintptr_t) start;
+ const uintptr_t symbol_val = (uintptr_t) symbol;
+ const uintptr_t end_val = (uintptr_t) end;
+
+ if (start_val <= symbol_val && symbol_val < end_val) {
+ return;
+ }
+
+ fprintf(
+ stderr,
+ "FIPS module doesn't span expected symbol. Expected %p <= %p < %p\n",
+ start, symbol, end);
+ BORINGSSL_FIPS_abort();
+}
+
#if defined(OPENSSL_ANDROID) && defined(OPENSSL_AARCH64)
static void BORINGSSL_maybe_set_module_text_permissions(int permission) {
// Android may be compiled in execute-only-memory mode, in which case the
@@ -147,11 +167,29 @@ BORINGSSL_bcm_power_on_self_test(void) {
// .text section, which triggers the global-buffer overflow detection.
const uint8_t *const start = BORINGSSL_bcm_text_start;
const uint8_t *const end = BORINGSSL_bcm_text_end;
+
+ assert_within(start, AES_encrypt, end);
+ assert_within(start, RSA_sign, end);
+ assert_within(start, RAND_bytes, end);
+ assert_within(start, EC_GROUP_cmp, end);
+ assert_within(start, SHA256_Update, end);
+ assert_within(start, ECDSA_do_verify, end);
+ assert_within(start, EVP_AEAD_CTX_seal, end);
+
#if defined(BORINGSSL_SHARED_LIBRARY)
const uint8_t *const rodata_start = BORINGSSL_bcm_rodata_start;
const uint8_t *const rodata_end = BORINGSSL_bcm_rodata_end;
+#else
+ // In the static build, read-only data is placed within the .text segment.
+ const uint8_t *const rodata_start = BORINGSSL_bcm_text_start;
+ const uint8_t *const rodata_end = BORINGSSL_bcm_text_end;
#endif
+ assert_within(rodata_start, kPrimes, rodata_end);
+ assert_within(rodata_start, des_skb, rodata_end);
+ assert_within(rodata_start, kP256Params, rodata_end);
+ assert_within(rodata_start, kPKCS1SigPrefixes, rodata_end);
+
#if defined(OPENSSL_ANDROID)
uint8_t result[SHA256_DIGEST_LENGTH];
const EVP_MD *const kHashFunction = EVP_sha256();
diff --git a/src/crypto/fipsmodule/bn/prime.c b/src/crypto/fipsmodule/bn/prime.c
index 9df4f95c..c0c795b1 100644
--- a/src/crypto/fipsmodule/bn/prime.c
+++ b/src/crypto/fipsmodule/bn/prime.c
@@ -443,6 +443,11 @@ loop:
goto err;
}
+ // Interleave |ret| and |t|'s primality tests to avoid paying the full
+ // iteration count on |ret| only to quickly discover |t| is composite.
+ //
+ // TODO(davidben): This doesn't quite work because an iteration count of 1
+ // still runs the blinding mechanism.
for (i = 0; i < checks; i++) {
j = BN_is_prime_fasttest_ex(ret, 1, ctx, 0, NULL);
if (j == -1) {
@@ -458,7 +463,7 @@ loop:
goto loop;
}
- if (!BN_GENCB_call(cb, i, c1 - 1)) {
+ if (!BN_GENCB_call(cb, BN_GENCB_PRIME_TEST, i)) {
goto err;
}
// We have a safe prime test pass
@@ -669,7 +674,7 @@ int BN_primality_test(int *out_is_probably_prime, const BIGNUM *w,
*out_is_probably_prime = BN_is_word(w, prime);
return 1;
}
- if (!BN_GENCB_call(cb, 1, -1)) {
+ if (!BN_GENCB_call(cb, BN_GENCB_PRIME_TEST, -1)) {
return 0;
}
}
@@ -755,7 +760,7 @@ int BN_primality_test(int *out_is_probably_prime, const BIGNUM *w,
}
// Step 4.7
- if (!BN_GENCB_call(cb, 1, i)) {
+ if (!BN_GENCB_call(cb, BN_GENCB_PRIME_TEST, i - 1)) {
goto err;
}
}
@@ -910,7 +915,7 @@ int BN_enhanced_miller_rabin_primality_test(
loop:
// Step 4.15
- if (!BN_GENCB_call(cb, 1, i)) {
+ if (!BN_GENCB_call(cb, BN_GENCB_PRIME_TEST, i - 1)) {
goto err;
}
}
@@ -926,80 +931,11 @@ err:
}
static int probable_prime(BIGNUM *rnd, int bits) {
- uint16_t mods[OPENSSL_ARRAY_SIZE(kPrimes)];
- const size_t num_primes = num_trial_division_primes(rnd);
- BN_ULONG delta;
- BN_ULONG maxdelta = BN_MASK2 - kPrimes[num_primes - 1];
- char is_single_word = bits <= BN_BITS2;
-
-again:
- if (!BN_rand(rnd, bits, BN_RAND_TOP_TWO, BN_RAND_BOTTOM_ODD)) {
- return 0;
- }
-
- // we now have a random number 'rnd' to test.
- for (size_t i = 1; i < num_primes; i++) {
- mods[i] = bn_mod_u16_consttime(rnd, kPrimes[i]);
- }
- // If bits is so small that it fits into a single word then we
- // additionally don't want to exceed that many bits.
- if (is_single_word) {
- BN_ULONG size_limit;
- if (bits == BN_BITS2) {
- // Avoid undefined behavior.
- size_limit = ~((BN_ULONG)0) - BN_get_word(rnd);
- } else {
- size_limit = (((BN_ULONG)1) << bits) - BN_get_word(rnd) - 1;
- }
- if (size_limit < maxdelta) {
- maxdelta = size_limit;
- }
- }
- delta = 0;
-
-loop:
- if (is_single_word) {
- BN_ULONG rnd_word = BN_get_word(rnd);
-
- // In the case that the candidate prime is a single word then
- // we check that:
- // 1) It's greater than kPrimes[i] because we shouldn't reject
- // 3 as being a prime number because it's a multiple of
- // three.
- // 2) That it's not a multiple of a known prime. We don't
- // check that rnd-1 is also coprime to all the known
- // primes because there aren't many small primes where
- // that's true.
- for (size_t i = 1; i < num_primes && kPrimes[i] < rnd_word; i++) {
- if ((mods[i] + delta) % kPrimes[i] == 0) {
- delta += 2;
- if (delta > maxdelta) {
- goto again;
- }
- goto loop;
- }
- }
- } else {
- for (size_t i = 1; i < num_primes; i++) {
- // check that rnd is not a prime and also
- // that gcd(rnd-1,primes) == 1 (except for 2)
- if (((mods[i] + delta) % kPrimes[i]) <= 1) {
- delta += 2;
- if (delta > maxdelta) {
- goto again;
- }
- goto loop;
- }
+ do {
+ if (!BN_rand(rnd, bits, BN_RAND_TOP_TWO, BN_RAND_BOTTOM_ODD)) {
+ return 0;
}
- }
-
- if (!BN_add_word(rnd, delta)) {
- return 0;
- }
- if (BN_num_bits(rnd) != (unsigned)bits) {
- goto again;
- }
-
+ } while (bn_odd_number_is_obviously_composite(rnd));
return 1;
}
diff --git a/src/crypto/fipsmodule/fips_shared.lds b/src/crypto/fipsmodule/fips_shared.lds
index 6d44abc4..c3db1013 100644
--- a/src/crypto/fipsmodule/fips_shared.lds
+++ b/src/crypto/fipsmodule/fips_shared.lds
@@ -15,5 +15,11 @@ SECTIONS
*(.rela.dyn)
*(.data)
*(.rel.ro)
+ *(*.text.*)
+ *(*.data.*)
+
+ /* This should be included to catch any unexpected rodata subsections, but
+ it crashes the linker!
+ *(*.rodata.*) */
}
}
diff --git a/src/crypto/fipsmodule/rand/urandom_test.cc b/src/crypto/fipsmodule/rand/urandom_test.cc
new file mode 100644
index 00000000..117c315b
--- /dev/null
+++ b/src/crypto/fipsmodule/rand/urandom_test.cc
@@ -0,0 +1,471 @@
+/* Copyright (c) 2019, 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 <gtest/gtest.h>
+#include <stdlib.h>
+
+#include <openssl/rand.h>
+
+#include "internal.h"
+
+
+#if defined(OPENSSL_X86_64) && defined(OPENSSL_LINUX) && \
+ !defined(BORINGSSL_SHARED_LIBRARY)
+
+#include <linux/random.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <sys/user.h>
+
+#if defined(OPENSSL_NO_ASM)
+static int have_rdrand() { return 0; }
+#endif
+
+// This test can be run with $OPENSSL_ia32cap=~0x4000000000000000 in order to
+// simulate the absence of RDRAND of machines that have it.
+
+// Event represents a system call from urandom.c that is observed by the ptrace
+// code in |GetTrace|.
+struct Event {
+ enum class Syscall {
+ kGetRandom,
+ kOpen,
+ kUrandomRead,
+ kUrandomIoctl,
+ kAbort,
+ };
+
+ explicit Event(Syscall syscall) : type(syscall) {}
+
+ bool operator==(const Event &other) const {
+ return type == other.type && length == other.length &&
+ flags == other.flags &&
+ ((filename == nullptr && other.filename == nullptr) ||
+ strcmp(filename, other.filename) == 0);
+ }
+
+ static Event GetRandom(size_t length, unsigned flags) {
+ Event e(Syscall::kGetRandom);
+ e.length = length;
+ e.flags = flags;
+ return e;
+ }
+
+ static Event Open(const char *filename) {
+ Event e(Syscall::kOpen);
+ e.filename = filename;
+ return e;
+ }
+
+ static Event UrandomRead(size_t length) {
+ Event e(Syscall::kUrandomRead);
+ e.length = length;
+ return e;
+ }
+
+ static Event UrandomIoctl() {
+ Event e(Syscall::kUrandomIoctl);
+ return e;
+ }
+
+ static Event Abort() {
+ Event e(Syscall::kAbort);
+ return e;
+ }
+
+ std::string String() const {
+ char buf[256];
+
+ switch (type) {
+ case Syscall::kGetRandom:
+ snprintf(buf, sizeof(buf), "getrandom(_, %zu, %d)", length, flags);
+ break;
+
+ case Syscall::kOpen:
+ snprintf(buf, sizeof(buf), "open(%s, _)", filename);
+ break;
+
+ case Syscall::kUrandomRead:
+ snprintf(buf, sizeof(buf), "read(urandom_fd, _, %zu)", length);
+ break;
+
+ case Syscall::kUrandomIoctl:
+ return "ioctl(urandom_fd, RNDGETENTCNT, _)";
+
+ case Syscall::kAbort:
+ return "abort()";
+ }
+
+ return std::string(buf);
+ }
+
+ const Syscall type;
+ size_t length = 0;
+ unsigned flags = 0;
+ const char *filename = nullptr;
+};
+
+static std::string ToString(const std::vector<Event> &trace) {
+ std::string ret;
+ for (const auto &event : trace) {
+ if (!ret.empty()) {
+ ret += ", ";
+ }
+ ret += event.String();
+ }
+ return ret;
+}
+
+// The following are flags to tell |GetTrace| to inject faults, using ptrace,
+// into the entropy-related system calls.
+
+// getrandom gives |ENOSYS|.
+static const unsigned NO_GETRANDOM = 1;
+// opening /dev/urandom fails.
+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;
+// Reading from /dev/urandom gives |EINVAL|.
+static const unsigned URANDOM_ERROR = 32;
+static const unsigned NEXT_FLAG = 64;
+
+// 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
+// of |flags| and records the observed events by appending to |out_trace|.
+static void GetTrace(std::vector<Event> *out_trace, unsigned flags,
+ std::function<void()> thunk) {
+ const int child_pid = fork();
+ ASSERT_NE(-1, child_pid);
+
+ if (child_pid == 0) {
+ // Child process
+ if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0) {
+ perror("PTRACE_TRACEME");
+ _exit(1);
+ }
+ raise(SIGSTOP);
+ thunk();
+ _exit(0);
+ }
+
+ // Parent process
+ int status;
+ ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
+
+ // Set options so that:
+ // a) the child process is killed once this process dies.
+ // b) System calls result in a WSTOPSIG value of (SIGTRAP | 0x80) rather
+ // than just SIGTRAP. (This doesn't matter here, but it's recommended
+ // practice so that it's distinct from the signal itself.)
+ ASSERT_EQ(0, ptrace(PTRACE_SETOPTIONS, child_pid, nullptr,
+ PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD))
+ << strerror(errno);
+
+ // urandom_fd tracks the file descriptor number for /dev/urandom in the child
+ // process, if it opens it.
+ int urandom_fd = -1;
+
+ for (;;) {
+ // Advance the child to the next system call.
+ ASSERT_EQ(0, ptrace(PTRACE_SYSCALL, child_pid, 0, 0));
+ ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
+
+ // The child may have aborted rather than made a system call.
+ if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGABRT) {
+ out_trace->push_back(Event::Abort());
+ break;
+ }
+
+ // Otherwise the only valid ptrace event is a system call stop.
+ ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80));
+
+ struct user_regs_struct regs;
+ ASSERT_EQ(0, ptrace(PTRACE_GETREGS, child_pid, nullptr, &regs));
+ 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.
+ int inject_error = 0;
+
+ switch (syscall_number) {
+ case __NR_getrandom:
+ if (flags & NO_GETRANDOM) {
+ inject_error = -ENOSYS;
+ } else if (flags & GETRANDOM_ERROR) {
+ inject_error = -EINVAL;
+ } else if (flags & GETRANDOM_NOT_READY) {
+ if (regs.rdx & GRND_NONBLOCK) {
+ inject_error = -EAGAIN;
+ }
+ }
+ out_trace->push_back(
+ Event::GetRandom(/*length=*/regs.rsi, /*flags=*/regs.rdx));
+ break;
+
+ case __NR_openat:
+ case __NR_open: {
+ // It's assumed that any arguments to open(2) are constants in read-only
+ // memory and thus the pointer in the child's context will also be a
+ // valid pointer in our address space.
+ const char *filename = reinterpret_cast<const char *>(
+ (syscall_number == __NR_openat) ? regs.rsi : regs.rdi);
+ out_trace->push_back(Event::Open(filename));
+ is_opening_urandom = strcmp(filename, "/dev/urandom") == 0;
+ if (is_opening_urandom && (flags & NO_URANDOM)) {
+ inject_error = -ENOENT;
+ }
+ break;
+ }
+
+ case __NR_read: {
+ const int read_fd = regs.rdi;
+ if (urandom_fd >= 0 && urandom_fd == read_fd) {
+ out_trace->push_back(Event::UrandomRead(/*length=*/regs.rdx));
+ if (flags & URANDOM_ERROR) {
+ inject_error = -EINVAL;
+ }
+ }
+ 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) {
+ // Replace the system call number with -1 to cause the kernel to ignore
+ // the call. The -ENOSYS will be replaced later with the value of
+ // |inject_error|.
+ regs.orig_rax = -1;
+ ASSERT_EQ(0, ptrace(PTRACE_SETREGS, child_pid, nullptr, &regs));
+ }
+
+ ASSERT_EQ(0, ptrace(PTRACE_SYSCALL, child_pid, 0, 0));
+ ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
+ // If the system call was exit/exit_group, the process may be terminated
+ // rather than have exited the system call.
+ if (WIFEXITED(status)) {
+ ASSERT_EQ(0, WEXITSTATUS(status));
+ return;
+ }
+
+ // Otherwise the next state must be a system call exit stop. This is
+ // indistinguishable from a system call entry, we just have to keep track
+ // and know that these events happen in pairs.
+ ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80));
+
+ if (inject_error) {
+ if (inject_error != -ENOSYS) {
+ ASSERT_EQ(0, ptrace(PTRACE_GETREGS, child_pid, nullptr, &regs));
+ regs.rax = inject_error;
+ ASSERT_EQ(0, ptrace(PTRACE_SETREGS, child_pid, nullptr, &regs));
+ }
+ } else if (is_opening_urandom) {
+ ASSERT_EQ(0, ptrace(PTRACE_GETREGS, child_pid, nullptr, &regs));
+ 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)));
+ }
+ }
+}
+
+// TestFunction is the function that |GetTrace| is asked to trace.
+static void TestFunction() {
+ uint8_t byte;
+ RAND_bytes(&byte, sizeof(byte));
+ RAND_bytes(&byte, sizeof(byte));
+}
+
+// TestFunctionPRNGModel is a model of how the urandom.c code will behave when
+// |TestFunction| is run. It should return the same trace of events that
+// |GetTrace| will observe the real code making.
+static std::vector<Event> TestFunctionPRNGModel(unsigned flags) {
+#if defined(BORINGSSL_FIPS)
+ static const bool is_fips = true;
+#else
+ static const bool is_fips = false;
+#endif
+
+ std::vector<Event> ret;
+ bool urandom_probed = false;
+ bool getrandom_ready = false;
+
+ // Probe for getrandom support
+ ret.push_back(Event::GetRandom(1, GRND_NONBLOCK));
+ std::function<void()> wait_for_entropy;
+ std::function<bool(bool, size_t)> sysrand;
+
+ if (flags & NO_GETRANDOM) {
+ 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();
+ }
+ ret.push_back(Event::UrandomRead(len));
+ if (flags & URANDOM_ERROR) {
+ ret.push_back(Event::Abort());
+ return false;
+ }
+ return true;
+ };
+ } else {
+ if (flags & GETRANDOM_ERROR) {
+ ret.push_back(Event::Abort());
+ return ret;
+ }
+
+ getrandom_ready = (flags & GETRANDOM_NOT_READY) == 0;
+ wait_for_entropy = [&ret, &getrandom_ready] {
+ if (getrandom_ready) {
+ return;
+ }
+
+ ret.push_back(Event::GetRandom(1, GRND_NONBLOCK));
+ ret.push_back(Event::GetRandom(1, 0));
+ getrandom_ready = true;
+ };
+ sysrand = [&ret, &wait_for_entropy](bool block, size_t len) {
+ if (block) {
+ wait_for_entropy();
+ }
+ ret.push_back(Event::GetRandom(len, block ? 0 : GRND_NONBLOCK));
+ return true;
+ };
+ }
+
+ const size_t kSeedLength = CTR_DRBG_ENTROPY_LEN * (is_fips ? 10 : 1);
+ const size_t kAdditionalDataLength = 32;
+
+ if (!have_rdrand()) {
+ if (!sysrand(true, kAdditionalDataLength) ||
+ // Initialise CRNGT.
+ (is_fips && !sysrand(true, 16)) ||
+ !sysrand(true, kSeedLength) ||
+ // Second entropy draw.
+ !sysrand(true, kAdditionalDataLength)) {
+ return ret;
+ }
+ } else {
+ // Opportuntistic entropy draw in FIPS mode because RDRAND was used.
+ // In non-FIPS mode it's just drawn from |CRYPTO_sysrand| in a blocking
+ // way.
+ if (!sysrand(!is_fips, CTR_DRBG_ENTROPY_LEN)) {
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+// Tests that |TestFunctionPRNGModel| is a correct model for the code in
+// urandom.c, at least to the limits of the the |Event| type.
+TEST(URandomTest, Test) {
+ char buf[256];
+
+#define TRACE_FLAG(flag) \
+ snprintf(buf, sizeof(buf), #flag ": %d", (flags & flag) != 0); \
+ SCOPED_TRACE(buf);
+
+ for (unsigned flags = 0; flags < NEXT_FLAG; flags++) {
+ 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);
+
+ const std::vector<Event> expected_trace = TestFunctionPRNGModel(flags);
+ std::vector<Event> actual_trace;
+ GetTrace(&actual_trace, flags, TestFunction);
+
+ if (expected_trace != actual_trace) {
+ ADD_FAILURE() << "Expected: " << ToString(expected_trace)
+ << "\nFound: " << ToString(actual_trace);
+ }
+ }
+}
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+#else
+
+int main(int argc, char **argv) { return 0; }
+
+#endif // X86_64 && LINUX && !SHARED_LIBRARY
diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc
index 12f4738b..15c18c2e 100644
--- a/src/ssl/tls13_client.cc
+++ b/src/ssl/tls13_client.cc
@@ -894,10 +894,10 @@ bool tls13_process_new_session_ticket(SSL *ssl, const SSLMessage &msg) {
}
// Parse out the extensions.
- bool have_early_data_info = false;
- CBS early_data_info;
+ bool have_early_data = false;
+ CBS early_data;
const SSL_EXTENSION_TYPE ext_types[] = {
- {TLSEXT_TYPE_early_data, &have_early_data_info, &early_data_info},
+ {TLSEXT_TYPE_early_data, &have_early_data, &early_data},
};
uint8_t alert = SSL_AD_DECODE_ERROR;
@@ -908,9 +908,9 @@ bool tls13_process_new_session_ticket(SSL *ssl, const SSLMessage &msg) {
return false;
}
- if (have_early_data_info) {
- if (!CBS_get_u32(&early_data_info, &session->ticket_max_early_data) ||
- CBS_len(&early_data_info) != 0) {
+ if (have_early_data) {
+ if (!CBS_get_u32(&early_data, &session->ticket_max_early_data) ||
+ CBS_len(&early_data) != 0) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
return false;
diff --git a/src/ssl/tls13_server.cc b/src/ssl/tls13_server.cc
index f6a81d45..abacefc7 100644
--- a/src/ssl/tls13_server.cc
+++ b/src/ssl/tls13_server.cc
@@ -171,10 +171,10 @@ static bool add_new_session_tickets(SSL_HANDSHAKE *hs, bool *out_sent_tickets) {
}
if (ssl->enable_early_data) {
- CBB early_data_info;
+ CBB early_data;
if (!CBB_add_u16(&extensions, TLSEXT_TYPE_early_data) ||
- !CBB_add_u16_length_prefixed(&extensions, &early_data_info) ||
- !CBB_add_u32(&early_data_info, session->ticket_max_early_data) ||
+ !CBB_add_u16_length_prefixed(&extensions, &early_data) ||
+ !CBB_add_u32(&early_data, session->ticket_max_early_data) ||
!CBB_flush(&extensions)) {
return false;
}
diff --git a/src/third_party/sike/utils.h b/src/third_party/sike/utils.h
index cbc83293..bc806da0 100644
--- a/src/third_party/sike/utils.h
+++ b/src/third_party/sike/utils.h
@@ -9,7 +9,7 @@
#include <openssl/base.h>
-#include "../crypto/internal.h"
+#include "../../crypto/internal.h"
#include "sike.h"
// Conversion macro from number of bits to number of bytes
diff --git a/src/util/fipstools/break-hash.go b/src/util/fipstools/break-hash.go
index 53e1c5cb..8c817d8d 100644
--- a/src/util/fipstools/break-hash.go
+++ b/src/util/fipstools/break-hash.go
@@ -56,6 +56,10 @@ func do(outPath, inPath string) error {
symbols, err := object.Symbols()
if err != nil {
+ fmt.Fprintf(os.Stderr, "%s\nTrying dynamic symbols\n", err)
+ symbols, err = object.DynamicSymbols()
+ }
+ if err != nil {
return errors.New("failed to parse symbols: " + err.Error())
}
@@ -123,7 +127,7 @@ func do(outPath, inPath string) error {
newHash := mac.Sum(nil)
fmt.Printf("Found start of module at offset 0x%x (VMA 0x%x):\n", start-textSection.Addr, start)
- fmt.Printf(hex.Dump(moduleText[:128]))
+ fmt.Println(hex.Dump(moduleText[:128]))
fmt.Printf("\nHash of module was: %x\n", hashWas)
fmt.Printf("Hash of corrupted module is: %x\n", newHash)
diff --git a/src/util/fipstools/delocate/delocate.go b/src/util/fipstools/delocate/delocate.go
index a43b4286..92b4c31d 100644
--- a/src/util/fipstools/delocate/delocate.go
+++ b/src/util/fipstools/delocate/delocate.go
@@ -803,6 +803,8 @@ const (
instrCombine
// instrThreeArg merges two sources into a destination in some fashion.
instrThreeArg
+ // instrCompare takes two arguments and writes outputs to the flags register.
+ instrCompare
instrOther
)
@@ -833,6 +835,11 @@ func classifyInstruction(instr string, args []*node32) instructionType {
return instrCombine
}
+ case "cmpq":
+ if len(args) == 2 {
+ return instrCompare
+ }
+
case "sarxq", "shlxq", "shrxq":
if len(args) == 3 {
return instrThreeArg
@@ -855,6 +862,13 @@ func push(w stringWriter) wrapperFunc {
}
}
+func compare(w stringWriter, instr, a, b string) wrapperFunc {
+ return func(k func()) {
+ k()
+ w.WriteString(fmt.Sprintf("\t%s %s, %s\n", instr, a, b))
+ }
+}
+
func (d *delocation) loadFromGOT(w stringWriter, destination, symbol, section string, redzoneCleared bool) wrapperFunc {
d.gotExternalsNeeded[symbol+"@"+section] = struct{}{}
@@ -1074,7 +1088,7 @@ Args:
}
classification := classifyInstruction(instructionName, argNodes)
- if classification != instrThreeArg && i != 0 {
+ if classification != instrThreeArg && classification != instrCompare && i != 0 {
return nil, errors.New("GOT access must be source operand")
}
@@ -1091,6 +1105,17 @@ Args:
case instrMove:
assertNodeType(argNodes[1], ruleRegisterOrConstant)
targetReg = d.contents(argNodes[1])
+ case instrCompare:
+ otherSource := d.contents(argNodes[i^1])
+ saveRegWrapper, tempReg := saveRegister(d.output, []string{otherSource})
+ redzoneCleared = true
+ wrappers = append(wrappers, saveRegWrapper)
+ if i == 0 {
+ wrappers = append(wrappers, compare(d.output, instructionName, tempReg, otherSource))
+ } else {
+ wrappers = append(wrappers, compare(d.output, instructionName, otherSource, tempReg))
+ }
+ targetReg = tempReg
case instrTransformingMove:
assertNodeType(argNodes[1], ruleRegisterOrConstant)
targetReg = d.contents(argNodes[1])
@@ -1114,7 +1139,7 @@ Args:
return nil, fmt.Errorf("three-argument instruction has %d arguments", n)
}
if i != 0 && i != 1 {
- return nil, errors.New("GOT access must be from soure operand")
+ return nil, errors.New("GOT access must be from source operand")
}
targetReg = d.contents(argNodes[2])
diff --git a/src/util/fipstools/delocate/testdata/x86_64-GOTRewrite/in.s b/src/util/fipstools/delocate/testdata/x86_64-GOTRewrite/in.s
index ccbc0bf3..398032f8 100644
--- a/src/util/fipstools/delocate/testdata/x86_64-GOTRewrite/in.s
+++ b/src/util/fipstools/delocate/testdata/x86_64-GOTRewrite/in.s
@@ -44,4 +44,7 @@ foo:
vpbroadcastq stderr@GOTPCREL(%rip), %xmm0
vpbroadcastq foo@GOTPCREL(%rip), %xmm0
+ cmpq foo@GOTPCREL(%rip), %rax
+ cmpq %rax, foo@GOTPCREL(%rip)
+
.comm foobar,64,32
diff --git a/src/util/fipstools/delocate/testdata/x86_64-GOTRewrite/out.s b/src/util/fipstools/delocate/testdata/x86_64-GOTRewrite/out.s
index 273cfe94..e14a71e6 100644
--- a/src/util/fipstools/delocate/testdata/x86_64-GOTRewrite/out.s
+++ b/src/util/fipstools/delocate/testdata/x86_64-GOTRewrite/out.s
@@ -172,6 +172,21 @@ foo:
leaq 128(%rsp), %rsp
vpbroadcastq %xmm0, %xmm0
+# WAS cmpq foo@GOTPCREL(%rip), %rax
+ leaq -128(%rsp), %rsp
+ pushq %rbx
+ leaq .Lfoo_local_target(%rip), %rbx
+ cmpq %rbx, %rax
+ popq %rbx
+ leaq 128(%rsp), %rsp
+# WAS cmpq %rax, foo@GOTPCREL(%rip)
+ leaq -128(%rsp), %rsp
+ pushq %rbx
+ leaq .Lfoo_local_target(%rip), %rbx
+ cmpq %rax, %rbx
+ popq %rbx
+ leaq 128(%rsp), %rsp
+
.comm foobar,64,32
.text
.loc 1 2 0
diff --git a/src/util/generate_build_files.py b/src/util/generate_build_files.py
index 57e5e1b1..8a5df75d 100644
--- a/src/util/generate_build_files.py
+++ b/src/util/generate_build_files.py
@@ -272,6 +272,8 @@ class Bazel(object):
self.PrintVariableSection(out, 'ssl_test_sources', files['ssl_test'])
self.PrintVariableSection(out, 'crypto_test_data',
files['crypto_test_data'])
+ self.PrintVariableSection(out, 'urandom_test_sources',
+ files['urandom_test'])
class Eureka(object):
@@ -685,6 +687,12 @@ def main(platforms):
'src/crypto/test/file_test_gtest.cc',
'src/crypto/test/gtest_main.cc',
]
+ # urandom_test.cc is in a separate binary so that it can be test PRNG
+ # initialisation.
+ crypto_test_files = [
+ file for file in crypto_test_files
+ if not file.endswith('/urandom_test.cc')
+ ]
ssl_test_files = FindCFiles(os.path.join('src', 'ssl'), OnlyTests)
ssl_test_files += [
@@ -692,6 +700,10 @@ def main(platforms):
'src/crypto/test/gtest_main.cc',
]
+ urandom_test_files = [
+ 'src/crypto/fipsmodule/rand/urandom_test.cc',
+ ]
+
fuzz_c_files = FindCFiles(os.path.join('src', 'fuzz'), NoTests)
ssl_h_files = (
@@ -729,6 +741,7 @@ def main(platforms):
'tool_headers': tool_h_files,
'test_support': test_support_c_files,
'test_support_headers': test_support_h_files,
+ 'urandom_test': sorted(urandom_test_files),
}
asm_outputs = sorted(WriteAsmFiles(ReadPerlAsmOperations()).iteritems())