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# pragma comment(lib, "advapi32.lib")
44#endif
45
46#if defined(BOOST_WINDOWS)
47# include <boost/detail/winapi/crypt.hpp> // for CryptAcquireContextA, CryptGenRandom, CryptReleaseContext
48# include <boost/detail/winapi/timers.hpp>
49# include <boost/detail/winapi/process.hpp>
50# include <boost/detail/winapi/thread.hpp>
51#else
52# include <sys/time.h> // for gettimeofday
53# include <sys/types.h> // for pid_t
54# include <unistd.h> // for getpid()
55#endif
56
57#ifdef BOOST_NO_STDC_NAMESPACE
58namespace std {
59 using ::memcpy;
60 using ::time_t;
61 using ::time;
62 using ::clock_t;
63 using ::clock;
64 using ::rand;
65 using ::FILE;
66 using ::fopen;
67 using ::fread;
68 using ::fclose;
69} //namespace std
70#endif
71
72// forward declare random number generators
73namespace boost { namespace random {
74class random_device;
75}} //namespace boost::random
76
77namespace boost {
78namespace uuids {
79namespace detail {
80
81// should this be part of Boost.Random?
82class seed_rng: private boost::noncopyable
83{
84public:
85 typedef unsigned int result_type;
86 BOOST_STATIC_CONSTANT(bool, has_fixed_range = false);
87
88public:
89 // note: rd_ intentionally left uninitialized
90 seed_rng() BOOST_NOEXCEPT
91 : rd_index_(5)
92 , random_(NULL)
93 {
94#if defined(BOOST_WINDOWS)
95 if (!boost::detail::winapi::CryptAcquireContextA(
96 &random_,
97 NULL,
98 NULL,
99 boost::detail::winapi::PROV_RSA_FULL_,
100 boost::detail::winapi::CRYPT_VERIFYCONTEXT_ | boost::detail::winapi::CRYPT_SILENT_))
101 {
102 random_ = NULL;
103 }
104#else
105 random_ = std::fopen( "/dev/urandom", "rb" );
106#endif
107
108 std::memset(rd_, 0, sizeof(rd_));
109 }
110
111 ~seed_rng() BOOST_NOEXCEPT
112 {
113 if (random_) {
114#if defined(BOOST_WINDOWS)
115 boost::detail::winapi::CryptReleaseContext(random_, 0);
116#else
117 std::fclose(random_);
118#endif
119 }
120 }
121
122 result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const BOOST_NOEXCEPT
123 {
124 return (std::numeric_limits<result_type>::min)();
125 }
126 result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const BOOST_NOEXCEPT
127 {
128 return (std::numeric_limits<result_type>::max)();
129 }
130
131 result_type operator()()
132 {
133 if (rd_index_ >= 5) {
134 //get new digest
135 sha1_random_digest_();
136
137 rd_index_ = 0;
138 }
139
140 return rd_[rd_index_++];
141 }
142
143private:
144 BOOST_STATIC_CONSTANT(std::size_t, internal_state_size = 5);
145 inline void ignore_size(size_t) {}
146
147 static unsigned int * sha1_random_digest_state_()
148 {
149 static unsigned int state[ internal_state_size ];
150 return state;
151 }
152
153 void sha1_random_digest_()
154 {
155 boost::uuids::detail::sha1 sha;
156
157
158 if (random_)
159 {
160 // intentionally left uninitialized
161 unsigned char state[ 20 ];
162#if defined(BOOST_WINDOWS)
163 boost::detail::winapi::CryptGenRandom(random_, sizeof(state), state);
164#else
165 ignore_size(std::fread( state, 1, sizeof(state), random_ ));
166#endif
167 sha.process_bytes( state, sizeof( state ) );
168 }
169
170 {
171 // Getting enropy from some system specific sources
172#if defined(BOOST_WINDOWS)
173 boost::detail::winapi::DWORD_ procid = boost::detail::winapi::GetCurrentProcessId();
174 sha.process_bytes( (unsigned char const*)&procid, sizeof( procid ) );
175
176 boost::detail::winapi::DWORD_ threadid = boost::detail::winapi::GetCurrentThreadId();
177 sha.process_bytes( (unsigned char const*)&threadid, sizeof( threadid ) );
178
179 boost::detail::winapi::LARGE_INTEGER_ ts;
180 ts.QuadPart = 0;
181 boost::detail::winapi::QueryPerformanceCounter( &ts );
182 sha.process_bytes( (unsigned char const*)&ts, sizeof( ts ) );
183
184 std::time_t tm = std::time( 0 );
185 sha.process_bytes( (unsigned char const*)&tm, sizeof( tm ) );
186#else
187 pid_t pid = getpid();
188 sha.process_bytes( (unsigned char const*)&pid, sizeof( pid ) );
189
190 timeval ts;
191 gettimeofday(&ts, NULL); // We do not use `clock_gettime` to avoid linkage with -lrt
192 sha.process_bytes( (unsigned char const*)&ts, sizeof( ts ) );
193#endif
194 }
195
196
197 unsigned int * ps = sha1_random_digest_state_();
198 sha.process_bytes( ps, internal_state_size * sizeof( unsigned int ) );
199 sha.process_bytes( (unsigned char const*)&ps, sizeof( ps ) );
200
201 {
202 std::clock_t ck = std::clock();
203 sha.process_bytes( (unsigned char const*)&ck, sizeof( ck ) );
204 }
205
206 {
207 unsigned int rn[] =
208 { static_cast<unsigned int>(std::rand())
209 , static_cast<unsigned int>(std::rand())
210 , static_cast<unsigned int>(std::rand())
211 };
212 sha.process_bytes( (unsigned char const*)rn, sizeof( rn ) );
213 }
214
215 {
216 unsigned int * p = new unsigned int;
217 sha.process_bytes( (unsigned char const*)&p, sizeof( p ) );
218 delete p;
219
220 const seed_rng* this_ptr = this;
221 sha.process_bytes( (unsigned char const*)&this_ptr, sizeof( this_ptr ) );
222 sha.process_bytes( (unsigned char const*)&std::rand, sizeof( void(*)() ) );
223 }
224
225 sha.process_bytes( (unsigned char const*)rd_, sizeof( rd_ ) );
226
227 unsigned int digest[ 5 ];
228 sha.get_digest( digest );
229
230 for( int i = 0; i < 5; ++i )
231 {
232 // harmless data race
233 ps[ i ] ^= digest[ i ];
234 rd_[ i ] ^= digest[ i ];
235 }
236 }
237
238private:
239 unsigned int rd_[5];
240 int rd_index_;
241
242#if defined(BOOST_WINDOWS)
243 boost::detail::winapi::HCRYPTPROV_ random_;
244#else
245 std::FILE * random_;
246#endif
247};
248
249// almost a copy of boost::generator_iterator
250// but default constructor sets m_g to NULL
251template <class Generator>
252class generator_iterator
253 : public iterator_facade<
254 generator_iterator<Generator>
255 , typename Generator::result_type
256 , single_pass_traversal_tag
257 , typename Generator::result_type const&
258 >
259{
260 typedef iterator_facade<
261 generator_iterator<Generator>
262 , typename Generator::result_type
263 , single_pass_traversal_tag
264 , typename Generator::result_type const&
265 > super_t;
266
267 public:
268 generator_iterator() : m_g(NULL), m_value(0) {}
269 generator_iterator(Generator* g) : m_g(g), m_value((*m_g)()) {}
270
271 void increment()
272 {
273 m_value = (*m_g)();
274 }
275
276 const typename Generator::result_type&
277 dereference() const
278 {
279 return m_value;
280 }
281
282 bool equal(generator_iterator const& y) const
283 {
284 return this->m_g == y.m_g && this->m_value == y.m_value;
285 }
286
287 private:
288 Generator* m_g;
289 typename Generator::result_type m_value;
290};
291
292// seed() seeds a random number generator with good seed values
293
294template <typename UniformRandomNumberGenerator>
295inline void seed(UniformRandomNumberGenerator& rng)
296{
297 seed_rng seed_gen;
298 generator_iterator<seed_rng> begin(&seed_gen);
299 generator_iterator<seed_rng> end;
300 rng.seed(begin, end);
301}
302
303// random_device does not / can not be seeded
304template <>
305inline void seed<boost::random::random_device>(boost::random::random_device&) {}
306
307// random_device does not / can not be seeded
308template <>
309inline void seed<seed_rng>(seed_rng&) {}
310
311}}} //namespace boost::uuids::detail
312
313#if defined(_MSC_VER)
314#pragma warning(pop) // Restore warnings to previous state.
315#endif
316
317#endif
318