Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2020-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <crypto/chacha20poly1305.h>6#include <random.h>7#include <span.h>8#include <test/fuzz/FuzzedDataProvider.h>9#include <test/fuzz/fuzz.h>10#include <test/fuzz/util.h>12#include <cstddef>13#include <cstdint>14#include <vector>16constexpr static inline void crypt_till_rekey(FSChaCha20Poly1305& aead, int rekey_interval, bool encrypt)17{18for (int i = 0; i < rekey_interval; ++i) {19std::byte dummy_tag[FSChaCha20Poly1305::EXPANSION] = {{}};20if (encrypt) {21aead.Encrypt(std::span{dummy_tag}.first(0), std::span{dummy_tag}.first(0), dummy_tag);22} else {23aead.Decrypt(dummy_tag, std::span{dummy_tag}.first(0), std::span{dummy_tag}.first(0));24}25}26}28FUZZ_TARGET(crypto_aeadchacha20poly1305)29{30FuzzedDataProvider provider{buffer.data(), buffer.size()};32auto key = provider.ConsumeBytes<std::byte>(32);33key.resize(32);34AEADChaCha20Poly1305 aead(key);36// Initialize RNG deterministically, to generate contents and AAD. We assume that there are no37// (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid38// reading the actual data for those from the fuzzer input (which would need large amounts of39// data).40InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());42LIMITED_WHILE (provider.ConsumeBool(), 100) {43// Mode:44// - Bit 0: whether to use single-plain Encrypt/Decrypt; otherwise use a split at prefix.45// - Bit 2: whether this ciphertext will be corrupted (making it the last sent one)46// - Bit 3-4: controls the maximum aad length (max 511 bytes)47// - Bit 5-7: controls the maximum content length (max 16383 bytes, for performance reasons)48unsigned mode = provider.ConsumeIntegral<uint8_t>();49bool use_splits = mode & 1;50bool damage = mode & 4;51unsigned aad_length_bits = 3 * ((mode >> 3) & 3);52unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);53unsigned length_bits = 2 * ((mode >> 5) & 7);54unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);55// Generate aad and content.56auto aad = rng.randbytes<std::byte>(aad_length);57auto plain = rng.randbytes<std::byte>(length);58std::vector<std::byte> cipher(length + AEADChaCha20Poly1305::EXPANSION);59// Generate nonce60AEADChaCha20Poly1305::Nonce96 nonce = {(uint32_t)rng(), rng()};62if (use_splits && length > 0) {63size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);64aead.Encrypt(std::span{plain}.first(split_index), std::span{plain}.subspan(split_index), aad, nonce, cipher);65} else {66aead.Encrypt(plain, aad, nonce, cipher);67}69// Test Keystream output70std::vector<std::byte> keystream(length);71aead.Keystream(nonce, keystream);72for (size_t i = 0; i < length; ++i) {73assert((plain[i] ^ keystream[i]) == cipher[i]);74}76std::vector<std::byte> decrypted_contents(length);77bool ok{false};79// damage the key80unsigned key_position = provider.ConsumeIntegralInRange<unsigned>(0, 31);81std::byte damage_val{(uint8_t)(1U << (key_position & 7))};82std::vector<std::byte> bad_key = key;83bad_key[key_position] ^= damage_val;85AEADChaCha20Poly1305 bad_aead(bad_key);86ok = bad_aead.Decrypt(cipher, aad, nonce, decrypted_contents);87assert(!ok);89// Optionally damage 1 bit in either the cipher (corresponding to a change in transit)90// or the aad (to make sure that decryption will fail if the AAD mismatches).91if (damage) {92unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, (cipher.size() + aad.size()) * 8U - 1U);93unsigned damage_pos = damage_bit >> 3;94std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};95if (damage_pos >= cipher.size()) {96aad[damage_pos - cipher.size()] ^= damage_val;97} else {98cipher[damage_pos] ^= damage_val;99}100}102if (use_splits && length > 0) {103size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);104ok = aead.Decrypt(cipher, aad, nonce, std::span{decrypted_contents}.first(split_index), std::span{decrypted_contents}.subspan(split_index));105} else {106ok = aead.Decrypt(cipher, aad, nonce, decrypted_contents);107}109// Decryption *must* fail if the packet was damaged, and succeed if it wasn't.110assert(!ok == damage);111if (!ok) break;112assert(decrypted_contents == plain);113}114}116FUZZ_TARGET(crypto_fschacha20poly1305)117{118FuzzedDataProvider provider{buffer.data(), buffer.size()};120uint32_t rekey_interval = provider.ConsumeIntegralInRange<size_t>(32, 512);121auto key = provider.ConsumeBytes<std::byte>(32);122key.resize(32);123FSChaCha20Poly1305 enc_aead(key, rekey_interval);124FSChaCha20Poly1305 dec_aead(key, rekey_interval);126// Initialize RNG deterministically, to generate contents and AAD. We assume that there are no127// (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid128// reading the actual data for those from the fuzzer input (which would need large amounts of129// data).130InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());132LIMITED_WHILE (provider.ConsumeBool(), 100) {133// Mode:134// - Bit 0: whether to use single-plain Encrypt/Decrypt; otherwise use a split at prefix.135// - Bit 2: whether this ciphertext will be corrupted (making it the last sent one)136// - Bit 3-4: controls the maximum aad length (max 511 bytes)137// - Bit 5-7: controls the maximum content length (max 16383 bytes, for performance reasons)138unsigned mode = provider.ConsumeIntegral<uint8_t>();139bool use_splits = mode & 1;140bool damage = mode & 4;141unsigned aad_length_bits = 3 * ((mode >> 3) & 3);142unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);143unsigned length_bits = 2 * ((mode >> 5) & 7);144unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);145// Generate aad and content.146auto aad = rng.randbytes<std::byte>(aad_length);147auto plain = rng.randbytes<std::byte>(length);148std::vector<std::byte> cipher(length + FSChaCha20Poly1305::EXPANSION);150crypt_till_rekey(enc_aead, rekey_interval, true);151if (use_splits && length > 0) {152size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);153enc_aead.Encrypt(std::span{plain}.first(split_index), std::span{plain}.subspan(split_index), aad, cipher);154} else {155enc_aead.Encrypt(plain, aad, cipher);156}158std::vector<std::byte> decrypted_contents(length);159bool ok{false};161// damage the key162unsigned key_position = provider.ConsumeIntegralInRange<unsigned>(0, 31);163std::byte damage_val{(uint8_t)(1U << (key_position & 7))};164std::vector<std::byte> bad_key = key;165bad_key[key_position] ^= damage_val;167FSChaCha20Poly1305 bad_fs_aead(bad_key, rekey_interval);168crypt_till_rekey(bad_fs_aead, rekey_interval, false);169ok = bad_fs_aead.Decrypt(cipher, aad, decrypted_contents);170assert(!ok);172// Optionally damage 1 bit in either the cipher (corresponding to a change in transit)173// or the aad (to make sure that decryption will fail if the AAD mismatches).174if (damage) {175unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, (cipher.size() + aad.size()) * 8U - 1U);176unsigned damage_pos = damage_bit >> 3;177std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};178if (damage_pos >= cipher.size()) {179aad[damage_pos - cipher.size()] ^= damage_val;180} else {181cipher[damage_pos] ^= damage_val;182}183}185crypt_till_rekey(dec_aead, rekey_interval, false);186if (use_splits && length > 0) {187size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);188ok = dec_aead.Decrypt(cipher, aad, std::span{decrypted_contents}.first(split_index), std::span{decrypted_contents}.subspan(split_index));189} else {190ok = dec_aead.Decrypt(cipher, aad, decrypted_contents);191}193// Decryption *must* fail if the packet was damaged, and succeed if it wasn't.194assert(!ok == damage);195if (!ok) break;196assert(decrypted_contents == plain);197}198}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.