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) 2014-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/aes.h>6#include <crypto/chacha20.h>7#include <crypto/chacha20poly1305.h>8#include <crypto/hkdf_sha256_32.h>9#include <crypto/hmac_sha256.h>10#include <crypto/hmac_sha512.h>11#include <crypto/poly1305.h>12#include <crypto/ripemd160.h>13#include <crypto/sha1.h>14#include <crypto/sha256.h>15#include <crypto/sha3.h>16#include <crypto/sha512.h>17#include <crypto/muhash.h>18#include <random.h>19#include <streams.h>20#include <test/util/common.h>21#include <test/util/random.h>22#include <test/util/setup_common.h>23#include <util/strencodings.h>25#include <algorithm>26#include <vector>28#include <boost/test/unit_test.hpp>30using namespace util::hex_literals;32namespace crypto_tests {33struct CryptoTest : BasicTestingSetup {35template<typename Hasher, typename In, typename Out>36void TestVector(const Hasher &h, const In &in, const Out &out) {37Out hash;38BOOST_CHECK(out.size() == h.OUTPUT_SIZE);39hash.resize(out.size());40{41// Test that writing the whole input string at once works.42Hasher(h).Write((const uint8_t*)in.data(), in.size()).Finalize(hash.data());43BOOST_CHECK(hash == out);44}45for (int i=0; i<32; i++) {46// Test that writing the string broken up in random pieces works.47Hasher hasher(h);48size_t pos = 0;49while (pos < in.size()) {50size_t len = m_rng.randrange((in.size() - pos + 1) / 2 + 1);51hasher.Write((const uint8_t*)in.data() + pos, len);52pos += len;53if (pos > 0 && pos + 2 * out.size() > in.size() && pos < in.size()) {54// Test that writing the rest at once to a copy of a hasher works.55Hasher(hasher).Write((const uint8_t*)in.data() + pos, in.size() - pos).Finalize(hash.data());56BOOST_CHECK(hash == out);57}58}59hasher.Finalize(hash.data());60BOOST_CHECK(hash == out);61}62}64void TestSHA1(const std::string &in, const std::string &hexout) { TestVector(CSHA1(), in, ParseHex(hexout));}65void TestSHA256(const std::string &in, const std::string &hexout) { TestVector(CSHA256(), in, ParseHex(hexout));}66void TestSHA512(const std::string &in, const std::string &hexout) { TestVector(CSHA512(), in, ParseHex(hexout));}67void TestRIPEMD160(const std::string &in, const std::string &hexout) { TestVector(CRIPEMD160(), in, ParseHex(hexout));}69void TestHMACSHA256(const std::string &hexkey, const std::string &hexin, const std::string &hexout) {70std::vector<unsigned char> key = ParseHex(hexkey);71TestVector(CHMAC_SHA256(key.data(), key.size()), ParseHex(hexin), ParseHex(hexout));72}74void TestHMACSHA512(const std::string &hexkey, const std::string &hexin, const std::string &hexout) {75std::vector<unsigned char> key = ParseHex(hexkey);76TestVector(CHMAC_SHA512(key.data(), key.size()), ParseHex(hexin), ParseHex(hexout));77}79void TestAES256(const std::string &hexkey, const std::string &hexin, const std::string &hexout)80{81std::vector<unsigned char> key = ParseHex(hexkey);82std::vector<unsigned char> in = ParseHex(hexin);83std::vector<unsigned char> correctout = ParseHex(hexout);84std::vector<unsigned char> buf;86assert(key.size() == 32);87assert(in.size() == 16);88assert(correctout.size() == 16);89AES256Encrypt enc(key.data());90buf.resize(correctout.size());91enc.Encrypt(buf.data(), in.data());92BOOST_CHECK(buf == correctout);93AES256Decrypt dec(key.data());94dec.Decrypt(buf.data(), buf.data());95BOOST_CHECK(buf == in);96}98void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout)99{100std::vector<unsigned char> key = ParseHex(hexkey);101std::vector<unsigned char> iv = ParseHex(hexiv);102std::vector<unsigned char> in = ParseHex(hexin);103std::vector<unsigned char> correctout = ParseHex(hexout);104std::vector<unsigned char> realout(in.size() + AES_BLOCKSIZE);106// Encrypt the plaintext and verify that it equals the cipher107AES256CBCEncrypt enc(key.data(), iv.data(), pad);108int size = enc.Encrypt(in.data(), in.size(), realout.data());109realout.resize(size);110BOOST_CHECK(realout.size() == correctout.size());111BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout);113// Decrypt the cipher and verify that it equals the plaintext114std::vector<unsigned char> decrypted(correctout.size());115AES256CBCDecrypt dec(key.data(), iv.data(), pad);116size = dec.Decrypt(correctout.data(), correctout.size(), decrypted.data());117decrypted.resize(size);118BOOST_CHECK(decrypted.size() == in.size());119BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin);121// Encrypt and re-decrypt substrings of the plaintext and verify that they equal each-other122for(std::vector<unsigned char>::iterator i(in.begin()); i != in.end(); ++i)123{124std::vector<unsigned char> sub(i, in.end());125std::vector<unsigned char> subout(sub.size() + AES_BLOCKSIZE);126int _size = enc.Encrypt(sub.data(), sub.size(), subout.data());127if (_size != 0)128{129subout.resize(_size);130std::vector<unsigned char> subdecrypted(subout.size());131_size = dec.Decrypt(subout.data(), subout.size(), subdecrypted.data());132subdecrypted.resize(_size);133BOOST_CHECK(decrypted.size() == in.size());134BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub));135}136}137}139void TestChaCha20(const std::string &hex_message, const std::string &hexkey, ChaCha20::Nonce96 nonce, uint32_t seek, const std::string& hexout)140{141auto key = ParseHex<std::byte>(hexkey);142assert(key.size() == 32);143auto m = ParseHex<std::byte>(hex_message);144ChaCha20 rng{key};145rng.Seek(nonce, seek);146std::vector<std::byte> outres;147outres.resize(hexout.size() / 2);148assert(hex_message.empty() || m.size() * 2 == hexout.size());150// perform the ChaCha20 round(s), if message is provided it will output the encrypted ciphertext otherwise the keystream151if (!hex_message.empty()) {152rng.Crypt(m, outres);153} else {154rng.Keystream(outres);155}156BOOST_CHECK_EQUAL(hexout, HexStr(outres));157if (!hex_message.empty()) {158// Manually XOR with the keystream and compare the output159rng.Seek(nonce, seek);160std::vector<std::byte> only_keystream(outres.size());161rng.Keystream(only_keystream);162for (size_t i = 0; i != m.size(); i++) {163outres[i] = m[i] ^ only_keystream[i];164}165BOOST_CHECK_EQUAL(hexout, HexStr(outres));166}168// Repeat 10x, but fragmented into 3 chunks, to exercise the ChaCha20 class's caching.169for (int i = 0; i < 10; ++i) {170size_t lens[3];171lens[0] = m_rng.randrange(hexout.size() / 2U + 1U);172lens[1] = m_rng.randrange(hexout.size() / 2U + 1U - lens[0]);173lens[2] = hexout.size() / 2U - lens[0] - lens[1];175rng.Seek(nonce, seek);176outres.assign(hexout.size() / 2U, {});177size_t pos = 0;178for (int j = 0; j < 3; ++j) {179if (!hex_message.empty()) {180rng.Crypt(std::span{m}.subspan(pos, lens[j]), std::span{outres}.subspan(pos, lens[j]));181} else {182rng.Keystream(std::span{outres}.subspan(pos, lens[j]));183}184pos += lens[j];185}186BOOST_CHECK_EQUAL(hexout, HexStr(outres));187}188}190void TestFSChaCha20(const std::string& hex_plaintext, const std::string& hexkey, uint32_t rekey_interval, const std::string& ciphertext_after_rotation)191{192auto key = ParseHex<std::byte>(hexkey);193BOOST_CHECK_EQUAL(FSChaCha20::KEYLEN, key.size());195auto plaintext = ParseHex<std::byte>(hex_plaintext);197auto fsc20 = FSChaCha20{key, rekey_interval};198auto c20 = ChaCha20{key};200std::vector<std::byte> fsc20_output;201fsc20_output.resize(plaintext.size());203std::vector<std::byte> c20_output;204c20_output.resize(plaintext.size());206for (size_t i = 0; i < rekey_interval; i++) {207fsc20.Crypt(plaintext, fsc20_output);208c20.Crypt(plaintext, c20_output);209BOOST_CHECK(c20_output == fsc20_output);210}212// At the rotation interval, the outputs will no longer match213fsc20.Crypt(plaintext, fsc20_output);214auto c20_copy = c20;215c20.Crypt(plaintext, c20_output);216BOOST_CHECK(c20_output != fsc20_output);218std::byte new_key[FSChaCha20::KEYLEN];219c20_copy.Keystream(new_key);220c20.SetKey(new_key);221c20.Seek({0, 1}, 0);223// Outputs should match again after simulating key rotation224c20.Crypt(plaintext, c20_output);225BOOST_CHECK(c20_output == fsc20_output);227BOOST_CHECK_EQUAL(HexStr(fsc20_output), ciphertext_after_rotation);228}230void TestPoly1305(const std::string &hexmessage, const std::string &hexkey, const std::string& hextag)231{232auto key = ParseHex<std::byte>(hexkey);233auto m = ParseHex<std::byte>(hexmessage);234std::vector<std::byte> tagres(Poly1305::TAGLEN);235Poly1305{key}.Update(m).Finalize(tagres);236BOOST_CHECK_EQUAL(HexStr(tagres), hextag);238// Test incremental interface239for (int splits = 0; splits < 10; ++splits) {240for (int iter = 0; iter < 10; ++iter) {241auto data = std::span{m};242Poly1305 poly1305{key};243for (int chunk = 0; chunk < splits; ++chunk) {244size_t now = m_rng.randrange(data.size() + 1);245poly1305.Update(data.first(now));246data = data.subspan(now);247}248tagres.assign(Poly1305::TAGLEN, std::byte{});249poly1305.Update(data).Finalize(tagres);250BOOST_CHECK_EQUAL(HexStr(tagres), hextag);
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.