Skip to content

Commit

Permalink
Merge pull request #6565 from WinterSolstice8/rng_updates
Browse files Browse the repository at this point in the history
RNG seeding updates, update default RNG to MT64
  • Loading branch information
zach2good authored Dec 21, 2024
2 parents de2f341 + e54188e commit e41fa00
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 72 deletions.
1 change: 1 addition & 0 deletions src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 7 additions & 21 deletions src/common/rng/mersennetwister.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
#include <array>
#include <random>

// 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:
Expand All @@ -38,27 +41,10 @@ class xirand
{
ShowInfo("Seeding Mersenne Twister 32 bit RNG");

std::array<uint32_t, std::mt19937::state_size> 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::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();

// mix with a high precision time in microseconds
*it ^= std::chrono::duration_cast<std::chrono::microseconds>(
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);
}

/*
Expand Down
28 changes: 7 additions & 21 deletions src/common/rng/mersennetwister64.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
#include <array>
#include <random>

// 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:
Expand All @@ -38,27 +41,10 @@ class xirand
{
ShowInfo("Seeding Mersenne Twister 64 bit RNG");

std::array<uint64_t, std::mt19937_64::state_size> 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::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();

// mix with a high precision time in microseconds
*it ^= std::chrono::duration_cast<std::chrono::microseconds>(
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);
}

/*
Expand Down
23 changes: 9 additions & 14 deletions src/common/rng/pcg.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
#include <array>
#include <random>

// 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:
Expand All @@ -41,25 +44,17 @@ class xirand
{
ShowInfo("Seeding PCG32 RNG");

std::array<uint32_t, sizeof(pcg32::state_type) / 4> seed_data; // get enough data to seed an N byte state with sets of 32 bit ints
std::array<pcg32::state_type, 2> 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::seconds>(
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::microseconds>(
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);
}
Expand Down
23 changes: 9 additions & 14 deletions src/common/rng/pcg64.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
#include <array>
#include <random>

// 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:
Expand All @@ -41,25 +44,17 @@ class xirand
{
ShowInfo("Seeding PCG64 RNG");

std::array<uint32_t, sizeof(pcg64::state_type) / 4> seed_data; // get enough data to seed an N byte state with sets of 32 bit ints
std::array<pcg64::state_type, 2> 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::seconds>(
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::microseconds>(
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);
}
Expand Down
67 changes: 67 additions & 0 deletions src/common/xirand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include <fstream>
#include <stddef.h>

// https://stackoverflow.com/a/45069417
#ifdef _WIN32

#include <windows.h>

#include <wincrypt.h>

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<BYTE*>(dst);
if (!CryptGenRandom(ctx, static_cast<DWORD>(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 <linux/random.h>
#include <sys/syscall.h>
#include <unistd.h>

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<char*>(dst);
std::ifstream stream("/dev/urandom", std::ios_base::binary | std::ios_base::in);
stream.read(buffer, dstlen);

return dstlen;
}
#endif
7 changes: 5 additions & 2 deletions src/common/xirand.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -138,4 +138,7 @@ inline T xirand::GetRandomElement(std::initializer_list<T> list)
return GetRandomElement(container);
}

// Get secure random numbers
size_t sysrandom(void* dst, size_t dstlen);

#endif // _XIRAND_H_

0 comments on commit e41fa00

Please sign in to comment.