mirror of
https://github.com/aria2/aria2.git
synced 2025-01-04 09:03:46 +00:00
Revise getRandom facilities
Use one of the following to provide random bytes: - Windows CryptGenRandom - Linux getrandom (syscall interface to urandom, without nasty corner cases such as file descriptor exhaustion or re-linked /dev/urandom) - std::device_random (C++ random device, which usually will be urandom) This also equalizes util::getRandom and SimpleRandomizer (the former will now use the latter) instead of having essentially two different PRNG interfaces with potentially different quality. Closes GH-320
This commit is contained in:
parent
747131a06c
commit
81bdd5f61a
14
configure.ac
14
configure.ac
@ -720,6 +720,7 @@ AC_CHECK_FUNCS([__argz_count \
|
||||
gethostbyname \
|
||||
getifaddrs \
|
||||
getpagesize \
|
||||
getrandom \
|
||||
memchr \
|
||||
memmove \
|
||||
mempcpy \
|
||||
@ -755,6 +756,19 @@ AC_CHECK_FUNCS([__argz_count \
|
||||
utime \
|
||||
utimes])
|
||||
|
||||
AC_MSG_CHECKING([for getrandom linux syscall interface])
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <linux/random.h>
|
||||
]],
|
||||
[[
|
||||
int x = GRND_NONBLOCK;
|
||||
]])],
|
||||
[have_getrandom_interface=yes
|
||||
AC_DEFINE([HAVE_GETRANDOM_INTERFACE], [1], [Define to 1 if getrandom linux syscall interface is available.])],
|
||||
[have_getrandom_interface=no])
|
||||
AC_MSG_RESULT([$have_getrandom_interface])
|
||||
AM_CONDITIONAL([HAVE_GETRANDOM_INTERFACE], [test "x$have_getrandom_interface" = "xyes"])
|
||||
|
||||
dnl Put tcmalloc/jemalloc checks after the posix_memalign check.
|
||||
dnl These libraries may implement posix_memalign, while the usual CRT may not
|
||||
dnl (e.g. mingw). Since we aren't including the corresponding library headers
|
||||
|
@ -163,7 +163,6 @@ Context::Context(bool standalone,
|
||||
throw DL_ABORT_EX("Option processing failed");
|
||||
}
|
||||
}
|
||||
SimpleRandomizer::getInstance()->init();
|
||||
#ifdef ENABLE_BITTORRENT
|
||||
bittorrent::generateStaticPeerId(op->get(PREF_PEER_ID_PREFIX));
|
||||
#endif // ENABLE_BITTORRENT
|
||||
|
@ -687,6 +687,10 @@ if HAVE_KQUEUE
|
||||
SRCS += KqueueEventPoll.cc KqueueEventPoll.h
|
||||
endif # HAVE_KQUEUE
|
||||
|
||||
if HAVE_GETRANDOM_INTERFACE
|
||||
SRCS += getrandom_linux.c getrandom_linux.h
|
||||
endif # HAVE_GETRANDOM_INTERFACE
|
||||
|
||||
if HAVE_LIBUV
|
||||
SRCS += LibuvEventPoll.cc LibuvEventPoll.h
|
||||
endif # HAVE_LIBUV
|
||||
|
@ -43,6 +43,10 @@
|
||||
#include "a2time.h"
|
||||
#include "a2functional.h"
|
||||
|
||||
#ifdef HAVE_GETRANDOM_INTERFACE
|
||||
# include "getrandom_linux.h"
|
||||
#endif
|
||||
|
||||
namespace aria2 {
|
||||
|
||||
std::unique_ptr<SimpleRandomizer> SimpleRandomizer::randomizer_;
|
||||
@ -55,75 +59,49 @@ const std::unique_ptr<SimpleRandomizer>& SimpleRandomizer::getInstance()
|
||||
return randomizer_;
|
||||
}
|
||||
|
||||
void SimpleRandomizer::init()
|
||||
{
|
||||
#ifndef __MINGW32__
|
||||
// Just in case std::random_device() is fixed, add time and pid too.
|
||||
eng_.seed(std::random_device()()^time(nullptr)^getpid());
|
||||
#endif // !__MINGW32__
|
||||
}
|
||||
|
||||
SimpleRandomizer::SimpleRandomizer()
|
||||
{
|
||||
#ifdef __MINGW32__
|
||||
BOOL r = CryptAcquireContext(&cryProvider_, 0, 0, PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT|CRYPT_SILENT);
|
||||
BOOL r = ::CryptAcquireContext(
|
||||
&provider_,
|
||||
0, 0, PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
|
||||
assert(r);
|
||||
#endif // __MINGW32__
|
||||
#endif
|
||||
}
|
||||
|
||||
SimpleRandomizer::~SimpleRandomizer()
|
||||
{
|
||||
#ifdef __MINGW32__
|
||||
CryptReleaseContext(cryProvider_, 0);
|
||||
#endif // __MINGW32__
|
||||
CryptReleaseContext(provider_, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
long int SimpleRandomizer::getRandomNumber(long int to)
|
||||
{
|
||||
assert(to > 0);
|
||||
return std::uniform_int_distribution<long int>(0, to - 1)(*this);
|
||||
}
|
||||
|
||||
void SimpleRandomizer::getRandomBytes(unsigned char* buf, size_t len)
|
||||
{
|
||||
#ifdef __MINGW32__
|
||||
int32_t val;
|
||||
BOOL r = CryptGenRandom(cryProvider_, sizeof(val),
|
||||
reinterpret_cast<BYTE*>(&val));
|
||||
BOOL r = CryptGenRandom(provider_, len, reinterpret_cast<BYTE*>(buf));
|
||||
assert(r);
|
||||
if(val == INT32_MIN) {
|
||||
val = INT32_MAX;
|
||||
} else if(val < 0) {
|
||||
val = -val;
|
||||
#elif defined(HAVE_GETRANDOM_INTERFACE)
|
||||
auto rv = getrandom_linux(buf, len);
|
||||
assert(rv >= 0 && (size_t)rv == len);
|
||||
#else // ! __MINGW32__
|
||||
auto ubuf = reinterpret_cast<result_type*>(buf);
|
||||
size_t q = len / sizeof(result_type);
|
||||
auto gen = std::uniform_int_distribution<result_type>();
|
||||
for(; q > 0; --q, ++ubuf) {
|
||||
*ubuf = gen(dev_);
|
||||
}
|
||||
return val % to;
|
||||
#else // !__MINGW32__
|
||||
return std::uniform_int_distribution<long int>(0, to - 1)(eng_);
|
||||
#endif // !__MINGW32__
|
||||
}
|
||||
|
||||
long int SimpleRandomizer::operator()(long int to)
|
||||
{
|
||||
return getRandomNumber(to);
|
||||
}
|
||||
|
||||
void SimpleRandomizer::getRandomBytes(unsigned char *buf, size_t len)
|
||||
{
|
||||
#ifdef __MINGW32__
|
||||
if (!CryptGenRandom(cryProvider_, len, (PBYTE)buf)) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
#else
|
||||
uint32_t val;
|
||||
size_t q = len / sizeof(val);
|
||||
size_t r = len % sizeof(val);
|
||||
auto gen = std::bind(std::uniform_int_distribution<uint32_t>
|
||||
(0, std::numeric_limits<uint32_t>::max()),
|
||||
eng_);
|
||||
for(; q > 0; --q) {
|
||||
val = gen();
|
||||
memcpy(buf, &val, sizeof(val));
|
||||
buf += sizeof(val);
|
||||
}
|
||||
val = gen();
|
||||
memcpy(buf, &val, r);
|
||||
#endif
|
||||
const size_t r = len % sizeof(result_type);
|
||||
auto last = gen(dev_);
|
||||
memcpy(ubuf, &last, r);
|
||||
#endif // ! __MINGW32__
|
||||
}
|
||||
|
||||
} // namespace aria2
|
||||
|
@ -41,30 +41,32 @@
|
||||
#include <random>
|
||||
|
||||
#ifdef __MINGW32__
|
||||
# include <wincrypt.h>
|
||||
#endif // __MINGW32__
|
||||
# include <wincrypt.h>
|
||||
#endif
|
||||
|
||||
namespace aria2 {
|
||||
|
||||
class SimpleRandomizer : public Randomizer {
|
||||
private:
|
||||
static std::unique_ptr<SimpleRandomizer> randomizer_;
|
||||
|
||||
#ifdef __MINGW32__
|
||||
HCRYPTPROV cryProvider_;
|
||||
#else // !__MINGW32__
|
||||
std::minstd_rand eng_;
|
||||
#endif //!__MINGW32__
|
||||
|
||||
SimpleRandomizer();
|
||||
|
||||
private:
|
||||
#ifdef __MINGW32__
|
||||
HCRYPTPROV provider_;
|
||||
#elif defined(HAVE_GETRANDOM_INTERFACE)
|
||||
// Nothing special needed
|
||||
#else
|
||||
std::random_device dev_;
|
||||
#endif // ! __MINGW32__
|
||||
|
||||
public:
|
||||
typedef std::random_device::result_type result_type;
|
||||
|
||||
static const std::unique_ptr<SimpleRandomizer>& getInstance();
|
||||
|
||||
virtual ~SimpleRandomizer();
|
||||
|
||||
void init();
|
||||
|
||||
/**
|
||||
* Returns random number in [0, to).
|
||||
*/
|
||||
@ -72,7 +74,32 @@ public:
|
||||
|
||||
void getRandomBytes(unsigned char *buf, size_t len);
|
||||
|
||||
long int operator()(long int to);
|
||||
long int operator()(long int to)
|
||||
{
|
||||
return getRandomNumber(to);
|
||||
}
|
||||
|
||||
result_type operator()()
|
||||
{
|
||||
result_type rv;
|
||||
getRandomBytes(reinterpret_cast<unsigned char*>(&rv), sizeof(rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
static constexpr result_type min()
|
||||
{
|
||||
return std::numeric_limits<result_type>::min();
|
||||
}
|
||||
|
||||
static constexpr result_type max()
|
||||
{
|
||||
return std::numeric_limits<result_type>::max();
|
||||
}
|
||||
|
||||
static double entropy()
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace aria2
|
||||
|
50
src/getrandom_linux.c
Normal file
50
src/getrandom_linux.c
Normal file
@ -0,0 +1,50 @@
|
||||
/* <!-- copyright */
|
||||
/*
|
||||
* aria2 - The high speed download utility
|
||||
*
|
||||
* Copyright (C) 2014 Nils Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*/
|
||||
/* copyright --> */
|
||||
|
||||
#define _GNUSOURCE
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/random.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "getrandom_linux.h"
|
||||
|
||||
int getrandom_linux(void *buf, size_t buflen) {
|
||||
#ifdef HAVE_GETRANDOM
|
||||
return getrandom(buf, buflen, 0);
|
||||
#else // HAVE_GETRANDOM
|
||||
return syscall(SYS_getrandom, buf, buflen, 0);
|
||||
#endif // HAVE_GETRANDOM
|
||||
}
|
49
src/getrandom_linux.h
Normal file
49
src/getrandom_linux.h
Normal file
@ -0,0 +1,49 @@
|
||||
/* <!-- copyright */
|
||||
/*
|
||||
* aria2 - The high speed download utility
|
||||
*
|
||||
* Copyright (C) 2014 Nils Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*/
|
||||
/* copyright --> */
|
||||
|
||||
#ifndef D_GETRANDOM_LINUX_H
|
||||
#define D_GETRANDOM_LINUX_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int getrandom_linux(void *buf, size_t buflen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* D_GETRANDOM_LINUX_H */
|
39
src/util.cc
39
src/util.cc
@ -1545,45 +1545,10 @@ std::vector<std::pair<size_t, std::string> > createIndexPaths(std::istream& i)
|
||||
return indexPaths;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void generateRandomDataRandom(unsigned char* data, size_t length)
|
||||
{
|
||||
const auto& rd = SimpleRandomizer::getInstance();
|
||||
rd->getRandomBytes(data, length);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#ifndef __MINGW32__
|
||||
namespace {
|
||||
void generateRandomDataUrandom
|
||||
(unsigned char* data, size_t length, std::ifstream& devUrand)
|
||||
{
|
||||
devUrand.read(reinterpret_cast<char*>(data), length);
|
||||
}
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
void generateRandomData(unsigned char* data, size_t length)
|
||||
{
|
||||
#ifdef __MINGW32__
|
||||
generateRandomDataRandom(data, length);
|
||||
#else // !__MINGW32__
|
||||
static int method = -1;
|
||||
static std::ifstream devUrand;
|
||||
if(method == 0) {
|
||||
generateRandomDataUrandom(data, length, devUrand);
|
||||
} else if(method == 1) {
|
||||
generateRandomDataRandom(data, length);
|
||||
} else {
|
||||
devUrand.open("/dev/urandom");
|
||||
if(devUrand) {
|
||||
method = 0;
|
||||
} else {
|
||||
method = 1;
|
||||
}
|
||||
generateRandomData(data, length);
|
||||
}
|
||||
#endif // !__MINGW32__
|
||||
const auto& rd = SimpleRandomizer::getInstance();
|
||||
return rd->getRandomBytes(data, length);
|
||||
}
|
||||
|
||||
bool saveAs
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "util.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
@ -1954,11 +1955,54 @@ void UtilTest::testCreateIndexPaths()
|
||||
|
||||
void UtilTest::testGenerateRandomData()
|
||||
{
|
||||
unsigned char data1[20];
|
||||
using namespace std;
|
||||
|
||||
// Simple sanity check
|
||||
unsigned char data1[25];
|
||||
memset(data1, 0, sizeof(data1));
|
||||
util::generateRandomData(data1, sizeof(data1));
|
||||
unsigned char data2[20];
|
||||
|
||||
unsigned char data2[25];
|
||||
memset(data2, 0, sizeof(data2));
|
||||
util::generateRandomData(data2, sizeof(data2));
|
||||
|
||||
CPPUNIT_ASSERT(memcmp(data1, data2, sizeof(data1)) != 0);
|
||||
|
||||
// Simple stddev/mean tests
|
||||
map<uint8_t, size_t> counts;
|
||||
uint8_t bytes[1 << 20];
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
util::generateRandomData(bytes, sizeof(bytes));
|
||||
for (auto b : bytes) {
|
||||
counts[b]++;
|
||||
}
|
||||
}
|
||||
CPPUNIT_ASSERT_MESSAGE("Should see all kinds of bytes", counts.size() == 256);
|
||||
if (counts.size() != 256) {
|
||||
throw std::domain_error(
|
||||
"Would have expected to see at one of each possible byte value!");
|
||||
}
|
||||
double sum = accumulate(
|
||||
counts.begin(),
|
||||
counts.end(),
|
||||
0.0,
|
||||
[](double total, const decltype(counts)::value_type & elem) {
|
||||
return total + elem.second;
|
||||
});
|
||||
double mean = sum / counts.size();
|
||||
vector<double> diff(counts.size());
|
||||
transform(
|
||||
counts.begin(),
|
||||
counts.end(),
|
||||
diff.begin(),
|
||||
[&](const decltype(counts)::value_type & elem) -> double {
|
||||
return (double)elem.second - mean;
|
||||
});
|
||||
double sq_sum = inner_product(diff.begin(), diff.end(), diff.begin(), 0.0);
|
||||
double stddev = sqrt(sq_sum / counts.size());
|
||||
cout << "stddev: " << fixed << stddev << endl;
|
||||
CPPUNIT_ASSERT_MESSAGE("stddev makes sense (lower)", stddev <= 320);
|
||||
CPPUNIT_ASSERT_MESSAGE("stddev makes sense (upper)", stddev >= 100);
|
||||
}
|
||||
|
||||
void UtilTest::testFromHex()
|
||||
|
Loading…
Reference in New Issue
Block a user