Changeset View
Changeset View
Standalone View
Standalone View
src/random.cpp
Show First 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
#include <cpuid.h> | #include <cpuid.h> | ||||
#endif | #endif | ||||
[[noreturn]] static void RandFailure() { | [[noreturn]] static void RandFailure() { | ||||
LogPrintf("Failed to read randomness, aborting\n"); | LogPrintf("Failed to read randomness, aborting\n"); | ||||
std::abort(); | std::abort(); | ||||
} | } | ||||
static inline int64_t GetPerformanceCounter() { | static inline int64_t GetPerformanceCounter() noexcept { | ||||
// Read the hardware time stamp counter when available. | // Read the hardware time stamp counter when available. | ||||
// See https://en.wikipedia.org/wiki/Time_Stamp_Counter for more information. | // See https://en.wikipedia.org/wiki/Time_Stamp_Counter for more information. | ||||
#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) | #if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) | ||||
return __rdtsc(); | return __rdtsc(); | ||||
#elif !defined(_MSC_VER) && defined(__i386__) | #elif !defined(_MSC_VER) && defined(__i386__) | ||||
uint64_t r = 0; | uint64_t r = 0; | ||||
// Constrain the r variable to the eax:edx pair. | // Constrain the r variable to the eax:edx pair. | ||||
__asm__ volatile("rdtsc" : "=A"(r)); | __asm__ volatile("rdtsc" : "=A"(r)); | ||||
Show All 38 Lines | |||||
* assuming it is sufficiently fast (in the order of a few hundred CPU cycles). | * assuming it is sufficiently fast (in the order of a few hundred CPU cycles). | ||||
* Slower sources should probably be invoked separately, and/or only from | * Slower sources should probably be invoked separately, and/or only from | ||||
* RandAddSeedSleep (which is called during idle background operation). | * RandAddSeedSleep (which is called during idle background operation). | ||||
*/ | */ | ||||
static void InitHardwareRand() {} | static void InitHardwareRand() {} | ||||
static void ReportHardwareRand() {} | static void ReportHardwareRand() {} | ||||
#endif | #endif | ||||
static bool GetHardwareRand(uint8_t *ent32) { | static bool GetHardwareRand(uint8_t *ent32) noexcept { | ||||
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) | #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) | ||||
assert(hwrand_initialized.load(std::memory_order_relaxed)); | assert(hwrand_initialized.load(std::memory_order_relaxed)); | ||||
if (rdrand_supported) { | if (rdrand_supported) { | ||||
uint8_t ok; | uint8_t ok; | ||||
// Not all assemblers support the rdrand instruction, write it in hex. | // Not all assemblers support the rdrand instruction, write it in hex. | ||||
#ifdef __i386__ | #ifdef __i386__ | ||||
for (int iter = 0; iter < 4; ++iter) { | for (int iter = 0; iter < 4; ++iter) { | ||||
uint32_t r1, r2; | uint32_t r1, r2; | ||||
▲ Show 20 Lines • Show All 177 Lines • ▼ Show 20 Lines | |||||
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; | 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() noexcept { | ||||
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 | ||||
Show All 15 Lines | struct RNGState { | ||||
/** | /** | ||||
* 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 | * If this function has never been called with strong_seed = true, false is | ||||
* returned. | * returned. | ||||
*/ | */ | ||||
bool MixExtract(uint8_t *out, size_t num, CSHA512 &&hasher, | bool MixExtract(uint8_t *out, size_t num, CSHA512 &&hasher, | ||||
bool strong_seed) { | bool strong_seed) noexcept { | ||||
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; | bool ret; | ||||
{ | { | ||||
LOCK(m_mutex); | LOCK(m_mutex); | ||||
ret = (m_strongly_seeded |= strong_seed); | ret = (m_strongly_seeded |= strong_seed); | ||||
Show All 15 Lines | bool MixExtract(uint8_t *out, size_t num, CSHA512 &&hasher, | ||||
} | } | ||||
// 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; | return ret; | ||||
} | } | ||||
}; | }; | ||||
RNGState &GetRNGState() { | RNGState &GetRNGState() noexcept { | ||||
// 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 SeedTimestamp(CSHA512 &hasher) { | /** | ||||
* A note on the use of noexcept in the seeding functions below: | |||||
* | |||||
* None of the RNG code should ever throw any exception, with the sole exception | |||||
* of MilliSleep in SeedSleep, which can (and does) support interruptions which | |||||
* cause a boost::thread_interrupted to be thrown. | |||||
* | |||||
* This means that SeedSleep, and all functions that invoke it are throwing. | |||||
* However, we know that GetRandBytes() and GetStrongRandBytes() never trigger | |||||
* this sleeping logic, so they are noexcept. The same is true for all the | |||||
* GetRand*() functions that use GetRandBytes() indirectly. | |||||
* | |||||
* TODO: After moving away from interruptible boost-based thread management, | |||||
* everything can become noexcept here. | |||||
*/ | |||||
static void SeedTimestamp(CSHA512 &hasher) noexcept { | |||||
int64_t perfcounter = GetPerformanceCounter(); | int64_t perfcounter = GetPerformanceCounter(); | ||||
hasher.Write((const uint8_t *)&perfcounter, sizeof(perfcounter)); | hasher.Write((const uint8_t *)&perfcounter, sizeof(perfcounter)); | ||||
} | } | ||||
static void SeedFast(CSHA512 &hasher) { | static void SeedFast(CSHA512 &hasher) noexcept { | ||||
uint8_t buffer[32]; | uint8_t buffer[32]; | ||||
// Stack pointer to indirectly commit to thread/callstack | // Stack pointer to indirectly commit to thread/callstack | ||||
const uint8_t *ptr = buffer; | const uint8_t *ptr = buffer; | ||||
hasher.Write((const uint8_t *)&ptr, sizeof(ptr)); | hasher.Write((const uint8_t *)&ptr, sizeof(ptr)); | ||||
// Hardware randomness is very fast when available; use it always. | // Hardware randomness is very fast when available; use it always. | ||||
bool have_hw_rand = GetHardwareRand(buffer); | bool have_hw_rand = GetHardwareRand(buffer); | ||||
if (have_hw_rand) { | if (have_hw_rand) { | ||||
hasher.Write(buffer, sizeof(buffer)); | hasher.Write(buffer, sizeof(buffer)); | ||||
} | } | ||||
// High-precision timestamp | // High-precision timestamp | ||||
SeedTimestamp(hasher); | SeedTimestamp(hasher); | ||||
} | } | ||||
static void SeedSlow(CSHA512 &hasher) { | static void SeedSlow(CSHA512 &hasher) noexcept { | ||||
uint8_t buffer[32]; | uint8_t buffer[32]; | ||||
// Everything that the 'fast' seeder includes | // Everything that the 'fast' seeder includes | ||||
SeedFast(hasher); | SeedFast(hasher); | ||||
// OS randomness | // OS randomness | ||||
GetOSRand(buffer); | GetOSRand(buffer); | ||||
hasher.Write(buffer, sizeof(buffer)); | hasher.Write(buffer, sizeof(buffer)); | ||||
Show All 23 Lines | static void SeedSleep(CSHA512 &hasher) { | ||||
// 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); | ||||
} | } | ||||
static void SeedStartup(CSHA512 &hasher) { | static void SeedStartup(CSHA512 &hasher) noexcept { | ||||
#ifdef WIN32 | #ifdef WIN32 | ||||
RAND_screen(); | RAND_screen(); | ||||
#endif | #endif | ||||
// Everything that the 'slow' seeder includes. | // Everything that the 'slow' seeder includes. | ||||
SeedSlow(hasher); | SeedSlow(hasher); | ||||
// Windows performance monitor data. | // Windows performance monitor data. | ||||
Show All 39 Lines | static void ProcRand(uint8_t *out, int num, RNGLevel level) { | ||||
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); | ||||
RAND_add(buf, sizeof(buf), num); | RAND_add(buf, sizeof(buf), num); | ||||
memory_cleanse(buf, 64); | memory_cleanse(buf, 64); | ||||
} | } | ||||
} | } | ||||
void GetRandBytes(uint8_t *buf, int num) { | void GetRandBytes(uint8_t *buf, int num) noexcept { | ||||
ProcRand(buf, num, RNGLevel::FAST); | ProcRand(buf, num, RNGLevel::FAST); | ||||
} | } | ||||
void GetStrongRandBytes(uint8_t *buf, int num) { | void GetStrongRandBytes(uint8_t *buf, int num) noexcept { | ||||
ProcRand(buf, num, RNGLevel::SLOW); | ProcRand(buf, num, RNGLevel::SLOW); | ||||
} | } | ||||
void RandAddSeedSleep() { | void RandAddSeedSleep() { | ||||
ProcRand(nullptr, 0, RNGLevel::SLEEP); | ProcRand(nullptr, 0, RNGLevel::SLEEP); | ||||
} | } | ||||
uint64_t GetRand(uint64_t nMax) { | uint64_t GetRand(uint64_t nMax) noexcept { | ||||
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 | ||||
uint64_t nRange = (std::numeric_limits<uint64_t>::max() / nMax) * nMax; | uint64_t nRange = (std::numeric_limits<uint64_t>::max() / nMax) * nMax; | ||||
uint64_t nRand = 0; | uint64_t nRand = 0; | ||||
do { | do { | ||||
GetRandBytes((uint8_t *)&nRand, sizeof(nRand)); | GetRandBytes((uint8_t *)&nRand, sizeof(nRand)); | ||||
} while (nRand >= nRange); | } while (nRand >= nRange); | ||||
return (nRand % nMax); | return (nRand % nMax); | ||||
} | } | ||||
int GetRandInt(int nMax) { | int GetRandInt(int nMax) noexcept { | ||||
return GetRand(nMax); | return GetRand(nMax); | ||||
} | } | ||||
uint256 GetRandHash() { | uint256 GetRandHash() noexcept { | ||||
uint256 hash; | uint256 hash; | ||||
GetRandBytes((uint8_t *)&hash, sizeof(hash)); | GetRandBytes((uint8_t *)&hash, sizeof(hash)); | ||||
return hash; | return hash; | ||||
} | } | ||||
void FastRandomContext::RandomSeed() { | void FastRandomContext::RandomSeed() { | ||||
uint256 seed = GetRandHash(); | uint256 seed = GetRandHash(); | ||||
rng.SetKey(seed.begin(), 32); | rng.SetKey(seed.begin(), 32); | ||||
requires_seed = false; | requires_seed = false; | ||||
} | } | ||||
uint256 FastRandomContext::rand256() { | uint256 FastRandomContext::rand256() noexcept { | ||||
if (bytebuf_size < 32) { | if (bytebuf_size < 32) { | ||||
FillByteBuffer(); | FillByteBuffer(); | ||||
} | } | ||||
uint256 ret; | uint256 ret; | ||||
memcpy(ret.begin(), bytebuf + 64 - bytebuf_size, 32); | memcpy(ret.begin(), bytebuf + 64 - bytebuf_size, 32); | ||||
bytebuf_size -= 32; | bytebuf_size -= 32; | ||||
return ret; | return ret; | ||||
} | } | ||||
std::vector<uint8_t> FastRandomContext::randbytes(size_t len) { | std::vector<uint8_t> FastRandomContext::randbytes(size_t len) { | ||||
if (requires_seed) { | if (requires_seed) { | ||||
RandomSeed(); | RandomSeed(); | ||||
} | } | ||||
std::vector<uint8_t> ret(len); | std::vector<uint8_t> ret(len); | ||||
if (len > 0) { | if (len > 0) { | ||||
rng.Output(&ret[0], len); | rng.Output(&ret[0], len); | ||||
} | } | ||||
return ret; | return ret; | ||||
} | } | ||||
FastRandomContext::FastRandomContext(const uint256 &seed) | FastRandomContext::FastRandomContext(const uint256 &seed) noexcept | ||||
: requires_seed(false), bytebuf_size(0), bitbuf_size(0) { | : requires_seed(false), bytebuf_size(0), bitbuf_size(0) { | ||||
rng.SetKey(seed.begin(), 32); | rng.SetKey(seed.begin(), 32); | ||||
} | } | ||||
bool Random_SanityCheck() { | bool Random_SanityCheck() { | ||||
uint64_t start = GetPerformanceCounter(); | uint64_t start = GetPerformanceCounter(); | ||||
/** | /** | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | bool Random_SanityCheck() { | ||||
CSHA512 to_add; | CSHA512 to_add; | ||||
to_add.Write((const uint8_t *)&start, sizeof(start)); | to_add.Write((const uint8_t *)&start, sizeof(start)); | ||||
to_add.Write((const uint8_t *)&stop, sizeof(stop)); | to_add.Write((const uint8_t *)&stop, sizeof(stop)); | ||||
GetRNGState().MixExtract(nullptr, 0, std::move(to_add), false); | GetRNGState().MixExtract(nullptr, 0, std::move(to_add), false); | ||||
return true; | return true; | ||||
} | } | ||||
FastRandomContext::FastRandomContext(bool fDeterministic) | FastRandomContext::FastRandomContext(bool fDeterministic) noexcept | ||||
: requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0) { | : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0) { | ||||
if (!fDeterministic) { | if (!fDeterministic) { | ||||
return; | return; | ||||
} | } | ||||
uint256 seed; | uint256 seed; | ||||
rng.SetKey(seed.begin(), 32); | rng.SetKey(seed.begin(), 32); | ||||
} | } | ||||
Show All 21 Lines |