wallet/hsmd: fix signmessagewithkey for BIP86 wallets
What changed, and why it matters
This commit fixes a bug where the `signmessagewithkey` RPC command did not work for newer BIP86-style wallets. Previously, the wallet RPC only searched through older BIP32-derived addresses, and the HSM signing code always used BIP32 key derivation. The fix makes both the wallet lookup and the HSM signing aware of BIP86 derivation, so users can sign messages with keys from BIP86 wallets. This is a functionality bug, not a clear security vulnerability, but it could have caused user confusion or application failures.
Review and merge the patch. After deployment, verify that `signmessagewithkey` works for both BIP32 and BIP86 wallets and produces valid BIP137 signatures. No urgent security response is indicated, but regression tests for both wallet types should be added if not already present.
Security signals we found
Functional bug in key derivation path selection
BIP86 wallet support added to message signing RPC
HSM now derives signing key according to wallet type
No explicit security impact described by vendor
Evidence from the diff
The signmessagewithkey RPC in wallet/walletrpc.c previously iterated only over BIP32-derived addresses using bip32_pubkey and bip32_max_index, so it could not locate BIP86-derived addresses. Additionally, hsmd/libhsmd.c’s handle_bip137_sign_message always called bitcoin_key() (BIP32) regardless of wallet type. The patch replaces the BIP32-only search with wallet_can_spend, which handles both BIP32 and BIP86 addresses, and derives the public key using bip86_pubkey when cmd->ld->bip86_base is set. In the HSM, it uses use_bip86_derivation() to choose between bip86_key() and bitcoin_key(). A test setup in wallet/test/run-wallet.c initializes ld->bip86_base = NULL to avoid undefined behavior.
Changed components
wallet/walletrpc.chsmd/libhsmd.cwallet/test/run-wallet.cInspect captured patch +18 / −27
diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c
index 4666012..c98f13c 100644
--- a/hsmd/libhsmd.c
+++ b/hsmd/libhsmd.c
@@ -767,8 +767,12 @@ static u8 *handle_bip137_sign_message(struct hsmd_client *c, const u8 *msg_in)
sha256_update(&sctx, msg, msg_len);
sha256_double_done(&sctx, &shad);
- /* get the private key BIP32 */
- bitcoin_key(&privkey, &pubkey, keyidx);
+ /* Get the private key using appropriate derivation method */
+ if (use_bip86_derivation(tal_bytelen(secretstuff.bip32_seed))) {
+ bip86_key(&privkey, &pubkey, keyidx);
+ } else {
+ bitcoin_key(&privkey, &pubkey, keyidx);
+ }
if (!secp256k1_ecdsa_sign_recoverable(
secp256k1_ctx, &rsig, shad.sha.u.u8, privkey.secret.data, NULL,
diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c
index 69b1414..86962e5 100644
--- a/wallet/test/run-wallet.c
+++ b/wallet/test/run-wallet.c
@@ -911,6 +911,7 @@ static struct wallet *create_test_wallet(struct lightningd *ld, const tal_t *ctx
w->ld = ld;
ld->wallet = w;
+ ld->bip86_base = NULL;
ld->bip32_base = tal(ld, struct ext_key);
CHECK(bip32_key_from_seed(badseed, sizeof(badseed),
BIP32_VER_TEST_PRIVATE, 0,
diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c
index 92f979a..0a53086 100644
--- a/wallet/walletrpc.c
+++ b/wallet/walletrpc.c
@@ -1282,38 +1282,24 @@ json_signmessagewithkey(struct command *cmd, const char *buffer,
"HSM does not support signing BIP137 signing.");
}
- const u32 bip32_max_index =
- db_get_intvar(cmd->ld->wallet->db, "bip32_max_index", 0);
- bool match_found = false;
u32 keyidx;
- enum addrtype addrtype;
+ enum addrtype addrtype;
- /* loop over all generated keys, find a matching key */
- for (keyidx = 1; keyidx <= bip32_max_index; keyidx++) {
- bip32_pubkey(cmd->ld, &pubkey, keyidx);
- u8 *redeemscript_p2wpkh;
- char *out_p2wpkh = encode_pubkey_to_addr(
- cmd, &pubkey, ADDR_BECH32, &redeemscript_p2wpkh);
- if (!out_p2wpkh) {
- abort();
- }
- /* wallet_get_addrtype fails for entries prior to v24.11, all
- * address types are assumed in that case. */
- if (!wallet_get_addrtype(cmd->ld->wallet, keyidx, &addrtype))
- addrtype = ADDR_ALL;
- if (streq(addr, out_p2wpkh) &&
- (addrtype == ADDR_BECH32 || addrtype == ADDR_ALL)) {
- match_found = true;
- break;
- }
- }
-
- if (!match_found) {
+ /* Use wallet_can_spend which handles both BIP32 and BIP86 addresses */
+ if (!wallet_can_spend(cmd->ld->wallet, scriptpubkey, script_len,
+ &keyidx, &addrtype)) {
return command_fail(
cmd, JSONRPC2_INVALID_PARAMS,
"Address is not found in the wallet's database");
}
+ /* Derive the pubkey for the found key index */
+ if (cmd->ld->bip86_base) {
+ bip86_pubkey(cmd->ld, &pubkey, keyidx);
+ } else {
+ bip32_pubkey(cmd->ld, &pubkey, keyidx);
+ }
+
/* wire to hsmd a sign request */
u8 *msg = towire_hsmd_bip137_sign_message(
cmd, tal_dup_arr(tmpctx, u8, (u8 *)message, strlen(message), 0),
Why this scored 28/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.