Bitcoin Core  22.99.0
P2P Digital Currency
crypto_diff_fuzz_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>
9 
10 #include <cstdint>
11 #include <vector>
12 
13 /*
14 From https://cr.yp.to/chacha.html
15 chacha-merged.c version 20080118
16 D. J. Bernstein
17 Public domain.
18 */
19 
20 typedef unsigned int u32;
21 typedef unsigned char u8;
22 
23 #define U8C(v) (v##U)
24 #define U32C(v) (v##U)
25 
26 #define U8V(v) ((u8)(v)&U8C(0xFF))
27 #define U32V(v) ((u32)(v)&U32C(0xFFFFFFFF))
28 
29 #define ROTL32(v, n) (U32V((v) << (n)) | ((v) >> (32 - (n))))
30 
31 #define U8TO32_LITTLE(p) \
32  (((u32)((p)[0])) | ((u32)((p)[1]) << 8) | ((u32)((p)[2]) << 16) | \
33  ((u32)((p)[3]) << 24))
34 
35 #define U32TO8_LITTLE(p, v) \
36  do { \
37  (p)[0] = U8V((v)); \
38  (p)[1] = U8V((v) >> 8); \
39  (p)[2] = U8V((v) >> 16); \
40  (p)[3] = U8V((v) >> 24); \
41  } while (0)
42 
43 /* ------------------------------------------------------------------------- */
44 /* Data structures */
45 
46 typedef struct
47 {
48  u32 input[16];
49 } ECRYPT_ctx;
50 
51 /* ------------------------------------------------------------------------- */
52 /* Mandatory functions */
53 
54 void ECRYPT_keysetup(
55  ECRYPT_ctx* ctx,
56  const u8* key,
57  u32 keysize, /* Key size in bits. */
58  u32 ivsize); /* IV size in bits. */
59 
60 void ECRYPT_ivsetup(
61  ECRYPT_ctx* ctx,
62  const u8* iv);
63 
65  ECRYPT_ctx* ctx,
66  const u8* plaintext,
67  u8* ciphertext,
68  u32 msglen); /* Message length in bytes. */
69 
70 /* ------------------------------------------------------------------------- */
71 
72 /* Optional features */
73 
75  ECRYPT_ctx* ctx,
76  u8* keystream,
77  u32 length); /* Length of keystream in bytes. */
78 
79 /* ------------------------------------------------------------------------- */
80 
81 #define ROTATE(v, c) (ROTL32(v, c))
82 #define XOR(v, w) ((v) ^ (w))
83 #define PLUS(v, w) (U32V((v) + (w)))
84 #define PLUSONE(v) (PLUS((v), 1))
85 
86 #define QUARTERROUND(a, b, c, d) \
87  a = PLUS(a, b); d = ROTATE(XOR(d, a), 16); \
88  c = PLUS(c, d); b = ROTATE(XOR(b, c), 12); \
89  a = PLUS(a, b); d = ROTATE(XOR(d, a), 8); \
90  c = PLUS(c, d); b = ROTATE(XOR(b, c), 7);
91 
92 static const char sigma[] = "expand 32-byte k";
93 static const char tau[] = "expand 16-byte k";
94 
95 void ECRYPT_keysetup(ECRYPT_ctx* x, const u8* k, u32 kbits, u32 ivbits)
96 {
97  const char* constants;
98 
99  x->input[4] = U8TO32_LITTLE(k + 0);
100  x->input[5] = U8TO32_LITTLE(k + 4);
101  x->input[6] = U8TO32_LITTLE(k + 8);
102  x->input[7] = U8TO32_LITTLE(k + 12);
103  if (kbits == 256) { /* recommended */
104  k += 16;
105  constants = sigma;
106  } else { /* kbits == 128 */
107  constants = tau;
108  }
109  x->input[8] = U8TO32_LITTLE(k + 0);
110  x->input[9] = U8TO32_LITTLE(k + 4);
111  x->input[10] = U8TO32_LITTLE(k + 8);
112  x->input[11] = U8TO32_LITTLE(k + 12);
113  x->input[0] = U8TO32_LITTLE(constants + 0);
114  x->input[1] = U8TO32_LITTLE(constants + 4);
115  x->input[2] = U8TO32_LITTLE(constants + 8);
116  x->input[3] = U8TO32_LITTLE(constants + 12);
117 }
118 
119 void ECRYPT_ivsetup(ECRYPT_ctx* x, const u8* iv)
120 {
121  x->input[12] = 0;
122  x->input[13] = 0;
123  x->input[14] = U8TO32_LITTLE(iv + 0);
124  x->input[15] = U8TO32_LITTLE(iv + 4);
125 }
126 
127 void ECRYPT_encrypt_bytes(ECRYPT_ctx* x, const u8* m, u8* c, u32 bytes)
128 {
129  u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
130  u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
131  u8* ctarget = NULL;
132  u8 tmp[64];
133  uint32_t i;
134 
135  if (!bytes) return;
136 
137  j0 = x->input[0];
138  j1 = x->input[1];
139  j2 = x->input[2];
140  j3 = x->input[3];
141  j4 = x->input[4];
142  j5 = x->input[5];
143  j6 = x->input[6];
144  j7 = x->input[7];
145  j8 = x->input[8];
146  j9 = x->input[9];
147  j10 = x->input[10];
148  j11 = x->input[11];
149  j12 = x->input[12];
150  j13 = x->input[13];
151  j14 = x->input[14];
152  j15 = x->input[15];
153 
154  for (;;) {
155  if (bytes < 64) {
156  for (i = 0; i < bytes; ++i)
157  tmp[i] = m[i];
158  m = tmp;
159  ctarget = c;
160  c = tmp;
161  }
162  x0 = j0;
163  x1 = j1;
164  x2 = j2;
165  x3 = j3;
166  x4 = j4;
167  x5 = j5;
168  x6 = j6;
169  x7 = j7;
170  x8 = j8;
171  x9 = j9;
172  x10 = j10;
173  x11 = j11;
174  x12 = j12;
175  x13 = j13;
176  x14 = j14;
177  x15 = j15;
178  for (i = 20; i > 0; i -= 2) {
179  QUARTERROUND(x0, x4, x8, x12)
180  QUARTERROUND(x1, x5, x9, x13)
181  QUARTERROUND(x2, x6, x10, x14)
182  QUARTERROUND(x3, x7, x11, x15)
183  QUARTERROUND(x0, x5, x10, x15)
184  QUARTERROUND(x1, x6, x11, x12)
185  QUARTERROUND(x2, x7, x8, x13)
186  QUARTERROUND(x3, x4, x9, x14)
187  }
188  x0 = PLUS(x0, j0);
189  x1 = PLUS(x1, j1);
190  x2 = PLUS(x2, j2);
191  x3 = PLUS(x3, j3);
192  x4 = PLUS(x4, j4);
193  x5 = PLUS(x5, j5);
194  x6 = PLUS(x6, j6);
195  x7 = PLUS(x7, j7);
196  x8 = PLUS(x8, j8);
197  x9 = PLUS(x9, j9);
198  x10 = PLUS(x10, j10);
199  x11 = PLUS(x11, j11);
200  x12 = PLUS(x12, j12);
201  x13 = PLUS(x13, j13);
202  x14 = PLUS(x14, j14);
203  x15 = PLUS(x15, j15);
204 
205  x0 = XOR(x0, U8TO32_LITTLE(m + 0));
206  x1 = XOR(x1, U8TO32_LITTLE(m + 4));
207  x2 = XOR(x2, U8TO32_LITTLE(m + 8));
208  x3 = XOR(x3, U8TO32_LITTLE(m + 12));
209  x4 = XOR(x4, U8TO32_LITTLE(m + 16));
210  x5 = XOR(x5, U8TO32_LITTLE(m + 20));
211  x6 = XOR(x6, U8TO32_LITTLE(m + 24));
212  x7 = XOR(x7, U8TO32_LITTLE(m + 28));
213  x8 = XOR(x8, U8TO32_LITTLE(m + 32));
214  x9 = XOR(x9, U8TO32_LITTLE(m + 36));
215  x10 = XOR(x10, U8TO32_LITTLE(m + 40));
216  x11 = XOR(x11, U8TO32_LITTLE(m + 44));
217  x12 = XOR(x12, U8TO32_LITTLE(m + 48));
218  x13 = XOR(x13, U8TO32_LITTLE(m + 52));
219  x14 = XOR(x14, U8TO32_LITTLE(m + 56));
220  x15 = XOR(x15, U8TO32_LITTLE(m + 60));
221 
222  j12 = PLUSONE(j12);
223  if (!j12) {
224  j13 = PLUSONE(j13);
225  /* stopping at 2^70 bytes per nonce is user's responsibility */
226  }
227 
228  U32TO8_LITTLE(c + 0, x0);
229  U32TO8_LITTLE(c + 4, x1);
230  U32TO8_LITTLE(c + 8, x2);
231  U32TO8_LITTLE(c + 12, x3);
232  U32TO8_LITTLE(c + 16, x4);
233  U32TO8_LITTLE(c + 20, x5);
234  U32TO8_LITTLE(c + 24, x6);
235  U32TO8_LITTLE(c + 28, x7);
236  U32TO8_LITTLE(c + 32, x8);
237  U32TO8_LITTLE(c + 36, x9);
238  U32TO8_LITTLE(c + 40, x10);
239  U32TO8_LITTLE(c + 44, x11);
240  U32TO8_LITTLE(c + 48, x12);
241  U32TO8_LITTLE(c + 52, x13);
242  U32TO8_LITTLE(c + 56, x14);
243  U32TO8_LITTLE(c + 60, x15);
244 
245  if (bytes <= 64) {
246  if (bytes < 64) {
247  for (i = 0; i < bytes; ++i)
248  ctarget[i] = c[i];
249  }
250  x->input[12] = j12;
251  x->input[13] = j13;
252  return;
253  }
254  bytes -= 64;
255  c += 64;
256  m += 64;
257  }
258 }
259 
260 void ECRYPT_keystream_bytes(ECRYPT_ctx* x, u8* stream, u32 bytes)
261 {
262  u32 i;
263  for (i = 0; i < bytes; ++i)
264  stream[i] = 0;
265  ECRYPT_encrypt_bytes(x, stream, stream, bytes);
266 }
267 
268 FUZZ_TARGET(crypto_diff_fuzz_chacha20)
269 {
270  FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
271 
272  ChaCha20 chacha20;
273  ECRYPT_ctx ctx;
274  // D. J. Bernstein doesn't initialise ctx to 0 while Bitcoin Core initialises chacha20 to 0 in the constructor
275  for (int i = 0; i < 16; i++) {
276  ctx.input[i] = 0;
277  }
278 
279  if (fuzzed_data_provider.ConsumeBool()) {
280  const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(16, 32));
281  chacha20 = ChaCha20{key.data(), key.size()};
282  ECRYPT_keysetup(&ctx, key.data(), key.size() * 8, 0);
283  // ECRYPT_keysetup() doesn't set the counter and nonce to 0 while SetKey() does
284  uint8_t iv[8] = {0, 0, 0, 0, 0, 0, 0, 0};
285  ECRYPT_ivsetup(&ctx, iv);
286  }
287 
288  LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 3000) {
289  CallOneOf(
290  fuzzed_data_provider,
291  [&] {
292  const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(16, 32));
293  chacha20.SetKey(key.data(), key.size());
294  ECRYPT_keysetup(&ctx, key.data(), key.size() * 8, 0);
295  // ECRYPT_keysetup() doesn't set the counter and nonce to 0 while SetKey() does
296  uint8_t iv[8] = {0, 0, 0, 0, 0, 0, 0, 0};
297  ECRYPT_ivsetup(&ctx, iv);
298  },
299  [&] {
300  uint64_t iv = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
301  chacha20.SetIV(iv);
302  ctx.input[14] = iv;
303  ctx.input[15] = iv >> 32;
304  },
305  [&] {
306  uint64_t counter = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
307  chacha20.Seek(counter);
308  ctx.input[12] = counter;
309  ctx.input[13] = counter >> 32;
310  },
311  [&] {
312  uint32_t integralInRange = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
313  std::vector<uint8_t> output(integralInRange);
314  chacha20.Keystream(output.data(), output.size());
315  std::vector<uint8_t> djb_output(integralInRange);
316  ECRYPT_keystream_bytes(&ctx, djb_output.data(), djb_output.size());
317  assert(output == djb_output);
318  },
319  [&] {
320  uint32_t integralInRange = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
321  std::vector<uint8_t> output(integralInRange);
322  const std::vector<uint8_t> input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size());
323  chacha20.Crypt(input.data(), output.data(), input.size());
324  std::vector<uint8_t> djb_output(integralInRange);
325  ECRYPT_encrypt_bytes(&ctx, input.data(), djb_output.data(), input.size());
326  assert(output == djb_output);
327  });
328  }
329 }
assert
assert(!tx.IsCoinBase())
ECRYPT_encrypt_bytes
void ECRYPT_encrypt_bytes(ECRYPT_ctx *ctx, const u8 *plaintext, u8 *ciphertext, u32 msglen)
Definition: crypto_diff_fuzz_chacha20.cpp:127
ChaCha20::SetKey
void SetKey(const unsigned char *key, size_t keylen)
set key with flexible keylength; 256bit recommended *‍/
Definition: chacha20.cpp:24
U32TO8_LITTLE
#define U32TO8_LITTLE(p, v)
Definition: crypto_diff_fuzz_chacha20.cpp:35
util.h
PLUS
#define PLUS(v, w)
Definition: crypto_diff_fuzz_chacha20.cpp:83
chacha20.h
tau
static const char tau[]
Definition: crypto_diff_fuzz_chacha20.cpp:93
ChaCha20::Keystream
void Keystream(unsigned char *c, size_t bytes)
outputs the keystream of size <bytes> into
Definition: chacha20.cpp:74
XOR
#define XOR(v, w)
Definition: crypto_diff_fuzz_chacha20.cpp:82
FUZZ_TARGET
FUZZ_TARGET(crypto_diff_fuzz_chacha20)
Definition: crypto_diff_fuzz_chacha20.cpp:268
CallOneOf
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:42
ChaCha20::Crypt
void Crypt(const unsigned char *input, unsigned char *output, size_t bytes)
enciphers the message <input> of length <bytes> and write the enciphered representation into <output>...
Definition: chacha20.cpp:182
ChaCha20::Seek
void Seek(uint64_t pos)
Definition: chacha20.cpp:68
PLUSONE
#define PLUSONE(v)
Definition: crypto_diff_fuzz_chacha20.cpp:84
FuzzedDataProvider.h
u8
unsigned char u8
Definition: crypto_diff_fuzz_chacha20.cpp:21
ECRYPT_keysetup
void ECRYPT_keysetup(ECRYPT_ctx *ctx, const u8 *key, u32 keysize, u32 ivsize)
Definition: crypto_diff_fuzz_chacha20.cpp:95
LIMITED_WHILE
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:18
ECRYPT_ivsetup
void ECRYPT_ivsetup(ECRYPT_ctx *ctx, const u8 *iv)
Definition: crypto_diff_fuzz_chacha20.cpp:119
U8TO32_LITTLE
#define U8TO32_LITTLE(p)
Definition: crypto_diff_fuzz_chacha20.cpp:31
ECRYPT_ctx::input
u32 input[16]
Definition: crypto_diff_fuzz_chacha20.cpp:48
ECRYPT_keystream_bytes
void ECRYPT_keystream_bytes(ECRYPT_ctx *ctx, u8 *keystream, u32 length)
Definition: crypto_diff_fuzz_chacha20.cpp:260
ChaCha20::SetIV
void SetIV(uint64_t iv)
Definition: chacha20.cpp:62
fuzz.h
sigma
static const char sigma[]
Definition: crypto_diff_fuzz_chacha20.cpp:92
FuzzedDataProvider
Definition: FuzzedDataProvider.h:31
QUARTERROUND
#define QUARTERROUND(a, b, c, d)
Definition: crypto_diff_fuzz_chacha20.cpp:86
ConsumeFixedLengthByteVector
std::vector< uint8_t > 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:221
u32
unsigned int u32
Definition: crypto_diff_fuzz_chacha20.cpp:20
ByteUnit::m
@ m
ChaCha20
A class for ChaCha20 256-bit stream cipher developed by Daniel J.
Definition: chacha20.h:13
ByteUnit::k
@ k
ECRYPT_ctx
Definition: crypto_diff_fuzz_chacha20.cpp:46
ctx
static secp256k1_context * ctx
Definition: tests.c:32