|
| 1 | +/************************************************************************* |
| 2 | + * Copyright (c) 2020-2021 Elichai Turkel * |
| 3 | + * Distributed under the CC0 software license, see the accompanying file * |
| 4 | + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * |
| 5 | + *************************************************************************/ |
| 6 | + |
| 7 | +/* |
| 8 | + * This file is an attempt at collecting best practice methods for obtaining randomness with different operating systems. |
| 9 | + * It may be out-of-date. Consult the documentation of the operating system before considering to use the methods below. |
| 10 | + * |
| 11 | + * Platform randomness sources: |
| 12 | + * 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 |
| 13 | + * 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 |
| 14 | + * 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 |
| 15 | + * OpenBSD -> `getentropy(2)`(`unistd.h`), if not available `/dev/urandom` should be used. https://man.openbsd.org/getentropy, https://man.openbsd.org/urandom |
| 16 | + * Windows -> `BCryptGenRandom`(`bcrypt.h`). https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom |
| 17 | + */ |
| 18 | + |
| 19 | +#if defined(_WIN32) |
| 20 | +/* |
| 21 | + * The defined WIN32_NO_STATUS macro disables return code definitions in |
| 22 | + * windows.h, which avoids "macro redefinition" MSVC warnings in ntstatus.h. |
| 23 | + */ |
| 24 | +#define WIN32_NO_STATUS |
| 25 | +#include <windows.h> |
| 26 | +#undef WIN32_NO_STATUS |
| 27 | +#include <ntstatus.h> |
| 28 | +#include <bcrypt.h> |
| 29 | +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) |
| 30 | +#include <sys/random.h> |
| 31 | +#elif defined(__OpenBSD__) |
| 32 | +#include <unistd.h> |
| 33 | +#else |
| 34 | +#error "Couldn't identify the OS" |
| 35 | +#endif |
| 36 | + |
| 37 | +#include <stddef.h> |
| 38 | +#include <limits.h> |
| 39 | +#include <stdio.h> |
| 40 | +#include <string.h> |
| 41 | + |
| 42 | +/* Returns 1 on success, and 0 on failure. */ |
| 43 | +static int fill_random(unsigned char* data, size_t size) { |
| 44 | +#if defined(_WIN32) |
| 45 | + NTSTATUS res = BCryptGenRandom(NULL, data, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG); |
| 46 | + if (res != STATUS_SUCCESS || size > ULONG_MAX) { |
| 47 | + return 0; |
| 48 | + } else { |
| 49 | + return 1; |
| 50 | + } |
| 51 | +#elif defined(__linux__) || defined(__FreeBSD__) |
| 52 | + /* If `getrandom(2)` is not available you should fallback to /dev/urandom */ |
| 53 | + ssize_t res = getrandom(data, size, 0); |
| 54 | + if (res < 0 || (size_t)res != size ) { |
| 55 | + return 0; |
| 56 | + } else { |
| 57 | + return 1; |
| 58 | + } |
| 59 | +#elif defined(__APPLE__) || defined(__OpenBSD__) |
| 60 | + /* If `getentropy(2)` is not available you should fallback to either |
| 61 | + * `SecRandomCopyBytes` or /dev/urandom */ |
| 62 | + int res = getentropy(data, size); |
| 63 | + if (res == 0) { |
| 64 | + return 1; |
| 65 | + } else { |
| 66 | + return 0; |
| 67 | + } |
| 68 | +#endif |
| 69 | + return 0; |
| 70 | +} |
| 71 | + |
| 72 | +static void print_hex(unsigned char* data, size_t size) { |
| 73 | + size_t i; |
| 74 | + printf("0x"); |
| 75 | + for (i = 0; i < size; i++) { |
| 76 | + printf("%02x", data[i]); |
| 77 | + } |
| 78 | + printf("\n"); |
| 79 | +} |
| 80 | + |
| 81 | +#if defined(_MSC_VER) |
| 82 | +// For SecureZeroMemory |
| 83 | +#include <Windows.h> |
| 84 | +#endif |
| 85 | +/* Cleanses memory to prevent leaking sensitive info. Won't be optimized out. */ |
| 86 | +static void secure_erase(void *ptr, size_t len) { |
| 87 | +#if defined(_MSC_VER) |
| 88 | + /* SecureZeroMemory is guaranteed not to be optimized out by MSVC. */ |
| 89 | + SecureZeroMemory(ptr, len); |
| 90 | +#elif defined(__GNUC__) |
| 91 | + /* We use a memory barrier that scares the compiler away from optimizing out the memset. |
| 92 | + * |
| 93 | + * Quoting Adam Langley <[email protected]> in commit ad1907fe73334d6c696c8539646c21b11178f20f |
| 94 | + * in BoringSSL (ISC License): |
| 95 | + * As best as we can tell, this is sufficient to break any optimisations that |
| 96 | + * might try to eliminate "superfluous" memsets. |
| 97 | + * This method used in memzero_explicit() the Linux kernel, too. Its advantage is that it is |
| 98 | + * pretty efficient, because the compiler can still implement the memset() efficiently, |
| 99 | + * just not remove it entirely. See "Dead Store Elimination (Still) Considered Harmful" by |
| 100 | + * Yang et al. (USENIX Security 2017) for more background. |
| 101 | + */ |
| 102 | + memset(ptr, 0, len); |
| 103 | + __asm__ __volatile__("" : : "r"(ptr) : "memory"); |
| 104 | +#else |
| 105 | + void *(*volatile const volatile_memset)(void *, int, size_t) = memset; |
| 106 | + volatile_memset(ptr, 0, len); |
| 107 | +#endif |
| 108 | +} |
0 commit comments