chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,29 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <openssl/rand.h>
#include "internal.h"
#if defined(OPENSSL_RAND_CCRANDOMGENERATEBYTES)
#include <CommonCrypto/CommonRandom.h>
#include <stdio.h>
#include <stdlib.h>
void CRYPTO_sysrand(uint8_t *out, size_t requested) {
if (requested == 0) {
return;
}
// To get system randomness on iOS we use |CCRandomGenerateBytes|. On MacOS we
// use |getentropy| but iOS doesn't expose that.
if (CCRandomGenerateBytes(out, requested) != kCCSuccess) {
fprintf(stderr, "CCRandomGenerateBytes failed.\n");
abort();
}
}
#endif

View File

@@ -0,0 +1,48 @@
// Copyright (c) 2020, Google Inc.
// SPDX-License-Identifier: ISC
#if !defined(_DEFAULT_SOURCE)
#define _DEFAULT_SOURCE // Needed for getentropy on musl and glibc
#endif
#include <openssl/rand.h>
#include "internal.h"
#if defined(OPENSSL_RAND_CCRANDOMGENERATEBYTES)
#include <gtest/gtest.h>
#include <openssl/span.h>
#include <CommonCrypto/CommonRandom.h>
#include "../test/test_util.h"
// This test is, strictly speaking, flaky, but we use large enough buffers
// that the probability of failing when we should pass is negligible.
TEST(CCRandomGenerateBytesTest, NotObviouslyBroken) {
static const uint8_t kZeros[256] = {0};
uint8_t buf1[256] = {0}, buf2[256] = {0}, buf3[256] = {0};
EXPECT_EQ(CCRandomGenerateBytes(buf1, sizeof(buf1)), kCCSuccess);
EXPECT_EQ(CCRandomGenerateBytes(buf2, sizeof(buf2)), kCCSuccess);
EXPECT_NE(Bytes(buf1), Bytes(buf2));
EXPECT_NE(Bytes(buf1), Bytes(kZeros));
EXPECT_NE(Bytes(buf2), Bytes(kZeros));
// Ensure that the implementation is not simply returning the memory unchanged.
memcpy(buf3, buf1, sizeof(buf3));
EXPECT_EQ(CCRandomGenerateBytes(buf1, sizeof(buf1)), kCCSuccess);
EXPECT_NE(Bytes(buf1), Bytes(buf3));
// |CCRandomGenerateBytes| supports bigger inputs.
uint8_t buf4[1024] = {0}, buf5[1024] = {0};
EXPECT_EQ(CCRandomGenerateBytes(buf4, sizeof(buf4)), kCCSuccess);
EXPECT_EQ(CCRandomGenerateBytes(buf5, sizeof(buf5)), kCCSuccess);
EXPECT_NE(Bytes(buf4), Bytes(buf5));
}
#endif // defined(OPENSSL_RAND_CCRANDOMGENERATEBYTES)

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2016, Google Inc.
// SPDX-License-Identifier: ISC
#include <openssl/rand.h>
#include "internal.h"
#if defined(OPENSSL_RAND_DETERMINISTIC)
#include <string.h>
#include <openssl/chacha.h>
#include "../internal.h"
// g_num_calls is the number of calls to |CRYPTO_sysrand| that have occurred.
//
// This is intentionally not thread-safe. If the fuzzer mode is ever used in a
// multi-threaded program, replace this with a thread-local. (A mutex would not
// be deterministic.)
static uint64_t g_num_calls = 0;
static struct CRYPTO_STATIC_MUTEX g_num_calls_lock = CRYPTO_STATIC_MUTEX_INIT;
void RAND_reset_for_fuzzing(void) { g_num_calls = 0; }
void CRYPTO_sysrand(uint8_t *out, size_t requested) {
static const uint8_t kZeroKey[32];
CRYPTO_STATIC_MUTEX_lock_write(&g_num_calls_lock);
uint64_t num_calls = g_num_calls++;
CRYPTO_STATIC_MUTEX_unlock_write(&g_num_calls_lock);
uint8_t nonce[12];
OPENSSL_memset(nonce, 0, sizeof(nonce));
OPENSSL_memcpy(nonce, &num_calls, sizeof(num_calls));
OPENSSL_memset(out, 0, requested);
CRYPTO_chacha_20(out, out, requested, kZeroKey, nonce, 0);
}
#endif // OPENSSL_RAND_DETERMINISTIC

View File

@@ -0,0 +1,44 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#if !defined(_DEFAULT_SOURCE)
// Needed for getentropy on musl and glibc per man pages.
#define _DEFAULT_SOURCE
#endif
#include <openssl/rand.h>
#include "internal.h"
#if defined(OPENSSL_RAND_GETENTROPY)
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#if defined(OPENSSL_MACOS)
// MacOS does not declare getentropy in uinstd.h like other OS's.
#include <sys/random.h>
#endif
#if !defined(GETENTROPY_MAX)
// Per POSIX 2024
// https://pubs.opengroup.org/onlinepubs/9799919799/functions/getentropy.html
#define GETENTROPY_MAX 256
#endif
void CRYPTO_sysrand(uint8_t *out, size_t requested) {
// getentropy max request size is GETENTROPY_MAX.
while (requested > 0) {
size_t request_chunk = (requested > GETENTROPY_MAX) ? GETENTROPY_MAX : requested;
if (getentropy(out, request_chunk) != 0) {
fprintf(stderr, "getentropy failed.\n");
abort();
}
requested -= request_chunk;
out += request_chunk;
}
}
#endif

View File

@@ -0,0 +1,54 @@
// Copyright (c) 2023, Google Inc.
// SPDX-License-Identifier: ISC
#if !defined(_DEFAULT_SOURCE)
#define _DEFAULT_SOURCE // Needed for getentropy on musl and glibc
#endif
#include <openssl/rand.h>
#include "internal.h"
#if defined(OPENSSL_RAND_GETENTROPY)
#include <unistd.h>
#include <errno.h>
#if defined(OPENSSL_MACOS)
#include <sys/random.h>
#endif
#include <gtest/gtest.h>
#include <openssl/span.h>
#include "../test/test_util.h"
// This test is, strictly speaking, flaky, but we use large enough buffers
// that the probability of failing when we should pass is negligible.
TEST(GetEntropyTest, NotObviouslyBroken) {
static const uint8_t kZeros[256] = {0};
uint8_t buf1[256], buf2[256];
EXPECT_EQ(getentropy(buf1, sizeof(buf1)), 0);
EXPECT_EQ(getentropy(buf2, sizeof(buf2)), 0);
EXPECT_NE(Bytes(buf1), Bytes(buf2));
EXPECT_NE(Bytes(buf1), Bytes(kZeros));
EXPECT_NE(Bytes(buf2), Bytes(kZeros));
uint8_t buf3[256];
// Ensure that the implementation is not simply returning the memory unchanged.
memcpy(buf3, buf1, sizeof(buf3));
EXPECT_EQ(getentropy(buf1, sizeof(buf1)), 0);
EXPECT_NE(Bytes(buf1), Bytes(buf3));
errno = 0;
uint8_t toobig[257];
// getentropy should fail returning -1 and setting errno to EIO if you request
// more than 256 bytes of entropy. macOS's man page says EIO but it actually
// returns EINVAL, so we accept either.
EXPECT_EQ(getentropy(toobig, 257), -1);
EXPECT_TRUE(errno == EIO || errno == EINVAL);
}
#endif

View File

@@ -0,0 +1,60 @@
// Copyright (c) 2020, Google Inc.
// SPDX-License-Identifier: ISC
#ifndef OPENSSL_HEADER_CRYPTO_RAND_GETRANDOM_FILLIN_H
#define OPENSSL_HEADER_CRYPTO_RAND_GETRANDOM_FILLIN_H
#include <openssl/base.h>
#include "internal.h"
#if defined(OPENSSL_RAND_URANDOM)
#include <sys/syscall.h>
#if defined(OPENSSL_X86_64)
#define EXPECTED_NR_getrandom 318
#elif defined(OPENSSL_X86)
#define EXPECTED_NR_getrandom 355
#elif defined(OPENSSL_AARCH64)
#define EXPECTED_NR_getrandom 278
#elif defined(OPENSSL_ARM)
#define EXPECTED_NR_getrandom 384
#elif defined(OPENSSL_PPC64LE) || defined(OPENSSL_PPC64BE) || defined(OPENSSL_PPC32BE) || defined(OPENSSL_PPC32LE)
#define EXPECTED_NR_getrandom 359
#elif defined(OPENSSL_RISCV64)
#define EXPECTED_NR_getrandom 278
#elif defined(OPENSSL_S390X)
#define EXPECTED_NR_getrandom 349
#elif defined(OPENSSL_LOONGARCH64)
#define EXPECTED_NR_getrandom 278
#endif
#if defined(EXPECTED_NR_getrandom)
#define USE_NR_getrandom
#if defined(__NR_getrandom)
#if __NR_getrandom != EXPECTED_NR_getrandom
#error "system call number for getrandom is not the expected value"
#endif
#else // __NR_getrandom
#define __NR_getrandom EXPECTED_NR_getrandom
#endif // __NR_getrandom
#endif // EXPECTED_NR_getrandom
#if !defined(GRND_NONBLOCK)
#define GRND_NONBLOCK 1
#endif
#if !defined(GRND_RANDOM)
#define GRND_RANDOM 2
#endif
#endif // OPENSSL_LINUX
#endif // OPENSSL_HEADER_CRYPTO_RAND_GETRANDOM_FILLIN_H

View File

@@ -0,0 +1,64 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#ifndef AWSLC_HEADER_CRYPTO_RAND_EXTRA_RAND_INTERNAL_H
#define AWSLC_HEADER_CRYPTO_RAND_EXTRA_RAND_INTERNAL_H
#include <openssl/ctrdrbg.h>
#if defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
#define OPENSSL_RAND_DETERMINISTIC
#elif defined(OPENSSL_WINDOWS)
#define OPENSSL_RAND_WINDOWS
#elif defined(OPENSSL_MACOS) || defined(OPENSSL_OPENBSD) || \
defined(OPENSSL_FREEBSD) || defined(OPENSSL_NETBSD) || \
defined(OPENSSL_SOLARIS) || defined(OPENSSL_WASM) || \
(defined(OPENSSL_LINUX) && !defined(HAVE_LINUX_RANDOM_H))
#define OPENSSL_RAND_GETENTROPY
#elif defined(OPENSSL_IOS)
#define OPENSSL_RAND_CCRANDOMGENERATEBYTES
#else
#define OPENSSL_RAND_URANDOM
#endif
#if defined(__cplusplus)
extern "C" {
#endif
// Functions:
// CRYPTO_sysrand
// CRYPTO_sysrand_if_available
// are the operating system entropy source interface used in the randomness
// generation implementation.
// CRYPTO_sysrand fills |len| bytes at |buf| with entropy from the operating
// system.
OPENSSL_EXPORT void CRYPTO_sysrand(uint8_t *buf, size_t len);
#if defined(OPENSSL_RAND_URANDOM)
// CRYPTO_sysrand_if_available fills |len| bytes at |buf| with entropy from the
// operating system, or early /dev/urandom data, and returns 1, _if_ the entropy
// pool is initialized or if getrandom() is not available. Otherwise it will not
// block and will instead fill |buf| with all zeros and return 0.
int CRYPTO_sysrand_if_available(uint8_t *buf, size_t len);
#else
OPENSSL_INLINE int CRYPTO_sysrand_if_available(uint8_t *buf, size_t len) {
CRYPTO_sysrand(buf, len);
return 1;
}
#endif // defined(OPENSSL_RAND_URANDOM)
// Don't retry forever. There is no science in picking this number and can be
// adjusted in the future if need be. We do not backoff forever, because we
// believe that it is easier to detect failing calls than detecting infinite
// spinning loops.
#define MAX_BACKOFF_RETRIES 9
OPENSSL_EXPORT int vm_ube_fallback_get_seed(
uint8_t seed[CTR_DRBG_ENTROPY_LEN]);
#if defined(__cplusplus)
} // extern C
#endif
#endif // AWSLC_HEADER_CRYPTO_RAND_EXTRA_RAND_INTERNAL_H

View File

@@ -0,0 +1,75 @@
// Copyright (c) 2017, Google Inc.
// SPDX-License-Identifier: ISC
#include <openssl/rand.h>
#include <limits.h>
#include "../internal.h"
void RAND_seed(const void *buf, int num) {
// OpenSSH calls |RAND_seed| before jailing on the assumption that any needed
// file descriptors etc will be opened.
uint8_t unused;
AWSLC_ABORT_IF_NOT_ONE(RAND_bytes(&unused, sizeof(unused)));
}
int RAND_load_file(const char *path, long num) {
if (num < 0) { // read the "whole file"
return 1;
} else if (num <= INT_MAX) {
return (int) num;
} else {
return INT_MAX;
}
}
int RAND_write_file(const char *file) {
return -1;
}
const char *RAND_file_name(char *buf, size_t num) { return NULL; }
void RAND_add(const void *buf, int num, double entropy) {}
int RAND_egd(const char *path) {
return 255;
}
int RAND_egd_bytes(const char *path, int bytes) {
return bytes;
}
int RAND_poll(void) {
return 1;
}
int RAND_status(void) {
return 1;
}
OPENSSL_BEGIN_ALLOW_DEPRECATED
static const struct rand_meth_st kSSLeayMethod = {
RAND_seed,
RAND_bytes,
RAND_cleanup,
RAND_add,
RAND_pseudo_bytes,
RAND_status,
};
RAND_METHOD *RAND_SSLeay(void) {
return (RAND_METHOD*) &kSSLeayMethod;
}
RAND_METHOD *RAND_OpenSSL(void) {
return RAND_SSLeay();
}
const RAND_METHOD *RAND_get_rand_method(void) { return RAND_SSLeay(); }
OPENSSL_END_ALLOW_DEPRECATED
int RAND_set_rand_method(const RAND_METHOD *method) { return 1; }
void RAND_keep_random_devices_open(int a) { }
void RAND_cleanup(void) {}

View File

@@ -0,0 +1,469 @@
// Copyright (c) 2014, Google Inc.
// SPDX-License-Identifier: ISC
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE // needed for syscall() on Linux.
#endif
#include <openssl/rand.h>
#include "internal.h"
#if defined(OPENSSL_RAND_URANDOM)
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#if defined(OPENSSL_LINUX)
#if defined(AWS_LC_URANDOM_NEEDS_U32)
// On old Linux OS: unknown type name '__u32' when include <linux/random.h>.
// If '__u32' is predefined, redefine will cause compiler error.
typedef unsigned int __u32;
#endif
#include <linux/random.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/syscall.h>
#if defined(OPENSSL_ANDROID)
#include <sys/system_properties.h>
#endif
#if !defined(OPENSSL_ANDROID)
#define OPENSSL_HAS_GETAUXVAL
#endif
// glibc prior to 2.16 does not have getauxval and sys/auxv.h. Android has some
// host builds (i.e. not building for Android itself, so |OPENSSL_ANDROID| is
// unset) which are still using a 2.15 sysroot.
//
// TODO(davidben): Remove this once Android updates their sysroot.
#if defined(__GLIBC_PREREQ)
#if !__GLIBC_PREREQ(2, 16)
#undef OPENSSL_HAS_GETAUXVAL
#endif
#endif
#if defined(OPENSSL_HAS_GETAUXVAL)
#include <sys/auxv.h>
#endif
#endif // OPENSSL_LINUX
#include <openssl/thread.h>
#include <openssl/mem.h>
#include "getrandom_fillin.h"
#include "../internal.h"
#if defined(OPENSSL_MSAN)
void __msan_unpoison(void *, size_t);
#endif
#ifndef MIN
#define AWSLC_MIN(X,Y) (((X) < (Y)) ? (X) : (Y))
#else
#define AWSLC_MIN(X,Y) MIN(X,Y)
#endif
// One second in nanoseconds.
#define ONE_SECOND INT64_C(1000000000)
// 250 milliseconds in nanoseconds.
#define MILLISECONDS_250 INT64_C(250000000)
#define INITIAL_BACKOFF_DELAY 1
enum random_flavor_t {
NOT_CHOSEN,
USE_GETRANDOM,
USE_DEV_URANDOM
};
// random_flavor determines the randomness function used. This is either
// getrandom or /dev/urandom. It's protected by |initialize_random_flavor_once|.
// If both getrandom and /dev/urandom are available, we prefer getrandom. The
// reasons are:
// - getrandom has better blocking semantics than /dev/urandom; Former blocks
// on initialization by default.
// - getrandom doesn't require a file descriptor.
// - Existence of vgetrandom can yield greater performance, see e.g.
// https://www.phoronix.com/news/glibc-getrandom-vDSO-Merged.
static enum random_flavor_t random_flavor = NOT_CHOSEN;
enum random_state_t {
STATE_NOT_READY,
STATE_READY
};
// random_flavor_state is |STATE_READY| if the entropy pool of |random_flavor|
// has been (fully) initialized and |STATE_NOT_READY| otherwise. Initialized in
// in this context means that the implementation of |random_flavor| has decided
// that enough entropy/noise has been accumulated. It's protected by
// |ensure_random_state_is_initialized_once|.
//
// If |random_flavor_state| has been initialized, we assume it stays
// initialized forever. That is, we assume "entropy depletion" does not exist.
// See e.g. https://www.nccgroup.com/us/research-blog/on-linux-s-random-number-generation/
// for an exposé on entropy depletion.
//
// If a consumer requests a non-blocking read of randomness from
// |random_flavor|, the state |random_flavor_state| is not checked before
// returning randomness.
static enum random_state_t random_flavor_state = STATE_NOT_READY;
// urandom_fd is a file descriptor to /dev/urandom. It's protected by
// |initialize_random_flavor_once|.
static int urandom_fd = 0;
static CRYPTO_once_t initialize_random_flavor_once = CRYPTO_ONCE_INIT;
static CRYPTO_once_t ensure_random_state_is_initialized_once = CRYPTO_ONCE_INIT;
// getrandom_syscall_number_available returns 1 if the getrandom system call
// number is defined, otherwise 0.
static int getrandom_syscall_number_available(void) {
#if defined(USE_NR_getrandom)
return 1;
#else
return 0;
#endif
}
// do_backoff initiates exponential backoff. |backoff| holds the previous
// backoff delay. Initial backoff delay is |INITIAL_BACKOFF_DELAY|. This
// function will be called so rarely (if ever), that we keep it as a function
// call and don't care about attempting to inline it.
static void do_backoff(long *backoff) {
// Exponential backoff.
//
// iteration delay
// --------- -----------------
// 1 10 nsec
// 2 100 nsec
// 3 1,000 nsec
// 4 10,000 nsec
// 5 100,000 nsec
// 6 1,000,000 nsec
// 7 10,000,000 nsec
// 8 99,999,999 nsec
// 9 99,999,999 nsec
// ...
struct timespec sleep_time = {.tv_sec = 0, .tv_nsec = 0 };
// Cap backoff at 99,999,999 nsec, which is the maximum value the nanoseconds
// field in |timespec| can hold.
*backoff = AWSLC_MIN((*backoff) * 10, ONE_SECOND - 1);
// |nanosleep| can mutate |sleep_time|. Hence, we use |backoff| for state.
sleep_time.tv_nsec = *backoff;
nanosleep(&sleep_time, &sleep_time);
}
static ssize_t wrapper_dev_urandom(void *buf, size_t buf_len, int block) {
ssize_t ret = 0;
size_t retry_counter = 0;
long backoff = INITIAL_BACKOFF_DELAY;
do {
ret = read(urandom_fd, buf, buf_len);
if ((ret == -1) && (errno != EINTR)) {
if (!block ||
retry_counter >= MAX_BACKOFF_RETRIES) {
break;
}
// We have observed extremely rare events in which a |read| on a
// |urandom| fd failed with |errno| != |EINTR|. We regard this as an
// intermittent error that is recoverable. Therefore, backoff to allow
// recovery and to avoid creating a tight spinning loop.
do_backoff(&backoff);
retry_counter = retry_counter + 1;
}
} while (ret == -1);
return ret;
}
static ssize_t wrapper_getrandom(void *buf, size_t buf_len, int block) {
ssize_t ret = -1;
#if defined(USE_NR_getrandom)
size_t retry_counter = 0;
long backoff = INITIAL_BACKOFF_DELAY;
do {
ret = syscall(__NR_getrandom, buf, buf_len, block ? 0 : GRND_NONBLOCK);
if ((ret == -1) && (errno != EINTR)) {
// Don't block in non-block mode except if a signal handler interrupted
// |getrandom|.
if (!block ||
(retry_counter >= MAX_BACKOFF_RETRIES)) {
break;
}
// We have observed extremely rare events in which a |read| on a
// |urandom| fd failed with |errno| != |EINTR|. |getrandom| uses |urandom|
// under the covers. Assuming transitivity, |getrandom| is therefore also
// subject to the same rare error events.
do_backoff(&backoff);
retry_counter = retry_counter + 1;
}
} while (ret == -1);
#if defined(OPENSSL_MSAN)
if (ret > 0) {
// MSAN doesn't recognise |syscall| and thus doesn't notice that we have
// initialised the output buffer.
__msan_unpoison(buf, ret);
}
#endif // OPENSSL_MSAN
#endif // defined(USE_NR_getrandom)
return ret;
}
// init_try_getrandom_once tests whether getrandom is supported. Returns 1 if
// getrandom is supported, 0 otherwise.
static int init_try_getrandom_once(void) {
if (!getrandom_syscall_number_available()) {
return 0;
}
uint8_t dummy = 0;
ssize_t getrandom_ret = wrapper_getrandom(&dummy, sizeof(dummy), 0);
if (getrandom_ret == 1) {
// We have getrandom and entropy pool is also fully initialized.
random_flavor = USE_GETRANDOM;
random_flavor_state = STATE_READY;
return 1;
} else if (getrandom_ret == -1 && errno == EAGAIN) {
// We have getrandom, but the entropy pool has not been initialized yet.
random_flavor = USE_GETRANDOM;
return 1;
} else if (getrandom_ret == -1 && errno == ENOSYS) {
// Kernel doesn't support getrandom.
return 0;
} else {
// Other errors are fatal.
perror("getrandom");
abort();
}
}
// init_try_urandom_once attempts to initialize /dev/urandom. Returns 1 if
// successful, 0 otherwise.
static int init_try_urandom_once(void) {
int ret = 0;
int fd = 0;
do {
fd = open("/dev/urandom", O_RDONLY);
} while (fd == -1 && errno == EINTR);
if (fd < 0) {
perror("failed to open /dev/urandom");
goto out;
}
int flags = fcntl(fd, F_GETFD);
if (flags == -1) {
// Native Client doesn't implement |fcntl|.
if (errno != ENOSYS) {
perror("failed to get flags from urandom fd");
goto out;
}
} else {
flags |= FD_CLOEXEC;
if (fcntl(fd, F_SETFD, flags) == -1) {
perror("failed to set FD_CLOEXEC on urandom fd");
goto out;
}
}
random_flavor = USE_DEV_URANDOM;
urandom_fd = fd;
ret = 1;
out:
return ret;
}
// init_once initializes the state of this module to values previously
// requested. Only function that mutates the chosen random function flavor.
static void init_random_flavor_once(void) {
// First determine if we can use getrandom. We prefer getrandom.
if (init_try_getrandom_once()) {
return;
}
// getrandom is not available. Fall-back to /dev/urandom.
if (init_try_urandom_once()) {
return;
}
// This is a fatal error and there is no way to recover.
abort();
}
static void ensure_getrandom_is_initialized(void) {
if (random_flavor_state == STATE_READY) {
// The entropy pool was already initialized in |init_try_getrandom_once|.
return;
}
uint8_t dummy = 0;
ssize_t getrandom_ret = wrapper_getrandom(&dummy, sizeof(dummy), 0);
if (getrandom_ret == -1 && errno == EAGAIN) {
// Attempt to get the path of the current process to aid in debugging when
// something blocks.
const char *current_process = "<unknown>";
#if defined(OPENSSL_HAS_GETAUXVAL)
const unsigned long getauxval_ret = getauxval(AT_EXECFN);
if (getauxval_ret != 0) {
current_process = (const char *)getauxval_ret;
}
#endif
fprintf(stderr,
"%s: getrandom indicates that the entropy pool has not been "
"initialized. Rather than continue with poor entropy, this process "
"will block until entropy is available.\n",
current_process);
getrandom_ret = wrapper_getrandom(&dummy, sizeof(dummy), 1);
}
if (getrandom_ret != 1) {
perror("getrandom");
abort();
}
random_flavor_state = STATE_READY;
}
static void ensure_dev_urandom_is_initialized(void) {
// On platforms where urandom doesn't block at startup, we ensure that the
// kernel has sufficient entropy before continuing.
for (;;) {
int entropy_bits = 0;
if (ioctl(urandom_fd, RNDGETENTCNT, &entropy_bits)) {
fprintf(stderr,
"RNDGETENTCNT on /dev/urandom failed. We cannot determine if the kernel "
"has enough entropy and must abort.\n");
abort();
}
static const int kBitsNeeded = 256;
if (entropy_bits >= kBitsNeeded) {
break;
}
fprintf(stderr,
"RNDGETENTCNT on /dev/urandom indicates that the entropy pool does not "
"have enough entropy. Rather than continue with poor entropy, this "
"process will block until entropy is available.\n");
struct timespec sleep_time = {.tv_sec = 0, .tv_nsec = MILLISECONDS_250 };
nanosleep(&sleep_time, &sleep_time);
}
random_flavor_state = STATE_READY;
}
// wait_for_entropy ensures the entropy pool has been initialized. If
// initialization haven't happened yet, it will block the process.
static void ensure_entropy_state_is_initd_once(void) {
if (random_flavor == USE_GETRANDOM) {
ensure_getrandom_is_initialized();
} else if (random_flavor == USE_DEV_URANDOM) {
ensure_dev_urandom_is_initialized();
} else {
// Indicates a state machine logical error which is fatal.
abort();
}
}
// fill_with_entropy writes |len| bytes of entropy into |out|. It returns one
// on success and zero on error. If |block| is one, this function will block
// until the entropy pool is initialized. Otherwise, this function may fail,
// setting |errno| to |EAGAIN| if the entropy pool has not yet been initialized.
static int fill_with_entropy(uint8_t *out, size_t len, int block, int seed) {
if (len == 0) {
return 1;
}
CRYPTO_once(&initialize_random_flavor_once, init_random_flavor_once);
if (random_flavor == NOT_CHOSEN) {
// This should never happen. It means there is a logical error somewhere.
// Hard bail.
abort();
}
if (block) {
// "Entropy depletion" is not a thing. So, it's enough to ensure the
// entropy pool is initialized once.
CRYPTO_once(&ensure_random_state_is_initialized_once, ensure_entropy_state_is_initd_once);
if (random_flavor_state == STATE_NOT_READY) {
// This should never happen. It means there is a logical error somewhere.
// Hard bail.
abort();
}
}
// Clear |errno| so it has defined value if |read| or |getrandom|
// "successfully" returns zero.
errno = 0;
while (len > 0) {
ssize_t ret = 0;
if (random_flavor == USE_GETRANDOM) {
ret = wrapper_getrandom(out, len, block);
} else {
ret = wrapper_dev_urandom(out, len, block);
}
if (ret <= 0) {
return 0;
}
out += ret;
len -= ret;
}
return 1;
}
// CRYPTO_sysrand puts |requested| random bytes into |out|.
void CRYPTO_sysrand(uint8_t *out, size_t requested) {
if (!fill_with_entropy(out, requested, /*block=*/1, /*seed=*/0)) {
perror("entropy fill failed");
abort();
}
}
int CRYPTO_sysrand_if_available(uint8_t *out, size_t requested) {
if (fill_with_entropy(out, requested, /*block=*/0, /*seed=*/0)) {
return 1;
} else if (errno == EAGAIN) {
OPENSSL_memset(out, 0, requested);
return 0;
} else {
perror("opportunistic entropy fill failed");
abort();
}
}
#endif // OPENSSL_RAND_URANDOM

View File

@@ -0,0 +1,636 @@
// Copyright (c) 2019, Google Inc.
// SPDX-License-Identifier: ISC
#include <gtest/gtest.h>
#include <stdlib.h>
#include <openssl/ctrdrbg.h>
#include <openssl/rand.h>
#include "getrandom_fillin.h"
#include "internal.h"
#include "../ube/internal.h"
#include "../ube/vm_ube_detect.h"
#if defined(OPENSSL_RAND_URANDOM) && \
defined(OPENSSL_X86_64) && \
!defined(BORINGSSL_SHARED_LIBRARY) && \
!defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE) && \
defined(USE_NR_getrandom) && !defined(AWSLC_VM_UBE_TESTING) && \
!defined(DISABLE_CPU_JITTER_ENTROPY)
#include <linux/types.h>
#include <linux/random.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <sys/user.h>
#include "../ube/fork_ube_detect.h"
#include "getrandom_fillin.h"
#include "../test/test_util.h"
#include <cstdlib>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#if !defined(PTRACE_O_EXITKILL)
#define PTRACE_O_EXITKILL (1 << 20)
#endif
#if !defined(PTRACE_O_TRACESYSGOOD)
#define PTRACE_O_TRACESYSGOOD (1)
#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,
kNanoSleep,
kAbort,
};
explicit Event(Syscall syscall) : type(syscall) {}
bool operator==(const Event &other) const {
return type == other.type && length == other.length &&
flags == other.flags && filename == other.filename;
}
static Event GetRandom(size_t length, unsigned flags) {
Event e(Syscall::kGetRandom);
e.length = length;
e.flags = flags;
return e;
}
static Event Open(std::string filename) {
Event e(Syscall::kOpen);
e.filename = std::move(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;
}
static Event NanoSleep() {
Event e(Syscall::kNanoSleep);
return e;
}
std::string String() const {
char buf[256];
switch (type) {
case Syscall::kGetRandom:
snprintf(buf, sizeof(buf), "getrandom(_, %zu, %u)", length, flags);
break;
case Syscall::kOpen:
snprintf(buf, sizeof(buf), "open(%s, _)", filename.c_str());
break;
case Syscall::kUrandomRead:
snprintf(buf, sizeof(buf), "read(urandom_fd, _, %zu)", length);
break;
case Syscall::kNanoSleep:
return "nanosleep(_)";
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;
std::string filename;
};
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;
// ReadString parses string at address |addr| in child process |pid|.
static std::string ReadString(pid_t pid, unsigned long addr) {
std::string result;
size_t i = 0;
while (i < 4096) { // Don't accept paths longer than this.
long data = ptrace(PTRACE_PEEKDATA, pid, addr + i, NULL);
if (data == -1 && errno) {
break;
}
char *p = (char*)&data;
for (size_t j = 0; j < sizeof(long); j++) {
if (p[j] == '\0') {
return result;
}
result += p[j];
}
i += sizeof(long);
}
return result;
}
// HasPrefix returns true of |prefix| is a prefix of |str| and false otherwise.
static bool HasPrefix(const std::string& str, const std::string& prefix) {
return str.length() >= prefix.length() &&
(str.compare(0, prefix.length(), prefix) == 0);
}
// 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)
<< "Child was not stopped with SIGSTOP: " << status;
// 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;
bool urandom_not_ready_was_cleared = false;
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))
<< "Child was not stopped with a syscall stop: " << status;
struct user_regs_struct regs;
ASSERT_EQ(0, ptrace(PTRACE_GETREGS, child_pid, nullptr, &regs));
const auto syscall_number = regs.orig_rax;
static auto previous_syscall = 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: {
std::string filename = ReadString(child_pid,
(syscall_number == __NR_openat) ? regs.rsi : regs.rdi);
// From https://github.com/aws/aws-lc/blob/6c961b6617adb773fd9fb79dd805498e7ecc7a8b/third_party/jitterentropy/jitterentropy-base-user.h#L273
// We do not model these system calls, because they are part of the
// internal implementation detail of Jitter Entropy and there is
// currently no exported method from Jitter Entropy that allow us to
// continuously predict the behaviour.
if (HasPrefix(filename, "/sys/devices/system/cpu/cpu0/cache")) {
break;
}
if (filename != CRYPTO_get_sysgenid_path()) {
out_trace->push_back(Event::Open(filename));
}
is_opening_urandom = (filename == "/dev/urandom");
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_nanosleep: {
// If blocking, an |ioctl| call with command |RNDGETENTCNT| is used. If
// this fails, a delay is injected. The failure happens when the test
// flag |URANDOM_NOT_READY| is set. But since this bit is cleared below
// we detect this event using |urandom_not_ready_was_cleared|.
//
// Second true condition: We can have two or more consecutive
// |nanosleep| calls. This happens if |nanosleep| returns -1. The PRNG
// model only accounts for one |nanosleep| call. Do the same here.
if (urandom_not_ready_was_cleared ||
((flags & URANDOM_ERROR) && (previous_syscall != __NR_nanosleep))) {
out_trace->push_back(Event::NanoSleep());
}
break;
}
// Alias for |__NR_nanosleep| on, at least, Ubuntu 20.04.
case __NR_clock_nanosleep: {
if (urandom_not_ready_was_cleared ||
((flags & URANDOM_ERROR) &&
(previous_syscall != __NR_clock_nanosleep))) {
out_trace->push_back(Event::NanoSleep());
}
break;
}
case __NR_ioctl: {
const int ioctl_fd = regs.rdi;
// Apparently, some operating systems sign-extend registers into the
// register object when read through ptrace. I assume this is when
// registers are 32-bit, while |struct user_regs_struct| contains all
// 64-bit type fields. This is, at least, currently the case on Alpine
// Linux. This works very poorly when checking the RNDGETENTCNT
// condition below. Chop off the leading 32-bits to have a consistent
// check over all operating systems that this test supports.
if (urandom_fd >= 0 && ioctl_fd == urandom_fd &&
(regs.rsi & 0xFFFFFFFF) == RNDGETENTCNT) {
out_trace->push_back(Event::UrandomIoctl());
is_urandom_ioctl = true;
ioctl_output_addr = regs.rdx;
}
}
}
previous_syscall = syscall_number;
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;
urandom_not_ready_was_cleared = true;
}
// 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));
}
static bool have_ube_detection() {
uint64_t tmp_gn = 0;
return CRYPTO_get_ube_generation_number(&tmp_gn) != 0;
}
// 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) {
std::vector<Event> ret;
bool urandom_ready = false;
bool getrandom_ready = false;
// Probe for getrandom support
ret.push_back(Event::GetRandom(1, GRND_NONBLOCK));
// Define callbacks that model system calls made for each of the random
// function flavors defined in urandom.c; currently, this is either getrandom
// or /dev/urandom.|ensure_entropy_pool_is_initialized| models
// |ensure_entropy_state_is_initd_once| while |sysrand| models either
// |wrapper_getrandom| or |wrapper_dev_urandom|.
std::function<void()> ensure_entropy_pool_is_initialized;
std::function<bool(bool, size_t)> sysrand;
if (flags & NO_GETRANDOM) {
ret.push_back(Event::Open(std::string("/dev/urandom")));
if (flags & NO_URANDOM) {
ret.push_back(Event::Abort());
return ret;
}
ensure_entropy_pool_is_initialized = [&ret, &urandom_ready, flags] {
if (urandom_ready) {
return;
}
// Probe urandom for entropy.
ret.push_back(Event::UrandomIoctl());
if (flags & URANDOM_NOT_READY) {
ret.push_back(Event::NanoSleep());
// 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_ready = true;
};
sysrand = [&ret, &ensure_entropy_pool_is_initialized, flags](bool block, size_t len) {
if (block) {
ensure_entropy_pool_is_initialized();
}
ret.push_back(Event::UrandomRead(len));
if (flags & URANDOM_ERROR) {
if (block) {
for (size_t i = 0; i < MAX_BACKOFF_RETRIES; i++) {
ret.push_back(Event::NanoSleep());
ret.push_back(Event::UrandomRead(len));
}
}
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;
ensure_entropy_pool_is_initialized = [&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, &ensure_entropy_pool_is_initialized](bool block, size_t len) {
if (block) {
ensure_entropy_pool_is_initialized();
}
ret.push_back(Event::GetRandom(len, block ? 0 : GRND_NONBLOCK));
return true;
};
}
const size_t kPersonalizationStringLength = CTR_DRBG_ENTROPY_LEN;
const size_t kPredictionResistanceStringLength = RAND_PRED_RESISTANCE_LEN;
const bool kHaveUbeDetection = have_ube_detection();
// We now build the randomness generation model. Only system call events
// can be captured. To build the model, we reason about the expected workflow
// for randomness generation and must correctly predict when a specific
// system call is made. We assume two consecutive RAND_bytes() calls, as
// specified by the test function TestFunction().
//
// First call to RAND_bytes(): Seed the frontend CTR-DRBG using seed source
// and personalization string source. The seed source is the tree-DRBG and
// personalization string the operating system source. The tree-DRBG will use
// Jitter Entropy at its root. The tree-DRBG per-thread CTR-DRBG will use the
// operating system entropy source for prediction resistance if there is no
// UBE detection.
// Capture tree-DRBG per-thread CTR-DRBG maybe using prediction resistance.
if (!kHaveUbeDetection) {
if (!sysrand(true, kPredictionResistanceStringLength)) {
return ret;
}
}
// Seeding of frontend CTR-DRBG will always use a personalization string.
if (!sysrand(true, kPersonalizationStringLength)) {
return ret;
}
// Second call to RAND_bytes(): If there is no UBE detection, we initiate a
// reseed before generating any output.
if (!kHaveUbeDetection) {
// Again, the tree-DRBG per-thread CTR-DRBG will use prediction resistance
// if there is no UBE detection.
if (!kHaveUbeDetection) {
if (!sysrand(true, kPredictionResistanceStringLength)) {
return ret;
}
}
// Seeding of frontend CTR-DRBG will always use a personalization string.
if (!sysrand(true, kPersonalizationStringLength)) {
return ret;
}
}
return ret;
}
#define SCOPED_TRACE_FLAG(flag) \
snprintf(buf, sizeof(buf), #flag ": %d", (flags & flag) != 0); \
SCOPED_TRACE(buf);
// Tests that |TestFunctionPRNGModel| is a correct model for the code in
// urandom.c, at least to the limits of the the |Event| type.
//
// |TestFunctionPRNGModel| creates the entropy function call model, for
// various configs. |GetTrace| records the actual entropy function calls for
// each config and compares it against the model.
// Only system entropy function calls are modeled e.g. /dev/random and getrandom.
TEST(URandomTest, Test) {
char buf[256];
// Some Android systems lack getrandom.
uint8_t scratch[1];
const bool has_getrandom =
(syscall(__NR_getrandom, scratch, sizeof(scratch), GRND_NONBLOCK) != -1 ||
errno != ENOSYS);
for (unsigned flags = 0; flags < NEXT_FLAG; flags++) {
if (!has_getrandom && !(flags & NO_GETRANDOM)) {
continue;
}
// Prints test configuration if an error is reported below. Scoped to this
// iteration of the for-loop.
SCOPED_TRACE_FLAG(NO_GETRANDOM);
SCOPED_TRACE_FLAG(NO_URANDOM);
SCOPED_TRACE_FLAG(GETRANDOM_NOT_READY);
SCOPED_TRACE_FLAG(URANDOM_NOT_READY);
SCOPED_TRACE_FLAG(GETRANDOM_ERROR);
SCOPED_TRACE_FLAG(URANDOM_ERROR);
// From PRNG model, generate the expected trace of system calls.
const std::vector<Event> expected_trace = TestFunctionPRNGModel(flags);
// Generate the real trace of system calls.
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);
maybeDisableSomeForkUbeDetectMechanisms();
return RUN_ALL_TESTS();
}
#else
int main(int argc, char **argv) {
printf("PASS\n");
return 0;
}
#endif // X86_64 && !SHARED_LIBRARY && !UNSAFE_DETERMINISTIC_MODE &&
// USE_NR_getrandom && !AWSLC_VM_UBE_TESTING

View File

@@ -0,0 +1,11 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <openssl/ctrdrbg.h>
#include "internal.h"
int vm_ube_fallback_get_seed(uint8_t seed[CTR_DRBG_ENTROPY_LEN]) {
CRYPTO_sysrand(seed, CTR_DRBG_ENTROPY_LEN);
return 1;
}

View File

@@ -0,0 +1,32 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <gtest/gtest.h>
#include <openssl/rand.h>
#include <openssl/span.h>
#include "internal.h"
#include "../test/test_util.h"
// This test is, strictly speaking, flaky, but we use large enough buffers
// (48 bytes) that the probability of failing when we should pass is negligible.
TEST(VmUbeFallbackTest, NotObviouslyBroken) {
static const uint8_t kZeros[CTR_DRBG_ENTROPY_LEN] = {0};
uint8_t seed1[CTR_DRBG_ENTROPY_LEN];
uint8_t seed2[CTR_DRBG_ENTROPY_LEN];
ASSERT_TRUE(vm_ube_fallback_get_seed(seed1));
ASSERT_TRUE(vm_ube_fallback_get_seed(seed2));
EXPECT_NE(Bytes(seed1), Bytes(seed2));
EXPECT_NE(Bytes(seed1), Bytes(kZeros));
EXPECT_NE(Bytes(seed2), Bytes(kZeros));
uint8_t seed3[CTR_DRBG_ENTROPY_LEN];
// Ensure that the implementation is not simply returning the memory unchanged.
memcpy(seed3, seed1, CTR_DRBG_ENTROPY_LEN);
ASSERT_TRUE(vm_ube_fallback_get_seed(seed1));
EXPECT_NE(Bytes(seed1), Bytes(seed3));
}

View File

@@ -0,0 +1,83 @@
// Copyright (c) 2014, Google Inc.
// SPDX-License-Identifier: ISC
#include <openssl/rand.h>
#include "internal.h"
#include "../internal.h"
#if defined(OPENSSL_RAND_WINDOWS)
#include <limits.h>
#include <stdlib.h>
OPENSSL_MSVC_PRAGMA(warning(push, 3))
#include <windows.h>
// ProcessPrng (from `bcryptprimitives.dll`) is only available on Windows 8+.
#if !defined(__MINGW32__) && defined(_WIN32_WINNT) && _WIN32_WINNT <= _WIN32_WINNT_WIN7
#define AWSLC_WINDOWS_7_COMPAT
#endif
#if defined(AWSLC_WINDOWS_7_COMPAT) || \
(WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP))
#include <bcrypt.h>
OPENSSL_MSVC_PRAGMA(comment(lib, "bcrypt.lib"))
#endif // AWSLC_WINDOWS_7_COMPAT || (WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP)
OPENSSL_MSVC_PRAGMA(warning(pop))
#if defined(AWSLC_WINDOWS_7_COMPAT) || \
(WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP))
void CRYPTO_sysrand(uint8_t *out, size_t requested) {
while (requested > 0) {
ULONG output_bytes_this_pass = ULONG_MAX;
if (requested < output_bytes_this_pass) {
output_bytes_this_pass = (ULONG)requested;
}
if (!BCRYPT_SUCCESS(BCryptGenRandom(
/*hAlgorithm=*/NULL, out, output_bytes_this_pass,
BCRYPT_USE_SYSTEM_PREFERRED_RNG))) {
abort();
}
requested -= output_bytes_this_pass;
out += output_bytes_this_pass;
}
}
#else
// See: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng
typedef BOOL (WINAPI *ProcessPrngFunction)(PBYTE pbData, SIZE_T cbData);
static ProcessPrngFunction g_processprng_fn = NULL;
static CRYPTO_once_t once = CRYPTO_ONCE_INIT;
static void init_processprng(void) {
HMODULE hmod = LoadLibraryW(L"bcryptprimitives");
if (hmod == NULL) {
abort();
}
g_processprng_fn = (ProcessPrngFunction)(void(*)(void))GetProcAddress(hmod, "ProcessPrng");
if (g_processprng_fn == NULL) {
abort();
}
}
void CRYPTO_sysrand(uint8_t *out, size_t requested) {
CRYPTO_once(&once, init_processprng);
// On non-UWP configurations, use ProcessPrng instead of BCryptGenRandom
// to avoid accessing resources that may be unavailable inside the
// Chromium sandbox. See https://crbug.com/74242
if (!g_processprng_fn(out, requested)) {
abort();
}
}
#endif // AWSLC_WINDOWS_7_COMPAT || (WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP)
#endif // OPENSSL_RAND_WINDOWS