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