diff --git a/src/random.cpp b/src/random.cpp --- a/src/random.cpp +++ b/src/random.cpp @@ -15,6 +15,7 @@ #include // for WAIT_LOCK #include // for GetTime() +#include #include #include @@ -306,13 +307,47 @@ } } +void LockingCallbackOpenSSL(int mode, int i, const char *file, int line); + namespace { + struct RNGState { Mutex m_mutex; uint8_t m_state[32] GUARDED_BY(m_mutex) = {0}; uint64_t m_counter GUARDED_BY(m_mutex) = 0; + std::unique_ptr m_mutex_openssl; + + RNGState() { + InitHardwareRand(); + + // Init OpenSSL library multithreading support + m_mutex_openssl.reset(new Mutex[CRYPTO_num_locks()]); + CRYPTO_set_locking_callback(LockingCallbackOpenSSL); + + // OpenSSL can optionally load a config file which lists optional + // loadable modules and engines. We don't use them so we don't require + // the config. However some of our libs may call functions which attempt + // to load the config file, possibly resulting in an exit() or crash if + // it is missing or corrupt. Explicitly tell OpenSSL not to try to load + // the file. The result for our libs will be that the config appears to + // have been loaded and there are no modules/engines available. + OPENSSL_no_config(); - RNGState() { InitHardwareRand(); } +#ifdef WIN32 + // Seed OpenSSL PRNG with current contents of the screen + RAND_screen(); +#endif + + // Seed OpenSSL PRNG with performance counter + RandAddSeed(); + } + + ~RNGState() { + // Securely erase the memory used by the OpenSSL PRNG + RAND_cleanup(); + // Shutdown OpenSSL library multithreading support + CRYPTO_set_locking_callback(nullptr); + } /** * Extract up to 32 bytes of entropy from the RNG state, mixing in new @@ -356,6 +391,17 @@ } } // namespace +void LockingCallbackOpenSSL(int mode, int i, const char *file, + int line) NO_THREAD_SAFETY_ANALYSIS { + RNGState &rng = GetRNGState(); + + if (mode & CRYPTO_LOCK) { + rng.m_mutex_openssl[i].lock(); + } else { + rng.m_mutex_openssl[i].unlock(); + } +} + static void AddDataToRng(void *data, size_t len, RNGState &rng); void RandAddSeedSleep() { diff --git a/src/util/system.cpp b/src/util/system.cpp --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -20,9 +20,6 @@ #include #include -#include -#include - #include #if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)) @@ -94,52 +91,6 @@ ArgsManager gArgs; -/** Init OpenSSL library multithreading support */ -static std::unique_ptr ppmutexOpenSSL; -void locking_callback(int mode, int i, const char *file, - int line) NO_THREAD_SAFETY_ANALYSIS { - if (mode & CRYPTO_LOCK) { - ENTER_CRITICAL_SECTION(ppmutexOpenSSL[i]); - } else { - LEAVE_CRITICAL_SECTION(ppmutexOpenSSL[i]); - } -} - -// Singleton for wrapping OpenSSL setup/teardown. -class CInit { -public: - CInit() { - // Init OpenSSL library multithreading support. - ppmutexOpenSSL.reset(new CCriticalSection[CRYPTO_num_locks()]); - CRYPTO_set_locking_callback(locking_callback); - - // OpenSSL can optionally load a config file which lists optional - // loadable modules and engines. We don't use them so we don't require - // the config. However some of our libs may call functions which attempt - // to load the config file, possibly resulting in an exit() or crash if - // it is missing or corrupt. Explicitly tell OpenSSL not to try to load - // the file. The result for our libs will be that the config appears to - // have been loaded and there are no modules/engines available. - OPENSSL_no_config(); - -#ifdef WIN32 - // Seed OpenSSL PRNG with current contents of the screen. - RAND_screen(); -#endif - - // Seed OpenSSL PRNG with performance counter. - RandAddSeed(); - } - ~CInit() { - // Securely erase the memory used by the PRNG. - RAND_cleanup(); - // Shutdown OpenSSL library multithreading support. - CRYPTO_set_locking_callback(nullptr); - // Clear the set of locks now to maintain symmetry with the constructor. - ppmutexOpenSSL.reset(); - } -} instance_of_cinit; - /** * A map that contains all the currently held directory locks. After successful * locking, these will be held here until the global destructor cleans them up