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>
7 #include <test/fuzz/fuzz.h>
8 #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 Xoroshiro128++ based
82  // stream.
83  if constexpr (UseCrypt) {
84  uint64_t seed = provider.ConsumeIntegral<uint64_t>();
85  XoRoShiRo128PlusPlus rng(seed);
86  uint64_t bytes = 0;
87  while (bytes < (total_bytes & ~uint64_t{7})) {
88  uint64_t val = rng();
89  WriteLE64(UCharCast(data1.data() + bytes), val);
90  WriteLE64(UCharCast(data2.data() + bytes), val);
91  bytes += 8;
92  }
93  if (bytes < total_bytes) {
94  std::byte valbytes[8];
95  uint64_t val = rng();
96  WriteLE64(UCharCast(valbytes), val);
97  std::copy(valbytes, valbytes + (total_bytes - bytes), data1.data() + bytes);
98  std::copy(valbytes, valbytes + (total_bytes - bytes), data2.data() + bytes);
99  }
100  }
101 
102  // Whether UseCrypt is used or not, the two byte arrays must match.
103  assert(data1 == data2);
104 
105  // Encrypt data1, the whole array at once.
106  if constexpr (UseCrypt) {
107  crypt1.Crypt(data1, data1);
108  } else {
109  crypt1.Keystream(data1);
110  }
111 
112  // Encrypt data2, in at most 256 chunks.
113  uint64_t bytes2 = 0;
114  int iter = 0;
115  while (true) {
116  bool is_last = (iter == 255) || (bytes2 == total_bytes) || provider.ConsumeBool();
117  ++iter;
118  // Determine how many bytes to encrypt in this chunk: a fuzzer-determined
119  // amount for all but the last chunk (which processes all remaining bytes).
120  uint64_t now = is_last ? total_bytes - bytes2 :
121  provider.ConsumeIntegralInRange<uint64_t>(0, total_bytes - bytes2);
122  // For each chunk, consider using Crypt() even when UseCrypt is false.
123  // This tests that Keystream() has the same behavior as Crypt() applied
124  // to 0x00 input bytes.
125  if (UseCrypt || provider.ConsumeBool()) {
126  crypt2.Crypt(Span{data2}.subspan(bytes2, now), Span{data2}.subspan(bytes2, now));
127  } else {
128  crypt2.Keystream(Span{data2}.subspan(bytes2, now));
129  }
130  bytes2 += now;
131  if (is_last) break;
132  }
133  // We should have processed everything now.
134  assert(bytes2 == total_bytes);
135  // And the result should match.
136  assert(data1 == data2);
137 }
138 
139 } // namespace
140 
141 FUZZ_TARGET(chacha20_split_crypt)
142 {
143  FuzzedDataProvider provider{buffer.data(), buffer.size()};
144  ChaCha20SplitFuzz<true>(provider);
145 }
146 
147 FUZZ_TARGET(chacha20_split_keystream)
148 {
149  FuzzedDataProvider provider{buffer.data(), buffer.size()};
150  ChaCha20SplitFuzz<false>(provider);
151 }
152 
153 FUZZ_TARGET(crypto_fschacha20)
154 {
155  FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
156 
157  auto key = fuzzed_data_provider.ConsumeBytes<std::byte>(FSChaCha20::KEYLEN);
158  key.resize(FSChaCha20::KEYLEN);
159 
160  auto fsc20 = FSChaCha20{key, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 1024)};
161 
162  LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000)
163  {
164  auto input = fuzzed_data_provider.ConsumeBytes<std::byte>(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096));
165  std::vector<std::byte> output;
166  output.resize(input.size());
167  fsc20.Crypt(input, output);
168  }
169 }
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)
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
xoroshiro128++ PRNG.
static void WriteLE64(unsigned char *ptr, uint64_t x)
Definition: common.h:46
FUZZ_TARGET(crypto_chacha20)
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:23
unsigned char * UCharCast(char *c)
Definition: span.h:288
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())