Automatically initialize RNG on first use.

This commit is contained in:
Pieter Wuille 2018-12-17 16:48:21 -08:00
parent 2d1cc50939
commit 05fde14e3a
4 changed files with 56 additions and 22 deletions

View file

@ -6,7 +6,6 @@
#include <crypto/sha256.h> #include <crypto/sha256.h>
#include <key.h> #include <key.h>
#include <random.h>
#include <util/system.h> #include <util/system.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <validation.h> #include <validation.h>
@ -67,7 +66,6 @@ int main(int argc, char** argv)
const fs::path bench_datadir{SetDataDir()}; const fs::path bench_datadir{SetDataDir()};
SHA256AutoDetect(); SHA256AutoDetect();
RandomInit();
ECC_Start(); ECC_Start();
SetupEnvironment(); SetupEnvironment();

View file

@ -74,7 +74,6 @@ static inline int64_t GetPerformanceCounter()
#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;
@ -83,13 +82,24 @@ static void RDRandInit()
{ {
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)) {
LogPrintf("Using RdRand as an additional entropy source\n");
rdrand_supported = true; rdrand_supported = true;
} }
hwrand_initialized.store(true); hwrand_initialized.store(true);
} }
static void RDRandReport()
{
assert(hwrand_initialized.load(std::memory_order_relaxed));
if (rdrand_supported) {
// This must be done in a separate function, as HWRandInit() may be indirectly called
// from global constructors, before logging is initialized.
LogPrintf("Using RdRand as an additional entropy source\n");
}
}
#else #else
static void RDRandInit() {} static void RDRandInit() {}
static void RDRandReport() {}
#endif #endif
static bool GetHWRand(unsigned char* ent32) { static bool GetHWRand(unsigned char* ent32) {
@ -279,6 +289,26 @@ void GetRandBytes(unsigned char* buf, int num)
} }
} }
namespace {
struct RNGState {
Mutex m_mutex;
unsigned char m_state[32] = {0};
uint64_t m_counter = 0;
explicit RNGState() {
RDRandInit();
}
};
RNGState& GetRNGState()
{
// This C++11 idiom relies on the guarantee that static variable are initialized
// on first call, even when multiple parallel calls are permitted.
static std::unique_ptr<RNGState> g_rng{new RNGState()};
return *g_rng;
}
}
static void AddDataToRng(void* data, size_t len); static void AddDataToRng(void* data, size_t len);
void RandAddSeedSleep() void RandAddSeedSleep()
@ -295,29 +325,28 @@ void RandAddSeedSleep()
memory_cleanse(&nPerfCounter2, sizeof(nPerfCounter2)); memory_cleanse(&nPerfCounter2, sizeof(nPerfCounter2));
} }
static Mutex cs_rng_state;
static unsigned char rng_state[32] = {0};
static uint64_t rng_counter = 0;
static void AddDataToRng(void* data, size_t len) { static void AddDataToRng(void* data, size_t len) {
RNGState& rng = GetRNGState();
CSHA512 hasher; CSHA512 hasher;
hasher.Write((const unsigned char*)&len, sizeof(len)); hasher.Write((const unsigned char*)&len, sizeof(len));
hasher.Write((const unsigned char*)data, len); hasher.Write((const unsigned char*)data, len);
unsigned char buf[64]; unsigned char buf[64];
{ {
WAIT_LOCK(cs_rng_state, lock); WAIT_LOCK(rng.m_mutex, lock);
hasher.Write(rng_state, sizeof(rng_state)); hasher.Write(rng.m_state, sizeof(rng.m_state));
hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter)); hasher.Write((const unsigned char*)&rng.m_counter, sizeof(rng.m_counter));
++rng_counter; ++rng.m_counter;
hasher.Finalize(buf); hasher.Finalize(buf);
memcpy(rng_state, buf + 32, 32); memcpy(rng.m_state, buf + 32, 32);
} }
memory_cleanse(buf, 64); memory_cleanse(buf, 64);
} }
void GetStrongRandBytes(unsigned char* out, int num) void GetStrongRandBytes(unsigned char* out, int num)
{ {
RNGState& rng = GetRNGState();
assert(num <= 32); assert(num <= 32);
CSHA512 hasher; CSHA512 hasher;
unsigned char buf[64]; unsigned char buf[64];
@ -338,12 +367,12 @@ void GetStrongRandBytes(unsigned char* out, int num)
// Combine with and update state // Combine with and update state
{ {
WAIT_LOCK(cs_rng_state, lock); WAIT_LOCK(rng.m_mutex, lock);
hasher.Write(rng_state, sizeof(rng_state)); hasher.Write(rng.m_state, sizeof(rng.m_state));
hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter)); hasher.Write((const unsigned char*)&rng.m_counter, sizeof(rng.m_counter));
++rng_counter; ++rng.m_counter;
hasher.Finalize(buf); hasher.Finalize(buf);
memcpy(rng_state, buf + 32, 32); memcpy(rng.m_state, buf + 32, 32);
} }
// Produce output // Produce output
@ -480,5 +509,8 @@ FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexce
void RandomInit() void RandomInit()
{ {
RDRandInit(); // Invoke RNG code to trigger initialization (if not already performed)
GetRNGState();
RDRandReport();
} }

View file

@ -178,7 +178,12 @@ void GetOSRand(unsigned char *ent32);
*/ */
bool Random_SanityCheck(); bool Random_SanityCheck();
/** Initialize the RNG. */ /**
* Initialize global RNG state and log any CPU features that are used.
*
* Calling this function is optional. RNG state will be initialized when first
* needed if it is not called.
*/
void RandomInit(); void RandomInit();
#endif // BITCOIN_RANDOM_H #endif // BITCOIN_RANDOM_H

View file

@ -49,7 +49,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
: m_path_root(fs::temp_directory_path() / "test_bitcoin" / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30)))) : m_path_root(fs::temp_directory_path() / "test_bitcoin" / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
{ {
SHA256AutoDetect(); SHA256AutoDetect();
RandomInit();
ECC_Start(); ECC_Start();
SetupEnvironment(); SetupEnvironment();
SetupNetworking(); SetupNetworking();