Always require OS randomness when generating secret keys

This commit is contained in:
Pieter Wuille 2016-04-16 12:25:12 +02:00
parent a80de15113
commit fa2637a3be
7 changed files with 59 additions and 17 deletions

View File

@ -374,7 +374,8 @@ endif
bitcoin_cli_LDADD = \ bitcoin_cli_LDADD = \
$(LIBBITCOIN_CLI) \ $(LIBBITCOIN_CLI) \
$(LIBUNIVALUE) \ $(LIBUNIVALUE) \
$(LIBBITCOIN_UTIL) $(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO)
bitcoin_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) bitcoin_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS)
# #

View File

@ -1401,8 +1401,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (!strErrors.str().empty()) if (!strErrors.str().empty())
return InitError(strErrors.str()); return InitError(strErrors.str());
RandAddSeedPerfmon();
//// debug print //// debug print
LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size());
LogPrintf("nBestHeight = %d\n", chainActive.Height()); LogPrintf("nBestHeight = %d\n", chainActive.Height());

View File

@ -124,9 +124,8 @@ bool CKey::Check(const unsigned char *vch) {
} }
void CKey::MakeNewKey(bool fCompressedIn) { void CKey::MakeNewKey(bool fCompressedIn) {
RandAddSeedPerfmon();
do { do {
GetRandBytes(vch, sizeof(vch)); GetStrongRandBytes(vch, sizeof(vch));
} while (!Check(vch)); } while (!Check(vch));
fValid = true; fValid = true;
fCompressed = fCompressedIn; fCompressed = fCompressedIn;

View File

@ -4547,7 +4547,6 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams) bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams)
{ {
RandAddSeedPerfmon();
LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id);
if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
{ {

View File

@ -5,9 +5,11 @@
#include "random.h" #include "random.h"
#include "crypto/sha512.h"
#include "support/cleanse.h" #include "support/cleanse.h"
#ifdef WIN32 #ifdef WIN32
#include "compat.h" // for Windows API #include "compat.h" // for Windows API
#include <wincrypt.h>
#endif #endif
#include "serialize.h" // for begin_ptr(vec) #include "serialize.h" // for begin_ptr(vec)
#include "util.h" // for LogPrint() #include "util.h" // for LogPrint()
@ -43,7 +45,7 @@ void RandAddSeed()
memory_cleanse((void*)&nCounter, sizeof(nCounter)); memory_cleanse((void*)&nCounter, sizeof(nCounter));
} }
void RandAddSeedPerfmon() static void RandAddSeedPerfmon()
{ {
RandAddSeed(); RandAddSeed();
@ -83,6 +85,29 @@ void RandAddSeedPerfmon()
#endif #endif
} }
/** Get 32 bytes of system entropy. */
static void GetOSRand(unsigned char *ent32)
{
#ifdef WIN32
HCRYPTPROV hProvider;
int ret = CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
assert(ret);
ret = CryptGenRandom(hProvider, 32, ent32);
assert(ret);
CryptReleaseContext(hProvider, 0);
#else
int f = open("/dev/urandom", O_RDONLY);
assert(f != -1);
int have = 0;
do {
ssize_t n = read(f, ent32 + have, 32 - have);
assert(n > 0 && n <= 32 - have);
have += n;
} while (have < 32);
close(f);
#endif
}
void GetRandBytes(unsigned char* buf, int num) void GetRandBytes(unsigned char* buf, int num)
{ {
if (RAND_bytes(buf, num) != 1) { if (RAND_bytes(buf, num) != 1) {
@ -91,6 +116,27 @@ void GetRandBytes(unsigned char* buf, int num)
} }
} }
void GetStrongRandBytes(unsigned char* out, int num)
{
assert(num <= 32);
CSHA512 hasher;
unsigned char buf[64];
// First source: OpenSSL's RNG
RandAddSeedPerfmon();
GetRandBytes(buf, 32);
hasher.Write(buf, 32);
// Second source: OS RNG
GetOSRand(buf);
hasher.Write(buf, 32);
// Produce output
hasher.Finalize(buf);
memcpy(out, buf, num);
memory_cleanse(buf, 64);
}
uint64_t GetRand(uint64_t nMax) uint64_t GetRand(uint64_t nMax)
{ {
if (nMax == 0) if (nMax == 0)

View File

@ -10,11 +10,8 @@
#include <stdint.h> #include <stdint.h>
/** /* Seed OpenSSL PRNG with additional entropy data */
* Seed OpenSSL PRNG with additional entropy data
*/
void RandAddSeed(); void RandAddSeed();
void RandAddSeedPerfmon();
/** /**
* Functions to gather random data via the OpenSSL PRNG * Functions to gather random data via the OpenSSL PRNG
@ -24,6 +21,12 @@ uint64_t GetRand(uint64_t nMax);
int GetRandInt(int nMax); int GetRandInt(int nMax);
uint256 GetRandHash(); uint256 GetRandHash();
/**
* Function to gather random data from multiple sources, failing whenever any
* of those source fail to provide a result.
*/
void GetStrongRandBytes(unsigned char* buf, int num);
/** /**
* Seed insecure_rand using the random pool. * Seed insecure_rand using the random pool.
* @param Deterministic Use a deterministic seed * @param Deterministic Use a deterministic seed

View File

@ -509,16 +509,14 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
return false; return false;
CKeyingMaterial vMasterKey; CKeyingMaterial vMasterKey;
RandAddSeedPerfmon();
vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
GetRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); GetStrongRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);
CMasterKey kMasterKey; CMasterKey kMasterKey;
RandAddSeedPerfmon();
kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
GetRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); GetStrongRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);
CCrypter crypter; CCrypter crypter;
int64_t nStartTime = GetTimeMillis(); int64_t nStartTime = GetTimeMillis();
@ -3147,8 +3145,6 @@ bool CWallet::InitLoadWallet()
if (fFirstRun) if (fFirstRun)
{ {
// Create new keyUser and set as default key // Create new keyUser and set as default key
RandAddSeedPerfmon();
CPubKey newDefaultKey; CPubKey newDefaultKey;
if (walletInstance->GetKeyFromPool(newDefaultKey)) { if (walletInstance->GetKeyFromPool(newDefaultKey)) {
walletInstance->SetDefaultKey(newDefaultKey); walletInstance->SetDefaultKey(newDefaultKey);