Crypto++ 8.6
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
58NAMESPACE_BEGIN(CryptoPP)
59
60#if defined(NONBLOCKING_RNG_AVAILABLE) || defined(BLOCKING_RNG_AVAILABLE)
61OS_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)
78inline 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
121MicrosoftCryptoProvider::~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
146NonblockingRng::~NonblockingRng()
147{
148#ifndef CRYPTOPP_WIN32_AVAILABLE
149 close(m_fd);
150#endif
151}
152
153void 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
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
234BlockingRng::~BlockingRng()
235{
236 close(m_fd);
237}
238
239void 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
266void 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
289void AutoSeededRandomPool::Reseed(bool blocking, unsigned int seedSize)
290{
291 SecByteBlock seed(seedSize);
292 OS_GenerateRandomBlock(blocking, seed, seedSize);
293 IncorporateEntropy(seed, seedSize);
294}
295
296NAMESPACE_END
297
298#endif // OS_RNG_AVAILABLE
299
300#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:159
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:307
const T & Ref(...) const
Return a reference to the inner Singleton object.
Definition: misc.h:327
Library configuration file.
std::string IntToString(T value, unsigned int base=10)
Converts a value to a string.
Definition: misc.h:724
bool SafeConvert(T1 from, T2 &to)
Tests whether a conversion from -> to is safe to perform.
Definition: misc.h:710
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