Skip to content

Commit 4d0ac72

Browse files
[fuzz] Add fuzzing harness to compare both implementations of ChaCha20
Co-authored-by: Prakash Choudhary <[email protected]>
1 parent 65ef932 commit 4d0ac72

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ test_fuzz_fuzz_SOURCES = \
238238
test/fuzz/crypto_chacha20.cpp \
239239
test/fuzz/crypto_chacha20_poly1305_aead.cpp \
240240
test/fuzz/crypto_common.cpp \
241+
test/fuzz/crypto_diff_fuzz_chacha20.cpp \
241242
test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp \
242243
test/fuzz/crypto_poly1305.cpp \
243244
test/fuzz/cuckoocache.cpp \

src/test/fuzz/crypto_diff_fuzz_chacha20.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
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 <test/fuzz/FuzzedDataProvider.h>
7+
#include <test/fuzz/fuzz.h>
8+
#include <test/fuzz/util.h>
9+
10+
#include <cstdint>
11+
#include <vector>
12+
113
/*
214
From https://cr.yp.to/chacha.html
315
chacha-merged.c version 20080118
@@ -253,3 +265,66 @@ void ECRYPT_keystream_bytes(ECRYPT_ctx* x, u8* stream, u32 bytes)
253265
ECRYPT_encrypt_bytes(x, stream, stream, bytes);
254266
}
255267

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+
if (output.data() != NULL && djb_output.data() != NULL) {
318+
assert(memcmp(output.data(), djb_output.data(), integralInRange) == 0);
319+
}
320+
},
321+
[&] {
322+
uint32_t integralInRange = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
323+
std::vector<uint8_t> output(integralInRange);
324+
const std::vector<uint8_t> input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size());
325+
chacha20.Crypt(input.data(), output.data(), input.size());
326+
std::vector<uint8_t> djb_output(integralInRange);
327+
ECRYPT_encrypt_bytes(&ctx, input.data(), djb_output.data(), input.size());
328+
});
329+
}
330+
}

0 commit comments

Comments
 (0)