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 <key.h>8#include <pubkey.h>9#include <span.h>10#include <test/util/random.h>11#include <test/util/setup_common.h>12#include <util/strencodings.h>14#include <algorithm>15#include <array>16#include <cstddef>17#include <cstdint>18#include <vector>20#include <boost/test/unit_test.hpp>22namespace {24struct BIP324Test : BasicTestingSetup {25void TestBIP324PacketVector(26uint32_t in_idx,27const std::string& in_priv_ours_hex,28const std::string& in_ellswift_ours_hex,29const std::string& in_ellswift_theirs_hex,30bool in_initiating,31const std::string& in_contents_hex,32uint32_t in_multiply,33const std::string& in_aad_hex,34bool in_ignore,35const std::string& mid_send_garbage_hex,36const std::string& mid_recv_garbage_hex,37const std::string& out_session_id_hex,38const std::string& out_ciphertext_hex,39const std::string& out_ciphertext_endswith_hex)40{41// Convert input from hex to char/byte vectors/arrays.42const auto in_priv_ours = ParseHex(in_priv_ours_hex);43const auto in_ellswift_ours = ParseHex<std::byte>(in_ellswift_ours_hex);44const auto in_ellswift_theirs = ParseHex<std::byte>(in_ellswift_theirs_hex);45const auto in_contents = ParseHex<std::byte>(in_contents_hex);46const auto in_aad = ParseHex<std::byte>(in_aad_hex);47const auto mid_send_garbage = ParseHex<std::byte>(mid_send_garbage_hex);48const auto mid_recv_garbage = ParseHex<std::byte>(mid_recv_garbage_hex);49const auto out_session_id = ParseHex<std::byte>(out_session_id_hex);50const auto out_ciphertext = ParseHex<std::byte>(out_ciphertext_hex);51const auto out_ciphertext_endswith = ParseHex<std::byte>(out_ciphertext_endswith_hex);53// Load keys54CKey key;55key.Set(in_priv_ours.begin(), in_priv_ours.end(), true);56EllSwiftPubKey ellswift_ours(in_ellswift_ours);57EllSwiftPubKey ellswift_theirs(in_ellswift_theirs);59// Instantiate encryption BIP324 cipher.60BIP324Cipher cipher(key, ellswift_ours);61BOOST_CHECK(!cipher);62BOOST_CHECK(cipher.GetOurPubKey() == ellswift_ours);63cipher.Initialize(ellswift_theirs, in_initiating);64BOOST_CHECK(cipher);66// Compare session variables.67BOOST_CHECK(std::ranges::equal(out_session_id, cipher.GetSessionID()));68BOOST_CHECK(std::ranges::equal(mid_send_garbage, cipher.GetSendGarbageTerminator()));69BOOST_CHECK(std::ranges::equal(mid_recv_garbage, cipher.GetReceiveGarbageTerminator()));71// Vector of encrypted empty messages, encrypted in order to seek to the right position.72std::vector<std::vector<std::byte>> dummies(in_idx);74// Seek to the numbered packet.75for (uint32_t i = 0; i < in_idx; ++i) {76dummies[i].resize(cipher.EXPANSION);77cipher.Encrypt({}, {}, true, dummies[i]);78}80// Construct contents and encrypt it.81std::vector<std::byte> contents;82for (uint32_t i = 0; i < in_multiply; ++i) {83contents.insert(contents.end(), in_contents.begin(), in_contents.end());84}85std::vector<std::byte> ciphertext(contents.size() + cipher.EXPANSION);86cipher.Encrypt(contents, in_aad, in_ignore, ciphertext);88// Verify ciphertext. Note that the test vectors specify either out_ciphertext (for short89// messages) or out_ciphertext_endswith (for long messages), so only check the relevant one.90if (!out_ciphertext.empty()) {91BOOST_CHECK(out_ciphertext == ciphertext);92} else {93BOOST_CHECK(ciphertext.size() >= out_ciphertext_endswith.size());94BOOST_CHECK(std::ranges::equal(out_ciphertext_endswith, std::span{ciphertext}.last(out_ciphertext_endswith.size())));95}97for (unsigned error = 0; error <= 12; ++error) {98// error selects a type of error introduced:99// - error=0: no errors, decryption should be successful100// - error=1: wrong side101// - error=2..9: bit error in ciphertext102// - error=10: bit error in aad103// - error=11: extra 0x00 at end of aad104// - error=12: message index wrong106// Instantiate self-decrypting BIP324 cipher.107BIP324Cipher dec_cipher(key, ellswift_ours);108BOOST_CHECK(!dec_cipher);109BOOST_CHECK(dec_cipher.GetOurPubKey() == ellswift_ours);110dec_cipher.Initialize(ellswift_theirs, (error == 1) ^ in_initiating, /*self_decrypt=*/true);111BOOST_CHECK(dec_cipher);113// Compare session variables.114BOOST_CHECK(std::ranges::equal(out_session_id, dec_cipher.GetSessionID()) == (error != 1));115BOOST_CHECK(std::ranges::equal(mid_send_garbage, dec_cipher.GetSendGarbageTerminator()) == (error != 1));116BOOST_CHECK(std::ranges::equal(mid_recv_garbage, dec_cipher.GetReceiveGarbageTerminator()) == (error != 1));118// Seek to the numbered packet.119if (in_idx == 0 && error == 12) continue;120uint32_t dec_idx = in_idx ^ (error == 12 ? (1U << m_rng.randrange(16)) : 0);121for (uint32_t i = 0; i < dec_idx; ++i) {122unsigned use_idx = i < in_idx ? i : 0;123bool dec_ignore{false};124dec_cipher.DecryptLength(std::span{dummies[use_idx]}.first(cipher.LENGTH_LEN));125dec_cipher.Decrypt(std::span{dummies[use_idx]}.subspan(cipher.LENGTH_LEN), {}, dec_ignore, {});126}128// Construct copied (and possibly damaged) copy of ciphertext.129// Decrypt length130auto to_decrypt = ciphertext;131if (error >= 2 && error <= 9) {132to_decrypt[m_rng.randrange(to_decrypt.size())] ^= std::byte(1U << (error - 2));133}135// Decrypt length and resize ciphertext to accommodate.136uint32_t dec_len = dec_cipher.DecryptLength(MakeByteSpan(to_decrypt).first(cipher.LENGTH_LEN));137to_decrypt.resize(dec_len + cipher.EXPANSION);139// Construct copied (and possibly damaged) copy of aad.140auto dec_aad = in_aad;141if (error == 10) {142if (in_aad.size() == 0) continue;143dec_aad[m_rng.randrange(dec_aad.size())] ^= std::byte(1U << m_rng.randrange(8));144}145if (error == 11) dec_aad.push_back({});147// Decrypt contents.148std::vector<std::byte> decrypted(dec_len);149bool dec_ignore{false};150bool dec_ok = dec_cipher.Decrypt(std::span{to_decrypt}.subspan(cipher.LENGTH_LEN), dec_aad, dec_ignore, decrypted);152// Verify result.153BOOST_CHECK(dec_ok == !error);154if (dec_ok) {155BOOST_CHECK(decrypted == contents);156BOOST_CHECK(dec_ignore == in_ignore);157}158}159}160}; // struct BIP324Test162} // namespace164BOOST_FIXTURE_TEST_SUITE(bip324_tests, BIP324Test)166BOOST_AUTO_TEST_CASE(packet_test_vectors) {167// BIP324 key derivation uses network magic in the HKDF process. We use mainnet params here168// as that is what the test vectors are written for.169SelectParams(ChainType::MAIN);171// The test vectors are converted using the following Python code in the BIP bip-0324/ directory:172//173// import sys174// import csv175// with open('packet_encoding_test_vectors.csv', newline='', encoding='utf-8') as csvfile:176// reader = csv.DictReader(csvfile)177// quote = lambda x: "\"" + x + "\""178// for row in reader:179// args = [180// row['in_idx'],181// quote(row['in_priv_ours']),182// quote(row['in_ellswift_ours']),183// quote(row['in_ellswift_theirs']),184// "true" if int(row['in_initiating']) else "false",185// quote(row['in_contents']),186// row['in_multiply'],187// quote(row['in_aad']),188// "true" if int(row['in_ignore']) else "false",189// quote(row['mid_send_garbage_terminator']),190// quote(row['mid_recv_garbage_terminator']),191// quote(row['out_session_id']),192// quote(row['out_ciphertext']),193// quote(row['out_ciphertext_endswith'])194// ]195// print(" TestBIP324PacketVector(\n " + ",\n ".join(args) + ");")196TestBIP324PacketVector(1971,198"61062ea5071d800bbfd59e2e8b53d47d194b095ae5a4df04936b49772ef0d4d7",199"ec0adff257bbfe500c188c80b4fdd640f6b45a482bbc15fc7cef5931deff0aa186f6eb9bba7b85dc4dcc28b28722de1e3d9108b985e2967045668f66098e475b",200"a4a94dfce69b4a2a0a099313d10f9f7e7d649d60501c9e1d274c300e0d89aafaffffffffffffffffffffffffffffffffffffffffffffffffffffffff8faf88d5",201true,202"8e",2031,204"",205false,206"faef555dfcdb936425d84aba524758f3",207"02cb8ff24307a6e27de3b4e7ea3fa65b",208"ce72dffb015da62b0d0f5474cab8bc72605225b0cee3f62312ec680ec5f41ba5",209"7530d2a18720162ac09c25329a60d75adf36eda3c3",210"");211TestBIP324PacketVector(212999,213"6f312890ec83bbb26798abaadd574684a53e74ccef7953b790fcc29409080246",214"a8785af31c029efc82fa9fc677d7118031358d7c6a25b5779a9b900e5ccd94aac97eb36a3c5dbcdb2ca5843cc4c2fe0aaa46d10eb3d233a81c3dde476da00eef",215"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0000000000000000000000000000000000000000000000000000000000000000",216false,217"3eb1d4e98035cfd8eeb29bac969ed3824a",2181,219"",220false,221"44737108aec5f8b6c1c277b31bbce9c1",222"ca29b3a35237f8212bd13ed187a1da2e",223"b0490e26111cb2d55bbff2ace00f7f644f64006539abb4e7513f05107bb10608",224"d78adbcba0eebfb15cfbd8142c84dc729d233d0dc11b1d851e46a114122b8d5b96b7d59317",225"");226TestBIP324PacketVector(2270,228"846a784f1a03dea59cc679754a60a7145542fa130e3efbd815c81e909ce32933",229"480eacf1536b52257bf8ce78d8f4ce09395d744767c6c129e7838947ee625af3245592c111275e877d5baae22584cb5f1153e67c16bcd7da767726cd0d0c846a",230"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff22d5e441524d571a52b3def126189d3f416890a99d4da6ede2b0cde1760ce2c3f98457ae",231true,232"054290a6c6ba8d80478172e89d32bf690913ae9835de6dcf206ff1f4d652286fe0ddf74deba41d55de3edc77c42a32af79bbea2c00bae7492264c60866ae5a",2331,234"84932a55aac22b51e7b128d31d9f0550da28e6a3f394224707d878603386b2f9d0c6bcd8046679bfed7b68c517e7431e75d9dd34605727d2ef1c2babbf680ecc8d68d2c4886e9953a4034abde6da4189cd47c6bb3192242cf714d502ca6103ee84e08bc2ca4fd370d5ad4e7d06c7fbf496c6c7cc7eb19c40c61fb33df2a9ba48497a96c98d7b10c1f91098a6b7b16b4bab9687f27585ade1491ae0dba6a79e1e2d85dd9d9d45c5135ca5fca3f0f99a60ea39edbc9efc7923111c937913f225d67788d5f7e8852b697e26b92ec7bfcaa334a1665511c2b4c0a42d06f7ab98a9719516c8fd17f73804555ee84ab3b7d1762f6096b778d3cb9c799cbd49a9e4a325197b4e6cc4a5c4651f8b41ff88a92ec428354531f970263b467c77ed11312e2617d0d53fe9a8707f51f9f57a77bfb49afe3d89d85ec05ee17b9186f360c94ab8bb2926b65ca99dae1d6ee1af96cad09de70b6767e949023e4b380e66669914a741ed0fa420a48dbc7bfae5ef2019af36d1022283dd90655f25eec7151d471265d22a6d3f91dc700ba749bb67c0fe4bc0888593fbaf59d3c6fff1bf756a125910a63b9682b597c20f560ecb99c11a92c8c8c3f7fbfaa103146083a0ccaecf7a5f5e735a784a8820155914a289d57d8141870ffcaf588882332e0bcd8779efa931aa108dab6c3cce76691e345df4a91a03b71074d66333fd3591bff071ea099360f787bbe43b7b3dff2a59c41c7642eb79870222ad1c6f2e5a191ed5acea51134679587c9cf71c7d8ee290be6bf465c4ee47897a125708704ad610d8d00252d01959209d7cd04d5ecbbb1419a7e84037a55fefa13dee464b48a35c96bcb9a53e7ed461c3a1607ee00c3c302fd47cd73fda7493e947c9834a92d63dcfbd65aa7c38c3e3a2748bb5d9a58e7495d243d6b741078c8f7ee9c8813e473a323375702702b0afae1550c8341eedf5247627343a95240cb02e3e17d5dca16f8d8d3b2228e19c06399f8ec5c5e9dbe4caef6a0ea3ffb1d3c7eac03ae030e791fa12e537c80d56b55b764cadf27a8701052df1282ba8b5e3eb62b5dc7973ac40160e00722fa958d95102fc25c549d8c0e84bed95b7acb61ba65700c4de4feebf78d13b9682c52e937d23026fb4c6193e6644e2d3c99f91f4f39a8b9fc6d013f89c3793ef703987954dc0412b550652c01d922f525704d32d70d6d4079bc3551b563fb29577b3aecdc9505011701dddfd94830431e7a4918927ee44fb3831ce8c4513839e2deea1287f3fa1ab9b61a256c09637dbc7b4f0f8fbb783840f9c24526da883b0df0c473cf231656bd7bc1aaba7f321fec0971c8c2c3444bff2f55e1df7fea66ec3e440a612db9aa87bb505163a59e06b96d46f50d8120b92814ac5ab146bc78dbbf91065af26107815678ce6e33812e6bf3285d4ef3b7b04b076f21e7820dcbfdb4ad5218cf4ff6a65812d8fcb98ecc1e95e2fa58e3efe4ce26cd0bd400d6036ab2ad4f6c713082b5e3f1e04eb9e3b6c8f63f57953894b9e220e0130308e1fd91f72d398c1e7962ca2c31be83f31d6157633581a0a6910496de8d55d3d07090b6aa087159e388b7e7dec60f5d8a60d93ca2ae91296bd484d916bfaaa17c8f45ea4b1a91b37c82821199a2b7596672c37156d8701e7352aa48671d3b1bbbd2bd5f0a2268894a25b0cb2514af39c8743f8cce8ab4b523053739fd8a522222a09acf51ac704489cf17e4b7125455cb8f125b4d31af1eba1f8cf7f81a5a100a141a7ee72e8083e065616649c241f233645c5fc865d17f0285f5c52d9f45312c979bfb3ce5f2a1b951deddf280ffb3f370410cffd1583bfa90077835aa201a0712d1dcd1293ee177738b14e6b5e2a496d05220c3253bb6578d6aff774be91946a614dd7e879fb3dcf7451e0b9adb6a8c44f53c2c464bcc0019e9fad89cac7791a0a3f2974f759a9856351d4d2d7c5612c17cfc50f8479945df57716767b120a590f4bf656f4645029a525694d8a238446c5f5c2c1c995c09c1405b8b1eb9e0352ffdf766cc964f8dcf9f8f043dfab6d102cf4b298021abd78f1d9025fa1f8e1d710b38d9d1652f2d88d1305874ec41609b6617b65c5adb19b6295dc5 … [line preview shortened]235false,236"3ba1f51de6272aa28fd21059b91d3893",237"faf3b317340de00e29f2181db270ff81",238"d083d09c1bdf71795b39a9534601cf7c7a7e767e578c44a17dfaf43a3c18f98c",239"6aa28bc4b6719eca144ac33a3f17859317d5450e4978db9365ce61e7085a617dd386ec18eb436c9056aa1d2d4736c9bffd25803d967fcae916ce1647ccae3d5258b17dfa1cdc7eb99581c48ff2898ef92d3aa1",240"");241TestBIP324PacketVector(242223,243"c0f15820459f64d98e5c48681d13340572c574533dd9f7161b85fcc8224fdf30",244"682871104d694baca8b9c7990ae6288f49e1ff4feb21dd5cffad67db7752fdfb6c3608d6996c54be04b35feef037da09ee4d9dca2363b343bc2d4f6d0ea609da",245"56bd0c06f10352c3a1a9f4b4c92f6fa2b26df124b57878353c1fc691c51abea77c8817daeeb9fa546b77c8daf79d89b22b0e1b87574ece42371f00237aa9d83a",246false,247"7e0e78eb6990b059e6cf0ded66ea93ef82e72aa2f18ac24f2fc6ebab561ae557420729da103f64cecfa20527e15f9fb669a49bbbf274ef0389b3e43c8c44e5f60bf2ac38e2b55e7ec4273dba15ba41d21f8f5b3ee1688b3c29951218caf847a97fb50d75a86515d445699497d968164bf740012679b8962de573be941c62b7ef",2481,249"",250true,
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.