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
NAMESPACE_BEGIN(CryptoPP)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)
NtStatusToErrorCode(NTSTATUS status)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
MicrosoftCryptoProvider()95 MicrosoftCryptoProvider::MicrosoftCryptoProvider() : m_hProvider(0)
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
~MicrosoftCryptoProvider()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
NonblockingRng()134 NonblockingRng::NonblockingRng()
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
~NonblockingRng()146 NonblockingRng::~NonblockingRng()
147 {
148 #ifndef CRYPTOPP_WIN32_AVAILABLE
149 close(m_fd);
150 #endif
151 }
152
GenerateBlock(byte * output,size_t size)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_STATIC_INIT)
158 static const MicrosoftCryptoProvider hProvider = MicrosoftCryptoProvider();
159 # else
160 const MicrosoftCryptoProvider &hProvider = Singleton<MicrosoftCryptoProvider>().Ref();
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
BlockingRng()224 BlockingRng::BlockingRng()
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
~BlockingRng()234 BlockingRng::~BlockingRng()
235 {
236 close(m_fd);
237 }
238
GenerateBlock(byte * output,size_t size)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
OS_GenerateRandomBlock(bool blocking,byte * output,size_t size)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
Reseed(bool blocking,unsigned int seedSize)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
301