Skip to content

Commit

Permalink
Merge pull request dogecoin#3521 from xanimo/1.15.0-dev-next-random-p…
Browse files Browse the repository at this point in the history
…re-14955

backport: random.h/.cpp cherry-picks
  • Loading branch information
patricklodder authored Jun 20, 2024
2 parents 5ac4640 + 47b4852 commit da22f33
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 9 deletions.
2 changes: 2 additions & 0 deletions src/bench/bench_bitcoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions src/compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,7 @@ bool AppInitSanityChecks()
// ********************************************************* Step 4: sanity checks

// Initialize elliptic curve code
RandomInit();
ECC_Start();
globalVerifyHandle.reset(new ECCVerifyHandle());

Expand Down
108 changes: 100 additions & 8 deletions src/random.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#include <stdlib.h>
#include <limits>
#include <chrono>
#include <thread>

#ifndef WIN32
#include <sys/time.h>
Expand All @@ -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>

Expand All @@ -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
// Fall back to using C++11 clock (usually microsecond or nanosecond precision)
return std::chrono::high_resolution_clock::now().time_since_epoch().count();
#endif
}


#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
timeval t;
gettimeofday(&t, NULL);
nCounter = (int64_t)(t.tv_sec * 1000000 + t.tv_usec);
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 nCounter;
return false;
}

void RandAddSeed()
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand All @@ -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)
Expand All @@ -323,3 +410,8 @@ FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDete
uint256 seed;
rng.SetKey(seed.begin(), 32);
}

void RandomInit()
{
RDRandInit();
}
12 changes: 11 additions & 1 deletion src/random.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "uint256.h"

#include <stdint.h>
#include <limits>

/* Seed OpenSSL PRNG with additional entropy data */
void RandAddSeed();
Expand Down Expand Up @@ -114,14 +115,20 @@ class FastRandomContext {

/** 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.
* When changing this constant make sure to change all call sites, and make
* 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.
Expand All @@ -133,4 +140,7 @@ void GetOSRand(unsigned char *ent32);
*/
bool Random_SanityCheck();

/** Initialize the RNG. */
void RandomInit();

#endif // BITCOIN_RANDOM_H
22 changes: 22 additions & 0 deletions src/test/random_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()
1 change: 1 addition & 0 deletions src/test/test_bitcoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit da22f33

Please sign in to comment.