Branch data Line data Source code
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 : :
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 : 11733 : constexpr static inline void crypt_till_rekey(FSChaCha20Poly1305& aead, int rekey_interval, bool encrypt)
17 : : {
18 [ + + ]: 2839296 : for (int i = 0; i < rekey_interval; ++i) {
19 : 2827563 : std::byte dummy_tag[FSChaCha20Poly1305::EXPANSION] = {{}};
20 [ + + ]: 2827563 : if (encrypt) {
21 : 942521 : aead.Encrypt(Span{dummy_tag}.first(0), Span{dummy_tag}.first(0), dummy_tag);
22 : : } else {
23 : 1885042 : aead.Decrypt(dummy_tag, Span{dummy_tag}.first(0), Span{dummy_tag}.first(0));
24 : : }
25 : : }
26 : 11733 : }
27 : :
28 [ + - ]: 786 : FUZZ_TARGET(crypto_aeadchacha20poly1305)
29 : : {
30 : 168 : FuzzedDataProvider provider{buffer.data(), buffer.size()};
31 : :
32 : 168 : auto key = provider.ConsumeBytes<std::byte>(32);
33 [ + - ]: 168 : key.resize(32);
34 : 168 : 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 : 168 : InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
41 : :
42 [ + + + + ]: 6191 : 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 : 6084 : unsigned mode = provider.ConsumeIntegral<uint8_t>();
50 : 6084 : bool use_splits = mode & 1;
51 : 6084 : bool damage = mode & 4;
52 : 6084 : unsigned aad_length_bits = 3 * ((mode >> 3) & 3);
53 : 6084 : unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
54 : 6084 : unsigned length_bits = 2 * ((mode >> 5) & 7);
55 : 6084 : unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
56 : : // Generate aad and content.
57 : 6084 : auto aad = rng.randbytes<std::byte>(aad_length);
58 : 6084 : auto plain = rng.randbytes<std::byte>(length);
59 [ + - ]: 6084 : std::vector<std::byte> cipher(length + AEADChaCha20Poly1305::EXPANSION);
60 : : // Generate nonce
61 [ + + ]: 6084 : AEADChaCha20Poly1305::Nonce96 nonce = {(uint32_t)rng(), rng()};
62 : :
63 [ + + ]: 6084 : if (use_splits && length > 0) {
64 : 4334 : size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
65 : 4334 : aead.Encrypt(Span{plain}.first(split_index), Span{plain}.subspan(split_index), aad, nonce, cipher);
66 : : } else {
67 : 1750 : aead.Encrypt(plain, aad, nonce, cipher);
68 : : }
69 : :
70 : : // Test Keystream output
71 [ + - ]: 6084 : std::vector<std::byte> keystream(length);
72 : 6084 : aead.Keystream(nonce, keystream);
73 [ + + ]: 9816952 : for (size_t i = 0; i < length; ++i) {
74 [ - + ]: 9810868 : assert((plain[i] ^ keystream[i]) == cipher[i]);
75 : : }
76 : :
77 [ + - ]: 6084 : std::vector<std::byte> decrypted_contents(length);
78 : 6084 : bool ok{false};
79 : :
80 : : // damage the key
81 : 6084 : unsigned key_position = provider.ConsumeIntegralInRange<unsigned>(0, 31);
82 : 6084 : std::byte damage_val{(uint8_t)(1U << (key_position & 7))};
83 [ + - ]: 6084 : std::vector<std::byte> bad_key = key;
84 : 6084 : bad_key[key_position] ^= damage_val;
85 : :
86 : 6084 : AEADChaCha20Poly1305 bad_aead(bad_key);
87 : 6084 : ok = bad_aead.Decrypt(cipher, aad, nonce, decrypted_contents);
88 [ - + ]: 6084 : 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 [ + + ]: 6084 : if (damage) {
93 : 61 : unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, (cipher.size() + aad.size()) * 8U - 1U);
94 : 61 : unsigned damage_pos = damage_bit >> 3;
95 : 61 : std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
96 [ + + ]: 61 : if (damage_pos >= cipher.size()) {
97 : 7 : aad[damage_pos - cipher.size()] ^= damage_val;
98 : : } else {
99 : 54 : cipher[damage_pos] ^= damage_val;
100 : : }
101 : : }
102 : :
103 [ + + ]: 6084 : if (use_splits && length > 0) {
104 : 4334 : size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
105 : 4334 : ok = aead.Decrypt(cipher, aad, nonce, Span{decrypted_contents}.first(split_index), Span{decrypted_contents}.subspan(split_index));
106 : : } else {
107 : 1750 : 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 [ - + ]: 6084 : assert(!ok == damage);
112 [ + + ]: 6084 : if (!ok) break;
113 [ - + ]: 6023 : assert(decrypted_contents == plain);
114 : 6084 : }
115 : 168 : }
116 : :
117 [ + - ]: 560 : FUZZ_TARGET(crypto_fschacha20poly1305)
118 : : {
119 : 148 : FuzzedDataProvider provider{buffer.data(), buffer.size()};
120 : :
121 : 148 : uint32_t rekey_interval = provider.ConsumeIntegralInRange<size_t>(32, 512);
122 : 148 : auto key = provider.ConsumeBytes<std::byte>(32);
123 [ + - ]: 148 : key.resize(32);
124 : 148 : FSChaCha20Poly1305 enc_aead(key, rekey_interval);
125 : 148 : 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 : 148 : InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
132 : :
133 [ + + + + ]: 4012 : 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 : 3911 : unsigned mode = provider.ConsumeIntegral<uint8_t>();
141 : 3911 : bool use_splits = mode & 1;
142 : 3911 : bool damage = mode & 4;
143 : 3911 : unsigned aad_length_bits = 3 * ((mode >> 3) & 3);
144 : 3911 : unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
145 : 3911 : unsigned length_bits = 2 * ((mode >> 5) & 7);
146 : 3911 : unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
147 : : // Generate aad and content.
148 : 3911 : auto aad = rng.randbytes<std::byte>(aad_length);
149 : 3911 : auto plain = rng.randbytes<std::byte>(length);
150 [ + - ]: 3911 : std::vector<std::byte> cipher(length + FSChaCha20Poly1305::EXPANSION);
151 : :
152 : 3911 : crypt_till_rekey(enc_aead, rekey_interval, true);
153 [ + + ]: 3911 : if (use_splits && length > 0) {
154 : 2950 : size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
155 : 2950 : enc_aead.Encrypt(Span{plain}.first(split_index), Span{plain}.subspan(split_index), aad, cipher);
156 : : } else {
157 : 961 : enc_aead.Encrypt(plain, aad, cipher);
158 : : }
159 : :
160 [ + - ]: 3911 : std::vector<std::byte> decrypted_contents(length);
161 : 3911 : bool ok{false};
162 : :
163 : : // damage the key
164 : 3911 : unsigned key_position = provider.ConsumeIntegralInRange<unsigned>(0, 31);
165 : 3911 : std::byte damage_val{(uint8_t)(1U << (key_position & 7))};
166 [ + - ]: 3911 : std::vector<std::byte> bad_key = key;
167 : 3911 : bad_key[key_position] ^= damage_val;
168 : :
169 : 3911 : FSChaCha20Poly1305 bad_fs_aead(bad_key, rekey_interval);
170 : 3911 : crypt_till_rekey(bad_fs_aead, rekey_interval, false);
171 : 3911 : ok = bad_fs_aead.Decrypt(cipher, aad, decrypted_contents);
172 [ - + ]: 3911 : 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 [ + + ]: 3911 : if (damage) {
177 : 47 : unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, (cipher.size() + aad.size()) * 8U - 1U);
178 : 47 : unsigned damage_pos = damage_bit >> 3;
179 : 47 : std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
180 [ + + ]: 47 : if (damage_pos >= cipher.size()) {
181 : 3 : aad[damage_pos - cipher.size()] ^= damage_val;
182 : : } else {
183 : 44 : cipher[damage_pos] ^= damage_val;
184 : : }
185 : : }
186 : :
187 : 3911 : crypt_till_rekey(dec_aead, rekey_interval, false);
188 [ + + ]: 3911 : if (use_splits && length > 0) {
189 : 2950 : size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
190 : 2950 : ok = dec_aead.Decrypt(cipher, aad, Span{decrypted_contents}.first(split_index), Span{decrypted_contents}.subspan(split_index));
191 : : } else {
192 : 961 : 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 [ - + ]: 3911 : assert(!ok == damage);
197 [ + + ]: 3911 : if (!ok) break;
198 [ - + ]: 3864 : assert(decrypted_contents == plain);
199 : 3911 : }
200 : 148 : }
|