Merge #15224: Add RNG strengthening (10ms once every minute)

3cb9ce85d0c6d01217babf0df7efc2eabde1b12f Document strenghtening (Pieter Wuille)
1d207bc46f995ad3b5ae89bb504affaca09d10b1 Add hash strengthening to the RNG (Pieter Wuille)

Pull request description:

  This patch improves the built-in RNG using hash strengthening.

  At startup, and once every minute, 32 bytes of entropy are produced from the RNG, repeatedly hashed using SHA512 for 10ms, and then fed back into the RNG, together with high-precision timestamps obtained every 1000 iterations.

ACKs for commit 3cb9ce:
  pstratem:
    utACK 3cb9ce85d0c6d01217babf0df7efc2eabde1b12f

Tree-SHA512: 4fb6f61639b392697beb81c5f0903f79f10dd1087bed7f34de2abb5c22704a671e37b2d828ed141492491863efb1e7d1fa04408a1d32c9de2f2cc8ac406bbe57
This commit is contained in:
Wladimir J. van der Laan 2019-05-18 10:01:21 +02:00 committed by Pasta
parent 13a57764e2
commit f1c784ffc3
No known key found for this signature in database
GPG Key ID: 52527BEDABE87984
2 changed files with 56 additions and 4 deletions

View File

@ -146,6 +146,34 @@ static bool GetHardwareRand(unsigned char* ent32) noexcept {
return false; 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) static void RandAddSeedPerfmon(CSHA512& hasher)
{ {
#ifdef WIN32 #ifdef WIN32
@ -432,7 +460,23 @@ static void SeedSlow(CSHA512& hasher) noexcept
SeedTimestamp(hasher); 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 // Everything that the 'fast' seeder includes
SeedFast(hasher); SeedFast(hasher);
@ -448,9 +492,12 @@ static void SeedSleep(CSHA512& hasher)
// Windows performance monitor data (once every 10 minutes) // Windows performance monitor data (once every 10 minutes)
RandAddSeedPerfmon(hasher); RandAddSeedPerfmon(hasher);
// Strengthen every minute
SeedStrengthen(hasher, rng);
} }
static void SeedStartup(CSHA512& hasher) noexcept static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
{ {
#ifdef WIN32 #ifdef WIN32
RAND_screen(); RAND_screen();
@ -461,6 +508,9 @@ static void SeedStartup(CSHA512& hasher) noexcept
// Windows performance monitor data. // Windows performance monitor data.
RandAddSeedPerfmon(hasher); RandAddSeedPerfmon(hasher);
// Strengthen
SeedStrengthen(hasher, rng);
} }
enum class RNGLevel { enum class RNGLevel {
@ -485,7 +535,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
SeedSlow(hasher); SeedSlow(hasher);
break; break;
case RNGLevel::SLEEP: case RNGLevel::SLEEP:
SeedSleep(hasher); SeedSleep(hasher, rng);
break; break;
} }
@ -493,7 +543,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
if (!rng.MixExtract(out, num, std::move(hasher), false)) { if (!rng.MixExtract(out, num, std::move(hasher), false)) {
// On the first invocation, also seed with SeedStartup(). // On the first invocation, also seed with SeedStartup().
CSHA512 startup_hasher; CSHA512 startup_hasher;
SeedStartup(startup_hasher); SeedStartup(startup_hasher, rng);
rng.MixExtract(out, num, std::move(startup_hasher), true); rng.MixExtract(out, num, std::move(startup_hasher), true);
} }

View File

@ -44,6 +44,7 @@
* - RandAddSeedSleep() seeds everything that fast seeding includes, but additionally: * - RandAddSeedSleep() seeds everything that fast seeding includes, but additionally:
* - A high-precision timestamp before and after sleeping 1ms. * - A high-precision timestamp before and after sleeping 1ms.
* - (On Windows) Once every 10 minutes, performance monitoring data from the OS. * - (On Windows) Once every 10 minutes, performance monitoring data from the OS.
- - Once every minute, strengthen the entropy for 10 ms using repeated SHA512.
* These just exploit the fact the system is idle to improve the quality of the RNG * These just exploit the fact the system is idle to improve the quality of the RNG
* slightly. * slightly.
* *
@ -51,6 +52,7 @@
* sources used in the 'slow' seeder are included, but also: * sources used in the 'slow' seeder are included, but also:
* - (On Windows) Performance monitoring data from the OS. * - (On Windows) Performance monitoring data from the OS.
* - (On Windows) Through OpenSSL, the screen contents. * - (On Windows) Through OpenSSL, the screen contents.
* - Strengthen the entropy for 100 ms using repeated SHA512.
* *
* When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and * When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and
* (up to) the first 32 bytes of H are produced as output, while the last 32 bytes * (up to) the first 32 bytes of H are produced as output, while the last 32 bytes