diff options
Diffstat (limited to 'boost/uuid/detail/seed_rng.hpp')
-rw-r--r-- | boost/uuid/detail/seed_rng.hpp | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/boost/uuid/detail/seed_rng.hpp b/boost/uuid/detail/seed_rng.hpp new file mode 100644 index 0000000000..6b7f7808db --- /dev/null +++ b/boost/uuid/detail/seed_rng.hpp @@ -0,0 +1,320 @@ +// Boost seed_rng.hpp header file ----------------------------------------------// + +// Copyright 2007 Andy Tompkins. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Revision History +// 09 Nov 2007 - Initial Revision +// 25 Feb 2008 - moved to namespace boost::uuids::detail +// 28 Nov 2009 - disabled deprecated warnings for MSVC +// 28 Jul 2014 - fixed valgrind warnings and better entropy sources for MSVC + +// seed_rng models a UniformRandomNumberGenerator (see Boost.Random). +// Random number generators are hard to seed well. This is intended to provide +// good seed values for random number generators. +// It creates random numbers from a sha1 hash of data from a variary of sources, +// all of which are standard function calls. It produces random numbers slowly. +// Peter Dimov provided the details of sha1_random_digest_(). +// see http://archives.free.net.ph/message/20070507.175609.4c4f503a.en.html + +#ifndef BOOST_UUID_SEED_RNG_HPP +#define BOOST_UUID_SEED_RNG_HPP + +#include <boost/config.hpp> +#include <cstring> // for memcpy +#include <limits> +#include <ctime> // for time_t, time, clock_t, clock +#include <cstdlib> // for rand +#include <cstdio> // for FILE, fopen, fread, fclose +#include <boost/core/noncopyable.hpp> +#include <boost/uuid/detail/sha1.hpp> +//#include <boost/nondet_random.hpp> //forward declare boost::random::random_device + +// can't use boost::generator_iterator since boost::random number seed(Iter&, Iter) +// functions need a last iterator +//#include <boost/generator_iterator.hpp> +# include <boost/iterator/iterator_facade.hpp> + +#if defined(_MSC_VER) +# pragma warning(push) // Save warning settings. +# pragma warning(disable : 4996) // Disable deprecated std::fopen +#if defined(_WIN32_WCE) +# pragma comment(lib, "coredll.lib") +#else +# pragma comment(lib, "advapi32.lib") +#endif +#endif + +#if defined(BOOST_WINDOWS) +# include <boost/winapi/crypt.hpp> // for CryptAcquireContextA, CryptGenRandom, CryptReleaseContext +# include <boost/winapi/timers.hpp> +# include <boost/winapi/get_current_process_id.hpp> +# include <boost/winapi/get_current_thread_id.hpp> +#else +# include <sys/time.h> // for gettimeofday +# include <sys/types.h> // for pid_t +# include <unistd.h> // for getpid() +#endif + +#ifdef BOOST_NO_STDC_NAMESPACE +namespace std { + using ::memcpy; + using ::time_t; + using ::time; + using ::clock_t; + using ::clock; + using ::rand; + using ::FILE; + using ::fopen; + using ::fread; + using ::fclose; +} //namespace std +#endif + +// forward declare random number generators +namespace boost { namespace random { +class random_device; +}} //namespace boost::random + +namespace boost { +namespace uuids { +namespace detail { + +// should this be part of Boost.Random? +class seed_rng: private boost::noncopyable +{ +public: + typedef unsigned int result_type; + BOOST_STATIC_CONSTANT(bool, has_fixed_range = false); + +public: + // note: rd_ intentionally left uninitialized + seed_rng() BOOST_NOEXCEPT + : rd_index_(5) + , random_(0) + { +#if defined(BOOST_WINDOWS) + if (!boost::winapi::CryptAcquireContextW( + &random_, + NULL, + NULL, + boost::winapi::PROV_RSA_FULL_, + boost::winapi::CRYPT_VERIFYCONTEXT_ | boost::winapi::CRYPT_SILENT_)) + { + random_ = 0; + } +#else + random_ = std::fopen( "/dev/urandom", "rb" ); +#endif + + std::memset(rd_, 0, sizeof(rd_)); + } + + ~seed_rng() BOOST_NOEXCEPT + { + if (random_) { +#if defined(BOOST_WINDOWS) + boost::winapi::CryptReleaseContext(random_, 0); +#else + std::fclose(random_); +#endif + } + } + + result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const BOOST_NOEXCEPT + { + return (std::numeric_limits<result_type>::min)(); + } + result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const BOOST_NOEXCEPT + { + return (std::numeric_limits<result_type>::max)(); + } + + result_type operator()() + { + if (rd_index_ >= 5) { + //get new digest + sha1_random_digest_(); + + rd_index_ = 0; + } + + return rd_[rd_index_++]; + } + +private: + BOOST_STATIC_CONSTANT(std::size_t, internal_state_size = 5); + inline void ignore_size(size_t) {} + + static unsigned int * sha1_random_digest_state_() + { + static unsigned int state[ internal_state_size ]; + return state; + } + + void sha1_random_digest_() + { + boost::uuids::detail::sha1 sha; + + + if (random_) + { + // intentionally left uninitialized + unsigned char state[ 20 ]; +#if defined(BOOST_WINDOWS) + boost::winapi::CryptGenRandom(random_, sizeof(state), state); +#else + ignore_size(std::fread( state, 1, sizeof(state), random_ )); +#endif + sha.process_bytes( state, sizeof( state ) ); + } + + { + // Getting enropy from some system specific sources +#if defined(BOOST_WINDOWS) + boost::winapi::DWORD_ procid = boost::winapi::GetCurrentProcessId(); + sha.process_bytes( (unsigned char const*)&procid, sizeof( procid ) ); + + boost::winapi::DWORD_ threadid = boost::winapi::GetCurrentThreadId(); + sha.process_bytes( (unsigned char const*)&threadid, sizeof( threadid ) ); + + boost::winapi::LARGE_INTEGER_ ts; + ts.QuadPart = 0; + boost::winapi::QueryPerformanceCounter( &ts ); + sha.process_bytes( (unsigned char const*)&ts, sizeof( ts ) ); + + std::time_t tm = std::time( 0 ); + sha.process_bytes( (unsigned char const*)&tm, sizeof( tm ) ); +#else + pid_t pid = getpid(); + sha.process_bytes( (unsigned char const*)&pid, sizeof( pid ) ); + + timeval ts; + gettimeofday(&ts, NULL); // We do not use `clock_gettime` to avoid linkage with -lrt + sha.process_bytes( (unsigned char const*)&ts, sizeof( ts ) ); +#endif + } + + + unsigned int * ps = sha1_random_digest_state_(); + sha.process_bytes( ps, internal_state_size * sizeof( unsigned int ) ); + sha.process_bytes( (unsigned char const*)&ps, sizeof( ps ) ); + + { + std::clock_t ck = std::clock(); + sha.process_bytes( (unsigned char const*)&ck, sizeof( ck ) ); + } + + { + unsigned int rn[] = + { static_cast<unsigned int>(std::rand()) + , static_cast<unsigned int>(std::rand()) + , static_cast<unsigned int>(std::rand()) + }; + sha.process_bytes( (unsigned char const*)rn, sizeof( rn ) ); + } + + { + unsigned int * p = new unsigned int; + sha.process_bytes( (unsigned char const*)&p, sizeof( p ) ); + delete p; + + const seed_rng* this_ptr = this; + sha.process_bytes( (unsigned char const*)&this_ptr, sizeof( this_ptr ) ); + } + + sha.process_bytes( (unsigned char const*)rd_, sizeof( rd_ ) ); + + unsigned int digest[ 5 ]; + sha.get_digest( digest ); + + for( int i = 0; i < 5; ++i ) + { + // harmless data race + ps[ i ] ^= digest[ i ]; + rd_[ i ] ^= digest[ i ]; + } + } + +private: + unsigned int rd_[5]; + int rd_index_; + +#if defined(BOOST_WINDOWS) + boost::winapi::HCRYPTPROV_ random_; +#else + std::FILE * random_; +#endif +}; + +// almost a copy of boost::generator_iterator +// but default constructor sets m_g to NULL +template <class Generator> +class generator_iterator + : public iterator_facade< + generator_iterator<Generator> + , typename Generator::result_type + , single_pass_traversal_tag + , typename Generator::result_type const& + > +{ + typedef iterator_facade< + generator_iterator<Generator> + , typename Generator::result_type + , single_pass_traversal_tag + , typename Generator::result_type const& + > super_t; + + public: + generator_iterator() : m_g(NULL), m_value(0) {} + generator_iterator(Generator* g) : m_g(g), m_value((*m_g)()) {} + + void increment() + { + m_value = (*m_g)(); + } + + const typename Generator::result_type& + dereference() const + { + return m_value; + } + + bool equal(generator_iterator const& y) const + { + return this->m_g == y.m_g && this->m_value == y.m_value; + } + + private: + Generator* m_g; + typename Generator::result_type m_value; +}; + +// seed() seeds a random number generator with good seed values + +template <typename UniformRandomNumberGenerator> +inline void seed(UniformRandomNumberGenerator& rng) +{ + seed_rng seed_gen; + generator_iterator<seed_rng> begin(&seed_gen); + generator_iterator<seed_rng> end; + rng.seed(begin, end); +} + +// random_device does not / can not be seeded +template <> +inline void seed<boost::random::random_device>(boost::random::random_device&) {} + +// random_device does not / can not be seeded +template <> +inline void seed<seed_rng>(seed_rng&) {} + +}}} //namespace boost::uuids::detail + +#if defined(_MSC_VER) +#pragma warning(pop) // Restore warnings to previous state. +#endif + +#endif |