From b51bae1a5a4fa8ef7825dd1bb09e3f47f96d7a5a Mon Sep 17 00:00:00 2001 From: fanquake Date: Sat, 26 Oct 2019 08:05:59 -0400 Subject: [PATCH 01/11] doc: minor corrections in random.cpp This should have been part of #17151. --- src/random.cpp | 6 +++--- src/random.h | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 48d20d7d72b..b453fdd9b85 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -11,8 +11,8 @@ #include // for Windows API #include #endif -#include // for LogPrint() -#include // for WAIT_LOCK +#include // for LogPrintf() +#include // for Mutex #include // for GetTime() #include @@ -716,7 +716,7 @@ 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 + * GetOSRand() overwrites all 32 bytes of the output given a maximum * number of tries. */ static const ssize_t MAX_TRIES = 1024; diff --git a/src/random.h b/src/random.h index 9d1f7517733..b5a7faf48cf 100644 --- a/src/random.h +++ b/src/random.h @@ -52,7 +52,6 @@ * sources used in the 'slow' seeder are included, but also: * - 256 bits from the hardware RNG (rdseed or rdrand) when available. * - (On Windows) Performance monitoring data from the OS. - * - (On Windows) Through OpenSSL, the screen contents. * - Strengthen the entropy for 100 ms using repeated SHA512. * * When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and From cea3902015185adc88adbd031d919f91bc844fd7 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 26 Oct 2019 12:20:12 -0700 Subject: [PATCH 02/11] [MOVEONLY] Move perfmon data gathering to new randomenv module --- src/Makefile.am | 2 ++ src/random.cpp | 51 +++++++---------------------------- src/randomenv.cpp | 69 +++++++++++++++++++++++++++++++++++++++++++++++ src/randomenv.h | 17 ++++++++++++ 4 files changed, 97 insertions(+), 42 deletions(-) create mode 100644 src/randomenv.cpp create mode 100644 src/randomenv.h diff --git a/src/Makefile.am b/src/Makefile.am index ff4f071a3c1..dd67bf3240c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -175,6 +175,7 @@ BITCOIN_CORE_H = \ protocol.h \ psbt.h \ random.h \ + randomenv.h \ reverse_iterator.h \ reverselock.h \ rpc/blockchain.h \ @@ -502,6 +503,7 @@ libbitcoin_util_a_SOURCES = \ interfaces/handler.cpp \ logging.cpp \ random.cpp \ + randomenv.cpp \ rpc/request.cpp \ support/cleanse.cpp \ sync.cpp \ diff --git a/src/random.cpp b/src/random.cpp index b453fdd9b85..25ff7b31989 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -18,6 +18,8 @@ #include #include +#include + #include #ifndef WIN32 @@ -263,44 +265,6 @@ static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA51 memory_cleanse(buffer, sizeof(buffer)); } -static void RandAddSeedPerfmon(CSHA512& hasher) -{ -#ifdef WIN32 - // Don't need this on Linux, OpenSSL automatically uses /dev/urandom - // Seed with the entire set of perfmon data - - // This can take up to 2 seconds, so only do it every 10 minutes - static int64_t nLastPerfmon; - if (GetTime() < nLastPerfmon + 10 * 60) - return; - nLastPerfmon = GetTime(); - - std::vector vData(250000, 0); - long ret = 0; - unsigned long nSize = 0; - const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data - while (true) { - nSize = vData.size(); - ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize); - if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize) - break; - vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially - } - RegCloseKey(HKEY_PERFORMANCE_DATA); - if (ret == ERROR_SUCCESS) { - hasher.Write(vData.data(), nSize); - memory_cleanse(vData.data(), nSize); - } else { - // 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 -} - #ifndef WIN32 /** Fallback: get 32 bytes of system entropy from /dev/urandom. The most * compatible way to get cryptographic randomness on UNIX-ish platforms. @@ -585,8 +549,8 @@ static void SeedSleep(CSHA512& hasher, RNGState& rng) // High-precision timestamp after sleeping (as we commit to both the time before and after, this measures the delay) SeedTimestamp(hasher); - // Windows performance monitor data (once every 10 minutes) - RandAddSeedPerfmon(hasher); + // Dynamic environment data (performance monitoring, ...; once every 10 minutes) + RandAddDynamicEnv(hasher); // Strengthen every minute SeedStrengthen(hasher, rng); @@ -600,8 +564,11 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept // Everything that the 'slow' seeder includes. SeedSlow(hasher); - // Windows performance monitor data. - RandAddSeedPerfmon(hasher); + // Dynamic environment data + RandAddDynamicEnv(hasher); + + // Static environment data + RandAddStaticEnv(hasher); // Strengthen SeedStrengthen(hasher, rng); diff --git a/src/randomenv.cpp b/src/randomenv.cpp new file mode 100644 index 00000000000..529dd039b22 --- /dev/null +++ b/src/randomenv.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include // for GetTime() +#ifdef WIN32 +#include // for Windows API +#endif + +#include +#include + +#include + +namespace { + +void RandAddSeedPerfmon(CSHA512& hasher) +{ +#ifdef WIN32 + // Don't need this on Linux, OpenSSL automatically uses /dev/urandom + // Seed with the entire set of perfmon data + + // This can take up to 2 seconds, so only do it every 10 minutes + static int64_t nLastPerfmon; + if (GetTime() < nLastPerfmon + 10 * 60) + return; + nLastPerfmon = GetTime(); + + std::vector vData(250000, 0); + long ret = 0; + unsigned long nSize = 0; + const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data + while (true) { + nSize = vData.size(); + ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize); + if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize) + break; + vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially + } + RegCloseKey(HKEY_PERFORMANCE_DATA); + if (ret == ERROR_SUCCESS) { + hasher.Write(vData.data(), nSize); + memory_cleanse(vData.data(), nSize); + } else { + // 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 +} + +} // namespace + +void RandAddDynamicEnv(CSHA512& hasher) +{ + RandAddSeedPerfmon(hasher); +} + +void RandAddStaticEnv(CSHA512& hasher) +{ +} diff --git a/src/randomenv.h b/src/randomenv.h new file mode 100644 index 00000000000..46cea6f6f28 --- /dev/null +++ b/src/randomenv.h @@ -0,0 +1,17 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RANDOMENV_H +#define BITCOIN_RANDOMENV_H + +#include + +/** Gather non-cryptographic environment data that changes over time. */ +void RandAddDynamicEnv(CSHA512& hasher); + +/** Gather non-cryptographic environment data that does not change over time. */ +void RandAddStaticEnv(CSHA512& hasher); + +#endif From 723c79666770b30cce9f962bed5ece8cc7d74580 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 27 Oct 2019 12:52:31 -0700 Subject: [PATCH 03/11] [MOVEONLY] Move cpuid code from random & sha256 to compat/cpuid --- src/Makefile.am | 1 + src/compat/cpuid.h | 24 ++++++++++++++++++++++++ src/crypto/sha256.cpp | 20 +++++--------------- src/random.cpp | 17 ++--------------- 4 files changed, 32 insertions(+), 30 deletions(-) create mode 100644 src/compat/cpuid.h diff --git a/src/Makefile.am b/src/Makefile.am index dd67bf3240c..b04d2ecbbfc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -119,6 +119,7 @@ BITCOIN_CORE_H = \ compat.h \ compat/assumptions.h \ compat/byteswap.h \ + compat/cpuid.h \ compat/endian.h \ compat/sanity.h \ compressor.h \ diff --git a/src/compat/cpuid.h b/src/compat/cpuid.h new file mode 100644 index 00000000000..0877ad47d30 --- /dev/null +++ b/src/compat/cpuid.h @@ -0,0 +1,24 @@ +// Copyright (c) 2017-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_COMPAT_CPUID_H +#define BITCOIN_COMPAT_CPUID_H + +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +#define HAVE_GETCPUID + +#include + +// We can't use cpuid.h's __get_cpuid as it does not support subleafs. +void static inline GetCPUID(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d) +{ +#ifdef __GNUC__ + __cpuid_count(leaf, subleaf, a, b, c, d); +#else + __asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf)); +#endif +} + +#endif // defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +#endif // BITCOIN_COMPAT_CPUID_H diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp index 3257ee7f97e..dda7e5230fe 100644 --- a/src/crypto/sha256.cpp +++ b/src/crypto/sha256.cpp @@ -8,9 +8,10 @@ #include #include +#include + #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) #if defined(USE_ASM) -#include namespace sha256_sse4 { void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks); @@ -546,18 +547,7 @@ bool SelfTest() { return true; } - #if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__)) -// We can't use cpuid.h's __get_cpuid as it does not support subleafs. -void inline cpuid(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d) -{ -#ifdef __GNUC__ - __cpuid_count(leaf, subleaf, a, b, c, d); -#else - __asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf)); -#endif -} - /** Check whether the OS has enabled AVX registers. */ bool AVXEnabled() { @@ -572,7 +562,7 @@ bool AVXEnabled() std::string SHA256AutoDetect() { std::string ret = "standard"; -#if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__)) +#if defined(USE_ASM) && defined(HAVE_GETCPUID) bool have_sse4 = false; bool have_xsave = false; bool have_avx = false; @@ -589,7 +579,7 @@ std::string SHA256AutoDetect() (void)enabled_avx; uint32_t eax, ebx, ecx, edx; - cpuid(1, 0, eax, ebx, ecx, edx); + GetCPUID(1, 0, eax, ebx, ecx, edx); have_sse4 = (ecx >> 19) & 1; have_xsave = (ecx >> 27) & 1; have_avx = (ecx >> 28) & 1; @@ -597,7 +587,7 @@ std::string SHA256AutoDetect() enabled_avx = AVXEnabled(); } if (have_sse4) { - cpuid(7, 0, eax, ebx, ecx, edx); + GetCPUID(7, 0, eax, ebx, ecx, edx); have_avx2 = (ebx >> 5) & 1; have_shani = (ebx >> 29) & 1; } diff --git a/src/random.cpp b/src/random.cpp index 25ff7b31989..6f162a7cf0a 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -5,6 +5,7 @@ #include +#include #include #include #ifdef WIN32 @@ -42,11 +43,6 @@ #include #endif - -#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) -#include -#endif - #include #include #include @@ -77,7 +73,7 @@ static inline int64_t GetPerformanceCounter() noexcept #endif } -#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +#ifdef HAVE_GETCPUID static bool g_rdrand_supported = false; static bool g_rdseed_supported = false; static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000; @@ -88,15 +84,6 @@ static_assert(CPUID_F1_ECX_RDRAND == bit_RDRND, "Unexpected value for bit_RDRND" #ifdef bit_RDSEED static_assert(CPUID_F7_EBX_RDSEED == bit_RDSEED, "Unexpected value for bit_RDSEED"); #endif -static void inline GetCPUID(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d) -{ - // We can't use __get_cpuid as it doesn't support subleafs. -#ifdef __GNUC__ - __cpuid_count(leaf, subleaf, a, b, c, d); -#else - __asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf)); -#endif -} static void InitHardwareRand() { From c2a262a78c3bcc4d5e13612ab0214874abe15de0 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 26 Oct 2019 17:14:21 -0700 Subject: [PATCH 04/11] Seed randomness with process id / thread id / various clocks This sort of data is also used by OpenSSL. --- src/randomenv.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/src/randomenv.cpp b/src/randomenv.cpp index 529dd039b22..3f970ec3303 100644 --- a/src/randomenv.cpp +++ b/src/randomenv.cpp @@ -13,9 +13,22 @@ #endif #include +#include +#include #include #include +#include +#ifndef WIN32 +#include +#include +#include +#endif +#ifdef __MACH__ +#include +#include +#include +#endif namespace { @@ -57,13 +70,77 @@ void RandAddSeedPerfmon(CSHA512& hasher) #endif } +/** Helper to easily feed data into a CSHA512. + * + * Note that this does not serialize the passed object (like stream.h's << operators do). + * Its raw memory representation is used directly. + */ +template +CSHA512& operator<<(CSHA512& hasher, const T& data) { + static_assert(!std::is_same::type, char*>::value, "Calling operator<<(CSHA512, char*) is probably not what you want"); + static_assert(!std::is_same::type, unsigned char*>::value, "Calling operator<<(CSHA512, unsigned char*) is probably not what you want"); + static_assert(!std::is_same::type, const char*>::value, "Calling operator<<(CSHA512, const char*) is probably not what you want"); + static_assert(!std::is_same::type, const unsigned char*>::value, "Calling operator<<(CSHA512, const unsigned char*) is probably not what you want"); + hasher.Write((const unsigned char*)&data, sizeof(data)); + return hasher; +} + } // namespace void RandAddDynamicEnv(CSHA512& hasher) { RandAddSeedPerfmon(hasher); + + // Various clocks +#ifdef WIN32 + FILETIME ftime; + GetSystemTimeAsFileTime(&ftime); + hasher << ftime; +#else +# ifndef __MACH__ + // On non-MacOS systems, use various clock_gettime() calls. + struct timespec ts; +# ifdef CLOCK_MONOTONIC + clock_gettime(CLOCK_MONOTONIC, &ts); + hasher << ts.tv_sec << ts.tv_nsec; +# endif +# ifdef CLOCK_REALTIME + clock_gettime(CLOCK_REALTIME, &ts); + hasher << ts.tv_sec << ts.tv_nsec; +# endif +# ifdef CLOCK_BOOTTIME + clock_gettime(CLOCK_BOOTTIME, &ts); + hasher << ts.tv_sec << ts.tv_nsec; +# endif +# else + // On MacOS use mach_absolute_time (number of CPU ticks since boot) as a replacement for CLOCK_MONOTONIC, + // and clock_get_time for CALENDAR_CLOCK as a replacement for CLOCK_REALTIME. + hasher << mach_absolute_time(); + // From https://gist.github.com/jbenet/1087739 + clock_serv_t cclock; + mach_timespec_t mts; + if (host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock) == KERN_SUCCESS && clock_get_time(cclock, &mts) == KERN_SUCCESS) { + hasher << mts.tv_sec << mts.tv_nsec; + mach_port_deallocate(mach_task_self(), cclock); + } +# endif + // gettimeofday is available on all UNIX systems, but only has microsecond precision. + struct timeval tv; + gettimeofday(&tv, nullptr); + hasher << tv.tv_sec << tv.tv_usec; +#endif + // Probably redundant, but also use all the clocks C++11 provides: + hasher << std::chrono::system_clock::now().time_since_epoch().count(); + hasher << std::chrono::steady_clock::now().time_since_epoch().count(); + hasher << std::chrono::high_resolution_clock::now().time_since_epoch().count(); } void RandAddStaticEnv(CSHA512& hasher) { +#ifdef WIN32 + hasher << GetCurrentProcessId() << GetCurrentThreadId(); +#else + hasher << getpid(); +#endif + hasher << std::this_thread::get_id(); } From 2554c1b81bb8c40e1989025c6f18e7935720b156 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 26 Oct 2019 18:05:10 -0700 Subject: [PATCH 05/11] Gather additional entropy from the environment This based on code by Gregory Maxwell. --- src/randomenv.cpp | 194 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 184 insertions(+), 10 deletions(-) diff --git a/src/randomenv.cpp b/src/randomenv.cpp index 3f970ec3303..1faf2f4613f 100644 --- a/src/randomenv.cpp +++ b/src/randomenv.cpp @@ -3,8 +3,13 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include +#include #include #include #include // for GetTime() @@ -14,14 +19,21 @@ #include #include +#include #include #include #include #include #ifndef WIN32 +#include // must go before a number of other headers +#include +#include +#include +#include +#include #include -#include +#include #include #endif #ifdef __MACH__ @@ -29,6 +41,12 @@ #include #include #endif +#if HAVE_DECL_GETIFADDRS +#include +#endif + +//! Necessary on some platforms +extern char** environ; namespace { @@ -85,6 +103,53 @@ CSHA512& operator<<(CSHA512& hasher, const T& data) { return hasher; } +#ifndef WIN32 +void AddSockaddr(CSHA512& hasher, const struct sockaddr *addr) +{ + if (addr == nullptr) return; + switch (addr->sa_family) { + case AF_INET: + hasher.Write((const unsigned char*)addr, sizeof(sockaddr_in)); + break; + case AF_INET6: + hasher.Write((const unsigned char*)addr, sizeof(sockaddr_in6)); + break; + default: + hasher.Write((const unsigned char*)&addr->sa_family, sizeof(addr->sa_family)); + } +} + +void AddFile(CSHA512& hasher, const char *path) +{ + struct stat sb = {}; + int f = open(path, O_RDONLY); + size_t total = 0; + if (f != -1) { + unsigned char fbuf[4096]; + int n; + hasher.Write((const unsigned char*)&f, sizeof(f)); + if (fstat(f, &sb) == 0) hasher << sb; + do { + n = read(f, fbuf, sizeof(fbuf)); + if (n > 0) hasher.Write(fbuf, n); + total += n; + /* not bothering with EINTR handling. */ + } while (n == sizeof(fbuf) && total < 1048576); // Read only the first 1 Mbyte + close(f); + } +} + +void AddPath(CSHA512& hasher, const char *path) +{ + struct stat sb = {}; + if (stat(path, &sb) == 0) { + hasher.Write((const unsigned char*)path, strlen(path) + 1); + hasher << sb; + } +} +#endif + + } // namespace void RandAddDynamicEnv(CSHA512& hasher) @@ -99,18 +164,18 @@ void RandAddDynamicEnv(CSHA512& hasher) #else # ifndef __MACH__ // On non-MacOS systems, use various clock_gettime() calls. - struct timespec ts; + struct timespec ts = {}; # ifdef CLOCK_MONOTONIC clock_gettime(CLOCK_MONOTONIC, &ts); - hasher << ts.tv_sec << ts.tv_nsec; + hasher << ts; # endif # ifdef CLOCK_REALTIME clock_gettime(CLOCK_REALTIME, &ts); - hasher << ts.tv_sec << ts.tv_nsec; + hasher << ts; # endif # ifdef CLOCK_BOOTTIME clock_gettime(CLOCK_BOOTTIME, &ts); - hasher << ts.tv_sec << ts.tv_nsec; + hasher << ts; # endif # else // On MacOS use mach_absolute_time (number of CPU ticks since boot) as a replacement for CLOCK_MONOTONIC, @@ -118,29 +183,138 @@ void RandAddDynamicEnv(CSHA512& hasher) hasher << mach_absolute_time(); // From https://gist.github.com/jbenet/1087739 clock_serv_t cclock; - mach_timespec_t mts; + mach_timespec_t mts = {}; if (host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock) == KERN_SUCCESS && clock_get_time(cclock, &mts) == KERN_SUCCESS) { - hasher << mts.tv_sec << mts.tv_nsec; + hasher << mts; mach_port_deallocate(mach_task_self(), cclock); } # endif // gettimeofday is available on all UNIX systems, but only has microsecond precision. - struct timeval tv; + struct timeval tv = {}; gettimeofday(&tv, nullptr); - hasher << tv.tv_sec << tv.tv_usec; + hasher << tv; #endif // Probably redundant, but also use all the clocks C++11 provides: hasher << std::chrono::system_clock::now().time_since_epoch().count(); hasher << std::chrono::steady_clock::now().time_since_epoch().count(); hasher << std::chrono::high_resolution_clock::now().time_since_epoch().count(); + +#ifndef WIN32 + // Current resource usage. + struct rusage usage = {}; + if (getrusage(RUSAGE_SELF, &usage) == 0) hasher << usage; +#endif + +#ifdef __linux__ + AddFile(hasher, "/proc/diskstats"); + AddFile(hasher, "/proc/vmstat"); + AddFile(hasher, "/proc/schedstat"); + AddFile(hasher, "/proc/zoneinfo"); + AddFile(hasher, "/proc/meminfo"); + AddFile(hasher, "/proc/softirqs"); + AddFile(hasher, "/proc/stat"); + AddFile(hasher, "/proc/self/schedstat"); + AddFile(hasher, "/proc/self/status"); +#endif + + // Stack and heap location + void* addr = malloc(4097); + hasher << &addr << addr; + free(addr); } void RandAddStaticEnv(CSHA512& hasher) { + // Some compile-time static properties + hasher << (CHAR_MIN < 0) << sizeof(void*) << sizeof(long) << sizeof(int); +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) + hasher << __GNUC__ << __GNUC_MINOR__ << __GNUC_PATCHLEVEL__; +#endif +#ifdef _MSC_VER + hasher << _MSC_VER; +#endif + hasher << __cplusplus; +#ifdef _XOPEN_VERSION + hasher << _XOPEN_VERSION; +#endif +#ifdef __VERSION__ + const char* COMPILER_VERSION = __VERSION__; + hasher.Write((const unsigned char*)COMPILER_VERSION, strlen(COMPILER_VERSION) + 1); +#endif + + // Bitcoin client version + hasher << CLIENT_VERSION; + + // Memory locations + hasher << &hasher << &RandAddStaticEnv << &malloc << &errno << &environ; + + // Hostname + char hname[256]; + if (gethostname(hname, 256) == 0) { + hasher.Write((const unsigned char*)hname, strnlen(hname, 256)); + } + +#if HAVE_DECL_GETIFADDRS + // Network interfaces + struct ifaddrs *ifad = NULL; + getifaddrs(&ifad); + struct ifaddrs *ifit = ifad; + while (ifit != NULL) { + hasher.Write((const unsigned char*)&ifit, sizeof(ifit)); + hasher.Write((const unsigned char*)ifit->ifa_name, strlen(ifit->ifa_name) + 1); + hasher.Write((const unsigned char*)&ifit->ifa_flags, sizeof(ifit->ifa_flags)); + AddSockaddr(hasher, ifit->ifa_addr); + AddSockaddr(hasher, ifit->ifa_netmask); + AddSockaddr(hasher, ifit->ifa_dstaddr); + ifit = ifit->ifa_next; + } + freeifaddrs(ifad); +#endif + +#ifndef WIN32 + // UNIX kernel information + struct utsname name; + if (uname(&name) != -1) { + hasher.Write((const unsigned char*)&name.sysname, strlen(name.sysname) + 1); + hasher.Write((const unsigned char*)&name.nodename, strlen(name.nodename) + 1); + hasher.Write((const unsigned char*)&name.release, strlen(name.release) + 1); + hasher.Write((const unsigned char*)&name.version, strlen(name.version) + 1); + hasher.Write((const unsigned char*)&name.machine, strlen(name.machine) + 1); + } + + /* Path and filesystem provided data */ + AddPath(hasher, "/"); + AddPath(hasher, "."); + AddPath(hasher, "/tmp"); + AddPath(hasher, "/home"); + AddPath(hasher, "/proc"); +#ifdef __linux__ + AddFile(hasher, "/proc/cmdline"); + AddFile(hasher, "/proc/cpuinfo"); + AddFile(hasher, "/proc/version"); +#endif + AddFile(hasher, "/etc/passwd"); + AddFile(hasher, "/etc/group"); + AddFile(hasher, "/etc/hosts"); + AddFile(hasher, "/etc/resolv.conf"); + AddFile(hasher, "/etc/timezone"); + AddFile(hasher, "/etc/localtime"); + + /* TODO: sysctl's for OSX to fetch information not available from /proc */ +#endif + + // Env variables + if (environ) { + for (size_t i = 0; environ[i]; ++i) { + hasher.Write((const unsigned char*)environ[i], strlen(environ[i])); + } + } + + // Process, thread, user, session, group, ... ids. #ifdef WIN32 hasher << GetCurrentProcessId() << GetCurrentThreadId(); #else - hasher << getpid(); + hasher << getpid() << getppid() << getsid(0) << getpgid(0) << getuid() << geteuid() << getgid() << getegid(); #endif hasher << std::this_thread::get_id(); } From a81c494b4c9a8c2f1a319a03375826f12863706f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 28 Oct 2019 10:42:25 -0700 Subject: [PATCH 06/11] Use sysctl for seeding on MacOS/BSD --- configure.ac | 14 ++++- src/randomenv.cpp | 128 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 140 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 8bde6d9bc43..e1db71a7bff 100644 --- a/configure.ac +++ b/configure.ac @@ -788,7 +788,7 @@ if test x$TARGET_OS = xdarwin; then AX_CHECK_LINK_FLAG([[-Wl,-dead_strip]], [LDFLAGS="$LDFLAGS -Wl,-dead_strip"]) fi -AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h]) +AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h sys/sysctl.h vm/vm_param.h sys/vmmeter.h sys/resources.h]) # FD_ZERO may be dependent on a declaration of memcpy, e.g. in SmartOS # check that it fails to build without memcpy, then that it builds with @@ -948,6 +948,18 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include [ AC_MSG_RESULT(no)] ) +AC_MSG_CHECKING(for sysctl) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + #include ]], + [[ static const int name[2] = {CTL_KERN, KERN_VERSION}; + #ifdef __linux__ + #error "Don't use sysctl on Linux, it's deprecated even when it works" + #endif + sysctl(name, 2, nullptr, nullptr, nullptr, 0); ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSCTL, 1,[Define this symbol if the BSD sysctl() is available]) ], + [ AC_MSG_RESULT(no)] +) + AC_MSG_CHECKING(for sysctl KERN_ARND) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include ]], diff --git a/src/randomenv.cpp b/src/randomenv.cpp index 1faf2f4613f..197c5ce154f 100644 --- a/src/randomenv.cpp +++ b/src/randomenv.cpp @@ -44,6 +44,18 @@ #if HAVE_DECL_GETIFADDRS #include #endif +#if HAVE_SYSCTL +#include +#if HAVE_VM_VM_PARAM_H +#include +#endif +#if HAVE_SYS_RESOURCES_H +#include +#endif +#if HAVE_SYS_VMMETER_H +#include +#endif +#endif //! Necessary on some platforms extern char** environ; @@ -149,6 +161,23 @@ void AddPath(CSHA512& hasher, const char *path) } #endif +#if HAVE_SYSCTL +template +void AddSysctl(CSHA512& hasher) +{ + int CTL[sizeof...(S)] = {S...}; + unsigned char buffer[65536]; + size_t siz = 65536; + int ret = sysctl(CTL, sizeof...(S), buffer, &siz, nullptr, 0); + if (ret == 0 || (ret == -1 && errno == ENOMEM)) { + hasher << sizeof(CTL); + hasher.Write((const unsigned char*)CTL, sizeof(CTL)); + if (siz > sizeof(buffer)) siz = sizeof(buffer); + hasher << siz; + hasher.Write(buffer, siz); + } +} +#endif } // namespace @@ -217,6 +246,30 @@ void RandAddDynamicEnv(CSHA512& hasher) AddFile(hasher, "/proc/self/status"); #endif +#if HAVE_SYSCTL +# ifdef CTL_KERN +# if defined(KERN_PROC) && defined(KERN_PROC_ALL) + AddSysctl(hasher); +# endif +# endif +# ifdef CTL_HW +# ifdef HW_DISKSTATS + AddSysctl(hasher); +# endif +# endif +# ifdef CTL_VM +# ifdef VM_LOADAVG + AddSysctl(hasher); +# endif +# ifdef VM_TOTAL + AddSysctl(hasher); +# endif +# ifdef VM_METER + AddSysctl(hasher); +# endif +# endif +#endif + // Stack and heap location void* addr = malloc(4097); hasher << &addr << addr; @@ -299,8 +352,81 @@ void RandAddStaticEnv(CSHA512& hasher) AddFile(hasher, "/etc/resolv.conf"); AddFile(hasher, "/etc/timezone"); AddFile(hasher, "/etc/localtime"); +#endif - /* TODO: sysctl's for OSX to fetch information not available from /proc */ + // For MacOS/BSDs, gather data through sysctl instead of /proc. Not all of these + // will exist on every system. +#if HAVE_SYSCTL +# ifdef CTL_HW +# ifdef HW_MACHINE + AddSysctl(hasher); +# endif +# ifdef HW_MODEL + AddSysctl(hasher); +# endif +# ifdef HW_NCPU + AddSysctl(hasher); +# endif +# ifdef HW_PHYSMEM + AddSysctl(hasher); +# endif +# ifdef HW_USERMEM + AddSysctl(hasher); +# endif +# ifdef HW_MACHINE_ARCH + AddSysctl(hasher); +# endif +# ifdef HW_REALMEM + AddSysctl(hasher); +# endif +# ifdef HW_CPU_FREQ + AddSysctl(hasher); +# endif +# ifdef HW_BUS_FREQ + AddSysctl(hasher); +# endif +# ifdef HW_CACHELINE + AddSysctl(hasher); +# endif +# endif +# ifdef CTL_KERN +# ifdef KERN_BOOTFILE + AddSysctl(hasher); +# endif +# ifdef KERN_BOOTTIME + AddSysctl(hasher); +# endif +# ifdef KERN_CLOCKRATE + AddSysctl(hasher); +# endif +# ifdef KERN_HOSTID + AddSysctl(hasher); +# endif +# ifdef KERN_HOSTUUID + AddSysctl(hasher); +# endif +# ifdef KERN_HOSTNAME + AddSysctl(hasher); +# endif +# ifdef KERN_OSRELDATE + AddSysctl(hasher); +# endif +# ifdef KERN_OSRELEASE + AddSysctl(hasher); +# endif +# ifdef KERN_OSREV + AddSysctl(hasher); +# endif +# ifdef KERN_OSTYPE + AddSysctl(hasher); +# endif +# ifdef KERN_POSIX1 + AddSysctl(hasher); +# endif +# ifdef KERN_VERSION + AddSysctl(hasher); +# endif +# endif #endif // Env variables From 11793ea22e1298fa7d3b44a5b6d20830248d8cf4 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 26 Oct 2019 18:17:20 -0700 Subject: [PATCH 07/11] Feed CPUID data into RNG --- src/randomenv.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/randomenv.cpp b/src/randomenv.cpp index 197c5ce154f..bfe57dafe50 100644 --- a/src/randomenv.cpp +++ b/src/randomenv.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include // for GetTime() @@ -179,6 +180,36 @@ void AddSysctl(CSHA512& hasher) } #endif +#ifdef HAVE_GETCPUID +void inline AddCPUID(CSHA512& hasher, uint32_t leaf, uint32_t subleaf, uint32_t& ax, uint32_t& bx, uint32_t& cx, uint32_t& dx) +{ + GetCPUID(leaf, subleaf, ax, bx, cx, dx); + hasher << leaf << subleaf << ax << bx << cx << dx; +} + +void AddAllCPUID(CSHA512& hasher) +{ + uint32_t ax, bx, cx, dx; + // Iterate over all standard leaves + AddCPUID(hasher, 0, 0, ax, bx, cx, dx); // Returns max leaf in ax + uint32_t max = ax; + for (uint32_t leaf = 1; leaf <= max; ++leaf) { + for (uint32_t subleaf = 0;; ++subleaf) { + AddCPUID(hasher, leaf, subleaf, ax, bx, cx, dx); + // Iterate over subleaves for leaf 4, 11, 13 + if (leaf != 4 && leaf != 11 && leaf != 13) break; + if ((leaf == 4 || leaf == 13) && ax == 0) break; + if (leaf == 11 && (cx & 0xFF00) == 0) break; + } + } + // Iterate over all extended leaves + AddCPUID(hasher, 0x80000000, 0, ax, bx, cx, dx); // Returns max extended leaf in ax + uint32_t ext_max = ax; + for (uint32_t leaf = 0x80000001; leaf <= ext_max; ++leaf) { + AddCPUID(hasher, leaf, 0, ax, bx, cx, dx); + } +} +#endif } // namespace void RandAddDynamicEnv(CSHA512& hasher) @@ -298,6 +329,10 @@ void RandAddStaticEnv(CSHA512& hasher) // Bitcoin client version hasher << CLIENT_VERSION; +#ifdef HAVE_GETCPUID + AddAllCPUID(hasher); +#endif + // Memory locations hasher << &hasher << &RandAddStaticEnv << &malloc << &errno << &environ; From 483b94292e89587e5ab40a30b8a90e2f56e847f3 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 27 Oct 2019 12:36:05 -0700 Subject: [PATCH 08/11] Add information gathered through getauxval() Suggested by Wladimir van der Laan. --- src/randomenv.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/randomenv.cpp b/src/randomenv.cpp index bfe57dafe50..05be090d932 100644 --- a/src/randomenv.cpp +++ b/src/randomenv.cpp @@ -57,6 +57,9 @@ #include #endif #endif +#ifdef __linux__ +#include +#endif //! Necessary on some platforms extern char** environ; @@ -329,6 +332,28 @@ void RandAddStaticEnv(CSHA512& hasher) // Bitcoin client version hasher << CLIENT_VERSION; +#ifdef __linux__ + // Information available through getauxval() +# ifdef AT_HWCAP + hasher << getauxval(AT_HWCAP); +# endif +# ifdef AT_HWCAP2 + hasher << getauxval(AT_HWCAP2); +# endif +# ifdef AT_RANDOM + const unsigned char* random_aux = (const unsigned char*)getauxval(AT_RANDOM); + if (random_aux) hasher.Write(random_aux, 16); +# endif +# ifdef AT_PLATFORM + const char* platform_str = (const char*)getauxval(AT_PLATFORM); + if (platform_str) hasher.Write((const unsigned char*)platform_str, strlen(platform_str) + 1); +# endif +# ifdef AT_EXECFN + const char* exec_str = (const char*)getauxval(AT_EXECFN); + if (exec_str) hasher.Write((const unsigned char*)exec_str, strlen(exec_str) + 1); +# endif +#endif // __linux__ + #ifdef HAVE_GETCPUID AddAllCPUID(hasher); #endif From d61f2bb076d8f17840a8e79f1583d7f6e3e6d09a Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 29 Oct 2019 11:55:59 -0700 Subject: [PATCH 09/11] Run background seeding periodically instead of unpredictably * Instead of calling RandAddSeedSleep anytime the scheduler goes idle, call its replacement (RandAddSeedPeriodic) just once per minute. This has better guarantees of actually being run, and helps limit how frequently the dynamic env data is gathered. * Since this code runs once per minute regardless now, we no longer need to keep track of the last time strengthening was run; just do it always. * Make strengthening time context dependent (100 ms at startup, 10 ms once per minute afterwards). --- src/init.cpp | 5 +++++ src/random.cpp | 46 +++++++++++++++++----------------------------- src/random.h | 4 ++-- src/scheduler.cpp | 2 -- 4 files changed, 24 insertions(+), 33 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index f02740786d2..2abdf7dbc40 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1258,6 +1258,11 @@ bool AppInitMain(NodeContext& node) CScheduler::Function serviceLoop = std::bind(&CScheduler::serviceQueue, &scheduler); threadGroup.create_thread(std::bind(&TraceThread, "scheduler", serviceLoop)); + // Gather some entropy once per minute. + scheduler.scheduleEvery([]{ + RandAddPeriodic(); + }, 60000); + GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); GetMainSignals().RegisterWithMempoolSignals(mempool); diff --git a/src/random.cpp b/src/random.cpp index 6f162a7cf0a..2847aacf6f3 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -507,22 +507,16 @@ static void SeedSlow(CSHA512& hasher) noexcept } /** Extract entropy from rng, strengthen it, and feed it into hasher. */ -static void SeedStrengthen(CSHA512& hasher, RNGState& rng) noexcept +static void SeedStrengthen(CSHA512& hasher, RNGState& rng, int microseconds) noexcept { - static std::atomic last_strengthen{0}; - int64_t last_time = last_strengthen.load(); - int64_t current_time = GetTimeMicros(); - if (current_time > last_time + 60000000) { // Only run once a minute - // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher. - unsigned char strengthen_seed[32]; - rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false); - // Strengthen it for 10ms (100ms on first run), and feed it into hasher. - Strengthen(strengthen_seed, last_time == 0 ? 100000 : 10000, hasher); - last_strengthen = current_time; - } + // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher. + unsigned char strengthen_seed[32]; + rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false); + // Strengthen the seed, and feed it into hasher. + Strengthen(strengthen_seed, microseconds, hasher); } -static void SeedSleep(CSHA512& hasher, RNGState& rng) +static void SeedPeriodic(CSHA512& hasher, RNGState& rng) { // Everything that the 'fast' seeder includes SeedFast(hasher); @@ -530,17 +524,11 @@ static void SeedSleep(CSHA512& hasher, RNGState& rng) // 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); - - // Dynamic environment data (performance monitoring, ...; once every 10 minutes) + // Dynamic environment data (performance monitoring, ...) RandAddDynamicEnv(hasher); - // Strengthen every minute - SeedStrengthen(hasher, rng); + // Strengthen for 10 ms + SeedStrengthen(hasher, rng, 10000); } static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept @@ -551,20 +539,20 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept // Everything that the 'slow' seeder includes. SeedSlow(hasher); - // Dynamic environment data + // Dynamic environment data (performance monitoring, ...) RandAddDynamicEnv(hasher); // Static environment data RandAddStaticEnv(hasher); - // Strengthen - SeedStrengthen(hasher, rng); + // Strengthen for 100 ms + SeedStrengthen(hasher, rng, 100000); } enum class RNGLevel { FAST, //!< Automatically called by GetRandBytes SLOW, //!< Automatically called by GetStrongRandBytes - SLEEP, //!< Called by RandAddSeedSleep() + PERIODIC, //!< Called by RandAddPeriodic() }; static void ProcRand(unsigned char* out, int num, RNGLevel level) @@ -582,8 +570,8 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) case RNGLevel::SLOW: SeedSlow(hasher); break; - case RNGLevel::SLEEP: - SeedSleep(hasher, rng); + case RNGLevel::PERIODIC: + SeedPeriodic(hasher, rng); break; } @@ -606,7 +594,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) 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); } +void RandAddPeriodic() { ProcRand(nullptr, 0, RNGLevel::PERIODIC); } bool g_mock_deterministic_tests{false}; diff --git a/src/random.h b/src/random.h index b5a7faf48cf..7768f9d3c50 100644 --- a/src/random.h +++ b/src/random.h @@ -84,11 +84,11 @@ uint256 GetRandHash() noexcept; void GetStrongRandBytes(unsigned char* buf, int num) noexcept; /** - * Sleep for 1ms, gather entropy from various sources, and feed them to the PRNG state. + * Gather entropy from various expensive sources, and feed them to the PRNG state. * * Thread-safe. */ -void RandAddSeedSleep(); +void RandAddPeriodic(); /** * Fast randomness source. This is seeded once with secure random data, but diff --git a/src/scheduler.cpp b/src/scheduler.cpp index fdc859b3a0f..07a54335ac8 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -41,8 +41,6 @@ void CScheduler::serviceQueue() try { if (!shouldStop() && taskQueue.empty()) { reverse_lock > rlock(lock); - // Use this chance to get more entropy - RandAddSeedSleep(); } while (!shouldStop() && taskQueue.empty()) { // Wait until there is something to do. From 64e1e022cedf6776c5dffd488ca2e766adca5dc3 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 29 Oct 2019 12:35:06 -0700 Subject: [PATCH 10/11] Use thread-safe atomic in perfmon seeder Also switch to chrono based types. --- src/randomenv.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/randomenv.cpp b/src/randomenv.cpp index 05be090d932..603c88eaabe 100644 --- a/src/randomenv.cpp +++ b/src/randomenv.cpp @@ -19,6 +19,7 @@ #endif #include +#include #include #include #include @@ -73,10 +74,11 @@ void RandAddSeedPerfmon(CSHA512& hasher) // Seed with the entire set of perfmon data // This can take up to 2 seconds, so only do it every 10 minutes - static int64_t nLastPerfmon; - if (GetTime() < nLastPerfmon + 10 * 60) - return; - nLastPerfmon = GetTime(); + static std::atomic last_perfmon{std::chrono::seconds{0}}; + auto last_time = last_perfmon.load(); + auto current_time = GetTime(); + if (current_time < last_time + std::chrono::minutes{10}) return; + last_perfmon = current_time; std::vector vData(250000, 0); long ret = 0; From d1c02775aa74a0610809ac54bb241ddad61d2d8c Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 5 Nov 2019 10:54:20 -0800 Subject: [PATCH 11/11] Report amount of data gathered from environment --- src/crypto/sha512.h | 1 + src/random.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/crypto/sha512.h b/src/crypto/sha512.h index 4118ac1b189..fc7dd1b87ea 100644 --- a/src/crypto/sha512.h +++ b/src/crypto/sha512.h @@ -23,6 +23,7 @@ public: CSHA512& Write(const unsigned char* data, size_t len); void Finalize(unsigned char hash[OUTPUT_SIZE]); CSHA512& Reset(); + uint64_t Size() const { return bytes; } }; #endif // BITCOIN_CRYPTO_SHA512_H diff --git a/src/random.cpp b/src/random.cpp index 2847aacf6f3..3e6398f7b49 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -525,7 +525,9 @@ static void SeedPeriodic(CSHA512& hasher, RNGState& rng) SeedTimestamp(hasher); // Dynamic environment data (performance monitoring, ...) + auto old_size = hasher.Size(); RandAddDynamicEnv(hasher); + LogPrintf("Feeding %i bytes of dynamic environment data into RNG\n", hasher.Size() - old_size); // Strengthen for 10 ms SeedStrengthen(hasher, rng, 10000); @@ -540,10 +542,12 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept SeedSlow(hasher); // Dynamic environment data (performance monitoring, ...) + auto old_size = hasher.Size(); RandAddDynamicEnv(hasher); // Static environment data RandAddStaticEnv(hasher); + LogPrintf("Feeding %i bytes of environment data into RNG\n", hasher.Size() - old_size); // Strengthen for 100 ms SeedStrengthen(hasher, rng, 100000);