diff --git a/src/Makefile.am b/src/Makefile.am index 3f5c3a2f4a..020c9a6b69 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -134,6 +134,8 @@ BITCOIN_CORE_H = \ consensus/tx_verify.h \ core_io.h \ core_memusage.h \ + crypto/bip324.h \ + crypto/bip324.cpp \ cuckoocache.h \ dbwrapper.h \ deploymentinfo.h \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 7df1ecb25d..00aa256e5b 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -73,6 +73,7 @@ BITCOIN_TESTS =\ test/base64_tests.cpp \ test/bech32_tests.cpp \ test/bip32_tests.cpp \ + test/bip324_derivation_tests.cpp \ test/blockchain_tests.cpp \ test/blockencodings_tests.cpp \ test/blockfilter_tests.cpp \ diff --git a/src/crypto/bip324.cpp b/src/crypto/bip324.cpp new file mode 100644 index 0000000000..68820abaf7 --- /dev/null +++ b/src/crypto/bip324.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include + +bool derive_bip324_keys(ECDHSecret&& ecdh_secret, const Span initiator_hdata, const Span responder_hdata, BIP324Keys& derived_keys) +{ + if (ecdh_secret.size() != ECDH_SECRET_SIZE) { + return false; + } + + std::string salt{"bitcoin_v2_shared_secret"}; + salt += std::string{reinterpret_cast(initiator_hdata.data()), initiator_hdata.size()}; + salt += std::string{reinterpret_cast(responder_hdata.data()), responder_hdata.size()}; + salt += std::string{reinterpret_cast(Params().MessageStart()), CMessageHeader::MESSAGE_START_SIZE}; + + CHKDF_HMAC_SHA256_L32 hkdf(ecdh_secret.data(), ecdh_secret.size(), salt); + + derived_keys.initiator_F.resize(BIP324_KEY_LEN); + hkdf.Expand32("initiator_F", derived_keys.initiator_F.data()); + + derived_keys.initiator_V.resize(BIP324_KEY_LEN); + hkdf.Expand32("initiator_V", derived_keys.initiator_V.data()); + + derived_keys.responder_F.resize(BIP324_KEY_LEN); + hkdf.Expand32("responder_F", derived_keys.responder_F.data()); + + derived_keys.responder_V.resize(BIP324_KEY_LEN); + hkdf.Expand32("responder_V", derived_keys.responder_V.data()); + + derived_keys.session_id.resize(BIP324_KEY_LEN); + hkdf.Expand32("session_id", derived_keys.session_id.data()); + + memory_cleanse(ecdh_secret.data(), ecdh_secret.size()); + return true; +} diff --git a/src/crypto/bip324.h b/src/crypto/bip324.h new file mode 100644 index 0000000000..963a54f04c --- /dev/null +++ b/src/crypto/bip324.h @@ -0,0 +1,29 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_BIP324_H +#define BITCOIN_CRYPTO_BIP324_H + +#include +#include +#include + +#include + +constexpr size_t BIP324_KEY_LEN = 32; + +using BIP324Key = std::vector>; + +struct BIP324Keys { + BIP324Key initiator_F; + BIP324Key initiator_V; + BIP324Key responder_F; + BIP324Key responder_V; + BIP324Key session_id; +}; + +// Returns false if the ecdh_secret is malformed. +bool derive_bip324_keys(ECDHSecret&& ecdh_secret, const Span initiator_hdata, const Span responder_hdata, BIP324Keys& derived_keys); + +#endif // BITCOIN_CRYPTO_BIP324_H diff --git a/src/test/bip324_derivation_tests.cpp b/src/test/bip324_derivation_tests.cpp new file mode 100644 index 0000000000..f36b236af3 --- /dev/null +++ b/src/test/bip324_derivation_tests.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include + +#include + +BOOST_FIXTURE_TEST_SUITE(bip324_derivation_tests, BasicTestingSetup) + +static const std::string strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"; +static const std::string strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"; +static const std::string initiator_hdata = "2deb41da6887640dda029ae41c9c9958881d0bb8e28f6bb9039ee9b7bb11091d62f4cbe65cc418df7aefd738f4d3e926c66365b4d38eefd0a883be64112f4495"; +static const std::string responder_hdata = "4c469c70ba242ae0fc98d4eff6258cf19ecab96611c9c736356a4cf11d66edfa4d2970e56744a6d071861a4cbe2730eb7733a38b166e3df73450ef37112dd32f"; + +BOOST_AUTO_TEST_CASE(bip324_derivation_test) +{ + CKey initiator_key = DecodeSecret(strSecret1); + CKey responder_key = DecodeSecret(strSecret2C); + + auto initiator_pubkey = initiator_key.GetPubKey(); + auto responder_pubkey = responder_key.GetPubKey(); + + ECDHSecret initiator_secret, responder_secret; + BOOST_CHECK(initiator_key.ComputeECDHSecret(responder_pubkey, initiator_secret)); + BOOST_CHECK(responder_key.ComputeECDHSecret(initiator_pubkey, responder_secret)); + + BOOST_CHECK_EQUAL(ECDH_SECRET_SIZE, initiator_secret.size()); + BOOST_CHECK_EQUAL(ECDH_SECRET_SIZE, responder_secret.size()); + BOOST_CHECK_EQUAL(0, memcmp(initiator_secret.data(), responder_secret.data(), ECDH_SECRET_SIZE)); + + auto initiator_hdata_vec = ParseHex(initiator_hdata); + Span initiator_hdata_span{initiator_hdata_vec.data(), initiator_hdata_vec.size()}; + + auto responder_hdata_vec = ParseHex(responder_hdata); + Span responder_hdata_span{responder_hdata_vec.data(), responder_hdata_vec.size()}; + + BIP324Keys initiator_keys, responder_keys; + + BOOST_CHECK(derive_bip324_keys(std::move(initiator_secret), initiator_hdata_span, responder_hdata_span, initiator_keys)); + BOOST_CHECK(derive_bip324_keys(std::move(responder_secret), initiator_hdata_span, responder_hdata_span, responder_keys)); + + // Make sure that the ephemeral ECDH secret is cleansed from memory once the keys are derived. + BOOST_CHECK_EQUAL("0000000000000000000000000000000000000000000000000000000000000000", HexStr(initiator_secret)); + BOOST_CHECK_EQUAL("0000000000000000000000000000000000000000000000000000000000000000", HexStr(responder_secret)); + + BOOST_CHECK_EQUAL(BIP324_KEY_LEN, initiator_keys.initiator_F.size()); + BOOST_CHECK_EQUAL(initiator_keys.initiator_F.size(), responder_keys.initiator_F.size()); + BOOST_CHECK_EQUAL(0, memcmp(initiator_keys.initiator_F.data(), responder_keys.initiator_F.data(), initiator_keys.initiator_F.size())); + BOOST_CHECK_EQUAL("11a84eb7ea351002f4ea981169567fcb6357581ecb4199c947bf294fc30638da", HexStr(MakeSpan(initiator_keys.initiator_F))); + + BOOST_CHECK_EQUAL(BIP324_KEY_LEN, initiator_keys.initiator_V.size()); + BOOST_CHECK_EQUAL(initiator_keys.initiator_V.size(), responder_keys.initiator_V.size()); + BOOST_CHECK_EQUAL(0, memcmp(initiator_keys.initiator_V.data(), responder_keys.initiator_V.data(), initiator_keys.initiator_V.size())); + BOOST_CHECK_EQUAL("b50e74125b810574aa00358392ff47fb992dd8adc47a397d398f8237e6fcb214", HexStr(MakeSpan(initiator_keys.initiator_V))); + + BOOST_CHECK_EQUAL(BIP324_KEY_LEN, initiator_keys.responder_F.size()); + BOOST_CHECK_EQUAL(initiator_keys.responder_F.size(), responder_keys.responder_F.size()); + BOOST_CHECK_EQUAL(0, memcmp(initiator_keys.responder_F.data(), responder_keys.responder_F.data(), initiator_keys.responder_F.size())); + BOOST_CHECK_EQUAL("7ce2b20cc16019223bdbf988d9bc84df970c9dfad94e04d74ac38f536c59178b", HexStr(MakeSpan(initiator_keys.responder_F))); + + BOOST_CHECK_EQUAL(BIP324_KEY_LEN, initiator_keys.responder_V.size()); + BOOST_CHECK_EQUAL(initiator_keys.responder_V.size(), responder_keys.responder_V.size()); + BOOST_CHECK_EQUAL(0, memcmp(initiator_keys.responder_V.data(), responder_keys.responder_V.data(), initiator_keys.responder_V.size())); + BOOST_CHECK_EQUAL("1737955f199515774e8dbfc80107bcd145396b0b2a0a7134e7f5d626dc201d35", HexStr(MakeSpan(initiator_keys.responder_V))); + + BOOST_CHECK_EQUAL(BIP324_KEY_LEN, initiator_keys.session_id.size()); + BOOST_CHECK_EQUAL(initiator_keys.session_id.size(), responder_keys.session_id.size()); + BOOST_CHECK_EQUAL(0, memcmp(initiator_keys.session_id.data(), responder_keys.session_id.data(), initiator_keys.session_id.size())); + BOOST_CHECK_EQUAL("c2af308f4d8a73b03dcd914866a242f7199c42158f6ac3d3c1f9e700ffe1d9a7", HexStr(MakeSpan(initiator_keys.session_id))); +} + +BOOST_AUTO_TEST_SUITE_END()