Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/crypter.cpp
Show All 14 Lines | |||||
int CCrypter::BytesToKeySHA512AES(const std::vector<uint8_t> &chSalt, | int CCrypter::BytesToKeySHA512AES(const std::vector<uint8_t> &chSalt, | ||||
const SecureString &strKeyData, int count, | const SecureString &strKeyData, int count, | ||||
uint8_t *key, uint8_t *iv) const { | uint8_t *key, uint8_t *iv) const { | ||||
// This mimics the behavior of openssl's EVP_BytesToKey with an aes256cbc | // This mimics the behavior of openssl's EVP_BytesToKey with an aes256cbc | ||||
// cipher and sha512 message digest. Because sha512's output size (64b) is | // cipher and sha512 message digest. Because sha512's output size (64b) is | ||||
// greater than the aes256 block size (16b) + aes256 key size (32b), there's | // greater than the aes256 block size (16b) + aes256 key size (32b), there's | ||||
// no need to process more than once (D_0). | // no need to process more than once (D_0). | ||||
if (!count || !key || !iv) return 0; | if (!count || !key || !iv) { | ||||
return 0; | |||||
} | |||||
uint8_t buf[CSHA512::OUTPUT_SIZE]; | uint8_t buf[CSHA512::OUTPUT_SIZE]; | ||||
CSHA512 di; | CSHA512 di; | ||||
di.Write((const uint8_t *)strKeyData.c_str(), strKeyData.size()); | di.Write((const uint8_t *)strKeyData.c_str(), strKeyData.size()); | ||||
if (chSalt.size()) di.Write(&chSalt[0], chSalt.size()); | if (chSalt.size()) { | ||||
di.Write(&chSalt[0], chSalt.size()); | |||||
} | |||||
di.Finalize(buf); | di.Finalize(buf); | ||||
for (int i = 0; i != count - 1; i++) | for (int i = 0; i != count - 1; i++) { | ||||
di.Reset().Write(buf, sizeof(buf)).Finalize(buf); | di.Reset().Write(buf, sizeof(buf)).Finalize(buf); | ||||
} | |||||
memcpy(key, buf, WALLET_CRYPTO_KEY_SIZE); | memcpy(key, buf, WALLET_CRYPTO_KEY_SIZE); | ||||
memcpy(iv, buf + WALLET_CRYPTO_KEY_SIZE, WALLET_CRYPTO_IV_SIZE); | memcpy(iv, buf + WALLET_CRYPTO_KEY_SIZE, WALLET_CRYPTO_IV_SIZE); | ||||
memory_cleanse(buf, sizeof(buf)); | memory_cleanse(buf, sizeof(buf)); | ||||
return WALLET_CRYPTO_KEY_SIZE; | return WALLET_CRYPTO_KEY_SIZE; | ||||
} | } | ||||
bool CCrypter::SetKeyFromPassphrase(const SecureString &strKeyData, | bool CCrypter::SetKeyFromPassphrase(const SecureString &strKeyData, | ||||
const std::vector<uint8_t> &chSalt, | const std::vector<uint8_t> &chSalt, | ||||
const unsigned int nRounds, | const unsigned int nRounds, | ||||
const unsigned int nDerivationMethod) { | const unsigned int nDerivationMethod) { | ||||
if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE) return false; | if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE) { | ||||
return false; | |||||
} | |||||
int i = 0; | int i = 0; | ||||
if (nDerivationMethod == 0) | if (nDerivationMethod == 0) { | ||||
i = BytesToKeySHA512AES(chSalt, strKeyData, nRounds, vchKey.data(), | i = BytesToKeySHA512AES(chSalt, strKeyData, nRounds, vchKey.data(), | ||||
vchIV.data()); | vchIV.data()); | ||||
} | |||||
if (i != (int)WALLET_CRYPTO_KEY_SIZE) { | if (i != (int)WALLET_CRYPTO_KEY_SIZE) { | ||||
memory_cleanse(vchKey.data(), vchKey.size()); | memory_cleanse(vchKey.data(), vchKey.size()); | ||||
memory_cleanse(vchIV.data(), vchIV.size()); | memory_cleanse(vchIV.data(), vchIV.size()); | ||||
return false; | return false; | ||||
} | } | ||||
fKeySet = true; | fKeySet = true; | ||||
return true; | return true; | ||||
} | } | ||||
bool CCrypter::SetKey(const CKeyingMaterial &chNewKey, | bool CCrypter::SetKey(const CKeyingMaterial &chNewKey, | ||||
const std::vector<uint8_t> &chNewIV) { | const std::vector<uint8_t> &chNewIV) { | ||||
if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || | if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || | ||||
chNewIV.size() != WALLET_CRYPTO_IV_SIZE) | chNewIV.size() != WALLET_CRYPTO_IV_SIZE) { | ||||
return false; | return false; | ||||
} | |||||
memcpy(vchKey.data(), chNewKey.data(), chNewKey.size()); | memcpy(vchKey.data(), chNewKey.data(), chNewKey.size()); | ||||
memcpy(vchIV.data(), chNewIV.data(), chNewIV.size()); | memcpy(vchIV.data(), chNewIV.data(), chNewIV.size()); | ||||
fKeySet = true; | fKeySet = true; | ||||
return true; | return true; | ||||
} | } | ||||
bool CCrypter::Encrypt(const CKeyingMaterial &vchPlaintext, | bool CCrypter::Encrypt(const CKeyingMaterial &vchPlaintext, | ||||
std::vector<uint8_t> &vchCiphertext) const { | std::vector<uint8_t> &vchCiphertext) const { | ||||
if (!fKeySet) return false; | if (!fKeySet) { | ||||
return false; | |||||
} | |||||
// max ciphertext len for a n bytes of plaintext is | // max ciphertext len for a n bytes of plaintext is | ||||
// n + AES_BLOCKSIZE bytes | // n + AES_BLOCKSIZE bytes | ||||
vchCiphertext.resize(vchPlaintext.size() + AES_BLOCKSIZE); | vchCiphertext.resize(vchPlaintext.size() + AES_BLOCKSIZE); | ||||
AES256CBCEncrypt enc(vchKey.data(), vchIV.data(), true); | AES256CBCEncrypt enc(vchKey.data(), vchIV.data(), true); | ||||
size_t nLen = | size_t nLen = | ||||
enc.Encrypt(&vchPlaintext[0], vchPlaintext.size(), &vchCiphertext[0]); | enc.Encrypt(&vchPlaintext[0], vchPlaintext.size(), &vchCiphertext[0]); | ||||
if (nLen < vchPlaintext.size()) return false; | if (nLen < vchPlaintext.size()) { | ||||
return false; | |||||
} | |||||
vchCiphertext.resize(nLen); | vchCiphertext.resize(nLen); | ||||
return true; | return true; | ||||
} | } | ||||
bool CCrypter::Decrypt(const std::vector<uint8_t> &vchCiphertext, | bool CCrypter::Decrypt(const std::vector<uint8_t> &vchCiphertext, | ||||
CKeyingMaterial &vchPlaintext) const { | CKeyingMaterial &vchPlaintext) const { | ||||
if (!fKeySet) return false; | if (!fKeySet) { | ||||
return false; | |||||
} | |||||
// plaintext will always be equal to or lesser than length of ciphertext | // plaintext will always be equal to or lesser than length of ciphertext | ||||
int nLen = vchCiphertext.size(); | int nLen = vchCiphertext.size(); | ||||
vchPlaintext.resize(nLen); | vchPlaintext.resize(nLen); | ||||
AES256CBCDecrypt dec(vchKey.data(), vchIV.data(), true); | AES256CBCDecrypt dec(vchKey.data(), vchIV.data(), true); | ||||
nLen = | nLen = | ||||
dec.Decrypt(&vchCiphertext[0], vchCiphertext.size(), &vchPlaintext[0]); | dec.Decrypt(&vchCiphertext[0], vchCiphertext.size(), &vchPlaintext[0]); | ||||
if (nLen == 0) return false; | if (nLen == 0) { | ||||
return false; | |||||
} | |||||
vchPlaintext.resize(nLen); | vchPlaintext.resize(nLen); | ||||
return true; | return true; | ||||
} | } | ||||
static bool EncryptSecret(const CKeyingMaterial &vMasterKey, | static bool EncryptSecret(const CKeyingMaterial &vMasterKey, | ||||
const CKeyingMaterial &vchPlaintext, | const CKeyingMaterial &vchPlaintext, | ||||
const uint256 &nIV, | const uint256 &nIV, | ||||
std::vector<uint8_t> &vchCiphertext) { | std::vector<uint8_t> &vchCiphertext) { | ||||
CCrypter cKeyCrypter; | CCrypter cKeyCrypter; | ||||
std::vector<uint8_t> chIV(WALLET_CRYPTO_IV_SIZE); | std::vector<uint8_t> chIV(WALLET_CRYPTO_IV_SIZE); | ||||
memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE); | memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE); | ||||
if (!cKeyCrypter.SetKey(vMasterKey, chIV)) return false; | if (!cKeyCrypter.SetKey(vMasterKey, chIV)) { | ||||
return false; | |||||
} | |||||
return cKeyCrypter.Encrypt(*((const CKeyingMaterial *)&vchPlaintext), | return cKeyCrypter.Encrypt(*((const CKeyingMaterial *)&vchPlaintext), | ||||
vchCiphertext); | vchCiphertext); | ||||
} | } | ||||
static bool DecryptSecret(const CKeyingMaterial &vMasterKey, | static bool DecryptSecret(const CKeyingMaterial &vMasterKey, | ||||
const std::vector<uint8_t> &vchCiphertext, | const std::vector<uint8_t> &vchCiphertext, | ||||
const uint256 &nIV, CKeyingMaterial &vchPlaintext) { | const uint256 &nIV, CKeyingMaterial &vchPlaintext) { | ||||
CCrypter cKeyCrypter; | CCrypter cKeyCrypter; | ||||
std::vector<uint8_t> chIV(WALLET_CRYPTO_IV_SIZE); | std::vector<uint8_t> chIV(WALLET_CRYPTO_IV_SIZE); | ||||
memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE); | memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE); | ||||
if (!cKeyCrypter.SetKey(vMasterKey, chIV)) return false; | if (!cKeyCrypter.SetKey(vMasterKey, chIV)) { | ||||
return false; | |||||
} | |||||
return cKeyCrypter.Decrypt(vchCiphertext, | return cKeyCrypter.Decrypt(vchCiphertext, | ||||
*((CKeyingMaterial *)&vchPlaintext)); | *((CKeyingMaterial *)&vchPlaintext)); | ||||
} | } | ||||
static bool DecryptKey(const CKeyingMaterial &vMasterKey, | static bool DecryptKey(const CKeyingMaterial &vMasterKey, | ||||
const std::vector<uint8_t> &vchCryptedSecret, | const std::vector<uint8_t> &vchCryptedSecret, | ||||
const CPubKey &vchPubKey, CKey &key) { | const CPubKey &vchPubKey, CKey &key) { | ||||
CKeyingMaterial vchSecret; | CKeyingMaterial vchSecret; | ||||
if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), | if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), | ||||
vchSecret)) | vchSecret)) { | ||||
return false; | return false; | ||||
} | |||||
if (vchSecret.size() != 32) return false; | if (vchSecret.size() != 32) { | ||||
return false; | |||||
} | |||||
key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); | key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); | ||||
return key.VerifyPubKey(vchPubKey); | return key.VerifyPubKey(vchPubKey); | ||||
} | } | ||||
bool CCryptoKeyStore::SetCrypted() { | bool CCryptoKeyStore::SetCrypted() { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
if (fUseCrypto) return true; | if (fUseCrypto) { | ||||
if (!mapKeys.empty()) return false; | return true; | ||||
} | |||||
if (!mapKeys.empty()) { | |||||
return false; | |||||
} | |||||
fUseCrypto = true; | fUseCrypto = true; | ||||
return true; | return true; | ||||
} | } | ||||
bool CCryptoKeyStore::IsLocked() const { | bool CCryptoKeyStore::IsLocked() const { | ||||
if (!IsCrypted()) { | if (!IsCrypted()) { | ||||
return false; | return false; | ||||
} | } | ||||
bool result; | bool result; | ||||
{ | { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
result = vMasterKey.empty(); | result = vMasterKey.empty(); | ||||
} | } | ||||
return result; | return result; | ||||
} | } | ||||
bool CCryptoKeyStore::Lock() { | bool CCryptoKeyStore::Lock() { | ||||
if (!SetCrypted()) return false; | if (!SetCrypted()) { | ||||
return false; | |||||
} | |||||
{ | { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
vMasterKey.clear(); | vMasterKey.clear(); | ||||
} | } | ||||
NotifyStatusChanged(this); | NotifyStatusChanged(this); | ||||
return true; | return true; | ||||
} | } | ||||
bool CCryptoKeyStore::Unlock(const CKeyingMaterial &vMasterKeyIn) { | bool CCryptoKeyStore::Unlock(const CKeyingMaterial &vMasterKeyIn) { | ||||
{ | { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
if (!SetCrypted()) return false; | if (!SetCrypted()) { | ||||
return false; | |||||
} | |||||
bool keyPass = false; | bool keyPass = false; | ||||
bool keyFail = false; | bool keyFail = false; | ||||
CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); | CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); | ||||
for (; mi != mapCryptedKeys.end(); ++mi) { | for (; mi != mapCryptedKeys.end(); ++mi) { | ||||
const CPubKey &vchPubKey = (*mi).second.first; | const CPubKey &vchPubKey = (*mi).second.first; | ||||
const std::vector<uint8_t> &vchCryptedSecret = (*mi).second.second; | const std::vector<uint8_t> &vchCryptedSecret = (*mi).second.second; | ||||
CKey key; | CKey key; | ||||
if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key)) { | if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key)) { | ||||
keyFail = true; | keyFail = true; | ||||
break; | break; | ||||
} | } | ||||
keyPass = true; | keyPass = true; | ||||
if (fDecryptionThoroughlyChecked) break; | if (fDecryptionThoroughlyChecked) { | ||||
break; | |||||
} | |||||
} | } | ||||
if (keyPass && keyFail) { | if (keyPass && keyFail) { | ||||
LogPrintf("The wallet is probably corrupted: Some keys decrypt but " | LogPrintf("The wallet is probably corrupted: Some keys decrypt but " | ||||
"not all.\n"); | "not all.\n"); | ||||
assert(false); | assert(false); | ||||
} | } | ||||
if (keyFail || !keyPass) return false; | if (keyFail || !keyPass) { | ||||
return false; | |||||
} | |||||
vMasterKey = vMasterKeyIn; | vMasterKey = vMasterKeyIn; | ||||
fDecryptionThoroughlyChecked = true; | fDecryptionThoroughlyChecked = true; | ||||
} | } | ||||
NotifyStatusChanged(this); | NotifyStatusChanged(this); | ||||
return true; | return true; | ||||
} | } | ||||
bool CCryptoKeyStore::AddKeyPubKey(const CKey &key, const CPubKey &pubkey) { | bool CCryptoKeyStore::AddKeyPubKey(const CKey &key, const CPubKey &pubkey) { | ||||
{ | { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
if (!IsCrypted()) return CBasicKeyStore::AddKeyPubKey(key, pubkey); | if (!IsCrypted()) { | ||||
return CBasicKeyStore::AddKeyPubKey(key, pubkey); | |||||
} | |||||
if (IsLocked()) return false; | if (IsLocked()) { | ||||
return false; | |||||
} | |||||
std::vector<uint8_t> vchCryptedSecret; | std::vector<uint8_t> vchCryptedSecret; | ||||
CKeyingMaterial vchSecret(key.begin(), key.end()); | CKeyingMaterial vchSecret(key.begin(), key.end()); | ||||
if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), | if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), | ||||
vchCryptedSecret)) | vchCryptedSecret)) { | ||||
return false; | return false; | ||||
} | |||||
if (!AddCryptedKey(pubkey, vchCryptedSecret)) return false; | if (!AddCryptedKey(pubkey, vchCryptedSecret)) { | ||||
return false; | |||||
} | |||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CCryptoKeyStore::AddCryptedKey( | bool CCryptoKeyStore::AddCryptedKey( | ||||
const CPubKey &vchPubKey, const std::vector<uint8_t> &vchCryptedSecret) { | const CPubKey &vchPubKey, const std::vector<uint8_t> &vchCryptedSecret) { | ||||
{ | { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
if (!SetCrypted()) return false; | if (!SetCrypted()) { | ||||
return false; | |||||
} | |||||
mapCryptedKeys[vchPubKey.GetID()] = | mapCryptedKeys[vchPubKey.GetID()] = | ||||
make_pair(vchPubKey, vchCryptedSecret); | make_pair(vchPubKey, vchCryptedSecret); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CCryptoKeyStore::HaveKey(const CKeyID &address) const { | bool CCryptoKeyStore::HaveKey(const CKeyID &address) const { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
if (!IsCrypted()) { | if (!IsCrypted()) { | ||||
return CBasicKeyStore::HaveKey(address); | return CBasicKeyStore::HaveKey(address); | ||||
} | } | ||||
return mapCryptedKeys.count(address) > 0; | return mapCryptedKeys.count(address) > 0; | ||||
} | } | ||||
bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey &keyOut) const { | bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey &keyOut) const { | ||||
{ | { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
if (!IsCrypted()) return CBasicKeyStore::GetKey(address, keyOut); | if (!IsCrypted()) { | ||||
return CBasicKeyStore::GetKey(address, keyOut); | |||||
} | |||||
CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); | CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); | ||||
if (mi != mapCryptedKeys.end()) { | if (mi != mapCryptedKeys.end()) { | ||||
const CPubKey &vchPubKey = (*mi).second.first; | const CPubKey &vchPubKey = (*mi).second.first; | ||||
const std::vector<uint8_t> &vchCryptedSecret = (*mi).second.second; | const std::vector<uint8_t> &vchCryptedSecret = (*mi).second.second; | ||||
return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut); | return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut); | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
bool CCryptoKeyStore::GetPubKey(const CKeyID &address, | bool CCryptoKeyStore::GetPubKey(const CKeyID &address, | ||||
CPubKey &vchPubKeyOut) const { | CPubKey &vchPubKeyOut) const { | ||||
{ | { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
if (!IsCrypted()) | if (!IsCrypted()) { | ||||
return CBasicKeyStore::GetPubKey(address, vchPubKeyOut); | return CBasicKeyStore::GetPubKey(address, vchPubKeyOut); | ||||
} | |||||
CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); | CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); | ||||
if (mi != mapCryptedKeys.end()) { | if (mi != mapCryptedKeys.end()) { | ||||
vchPubKeyOut = (*mi).second.first; | vchPubKeyOut = (*mi).second.first; | ||||
return true; | return true; | ||||
} | } | ||||
// Check for watch-only pubkeys | // Check for watch-only pubkeys | ||||
return CBasicKeyStore::GetPubKey(address, vchPubKeyOut); | return CBasicKeyStore::GetPubKey(address, vchPubKeyOut); | ||||
Show All 11 Lines | for (const auto &mi : mapCryptedKeys) { | ||||
set_address.insert(mi.first); | set_address.insert(mi.first); | ||||
} | } | ||||
return set_address; | return set_address; | ||||
} | } | ||||
bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial &vMasterKeyIn) { | bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial &vMasterKeyIn) { | ||||
{ | { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
if (!mapCryptedKeys.empty() || IsCrypted()) return false; | if (!mapCryptedKeys.empty() || IsCrypted()) { | ||||
return false; | |||||
} | |||||
fUseCrypto = true; | fUseCrypto = true; | ||||
for (KeyMap::value_type &mKey : mapKeys) { | for (KeyMap::value_type &mKey : mapKeys) { | ||||
const CKey &key = mKey.second; | const CKey &key = mKey.second; | ||||
CPubKey vchPubKey = key.GetPubKey(); | CPubKey vchPubKey = key.GetPubKey(); | ||||
CKeyingMaterial vchSecret(key.begin(), key.end()); | CKeyingMaterial vchSecret(key.begin(), key.end()); | ||||
std::vector<uint8_t> vchCryptedSecret; | std::vector<uint8_t> vchCryptedSecret; | ||||
if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), | if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), | ||||
vchCryptedSecret)) | vchCryptedSecret)) { | ||||
return false; | return false; | ||||
if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) return false; | } | ||||
if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) { | |||||
return false; | |||||
} | |||||
} | } | ||||
mapKeys.clear(); | mapKeys.clear(); | ||||
} | } | ||||
return true; | return true; | ||||
} | } |