Changeset View
Changeset View
Standalone View
Standalone View
src/random.cpp
Show First 20 Lines • Show All 145 Lines • ▼ Show 20 Lines | #else | ||||
WriteLE64(ent32 + 24, r4); | WriteLE64(ent32 + 24, r4); | ||||
#endif | #endif | ||||
return true; | return true; | ||||
} | } | ||||
#endif | #endif | ||||
return false; | return false; | ||||
} | } | ||||
void RandAddSeed() { | static void RandAddSeedPerfmon(CSHA512 &hasher) { | ||||
// Seed with CPU performance counter | |||||
int64_t nCounter = GetPerformanceCounter(); | |||||
RAND_add(&nCounter, sizeof(nCounter), 1.5); | |||||
memory_cleanse((void *)&nCounter, sizeof(nCounter)); | |||||
} | |||||
static void RandAddSeedPerfmon() { | |||||
RandAddSeed(); | |||||
#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) { | ||||
return; | return; | ||||
Show All 12 Lines | while (true) { | ||||
if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize) { | if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize) { | ||||
break; | break; | ||||
} | } | ||||
// Grow size of buffer exponentially | // Grow size of buffer exponentially | ||||
vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); | vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); | ||||
} | } | ||||
RegCloseKey(HKEY_PERFORMANCE_DATA); | RegCloseKey(HKEY_PERFORMANCE_DATA); | ||||
if (ret == ERROR_SUCCESS) { | if (ret == ERROR_SUCCESS) { | ||||
RAND_add(vData.data(), nSize, nSize / 100.0); | hasher.Write(vData.data(), nSize); | ||||
memory_cleanse(vData.data(), nSize); | memory_cleanse(vData.data(), nSize); | ||||
} else { | } else { | ||||
// Performance data is only a best-effort attempt at improving the | // Performance data is only a best-effort attempt at improving the | ||||
// situation when the OS randomness (and other sources) aren't | // situation when the OS randomness (and other sources) aren't | ||||
// adequate. As a result, failure to read it is isn't considered | // adequate. As a result, failure to read it is isn't considered | ||||
// critical, so we don't call RandFailure(). | // critical, so we don't call RandFailure(). | ||||
// TODO: Add logging when the logger is made functional before global | // TODO: Add logging when the logger is made functional before global | ||||
// constructors have been invoked. | // constructors have been invoked. | ||||
▲ Show 20 Lines • Show All 95 Lines • ▼ Show 20 Lines | #else | ||||
/** | /** | ||||
* Fall back to /dev/urandom if there is no specific method implemented to | * Fall back to /dev/urandom if there is no specific method implemented to | ||||
* get system entropy for this OS. | * get system entropy for this OS. | ||||
*/ | */ | ||||
GetDevURandom(ent32); | GetDevURandom(ent32); | ||||
#endif | #endif | ||||
} | } | ||||
void GetRandBytes(uint8_t *buf, int num) { | |||||
if (RAND_bytes(buf, num) != 1) { | |||||
RandFailure(); | |||||
} | |||||
} | |||||
void LockingCallbackOpenSSL(int mode, int i, const char *file, int line); | void LockingCallbackOpenSSL(int mode, int i, const char *file, int line); | ||||
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; | ||||
bool m_strongly_seeded GUARDED_BY(m_mutex) = false; | |||||
std::unique_ptr<Mutex[]> m_mutex_openssl; | std::unique_ptr<Mutex[]> m_mutex_openssl; | ||||
RNGState() { | RNGState() { | ||||
InitHardwareRand(); | InitHardwareRand(); | ||||
// Init OpenSSL library multithreading support | // Init OpenSSL library multithreading support | ||||
m_mutex_openssl.reset(new Mutex[CRYPTO_num_locks()]); | m_mutex_openssl.reset(new Mutex[CRYPTO_num_locks()]); | ||||
CRYPTO_set_locking_callback(LockingCallbackOpenSSL); | CRYPTO_set_locking_callback(LockingCallbackOpenSSL); | ||||
// OpenSSL can optionally load a config file which lists optional | // OpenSSL can optionally load a config file which lists optional | ||||
// loadable modules and engines. We don't use them so we don't require | // loadable modules and engines. We don't use them so we don't require | ||||
// the config. However some of our libs may call functions which attempt | // the config. However some of our libs may call functions which attempt | ||||
// to load the config file, possibly resulting in an exit() or crash if | // to load the config file, possibly resulting in an exit() or crash if | ||||
// it is missing or corrupt. Explicitly tell OpenSSL not to try to load | // it is missing or corrupt. Explicitly tell OpenSSL not to try to load | ||||
// the file. The result for our libs will be that the config appears to | // the file. The result for our libs will be that the config appears to | ||||
// have been loaded and there are no modules/engines available. | // have been loaded and there are no modules/engines available. | ||||
OPENSSL_no_config(); | OPENSSL_no_config(); | ||||
#ifdef WIN32 | |||||
// Seed OpenSSL PRNG with current contents of the screen | |||||
RAND_screen(); | |||||
#endif | |||||
// Seed OpenSSL PRNG with performance counter | |||||
RandAddSeed(); | |||||
} | } | ||||
~RNGState() { | ~RNGState() { | ||||
// Securely erase the memory used by the OpenSSL PRNG | // Securely erase the memory used by the OpenSSL PRNG | ||||
RAND_cleanup(); | RAND_cleanup(); | ||||
// Shutdown OpenSSL library multithreading support | // Shutdown OpenSSL library multithreading support | ||||
CRYPTO_set_locking_callback(nullptr); | CRYPTO_set_locking_callback(nullptr); | ||||
} | } | ||||
/** | /** | ||||
* Extract up to 32 bytes of entropy from the RNG state, mixing in new | * Extract up to 32 bytes of entropy from the RNG state, mixing in new | ||||
* entropy from hasher. | * entropy from hasher. | ||||
* | |||||
* If this function has never been called with strong_seed = true, false is | |||||
* returned. | |||||
*/ | */ | ||||
void MixExtract(uint8_t *out, size_t num, CSHA512 &&hasher) { | bool MixExtract(uint8_t *out, size_t num, CSHA512 &&hasher, | ||||
bool strong_seed) { | |||||
assert(num <= 32); | assert(num <= 32); | ||||
uint8_t buf[64]; | uint8_t buf[64]; | ||||
static_assert(sizeof(buf) == CSHA512::OUTPUT_SIZE, | static_assert(sizeof(buf) == CSHA512::OUTPUT_SIZE, | ||||
"Buffer needs to have hasher's output size"); | "Buffer needs to have hasher's output size"); | ||||
bool ret; | |||||
{ | { | ||||
LOCK(m_mutex); | LOCK(m_mutex); | ||||
ret = (m_strongly_seeded |= strong_seed); | |||||
// Write the current state of the RNG into the hasher | // Write the current state of the RNG into the hasher | ||||
hasher.Write(m_state, 32); | hasher.Write(m_state, 32); | ||||
// Write a new counter number into the state | // Write a new counter number into the state | ||||
hasher.Write((const uint8_t *)&m_counter, sizeof(m_counter)); | hasher.Write((const uint8_t *)&m_counter, sizeof(m_counter)); | ||||
++m_counter; | ++m_counter; | ||||
// Finalize the hasher | // Finalize the hasher | ||||
hasher.Finalize(buf); | hasher.Finalize(buf); | ||||
// Store the last 32 bytes of the hash output as new RNG state. | // Store the last 32 bytes of the hash output as new RNG state. | ||||
memcpy(m_state, buf + 32, 32); | memcpy(m_state, buf + 32, 32); | ||||
} | } | ||||
// If desired, copy (up to) the first 32 bytes of the hash output as | // If desired, copy (up to) the first 32 bytes of the hash output as | ||||
// output. | // output. | ||||
if (num) { | if (num) { | ||||
assert(out != nullptr); | assert(out != nullptr); | ||||
memcpy(out, buf, num); | memcpy(out, buf, num); | ||||
} | } | ||||
// Best effort cleanup of internal state | // Best effort cleanup of internal state | ||||
hasher.Reset(); | hasher.Reset(); | ||||
memory_cleanse(buf, 64); | memory_cleanse(buf, 64); | ||||
return ret; | |||||
} | } | ||||
}; | }; | ||||
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 | ||||
void LockingCallbackOpenSSL(int mode, int i, const char *file, | void LockingCallbackOpenSSL(int mode, int i, const char *file, | ||||
int line) NO_THREAD_SAFETY_ANALYSIS { | int line) NO_THREAD_SAFETY_ANALYSIS { | ||||
RNGState &rng = GetRNGState(); | RNGState &rng = GetRNGState(); | ||||
if (mode & CRYPTO_LOCK) { | if (mode & CRYPTO_LOCK) { | ||||
rng.m_mutex_openssl[i].lock(); | rng.m_mutex_openssl[i].lock(); | ||||
} else { | } else { | ||||
rng.m_mutex_openssl[i].unlock(); | rng.m_mutex_openssl[i].unlock(); | ||||
} | } | ||||
} | } | ||||
static void AddDataToRng(void *data, size_t len, RNGState &rng); | static void SeedTimestamp(CSHA512 &hasher) { | ||||
int64_t perfcounter = GetPerformanceCounter(); | |||||
hasher.Write((const uint8_t *)&perfcounter, sizeof(perfcounter)); | |||||
} | |||||
void RandAddSeedSleep() { | static void SeedFast(CSHA512 &hasher) { | ||||
RNGState &rng = GetRNGState(); | uint8_t buffer[32]; | ||||
int64_t nPerfCounter1 = GetPerformanceCounter(); | // Stack pointer to indirectly commit to thread/callstack | ||||
std::this_thread::sleep_for(std::chrono::milliseconds(1)); | const uint8_t *ptr = buffer; | ||||
int64_t nPerfCounter2 = GetPerformanceCounter(); | hasher.Write((const uint8_t *)&ptr, sizeof(ptr)); | ||||
// Combine with and update state | // Hardware randomness is very fast when available; use it always. | ||||
AddDataToRng(&nPerfCounter1, sizeof(nPerfCounter1), rng); | bool have_hw_rand = GetHardwareRand(buffer); | ||||
AddDataToRng(&nPerfCounter2, sizeof(nPerfCounter2), rng); | if (have_hw_rand) { | ||||
hasher.Write(buffer, sizeof(buffer)); | |||||
} | |||||
memory_cleanse(&nPerfCounter1, sizeof(nPerfCounter1)); | // High-precision timestamp | ||||
memory_cleanse(&nPerfCounter2, sizeof(nPerfCounter2)); | SeedTimestamp(hasher); | ||||
} | } | ||||
static void AddDataToRng(void *data, size_t len, RNGState &rng) { | static void SeedSlow(CSHA512 &hasher) { | ||||
CSHA512 hasher; | uint8_t buffer[32]; | ||||
hasher.Write((const uint8_t *)&len, sizeof(len)); | |||||
hasher.Write((const uint8_t *)data, len); | // Everything that the 'fast' seeder includes | ||||
rng.MixExtract(nullptr, 0, std::move(hasher)); | SeedFast(hasher); | ||||
// OS randomness | |||||
GetOSRand(buffer); | |||||
hasher.Write(buffer, sizeof(buffer)); | |||||
// OpenSSL RNG (for now) | |||||
RAND_bytes(buffer, sizeof(buffer)); | |||||
hasher.Write(buffer, sizeof(buffer)); | |||||
// High-precision timestamp. | |||||
// | |||||
// 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 | |||||
// this function). | |||||
SeedTimestamp(hasher); | |||||
} | |||||
static void SeedSleep(CSHA512 &hasher) { | |||||
// Everything that the 'fast' seeder includes | |||||
SeedFast(hasher); | |||||
// High-precision timestamp | |||||
SeedTimestamp(hasher); | |||||
// Sleep for 1ms | |||||
MilliSleep(1); | |||||
// High-precision timestamp after sleeping (as we commit to both the time | |||||
// before and after, this measures the delay) | |||||
SeedTimestamp(hasher); | |||||
// Windows performance monitor data (once every 10 minutes) | |||||
RandAddSeedPerfmon(hasher); | |||||
} | |||||
static void SeedStartup(CSHA512 &hasher) { | |||||
#ifdef WIN32 | |||||
RAND_screen(); | |||||
#endif | |||||
// Everything that the 'slow' seeder includes. | |||||
SeedSlow(hasher); | |||||
// Windows performance monitor data. | |||||
RandAddSeedPerfmon(hasher); | |||||
} | } | ||||
void GetStrongRandBytes(uint8_t *out, int num) { | enum class RNGLevel { | ||||
FAST, //!< Automatically called by GetRandBytes | |||||
SLOW, //!< Automatically called by GetStrongRandBytes | |||||
SLEEP, //!< Called by RandAddSeedSleep() | |||||
}; | |||||
static void ProcRand(uint8_t *out, int num, RNGLevel level) { | |||||
// Make sure the RNG is initialized first (as all Seed* function possibly | |||||
// need hwrand to be available). | |||||
RNGState &rng = GetRNGState(); | RNGState &rng = GetRNGState(); | ||||
assert(num <= 32); | assert(num <= 32); | ||||
CSHA512 hasher; | |||||
uint8_t buf[64]; | |||||
// First source: OpenSSL's RNG | CSHA512 hasher; | ||||
RandAddSeedPerfmon(); | switch (level) { | ||||
GetRandBytes(buf, 32); | case RNGLevel::FAST: | ||||
hasher.Write(buf, 32); | SeedFast(hasher); | ||||
break; | |||||
// Second source: OS RNG | case RNGLevel::SLOW: | ||||
GetOSRand(buf); | SeedSlow(hasher); | ||||
hasher.Write(buf, 32); | break; | ||||
case RNGLevel::SLEEP: | |||||
// Third source: HW RNG, if available. | SeedSleep(hasher); | ||||
if (GetHardwareRand(buf)) { | break; | ||||
hasher.Write(buf, 32); | |||||
} | } | ||||
// Combine with and update state | // Combine with and update state | ||||
rng.MixExtract(out, num, std::move(hasher)); | if (!rng.MixExtract(out, num, std::move(hasher), false)) { | ||||
// On the first invocation, also seed with SeedStartup(). | |||||
CSHA512 startup_hasher; | |||||
SeedStartup(startup_hasher); | |||||
rng.MixExtract(out, num, std::move(startup_hasher), true); | |||||
} | |||||
// Produce output | // For anything but the 'fast' level, feed the resulting RNG output (after | ||||
memcpy(out, buf, num); | // an additional hashing step) back into OpenSSL. | ||||
if (level != RNGLevel::FAST) { | |||||
uint8_t buf[64]; | |||||
CSHA512().Write(out, num).Finalize(buf); | |||||
RAND_add(buf, sizeof(buf), num); | |||||
memory_cleanse(buf, 64); | memory_cleanse(buf, 64); | ||||
} | } | ||||
} | |||||
void GetRandBytes(uint8_t *buf, int num) { | |||||
ProcRand(buf, num, RNGLevel::FAST); | |||||
} | |||||
void GetStrongRandBytes(uint8_t *buf, int num) { | |||||
ProcRand(buf, num, RNGLevel::SLOW); | |||||
} | |||||
void RandAddSeedSleep() { | |||||
ProcRand(nullptr, 0, RNGLevel::SLEEP); | |||||
} | |||||
uint64_t GetRand(uint64_t nMax) { | uint64_t GetRand(uint64_t nMax) { | ||||
if (nMax == 0) { | if (nMax == 0) { | ||||
return 0; | return 0; | ||||
} | } | ||||
// The range of the random source must be a multiple of the modulus to give | // The range of the random source must be a multiple of the modulus to give | ||||
// every possible output value an equal possibility | // every possible output value an equal possibility | ||||
▲ Show 20 Lines • Show All 90 Lines • ▼ Show 20 Lines | bool Random_SanityCheck() { | ||||
// call + 1ms sleep. | // call + 1ms sleep. | ||||
std::this_thread::sleep_for(std::chrono::milliseconds(1)); | std::this_thread::sleep_for(std::chrono::milliseconds(1)); | ||||
uint64_t stop = GetPerformanceCounter(); | uint64_t stop = GetPerformanceCounter(); | ||||
if (stop == start) { | if (stop == start) { | ||||
return false; | return false; | ||||
} | } | ||||
// We called GetPerformanceCounter. Use it as entropy. | // We called GetPerformanceCounter. Use it as entropy. | ||||
RAND_add((const uint8_t *)&start, sizeof(start), 1); | CSHA512 to_add; | ||||
RAND_add((const uint8_t *)&stop, sizeof(stop), 1); | to_add.Write((const uint8_t *)&start, sizeof(start)); | ||||
to_add.Write((const uint8_t *)&stop, sizeof(stop)); | |||||
GetRNGState().MixExtract(nullptr, 0, std::move(to_add), false); | |||||
return true; | return true; | ||||
} | } | ||||
FastRandomContext::FastRandomContext(bool fDeterministic) | FastRandomContext::FastRandomContext(bool fDeterministic) | ||||
: requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0) { | : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0) { | ||||
if (!fDeterministic) { | if (!fDeterministic) { | ||||
return; | return; | ||||
Show All 14 Lines | operator=(FastRandomContext &&from) noexcept { | ||||
from.requires_seed = true; | from.requires_seed = true; | ||||
from.bytebuf_size = 0; | from.bytebuf_size = 0; | ||||
from.bitbuf_size = 0; | from.bitbuf_size = 0; | ||||
return *this; | return *this; | ||||
} | } | ||||
void RandomInit() { | void RandomInit() { | ||||
// Invoke RNG code to trigger initialization (if not already performed) | // Invoke RNG code to trigger initialization (if not already performed) | ||||
GetRNGState(); | ProcRand(nullptr, 0, RNGLevel::FAST); | ||||
ReportHardwareRand(); | ReportHardwareRand(); | ||||
} | } |