Changeset View
Changeset View
Standalone View
Standalone View
src/random.cpp
Show First 20 Lines • Show All 269 Lines • ▼ Show 20 Lines | if (g_rdrand_supported) { | ||||
} | } | ||||
hasher.Write((const uint8_t *)&out, sizeof(out)); | hasher.Write((const uint8_t *)&out, sizeof(out)); | ||||
} | } | ||||
return; | return; | ||||
} | } | ||||
#endif | #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) { | static void RandAddSeedPerfmon(CSHA512 &hasher) { | ||||
#ifdef WIN32 | #ifdef WIN32 | ||||
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom | // Don't need this on Linux, OpenSSL automatically uses /dev/urandom | ||||
// Seed with the entire set of perfmon data | // Seed with the entire set of perfmon data | ||||
// This can take up to 2 seconds, so only do it every 10 minutes | // This can take up to 2 seconds, so only do it every 10 minutes | ||||
static int64_t nLastPerfmon; | static int64_t nLastPerfmon; | ||||
if (GetTime() < nLastPerfmon + 10 * 60) { | if (GetTime() < nLastPerfmon + 10 * 60) { | ||||
▲ Show 20 Lines • Show All 289 Lines • ▼ Show 20 Lines | static void SeedSlow(CSHA512 &hasher) noexcept { | ||||
// High-precision timestamp. | // High-precision timestamp. | ||||
// | // | ||||
// Note that we also commit to a timestamp in the Fast seeder, so we | // Note that we also commit to a timestamp in the Fast seeder, so we | ||||
// indirectly commit to a benchmark of all the entropy gathering sources in | // indirectly commit to a benchmark of all the entropy gathering sources in | ||||
// this function). | // this function). | ||||
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(); | |||||
// 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 | // Everything that the 'fast' seeder includes | ||||
SeedFast(hasher); | SeedFast(hasher); | ||||
// High-precision timestamp | // High-precision timestamp | ||||
SeedTimestamp(hasher); | SeedTimestamp(hasher); | ||||
// Sleep for 1ms | // Sleep for 1ms | ||||
MilliSleep(1); | MilliSleep(1); | ||||
// High-precision timestamp after sleeping (as we commit to both the time | // High-precision timestamp after sleeping (as we commit to both the time | ||||
// before and after, this measures the delay) | // before and after, this measures the delay) | ||||
SeedTimestamp(hasher); | SeedTimestamp(hasher); | ||||
// Windows performance monitor data (once every 10 minutes) | // Windows performance monitor data (once every 10 minutes) | ||||
RandAddSeedPerfmon(hasher); | RandAddSeedPerfmon(hasher); | ||||
// Strengthen | |||||
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(); | ||||
#endif | #endif | ||||
// Gather 256 bits of hardware randomness, if available | // Gather 256 bits of hardware randomness, if available | ||||
SeedHardwareSlow(hasher); | SeedHardwareSlow(hasher); | ||||
// Everything that the 'slow' seeder includes. | // Everything that the 'slow' seeder includes. | ||||
SeedSlow(hasher); | SeedSlow(hasher); | ||||
// Windows performance monitor data. | // Windows performance monitor data. | ||||
RandAddSeedPerfmon(hasher); | RandAddSeedPerfmon(hasher); | ||||
// Strengthen | |||||
SeedStrengthen(hasher, rng); | |||||
} | } | ||||
enum class RNGLevel { | enum class RNGLevel { | ||||
FAST, //!< Automatically called by GetRandBytes | FAST, //!< Automatically called by GetRandBytes | ||||
SLOW, //!< Automatically called by GetStrongRandBytes | SLOW, //!< Automatically called by GetStrongRandBytes | ||||
SLEEP, //!< Called by RandAddSeedSleep() | SLEEP, //!< Called by RandAddSeedSleep() | ||||
}; | }; | ||||
static void ProcRand(uint8_t *out, int num, RNGLevel level) { | static void ProcRand(uint8_t *out, int num, RNGLevel level) { | ||||
// Make sure the RNG is initialized first (as all Seed* function possibly | // Make sure the RNG is initialized first (as all Seed* function possibly | ||||
// need hwrand to be available). | // need hwrand to be available). | ||||
RNGState &rng = GetRNGState(); | RNGState &rng = GetRNGState(); | ||||
assert(num <= 32); | assert(num <= 32); | ||||
CSHA512 hasher; | CSHA512 hasher; | ||||
switch (level) { | switch (level) { | ||||
case RNGLevel::FAST: | case RNGLevel::FAST: | ||||
SeedFast(hasher); | SeedFast(hasher); | ||||
break; | break; | ||||
case RNGLevel::SLOW: | case RNGLevel::SLOW: | ||||
SeedSlow(hasher); | SeedSlow(hasher); | ||||
break; | break; | ||||
case RNGLevel::SLEEP: | case RNGLevel::SLEEP: | ||||
SeedSleep(hasher); | SeedSleep(hasher, rng); | ||||
break; | break; | ||||
} | } | ||||
// Combine with and update state | // Combine with and update state | ||||
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); | ||||
} | } | ||||
// For anything but the 'fast' level, feed the resulting RNG output (after | // For anything but the 'fast' level, feed the resulting RNG output (after | ||||
// an additional hashing step) back into OpenSSL. | // an additional hashing step) back into OpenSSL. | ||||
if (level != RNGLevel::FAST) { | if (level != RNGLevel::FAST) { | ||||
uint8_t buf[64]; | uint8_t buf[64]; | ||||
CSHA512().Write(out, num).Finalize(buf); | CSHA512().Write(out, num).Finalize(buf); | ||||
▲ Show 20 Lines • Show All 149 Lines • Show Last 20 Lines |