Crypto++  8.8
Free C++ class library of cryptographic schemes
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 
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 {
93 }
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 
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 
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 
173 NonblockingRng::~NonblockingRng()
174 {
175 #if !defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(USE_FREEBSD_ARC4RANDOM)
176  close(m_fd);
177 #endif
178 }
179 
180 void 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
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 
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 
271 BlockingRng::~BlockingRng()
272 {
273  close(m_fd);
274 }
275 
276 void 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 
303 void 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 
326 void AutoSeededRandomPool::Reseed(bool blocking, unsigned int seedSize)
327 {
328  SecByteBlock seed(seedSize);
329  OS_GenerateRandomBlock(blocking, seed, seedSize);
330  IncorporateEntropy(seed, seedSize);
331 }
332 
333 NAMESPACE_END
334 
335 #endif // OS_RNG_AVAILABLE
336 
337 #endif // CRYPTOPP_IMPORTS
void Reseed(bool blocking=false, unsigned int seedSize=32)
Reseed an AutoSeededRandomPool.
Wrapper class for /dev/random and /dev/srandom.
Definition: osrng.h:120
void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
BlockingRng()
Construct a BlockingRng.
Base class for all exceptions thrown by the library.
Definition: cryptlib.h:164
MicrosoftCryptoProvider()
Construct a MicrosoftCryptoProvider.
ProviderHandle GetProviderHandle() const
Retrieves the provider handle.
Definition: osrng.h:66
Wrapper class for /dev/random and /dev/srandom.
Definition: osrng.h:86
NonblockingRng()
Construct a NonblockingRng.
void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Exception thrown when an operating system error is encountered.
Definition: osrng.h:26
OS_RNG_Err(const std::string &operation)
Constructs an OS_RNG_Err.
void IncorporateEntropy(const byte *input, size_t length)
Update RNG state with additional unpredictable values.
SecBlock<byte> typedef.
Definition: secblock.h:1226
Restricts the instantiation of a class to one static object without locks.
Definition: misc.h:309
const T & Ref(...) const
Return a reference to the inner Singleton object.
Definition: misc.h:329
Library configuration file.
std::string IntToString(T value, unsigned int base=10)
Converts a value to a string.
Definition: misc.h:929
bool SafeConvert(T1 from, T2 &to)
Perform a conversion from from to to.
Definition: misc.h:718
Crypto++ library namespace.
Classes for access to the operating system's random number generators.
CRYPTOPP_DLL void OS_GenerateRandomBlock(bool blocking, byte *output, size_t size)
OS_GenerateRandomBlock.
Precompiled header file.
Miscellaneous classes for RNGs.
#define CRYPTOPP_ASSERT(exp)
Debugging and diagnostic assertion.
Definition: trap.h:68