From 81a4f3ced09eb0754101eac492173119a7869598 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Fri, 6 Apr 2018 17:43:42 +0800 Subject: [PATCH] Use RNG provided by OS The general consensus is to use random facility provided by OS instead of using other software random generators. --- src/base/utils/random.cpp | 124 +++++++++++++++++++++++++++++--------- src/base/utils/random.h | 3 +- 2 files changed, 96 insertions(+), 31 deletions(-) diff --git a/src/base/utils/random.cpp b/src/base/utils/random.cpp index add0b4e16..fac913227 100644 --- a/src/base/utils/random.cpp +++ b/src/base/utils/random.cpp @@ -29,47 +29,113 @@ #include "random.h" -#include -#include +#include #include #include -#ifdef Q_OS_MAC -#include +#ifdef Q_OS_WIN +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#else // Q_OS_WIN +#include #endif -// on some platform `std::random_device` may generate the same number sequence -static bool hasTrueRandomDevice{ std::random_device{}() != std::random_device{}() }; +#include "misc.h" -uint32_t Utils::Random::rand(const uint32_t min, const uint32_t max) +namespace { -#ifdef Q_OS_MAC // workaround for Apple xcode: https://stackoverflow.com/a/29929949 - static QThreadStorage generator; - if (!generator.hasLocalData()) - generator.localData().seed( - hasTrueRandomDevice - ? std::random_device{}() - : (std::random_device::result_type) std::chrono::system_clock::now().time_since_epoch().count() - ); -#else - static thread_local std::mt19937 generator{ - hasTrueRandomDevice - ? std::random_device{}() - : static_cast(std::chrono::system_clock::now().time_since_epoch().count()) +#ifdef Q_OS_WIN + class RandomLayer + { + // need to satisfy UniformRandomBitGenerator requirements + public: + using result_type = uint32_t; + + RandomLayer() + : m_rtlGenRandom {Utils::Misc::loadWinAPI("Advapi32.dll", "SystemFunction036")} + { + if (!m_rtlGenRandom) + qFatal("Failed to load RtlGenRandom()"); + } + + static constexpr result_type min() + { + return std::numeric_limits::min(); + } + + static constexpr result_type max() + { + return std::numeric_limits::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::min(); + } + + static constexpr result_type max() + { + return std::numeric_limits::max(); + } + + result_type operator()() const + { + result_type buf = 0; + if (fread(&buf, sizeof(buf), 1, m_randDev) != 1) + qFatal("Read /dev/urandom error"); + + return buf; + } + + private: + FILE *m_randDev; }; #endif +} - // better replacement for `std::rand`, don't use this for real cryptography application - // min <= returned_value <= max - assert(min <= max); +uint32_t Utils::Random::rand(const uint32_t min, const uint32_t 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 uniform(min, max); -#ifdef Q_OS_MAC - return uniform(generator.localData()); -#else - return uniform(generator); -#endif + return uniform(layer); } diff --git a/src/base/utils/random.h b/src/base/utils/random.h index d4198deca..106d0b90f 100644 --- a/src/base/utils/random.h +++ b/src/base/utils/random.h @@ -31,13 +31,12 @@ #define UTILS_RANDOM_H #include -#include namespace Utils { namespace Random { - uint32_t rand(uint32_t min = 0, uint32_t max = UINT32_MAX); + uint32_t rand(const uint32_t min = 0, const uint32_t max = UINT32_MAX); } }