|
|
@ -29,47 +29,113 @@ |
|
|
|
|
|
|
|
|
|
|
|
#include "random.h" |
|
|
|
#include "random.h" |
|
|
|
|
|
|
|
|
|
|
|
#include <cassert> |
|
|
|
#include <limits> |
|
|
|
#include <chrono> |
|
|
|
|
|
|
|
#include <random> |
|
|
|
#include <random> |
|
|
|
|
|
|
|
|
|
|
|
#include <QtGlobal> |
|
|
|
#include <QtGlobal> |
|
|
|
|
|
|
|
|
|
|
|
#ifdef Q_OS_MAC |
|
|
|
#ifdef Q_OS_WIN |
|
|
|
#include <QThreadStorage> |
|
|
|
#ifndef NOMINMAX |
|
|
|
|
|
|
|
#define NOMINMAX |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
#include <Windows.h> |
|
|
|
|
|
|
|
#include <Ntsecapi.h> |
|
|
|
|
|
|
|
#else // Q_OS_WIN
|
|
|
|
|
|
|
|
#include <cstdio> |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
// on some platform `std::random_device` may generate the same number sequence
|
|
|
|
#include "misc.h" |
|
|
|
static bool hasTrueRandomDevice{ std::random_device{}() != std::random_device{}() }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t Utils::Random::rand(const uint32_t min, const uint32_t max) |
|
|
|
namespace |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
#ifdef Q_OS_WIN |
|
|
|
|
|
|
|
class RandomLayer |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// need to satisfy UniformRandomBitGenerator requirements
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
using result_type = uint32_t; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RandomLayer() |
|
|
|
|
|
|
|
: m_rtlGenRandom {Utils::Misc::loadWinAPI<PRTLGENRANDOM>("Advapi32.dll", "SystemFunction036")} |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (!m_rtlGenRandom) |
|
|
|
|
|
|
|
qFatal("Failed to load RtlGenRandom()"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static constexpr result_type min() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return std::numeric_limits<result_type>::min(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static constexpr result_type max() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return std::numeric_limits<result_type>::max(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result_type operator()() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
result_type buf = 0; |
|
|
|
|
|
|
|
const bool result = m_rtlGenRandom(&buf, sizeof(buf)); |
|
|
|
|
|
|
|
if (!result) |
|
|
|
|
|
|
|
qFatal("RtlGenRandom() failed"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return buf; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
|
|
|
using PRTLGENRANDOM = BOOLEAN (WINAPI *)(PVOID, ULONG); |
|
|
|
|
|
|
|
const PRTLGENRANDOM m_rtlGenRandom; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
#else // Q_OS_WIN
|
|
|
|
|
|
|
|
class RandomLayer |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// need to satisfy UniformRandomBitGenerator requirements
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
using result_type = uint32_t; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RandomLayer() |
|
|
|
|
|
|
|
: m_randDev {fopen("/dev/urandom", "rb")} |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (!m_randDev) |
|
|
|
|
|
|
|
qFatal("Failed to open /dev/urandom"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
~RandomLayer() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
fclose(m_randDev); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static constexpr result_type min() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return std::numeric_limits<result_type>::min(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static constexpr result_type max() |
|
|
|
{ |
|
|
|
{ |
|
|
|
#ifdef Q_OS_MAC // workaround for Apple xcode: https://stackoverflow.com/a/29929949
|
|
|
|
return std::numeric_limits<result_type>::max(); |
|
|
|
static QThreadStorage<std::mt19937> generator; |
|
|
|
} |
|
|
|
if (!generator.hasLocalData()) |
|
|
|
|
|
|
|
generator.localData().seed( |
|
|
|
result_type operator()() const |
|
|
|
hasTrueRandomDevice |
|
|
|
{ |
|
|
|
? std::random_device{}() |
|
|
|
result_type buf = 0; |
|
|
|
: (std::random_device::result_type) std::chrono::system_clock::now().time_since_epoch().count() |
|
|
|
if (fread(&buf, sizeof(buf), 1, m_randDev) != 1) |
|
|
|
); |
|
|
|
qFatal("Read /dev/urandom error"); |
|
|
|
#else |
|
|
|
|
|
|
|
static thread_local std::mt19937 generator{ |
|
|
|
return buf; |
|
|
|
hasTrueRandomDevice |
|
|
|
} |
|
|
|
? std::random_device{}() |
|
|
|
|
|
|
|
: static_cast<std::random_device::result_type>(std::chrono::system_clock::now().time_since_epoch().count()) |
|
|
|
private: |
|
|
|
|
|
|
|
FILE *m_randDev; |
|
|
|
}; |
|
|
|
}; |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// better replacement for `std::rand`, don't use this for real cryptography application
|
|
|
|
uint32_t Utils::Random::rand(const uint32_t min, const uint32_t max) |
|
|
|
// min <= returned_value <= max
|
|
|
|
{ |
|
|
|
assert(min <= max); |
|
|
|
static RandomLayer layer; |
|
|
|
|
|
|
|
|
|
|
|
// new distribution is cheap: http://stackoverflow.com/a/19036349
|
|
|
|
// new distribution is cheap: https://stackoverflow.com/a/19036349
|
|
|
|
std::uniform_int_distribution<uint32_t> uniform(min, max); |
|
|
|
std::uniform_int_distribution<uint32_t> uniform(min, max); |
|
|
|
|
|
|
|
|
|
|
|
#ifdef Q_OS_MAC |
|
|
|
return uniform(layer); |
|
|
|
return uniform(generator.localData()); |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
return uniform(generator); |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|