Changeset View
Changeset View
Standalone View
Standalone View
src/random.cpp
Show First 20 Lines • Show All 307 Lines • ▼ Show 20 Lines | |||||
namespace { | namespace { | ||||
struct RNGState { | struct RNGState { | ||||
Mutex m_mutex; | Mutex m_mutex; | ||||
uint8_t m_state[32] GUARDED_BY(m_mutex) = {0}; | uint8_t m_state[32] GUARDED_BY(m_mutex) = {0}; | ||||
uint64_t m_counter GUARDED_BY(m_mutex) = 0; | uint64_t m_counter GUARDED_BY(m_mutex) = 0; | ||||
RNGState() { InitHardwareRand(); } | RNGState() { InitHardwareRand(); } | ||||
/** | |||||
* Extract up to 32 bytes of entropy from the RNG state, mixing in new | |||||
* entropy from hasher. | |||||
*/ | |||||
void MixExtract(uint8_t *out, size_t num, CSHA512 &&hasher) { | |||||
assert(num <= 32); | |||||
uint8_t buf[64]; | |||||
static_assert(sizeof(buf) == CSHA512::OUTPUT_SIZE, | |||||
"Buffer needs to have hasher's output size"); | |||||
{ | |||||
LOCK(m_mutex); | |||||
// Write the current state of the RNG into the hasher | |||||
hasher.Write(m_state, 32); | |||||
// Write a new counter number into the state | |||||
hasher.Write((const uint8_t *)&m_counter, sizeof(m_counter)); | |||||
++m_counter; | |||||
// Finalize the hasher | |||||
hasher.Finalize(buf); | |||||
// Store the last 32 bytes of the hash output as new RNG state. | |||||
memcpy(m_state, buf + 32, 32); | |||||
} | |||||
// If desired, copy (up to) the first 32 bytes of the hash output as | |||||
// output. | |||||
if (num) { | |||||
assert(out != nullptr); | |||||
memcpy(out, buf, num); | |||||
} | |||||
// Best effort cleanup of internal state | |||||
hasher.Reset(); | |||||
memory_cleanse(buf, 64); | |||||
} | |||||
}; | }; | ||||
RNGState &GetRNGState() { | RNGState &GetRNGState() { | ||||
// This C++11 idiom relies on the guarantee that static variable are | // This C++11 idiom relies on the guarantee that static variable are | ||||
// initialized on first call, even when multiple parallel calls are | // initialized on first call, even when multiple parallel calls are | ||||
// permitted. | // permitted. | ||||
static std::unique_ptr<RNGState> g_rng{new RNGState()}; | static std::unique_ptr<RNGState> g_rng{new RNGState()}; | ||||
return *g_rng; | return *g_rng; | ||||
} | } | ||||
} // namespace | } // namespace | ||||
static void AddDataToRng(void *data, size_t len); | static void AddDataToRng(void *data, size_t len, RNGState &rng); | ||||
void RandAddSeedSleep() { | void RandAddSeedSleep() { | ||||
RNGState &rng = GetRNGState(); | |||||
int64_t nPerfCounter1 = GetPerformanceCounter(); | int64_t nPerfCounter1 = GetPerformanceCounter(); | ||||
std::this_thread::sleep_for(std::chrono::milliseconds(1)); | std::this_thread::sleep_for(std::chrono::milliseconds(1)); | ||||
int64_t nPerfCounter2 = GetPerformanceCounter(); | int64_t nPerfCounter2 = GetPerformanceCounter(); | ||||
// Combine with and update state | // Combine with and update state | ||||
AddDataToRng(&nPerfCounter1, sizeof(nPerfCounter1)); | AddDataToRng(&nPerfCounter1, sizeof(nPerfCounter1), rng); | ||||
AddDataToRng(&nPerfCounter2, sizeof(nPerfCounter2)); | AddDataToRng(&nPerfCounter2, sizeof(nPerfCounter2), rng); | ||||
memory_cleanse(&nPerfCounter1, sizeof(nPerfCounter1)); | memory_cleanse(&nPerfCounter1, sizeof(nPerfCounter1)); | ||||
memory_cleanse(&nPerfCounter2, sizeof(nPerfCounter2)); | memory_cleanse(&nPerfCounter2, sizeof(nPerfCounter2)); | ||||
} | } | ||||
static void AddDataToRng(void *data, size_t len) { | static void AddDataToRng(void *data, size_t len, RNGState &rng) { | ||||
RNGState &rng = GetRNGState(); | |||||
CSHA512 hasher; | CSHA512 hasher; | ||||
hasher.Write((const uint8_t *)&len, sizeof(len)); | hasher.Write((const uint8_t *)&len, sizeof(len)); | ||||
hasher.Write((const uint8_t *)data, len); | hasher.Write((const uint8_t *)data, len); | ||||
uint8_t buf[64]; | rng.MixExtract(nullptr, 0, std::move(hasher)); | ||||
{ | |||||
WAIT_LOCK(rng.m_mutex, lock); | |||||
hasher.Write(rng.m_state, sizeof(rng.m_state)); | |||||
hasher.Write((const uint8_t *)&rng.m_counter, sizeof(rng.m_counter)); | |||||
++rng.m_counter; | |||||
hasher.Finalize(buf); | |||||
memcpy(rng.m_state, buf + 32, 32); | |||||
} | |||||
memory_cleanse(buf, 64); | |||||
} | } | ||||
void GetStrongRandBytes(uint8_t *out, int num) { | void GetStrongRandBytes(uint8_t *out, int num) { | ||||
RNGState &rng = GetRNGState(); | RNGState &rng = GetRNGState(); | ||||
assert(num <= 32); | assert(num <= 32); | ||||
CSHA512 hasher; | CSHA512 hasher; | ||||
uint8_t buf[64]; | uint8_t buf[64]; | ||||
// First source: OpenSSL's RNG | // First source: OpenSSL's RNG | ||||
RandAddSeedPerfmon(); | RandAddSeedPerfmon(); | ||||
GetRandBytes(buf, 32); | GetRandBytes(buf, 32); | ||||
hasher.Write(buf, 32); | hasher.Write(buf, 32); | ||||
// Second source: OS RNG | // Second source: OS RNG | ||||
GetOSRand(buf); | GetOSRand(buf); | ||||
hasher.Write(buf, 32); | hasher.Write(buf, 32); | ||||
// Third source: HW RNG, if available. | // Third source: HW RNG, if available. | ||||
if (GetHardwareRand(buf)) { | if (GetHardwareRand(buf)) { | ||||
hasher.Write(buf, 32); | hasher.Write(buf, 32); | ||||
} | } | ||||
// Combine with and update state | // Combine with and update state | ||||
{ | rng.MixExtract(out, num, std::move(hasher)); | ||||
WAIT_LOCK(rng.m_mutex, lock); | |||||
hasher.Write(rng.m_state, sizeof(rng.m_state)); | |||||
hasher.Write((const uint8_t *)&rng.m_counter, sizeof(rng.m_counter)); | |||||
++rng.m_counter; | |||||
hasher.Finalize(buf); | |||||
memcpy(rng.m_state, buf + 32, 32); | |||||
} | |||||
// Produce output | // Produce output | ||||
memcpy(out, buf, num); | memcpy(out, buf, num); | ||||
memory_cleanse(buf, 64); | memory_cleanse(buf, 64); | ||||
} | } | ||||
uint64_t GetRand(uint64_t nMax) { | uint64_t GetRand(uint64_t nMax) { | ||||
if (nMax == 0) { | if (nMax == 0) { | ||||
▲ Show 20 Lines • Show All 139 Lines • Show Last 20 Lines |