mirror of
https://github.com/dogecoin/dogecoin.git
synced 2026-01-31 10:30:52 +00:00
Merge pull request #3521 from xanimo/1.15.0-dev-next-random-pre-14955
backport: random.h/.cpp cherry-picks
This commit is contained in:
commit
da22f3312d
@ -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
|
||||
|
||||
@ -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 <BaseTsd.h>
|
||||
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
|
||||
|
||||
@ -1188,6 +1188,7 @@ bool AppInitSanityChecks()
|
||||
// ********************************************************* Step 4: sanity checks
|
||||
|
||||
// Initialize elliptic curve code
|
||||
RandomInit();
|
||||
ECC_Start();
|
||||
globalVerifyHandle.reset(new ECCVerifyHandle());
|
||||
|
||||
|
||||
108
src/random.cpp
108
src/random.cpp
@ -16,6 +16,8 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <limits>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/time.h>
|
||||
@ -35,6 +37,10 @@
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
@ -46,15 +52,77 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
|
||||
static std::atomic<bool> 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()
|
||||
@ -119,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;
|
||||
@ -219,6 +288,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);
|
||||
@ -286,6 +360,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.
|
||||
@ -312,7 +388,18 @@ 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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0)
|
||||
@ -323,3 +410,8 @@ FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDete
|
||||
uint256 seed;
|
||||
rng.SetKey(seed.begin(), 32);
|
||||
}
|
||||
|
||||
void RandomInit()
|
||||
{
|
||||
RDRandInit();
|
||||
}
|
||||
|
||||
12
src/random.h
12
src/random.h
@ -11,6 +11,7 @@
|
||||
#include "uint256.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <limits>
|
||||
|
||||
/* 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<uint64_t>::max(); }
|
||||
inline uint64_t operator()() { return rand64(); }
|
||||
};
|
||||
|
||||
/* Number of random bytes returned by GetOSRand.
|
||||
@ -121,7 +128,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.
|
||||
@ -133,4 +140,7 @@ void GetOSRand(unsigned char *ent32);
|
||||
*/
|
||||
bool Random_SanityCheck();
|
||||
|
||||
/** Initialize the RNG. */
|
||||
void RandomInit();
|
||||
|
||||
#endif // BITCOIN_RANDOM_H
|
||||
|
||||
@ -8,6 +8,9 @@
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
|
||||
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<int> distribution(3, 9);
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
int x = distribution(ctx);
|
||||
BOOST_CHECK(x >= 3);
|
||||
BOOST_CHECK(x <= 9);
|
||||
|
||||
std::vector<int> 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()
|
||||
|
||||
@ -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();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user