Skip to content

Commit 1b77769

Browse files
laanwjsidhujag
authored and
sidhujag
committed
Merge bitcoin#17270: Feed environment data into RNG initializers
d1c0277 Report amount of data gathered from environment (Pieter Wuille) 64e1e02 Use thread-safe atomic in perfmon seeder (Pieter Wuille) d61f2bb Run background seeding periodically instead of unpredictably (Pieter Wuille) 483b942 Add information gathered through getauxval() (Pieter Wuille) 11793ea Feed CPUID data into RNG (Pieter Wuille) a81c494 Use sysctl for seeding on MacOS/BSD (Pieter Wuille) 2554c1b Gather additional entropy from the environment (Pieter Wuille) c2a262a Seed randomness with process id / thread id / various clocks (Pieter Wuille) 723c796 [MOVEONLY] Move cpuid code from random & sha256 to compat/cpuid (Pieter Wuille) cea3902 [MOVEONLY] Move perfmon data gathering to new randomenv module (Pieter Wuille) b51bae1 doc: minor corrections in random.cpp (fanquake) Pull request description: This introduces a new `randomenv` module that queries varies non-cryptographic (and non-RNG) sources of entropy available on the system; things like user IDs, system configuration, time, statistics, CPUID data. The idea is that these provide a fallback in scenarios where system entropy is somehow broken (note that if system entropy *fails* we will abort regardless; this is only meant to function as a last resort against undetected failure). It includes some data sources OpenSSL currently uses, and more. The separation between random and randomenv is a bit arbitrary, but I felt that all this "non-essential" functionality deserved to be separated from the core random module. ACKs for top commit: TheBlueMatt: utACK d1c0277. Certainly no longer measuring the time elapsed between a 1ms sleep (which got removed in the latest change) is a fair tradeoff for adding about 2 million other actually-higher-entropy bits :). laanwj: ACK d1c0277 Tree-SHA512: d290a8db6538a164348118ee02079e4f4c8551749ea78fa44b2aad57f5df2ccbc2a12dc7d80d8f3e916d68cdd8e204faf9e1bcbec15f9054eba6b22f17c66ae3
1 parent 4c157a3 commit 1b77769

11 files changed

+611
-108
lines changed

configure.ac

+13-1
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,7 @@ if test x$TARGET_OS = xdarwin; then
787787
AX_CHECK_LINK_FLAG([[-Wl,-dead_strip]], [LDFLAGS="$LDFLAGS -Wl,-dead_strip"])
788788
fi
789789

790-
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])
790+
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])
791791

792792
# FD_ZERO may be dependent on a declaration of memcpy, e.g. in SmartOS
793793
# check that it fails to build without memcpy, then that it builds with
@@ -947,6 +947,18 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h>
947947
[ AC_MSG_RESULT(no)]
948948
)
949949

950+
AC_MSG_CHECKING(for sysctl)
951+
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
952+
#include <sys/sysctl.h>]],
953+
[[ static const int name[2] = {CTL_KERN, KERN_VERSION};
954+
#ifdef __linux__
955+
#error "Don't use sysctl on Linux, it's deprecated even when it works"
956+
#endif
957+
sysctl(name, 2, nullptr, nullptr, nullptr, 0); ]])],
958+
[ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSCTL, 1,[Define this symbol if the BSD sysctl() is available]) ],
959+
[ AC_MSG_RESULT(no)]
960+
)
961+
950962
AC_MSG_CHECKING(for sysctl KERN_ARND)
951963
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
952964
#include <sys/sysctl.h>]],

src/Makefile.am

+3
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ SYSCOIN_CORE_H = \
149149
compat.h \
150150
compat/assumptions.h \
151151
compat/byteswap.h \
152+
compat/cpuid.h \
152153
compat/endian.h \
153154
compat/sanity.h \
154155
compressor.h \
@@ -205,6 +206,7 @@ SYSCOIN_CORE_H = \
205206
protocol.h \
206207
psbt.h \
207208
random.h \
209+
randomenv.h \
208210
reverse_iterator.h \
209211
reverselock.h \
210212
rpc/auxpow_miner.h \
@@ -592,6 +594,7 @@ libsyscoin_util_a_SOURCES = \
592594
interfaces/handler.cpp \
593595
logging.cpp \
594596
random.cpp \
597+
randomenv.cpp \
595598
rpc/request.cpp \
596599
support/cleanse.cpp \
597600
sync.cpp \

src/compat/cpuid.h

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) 2017-2019 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_COMPAT_CPUID_H
6+
#define BITCOIN_COMPAT_CPUID_H
7+
8+
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
9+
#define HAVE_GETCPUID
10+
11+
#include <cpuid.h>
12+
13+
// We can't use cpuid.h's __get_cpuid as it does not support subleafs.
14+
void static inline GetCPUID(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d)
15+
{
16+
#ifdef __GNUC__
17+
__cpuid_count(leaf, subleaf, a, b, c, d);
18+
#else
19+
__asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf));
20+
#endif
21+
}
22+
23+
#endif // defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
24+
#endif // BITCOIN_COMPAT_CPUID_H

src/crypto/sha256.cpp

+5-15
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
#include <assert.h>
99
#include <string.h>
1010

11+
#include <compat/cpuid.h>
12+
1113
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
1214
#if defined(USE_ASM)
13-
#include <cpuid.h>
1415
namespace sha256_sse4
1516
{
1617
void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks);
@@ -546,18 +547,7 @@ bool SelfTest() {
546547
return true;
547548
}
548549

549-
550550
#if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__))
551-
// We can't use cpuid.h's __get_cpuid as it does not support subleafs.
552-
void inline cpuid(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d)
553-
{
554-
#ifdef __GNUC__
555-
__cpuid_count(leaf, subleaf, a, b, c, d);
556-
#else
557-
__asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf));
558-
#endif
559-
}
560-
561551
/** Check whether the OS has enabled AVX registers. */
562552
bool AVXEnabled()
563553
{
@@ -572,7 +562,7 @@ bool AVXEnabled()
572562
std::string SHA256AutoDetect()
573563
{
574564
std::string ret = "standard";
575-
#if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__))
565+
#if defined(USE_ASM) && defined(HAVE_GETCPUID)
576566
bool have_sse4 = false;
577567
bool have_xsave = false;
578568
bool have_avx = false;
@@ -589,15 +579,15 @@ std::string SHA256AutoDetect()
589579
(void)enabled_avx;
590580

591581
uint32_t eax, ebx, ecx, edx;
592-
cpuid(1, 0, eax, ebx, ecx, edx);
582+
GetCPUID(1, 0, eax, ebx, ecx, edx);
593583
have_sse4 = (ecx >> 19) & 1;
594584
have_xsave = (ecx >> 27) & 1;
595585
have_avx = (ecx >> 28) & 1;
596586
if (have_xsave && have_avx) {
597587
enabled_avx = AVXEnabled();
598588
}
599589
if (have_sse4) {
600-
cpuid(7, 0, eax, ebx, ecx, edx);
590+
GetCPUID(7, 0, eax, ebx, ecx, edx);
601591
have_avx2 = (ebx >> 5) & 1;
602592
have_shani = (ebx >> 29) & 1;
603593
}

src/crypto/sha512.h

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class CSHA512
2323
CSHA512& Write(const unsigned char* data, size_t len);
2424
void Finalize(unsigned char hash[OUTPUT_SIZE]);
2525
CSHA512& Reset();
26+
uint64_t Size() const { return bytes; }
2627
};
2728

2829
#endif // SYSCOIN_CRYPTO_SHA512_H

src/init.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,11 @@ bool AppInitMain(NodeContext& node)
13841384
CScheduler::Function serviceLoop = std::bind(&CScheduler::serviceQueue, &scheduler);
13851385
threadGroup.create_thread(std::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));
13861386

1387+
// Gather some entropy once per minute.
1388+
scheduler.scheduleEvery([]{
1389+
RandAddPeriodic();
1390+
}, 60000);
1391+
13871392
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
13881393
GetMainSignals().RegisterWithMempoolSignals(mempool);
13891394

src/random.cpp

+33-87
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,22 @@
55

66
#include <random.h>
77

8+
#include <compat/cpuid.h>
89
#include <crypto/sha512.h>
910
#include <support/cleanse.h>
1011
#ifdef WIN32
1112
#include <compat.h> // for Windows API
1213
#include <wincrypt.h>
1314
#endif
14-
#include <logging.h> // for LogPrint()
15-
#include <sync.h> // for WAIT_LOCK
15+
#include <logging.h> // for LogPrintf()
16+
#include <sync.h> // for Mutex
1617
#include <util/time.h> // for GetTime()
1718

1819
#include <stdlib.h>
1920
#include <thread>
2021

22+
#include <randomenv.h>
23+
2124
#include <support/allocators/secure.h>
2225

2326
#ifndef WIN32
@@ -40,11 +43,6 @@
4043
#include <sys/sysctl.h>
4144
#endif
4245

43-
44-
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
45-
#include <cpuid.h>
46-
#endif
47-
4846
#include <openssl/err.h>
4947
#include <openssl/rand.h>
5048
#include <openssl/conf.h>
@@ -75,7 +73,7 @@ static inline int64_t GetPerformanceCounter() noexcept
7573
#endif
7674
}
7775

78-
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
76+
#ifdef HAVE_GETCPUID
7977
static bool g_rdrand_supported = false;
8078
static bool g_rdseed_supported = false;
8179
static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000;
@@ -86,15 +84,6 @@ static_assert(CPUID_F1_ECX_RDRAND == bit_RDRND, "Unexpected value for bit_RDRND"
8684
#ifdef bit_RDSEED
8785
static_assert(CPUID_F7_EBX_RDSEED == bit_RDSEED, "Unexpected value for bit_RDSEED");
8886
#endif
89-
static void inline GetCPUID(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d)
90-
{
91-
// We can't use __get_cpuid as it doesn't support subleafs.
92-
#ifdef __GNUC__
93-
__cpuid_count(leaf, subleaf, a, b, c, d);
94-
#else
95-
__asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf));
96-
#endif
97-
}
9887

9988
static void InitHardwareRand()
10089
{
@@ -263,44 +252,6 @@ static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA51
263252
memory_cleanse(buffer, sizeof(buffer));
264253
}
265254

266-
static void RandAddSeedPerfmon(CSHA512& hasher)
267-
{
268-
#ifdef WIN32
269-
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
270-
// Seed with the entire set of perfmon data
271-
272-
// This can take up to 2 seconds, so only do it every 10 minutes
273-
static int64_t nLastPerfmon;
274-
if (GetTime() < nLastPerfmon + 10 * 60)
275-
return;
276-
nLastPerfmon = GetTime();
277-
278-
std::vector<unsigned char> vData(250000, 0);
279-
long ret = 0;
280-
unsigned long nSize = 0;
281-
const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data
282-
while (true) {
283-
nSize = vData.size();
284-
ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize);
285-
if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize)
286-
break;
287-
vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially
288-
}
289-
RegCloseKey(HKEY_PERFORMANCE_DATA);
290-
if (ret == ERROR_SUCCESS) {
291-
hasher.Write(vData.data(), nSize);
292-
memory_cleanse(vData.data(), nSize);
293-
} else {
294-
// Performance data is only a best-effort attempt at improving the
295-
// situation when the OS randomness (and other sources) aren't
296-
// adequate. As a result, failure to read it is isn't considered critical,
297-
// so we don't call RandFailure().
298-
// TODO: Add logging when the logger is made functional before global
299-
// constructors have been invoked.
300-
}
301-
#endif
302-
}
303-
304255
#ifndef WIN32
305256
/** Fallback: get 32 bytes of system entropy from /dev/urandom. The most
306257
* compatible way to get cryptographic randomness on UNIX-ish platforms.
@@ -556,40 +507,30 @@ static void SeedSlow(CSHA512& hasher) noexcept
556507
}
557508

558509
/** Extract entropy from rng, strengthen it, and feed it into hasher. */
559-
static void SeedStrengthen(CSHA512& hasher, RNGState& rng) noexcept
510+
static void SeedStrengthen(CSHA512& hasher, RNGState& rng, int microseconds) noexcept
560511
{
561-
static std::atomic<int64_t> last_strengthen{0};
562-
int64_t last_time = last_strengthen.load();
563-
int64_t current_time = GetTimeMicros();
564-
if (current_time > last_time + 60000000) { // Only run once a minute
565-
// Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
566-
unsigned char strengthen_seed[32];
567-
rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false);
568-
// Strengthen it for 10ms (100ms on first run), and feed it into hasher.
569-
Strengthen(strengthen_seed, last_time == 0 ? 100000 : 10000, hasher);
570-
last_strengthen = current_time;
571-
}
512+
// Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
513+
unsigned char strengthen_seed[32];
514+
rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false);
515+
// Strengthen the seed, and feed it into hasher.
516+
Strengthen(strengthen_seed, microseconds, hasher);
572517
}
573518

574-
static void SeedSleep(CSHA512& hasher, RNGState& rng)
519+
static void SeedPeriodic(CSHA512& hasher, RNGState& rng)
575520
{
576521
// Everything that the 'fast' seeder includes
577522
SeedFast(hasher);
578523

579524
// High-precision timestamp
580525
SeedTimestamp(hasher);
581526

582-
// Sleep for 1ms
583-
MilliSleep(1);
584-
585-
// High-precision timestamp after sleeping (as we commit to both the time before and after, this measures the delay)
586-
SeedTimestamp(hasher);
587-
588-
// Windows performance monitor data (once every 10 minutes)
589-
RandAddSeedPerfmon(hasher);
527+
// Dynamic environment data (performance monitoring, ...)
528+
auto old_size = hasher.Size();
529+
RandAddDynamicEnv(hasher);
530+
LogPrintf("Feeding %i bytes of dynamic environment data into RNG\n", hasher.Size() - old_size);
590531

591-
// Strengthen every minute
592-
SeedStrengthen(hasher, rng);
532+
// Strengthen for 10 ms
533+
SeedStrengthen(hasher, rng, 10000);
593534
}
594535

595536
static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
@@ -600,17 +541,22 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
600541
// Everything that the 'slow' seeder includes.
601542
SeedSlow(hasher);
602543

603-
// Windows performance monitor data.
604-
RandAddSeedPerfmon(hasher);
544+
// Dynamic environment data (performance monitoring, ...)
545+
auto old_size = hasher.Size();
546+
RandAddDynamicEnv(hasher);
547+
548+
// Static environment data
549+
RandAddStaticEnv(hasher);
550+
LogPrintf("Feeding %i bytes of environment data into RNG\n", hasher.Size() - old_size);
605551

606-
// Strengthen
607-
SeedStrengthen(hasher, rng);
552+
// Strengthen for 100 ms
553+
SeedStrengthen(hasher, rng, 100000);
608554
}
609555

610556
enum class RNGLevel {
611557
FAST, //!< Automatically called by GetRandBytes
612558
SLOW, //!< Automatically called by GetStrongRandBytes
613-
SLEEP, //!< Called by RandAddSeedSleep()
559+
PERIODIC, //!< Called by RandAddPeriodic()
614560
};
615561

616562
static void ProcRand(unsigned char* out, int num, RNGLevel level)
@@ -628,8 +574,8 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
628574
case RNGLevel::SLOW:
629575
SeedSlow(hasher);
630576
break;
631-
case RNGLevel::SLEEP:
632-
SeedSleep(hasher, rng);
577+
case RNGLevel::PERIODIC:
578+
SeedPeriodic(hasher, rng);
633579
break;
634580
}
635581

@@ -652,7 +598,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
652598

653599
void GetRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::FAST); }
654600
void GetStrongRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::SLOW); }
655-
void RandAddSeedSleep() { ProcRand(nullptr, 0, RNGLevel::SLEEP); }
601+
void RandAddPeriodic() { ProcRand(nullptr, 0, RNGLevel::PERIODIC); }
656602

657603
bool g_mock_deterministic_tests{false};
658604

@@ -716,7 +662,7 @@ bool Random_SanityCheck()
716662
uint64_t start = GetPerformanceCounter();
717663

718664
/* This does not measure the quality of randomness, but it does test that
719-
* OSRandom() overwrites all 32 bytes of the output given a maximum
665+
* GetOSRand() overwrites all 32 bytes of the output given a maximum
720666
* number of tries.
721667
*/
722668
static const ssize_t MAX_TRIES = 1024;

src/random.h

+2-3
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
* sources used in the 'slow' seeder are included, but also:
5353
* - 256 bits from the hardware RNG (rdseed or rdrand) when available.
5454
* - (On Windows) Performance monitoring data from the OS.
55-
* - (On Windows) Through OpenSSL, the screen contents.
5655
* - Strengthen the entropy for 100 ms using repeated SHA512.
5756
*
5857
* When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and
@@ -85,11 +84,11 @@ uint256 GetRandHash() noexcept;
8584
void GetStrongRandBytes(unsigned char* buf, int num) noexcept;
8685

8786
/**
88-
* Sleep for 1ms, gather entropy from various sources, and feed them to the PRNG state.
87+
* Gather entropy from various expensive sources, and feed them to the PRNG state.
8988
*
9089
* Thread-safe.
9190
*/
92-
void RandAddSeedSleep();
91+
void RandAddPeriodic();
9392

9493
/**
9594
* Fast randomness source. This is seeded once with secure random data, but

0 commit comments

Comments
 (0)