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