Bitcoin Core  27.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 
16 FUZZ_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) {
24  CallOneOf(
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 
49 namespace
50 {
51 
59 template<bool UseCrypt>
60 void ChaCha20SplitFuzz(FuzzedDataProvider& provider)
61 {
62  // Determine key, iv, start position, length.
63  auto key_bytes = ConsumeFixedLengthByteVector<std::byte>(provider, ChaCha20::KEYLEN);
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 
127 FUZZ_TARGET(chacha20_split_crypt)
128 {
129  FuzzedDataProvider provider{buffer.data(), buffer.size()};
130  ChaCha20SplitFuzz<true>(provider);
131 }
132 
133 FUZZ_TARGET(chacha20_split_keystream)
134 {
135  FuzzedDataProvider provider{buffer.data(), buffer.size()};
136  ChaCha20SplitFuzz<false>(provider);
137 }
138 
139 FUZZ_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
FUZZ_TARGET(crypto_chacha20)
#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
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:35
assert(!tx.IsCoinBase())