hsmd: capability addition: ability to check pubkeys.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2023-03-21 14:28:15 +10:30
parent e02f5f5bb8
commit 91a9cf3512
5 changed files with 57 additions and 1 deletions

View File

@@ -11,6 +11,7 @@
* v3: edd3d288fc88a5470adc2f99abcbfe4d4af29fae0c7a80b4226f28810a815524
* v3 without v1: 3f813898f7de490e9126ab817e1c9a29af79c0413d5e37068acedce3ea7b5429
* v4: 41a730986c51b930e2d8d12b3169d24966c2004e08d424bdda310edbbde5ba70
* v4 with check_pubkey: 48b3992745aa3c6ab6ce5cdaee9082cb7d70017f523d322015e9710bf49fd193
*/
#define HSM_MIN_VERSION 3
#define HSM_MAX_VERSION 4

View File

@@ -680,6 +680,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c)
case WIRE_HSMD_SIGN_LOCAL_HTLC_TX:
case WIRE_HSMD_SIGN_REMOTE_HTLC_TO_US:
case WIRE_HSMD_SIGN_DELAYED_PAYMENT_TO_US:
case WIRE_HSMD_CHECK_PUBKEY:
/* Hand off to libhsmd for processing */
return req_reply(conn, c,
take(hsmd_handle_client_message(
@@ -712,6 +713,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c)
case WIRE_HSMD_SIGN_BOLT12_REPLY:
case WIRE_HSMD_PREAPPROVE_INVOICE_REPLY:
case WIRE_HSMD_PREAPPROVE_KEYSEND_REPLY:
case WIRE_HSMD_CHECK_PUBKEY_REPLY:
return bad_req_fmt(conn, c, c->msg_in,
"Received an incoming message of type %s, "
"which is not a request",

View File

@@ -323,3 +323,12 @@ msgdata,hsmd_derive_secret,info,u8,len
# Reply with the derived secret
msgtype,hsmd_derive_secret_reply,127
msgdata,hsmd_derive_secret_reply,secret,secret,
# Sanity check this pubkey derivation is correct (unhardened only)
msgtype,hsmd_check_pubkey,28
msgdata,hsmd_check_pubkey,index,u32,
msgdata,hsmd_check_pubkey,pubkey,pubkey,
# Reply
msgtype,hsmd_check_pubkey_reply,128
msgdata,hsmd_check_pubkey_reply,ok,bool,
1 # Clients should not give a bad request but not the HSM's decision to crash.
323
324
325
326
327
328
329
330
331
332
333
334

View File

@@ -1,5 +1,6 @@
#include "config.h"
#include <bitcoin/script.h>
#include <ccan/array_size/array_size.h>
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
#include <ccan/tal/str/str.h>
#include <common/bolt12_merkle.h>
@@ -122,6 +123,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client,
case WIRE_HSMD_PREAPPROVE_INVOICE:
case WIRE_HSMD_PREAPPROVE_KEYSEND:
case WIRE_HSMD_DERIVE_SECRET:
case WIRE_HSMD_CHECK_PUBKEY:
return (client->capabilities & HSM_CAP_MASTER) != 0;
/*~ These are messages sent by the HSM so we should never receive them. */
@@ -154,6 +156,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client,
case WIRE_HSMD_PREAPPROVE_INVOICE_REPLY:
case WIRE_HSMD_PREAPPROVE_KEYSEND_REPLY:
case WIRE_HSMD_DERIVE_SECRET_REPLY:
case WIRE_HSMD_CHECK_PUBKEY_REPLY:
break;
}
return false;
@@ -533,6 +536,33 @@ static u8 *handle_sign_to_us_tx(struct hsmd_client *c, const u8 *msg_in,
return towire_hsmd_sign_tx_reply(NULL, &sig);
}
/* This will check lightningd's key derivation: hopefully any errors in
* this process are independent of errors in lightningd! */
static u8 *handle_check_pubkey(struct hsmd_client *c, const u8 *msg_in)
{
u32 index;
struct pubkey their_pubkey, our_pubkey;
struct privkey our_privkey;
if (!fromwire_hsmd_check_pubkey(msg_in, &index, &their_pubkey))
return hsmd_status_malformed_request(c, msg_in);
/* We abort if lightningd asks for a stupid index. */
bitcoin_key(&our_privkey, &our_pubkey, index);
if (!pubkey_eq(&our_pubkey, &their_pubkey)) {
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
"BIP32 derivation index %u differed:"
" they got %s, we got %s",
index,
type_to_string(tmpctx, struct pubkey,
&their_pubkey),
type_to_string(tmpctx, struct pubkey,
&our_pubkey));
}
return towire_hsmd_check_pubkey_reply(NULL, true);
}
/*~ lightningd asks us to sign a message. I tweeted the spec
* in https://twitter.com/rusty_twit/status/1182102005914800128:
*
@@ -1650,6 +1680,8 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client,
return handle_sign_delayed_payment_to_us(client, msg);
case WIRE_HSMD_DERIVE_SECRET:
return handle_derive_secret(client, msg);
case WIRE_HSMD_CHECK_PUBKEY:
return handle_check_pubkey(client, msg);
case WIRE_HSMD_DEV_MEMLEAK:
case WIRE_HSMD_ECDH_RESP:
@@ -1679,6 +1711,7 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client,
case WIRE_HSMD_SIGN_BOLT12_REPLY:
case WIRE_HSMD_PREAPPROVE_INVOICE_REPLY:
case WIRE_HSMD_PREAPPROVE_KEYSEND_REPLY:
case WIRE_HSMD_CHECK_PUBKEY_REPLY:
break;
}
return hsmd_status_bad_request(client, msg, "Unknown request");
@@ -1692,6 +1725,7 @@ u8 *hsmd_init(struct secret hsm_secret,
u32 salt = 0;
struct ext_key master_extkey, child_extkey;
struct node_id node_id;
static const u32 capabilities[] = { WIRE_HSMD_CHECK_PUBKEY };
/*~ Don't swap this. */
sodium_mlock(secretstuff.hsm_secret.data,
@@ -1822,6 +1856,10 @@ u8 *hsmd_init(struct secret hsm_secret,
* incompatibility detection) with alternate implementations.
*/
return take(towire_hsmd_init_reply_v4(
NULL, 4, NULL, &node_id, &secretstuff.bip32,
NULL, 4,
/* Capabilities arg needs to be a tal array */
tal_dup_arr(tmpctx, u32, capabilities,
ARRAY_SIZE(capabilities), 0),
&node_id, &secretstuff.bip32,
&bolt12));
}

View File

@@ -3096,3 +3096,9 @@ def test_checkmessage_pubkey_not_found(node_factory):
check_result = l1.rpc.checkmessage(msg, zbase, pubkey=pubkey)
assert check_result["pubkey"] == pubkey
assert check_result["verified"] is True
def test_hsm_capabilities(node_factory):
l1 = node_factory.get_node()
# This appears before the start message, so it'll already be present.
assert l1.daemon.is_in_log(r"hsmd: capability \+WIRE_HSMD_CHECK_PUBKEY")