|
| 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 | + |
1 | 13 | /*
|
2 | 14 | From https://cr.yp.to/chacha.html
|
3 | 15 | chacha-merged.c version 20080118
|
@@ -253,3 +265,66 @@ void ECRYPT_keystream_bytes(ECRYPT_ctx* x, u8* stream, u32 bytes)
|
253 | 265 | ECRYPT_encrypt_bytes(x, stream, stream, bytes);
|
254 | 266 | }
|
255 | 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 | + 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