diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 61d63cef014..d7ffbeb3a89 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -37,6 +37,7 @@ set(COMMON_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/timer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/timer.h ${CMAKE_CURRENT_SOURCE_DIR}/xirand.h + ${CMAKE_CURRENT_SOURCE_DIR}/xirand.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utils.h ${CMAKE_CURRENT_SOURCE_DIR}/uuid.h diff --git a/src/common/rng/mersennetwister.h b/src/common/rng/mersennetwister.h index 339ab6a3ee6..727567386be 100644 --- a/src/common/rng/mersennetwister.h +++ b/src/common/rng/mersennetwister.h @@ -25,6 +25,9 @@ #include #include +// Forward declare sysrandom which is built in the xirand.h/cpp compilation unit +extern size_t sysrandom(void* dst, size_t dstlen); + class xirand { public: @@ -38,27 +41,10 @@ class xirand { ShowInfo("Seeding Mersenne Twister 32 bit RNG"); - std::array seed_data; - - // Certain systems were noted to have bad seeding via only std::random_device, - // the following indicated how we could mix in std::random_device with other seed sources - // https://stackoverflow.com/a/68382489 - for (auto it = seed_data.begin(); it != seed_data.end(); ++it) - { - // start with a C++ equivalent of time(nullptr) - UNIX time in seconds - *it = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - - // mix with a high precision time in microseconds - *it ^= std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()) - .count(); - - // *it ^= more_external_random_stuff; - } - std::seed_seq seq(seed_data.cbegin(), seed_data.cend()); - rng().seed(seq); + uint32_t seed; + sysrandom(&seed, sizeof(seed)); + + rng().seed(seed); } /* diff --git a/src/common/rng/mersennetwister64.h b/src/common/rng/mersennetwister64.h index 4eaf42ae888..bb8765b55eb 100644 --- a/src/common/rng/mersennetwister64.h +++ b/src/common/rng/mersennetwister64.h @@ -25,6 +25,9 @@ #include #include +// Forward declare sysrandom which is built in the xirand.h/cpp compilation unit +extern size_t sysrandom(void* dst, size_t dstlen); + class xirand { public: @@ -38,27 +41,10 @@ class xirand { ShowInfo("Seeding Mersenne Twister 64 bit RNG"); - std::array seed_data; - - // Certain systems were noted to have bad seeding via only std::random_device, - // the following indicated how we could mix in std::random_device with other seed sources - // https://stackoverflow.com/a/68382489 - for (auto it = seed_data.begin(); it != seed_data.end(); ++it) - { - // start with a C++ equivalent of time(nullptr) - UNIX time in seconds - *it = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - - // mix with a high precision time in microseconds - *it ^= std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()) - .count(); - - // *it ^= more_external_random_stuff; - } - std::seed_seq seq(seed_data.cbegin(), seed_data.cend()); - rng().seed(seq); + uint64_t seed; + sysrandom(&seed, sizeof(seed)); + + rng().seed(seed); } /* diff --git a/src/common/rng/pcg.h b/src/common/rng/pcg.h index cbe3060ea66..3c74b6d7517 100644 --- a/src/common/rng/pcg.h +++ b/src/common/rng/pcg.h @@ -28,6 +28,9 @@ #include #include +// Forward declare sysrandom which is built in the xirand.h/cpp compilation unit +extern size_t sysrandom(void* dst, size_t dstlen); + class xirand { public: @@ -41,25 +44,17 @@ class xirand { ShowInfo("Seeding PCG32 RNG"); - std::array seed_data; // get enough data to seed an N byte state with sets of 32 bit ints + std::array seed_data; // PCG states seem to be bit size * 2 * 2, here 64 bit numbers * 2 + + pcg32::state_type seed; - // Certain systems were noted to have bad seeding via only std::random_device, - // the following indicated how we could mix in std::random_device with other seed sources - // https://stackoverflow.com/a/68382489 for (auto it = seed_data.begin(); it != seed_data.end(); ++it) { - // start with a C++ equivalent of time(nullptr) - UNIX time in seconds - *it = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); + sysrandom(&seed, sizeof(seed)); - // mix with a high precision time in microseconds - *it ^= std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()) - .count(); - - // *it ^= more_external_random_stuff; + *it = seed; } + std::seed_seq seq(seed_data.cbegin(), seed_data.cend()); rng().seed(seq); } diff --git a/src/common/rng/pcg64.h b/src/common/rng/pcg64.h index b2e15e2419b..0e86906c1c1 100644 --- a/src/common/rng/pcg64.h +++ b/src/common/rng/pcg64.h @@ -28,6 +28,9 @@ #include #include +// Forward declare sysrandom which is built in the xirand.h/cpp compilation unit +extern size_t sysrandom(void* dst, size_t dstlen); + class xirand { public: @@ -41,25 +44,17 @@ class xirand { ShowInfo("Seeding PCG64 RNG"); - std::array seed_data; // get enough data to seed an N byte state with sets of 32 bit ints + std::array seed_data; + + pcg64::state_type seed; - // Certain systems were noted to have bad seeding via only std::random_device, - // the following indicated how we could mix in std::random_device with other seed sources - // https://stackoverflow.com/a/68382489 for (auto it = seed_data.begin(); it != seed_data.end(); ++it) { - // start with a C++ equivalent of time(nullptr) - UNIX time in seconds - *it = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); + sysrandom(&seed, sizeof(seed)); - // mix with a high precision time in microseconds - *it ^= std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()) - .count(); - - // *it ^= more_external_random_stuff; + *it = seed; } + std::seed_seq seq(seed_data.cbegin(), seed_data.cend()); rng().seed(seq); } diff --git a/src/common/xirand.cpp b/src/common/xirand.cpp new file mode 100644 index 00000000000..a0043e72f9e --- /dev/null +++ b/src/common/xirand.cpp @@ -0,0 +1,67 @@ +#include +#include + +// https://stackoverflow.com/a/45069417 +#ifdef _WIN32 + +#include + +#include + +bool acquire_context(HCRYPTPROV* ctx) +{ + if (!CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, 0)) + { + return CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET); + } + + return true; +} + +size_t sysrandom(void* dst, size_t dstlen) +{ + HCRYPTPROV ctx; + if (!acquire_context(&ctx)) + { + throw std::runtime_error("Unable to initialize Win32 crypt library."); + } + + BYTE* buffer = reinterpret_cast(dst); + if (!CryptGenRandom(ctx, static_cast(dstlen), buffer)) + { + throw std::runtime_error("Unable to generate random bytes."); + } + + if (!CryptReleaseContext(ctx, 0)) + { + throw std::runtime_error("Unable to release Win32 crypt library."); + } + + return dstlen; +} +#elif defined(__linux__) || defined(linux) || defined(__linux) + +#include +#include +#include + +size_t sysrandom(void* dst, size_t dstlen) +{ + int bytes = syscall(SYS_getrandom, dst, dstlen, 0); + if (bytes != dstlen) + { + throw std::runtime_error("Unable to read N bytes from CSPRNG."); + } + + return dstlen; +} +#else // OSX +size_t sysrandom(void* dst, size_t dstlen) +{ + char* buffer = reinterpret_cast(dst); + std::ifstream stream("/dev/urandom", std::ios_base::binary | std::ios_base::in); + stream.read(buffer, dstlen); + + return dstlen; +} +#endif diff --git a/src/common/xirand.h b/src/common/xirand.h index bd8f2f2e2fd..3c55ff31f5e 100644 --- a/src/common/xirand.h +++ b/src/common/xirand.h @@ -24,8 +24,8 @@ #define _XIRAND_H_ // You can choose an RNG by commenting/uncommenting this line. The default is Mersenne Twister in 32 bit. -#include "rng/mersennetwister.h" -// #include "rng/mersennetwister64.h" +// #include "rng/mersennetwister.h" +#include "rng/mersennetwister64.h" // #include "rng/pcg.h" // #include "rng/pcg64.h" @@ -138,4 +138,7 @@ inline T xirand::GetRandomElement(std::initializer_list list) return GetRandomElement(container); } +// Get secure random numbers +size_t sysrandom(void* dst, size_t dstlen); + #endif // _XIRAND_H_