]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // Boost seed_rng.hpp header file ----------------------------------------------// |
2 | ||
3 | // Copyright 2007 Andy Tompkins. | |
4 | // Distributed under the Boost Software License, Version 1.0. (See | |
5 | // accompanying file LICENSE_1_0.txt or copy at | |
6 | // http://www.boost.org/LICENSE_1_0.txt) | |
7 | ||
8 | // Revision History | |
9 | // 09 Nov 2007 - Initial Revision | |
10 | // 25 Feb 2008 - moved to namespace boost::uuids::detail | |
11 | // 28 Nov 2009 - disabled deprecated warnings for MSVC | |
12 | // 28 Jul 2014 - fixed valgrind warnings and better entropy sources for MSVC | |
13 | ||
14 | // seed_rng models a UniformRandomNumberGenerator (see Boost.Random). | |
15 | // Random number generators are hard to seed well. This is intended to provide | |
16 | // good seed values for random number generators. | |
17 | // It creates random numbers from a sha1 hash of data from a variary of sources, | |
18 | // all of which are standard function calls. It produces random numbers slowly. | |
19 | // Peter Dimov provided the details of sha1_random_digest_(). | |
20 | // see http://archives.free.net.ph/message/20070507.175609.4c4f503a.en.html | |
21 | ||
22 | #ifndef BOOST_UUID_SEED_RNG_HPP | |
23 | #define BOOST_UUID_SEED_RNG_HPP | |
24 | ||
25 | #include <boost/config.hpp> | |
26 | #include <cstring> // for memcpy | |
27 | #include <limits> | |
28 | #include <ctime> // for time_t, time, clock_t, clock | |
29 | #include <cstdlib> // for rand | |
30 | #include <cstdio> // for FILE, fopen, fread, fclose | |
31 | #include <boost/core/noncopyable.hpp> | |
32 | #include <boost/uuid/sha1.hpp> | |
33 | //#include <boost/nondet_random.hpp> //forward declare boost::random::random_device | |
34 | ||
35 | // can't use boost::generator_iterator since boost::random number seed(Iter&, Iter) | |
36 | // functions need a last iterator | |
37 | //#include <boost/generator_iterator.hpp> | |
38 | # include <boost/iterator/iterator_facade.hpp> | |
39 | ||
40 | #if defined(_MSC_VER) | |
41 | # pragma warning(push) // Save warning settings. | |
42 | # pragma warning(disable : 4996) // Disable deprecated std::fopen | |
43 | #if defined(_WIN32_WCE) | |
44 | # pragma comment(lib, "coredll.lib") | |
45 | #else | |
46 | # pragma comment(lib, "advapi32.lib") | |
47 | #endif | |
48 | #endif | |
49 | ||
50 | #if defined(BOOST_WINDOWS) | |
51 | # include <boost/detail/winapi/crypt.hpp> // for CryptAcquireContextA, CryptGenRandom, CryptReleaseContext | |
52 | # include <boost/detail/winapi/timers.hpp> | |
53 | # include <boost/detail/winapi/get_current_process_id.hpp> | |
54 | # include <boost/detail/winapi/get_current_thread_id.hpp> | |
55 | #else | |
56 | # include <sys/time.h> // for gettimeofday | |
57 | # include <sys/types.h> // for pid_t | |
58 | # include <unistd.h> // for getpid() | |
59 | #endif | |
60 | ||
61 | #ifdef BOOST_NO_STDC_NAMESPACE | |
62 | namespace std { | |
63 | using ::memcpy; | |
64 | using ::time_t; | |
65 | using ::time; | |
66 | using ::clock_t; | |
67 | using ::clock; | |
68 | using ::rand; | |
69 | using ::FILE; | |
70 | using ::fopen; | |
71 | using ::fread; | |
72 | using ::fclose; | |
73 | } //namespace std | |
74 | #endif | |
75 | ||
76 | // forward declare random number generators | |
77 | namespace boost { namespace random { | |
78 | class random_device; | |
79 | }} //namespace boost::random | |
80 | ||
81 | namespace boost { | |
82 | namespace uuids { | |
83 | namespace detail { | |
84 | ||
85 | // should this be part of Boost.Random? | |
86 | class seed_rng: private boost::noncopyable | |
87 | { | |
88 | public: | |
89 | typedef unsigned int result_type; | |
90 | BOOST_STATIC_CONSTANT(bool, has_fixed_range = false); | |
91 | ||
92 | public: | |
93 | // note: rd_ intentionally left uninitialized | |
94 | seed_rng() BOOST_NOEXCEPT | |
95 | : rd_index_(5) | |
96 | , random_(NULL) | |
97 | { | |
98 | #if defined(BOOST_WINDOWS) | |
99 | if (!boost::detail::winapi::CryptAcquireContextW( | |
100 | &random_, | |
101 | NULL, | |
102 | NULL, | |
103 | boost::detail::winapi::PROV_RSA_FULL_, | |
104 | boost::detail::winapi::CRYPT_VERIFYCONTEXT_ | boost::detail::winapi::CRYPT_SILENT_)) | |
105 | { | |
106 | random_ = NULL; | |
107 | } | |
108 | #else | |
109 | random_ = std::fopen( "/dev/urandom", "rb" ); | |
110 | #endif | |
111 | ||
112 | std::memset(rd_, 0, sizeof(rd_)); | |
113 | } | |
114 | ||
115 | ~seed_rng() BOOST_NOEXCEPT | |
116 | { | |
117 | if (random_) { | |
118 | #if defined(BOOST_WINDOWS) | |
119 | boost::detail::winapi::CryptReleaseContext(random_, 0); | |
120 | #else | |
121 | std::fclose(random_); | |
122 | #endif | |
123 | } | |
124 | } | |
125 | ||
126 | result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const BOOST_NOEXCEPT | |
127 | { | |
128 | return (std::numeric_limits<result_type>::min)(); | |
129 | } | |
130 | result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const BOOST_NOEXCEPT | |
131 | { | |
132 | return (std::numeric_limits<result_type>::max)(); | |
133 | } | |
134 | ||
135 | result_type operator()() | |
136 | { | |
137 | if (rd_index_ >= 5) { | |
138 | //get new digest | |
139 | sha1_random_digest_(); | |
140 | ||
141 | rd_index_ = 0; | |
142 | } | |
143 | ||
144 | return rd_[rd_index_++]; | |
145 | } | |
146 | ||
147 | private: | |
148 | BOOST_STATIC_CONSTANT(std::size_t, internal_state_size = 5); | |
149 | inline void ignore_size(size_t) {} | |
150 | ||
151 | static unsigned int * sha1_random_digest_state_() | |
152 | { | |
153 | static unsigned int state[ internal_state_size ]; | |
154 | return state; | |
155 | } | |
156 | ||
157 | void sha1_random_digest_() | |
158 | { | |
159 | boost::uuids::detail::sha1 sha; | |
160 | ||
161 | ||
162 | if (random_) | |
163 | { | |
164 | // intentionally left uninitialized | |
165 | unsigned char state[ 20 ]; | |
166 | #if defined(BOOST_WINDOWS) | |
167 | boost::detail::winapi::CryptGenRandom(random_, sizeof(state), state); | |
168 | #else | |
169 | ignore_size(std::fread( state, 1, sizeof(state), random_ )); | |
170 | #endif | |
171 | sha.process_bytes( state, sizeof( state ) ); | |
172 | } | |
173 | ||
174 | { | |
175 | // Getting enropy from some system specific sources | |
176 | #if defined(BOOST_WINDOWS) | |
177 | boost::detail::winapi::DWORD_ procid = boost::detail::winapi::GetCurrentProcessId(); | |
178 | sha.process_bytes( (unsigned char const*)&procid, sizeof( procid ) ); | |
179 | ||
180 | boost::detail::winapi::DWORD_ threadid = boost::detail::winapi::GetCurrentThreadId(); | |
181 | sha.process_bytes( (unsigned char const*)&threadid, sizeof( threadid ) ); | |
182 | ||
183 | boost::detail::winapi::LARGE_INTEGER_ ts; | |
184 | ts.QuadPart = 0; | |
185 | boost::detail::winapi::QueryPerformanceCounter( &ts ); | |
186 | sha.process_bytes( (unsigned char const*)&ts, sizeof( ts ) ); | |
187 | ||
188 | std::time_t tm = std::time( 0 ); | |
189 | sha.process_bytes( (unsigned char const*)&tm, sizeof( tm ) ); | |
190 | #else | |
191 | pid_t pid = getpid(); | |
192 | sha.process_bytes( (unsigned char const*)&pid, sizeof( pid ) ); | |
193 | ||
194 | timeval ts; | |
195 | gettimeofday(&ts, NULL); // We do not use `clock_gettime` to avoid linkage with -lrt | |
196 | sha.process_bytes( (unsigned char const*)&ts, sizeof( ts ) ); | |
197 | #endif | |
198 | } | |
199 | ||
200 | ||
201 | unsigned int * ps = sha1_random_digest_state_(); | |
202 | sha.process_bytes( ps, internal_state_size * sizeof( unsigned int ) ); | |
203 | sha.process_bytes( (unsigned char const*)&ps, sizeof( ps ) ); | |
204 | ||
205 | { | |
206 | std::clock_t ck = std::clock(); | |
207 | sha.process_bytes( (unsigned char const*)&ck, sizeof( ck ) ); | |
208 | } | |
209 | ||
210 | { | |
211 | unsigned int rn[] = | |
212 | { static_cast<unsigned int>(std::rand()) | |
213 | , static_cast<unsigned int>(std::rand()) | |
214 | , static_cast<unsigned int>(std::rand()) | |
215 | }; | |
216 | sha.process_bytes( (unsigned char const*)rn, sizeof( rn ) ); | |
217 | } | |
218 | ||
219 | { | |
220 | unsigned int * p = new unsigned int; | |
221 | sha.process_bytes( (unsigned char const*)&p, sizeof( p ) ); | |
222 | delete p; | |
223 | ||
224 | const seed_rng* this_ptr = this; | |
225 | sha.process_bytes( (unsigned char const*)&this_ptr, sizeof( this_ptr ) ); | |
226 | sha.process_bytes( (unsigned char const*)&std::rand, sizeof( void(*)() ) ); | |
227 | } | |
228 | ||
229 | sha.process_bytes( (unsigned char const*)rd_, sizeof( rd_ ) ); | |
230 | ||
231 | unsigned int digest[ 5 ]; | |
232 | sha.get_digest( digest ); | |
233 | ||
234 | for( int i = 0; i < 5; ++i ) | |
235 | { | |
236 | // harmless data race | |
237 | ps[ i ] ^= digest[ i ]; | |
238 | rd_[ i ] ^= digest[ i ]; | |
239 | } | |
240 | } | |
241 | ||
242 | private: | |
243 | unsigned int rd_[5]; | |
244 | int rd_index_; | |
245 | ||
246 | #if defined(BOOST_WINDOWS) | |
247 | boost::detail::winapi::HCRYPTPROV_ random_; | |
248 | #else | |
249 | std::FILE * random_; | |
250 | #endif | |
251 | }; | |
252 | ||
253 | // almost a copy of boost::generator_iterator | |
254 | // but default constructor sets m_g to NULL | |
255 | template <class Generator> | |
256 | class generator_iterator | |
257 | : public iterator_facade< | |
258 | generator_iterator<Generator> | |
259 | , typename Generator::result_type | |
260 | , single_pass_traversal_tag | |
261 | , typename Generator::result_type const& | |
262 | > | |
263 | { | |
264 | typedef iterator_facade< | |
265 | generator_iterator<Generator> | |
266 | , typename Generator::result_type | |
267 | , single_pass_traversal_tag | |
268 | , typename Generator::result_type const& | |
269 | > super_t; | |
270 | ||
271 | public: | |
272 | generator_iterator() : m_g(NULL), m_value(0) {} | |
273 | generator_iterator(Generator* g) : m_g(g), m_value((*m_g)()) {} | |
274 | ||
275 | void increment() | |
276 | { | |
277 | m_value = (*m_g)(); | |
278 | } | |
279 | ||
280 | const typename Generator::result_type& | |
281 | dereference() const | |
282 | { | |
283 | return m_value; | |
284 | } | |
285 | ||
286 | bool equal(generator_iterator const& y) const | |
287 | { | |
288 | return this->m_g == y.m_g && this->m_value == y.m_value; | |
289 | } | |
290 | ||
291 | private: | |
292 | Generator* m_g; | |
293 | typename Generator::result_type m_value; | |
294 | }; | |
295 | ||
296 | // seed() seeds a random number generator with good seed values | |
297 | ||
298 | template <typename UniformRandomNumberGenerator> | |
299 | inline void seed(UniformRandomNumberGenerator& rng) | |
300 | { | |
301 | seed_rng seed_gen; | |
302 | generator_iterator<seed_rng> begin(&seed_gen); | |
303 | generator_iterator<seed_rng> end; | |
304 | rng.seed(begin, end); | |
305 | } | |
306 | ||
307 | // random_device does not / can not be seeded | |
308 | template <> | |
309 | inline void seed<boost::random::random_device>(boost::random::random_device&) {} | |
310 | ||
311 | // random_device does not / can not be seeded | |
312 | template <> | |
313 | inline void seed<seed_rng>(seed_rng&) {} | |
314 | ||
315 | }}} //namespace boost::uuids::detail | |
316 | ||
317 | #if defined(_MSC_VER) | |
318 | #pragma warning(pop) // Restore warnings to previous state. | |
319 | #endif | |
320 | ||
321 | #endif |