Line | Count | Source (jump to first uncovered line) |
1 | | // osrng.cpp - originally written and placed in the public domain by Wei Dai |
2 | | |
3 | | // Thanks to Leonard Janke for the suggestion for AutoSeededRandomPool. |
4 | | |
5 | | #include "pch.h" |
6 | | #include "config.h" |
7 | | |
8 | | #ifndef CRYPTOPP_IMPORTS |
9 | | |
10 | | // Win32 has CryptoAPI and <wincrypt.h>. Windows 10 and Windows Store 10 have CNG and <bcrypt.h>. |
11 | | // There's a hole for Windows Phone 8 and Windows Store 8. There is no userland RNG available. |
12 | | // Also see http://www.drdobbs.com/windows/using-c-and-com-with-winrt/240168150 and |
13 | | // http://stackoverflow.com/questions/36974545/random-numbers-for-windows-phone-8-and-windows-store-8 and |
14 | | // https://social.msdn.microsoft.com/Forums/vstudio/en-US/25b83e13-c85f-4aa1-a057-88a279ea3fd6/what-crypto-random-generator-c-code-could-use-on-wp81 |
15 | | #if defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(OS_RNG_AVAILABLE) |
16 | | # pragma message("WARNING: Compiling for Windows but an OS RNG is not available. This is likely a Windows Phone 8 or Windows Store 8 app.") |
17 | | #endif |
18 | | |
19 | | #if !defined(NO_OS_DEPENDENCE) && defined(OS_RNG_AVAILABLE) |
20 | | |
21 | | #include "osrng.h" |
22 | | #include "rng.h" |
23 | | |
24 | | // FreeBSD links /dev/urandom -> /dev/random. It showed up when we added |
25 | | // O_NOFOLLOW to harden the non-blocking generator. Use Arc4Random instead |
26 | | // for a non-blocking generator. Arc4Random is cryptographic quality prng |
27 | | // based on ChaCha20. The ChaCha20 generator is seeded from /dev/random, |
28 | | // so we can't completely avoid the blocking. |
29 | | // https://www.freebsd.org/cgi/man.cgi?query=arc4random_buf. |
30 | | #ifdef __FreeBSD__ |
31 | | # define DONT_USE_O_NOFOLLOW 1 |
32 | | # define USE_FREEBSD_ARC4RANDOM 1 |
33 | | # include <stdlib.h> |
34 | | #endif |
35 | | |
36 | | // Solaris links /dev/urandom -> ../devices/pseudo/random@0:urandom |
37 | | // We can't access the device. Avoid O_NOFOLLOW for the platform. |
38 | | #ifdef __sun |
39 | | # define DONT_USE_O_NOFOLLOW 1 |
40 | | #endif |
41 | | |
42 | | // And other OSes that don't define it |
43 | | #ifndef O_NOFOLLOW |
44 | | # define DONT_USE_O_NOFOLLOW 1 |
45 | | #endif |
46 | | |
47 | | #ifdef CRYPTOPP_WIN32_AVAILABLE |
48 | | #define WIN32_LEAN_AND_MEAN |
49 | | #include <windows.h> |
50 | | #ifndef ERROR_INCORRECT_SIZE |
51 | | # define ERROR_INCORRECT_SIZE 0x000005B6 |
52 | | #endif |
53 | | #if defined(USE_MS_CRYPTOAPI) |
54 | | #include <wincrypt.h> |
55 | | #ifndef CRYPT_NEWKEYSET |
56 | | # define CRYPT_NEWKEYSET 0x00000008 |
57 | | #endif |
58 | | #ifndef CRYPT_MACHINE_KEYSET |
59 | | # define CRYPT_MACHINE_KEYSET 0x00000020 |
60 | | #endif |
61 | | #elif defined(USE_MS_CNGAPI) |
62 | | #include <bcrypt.h> |
63 | | #ifndef BCRYPT_SUCCESS |
64 | | # define BCRYPT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) |
65 | | #endif |
66 | | #ifndef STATUS_INVALID_PARAMETER |
67 | | # define STATUS_INVALID_PARAMETER 0xC000000D |
68 | | #endif |
69 | | #ifndef STATUS_INVALID_HANDLE |
70 | | # define STATUS_INVALID_HANDLE 0xC0000008 |
71 | | #endif |
72 | | #endif |
73 | | #endif // Win32 |
74 | | |
75 | | #ifdef CRYPTOPP_UNIX_AVAILABLE |
76 | | #include <errno.h> |
77 | | #include <fcntl.h> |
78 | | #include <unistd.h> |
79 | | #endif |
80 | | |
81 | | NAMESPACE_BEGIN(CryptoPP) |
82 | | |
83 | | #if defined(NONBLOCKING_RNG_AVAILABLE) || defined(BLOCKING_RNG_AVAILABLE) |
84 | | OS_RNG_Err::OS_RNG_Err(const std::string &operation) |
85 | | : Exception(OTHER_ERROR, "OS_Rng: " + operation + " operation failed with error " + |
86 | | #ifdef CRYPTOPP_WIN32_AVAILABLE |
87 | | "0x" + IntToString(GetLastError(), 16) |
88 | | #else |
89 | | IntToString(errno) |
90 | | #endif |
91 | | ) |
92 | 0 | { |
93 | 0 | } |
94 | | #endif |
95 | | |
96 | | #ifdef NONBLOCKING_RNG_AVAILABLE |
97 | | |
98 | | #ifdef CRYPTOPP_WIN32_AVAILABLE |
99 | | |
100 | | #if defined(USE_MS_CNGAPI) |
101 | | inline DWORD NtStatusToErrorCode(NTSTATUS status) |
102 | | { |
103 | | if (status == static_cast<NTSTATUS>(STATUS_INVALID_PARAMETER)) |
104 | | return ERROR_INVALID_PARAMETER; |
105 | | else if (status == static_cast<NTSTATUS>(STATUS_INVALID_HANDLE)) |
106 | | return ERROR_INVALID_HANDLE; |
107 | | else |
108 | | return static_cast<DWORD>(status); |
109 | | } |
110 | | #endif |
111 | | |
112 | | #if defined(UNICODE) || defined(_UNICODE) |
113 | | # define CRYPTOPP_CONTAINER L"Crypto++ RNG" |
114 | | #else |
115 | | # define CRYPTOPP_CONTAINER "Crypto++ RNG" |
116 | | #endif |
117 | | |
118 | | MicrosoftCryptoProvider::MicrosoftCryptoProvider() : m_hProvider(0) |
119 | | { |
120 | | #if defined(USE_MS_CRYPTOAPI) |
121 | | // See http://support.microsoft.com/en-us/kb/238187 for CRYPT_NEWKEYSET fallback strategy |
122 | | if (!CryptAcquireContext(&m_hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) |
123 | | { |
124 | | const DWORD firstErr = GetLastError(); |
125 | | if (!CryptAcquireContext(&m_hProvider, CRYPTOPP_CONTAINER, 0, PROV_RSA_FULL, CRYPT_NEWKEYSET /*user*/) && |
126 | | !CryptAcquireContext(&m_hProvider, CRYPTOPP_CONTAINER, 0, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET|CRYPT_NEWKEYSET)) |
127 | | { |
128 | | // Set original error with original code |
129 | | SetLastError(firstErr); |
130 | | throw OS_RNG_Err("CryptAcquireContext"); |
131 | | } |
132 | | } |
133 | | #elif defined(USE_MS_CNGAPI) |
134 | | NTSTATUS ret = BCryptOpenAlgorithmProvider(&m_hProvider, BCRYPT_RNG_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); |
135 | | if (!(BCRYPT_SUCCESS(ret))) |
136 | | { |
137 | | // Hack... OS_RNG_Err calls GetLastError() |
138 | | SetLastError(NtStatusToErrorCode(ret)); |
139 | | throw OS_RNG_Err("BCryptOpenAlgorithmProvider"); |
140 | | } |
141 | | #endif |
142 | | } |
143 | | |
144 | | MicrosoftCryptoProvider::~MicrosoftCryptoProvider() |
145 | | { |
146 | | #if defined(USE_MS_CRYPTOAPI) |
147 | | if (m_hProvider) |
148 | | CryptReleaseContext(m_hProvider, 0); |
149 | | #elif defined(USE_MS_CNGAPI) |
150 | | if (m_hProvider) |
151 | | BCryptCloseAlgorithmProvider(m_hProvider, 0); |
152 | | #endif |
153 | | } |
154 | | |
155 | | #endif // CRYPTOPP_WIN32_AVAILABLE |
156 | | |
157 | | NonblockingRng::NonblockingRng() |
158 | 28 | { |
159 | 28 | #if !defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(USE_FREEBSD_ARC4RANDOM) |
160 | | # ifndef DONT_USE_O_NOFOLLOW |
161 | | const int flags = O_RDONLY|O_NOFOLLOW; |
162 | | # else |
163 | 28 | const int flags = O_RDONLY; |
164 | 28 | # endif |
165 | | |
166 | 28 | m_fd = open("/dev/urandom", flags); |
167 | 28 | if (m_fd == -1) |
168 | 0 | throw OS_RNG_Err("open /dev/urandom"); |
169 | | |
170 | 28 | #endif |
171 | 28 | } |
172 | | |
173 | | NonblockingRng::~NonblockingRng() |
174 | 28 | { |
175 | 28 | #if !defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(USE_FREEBSD_ARC4RANDOM) |
176 | 28 | close(m_fd); |
177 | 28 | #endif |
178 | 28 | } |
179 | | |
180 | | void NonblockingRng::GenerateBlock(byte *output, size_t size) |
181 | 28 | { |
182 | | #ifdef CRYPTOPP_WIN32_AVAILABLE |
183 | | // Acquiring a provider is expensive. Do it once and retain the reference. |
184 | | # if defined(CRYPTOPP_CXX11_STATIC_INIT) |
185 | | static const MicrosoftCryptoProvider hProvider = MicrosoftCryptoProvider(); |
186 | | # else |
187 | | const MicrosoftCryptoProvider &hProvider = Singleton<MicrosoftCryptoProvider>().Ref(); |
188 | | # endif |
189 | | # if defined(USE_MS_CRYPTOAPI) |
190 | | DWORD dwSize; |
191 | | CRYPTOPP_ASSERT(SafeConvert(size, dwSize)); |
192 | | if (!SafeConvert(size, dwSize)) |
193 | | { |
194 | | SetLastError(ERROR_INCORRECT_SIZE); |
195 | | throw OS_RNG_Err("GenerateBlock size"); |
196 | | } |
197 | | BOOL ret = CryptGenRandom(hProvider.GetProviderHandle(), dwSize, output); |
198 | | CRYPTOPP_ASSERT(ret != FALSE); |
199 | | if (ret == FALSE) |
200 | | throw OS_RNG_Err("CryptGenRandom"); |
201 | | # elif defined(USE_MS_CNGAPI) |
202 | | ULONG ulSize; |
203 | | CRYPTOPP_ASSERT(SafeConvert(size, ulSize)); |
204 | | if (!SafeConvert(size, ulSize)) |
205 | | { |
206 | | SetLastError(ERROR_INCORRECT_SIZE); |
207 | | throw OS_RNG_Err("GenerateBlock size"); |
208 | | } |
209 | | NTSTATUS ret = BCryptGenRandom(hProvider.GetProviderHandle(), output, ulSize, 0); |
210 | | CRYPTOPP_ASSERT(BCRYPT_SUCCESS(ret)); |
211 | | if (!(BCRYPT_SUCCESS(ret))) |
212 | | { |
213 | | // Hack... OS_RNG_Err calls GetLastError() |
214 | | SetLastError(NtStatusToErrorCode(ret)); |
215 | | throw OS_RNG_Err("BCryptGenRandom"); |
216 | | } |
217 | | # endif |
218 | | #else |
219 | | |
220 | | # if defined(USE_FREEBSD_ARC4RANDOM) |
221 | | // Cryptographic quality prng based on ChaCha20, |
222 | | // https://www.freebsd.org/cgi/man.cgi?query=arc4random_buf |
223 | | arc4random_buf(output, size); |
224 | | # else |
225 | 56 | while (size) |
226 | 28 | { |
227 | 28 | ssize_t len = read(m_fd, output, size); |
228 | 28 | if (len < 0) |
229 | 0 | { |
230 | | // /dev/urandom reads CAN give EAGAIN errors! (maybe EINTR as well) |
231 | 0 | if (errno != EINTR && errno != EAGAIN) |
232 | 0 | throw OS_RNG_Err("read /dev/urandom"); |
233 | | |
234 | 0 | continue; |
235 | 0 | } |
236 | 28 | output += len; |
237 | 28 | size -= len; |
238 | 28 | } |
239 | 28 | # endif // USE_FREEBSD_ARC4RANDOM |
240 | | |
241 | 28 | #endif // CRYPTOPP_WIN32_AVAILABLE |
242 | 28 | } |
243 | | |
244 | | #endif // NONBLOCKING_RNG_AVAILABLE |
245 | | |
246 | | // ************************************************************* |
247 | | |
248 | | #ifdef BLOCKING_RNG_AVAILABLE |
249 | | |
250 | | #ifndef CRYPTOPP_BLOCKING_RNG_FILENAME |
251 | | # ifdef __OpenBSD__ |
252 | | # define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/srandom" |
253 | | # else |
254 | 0 | # define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/random" |
255 | | # endif |
256 | | #endif |
257 | | |
258 | | BlockingRng::BlockingRng() |
259 | 0 | { |
260 | | #ifndef DONT_USE_O_NOFOLLOW |
261 | | const int flags = O_RDONLY|O_NOFOLLOW; |
262 | | #else |
263 | 0 | const int flags = O_RDONLY; |
264 | 0 | #endif |
265 | |
|
266 | 0 | m_fd = open(CRYPTOPP_BLOCKING_RNG_FILENAME, flags); |
267 | 0 | if (m_fd == -1) |
268 | 0 | throw OS_RNG_Err("open " CRYPTOPP_BLOCKING_RNG_FILENAME); |
269 | 0 | } |
270 | | |
271 | | BlockingRng::~BlockingRng() |
272 | 0 | { |
273 | 0 | close(m_fd); |
274 | 0 | } |
275 | | |
276 | | void BlockingRng::GenerateBlock(byte *output, size_t size) |
277 | 0 | { |
278 | 0 | while (size) |
279 | 0 | { |
280 | | // on some systems /dev/random will block until all bytes |
281 | | // are available, on others it returns immediately |
282 | 0 | ssize_t len = read(m_fd, output, size); |
283 | 0 | if (len < 0) |
284 | 0 | { |
285 | | // /dev/random reads CAN give EAGAIN errors! (maybe EINTR as well) |
286 | 0 | if (errno != EINTR && errno != EAGAIN) |
287 | 0 | throw OS_RNG_Err("read " CRYPTOPP_BLOCKING_RNG_FILENAME); |
288 | | |
289 | 0 | continue; |
290 | 0 | } |
291 | | |
292 | 0 | size -= len; |
293 | 0 | output += len; |
294 | 0 | if (size) |
295 | 0 | sleep(1); |
296 | 0 | } |
297 | 0 | } |
298 | | |
299 | | #endif // BLOCKING_RNG_AVAILABLE |
300 | | |
301 | | // ************************************************************* |
302 | | |
303 | | void OS_GenerateRandomBlock(bool blocking, byte *output, size_t size) |
304 | 28 | { |
305 | 28 | #ifdef NONBLOCKING_RNG_AVAILABLE |
306 | 28 | if (blocking) |
307 | 0 | #endif |
308 | 0 | { |
309 | 0 | #ifdef BLOCKING_RNG_AVAILABLE |
310 | 0 | BlockingRng rng; |
311 | 0 | rng.GenerateBlock(output, size); |
312 | 0 | #endif |
313 | 0 | } |
314 | | |
315 | 28 | #ifdef BLOCKING_RNG_AVAILABLE |
316 | 28 | if (!blocking) |
317 | 28 | #endif |
318 | 28 | { |
319 | 28 | #ifdef NONBLOCKING_RNG_AVAILABLE |
320 | 28 | NonblockingRng rng; |
321 | 28 | rng.GenerateBlock(output, size); |
322 | 28 | #endif |
323 | 28 | } |
324 | 28 | } |
325 | | |
326 | | void AutoSeededRandomPool::Reseed(bool blocking, unsigned int seedSize) |
327 | 28 | { |
328 | 28 | SecByteBlock seed(seedSize); |
329 | 28 | OS_GenerateRandomBlock(blocking, seed, seedSize); |
330 | 28 | IncorporateEntropy(seed, seedSize); |
331 | 28 | } |
332 | | |
333 | | NAMESPACE_END |
334 | | |
335 | | #endif // OS_RNG_AVAILABLE |
336 | | |
337 | | #endif // CRYPTOPP_IMPORTS |