Bitcoin Core 28.99.0
P2P Digital Currency
bip324.cpp
Go to the documentation of this file.
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>
10#include <test/fuzz/fuzz.h>
11#include <test/fuzz/util.h>
12
13#include <algorithm>
14#include <cstdint>
15#include <vector>
16
17namespace {
18
19void Initialize()
20{
21 static ECC_Context ecc_context{};
23}
24
25} // namespace
26
27FUZZ_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(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(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}
ECC_Context ecc_context
void SelectParams(const ChainType chain)
Sets the params returned by Params() to those for the given chain type.
The BIP324 packet cipher, encapsulating its key derivation, stream cipher, and AEAD.
Definition: bip324.h:20
Span< const std::byte > GetReceiveGarbageTerminator() const noexcept
Get the expected Garbage Terminator to receive.
Definition: bip324.h:93
Span< const std::byte > GetSendGarbageTerminator() const noexcept
Get the Garbage Terminator to send.
Definition: bip324.h:90
const EllSwiftPubKey & GetOurPubKey() const noexcept
Retrieve our public key.
Definition: bip324.h:54
Span< const std::byte > GetSessionID() const noexcept
Get the Session ID.
Definition: bip324.h:87
static constexpr unsigned LENGTH_LEN
Definition: bip324.h:25
static constexpr unsigned EXPANSION
Definition: bip324.h:27
void Initialize(const EllSwiftPubKey &their_pubkey, bool initiator, bool self_decrypt=false) noexcept
Initialize when the other side's public key is received.
Definition: bip324.cpp:34
An encapsulated private key.
Definition: key.h:35
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:123
RAII class initializing and deinitializing global state for elliptic curve support.
Definition: key.h:322
std::vector< T > ConsumeBytes(size_t num_bytes)
T ConsumeIntegralInRange(T min, T max)
xoroshiro128++ PRNG.
Definition: random.h:416
std::vector< B > randbytes(size_t len) noexcept
Generate random bytes.
Definition: random.h:297
A Span is an object that can refer to a contiguous sequence of objects.
Definition: span.h:98
CONSTEXPR_IF_NOT_DEBUG Span< C > subspan(std::size_t offset) const noexcept
Definition: span.h:195
CONSTEXPR_IF_NOT_DEBUG Span< C > first(std::size_t count) const noexcept
Definition: span.h:205
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:22
FUZZ_TARGET(bip324_cipher_roundtrip,.init=Initialize)
Definition: bip324.cpp:27
CKey ConsumePrivateKey(FuzzedDataProvider &fuzzed_data_provider, std::optional< bool > compressed) noexcept
Definition: util.cpp:230
assert(!tx.IsCoinBase())