Bitcoin Core 28.99.0
P2P Digital Currency
crypto_chacha20poly1305.cpp
Go to the documentation of this file.
1// Copyright (c) 2020-2021 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
6#include <random.h>
7#include <span.h>
9#include <test/fuzz/fuzz.h>
10#include <test/fuzz/util.h>
11
12#include <cstddef>
13#include <cstdint>
14#include <vector>
15
16constexpr static inline void crypt_till_rekey(FSChaCha20Poly1305& aead, int rekey_interval, bool encrypt)
17{
18 for (int i = 0; i < rekey_interval; ++i) {
19 std::byte dummy_tag[FSChaCha20Poly1305::EXPANSION] = {{}};
20 if (encrypt) {
21 aead.Encrypt(Span{dummy_tag}.first(0), Span{dummy_tag}.first(0), dummy_tag);
22 } else {
23 aead.Decrypt(dummy_tag, Span{dummy_tag}.first(0), Span{dummy_tag}.first(0));
24 }
25 }
26}
27
28FUZZ_TARGET(crypto_aeadchacha20poly1305)
29{
30 FuzzedDataProvider provider{buffer.data(), buffer.size()};
31
32 auto key = provider.ConsumeBytes<std::byte>(32);
33 key.resize(32);
34 AEADChaCha20Poly1305 aead(key);
35
36 // Initialize RNG deterministically, to generate contents and AAD. We assume that there are no
37 // (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid
38 // reading the actual data for those from the fuzzer input (which would need large amounts of
39 // data).
40 InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
41
42 LIMITED_WHILE(provider.ConsumeBool(), 100)
43 {
44 // Mode:
45 // - Bit 0: whether to use single-plain Encrypt/Decrypt; otherwise use a split at prefix.
46 // - Bit 2: whether this ciphertext will be corrupted (making it the last sent one)
47 // - Bit 3-4: controls the maximum aad length (max 511 bytes)
48 // - Bit 5-7: controls the maximum content length (max 16383 bytes, for performance reasons)
49 unsigned mode = provider.ConsumeIntegral<uint8_t>();
50 bool use_splits = mode & 1;
51 bool damage = mode & 4;
52 unsigned aad_length_bits = 3 * ((mode >> 3) & 3);
53 unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
54 unsigned length_bits = 2 * ((mode >> 5) & 7);
55 unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
56 // Generate aad and content.
57 auto aad = rng.randbytes<std::byte>(aad_length);
58 auto plain = rng.randbytes<std::byte>(length);
59 std::vector<std::byte> cipher(length + AEADChaCha20Poly1305::EXPANSION);
60 // Generate nonce
61 AEADChaCha20Poly1305::Nonce96 nonce = {(uint32_t)rng(), rng()};
62
63 if (use_splits && length > 0) {
64 size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
65 aead.Encrypt(Span{plain}.first(split_index), Span{plain}.subspan(split_index), aad, nonce, cipher);
66 } else {
67 aead.Encrypt(plain, aad, nonce, cipher);
68 }
69
70 // Test Keystream output
71 std::vector<std::byte> keystream(length);
72 aead.Keystream(nonce, keystream);
73 for (size_t i = 0; i < length; ++i) {
74 assert((plain[i] ^ keystream[i]) == cipher[i]);
75 }
76
77 std::vector<std::byte> decrypted_contents(length);
78 bool ok{false};
79
80 // damage the key
81 unsigned key_position = provider.ConsumeIntegralInRange<unsigned>(0, 31);
82 std::byte damage_val{(uint8_t)(1U << (key_position & 7))};
83 std::vector<std::byte> bad_key = key;
84 bad_key[key_position] ^= damage_val;
85
86 AEADChaCha20Poly1305 bad_aead(bad_key);
87 ok = bad_aead.Decrypt(cipher, aad, nonce, decrypted_contents);
88 assert(!ok);
89
90 // Optionally damage 1 bit in either the cipher (corresponding to a change in transit)
91 // or the aad (to make sure that decryption will fail if the AAD mismatches).
92 if (damage) {
93 unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, (cipher.size() + aad.size()) * 8U - 1U);
94 unsigned damage_pos = damage_bit >> 3;
95 std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
96 if (damage_pos >= cipher.size()) {
97 aad[damage_pos - cipher.size()] ^= damage_val;
98 } else {
99 cipher[damage_pos] ^= damage_val;
100 }
101 }
102
103 if (use_splits && length > 0) {
104 size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
105 ok = aead.Decrypt(cipher, aad, nonce, Span{decrypted_contents}.first(split_index), Span{decrypted_contents}.subspan(split_index));
106 } else {
107 ok = aead.Decrypt(cipher, aad, nonce, decrypted_contents);
108 }
109
110 // Decryption *must* fail if the packet was damaged, and succeed if it wasn't.
111 assert(!ok == damage);
112 if (!ok) break;
113 assert(decrypted_contents == plain);
114 }
115}
116
117FUZZ_TARGET(crypto_fschacha20poly1305)
118{
119 FuzzedDataProvider provider{buffer.data(), buffer.size()};
120
121 uint32_t rekey_interval = provider.ConsumeIntegralInRange<size_t>(32, 512);
122 auto key = provider.ConsumeBytes<std::byte>(32);
123 key.resize(32);
124 FSChaCha20Poly1305 enc_aead(key, rekey_interval);
125 FSChaCha20Poly1305 dec_aead(key, rekey_interval);
126
127 // Initialize RNG deterministically, to generate contents and AAD. We assume that there are no
128 // (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid
129 // reading the actual data for those from the fuzzer input (which would need large amounts of
130 // data).
131 InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
132
133 LIMITED_WHILE(provider.ConsumeBool(), 100)
134 {
135 // Mode:
136 // - Bit 0: whether to use single-plain Encrypt/Decrypt; otherwise use a split at prefix.
137 // - Bit 2: whether this ciphertext will be corrupted (making it the last sent one)
138 // - Bit 3-4: controls the maximum aad length (max 511 bytes)
139 // - Bit 5-7: controls the maximum content length (max 16383 bytes, for performance reasons)
140 unsigned mode = provider.ConsumeIntegral<uint8_t>();
141 bool use_splits = mode & 1;
142 bool damage = mode & 4;
143 unsigned aad_length_bits = 3 * ((mode >> 3) & 3);
144 unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
145 unsigned length_bits = 2 * ((mode >> 5) & 7);
146 unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
147 // Generate aad and content.
148 auto aad = rng.randbytes<std::byte>(aad_length);
149 auto plain = rng.randbytes<std::byte>(length);
150 std::vector<std::byte> cipher(length + FSChaCha20Poly1305::EXPANSION);
151
152 crypt_till_rekey(enc_aead, rekey_interval, true);
153 if (use_splits && length > 0) {
154 size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
155 enc_aead.Encrypt(Span{plain}.first(split_index), Span{plain}.subspan(split_index), aad, cipher);
156 } else {
157 enc_aead.Encrypt(plain, aad, cipher);
158 }
159
160 std::vector<std::byte> decrypted_contents(length);
161 bool ok{false};
162
163 // damage the key
164 unsigned key_position = provider.ConsumeIntegralInRange<unsigned>(0, 31);
165 std::byte damage_val{(uint8_t)(1U << (key_position & 7))};
166 std::vector<std::byte> bad_key = key;
167 bad_key[key_position] ^= damage_val;
168
169 FSChaCha20Poly1305 bad_fs_aead(bad_key, rekey_interval);
170 crypt_till_rekey(bad_fs_aead, rekey_interval, false);
171 ok = bad_fs_aead.Decrypt(cipher, aad, decrypted_contents);
172 assert(!ok);
173
174 // Optionally damage 1 bit in either the cipher (corresponding to a change in transit)
175 // or the aad (to make sure that decryption will fail if the AAD mismatches).
176 if (damage) {
177 unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, (cipher.size() + aad.size()) * 8U - 1U);
178 unsigned damage_pos = damage_bit >> 3;
179 std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
180 if (damage_pos >= cipher.size()) {
181 aad[damage_pos - cipher.size()] ^= damage_val;
182 } else {
183 cipher[damage_pos] ^= damage_val;
184 }
185 }
186
187 crypt_till_rekey(dec_aead, rekey_interval, false);
188 if (use_splits && length > 0) {
189 size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
190 ok = dec_aead.Decrypt(cipher, aad, Span{decrypted_contents}.first(split_index), Span{decrypted_contents}.subspan(split_index));
191 } else {
192 ok = dec_aead.Decrypt(cipher, aad, decrypted_contents);
193 }
194
195 // Decryption *must* fail if the packet was damaged, and succeed if it wasn't.
196 assert(!ok == damage);
197 if (!ok) break;
198 assert(decrypted_contents == plain);
199 }
200}
The AEAD_CHACHA20_POLY1305 authenticated encryption algorithm from RFC8439 section 2....
ChaCha20::Nonce96 Nonce96
96-bit nonce type.
void Encrypt(Span< const std::byte > plain, Span< const std::byte > aad, Nonce96 nonce, Span< std::byte > cipher) noexcept
Encrypt a message with a specified 96-bit nonce and aad.
static constexpr unsigned EXPANSION
Expansion when encrypting.
bool Decrypt(Span< const std::byte > cipher, Span< const std::byte > aad, Nonce96 nonce, Span< std::byte > plain) noexcept
Decrypt a message with a specified 96-bit nonce and aad.
void Keystream(Nonce96 nonce, Span< std::byte > keystream) noexcept
Get a number of keystream bytes from the underlying stream cipher.
Forward-secure wrapper around AEADChaCha20Poly1305.
bool Decrypt(Span< const std::byte > cipher, Span< const std::byte > aad, Span< std::byte > plain) noexcept
Decrypt a message with a specified aad.
static constexpr auto EXPANSION
Expansion when encrypting.
void Encrypt(Span< const std::byte > plain, Span< const std::byte > aad, Span< std::byte > cipher) noexcept
Encrypt a message with a specified aad.
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
static constexpr void crypt_till_rekey(FSChaCha20Poly1305 &aead, int rekey_interval, bool encrypt)
FUZZ_TARGET(crypto_aeadchacha20poly1305)
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:22
unsigned int nonce
Definition: miner_tests.cpp:74
assert(!tx.IsCoinBase())