Skip to content

Commit 401ab88

Browse files
committed
HKDF key derivation from ECDH secret for BIP324
1 parent 34fc392 commit 401ab88

File tree

4 files changed

+107
-7
lines changed

4 files changed

+107
-7
lines changed

src/crypto/bip324_suite.h

+6-5
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212
#include <array>
1313
#include <cstddef>
1414

15-
static constexpr size_t BIP324_KEY_LEN = 32; // bytes
16-
static constexpr size_t BIP324_HEADER_LEN = 1; // bytes
17-
static constexpr size_t BIP324_LENGTH_FIELD_LEN = 3; // bytes
18-
static constexpr size_t REKEY_INTERVAL = 224; // packets
19-
static constexpr size_t NONCE_LENGTH = 12; // bytes
15+
static constexpr size_t BIP324_KEY_LEN = 32; // bytes
16+
static constexpr size_t BIP324_GARBAGE_TERMINATOR_LEN = 16; // bytes
17+
static constexpr size_t BIP324_HEADER_LEN = 1; // bytes
18+
static constexpr size_t BIP324_LENGTH_FIELD_LEN = 3; // bytes
19+
static constexpr size_t REKEY_INTERVAL = 224; // packets
20+
static constexpr size_t NONCE_LENGTH = 12; // bytes
2021

2122
enum BIP324HeaderFlags : uint8_t {
2223
BIP324_NONE = 0,

src/net.cpp

+29-1
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@
1515
#include <clientversion.h>
1616
#include <compat/compat.h>
1717
#include <consensus/consensus.h>
18+
#include <crypto/hkdf_sha256_32.h>
1819
#include <crypto/sha256.h>
19-
#include <node/eviction.h>
2020
#include <fs.h>
2121
#include <i2p.h>
2222
#include <net_permissions.h>
2323
#include <netaddress.h>
2424
#include <netbase.h>
25+
#include <node/eviction.h>
2526
#include <node/interface_ui.h>
2627
#include <protocol.h>
2728
#include <random.h>
2829
#include <scheduler.h>
30+
#include <support/cleanse.h>
2931
#include <util/sock.h>
3032
#include <util/strencodings.h>
3133
#include <util/syscall_sandbox.h>
@@ -434,6 +436,32 @@ static CAddress GetBindAddress(const Sock& sock)
434436
return addr_bind;
435437
}
436438

439+
void DeriveBIP324Session(ECDHSecret&& ecdh_secret, BIP324Session& session)
440+
{
441+
std::string salt{"bitcoin_v2_shared_secret"};
442+
salt += std::string{reinterpret_cast<const char*>(Params().MessageStart()), CMessageHeader::MESSAGE_START_SIZE};
443+
444+
CHKDF_HMAC_SHA256_L32 hkdf(reinterpret_cast<const unsigned char*>(ecdh_secret.data()), ecdh_secret.size(), salt);
445+
446+
hkdf.Expand32("initiator_L", reinterpret_cast<unsigned char*>(session.initiator_L.data()));
447+
hkdf.Expand32("initiator_P", reinterpret_cast<unsigned char*>(session.initiator_P.data()));
448+
hkdf.Expand32("responder_L", reinterpret_cast<unsigned char*>(session.responder_L.data()));
449+
hkdf.Expand32("responder_P", reinterpret_cast<unsigned char*>(session.responder_P.data()));
450+
hkdf.Expand32("session_id", reinterpret_cast<unsigned char*>(session.session_id.data()));
451+
452+
unsigned char hkdf_32_okm[32];
453+
hkdf.Expand32("garbage_terminators", hkdf_32_okm);
454+
memcpy(session.initiator_garbage_terminator.data(),
455+
hkdf_32_okm,
456+
BIP324_GARBAGE_TERMINATOR_LEN);
457+
memcpy(session.responder_garbage_terminator.data(),
458+
hkdf_32_okm + BIP324_GARBAGE_TERMINATOR_LEN,
459+
BIP324_GARBAGE_TERMINATOR_LEN);
460+
461+
memory_cleanse(hkdf_32_okm, 32);
462+
memory_cleanse(ecdh_secret.data(), ecdh_secret.size());
463+
}
464+
437465
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type)
438466
{
439467
assert(conn_type != ConnectionType::INBOUND);

src/net.h

+17-1
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,23 @@
99
#include <chainparams.h>
1010
#include <common/bloom.h>
1111
#include <compat/compat.h>
12-
#include <node/connection_types.h>
1312
#include <consensus/amount.h>
13+
#include <crypto/bip324_suite.h>
1414
#include <crypto/siphash.h>
1515
#include <hash.h>
1616
#include <i2p.h>
17+
#include <key.h>
1718
#include <net_permissions.h>
1819
#include <netaddress.h>
1920
#include <netbase.h>
2021
#include <netgroup.h>
22+
#include <node/connection_types.h>
2123
#include <policy/feerate.h>
2224
#include <protocol.h>
2325
#include <random.h>
2426
#include <span.h>
2527
#include <streams.h>
28+
#include <support/allocators/secure.h>
2629
#include <sync.h>
2730
#include <uint256.h>
2831
#include <util/check.h>
@@ -31,6 +34,7 @@
3134

3235
#include <atomic>
3336
#include <condition_variable>
37+
#include <cstddef>
3438
#include <cstdint>
3539
#include <deque>
3640
#include <functional>
@@ -342,6 +346,18 @@ struct CNodeOptions
342346
bool prefer_evict = false;
343347
};
344348

349+
struct BIP324Session {
350+
BIP324Key initiator_L;
351+
BIP324Key initiator_P;
352+
BIP324Key responder_L;
353+
BIP324Key responder_P;
354+
BIP324Key session_id;
355+
std::array<std::byte, BIP324_GARBAGE_TERMINATOR_LEN> initiator_garbage_terminator;
356+
std::array<std::byte, BIP324_GARBAGE_TERMINATOR_LEN> responder_garbage_terminator;
357+
};
358+
359+
void DeriveBIP324Session(ECDHSecret&& ecdh_secret, BIP324Session& session);
360+
345361
/** Information about a peer */
346362
class CNode
347363
{

src/test/net_tests.cpp

+55
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include <clientversion.h>
77
#include <compat/compat.h>
88
#include <cstdint>
9+
#include <key.h>
10+
#include <key_io.h>
911
#include <net.h>
1012
#include <net_processing.h>
1113
#include <netaddress.h>
@@ -905,4 +907,57 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
905907
TestOnlyResetTimeData();
906908
}
907909

910+
BOOST_AUTO_TEST_CASE(bip324_derivation_test)
911+
{
912+
// BIP324 key derivation uses network magic in the HKDF process. We use mainnet
913+
// params here to make it easier for other implementors to use this test as a test vector.
914+
SelectParams(CBaseChainParams::MAIN);
915+
static const std::string strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj";
916+
static const std::string strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g";
917+
static const std::string initiator_ellswift_str = "b654960dff0ba8808a34337f46cc68ba7619c9df76d0550639dea62de07d17f9cb61b85f2897834ce12c50b1aefa281944abf2223a5fcf0a2a7d8c022498db35";
918+
static const std::string responder_ellswift_str = "ea57aae33e8dd38380c303fb561b741293ef97c780445184cabdb5ef207053db628f2765e5d770f666738112c94714991362f6643d9837e1c89cbd9710b80929";
919+
920+
auto initiator_ellswift = ParseHex(initiator_ellswift_str);
921+
auto responder_ellswift = ParseHex(responder_ellswift_str);
922+
923+
CKey initiator_key = DecodeSecret(strSecret1);
924+
CKey responder_key = DecodeSecret(strSecret2C);
925+
926+
auto initiator_secret = initiator_key.ComputeBIP324ECDHSecret(MakeByteSpan(responder_ellswift), MakeByteSpan(initiator_ellswift), true);
927+
BOOST_CHECK(initiator_secret.has_value());
928+
auto responder_secret = responder_key.ComputeBIP324ECDHSecret(MakeByteSpan(initiator_ellswift), MakeByteSpan(responder_ellswift), false);
929+
BOOST_CHECK(responder_secret.has_value());
930+
BOOST_CHECK(initiator_secret.value() == responder_secret.value());
931+
BOOST_CHECK_EQUAL("85ac83c8b2cd328293d49b9ed999d9eff79847e767a6252dc17ae248b0040de0", HexStr(initiator_secret.value()));
932+
BOOST_CHECK_EQUAL("85ac83c8b2cd328293d49b9ed999d9eff79847e767a6252dc17ae248b0040de0", HexStr(responder_secret.value()));
933+
934+
BIP324Session initiator_session, responder_session;
935+
936+
DeriveBIP324Session(std::move(initiator_secret.value()), initiator_session);
937+
DeriveBIP324Session(std::move(responder_secret.value()), responder_session);
938+
939+
BOOST_CHECK(initiator_session.initiator_L == responder_session.initiator_L);
940+
BOOST_CHECK_EQUAL("6bb300568ba8c0e19d78a0615854748ca675448e402480f3f260a8ccf808335a", HexStr(initiator_session.initiator_L));
941+
942+
BOOST_CHECK(initiator_session.initiator_P == responder_session.initiator_P);
943+
BOOST_CHECK_EQUAL("128962f7dc651d92a9f4f4925bbf4a58f77624d80b9234171a9b7d1ab15f5c05", HexStr(initiator_session.initiator_P));
944+
945+
BOOST_CHECK(initiator_session.responder_L == responder_session.responder_L);
946+
BOOST_CHECK_EQUAL("e3a471e934b306015cb33727ccdc3c458960792d48d2207e14b5b0b88fd464c2", HexStr(initiator_session.responder_L));
947+
948+
BOOST_CHECK(initiator_session.responder_P == responder_session.responder_P);
949+
BOOST_CHECK_EQUAL("1b251c795df35bda9351f3b027834517974fc2a092b450e5bf99152ebf159746", HexStr(initiator_session.responder_P));
950+
951+
BOOST_CHECK(initiator_session.session_id == responder_session.session_id);
952+
BOOST_CHECK_EQUAL("e7047d2a41c8f040ea7f278fbf03e40b40d70ed3d555b6edb163d91af518cf6b", HexStr(initiator_session.session_id));
953+
954+
BOOST_CHECK(initiator_session.initiator_garbage_terminator == responder_session.initiator_garbage_terminator);
955+
BOOST_CHECK_EQUAL("00fdde2e0174d8abcfba3ed0c3d31600", HexStr(initiator_session.initiator_garbage_terminator));
956+
957+
BOOST_CHECK(initiator_session.responder_garbage_terminator == responder_session.responder_garbage_terminator);
958+
BOOST_CHECK_EQUAL("6fad393127f7a80c23e5e08d203dfe3d", HexStr(initiator_session.responder_garbage_terminator));
959+
960+
SelectParams(CBaseChainParams::REGTEST);
961+
}
962+
908963
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)