diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 6f4315978..d7bc77a04 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -914,34 +914,6 @@ static struct io_plan *handle_channel_update_sig(struct io_conn *conn, return req_reply(conn, c, take(towire_hsmd_cupdate_sig_reply(NULL, cu))); } -/*~ This gets the basepoints for a channel; it's not private information really - * (we tell the peer this to establish a channel, as it sets up the keys used - * for each transaction). - * - * Note that this is asked by lightningd, so it tells us what channels it wants. - */ -static struct io_plan *handle_get_channel_basepoints(struct io_conn *conn, - struct client *c, - const u8 *msg_in) -{ - struct node_id peer_id; - u64 dbid; - struct secret seed; - struct basepoints basepoints; - struct pubkey funding_pubkey; - - if (!fromwire_hsmd_get_channel_basepoints(msg_in, &peer_id, &dbid)) - return bad_req(conn, c, msg_in); - - get_channel_seed(&peer_id, dbid, &seed); - derive_basepoints(&seed, &funding_pubkey, &basepoints, NULL, NULL); - - return req_reply(conn, c, - take(towire_hsmd_get_channel_basepoints_reply(NULL, - &basepoints, - &funding_pubkey))); -} - /*~ This is another lightningd-only interface; signing a commit transaction. * This is dangerous, since if we sign a revoked commitment tx we'll lose * funds, thus it's only available to lightningd. @@ -1810,9 +1782,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_GET_CHANNEL_BASEPOINTS: - return handle_get_channel_basepoints(conn, c, c->msg_in); - case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY: return handle_get_output_scriptpubkey(conn, c, c->msg_in); @@ -1862,6 +1831,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_SIGN_MUTUAL_CLOSE_TX: return handle_sign_mutual_close_tx(conn, c, c->msg_in); + case WIRE_HSMD_GET_CHANNEL_BASEPOINTS: case WIRE_HSMD_SIGN_INVOICE: case WIRE_HSMD_SIGN_MESSAGE: case WIRE_HSMD_SIGN_BOLT12: diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index b2a304e5f..231c7b028 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -215,6 +215,49 @@ static void node_schnorrkey(secp256k1_keypair *node_keypair, } } +/*~ This secret is the basis for all per-channel secrets: the per-channel seeds + * will be generated by mixing in the dbid and the peer node_id. */ +static void hsm_channel_secret_base(struct secret *channel_seed_base) +{ + hkdf_sha256(channel_seed_base, sizeof(struct secret), NULL, 0, + &secretstuff.hsm_secret, sizeof(secretstuff.hsm_secret), + /*~ Initially, we didn't support multiple channels per + * peer at all: a channel had to be completely forgotten + * before another could exist. That was slightly relaxed, + * but the phrase "peer seed" is wired into the seed + * generation here, so we need to keep it that way for + * existing clients, rather than using "channel seed". */ + "peer seed", strlen("peer seed")); +} + +/*~ This gets the seed for this particular channel. */ +static void get_channel_seed(const struct node_id *peer_id, u64 dbid, + struct secret *channel_seed) +{ + struct secret channel_base; + u8 input[sizeof(peer_id->k) + sizeof(dbid)]; + /*~ Again, "per-peer" should be "per-channel", but Hysterical Raisins */ + const char *info = "per-peer seed"; + + /*~ We use the DER encoding of the pubkey, because it's platform + * independent. Since the dbid is unique, however, it's completely + * unnecessary, but again, existing users can't be broken. */ + /* FIXME: lnd has a nicer BIP32 method for deriving secrets which we + * should migrate to. */ + hsm_channel_secret_base(&channel_base); + memcpy(input, peer_id->k, sizeof(peer_id->k)); + BUILD_ASSERT(sizeof(peer_id->k) == PUBKEY_CMPR_LEN); + /*~ For all that talk about platform-independence, note that this + * field is endian-dependent! But let's face it, little-endian won. + * In related news, we don't support EBCDIC or middle-endian. */ + memcpy(input + PUBKEY_CMPR_LEN, &dbid, sizeof(dbid)); + + hkdf_sha256(channel_seed, sizeof(*channel_seed), + input, sizeof(input), + &channel_base, sizeof(channel_base), + info, strlen(info)); +} + /*~ lightningd asks us to sign a message. I tweeted the spec * in https://twitter.com/rusty_twit/status/1182102005914800128: * @@ -369,6 +412,31 @@ static u8 *handle_sign_invoice(struct hsmd_client *c, const u8 *msg_in) return towire_hsmd_sign_invoice_reply(NULL, &rsig); } +/*~ This gets the basepoints for a channel; it's not private information really + * (we tell the peer this to establish a channel, as it sets up the keys used + * for each transaction). + * + * Note that this is asked by lightningd, so it tells us what channels it wants. + */ +static u8 *handle_get_channel_basepoints(struct hsmd_client *c, + const u8 *msg_in) +{ + struct node_id peer_id; + u64 dbid; + struct secret seed; + struct basepoints basepoints; + struct pubkey funding_pubkey; + + if (!fromwire_hsmd_get_channel_basepoints(msg_in, &peer_id, &dbid)) + return hsmd_status_malformed_request(c, msg_in); + + get_channel_seed(&peer_id, dbid, &seed); + derive_basepoints(&seed, &funding_pubkey, &basepoints, NULL, NULL); + + return towire_hsmd_get_channel_basepoints_reply(NULL, &basepoints, + &funding_pubkey); +} + u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, const u8 *msg) { @@ -395,7 +463,6 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, switch (t) { case WIRE_HSMD_INIT: case WIRE_HSMD_CLIENT_HSMFD: - case WIRE_HSMD_GET_CHANNEL_BASEPOINTS: case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY: case WIRE_HSMD_ECDH_REQ: case WIRE_HSMD_CANNOUNCEMENT_SIG_REQ: @@ -421,6 +488,8 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, return handle_sign_bolt12(client, msg); case WIRE_HSMD_SIGN_MESSAGE: return handle_sign_message(client, msg); + case WIRE_HSMD_GET_CHANNEL_BASEPOINTS: + return handle_get_channel_basepoints(client, msg); case WIRE_HSMD_DEV_MEMLEAK: case WIRE_HSMD_ECDH_RESP: