2022-12-20 21:11:14 +00:00
|
|
|
/*************************************************************************
|
|
|
|
* Copyright (c) 2020-2021 Elichai Turkel *
|
|
|
|
* Distributed under the CC0 software license, see the accompanying file *
|
|
|
|
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
|
|
|
|
*************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This file is an attempt at collecting best practice methods for obtaining randomness with different operating systems.
|
|
|
|
* It may be out-of-date. Consult the documentation of the operating system before considering to use the methods below.
|
|
|
|
*
|
|
|
|
* Platform randomness sources:
|
|
|
|
* Linux -> `getrandom(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. http://man7.org/linux/man-pages/man2/getrandom.2.html, https://linux.die.net/man/4/urandom
|
|
|
|
* macOS -> `getentropy(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. https://www.unix.com/man-page/mojave/2/getentropy, https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man4/random.4.auto.html
|
|
|
|
* FreeBSD -> `getrandom(2)`(`sys/random.h`), if not available `kern.arandom` should be used. https://www.freebsd.org/cgi/man.cgi?query=getrandom, https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
|
|
|
|
* OpenBSD -> `getentropy(2)`(`unistd.h`), if not available `/dev/urandom` should be used. https://man.openbsd.org/getentropy, https://man.openbsd.org/urandom
|
|
|
|
* Windows -> `BCryptGenRandom`(`bcrypt.h`). https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if defined(_WIN32)
|
2023-09-27 18:37:09 +00:00
|
|
|
/*
|
|
|
|
* The defined WIN32_NO_STATUS macro disables return code definitions in
|
|
|
|
* windows.h, which avoids "macro redefinition" MSVC warnings in ntstatus.h.
|
|
|
|
*/
|
|
|
|
#define WIN32_NO_STATUS
|
2022-12-20 21:11:14 +00:00
|
|
|
#include <windows.h>
|
2023-09-27 18:37:09 +00:00
|
|
|
#undef WIN32_NO_STATUS
|
2022-12-20 21:11:14 +00:00
|
|
|
#include <ntstatus.h>
|
|
|
|
#include <bcrypt.h>
|
|
|
|
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
|
|
|
|
#include <sys/random.h>
|
|
|
|
#elif defined(__OpenBSD__)
|
|
|
|
#include <unistd.h>
|
|
|
|
#else
|
|
|
|
#error "Couldn't identify the OS"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
|
|
/* Returns 1 on success, and 0 on failure. */
|
|
|
|
static int fill_random(unsigned char* data, size_t size) {
|
|
|
|
#if defined(_WIN32)
|
|
|
|
NTSTATUS res = BCryptGenRandom(NULL, data, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
|
|
|
if (res != STATUS_SUCCESS || size > ULONG_MAX) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#elif defined(__linux__) || defined(__FreeBSD__)
|
|
|
|
/* If `getrandom(2)` is not available you should fallback to /dev/urandom */
|
|
|
|
ssize_t res = getrandom(data, size, 0);
|
|
|
|
if (res < 0 || (size_t)res != size ) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#elif defined(__APPLE__) || defined(__OpenBSD__)
|
|
|
|
/* If `getentropy(2)` is not available you should fallback to either
|
|
|
|
* `SecRandomCopyBytes` or /dev/urandom */
|
|
|
|
int res = getentropy(data, size);
|
|
|
|
if (res == 0) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_hex(unsigned char* data, size_t size) {
|
|
|
|
size_t i;
|
|
|
|
printf("0x");
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
printf("%02x", data[i]);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
2023-09-27 18:37:09 +00:00
|
|
|
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
// For SecureZeroMemory
|
|
|
|
#include <Windows.h>
|
|
|
|
#endif
|
|
|
|
/* Cleanses memory to prevent leaking sensitive info. Won't be optimized out. */
|
|
|
|
static void secure_erase(void *ptr, size_t len) {
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
/* SecureZeroMemory is guaranteed not to be optimized out by MSVC. */
|
|
|
|
SecureZeroMemory(ptr, len);
|
|
|
|
#elif defined(__GNUC__)
|
|
|
|
/* We use a memory barrier that scares the compiler away from optimizing out the memset.
|
|
|
|
*
|
|
|
|
* Quoting Adam Langley <agl@google.com> in commit ad1907fe73334d6c696c8539646c21b11178f20f
|
|
|
|
* in BoringSSL (ISC License):
|
|
|
|
* As best as we can tell, this is sufficient to break any optimisations that
|
|
|
|
* might try to eliminate "superfluous" memsets.
|
|
|
|
* This method used in memzero_explicit() the Linux kernel, too. Its advantage is that it is
|
|
|
|
* pretty efficient, because the compiler can still implement the memset() efficiently,
|
|
|
|
* just not remove it entirely. See "Dead Store Elimination (Still) Considered Harmful" by
|
|
|
|
* Yang et al. (USENIX Security 2017) for more background.
|
|
|
|
*/
|
|
|
|
memset(ptr, 0, len);
|
|
|
|
__asm__ __volatile__("" : : "r"(ptr) : "memory");
|
|
|
|
#else
|
|
|
|
void *(*volatile const volatile_memset)(void *, int, size_t) = memset;
|
|
|
|
volatile_memset(ptr, 0, len);
|
|
|
|
#endif
|
|
|
|
}
|