wallet_crypto_tests.cpp
1 // Copyright (c) 2014-present The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 5 #include <test/util/random.h> 6 #include <test/util/setup_common.h> 7 #include <util/strencodings.h> 8 #include <wallet/crypter.h> 9 10 #include <vector> 11 12 #include <boost/test/unit_test.hpp> 13 14 using namespace util::hex_literals; 15 16 namespace wallet { 17 BOOST_FIXTURE_TEST_SUITE(wallet_crypto_tests, BasicTestingSetup) 18 19 class TestCrypter 20 { 21 public: 22 static void TestPassphraseSingle(const std::span<const unsigned char> salt, const SecureString& passphrase, uint32_t rounds, 23 const std::span<const unsigned char> correct_key = {}, 24 const std::span<const unsigned char> correct_iv = {}) 25 { 26 CCrypter crypt; 27 crypt.SetKeyFromPassphrase(passphrase, salt, rounds, 0); 28 29 if (!correct_key.empty()) { 30 BOOST_CHECK_MESSAGE(memcmp(crypt.vchKey.data(), correct_key.data(), crypt.vchKey.size()) == 0, 31 HexStr(crypt.vchKey) + std::string(" != ") + HexStr(correct_key)); 32 } 33 if (!correct_iv.empty()) { 34 BOOST_CHECK_MESSAGE(memcmp(crypt.vchIV.data(), correct_iv.data(), crypt.vchIV.size()) == 0, 35 HexStr(crypt.vchIV) + std::string(" != ") + HexStr(correct_iv)); 36 } 37 } 38 39 static void TestPassphrase(const std::span<const unsigned char> salt, const SecureString& passphrase, uint32_t rounds, 40 const std::span<const unsigned char> correct_key = {}, 41 const std::span<const unsigned char> correct_iv = {}) 42 { 43 TestPassphraseSingle(salt, passphrase, rounds, correct_key, correct_iv); 44 for (SecureString::const_iterator it{passphrase.begin()}; it != passphrase.end(); ++it) { 45 TestPassphraseSingle(salt, SecureString{it, passphrase.end()}, rounds); 46 } 47 } 48 49 static void TestDecrypt(const CCrypter& crypt, const std::span<const unsigned char> ciphertext, 50 const std::span<const unsigned char> correct_plaintext = {}) 51 { 52 CKeyingMaterial decrypted; 53 crypt.Decrypt(ciphertext, decrypted); 54 if (!correct_plaintext.empty()) { 55 BOOST_CHECK_EQUAL_COLLECTIONS(decrypted.begin(), decrypted.end(), correct_plaintext.begin(), correct_plaintext.end()); 56 } 57 } 58 59 static void TestEncryptSingle(const CCrypter& crypt, const CKeyingMaterial& plaintext, 60 const std::span<const unsigned char> correct_ciphertext = {}) 61 { 62 std::vector<unsigned char> ciphertext; 63 crypt.Encrypt(plaintext, ciphertext); 64 65 if (!correct_ciphertext.empty()) { 66 BOOST_CHECK_EQUAL_COLLECTIONS(ciphertext.begin(), ciphertext.end(), correct_ciphertext.begin(), correct_ciphertext.end()); 67 } 68 69 TestDecrypt(crypt, ciphertext, /*correct_plaintext=*/plaintext); 70 } 71 72 static void TestEncrypt(const CCrypter& crypt, const std::span<const unsigned char> plaintext, 73 const std::span<const unsigned char> correct_ciphertext = {}) 74 { 75 TestEncryptSingle(crypt, CKeyingMaterial{plaintext.begin(), plaintext.end()}, correct_ciphertext); 76 for (auto it{plaintext.begin()}; it != plaintext.end(); ++it) { 77 TestEncryptSingle(crypt, CKeyingMaterial{it, plaintext.end()}); 78 } 79 } 80 81 }; 82 83 BOOST_AUTO_TEST_CASE(passphrase) { 84 // These are expensive. 85 86 TestCrypter::TestPassphrase("0000deadbeef0000"_hex_u8, "test", CMasterKey::DEFAULT_DERIVE_ITERATIONS, 87 "fc7aba077ad5f4c3a0988d8daa4810d0d4a0e3bcb53af662998898f33df0556a"_hex_u8, 88 "cf2f2691526dd1aa220896fb8bf7c369"_hex_u8); 89 90 std::string hash(GetRandHash().ToString()); 91 std::vector<unsigned char> vchSalt(8); 92 GetRandBytes(vchSalt); 93 uint32_t rounds = m_rng.rand32(); 94 if (rounds > 30000) 95 rounds = 30000; 96 TestCrypter::TestPassphrase(vchSalt, SecureString(hash.begin(), hash.end()), rounds); 97 } 98 99 BOOST_AUTO_TEST_CASE(encrypt) { 100 constexpr std::array<uint8_t, WALLET_CRYPTO_SALT_SIZE> salt{"0000deadbeef0000"_hex_u8}; 101 CCrypter crypt; 102 crypt.SetKeyFromPassphrase("passphrase", salt, CMasterKey::DEFAULT_DERIVE_ITERATIONS, 0); 103 TestCrypter::TestEncrypt(crypt, "22bcade09ac03ff6386914359cfe885cfeb5f77ff0d670f102f619687453b29d"_hex_u8); 104 105 for (int i = 0; i != 100; i++) 106 { 107 uint256 hash(GetRandHash()); 108 TestCrypter::TestEncrypt(crypt, std::span<unsigned char>{hash.begin(), hash.end()}); 109 } 110 111 } 112 113 BOOST_AUTO_TEST_CASE(decrypt) { 114 constexpr std::array<uint8_t, WALLET_CRYPTO_SALT_SIZE> salt{"0000deadbeef0000"_hex_u8}; 115 CCrypter crypt; 116 crypt.SetKeyFromPassphrase("passphrase", salt, CMasterKey::DEFAULT_DERIVE_ITERATIONS, 0); 117 118 // Some corner cases the came up while testing 119 TestCrypter::TestDecrypt(crypt,"795643ce39d736088367822cdc50535ec6f103715e3e48f4f3b1a60a08ef59ca"_hex_u8); 120 TestCrypter::TestDecrypt(crypt,"de096f4a8f9bd97db012aa9d90d74de8cdea779c3ee8bc7633d8b5d6da703486"_hex_u8); 121 TestCrypter::TestDecrypt(crypt,"32d0a8974e3afd9c6c3ebf4d66aa4e6419f8c173de25947f98cf8b7ace49449c"_hex_u8); 122 TestCrypter::TestDecrypt(crypt,"e7c055cca2faa78cb9ac22c9357a90b4778ded9b2cc220a14cea49f931e596ea"_hex_u8); 123 TestCrypter::TestDecrypt(crypt,"b88efddd668a6801d19516d6830da4ae9811988ccbaf40df8fbb72f3f4d335fd"_hex_u8); 124 TestCrypter::TestDecrypt(crypt,"8cae76aa6a43694e961ebcb28c8ca8f8540b84153d72865e8561ddd93fa7bfa9"_hex_u8); 125 126 for (int i = 0; i != 100; i++) 127 { 128 uint256 hash(GetRandHash()); 129 TestCrypter::TestDecrypt(crypt, std::vector<unsigned char>(hash.begin(), hash.end())); 130 } 131 } 132 133 BOOST_AUTO_TEST_SUITE_END() 134 } // namespace wallet