From dfaf74d9727f92d17343a002486859c67ef88ccd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 23 Jul 2018 11:53:02 +0930 Subject: [PATCH] hsmd: add routines to sign onchain transactions, part 1. This handles the "to-us" transactions which return funds to the wallet. Signed-off-by: Rusty Russell --- gossipd/Makefile | 2 +- hsmd/capabilities.h | 1 + hsmd/hsm.c | 183 +++++++++++++++++++++++++++++++++++++++ hsmd/hsm_client_wire_csv | 26 ++++++ lightningd/Makefile | 2 +- 5 files changed, 212 insertions(+), 2 deletions(-) diff --git a/gossipd/Makefile b/gossipd/Makefile index 77728c1ef..0e02fc841 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -76,7 +76,7 @@ $(LIGHTNINGD_GOSSIP_CONTROL_OBJS) : $(LIGHTNINGD_GOSSIP_CONTROL_HEADERS) gossipd-all: lightningd/lightning_gossipd $(LIGHTNINGD_GOSSIP_CLIENT_OBJS) -lightningd/lightning_gossipd: $(LIGHTNINGD_GOSSIP_OBJS) $(GOSSIPD_COMMON_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) +lightningd/lightning_gossipd: $(LIGHTNINGD_GOSSIP_OBJS) $(GOSSIPD_COMMON_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(LIGHTNINGD_HSM_CLIENT_OBJS) gossipd/gen_gossip_wire.h: $(WIRE_GEN) gossipd/gossip_wire.csv $(WIRE_GEN) --header $@ gossip_wire_type < gossipd/gossip_wire.csv > $@ diff --git a/hsmd/capabilities.h b/hsmd/capabilities.h index fa1398fe4..629fbff36 100644 --- a/hsmd/capabilities.h +++ b/hsmd/capabilities.h @@ -3,6 +3,7 @@ #define HSM_CAP_ECDH 1 #define HSM_CAP_SIGN_GOSSIP 2 +#define HSM_CAP_SIGN_ONCHAIN_TX 4 #define HSM_CAP_MASTER 1024 #endif /* LIGHTNING_HSMD_CAPABILITIES_H */ diff --git a/hsmd/hsm.c b/hsmd/hsm.c index ee22e51b3..c36379402 100644 --- a/hsmd/hsm.c +++ b/hsmd/hsm.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -315,6 +316,172 @@ static struct io_plan *handle_sign_commitment_tx(struct io_conn *conn, return daemon_conn_read_next(conn, dc); } +static PRINTF_FMT(3,4) + struct io_plan *bad_sign_request(struct io_conn *conn, + struct client *c, + const char *fmt, ...) +{ + va_list ap; + char *str; + + va_start(ap, fmt); + str = tal_fmt(tmpctx, fmt, ap); + status_broken("Client %s bad sign request: %s", + type_to_string(tmpctx, struct pubkey, &c->id), str); + va_end(ap); + + daemon_conn_send(c->master, + take(towire_hsmstatus_client_bad_request(NULL, + &c->id, + c->dc.msg_in))); + return io_close(conn); +} + +/* FIXME: Derive output address for this client, and check it here! */ +static struct io_plan *handle_sign_to_us_tx(struct io_conn *conn, + struct client *c, + struct bitcoin_tx *tx, + const struct privkey *privkey, + const u8 *wscript, + u64 input_amount) +{ + secp256k1_ecdsa_signature sig; + struct pubkey pubkey; + + if (!pubkey_from_privkey(privkey, &pubkey)) + return bad_sign_request(conn, c, "bad pubkey_from_privkey"); + + if (tal_count(tx->input) != 1) + return bad_sign_request(conn, c, "bad txinput count"); + + tx->input[0].amount = tal_dup(tx->input, u64, &input_amount); + sign_tx_input(tx, 0, NULL, wscript, privkey, &pubkey, &sig); + + daemon_conn_send(&c->dc, take(towire_hsm_sign_tx_reply(NULL, &sig))); + return daemon_conn_read_next(conn, &c->dc); +} + +static struct io_plan *handle_sign_delayed_payment_to_us(struct io_conn *conn, + struct client *c) +{ + u64 commit_num, input_amount; + struct secret channel_seed, basepoint_secret; + struct pubkey basepoint; + struct bitcoin_tx *tx; + struct sha256 shaseed; + struct pubkey per_commitment_point; + struct privkey privkey; + u8 *wscript; + + if (!fromwire_hsm_sign_delayed_payment_to_us(tmpctx, c->dc.msg_in, + &commit_num, + &tx, &wscript, + &input_amount)) + return bad_sign_request(conn, c, + "malformed hsm_sign_delayed_payment"); + + get_channel_seed(&c->id, c->dbid, &channel_seed); + + if (!derive_shaseed(&channel_seed, &shaseed)) + return bad_sign_request(conn, c, "bad derive_shaseed"); + + if (!per_commit_point(&shaseed, &per_commitment_point, commit_num)) + return bad_sign_request(conn, c, + "bad per_commitment_point %"PRIu64, + commit_num); + + if (!derive_delayed_payment_basepoint(&channel_seed, + &basepoint, + &basepoint_secret)) + return bad_sign_request(conn, c, "failed deriving basepoint"); + + if (!derive_simple_privkey(&basepoint_secret, + &basepoint, + &per_commitment_point, + &privkey)) + return bad_sign_request(conn, c, "failed deriving privkey"); + + return handle_sign_to_us_tx(conn, c, tx, &privkey, wscript, + input_amount); +} + +static struct io_plan *handle_sign_remote_htlc_to_us(struct io_conn *conn, + struct client *c) +{ + u64 input_amount; + struct secret channel_seed, htlc_basepoint_secret; + struct pubkey htlc_basepoint; + struct bitcoin_tx *tx; + struct pubkey remote_per_commitment_point; + struct privkey privkey; + u8 *wscript; + + if (!fromwire_hsm_sign_remote_htlc_to_us(tmpctx, c->dc.msg_in, + &remote_per_commitment_point, + &tx, &wscript, + &input_amount)) + return bad_sign_request(conn, c, + "malformed hsm_sign_remote_htlc_to_us"); + + get_channel_seed(&c->id, c->dbid, &channel_seed); + + if (!derive_htlc_basepoint(&channel_seed, &htlc_basepoint, + &htlc_basepoint_secret)) + return bad_sign_request(conn, c, + "Failed derive_htlc_basepoint"); + + if (!derive_simple_privkey(&htlc_basepoint_secret, + &htlc_basepoint, + &remote_per_commitment_point, + &privkey)) + return bad_sign_request(conn, c, + "Failed deriving htlc privkey"); + + return handle_sign_to_us_tx(conn, c, tx, &privkey, wscript, + input_amount); +} + +static struct io_plan *handle_sign_penalty_to_us(struct io_conn *conn, + struct client *c) +{ + u64 input_amount; + struct secret channel_seed, revocation_secret, revocation_basepoint_secret; + struct pubkey revocation_basepoint; + struct bitcoin_tx *tx; + struct pubkey point; + struct privkey privkey; + u8 *wscript; + + if (!fromwire_hsm_sign_penalty_to_us(tmpctx, c->dc.msg_in, + &revocation_secret, + &tx, &wscript, + &input_amount)) + return bad_sign_request(conn, c, + "malformed hsm_sign_penalty_to_us"); + + if (!pubkey_from_secret(&revocation_secret, &point)) + return bad_sign_request(conn, c, + "Failed deriving pubkey"); + + get_channel_seed(&c->id, c->dbid, &channel_seed); + if (!derive_revocation_basepoint(&channel_seed, + &revocation_basepoint, + &revocation_basepoint_secret)) + return bad_sign_request(conn, c, + "Failed deriving revocation basepoint"); + + if (!derive_revocation_privkey(&revocation_basepoint_secret, + &revocation_secret, + &revocation_basepoint, + &point, + &privkey)) + return bad_sign_request(conn, c, + "Failed deriving revocation privkey"); + + return handle_sign_to_us_tx(conn, c, tx, &privkey, wscript, + input_amount); +} + static bool check_client_capabilities(struct client *client, enum hsm_client_wire_type t) { @@ -327,6 +494,11 @@ static bool check_client_capabilities(struct client *client, case WIRE_HSM_NODE_ANNOUNCEMENT_SIG_REQ: return (client->capabilities & HSM_CAP_SIGN_GOSSIP) != 0; + case WIRE_HSM_SIGN_DELAYED_PAYMENT_TO_US: + case WIRE_HSM_SIGN_REMOTE_HTLC_TO_US: + case WIRE_HSM_SIGN_PENALTY_TO_US: + return (client->capabilities & HSM_CAP_SIGN_ONCHAIN_TX) != 0; + case WIRE_HSM_INIT: case WIRE_HSM_CLIENT_HSMFD: case WIRE_HSM_SIGN_FUNDING: @@ -347,6 +519,7 @@ static bool check_client_capabilities(struct client *client, case WIRE_HSM_INIT_REPLY: case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST: case WIRE_HSM_SIGN_COMMITMENT_TX_REPLY: + case WIRE_HSM_SIGN_TX_REPLY: break; } return false; @@ -408,6 +581,15 @@ static struct io_plan *handle_client(struct io_conn *conn, case WIRE_HSM_SIGN_COMMITMENT_TX: return handle_sign_commitment_tx(conn, dc); + case WIRE_HSM_SIGN_DELAYED_PAYMENT_TO_US: + return handle_sign_delayed_payment_to_us(conn, c); + + case WIRE_HSM_SIGN_REMOTE_HTLC_TO_US: + return handle_sign_remote_htlc_to_us(conn, c); + + case WIRE_HSM_SIGN_PENALTY_TO_US: + return handle_sign_penalty_to_us(conn, c); + case WIRE_HSM_ECDH_RESP: case WIRE_HSM_CANNOUNCEMENT_SIG_REPLY: case WIRE_HSM_CUPDATE_SIG_REPLY: @@ -419,6 +601,7 @@ static struct io_plan *handle_client(struct io_conn *conn, case WIRE_HSM_INIT_REPLY: case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST: case WIRE_HSM_SIGN_COMMITMENT_TX_REPLY: + case WIRE_HSM_SIGN_TX_REPLY: break; } diff --git a/hsmd/hsm_client_wire_csv b/hsmd/hsm_client_wire_csv index 4759e6cd6..d79529756 100644 --- a/hsmd/hsm_client_wire_csv +++ b/hsmd/hsm_client_wire_csv @@ -103,4 +103,30 @@ hsm_sign_commitment_tx,,funding_amount,u64 hsm_sign_commitment_tx_reply,105 hsm_sign_commitment_tx_reply,,sig,secp256k1_ecdsa_signature +# Onchaind asks HSM to sign a spend to-us. Four variants, since each set +# of keys is derived differently... +# FIXME: Have master tell hsmd the keyindex, so it can validate output! +hsm_sign_delayed_payment_to_us,12 +hsm_sign_delayed_payment_to_us,,commit_num,u64 +hsm_sign_delayed_payment_to_us,,tx,struct bitcoin_tx +hsm_sign_delayed_payment_to_us,,wscript_len,u16 +hsm_sign_delayed_payment_to_us,,wscript,wscript_len*u8 +hsm_sign_delayed_payment_to_us,,input_amount,u64 +hsm_sign_remote_htlc_to_us,13 +hsm_sign_remote_htlc_to_us,,remote_per_commitment_point,struct pubkey +hsm_sign_remote_htlc_to_us,,tx,struct bitcoin_tx +hsm_sign_remote_htlc_to_us,,wscript_len,u16 +hsm_sign_remote_htlc_to_us,,wscript,wscript_len*u8 +hsm_sign_remote_htlc_to_us,,input_amount,u64 + +hsm_sign_penalty_to_us,14 +hsm_sign_penalty_to_us,,revocation_secret,struct secret +hsm_sign_penalty_to_us,,tx,struct bitcoin_tx +hsm_sign_penalty_to_us,,wscript_len,u16 +hsm_sign_penalty_to_us,,wscript,wscript_len*u8 +hsm_sign_penalty_to_us,,input_amount,u64 + +# Reply for all the above requests from onchaind. +hsm_sign_tx_reply,112 +hsm_sign_tx_reply,,sig,secp256k1_ecdsa_signature diff --git a/lightningd/Makefile b/lightningd/Makefile index 433c15b4c..009c4c88d 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -101,7 +101,7 @@ LIGHTNINGD_HEADERS_GEN = \ ALL_GEN_HEADERS += $(LIGHTNINGD_HEADERS_GEN) # All together in one convenient var -LIGHTNINGD_HEADERS = $(LIGHTNINGD_HEADERS_NOGEN) $(LIGHTNINGD_HEADERS_GEN) $(EXTERNAL_HEADERS) $(WIRE_HEADERS) $(BITCOIN_HEADERS) $(COMMON_HEADERS) $(WALLET_LIB_HEADERS) +LIGHTNINGD_HEADERS = $(LIGHTNINGD_HEADERS_NOGEN) $(LIGHTNINGD_HEADERS_GEN) $(EXTERNAL_HEADERS) $(WIRE_HEADERS) $(BITCOIN_HEADERS) $(COMMON_HEADERS) $(WALLET_LIB_HEADERS) $(LIGHTNINGD_HSM_CLIENT_HEADERS) $(LIGHTNINGD_OBJS): $(LIGHTNINGD_HEADERS)