/root/bitcoin/src/test/fuzz/bip324.cpp
Line | Count | Source |
1 | | // Copyright (c) 2023-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 <bip324.h> |
6 | | #include <chainparams.h> |
7 | | #include <random.h> |
8 | | #include <span.h> |
9 | | #include <test/fuzz/FuzzedDataProvider.h> |
10 | | #include <test/fuzz/fuzz.h> |
11 | | #include <test/fuzz/util.h> |
12 | | |
13 | | #include <algorithm> |
14 | | #include <cstdint> |
15 | | #include <vector> |
16 | | |
17 | | namespace { |
18 | | |
19 | | void Initialize() |
20 | 0 | { |
21 | 0 | static ECC_Context ecc_context{}; |
22 | 0 | SelectParams(ChainType::MAIN); |
23 | 0 | } |
24 | | |
25 | | } // namespace |
26 | | |
27 | | FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize) |
28 | 1.58k | { |
29 | | // Test that BIP324Cipher's encryption and decryption agree. |
30 | | |
31 | | // Load keys from fuzzer. |
32 | 1.58k | FuzzedDataProvider provider(buffer.data(), buffer.size()); |
33 | | // Initiator key |
34 | 1.58k | CKey init_key = ConsumePrivateKey(provider, /*compressed=*/true); |
35 | 1.58k | if (!init_key.IsValid()) return; Branch (35:9): [True: 3, False: 1.58k]
|
36 | | // Initiator entropy |
37 | 1.58k | auto init_ent = provider.ConsumeBytes<std::byte>(32); |
38 | 1.58k | init_ent.resize(32); |
39 | | // Responder key |
40 | 1.58k | CKey resp_key = ConsumePrivateKey(provider, /*compressed=*/true); |
41 | 1.58k | if (!resp_key.IsValid()) return; Branch (41:9): [True: 17, False: 1.56k]
|
42 | | // Responder entropy |
43 | 1.56k | auto resp_ent = provider.ConsumeBytes<std::byte>(32); |
44 | 1.56k | resp_ent.resize(32); |
45 | | |
46 | | // Initialize ciphers by exchanging public keys. |
47 | 1.56k | BIP324Cipher initiator(init_key, init_ent); |
48 | 1.56k | assert(!initiator); Branch (48:5): [True: 1.56k, False: 0]
|
49 | 1.56k | BIP324Cipher responder(resp_key, resp_ent); |
50 | 1.56k | assert(!responder); Branch (50:5): [True: 1.56k, False: 0]
|
51 | 1.56k | initiator.Initialize(responder.GetOurPubKey(), true); |
52 | 1.56k | assert(initiator); Branch (52:5): [True: 1.56k, False: 0]
|
53 | 1.56k | responder.Initialize(initiator.GetOurPubKey(), false); |
54 | 1.56k | assert(responder); Branch (54:5): [True: 1.56k, False: 0]
|
55 | | |
56 | | // Initialize RNG deterministically, to generate contents and AAD. We assume that there are no |
57 | | // (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid |
58 | | // reading the actual data for those from the fuzzer input (which would need large amounts of |
59 | | // data). |
60 | 1.56k | InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>()); |
61 | | |
62 | | // Compare session IDs and garbage terminators. |
63 | 1.56k | assert(std::ranges::equal(initiator.GetSessionID(), responder.GetSessionID())); Branch (63:5): [True: 1.56k, False: 0]
|
64 | 1.56k | assert(std::ranges::equal(initiator.GetSendGarbageTerminator(), responder.GetReceiveGarbageTerminator())); Branch (64:5): [True: 1.56k, False: 0]
|
65 | 1.56k | assert(std::ranges::equal(initiator.GetReceiveGarbageTerminator(), responder.GetSendGarbageTerminator())); Branch (65:5): [True: 1.56k, False: 0]
|
66 | | |
67 | 135k | LIMITED_WHILE (provider.remaining_bytes(), 1000) { |
68 | | // Mode: |
69 | | // - Bit 0: whether the ignore bit is set in message |
70 | | // - Bit 1: whether the responder (0) or initiator (1) sends |
71 | | // - Bit 2: whether this ciphertext will be corrupted (making it the last sent one) |
72 | | // - Bit 3-4: controls the maximum aad length (max 4095 bytes) |
73 | | // - Bit 5-7: controls the maximum content length (max 16383 bytes, for performance reasons) |
74 | 135k | unsigned mode = provider.ConsumeIntegral<uint8_t>(); |
75 | 135k | bool ignore = mode & 1; |
76 | 135k | bool from_init = mode & 2; |
77 | 135k | bool damage = mode & 4; |
78 | 135k | unsigned aad_length_bits = 4 * ((mode >> 3) & 3); |
79 | 135k | unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1); |
80 | 135k | unsigned length_bits = 2 * ((mode >> 5) & 7); |
81 | 135k | unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1); |
82 | | // Generate aad and content. |
83 | 135k | auto aad = rng.randbytes<std::byte>(aad_length); |
84 | 135k | auto contents = rng.randbytes<std::byte>(length); |
85 | | |
86 | | // Pick sides. |
87 | 135k | auto& sender{from_init ? initiator : responder}; Branch (87:22): [True: 37.9k, False: 97.5k]
|
88 | 135k | auto& receiver{from_init ? responder : initiator}; Branch (88:24): [True: 37.9k, False: 97.5k]
|
89 | | |
90 | | // Encrypt |
91 | 135k | std::vector<std::byte> ciphertext(length + initiator.EXPANSION); |
92 | 135k | sender.Encrypt(contents, aad, ignore, ciphertext); |
93 | | |
94 | | // Optionally damage 1 bit in either the ciphertext (corresponding to a change in transit) |
95 | | // or the aad (to make sure that decryption will fail if the AAD mismatches). |
96 | 135k | if (damage) { Branch (96:13): [True: 967, False: 134k]
|
97 | 967 | unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, |
98 | 967 | (ciphertext.size() + aad.size()) * 8U - 1U); |
99 | 967 | unsigned damage_pos = damage_bit >> 3; |
100 | 967 | std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))}; |
101 | 967 | if (damage_pos >= ciphertext.size()) { Branch (101:17): [True: 174, False: 793]
|
102 | 174 | aad[damage_pos - ciphertext.size()] ^= damage_val; |
103 | 793 | } else { |
104 | 793 | ciphertext[damage_pos] ^= damage_val; |
105 | 793 | } |
106 | 967 | } |
107 | | |
108 | | // Decrypt length |
109 | 135k | uint32_t dec_length = receiver.DecryptLength(std::span{ciphertext}.first(initiator.LENGTH_LEN)); |
110 | 135k | if (!damage) { Branch (110:13): [True: 134k, False: 967]
|
111 | 134k | assert(dec_length == length); Branch (111:13): [True: 134k, False: 0]
|
112 | 134k | } else { |
113 | | // For performance reasons, don't try to decode if length got increased too much. |
114 | 967 | if (dec_length > 16384 + length) break; Branch (114:17): [True: 50, False: 917]
|
115 | | // Otherwise, just append zeros if dec_length > length. |
116 | 917 | ciphertext.resize(dec_length + initiator.EXPANSION); |
117 | 917 | } |
118 | | |
119 | | // Decrypt |
120 | 135k | std::vector<std::byte> decrypt(dec_length); |
121 | 135k | bool dec_ignore{false}; |
122 | 135k | bool ok = receiver.Decrypt(std::span{ciphertext}.subspan(initiator.LENGTH_LEN), aad, dec_ignore, decrypt); |
123 | | // Decryption *must* fail if the packet was damaged, and succeed if it wasn't. |
124 | 135k | assert(!ok == damage); Branch (124:9): [True: 135k, False: 0]
|
125 | 135k | if (!ok) break; Branch (125:13): [True: 917, False: 134k]
|
126 | 135k | assert(ignore == dec_ignore); Branch (126:9): [True: 134k, False: 0]
|
127 | 134k | assert(decrypt == contents); Branch (127:9): [True: 134k, False: 0]
|
128 | 134k | } |
129 | 1.56k | } |