This PR adds fuzz tests for AEADChaCha20Poly1305 and FSChaCha20Poly1305 introduced in #28008.
Run using:
$ FUZZ=crypto_aeadchacha20poly1305 src/test/fuzz/fuzz
$ FUZZ=crypto_fschacha20poly1305 src/test/fuzz/fuzz
This PR adds fuzz tests for AEADChaCha20Poly1305 and FSChaCha20Poly1305 introduced in #28008.
Run using:
$ FUZZ=crypto_aeadchacha20poly1305 src/test/fuzz/fuzz
$ FUZZ=crypto_fschacha20poly1305 src/test/fuzz/fuzz
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--006a51241073e994b41acfe9ec718e94-->
For detailed information about the code coverage, see the test coverage report.
<!--021abf342d371248e50ceaed478a90ca-->
See the guideline for information on the review process.
| Type | Reviewers |
|---|---|
| ACK | dergoegge, marcofleon |
| Concept ACK | theStack |
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.
<!--174a7506f384e20aa4161008e828411d-->
No conflicts as of last run.
16 | +{ 17 | + FuzzedDataProvider provider{buffer.data(), buffer.size()}; 18 | + 19 | + auto key = provider.ConsumeBytes<std::byte>(32); 20 | + key.resize(32); 21 | + auto aad = provider.ConsumeBytes<std::byte>(512);
This will always construct an AAD of 512 bytes, if that much data is available in the fuzzer input. I think it's better to make the AAD size dynamic, and a function of the fuzzer input data too.
true! it's done like in bip324_cipher_roundtrip fuzz test now.
55 | + cipher); 56 | + } 57 | + }, 58 | + [&] { 59 | + bool ok = aead.Decrypt(cipher, aad, nonce, contents); // with splits too 60 | + if (ok) {
This will never trigger, as the AEAD advances between between the preceding Encrypt call and the Decrypt call here, so encryption and decryption will never use the same key. With distinct keys, it should be cryptographically impossible for anyone (let alone a fuzzer) to get a match (and if, with extremely low probability, ok=true is returned, it'll be accidental, and plaintext and decrypted result won't match).
There are two possible styles of fuzz tests we could write here, and this feels like a mix between the two:
crypto_chacha20 and crypto_fschacha20). If that's the goal here, you can probably just ignore the return value of Decrypt.AEADChaCha20Poly1305 objects (one for encrypting, one for decrypting), initialized with matching (or deliberately mismatching) keys, and then running through some scenario that's applied to both to keep them in sync. The bip324_cipher_roundtrip fuzz test is of this style (but at a higher level).thanks for the interesting explanation! updated it to a "test encryption and decryption match" kind of fuzz test.
111 | + } 112 | + }, 113 | + [&] { 114 | + bool ok = aead.Decrypt(cipher, aad, contents); 115 | + if (ok) { 116 | + assert(plain == contents);
The same comment as I've given on the other test about mixing "exercise API" and "test correctness" fuzzing applies here: ok will never be true (except with negligible probability), and if it does, plain and contents won't match.
makes sense. updated it to a "test encryption and decryption match" kind of fuzz test.
53 | @@ -54,6 +54,7 @@ class BIP324Cipher 54 | 55 | /** Initialize when the other side's public key is received. Can only be called once. 56 | * 57 | + * initiator is set to true if the BIP324 cipher involves initiating the v2 P2P connection
Nit: . at the end of the sentence so it's clear the lines are about different arguments.
Also perhaps say "if we are the initiator" or so; every v2 P2P connection involves initiating somehow; the question is whether it's us or the remote side doing that.
Mostly comments on the fuzz tests. The last commit looks good to me (and maybe we want to split it off).
@stratospher Want to also address #28008 (review) and #28008 (review) here?
so split off the last commit into #28267 since they aren't related to the fuzz test. (included #28008 (review) and #28008 (review) there)
thinking about #28263 (review). will address it soon.
Are you still working on this?
Are you still working on this?
yes! updated the PR to address #28263 (review).
57 | + aead.Encrypt(plain, aad, nonce, cipher); 58 | + } 59 | + 60 | + // Test Keystream output 61 | + std::vector<std::byte> keystream(length); 62 | + AEADChaCha20Poly1305 aead_for_ks(key);
nit: aead_for_ks is unused (can be just removed AFAICT, as it seems fine to reuse the existing aead object).
thanks! done.
81 | + // Optionally damage 1 bit in either the cipher (corresponding to a change in transit) 82 | + // or the aad (to make sure that decryption will fail if the AAD mismatches). 83 | + if (damage) { 84 | + unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, (cipher.size() + aad.size()) * 8U - 1U); 85 | + unsigned damage_pos = damage_bit >> 3; 86 | + std::byte damage_val{(uint8_t)(1U << (damage_bit & 3))};
true! done.
Concept ACK
@theStack want to re-review this? :)
Could rebase for fresh CI?
<!--85328a0da195eb286784d51f73fa0af9-->
🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.
Possibly this is due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.
Leave a comment here, if you need help tracking down a confusing failure.
<sub>Debug: https://github.com/bitcoin/bitcoin/runs/23942811284</sub>
53 | + unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1); 54 | + unsigned length_bits = 2 * ((mode >> 5) & 7); 55 | + unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1); 56 | + // Generate aad and content. 57 | + std::vector<std::byte> aad(aad_length); 58 | + for (auto& val : aad) val = std::byte{(uint8_t)rng()};
nit:
auto aad{rng.randbytes<std::byte>(aad_length)};
done.
tACK 39805638663bdd567a50ecac962631b45ce8d73a
tACK 8607773750e60931e51a33e48cd077a1dedf9db3
Tested ACK 8607773750e60931e51a33e48cd077a1dedf9db3. Ran both targets for ~200 CPU hours. Coverage of intended targets looks good to me. The simulation of damaged keys and checks that follow seem useful as well.
Ported to the CMake-based build system in https://github.com/hebasto/bitcoin/pull/280.