Bitcoin Core  29.1.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  [&] {
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 
48 namespace
49 {
50 
58 template<bool UseCrypt>
59 void 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 
126 FUZZ_TARGET(chacha20_split_crypt)
127 {
128  FuzzedDataProvider provider{buffer.data(), buffer.size()};
129  ChaCha20SplitFuzz<true>(provider);
130 }
131 
132 FUZZ_TARGET(chacha20_split_keystream)
133 {
134  FuzzedDataProvider provider{buffer.data(), buffer.size()};
135  ChaCha20SplitFuzz<false>(provider);
136 }
137 
138 FUZZ_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 }
CONSTEXPR_IF_NOT_DEBUG Span< C > subspan(std::size_t offset) const noexcept
Definition: span.h:195
assert(!tx.IsCoinBase())
void fillrand(Span< std::byte > span) noexcept
Fill a Span with random bytes.
Definition: random.h:267
unsigned int nonce
Definition: miner_tests.cpp:75
Span< std::byte > MakeWritableByteSpan(V &&v) noexcept
Definition: span.h:274
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:22
ChaCha20Aligned::Nonce96 Nonce96
96-bit nonce type.
Definition: chacha20.h:101
Unrestricted ChaCha20 cipher.
Definition: chacha20.h:77
xoroshiro128++ PRNG.
Definition: random.h:415
Forward-secure ChaCha20.
Definition: chacha20.h:127
std::vector< T > ConsumeBytes(size_t num_bytes)
static constexpr unsigned KEYLEN
Length of keys expected by the constructor.
Definition: chacha20.h:144
static constexpr unsigned KEYLEN
Expected key length in constructor and SetKey.
Definition: chacha20.h:86
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:35
A Span is an object that can refer to a contiguous sequence of objects.
Definition: span.h:97
FUZZ_TARGET(crypto_chacha20)