hsmd_wire: add HSM wire protocol support for secret type detection
What changed, and why it matters
This commit adds a small metadata field to the internal HSM (Hardware Security Module) initialization message so that lightningd can tell whether the wallet seed was created from a mnemonic phrase or from an older legacy secret. It is a protocol plumbing change, not a fix for a known vulnerability, and does not by itself change how secrets are generated, stored, or protected.
No immediate security action required. Review as part of normal code review; ensure downstream consumers of hsm_secret_type handle unknown/legacy values safely and that TLV parsing rejects invalid lengths.
Security signals we found
Adds new wire-protocol TLV field for HSM secret type metadata
Touches HSM initialization reply path
Does not alter secret generation, storage, or access-control logic
No mention of vulnerability, CVE, or security fix in commit message
Evidence from the diff
The patch extends the hsmd_init_reply_v4 wire message with an optional TLV field hsm_secret_type (type 1, u8). The HSM now includes the secret type when replying to lightningd during initialization, and lightningd parses the new TLV. The change is backward-compatible because TLV fields are optional and ignored if not understood. Supporting test stubs and a new HSM version capability hash are updated accordingly. No cryptographic operations, access controls, or secret-handling paths are modified.
Changed components
hsmd/hsmd.chsmd/libhsmd.chsmd/libhsmd.hhsmd/hsmd_wire.csvlightningd/hsm_control.ccommon/hsm_version.hInspect captured patch +25 / −14
diff --git a/common/hsm_version.h b/common/hsm_version.h
index 5b7df62..5e78238 100644
--- a/common/hsm_version.h
+++ b/common/hsm_version.h
@@ -31,6 +31,7 @@
* v6 with bip137_sign_message: 4bfe28b02e92aae276b8eca2228e32f32d5dee8d5381639e7364939fa2fa1370
* v6 with hsm_passphrase changes: c646d557d7561dd885df3cad5b99c82895cda4b040699f3853980ec61b2873fa
* v6 with hsm_secret struct cleanup: 06c56396fe42f4f47911d7f865dd0004d264fc1348f89547743755b6b33fec90
+ * v6 with hsm_secret_type TLV: 7bb5deb2367482feb084d304ee14b2373d42910ad56484fbf47614dbb3d4cb74
*/
#define HSM_MIN_VERSION 5
#define HSM_MAX_VERSION 6
diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c
index 1205d14..11bcfe2 100644
--- a/hsmd/hsmd.c
+++ b/hsmd/hsmd.c
@@ -554,7 +554,7 @@ static struct io_plan *init_hsm(struct io_conn *conn,
return req_reply(conn, c, hsmd_init(hsm_secret->secret_data,
tal_bytelen(hsm_secret->secret_data),
hsmd_mutual_version,
- bip32_key_version));
+ bip32_key_version, hsm_secret->type));
}
/*~ Since we process requests then service them in strict order, and because
diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv
index 95ef50d..6472147 100644
--- a/hsmd/hsmd_wire.csv
+++ b/hsmd/hsmd_wire.csv
@@ -46,6 +46,10 @@ msgdata,hsmd_init_reply_v4,hsm_capabilities,u32,num_hsm_capabilities
msgdata,hsmd_init_reply_v4,node_id,node_id,
msgdata,hsmd_init_reply_v4,bip32,ext_key,
msgdata,hsmd_init_reply_v4,bolt12,pubkey,
+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,
# HSM initialization failure response
msgtype,hsmd_init_reply_failure,115
diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c
index 1f1096b..bff142b 100644
--- a/hsmd/libhsmd.c
+++ b/hsmd/libhsmd.c
@@ -2450,7 +2450,7 @@ void bip86_key(struct privkey *privkey, struct pubkey *pubkey, u32 index)
}
u8 *hsmd_init(const u8 *secret_data, size_t secret_len, const u64 hsmd_version,
- struct bip32_key_version bip32_key_version)
+ struct bip32_key_version bip32_key_version, u8 hsm_secret_type)
{
u8 bip32_seed[BIP32_ENTROPY_LEN_256];
struct pubkey key, bolt12;
@@ -2608,8 +2608,13 @@ u8 *hsmd_init(const u8 *secret_data, size_t secret_len, const u64 hsmd_version,
* And version is 4: we offer limited compatibility (or at least,
* incompatibility detection) with alternate implementations.
*/
+ /* 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;
+
return take(towire_hsmd_init_reply_v4(
- NULL, hsmd_version, caps,
- &node_id, &secretstuff.bip32,
- &bolt12));
+ NULL, hsmd_version, caps,
+ &node_id, &secretstuff.bip32,
+ &bolt12, tlvs));
}
diff --git a/hsmd/libhsmd.h b/hsmd/libhsmd.h
index fe6aa47..484089d 100644
--- a/hsmd/libhsmd.h
+++ b/hsmd/libhsmd.h
@@ -47,7 +47,7 @@ struct hsmd_client {
* `lightningd`.
*/
u8 *hsmd_init(const u8 *secret_data, size_t secret_len, const u64 hsmd_version,
- struct bip32_key_version bip32_key_version);
+ struct bip32_key_version bip32_key_version, u8 hsm_secret_type);
struct hsmd_client *hsmd_client_new_main(const tal_t *ctx, u64 capabilities,
void *extra);
diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c
index ae13a08..f8be6cc 100644
--- a/lightningd/hsm_control.c
+++ b/lightningd/hsm_control.c
@@ -146,11 +146,12 @@ struct ext_key *hsm_init(struct lightningd *ld)
}
/* Check for successful init reply */
+ struct tlv_hsmd_init_reply_v4_tlvs *tlvs;
if (fromwire_hsmd_init_reply_v4(ld, msg,
&hsm_version,
&ld->hsm_capabilities,
&ld->our_nodeid, bip32_base,
- &unused)) {
+ &unused, &tlvs)) {
/* nothing to do. */
} else {
/* Unknown message type */
diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c
index 9953225..14a3998 100644
--- a/lightningd/test/run-find_my_abspath.c
+++ b/lightningd/test/run-find_my_abspath.c
@@ -12,6 +12,9 @@ int unused_main(int argc, char *argv[]);
/* Generated stub for begin_topology */
void begin_topology(struct chain_topology *topo UNNEEDED)
{ fprintf(stderr, "begin_topology called!\n"); abort(); }
+/* Generated stub for bip86_pubkey */
+void bip86_pubkey(struct lightningd *ld UNNEEDED, struct pubkey *pubkey UNNEEDED, u32 index UNNEEDED)
+{ fprintf(stderr, "bip86_pubkey called!\n"); abort(); }
/* Generated stub for channel_gossip_notify_new_block */
void channel_gossip_notify_new_block(struct lightningd *ld UNNEEDED)
{ fprintf(stderr, "channel_gossip_notify_new_block called!\n"); abort(); }
@@ -209,6 +212,9 @@ u8 *towire_hsmd_ecdh_req(const tal_t *ctx UNNEEDED, const struct pubkey *point U
void txfilter_add_derkey(struct txfilter *filter UNNEEDED,
const u8 derkey[PUBKEY_CMPR_LEN])
{ fprintf(stderr, "txfilter_add_derkey called!\n"); abort(); }
+/* Generated stub for txfilter_add_scriptpubkey */
+void txfilter_add_scriptpubkey(struct txfilter *filter UNNEEDED, const u8 *script TAKES UNNEEDED)
+{ fprintf(stderr, "txfilter_add_scriptpubkey called!\n"); abort(); }
/* Generated stub for txfilter_new */
struct txfilter *txfilter_new(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "txfilter_new called!\n"); abort(); }
@@ -227,12 +233,6 @@ struct wallet *wallet_new(struct lightningd *ld UNNEEDED, struct timers *timers
/* Generated stub for wallet_sanity_check */
bool wallet_sanity_check(struct wallet *w UNNEEDED)
{ fprintf(stderr, "wallet_sanity_check called!\n"); abort(); }
-/* Generated stub for bip86_pubkey */
-void bip86_pubkey(struct lightningd *ld UNNEEDED, struct pubkey *pubkey UNNEEDED, u32 index UNNEEDED)
-{ fprintf(stderr, "bip86_pubkey called!\n"); abort(); }
-/* Generated stub for txfilter_add_scriptpubkey */
-void txfilter_add_scriptpubkey(struct txfilter *filter UNNEEDED, const u8 *script TAKES UNNEEDED)
-{ fprintf(stderr, "txfilter_add_scriptpubkey called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */
struct logger *crashlog;
diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c
index d3087cc..a91ec6d 100644
--- a/wallet/test/run-wallet.c
+++ b/wallet/test/run-wallet.c
@@ -343,7 +343,7 @@ bool fromwire_hsmd_get_output_scriptpubkey_reply(const tal_t *ctx UNNEEDED, cons
bool fromwire_hsmd_init_reply_failure(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u32 *error_code UNNEEDED, wirestring **error_message UNNEEDED)
{ fprintf(stderr, "fromwire_hsmd_init_reply_failure called!\n"); abort(); }
/* Generated stub for fromwire_hsmd_init_reply_v4 */
-bool fromwire_hsmd_init_reply_v4(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u32 *hsm_version UNNEEDED, u32 **hsm_capabilities UNNEEDED, struct node_id *node_id UNNEEDED, struct ext_key *bip32 UNNEEDED, struct pubkey *bolt12 UNNEEDED)
+bool fromwire_hsmd_init_reply_v4(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u32 *hsm_version UNNEEDED, u32 **hsm_capabilities UNNEEDED, struct node_id *node_id UNNEEDED, struct ext_key *bip32 UNNEEDED, struct pubkey *bolt12 UNNEEDED, struct tlv_hsmd_init_reply_v4_tlvs **tlvs UNNEEDED)
{ fprintf(stderr, "fromwire_hsmd_init_reply_v4 called!\n"); abort(); }
/* Generated stub for fromwire_hsmd_new_channel_reply */
bool fromwire_hsmd_new_channel_reply(const void *p UNNEEDED)
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.