From 930acbfe496535281059545fbb2f8c19b3db8629 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 2 May 2017 18:21:33 -0700 Subject: [PATCH 1/7] Use hardware timestamps in RNG seeding --- src/random.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 0243511ff..6fe03e69a 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -16,6 +16,7 @@ #include #include +#include #ifndef WIN32 #include @@ -46,15 +47,22 @@ static void RandFailure() static inline int64_t GetPerformanceCounter() { - int64_t nCounter = 0; -#ifdef WIN32 - QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); + // Read the hardware time stamp counter when available. + // See https://en.wikipedia.org/wiki/Time_Stamp_Counter for more information. +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) + return __rdtsc(); +#elif !defined(_MSC_VER) && defined(__i386__) + uint64_t r = 0; + __asm__ volatile ("rdtsc" : "=A"(r)); // Constrain the r variable to the eax:edx pair. + return r; +#elif !defined(_MSC_VER) && (defined(__x86_64__) || defined(__amd64__)) + uint64_t r1 = 0, r2 = 0; + __asm__ volatile ("rdtsc" : "=a"(r1), "=d"(r2)); // Constrain r1 to rax and r2 to rdx. + return (r2 << 32) | r1; #else - timeval t; - gettimeofday(&t, NULL); - nCounter = (int64_t)(t.tv_sec * 1000000 + t.tv_usec); + // Fall back to using C++11 clock (usually microsecond or nanosecond precision) + return std::chrono::high_resolution_clock::now().time_since_epoch().count(); #endif - return nCounter; } void RandAddSeed() From 4a3c6ddc11177c2f77e2a3c575567fd4b768b4ba Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 5 May 2017 11:32:06 -0700 Subject: [PATCH 2/7] Test that GetPerformanceCounter() increments --- src/random.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/random.cpp b/src/random.cpp index 6fe03e69a..61c6f6f33 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #ifndef WIN32 #include @@ -294,6 +295,8 @@ FastRandomContext::FastRandomContext(const uint256& seed) : requires_seed(false) bool Random_SanityCheck() { + uint64_t start = GetPerformanceCounter(); + /* This does not measure the quality of randomness, but it does test that * OSRandom() overwrites all 32 bytes of the output given a maximum * number of tries. @@ -320,7 +323,14 @@ bool Random_SanityCheck() tries += 1; } while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES); - return (num_overwritten == NUM_OS_RANDOM_BYTES); /* If this failed, bailed out after too many tries */ + if (num_overwritten != NUM_OS_RANDOM_BYTES) return false; /* If this failed, bailed out after too many tries */ + + // Check that GetPerformanceCounter increases at least during a GetOSRand() call + 1ms sleep. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + uint64_t stop = GetPerformanceCounter(); + if (stop == start) return false; + + return true; } FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0) From 23bc7a072e71dca5838607e411c0158c9a62cae3 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 5 May 2017 11:45:37 -0700 Subject: [PATCH 3/7] Use sanity check timestamps as entropy Cherry-picked from: 2c0a6f157da3c6bb3b0a1e77f003caf0d9cb9d6c --- src/random.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/random.cpp b/src/random.cpp index 61c6f6f33..8e10bd77d 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -330,6 +330,10 @@ bool Random_SanityCheck() uint64_t stop = GetPerformanceCounter(); if (stop == start) return false; + // We called GetPerformanceCounter. Use it as entropy. + RAND_add((const unsigned char*)&start, sizeof(start), 1); + RAND_add((const unsigned char*)&stop, sizeof(stop), 1); + return true; } From 60bd38982387f0d8491c1ca1c8c7db3db504010d Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 9 May 2017 15:13:00 -0700 Subject: [PATCH 4/7] Use rdrand as entropy source on supported platforms Includes squashed commits: 5155d1101eb4fc9d4d797b583bb29f71807bd10b a9e82f6512662054f64ed2bde590b2bb0831fc9d 674848fe1c43fb88870cf5ba16fca4e2524da793 Cherry-picked from: cb24c8539d1098d1a61605b452ecfa11a693320d --- src/bench/bench_bitcoin.cpp | 2 ++ src/init.cpp | 1 + src/random.cpp | 69 +++++++++++++++++++++++++++++++++++++ src/random.h | 3 ++ src/test/test_bitcoin.cpp | 1 + 5 files changed, 76 insertions(+) diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index c4e3fd5d0..6e5ced4b3 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -5,10 +5,12 @@ #include "bench.h" // for BenchRunner #include "key.h" // for ECC_Start, ECC_Stop #include "util.h" // for SetupEnvironment, fPrintToDebugLog +#include "random.h" int main(int argc, char** argv) { + RandomInit(); ECC_Start(); SetupEnvironment(); fPrintToDebugLog = false; // don't want to write to debug.log file diff --git a/src/init.cpp b/src/init.cpp index a39713d4a..d6c224a10 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1188,6 +1188,7 @@ bool AppInitSanityChecks() // ********************************************************* Step 4: sanity checks // Initialize elliptic curve code + RandomInit(); ECC_Start(); globalVerifyHandle.reset(new ECCVerifyHandle()); diff --git a/src/random.cpp b/src/random.cpp index 8e10bd77d..46d046f71 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -37,6 +37,10 @@ #include #endif +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +#include +#endif + #include #include @@ -66,6 +70,61 @@ static inline int64_t GetPerformanceCounter() #endif } + +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +static std::atomic hwrand_initialized{false}; +static bool rdrand_supported = false; +static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000; +static void RDRandInit() +{ + uint32_t eax, ebx, ecx, edx; + 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; + } + hwrand_initialized.store(true); +} +#else +static void RDRandInit() {} +#endif + +static bool GetHWRand(unsigned char* ent32) { +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) + assert(hwrand_initialized.load(std::memory_order_relaxed)); + if (rdrand_supported) { + uint8_t ok; + // Not all assemblers support the rdrand instruction, write it in hex. +#ifdef __i386__ + for (int iter = 0; iter < 4; ++iter) { + uint32_t r1, r2; + __asm__ volatile (".byte 0x0f, 0xc7, 0xf0;" // rdrand %eax + ".byte 0x0f, 0xc7, 0xf2;" // rdrand %edx + "setc %2" : + "=a"(r1), "=d"(r2), "=q"(ok) :: "cc"); + if (!ok) return false; + WriteLE32(ent32 + 8 * iter, r1); + WriteLE32(ent32 + 8 * iter + 4, r2); + } +#else + uint64_t r1, r2, r3, r4; + __asm__ volatile (".byte 0x48, 0x0f, 0xc7, 0xf0, " // rdrand %rax + "0x48, 0x0f, 0xc7, 0xf3, " // rdrand %rbx + "0x48, 0x0f, 0xc7, 0xf1, " // rdrand %rcx + "0x48, 0x0f, 0xc7, 0xf2; " // rdrand %rdx + "setc %4" : + "=a"(r1), "=b"(r2), "=c"(r3), "=d"(r4), "=q"(ok) :: "cc"); + if (!ok) return false; + WriteLE64(ent32, r1); + WriteLE64(ent32 + 8, r2); + WriteLE64(ent32 + 16, r3); + WriteLE64(ent32 + 24, r4); +#endif + return true; + } +#endif + return false; +} + void RandAddSeed() { // Seed with CPU performance counter @@ -228,6 +287,11 @@ void GetStrongRandBytes(unsigned char* out, int num) GetOSRand(buf); hasher.Write(buf, 32); + // Third source: HW RNG, if available. + if (GetHWRand(buf)) { + hasher.Write(buf, 32); + } + // Produce output hasher.Finalize(buf); memcpy(out, buf, num); @@ -345,3 +409,8 @@ FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDete uint256 seed; rng.SetKey(seed.begin(), 32); } + +void RandomInit() +{ + RDRandInit(); +} diff --git a/src/random.h b/src/random.h index f8e2e0dc5..b35cf51a1 100644 --- a/src/random.h +++ b/src/random.h @@ -133,4 +133,7 @@ void GetOSRand(unsigned char *ent32); */ bool Random_SanityCheck(); +/** Initialize the RNG. */ +void RandomInit(); + #endif // BITCOIN_RANDOM_H diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index b85f61132..a3459726c 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -43,6 +43,7 @@ static const int COINBASE_MATURITY = 60*4; // 4 hours of blocks BasicTestingSetup::BasicTestingSetup(const std::string& chainName) { + RandomInit(); ECC_Start(); SetupEnvironment(); SetupNetworking(); From 90d1972d817f243aa9249654b92b1f83c3a1f3b0 Mon Sep 17 00:00:00 2001 From: Dag Robole Date: Sat, 15 Jul 2017 21:34:52 +0200 Subject: [PATCH 5/7] Fix resource leak Cherry-picked from: a8ae0b252a2007568e77f5aca1c7fa3ec5941b72 --- src/random.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/random.cpp b/src/random.cpp index 46d046f71..267e35eef 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -187,6 +187,7 @@ void GetDevURandom(unsigned char *ent32) do { ssize_t n = read(f, ent32 + have, NUM_OS_RANDOM_BYTES - have); if (n <= 0 || n + have > NUM_OS_RANDOM_BYTES) { + close(f); RandFailure(); } have += n; From 3606c85c7dfc1db73242678240f0cf9eb12df50f Mon Sep 17 00:00:00 2001 From: Aaron Clauson Date: Fri, 10 Nov 2017 07:06:49 +1100 Subject: [PATCH 6/7] Minimal code changes to allow msvc compilation. -add msvc ssize_t definition in compat.h -change NUM_OS_RANDOM_BYTES to int from ssize_t in random.h Includes squashed commit: f7dc99244c8e78dbd0196f612690efcc449c37dc Inspired by: fbf327b13868861c2877c5754caf5a9816f2603c --- src/compat.h | 6 ++++++ src/random.h | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/compat.h b/src/compat.h index d4dc3ba99..d9253c867 100644 --- a/src/compat.h +++ b/src/compat.h @@ -75,6 +75,12 @@ typedef u_int SOCKET; #define MAX_PATH 1024 #endif +// ssize_t is POSIX, and not present when using MSVC. +#ifdef _MSC_VER +#include +typedef SSIZE_T ssize_t; +#endif + // As Solaris does not have the MSG_NOSIGNAL flag for send(2) syscall, it is defined as 0 #if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 diff --git a/src/random.h b/src/random.h index b35cf51a1..6503d8359 100644 --- a/src/random.h +++ b/src/random.h @@ -121,7 +121,7 @@ public: * sure that the underlying OS APIs for all platforms support the number. * (many cap out at 256 bytes). */ -static const ssize_t NUM_OS_RANDOM_BYTES = 32; +static const int NUM_OS_RANDOM_BYTES = 32; /** Get 32 bytes of system entropy. Do not use this in application code: use * GetStrongRandBytes instead. From 47b48521bbfb110f1c8b69209d2e675ca4c576d0 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 20 Mar 2018 19:10:39 -0700 Subject: [PATCH 7/7] Make FastRandomContext support standard C++11 RNG interface This makes it possible to plug it into the various standard C++11 random distribution algorithms and other functions like std::shuffle. Cherry-picked from: 1ec1602a4549f6b68586cead8eff701bceb624f5 --- src/random.h | 7 +++++++ src/test/random_tests.cpp | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/random.h b/src/random.h index 6503d8359..b14028a43 100644 --- a/src/random.h +++ b/src/random.h @@ -11,6 +11,7 @@ #include "uint256.h" #include +#include /* Seed OpenSSL PRNG with additional entropy data */ void RandAddSeed(); @@ -114,6 +115,12 @@ public: /** Generate a random boolean. */ bool randbool() { return randbits(1); } + + // Compatibility with the C++11 UniformRandomBitGenerator concept + typedef uint64_t result_type; + static constexpr uint64_t min() { return 0; } + static constexpr uint64_t max() { return std::numeric_limits::max(); } + inline uint64_t operator()() { return rand64(); } }; /* Number of random bytes returned by GetOSRand. diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp index 132e19005..1f5c99552 100644 --- a/src/test/random_tests.cpp +++ b/src/test/random_tests.cpp @@ -8,6 +8,9 @@ #include +#include +#include + BOOST_FIXTURE_TEST_SUITE(random_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(osrandom_tests) @@ -57,4 +60,23 @@ BOOST_AUTO_TEST_CASE(fastrandom_randbits) } } +/** Does-it-compile test for compatibility with standard C++11 RNG interface. */ +BOOST_AUTO_TEST_CASE(stdrandom_test) +{ + FastRandomContext ctx; + std::uniform_int_distribution distribution(3, 9); + for (int i = 0; i < 100; ++i) { + int x = distribution(ctx); + BOOST_CHECK(x >= 3); + BOOST_CHECK(x <= 9); + + std::vector test{1,2,3,4,5,6,7,8,9,10}; + std::shuffle(test.begin(), test.end(), ctx); + for (int j = 1; j <= 10; ++j) { + BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end()); + } + } + +} + BOOST_AUTO_TEST_SUITE_END()