wallet: Get rid of LockObject and UnlockObject calls in key.h

Replace these with vectors allocated from the secure allocator.

This avoids mlock syscall churn on stack pages, as well as makes
it possible to get rid of these functions.

Please review this commit and the previous one carefully that
no `sizeof(vectortype)` remains in the memcpys and memcmps usage
(ick!), and `.data()` or `&vec[x]` is used as appropriate instead of
&vec.
This commit is contained in:
Wladimir J. van der Laan 2016-09-18 08:40:14 +02:00
parent 999e4c91c2
commit f4d1fc259b
3 changed files with 23 additions and 55 deletions

View file

@ -125,8 +125,8 @@ bool CKey::Check(const unsigned char *vch) {
void CKey::MakeNewKey(bool fCompressedIn) { void CKey::MakeNewKey(bool fCompressedIn) {
do { do {
GetStrongRandBytes(vch, sizeof(vch)); GetStrongRandBytes(keydata.data(), keydata.size());
} while (!Check(vch)); } while (!Check(keydata.data()));
fValid = true; fValid = true;
fCompressed = fCompressedIn; fCompressed = fCompressedIn;
} }
@ -224,20 +224,18 @@ bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) {
bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const { bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const {
assert(IsValid()); assert(IsValid());
assert(IsCompressed()); assert(IsCompressed());
unsigned char out[64]; std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
LockObject(out);
if ((nChild >> 31) == 0) { if ((nChild >> 31) == 0) {
CPubKey pubkey = GetPubKey(); CPubKey pubkey = GetPubKey();
assert(pubkey.begin() + 33 == pubkey.end()); assert(pubkey.begin() + 33 == pubkey.end());
BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin()+1, out); BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin()+1, vout.data());
} else { } else {
assert(begin() + 32 == end()); assert(begin() + 32 == end());
BIP32Hash(cc, nChild, 0, begin(), out); BIP32Hash(cc, nChild, 0, begin(), vout.data());
} }
memcpy(ccChild.begin(), out+32, 32); memcpy(ccChild.begin(), vout.data()+32, 32);
memcpy((unsigned char*)keyChild.begin(), begin(), 32); memcpy((unsigned char*)keyChild.begin(), begin(), 32);
bool ret = secp256k1_ec_privkey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), out); bool ret = secp256k1_ec_privkey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), vout.data());
UnlockObject(out);
keyChild.fCompressed = true; keyChild.fCompressed = true;
keyChild.fValid = ret; keyChild.fValid = ret;
return ret; return ret;
@ -253,12 +251,10 @@ bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) { void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) {
static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'}; static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
unsigned char out[64]; std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
LockObject(out); CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(vout.data());
CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(out); key.Set(&vout[0], &vout[32], true);
key.Set(&out[0], &out[32], true); memcpy(chaincode.begin(), &vout[32], 32);
memcpy(chaincode.begin(), &out[32], 32);
UnlockObject(out);
nDepth = 0; nDepth = 0;
nChild = 0; nChild = 0;
memset(vchFingerprint, 0, sizeof(vchFingerprint)); memset(vchFingerprint, 0, sizeof(vchFingerprint));
@ -308,12 +304,10 @@ void ECC_Start() {
{ {
// Pass in a random blinding seed to the secp256k1 context. // Pass in a random blinding seed to the secp256k1 context.
unsigned char seed[32]; std::vector<unsigned char, secure_allocator<unsigned char>> vseed(32);
LockObject(seed); GetRandBytes(vseed.data(), 32);
GetRandBytes(seed, 32); bool ret = secp256k1_context_randomize(ctx, vseed.data());
bool ret = secp256k1_context_randomize(ctx, seed);
assert(ret); assert(ret);
UnlockObject(seed);
} }
secp256k1_context_sign = ctx; secp256k1_context_sign = ctx;

View file

@ -43,9 +43,7 @@ private:
bool fCompressed; bool fCompressed;
//! The actual byte data //! The actual byte data
unsigned char vch[32]; std::vector<unsigned char, secure_allocator<unsigned char> > keydata;
static_assert(sizeof(vch) == 32, "vch must be 32 bytes in length to not break serialization");
//! Check whether the 32-byte array pointed to be vch is valid keydata. //! Check whether the 32-byte array pointed to be vch is valid keydata.
bool static Check(const unsigned char* vch); bool static Check(const unsigned char* vch);
@ -54,37 +52,30 @@ public:
//! Construct an invalid private key. //! Construct an invalid private key.
CKey() : fValid(false), fCompressed(false) CKey() : fValid(false), fCompressed(false)
{ {
LockObject(vch); // Important: vch must be 32 bytes in length to not break serialization
} keydata.resize(32);
//! Copy constructor. This is necessary because of memlocking.
CKey(const CKey& secret) : fValid(secret.fValid), fCompressed(secret.fCompressed)
{
LockObject(vch);
memcpy(vch, secret.vch, sizeof(vch));
} }
//! Destructor (again necessary because of memlocking). //! Destructor (again necessary because of memlocking).
~CKey() ~CKey()
{ {
UnlockObject(vch);
} }
friend bool operator==(const CKey& a, const CKey& b) friend bool operator==(const CKey& a, const CKey& b)
{ {
return a.fCompressed == b.fCompressed && return a.fCompressed == b.fCompressed &&
a.size() == b.size() && a.size() == b.size() &&
memcmp(&a.vch[0], &b.vch[0], a.size()) == 0; memcmp(a.keydata.data(), b.keydata.data(), a.size()) == 0;
} }
//! Initialize using begin and end iterators to byte data. //! Initialize using begin and end iterators to byte data.
template <typename T> template <typename T>
void Set(const T pbegin, const T pend, bool fCompressedIn) void Set(const T pbegin, const T pend, bool fCompressedIn)
{ {
if (pend - pbegin != sizeof(vch)) { if (size_t(pend - pbegin) != keydata.size()) {
fValid = false; fValid = false;
} else if (Check(&pbegin[0])) { } else if (Check(&pbegin[0])) {
memcpy(vch, (unsigned char*)&pbegin[0], sizeof(vch)); memcpy(keydata.data(), (unsigned char*)&pbegin[0], keydata.size());
fValid = true; fValid = true;
fCompressed = fCompressedIn; fCompressed = fCompressedIn;
} else { } else {
@ -93,9 +84,9 @@ public:
} }
//! Simple read-only vector-like interface. //! Simple read-only vector-like interface.
unsigned int size() const { return (fValid ? sizeof(vch) : 0); } unsigned int size() const { return (fValid ? keydata.size() : 0); }
const unsigned char* begin() const { return vch; } const unsigned char* begin() const { return keydata.data(); }
const unsigned char* end() const { return vch + size(); } const unsigned char* end() const { return keydata.data() + size(); }
//! Check whether this private key is valid. //! Check whether this private key is valid.
bool IsValid() const { return fValid; } bool IsValid() const { return fValid; }

View file

@ -157,21 +157,4 @@ private:
static boost::once_flag init_flag; static boost::once_flag init_flag;
}; };
//
// Functions for directly locking/unlocking memory objects.
// Intended for non-dynamically allocated structures.
//
template <typename T>
void LockObject(const T& t)
{
LockedPageManager::Instance().LockRange((void*)(&t), sizeof(T));
}
template <typename T>
void UnlockObject(const T& t)
{
memory_cleanse((void*)(&t), sizeof(T));
LockedPageManager::Instance().UnlockRange((void*)(&t), sizeof(T));
}
#endif // BITCOIN_SUPPORT_PAGELOCKER_H #endif // BITCOIN_SUPPORT_PAGELOCKER_H