exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / getrandom.c
blob44283cf1816ae322657d3521a7bdf02140c038ed
1 /* Obtain a series of random bytes.
3 Copyright 2020-2024 Free Software Foundation, Inc.
5 This file is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as
7 published by the Free Software Foundation; either version 2.1 of the
8 License, or (at your option) any later version.
10 This file is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
18 /* Written by Paul Eggert. */
20 #include <config.h>
22 #include <sys/random.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <unistd.h>
28 #if defined _WIN32 && ! defined __CYGWIN__
29 # define WIN32_LEAN_AND_MEAN
30 # include <windows.h>
31 # if HAVE_BCRYPT_H
32 # include <bcrypt.h>
33 # else
34 # define NTSTATUS LONG
35 typedef void * BCRYPT_ALG_HANDLE;
36 # define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002
37 # if HAVE_LIB_BCRYPT
38 extern NTSTATUS WINAPI BCryptGenRandom (BCRYPT_ALG_HANDLE, UCHAR *, ULONG, ULONG);
39 # endif
40 # endif
41 # if !HAVE_LIB_BCRYPT
42 # include <wincrypt.h>
43 # ifndef CRYPT_VERIFY_CONTEXT
44 # define CRYPT_VERIFY_CONTEXT 0xF0000000
45 # endif
46 # endif
47 #endif
49 #include "minmax.h"
51 #if defined _WIN32 && ! defined __CYGWIN__
53 /* Don't assume that UNICODE is not defined. */
54 # undef LoadLibrary
55 # define LoadLibrary LoadLibraryA
56 # undef CryptAcquireContext
57 # define CryptAcquireContext CryptAcquireContextA
59 # if !HAVE_LIB_BCRYPT
61 /* Avoid warnings from gcc -Wcast-function-type. */
62 # define GetProcAddress \
63 (void *) GetProcAddress
65 /* BCryptGenRandom with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag works only
66 starting with Windows 7. */
67 typedef NTSTATUS (WINAPI * BCryptGenRandomFuncType) (BCRYPT_ALG_HANDLE, UCHAR *, ULONG, ULONG);
68 static BCryptGenRandomFuncType BCryptGenRandomFunc = NULL;
69 static BOOL initialized = FALSE;
71 static void
72 initialize (void)
74 HMODULE bcrypt = LoadLibrary ("bcrypt.dll");
75 if (bcrypt != NULL)
77 BCryptGenRandomFunc =
78 (BCryptGenRandomFuncType) GetProcAddress (bcrypt, "BCryptGenRandom");
80 initialized = TRUE;
83 # else
85 # define BCryptGenRandomFunc BCryptGenRandom
87 # endif
89 #else
90 /* These devices exist on all platforms except native Windows. */
92 /* Name of a device through which the kernel returns high quality random
93 numbers, from an entropy pool. When the pool is empty, the call blocks
94 until entropy sources have added enough bits of entropy. */
95 # ifndef NAME_OF_RANDOM_DEVICE
96 # define NAME_OF_RANDOM_DEVICE "/dev/random"
97 # endif
99 /* Name of a device through which the kernel returns random or pseudo-random
100 numbers. It uses an entropy pool, but, in order to avoid blocking, adds
101 bits generated by a pseudo-random number generator, as needed. */
102 # ifndef NAME_OF_NONCE_DEVICE
103 # define NAME_OF_NONCE_DEVICE "/dev/urandom"
104 # endif
106 #endif
108 /* Set BUFFER (of size LENGTH) to random bytes under the control of FLAGS.
109 Return the number of bytes written (> 0).
110 Upon error, return -1 and set errno. */
111 ssize_t
112 getrandom (void *buffer, size_t length, unsigned int flags)
113 #undef getrandom
115 #if defined _WIN32 && ! defined __CYGWIN__
116 /* BCryptGenRandom, defined in <bcrypt.h>
117 <https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom>
118 with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag
119 works in Windows 7 and newer. */
120 static int bcrypt_not_working /* = 0 */;
121 if (!bcrypt_not_working)
123 # if !HAVE_LIB_BCRYPT
124 if (!initialized)
125 initialize ();
126 # endif
127 if (BCryptGenRandomFunc != NULL
128 && BCryptGenRandomFunc (NULL, buffer, length,
129 BCRYPT_USE_SYSTEM_PREFERRED_RNG)
130 == 0 /*STATUS_SUCCESS*/)
131 return length;
132 bcrypt_not_working = 1;
134 # if !HAVE_LIB_BCRYPT
135 /* CryptGenRandom, defined in <wincrypt.h>
136 <https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom>
137 works in older releases as well, but is now deprecated.
138 CryptAcquireContext, defined in <wincrypt.h>
139 <https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecontexta> */
141 static int crypt_initialized /* = 0 */;
142 static HCRYPTPROV provider;
143 if (!crypt_initialized)
145 if (CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL,
146 CRYPT_VERIFY_CONTEXT))
147 crypt_initialized = 1;
148 else
149 crypt_initialized = -1;
151 if (crypt_initialized >= 0)
153 if (!CryptGenRandom (provider, length, buffer))
155 errno = EIO;
156 return -1;
158 return length;
161 # endif
162 errno = ENOSYS;
163 return -1;
164 #elif HAVE_GETRANDOM
165 return getrandom (buffer, length, flags);
166 #else
167 static int randfd[2] = { -1, -1 };
168 bool devrandom = (flags & GRND_RANDOM) != 0;
169 int fd = randfd[devrandom];
171 if (fd < 0)
173 static char const randdevice[][MAX (sizeof NAME_OF_NONCE_DEVICE,
174 sizeof NAME_OF_RANDOM_DEVICE)]
175 = { NAME_OF_NONCE_DEVICE, NAME_OF_RANDOM_DEVICE };
176 int oflags = (O_RDONLY + O_CLOEXEC
177 + (flags & GRND_NONBLOCK ? O_NONBLOCK : 0));
178 fd = open (randdevice[devrandom], oflags);
179 if (fd < 0)
181 if (errno == ENOENT || errno == ENOTDIR)
182 errno = ENOSYS;
183 return -1;
185 randfd[devrandom] = fd;
188 return read (fd, buffer, length);
189 #endif