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