key: add DeriveExtKey() helper

Co-authored-by: w0xlt <94266259+w0xlt@users.noreply.github.com>
This commit is contained in:
Sjors Provoost
2025-06-20 10:13:31 +02:00
parent 71c06c5cbc
commit dab525eb77
3 changed files with 51 additions and 0 deletions

View File

@@ -16,6 +16,8 @@
#include <secp256k1_recovery.h>
#include <secp256k1_schnorrsig.h>
#include <algorithm>
static secp256k1_context* secp256k1_context_sign = nullptr;
/** These functions are taken from the libsecp256k1 distribution and are very ugly. */
@@ -364,6 +366,18 @@ bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
return key.Derive(out.key, out.chaincode, _nChild, chaincode);
}
std::optional<std::pair<CExtKey, KeyOriginInfo>> DeriveExtKey(const CExtKey& ext_key, const std::vector<uint32_t>& path)
{
CExtKey descendant = ext_key;
KeyOriginInfo origin;
origin.fingerprint = ext_key.id_key_fingerprint();
origin.path = path;
for (uint32_t i : path) {
if (!descendant.Derive(descendant, i)) return std::nullopt;
}
return std::make_pair(descendant, origin);
}
void CExtKey::SetSeed(std::span<const std::byte> seed)
{
Assert(16 <= seed.size() && seed.size() <= 64);

View File

@@ -8,11 +8,14 @@
#define BITCOIN_KEY_H
#include <pubkey.h>
#include <script/keyorigin.h>
#include <serialize.h>
#include <support/allocators/secure.h>
#include <uint256.h>
#include <optional>
#include <stdexcept>
#include <utility>
#include <vector>
struct secp256k1_context_struct;
@@ -257,6 +260,12 @@ struct CExtKey {
void SetSeed(std::span<const std::byte> seed);
};
//! Get extended key and origin info for a given path
//! @param[in] ext_key The extended private key to derive from
//! @param[in] path The BIP 32 path
//! @return the resulting extended private key and origin info
std::optional<std::pair<CExtKey, KeyOriginInfo>> DeriveExtKey(const CExtKey& ext_key, const std::vector<uint32_t>& path);
/** KeyPair
*
* Wraps a `secp256k1_keypair` type, an opaque data structure for holding a secret and public key.

View File

@@ -184,6 +184,34 @@ BOOST_AUTO_TEST_CASE(bip32_test5) {
}
}
BOOST_AUTO_TEST_CASE(bip32_derive_ext_key)
{
const CExtKey master{DecodeExtKey(test1.vDerive[0].prv)};
const std::vector<uint32_t> path{test1.vDerive[0].nChild, test1.vDerive[1].nChild};
const auto derived{DeriveExtKey(master, path)};
BOOST_REQUIRE(derived);
BOOST_CHECK(EncodeExtKey(derived->first) == test1.vDerive[2].prv);
KeyOriginInfo expected_origin;
expected_origin.fingerprint = master.id_key_fingerprint();
expected_origin.path = path;
BOOST_CHECK(derived->second == expected_origin);
const auto root{DeriveExtKey(master, {})};
BOOST_REQUIRE(root);
BOOST_CHECK(root->first == master);
expected_origin.path.clear();
BOOST_CHECK(root->second == expected_origin);
CExtKey max_depth{master};
for (auto i{0}; i++ < 255;) {
CExtKey next_key;
BOOST_REQUIRE(max_depth.Derive(next_key, 0));
max_depth = next_key;
}
BOOST_CHECK(!DeriveExtKey(max_depth, {0}));
}
BOOST_AUTO_TEST_CASE(bip32_max_depth) {
CExtKey key_parent{DecodeExtKey(test1.vDerive[0].prv)}, key_child;
CExtPubKey pubkey_parent{DecodeExtPubKey(test1.vDerive[0].pub)}, pubkey_child;