-
Notifications
You must be signed in to change notification settings - Fork 37.7k
fuzz: Differential fuzzing to compare Bitcoin Core's and D. J. Bernstein's implementation of ChaCha20 #22704
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Concept ACK. We have to implement crypto ourselves to keep the surface area small and avoid bringing in large dependencies. Differential fuzzing against a reference implementation is a great addition to testing the reference test vectors. Thank you for your work, and welcome to Bitcoin Core! |
@agroce / @guidovranken this may also interest you. |
Essentially this is already done by Cryptofuzz which compares it against the Botan implementation, and is running on OSS-Fuzz in the bitcoin-core project |
Oh..I wasn't aware of the differential fuzzing for Bitcoin Core cryptographic libraries being done in Cryptofuzz. Crypofuzz is an incredible project! Could you please elaborate more on where the comparison with Botan implementation is happening? Would there be incremental value in fuzzing against D.J. Bernstein's reference implementation? And including the Keystream() function too in the Bitcoin Core/Cryptofuzz diferential fuzz tests? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See also #19920
btw, I don't mind adding the fuzz test here, even if it is redundant with oss-fuzz. Oss-fuzz is just one fuzzing providre, but I also run my own fuzzing servers to not put all eggs into one basket. I am sure others are running the Bitcoin Core fuzz target, too. |
+1 for what @MarcoFalke said. It'd be nice to be able to run this along with other fuzz targets on personal machines. |
Concept ACK for the reasons @MarcoFalke mentioned |
There are fuzzers (e.g. #22585) not available via OSS-Fuzz, also. |
48b07c2
to
258d751
Compare
Added |
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK 258d751. I compared this code change against DJ Bernstein's chacha20 implementation. The changes look good!
I am yet to test the changes on my local machine. Feel free to ignore the nits that I suggested :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK 258d751. I compared this code change against DJ Bernstein's chacha20 implementation. The changes look good!
I am yet to test the changes on my local machine. Feel free to ignore the nits that I suggested :)
258d751
to
c0554c7
Compare
Addressed #22704(comment). (Thanks @siv2r!) Ready for further review. |
You might want to set a different author name for your commits than "root" if you want to be credited properly in the release notes. |
Co-authored-by: Prakash Choudhary <44579179+prakash1512@users.noreply.github.com>
Co-authored-by: Prakash Choudhary <44579179+prakash1512@users.noreply.github.com>
c0554c7
to
4d0ac72
Compare
Thank you for letting me know! I've updated the author name. |
Code review ACK 4d0ac72 |
std::vector<uint8_t> djb_output(integralInRange); | ||
ECRYPT_keystream_bytes(&ctx, djb_output.data(), djb_output.size()); | ||
if (output.data() != NULL && djb_output.data() != NULL) { | ||
assert(memcmp(output.data(), djb_output.data(), integralInRange) == 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason to use a low level function, when https://en.cppreference.com/w/cpp/container/vector/operator_cmp could be used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went with this because some of the crypto unit tests used memcmp. I feel you're right though and memcmp doesn't really provide any added advantage.
chacha20.Keystream(output.data(), output.size()); | ||
std::vector<uint8_t> djb_output(integralInRange); | ||
ECRYPT_keystream_bytes(&ctx, djb_output.data(), djb_output.size()); | ||
if (output.data() != NULL && djb_output.data() != NULL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when would this fail?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant this as a check to not pass a null pointer(In a situation where integralInRange
is 0) to memcmp since it would exhibit undefined behaviour. This can be removed if memcmp is removed.
const std::vector<uint8_t> input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size()); | ||
chacha20.Crypt(input.data(), output.data(), input.size()); | ||
std::vector<uint8_t> djb_output(integralInRange); | ||
ECRYPT_encrypt_bytes(&ctx, input.data(), djb_output.data(), input.size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch. Thanks!
I can open a follow up PR to include these suggestions.
Thanks for the review @MarcoFalke. I've opened #23806 to address your comments. |
…re's and D. J. Bernstein's implementation of ChaCha20 4d0ac72 [fuzz] Add fuzzing harness to compare both implementations of ChaCha20 (stratospher) 65ef932 [fuzz] Add D. J. Bernstein's implementation of ChaCha20 (stratospher) Pull request description: This PR compares Bitcoin Core's implementation of ChaCha20 with D. J. Bernstein's in order to find implementation discrepancies if any. ACKs for top commit: laanwj: Code review ACK 4d0ac72 Tree-SHA512: f826144b4db61b9cbdd7efaaca8fa9cbb899953065bc8a26820a566303b2ab6a17431e7c114635789f0a63fbe3b65cb0bf2ab85baf882803a5ee172af4881544
8f79831 Refactor the chacha20 differential fuzz test (stratospher) Pull request description: This PR addresses [comments from #22704](https://github.com/bitcoin/bitcoin/pull/22704/files#discussion_r771510963) to make the following changes in `src/test/fuzz/crypto_diff_fuzz_chacha20.cpp`: - replace `memcmp()` with == - add a missing assert statement to compare the encrypted bytes Top commit has no ACKs. Tree-SHA512: 02338460fb3a89e732558bf00f3aebf8f04daba194e03ae0e3339bb2ff6ba35d06841452585b739047a29f8ec64f36b1b4ce2dfa39a08f6ad44a6a937e7b3acb
8f79831 Refactor the chacha20 differential fuzz test (stratospher) Pull request description: This PR addresses [comments from bitcoin#22704](https://github.com/bitcoin/bitcoin/pull/22704/files#discussion_r771510963) to make the following changes in `src/test/fuzz/crypto_diff_fuzz_chacha20.cpp`: - replace `memcmp()` with == - add a missing assert statement to compare the encrypted bytes Top commit has no ACKs. Tree-SHA512: 02338460fb3a89e732558bf00f3aebf8f04daba194e03ae0e3339bb2ff6ba35d06841452585b739047a29f8ec64f36b1b4ce2dfa39a08f6ad44a6a937e7b3acb
|
[&] { | ||
uint64_t iv = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); | ||
chacha20.SetIV(iv); | ||
ctx.input[14] = iv; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test/fuzz/crypto_diff_fuzz_chacha20.cpp:302:33: runtime error: implicit conversion from type 'uint64_t' (aka 'unsigned long') of value 270751369970 (64-bit, unsigned) to type 'u32' (aka 'unsigned int') changed the value to 168430322 (32-bit, unsigned)
#0 0x55b2974ba838 in crypto_diff_fuzz_chacha20_fuzz_target(Span<unsigned char const>)::$_1::operator()() const src/test/fuzz/crypto_diff_fuzz_chacha20.cpp:302:33
#1 0x55b2974ba838 in unsigned long CallOneOf<crypto_diff_fuzz_chacha20_fuzz_target(Span<unsigned char const>)::$_0, crypto_diff_fuzz_chacha20_fuzz_target(Span<unsigned char const>)::$_1, crypto_diff_fuzz_chacha20_fuzz_target(Span<unsigned char const>)::$_2, crypto_diff_fuzz_chacha20_fuzz_target(Span<unsigned char const>)::$_3, crypto_diff_fuzz_chacha20_fuzz_target(Span<unsigned char const>)::$_4>(FuzzedDataProvider&, crypto_diff_fuzz_chacha20_fuzz_target(Span<unsigned char const>)::$_0, crypto_diff_fuzz_chacha20_fuzz_target(Span<unsigned char const>)::$_1, crypto_diff_fuzz_chacha20_fuzz_target(Span<unsigned char const>)::$_2, crypto_diff_fuzz_chacha20_fuzz_target(Span<unsigned char const>)::$_3, crypto_diff_fuzz_chacha20_fuzz_target(Span<unsigned char const>)::$_4) src/./test/fuzz/util.h:49:27
#2 0x55b2974b93d5 in crypto_diff_fuzz_chacha20_fuzz_target(Span<unsigned char const>) src/test/fuzz/crypto_diff_fuzz_chacha20.cpp:289:9
#3 0x55b2973e7628 in std::_Function_handler<void (Span<unsigned char const>), void (*)(Span<unsigned char const>)>::_M_invoke(std::_Any_data const&, Span<unsigned char const>&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/std_function.h:300:2
#4 0x55b29837997d in std::function<void (Span<unsigned char const>)>::operator()(Span<unsigned char const>) const /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/std_function.h:688:14
#5 0x55b298379628 in LLVMFuzzerTestOneInput src/test/fuzz/fuzz.cpp:91:5
#6 0x55b2972f6fb3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/root/fuzz_dir/scratch/fuzz_gen/code/src/test/fuzz/fuzz+0x1461fb3)
#7 0x55b2972f6709 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/root/fuzz_dir/scratch/fuzz_gen/code/src/test/fuzz/fuzz+0x1461709)
#8 0x55b2972f7ef9 in fuzzer::Fuzzer::MutateAndTestOne() (/root/fuzz_dir/scratch/fuzz_gen/code/src/test/fuzz/fuzz+0x1462ef9)
#9 0x55b2972f8a75 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile> >&) (/root/fuzz_dir/scratch/fuzz_gen/code/src/test/fuzz/fuzz+0x1463a75)
#10 0x55b2972e6288 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/root/fuzz_dir/scratch/fuzz_gen/code/src/test/fuzz/fuzz+0x1451288)
#11 0x55b297310d72 in main (/root/fuzz_dir/scratch/fuzz_gen/code/src/test/fuzz/fuzz+0x147bd72)
#12 0x7fcb06fc90b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16
#13 0x55b2972dab0d in _start (/root/fuzz_dir/scratch/fuzz_gen/code/src/test/fuzz/fuzz+0x1445b0d)
SUMMARY: UndefinedBehaviorSanitizer: implicit-unsigned-integer-truncation test/fuzz/crypto_diff_fuzz_chacha20.cpp:302:33 in
MS: 4 ShuffleBytes-CrossOver-ChangeBinInt-CopyPart-; base unit: 47966b9379790c601d5e098f1ba5ccae76f25861
0xf2,0xa,0xa,0xa,0x3f,0x38,0x1b,0xf4,
\362\012\012\012?8\033\364
artifact_prefix='./'; Test unit written to ./crash-ce471b8e19b31491ff5f4a3669aefa84b1848f06
Base64: 8goKCj84G/Q=
This PR compares Bitcoin Core's implementation of ChaCha20 with D. J. Bernstein's in order to find implementation discrepancies if any.