Files
bitcoin/src/wallet/crypter.h
benthecarman b76afff274 Wallet: Use unsigned KDF iteration count
CMasterKey::nDeriveIterations values are deserialized from wallet
files as unsigned 32-bit integers, but key derivation narrowed the
count to a signed int. A count above INT_MAX became negative in the
conversion, and the derivation loop counter then overflowed, which
is undefined behavior.

Keep the count unsigned through the derivation path to match the
serialized type. Add a unit test for zero and normal counts.
2026-08-14 10:04:17 -05:00

113 lines
4.0 KiB
C++

// Copyright (c) 2009-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_WALLET_CRYPTER_H
#define BITCOIN_WALLET_CRYPTER_H
#include <serialize.h>
#include <support/allocators/secure.h>
#include <script/signingprovider.h>
namespace wallet {
const unsigned int WALLET_CRYPTO_KEY_SIZE = 32;
const unsigned int WALLET_CRYPTO_SALT_SIZE = 8;
const unsigned int WALLET_CRYPTO_IV_SIZE = 16;
/**
* Private key encryption is done based on a CMasterKey,
* which holds a salt and random encryption key.
*
* CMasterKeys are encrypted using AES-256-CBC using a key
* derived using derivation method nDerivationMethod
* (0 == EVP_sha512()) and derivation iterations nDeriveIterations.
* vchOtherDerivationParameters is provided for alternative algorithms
* which may require more parameters.
*
* Wallet Private Keys are then encrypted using AES-256-CBC
* with the double-sha256 of the public key as the IV, and the
* master key's key as the encryption key (see keystore.[ch]).
*/
/** Master key for wallet encryption */
class CMasterKey
{
public:
std::vector<unsigned char> vchCryptedKey;
std::vector<unsigned char> vchSalt;
//! 0 = EVP_sha512()
unsigned int nDerivationMethod;
unsigned int nDeriveIterations;
//! Use this for more parameters to key derivation (currently unused)
std::vector<unsigned char> vchOtherDerivationParameters;
//! Default/minimum number of key derivation rounds
// 25000 rounds is just under 0.1 seconds on a 1.86 GHz Pentium M
// ie slightly lower than the lowest hardware we need bother supporting
static constexpr unsigned int DEFAULT_DERIVE_ITERATIONS = 25000;
SERIALIZE_METHODS(CMasterKey, obj)
{
READWRITE(obj.vchCryptedKey, obj.vchSalt, obj.nDerivationMethod, obj.nDeriveIterations, obj.vchOtherDerivationParameters);
}
CMasterKey()
{
nDeriveIterations = DEFAULT_DERIVE_ITERATIONS;
nDerivationMethod = 0;
vchOtherDerivationParameters = std::vector<unsigned char>(0);
}
};
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
namespace wallet_crypto_tests
{
class TestCrypter;
}
/** Encryption/decryption context with key information */
class CCrypter
{
friend class wallet_crypto_tests::TestCrypter; // for test access to chKey/chIV
private:
std::vector<unsigned char, secure_allocator<unsigned char>> vchKey;
std::vector<unsigned char, secure_allocator<unsigned char>> vchIV;
bool fKeySet;
int BytesToKeySHA512AES(std::span<const unsigned char> salt, const SecureString& key_data, unsigned int count, unsigned char* key, unsigned char* iv) const;
public:
bool SetKeyFromPassphrase(const SecureString& key_data, std::span<const unsigned char> salt, unsigned int rounds, unsigned int derivation_method);
bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext) const;
bool Decrypt(std::span<const unsigned char> ciphertext, CKeyingMaterial& plaintext) const;
bool SetKey(const CKeyingMaterial& new_key, std::span<const unsigned char> new_iv);
void CleanKey()
{
memory_cleanse(vchKey.data(), vchKey.size());
memory_cleanse(vchIV.data(), vchIV.size());
fKeySet = false;
}
CCrypter()
{
fKeySet = false;
vchKey.resize(WALLET_CRYPTO_KEY_SIZE);
vchIV.resize(WALLET_CRYPTO_IV_SIZE);
}
~CCrypter()
{
CleanKey();
}
};
bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext);
bool DecryptSecret(const CKeyingMaterial& master_key, std::span<const unsigned char> ciphertext, const uint256& iv, CKeyingMaterial& plaintext);
bool DecryptKey(const CKeyingMaterial& master_key, std::span<const unsigned char> crypted_secret, const CPubKey& pub_key, CKey& key);
} // namespace wallet
#endif // BITCOIN_WALLET_CRYPTER_H