Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
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 <cstdint>
14#include <vector>
15
16namespace {
17
18void Initialize()
19{
20 static ECC_Context ecc_context{};
22}
23
24} // namespace
25
26FUZZ_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 InsecureRandomContext 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 auto aad = rng.randbytes<std::byte>(aad_length);
83 auto contents = rng.randbytes<std::byte>(length);
84
85 // Pick sides.
86 auto& sender{from_init ? initiator : responder};
87 auto& receiver{from_init ? responder : initiator};
88
89 // Encrypt
90 std::vector<std::byte> ciphertext(length + initiator.EXPANSION);
91 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 if (damage) {
96 unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0,
97 (ciphertext.size() + aad.size()) * 8U - 1U);
98 unsigned damage_pos = damage_bit >> 3;
99 std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
100 if (damage_pos >= ciphertext.size()) {
101 aad[damage_pos - ciphertext.size()] ^= damage_val;
102 } else {
103 ciphertext[damage_pos] ^= damage_val;
104 }
105 }
106
107 // Decrypt length
108 uint32_t dec_length = receiver.DecryptLength(Span{ciphertext}.first(initiator.LENGTH_LEN));
109 if (!damage) {
110 assert(dec_length == length);
111 } else {
112 // For performance reasons, don't try to decode if length got increased too much.
113 if (dec_length > 16384 + length) break;
114 // Otherwise, just append zeros if dec_length > length.
115 ciphertext.resize(dec_length + initiator.EXPANSION);
116 }
117
118 // Decrypt
119 std::vector<std::byte> decrypt(dec_length);
120 bool dec_ignore{false};
121 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 assert(!ok == damage);
124 if (!ok) break;
125 assert(ignore == dec_ignore);
126 assert(decrypt == contents);
127 }
128}
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 FUZZ_TARGET(...)
Definition fuzz.h:35
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition fuzz.h:22
CKey ConsumePrivateKey(FuzzedDataProvider &fuzzed_data_provider, std::optional< bool > compressed) noexcept
Definition util.cpp:230
assert(!tx.IsCoinBase())