diff options
Diffstat (limited to 'src/random.cpp')
-rw-r--r-- | src/random.cpp | 118 |
1 files changed, 111 insertions, 7 deletions
diff --git a/src/random.cpp b/src/random.cpp index de7553c825..7e0e94439e 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -27,22 +27,29 @@ #include <sys/syscall.h> #include <linux/random.h> #endif -#ifdef HAVE_GETENTROPY +#if defined(HAVE_GETENTROPY) || (defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX)) #include <unistd.h> #endif +#if defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX) +#include <sys/random.h> +#endif #ifdef HAVE_SYSCTL_ARND #include <sys/sysctl.h> #endif #include <mutex> +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +#include <cpuid.h> +#endif + #include <openssl/err.h> #include <openssl/rand.h> -static void RandFailure() +[[noreturn]] static void RandFailure() { LogPrintf("Failed to read randomness, aborting\n"); - abort(); + std::abort(); } static inline int64_t GetPerformanceCounter() @@ -65,6 +72,61 @@ 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() +{ + uint32_t eax, ebx, ecx, edx; + if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) { + LogPrintf("Using RdRand as an additional entropy source\n"); + rdrand_supported = true; + } + hwrand_initialized.store(true); +} +#else +static void RDRandInit() {} +#endif + +static bool GetHWRand(unsigned char* ent32) { +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) + assert(hwrand_initialized.load(std::memory_order_relaxed)); + if (rdrand_supported) { + uint8_t ok; + // Not all assemblers support the rdrand instruction, write it in hex. +#ifdef __i386__ + for (int iter = 0; iter < 4; ++iter) { + uint32_t r1, r2; + __asm__ volatile (".byte 0x0f, 0xc7, 0xf0;" // rdrand %eax + ".byte 0x0f, 0xc7, 0xf2;" // rdrand %edx + "setc %2" : + "=a"(r1), "=d"(r2), "=q"(ok) :: "cc"); + if (!ok) return false; + WriteLE32(ent32 + 8 * iter, r1); + WriteLE32(ent32 + 8 * iter + 4, r2); + } +#else + uint64_t r1, r2, r3, r4; + __asm__ volatile (".byte 0x48, 0x0f, 0xc7, 0xf0, " // rdrand %rax + "0x48, 0x0f, 0xc7, 0xf3, " // rdrand %rbx + "0x48, 0x0f, 0xc7, 0xf1, " // rdrand %rcx + "0x48, 0x0f, 0xc7, 0xf2; " // rdrand %rdx + "setc %4" : + "=a"(r1), "=b"(r2), "=c"(r3), "=d"(r4), "=q"(ok) :: "cc"); + if (!ok) return false; + WriteLE64(ent32, r1); + WriteLE64(ent32 + 8, r2); + WriteLE64(ent32 + 16, r3); + WriteLE64(ent32 + 24, r4); +#endif + return true; + } +#endif + return false; +} + void RandAddSeed() { // Seed with CPU performance counter @@ -93,7 +155,7 @@ static void RandAddSeedPerfmon() 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", NULL, NULL, vData.data(), &nSize); + 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 @@ -127,6 +189,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; @@ -140,7 +203,7 @@ void GetOSRand(unsigned char *ent32) { #if defined(WIN32) HCRYPTPROV hProvider; - int ret = CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + int ret = CryptAcquireContextW(&hProvider, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); if (!ret) { RandFailure(); } @@ -167,14 +230,25 @@ void GetOSRand(unsigned char *ent32) RandFailure(); } } -#elif defined(HAVE_GETENTROPY) +#elif defined(HAVE_GETENTROPY) && defined(__OpenBSD__) /* On OpenBSD this can return up to 256 bytes of entropy, will return an * error if more are requested. * The call cannot return less than the requested number of bytes. + getentropy is explicitly limited to openbsd here, as a similar (but not + the same) function may exist on other platforms via glibc. */ if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { RandFailure(); } +#elif defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX) + // We need a fallback for OSX < 10.12 + if (&getentropy != nullptr) { + if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { + RandFailure(); + } + } else { + GetDevURandom(ent32); + } #elif defined(HAVE_SYSCTL_ARND) /* FreeBSD and similar. It is possible for the call to return less * bytes than requested, so need to read in a loop. @@ -183,7 +257,7 @@ void GetOSRand(unsigned char *ent32) int have = 0; do { size_t len = NUM_OS_RANDOM_BYTES - have; - if (sysctl(name, ARRAYLEN(name), ent32 + have, &len, NULL, 0) != 0) { + if (sysctl(name, ARRAYLEN(name), ent32 + have, &len, nullptr, 0) != 0) { RandFailure(); } have += len; @@ -255,6 +329,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); + } + // Combine with and update state { std::unique_lock<std::mutex> lock(cs_rng_state); @@ -304,6 +383,26 @@ void FastRandomContext::RandomSeed() requires_seed = false; } +uint256 FastRandomContext::rand256() +{ + if (bytebuf_size < 32) { + FillByteBuffer(); + } + uint256 ret; + memcpy(ret.begin(), bytebuf + 64 - bytebuf_size, 32); + bytebuf_size -= 32; + return ret; +} + +std::vector<unsigned char> FastRandomContext::randbytes(size_t len) +{ + std::vector<unsigned char> ret(len); + if (len > 0) { + rng.Output(&ret[0], len); + } + return ret; +} + FastRandomContext::FastRandomContext(const uint256& seed) : requires_seed(false), bytebuf_size(0), bitbuf_size(0) { rng.SetKey(seed.begin(), 32); @@ -361,3 +460,8 @@ FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDete uint256 seed; rng.SetKey(seed.begin(), 32); } + +void RandomInit() +{ + RDRandInit(); +} |