aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bench/bench_bitcoin.cpp2
-rw-r--r--src/crypto/sha512.h2
-rw-r--r--src/qt/test/paymentservertests.cpp6
-rw-r--r--src/random.cpp360
-rw-r--r--src/random.h105
-rw-r--r--src/scheduler.cpp2
-rw-r--r--src/test/test_bitcoin.cpp1
-rw-r--r--src/util/system.cpp51
8 files changed, 344 insertions, 185 deletions
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 32faba86b4..b804a84478 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -6,7 +6,6 @@
#include <crypto/sha256.h>
#include <key.h>
-#include <random.h>
#include <util/system.h>
#include <util/strencodings.h>
#include <validation.h>
@@ -67,7 +66,6 @@ int main(int argc, char** argv)
const fs::path bench_datadir{SetDataDir()};
SHA256AutoDetect();
- RandomInit();
ECC_Start();
SetupEnvironment();
diff --git a/src/crypto/sha512.h b/src/crypto/sha512.h
index cd1023bc85..4118ac1b18 100644
--- a/src/crypto/sha512.h
+++ b/src/crypto/sha512.h
@@ -17,7 +17,7 @@ private:
uint64_t bytes;
public:
- static const size_t OUTPUT_SIZE = 64;
+ static constexpr size_t OUTPUT_SIZE = 64;
CSHA512();
CSHA512& Write(const unsigned char* data, size_t len);
diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp
index 94907595f5..f0eca899fc 100644
--- a/src/qt/test/paymentservertests.cpp
+++ b/src/qt/test/paymentservertests.cpp
@@ -181,12 +181,12 @@ void PaymentServerTests::paymentServerTests()
QCOMPARE(PaymentServer::verifyExpired(r.paymentRequest.getDetails()), true);
// Test BIP70 DoS protection:
- unsigned char randData[BIP70_MAX_PAYMENTREQUEST_SIZE + 1];
- GetRandBytes(randData, sizeof(randData));
+ auto randdata = FastRandomContext().randbytes(BIP70_MAX_PAYMENTREQUEST_SIZE + 1);
+
// Write data to a temp file:
QTemporaryFile tempFile;
tempFile.open();
- tempFile.write((const char*)randData, sizeof(randData));
+ tempFile.write((const char*)randdata.data(), randdata.size());
tempFile.close();
// compares 50001 <= BIP70_MAX_PAYMENTREQUEST_SIZE == false
QCOMPARE(PaymentServer::verifySize(tempFile.size()), false);
diff --git a/src/random.cpp b/src/random.cpp
index f8ffda136d..3b7f7910b0 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -19,6 +19,8 @@
#include <chrono>
#include <thread>
+#include <support/allocators/secure.h>
+
#ifndef WIN32
#include <fcntl.h>
#include <sys/time.h>
@@ -47,6 +49,7 @@
#include <openssl/err.h>
#include <openssl/rand.h>
+#include <openssl/conf.h>
[[noreturn]] static void RandFailure()
{
@@ -54,7 +57,7 @@
std::abort();
}
-static inline int64_t GetPerformanceCounter()
+static inline int64_t GetPerformanceCounter() noexcept
{
// Read the hardware time stamp counter when available.
// See https://en.wikipedia.org/wiki/Time_Stamp_Counter for more information.
@@ -74,27 +77,38 @@ static inline int64_t GetPerformanceCounter()
#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()
+static void InitHardwareRand()
{
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);
}
+
+static void ReportHardwareRand()
+{
+ 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
-static void RDRandInit() {}
+/* Access to other hardware random number generators could be added here later,
+ * assuming it is sufficiently fast (in the order of a few hundred CPU cycles).
+ * Slower sources should probably be invoked separately, and/or only from
+ * RandAddSeedSleep (which is called during idle background operation).
+ */
+static void InitHardwareRand() {}
+static void ReportHardwareRand() {}
#endif
-static bool GetHWRand(unsigned char* ent32) {
+static bool GetHardwareRand(unsigned char* ent32) noexcept {
#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.
@@ -129,18 +143,8 @@ static bool GetHWRand(unsigned char* ent32) {
return false;
}
-void RandAddSeed()
+static void RandAddSeedPerfmon(CSHA512& hasher)
{
- // Seed with CPU performance counter
- int64_t nCounter = GetPerformanceCounter();
- RAND_add(&nCounter, sizeof(nCounter), 1.5);
- memory_cleanse((void*)&nCounter, sizeof(nCounter));
-}
-
-static void RandAddSeedPerfmon()
-{
- RandAddSeed();
-
#ifdef WIN32
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
// Seed with the entire set of perfmon data
@@ -164,15 +168,15 @@ static void RandAddSeedPerfmon()
}
RegCloseKey(HKEY_PERFORMANCE_DATA);
if (ret == ERROR_SUCCESS) {
- RAND_add(vData.data(), nSize, nSize / 100.0);
+ hasher.Write(vData.data(), nSize);
memory_cleanse(vData.data(), nSize);
- LogPrint(BCLog::RAND, "%s: %lu bytes\n", __func__, nSize);
} else {
- static bool warned = false; // Warn only once
- if (!warned) {
- LogPrintf("%s: Warning: RegQueryValueExA(HKEY_PERFORMANCE_DATA) failed with code %i\n", __func__, ret);
- warned = true;
- }
+ // Performance data is only a best-effort attempt at improving the
+ // situation when the OS randomness (and other sources) aren't
+ // adequate. As a result, failure to read it is isn't considered critical,
+ // so we don't call RandFailure().
+ // TODO: Add logging when the logger is made functional before global
+ // constructors have been invoked.
}
#endif
}
@@ -272,106 +276,255 @@ void GetOSRand(unsigned char *ent32)
#endif
}
-void GetRandBytes(unsigned char* buf, int num)
+void LockingCallbackOpenSSL(int mode, int i, const char* file, int line);
+
+namespace {
+
+class RNGState {
+ Mutex m_mutex;
+ /* The RNG state consists of 256 bits of entropy, taken from the output of
+ * one operation's SHA512 output, and fed as input to the next one.
+ * Carrying 256 bits of entropy should be sufficient to guarantee
+ * unpredictability as long as any entropy source was ever unpredictable
+ * to an attacker. To protect against situations where an attacker might
+ * observe the RNG's state, fresh entropy is always mixed when
+ * GetStrongRandBytes is called.
+ */
+ unsigned char m_state[32] GUARDED_BY(m_mutex) = {0};
+ uint64_t m_counter GUARDED_BY(m_mutex) = 0;
+ bool m_strongly_seeded GUARDED_BY(m_mutex) = false;
+ std::unique_ptr<Mutex[]> m_mutex_openssl;
+
+public:
+ RNGState() noexcept
+ {
+ InitHardwareRand();
+
+ // Init OpenSSL library multithreading support
+ m_mutex_openssl.reset(new Mutex[CRYPTO_num_locks()]);
+ CRYPTO_set_locking_callback(LockingCallbackOpenSSL);
+
+ // OpenSSL can optionally load a config file which lists optional loadable modules and engines.
+ // We don't use them so we don't require the config. However some of our libs may call functions
+ // which attempt to load the config file, possibly resulting in an exit() or crash if it is missing
+ // or corrupt. Explicitly tell OpenSSL not to try to load the file. The result for our libs will be
+ // that the config appears to have been loaded and there are no modules/engines available.
+ OPENSSL_no_config();
+ }
+
+ ~RNGState()
+ {
+ // Securely erase the memory used by the OpenSSL PRNG
+ RAND_cleanup();
+ // Shutdown OpenSSL library multithreading support
+ CRYPTO_set_locking_callback(nullptr);
+ }
+
+ /** Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher.
+ *
+ * If this function has never been called with strong_seed = true, false is returned.
+ */
+ bool MixExtract(unsigned char* out, size_t num, CSHA512&& hasher, bool strong_seed) noexcept
+ {
+ assert(num <= 32);
+ unsigned char buf[64];
+ static_assert(sizeof(buf) == CSHA512::OUTPUT_SIZE, "Buffer needs to have hasher's output size");
+ bool ret;
+ {
+ LOCK(m_mutex);
+ ret = (m_strongly_seeded |= strong_seed);
+ // Write the current state of the RNG into the hasher
+ hasher.Write(m_state, 32);
+ // Write a new counter number into the state
+ hasher.Write((const unsigned char*)&m_counter, sizeof(m_counter));
+ ++m_counter;
+ // Finalize the hasher
+ hasher.Finalize(buf);
+ // Store the last 32 bytes of the hash output as new RNG state.
+ memcpy(m_state, buf + 32, 32);
+ }
+ // If desired, copy (up to) the first 32 bytes of the hash output as output.
+ if (num) {
+ assert(out != nullptr);
+ memcpy(out, buf, num);
+ }
+ // Best effort cleanup of internal state
+ hasher.Reset();
+ memory_cleanse(buf, 64);
+ return ret;
+ }
+
+ Mutex& GetOpenSSLMutex(int i) { return m_mutex_openssl[i]; }
+};
+
+RNGState& GetRNGState() noexcept
{
- if (RAND_bytes(buf, num) != 1) {
- RandFailure();
+ // 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::vector<RNGState, secure_allocator<RNGState>> g_rng(1);
+ return g_rng[0];
+}
+}
+
+void LockingCallbackOpenSSL(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS
+{
+ RNGState& rng = GetRNGState();
+
+ if (mode & CRYPTO_LOCK) {
+ rng.GetOpenSSLMutex(i).lock();
+ } else {
+ rng.GetOpenSSLMutex(i).unlock();
}
}
-static void AddDataToRng(void* data, size_t len);
+/* A note on the use of noexcept in the seeding functions below:
+ *
+ * None of the RNG code should ever throw any exception, with the sole exception
+ * of MilliSleep in SeedSleep, which can (and does) support interruptions which
+ * cause a boost::thread_interrupted to be thrown.
+ *
+ * This means that SeedSleep, and all functions that invoke it are throwing.
+ * However, we know that GetRandBytes() and GetStrongRandBytes() never trigger
+ * this sleeping logic, so they are noexcept. The same is true for all the
+ * GetRand*() functions that use GetRandBytes() indirectly.
+ *
+ * TODO: After moving away from interruptible boost-based thread management,
+ * everything can become noexcept here.
+ */
-void RandAddSeedSleep()
+static void SeedTimestamp(CSHA512& hasher) noexcept
{
- int64_t nPerfCounter1 = GetPerformanceCounter();
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
- int64_t nPerfCounter2 = GetPerformanceCounter();
+ int64_t perfcounter = GetPerformanceCounter();
+ hasher.Write((const unsigned char*)&perfcounter, sizeof(perfcounter));
+}
- // Combine with and update state
- AddDataToRng(&nPerfCounter1, sizeof(nPerfCounter1));
- AddDataToRng(&nPerfCounter2, sizeof(nPerfCounter2));
+static void SeedFast(CSHA512& hasher) noexcept
+{
+ unsigned char buffer[32];
+
+ // Stack pointer to indirectly commit to thread/callstack
+ const unsigned char* ptr = buffer;
+ hasher.Write((const unsigned char*)&ptr, sizeof(ptr));
+
+ // Hardware randomness is very fast when available; use it always.
+ bool have_hw_rand = GetHardwareRand(buffer);
+ if (have_hw_rand) hasher.Write(buffer, sizeof(buffer));
- memory_cleanse(&nPerfCounter1, sizeof(nPerfCounter1));
- memory_cleanse(&nPerfCounter2, sizeof(nPerfCounter2));
+ // High-precision timestamp
+ SeedTimestamp(hasher);
}
+static void SeedSlow(CSHA512& hasher) noexcept
+{
+ unsigned char buffer[32];
-static Mutex cs_rng_state;
-static unsigned char rng_state[32] = {0};
-static uint64_t rng_counter = 0;
+ // Everything that the 'fast' seeder includes
+ SeedFast(hasher);
-static void AddDataToRng(void* data, size_t len) {
- CSHA512 hasher;
- hasher.Write((const unsigned char*)&len, sizeof(len));
- hasher.Write((const unsigned char*)data, len);
- unsigned char buf[64];
- {
- WAIT_LOCK(cs_rng_state, lock);
- hasher.Write(rng_state, sizeof(rng_state));
- hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter));
- ++rng_counter;
- hasher.Finalize(buf);
- memcpy(rng_state, buf + 32, 32);
- }
- memory_cleanse(buf, 64);
+ // OS randomness
+ GetOSRand(buffer);
+ hasher.Write(buffer, sizeof(buffer));
+
+ // OpenSSL RNG (for now)
+ RAND_bytes(buffer, sizeof(buffer));
+ hasher.Write(buffer, sizeof(buffer));
+
+ // High-precision timestamp.
+ //
+ // Note that we also commit to a timestamp in the Fast seeder, so we indirectly commit to a
+ // benchmark of all the entropy gathering sources in this function).
+ SeedTimestamp(hasher);
}
-void GetStrongRandBytes(unsigned char* out, int num)
+static void SeedSleep(CSHA512& hasher)
{
- assert(num <= 32);
- CSHA512 hasher;
- unsigned char buf[64];
+ // Everything that the 'fast' seeder includes
+ SeedFast(hasher);
+
+ // High-precision timestamp
+ SeedTimestamp(hasher);
+
+ // Sleep for 1ms
+ MilliSleep(1);
+
+ // High-precision timestamp after sleeping (as we commit to both the time before and after, this measures the delay)
+ SeedTimestamp(hasher);
- // First source: OpenSSL's RNG
- RandAddSeedPerfmon();
- GetRandBytes(buf, 32);
- hasher.Write(buf, 32);
+ // Windows performance monitor data (once every 10 minutes)
+ RandAddSeedPerfmon(hasher);
+}
+
+static void SeedStartup(CSHA512& hasher) noexcept
+{
+#ifdef WIN32
+ RAND_screen();
+#endif
- // Second source: OS RNG
- GetOSRand(buf);
- hasher.Write(buf, 32);
+ // Everything that the 'slow' seeder includes.
+ SeedSlow(hasher);
+
+ // Windows performance monitor data.
+ RandAddSeedPerfmon(hasher);
+}
+
+enum class RNGLevel {
+ FAST, //!< Automatically called by GetRandBytes
+ SLOW, //!< Automatically called by GetStrongRandBytes
+ SLEEP, //!< Called by RandAddSeedSleep()
+};
+
+static void ProcRand(unsigned char* out, int num, RNGLevel level)
+{
+ // Make sure the RNG is initialized first (as all Seed* function possibly need hwrand to be available).
+ RNGState& rng = GetRNGState();
- // Third source: HW RNG, if available.
- if (GetHWRand(buf)) {
- hasher.Write(buf, 32);
+ assert(num <= 32);
+
+ CSHA512 hasher;
+ switch (level) {
+ case RNGLevel::FAST:
+ SeedFast(hasher);
+ break;
+ case RNGLevel::SLOW:
+ SeedSlow(hasher);
+ break;
+ case RNGLevel::SLEEP:
+ SeedSleep(hasher);
+ break;
}
// Combine with and update state
- {
- WAIT_LOCK(cs_rng_state, lock);
- hasher.Write(rng_state, sizeof(rng_state));
- hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter));
- ++rng_counter;
- hasher.Finalize(buf);
- memcpy(rng_state, buf + 32, 32);
+ if (!rng.MixExtract(out, num, std::move(hasher), false)) {
+ // On the first invocation, also seed with SeedStartup().
+ CSHA512 startup_hasher;
+ SeedStartup(startup_hasher);
+ rng.MixExtract(out, num, std::move(startup_hasher), true);
}
- // Produce output
- memcpy(out, buf, num);
- memory_cleanse(buf, 64);
+ // For anything but the 'fast' level, feed the resulting RNG output (after an additional hashing step) back into OpenSSL.
+ if (level != RNGLevel::FAST) {
+ unsigned char buf[64];
+ CSHA512().Write(out, num).Finalize(buf);
+ RAND_add(buf, sizeof(buf), num);
+ memory_cleanse(buf, 64);
+ }
}
-uint64_t GetRand(uint64_t nMax)
-{
- if (nMax == 0)
- return 0;
+void GetRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::FAST); }
+void GetStrongRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::SLOW); }
+void RandAddSeedSleep() { ProcRand(nullptr, 0, RNGLevel::SLEEP); }
- // The range of the random source must be a multiple of the modulus
- // to give every possible output value an equal possibility
- uint64_t nRange = (std::numeric_limits<uint64_t>::max() / nMax) * nMax;
- uint64_t nRand = 0;
- do {
- GetRandBytes((unsigned char*)&nRand, sizeof(nRand));
- } while (nRand >= nRange);
- return (nRand % nMax);
+uint64_t GetRand(uint64_t nMax) noexcept
+{
+ return FastRandomContext().randrange(nMax);
}
-int GetRandInt(int nMax)
+int GetRandInt(int nMax) noexcept
{
return GetRand(nMax);
}
-uint256 GetRandHash()
+uint256 GetRandHash() noexcept
{
uint256 hash;
GetRandBytes((unsigned char*)&hash, sizeof(hash));
@@ -385,7 +538,7 @@ void FastRandomContext::RandomSeed()
requires_seed = false;
}
-uint256 FastRandomContext::rand256()
+uint256 FastRandomContext::rand256() noexcept
{
if (bytebuf_size < 32) {
FillByteBuffer();
@@ -406,7 +559,7 @@ std::vector<unsigned char> FastRandomContext::randbytes(size_t len)
return ret;
}
-FastRandomContext::FastRandomContext(const uint256& seed) : requires_seed(false), bytebuf_size(0), bitbuf_size(0)
+FastRandomContext::FastRandomContext(const uint256& seed) noexcept : requires_seed(false), bytebuf_size(0), bitbuf_size(0)
{
rng.SetKey(seed.begin(), 32);
}
@@ -449,13 +602,15 @@ bool Random_SanityCheck()
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);
+ CSHA512 to_add;
+ to_add.Write((const unsigned char*)&start, sizeof(start));
+ to_add.Write((const unsigned char*)&stop, sizeof(stop));
+ GetRNGState().MixExtract(nullptr, 0, std::move(to_add), false);
return true;
}
-FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0)
+FastRandomContext::FastRandomContext(bool fDeterministic) noexcept : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0)
{
if (!fDeterministic) {
return;
@@ -480,5 +635,8 @@ FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexce
void RandomInit()
{
- RDRandInit();
+ // Invoke RNG code to trigger initialization (if not already performed)
+ ProcRand(nullptr, 0, RNGLevel::FAST);
+
+ ReportHardwareRand();
}
diff --git a/src/random.h b/src/random.h
index 00e90abbc5..4c73f3822a 100644
--- a/src/random.h
+++ b/src/random.h
@@ -13,33 +13,83 @@
#include <stdint.h>
#include <limits>
-/* Seed OpenSSL PRNG with additional entropy data */
-void RandAddSeed();
+/**
+ * Overall design of the RNG and entropy sources.
+ *
+ * We maintain a single global 256-bit RNG state for all high-quality randomness.
+ * The following (classes of) functions interact with that state by mixing in new
+ * entropy, and optionally extracting random output from it:
+ *
+ * - The GetRand*() class of functions, as well as construction of FastRandomContext objects,
+ * perform 'fast' seeding, consisting of mixing in:
+ * - A stack pointer (indirectly committing to calling thread and call stack)
+ * - A high-precision timestamp (rdtsc when available, c++ high_resolution_clock otherwise)
+ * - Hardware RNG (rdrand) when available.
+ * These entropy sources are very fast, and only designed to protect against situations
+ * where a VM state restore/copy results in multiple systems with the same randomness.
+ * FastRandomContext on the other hand does not protect against this once created, but
+ * is even faster (and acceptable to use inside tight loops).
+ *
+ * - The GetStrongRand*() class of function perform 'slow' seeding, including everything
+ * that fast seeding includes, but additionally:
+ * - OS entropy (/dev/urandom, getrandom(), ...). The application will terminate if
+ * this entropy source fails.
+ * - Bytes from OpenSSL's RNG (which itself may be seeded from various sources)
+ * - Another high-precision timestamp (indirectly committing to a benchmark of all the
+ * previous sources).
+ * These entropy sources are slower, but designed to make sure the RNG state contains
+ * fresh data that is unpredictable to attackers.
+ *
+ * - RandAddSeedSleep() seeds everything that fast seeding includes, but additionally:
+ * - A high-precision timestamp before and after sleeping 1ms.
+ * - (On Windows) Once every 10 minutes, performance monitoring data from the OS.
+ * These just exploit the fact the system is idle to improve the quality of the RNG
+ * slightly.
+ *
+ * On first use of the RNG (regardless of what function is called first), all entropy
+ * sources used in the 'slow' seeder are included, but also:
+ * - (On Windows) Performance monitoring data from the OS.
+ * - (On Windows) Through OpenSSL, the screen contents.
+ *
+ * When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and
+ * (up to) the first 32 bytes of H are produced as output, while the last 32 bytes
+ * become the new RNG state.
+*/
/**
- * Functions to gather random data via the OpenSSL PRNG
+ * Generate random data via the internal PRNG.
+ *
+ * These functions are designed to be fast (sub microsecond), but do not necessarily
+ * meaningfully add entropy to the PRNG state.
+ *
+ * Thread-safe.
*/
-void GetRandBytes(unsigned char* buf, int num);
-uint64_t GetRand(uint64_t nMax);
-int GetRandInt(int nMax);
-uint256 GetRandHash();
+void GetRandBytes(unsigned char* buf, int num) noexcept;
+uint64_t GetRand(uint64_t nMax) noexcept;
+int GetRandInt(int nMax) noexcept;
+uint256 GetRandHash() noexcept;
/**
- * Add a little bit of randomness to the output of GetStrongRangBytes.
- * This sleeps for a millisecond, so should only be called when there is
- * no other work to be done.
+ * Gather entropy from various sources, feed it into the internal PRNG, and
+ * generate random data using it.
+ *
+ * This function will cause failure whenever the OS RNG fails.
+ *
+ * Thread-safe.
*/
-void RandAddSeedSleep();
+void GetStrongRandBytes(unsigned char* buf, int num) noexcept;
/**
- * Function to gather random data from multiple sources, failing whenever any
- * of those sources fail to provide a result.
+ * Sleep for 1ms, gather entropy from various sources, and feed them to the PRNG state.
+ *
+ * Thread-safe.
*/
-void GetStrongRandBytes(unsigned char* buf, int num);
+void RandAddSeedSleep();
/**
* Fast randomness source. This is seeded once with secure random data, but
- * is completely deterministic and insecure after that.
+ * is completely deterministic and does not gather more entropy after that.
+ *
* This class is not thread-safe.
*/
class FastRandomContext {
@@ -71,10 +121,10 @@ private:
}
public:
- explicit FastRandomContext(bool fDeterministic = false);
+ explicit FastRandomContext(bool fDeterministic = false) noexcept;
/** Initialize with explicit seed (only for testing) */
- explicit FastRandomContext(const uint256& seed);
+ explicit FastRandomContext(const uint256& seed) noexcept;
// Do not permit copying a FastRandomContext (move it, or create a new one to get reseeded).
FastRandomContext(const FastRandomContext&) = delete;
@@ -85,7 +135,7 @@ public:
FastRandomContext& operator=(FastRandomContext&& from) noexcept;
/** Generate a random 64-bit integer. */
- uint64_t rand64()
+ uint64_t rand64() noexcept
{
if (bytebuf_size < 8) FillByteBuffer();
uint64_t ret = ReadLE64(bytebuf + 64 - bytebuf_size);
@@ -94,7 +144,7 @@ public:
}
/** Generate a random (bits)-bit integer. */
- uint64_t randbits(int bits) {
+ uint64_t randbits(int bits) noexcept {
if (bits == 0) {
return 0;
} else if (bits > 32) {
@@ -109,7 +159,7 @@ public:
}
/** Generate a random integer in the range [0..range). */
- uint64_t randrange(uint64_t range)
+ uint64_t randrange(uint64_t range) noexcept
{
--range;
int bits = CountBits(range);
@@ -123,19 +173,19 @@ public:
std::vector<unsigned char> randbytes(size_t len);
/** Generate a random 32-bit integer. */
- uint32_t rand32() { return randbits(32); }
+ uint32_t rand32() noexcept { return randbits(32); }
/** generate a random uint256. */
- uint256 rand256();
+ uint256 rand256() noexcept;
/** Generate a random boolean. */
- bool randbool() { return randbits(1); }
+ bool randbool() noexcept { 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(); }
+ inline uint64_t operator()() noexcept { return rand64(); }
};
/** More efficient than using std::shuffle on a FastRandomContext.
@@ -178,7 +228,12 @@ void GetOSRand(unsigned char *ent32);
*/
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();
#endif // BITCOIN_RANDOM_H
diff --git a/src/scheduler.cpp b/src/scheduler.cpp
index b2da62fc75..fdc859b3a0 100644
--- a/src/scheduler.cpp
+++ b/src/scheduler.cpp
@@ -41,7 +41,7 @@ void CScheduler::serviceQueue()
try {
if (!shouldStop() && taskQueue.empty()) {
reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
- // Use this chance to get a tiny bit more entropy
+ // Use this chance to get more entropy
RandAddSeedSleep();
}
while (!shouldStop() && taskQueue.empty()) {
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index ad7fa01710..0c3fb7c398 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -35,7 +35,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))))
{
SHA256AutoDetect();
- RandomInit();
ECC_Start();
SetupEnvironment();
SetupNetworking();
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 3ef8111b32..06317a3a90 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -73,9 +73,6 @@
#include <malloc.h>
#endif
-#include <openssl/crypto.h>
-#include <openssl/rand.h>
-#include <openssl/conf.h>
#include <thread>
// Application startup time (used for uptime calculation)
@@ -86,54 +83,6 @@ const char * const BITCOIN_PID_FILENAME = "bitcoind.pid";
ArgsManager gArgs;
-/** Init OpenSSL library multithreading support */
-static std::unique_ptr<CCriticalSection[]> ppmutexOpenSSL;
-void locking_callback(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS
-{
- if (mode & CRYPTO_LOCK) {
- ENTER_CRITICAL_SECTION(ppmutexOpenSSL[i]);
- } else {
- LEAVE_CRITICAL_SECTION(ppmutexOpenSSL[i]);
- }
-}
-
-// Singleton for wrapping OpenSSL setup/teardown.
-class CInit
-{
-public:
- CInit()
- {
- // Init OpenSSL library multithreading support
- ppmutexOpenSSL.reset(new CCriticalSection[CRYPTO_num_locks()]);
- CRYPTO_set_locking_callback(locking_callback);
-
- // OpenSSL can optionally load a config file which lists optional loadable modules and engines.
- // We don't use them so we don't require the config. However some of our libs may call functions
- // which attempt to load the config file, possibly resulting in an exit() or crash if it is missing
- // or corrupt. Explicitly tell OpenSSL not to try to load the file. The result for our libs will be
- // that the config appears to have been loaded and there are no modules/engines available.
- OPENSSL_no_config();
-
-#ifdef WIN32
- // Seed OpenSSL PRNG with current contents of the screen
- RAND_screen();
-#endif
-
- // Seed OpenSSL PRNG with performance counter
- RandAddSeed();
- }
- ~CInit()
- {
- // Securely erase the memory used by the PRNG
- RAND_cleanup();
- // Shutdown OpenSSL library multithreading support
- CRYPTO_set_locking_callback(nullptr);
- // Clear the set of locks now to maintain symmetry with the constructor.
- ppmutexOpenSSL.reset();
- }
-}
-instance_of_cinit;
-
/** A map that contains all the currently held directory locks. After
* successful locking, these will be held here until the global destructor
* cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks