aboutsummaryrefslogtreecommitdiff
path: root/src/test/fuzz
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/fuzz')
-rw-r--r--src/test/fuzz/bip324.cpp137
-rw-r--r--src/test/fuzz/crypto_chacha20.cpp20
-rw-r--r--src/test/fuzz/crypto_chacha20_poly1305_aead.cpp72
3 files changed, 157 insertions, 72 deletions
diff --git a/src/test/fuzz/bip324.cpp b/src/test/fuzz/bip324.cpp
new file mode 100644
index 0000000000..359de6c66a
--- /dev/null
+++ b/src/test/fuzz/bip324.cpp
@@ -0,0 +1,137 @@
+// Copyright (c) 2023 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bip324.h>
+#include <chainparams.h>
+#include <span.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/xoroshiro128plusplus.h>
+
+#include <cstdint>
+#include <tuple>
+#include <vector>
+
+namespace {
+
+void Initialize()
+{
+ ECC_Start();
+ SelectParams(ChainType::MAIN);
+}
+
+} // namespace
+
+FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize)
+{
+ // Test that BIP324Cipher's encryption and decryption agree.
+
+ // Load keys from fuzzer.
+ FuzzedDataProvider provider(buffer.data(), buffer.size());
+ // Initiator key
+ auto init_key_data = provider.ConsumeBytes<unsigned char>(32);
+ init_key_data.resize(32);
+ CKey init_key;
+ init_key.Set(init_key_data.begin(), init_key_data.end(), true);
+ if (!init_key.IsValid()) return;
+ // Initiator entropy
+ auto init_ent = provider.ConsumeBytes<std::byte>(32);
+ init_ent.resize(32);
+ // Responder key
+ auto resp_key_data = provider.ConsumeBytes<unsigned char>(32);
+ resp_key_data.resize(32);
+ CKey resp_key;
+ resp_key.Set(resp_key_data.begin(), resp_key_data.end(), true);
+ if (!resp_key.IsValid()) return;
+ // Responder entropy
+ auto resp_ent = provider.ConsumeBytes<std::byte>(32);
+ resp_ent.resize(32);
+
+ // Initialize ciphers by exchanging public keys.
+ BIP324Cipher initiator(init_key, init_ent);
+ assert(!initiator);
+ BIP324Cipher responder(resp_key, resp_ent);
+ assert(!responder);
+ initiator.Initialize(responder.GetOurPubKey(), true);
+ assert(initiator);
+ responder.Initialize(initiator.GetOurPubKey(), false);
+ assert(responder);
+
+ // Initialize RNG deterministically, to generate contents and AAD. We assume that there are no
+ // (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid
+ // reading the actual data for those from the fuzzer input (which would need large amounts of
+ // data).
+ XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
+
+ // Compare session IDs and garbage terminators.
+ assert(initiator.GetSessionID() == responder.GetSessionID());
+ assert(initiator.GetSendGarbageTerminator() == responder.GetReceiveGarbageTerminator());
+ assert(initiator.GetReceiveGarbageTerminator() == responder.GetSendGarbageTerminator());
+
+ LIMITED_WHILE(provider.remaining_bytes(), 1000) {
+ // Mode:
+ // - Bit 0: whether the ignore bit is set in message
+ // - Bit 1: whether the responder (0) or initiator (1) sends
+ // - Bit 2: whether this ciphertext will be corrupted (making it the last sent one)
+ // - Bit 3-4: controls the maximum aad length (max 511 bytes)
+ // - Bit 5-7: controls the maximum content length (max 16383 bytes, for performance reasons)
+ unsigned mode = provider.ConsumeIntegral<uint8_t>();
+ bool ignore = mode & 1;
+ bool from_init = mode & 2;
+ bool damage = mode & 4;
+ unsigned aad_length_bits = 3 * ((mode >> 3) & 3);
+ unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
+ unsigned length_bits = 2 * ((mode >> 5) & 7);
+ unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
+ // Generate aad and content.
+ std::vector<std::byte> aad(aad_length);
+ for (auto& val : aad) val = std::byte{(uint8_t)rng()};
+ std::vector<std::byte> contents(length);
+ for (auto& val : contents) val = std::byte{(uint8_t)rng()};
+
+ // Pick sides.
+ auto& sender{from_init ? initiator : responder};
+ auto& receiver{from_init ? responder : initiator};
+
+ // Encrypt
+ std::vector<std::byte> ciphertext(length + initiator.EXPANSION);
+ sender.Encrypt(contents, aad, ignore, ciphertext);
+
+ // Optionally damage 1 bit in either the ciphertext (corresponding to a change in transit)
+ // or the aad (to make sure that decryption will fail if the AAD mismatches).
+ if (damage) {
+ unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0,
+ (ciphertext.size() + aad.size()) * 8U - 1U);
+ unsigned damage_pos = damage_bit >> 3;
+ std::byte damage_val{(uint8_t)(1U << (damage_bit & 3))};
+ if (damage_pos >= ciphertext.size()) {
+ aad[damage_pos - ciphertext.size()] ^= damage_val;
+ } else {
+ ciphertext[damage_pos] ^= damage_val;
+ }
+ }
+
+ // Decrypt length
+ uint32_t dec_length = receiver.DecryptLength(Span{ciphertext}.first(initiator.LENGTH_LEN));
+ if (!damage) {
+ assert(dec_length == length);
+ } else {
+ // For performance reasons, don't try to decode if length got increased too much.
+ if (dec_length > 16384 + length) break;
+ // Otherwise, just append zeros if dec_length > length.
+ ciphertext.resize(dec_length + initiator.EXPANSION);
+ }
+
+ // Decrypt
+ std::vector<std::byte> decrypt(dec_length);
+ bool dec_ignore{false};
+ bool ok = receiver.Decrypt(Span{ciphertext}.subspan(initiator.LENGTH_LEN), aad, dec_ignore, decrypt);
+ // Decryption *must* fail if the packet was damaged, and succeed if it wasn't.
+ assert(!ok == damage);
+ if (!ok) break;
+ assert(ignore == dec_ignore);
+ assert(decrypt == contents);
+ }
+}
diff --git a/src/test/fuzz/crypto_chacha20.cpp b/src/test/fuzz/crypto_chacha20.cpp
index 63c7bf3b45..76370b4e57 100644
--- a/src/test/fuzz/crypto_chacha20.cpp
+++ b/src/test/fuzz/crypto_chacha20.cpp
@@ -8,6 +8,8 @@
#include <test/fuzz/util.h>
#include <test/util/xoroshiro128plusplus.h>
+#include <array>
+#include <cstddef>
#include <cstdint>
#include <vector>
@@ -151,3 +153,21 @@ FUZZ_TARGET(chacha20_split_keystream)
FuzzedDataProvider provider{buffer.data(), buffer.size()};
ChaCha20SplitFuzz<false>(provider);
}
+
+FUZZ_TARGET(crypto_fschacha20)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+
+ auto key = fuzzed_data_provider.ConsumeBytes<std::byte>(FSChaCha20::KEYLEN);
+ key.resize(FSChaCha20::KEYLEN);
+
+ auto fsc20 = FSChaCha20{key, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 1024)};
+
+ LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000)
+ {
+ auto input = fuzzed_data_provider.ConsumeBytes<std::byte>(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096));
+ std::vector<std::byte> output;
+ output.resize(input.size());
+ fsc20.Crypt(input, output);
+ }
+}
diff --git a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
deleted file mode 100644
index 84ac65dd88..0000000000
--- a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2020-2021 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include <crypto/chacha_poly_aead.h>
-#include <crypto/poly1305.h>
-#include <test/fuzz/FuzzedDataProvider.h>
-#include <test/fuzz/fuzz.h>
-#include <test/fuzz/util.h>
-#include <util/overflow.h>
-
-#include <cassert>
-#include <cstdint>
-#include <limits>
-#include <vector>
-
-FUZZ_TARGET(crypto_chacha20_poly1305_aead)
-{
- FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
-
- const std::vector<uint8_t> k1 = ConsumeFixedLengthByteVector(fuzzed_data_provider, CHACHA20_POLY1305_AEAD_KEY_LEN);
- const std::vector<uint8_t> k2 = ConsumeFixedLengthByteVector(fuzzed_data_provider, CHACHA20_POLY1305_AEAD_KEY_LEN);
-
- ChaCha20Poly1305AEAD aead(k1.data(), k1.size(), k2.data(), k2.size());
- uint64_t seqnr_payload = 0;
- uint64_t seqnr_aad = 0;
- int aad_pos = 0;
- size_t buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
- std::vector<uint8_t> in(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0);
- std::vector<uint8_t> out(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0);
- bool is_encrypt = fuzzed_data_provider.ConsumeBool();
- LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
- CallOneOf(
- fuzzed_data_provider,
- [&] {
- buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(64, 4096);
- in = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0);
- out = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0);
- },
- [&] {
- (void)aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffer_size, is_encrypt);
- },
- [&] {
- uint32_t len = 0;
- const bool ok = aead.GetLength(&len, seqnr_aad, aad_pos, in.data());
- assert(ok);
- },
- [&] {
- if (AdditionOverflow(seqnr_payload, static_cast<uint64_t>(1))) {
- return;
- }
- seqnr_payload += 1;
- aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN;
- if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) {
- aad_pos = 0;
- if (AdditionOverflow(seqnr_aad, static_cast<uint64_t>(1))) {
- return;
- }
- seqnr_aad += 1;
- }
- },
- [&] {
- seqnr_payload = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
- },
- [&] {
- seqnr_aad = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
- },
- [&] {
- is_encrypt = fuzzed_data_provider.ConsumeBool();
- });
- }
-}