From 32bb0ed20e8a0208057fc958aac5f37e527cc5d2 Mon Sep 17 00:00:00 2001 From: Sangbida Chaudhuri <101164840+sangbida@users.noreply.github.com> Date: Fri, 24 Oct 2025 13:57:52 +1030 Subject: [PATCH] hsmd_wire: remove hsmd_derive_bip86_key and add it to hsmd_init_reply_v4 Instead of having a separate field to derive the bip86 base key, we return it in the hsmd init reply once we know that the hsm_secret is of mnemonic type --- common/hsm_version.h | 1 + hsmd/hsmd.c | 31 ------------------------------- hsmd/hsmd_wire.csv | 11 +++-------- hsmd/libhsmd.c | 36 ++++++++++-------------------------- lightningd/hsm_control.c | 22 +++------------------- wallet/test/run-wallet.c | 8 -------- 6 files changed, 17 insertions(+), 92 deletions(-) diff --git a/common/hsm_version.h b/common/hsm_version.h index 5e782383c..8b6c54bdf 100644 --- a/common/hsm_version.h +++ b/common/hsm_version.h @@ -32,6 +32,7 @@ * v6 with hsm_passphrase changes: c646d557d7561dd885df3cad5b99c82895cda4b040699f3853980ec61b2873fa * v6 with hsm_secret struct cleanup: 06c56396fe42f4f47911d7f865dd0004d264fc1348f89547743755b6b33fec90 * v6 with hsm_secret_type TLV: 7bb5deb2367482feb084d304ee14b2373d42910ad56484fbf47614dbb3d4cb74 + * v6 with bip86_base in TLV: 6bb6e6ee256f22a6fb41856c90feebde3065a9074e79a46731e453a932be83f0 */ #define HSM_MIN_VERSION 5 #define HSM_MAX_VERSION 6 diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 7266b4f1c..708fa84b3 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -680,34 +680,6 @@ void hsmd_status_failed(enum status_failreason reason, const char *fmt, ...) status_send_fatal(take(towire_status_fail(NULL, reason, str))); } -/* Handle BIP86 key derivation request */ -static struct io_plan *handle_derive_bip86_key(struct io_conn *conn, - struct client *c, - const u8 *msg_in) -{ - u8 *reply; - u32 index; - bool is_change; - - /* Extract parameters from the wire message */ - if (!fromwire_hsmd_derive_bip86_key(msg_in, &index, &is_change)) - return bad_req(conn, c, msg_in); - - /* Check if we have a mnemonic-based HSM secret */ - if (!use_bip86_derivation(tal_bytelen(hsm_secret->secret_data))) { - return bad_req_fmt(conn, c, msg_in, - "BIP86 derivation requires mnemonic-based HSM secret"); - } - - /* Derive only the BIP86 base key (m/86'/0'/0') */ - struct ext_key bip86_base; - derive_bip86_base_key(&bip86_base); - - /* Return the full BIP86 base extended key */ - reply = towire_hsmd_derive_bip86_key_reply(NULL, &bip86_base); - return req_reply(conn, c, take(reply)); -} - /* Handle BIP86 pubkey check request */ static struct io_plan *handle_check_bip86_pubkey(struct io_conn *conn, struct client *c, @@ -768,8 +740,6 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_CLIENT_HSMFD: return pass_client_hsmfd(conn, c, c->msg_in); - case WIRE_HSMD_DERIVE_BIP86_KEY: - return handle_derive_bip86_key(conn, c, c->msg_in); case WIRE_HSMD_CHECK_BIP86_PUBKEY: return handle_check_bip86_pubkey(conn, c, c->msg_in); @@ -864,7 +834,6 @@ 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: diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index 6472147fe..7336feef4 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -34,6 +34,7 @@ tlvtype,hsmd_init_tlvs,hsm_passphrase,1 tlvdata,hsmd_init_tlvs,hsm_passphrase,passphrase,wirestring, #include +#include # Sorry: I should have put version in v2 :( msgtype,hsmd_init_reply_v4,114 # This gets upgraded when the wire protocol changes in incompatible @@ -50,6 +51,8 @@ msgdata,hsmd_init_reply_v4,tlvs,hsmd_init_reply_v4_tlvs, # TLV to indicate HSM secret type tlvtype,hsmd_init_reply_v4_tlvs,hsm_secret_type,1 tlvdata,hsmd_init_reply_v4_tlvs,hsm_secret_type,hsm_type,u8, +tlvtype,hsmd_init_reply_v4_tlvs,bip86_base,2 +tlvdata,hsmd_init_reply_v4_tlvs,bip86_base,base,ext_key, # HSM initialization failure response msgtype,hsmd_init_reply_failure,115 @@ -200,14 +203,6 @@ 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, diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index b362c812a..5488302e9 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -138,7 +138,6 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_LOCK_OUTPOINT: return (client->capabilities & HSM_PERM_LOCK_OUTPOINT) != 0; - case WIRE_HSMD_DERIVE_BIP86_KEY: case WIRE_HSMD_CHECK_BIP86_PUBKEY: case WIRE_HSMD_INIT: case WIRE_HSMD_DEV_PREINIT: @@ -209,7 +208,6 @@ 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: @@ -542,26 +540,9 @@ static void hsm_key_for_utxo(struct privkey *privkey, struct pubkey *pubkey, hsmd_status_debug("Derived public key %s from unilateral close", fmt_pubkey(tmpctx, pubkey)); } else { - /* Check if this is a BIP86 UTXO by examining the scriptPubkey */ - const size_t script_len = tal_bytelen(utxo->scriptPubkey); - bool is_bip86 = false; - - /* For P2TR scripts, we need to determine if it's BIP86 or regular P2TR - * But BIP86 derivation requires mnemonic-based secrets */ - if (is_p2tr(utxo->scriptPubkey, script_len, NULL) && - use_bip86_derivation(tal_bytelen(secretstuff.bip32_seed))) { - /* Try BIP86 derivation first and see if it matches */ - struct pubkey test_pubkey; - bip86_key(NULL, &test_pubkey, utxo->keyindex); - - /* Create P2TR scriptpubkey from BIP86 key and compare */ - const u8 *bip86_script = scriptpubkey_p2tr(tmpctx, &test_pubkey); - if (memeq(utxo->scriptPubkey, script_len, bip86_script, tal_bytelen(bip86_script))) { - is_bip86 = true; - } - } - - if (is_bip86) { + /* Modern HSMs use bip86 for p2tr. */ + if (is_p2tr(utxo->scriptPubkey, tal_bytelen(utxo->scriptPubkey), NULL) + && use_bip86_derivation(tal_bytelen(secretstuff.bip32_seed))) { /* Use BIP86 derivation */ bip86_key(privkey, pubkey, utxo->keyindex); } else { @@ -2316,7 +2297,6 @@ 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: /* This should be handled by hsmd.c, not libhsmd */ return hsmd_status_bad_request_fmt( @@ -2373,7 +2353,6 @@ 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: @@ -2610,8 +2589,13 @@ u8 *hsmd_init(const u8 *secret_data, size_t secret_len, const u64 hsmd_version, */ /* Create TLV with HSM secret type */ struct tlv_hsmd_init_reply_v4_tlvs *tlvs = tlv_hsmd_init_reply_v4_tlvs_new(tmpctx); - tlvs->hsm_secret_type = tal(tlvs, u8); - *tlvs->hsm_secret_type = hsm_secret_type; + tlvs->hsm_secret_type = tal_dup(tlvs, u8, &hsm_secret_type); + + /* If we have a mnemonic-based HSM, include the BIP86 base key */ + if (use_bip86_derivation(tal_bytelen(secretstuff.bip32_seed))) { + tlvs->bip86_base = tal(tlvs, struct ext_key); + derive_bip86_base_key(tlvs->bip86_base); + } return take(towire_hsmd_init_reply_v4( NULL, hsmd_version, caps, diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index 182cbe0d4..1f0a2192f 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -190,25 +190,9 @@ struct ext_key *hsm_init(struct lightningd *ld) } /* Check if we have a mnemonic-based HSM secret from TLV */ - bool is_mnemonic_secret = false; - if (tlvs && tlvs->hsm_secret_type) { - u8 secret_type = *tlvs->hsm_secret_type; - is_mnemonic_secret = (secret_type == 2 || secret_type == 3); /* HSM_SECRET_MNEMONIC_NO_PASS or HSM_SECRET_MNEMONIC_WITH_PASS */ - } - - if (is_mnemonic_secret) { - /* Try to get BIP86 base key from HSM (works only for mnemonic secrets) */ - ld->bip86_base = tal(ld, struct ext_key); - msg = towire_hsmd_derive_bip86_key(NULL, 0, false); - const u8 *reply = hsm_sync_req(tmpctx, ld, take(msg)); - if (fromwire_hsmd_derive_bip86_key_reply(reply, ld->bip86_base)) { - /* BIP86 derivation succeeded */ - log_info(ld->log, "Using BIP86 for new addresses, BIP32 for channels (mnemonic HSM secret)"); - /* Keep bip32_base for channel operations, database, etc. */ - } else { - /* BIP86 derivation failed unexpectedly */ - ld->bip86_base = tal_free(ld->bip86_base); - } + if (tlvs->bip86_base) { + ld->bip86_base = tal_steal(ld, tlvs->bip86_base); + log_info(ld->log, "Using BIP86 for new addresses, BIP32 for channels (mnemonic HSM secret)"); } else { /* Legacy HSM secret - don't attempt BIP86 derivation */ log_info(ld->log, "Using BIP32 derivation for all operations (legacy HSM secret)"); diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index a91ec6dbb..2c5454b35 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -796,14 +796,6 @@ u8 *towire_hsmd_get_channel_basepoints(const tal_t *ctx UNNEEDED, const struct n { return NULL; } -u8 *towire_hsmd_derive_bip86_key(const tal_t *ctx UNNEEDED, u32 index UNNEEDED, bool is_change UNNEEDED) -{ - return NULL; -} -bool fromwire_hsmd_derive_bip86_key_reply(const void *p UNNEEDED, struct ext_key *bip86_base UNNEEDED) -{ - return true; -} bool wire_sync_write(int fd UNNEEDED, const void *msg TAKES UNNEEDED) { return true;