@@ -145,18 +145,8 @@ static bool GetHardwareRand(unsigned char* ent32) {
145
145
return false ;
146
146
}
147
147
148
- void RandAddSeed ( )
148
+ static void RandAddSeedPerfmon (CSHA512& hasher )
149
149
{
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
-
160
150
#ifdef WIN32
161
151
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
162
152
// Seed with the entire set of perfmon data
@@ -180,7 +170,7 @@ static void RandAddSeedPerfmon()
180
170
}
181
171
RegCloseKey (HKEY_PERFORMANCE_DATA);
182
172
if (ret == ERROR_SUCCESS) {
183
- RAND_add (vData.data (), nSize, nSize / 100.0 );
173
+ hasher. Write (vData.data (), nSize);
184
174
memory_cleanse (vData.data (), nSize);
185
175
} else {
186
176
// Performance data is only a best-effort attempt at improving the
@@ -288,13 +278,6 @@ void GetOSRand(unsigned char *ent32)
288
278
#endif
289
279
}
290
280
291
- void GetRandBytes (unsigned char * buf, int num)
292
- {
293
- if (RAND_bytes (buf, num) != 1 ) {
294
- RandFailure ();
295
- }
296
- }
297
-
298
281
void LockingCallbackOpenSSL (int mode, int i, const char * file, int line);
299
282
300
283
namespace {
@@ -303,6 +286,7 @@ struct RNGState {
303
286
Mutex m_mutex;
304
287
unsigned char m_state[32 ] GUARDED_BY(m_mutex) = {0 };
305
288
uint64_t m_counter GUARDED_BY (m_mutex) = 0;
289
+ bool m_strongly_seeded GUARDED_BY (m_mutex) = false;
306
290
std::unique_ptr<Mutex[]> m_mutex_openssl;
307
291
308
292
RNGState ()
@@ -319,14 +303,6 @@ struct RNGState {
319
303
// or corrupt. Explicitly tell OpenSSL not to try to load the file. The result for our libs will be
320
304
// that the config appears to have been loaded and there are no modules/engines available.
321
305
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 ();
330
306
}
331
307
332
308
~RNGState ()
@@ -337,14 +313,19 @@ struct RNGState {
337
313
CRYPTO_set_locking_callback (nullptr );
338
314
}
339
315
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)
342
321
{
343
322
assert (num <= 32 );
344
323
unsigned char buf[64 ];
345
324
static_assert (sizeof (buf) == CSHA512::OUTPUT_SIZE, " Buffer needs to have hasher's output size" );
325
+ bool ret;
346
326
{
347
327
LOCK (m_mutex);
328
+ ret = (m_strongly_seeded |= strong_seed);
348
329
// Write the current state of the RNG into the hasher
349
330
hasher.Write (m_state, 32 );
350
331
// Write a new counter number into the state
@@ -363,6 +344,7 @@ struct RNGState {
363
344
// Best effort cleanup of internal state
364
345
hasher.Reset ();
365
346
memory_cleanse (buf, 64 );
347
+ return ret;
366
348
}
367
349
};
368
350
@@ -386,61 +368,128 @@ void LockingCallbackOpenSSL(int mode, int i, const char* file, int line) NO_THRE
386
368
}
387
369
}
388
370
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
+ }
390
376
391
- void RandAddSeedSleep ( )
377
+ static void SeedFast (CSHA512& hasher )
392
378
{
393
- RNGState& rng = GetRNGState () ;
379
+ unsigned char buffer[ 32 ] ;
394
380
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) );
398
384
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) );
402
388
403
- memory_cleanse (&nPerfCounter1, sizeof (nPerfCounter1));
404
- memory_cleanse (&nPerfCounter2, sizeof (nPerfCounter2) );
389
+ // High-precision timestamp
390
+ SeedTimestamp (hasher );
405
391
}
406
392
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);
412
413
}
413
414
414
- void GetStrongRandBytes ( unsigned char * out, int num )
415
+ static void SeedSleep (CSHA512& hasher )
415
416
{
416
- RNGState& rng = GetRNGState ();
417
+ // Everything that the 'fast' seeder includes
418
+ SeedFast (hasher);
417
419
418
- assert (num <= 32 );
419
- CSHA512 hasher;
420
- unsigned char buf[64 ];
420
+ // High-precision timestamp
421
+ SeedTimestamp (hasher);
421
422
422
- // First source: OpenSSL's RNG
423
- RandAddSeedPerfmon ();
424
- GetRandBytes (buf, 32 );
425
- hasher.Write (buf, 32 );
423
+ // Sleep for 1ms
424
+ MilliSleep (1 );
426
425
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);
430
428
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 ;
434
470
}
435
471
436
472
// 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
+ }
438
479
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
+ }
442
487
}
443
488
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
+
444
493
uint64_t GetRand (uint64_t nMax)
445
494
{
446
495
if (nMax == 0 )
@@ -539,8 +588,10 @@ bool Random_SanityCheck()
539
588
if (stop == start) return false ;
540
589
541
590
// 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 );
544
595
545
596
return true ;
546
597
}
@@ -571,7 +622,7 @@ FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexce
571
622
void RandomInit ()
572
623
{
573
624
// Invoke RNG code to trigger initialization (if not already performed)
574
- GetRNGState ( );
625
+ ProcRand ( nullptr , 0 , RNGLevel::FAST );
575
626
576
627
ReportHardwareRand ();
577
628
}
0 commit comments