Skip to content

Commit

Permalink
Add RNG strengthening (10ms once every minute)
Browse files Browse the repository at this point in the history
Summary:
```
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.
```

Backport of core [[bitcoin/bitcoin#15224 | PR15224]].

Test Plan:
  ninja all check-all

Reviewers: #bitcoin_abc, deadalnix

Reviewed By: #bitcoin_abc, deadalnix

Subscribers: deadalnix

Differential Revision: https://reviews.bitcoinabc.org/D6139
  • Loading branch information
sipa authored and Fabcien committed May 19, 2020
1 parent 4de8d18 commit fd5dffc
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 6 deletions.
63 changes: 59 additions & 4 deletions src/random.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,37 @@ static void SeedHardwareSlow(CSHA512 &hasher) noexcept {
#endif
}

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

// Hash loop
uint8_t 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 uint8_t *)&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
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
Expand Down Expand Up @@ -580,7 +611,25 @@ 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();
// Only run once a minute
if (current_time > last_time + 60000000) {
// Generate 32 bytes of entropy from the RNG, and a copy of the entropy
// already in hasher.
uint8_t 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 @@ -596,9 +645,12 @@ static void SeedSleep(CSHA512 &hasher) {

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

// Strengthen
SeedStrengthen(hasher, rng);
}

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

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

// Strengthen
SeedStrengthen(hasher, rng);
}

enum class RNGLevel {
Expand All @@ -635,15 +690,15 @@ static void ProcRand(uint8_t *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
8 changes: 6 additions & 2 deletions src/random.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,18 @@
* additionally:
* - A high-precision timestamp before and after sleeping 1ms.
* - (On Windows) Once every 10 minutes, performance monitoring data from the
* OS. These just exploit the fact the system is idle to improve the quality of
* the RNG slightly.
* 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 slightly.
*
* On first use of the RNG (regardless of what function is called first), all
* entropy sources used in the 'slow' seeder are included, but also:
* - 256 bits from the hardware RNG (rdseed or rdrand) when available.
* - (On Windows) Performance monitoring data from the OS.
* - (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 (up to) the first 32 bytes of H are produced as output, while the last 32
Expand Down

0 comments on commit fd5dffc

Please sign in to comment.