diff --git a/src/net.cpp b/src/net.cpp --- a/src/net.cpp +++ b/src/net.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -457,6 +458,10 @@ pszDest ? pszDest : "", false, block_relay_only); pnode->AddRef(); + // We're making a new connection, harvest entropy from the time (and our + // peer count) + RandAddEvent((uint32_t)id); + return pnode; } @@ -721,8 +726,13 @@ msg.m_message_size = hdr.nMessageSize; msg.m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE; + // We just received a message off the wire, harvest entropy from the time + // (and the message checksum) + RandAddEvent(ReadLE32(hash.begin())); + msg.m_valid_checksum = (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) == 0); + if (!msg.m_valid_checksum) { LogPrint( BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s\n", @@ -1130,6 +1140,10 @@ LOCK(cs_vNodes); vNodes.push_back(pnode); } + + // We received a new connection, harvest entropy from the time (and our peer + // count) + RandAddEvent((uint32_t)id); } void CConnman::DisconnectNodes() { diff --git a/src/random.h b/src/random.h --- a/src/random.h +++ b/src/random.h @@ -94,6 +94,14 @@ */ void RandAddPeriodic() noexcept; +/** + * Gathers entropy from the low bits of the time at which events occur. Should + * be called with a uint32_t describing the event at the time an event occurs. + * + * Thread-safe. + */ +void RandAddEvent(const uint32_t event_info) noexcept; + /** * Fast randomness source. This is seeded once with secure random data, but * is completely deterministic and does not gather more entropy after that. diff --git a/src/random.cpp b/src/random.cpp --- a/src/random.cpp +++ b/src/random.cpp @@ -10,6 +10,7 @@ #include #endif #include +#include #include #include // for LogPrintf() #include @@ -403,11 +404,43 @@ uint64_t m_counter GUARDED_BY(m_mutex) = 0; bool m_strongly_seeded GUARDED_BY(m_mutex) = false; + Mutex m_events_mutex; + CSHA256 m_events_hasher GUARDED_BY(m_events_mutex); + public: RNGState() noexcept { InitHardwareRand(); } ~RNGState() {} + void AddEvent(uint32_t event_info) noexcept { + LOCK(m_events_mutex); + + m_events_hasher.Write((const uint8_t *)&event_info, sizeof(event_info)); + // Get the low four bytes of the performance counter. This translates to + // roughly the subsecond part. + uint32_t perfcounter = (GetPerformanceCounter() & 0xffffffff); + m_events_hasher.Write((const uint8_t *)&perfcounter, + sizeof(perfcounter)); + } + + /** + * Feed (the hash of) all events added through AddEvent() to hasher. + */ + void SeedEvents(CSHA512 &hasher) noexcept { + // We use only SHA256 for the events hashing to get the ASM speedups we + // have for SHA256, since we want it to be fast as network peers may be + // able to trigger it repeatedly. + LOCK(m_events_mutex); + + uint8_t events_hash[32]; + m_events_hasher.Finalize(events_hash); + hasher.Write(events_hash, 32); + + // Re-initialize the hasher with the finalized state to use later. + m_events_hasher.Reset(); + m_events_hasher.Write(events_hash, 32); + } + /** * Extract up to 32 bytes of entropy from the RNG state, mixing in new * entropy from hasher. @@ -482,7 +515,7 @@ SeedTimestamp(hasher); } -static void SeedSlow(CSHA512 &hasher) noexcept { +static void SeedSlow(CSHA512 &hasher, RNGState &rng) noexcept { uint8_t buffer[32]; // Everything that the 'fast' seeder includes @@ -492,6 +525,9 @@ GetOSRand(buffer); hasher.Write(buffer, sizeof(buffer)); + // Add the events hasher into the mix + rng.SeedEvents(hasher); + // High-precision timestamp. // // Note that we also commit to a timestamp in the Fast seeder, so we @@ -519,6 +555,9 @@ // High-precision timestamp SeedTimestamp(hasher); + // Add the events hasher into the mix + rng.SeedEvents(hasher); + // Dynamic environment data (performance monitoring, ...) auto old_size = hasher.Size(); RandAddDynamicEnv(hasher); @@ -535,7 +574,7 @@ SeedHardwareSlow(hasher); // Everything that the 'slow' seeder includes. - SeedSlow(hasher); + SeedSlow(hasher, rng); // Dynamic environment data (performance monitoring, ...) auto old_size = hasher.Size(); @@ -556,7 +595,7 @@ PERIODIC, //!< Called by RandAddPeriodic() }; -static void ProcRand(uint8_t *out, int num, RNGLevel level) { +static void ProcRand(uint8_t *out, int num, RNGLevel level) noexcept { // Make sure the RNG is initialized first (as all Seed* function possibly // need hwrand to be available). RNGState &rng = GetRNGState(); @@ -569,7 +608,7 @@ SeedFast(hasher); break; case RNGLevel::SLOW: - SeedSlow(hasher); + SeedSlow(hasher, rng); break; case RNGLevel::PERIODIC: SeedPeriodic(hasher, rng); @@ -595,6 +634,10 @@ 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 {