From 1d207bc46f995ad3b5ae89bb504affaca09d10b1 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 17 Dec 2018 15:50:31 -0800 Subject: [PATCH] Add hash strengthening to the RNG 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. --- src/random.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 3b7f7910b..20bab363e 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -143,6 +143,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 @@ -436,7 +464,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 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); @@ -452,9 +496,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(); @@ -465,6 +512,9 @@ static void SeedStartup(CSHA512& hasher) noexcept // Windows performance monitor data. RandAddSeedPerfmon(hasher); + + // Strengthen + SeedStrengthen(hasher, rng); } enum class RNGLevel { @@ -489,7 +539,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) SeedSlow(hasher); break; case RNGLevel::SLEEP: - SeedSleep(hasher); + SeedSleep(hasher, rng); break; } @@ -497,7 +547,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) 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); }