From 121fcfc92b3e73c197a31c1e9868748e8f738675 Mon Sep 17 00:00:00 2001 From: Sangbida Chaudhuri <101164840+sangbida@users.noreply.github.com> Date: Fri, 24 Oct 2025 13:57:45 +1030 Subject: [PATCH] hsmd: add BIP86 wire scaffolding (derive/check), no behavior yet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Here's some *foreshadowing* for what's to come. Here's what we're aiming for with our derivation flow: Derivation split (hardened vs unhardened) ======================================== ┌───────────────┐ │ HSM │ (secrets live here) │ │ │ BIP39 → seed (64B) │ ↓ │ m/86'/0'/0' ← derive hardened base (private) │ ↓ (neuter) │ BIP86 base xpub ← public-only + chain code │ ↓ │ [send once over wire] └───────────────┘ │ ▼ ┌───────────────────────┐ │ lightningd / wallet │ │ │ │ local (unhardened) derivations: │ /0/i → external │ /1/i → change │ │ │ P2TR(BIP86) from pubkey_i │ (optionally: CHECK with HSM) └───────────────────────┘ We want to do part of the derivation inside hsmd and then send this base "pubkey" over the wire so our wallet can do the remaining derivation based on the address type and index. This lays the foundation for the base key wire message. --- hsmd/hsmd.c | 4 ++++ hsmd/hsmd_wire.csv | 16 ++++++++++++++++ hsmd/libhsmd.c | 14 ++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 0d7de8bca..63cbc0fb5 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -750,6 +750,8 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_SIGN_REMOTE_HTLC_TO_US: case WIRE_HSMD_SIGN_DELAYED_PAYMENT_TO_US: case WIRE_HSMD_CHECK_PUBKEY: + case WIRE_HSMD_DERIVE_BIP86_KEY: + case WIRE_HSMD_CHECK_BIP86_PUBKEY: case WIRE_HSMD_SIGN_ANY_PENALTY_TO_US: case WIRE_HSMD_SIGN_ANY_DELAYED_PAYMENT_TO_US: case WIRE_HSMD_SIGN_ANY_REMOTE_HTLC_TO_US: @@ -797,6 +799,8 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_PREAPPROVE_INVOICE_CHECK_REPLY: case WIRE_HSMD_PREAPPROVE_KEYSEND_CHECK_REPLY: case WIRE_HSMD_CHECK_PUBKEY_REPLY: + case WIRE_HSMD_DERIVE_BIP86_KEY_REPLY: + case WIRE_HSMD_CHECK_BIP86_PUBKEY_REPLY: case WIRE_HSMD_SIGN_ANCHORSPEND_REPLY: case WIRE_HSMD_SIGN_HTLC_TX_MINGLE_REPLY: case WIRE_HSMD_SIGN_ANY_CANNOUNCEMENT_REPLY: diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index cfca1e69e..95ef50dee 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -196,6 +196,14 @@ msgdata,hsmd_preapprove_keysend_check,check_only,bool, msgtype,hsmd_preapprove_keysend_check_reply,152 msgdata,hsmd_preapprove_keysend_check_reply,approved,bool, +# Derive BIP86 key using path m/86'/0'/0'/change/address_index +msgtype,hsmd_derive_bip86_key,54 +msgdata,hsmd_derive_bip86_key,index,u32, +msgdata,hsmd_derive_bip86_key,is_change,bool, + +msgtype,hsmd_derive_bip86_key_reply,154 +msgdata,hsmd_derive_bip86_key_reply,bip86_base,ext_key, + # Give me ECDH(node-id-secret,point) msgtype,hsmd_ecdh_req,1 msgdata,hsmd_ecdh_req,point,pubkey, @@ -447,6 +455,14 @@ msgdata,hsmd_check_pubkey,pubkey,pubkey, msgtype,hsmd_check_pubkey_reply,128 msgdata,hsmd_check_pubkey_reply,ok,bool, +# Sanity check this BIP86 pubkey derivation is correct (unhardened only) +msgtype,hsmd_check_bip86_pubkey,56 +msgdata,hsmd_check_bip86_pubkey,index,u32, +msgdata,hsmd_check_bip86_pubkey,pubkey,pubkey, + +msgtype,hsmd_check_bip86_pubkey_reply,156 +msgdata,hsmd_check_bip86_pubkey_reply,ok,bool, + msgtype,hsmd_sign_anchorspend,147 msgdata,hsmd_sign_anchorspend,peerid,node_id, msgdata,hsmd_sign_anchorspend,channel_dbid,u64, diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index 2adeed11a..51cb238f7 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -154,6 +154,8 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_PREAPPROVE_KEYSEND_CHECK: case WIRE_HSMD_DERIVE_SECRET: case WIRE_HSMD_CHECK_PUBKEY: + case WIRE_HSMD_DERIVE_BIP86_KEY: + case WIRE_HSMD_CHECK_BIP86_PUBKEY: case WIRE_HSMD_SIGN_ANY_PENALTY_TO_US: case WIRE_HSMD_SIGN_ANY_DELAYED_PAYMENT_TO_US: case WIRE_HSMD_SIGN_ANY_REMOTE_HTLC_TO_US: @@ -202,6 +204,8 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_PREAPPROVE_KEYSEND_CHECK_REPLY: case WIRE_HSMD_DERIVE_SECRET_REPLY: case WIRE_HSMD_CHECK_PUBKEY_REPLY: + case WIRE_HSMD_DERIVE_BIP86_KEY_REPLY: + case WIRE_HSMD_CHECK_BIP86_PUBKEY_REPLY: case WIRE_HSMD_SIGN_ANCHORSPEND_REPLY: case WIRE_HSMD_SIGN_HTLC_TX_MINGLE_REPLY: case WIRE_HSMD_SIGN_ANY_CANNOUNCEMENT_REPLY: @@ -2283,6 +2287,13 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, return handle_derive_secret(client, msg); case WIRE_HSMD_CHECK_PUBKEY: return handle_check_pubkey(client, msg); + case WIRE_HSMD_DERIVE_BIP86_KEY: + case WIRE_HSMD_CHECK_BIP86_PUBKEY: + /* Not implemented yet */ + return hsmd_status_bad_request_fmt( + client, msg, + "Message of type %s not implemented yet", + hsmd_wire_name(fromwire_peektype(msg))); case WIRE_HSMD_SIGN_ANY_DELAYED_PAYMENT_TO_US: return handle_sign_any_delayed_payment_to_us(client, msg); case WIRE_HSMD_SIGN_ANY_REMOTE_HTLC_TO_US: @@ -2332,7 +2343,9 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, case WIRE_HSMD_PREAPPROVE_KEYSEND_REPLY: case WIRE_HSMD_PREAPPROVE_INVOICE_CHECK_REPLY: case WIRE_HSMD_PREAPPROVE_KEYSEND_CHECK_REPLY: + case WIRE_HSMD_DERIVE_BIP86_KEY_REPLY: case WIRE_HSMD_CHECK_PUBKEY_REPLY: + case WIRE_HSMD_CHECK_BIP86_PUBKEY_REPLY: case WIRE_HSMD_SIGN_ANCHORSPEND_REPLY: case WIRE_HSMD_SIGN_HTLC_TX_MINGLE_REPLY: case WIRE_HSMD_SIGN_ANY_CANNOUNCEMENT_REPLY: @@ -2351,6 +2364,7 @@ u8 *hsmd_init(const u8 *secret_data, size_t secret_len, const u64 hsmd_version, struct node_id node_id; static const u32 capabilities[] = { WIRE_HSMD_CHECK_PUBKEY, + WIRE_HSMD_CHECK_BIP86_PUBKEY, WIRE_HSMD_SIGN_ANY_DELAYED_PAYMENT_TO_US, WIRE_HSMD_SIGN_ANCHORSPEND, WIRE_HSMD_SIGN_HTLC_TX_MINGLE,