123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- /* 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 Lesser 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 Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser 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
- }
|