From 9fd085a1a49d317abcaf1492b71c48bf1a1b3007 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 28 Jun 2023 14:09:53 -0400 Subject: crypto: remove outdated variant of ChaCha20Poly1305 AEAD Remove the variant of ChaCha20Poly1305 AEAD that was previously added in anticipation of BIP324 using it. BIP324 was updated to instead use rekeying wrappers around otherwise unmodified versions of the ChaCha20 stream cipher and the ChaCha20Poly1305 AEAD as specified in RFC8439. --- src/test/fuzz/crypto_chacha20_poly1305_aead.cpp | 72 ------------------------- 1 file changed, 72 deletions(-) delete mode 100644 src/test/fuzz/crypto_chacha20_poly1305_aead.cpp (limited to 'src/test/fuzz') 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -FUZZ_TARGET(crypto_chacha20_poly1305_aead) -{ - FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; - - const std::vector k1 = ConsumeFixedLengthByteVector(fuzzed_data_provider, CHACHA20_POLY1305_AEAD_KEY_LEN); - const std::vector 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(0, 4096); - std::vector in(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0); - std::vector 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(64, 4096); - in = std::vector(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0); - out = std::vector(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(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(1))) { - return; - } - seqnr_aad += 1; - } - }, - [&] { - seqnr_payload = fuzzed_data_provider.ConsumeIntegral(); - }, - [&] { - seqnr_aad = fuzzed_data_provider.ConsumeIntegral(); - }, - [&] { - is_encrypt = fuzzed_data_provider.ConsumeBool(); - }); - } -} -- cgit v1.2.3 From 0fee267792eb8cbdd48ad78f1712420b5d8d905b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 28 Jun 2023 18:20:30 -0400 Subject: crypto: add FSChaCha20, a rekeying wrapper around ChaCha20 This adds the FSChaCha20 stream cipher as specified in BIP324, a wrapper around the ChaCha20 stream cipher (specified in RFC8439 section 2.4) which automatically rekeys every N messages, and manages the nonces used for encryption. Co-authored-by: dhruv <856960+dhruv@users.noreply.github.com> --- src/test/fuzz/crypto_chacha20.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src/test/fuzz') 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 #include +#include +#include #include #include @@ -151,3 +153,21 @@ FUZZ_TARGET(chacha20_split_keystream) FuzzedDataProvider provider{buffer.data(), buffer.size()}; ChaCha20SplitFuzz(provider); } + +FUZZ_TARGET(crypto_fschacha20) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + + auto key = fuzzed_data_provider.ConsumeBytes(FSChaCha20::KEYLEN); + key.resize(FSChaCha20::KEYLEN); + + auto fsc20 = FSChaCha20{key, fuzzed_data_provider.ConsumeIntegralInRange(1, 1024)}; + + LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) + { + auto input = fuzzed_data_provider.ConsumeBytes(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); + std::vector output; + output.resize(input.size()); + fsc20.Crypt(input, output); + } +} -- cgit v1.2.3 From 990f0f8da92a2d11828a7c05ca93bf0520b2a95e Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 6 Jul 2023 14:19:29 -0400 Subject: Add BIP324Cipher, encapsulating key agreement, derivation, and stream/AEAD ciphers Co-authored-by: dhruv <856960+dhruv@users.noreply.github.com> --- src/test/fuzz/bip324.cpp | 137 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 src/test/fuzz/bip324.cpp (limited to 'src/test/fuzz') 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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(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(32); + init_ent.resize(32); + // Responder key + auto resp_key_data = provider.ConsumeBytes(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(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()); + + // 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(); + 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(0, (1 << aad_length_bits) - 1); + unsigned length_bits = 2 * ((mode >> 5) & 7); + unsigned length = provider.ConsumeIntegralInRange(0, (1 << length_bits) - 1); + // Generate aad and content. + std::vector aad(aad_length); + for (auto& val : aad) val = std::byte{(uint8_t)rng()}; + std::vector 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 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(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 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); + } +} -- cgit v1.2.3