Bitcoin Core 28.99.0
P2P Digital Currency
crypto_chacha20.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
5#include <crypto/chacha20.h>
6#include <random.h>
8#include <test/fuzz/fuzz.h>
9#include <test/fuzz/util.h>
10
11#include <array>
12#include <cstddef>
13#include <cstdint>
14#include <vector>
15
16FUZZ_TARGET(crypto_chacha20)
17{
18 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
19
20 const auto key = ConsumeFixedLengthByteVector<std::byte>(fuzzed_data_provider, ChaCha20::KEYLEN);
21 ChaCha20 chacha20{key};
22
23 LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
25 fuzzed_data_provider,
26 [&] {
27 auto key = ConsumeFixedLengthByteVector<std::byte>(fuzzed_data_provider, ChaCha20::KEYLEN);
28 chacha20.SetKey(key);
29 },
30 [&] {
32 fuzzed_data_provider.ConsumeIntegral<uint32_t>(),
33 fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
34 chacha20.Seek(nonce, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
35 },
36 [&] {
37 std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
38 chacha20.Keystream(MakeWritableByteSpan(output));
39 },
40 [&] {
41 std::vector<std::byte> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
42 const auto input = ConsumeFixedLengthByteVector<std::byte>(fuzzed_data_provider, output.size());
43 chacha20.Crypt(input, output);
44 });
45 }
46}
47
48namespace
49{
50
58template<bool UseCrypt>
59void ChaCha20SplitFuzz(FuzzedDataProvider& provider)
60{
61 // Determine key, iv, start position, length.
62 auto key_bytes = ConsumeFixedLengthByteVector<std::byte>(provider, ChaCha20::KEYLEN);
63 uint64_t iv = provider.ConsumeIntegral<uint64_t>();
64 uint32_t iv_prefix = provider.ConsumeIntegral<uint32_t>();
65 uint64_t total_bytes = provider.ConsumeIntegralInRange<uint64_t>(0, 1000000);
66 /* ~x = 2^BITS - 1 - x, so ~(total_bytes >> 6) is the maximal seek position. */
67 uint32_t seek = provider.ConsumeIntegralInRange<uint32_t>(0, ~(uint32_t)(total_bytes >> 6));
68
69 // Initialize two ChaCha20 ciphers, with the same key/iv/position.
70 ChaCha20 crypt1(key_bytes);
71 ChaCha20 crypt2(key_bytes);
72 crypt1.Seek({iv_prefix, iv}, seek);
73 crypt2.Seek({iv_prefix, iv}, seek);
74
75 // Construct vectors with data.
76 std::vector<std::byte> data1, data2;
77 data1.resize(total_bytes);
78 data2.resize(total_bytes);
79
80 // If using Crypt(), initialize data1 and data2 with the same InsecureRandomContext based
81 // stream.
82 if constexpr (UseCrypt) {
83 InsecureRandomContext(provider.ConsumeIntegral<uint64_t>()).fillrand(data1);
84 std::copy(data1.begin(), data1.end(), data2.begin());
85 }
86
87 // Whether UseCrypt is used or not, the two byte arrays must match.
88 assert(data1 == data2);
89
90 // Encrypt data1, the whole array at once.
91 if constexpr (UseCrypt) {
92 crypt1.Crypt(data1, data1);
93 } else {
94 crypt1.Keystream(data1);
95 }
96
97 // Encrypt data2, in at most 256 chunks.
98 uint64_t bytes2 = 0;
99 int iter = 0;
100 while (true) {
101 bool is_last = (iter == 255) || (bytes2 == total_bytes) || provider.ConsumeBool();
102 ++iter;
103 // Determine how many bytes to encrypt in this chunk: a fuzzer-determined
104 // amount for all but the last chunk (which processes all remaining bytes).
105 uint64_t now = is_last ? total_bytes - bytes2 :
106 provider.ConsumeIntegralInRange<uint64_t>(0, total_bytes - bytes2);
107 // For each chunk, consider using Crypt() even when UseCrypt is false.
108 // This tests that Keystream() has the same behavior as Crypt() applied
109 // to 0x00 input bytes.
110 if (UseCrypt || provider.ConsumeBool()) {
111 crypt2.Crypt(Span{data2}.subspan(bytes2, now), Span{data2}.subspan(bytes2, now));
112 } else {
113 crypt2.Keystream(Span{data2}.subspan(bytes2, now));
114 }
115 bytes2 += now;
116 if (is_last) break;
117 }
118 // We should have processed everything now.
119 assert(bytes2 == total_bytes);
120 // And the result should match.
121 assert(data1 == data2);
122}
123
124} // namespace
125
126FUZZ_TARGET(chacha20_split_crypt)
127{
128 FuzzedDataProvider provider{buffer.data(), buffer.size()};
129 ChaCha20SplitFuzz<true>(provider);
130}
131
132FUZZ_TARGET(chacha20_split_keystream)
133{
134 FuzzedDataProvider provider{buffer.data(), buffer.size()};
135 ChaCha20SplitFuzz<false>(provider);
136}
137
138FUZZ_TARGET(crypto_fschacha20)
139{
140 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
141
142 auto key = fuzzed_data_provider.ConsumeBytes<std::byte>(FSChaCha20::KEYLEN);
143 key.resize(FSChaCha20::KEYLEN);
144
145 auto fsc20 = FSChaCha20{key, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 1024)};
146
147 LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000)
148 {
149 auto input = fuzzed_data_provider.ConsumeBytes<std::byte>(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096));
150 std::vector<std::byte> output;
151 output.resize(input.size());
152 fsc20.Crypt(input, output);
153 }
154}
Unrestricted ChaCha20 cipher.
Definition: chacha20.h:78
static constexpr unsigned KEYLEN
Expected key length in constructor and SetKey.
Definition: chacha20.h:86
ChaCha20Aligned::Nonce96 Nonce96
96-bit nonce type.
Definition: chacha20.h:101
Forward-secure ChaCha20.
Definition: chacha20.h:128
static constexpr unsigned KEYLEN
Length of keys expected by the constructor.
Definition: chacha20.h:144
std::vector< T > ConsumeBytes(size_t num_bytes)
T ConsumeIntegralInRange(T min, T max)
xoroshiro128++ PRNG.
Definition: random.h:416
void fillrand(Span< std::byte > span) noexcept
Fill a Span with random bytes.
Definition: random.h:267
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
FUZZ_TARGET(crypto_chacha20)
#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
Span< std::byte > MakeWritableByteSpan(V &&v) noexcept
Definition: span.h:275
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:35
assert(!tx.IsCoinBase())