/tmp/bitcoin/src/wallet/test/wallet_crypto_tests.cpp
Line | Count | Source |
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 | 70 | { |
26 | 70 | CCrypter crypt; |
27 | 70 | crypt.SetKeyFromPassphrase(passphrase, salt, rounds, 0); |
28 | | |
29 | 70 | if (!correct_key.empty()) { |
30 | 1 | BOOST_CHECK_MESSAGE(memcmp(crypt.vchKey.data(), correct_key.data(), crypt.vchKey.size()) == 0, |
31 | 1 | HexStr(crypt.vchKey) + std::string(" != ") + HexStr(correct_key)); |
32 | 1 | } |
33 | 70 | if (!correct_iv.empty()) { |
34 | 1 | BOOST_CHECK_MESSAGE(memcmp(crypt.vchIV.data(), correct_iv.data(), crypt.vchIV.size()) == 0, |
35 | 1 | HexStr(crypt.vchIV) + std::string(" != ") + HexStr(correct_iv)); |
36 | 1 | } |
37 | 70 | } |
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 | 2 | { |
43 | 2 | TestPassphraseSingle(salt, passphrase, rounds, correct_key, correct_iv); |
44 | 70 | for (SecureString::const_iterator it{passphrase.begin()}; it != passphrase.end(); ++it) { |
45 | 68 | TestPassphraseSingle(salt, SecureString{it, passphrase.end()}, rounds); |
46 | 68 | } |
47 | 2 | } |
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 | 3.43k | { |
52 | 3.43k | CKeyingMaterial decrypted; |
53 | 3.43k | crypt.Decrypt(ciphertext, decrypted); |
54 | 3.43k | if (!correct_plaintext.empty()) { |
55 | 3.33k | BOOST_CHECK_EQUAL_COLLECTIONS(decrypted.begin(), decrypted.end(), correct_plaintext.begin(), correct_plaintext.end()); |
56 | 3.33k | } |
57 | 3.43k | } |
58 | | |
59 | | static void TestEncryptSingle(const CCrypter& crypt, const CKeyingMaterial& plaintext, |
60 | | const std::span<const unsigned char> correct_ciphertext = {}) |
61 | 3.33k | { |
62 | 3.33k | std::vector<unsigned char> ciphertext; |
63 | 3.33k | crypt.Encrypt(plaintext, ciphertext); |
64 | | |
65 | 3.33k | if (!correct_ciphertext.empty()) { |
66 | 0 | BOOST_CHECK_EQUAL_COLLECTIONS(ciphertext.begin(), ciphertext.end(), correct_ciphertext.begin(), correct_ciphertext.end()); |
67 | 0 | } |
68 | | |
69 | 3.33k | TestDecrypt(crypt, ciphertext, /*correct_plaintext=*/plaintext); |
70 | 3.33k | } |
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 | 101 | { |
75 | 101 | TestEncryptSingle(crypt, CKeyingMaterial{plaintext.begin(), plaintext.end()}, correct_ciphertext); |
76 | 3.33k | for (auto it{plaintext.begin()}; it != plaintext.end(); ++it) { |
77 | 3.23k | TestEncryptSingle(crypt, CKeyingMaterial{it, plaintext.end()}); |
78 | 3.23k | } |
79 | 101 | } |
80 | | |
81 | | }; |
82 | | |
83 | 1 | BOOST_AUTO_TEST_CASE(passphrase) { |
84 | | // These are expensive. |
85 | | |
86 | 1 | TestCrypter::TestPassphrase("0000deadbeef0000"_hex_u8, "test", CMasterKey::DEFAULT_DERIVE_ITERATIONS, |
87 | 1 | "fc7aba077ad5f4c3a0988d8daa4810d0d4a0e3bcb53af662998898f33df0556a"_hex_u8, |
88 | 1 | "cf2f2691526dd1aa220896fb8bf7c369"_hex_u8); |
89 | | |
90 | 1 | std::string hash(GetRandHash().ToString()); |
91 | 1 | std::vector<unsigned char> vchSalt(8); |
92 | 1 | GetRandBytes(vchSalt); |
93 | 1 | uint32_t rounds = m_rng.rand32(); |
94 | 1 | if (rounds > 30000) |
95 | 1 | rounds = 30000; |
96 | 1 | TestCrypter::TestPassphrase(vchSalt, SecureString(hash.begin(), hash.end()), rounds); |
97 | 1 | } |
98 | | |
99 | 1 | BOOST_AUTO_TEST_CASE(encrypt) { |
100 | 1 | constexpr std::array<uint8_t, WALLET_CRYPTO_SALT_SIZE> salt{"0000deadbeef0000"_hex_u8}; |
101 | 1 | CCrypter crypt; |
102 | 1 | crypt.SetKeyFromPassphrase("passphrase", salt, CMasterKey::DEFAULT_DERIVE_ITERATIONS, 0); |
103 | 1 | TestCrypter::TestEncrypt(crypt, "22bcade09ac03ff6386914359cfe885cfeb5f77ff0d670f102f619687453b29d"_hex_u8); |
104 | | |
105 | 101 | for (int i = 0; i != 100; i++) |
106 | 100 | { |
107 | 100 | uint256 hash(GetRandHash()); |
108 | 100 | TestCrypter::TestEncrypt(crypt, std::span<unsigned char>{hash.begin(), hash.end()}); |
109 | 100 | } |
110 | | |
111 | 1 | } |
112 | | |
113 | 1 | BOOST_AUTO_TEST_CASE(decrypt) { |
114 | 1 | constexpr std::array<uint8_t, WALLET_CRYPTO_SALT_SIZE> salt{"0000deadbeef0000"_hex_u8}; |
115 | 1 | CCrypter crypt; |
116 | 1 | crypt.SetKeyFromPassphrase("passphrase", salt, CMasterKey::DEFAULT_DERIVE_ITERATIONS, 0); |
117 | | |
118 | | // Some corner cases the came up while testing |
119 | 1 | TestCrypter::TestDecrypt(crypt,"795643ce39d736088367822cdc50535ec6f103715e3e48f4f3b1a60a08ef59ca"_hex_u8); |
120 | 1 | TestCrypter::TestDecrypt(crypt,"de096f4a8f9bd97db012aa9d90d74de8cdea779c3ee8bc7633d8b5d6da703486"_hex_u8); |
121 | 1 | TestCrypter::TestDecrypt(crypt,"32d0a8974e3afd9c6c3ebf4d66aa4e6419f8c173de25947f98cf8b7ace49449c"_hex_u8); |
122 | 1 | TestCrypter::TestDecrypt(crypt,"e7c055cca2faa78cb9ac22c9357a90b4778ded9b2cc220a14cea49f931e596ea"_hex_u8); |
123 | 1 | TestCrypter::TestDecrypt(crypt,"b88efddd668a6801d19516d6830da4ae9811988ccbaf40df8fbb72f3f4d335fd"_hex_u8); |
124 | 1 | TestCrypter::TestDecrypt(crypt,"8cae76aa6a43694e961ebcb28c8ca8f8540b84153d72865e8561ddd93fa7bfa9"_hex_u8); |
125 | | |
126 | 101 | for (int i = 0; i != 100; i++) |
127 | 100 | { |
128 | 100 | uint256 hash(GetRandHash()); |
129 | 100 | TestCrypter::TestDecrypt(crypt, std::vector<unsigned char>(hash.begin(), hash.end())); |
130 | 100 | } |
131 | 1 | } |
132 | | |
133 | | BOOST_AUTO_TEST_SUITE_END() |
134 | | } // namespace wallet |