blob: a5467a776cd19e71087dba329fe1ad4cfa314738 [file] [log] [blame]
/* boost random_device.cpp implementation
*
* Copyright Jens Maurer 2000
* Copyright Steven Watanabe 2010
* 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: random_device.cpp 62347 2010-05-31 16:44:36Z steven_watanabe $
*
*/
#define BOOST_RANDOM_SOURCE
#include <boost/nondet_random.hpp>
#include <string>
#include <cassert>
#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_device::has_fixed_range;
const boost::random_device::result_type boost::random_device::min_value;
const boost::random_device::result_type boost::random_device::max_value;
#endif
#if defined(BOOST_WINDOWS)
#include <windows.h>
#include <wincrypt.h>
#include <stdexcept> // std::invalid_argument
#define BOOST_AUTO_LINK_NOMANGLE
#define BOOST_LIB_NAME "Advapi32"
#include <boost/config/auto_link.hpp>
#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
BOOST_RANDOM_DECL const char * const boost::random_device::default_token = MS_DEF_PROV_A;
class boost::random_device::impl
{
public:
impl(const std::string & token) : provider(token) {
char buffer[80];
DWORD type;
DWORD len;
// Find the type of the provider
for(DWORD i = 0; ; ++i) {
len = sizeof(buffer);
if(!CryptEnumProvidersA(i, NULL, 0, &type, buffer, &len)) {
error("Could not find provider name");
}
if(buffer == provider) {
break;
}
}
if(!CryptAcquireContextA(&hProv, NULL, provider.c_str(), type,
CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
error("Could not acquire CSP context");
}
}
~impl() {
if(!CryptReleaseContext(hProv, 0)) error("Could not release CSP context");
}
unsigned int next() {
unsigned int result;
if(!CryptGenRandom(hProv, sizeof(result),
static_cast<BYTE*>(static_cast<void*>(&result)))) {
error("error while reading");
}
return result;
}
private:
void error(const std::string & msg) {
char buf[80];
DWORD num = FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
0,
buf,
sizeof(buf),
NULL);
throw std::invalid_argument("boost::random_device: " + msg +
" Cryptopraphic Service Provider " + provider +
": " + std::string(&buf[0], &buf[0] + num));
}
const std::string provider;
HCRYPTPROV hProv;
};
#else
// 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 boost::random_device::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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> // open
#include <unistd.h> // read, close
#endif
#include <errno.h> // errno
#include <string.h> // strerror
#include <stdexcept> // std::invalid_argument
class boost::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;
long sz = read(fd, reinterpret_cast<char *>(&result), sizeof(result));
if(sz == -1)
error("error while reading");
else if(sz != sizeof(result)) {
errno = 0;
error("EOF while reading");
}
return result;
}
private:
void error(const std::string & msg) {
throw std::invalid_argument("boost::random_device: " + msg +
" random-number pseudo-device " + path +
": " + strerror(errno));
}
const std::string path;
int fd;
};
#endif // BOOST_WINDOWS
BOOST_RANDOM_DECL boost::random_device::random_device(const std::string& token)
: pimpl(new impl(token))
{
assert((std::numeric_limits<result_type>::max)() == max_value);
}
BOOST_RANDOM_DECL boost::random_device::~random_device()
{
// the complete class impl is now visible, so we're safe
// (see comment in random.hpp)
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();
}