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