Crypto++ 8.1 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
92 MicrosoftCryptoProvider::MicrosoftCryptoProvider() : m_hProvider(0)
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
131 NonblockingRng::NonblockingRng()
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.
151 const MicrosoftCryptoProvider &hProvider = Singleton<MicrosoftCryptoProvider>().Ref();
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
197 BlockingRng::BlockingRng()
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
RandomPool::IncorporateEntropy
void IncorporateEntropy(const byte *input, size_t length)
Update RNG state with additional unpredictable values.
Definition: randpool.cpp:32
Exception
Base class for all exceptions thrown by the library.
Definition: cryptlib.h:158
MicrosoftCryptoProvider
Definition: osrng.h:37
BlockingRng::BlockingRng
BlockingRng()
Construct a BlockingRng.
Definition: osrng.cpp:197
OS_RNG_Err::OS_RNG_Err
OS_RNG_Err(const std::string &operation)
Constructs an OS_RNG_Err.
Definition: osrng.cpp:58
Singleton
Restricts the instantiation of a class to one static object without locks.
Definition: misc.h:263
NonblockingRng
Wrapper class for /dev/random and /dev/srandom.
Definition: osrng.h:84
AutoSeededRandomPool::Reseed
void Reseed(bool blocking=false, unsigned int seedSize=32)
Reseed an AutoSeededRandomPool.
Definition: osrng.cpp:259
config.h
Library configuration file.
SecByteBlock
SecBlock<byte> typedef.
Definition: secblock.h:1058
NonblockingRng::GenerateBlock
void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: osrng.cpp:147
BlockingRng::GenerateBlock
void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: osrng.cpp:209
NonblockingRng::NonblockingRng
NonblockingRng()
Construct a NonblockingRng.
Definition: osrng.cpp:131
OS_RNG_Err
Exception thrown when an operating system error is encountered.
Definition: osrng.h:24
pch.h
Precompiled header file.
rng.h
Miscellaneous classes for RNGs.
MicrosoftCryptoProvider::MicrosoftCryptoProvider
MicrosoftCryptoProvider()
Construct a MicrosoftCryptoProvider.
Definition: osrng.cpp:92
BlockingRng
Wrapper class for /dev/random and /dev/srandom.
Definition: osrng.h:118
OS_GenerateRandomBlock
void OS_GenerateRandomBlock(bool blocking, byte *output, size_t size)
OS_GenerateRandomBlock.
Definition: osrng.cpp:236
IntToString
std::string IntToString(T value, unsigned int base=10)
Converts a value to a string.
Definition: misc.h:604
CryptoPP
Crypto++ library namespace.
MicrosoftCryptoProvider::GetProviderHandle
ProviderHandle GetProviderHandle() const
Retrieves the provider handle.
Definition: osrng.h:65
osrng.h
Classes for access to the operating system&#39;s random number generators.