From 4862b8a49ec195e155e6046587e4a5cf680898aa Mon Sep 17 00:00:00 2001 From: "James E. King, III" Date: Wed, 20 Sep 2017 16:16:58 -0400 Subject: [PATCH] refactored random_device to be concept-driven and provide Universal Windows Platform (UWP) support - configurable entropy size - file provider for unix (/dev/urandom) - wincrypt provider for windows desktop targets - bcrypt provider for windows store/phone targets - fully compatible with previous implementations - Boost.Random becomes header-only as a result --- Jamfile | 13 + build/Jamfile.v2 | 18 -- .../random/detail/random_device_bcrypt.hpp | 110 ++++++++ .../random/detail/random_device_file.hpp | 100 +++++++ .../random/detail/random_device_wincrypt.hpp | 140 ++++++++++ include/boost/random/random_device.hpp | 149 +++++++---- src/random_device.cpp | 250 ------------------ test/Jamfile.v2 | 4 +- test/test_random_device.cpp | 66 ++++- 9 files changed, 511 insertions(+), 339 deletions(-) create mode 100644 Jamfile delete mode 100644 build/Jamfile.v2 create mode 100644 include/boost/random/detail/random_device_bcrypt.hpp create mode 100644 include/boost/random/detail/random_device_file.hpp create mode 100644 include/boost/random/detail/random_device_wincrypt.hpp delete mode 100644 src/random_device.cpp diff --git a/Jamfile b/Jamfile new file mode 100644 index 0000000000..489c9a4c93 --- /dev/null +++ b/Jamfile @@ -0,0 +1,13 @@ +# Boost.Random Library Jamfile +# +# Copyright (c) 2010 +# Steven Watanabe +# +# Use, modification, and distribution are subject to 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) + +project libs/random ; + +# please order by name to ease maintenance +build-project test ; diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 deleted file mode 100644 index b4e9ceae30..0000000000 --- a/build/Jamfile.v2 +++ /dev/null @@ -1,18 +0,0 @@ -# Jamfile.v2 -# -# Copyright (c) 2010 -# Steven Watanabe -# -# 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) - -project /boost/random - : source-location ../src - : requirements shared:BOOST_RANDOM_DYN_LINK - : usage-requirements shared:BOOST_RANDOM_DYN_LINK -; - -lib boost_random : [ glob *.cpp ] /boost//system ; - -boost-install boost_random ; diff --git a/include/boost/random/detail/random_device_bcrypt.hpp b/include/boost/random/detail/random_device_bcrypt.hpp new file mode 100644 index 0000000000..adf3b105fc --- /dev/null +++ b/include/boost/random/detail/random_device_bcrypt.hpp @@ -0,0 +1,110 @@ +/* boost random/detail/random_device_bcrypt header file +* +* Copyright 2017 James E. King, III +* +* 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) +* +* $Id$ +* +* Revision history +* 2017-09-14 initial bcrypt implementation +*/ + +#ifndef BOOST_RANDOM_DETAIL_RANDOM_DEVICE_BCRYPT +#define BOOST_RANDOM_DETAIL_RANDOM_DEVICE_BCRYPT + +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +#if defined(BOOST_WINDOWS) +#include + +#if !(BOOST_WINAPI_PARTITION_DESKTOP || BOOST_WINAPI_PARTITION_SYSTEM) || defined(BOOST_RANDOM_DEVICE_FORCE_BCRYPT) + +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(BOOST_RANDOM_DEVICE_NO_LIB) +# define BOOST_LIB_NAME "bcrypt" +# define BOOST_AUTO_LINK_NOMANGLE +# include +#endif + +namespace boost { +namespace random { +namespace detail { + +template +class random_device_bcrypt : private noncopyable +{ +public: + typedef Entropy result_type; + + random_device_bcrypt(const std::string& token = std::string()) + : hProv_(NULL) + { + boost::ignore_unused(token); + boost::winapi::NTSTATUS_ status = + boost::winapi::BCryptOpenAlgorithmProvider( + &hProv_, + boost::winapi::BCRYPT_RNG_ALGORITHM_, + NULL, + 0); + + if (status) + { + BOOST_THROW_EXCEPTION(system::system_error( + status, system::system_category(), "BCryptOpenAlgorithmProvider")); + } + } + + ~random_device_bcrypt() BOOST_NOEXCEPT + { + if (hProv_) + { + ignore_unused(boost::winapi::BCryptCloseAlgorithmProvider(hProv_, 0)); + } + } + + result_type operator()() + { + result_type result; + + boost::winapi::NTSTATUS_ status = + boost::winapi::BCryptGenRandom( + hProv_, + reinterpret_cast(&result), + sizeof(result), + 0); + + if (status) + { + BOOST_THROW_EXCEPTION(system::system_error( + status, system::system_category(), "BCryptGenRandom")); + } + + return result; + } + +private: + boost::winapi::BCRYPT_ALG_HANDLE_ hProv_; +}; + +} // detail +} // random +} // boost + +#endif // !(BOOST_WINAPI_PARTITION_DESKTOP || BOOST_WINAPI_PARTITION_SYSTEM) || defined(BOOST_RANDOM_DEVICE_FORCE_BCRYPT) +#endif // BOOST_WINDOWS +#endif // BOOST_RANDOM_DETAIL_RANDOM_DEVICE_BCRYPT diff --git a/include/boost/random/detail/random_device_file.hpp b/include/boost/random/detail/random_device_file.hpp new file mode 100644 index 0000000000..71a2c311e8 --- /dev/null +++ b/include/boost/random/detail/random_device_file.hpp @@ -0,0 +1,100 @@ +/* boost random/detail/random_device_file header file +* +* Copyright Jens Maurer 2000 +* Copyright 2007 Andy Tompkins. +* Copyright Steven Watanabe 2010-2011 +* Copyright 2017 James E. King, III +* +* 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) +* +* $Id$ +* +* Revision history +* 2017-09-14 urandom implementation moved here +*/ + +#ifndef BOOST_RANDOM_DETAIL_RANDOM_DEVICE_FILE +#define BOOST_RANDOM_DETAIL_RANDOM_DEVICE_FILE + +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +#if !defined(BOOST_WINDOWS) + +#include +#include +#include +#include +#include +#include // open +#include +#include +#include +#if defined(BOOST_HAS_UNISTD_H) +#include +#endif + +namespace boost { +namespace random { +namespace detail { + +template +class random_device_file : private noncopyable +{ +public: + typedef Entropy result_type; + + random_device_file(const std::string& token = std::string()) + : fd_(0) + { + fd_ = open(token.empty() ? "/dev/urandom" : token.c_str(), O_RDONLY); + + if (-1 == fd_) + { + BOOST_THROW_EXCEPTION(system::system_error( + errno, system::system_category(), "open " + std::string(token.empty() ? "/dev/urandom" : token.c_str()))); + } + } + + ~random_device_file() BOOST_NOEXCEPT + { + if (fd_) + { + ignore_unused(close(fd_)); + } + } + + result_type operator()() + { + result_type result; + size_t offset = 0; + do + { + ssize_t sz = read(fd_, reinterpret_cast(&result) + offset, sizeof(result) - offset); + + if (sz < 1) + { + BOOST_THROW_EXCEPTION(system::system_error( + errno, system::system_category(), "read")); + } + offset += sz; + } while (offset < sizeof(result)); + + return result; + } + +private: + int fd_; +}; + +} // detail +} // random +} // boost + +#endif // !defined(BOOST_WINDOWS) +#endif // BOOST_RANDOM_DETAIL_RANDOM_DEVICE_FILE diff --git a/include/boost/random/detail/random_device_wincrypt.hpp b/include/boost/random/detail/random_device_wincrypt.hpp new file mode 100644 index 0000000000..e9face076b --- /dev/null +++ b/include/boost/random/detail/random_device_wincrypt.hpp @@ -0,0 +1,140 @@ +/* boost random/detail/random_device_wincrypt header file + * + * Copyright Jens Maurer 2000 + * Copyright 2007 Andy Tompkins. + * Copyright Steven Watanabe 2010-2011 + * Copyright 2017 James E. King, III + * + * 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) + * + * $Id$ + * + * Revision history + * 2017-09-14 wincrypt implementation moved here + */ + +#ifndef BOOST_RANDOM_DETAIL_RANDOM_DEVICE_WINCRYPT +#define BOOST_RANDOM_DETAIL_RANDOM_DEVICE_WINCRYPT + +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +#if defined(BOOST_WINDOWS) +#include + +#if BOOST_WINAPI_PARTITION_DESKTOP || BOOST_WINAPI_PARTITION_SYSTEM + +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(BOOST_RANDOM_DEVICE_NO_LIB) +# if defined(_WIN32_WCE) +# define BOOST_LIB_NAME "coredll" +# else +# define BOOST_LIB_NAME "advapi32" +# endif +# define BOOST_AUTO_LINK_NOMANGLE +# include +#endif + +namespace boost { +namespace random { +namespace detail { + +template +class random_device_wincrypt : private noncopyable +{ +public: + typedef Entropy result_type; + + random_device_wincrypt(const std::string& token = std::string()) + : hProv_(NULL) + { +#if defined(BOOST_NO_ANSI_APIS) + // Without ANSI APIs, the token is ignored + ignore_unused(token); + if (!boost::winapi::CryptAcquireContextW( + &hProv_, + NULL, + NULL, + boost::winapi::PROV_RNG_, + boost::winapi::CRYPT_VERIFYCONTEXT_ | boost::winapi::CRYPT_SILENT_)) + { + BOOST_THROW_EXCEPTION(system::system_error( + boost::winapi::GetLastError(), system::system_category(), "CryptAcquireContextW")); + } +#else + // With ANSI APIs, the behavior is backwards compatible to previous releases + boost::winapi::CHAR_ buffer[256]; + boost::winapi::DWORD_ type; + boost::winapi::DWORD_ len; + std::string provider = token.empty() ? "Microsoft Base Cryptographic Provider v1.0" : token; + + // Find the type of a specific provider + for (boost::winapi::DWORD_ i = 0; ; ++i) + { + len = sizeof(buffer); + if (!boost::winapi::CryptEnumProvidersA(i, NULL, 0, &type, buffer, &len)) + { + BOOST_THROW_EXCEPTION(system::system_error( + boost::winapi::GetLastError(), system::system_category(), "CryptEnumProvidersA")); + } + if (buffer == provider) { + break; + } + } + + if (!boost::winapi::CryptAcquireContextA( + &hProv_, + NULL, + provider.c_str(), + type, + boost::winapi::CRYPT_VERIFYCONTEXT_ | boost::winapi::CRYPT_SILENT_)) + { + BOOST_THROW_EXCEPTION(system::system_error( + boost::winapi::GetLastError(), system::system_category(), "CryptAcquireContextA")); + } +#endif + } + + ~random_device_wincrypt() BOOST_NOEXCEPT + { + if (hProv_) + { + ignore_unused(boost::winapi::CryptReleaseContext(hProv_, 0)); + } + } + + result_type operator()() + { + result_type result; + if (!boost::winapi::CryptGenRandom(hProv_, sizeof(result), boost::winapi::detail::cast_ptr(&result))) + { + BOOST_THROW_EXCEPTION(system::system_error( + boost::winapi::GetLastError(), system::system_category(), "CryptGenRandom")); + } + return result; + } + +private: + boost::winapi::HCRYPTPROV_ hProv_; +}; + +} // detail +} // random +} // boost + +#endif // BOOST_WINAPI_PARTITION_DESKTOP || BOOST_WINAPI_PARTITION_SYSTEM +#endif // BOOST_WINDOWS +#endif // BOOST_RANDOM_DETAIL_RANDOM_DEVICE_WINCRYPT diff --git a/include/boost/random/random_device.hpp b/include/boost/random/random_device.hpp index 8f3903c953..9bb1dc0cd7 100644 --- a/include/boost/random/random_device.hpp +++ b/include/boost/random/random_device.hpp @@ -1,7 +1,10 @@ /* boost random/random_device.hpp header file * * Copyright Jens Maurer 2000 + * Copyright 2007 Andy Tompkins. * Copyright Steven Watanabe 2010-2011 + * Copyright 2017 James E. King, III + * * 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) @@ -10,19 +13,21 @@ * * Revision history * 2000-02-18 Portability fixes (thanks to Beman Dawes) + * 2017-09-04 Made header-only, configurable entropy size, UWP compatible */ // See http://www.boost.org/libs/random for documentation. - #ifndef BOOST_RANDOM_RANDOM_DEVICE_HPP #define BOOST_RANDOM_RANDOM_DEVICE_HPP -#include #include -#include -#include -#include // force autolink to find Boost.System +#include +#include + +#include +#include +#include namespace boost { namespace random { @@ -50,93 +55,123 @@ namespace random { * on all platforms. * @endxmlnote * - * Implementation Note for Linux + * Implementation Note for Unix * - * On the Linux operating system, token is interpreted as a filesystem + * On Unix operating systems, the token is interpreted as a filesystem * path. It is assumed that this path denotes an operating system * pseudo-device which generates a stream of non-deterministic random * numbers. The pseudo-device should never signal an error or end-of-file. - * Otherwise, @c std::ios_base::failure is thrown. By default, - * \random_device uses the /dev/urandom pseudo-device to retrieve + * + * By default, \random_device uses the /dev/urandom pseudo-device to retrieve * the random numbers. Another option would be to specify the /dev/random * pseudo-device, which blocks on reads if the entropy pool has no more * random bits available. * * Implementation Note for Windows + * + * When targeting Windows Desktop or System applications with UWP, or when + * not using UWP (older SDKs), the traditional wincrypt provider is used to + * get entropy for backwards compatibility with the previous implementation. + * This ensures anyone who has developed and use their own crypto provider + * for random number generation can still use it. The optional token can + * be the name of a wincrypt provider. If no token is specified, the default + * MS_DEF_PROV_A is used. If BOOST_NO_ANSI_APIS is defined, the token is ignored. + * + * For all other windows targets, for example Windows Store UWP targets, + * bcrypt is used to acquire entropy. In these cases, the token is ignored. + * There are some platform/SDK combinations where it is not possible to have + * a random device on Windows - for example Windows SDK 8.x with a non-desktop + * target, since bcrypt is specified as desktop-only in SDK 8.x and expanded to + * other partitions in SDK 10.x. + * + * Compile-Time Definitions + * + * [Windows] To force use of bcrypt over wincrypt on desktop platforms, + * define BOOST_RANDOM_DEVICE_FORCE_BCRYPT + * + * [Windows] To disable automatic link libraries being added by providers, + * define BOOST_RANDOM_DEVICE_NO_LIB + * + * [All] To prevent the automatic definition of boost::random::random_device, + * define BOOST_RANDOM_DEVICE_NO_DEFAULT_IMPL + * + * Exceptions * - * On the Windows operating system, token is interpreted as the name - * of a cryptographic service provider. By default \random_device uses - * MS_DEF_PROV. + * Errors will result in a boost::system::system_error exception. * - * Performance + * Concepts * - * The test program - * nondet_random_speed.cpp measures the execution times of the - * random_device.hpp implementation of the above algorithms in a tight - * loop. The performance has been evaluated on an - * Intel(R) Core(TM) i7 CPU Q 840 \@ 1.87GHz, 1867 Mhz with - * Visual C++ 2010, Microsoft Windows 7 Professional and with gcc 4.4.5, - * Ubuntu Linux 2.6.35-25-generic. + * A RandomDeviceProvider must: + * - provide a result_type type definition + * - provide an operator() implementation returning a result_type * - * - * - * - * - *
Platformtime per invocation [microseconds]
Windows 2.9
Linux 1.7
+ * A UniformRandomNumberGenerator must (as documented): + * - provide a min() + * - provide a max() + * - provide an operator() * - * The measurement error is estimated at +/- 1 usec. + * The common parts of a UniformRandomNumberGenerator are provided by + * basic_random_device. */ -class random_device : private noncopyable +template +class basic_random_device : public RandomDeviceProvider { public: - typedef unsigned int result_type; + basic_random_device(const std::string& token = std::string()) + : RandomDeviceProvider(token) + { + } + + typedef typename RandomDeviceProvider::result_type result_type; BOOST_STATIC_CONSTANT(bool, has_fixed_range = false); /** Returns the smallest value that the \random_device can produce. */ - static result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () { return 0; } - /** Returns the largest value that the \random_device can produce. */ - static result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () { return ~0u; } - - /** Constructs a @c random_device, optionally using the default device. */ - BOOST_RANDOM_DECL random_device(); - /** - * Constructs a @c random_device, optionally using the given token as an - * access specification (for example, a URL) to some implementation-defined - * service for monitoring a stochastic process. - */ - BOOST_RANDOM_DECL explicit random_device(const std::string& token); + static result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () + { + return (std::numeric_limits::min)(); + } - BOOST_RANDOM_DECL ~random_device(); + /** Returns the largest value that the \random_device can produce. */ + static result_type max BOOST_PREVENT_MACRO_SUBSTITUTION() + { + return (std::numeric_limits::max)(); + } /** - * Returns: An entropy estimate for the random numbers returned by - * operator(), in the range min() to log2( max()+1). A deterministic - * random number generator (e.g. a pseudo-random number engine) - * has entropy 0. - * - * Throws: Nothing. + * Fills a range with random values. + * Throws boost::system::system_error if an error occurs. */ - BOOST_RANDOM_DECL double entropy() const; - /** Returns a random value in the range [min, max]. */ - BOOST_RANDOM_DECL unsigned int operator()(); - - /** Fills a range with random 32-bit values. */ template void generate(Iter begin, Iter end) { - for(; begin != end; ++begin) { + for(; begin != end; ++begin) + { *begin = (*this)(); } } - -private: - class impl; - impl * pimpl; }; +// +// Automatically select a default platform specific implementation +// + +#if !defined(BOOST_RANDOM_DEVICE_NO_DEFAULT_IMPL) +#if defined(BOOST_WINDOWS) +#if (BOOST_WINAPI_PARTITION_DESKTOP || BOOST_WINAPI_PARTITION_SYSTEM) && !defined(BOOST_RANDOM_DEVICE_FORCE_BCRYPT) + typedef basic_random_device > random_device; +#else + typedef basic_random_device > random_device; +#endif +#else + typedef basic_random_device > random_device; +#endif +#endif + } // namespace random +#if !defined(BOOST_RANDOM_DEVICE_NO_DEFAULT_IMPL) using random::random_device; +#endif } // namespace boost diff --git a/src/random_device.cpp b/src/random_device.cpp deleted file mode 100644 index 8ec3863107..0000000000 --- a/src/random_device.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* boost random_device.cpp implementation - * - * Copyright Jens Maurer 2000 - * Copyright Steven Watanabe 2010-2011 - * 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) - * - * $Id$ - * - */ - -#define BOOST_RANDOM_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include - -#if !defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION) && !BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600)) -// A definition is required even for integral static constants -const bool boost::random::random_device::has_fixed_range; -#endif - -// WinRT target. -#if !defined(BOOST_RANDOM_WINDOWS_RUNTIME) -# if defined(__cplusplus_winrt) -# include -# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) -# define BOOST_RANDOM_WINDOWS_RUNTIME 1 -# endif -# endif -#endif - -#if defined(BOOST_WINDOWS) - -#if !defined(BOOST_RANDOM_WINDOWS_RUNTIME) -#include -#include -#include // std::invalid_argument -#else -using namespace Platform; -using namespace Windows::Security::Cryptography; -#endif - -#define BOOST_AUTO_LINK_NOMANGLE -#define BOOST_LIB_NAME "Advapi32" -#include - -#ifdef __MINGW32__ - -extern "C" { - -// mingw's wincrypt.h appears to be missing some things -WINADVAPI -BOOL -WINAPI -CryptEnumProvidersA( - DWORD dwIndex, - DWORD *pdwReserved, - DWORD dwFlags, - DWORD *pdwProvType, - LPSTR szProvName, - DWORD *pcbProvName - ); - -} - -#endif - -namespace { -#if !defined(BOOST_RANDOM_WINDOWS_RUNTIME) -const char * const default_token = MS_DEF_PROV_A; -#else -const char * const default_token = ""; -#endif -} - -class boost::random::random_device::impl -{ -public: - impl(const std::string & token) : provider(token) { -#if !defined(BOOST_RANDOM_WINDOWS_RUNTIME) - char buffer[80]; - DWORD type; - DWORD len; - - // Find the type of a specific provider - for(DWORD i = 0; ; ++i) { - len = sizeof(buffer); - if(!CryptEnumProvidersA(i, NULL, 0, &type, buffer, &len)) { - if (GetLastError() == ERROR_NO_MORE_ITEMS) break; - continue; - } - if(buffer == provider) { - break; - } - } - - if(!CryptAcquireContextA(&hProv, NULL, provider.c_str(), type, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { - error("Could not acquire CSP context"); - } -#endif - } - -#if !defined(BOOST_RANDOM_WINDOWS_RUNTIME) - ~impl() { - if(!CryptReleaseContext(hProv, 0)) error("Could not release CSP context"); - } -#endif - - unsigned int next() { - unsigned int result; - -#if !defined(BOOST_RANDOM_WINDOWS_RUNTIME) - if(!CryptGenRandom(hProv, sizeof(result), - static_cast(static_cast(&result)))) { - error("error while reading"); - } -#else - auto buffer = CryptographicBuffer::GenerateRandom(sizeof(result)); - auto data = ref new Array(buffer->Length); - CryptographicBuffer::CopyToByteArray(buffer, &data); - memcpy(&result, data->begin(), data->end() - data->begin()); -#endif - - return result; - } - -private: -#if !defined(BOOST_RANDOM_WINDOWS_RUNTIME) - void error(const char * msg) { - DWORD error_code = GetLastError(); - boost::throw_exception( - boost::system::system_error( - error_code, boost::system::system_category(), - std::string("boost::random_device: ") + msg + - " Cryptographic Service Provider " + provider)); - } - HCRYPTPROV hProv; -#endif - const std::string provider; -}; - -#else - -namespace { -// the default is the unlimited capacity device, using some secure hash -// try "/dev/random" for blocking when the entropy pool has drained -const char * const default_token = "/dev/urandom"; -} - -/* - * This uses the POSIX interface for unbuffered reading. - * Using buffered std::istream would consume entropy which may - * not actually be used. Entropy is a precious good we avoid - * wasting. - */ - -#if defined(__GNUC__) && defined(_CXXRT_STD_NAME) -// I have severe difficulty to get the POSIX includes to work with -// -fhonor-std and Dietmar Kuhl's standard C++ library. Hack around that -// problem for now. -extern "C" { -static const int O_RDONLY = 0; -extern int open(const char *__file, int __oflag, ...); -extern int read(int __fd, __ptr_t __buf, size_t __nbytes); -extern int close(int __fd); -} -#else -#include -#include -#include // open -#include // read, close -#endif - -#include // errno -#include // strerror -#include // std::invalid_argument - - -class boost::random::random_device::impl -{ -public: - impl(const std::string & token) : path(token) { - fd = open(token.c_str(), O_RDONLY); - if(fd < 0) - error("cannot open"); - } - - ~impl() { if(close(fd) < 0) error("could not close"); } - - unsigned int next() { - unsigned int result; - std::size_t offset = 0; - do { - long sz = read(fd, reinterpret_cast(&result) + offset, sizeof(result) - offset); - if(sz == -1) - error("error while reading"); - else if(sz == 0) { - errno = 0; - error("EOF while reading"); - } - offset += sz; - } while(offset < sizeof(result)); - return result; - } - -private: - void error(const char * msg) { - int error_code = errno; - boost::throw_exception( - boost::system::system_error( - error_code, boost::system::system_category(), - std::string("boost::random_device: ") + msg + - " random-number pseudo-device " + path)); - } - const std::string path; - int fd; -}; - -#endif // BOOST_WINDOWS - -BOOST_RANDOM_DECL boost::random::random_device::random_device() - : pimpl(new impl(default_token)) -{} - -BOOST_RANDOM_DECL boost::random::random_device::random_device(const std::string& token) - : pimpl(new impl(token)) -{} - -BOOST_RANDOM_DECL boost::random_device::~random_device() -{ - delete pimpl; -} - -BOOST_RANDOM_DECL double boost::random_device::entropy() const -{ - return 10; -} - -BOOST_RANDOM_DECL unsigned int boost::random_device::operator()() -{ - return pimpl->next(); -} diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 25424bc671..7219eeed26 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -13,9 +13,8 @@ project /boost/random/test : requirements msvc:_SCL_SECURE_NO_W run test_const_mod.cpp /boost//unit_test_framework ; run test_generate_canonical.cpp /boost//unit_test_framework ; run test_random_number_generator.cpp /boost//unit_test_framework ; +run test_random_device.cpp /boost//unit_test_framework ; run ../example/random_demo.cpp ; -run test_random_device.cpp /boost//random : : : static : test_random_device ; -run test_random_device.cpp /boost//random : : : shared : test_random_device_dll ; run test_minstd_rand0.cpp /boost//unit_test_framework ; run test_minstd_rand.cpp /boost//unit_test_framework ; @@ -125,7 +124,6 @@ run test_hyperexponential.cpp ; run test_hyperexponential_distribution.cpp /boost//unit_test_framework ; # run nondet_random_speed.cpp ; -# run random_device.cpp ; # run random_speed.cpp ; # run statistic_tests.cpp ; diff --git a/test/test_random_device.cpp b/test/test_random_device.cpp index 2f9e61c76e..def25b1136 100644 --- a/test/test_random_device.cpp +++ b/test/test_random_device.cpp @@ -8,22 +8,66 @@ * $Id$ */ -#include +#define BOOST_TEST_MAIN +#include +#include -#include -#include +#include +#include -int test_main(int, char**) { +BOOST_AUTO_TEST_CASE(random_device) +{ boost::random_device rng; - double entropy = rng.entropy(); - BOOST_CHECK_GE(entropy, 0); - for(int i = 0; i < 100; ++i) { + + BOOST_CHECK_EQUAL(0u, rng.min()); + BOOST_CHECK_EQUAL(~0u, rng.max()); + + for (int i = 0; i < 100; ++i) + { boost::random_device::result_type val = rng(); - BOOST_CHECK_GE(val, (rng.min)()); - BOOST_CHECK_LE(val, (rng.max)()); + BOOST_CHECK_GE(val, 0u); + BOOST_CHECK_LE(val, ~0u); } - boost::uint32_t a[10]; + boost::random_device::result_type a[10]; rng.generate(a, a + 10); - return 0; } + +#if !defined(BOOST_WINDOWS) +BOOST_AUTO_TEST_CASE(random_device_file_not_there) +{ + using namespace boost::random; + using namespace boost::system; + + BOOST_CHECK_THROW( + basic_random_device >("__fictitious_and_nonexistent_filename__"), + system_error + ); +} + +BOOST_AUTO_TEST_CASE(random_device_file) +{ + using namespace boost::random; + using namespace boost::system; + + const char *tmpfn = "random_device_file_entropy"; + int wfd = open(tmpfn, O_WRONLY | O_CREAT | O_TRUNC, 0777); + BOOST_REQUIRE_NE(wfd, -1); + + basic_random_device > rng(tmpfn); + + // Add some data to the entropy file + BOOST_CHECK_EQUAL(2, write(wfd, "AB", 2)); + + // We can read two characters before we run out of entropy from the file + BOOST_CHECK_EQUAL(0x41, rng()); + BOOST_CHECK_EQUAL(0x42, rng()); + + // Not enough entropy exists for another + BOOST_CHECK_THROW(rng(), system_error); + + // clean up + BOOST_CHECK_EQUAL(close(wfd), 0); + BOOST_CHECK_EQUAL(unlink(tmpfn), 0); +} +#endif