Skip to content

Commit 82b64a5

Browse files
committed
Merge #15224: Add RNG strengthening (10ms once every minute)
3cb9ce8 Document strenghtening (Pieter Wuille) 1d207bc 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 3cb9ce8 Tree-SHA512: 4fb6f61639b392697beb81c5f0903f79f10dd1087bed7f34de2abb5c22704a671e37b2d828ed141492491863efb1e7d1fa04408a1d32c9de2f2cc8ac406bbe57
2 parents c89a634 + 3cb9ce8 commit 82b64a5

File tree

2 files changed

+56
-4
lines changed

2 files changed

+56
-4
lines changed

src/random.cpp

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,34 @@ static void SeedHardwareSlow(CSHA512& hasher) noexcept {
237237
#endif
238238
}
239239

240+
/** Use repeated SHA512 to strengthen the randomness in seed32, and feed into hasher. */
241+
static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA512& hasher) noexcept
242+
{
243+
CSHA512 inner_hasher;
244+
inner_hasher.Write(seed, sizeof(seed));
245+
246+
// Hash loop
247+
unsigned char buffer[64];
248+
int64_t stop = GetTimeMicros() + microseconds;
249+
do {
250+
for (int i = 0; i < 1000; ++i) {
251+
inner_hasher.Finalize(buffer);
252+
inner_hasher.Reset();
253+
inner_hasher.Write(buffer, sizeof(buffer));
254+
}
255+
// Benchmark operation and feed it into outer hasher.
256+
int64_t perf = GetPerformanceCounter();
257+
hasher.Write((const unsigned char*)&perf, sizeof(perf));
258+
} while (GetTimeMicros() < stop);
259+
260+
// Produce output from inner state and feed it to outer hasher.
261+
inner_hasher.Finalize(buffer);
262+
hasher.Write(buffer, sizeof(buffer));
263+
// Try to clean up.
264+
inner_hasher.Reset();
265+
memory_cleanse(buffer, sizeof(buffer));
266+
}
267+
240268
static void RandAddSeedPerfmon(CSHA512& hasher)
241269
{
242270
#ifdef WIN32
@@ -529,7 +557,23 @@ static void SeedSlow(CSHA512& hasher) noexcept
529557
SeedTimestamp(hasher);
530558
}
531559

532-
static void SeedSleep(CSHA512& hasher)
560+
/** Extract entropy from rng, strengthen it, and feed it into hasher. */
561+
static void SeedStrengthen(CSHA512& hasher, RNGState& rng) noexcept
562+
{
563+
static std::atomic<int64_t> last_strengthen{0};
564+
int64_t last_time = last_strengthen.load();
565+
int64_t current_time = GetTimeMicros();
566+
if (current_time > last_time + 60000000) { // Only run once a minute
567+
// Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
568+
unsigned char strengthen_seed[32];
569+
rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false);
570+
// Strengthen it for 10ms (100ms on first run), and feed it into hasher.
571+
Strengthen(strengthen_seed, last_time == 0 ? 100000 : 10000, hasher);
572+
last_strengthen = current_time;
573+
}
574+
}
575+
576+
static void SeedSleep(CSHA512& hasher, RNGState& rng)
533577
{
534578
// Everything that the 'fast' seeder includes
535579
SeedFast(hasher);
@@ -545,9 +589,12 @@ static void SeedSleep(CSHA512& hasher)
545589

546590
// Windows performance monitor data (once every 10 minutes)
547591
RandAddSeedPerfmon(hasher);
592+
593+
// Strengthen every minute
594+
SeedStrengthen(hasher, rng);
548595
}
549596

550-
static void SeedStartup(CSHA512& hasher) noexcept
597+
static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
551598
{
552599
#ifdef WIN32
553600
RAND_screen();
@@ -561,6 +608,9 @@ static void SeedStartup(CSHA512& hasher) noexcept
561608

562609
// Windows performance monitor data.
563610
RandAddSeedPerfmon(hasher);
611+
612+
// Strengthen
613+
SeedStrengthen(hasher, rng);
564614
}
565615

566616
enum class RNGLevel {
@@ -585,15 +635,15 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
585635
SeedSlow(hasher);
586636
break;
587637
case RNGLevel::SLEEP:
588-
SeedSleep(hasher);
638+
SeedSleep(hasher, rng);
589639
break;
590640
}
591641

592642
// Combine with and update state
593643
if (!rng.MixExtract(out, num, std::move(hasher), false)) {
594644
// On the first invocation, also seed with SeedStartup().
595645
CSHA512 startup_hasher;
596-
SeedStartup(startup_hasher);
646+
SeedStartup(startup_hasher, rng);
597647
rng.MixExtract(out, num, std::move(startup_hasher), true);
598648
}
599649

src/random.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
* - RandAddSeedSleep() seeds everything that fast seeding includes, but additionally:
4444
* - A high-precision timestamp before and after sleeping 1ms.
4545
* - (On Windows) Once every 10 minutes, performance monitoring data from the OS.
46+
- - Once every minute, strengthen the entropy for 10 ms using repeated SHA512.
4647
* These just exploit the fact the system is idle to improve the quality of the RNG
4748
* slightly.
4849
*
@@ -51,6 +52,7 @@
5152
* - 256 bits from the hardware RNG (rdseed or rdrand) when available.
5253
* - (On Windows) Performance monitoring data from the OS.
5354
* - (On Windows) Through OpenSSL, the screen contents.
55+
* - Strengthen the entropy for 100 ms using repeated SHA512.
5456
*
5557
* When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and
5658
* (up to) the first 32 bytes of H are produced as output, while the last 32 bytes

0 commit comments

Comments
 (0)