/************************************************************************* * 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) /* * 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 #include #undef WIN32_NO_STATUS #include #include #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) #include #elif defined(__OpenBSD__) #include #else #error "Couldn't identify the OS" #endif #include #include #include /* 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"); } #if defined(_MSC_VER) // For SecureZeroMemory #include #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 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() efficently, * 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 }