• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
impl(const std::string & token)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)
~impl()112   ~impl() {
113     if(!CryptReleaseContext(hProv, 0)) error("Could not release CSP context");
114   }
115 #endif
116 
next()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)
error(const char * msg)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:
impl(const std::string & token)190   impl(const std::string & token) : path(token) {
191     fd = open(token.c_str(), O_RDONLY);
192     if(fd < 0)
193       error("cannot open");
194   }
195 
~impl()196   ~impl() { if(close(fd) < 0) error("could not close"); }
197 
next()198   unsigned int next() {
199     unsigned int result;
200     std::size_t offset = 0;
201     do {
202       long sz = read(fd, reinterpret_cast<char *>(&result) + offset, sizeof(result) - offset);
203       if(sz == -1)
204         error("error while reading");
205       else if(sz == 0) {
206         errno = 0;
207         error("EOF while reading");
208       }
209       offset += sz;
210     } while(offset < sizeof(result));
211     return result;
212   }
213 
214 private:
error(const char * msg)215   void error(const char * msg) {
216     int error_code = errno;
217     boost::throw_exception(
218       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 
random_device()229 BOOST_RANDOM_DECL boost::random::random_device::random_device()
230   : pimpl(new impl(default_token))
231 {}
232 
random_device(const std::string & token)233 BOOST_RANDOM_DECL boost::random::random_device::random_device(const std::string& token)
234   : pimpl(new impl(token))
235 {}
236 
~random_device()237 BOOST_RANDOM_DECL boost::random_device::~random_device()
238 {
239   delete pimpl;
240 }
241 
entropy() const242 BOOST_RANDOM_DECL double boost::random_device::entropy() const
243 {
244   return 10;
245 }
246 
operator ()()247 BOOST_RANDOM_DECL unsigned int boost::random_device::operator()()
248 {
249   return pimpl->next();
250 }
251