source: trunk/src-cryptopp/osrng.cpp

Last change on this file was e230cb0, checked in by David Stainton <dstainton415@…>, at 2016-10-12T13:27:29Z

Add cryptopp from tag CRYPTOPP_5_6_5

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