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:
Old Dip Tracker 2024-06-20 15:20:35 +02:00 committed by GitHub
commit da22f3312d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 143 additions and 9 deletions

View File

@ -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

View 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

View File

@ -1188,6 +1188,7 @@ bool AppInitSanityChecks()
// ********************************************************* Step 4: sanity checks
// Initialize elliptic curve code
RandomInit();
ECC_Start();
globalVerifyHandle.reset(new ECCVerifyHandle());

View File

@ -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();
}

View File

@ -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

View File

@ -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()

View File

@ -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();