diff --git a/src/randomenv.cpp b/src/randomenv.cpp index c3bcb1138..8339f6918 100644 --- a/src/randomenv.cpp +++ b/src/randomenv.cpp @@ -1,69 +1,162 @@ // 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 #include #include +#include +#ifndef WIN32 +#include +#include +#include +#endif +#ifdef __MACH__ +#include +#include +#include +#endif 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; // Bail out at more than 10MB of performance data const size_t nMaxSize = 10000000; 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; } // Grow size of buffer exponentially vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); } 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 } +/** 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, uint8_t *>::value, + "Calling operator<<(CSHA512, uint8_t*) 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 uint8_t *>::value, + "Calling operator<<(CSHA512, const uint8_t*) is " + "probably not what you want"); + hasher.Write((const uint8_t *)&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) {} +void RandAddStaticEnv(CSHA512 &hasher) { +#ifdef WIN32 + hasher << GetCurrentProcessId() << GetCurrentThreadId(); +#else + hasher << getpid(); +#endif + hasher << std::this_thread::get_id(); +}