| /* Obtain a series of random bytes. | |
| Copyright 2020-2021 Free Software Foundation, Inc. | |
| This program is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ | |
| /* Written by Paul Eggert. */ | |
| #include <config.h> | |
| #include <sys/random.h> | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <stdbool.h> | |
| #include <unistd.h> | |
| #if defined _WIN32 && ! defined __CYGWIN__ | |
| # define WIN32_LEAN_AND_MEAN | |
| # include <windows.h> | |
| # if HAVE_BCRYPT_H | |
| # include <bcrypt.h> | |
| # else | |
| # define NTSTATUS LONG | |
| typedef void * BCRYPT_ALG_HANDLE; | |
| # define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002 | |
| # if HAVE_LIB_BCRYPT | |
| extern NTSTATUS WINAPI BCryptGenRandom (BCRYPT_ALG_HANDLE, UCHAR *, ULONG, ULONG); | |
| # endif | |
| # endif | |
| # if !HAVE_LIB_BCRYPT | |
| # include <wincrypt.h> | |
| # ifndef CRYPT_VERIFY_CONTEXT | |
| # define CRYPT_VERIFY_CONTEXT 0xF0000000 | |
| # endif | |
| # endif | |
| #endif | |
| #include "minmax.h" | |
| #if defined _WIN32 && ! defined __CYGWIN__ | |
| /* Don't assume that UNICODE is not defined. */ | |
| # undef LoadLibrary | |
| # define LoadLibrary LoadLibraryA | |
| # undef CryptAcquireContext | |
| # define CryptAcquireContext CryptAcquireContextA | |
| # if !HAVE_LIB_BCRYPT | |
| /* Avoid warnings from gcc -Wcast-function-type. */ | |
| # define GetProcAddress \ | |
| (void *) GetProcAddress | |
| /* BCryptGenRandom with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag works only | |
| starting with Windows 7. */ | |
| typedef NTSTATUS (WINAPI * BCryptGenRandomFuncType) (BCRYPT_ALG_HANDLE, UCHAR *, ULONG, ULONG); | |
| static BCryptGenRandomFuncType BCryptGenRandomFunc = NULL; | |
| static BOOL initialized = FALSE; | |
| static void | |
| initialize (void) | |
| { | |
| HMODULE bcrypt = LoadLibrary ("bcrypt.dll"); | |
| if (bcrypt != NULL) | |
| { | |
| BCryptGenRandomFunc = | |
| (BCryptGenRandomFuncType) GetProcAddress (bcrypt, "BCryptGenRandom"); | |
| } | |
| initialized = TRUE; | |
| } | |
| # else | |
| # define BCryptGenRandomFunc BCryptGenRandom | |
| # endif | |
| #else | |
| /* These devices exist on all platforms except native Windows. */ | |
| /* Name of a device through which the kernel returns high quality random | |
| numbers, from an entropy pool. When the pool is empty, the call blocks | |
| until entropy sources have added enough bits of entropy. */ | |
| # ifndef NAME_OF_RANDOM_DEVICE | |
| # define NAME_OF_RANDOM_DEVICE "/dev/random" | |
| # endif | |
| /* Name of a device through which the kernel returns random or pseudo-random | |
| numbers. It uses an entropy pool, but, in order to avoid blocking, adds | |
| bits generated by a pseudo-random number generator, as needed. */ | |
| # ifndef NAME_OF_NONCE_DEVICE | |
| # define NAME_OF_NONCE_DEVICE "/dev/urandom" | |
| # endif | |
| #endif | |
| /* Set BUFFER (of size LENGTH) to random bytes under the control of FLAGS. | |
| Return the number of bytes written (> 0). | |
| Upon error, return -1 and set errno. */ | |
| ssize_t | |
| getrandom (void *buffer, size_t length, unsigned int flags) | |
| #undef getrandom | |
| { | |
| #if defined _WIN32 && ! defined __CYGWIN__ | |
| /* BCryptGenRandom, defined in <bcrypt.h> | |
| <https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom> | |
| with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag | |
| works in Windows 7 and newer. */ | |
| static int bcrypt_not_working /* = 0 */; | |
| if (!bcrypt_not_working) | |
| { | |
| # if !HAVE_LIB_BCRYPT | |
| if (!initialized) | |
| initialize (); | |
| # endif | |
| if (BCryptGenRandomFunc != NULL | |
| && BCryptGenRandomFunc (NULL, buffer, length, | |
| BCRYPT_USE_SYSTEM_PREFERRED_RNG) | |
| == 0 /*STATUS_SUCCESS*/) | |
| return length; | |
| bcrypt_not_working = 1; | |
| } | |
| # if !HAVE_LIB_BCRYPT | |
| /* CryptGenRandom, defined in <wincrypt.h> | |
| <https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom> | |
| works in older releases as well, but is now deprecated. | |
| CryptAcquireContext, defined in <wincrypt.h> | |
| <https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecontexta> */ | |
| { | |
| static int crypt_initialized /* = 0 */; | |
| static HCRYPTPROV provider; | |
| if (!crypt_initialized) | |
| { | |
| if (CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL, | |
| CRYPT_VERIFY_CONTEXT)) | |
| crypt_initialized = 1; | |
| else | |
| crypt_initialized = -1; | |
| } | |
| if (crypt_initialized >= 0) | |
| { | |
| if (!CryptGenRandom (provider, length, buffer)) | |
| { | |
| errno = EIO; | |
| return -1; | |
| } | |
| return length; | |
| } | |
| } | |
| # endif | |
| errno = ENOSYS; | |
| return -1; | |
| #elif HAVE_GETRANDOM | |
| return getrandom (buffer, length, flags); | |
| #else | |
| static int randfd[2] = { -1, -1 }; | |
| bool devrandom = (flags & GRND_RANDOM) != 0; | |
| int fd = randfd[devrandom]; | |
| if (fd < 0) | |
| { | |
| static char const randdevice[][MAX (sizeof NAME_OF_NONCE_DEVICE, | |
| sizeof NAME_OF_RANDOM_DEVICE)] | |
| = { NAME_OF_NONCE_DEVICE, NAME_OF_RANDOM_DEVICE }; | |
| int oflags = (O_RDONLY + O_CLOEXEC | |
| + (flags & GRND_NONBLOCK ? O_NONBLOCK : 0)); | |
| fd = open (randdevice[devrandom], oflags); | |
| if (fd < 0) | |
| return fd; | |
| randfd[devrandom] = fd; | |
| } | |
| return read (fd, buffer, length); | |
| #endif | |
| } |