From 7e1d9a84689d77a9349a3a09fd5f9dd3f9c293aa Mon Sep 17 00:00:00 2001 From: Hodlinator <172445034+hodlinator@users.noreply.github.com> Date: Mon, 19 Aug 2024 14:45:54 +0200 Subject: [PATCH 01/10] refactor: Enforce lowercase hex digits for consteval uint256 Also changes compile-time asserts with comments into throws. --- src/test/pow_tests.cpp | 2 +- src/test/uint256_tests.cpp | 2 +- src/uint256.h | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp index edbc1de91f..76e4600441 100644 --- a/src/test/pow_tests.cpp +++ b/src/test/pow_tests.cpp @@ -177,7 +177,7 @@ void sanity_check_chainparams(const ArgsManager& args, ChainType chain_type) // check max target * 4*nPowTargetTimespan doesn't overflow -- see pow.cpp:CalculateNextWorkRequired() if (!consensus.fPowNoRetargeting) { - arith_uint256 targ_max{UintToArith256(uint256{"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"})}; + arith_uint256 targ_max{UintToArith256(uint256{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"})}; targ_max /= consensus.nPowTargetTimespan*4; BOOST_CHECK(UintToArith256(consensus.powLimit) < targ_max); } diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp index 4777f7ed47..8b76e0865a 100644 --- a/src/test/uint256_tests.cpp +++ b/src/test/uint256_tests.cpp @@ -431,7 +431,7 @@ BOOST_AUTO_TEST_CASE( check_ONE ) BOOST_AUTO_TEST_CASE(FromHex_vs_uint256) { auto runtime_uint{uint256::FromHex("4A5E1E4BAAB89F3A32518A88C31BC87F618f76673e2cc77ab2127b7afdeda33b").value()}; - constexpr uint256 consteval_uint{ "4a5e1e4baab89f3a32518a88c31bc87f618F76673E2CC77AB2127B7AFDEDA33B"}; + constexpr uint256 consteval_uint{ "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"}; BOOST_CHECK_EQUAL(consteval_uint, runtime_uint); } diff --git a/src/uint256.h b/src/uint256.h index db37adfe6d..c7499c3f2c 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -130,13 +130,12 @@ consteval base_blob::base_blob(std::string_view hex_str) // Non-lookup table version of HexDigit(). auto from_hex = [](const char c) -> int8_t { if (c >= '0' && c <= '9') return c - '0'; - if (c >= 'a' && c <= 'f') return c - 'a' + 0xA; - if (c >= 'A' && c <= 'F') return c - 'A' + 0xA; + if (c >= 'a' && c <= 'f') return c - 'a' + 0xa; - assert(false); // Reached if ctor is called with an invalid hex digit. + throw "Only lowercase hex digits are allowed, for consistency"; }; - assert(hex_str.length() == m_data.size() * 2); // 2 hex digits per byte. + if (hex_str.length() != m_data.size() * 2) throw "Hex string must fit exactly"; auto str_it = hex_str.rbegin(); for (auto& elem : m_data) { auto lo = from_hex(*(str_it++)); From d99c81697148a9695c0fba614dff9fbe728a3acd Mon Sep 17 00:00:00 2001 From: Hodlinator <172445034+hodlinator@users.noreply.github.com> Date: Tue, 20 Aug 2024 23:41:02 +0200 Subject: [PATCH 02/10] refactor: Improve CCrypter related lines Lines will be touched in next 2 commits. --- src/wallet/crypter.cpp | 13 +++++------ src/wallet/test/wallet_crypto_tests.cpp | 30 ++++++++++++------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 57a19fb5f2..5ee755452d 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -8,6 +8,7 @@ #include #include +#include #include namespace wallet { @@ -24,7 +25,7 @@ int CCrypter::BytesToKeySHA512AES(const std::vector& chSalt, cons unsigned char buf[CSHA512::OUTPUT_SIZE]; CSHA512 di; - di.Write((const unsigned char*)strKeyData.data(), strKeyData.size()); + di.Write(UCharCast(strKeyData.data()), strKeyData.size()); di.Write(chSalt.data(), chSalt.size()); di.Finalize(buf); @@ -93,12 +94,10 @@ bool CCrypter::Decrypt(const std::vector& vchCiphertext, CKeyingM return false; // plaintext will always be equal to or lesser than length of ciphertext - int nLen = vchCiphertext.size(); - - vchPlaintext.resize(nLen); + vchPlaintext.resize(vchCiphertext.size()); AES256CBCDecrypt dec(vchKey.data(), vchIV.data(), true); - nLen = dec.Decrypt(vchCiphertext.data(), vchCiphertext.size(), vchPlaintext.data()); + int nLen = dec.Decrypt(vchCiphertext.data(), vchCiphertext.size(), vchPlaintext.data()); if(nLen == 0) return false; vchPlaintext.resize(nLen); @@ -118,8 +117,8 @@ bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vch bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext) { CCrypter cKeyCrypter; - std::vector chIV(WALLET_CRYPTO_IV_SIZE); - memcpy(chIV.data(), &nIV, WALLET_CRYPTO_IV_SIZE); + static_assert(WALLET_CRYPTO_IV_SIZE <= std::remove_reference_t::size()); + std::vector chIV{nIV.begin(), nIV.begin() + WALLET_CRYPTO_IV_SIZE}; if(!cKeyCrypter.SetKey(vMasterKey, chIV)) return false; return cKeyCrypter.Decrypt(vchCiphertext, vchPlaintext); diff --git a/src/wallet/test/wallet_crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp index e413bc1fa0..09b08fa433 100644 --- a/src/wallet/test/wallet_crypto_tests.cpp +++ b/src/wallet/test/wallet_crypto_tests.cpp @@ -18,14 +18,14 @@ class TestCrypter { public: static void TestPassphraseSingle(const std::vector& vchSalt, const SecureString& passphrase, uint32_t rounds, - const std::vector& correctKey = std::vector(), - const std::vector& correctIV=std::vector()) + const std::vector& correctKey = {}, + const std::vector& correctIV = {}) { CCrypter crypt; crypt.SetKeyFromPassphrase(passphrase, vchSalt, rounds, 0); if(!correctKey.empty()) - BOOST_CHECK_MESSAGE(memcmp(crypt.vchKey.data(), correctKey.data(), crypt.vchKey.size()) == 0, \ + BOOST_CHECK_MESSAGE(memcmp(crypt.vchKey.data(), correctKey.data(), crypt.vchKey.size()) == 0, HexStr(crypt.vchKey) + std::string(" != ") + HexStr(correctKey)); if(!correctIV.empty()) BOOST_CHECK_MESSAGE(memcmp(crypt.vchIV.data(), correctIV.data(), crypt.vchIV.size()) == 0, @@ -33,40 +33,40 @@ static void TestPassphraseSingle(const std::vector& vchSalt, cons } static void TestPassphrase(const std::vector& vchSalt, const SecureString& passphrase, uint32_t rounds, - const std::vector& correctKey = std::vector(), - const std::vector& correctIV=std::vector()) + const std::vector& correctKey = {}, + const std::vector& correctIV = {}) { TestPassphraseSingle(vchSalt, passphrase, rounds, correctKey, correctIV); for(SecureString::const_iterator i(passphrase.begin()); i != passphrase.end(); ++i) TestPassphraseSingle(vchSalt, SecureString(i, passphrase.end()), rounds); } -static void TestDecrypt(const CCrypter& crypt, const std::vector& vchCiphertext, \ - const std::vector& vchPlaintext = std::vector()) +static void TestDecrypt(const CCrypter& crypt, const std::vector& vchCiphertext, + const std::vector& vchCorrectPlaintext = {}) { CKeyingMaterial vchDecrypted; crypt.Decrypt(vchCiphertext, vchDecrypted); if (vchPlaintext.size()) - BOOST_CHECK(CKeyingMaterial(vchPlaintext.begin(), vchPlaintext.end()) == vchDecrypted); + BOOST_CHECK_EQUAL_COLLECTIONS(vchDecrypted.begin(), vchDecrypted.end(), vchCorrectPlaintext.begin(), vchCorrectPlaintext.end()); } static void TestEncryptSingle(const CCrypter& crypt, const CKeyingMaterial& vchPlaintext, - const std::vector& vchCiphertextCorrect = std::vector()) + const std::vector& vchCiphertextCorrect = {}) { std::vector vchCiphertext; crypt.Encrypt(vchPlaintext, vchCiphertext); if (!vchCiphertextCorrect.empty()) - BOOST_CHECK(vchCiphertext == vchCiphertextCorrect); + BOOST_CHECK_EQUAL_COLLECTIONS(vchCiphertext.begin(), vchCiphertext.end(), vchCiphertextCorrect.begin(), vchCiphertextCorrect.end()); const std::vector vchPlaintext2(vchPlaintext.begin(), vchPlaintext.end()); TestDecrypt(crypt, vchCiphertext, vchPlaintext2); } -static void TestEncrypt(const CCrypter& crypt, const std::vector& vchPlaintextIn, \ - const std::vector& vchCiphertextCorrect = std::vector()) +static void TestEncrypt(const CCrypter& crypt, const std::vector& vchPlaintextIn, + const std::vector& vchCiphertextCorrect = {}) { - TestEncryptSingle(crypt, CKeyingMaterial(vchPlaintextIn.begin(), vchPlaintextIn.end()), vchCiphertextCorrect); + TestEncryptSingle(crypt, CKeyingMaterial{vchPlaintextIn.begin(), vchPlaintextIn.end()}, vchCiphertextCorrect); for(std::vector::const_iterator i(vchPlaintextIn.begin()); i != vchPlaintextIn.end(); ++i) TestEncryptSingle(crypt, CKeyingMaterial(i, vchPlaintextIn.end())); } @@ -76,8 +76,8 @@ static void TestEncrypt(const CCrypter& crypt, const std::vector& BOOST_AUTO_TEST_CASE(passphrase) { // These are expensive. - TestCrypter::TestPassphrase(ParseHex("0000deadbeef0000"), "test", 25000, \ - ParseHex("fc7aba077ad5f4c3a0988d8daa4810d0d4a0e3bcb53af662998898f33df0556a"), \ + TestCrypter::TestPassphrase(ParseHex("0000deadbeef0000"), "test", 25000, + ParseHex("fc7aba077ad5f4c3a0988d8daa4810d0d4a0e3bcb53af662998898f33df0556a"), ParseHex("cf2f2691526dd1aa220896fb8bf7c369")); std::string hash(GetRandHash().ToString()); From bd0830bbd4105af1953b6b897ba6bc35098cbe13 Mon Sep 17 00:00:00 2001 From: Hodlinator <172445034+hodlinator@users.noreply.github.com> Date: Tue, 20 Aug 2024 23:44:57 +0200 Subject: [PATCH 03/10] refactor: de-Hungarianize CCrypter Beyond renaming it also adjusts whitespace and adds braces to conform to current doc/developer-notes.md. TestEncrypt: Change iterator type to auto in ahead of vector -> span conversion. Only touches functions that will be modified in next commit. --- src/wallet/crypter.cpp | 63 +++++++++++--------- src/wallet/crypter.h | 12 ++-- src/wallet/test/fuzz/crypter.cpp | 10 ++-- src/wallet/test/wallet_crypto_tests.cpp | 76 +++++++++++++------------ 4 files changed, 87 insertions(+), 74 deletions(-) diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 5ee755452d..1d6af9dda4 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -12,7 +12,7 @@ #include namespace wallet { -int CCrypter::BytesToKeySHA512AES(const std::vector& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const +int CCrypter::BytesToKeySHA512AES(const std::vector& salt, const SecureString& key_data, 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 @@ -25,8 +25,8 @@ int CCrypter::BytesToKeySHA512AES(const std::vector& chSalt, cons unsigned char buf[CSHA512::OUTPUT_SIZE]; CSHA512 di; - di.Write(UCharCast(strKeyData.data()), strKeyData.size()); - di.Write(chSalt.data(), chSalt.size()); + di.Write(UCharCast(key_data.data()), key_data.size()); + di.Write(salt.data(), salt.size()); di.Finalize(buf); for(int i = 0; i != count - 1; i++) @@ -38,14 +38,16 @@ int CCrypter::BytesToKeySHA512AES(const std::vector& chSalt, cons return WALLET_CRYPTO_KEY_SIZE; } -bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod) +bool CCrypter::SetKeyFromPassphrase(const SecureString& key_data, const std::vector& salt, const unsigned int rounds, const unsigned int derivation_method) { - if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE) + if (rounds < 1 || salt.size() != WALLET_CRYPTO_SALT_SIZE) { return false; + } int i = 0; - if (nDerivationMethod == 0) - i = BytesToKeySHA512AES(chSalt, strKeyData, nRounds, vchKey.data(), vchIV.data()); + if (derivation_method == 0) { + i = BytesToKeySHA512AES(salt, key_data, rounds, vchKey.data(), vchIV.data()); + } if (i != (int)WALLET_CRYPTO_KEY_SIZE) { @@ -58,13 +60,14 @@ bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::v return true; } -bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector& chNewIV) +bool CCrypter::SetKey(const CKeyingMaterial& new_key, const std::vector& new_iv) { - if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_IV_SIZE) + if (new_key.size() != WALLET_CRYPTO_KEY_SIZE || new_iv.size() != WALLET_CRYPTO_IV_SIZE) { return false; + } - memcpy(vchKey.data(), chNewKey.data(), chNewKey.size()); - memcpy(vchIV.data(), chNewIV.data(), chNewIV.size()); + memcpy(vchKey.data(), new_key.data(), new_key.size()); + memcpy(vchIV.data(), new_iv.data(), new_iv.size()); fKeySet = true; return true; @@ -88,19 +91,20 @@ bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector& vchCiphertext, CKeyingMaterial& vchPlaintext) const +bool CCrypter::Decrypt(const std::vector& ciphertext, CKeyingMaterial& plaintext) const { if (!fKeySet) return false; // plaintext will always be equal to or lesser than length of ciphertext - vchPlaintext.resize(vchCiphertext.size()); + plaintext.resize(ciphertext.size()); AES256CBCDecrypt dec(vchKey.data(), vchIV.data(), true); - int nLen = dec.Decrypt(vchCiphertext.data(), vchCiphertext.size(), vchPlaintext.data()); - if(nLen == 0) + int len = dec.Decrypt(ciphertext.data(), ciphertext.size(), plaintext.data()); + if (len == 0) { return false; - vchPlaintext.resize(nLen); + } + plaintext.resize(len); return true; } @@ -114,26 +118,29 @@ bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vch return cKeyCrypter.Encrypt(vchPlaintext, vchCiphertext); } -bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext) +bool DecryptSecret(const CKeyingMaterial& master_key, const std::vector& ciphertext, const uint256& iv, CKeyingMaterial& plaintext) { - CCrypter cKeyCrypter; - static_assert(WALLET_CRYPTO_IV_SIZE <= std::remove_reference_t::size()); - std::vector chIV{nIV.begin(), nIV.begin() + WALLET_CRYPTO_IV_SIZE}; - if(!cKeyCrypter.SetKey(vMasterKey, chIV)) + CCrypter key_crypter; + static_assert(WALLET_CRYPTO_IV_SIZE <= std::remove_reference_t::size()); + std::vector iv_prefix{iv.begin(), iv.begin() + WALLET_CRYPTO_IV_SIZE}; + if (!key_crypter.SetKey(master_key, iv_prefix)) { return false; - return cKeyCrypter.Decrypt(vchCiphertext, vchPlaintext); + } + return key_crypter.Decrypt(ciphertext, plaintext); } -bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key) +bool DecryptKey(const CKeyingMaterial& master_key, const std::vector& crypted_secret, const CPubKey& pub_key, CKey& key) { - CKeyingMaterial vchSecret; - if(!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) + CKeyingMaterial secret; + if (!DecryptSecret(master_key, crypted_secret, pub_key.GetHash(), secret)) { return false; + } - if (vchSecret.size() != 32) + if (secret.size() != 32) { return false; + } - key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); - return key.VerifyPubKey(vchPubKey); + key.Set(secret.begin(), secret.end(), pub_key.IsCompressed()); + return key.VerifyPubKey(pub_key); } } // namespace wallet diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index b776a9c497..4c3d49175c 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -75,13 +75,13 @@ private: std::vector> vchIV; bool fKeySet; - int BytesToKeySHA512AES(const std::vector& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const; + int BytesToKeySHA512AES(const std::vector& salt, const SecureString& key_data, int count, unsigned char* key, unsigned char* iv) const; public: - bool SetKeyFromPassphrase(const SecureString &strKeyData, const std::vector& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod); + bool SetKeyFromPassphrase(const SecureString& key_data, const std::vector& salt, const unsigned int rounds, const unsigned int derivation_method); bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector &vchCiphertext) const; - bool Decrypt(const std::vector& vchCiphertext, CKeyingMaterial& vchPlaintext) const; - bool SetKey(const CKeyingMaterial& chNewKey, const std::vector& chNewIV); + bool Decrypt(const std::vector& ciphertext, CKeyingMaterial& plaintext) const; + bool SetKey(const CKeyingMaterial& new_key, const std::vector& new_iv); void CleanKey() { @@ -104,8 +104,8 @@ public: }; bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector &vchCiphertext); -bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext); -bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key); +bool DecryptSecret(const CKeyingMaterial& master_key, const std::vector& ciphertext, const uint256& iv, CKeyingMaterial& plaintext); +bool DecryptKey(const CKeyingMaterial& master_key, const std::vector& crypted_secret, const CPubKey& pub_key, CKey& key); } // namespace wallet #endif // BITCOIN_WALLET_CRYPTER_H diff --git a/src/wallet/test/fuzz/crypter.cpp b/src/wallet/test/fuzz/crypter.cpp index 4d6dd43c5f..7869f5f39c 100644 --- a/src/wallet/test/fuzz/crypter.cpp +++ b/src/wallet/test/fuzz/crypter.cpp @@ -35,11 +35,11 @@ FUZZ_TARGET(crypter, .init = initialize_crypter) const unsigned int derivation_method = fuzzed_data_provider.ConsumeBool() ? 0 : fuzzed_data_provider.ConsumeIntegral(); - // Limiting the value of nRounds since it is otherwise uselessly expensive and causes a timeout when fuzzing. - crypt.SetKeyFromPassphrase(/*strKeyData=*/secure_string, - /*chSalt=*/ConsumeFixedLengthByteVector(fuzzed_data_provider, WALLET_CRYPTO_SALT_SIZE), - /*nRounds=*/fuzzed_data_provider.ConsumeIntegralInRange(0, 25000), - /*nDerivationMethod=*/derivation_method); + // Limiting the value of rounds since it is otherwise uselessly expensive and causes a timeout when fuzzing. + crypt.SetKeyFromPassphrase(/*key_data=*/secure_string, + /*salt=*/ConsumeFixedLengthByteVector(fuzzed_data_provider, WALLET_CRYPTO_SALT_SIZE), + /*rounds=*/fuzzed_data_provider.ConsumeIntegralInRange(0, 25000), + /*derivation_method=*/derivation_method); } LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100) diff --git a/src/wallet/test/wallet_crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp index 09b08fa433..34b909e85d 100644 --- a/src/wallet/test/wallet_crypto_tests.cpp +++ b/src/wallet/test/wallet_crypto_tests.cpp @@ -17,58 +17,64 @@ BOOST_FIXTURE_TEST_SUITE(wallet_crypto_tests, BasicTestingSetup) class TestCrypter { public: -static void TestPassphraseSingle(const std::vector& vchSalt, const SecureString& passphrase, uint32_t rounds, - const std::vector& correctKey = {}, - const std::vector& correctIV = {}) +static void TestPassphraseSingle(const std::vector& salt, const SecureString& passphrase, uint32_t rounds, + const std::vector& correct_key = {}, + const std::vector& correct_iv = {}) { CCrypter crypt; - crypt.SetKeyFromPassphrase(passphrase, vchSalt, rounds, 0); + crypt.SetKeyFromPassphrase(passphrase, salt, rounds, 0); - if(!correctKey.empty()) - BOOST_CHECK_MESSAGE(memcmp(crypt.vchKey.data(), correctKey.data(), crypt.vchKey.size()) == 0, - HexStr(crypt.vchKey) + std::string(" != ") + HexStr(correctKey)); - if(!correctIV.empty()) - BOOST_CHECK_MESSAGE(memcmp(crypt.vchIV.data(), correctIV.data(), crypt.vchIV.size()) == 0, - HexStr(crypt.vchIV) + std::string(" != ") + HexStr(correctIV)); + if (!correct_key.empty()) { + BOOST_CHECK_MESSAGE(memcmp(crypt.vchKey.data(), correct_key.data(), crypt.vchKey.size()) == 0, + HexStr(crypt.vchKey) + std::string(" != ") + HexStr(correct_key)); + } + if (!correct_iv.empty()) { + BOOST_CHECK_MESSAGE(memcmp(crypt.vchIV.data(), correct_iv.data(), crypt.vchIV.size()) == 0, + HexStr(crypt.vchIV) + std::string(" != ") + HexStr(correct_iv)); + } } -static void TestPassphrase(const std::vector& vchSalt, const SecureString& passphrase, uint32_t rounds, - const std::vector& correctKey = {}, - const std::vector& correctIV = {}) +static void TestPassphrase(const std::vector& salt, const SecureString& passphrase, uint32_t rounds, + const std::vector& correct_key = {}, + const std::vector& correct_iv = {}) { - TestPassphraseSingle(vchSalt, passphrase, rounds, correctKey, correctIV); - for(SecureString::const_iterator i(passphrase.begin()); i != passphrase.end(); ++i) - TestPassphraseSingle(vchSalt, SecureString(i, passphrase.end()), rounds); + TestPassphraseSingle(salt, passphrase, rounds, correct_key, correct_iv); + for (SecureString::const_iterator it{passphrase.begin()}; it != passphrase.end(); ++it) { + TestPassphraseSingle(salt, SecureString{it, passphrase.end()}, rounds); + } } -static void TestDecrypt(const CCrypter& crypt, const std::vector& vchCiphertext, - const std::vector& vchCorrectPlaintext = {}) +static void TestDecrypt(const CCrypter& crypt, const std::vector& ciphertext, + const std::vector& correct_plaintext = {}) { - CKeyingMaterial vchDecrypted; - crypt.Decrypt(vchCiphertext, vchDecrypted); - if (vchPlaintext.size()) - BOOST_CHECK_EQUAL_COLLECTIONS(vchDecrypted.begin(), vchDecrypted.end(), vchCorrectPlaintext.begin(), vchCorrectPlaintext.end()); + CKeyingMaterial decrypted; + crypt.Decrypt(ciphertext, decrypted); + if (!correct_plaintext.empty()) { + BOOST_CHECK_EQUAL_COLLECTIONS(decrypted.begin(), decrypted.end(), correct_plaintext.begin(), correct_plaintext.end()); + } } -static void TestEncryptSingle(const CCrypter& crypt, const CKeyingMaterial& vchPlaintext, - const std::vector& vchCiphertextCorrect = {}) +static void TestEncryptSingle(const CCrypter& crypt, const CKeyingMaterial& plaintext, + const std::vector& correct_ciphertext = {}) { - std::vector vchCiphertext; - crypt.Encrypt(vchPlaintext, vchCiphertext); + std::vector ciphertext; + crypt.Encrypt(plaintext, ciphertext); - if (!vchCiphertextCorrect.empty()) - BOOST_CHECK_EQUAL_COLLECTIONS(vchCiphertext.begin(), vchCiphertext.end(), vchCiphertextCorrect.begin(), vchCiphertextCorrect.end()); + if (!correct_ciphertext.empty()) { + BOOST_CHECK_EQUAL_COLLECTIONS(ciphertext.begin(), ciphertext.end(), correct_ciphertext.begin(), correct_ciphertext.end()); + } - const std::vector vchPlaintext2(vchPlaintext.begin(), vchPlaintext.end()); - TestDecrypt(crypt, vchCiphertext, vchPlaintext2); + const std::vector plaintext2(plaintext.begin(), plaintext.end()); + TestDecrypt(crypt, ciphertext, /*correct_plaintext=*/plaintext2); } -static void TestEncrypt(const CCrypter& crypt, const std::vector& vchPlaintextIn, - const std::vector& vchCiphertextCorrect = {}) +static void TestEncrypt(const CCrypter& crypt, const std::vector& plaintext, + const std::vector& correct_ciphertext = {}) { - TestEncryptSingle(crypt, CKeyingMaterial{vchPlaintextIn.begin(), vchPlaintextIn.end()}, vchCiphertextCorrect); - for(std::vector::const_iterator i(vchPlaintextIn.begin()); i != vchPlaintextIn.end(); ++i) - TestEncryptSingle(crypt, CKeyingMaterial(i, vchPlaintextIn.end())); + TestEncryptSingle(crypt, CKeyingMaterial{plaintext.begin(), plaintext.end()}, correct_ciphertext); + for (auto it{plaintext.begin()}; it != plaintext.end(); ++it) { + TestEncryptSingle(crypt, CKeyingMaterial{it, plaintext.end()}); + } } }; From 403d86f1ccf0b73f042d42a9722bb007ba8c7a31 Mon Sep 17 00:00:00 2001 From: Hodlinator <172445034+hodlinator@users.noreply.github.com> Date: Mon, 19 Aug 2024 18:21:49 +0200 Subject: [PATCH 04/10] refactor: vector -> span in CCrypter TestEncryptSingle: Remove no longer needed plaintext2-variable that existed because vectors had different allocators. --- src/wallet/crypter.cpp | 14 ++++++------- src/wallet/crypter.h | 12 +++++------ src/wallet/test/wallet_crypto_tests.cpp | 27 ++++++++++++------------- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 1d6af9dda4..ffb1d29e16 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -12,7 +12,7 @@ #include namespace wallet { -int CCrypter::BytesToKeySHA512AES(const std::vector& salt, const SecureString& key_data, int count, unsigned char* key, unsigned char* iv) const +int CCrypter::BytesToKeySHA512AES(const std::span salt, const SecureString& key_data, 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 @@ -38,7 +38,7 @@ int CCrypter::BytesToKeySHA512AES(const std::vector& salt, const return WALLET_CRYPTO_KEY_SIZE; } -bool CCrypter::SetKeyFromPassphrase(const SecureString& key_data, const std::vector& salt, const unsigned int rounds, const unsigned int derivation_method) +bool CCrypter::SetKeyFromPassphrase(const SecureString& key_data, const std::span salt, const unsigned int rounds, const unsigned int derivation_method) { if (rounds < 1 || salt.size() != WALLET_CRYPTO_SALT_SIZE) { return false; @@ -60,7 +60,7 @@ bool CCrypter::SetKeyFromPassphrase(const SecureString& key_data, const std::vec return true; } -bool CCrypter::SetKey(const CKeyingMaterial& new_key, const std::vector& new_iv) +bool CCrypter::SetKey(const CKeyingMaterial& new_key, const std::span new_iv) { if (new_key.size() != WALLET_CRYPTO_KEY_SIZE || new_iv.size() != WALLET_CRYPTO_IV_SIZE) { return false; @@ -91,7 +91,7 @@ bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector& ciphertext, CKeyingMaterial& plaintext) const +bool CCrypter::Decrypt(const std::span ciphertext, CKeyingMaterial& plaintext) const { if (!fKeySet) return false; @@ -118,18 +118,18 @@ bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vch return cKeyCrypter.Encrypt(vchPlaintext, vchCiphertext); } -bool DecryptSecret(const CKeyingMaterial& master_key, const std::vector& ciphertext, const uint256& iv, CKeyingMaterial& plaintext) +bool DecryptSecret(const CKeyingMaterial& master_key, const std::span ciphertext, const uint256& iv, CKeyingMaterial& plaintext) { CCrypter key_crypter; static_assert(WALLET_CRYPTO_IV_SIZE <= std::remove_reference_t::size()); - std::vector iv_prefix{iv.begin(), iv.begin() + WALLET_CRYPTO_IV_SIZE}; + const std::span iv_prefix{iv.data(), WALLET_CRYPTO_IV_SIZE}; if (!key_crypter.SetKey(master_key, iv_prefix)) { return false; } return key_crypter.Decrypt(ciphertext, plaintext); } -bool DecryptKey(const CKeyingMaterial& master_key, const std::vector& crypted_secret, const CPubKey& pub_key, CKey& key) +bool DecryptKey(const CKeyingMaterial& master_key, const std::span crypted_secret, const CPubKey& pub_key, CKey& key) { CKeyingMaterial secret; if (!DecryptSecret(master_key, crypted_secret, pub_key.GetHash(), secret)) { diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index 4c3d49175c..944858fb3f 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -75,13 +75,13 @@ private: std::vector> vchIV; bool fKeySet; - int BytesToKeySHA512AES(const std::vector& salt, const SecureString& key_data, int count, unsigned char* key, unsigned char* iv) const; + int BytesToKeySHA512AES(std::span salt, const SecureString& key_data, int count, unsigned char* key, unsigned char* iv) const; public: - bool SetKeyFromPassphrase(const SecureString& key_data, const std::vector& salt, const unsigned int rounds, const unsigned int derivation_method); + bool SetKeyFromPassphrase(const SecureString& key_data, std::span salt, const unsigned int rounds, const unsigned int derivation_method); bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector &vchCiphertext) const; - bool Decrypt(const std::vector& ciphertext, CKeyingMaterial& plaintext) const; - bool SetKey(const CKeyingMaterial& new_key, const std::vector& new_iv); + bool Decrypt(std::span ciphertext, CKeyingMaterial& plaintext) const; + bool SetKey(const CKeyingMaterial& new_key, std::span new_iv); void CleanKey() { @@ -104,8 +104,8 @@ public: }; bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector &vchCiphertext); -bool DecryptSecret(const CKeyingMaterial& master_key, const std::vector& ciphertext, const uint256& iv, CKeyingMaterial& plaintext); -bool DecryptKey(const CKeyingMaterial& master_key, const std::vector& crypted_secret, const CPubKey& pub_key, CKey& key); +bool DecryptSecret(const CKeyingMaterial& master_key, std::span ciphertext, const uint256& iv, CKeyingMaterial& plaintext); +bool DecryptKey(const CKeyingMaterial& master_key, std::span crypted_secret, const CPubKey& pub_key, CKey& key); } // namespace wallet #endif // BITCOIN_WALLET_CRYPTER_H diff --git a/src/wallet/test/wallet_crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp index 34b909e85d..6e8615c3dd 100644 --- a/src/wallet/test/wallet_crypto_tests.cpp +++ b/src/wallet/test/wallet_crypto_tests.cpp @@ -17,9 +17,9 @@ BOOST_FIXTURE_TEST_SUITE(wallet_crypto_tests, BasicTestingSetup) class TestCrypter { public: -static void TestPassphraseSingle(const std::vector& salt, const SecureString& passphrase, uint32_t rounds, - const std::vector& correct_key = {}, - const std::vector& correct_iv = {}) +static void TestPassphraseSingle(const std::span salt, const SecureString& passphrase, uint32_t rounds, + const std::span correct_key = {}, + const std::span correct_iv = {}) { CCrypter crypt; crypt.SetKeyFromPassphrase(passphrase, salt, rounds, 0); @@ -34,9 +34,9 @@ static void TestPassphraseSingle(const std::vector& salt, const S } } -static void TestPassphrase(const std::vector& salt, const SecureString& passphrase, uint32_t rounds, - const std::vector& correct_key = {}, - const std::vector& correct_iv = {}) +static void TestPassphrase(const std::span salt, const SecureString& passphrase, uint32_t rounds, + const std::span correct_key = {}, + const std::span correct_iv = {}) { TestPassphraseSingle(salt, passphrase, rounds, correct_key, correct_iv); for (SecureString::const_iterator it{passphrase.begin()}; it != passphrase.end(); ++it) { @@ -44,8 +44,8 @@ static void TestPassphrase(const std::vector& salt, const SecureS } } -static void TestDecrypt(const CCrypter& crypt, const std::vector& ciphertext, - const std::vector& correct_plaintext = {}) +static void TestDecrypt(const CCrypter& crypt, const std::span ciphertext, + const std::span correct_plaintext = {}) { CKeyingMaterial decrypted; crypt.Decrypt(ciphertext, decrypted); @@ -55,7 +55,7 @@ static void TestDecrypt(const CCrypter& crypt, const std::vector& } static void TestEncryptSingle(const CCrypter& crypt, const CKeyingMaterial& plaintext, - const std::vector& correct_ciphertext = {}) + const std::span correct_ciphertext = {}) { std::vector ciphertext; crypt.Encrypt(plaintext, ciphertext); @@ -64,12 +64,11 @@ static void TestEncryptSingle(const CCrypter& crypt, const CKeyingMaterial& plai BOOST_CHECK_EQUAL_COLLECTIONS(ciphertext.begin(), ciphertext.end(), correct_ciphertext.begin(), correct_ciphertext.end()); } - const std::vector plaintext2(plaintext.begin(), plaintext.end()); - TestDecrypt(crypt, ciphertext, /*correct_plaintext=*/plaintext2); + TestDecrypt(crypt, ciphertext, /*correct_plaintext=*/plaintext); } -static void TestEncrypt(const CCrypter& crypt, const std::vector& plaintext, - const std::vector& correct_ciphertext = {}) +static void TestEncrypt(const CCrypter& crypt, const std::span plaintext, + const std::span correct_ciphertext = {}) { TestEncryptSingle(crypt, CKeyingMaterial{plaintext.begin(), plaintext.end()}, correct_ciphertext); for (auto it{plaintext.begin()}; it != plaintext.end(); ++it) { @@ -105,7 +104,7 @@ BOOST_AUTO_TEST_CASE(encrypt) { for (int i = 0; i != 100; i++) { uint256 hash(GetRandHash()); - TestCrypter::TestEncrypt(crypt, std::vector(hash.begin(), hash.end())); + TestCrypter::TestEncrypt(crypt, std::span{hash.begin(), hash.end()}); } } From 2b5e6eff36abe4c23b8789ef1babfafedc90b973 Mon Sep 17 00:00:00 2001 From: Hodlinator <172445034+hodlinator@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:11:54 +0200 Subject: [PATCH 05/10] refactor: Make XOnlyPubKey tolerate constexpr std::arrays Length was already asserted inside of base_blob-ctor. --- src/pubkey.cpp | 6 ------ src/pubkey.h | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/pubkey.cpp b/src/pubkey.cpp index 13e3c2dbe0..148b268e2e 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -193,12 +193,6 @@ int ecdsa_signature_parse_der_lax(secp256k1_ecdsa_signature* sig, const unsigned static const std::vector NUMS_H_DATA{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")}; const XOnlyPubKey XOnlyPubKey::NUMS_H{NUMS_H_DATA}; -XOnlyPubKey::XOnlyPubKey(Span bytes) -{ - assert(bytes.size() == 32); - std::copy(bytes.begin(), bytes.end(), m_keydata.begin()); -} - std::vector XOnlyPubKey::GetKeyIDs() const { std::vector out; diff --git a/src/pubkey.h b/src/pubkey.h index ae34ddd0af..b4666aad22 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -254,7 +254,7 @@ public: bool IsNull() const { return m_keydata.IsNull(); } /** Construct an x-only pubkey from exactly 32 bytes. */ - explicit XOnlyPubKey(Span bytes); + constexpr explicit XOnlyPubKey(std::span bytes) : m_keydata{bytes} {} /** Construct an x-only pubkey from a normal pubkey. */ explicit XOnlyPubKey(const CPubKey& pubkey) : XOnlyPubKey(Span{pubkey}.subspan(1, 32)) {} From dc5f6f681275f56ff389500e3dd98fbe791f4a45 Mon Sep 17 00:00:00 2001 From: Hodlinator <172445034+hodlinator@users.noreply.github.com> Date: Wed, 21 Aug 2024 14:55:10 +0200 Subject: [PATCH 06/10] test refactor: util_tests - parse_hex clean up * Use BOOST_CHECK_EQUAL_COLLECTIONS and BOOST_CHECK_EQUAL instead of deprecated BOOST_CHECK. * Avoid repeating expected values. * Break out repeated HEX_PARSE_INPUT and rename ParseHex_expected to HEX_PARSE_OUTPUT. Done in preparation for adding a couple more tests in the next commit. Co-Authored-By: l0rinc --- src/test/util_tests.cpp | 51 +++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 9598b9c182..fbc8af789d 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -136,46 +136,51 @@ BOOST_AUTO_TEST_CASE(util_criticalsection) } while(0); } -static const unsigned char ParseHex_expected[65] = { +constexpr char HEX_PARSE_INPUT[] = "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"; +constexpr uint8_t HEX_PARSE_OUTPUT[] = { 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f }; +static_assert((sizeof(HEX_PARSE_INPUT) - 1) == 2 * sizeof(HEX_PARSE_OUTPUT)); BOOST_AUTO_TEST_CASE(parse_hex) { std::vector result; - std::vector expected(ParseHex_expected, ParseHex_expected + sizeof(ParseHex_expected)); + // Basic test vector - result = ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"); + std::vector expected(std::begin(HEX_PARSE_OUTPUT), std::end(HEX_PARSE_OUTPUT)); + result = ParseHex(HEX_PARSE_INPUT); BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); - result = TryParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").value(); + + result = TryParseHex(HEX_PARSE_INPUT).value(); BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); // Spaces between bytes must be supported + expected = {0x12, 0x34, 0x56, 0x78}; result = ParseHex("12 34 56 78"); - BOOST_CHECK(result.size() == 4 && result[0] == 0x12 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78); + BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); result = TryParseHex("12 34 56 78").value(); - BOOST_CHECK(result.size() == 4 && result[0] == 0x12 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78); + BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); // Leading space must be supported (used in BerkeleyEnvironment::Salvage) + expected = {0x89, 0x34, 0x56, 0x78}; result = ParseHex(" 89 34 56 78"); - BOOST_CHECK(result.size() == 4 && result[0] == 0x89 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78); + BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); result = TryParseHex(" 89 34 56 78").value(); - BOOST_CHECK(result.size() == 4 && result[0] == 0x89 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78); + BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); // Mixed case and spaces are supported + expected = {0xff, 0xaa}; result = ParseHex(" Ff aA "); - BOOST_CHECK(result.size() == 2 && result[0] == 0xff && result[1] == 0xaa); + BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); result = TryParseHex(" Ff aA ").value(); - BOOST_CHECK(result.size() == 2 && result[0] == 0xff && result[1] == 0xaa); + BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); // Empty string is supported - result = ParseHex(""); - BOOST_CHECK(result.size() == 0); - result = TryParseHex("").value(); - BOOST_CHECK(result.size() == 0); + BOOST_CHECK_EQUAL(ParseHex("").size(), 0); + BOOST_CHECK_EQUAL(TryParseHex("").value().size(), 0); // Spaces between nibbles is treated as invalid BOOST_CHECK_EQUAL(ParseHex("AAF F").size(), 0); @@ -200,23 +205,15 @@ BOOST_AUTO_TEST_CASE(parse_hex) BOOST_AUTO_TEST_CASE(util_HexStr) { - BOOST_CHECK_EQUAL( - HexStr(ParseHex_expected), - "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"); - - BOOST_CHECK_EQUAL( - HexStr(Span{ParseHex_expected}.last(0)), - ""); - - BOOST_CHECK_EQUAL( - HexStr(Span{ParseHex_expected}.first(0)), - ""); + BOOST_CHECK_EQUAL(HexStr(HEX_PARSE_OUTPUT), HEX_PARSE_INPUT); + BOOST_CHECK_EQUAL(HexStr(Span{HEX_PARSE_OUTPUT}.last(0)), ""); + BOOST_CHECK_EQUAL(HexStr(Span{HEX_PARSE_OUTPUT}.first(0)), ""); { - const std::vector in_s{ParseHex_expected, ParseHex_expected + 5}; + constexpr std::string_view out_exp{"04678afdb0"}; + constexpr std::span in_s{HEX_PARSE_OUTPUT, out_exp.size() / 2}; const Span in_u{MakeUCharSpan(in_s)}; const Span in_b{MakeByteSpan(in_s)}; - const std::string out_exp{"04678afdb0"}; BOOST_CHECK_EQUAL(HexStr(in_u), out_exp); BOOST_CHECK_EQUAL(HexStr(in_s), out_exp); From 5b74a849cf5c54543280ba6488ae7f87361b1e2f Mon Sep 17 00:00:00 2001 From: l0rinc Date: Wed, 28 Aug 2024 13:28:25 +0200 Subject: [PATCH 07/10] util: Add consteval ""_hex[_v][_u8] literals ""_hex is a compile-time user-defined literal returning std::array, equivalent of ParseHex. Variants: - ""_hex_v returns std::vector - ""_hex_u8 returns std::array - ""_hex_v_u8 returns std::vector - Directly serializable as a size-prefixed OP_PUSH CScript payload using operator<<. Also extracts from_hex into shared util::ConstevalHexDigit function. Co-Authored-By: hodlinator <172445034+hodlinator@users.noreply.github.com> Co-Authored-By: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz> Co-Authored-By: Ryan Ofsky Co-Authored-By: stickies-v --- src/test/util_tests.cpp | 29 ++++++++++++++++ src/uint256.h | 12 ++----- src/util/strencodings.h | 77 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 10 deletions(-) diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index fbc8af789d..1624fb8b5b 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -7,6 +7,7 @@ #include // For Hash() #include // For CKey #include