hsmd: code to sign bolt12 messages with a tweaked key.

Invoices are signed with our own key, but we use a transient payer_key with a
tweak for invoice_requests (and refunds).

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2020-12-16 13:43:37 +10:30 committed by Christian Decker
parent 9d75fbe237
commit 59efd160c1
11 changed files with 134 additions and 20 deletions

View File

@ -129,3 +129,21 @@ void sighash_from_merkle(const char *messagename,
sha256_update(&sctx, merkle, sizeof(*merkle));
sha256_done(&sctx, sighash);
}
/* We use the SHA(pubkey | publictweak); so reader cannot figure out the
* tweak and derive the base key */
void payer_key_tweak(const struct pubkey32 *bolt12,
const u8 *publictweak, size_t publictweaklen,
struct sha256 *tweak)
{
u8 rawkey[32];
struct sha256_ctx sha;
secp256k1_xonly_pubkey_serialize(secp256k1_ctx, rawkey, &bolt12->pubkey);
sha256_init(&sha);
sha256_update(&sha, rawkey, sizeof(rawkey));
sha256_update(&sha,
memcheck(publictweak, publictweaklen),
publictweaklen);
sha256_done(&sha, tweak);
}

View File

@ -21,4 +21,12 @@ void sighash_from_merkle(const char *messagename,
const char *fieldname,
const struct sha256 *merkle,
struct sha256 *sighash);
/**
* payer_key_tweak - get the actual tweak to use for a payer_key
*/
void payer_key_tweak(const struct pubkey32 *bolt12,
const u8 *publictweak, size_t publictweaklen,
struct sha256 *tweak);
#endif /* LIGHTNING_COMMON_BOLT12_MERKLE_H */

View File

@ -63,10 +63,11 @@
#define REQ_FD 3
/*~ Nobody will ever find it here! hsm_secret is our root secret, the bip32
* tree is derived from that, and cached here. */
* tree and bolt12 payer_id keys are derived from that, and cached here. */
static struct {
struct secret hsm_secret;
struct ext_key bip32;
secp256k1_keypair bolt12;
} secretstuff;
/* Version codes for BIP32 extended keys in libwally-core.
@ -490,6 +491,38 @@ static void populate_secretstuff(void)
&secretstuff.bip32) != WALLY_OK)
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Can't derive private bip32 key");
/* BIP 33:
*
* We propose the first level of BIP32 tree structure to be used as
* "purpose". This purpose determines the further structure beneath
* this node.
*
* m / purpose' / *
*
* Apostrophe indicates that BIP32 hardened derivation is used.
*
* We encourage different schemes to apply for assigning a separate
* BIP number and use the same number for purpose field, so addresses
* won't be generated from overlapping BIP32 spaces.
*
* Example: Scheme described in BIP44 should use 44' (or 0x8000002C)
* as purpose.
*/
/* Clearly, we should use 9735, the unicode point for lightning! */
if (bip32_key_from_parent(&master_extkey,
BIP32_INITIAL_HARDENED_CHILD|9735,
BIP32_FLAG_KEY_PRIVATE,
&child_extkey) != WALLY_OK)
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Can't derive bolt12 bip32 key");
/* libwally says: The private key with prefix byte 0; remove it
* for libsecp256k1. */
if (secp256k1_keypair_create(secp256k1_ctx, &secretstuff.bolt12,
child_extkey.priv_key+1) != 1)
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Can't derive bolt12 keypair");
}
/*~ Get the keys for this given BIP32 index: if privkey is NULL, we
@ -706,6 +739,7 @@ static struct io_plan *init_hsm(struct io_conn *conn,
{
struct node_id node_id;
struct pubkey key;
struct pubkey32 bolt12;
struct privkey *privkey;
struct secret *seed;
struct secrets *secrets;
@ -756,12 +790,19 @@ static struct io_plan *init_hsm(struct io_conn *conn,
node_key(NULL, &key);
node_id_from_pubkey(&node_id, &key);
/* We also give it the base key for bolt12 payerids */
if (secp256k1_keypair_xonly_pub(secp256k1_ctx, &bolt12.pubkey, NULL,
&secretstuff.bolt12) != 1)
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Could derive bolt12 public key.");
/*~ Note: marshalling a bip32 tree only marshals the public side,
* not the secrets! So we're not actually handing them out here!
*/
return req_reply(conn, c,
take(towire_hsmd_init_reply(NULL, &node_id,
&secretstuff.bip32)));
&secretstuff.bip32,
&bolt12)));
}
/*~ The client has asked us to extract the shared secret from an EC Diffie
@ -1830,18 +1871,44 @@ static struct io_plan *handle_sign_bolt12(struct io_conn *conn,
char *messagename, *fieldname;
struct sha256 merkle, sha;
struct bip340sig sig;
secp256k1_keypair node_kp;
secp256k1_keypair kp;
u8 *publictweak;
if (!fromwire_hsmd_sign_bolt12(tmpctx, msg_in,
&messagename, &fieldname, &merkle))
&messagename, &fieldname, &merkle,
&publictweak))
return bad_req(conn, c, msg_in);
sighash_from_merkle(messagename, fieldname, &merkle, &sha);
node_schnorrkey(&node_kp, NULL);
if (!publictweak) {
node_schnorrkey(&kp, NULL);
} else {
/* If we're tweaking key, we use bolt12 key */
struct pubkey32 bolt12;
struct sha256 tweak;
if (secp256k1_keypair_xonly_pub(secp256k1_ctx,
&bolt12.pubkey, NULL,
&secretstuff.bolt12) != 1)
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Could not derive bolt12 public key.");
payer_key_tweak(&bolt12, publictweak, tal_bytelen(publictweak),
&tweak);
kp = secretstuff.bolt12;
if (secp256k1_keypair_xonly_tweak_add(secp256k1_ctx,
&kp,
tweak.u.u8) != 1) {
return bad_req_fmt(conn, c, msg_in,
"Failed to get tweak key");
}
}
if (!secp256k1_schnorrsig_sign(secp256k1_ctx, sig.u8,
sha.u.u8,
&node_kp,
&kp,
NULL, NULL)) {
return bad_req_fmt(conn, c, msg_in, "Failed to sign bolt12");
}

View File

@ -20,6 +20,7 @@ msgdata,hsmd_init,dev_force_channel_secrets_shaseed,?sha256,
msgtype,hsmd_init_reply,111
msgdata,hsmd_init_reply,node_id,node_id,
msgdata,hsmd_init_reply,bip32,ext_key,
msgdata,hsmd_init_reply,bolt12,pubkey32,
# Get a new HSM FD, with the specified capabilities
msgtype,hsmd_client_hsmfd,9
@ -204,6 +205,9 @@ msgtype,hsmd_sign_bolt12,25
msgdata,hsmd_sign_bolt12,messagename,wirestring,
msgdata,hsmd_sign_bolt12,fieldname,wirestring,
msgdata,hsmd_sign_bolt12,merkleroot,sha256,
# This is for invreq payer_id (temporary keys)
msgdata,hsmd_sign_bolt12,publictweaklen,u16,
msgdata,hsmd_sign_bolt12,publictweak,u8,publictweaklen
msgtype,hsmd_sign_bolt12_reply,125
msgdata,hsmd_sign_bolt12_reply,sig,bip340sig,

1 # Clients should not give a bad request but not the HSM's decision to crash.
20 msgdata,hsmd_init_reply,bip32,ext_key,
21 # Get a new HSM FD, with the specified capabilities msgdata,hsmd_init_reply,bolt12,pubkey32,
22 msgtype,hsmd_client_hsmfd,9 # Get a new HSM FD, with the specified capabilities
23 msgtype,hsmd_client_hsmfd,9
24 # Which identity to use for requests
25 msgdata,hsmd_client_hsmfd,id,node_id,
26 # Database id for this client, if any.
205
206
207
208
209
210
211
212
213

23
hsmd/hsmd_wiregen.c generated
View File

@ -239,17 +239,18 @@ bool fromwire_hsmd_init(const tal_t *ctx, const void *p, struct bip32_key_versio
}
/* WIRE: HSMD_INIT_REPLY */
u8 *towire_hsmd_init_reply(const tal_t *ctx, const struct node_id *node_id, const struct ext_key *bip32)
u8 *towire_hsmd_init_reply(const tal_t *ctx, const struct node_id *node_id, const struct ext_key *bip32, const struct pubkey32 *bolt12)
{
u8 *p = tal_arr(ctx, u8, 0);
towire_u16(&p, WIRE_HSMD_INIT_REPLY);
towire_node_id(&p, node_id);
towire_ext_key(&p, bip32);
towire_pubkey32(&p, bolt12);
return memcheck(p, tal_count(p));
}
bool fromwire_hsmd_init_reply(const void *p, struct node_id *node_id, struct ext_key *bip32)
bool fromwire_hsmd_init_reply(const void *p, struct node_id *node_id, struct ext_key *bip32, struct pubkey32 *bolt12)
{
const u8 *cursor = p;
size_t plen = tal_count(p);
@ -258,6 +259,7 @@ bool fromwire_hsmd_init_reply(const void *p, struct node_id *node_id, struct ext
return false;
fromwire_node_id(&cursor, &plen, node_id);
fromwire_ext_key(&cursor, &plen, bip32);
fromwire_pubkey32(&cursor, &plen, bolt12);
return cursor != NULL;
}
@ -1221,19 +1223,25 @@ bool fromwire_hsmd_get_output_scriptpubkey_reply(const tal_t *ctx, const void *p
/* WIRE: HSMD_SIGN_BOLT12 */
/* Sign a bolt12-style merkle hash */
u8 *towire_hsmd_sign_bolt12(const tal_t *ctx, const wirestring *messagename, const wirestring *fieldname, const struct sha256 *merkleroot)
u8 *towire_hsmd_sign_bolt12(const tal_t *ctx, const wirestring *messagename, const wirestring *fieldname, const struct sha256 *merkleroot, const u8 *publictweak)
{
u16 publictweaklen = tal_count(publictweak);
u8 *p = tal_arr(ctx, u8, 0);
towire_u16(&p, WIRE_HSMD_SIGN_BOLT12);
towire_wirestring(&p, messagename);
towire_wirestring(&p, fieldname);
towire_sha256(&p, merkleroot);
/* This is for invreq payer_id (temporary keys) */
towire_u16(&p, publictweaklen);
towire_u8_array(&p, publictweak, publictweaklen);
return memcheck(p, tal_count(p));
}
bool fromwire_hsmd_sign_bolt12(const tal_t *ctx, const void *p, wirestring **messagename, wirestring **fieldname, struct sha256 *merkleroot)
bool fromwire_hsmd_sign_bolt12(const tal_t *ctx, const void *p, wirestring **messagename, wirestring **fieldname, struct sha256 *merkleroot, u8 **publictweak)
{
u16 publictweaklen;
const u8 *cursor = p;
size_t plen = tal_count(p);
@ -1242,6 +1250,11 @@ bool fromwire_hsmd_sign_bolt12(const tal_t *ctx, const void *p, wirestring **mes
*messagename = fromwire_wirestring(ctx, &cursor, &plen);
*fieldname = fromwire_wirestring(ctx, &cursor, &plen);
fromwire_sha256(&cursor, &plen, merkleroot);
/* This is for invreq payer_id (temporary keys) */
publictweaklen = fromwire_u16(&cursor, &plen);
// 2nd case publictweak
*publictweak = publictweaklen ? tal_arr(ctx, u8, publictweaklen) : NULL;
fromwire_u8_array(&cursor, &plen, *publictweak, publictweaklen);
return cursor != NULL;
}
@ -1265,4 +1278,4 @@ bool fromwire_hsmd_sign_bolt12_reply(const void *p, struct bip340sig *sig)
fromwire_bip340sig(&cursor, &plen, sig);
return cursor != NULL;
}
// SHA256STAMP:cb033b99e13d9bdd06582a34132ba7cd311f4cf074298da1addcdad06b3fdf8f
// SHA256STAMP:bba9aa92e35397eb79f9518bbc058ccac4b51c3e48039f29205703f8bc20111e

10
hsmd/hsmd_wiregen.h generated
View File

@ -104,8 +104,8 @@ u8 *towire_hsmd_init(const tal_t *ctx, const struct bip32_key_version *bip32_key
bool fromwire_hsmd_init(const tal_t *ctx, const void *p, struct bip32_key_version *bip32_key_version, const struct chainparams **chainparams, struct secret **hsm_encryption_key, struct privkey **dev_force_privkey, struct secret **dev_force_bip32_seed, struct secrets **dev_force_channel_secrets, struct sha256 **dev_force_channel_secrets_shaseed);
/* WIRE: HSMD_INIT_REPLY */
u8 *towire_hsmd_init_reply(const tal_t *ctx, const struct node_id *node_id, const struct ext_key *bip32);
bool fromwire_hsmd_init_reply(const void *p, struct node_id *node_id, struct ext_key *bip32);
u8 *towire_hsmd_init_reply(const tal_t *ctx, const struct node_id *node_id, const struct ext_key *bip32, const struct pubkey32 *bolt12);
bool fromwire_hsmd_init_reply(const void *p, struct node_id *node_id, struct ext_key *bip32, struct pubkey32 *bolt12);
/* WIRE: HSMD_CLIENT_HSMFD */
/* Get a new HSM FD */
@ -274,8 +274,8 @@ bool fromwire_hsmd_get_output_scriptpubkey_reply(const tal_t *ctx, const void *p
/* WIRE: HSMD_SIGN_BOLT12 */
/* Sign a bolt12-style merkle hash */
u8 *towire_hsmd_sign_bolt12(const tal_t *ctx, const wirestring *messagename, const wirestring *fieldname, const struct sha256 *merkleroot);
bool fromwire_hsmd_sign_bolt12(const tal_t *ctx, const void *p, wirestring **messagename, wirestring **fieldname, struct sha256 *merkleroot);
u8 *towire_hsmd_sign_bolt12(const tal_t *ctx, const wirestring *messagename, const wirestring *fieldname, const struct sha256 *merkleroot, const u8 *publictweak);
bool fromwire_hsmd_sign_bolt12(const tal_t *ctx, const void *p, wirestring **messagename, wirestring **fieldname, struct sha256 *merkleroot, u8 **publictweak);
/* WIRE: HSMD_SIGN_BOLT12_REPLY */
u8 *towire_hsmd_sign_bolt12_reply(const tal_t *ctx, const struct bip340sig *sig);
@ -283,4 +283,4 @@ bool fromwire_hsmd_sign_bolt12_reply(const void *p, struct bip340sig *sig);
#endif /* LIGHTNING_HSMD_HSMD_WIREGEN_H */
// SHA256STAMP:cb033b99e13d9bdd06582a34132ba7cd311f4cf074298da1addcdad06b3fdf8f
// SHA256STAMP:bba9aa92e35397eb79f9518bbc058ccac4b51c3e48039f29205703f8bc20111e

View File

@ -125,7 +125,8 @@ struct ext_key *hsm_init(struct lightningd *ld)
bip32_base = tal(ld, struct ext_key);
msg = wire_sync_read(tmpctx, ld->hsm_fd);
if (!fromwire_hsmd_init_reply(msg,
&ld->id, bip32_base)) {
&ld->id, bip32_base,
&ld->bolt12_base)) {
if (ld->config.keypass)
errx(1, "Wrong password for encrypted hsm_secret.");
errx(1, "HSM did not give init reply");

View File

@ -442,7 +442,7 @@ static void hsm_sign_b12_invoice(struct lightningd *ld,
assert(!invoice->signature);
merkle_tlv(invoice->fields, &merkle);
msg = towire_hsmd_sign_bolt12(NULL, "invoice", "signature", &merkle);
msg = towire_hsmd_sign_bolt12(NULL, "invoice", "signature", &merkle, NULL);
if (!wire_sync_write(ld->hsm_fd, take(msg)))
fatal("Could not write to HSM: %s", strerror(errno));

View File

@ -111,6 +111,9 @@ struct lightningd {
/* This is us. */
struct node_id id;
/* The public base for our payer_id keys */
struct pubkey32 bolt12_base;
/* Feature set we offer. */
struct feature_set *our_features;

View File

@ -49,7 +49,7 @@ static void hsm_sign_b12_offer(struct lightningd *ld,
{
u8 *msg;
msg = towire_hsmd_sign_bolt12(NULL, "offer", "signature", merkle);
msg = towire_hsmd_sign_bolt12(NULL, "offer", "signature", merkle, NULL);
if (!wire_sync_write(ld->hsm_fd, take(msg)))
fatal("Could not write to HSM: %s", strerror(errno));

View File

@ -569,7 +569,7 @@ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED,
u8 *towire_gossipd_get_incoming_channels(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "towire_gossipd_get_incoming_channels called!\n"); abort(); }
/* Generated stub for towire_hsmd_sign_bolt12 */
u8 *towire_hsmd_sign_bolt12(const tal_t *ctx UNNEEDED, const wirestring *messagename UNNEEDED, const wirestring *fieldname UNNEEDED, const struct sha256 *merkleroot UNNEEDED)
u8 *towire_hsmd_sign_bolt12(const tal_t *ctx UNNEEDED, const wirestring *messagename UNNEEDED, const wirestring *fieldname UNNEEDED, const struct sha256 *merkleroot UNNEEDED, const u8 *publictweak UNNEEDED)
{ fprintf(stderr, "towire_hsmd_sign_bolt12 called!\n"); abort(); }
/* Generated stub for towire_hsmd_sign_commitment_tx */
u8 *towire_hsmd_sign_commitment_tx(const tal_t *ctx UNNEEDED, const struct node_id *peer_id UNNEEDED, u64 channel_dbid UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const struct pubkey *remote_funding_key UNNEEDED)