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