Security Scol plugin
osrng.cpp
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 cryptograhic 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
81NAMESPACE_BEGIN(CryptoPP)
82
83#if defined(NONBLOCKING_RNG_AVAILABLE) || defined(BLOCKING_RNG_AVAILABLE)
84OS_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{
93}
94#endif
95
96#ifdef NONBLOCKING_RNG_AVAILABLE
97
98#ifdef CRYPTOPP_WIN32_AVAILABLE
99
100#if defined(USE_MS_CNGAPI)
101inline DWORD NtStatusToErrorCode(NTSTATUS status)
102{
103 if (status == STATUS_INVALID_PARAMETER)
104 return ERROR_INVALID_PARAMETER;
105 else if (status == STATUS_INVALID_HANDLE)
106 return ERROR_INVALID_HANDLE;
107 else
108 return (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
118MicrosoftCryptoProvider::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
144MicrosoftCryptoProvider::~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
157NonblockingRng::NonblockingRng()
158{
159#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 const int flags = O_RDONLY;
164# endif
165
166 m_fd = open("/dev/urandom", flags);
167 if (m_fd == -1)
168 throw OS_RNG_Err("open /dev/urandom");
169
170#endif
171}
172
173NonblockingRng::~NonblockingRng()
174{
175#if !defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(USE_FREEBSD_ARC4RANDOM)
176 close(m_fd);
177#endif
178}
179
180void NonblockingRng::GenerateBlock(byte *output, size_t size)
181{
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 while (size)
226 {
227 ssize_t len = read(m_fd, output, size);
228 if (len < 0)
229 {
230 // /dev/urandom reads CAN give EAGAIN errors! (maybe EINTR as well)
231 if (errno != EINTR && errno != EAGAIN)
232 throw OS_RNG_Err("read /dev/urandom");
233
234 continue;
235 }
236 output += len;
237 size -= len;
238 }
239# endif // USE_FREEBSD_ARC4RANDOM
240
241#endif // CRYPTOPP_WIN32_AVAILABLE
242}
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# define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/random"
255# endif
256#endif
257
258BlockingRng::BlockingRng()
259{
260#ifndef DONT_USE_O_NOFOLLOW
261 const int flags = O_RDONLY|O_NOFOLLOW;
262#else
263 const int flags = O_RDONLY;
264#endif
265
266 m_fd = open(CRYPTOPP_BLOCKING_RNG_FILENAME, flags);
267 if (m_fd == -1)
268 throw OS_RNG_Err("open " CRYPTOPP_BLOCKING_RNG_FILENAME);
269}
270
271BlockingRng::~BlockingRng()
272{
273 close(m_fd);
274}
275
276void BlockingRng::GenerateBlock(byte *output, size_t size)
277{
278 while (size)
279 {
280 // on some systems /dev/random will block until all bytes
281 // are available, on others it returns immediately
282 ssize_t len = read(m_fd, output, size);
283 if (len < 0)
284 {
285 // /dev/random reads CAN give EAGAIN errors! (maybe EINTR as well)
286 if (errno != EINTR && errno != EAGAIN)
287 throw OS_RNG_Err("read " CRYPTOPP_BLOCKING_RNG_FILENAME);
288
289 continue;
290 }
291
292 size -= len;
293 output += len;
294 if (size)
295 sleep(1);
296 }
297}
298
299#endif // BLOCKING_RNG_AVAILABLE
300
301// *************************************************************
302
303void OS_GenerateRandomBlock(bool blocking, byte *output, size_t size)
304{
305#ifdef NONBLOCKING_RNG_AVAILABLE
306 if (blocking)
307#endif
308 {
309#ifdef BLOCKING_RNG_AVAILABLE
310 BlockingRng rng;
311 rng.GenerateBlock(output, size);
312#endif
313 }
314
315#ifdef BLOCKING_RNG_AVAILABLE
316 if (!blocking)
317#endif
318 {
319#ifdef NONBLOCKING_RNG_AVAILABLE
320 NonblockingRng rng;
321 rng.GenerateBlock(output, size);
322#endif
323 }
324}
325
326void AutoSeededRandomPool::Reseed(bool blocking, unsigned int seedSize)
327{
328 SecByteBlock seed(seedSize);
329 OS_GenerateRandomBlock(blocking, seed, seedSize);
330 IncorporateEntropy(seed, seedSize);
331}
332
333NAMESPACE_END
334
335#endif // OS_RNG_AVAILABLE
336
337#endif // CRYPTOPP_IMPORTS
Base class for all exceptions thrown by the library.
Definition cryptlib.h:159
Restricts the instantiation of a class to one static object without locks.
Definition misc.h:307
CRYPTOPP_NOINLINE const T & Ref(CRYPTOPP_NOINLINE_DOTDOTDOT) const
Return a reference to the inner Singleton object.
Definition misc.h:327
Library configuration file.
std::string IntToString(T value, unsigned int base=10)
Converts a value to a string.
Definition misc.h:724
bool SafeConvert(T1 from, T2 &to)
Tests whether a conversion from -> to is safe to perform.
Definition misc.h:710
Classes for access to the operating system's random number generators.
Precompiled header file.
Miscellaneous classes for RNGs.