Changeset View
Changeset View
Standalone View
Standalone View
src/random.cpp
Show First 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | #else | ||||
return std::chrono::high_resolution_clock::now().time_since_epoch().count(); | return std::chrono::high_resolution_clock::now().time_since_epoch().count(); | ||||
#endif | #endif | ||||
} | } | ||||
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) | #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) | ||||
static std::atomic<bool> hwrand_initialized{false}; | static std::atomic<bool> hwrand_initialized{false}; | ||||
static bool rdrand_supported = false; | static bool rdrand_supported = false; | ||||
static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000; | static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000; | ||||
static void RDRandInit() { | static void InitHardwareRand() { | ||||
uint32_t eax, ebx, ecx, edx; | uint32_t eax, ebx, ecx, edx; | ||||
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) { | if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) { | ||||
rdrand_supported = true; | rdrand_supported = true; | ||||
} | } | ||||
hwrand_initialized.store(true); | hwrand_initialized.store(true); | ||||
} | } | ||||
static void RDRandReport() { | static void ReportHardwareRand() { | ||||
assert(hwrand_initialized.load(std::memory_order_relaxed)); | assert(hwrand_initialized.load(std::memory_order_relaxed)); | ||||
if (rdrand_supported) { | if (rdrand_supported) { | ||||
// This must be done in a separate function, as HWRandInit() may be | // This must be done in a separate function, as HWRandInit() may be | ||||
// indirectly called from global constructors, before logging is | // indirectly called from global constructors, before logging is | ||||
// initialized. | // initialized. | ||||
LogPrintf("Using RdRand as an additional entropy source\n"); | LogPrintf("Using RdRand as an additional entropy source\n"); | ||||
} | } | ||||
} | } | ||||
#else | #else | ||||
static void RDRandInit() {} | /** | ||||
static void RDRandReport() {} | * Access to other hardware random number generators could be added here later, | ||||
* 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 | |||||
* RandAddSeedSleep (which is called during idle background operation). | |||||
*/ | |||||
static void InitHardwareRand() {} | |||||
static void ReportHardwareRand() {} | |||||
#endif | #endif | ||||
static bool GetHWRand(uint8_t *ent32) { | static bool GetHardwareRand(uint8_t *ent32) { | ||||
#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 187 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
namespace { | namespace { | ||||
struct RNGState { | struct RNGState { | ||||
Mutex m_mutex; | Mutex m_mutex; | ||||
uint8_t m_state[32] = {0}; | uint8_t m_state[32] = {0}; | ||||
uint64_t m_counter = 0; | uint64_t m_counter = 0; | ||||
explicit RNGState() { RDRandInit(); } | explicit RNGState() { InitHardwareRand(); } | ||||
}; | }; | ||||
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; | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | void GetStrongRandBytes(uint8_t *out, int num) { | ||||
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 (GetHWRand(buf)) { | if (GetHardwareRand(buf)) { | ||||
hasher.Write(buf, 32); | hasher.Write(buf, 32); | ||||
} | } | ||||
// Combine with and update state | // Combine with and update state | ||||
{ | { | ||||
WAIT_LOCK(rng.m_mutex, lock); | WAIT_LOCK(rng.m_mutex, lock); | ||||
hasher.Write(rng.m_state, sizeof(rng.m_state)); | hasher.Write(rng.m_state, sizeof(rng.m_state)); | ||||
hasher.Write((const uint8_t *)&rng.m_counter, sizeof(rng.m_counter)); | hasher.Write((const uint8_t *)&rng.m_counter, sizeof(rng.m_counter)); | ||||
▲ Show 20 Lines • Show All 141 Lines • ▼ Show 20 Lines | operator=(FastRandomContext &&from) noexcept { | ||||
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(); | GetRNGState(); | ||||
RDRandReport(); | ReportHardwareRand(); | ||||
} | } |