Crypto++  8.2
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 #ifdef CRYPTOPP_WIN32_AVAILABLE
25 #define WIN32_LEAN_AND_MEAN
26 #include <windows.h>
27 #ifndef ERROR_INCORRECT_SIZE
28 # define ERROR_INCORRECT_SIZE 0x000005B6
29 #endif
30 #if defined(USE_MS_CRYPTOAPI)
31 #include <wincrypt.h>
32 #ifndef CRYPT_NEWKEYSET
33 # define CRYPT_NEWKEYSET 0x00000008
34 #endif
35 #ifndef CRYPT_MACHINE_KEYSET
36 # define CRYPT_MACHINE_KEYSET 0x00000020
37 #endif
38 #elif defined(USE_MS_CNGAPI)
39 #include <bcrypt.h>
40 #ifndef BCRYPT_SUCCESS
41 # define BCRYPT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
42 #endif
43 #ifndef STATUS_INVALID_PARAMETER
44 # define STATUS_INVALID_PARAMETER 0xC000000D
45 #endif
46 #ifndef STATUS_INVALID_HANDLE
47 # define STATUS_INVALID_HANDLE 0xC0000008
48 #endif
49 #endif
50 #endif // Win32
51 
52 #ifdef CRYPTOPP_UNIX_AVAILABLE
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <unistd.h>
56 #endif
57 
58 NAMESPACE_BEGIN(CryptoPP)
59 
60 #if defined(NONBLOCKING_RNG_AVAILABLE) || defined(BLOCKING_RNG_AVAILABLE)
61 OS_RNG_Err::OS_RNG_Err(const std::string &operation)
62  : Exception(OTHER_ERROR, "OS_Rng: " + operation + " operation failed with error " +
63 #ifdef CRYPTOPP_WIN32_AVAILABLE
64  "0x" + IntToString(GetLastError(), 16)
65 #else
66  IntToString(errno)
67 #endif
68  )
69 {
70 }
71 #endif
72 
73 #ifdef NONBLOCKING_RNG_AVAILABLE
74 
75 #ifdef CRYPTOPP_WIN32_AVAILABLE
76 
77 #if defined(USE_MS_CNGAPI)
78 inline DWORD NtStatusToErrorCode(NTSTATUS status)
79 {
80  if (status == STATUS_INVALID_PARAMETER)
81  return ERROR_INVALID_PARAMETER;
82  else if (status == STATUS_INVALID_HANDLE)
83  return ERROR_INVALID_HANDLE;
84  else
85  return (DWORD)status;
86 }
87 #endif
88 
89 #if defined(UNICODE) || defined(_UNICODE)
90 # define CRYPTOPP_CONTAINER L"Crypto++ RNG"
91 #else
92 # define CRYPTOPP_CONTAINER "Crypto++ RNG"
93 #endif
94 
96 {
97 #if defined(USE_MS_CRYPTOAPI)
98  // See http://support.microsoft.com/en-us/kb/238187 for CRYPT_NEWKEYSET fallback strategy
99  if (!CryptAcquireContext(&m_hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
100  {
101  const DWORD firstErr = GetLastError();
102  if (!CryptAcquireContext(&m_hProvider, CRYPTOPP_CONTAINER, 0, PROV_RSA_FULL, CRYPT_NEWKEYSET /*user*/) &&
103  !CryptAcquireContext(&m_hProvider, CRYPTOPP_CONTAINER, 0, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET|CRYPT_NEWKEYSET))
104  {
105  // Set original error with original code
106  SetLastError(firstErr);
107  throw OS_RNG_Err("CryptAcquireContext");
108  }
109  }
110 #elif defined(USE_MS_CNGAPI)
111  NTSTATUS ret = BCryptOpenAlgorithmProvider(&m_hProvider, BCRYPT_RNG_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
112  if (!(BCRYPT_SUCCESS(ret)))
113  {
114  // Hack... OS_RNG_Err calls GetLastError()
115  SetLastError(NtStatusToErrorCode(ret));
116  throw OS_RNG_Err("BCryptOpenAlgorithmProvider");
117  }
118 #endif
119 }
120 
121 MicrosoftCryptoProvider::~MicrosoftCryptoProvider()
122 {
123 #if defined(USE_MS_CRYPTOAPI)
124  if (m_hProvider)
125  CryptReleaseContext(m_hProvider, 0);
126 #elif defined(USE_MS_CNGAPI)
127  if (m_hProvider)
128  BCryptCloseAlgorithmProvider(m_hProvider, 0);
129 #endif
130 }
131 
132 #endif // CRYPTOPP_WIN32_AVAILABLE
133 
135 {
136 #ifndef CRYPTOPP_WIN32_AVAILABLE
137  m_fd = open("/dev/urandom",O_RDONLY);
138  if (m_fd == -1)
139  throw OS_RNG_Err("open /dev/urandom");
140 
141  // Do some OSes return -NNN instead of -1?
142  CRYPTOPP_ASSERT(m_fd >= 0);
143 #endif
144 }
145 
146 NonblockingRng::~NonblockingRng()
147 {
148 #ifndef CRYPTOPP_WIN32_AVAILABLE
149  close(m_fd);
150 #endif
151 }
152 
153 void NonblockingRng::GenerateBlock(byte *output, size_t size)
154 {
155 #ifdef CRYPTOPP_WIN32_AVAILABLE
156  // Acquiring a provider is expensive. Do it once and retain the reference.
157 # if defined(CRYPTOPP_CXX11_DYNAMIC_INIT)
158  static const MicrosoftCryptoProvider hProvider = MicrosoftCryptoProvider();
159 # else
161 # endif
162 # if defined(USE_MS_CRYPTOAPI)
163  DWORD dwSize;
164  CRYPTOPP_ASSERT(SafeConvert(size, dwSize));
165  if (!SafeConvert(size, dwSize))
166  {
167  SetLastError(ERROR_INCORRECT_SIZE);
168  throw OS_RNG_Err("GenerateBlock size");
169  }
170  BOOL ret = CryptGenRandom(hProvider.GetProviderHandle(), dwSize, output);
171  CRYPTOPP_ASSERT(ret != FALSE);
172  if (ret == FALSE)
173  throw OS_RNG_Err("CryptGenRandom");
174 # elif defined(USE_MS_CNGAPI)
175  ULONG ulSize;
176  CRYPTOPP_ASSERT(SafeConvert(size, ulSize));
177  if (!SafeConvert(size, ulSize))
178  {
179  SetLastError(ERROR_INCORRECT_SIZE);
180  throw OS_RNG_Err("GenerateBlock size");
181  }
182  NTSTATUS ret = BCryptGenRandom(hProvider.GetProviderHandle(), output, ulSize, 0);
183  CRYPTOPP_ASSERT(BCRYPT_SUCCESS(ret));
184  if (!(BCRYPT_SUCCESS(ret)))
185  {
186  // Hack... OS_RNG_Err calls GetLastError()
187  SetLastError(NtStatusToErrorCode(ret));
188  throw OS_RNG_Err("BCryptGenRandom");
189  }
190 # endif
191 #else
192  while (size)
193  {
194  ssize_t len = read(m_fd, output, size);
195  if (len < 0)
196  {
197  // /dev/urandom reads CAN give EAGAIN errors! (maybe EINTR as well)
198  if (errno != EINTR && errno != EAGAIN)
199  throw OS_RNG_Err("read /dev/urandom");
200 
201  continue;
202  }
203 
204  output += len;
205  size -= len;
206  }
207 #endif // CRYPTOPP_WIN32_AVAILABLE
208 }
209 
210 #endif // NONBLOCKING_RNG_AVAILABLE
211 
212 // *************************************************************
213 
214 #ifdef BLOCKING_RNG_AVAILABLE
215 
216 #ifndef CRYPTOPP_BLOCKING_RNG_FILENAME
217 #ifdef __OpenBSD__
218 #define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/srandom"
219 #else
220 #define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/random"
221 #endif
222 #endif
223 
225 {
226  m_fd = open(CRYPTOPP_BLOCKING_RNG_FILENAME,O_RDONLY);
227  if (m_fd == -1)
228  throw OS_RNG_Err("open " CRYPTOPP_BLOCKING_RNG_FILENAME);
229 
230  // Do some OSes return -NNN instead of -1?
231  CRYPTOPP_ASSERT(m_fd >= 0);
232 }
233 
234 BlockingRng::~BlockingRng()
235 {
236  close(m_fd);
237 }
238 
239 void BlockingRng::GenerateBlock(byte *output, size_t size)
240 {
241  while (size)
242  {
243  // on some systems /dev/random will block until all bytes
244  // are available, on others it returns immediately
245  ssize_t len = read(m_fd, output, size);
246  if (len < 0)
247  {
248  // /dev/random reads CAN give EAGAIN errors! (maybe EINTR as well)
249  if (errno != EINTR && errno != EAGAIN)
250  throw OS_RNG_Err("read " CRYPTOPP_BLOCKING_RNG_FILENAME);
251 
252  continue;
253  }
254 
255  size -= len;
256  output += len;
257  if (size)
258  sleep(1);
259  }
260 }
261 
262 #endif // BLOCKING_RNG_AVAILABLE
263 
264 // *************************************************************
265 
266 void OS_GenerateRandomBlock(bool blocking, byte *output, size_t size)
267 {
268 #ifdef NONBLOCKING_RNG_AVAILABLE
269  if (blocking)
270 #endif
271  {
272 #ifdef BLOCKING_RNG_AVAILABLE
273  BlockingRng rng;
274  rng.GenerateBlock(output, size);
275 #endif
276  }
277 
278 #ifdef BLOCKING_RNG_AVAILABLE
279  if (!blocking)
280 #endif
281  {
282 #ifdef NONBLOCKING_RNG_AVAILABLE
283  NonblockingRng rng;
284  rng.GenerateBlock(output, size);
285 #endif
286  }
287 }
288 
289 void AutoSeededRandomPool::Reseed(bool blocking, unsigned int seedSize)
290 {
291  SecByteBlock seed(seedSize);
292  OS_GenerateRandomBlock(blocking, seed, seedSize);
293  IncorporateEntropy(seed, seedSize);
294 }
295 
296 NAMESPACE_END
297 
298 #endif // OS_RNG_AVAILABLE
299 
300 #endif // CRYPTOPP_IMPORTS
void IncorporateEntropy(const byte *input, size_t length)
Update RNG state with additional unpredictable values.
Definition: randpool.cpp:32
Base class for all exceptions thrown by the library.
Definition: cryptlib.h:158
BlockingRng()
Construct a BlockingRng.
Definition: osrng.cpp:224
OS_RNG_Err(const std::string &operation)
Constructs an OS_RNG_Err.
Definition: osrng.cpp:61
bool SafeConvert(T1 from, T2 &to)
Tests whether a conversion from -> to is safe to perform.
Definition: misc.h:685
Restricts the instantiation of a class to one static object without locks.
Definition: misc.h:304
Wrapper class for /dev/random and /dev/srandom.
Definition: osrng.h:85
void Reseed(bool blocking=false, unsigned int seedSize=32)
Reseed an AutoSeededRandomPool.
Definition: osrng.cpp:289
Library configuration file.
SecBlock<byte> typedef.
Definition: secblock.h:1090
void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: osrng.cpp:153
CRYPTOPP_DLL void CRYPTOPP_API OS_GenerateRandomBlock(bool blocking, byte *output, size_t size)
OS_GenerateRandomBlock.
Definition: osrng.cpp:266
void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: osrng.cpp:239
NonblockingRng()
Construct a NonblockingRng.
Definition: osrng.cpp:134
Exception thrown when an operating system error is encountered.
Definition: osrng.h:25
Precompiled header file.
Miscellaneous classes for RNGs.
MicrosoftCryptoProvider()
Construct a MicrosoftCryptoProvider.
Definition: osrng.cpp:95
#define CRYPTOPP_ASSERT(exp)
Debugging and diagnostic assertion.
Definition: trap.h:69
Wrapper class for /dev/random and /dev/srandom.
Definition: osrng.h:119
std::string IntToString(T value, unsigned int base=10)
Converts a value to a string.
Definition: misc.h:699
Crypto++ library namespace.
ProviderHandle GetProviderHandle() const
Retrieves the provider handle.
Definition: osrng.h:66
const T & Ref(...) const
Return a reference to the inner Singleton object.
Definition: misc.h:325
Classes for access to the operating system's random number generators.