Skip to content

Commit

Permalink
HKDF key derivation from ECDH secret for BIP324
Browse files Browse the repository at this point in the history
  • Loading branch information
dhruv committed Nov 24, 2021
1 parent b5be6c1 commit 271b390
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
41 changes: 41 additions & 0 deletions src/crypto/bip324.cpp
Original file line number Diff line number Diff line change
@@ -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 <crypto/bip324.h>

#include <chainparams.h>
#include <crypto/hkdf_sha256_32.h>
#include <support/cleanse.h>

bool derive_bip324_keys(ECDHSecret&& ecdh_secret, const Span<uint8_t> initiator_hdata, const Span<uint8_t> 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<const char*>(initiator_hdata.data()), initiator_hdata.size()};
salt += std::string{reinterpret_cast<const char*>(responder_hdata.data()), responder_hdata.size()};
salt += std::string{reinterpret_cast<const char*>(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;
}
29 changes: 29 additions & 0 deletions src/crypto/bip324.h
Original file line number Diff line number Diff line change
@@ -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 <key.h>
#include <span.h>
#include <support/allocators/secure.h>

#include <vector>

constexpr size_t BIP324_KEY_LEN = 32;

using BIP324Key = std::vector<uint8_t, secure_allocator<uint8_t>>;

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<uint8_t> initiator_hdata, const Span<uint8_t> responder_hdata, BIP324Keys& derived_keys);

#endif // BITCOIN_CRYPTO_BIP324_H
79 changes: 79 additions & 0 deletions src/test/bip324_derivation_tests.cpp
Original file line number Diff line number Diff line change
@@ -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 <crypto/bip324.h>

#include <key.h>
#include <key_io.h>
#include <span.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>

#include <boost/test/unit_test.hpp>

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<uint8_t> initiator_hdata_span{initiator_hdata_vec.data(), initiator_hdata_vec.size()};

auto responder_hdata_vec = ParseHex(responder_hdata);
Span<uint8_t> 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()

0 comments on commit 271b390

Please sign in to comment.