diff --git a/common/hsm_version.h b/common/hsm_version.h index 5720c3f9c..e4c4cecf2 100644 --- a/common/hsm_version.h +++ b/common/hsm_version.h @@ -26,6 +26,7 @@ * v5 with dev_preinit: b93e18534a468a4aa9f7015db42e9c363c32aeee5f9146b36dc953ebbdc3d33c * v5 with preapprove_check: 0ed6dd4ea2c02b67c51b1420b3d07ab2227a4c06ce7e2942d946967687e9baf7 * v6 no secret from get_per_commitment_point: 0cad1790beb3473d64355f4cb4f64daa80c28c8a241998b7ef0223385d7ffff9 + * v6 with sign_bolt12_2 (tweak using node id): 8fcb731279a10af3f95aeb8be1da6b2ced76a1984afa18c5f46a03515d70ea0e */ #define HSM_MIN_VERSION 5 #define HSM_MAX_VERSION 6 diff --git a/hsmd/Makefile b/hsmd/Makefile index e7f626038..8a788cef5 100644 --- a/hsmd/Makefile +++ b/hsmd/Makefile @@ -26,6 +26,7 @@ HSMD_COMMON_OBJS := \ common/base32.o \ common/bigsize.o \ common/bip32.o \ + common/bolt12_id.o \ common/bolt12_merkle.o \ common/channel_id.o \ common/daemon.o \ diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index c41a08546..16bf1e09a 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -691,6 +691,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_SIGN_MESSAGE: case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER: case WIRE_HSMD_SIGN_BOLT12: + case WIRE_HSMD_SIGN_BOLT12_2: case WIRE_HSMD_PREAPPROVE_INVOICE: case WIRE_HSMD_PREAPPROVE_KEYSEND: case WIRE_HSMD_PREAPPROVE_INVOICE_CHECK: @@ -746,6 +747,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_SIGN_MESSAGE_REPLY: case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY_REPLY: case WIRE_HSMD_SIGN_BOLT12_REPLY: + case WIRE_HSMD_SIGN_BOLT12_2_REPLY: case WIRE_HSMD_PREAPPROVE_INVOICE_REPLY: case WIRE_HSMD_PREAPPROVE_KEYSEND_REPLY: case WIRE_HSMD_PREAPPROVE_INVOICE_CHECK_REPLY: diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index 24173fbec..98faee7d4 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -378,6 +378,21 @@ msgdata,hsmd_sign_bolt12,publictweak,u8,publictweaklen msgtype,hsmd_sign_bolt12_reply,125 msgdata,hsmd_sign_bolt12_reply,sig,bip340sig, +# Sign a bolt12-style merkle hash (modern) +msgtype,hsmd_sign_bolt12_2,41 +msgdata,hsmd_sign_bolt12_2,messagename,wirestring, +msgdata,hsmd_sign_bolt12_2,fieldname,wirestring, +msgdata,hsmd_sign_bolt12_2,merkleroot,sha256, +# This is for signing with an alias (temporary key), used if *publictweak* not empty. +# derive_secret with info, then tweak privkey with SHA256(derived_secret || publictweak). +msgdata,hsmd_sign_bolt12_2,infolen,u16, +msgdata,hsmd_sign_bolt12_2,info,u8,infolen +msgdata,hsmd_sign_bolt12_2,publictweaklen,u16, +msgdata,hsmd_sign_bolt12_2,publictweak,u8,publictweaklen + +msgtype,hsmd_sign_bolt12_2_reply,141 +msgdata,hsmd_sign_bolt12_2_reply,sig,bip340sig, + # Sign an option_will_fund offer hash msgtype,hsmd_sign_option_will_fund_offer,26 msgdata,hsmd_sign_option_will_fund_offer,funding_pubkey,pubkey, diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index 43c231e76..0b62a17cc 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -136,6 +137,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_SIGN_MESSAGE: case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY: case WIRE_HSMD_SIGN_BOLT12: + case WIRE_HSMD_SIGN_BOLT12_2: case WIRE_HSMD_PREAPPROVE_INVOICE: case WIRE_HSMD_PREAPPROVE_KEYSEND: case WIRE_HSMD_PREAPPROVE_INVOICE_CHECK: @@ -181,6 +183,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_SIGN_MESSAGE_REPLY: case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY_REPLY: case WIRE_HSMD_SIGN_BOLT12_REPLY: + case WIRE_HSMD_SIGN_BOLT12_2_REPLY: case WIRE_HSMD_PREAPPROVE_INVOICE_REPLY: case WIRE_HSMD_PREAPPROVE_KEYSEND_REPLY: case WIRE_HSMD_PREAPPROVE_INVOICE_CHECK_REPLY: @@ -783,6 +786,64 @@ static u8 *handle_sign_bolt12(struct hsmd_client *c, const u8 *msg_in) return towire_hsmd_sign_bolt12_reply(NULL, &sig); } +/*~ lightningd asks us to sign a bolt12 (e.g. offer): modern version */ +static u8 *handle_sign_bolt12_2(struct hsmd_client *c, const u8 *msg_in) +{ + char *messagename, *fieldname; + struct sha256 merkle, sha; + struct bip340sig sig; + secp256k1_keypair kp; + u8 *info; + u8 *tweakmessage; + + if (!fromwire_hsmd_sign_bolt12_2(tmpctx, msg_in, + &messagename, &fieldname, &merkle, + &info, &tweakmessage)) + return hsmd_status_malformed_request(c, msg_in); + + sighash_from_merkle(messagename, fieldname, &merkle, &sha); + + if (tweakmessage) { + struct secret base_secret; + struct sha256 tweak; + struct privkey tweakedkey; + + /* See handle_derive_secret: this gives a base secret. */ + hkdf_sha256(&base_secret, sizeof(base_secret), NULL, 0, + &secretstuff.derived_secret, + sizeof(secretstuff.derived_secret), + info, tal_bytelen(info)); + + /* This is simply SHA256(secret || tweakmessage) */ + bolt12_alias_tweak(&base_secret, + tweakmessage, tal_bytelen(tweakmessage), + &tweak); + + node_key(&tweakedkey, NULL); + if (secp256k1_ec_seckey_tweak_add(secp256k1_ctx, + tweakedkey.secret.data, + tweak.u.u8) != 1) + hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Couldn't tweak key."); + if (secp256k1_keypair_create(secp256k1_ctx, &kp, + tweakedkey.secret.data) != 1) + hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Failed to derive tweaked keypair"); + } else { + node_schnorrkey(&kp); + } + + if (!secp256k1_schnorrsig_sign32(secp256k1_ctx, sig.u8, + sha.u.u8, + &kp, + NULL)) { + return hsmd_status_bad_request_fmt(c, msg_in, + "Failed to sign bolt12"); + } + + return towire_hsmd_sign_bolt12_2_reply(NULL, &sig); +} + /*~ lightningd asks us to approve an invoice. This stub implementation * is overriden by fully validating signers that need to track invoice * payments. */ @@ -2075,6 +2136,8 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, return handle_sign_option_will_fund_offer(client, msg); case WIRE_HSMD_SIGN_BOLT12: return handle_sign_bolt12(client, msg); + case WIRE_HSMD_SIGN_BOLT12_2: + return handle_sign_bolt12_2(client, msg); case WIRE_HSMD_PREAPPROVE_INVOICE: case WIRE_HSMD_PREAPPROVE_INVOICE_CHECK: return handle_preapprove_invoice(client, msg); @@ -2167,6 +2230,7 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, case WIRE_HSMD_SIGN_MESSAGE_REPLY: case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY_REPLY: case WIRE_HSMD_SIGN_BOLT12_REPLY: + case WIRE_HSMD_SIGN_BOLT12_2_REPLY: case WIRE_HSMD_PREAPPROVE_INVOICE_REPLY: case WIRE_HSMD_PREAPPROVE_KEYSEND_REPLY: case WIRE_HSMD_PREAPPROVE_INVOICE_CHECK_REPLY: @@ -2197,6 +2261,7 @@ u8 *hsmd_init(struct secret hsm_secret, const u64 hsmd_version, WIRE_HSMD_CHECK_OUTPOINT, WIRE_HSMD_FORGET_CHANNEL, WIRE_HSMD_REVOKE_COMMITMENT_TX, + WIRE_HSMD_SIGN_BOLT12_2, WIRE_HSMD_PREAPPROVE_INVOICE_CHECK, WIRE_HSMD_PREAPPROVE_KEYSEND_CHECK, };