@@ -145,18 +145,8 @@ static bool GetHardwareRand(unsigned char* ent32) {
145145 return false ;
146146}
147147
148- void RandAddSeed ( )
148+ static void RandAddSeedPerfmon (CSHA512& hasher )
149149{
150- // Seed with CPU performance counter
151- int64_t nCounter = GetPerformanceCounter ();
152- RAND_add (&nCounter, sizeof (nCounter), 1.5 );
153- memory_cleanse ((void *)&nCounter, sizeof (nCounter));
154- }
155-
156- static void RandAddSeedPerfmon ()
157- {
158- RandAddSeed ();
159-
160150#ifdef WIN32
161151 // Don't need this on Linux, OpenSSL automatically uses /dev/urandom
162152 // Seed with the entire set of perfmon data
@@ -180,7 +170,7 @@ static void RandAddSeedPerfmon()
180170 }
181171 RegCloseKey (HKEY_PERFORMANCE_DATA);
182172 if (ret == ERROR_SUCCESS) {
183- RAND_add (vData.data (), nSize, nSize / 100.0 );
173+ hasher. Write (vData.data (), nSize);
184174 memory_cleanse (vData.data (), nSize);
185175 } else {
186176 // Performance data is only a best-effort attempt at improving the
@@ -288,13 +278,6 @@ void GetOSRand(unsigned char *ent32)
288278#endif
289279}
290280
291- void GetRandBytes (unsigned char * buf, int num)
292- {
293- if (RAND_bytes (buf, num) != 1 ) {
294- RandFailure ();
295- }
296- }
297-
298281void LockingCallbackOpenSSL (int mode, int i, const char * file, int line);
299282
300283namespace {
@@ -303,6 +286,7 @@ struct RNGState {
303286 Mutex m_mutex;
304287 unsigned char m_state[32 ] GUARDED_BY(m_mutex) = {0 };
305288 uint64_t m_counter GUARDED_BY (m_mutex) = 0;
289+ bool m_strongly_seeded GUARDED_BY (m_mutex) = false;
306290 std::unique_ptr<Mutex[]> m_mutex_openssl;
307291
308292 RNGState ()
@@ -319,14 +303,6 @@ struct RNGState {
319303 // or corrupt. Explicitly tell OpenSSL not to try to load the file. The result for our libs will be
320304 // that the config appears to have been loaded and there are no modules/engines available.
321305 OPENSSL_no_config ();
322-
323- #ifdef WIN32
324- // Seed OpenSSL PRNG with current contents of the screen
325- RAND_screen ();
326- #endif
327-
328- // Seed OpenSSL PRNG with performance counter
329- RandAddSeed ();
330306 }
331307
332308 ~RNGState ()
@@ -337,14 +313,19 @@ struct RNGState {
337313 CRYPTO_set_locking_callback (nullptr );
338314 }
339315
340- /* * Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher. */
341- void MixExtract (unsigned char * out, size_t num, CSHA512&& hasher)
316+ /* * Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher.
317+ *
318+ * If this function has never been called with strong_seed = true, false is returned.
319+ */
320+ bool MixExtract (unsigned char * out, size_t num, CSHA512&& hasher, bool strong_seed)
342321 {
343322 assert (num <= 32 );
344323 unsigned char buf[64 ];
345324 static_assert (sizeof (buf) == CSHA512::OUTPUT_SIZE, " Buffer needs to have hasher's output size" );
325+ bool ret;
346326 {
347327 LOCK (m_mutex);
328+ ret = (m_strongly_seeded |= strong_seed);
348329 // Write the current state of the RNG into the hasher
349330 hasher.Write (m_state, 32 );
350331 // Write a new counter number into the state
@@ -363,6 +344,7 @@ struct RNGState {
363344 // Best effort cleanup of internal state
364345 hasher.Reset ();
365346 memory_cleanse (buf, 64 );
347+ return ret;
366348 }
367349};
368350
@@ -386,61 +368,128 @@ void LockingCallbackOpenSSL(int mode, int i, const char* file, int line) NO_THRE
386368 }
387369}
388370
389- static void AddDataToRng (void * data, size_t len, RNGState& rng);
371+ static void SeedTimestamp (CSHA512& hasher)
372+ {
373+ int64_t perfcounter = GetPerformanceCounter ();
374+ hasher.Write ((const unsigned char *)&perfcounter, sizeof (perfcounter));
375+ }
390376
391- void RandAddSeedSleep ( )
377+ static void SeedFast (CSHA512& hasher )
392378{
393- RNGState& rng = GetRNGState () ;
379+ unsigned char buffer[ 32 ] ;
394380
395- int64_t nPerfCounter1 = GetPerformanceCounter ();
396- std::this_thread::sleep_for ( std::chrono::milliseconds ( 1 )) ;
397- int64_t nPerfCounter2 = GetPerformanceCounter ( );
381+ // Stack pointer to indirectly commit to thread/callstack
382+ const unsigned char * ptr = buffer ;
383+ hasher. Write (( const unsigned char *)&ptr, sizeof (ptr) );
398384
399- // Combine with and update state
400- AddDataToRng (&nPerfCounter1, sizeof (nPerfCounter1), rng );
401- AddDataToRng (&nPerfCounter2 , sizeof (nPerfCounter2), rng );
385+ // Hardware randomness is very fast when available; use it always.
386+ bool have_hw_rand = GetHardwareRand (buffer );
387+ if (have_hw_rand) hasher. Write (buffer , sizeof (buffer) );
402388
403- memory_cleanse (&nPerfCounter1, sizeof (nPerfCounter1));
404- memory_cleanse (&nPerfCounter2, sizeof (nPerfCounter2) );
389+ // High-precision timestamp
390+ SeedTimestamp (hasher );
405391}
406392
407- static void AddDataToRng (void * data, size_t len, RNGState& rng) {
408- CSHA512 hasher;
409- hasher.Write ((const unsigned char *)&len, sizeof (len));
410- hasher.Write ((const unsigned char *)data, len);
411- rng.MixExtract (nullptr , 0 , std::move (hasher));
393+ static void SeedSlow (CSHA512& hasher)
394+ {
395+ unsigned char buffer[32 ];
396+
397+ // Everything that the 'fast' seeder includes
398+ SeedFast (hasher);
399+
400+ // OS randomness
401+ GetOSRand (buffer);
402+ hasher.Write (buffer, sizeof (buffer));
403+
404+ // OpenSSL RNG (for now)
405+ RAND_bytes (buffer, sizeof (buffer));
406+ hasher.Write (buffer, sizeof (buffer));
407+
408+ // High-precision timestamp.
409+ //
410+ // Note that we also commit to a timestamp in the Fast seeder, so we indirectly commit to a
411+ // benchmark of all the entropy gathering sources in this function).
412+ SeedTimestamp (hasher);
412413}
413414
414- void GetStrongRandBytes ( unsigned char * out, int num )
415+ static void SeedSleep (CSHA512& hasher )
415416{
416- RNGState& rng = GetRNGState ();
417+ // Everything that the 'fast' seeder includes
418+ SeedFast (hasher);
417419
418- assert (num <= 32 );
419- CSHA512 hasher;
420- unsigned char buf[64 ];
420+ // High-precision timestamp
421+ SeedTimestamp (hasher);
421422
422- // First source: OpenSSL's RNG
423- RandAddSeedPerfmon ();
424- GetRandBytes (buf, 32 );
425- hasher.Write (buf, 32 );
423+ // Sleep for 1ms
424+ MilliSleep (1 );
426425
427- // Second source: OS RNG
428- GetOSRand (buf);
429- hasher.Write (buf, 32 );
426+ // High-precision timestamp after sleeping (as we commit to both the time before and after, this measures the delay)
427+ SeedTimestamp (hasher);
430428
431- // Third source: HW RNG, if available.
432- if (GetHardwareRand (buf)) {
433- hasher.Write (buf, 32 );
429+ // Windows performance monitor data (once every 10 minutes)
430+ RandAddSeedPerfmon (hasher);
431+ }
432+
433+ static void SeedStartup (CSHA512& hasher)
434+ {
435+ #ifdef WIN32
436+ RAND_screen ();
437+ #endif
438+
439+ // Everything that the 'slow' seeder includes.
440+ SeedSlow (hasher);
441+
442+ // Windows performance monitor data.
443+ RandAddSeedPerfmon (hasher);
444+ }
445+
446+ enum class RNGLevel {
447+ FAST, // !< Automatically called by GetRandBytes
448+ SLOW, // !< Automatically called by GetStrongRandBytes
449+ SLEEP, // !< Called by RandAddSeedSleep()
450+ };
451+
452+ static void ProcRand (unsigned char * out, int num, RNGLevel level)
453+ {
454+ // Make sure the RNG is initialized first (as all Seed* function possibly need hwrand to be available).
455+ RNGState& rng = GetRNGState ();
456+
457+ assert (num <= 32 );
458+
459+ CSHA512 hasher;
460+ switch (level) {
461+ case RNGLevel::FAST:
462+ SeedFast (hasher);
463+ break ;
464+ case RNGLevel::SLOW:
465+ SeedSlow (hasher);
466+ break ;
467+ case RNGLevel::SLEEP:
468+ SeedSleep (hasher);
469+ break ;
434470 }
435471
436472 // Combine with and update state
437- rng.MixExtract (out, num, std::move (hasher));
473+ if (!rng.MixExtract (out, num, std::move (hasher), false )) {
474+ // On the first invocation, also seed with SeedStartup().
475+ CSHA512 startup_hasher;
476+ SeedStartup (startup_hasher);
477+ rng.MixExtract (out, num, std::move (startup_hasher), true );
478+ }
438479
439- // Produce output
440- memcpy (out, buf, num);
441- memory_cleanse (buf, 64 );
480+ // For anything but the 'fast' level, feed the resulting RNG output (after an additional hashing step) back into OpenSSL.
481+ if (level != RNGLevel::FAST) {
482+ unsigned char buf[64 ];
483+ CSHA512 ().Write (out, num).Finalize (buf);
484+ RAND_add (buf, sizeof (buf), num);
485+ memory_cleanse (buf, 64 );
486+ }
442487}
443488
489+ void GetRandBytes (unsigned char * buf, int num) { ProcRand (buf, num, RNGLevel::FAST); }
490+ void GetStrongRandBytes (unsigned char * buf, int num) { ProcRand (buf, num, RNGLevel::SLOW); }
491+ void RandAddSeedSleep () { ProcRand (nullptr , 0 , RNGLevel::SLEEP); }
492+
444493uint64_t GetRand (uint64_t nMax)
445494{
446495 if (nMax == 0 )
@@ -539,8 +588,10 @@ bool Random_SanityCheck()
539588 if (stop == start) return false ;
540589
541590 // We called GetPerformanceCounter. Use it as entropy.
542- RAND_add ((const unsigned char *)&start, sizeof (start), 1 );
543- RAND_add ((const unsigned char *)&stop, sizeof (stop), 1 );
591+ CSHA512 to_add;
592+ to_add.Write ((const unsigned char *)&start, sizeof (start));
593+ to_add.Write ((const unsigned char *)&stop, sizeof (stop));
594+ GetRNGState ().MixExtract (nullptr , 0 , std::move (to_add), false );
544595
545596 return true ;
546597}
@@ -571,7 +622,7 @@ FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexce
571622void RandomInit ()
572623{
573624 // Invoke RNG code to trigger initialization (if not already performed)
574- GetRNGState ( );
625+ ProcRand ( nullptr , 0 , RNGLevel::FAST );
575626
576627 ReportHardwareRand ();
577628}
0 commit comments