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) 2023-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 <bip324.h>6#include <chainparams.h>7#include <random.h>8#include <span.h>9#include <test/fuzz/FuzzedDataProvider.h>10#include <test/fuzz/fuzz.h>11#include <test/fuzz/util.h>13#include <algorithm>14#include <cstdint>15#include <vector>17namespace {19void Initialize()20{21static ECC_Context ecc_context{};22SelectParams(ChainType::MAIN);23}25} // namespace27FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize)28{29// Test that BIP324Cipher's encryption and decryption agree.31// Load keys from fuzzer.32FuzzedDataProvider provider(buffer.data(), buffer.size());33// Initiator key34CKey init_key = ConsumePrivateKey(provider, /*compressed=*/true);35if (!init_key.IsValid()) return;36// Initiator entropy37auto init_ent = provider.ConsumeBytes<std::byte>(32);38init_ent.resize(32);39// Responder key40CKey resp_key = ConsumePrivateKey(provider, /*compressed=*/true);41if (!resp_key.IsValid()) return;42// Responder entropy43auto resp_ent = provider.ConsumeBytes<std::byte>(32);44resp_ent.resize(32);46// Initialize ciphers by exchanging public keys.47BIP324Cipher initiator(init_key, init_ent);48assert(!initiator);49BIP324Cipher responder(resp_key, resp_ent);50assert(!responder);51initiator.Initialize(responder.GetOurPubKey(), true);52assert(initiator);53responder.Initialize(initiator.GetOurPubKey(), false);54assert(responder);56// Initialize RNG deterministically, to generate contents and AAD. We assume that there are no57// (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid58// reading the actual data for those from the fuzzer input (which would need large amounts of59// data).60InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());62// Compare session IDs and garbage terminators.63assert(std::ranges::equal(initiator.GetSessionID(), responder.GetSessionID()));64assert(std::ranges::equal(initiator.GetSendGarbageTerminator(), responder.GetReceiveGarbageTerminator()));65assert(std::ranges::equal(initiator.GetReceiveGarbageTerminator(), responder.GetSendGarbageTerminator()));67LIMITED_WHILE (provider.remaining_bytes(), 1000) {68// Mode:69// - Bit 0: whether the ignore bit is set in message70// - Bit 1: whether the responder (0) or initiator (1) sends71// - Bit 2: whether this ciphertext will be corrupted (making it the last sent one)72// - Bit 3-4: controls the maximum aad length (max 4095 bytes)73// - Bit 5-7: controls the maximum content length (max 16383 bytes, for performance reasons)74unsigned mode = provider.ConsumeIntegral<uint8_t>();75bool ignore = mode & 1;76bool from_init = mode & 2;77bool damage = mode & 4;78unsigned aad_length_bits = 4 * ((mode >> 3) & 3);79unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);80unsigned length_bits = 2 * ((mode >> 5) & 7);81unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);82// Generate aad and content.83auto aad = rng.randbytes<std::byte>(aad_length);84auto contents = rng.randbytes<std::byte>(length);86// Pick sides.87auto& sender{from_init ? initiator : responder};88auto& receiver{from_init ? responder : initiator};90// Encrypt91std::vector<std::byte> ciphertext(length + initiator.EXPANSION);92sender.Encrypt(contents, aad, ignore, ciphertext);94// Optionally damage 1 bit in either the ciphertext (corresponding to a change in transit)95// or the aad (to make sure that decryption will fail if the AAD mismatches).96if (damage) {97unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0,98(ciphertext.size() + aad.size()) * 8U - 1U);99unsigned damage_pos = damage_bit >> 3;100std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};101if (damage_pos >= ciphertext.size()) {102aad[damage_pos - ciphertext.size()] ^= damage_val;103} else {104ciphertext[damage_pos] ^= damage_val;105}106}108// Decrypt length109uint32_t dec_length = receiver.DecryptLength(std::span{ciphertext}.first(initiator.LENGTH_LEN));110if (!damage) {111assert(dec_length == length);112} else {113// For performance reasons, don't try to decode if length got increased too much.114if (dec_length > 16384 + length) break;115// Otherwise, just append zeros if dec_length > length.116ciphertext.resize(dec_length + initiator.EXPANSION);117}119// Decrypt120std::vector<std::byte> decrypt(dec_length);121bool dec_ignore{false};122bool ok = receiver.Decrypt(std::span{ciphertext}.subspan(initiator.LENGTH_LEN), aad, dec_ignore, decrypt);123// Decryption *must* fail if the packet was damaged, and succeed if it wasn't.124assert(!ok == damage);125if (!ok) break;126assert(ignore == dec_ignore);127assert(decrypt == contents);128}129}
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.