Browse Source

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>
ppa
Rusty Russell 4 years ago
committed by Christian Decker
parent
commit
59efd160c1
  1. 18
      common/bolt12_merkle.c
  2. 8
      common/bolt12_merkle.h
  3. 79
      hsmd/hsmd.c
  4. 4
      hsmd/hsmd_wire.csv
  5. 23
      hsmd/hsmd_wiregen.c
  6. 10
      hsmd/hsmd_wiregen.h
  7. 3
      lightningd/hsm_control.c
  8. 2
      lightningd/invoice.c
  9. 3
      lightningd/lightningd.h
  10. 2
      lightningd/offer.c
  11. 2
      lightningd/test/run-invoice-select-inchan.c

18
common/bolt12_merkle.c

@ -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);
}

8
common/bolt12_merkle.h

@ -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 */

79
hsmd/hsmd.c

@ -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");
}

4
hsmd/hsmd_wire.csv

@ -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,

Can't render this file because it has a wrong number of fields in line 2.

23
hsmd/hsmd_wiregen.c

@ -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

@ -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

3
lightningd/hsm_control.c

@ -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");

2
lightningd/invoice.c

@ -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));

3
lightningd/lightningd.h

@ -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;

2
lightningd/offer.c

@ -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));

2
lightningd/test/run-invoice-select-inchan.c

@ -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)

Loading…
Cancel
Save