Inside Bitcoin
Opening the selected file…
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 <crypto/chacha20poly1305.h>7#include <crypto/chacha20.h>8#include <crypto/common.h>9#include <crypto/poly1305.h>10#include <span.h>11#include <support/cleanse.h>13#include <cassert>14#include <cstddef>16AEADChaCha20Poly1305::AEADChaCha20Poly1305(std::span<const std::byte> key) noexcept : m_chacha20(key)17{18assert(key.size() == KEYLEN);19}21void AEADChaCha20Poly1305::SetKey(std::span<const std::byte> key) noexcept22{23assert(key.size() == KEYLEN);24m_chacha20.SetKey(key);25}27namespace {29int timingsafe_bcmp_internal(const unsigned char* b1, const unsigned char* b2, size_t n) noexcept30{31const unsigned char *p1 = b1, *p2 = b2;32int ret = 0;33for (; n > 0; n--)34ret |= *p1++ ^ *p2++;35return (ret != 0);36}38/** Compute poly1305 tag. chacha20 must be set to the right nonce, block 0. Will be at block 1 after. */39void ComputeTag(ChaCha20& chacha20, std::span<const std::byte> aad, std::span<const std::byte> cipher, std::span<std::byte> tag) noexcept40{41static const std::byte PADDING[16] = {{}};43// Get block of keystream (use a full 64 byte buffer to avoid the need for chacha20's own buffering).44std::byte first_block[ChaCha20Aligned::BLOCKLEN];45chacha20.Keystream(first_block);47// Use the first 32 bytes of the first keystream block as poly1305 key.48Poly1305 poly1305{std::span{first_block}.first(Poly1305::KEYLEN)};50// Compute tag:51// - Process the padded AAD with Poly1305.52const unsigned aad_padding_length = (16 - (aad.size() % 16)) % 16;53poly1305.Update(aad).Update(std::span{PADDING}.first(aad_padding_length));54// - Process the padded ciphertext with Poly1305.55const unsigned cipher_padding_length = (16 - (cipher.size() % 16)) % 16;56poly1305.Update(cipher).Update(std::span{PADDING}.first(cipher_padding_length));57// - Process the AAD and plaintext length with Poly1305.58std::byte length_desc[Poly1305::TAGLEN];59WriteLE64(length_desc, aad.size());60WriteLE64(length_desc + 8, cipher.size());61poly1305.Update(length_desc);63// Output tag.64poly1305.Finalize(tag);65}67} // namespace69void AEADChaCha20Poly1305::Encrypt(std::span<const std::byte> plain1, std::span<const std::byte> plain2, std::span<const std::byte> aad, Nonce96 nonce, std::span<std::byte> cipher) noexcept70{71assert(cipher.size() == plain1.size() + plain2.size() + EXPANSION);73// Encrypt using ChaCha20 (starting at block 1).74m_chacha20.Seek(nonce, 1);75m_chacha20.Crypt(plain1, cipher.first(plain1.size()));76m_chacha20.Crypt(plain2, cipher.subspan(plain1.size()).first(plain2.size()));78// Seek to block 0, and compute tag using key drawn from there.79m_chacha20.Seek(nonce, 0);80ComputeTag(m_chacha20, aad, cipher.first(cipher.size() - EXPANSION), cipher.last(EXPANSION));81}83bool AEADChaCha20Poly1305::Decrypt(std::span<const std::byte> cipher, std::span<const std::byte> aad, Nonce96 nonce, std::span<std::byte> plain1, std::span<std::byte> plain2) noexcept84{85assert(cipher.size() == plain1.size() + plain2.size() + EXPANSION);87// Verify tag (using key drawn from block 0).88m_chacha20.Seek(nonce, 0);89std::byte expected_tag[EXPANSION];90ComputeTag(m_chacha20, aad, cipher.first(cipher.size() - EXPANSION), expected_tag);91if (timingsafe_bcmp_internal(UCharCast(expected_tag), UCharCast(cipher.last(EXPANSION).data()), EXPANSION)) return false;93// Decrypt (starting at block 1).94m_chacha20.Crypt(cipher.first(plain1.size()), plain1);95m_chacha20.Crypt(cipher.subspan(plain1.size()).first(plain2.size()), plain2);96return true;97}99void AEADChaCha20Poly1305::Keystream(Nonce96 nonce, std::span<std::byte> keystream) noexcept100{101// Skip the first output block, as it's used for generating the poly1305 key.102m_chacha20.Seek(nonce, 1);103m_chacha20.Keystream(keystream);104}106void FSChaCha20Poly1305::NextPacket() noexcept107{108if (++m_packet_counter == m_rekey_interval) {109// Generate a full block of keystream, to avoid needing the ChaCha20 buffer, even though110// we only need KEYLEN (32) bytes.111std::byte one_block[ChaCha20Aligned::BLOCKLEN];112m_aead.Keystream({0xFFFFFFFF, m_rekey_counter}, one_block);113// Switch keys.114m_aead.SetKey(std::span{one_block}.first(KEYLEN));115// Wipe the generated keystream (a copy remains inside m_aead, which will be cleaned up116// once it cycles again, or is destroyed).117memory_cleanse(one_block, sizeof(one_block));118// Update counters.119m_packet_counter = 0;120++m_rekey_counter;121}122}124void FSChaCha20Poly1305::Encrypt(std::span<const std::byte> plain1, std::span<const std::byte> plain2, std::span<const std::byte> aad, std::span<std::byte> cipher) noexcept125{126m_aead.Encrypt(plain1, plain2, aad, {m_packet_counter, m_rekey_counter}, cipher);127NextPacket();128}130bool FSChaCha20Poly1305::Decrypt(std::span<const std::byte> cipher, std::span<const std::byte> aad, std::span<std::byte> plain1, std::span<std::byte> plain2) noexcept131{132bool ret = m_aead.Decrypt(cipher, aad, {m_packet_counter, m_rekey_counter}, plain1, plain2);133NextPacket();134return ret;135}