Skip to content

Commit

Permalink
Add hash strengthening to the RNG
Browse files Browse the repository at this point in the history
Once every minute, this will feed the RNG state through repeated SHA512
for 10ms. The timings of that operation are used as entropy source as
well.

Cherry-picked from: 1d207bc
  • Loading branch information
sipa authored and xanimo committed Apr 3, 2024
1 parent c4c8819 commit d171765
Showing 1 changed file with 54 additions and 4 deletions.
58 changes: 54 additions & 4 deletions src/random.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,34 @@ static bool GetHardwareRand(unsigned char* ent32) noexcept {
return false;
}

/** Use repeated SHA512 to strengthen the randomness in seed32, and feed into hasher. */
static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA512& hasher) noexcept
{
CSHA512 inner_hasher;
inner_hasher.Write(seed, sizeof(seed));

// Hash loop
unsigned char buffer[64];
int64_t stop = GetTimeMicros() + microseconds;
do {
for (int i = 0; i < 1000; ++i) {
inner_hasher.Finalize(buffer);
inner_hasher.Reset();
inner_hasher.Write(buffer, sizeof(buffer));
}
// Benchmark operation and feed it into outer hasher.
int64_t perf = GetPerformanceCounter();
hasher.Write((const unsigned char*)&perf, sizeof(perf));
} while (GetTimeMicros() < stop);

// Produce output from inner state and feed it to outer hasher.
inner_hasher.Finalize(buffer);
hasher.Write(buffer, sizeof(buffer));
// Try to clean up.
inner_hasher.Reset();
memory_cleanse(buffer, sizeof(buffer));
}

static void RandAddSeedPerfmon(CSHA512& hasher)
{
#ifdef WIN32
Expand Down Expand Up @@ -437,7 +465,23 @@ static void SeedSlow(CSHA512& hasher) noexcept
SeedTimestamp(hasher);
}

static void SeedSleep(CSHA512& hasher)
/** Extract entropy from rng, strengthen it, and feed it into hasher. */
static void SeedStrengthen(CSHA512& hasher, RNGState& rng) noexcept
{
static std::atomic<int64_t> last_strengthen{0};
int64_t last_time = last_strengthen.load();
int64_t current_time = GetTimeMicros();
if (current_time > last_time + 60000000) { // Only run once a minute
// Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
unsigned char strengthen_seed[32];
rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false);
// Strengthen it for 10ms (100ms on first run), and feed it into hasher.
Strengthen(strengthen_seed, last_time == 0 ? 100000 : 10000, hasher);
last_strengthen = current_time;
}
}

static void SeedSleep(CSHA512& hasher, RNGState& rng)
{
// Everything that the 'fast' seeder includes
SeedFast(hasher);
Expand All @@ -453,9 +497,12 @@ static void SeedSleep(CSHA512& hasher)

// Windows performance monitor data (once every 10 minutes)
RandAddSeedPerfmon(hasher);

// Strengthen every minute
SeedStrengthen(hasher, rng);
}

static void SeedStartup(CSHA512& hasher) noexcept
static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
{
#ifdef WIN32
RAND_screen();
Expand All @@ -466,6 +513,9 @@ static void SeedStartup(CSHA512& hasher) noexcept

// Windows performance monitor data.
RandAddSeedPerfmon(hasher);

// Strengthen
SeedStrengthen(hasher, rng);
}

enum class RNGLevel {
Expand All @@ -490,15 +540,15 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
SeedSlow(hasher);
break;
case RNGLevel::SLEEP:
SeedSleep(hasher);
SeedSleep(hasher, rng);
break;
}

// Combine with and update state
if (!rng.MixExtract(out, num, std::move(hasher), false)) {
// On the first invocation, also seed with SeedStartup().
CSHA512 startup_hasher;
SeedStartup(startup_hasher);
SeedStartup(startup_hasher, rng);
rng.MixExtract(out, num, std::move(startup_hasher), true);
}

Expand Down

0 comments on commit d171765

Please sign in to comment.