1 | /* boost random_device.cpp implementation |
2 | * |
3 | * Copyright Jens Maurer 2000 |
4 | * Copyright Steven Watanabe 2010-2011 |
5 | * Distributed under the Boost Software License, Version 1.0. (See |
6 | * accompanying file LICENSE_1_0.txt or copy at |
7 | * http://www.boost.org/LICENSE_1_0.txt) |
8 | * |
9 | * $Id$ |
10 | * |
11 | */ |
12 | |
13 | #define BOOST_RANDOM_SOURCE |
14 | |
15 | #include <boost/random/random_device.hpp> |
16 | #include <boost/config.hpp> |
17 | #include <boost/throw_exception.hpp> |
18 | #include <boost/assert.hpp> |
19 | #include <boost/detail/workaround.hpp> |
20 | #include <boost/system/system_error.hpp> |
21 | #include <boost/system/error_code.hpp> |
22 | #include <string> |
23 | |
24 | #if !defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION) && !BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600)) |
25 | // A definition is required even for integral static constants |
26 | const bool boost::random::random_device::has_fixed_range; |
27 | #endif |
28 | |
29 | // WinRT target. |
30 | #if !defined(BOOST_RANDOM_WINDOWS_RUNTIME) |
31 | # if defined(__cplusplus_winrt) |
32 | # include <winapifamily.h> |
33 | # if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) |
34 | # define BOOST_RANDOM_WINDOWS_RUNTIME 1 |
35 | # endif |
36 | # endif |
37 | #endif |
38 | |
39 | #if defined(BOOST_WINDOWS) |
40 | |
41 | #if !defined(BOOST_RANDOM_WINDOWS_RUNTIME) |
42 | #include <windows.h> |
43 | #include <wincrypt.h> |
44 | #include <stdexcept> // std::invalid_argument |
45 | #else |
46 | using namespace Platform; |
47 | using namespace Windows::Security::Cryptography; |
48 | #endif |
49 | |
50 | #define BOOST_AUTO_LINK_NOMANGLE |
51 | #define BOOST_LIB_NAME Advapi32 |
52 | #include <boost/config/auto_link.hpp> |
53 | |
54 | #ifdef __MINGW32__ |
55 | |
56 | extern "C" { |
57 | |
58 | // mingw's wincrypt.h appears to be missing some things |
59 | WINADVAPI |
60 | BOOL |
61 | WINAPI |
62 | CryptEnumProvidersA( |
63 | DWORD dwIndex, |
64 | DWORD *pdwReserved, |
65 | DWORD dwFlags, |
66 | DWORD *pdwProvType, |
67 | LPSTR szProvName, |
68 | DWORD *pcbProvName |
69 | ); |
70 | |
71 | } |
72 | |
73 | #endif |
74 | |
75 | namespace { |
76 | #if !defined(BOOST_RANDOM_WINDOWS_RUNTIME) |
77 | const char * const default_token = MS_DEF_PROV_A; |
78 | #else |
79 | const char * const default_token = "" ; |
80 | #endif |
81 | } |
82 | |
83 | class boost::random::random_device::impl |
84 | { |
85 | public: |
86 | impl(const std::string & token) : provider(token) { |
87 | #if !defined(BOOST_RANDOM_WINDOWS_RUNTIME) |
88 | char buffer[80]; |
89 | DWORD type; |
90 | DWORD len; |
91 | |
92 | // Find the type of a specific provider |
93 | for(DWORD i = 0; ; ++i) { |
94 | len = sizeof(buffer); |
95 | if(!CryptEnumProvidersA(i, NULL, 0, &type, buffer, &len)) { |
96 | if (GetLastError() == ERROR_NO_MORE_ITEMS) break; |
97 | continue; |
98 | } |
99 | if(buffer == provider) { |
100 | break; |
101 | } |
102 | } |
103 | |
104 | if(!CryptAcquireContextA(&hProv, NULL, provider.c_str(), type, |
105 | CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { |
106 | error("Could not acquire CSP context" ); |
107 | } |
108 | #endif |
109 | } |
110 | |
111 | #if !defined(BOOST_RANDOM_WINDOWS_RUNTIME) |
112 | ~impl() { |
113 | if(!CryptReleaseContext(hProv, 0)) error("Could not release CSP context" ); |
114 | } |
115 | #endif |
116 | |
117 | unsigned int next() { |
118 | unsigned int result; |
119 | |
120 | #if !defined(BOOST_RANDOM_WINDOWS_RUNTIME) |
121 | if(!CryptGenRandom(hProv, sizeof(result), |
122 | static_cast<BYTE*>(static_cast<void*>(&result)))) { |
123 | error("error while reading" ); |
124 | } |
125 | #else |
126 | auto buffer = CryptographicBuffer::GenerateRandom(sizeof(result)); |
127 | auto data = ref new Array<unsigned char>(buffer->Length); |
128 | CryptographicBuffer::CopyToByteArray(buffer, &data); |
129 | memcpy(&result, data->begin(), data->end() - data->begin()); |
130 | #endif |
131 | |
132 | return result; |
133 | } |
134 | |
135 | private: |
136 | #if !defined(BOOST_RANDOM_WINDOWS_RUNTIME) |
137 | void error(const char * msg) { |
138 | DWORD error_code = GetLastError(); |
139 | boost::throw_exception( |
140 | boost::system::system_error( |
141 | error_code, boost::system::system_category(), |
142 | std::string("boost::random_device: " ) + msg + |
143 | " Cryptographic Service Provider " + provider)); |
144 | } |
145 | HCRYPTPROV hProv; |
146 | #endif |
147 | const std::string provider; |
148 | }; |
149 | |
150 | #else |
151 | |
152 | namespace { |
153 | // the default is the unlimited capacity device, using some secure hash |
154 | // try "/dev/random" for blocking when the entropy pool has drained |
155 | const char * const default_token = "/dev/urandom" ; |
156 | } |
157 | |
158 | /* |
159 | * This uses the POSIX interface for unbuffered reading. |
160 | * Using buffered std::istream would consume entropy which may |
161 | * not actually be used. Entropy is a precious good we avoid |
162 | * wasting. |
163 | */ |
164 | |
165 | #if defined(__GNUC__) && defined(_CXXRT_STD_NAME) |
166 | // I have severe difficulty to get the POSIX includes to work with |
167 | // -fhonor-std and Dietmar Kuhl's standard C++ library. Hack around that |
168 | // problem for now. |
169 | extern "C" { |
170 | static const int O_RDONLY = 0; |
171 | extern int open(const char *__file, int __oflag, ...); |
172 | extern int read(int __fd, __ptr_t __buf, size_t __nbytes); |
173 | extern int close(int __fd); |
174 | } |
175 | #else |
176 | #include <sys/types.h> |
177 | #include <sys/stat.h> |
178 | #include <fcntl.h> // open |
179 | #include <unistd.h> // read, close |
180 | #endif |
181 | |
182 | #include <errno.h> // errno |
183 | #include <string.h> // strerror |
184 | #include <stdexcept> // std::invalid_argument |
185 | |
186 | |
187 | class boost::random::random_device::impl |
188 | { |
189 | public: |
190 | impl(const std::string & token) : path(token) { |
191 | fd = open(file: token.c_str(), O_RDONLY); |
192 | if(fd < 0) |
193 | error(msg: "cannot open" ); |
194 | } |
195 | |
196 | ~impl() { if(close(fd: fd) < 0) error(msg: "could not close" ); } |
197 | |
198 | unsigned int next() { |
199 | unsigned int result; |
200 | std::size_t offset = 0; |
201 | do { |
202 | long sz = read(fd: fd, buf: reinterpret_cast<char *>(&result) + offset, nbytes: sizeof(result) - offset); |
203 | if(sz == -1) |
204 | error(msg: "error while reading" ); |
205 | else if(sz == 0) { |
206 | errno = 0; |
207 | error(msg: "EOF while reading" ); |
208 | } |
209 | offset += sz; |
210 | } while(offset < sizeof(result)); |
211 | return result; |
212 | } |
213 | |
214 | private: |
215 | void error(const char * msg) { |
216 | int error_code = errno; |
217 | boost::throw_exception( |
218 | e: boost::system::system_error( |
219 | error_code, boost::system::system_category(), |
220 | std::string("boost::random_device: " ) + msg + |
221 | " random-number pseudo-device " + path)); |
222 | } |
223 | const std::string path; |
224 | int fd; |
225 | }; |
226 | |
227 | #endif // BOOST_WINDOWS |
228 | |
229 | BOOST_RANDOM_DECL boost::random::random_device::random_device() |
230 | : pimpl(new impl(default_token)) |
231 | {} |
232 | |
233 | BOOST_RANDOM_DECL boost::random::random_device::random_device(const std::string& token) |
234 | : pimpl(new impl(token)) |
235 | {} |
236 | |
237 | BOOST_RANDOM_DECL boost::random_device::~random_device() |
238 | { |
239 | delete pimpl; |
240 | } |
241 | |
242 | BOOST_RANDOM_DECL double boost::random_device::entropy() const |
243 | { |
244 | return 10; |
245 | } |
246 | |
247 | BOOST_RANDOM_DECL unsigned int boost::random_device::operator()() |
248 | { |
249 | return pimpl->next(); |
250 | } |
251 | |