Bitcoin Core  27.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 <span.h>
9 #include <test/fuzz/fuzz.h>
10 #include <test/fuzz/util.h>
12 
13 #include <cstdint>
14 #include <vector>
15 
16 namespace {
17 
18 void Initialize()
19 {
20  ECC_Start();
22 }
23 
24 } // namespace
25 
26 FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize)
27 {
28  // Test that BIP324Cipher's encryption and decryption agree.
29 
30  // Load keys from fuzzer.
31  FuzzedDataProvider provider(buffer.data(), buffer.size());
32  // Initiator key
33  CKey init_key = ConsumePrivateKey(provider, /*compressed=*/true);
34  if (!init_key.IsValid()) return;
35  // Initiator entropy
36  auto init_ent = provider.ConsumeBytes<std::byte>(32);
37  init_ent.resize(32);
38  // Responder key
39  CKey resp_key = ConsumePrivateKey(provider, /*compressed=*/true);
40  if (!resp_key.IsValid()) return;
41  // Responder entropy
42  auto resp_ent = provider.ConsumeBytes<std::byte>(32);
43  resp_ent.resize(32);
44 
45  // Initialize ciphers by exchanging public keys.
46  BIP324Cipher initiator(init_key, init_ent);
47  assert(!initiator);
48  BIP324Cipher responder(resp_key, resp_ent);
49  assert(!responder);
50  initiator.Initialize(responder.GetOurPubKey(), true);
51  assert(initiator);
52  responder.Initialize(initiator.GetOurPubKey(), false);
53  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  XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
60 
61  // Compare session IDs and garbage terminators.
62  assert(initiator.GetSessionID() == responder.GetSessionID());
65 
66  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  unsigned mode = provider.ConsumeIntegral<uint8_t>();
74  bool ignore = mode & 1;
75  bool from_init = mode & 2;
76  bool damage = mode & 4;
77  unsigned aad_length_bits = 4 * ((mode >> 3) & 3);
78  unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
79  unsigned length_bits = 2 * ((mode >> 5) & 7);
80  unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
81  // Generate aad and content.
82  std::vector<std::byte> aad(aad_length);
83  for (auto& val : aad) val = std::byte{(uint8_t)rng()};
84  std::vector<std::byte> contents(length);
85  for (auto& val : contents) val = std::byte{(uint8_t)rng()};
86 
87  // Pick sides.
88  auto& sender{from_init ? initiator : responder};
89  auto& receiver{from_init ? responder : initiator};
90 
91  // Encrypt
92  std::vector<std::byte> ciphertext(length + initiator.EXPANSION);
93  sender.Encrypt(contents, aad, ignore, ciphertext);
94 
95  // Optionally damage 1 bit in either the ciphertext (corresponding to a change in transit)
96  // or the aad (to make sure that decryption will fail if the AAD mismatches).
97  if (damage) {
98  unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0,
99  (ciphertext.size() + aad.size()) * 8U - 1U);
100  unsigned damage_pos = damage_bit >> 3;
101  std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
102  if (damage_pos >= ciphertext.size()) {
103  aad[damage_pos - ciphertext.size()] ^= damage_val;
104  } else {
105  ciphertext[damage_pos] ^= damage_val;
106  }
107  }
108 
109  // Decrypt length
110  uint32_t dec_length = receiver.DecryptLength(Span{ciphertext}.first(initiator.LENGTH_LEN));
111  if (!damage) {
112  assert(dec_length == length);
113  } else {
114  // For performance reasons, don't try to decode if length got increased too much.
115  if (dec_length > 16384 + length) break;
116  // Otherwise, just append zeros if dec_length > length.
117  ciphertext.resize(dec_length + initiator.EXPANSION);
118  }
119 
120  // Decrypt
121  std::vector<std::byte> decrypt(dec_length);
122  bool dec_ignore{false};
123  bool ok = receiver.Decrypt(Span{ciphertext}.subspan(initiator.LENGTH_LEN), aad, dec_ignore, decrypt);
124  // Decryption *must* fail if the packet was damaged, and succeed if it wasn't.
125  assert(!ok == damage);
126  if (!ok) break;
127  assert(ignore == dec_ignore);
128  assert(decrypt == contents);
129  }
130 }
ECC_Start()
Definition: key.cpp:435
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 > GetSendGarbageTerminator() const noexcept
Get the Garbage Terminator to send.
Definition: bip324.h:90
Span< const std::byte > GetSessionID() const noexcept
Get the Session ID.
Definition: bip324.h:87
const EllSwiftPubKey & GetOurPubKey() const noexcept
Retrieve our public key.
Definition: bip324.h:54
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
Span< const std::byte > GetReceiveGarbageTerminator() const noexcept
Get the expected Garbage Terminator to receive.
Definition: bip324.h:93
An encapsulated private key.
Definition: key.h:33
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:119
std::vector< T > ConsumeBytes(size_t num_bytes)
T ConsumeIntegralInRange(T min, T max)
A Span is an object that can refer to a contiguous sequence of objects.
Definition: span.h:98
CONSTEXPR_IF_NOT_DEBUG Span< C > first(std::size_t count) const noexcept
Definition: span.h:205
CONSTEXPR_IF_NOT_DEBUG Span< C > subspan(std::size_t offset) const noexcept
Definition: span.h:195
xoroshiro128++ PRNG.
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:23
FUZZ_TARGET(bip324_cipher_roundtrip,.init=Initialize)
Definition: bip324.cpp:26
CKey ConsumePrivateKey(FuzzedDataProvider &fuzzed_data_provider, std::optional< bool > compressed) noexcept
Definition: util.cpp:227
assert(!tx.IsCoinBase())