bip324.cpp raw
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 {
21 static ECC_Context ecc_context{};
22 SelectParams(ChainType::MAIN);
23 }
24
25 } // namespace
26
27 FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize)
28 {
29 // Test that BIP324Cipher's encryption and decryption agree.
30
31 // Load keys from fuzzer.
32 FuzzedDataProvider provider(buffer.data(), buffer.size());
33 // Initiator key
34 CKey init_key = ConsumePrivateKey(provider, /*compressed=*/true);
35 if (!init_key.IsValid()) return;
36 // Initiator entropy
37 auto init_ent = provider.ConsumeBytes<std::byte>(32);
38 init_ent.resize(32);
39 // Responder key
40 CKey resp_key = ConsumePrivateKey(provider, /*compressed=*/true);
41 if (!resp_key.IsValid()) return;
42 // Responder entropy
43 auto resp_ent = provider.ConsumeBytes<std::byte>(32);
44 resp_ent.resize(32);
45
46 // Initialize ciphers by exchanging public keys.
47 BIP324Cipher initiator(init_key, init_ent);
48 assert(!initiator);
49 BIP324Cipher responder(resp_key, resp_ent);
50 assert(!responder);
51 initiator.Initialize(responder.GetOurPubKey(), true);
52 assert(initiator);
53 responder.Initialize(initiator.GetOurPubKey(), false);
54 assert(responder);
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 InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
61
62 // Compare session IDs and garbage terminators.
63 assert(std::ranges::equal(initiator.GetSessionID(), responder.GetSessionID()));
64 assert(std::ranges::equal(initiator.GetSendGarbageTerminator(), responder.GetReceiveGarbageTerminator()));
65 assert(std::ranges::equal(initiator.GetReceiveGarbageTerminator(), responder.GetSendGarbageTerminator()));
66
67 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 unsigned mode = provider.ConsumeIntegral<uint8_t>();
75 bool ignore = mode & 1;
76 bool from_init = mode & 2;
77 bool damage = mode & 4;
78 unsigned aad_length_bits = 4 * ((mode >> 3) & 3);
79 unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
80 unsigned length_bits = 2 * ((mode >> 5) & 7);
81 unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
82 // Generate aad and content.
83 auto aad = rng.randbytes<std::byte>(aad_length);
84 auto contents = rng.randbytes<std::byte>(length);
85
86 // Pick sides.
87 auto& sender{from_init ? initiator : responder};
88 auto& receiver{from_init ? responder : initiator};
89
90 // Encrypt
91 std::vector<std::byte> ciphertext(length + initiator.EXPANSION);
92 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 if (damage) {
97 unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0,
98 (ciphertext.size() + aad.size()) * 8U - 1U);
99 unsigned damage_pos = damage_bit >> 3;
100 std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
101 if (damage_pos >= ciphertext.size()) {
102 aad[damage_pos - ciphertext.size()] ^= damage_val;
103 } else {
104 ciphertext[damage_pos] ^= damage_val;
105 }
106 }
107
108 // Decrypt length
109 uint32_t dec_length = receiver.DecryptLength(std::span{ciphertext}.first(initiator.LENGTH_LEN));
110 if (!damage) {
111 assert(dec_length == length);
112 } else {
113 // For performance reasons, don't try to decode if length got increased too much.
114 if (dec_length > 16384 + length) break;
115 // Otherwise, just append zeros if dec_length > length.
116 ciphertext.resize(dec_length + initiator.EXPANSION);
117 }
118
119 // Decrypt
120 std::vector<std::byte> decrypt(dec_length);
121 bool dec_ignore{false};
122 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 assert(!ok == damage);
125 if (!ok) break;
126 assert(ignore == dec_ignore);
127 assert(decrypt == contents);
128 }
129 }
130