mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 09:54:16 +01:00
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:
parent
9d75fbe237
commit
59efd160c1
@ -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);
|
||||
}
|
||||
|
@ -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
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");
|
||||
}
|
||||
|
@ -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,
|
||||
|
|
23
hsmd/hsmd_wiregen.c
generated
23
hsmd/hsmd_wiregen.c
generated
@ -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
10
hsmd/hsmd_wiregen.h
generated
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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…
Reference in New Issue
Block a user