diff --git a/configure.ac b/configure.ac index 9956136de8..f42a4a724d 100755 --- a/configure.ac +++ b/configure.ac @@ -877,13 +877,6 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include [ AC_MSG_RESULT(no)] ) -AC_MSG_CHECKING(for getentropy) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], - [[ getentropy(nullptr, 32) ]])], - [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_GETENTROPY, 1,[Define this symbol if the BSD getentropy system call 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/addrdb.cpp b/src/addrdb.cpp index c16078c41a..135ebf9176 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -40,7 +40,8 @@ template bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data) { // Generate random temporary filename - std::string tmpfn = strprintf("%s.%" PRIx64, prefix, GetPerformanceCounter()); + const uint16_t randv{GetRand()}; + std::string tmpfn = strprintf("%s.%04x", prefix, randv); // open temp output file, and associate with CAutoFile fs::path pathTmp = GetDataDir() / tmpfn; diff --git a/src/addrman.cpp b/src/addrman.cpp index fb896a629a..7d62aef2c9 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -221,7 +221,7 @@ void CAddrMan::Good_(const CService& addr, int64_t nTime) return; // find a bucket it is in now - int nRnd = GetRandInt(ADDRMAN_NEW_BUCKET_COUNT); + int nRnd = GetRand(ADDRMAN_NEW_BUCKET_COUNT); int nUBucket = -1; for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) { int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT; @@ -278,7 +278,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP int nFactor = 1; for (int n = 0; n < pinfo->nRefCount; n++) nFactor *= 2; - if (nFactor > 1 && (GetRandInt(nFactor) != 0)) + if (nFactor > 1 && (GetRand(nFactor) != 0)) return false; } else { pinfo = Create(addr, source, &nId); @@ -467,7 +467,7 @@ void CAddrMan::GetAddr_(std::vector &vAddr) if (vAddr.size() >= nNodes) break; - int nRndPos = GetRandInt(vRandom.size() - n) + n; + int nRndPos = GetRand(vRandom.size() - n) + n; SwapRandom(n, nRndPos); assert(mapInfo.count(vRandom[n]) == 1); @@ -516,5 +516,5 @@ void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices) } int CAddrMan::RandomInt(int nMax){ - return GetRandInt(nMax); -} \ No newline at end of file + return GetRand(nMax); +} diff --git a/src/addrman.h b/src/addrman.h index 7b37f9a9bc..47a590054e 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -234,7 +234,7 @@ class CAddrMan //! Select an address to connect to, if newOnly is set to true, only the new table is selected from. CAddrInfo Select_(bool newOnly); - //! Wraps GetRandInt to allow tests to override RandomInt and make it determinismistic. + //! Wraps GetRand to allow tests to override RandomInt and make it determinismistic. virtual int RandomInt(int nMax); #ifdef DEBUG_ADDRMAN diff --git a/src/gridcoin/scraper/scraper.cpp b/src/gridcoin/scraper/scraper.cpp index 5ccb008ddb..95788b33e6 100755 --- a/src/gridcoin/scraper/scraper.cpp +++ b/src/gridcoin/scraper/scraper.cpp @@ -6175,7 +6175,7 @@ UniValue testnewsb(const UniValue& params, bool fHelp) if (PastConvergencesSize > 1) { - int i = GetRandInt(PastConvergencesSize - 1); + int i = GetRand(PastConvergencesSize - 1); _log(logattribute::INFO, "testnewsb", "ValidateSuperblock random past RandomPastConvergedManifest index " + ToString(i) + " selected."); diff --git a/src/gridcoin/staking/spam.h b/src/gridcoin/staking/spam.h index 41a27eaa1a..84918e8eeb 100644 --- a/src/gridcoin/staking/spam.h +++ b/src/gridcoin/staking/spam.h @@ -201,12 +201,11 @@ class SeenStakes // ...to produce a distribution for values of x with a minimal rate of // collision. // - using limit_t = std::numeric_limits; static const size_t w = sizeof(size_t) * 8; static const size_t M = std::log2(m_proofs_seen.size()); - static const size_t a = (GetRand(limit_t::max()) * 2) + 1; - static const size_t b = GetRand(std::pow(2, w - M) - 1); + static const size_t a = (GetRand() * 2) + 1; + static const size_t b = GetRand(std::pow(2, w - M) - 1); size_t x = 0; diff --git a/src/key.cpp b/src/key.cpp index 1106f73100..2afde1763c 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -165,7 +165,7 @@ bool CKey::Check(const unsigned char *vch) { void CKey::MakeNewKey(bool fCompressedIn) { do { - GetStrongRandBytes(keydata.data(), keydata.size()); + GetStrongRandBytes(keydata); } while (!Check(keydata.data())); fValid = true; fCompressed = fCompressedIn; @@ -250,7 +250,7 @@ bool CKey::VerifyPubKey(const CPubKey& pubkey) const { } unsigned char rnd[8]; std::string str = "Bitcoin key verification\n"; - GetRandBytes(rnd, sizeof(rnd)); + GetRandBytes(rnd); uint256 hash; CHash256().Write(MakeUCharSpan(str)).Write(rnd).Finalize(hash); std::vector vchSig; @@ -378,7 +378,7 @@ void ECC_Start() { { // Pass in a random blinding seed to the secp256k1 context. std::vector> vseed(32); - GetRandBytes(vseed.data(), 32); + GetRandBytes(vseed); bool ret = secp256k1_context_randomize(ctx, vseed.data()); assert(ret); } diff --git a/src/main.cpp b/src/main.cpp index 30c8f1d1be..32aad9f3aa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2929,7 +2929,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { uint64_t nonce = 0; while (nonce == 0) { - GetRandBytes((unsigned char*)&nonce, sizeof(nonce)); + GetRandBytes({(unsigned char*)&nonce, sizeof(nonce)}); } pto->fPingQueued = false; pto->nPingUsecStart = GetTimeMicros(); diff --git a/src/net.cpp b/src/net.cpp index b7d0b699d6..e6e26e5d90 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -212,7 +212,7 @@ void AdvertiseLocal(CNode *pnode) // If discovery is enabled, sometimes give our peer the address it // tells us that it sees us as in case it has a better idea of our // address than we do. - const int randomNumber = GetRandInt((GetnScore(addrLocal) > LOCAL_MANUAL) ? 3+1 : 1+1); + const int randomNumber = GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 3+1 : 1+1); if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() || randomNumber == 0)) { @@ -486,7 +486,7 @@ void CNode::PushVersion() int64_t nTime = GetAdjustedTime(); CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(LookupNumeric("0.0.0.0", 0))); CAddress addrMe = CAddress(CService(), nLocalServices); - GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); + GetRandBytes({(unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)}); LogPrint(BCLog::LogFlags::NET, "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), addr.ToString()); diff --git a/src/random.cpp b/src/random.cpp index 2ebc5bd86f..368fa1f9bc 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -16,9 +16,11 @@ #include // for LogPrintf() #include #include +#include #include // for Mutex #include // for GetTimeMicros() +#include #include #include @@ -31,10 +33,8 @@ #include #include #endif -#if defined(HAVE_GETENTROPY) || (defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX)) -#include -#endif #if defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX) +#include #include #endif #ifdef HAVE_SYSCTL_ARND @@ -304,16 +304,14 @@ void GetOSRand(unsigned char *ent32) RandFailure(); } } -#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. +#elif defined(__OpenBSD__) + /* OpenBSD. From the arc4random(3) man page: + "Use of these functions is encouraged for almost all random number + consumption because the other interfaces are deficient in either + quality, portability, standardization, or availability." + The function call is always successful. */ - if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { - RandFailure(); - } + arc4random_buf(ent32, NUM_OS_RANDOM_BYTES); // Silence a compiler warning about unused function. (void)GetDevURandom; #elif defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX) @@ -581,27 +579,22 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) noexcept } } -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 GetRandBytes(Span bytes) noexcept { ProcRand(bytes.data(), bytes.size(), RNGLevel::FAST); } +void GetStrongRandBytes(Span bytes) noexcept { ProcRand(bytes.data(), bytes.size(), RNGLevel::SLOW); } void RandAddPeriodic() noexcept { ProcRand(nullptr, 0, RNGLevel::PERIODIC); } void RandAddEvent(const uint32_t event_info) noexcept { GetRNGState().AddEvent(event_info); } bool g_mock_deterministic_tests{false}; -uint64_t GetRand(uint64_t nMax) noexcept +uint64_t GetRandInternal(uint64_t nMax) noexcept { return FastRandomContext(g_mock_deterministic_tests).randrange(nMax); } -int GetRandInt(int nMax) noexcept -{ - return GetRand(nMax); -} - uint256 GetRandHash() noexcept { uint256 hash; - GetRandBytes((unsigned char*)&hash, sizeof(hash)); + GetRandBytes(hash); return hash; } @@ -714,3 +707,9 @@ void RandomInit() ReportHardwareRand(); } + +std::chrono::microseconds GetExponentialRand(std::chrono::microseconds now, std::chrono::seconds average_interval) +{ + double unscaled = -std::log1p(GetRand(uint64_t{1} << 48) * -0.0000000000000035527136788 /* -1/2^48 */); + return now + std::chrono::duration_cast(unscaled * average_interval + 0.5us); +} diff --git a/src/random.h b/src/random.h index 95362b421b..821927af92 100644 --- a/src/random.h +++ b/src/random.h @@ -8,9 +8,10 @@ #include #include +#include #include -#include // For std::chrono::microseconds +#include #include #include @@ -66,9 +67,19 @@ * * Thread-safe. */ -void GetRandBytes(unsigned char* buf, int num) noexcept; +void GetRandBytes(Span bytes) noexcept; /** Generate a uniform random integer in the range [0..range). Precondition: range > 0 */ -uint64_t GetRand(uint64_t nMax) noexcept; +uint64_t GetRandInternal(uint64_t nMax) noexcept; +/** Generate a uniform random integer of type T in the range [0..nMax) + * nMax defaults to std::numeric_limits::max() + * Precondition: nMax > 0, T is an integral type, no larger than uint64_t + */ +template +T GetRand(T nMax=std::numeric_limits::max()) noexcept { + static_assert(std::is_integral(), "T must be integral"); + static_assert(std::numeric_limits::max() <= std::numeric_limits::max(), "GetRand only supports up to uint64_t"); + return T(GetRandInternal(nMax)); +} /** Generate a uniform random duration in the range [0..max). Precondition: max.count() > 0 */ template D GetRandomDuration(typename std::common_type::type max) noexcept @@ -82,6 +93,18 @@ D GetRandomDuration(typename std::common_type::type max) noexcept }; constexpr auto GetRandMicros = GetRandomDuration; constexpr auto GetRandMillis = GetRandomDuration; + +/** + * Return a timestamp in the future sampled from an exponential distribution + * (https://en.wikipedia.org/wiki/Exponential_distribution). This distribution + * is memoryless and should be used for repeated network events (e.g. sending a + * certain type of message) to minimize leaking information to observers. + * + * The probability of an event occuring before time x is 1 - e^-(x/a) where a + * is the average interval between events. + * */ +std::chrono::microseconds GetExponentialRand(std::chrono::microseconds now, std::chrono::seconds average_interval); + int GetRandInt(int nMax) noexcept; uint256 GetRandHash() noexcept; @@ -93,7 +116,7 @@ uint256 GetRandHash() noexcept; * * Thread-safe. */ -void GetStrongRandBytes(unsigned char* buf, int num) noexcept; +void GetStrongRandBytes(Span bytes) noexcept; /** * Gather entropy from various expensive sources, and feed them to the PRNG state. diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 6e9d215864..5bf4a69b26 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -594,7 +594,7 @@ void StartRPCThreads() (gArgs.GetArg("-rpcuser", "") == gArgs.GetArg("-rpcpassword", "")))) { unsigned char rand_pwd[32]; - GetRandBytes(rand_pwd, sizeof(rand_pwd)); + GetRandBytes({rand_pwd, sizeof(rand_pwd)}); string strWhatAmI = "To use gridcoind"; if (gArgs.IsArgSet("-server")) strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp index 452a6b7202..289b063445 100644 --- a/src/test/random_tests.cpp +++ b/src/test/random_tests.cpp @@ -28,8 +28,8 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests) FastRandomContext ctx2(true); for (int i = 10; i > 0; --i) { - BOOST_CHECK_EQUAL(GetRand(std::numeric_limits::max()), uint64_t{10393729187455219830U}); - BOOST_CHECK_EQUAL(GetRandInt(std::numeric_limits::max()), int{769702006}); + BOOST_CHECK_EQUAL(GetRand(), uint64_t{10393729187455219830U}); + BOOST_CHECK_EQUAL(GetRand(), int{769702006}); BOOST_CHECK_EQUAL(GetRandMicros(std::chrono::hours{1}).count(), 2917185654); BOOST_CHECK_EQUAL(GetRandMillis(std::chrono::hours{1}).count(), 2144374); } @@ -49,8 +49,8 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests) // Check that a nondeterministic ones are not g_mock_deterministic_tests = false; for (int i = 10; i > 0; --i) { - BOOST_CHECK(GetRand(std::numeric_limits::max()) != uint64_t{10393729187455219830U}); - BOOST_CHECK(GetRandInt(std::numeric_limits::max()) != int{769702006}); + BOOST_CHECK(GetRand() != uint64_t{10393729187455219830U}); + BOOST_CHECK(GetRand() != int{769702006}); BOOST_CHECK(GetRandMicros(std::chrono::hours{1}) != std::chrono::microseconds{2917185654}); BOOST_CHECK(GetRandMillis(std::chrono::hours{1}) != std::chrono::milliseconds{2144374}); } diff --git a/src/util.h b/src/util.h index c21fc77d29..43480785ee 100644 --- a/src/util.h +++ b/src/util.h @@ -208,19 +208,6 @@ inline std::string leftTrim(std::string src, char chr) return src; } -inline int64_t GetPerformanceCounter() -{ - int64_t nCounter = 0; -#ifdef WIN32 - QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); -#else - timeval t; - gettimeofday(&t, nullptr); - nCounter = (int64_t) t.tv_sec * 1000000 + t.tv_usec; -#endif - return nCounter; -} - /** Median filter over a stream of values. * Returns the median of the last N numbers */ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c12db1915e..df01975f35 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -322,12 +322,12 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) CKeyingMaterial vMasterKey; vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); - GetStrongRandBytes(vMasterKey.data(), WALLET_CRYPTO_KEY_SIZE); + GetStrongRandBytes(vMasterKey); CMasterKey kMasterKey(nDerivationMethodIndex); kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); - GetStrongRandBytes(kMasterKey.vchSalt.data(), WALLET_CRYPTO_SALT_SIZE); + GetStrongRandBytes(kMasterKey.vchSalt); CCrypter crypter; int64_t nStartTime = GetTimeMillis(); @@ -2178,7 +2178,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, } // Insert change output at random position in the transaction: - vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); + vector::iterator position = wtxNew.vout.begin() + GetRand(wtxNew.vout.size()); wtxNew.vout.insert(position, CTxOut(nChange, scriptChange)); } else