mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-12 05:32:22 +02:00
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.
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
#include <vector>
|
||||
|
||||
namespace wallet {
|
||||
int CCrypter::BytesToKeySHA512AES(const std::span<const unsigned char> salt, const SecureString& key_data, int count, unsigned char* key, unsigned char* iv) const
|
||||
int CCrypter::BytesToKeySHA512AES(const std::span<const unsigned char> salt, const SecureString& key_data, unsigned int count, unsigned char* key, unsigned char* iv) const
|
||||
{
|
||||
// This mimics the behavior of openssl's EVP_BytesToKey with an aes256cbc
|
||||
// cipher and sha512 message digest. Because sha512's output size (64b) is
|
||||
@@ -29,7 +29,7 @@ int CCrypter::BytesToKeySHA512AES(const std::span<const unsigned char> salt, con
|
||||
di.Write(salt.data(), salt.size());
|
||||
di.Finalize(buf);
|
||||
|
||||
for(int i = 0; i != count - 1; i++)
|
||||
for (unsigned int i = 0; i != count - 1; ++i)
|
||||
di.Reset().Write(buf, sizeof(buf)).Finalize(buf);
|
||||
|
||||
memcpy(key, buf, WALLET_CRYPTO_KEY_SIZE);
|
||||
@@ -40,7 +40,7 @@ int CCrypter::BytesToKeySHA512AES(const std::span<const unsigned char> salt, con
|
||||
|
||||
bool CCrypter::SetKeyFromPassphrase(const SecureString& key_data, const std::span<const unsigned char> salt, const unsigned int rounds, const unsigned int derivation_method)
|
||||
{
|
||||
if (rounds < 1 || salt.size() != WALLET_CRYPTO_SALT_SIZE) {
|
||||
if (!rounds || salt.size() != WALLET_CRYPTO_SALT_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ private:
|
||||
std::vector<unsigned char, secure_allocator<unsigned char>> vchIV;
|
||||
bool fKeySet;
|
||||
|
||||
int BytesToKeySHA512AES(std::span<const unsigned char> salt, const SecureString& key_data, int count, unsigned char* key, unsigned char* iv) const;
|
||||
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);
|
||||
|
||||
@@ -96,6 +96,13 @@ BOOST_AUTO_TEST_CASE(passphrase) {
|
||||
TestCrypter::TestPassphrase(vchSalt, SecureString(hash.begin(), hash.end()), rounds);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(passphrase_zero_rounds) {
|
||||
constexpr auto salt{"0000deadbeef0000"_hex_u8};
|
||||
CCrypter crypt;
|
||||
BOOST_CHECK(!crypt.SetKeyFromPassphrase("passphrase", salt, /*rounds=*/0, /*derivation_method=*/0));
|
||||
BOOST_CHECK(crypt.SetKeyFromPassphrase("passphrase", salt, /*rounds=*/1, /*derivation_method=*/0));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(encrypt) {
|
||||
constexpr std::array<uint8_t, WALLET_CRYPTO_SALT_SIZE> salt{"0000deadbeef0000"_hex_u8};
|
||||
CCrypter crypt;
|
||||
|
||||
Reference in New Issue
Block a user