This PR adds fuzz tests for AEADChaCha20Poly1305
and FSChaCha20Poly1305
introduced in #28008.
Run using:
0$ FUZZ=crypto_aeadchacha20poly1305 src/test/fuzz/fuzz
1$ FUZZ=crypto_fschacha20poly1305 src/test/fuzz/fuzz
This PR adds fuzz tests for AEADChaCha20Poly1305
and FSChaCha20Poly1305
introduced in #28008.
Run using:
0$ FUZZ=crypto_aeadchacha20poly1305 src/test/fuzz/fuzz
1$ FUZZ=crypto_fschacha20poly1305 src/test/fuzz/fuzz
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
For detailed information about the code coverage, see the test coverage report.
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.
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);
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).111+ }
112+ },
113+ [&] {
114+ bool ok = aead.Decrypt(cipher, aad, contents);
115+ if (ok) {
116+ assert(plain == contents);
ok
will never be true (except with negligible probability), and if it does, plain
and contents
won’t match.
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.
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?
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);
aead_for_ks
is unused (can be just removed AFAICT, as it seems fine to reuse the existing aead
object).
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))};
🚧 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.
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:
0 auto aad{rng.randbytes<std::byte>(aad_length)};