Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion common/hsm_version.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
* v6 with hsm_secret struct cleanup: 06c56396fe42f4f47911d7f865dd0004d264fc1348f89547743755b6b33fec90
* v6 with hsm_secret_type TLV: 7bb5deb2367482feb084d304ee14b2373d42910ad56484fbf47614dbb3d4cb74
* v6 with bip86_base in TLV: 6bb6e6ee256f22a6fb41856c90feebde3065a9074e79a46731e453a932be83f0
* v7 with sign_bolt12 using path_pubkey for invoices: 53792d2d257dd1b1b29d5945903c8d11190b82d1ff27d44d9ac155d06851de5c
*/
#define HSM_MIN_VERSION 5
#define HSM_MAX_VERSION 6
#define HSM_MAX_VERSION 7
#endif /* LIGHTNING_COMMON_HSM_VERSION_H */
2 changes: 2 additions & 0 deletions connectd/connectd_wire.csv
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ msgdata,connectd_got_onionmsg_to_us,path_secret,?secret,
msgdata,connectd_got_onionmsg_to_us,reply,?blinded_path,
msgdata,connectd_got_onionmsg_to_us,rawmsg_len,u16,
msgdata,connectd_got_onionmsg_to_us,rawmsg,u8,rawmsg_len
msgdata,connectd_got_onionmsg_to_us,final_alias,pubkey,
msgdata,connectd_got_onionmsg_to_us,path_pubkey,pubkey,

# Lightningd tells us to send an onion message.
msgtype,connectd_send_onionmsg,2041
Expand Down
2 changes: 1 addition & 1 deletion connectd/onion_message.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ static const char *handle_onion(const tal_t *ctx,
take(towire_connectd_got_onionmsg_to_us(NULL,
final_path_id,
final_om->reply_path,
omsg)));
omsg, &final_alias, path_key)));
} else {
struct node_id next_node_id;
struct peer *next_peer;
Expand Down
4 changes: 1 addition & 3 deletions hsmd/hsmd_wire.csv
Original file line number Diff line number Diff line change
Expand Up @@ -403,9 +403,7 @@ 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
msgdata,hsmd_sign_bolt12,path_pubkey,?pubkey,

#include <bitcoin/signature.h>
msgtype,hsmd_sign_bolt12_reply,125
Expand Down
62 changes: 39 additions & 23 deletions hsmd/libhsmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,7 @@ static u8 *handle_sign_option_will_fund_offer(struct hsmd_client *c,
return towire_hsmd_sign_option_will_fund_offer_reply(NULL, &sig);
}

/*
static void payer_key_tweak(const struct pubkey *bolt12,
const u8 *publictweak, size_t publictweaklen,
struct sha256 *tweak)
Expand All @@ -830,48 +831,63 @@ static void payer_key_tweak(const struct pubkey *bolt12,
sha256_update(&sha, publictweak, publictweaklen);
sha256_done(&sha, tweak);
}
*/

/*~ lightningd asks us to sign a bolt12 (e.g. offer). */
static void node_blinded_privkey(const struct pubkey *path_pubkey, struct privkey *blinded_privkey)
{
struct secret ss;
struct secret node_id_blinding;

node_key(blinded_privkey, NULL);

/* BOLT #4:
* - $`ss_i = SHA256(e_i * N_i) = SHA256(k_i * E_i)`$
* (ECDH shared secret known only by $`N_r`$ and $`N_i`$)
*/
if (secp256k1_ecdh(secp256k1_ctx, ss.data,
&path_pubkey->pubkey, blinded_privkey->secret.data,
NULL, NULL) != 1)
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Could not compute ss from path_key.");

/* BOLT #4:
* - $`B_i = HMAC256(\text{"blinded\_node\_id"}, ss_i) * N_i`$
* (blinded `node_id` for $`N_i`$, private key known only by $`N_i`$)
*/
subkey_from_hmac("blinded_node_id", &ss, &node_id_blinding);

if (secp256k1_ec_seckey_tweak_mul(secp256k1_ctx,
blinded_privkey->secret.data,
node_id_blinding.data) != 1)
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Could tweak bolt12 key.");
}

/*~ lightningd asks us to sign a bolt12 invoice. */
static u8 *handle_sign_bolt12(struct hsmd_client *c, const u8 *msg_in)
{
char *messagename, *fieldname;
struct sha256 merkle, sha;
struct bip340sig sig;
secp256k1_keypair kp;
u8 *publictweak;
struct pubkey* path_pubkey;

if (!fromwire_hsmd_sign_bolt12(tmpctx, msg_in,
&messagename, &fieldname, &merkle,
&publictweak))
&path_pubkey))
return hsmd_status_malformed_request(c, msg_in);

sighash_from_merkle(messagename, fieldname, &merkle, &sha);

if (!publictweak) {
if (!path_pubkey) {
node_schnorrkey(&kp);
} else {
/* If we're tweaking key, we use bolt12 key */
struct privkey tweakedkey;
struct pubkey bolt12;
struct sha256 tweak;
struct privkey blinded_privkey;

if (secp256k1_ec_pubkey_create(secp256k1_ctx, &bolt12.pubkey,
secretstuff.bolt12.data) != 1)
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Could derive bolt12 public key.");

payer_key_tweak(&bolt12, publictweak, tal_bytelen(publictweak),
&tweak);

tweakedkey.secret = secretstuff.bolt12;
if (secp256k1_ec_seckey_tweak_add(secp256k1_ctx,
tweakedkey.secret.data,
tweak.u.u8) != 1)
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Could tweak bolt12 key.");
node_blinded_privkey(path_pubkey, &blinded_privkey);

if (secp256k1_keypair_create(secp256k1_ctx, &kp,
tweakedkey.secret.data) != 1)
blinded_privkey.secret.data) != 1)
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Failed to derive bolt12 keypair");
}
Expand Down
10 changes: 7 additions & 3 deletions lightningd/invoice.c
Original file line number Diff line number Diff line change
Expand Up @@ -479,15 +479,16 @@ static bool hsm_sign_b11(const u5 *u5bytes,
}

static void hsm_sign_b12_invoice(struct lightningd *ld,
struct tlv_invoice *invoice)
struct tlv_invoice *invoice,
const struct pubkey* path_pubkey)
{
struct sha256 merkle;
const u8 *msg;

assert(!invoice->signature);

merkle_tlv(invoice->fields, &merkle);
msg = towire_hsmd_sign_bolt12(NULL, "invoice", "signature", &merkle, NULL);
msg = towire_hsmd_sign_bolt12(NULL, "invoice", "signature", &merkle, path_pubkey);

msg = hsm_sync_req(tmpctx, ld, take(msg));
invoice->signature = tal(invoice, struct bip340sig);
Expand Down Expand Up @@ -1672,6 +1673,7 @@ static struct command_result *json_createinvoice(struct command *cmd,
const char *invstring;
struct json_escape *label;
struct preimage *preimage;
struct pubkey *path_pubkey;
u64 inv_dbid;
struct sha256 payment_hash;
struct json_stream *response;
Expand All @@ -1685,6 +1687,7 @@ static struct command_result *json_createinvoice(struct command *cmd,
p_req("invstring", param_invstring, &invstring),
p_req("label", param_label, &label),
p_req("preimage", param_preimage, &preimage),
p_opt("path_pubkey", param_pubkey, &path_pubkey),
NULL))
return command_param_failed();

Expand Down Expand Up @@ -1758,7 +1761,8 @@ static struct command_result *json_createinvoice(struct command *cmd,
if (inv->signature)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"invoice already signed");
hsm_sign_b12_invoice(cmd->ld, inv);

hsm_sign_b12_invoice(cmd->ld, inv, path_pubkey);
b12enc = invoice_encode(cmd, inv);

if (inv->offer_issuer_id || inv->offer_paths) {
Expand Down
9 changes: 8 additions & 1 deletion lightningd/onion_message.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ struct onion_message_hook_payload {
/* Optional */
struct blinded_path *reply_path;
struct secret *pathsecret;
struct pubkey blinded_node_id;
struct pubkey path_pubkey;
struct tlv_onionmsg_tlv *om;
};

Expand Down Expand Up @@ -49,6 +51,9 @@ static void onion_message_serialize(struct onion_message_hook_payload *payload,
if (payload->pathsecret)
json_add_secret(stream, "pathsecret", payload->pathsecret);

json_add_pubkey(stream, "blinded_node_id", &payload->blinded_node_id);
json_add_pubkey(stream, "path_pubkey", &payload->path_pubkey);

if (payload->reply_path)
json_add_blindedpath(plugin, stream, "reply_blindedpath",
payload->reply_path);
Expand Down Expand Up @@ -114,7 +119,9 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg)
if (!fromwire_connectd_got_onionmsg_to_us(payload, msg,
&payload->pathsecret,
&payload->reply_path,
&submsg)) {
&submsg,
&payload->blinded_node_id,
&payload->path_pubkey)) {
log_broken(ld->log, "bad got_onionmsg_tous: %s",
tal_hex(tmpctx, msg));
return;
Expand Down
2 changes: 1 addition & 1 deletion lightningd/test/run-invoice-select-inchan.c
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ u8 *towire_hsmd_preapprove_keysend(const tal_t *ctx UNNEEDED, const struct node_
u8 *towire_hsmd_preapprove_keysend_check(const tal_t *ctx UNNEEDED, const struct node_id *destination UNNEEDED, const struct sha256 *payment_hash UNNEEDED, struct amount_msat amount_msat UNNEEDED, bool check_only UNNEEDED)
{ fprintf(stderr, "towire_hsmd_preapprove_keysend_check 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, const u8 *publictweak 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 struct pubkey *path_pubkey 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, u64 commit_num UNNEEDED)
Expand Down
14 changes: 12 additions & 2 deletions plugins/offers.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,11 @@ static struct command_result *onion_message_recv(struct command *cmd,
const char *buf,
const jsmntok_t *params)
{
const jsmntok_t *om, *secrettok, *replytok, *invreqtok, *invtok;
const jsmntok_t *om, *secrettok, *nodeidtok, *pubkeytok, *replytok, *invreqtok, *invtok;
struct blinded_path *reply_path = NULL;
struct secret *secret;
struct pubkey *blinded_node_id;
struct pubkey *path_pubkey;

om = json_get_member(buf, params, "onion_message");
secrettok = json_get_member(buf, om, "pathsecret");
Expand All @@ -274,6 +276,14 @@ static struct command_result *onion_message_recv(struct command *cmd,
json_to_secret(buf, secrettok, secret);
} else
secret = NULL;
nodeidtok = json_get_member(buf, om, "blinded_node_id");
if(!nodeidtok) plugin_err(cmd->plugin, "Missing blinded node id");
blinded_node_id = tal(tmpctx, struct pubkey);
json_to_pubkey(buf, nodeidtok, blinded_node_id);
pubkeytok = json_get_member(buf, om, "path_pubkey");
if(!pubkeytok) plugin_err(cmd->plugin, "Missing path pubkey");
path_pubkey = tal(tmpctx, struct pubkey);
json_to_pubkey(buf, pubkeytok, path_pubkey);

/* Might be reply for fetchinvoice (which always has a secret,
* so we can tell it's a response). */
Expand All @@ -298,7 +308,7 @@ static struct command_result *onion_message_recv(struct command *cmd,
const u8 *invreqbin = json_tok_bin_from_hex(tmpctx, buf, invreqtok);
return handle_invoice_request(cmd,
invreqbin,
reply_path, secret);
reply_path, secret, blinded_node_id, path_pubkey);
}

invtok = json_get_member(buf, om, "invoice");
Expand Down
29 changes: 24 additions & 5 deletions plugins/offers_invreq_hook.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ struct invreq {

/* Optional secret. */
const struct secret *secret;

/* The blinded node id the invoice request was received through. */
struct pubkey *blinded_node_id;

/* The path pubkey the invoice request was received through. */
struct pubkey *path_pubkey;
};

static struct command_result *WARN_UNUSED_RESULT
Expand Down Expand Up @@ -244,6 +250,7 @@ static struct command_result *create_invoicereq(struct command *cmd,

json_add_string(req->js, "invstring", invoice_encode(tmpctx, ir->inv));
json_add_preimage(req->js, "preimage", &ir->preimage);
if(ir->path_pubkey) json_add_pubkey(req->js, "path_pubkey", ir->path_pubkey);
json_add_label(req->js, &ir->offer_id, ir->inv->invreq_payer_id,
ir->inv->invreq_recurrence_counter
? *ir->inv->invreq_recurrence_counter : 0);
Expand Down Expand Up @@ -995,11 +1002,19 @@ static struct command_result *listoffers_done(struct command *cmd,
ir->invreq->invreq_recurrence_cancel = cancel;

/* BOLT #12:
* - if `offer_issuer_id` is present:
* - MUST set `invoice_node_id` to the `offer_issuer_id`
* - if `offer_issuer_id` is present:
* - MUST set `invoice_node_id` to the `offer_issuer_id`
* - otherwise, if `offer_paths` is present:
* - MUST set `invoice_node_id` to the final `blinded_node_id` on the
* path it received the invoice request
*/
/* FIXME: We always provide an offer_issuer_id! */
ir->inv->invoice_node_id = ir->inv->offer_issuer_id;
if(!ir->inv->offer_issuer_id && ir->invreq->offer_paths) {
ir->inv->invoice_node_id = ir->blinded_node_id;

} else {
ir->inv->invoice_node_id = ir->inv->offer_issuer_id;
ir->path_pubkey = NULL;
}

/* BOLT #12:
* - MUST set `invoice_created_at` to the number of seconds since
Expand Down Expand Up @@ -1037,7 +1052,9 @@ static struct command_result *listoffers_done(struct command *cmd,
struct command_result *handle_invoice_request(struct command *cmd,
const u8 *invreqbin,
struct blinded_path *reply_path,
const struct secret *secret)
const struct secret *secret,
const struct pubkey *blinded_node_id,
const struct pubkey *path_pubkey)
{
struct out_req *req;
int bad_feature;
Expand All @@ -1047,6 +1064,8 @@ struct command_result *handle_invoice_request(struct command *cmd,

ir->reply_path = tal_steal(ir, reply_path);
ir->secret = tal_dup_or_null(ir, struct secret, secret);
ir->blinded_node_id = tal_dup(ir, struct pubkey, blinded_node_id);
ir->path_pubkey = tal_dup(ir, struct pubkey, path_pubkey);
ir->invreq = fromwire_tlv_invoice_request(cmd, &cursor, &len);

if (!ir->invreq) {
Expand Down
4 changes: 3 additions & 1 deletion plugins/offers_invreq_hook.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@
struct command_result *handle_invoice_request(struct command *cmd,
const u8 *invreqbin,
struct blinded_path *reply_path STEALS,
const struct secret *secret TAKES);
const struct secret *secret TAKES,
const struct pubkey *blinded_node_id TAKES,
const struct pubkey *path_pubkey TAKES);
#endif /* LIGHTNING_PLUGINS_OFFERS_INVREQ_HOOK_H */
6 changes: 6 additions & 0 deletions plugins/offers_offer.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,12 @@ static struct command_result *found_best_peer(struct command *cmd,
struct secret blinding_path_secret;
struct sha256 offer_id;

/* offer_issuer_id is not needed when offer_paths are used.
* The following line seems to produce a valid offer with
* offer_issuer_id removed.
*/
offinfo->offer->offer_issuer_id = NULL;

/* Note: "id" of offer minus paths */
offer_offer_id(offinfo->offer, &offer_id);

Expand Down
Loading