Skip to content

Commit fbcf9db

Browse files
committed
Changelog-Fixed
-Fixing offers when offer_paths are present and offer_issuer_id is no longer needed. -listoffers_done: Fix invoice_node_id so it is set to the final blinded_node_id when offer_issuer_id is not present and offers_path is present. -Fixing the signature of invoices without an offer issuer (i.e. using a blinded node id for signing).
1 parent e02f76b commit fbcf9db

File tree

12 files changed

+106
-41
lines changed

12 files changed

+106
-41
lines changed

common/hsm_version.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
* v6 with hsm_secret struct cleanup: 06c56396fe42f4f47911d7f865dd0004d264fc1348f89547743755b6b33fec90
3434
* v6 with hsm_secret_type TLV: 7bb5deb2367482feb084d304ee14b2373d42910ad56484fbf47614dbb3d4cb74
3535
* v6 with bip86_base in TLV: 6bb6e6ee256f22a6fb41856c90feebde3065a9074e79a46731e453a932be83f0
36+
* v7 with sign_bolt12 using path_pubkey for invoices: 53792d2d257dd1b1b29d5945903c8d11190b82d1ff27d44d9ac155d06851de5c
3637
*/
3738
#define HSM_MIN_VERSION 5
38-
#define HSM_MAX_VERSION 6
39+
#define HSM_MAX_VERSION 7
3940
#endif /* LIGHTNING_COMMON_HSM_VERSION_H */

connectd/connectd_wire.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,8 @@ msgdata,connectd_got_onionmsg_to_us,path_secret,?secret,
170170
msgdata,connectd_got_onionmsg_to_us,reply,?blinded_path,
171171
msgdata,connectd_got_onionmsg_to_us,rawmsg_len,u16,
172172
msgdata,connectd_got_onionmsg_to_us,rawmsg,u8,rawmsg_len
173+
msgdata,connectd_got_onionmsg_to_us,final_alias,pubkey,
174+
msgdata,connectd_got_onionmsg_to_us,path_pubkey,pubkey,
173175

174176
# Lightningd tells us to send an onion message.
175177
msgtype,connectd_send_onionmsg,2041

connectd/onion_message.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ static const char *handle_onion(const tal_t *ctx,
7272
take(towire_connectd_got_onionmsg_to_us(NULL,
7373
final_path_id,
7474
final_om->reply_path,
75-
omsg)));
75+
omsg, &final_alias, path_key)));
7676
} else {
7777
struct node_id next_node_id;
7878
struct peer *next_peer;

hsmd/hsmd_wire.csv

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -403,9 +403,7 @@ msgtype,hsmd_sign_bolt12,25
403403
msgdata,hsmd_sign_bolt12,messagename,wirestring,
404404
msgdata,hsmd_sign_bolt12,fieldname,wirestring,
405405
msgdata,hsmd_sign_bolt12,merkleroot,sha256,
406-
# This is for invreq payer_id (temporary keys)
407-
msgdata,hsmd_sign_bolt12,publictweaklen,u16,
408-
msgdata,hsmd_sign_bolt12,publictweak,u8,publictweaklen
406+
msgdata,hsmd_sign_bolt12,path_pubkey,?pubkey,
409407

410408
#include <bitcoin/signature.h>
411409
msgtype,hsmd_sign_bolt12_reply,125

hsmd/libhsmd.c

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,7 @@ static u8 *handle_sign_option_will_fund_offer(struct hsmd_client *c,
816816
return towire_hsmd_sign_option_will_fund_offer_reply(NULL, &sig);
817817
}
818818

819+
/*
819820
static void payer_key_tweak(const struct pubkey *bolt12,
820821
const u8 *publictweak, size_t publictweaklen,
821822
struct sha256 *tweak)
@@ -830,48 +831,63 @@ static void payer_key_tweak(const struct pubkey *bolt12,
830831
sha256_update(&sha, publictweak, publictweaklen);
831832
sha256_done(&sha, tweak);
832833
}
834+
*/
833835

834-
/*~ lightningd asks us to sign a bolt12 (e.g. offer). */
836+
static void node_blinded_privkey(const struct pubkey *path_pubkey, struct privkey *blinded_privkey)
837+
{
838+
struct secret ss;
839+
struct secret node_id_blinding;
840+
841+
node_key(blinded_privkey, NULL);
842+
843+
/* BOLT #4:
844+
* - $`ss_i = SHA256(e_i * N_i) = SHA256(k_i * E_i)`$
845+
* (ECDH shared secret known only by $`N_r`$ and $`N_i`$)
846+
*/
847+
if (secp256k1_ecdh(secp256k1_ctx, ss.data,
848+
&path_pubkey->pubkey, blinded_privkey->secret.data,
849+
NULL, NULL) != 1)
850+
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
851+
"Could not compute ss from path_key.");
852+
853+
/* BOLT #4:
854+
* - $`B_i = HMAC256(\text{"blinded\_node\_id"}, ss_i) * N_i`$
855+
* (blinded `node_id` for $`N_i`$, private key known only by $`N_i`$)
856+
*/
857+
subkey_from_hmac("blinded_node_id", &ss, &node_id_blinding);
858+
859+
if (secp256k1_ec_seckey_tweak_mul(secp256k1_ctx,
860+
blinded_privkey->secret.data,
861+
node_id_blinding.data) != 1)
862+
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
863+
"Could tweak bolt12 key.");
864+
}
865+
866+
/*~ lightningd asks us to sign a bolt12 invoice. */
835867
static u8 *handle_sign_bolt12(struct hsmd_client *c, const u8 *msg_in)
836868
{
837869
char *messagename, *fieldname;
838870
struct sha256 merkle, sha;
839871
struct bip340sig sig;
840872
secp256k1_keypair kp;
841-
u8 *publictweak;
873+
struct pubkey* path_pubkey;
842874

843875
if (!fromwire_hsmd_sign_bolt12(tmpctx, msg_in,
844876
&messagename, &fieldname, &merkle,
845-
&publictweak))
877+
&path_pubkey))
846878
return hsmd_status_malformed_request(c, msg_in);
847879

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

850-
if (!publictweak) {
882+
if (!path_pubkey) {
851883
node_schnorrkey(&kp);
852884
} else {
853-
/* If we're tweaking key, we use bolt12 key */
854-
struct privkey tweakedkey;
855-
struct pubkey bolt12;
856-
struct sha256 tweak;
885+
struct privkey blinded_privkey;
857886

858-
if (secp256k1_ec_pubkey_create(secp256k1_ctx, &bolt12.pubkey,
859-
secretstuff.bolt12.data) != 1)
860-
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
861-
"Could derive bolt12 public key.");
862-
863-
payer_key_tweak(&bolt12, publictweak, tal_bytelen(publictweak),
864-
&tweak);
865-
866-
tweakedkey.secret = secretstuff.bolt12;
867-
if (secp256k1_ec_seckey_tweak_add(secp256k1_ctx,
868-
tweakedkey.secret.data,
869-
tweak.u.u8) != 1)
870-
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
871-
"Could tweak bolt12 key.");
887+
node_blinded_privkey(path_pubkey, &blinded_privkey);
872888

873889
if (secp256k1_keypair_create(secp256k1_ctx, &kp,
874-
tweakedkey.secret.data) != 1)
890+
blinded_privkey.secret.data) != 1)
875891
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
876892
"Failed to derive bolt12 keypair");
877893
}

lightningd/invoice.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -479,15 +479,16 @@ static bool hsm_sign_b11(const u5 *u5bytes,
479479
}
480480

481481
static void hsm_sign_b12_invoice(struct lightningd *ld,
482-
struct tlv_invoice *invoice)
482+
struct tlv_invoice *invoice,
483+
const struct pubkey* path_pubkey)
483484
{
484485
struct sha256 merkle;
485486
const u8 *msg;
486487

487488
assert(!invoice->signature);
488489

489490
merkle_tlv(invoice->fields, &merkle);
490-
msg = towire_hsmd_sign_bolt12(NULL, "invoice", "signature", &merkle, NULL);
491+
msg = towire_hsmd_sign_bolt12(NULL, "invoice", "signature", &merkle, path_pubkey);
491492

492493
msg = hsm_sync_req(tmpctx, ld, take(msg));
493494
invoice->signature = tal(invoice, struct bip340sig);
@@ -1672,6 +1673,7 @@ static struct command_result *json_createinvoice(struct command *cmd,
16721673
const char *invstring;
16731674
struct json_escape *label;
16741675
struct preimage *preimage;
1676+
struct pubkey *path_pubkey;
16751677
u64 inv_dbid;
16761678
struct sha256 payment_hash;
16771679
struct json_stream *response;
@@ -1685,6 +1687,7 @@ static struct command_result *json_createinvoice(struct command *cmd,
16851687
p_req("invstring", param_invstring, &invstring),
16861688
p_req("label", param_label, &label),
16871689
p_req("preimage", param_preimage, &preimage),
1690+
p_opt("path_pubkey", param_pubkey, &path_pubkey),
16881691
NULL))
16891692
return command_param_failed();
16901693

@@ -1758,7 +1761,8 @@ static struct command_result *json_createinvoice(struct command *cmd,
17581761
if (inv->signature)
17591762
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
17601763
"invoice already signed");
1761-
hsm_sign_b12_invoice(cmd->ld, inv);
1764+
1765+
hsm_sign_b12_invoice(cmd->ld, inv, path_pubkey);
17621766
b12enc = invoice_encode(cmd, inv);
17631767

17641768
if (inv->offer_issuer_id || inv->offer_paths) {

lightningd/onion_message.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ struct onion_message_hook_payload {
1212
/* Optional */
1313
struct blinded_path *reply_path;
1414
struct secret *pathsecret;
15+
struct pubkey blinded_node_id;
16+
struct pubkey path_pubkey;
1517
struct tlv_onionmsg_tlv *om;
1618
};
1719

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

54+
json_add_pubkey(stream, "blinded_node_id", &payload->blinded_node_id);
55+
json_add_pubkey(stream, "path_pubkey", &payload->path_pubkey);
56+
5257
if (payload->reply_path)
5358
json_add_blindedpath(plugin, stream, "reply_blindedpath",
5459
payload->reply_path);
@@ -114,7 +119,9 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg)
114119
if (!fromwire_connectd_got_onionmsg_to_us(payload, msg,
115120
&payload->pathsecret,
116121
&payload->reply_path,
117-
&submsg)) {
122+
&submsg,
123+
&payload->blinded_node_id,
124+
&payload->path_pubkey)) {
118125
log_broken(ld->log, "bad got_onionmsg_tous: %s",
119126
tal_hex(tmpctx, msg));
120127
return;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ u8 *towire_hsmd_preapprove_keysend(const tal_t *ctx UNNEEDED, const struct node_
650650
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)
651651
{ fprintf(stderr, "towire_hsmd_preapprove_keysend_check called!\n"); abort(); }
652652
/* Generated stub for towire_hsmd_sign_bolt12 */
653-
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)
653+
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)
654654
{ fprintf(stderr, "towire_hsmd_sign_bolt12 called!\n"); abort(); }
655655
/* Generated stub for towire_hsmd_sign_commitment_tx */
656656
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)

plugins/offers.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,11 @@ static struct command_result *onion_message_recv(struct command *cmd,
263263
const char *buf,
264264
const jsmntok_t *params)
265265
{
266-
const jsmntok_t *om, *secrettok, *replytok, *invreqtok, *invtok;
266+
const jsmntok_t *om, *secrettok, *nodeidtok, *pubkeytok, *replytok, *invreqtok, *invtok;
267267
struct blinded_path *reply_path = NULL;
268268
struct secret *secret;
269+
struct pubkey *blinded_node_id;
270+
struct pubkey *path_pubkey;
269271

270272
om = json_get_member(buf, params, "onion_message");
271273
secrettok = json_get_member(buf, om, "pathsecret");
@@ -274,6 +276,14 @@ static struct command_result *onion_message_recv(struct command *cmd,
274276
json_to_secret(buf, secrettok, secret);
275277
} else
276278
secret = NULL;
279+
nodeidtok = json_get_member(buf, om, "blinded_node_id");
280+
if(!nodeidtok) plugin_err(cmd->plugin, "Missing blinded node id");
281+
blinded_node_id = tal(tmpctx, struct pubkey);
282+
json_to_pubkey(buf, nodeidtok, blinded_node_id);
283+
pubkeytok = json_get_member(buf, om, "path_pubkey");
284+
if(!pubkeytok) plugin_err(cmd->plugin, "Missing path pubkey");
285+
path_pubkey = tal(tmpctx, struct pubkey);
286+
json_to_pubkey(buf, pubkeytok, path_pubkey);
277287

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

304314
invtok = json_get_member(buf, om, "invoice");

plugins/offers_invreq_hook.c

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ struct invreq {
3636

3737
/* Optional secret. */
3838
const struct secret *secret;
39+
40+
/* The blinded node id the invoice request was received through. */
41+
struct pubkey *blinded_node_id;
42+
43+
/* The path pubkey the invoice request was received through. */
44+
struct pubkey *path_pubkey;
3945
};
4046

4147
static struct command_result *WARN_UNUSED_RESULT
@@ -244,6 +250,7 @@ static struct command_result *create_invoicereq(struct command *cmd,
244250

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

9971004
/* BOLT #12:
998-
* - if `offer_issuer_id` is present:
999-
* - MUST set `invoice_node_id` to the `offer_issuer_id`
1005+
* - if `offer_issuer_id` is present:
1006+
* - MUST set `invoice_node_id` to the `offer_issuer_id`
1007+
* - otherwise, if `offer_paths` is present:
1008+
* - MUST set `invoice_node_id` to the final `blinded_node_id` on the
1009+
* path it received the invoice request
10001010
*/
1001-
/* FIXME: We always provide an offer_issuer_id! */
1002-
ir->inv->invoice_node_id = ir->inv->offer_issuer_id;
1011+
if(!ir->inv->offer_issuer_id && ir->invreq->offer_paths) {
1012+
ir->inv->invoice_node_id = ir->blinded_node_id;
1013+
1014+
} else {
1015+
ir->inv->invoice_node_id = ir->inv->offer_issuer_id;
1016+
ir->path_pubkey = NULL;
1017+
}
10031018

10041019
/* BOLT #12:
10051020
* - MUST set `invoice_created_at` to the number of seconds since
@@ -1037,7 +1052,9 @@ static struct command_result *listoffers_done(struct command *cmd,
10371052
struct command_result *handle_invoice_request(struct command *cmd,
10381053
const u8 *invreqbin,
10391054
struct blinded_path *reply_path,
1040-
const struct secret *secret)
1055+
const struct secret *secret,
1056+
const struct pubkey *blinded_node_id,
1057+
const struct pubkey *path_pubkey)
10411058
{
10421059
struct out_req *req;
10431060
int bad_feature;
@@ -1047,6 +1064,8 @@ struct command_result *handle_invoice_request(struct command *cmd,
10471064

10481065
ir->reply_path = tal_steal(ir, reply_path);
10491066
ir->secret = tal_dup_or_null(ir, struct secret, secret);
1067+
ir->blinded_node_id = tal_dup(ir, struct pubkey, blinded_node_id);
1068+
ir->path_pubkey = tal_dup(ir, struct pubkey, path_pubkey);
10501069
ir->invreq = fromwire_tlv_invoice_request(cmd, &cursor, &len);
10511070

10521071
if (!ir->invreq) {

0 commit comments

Comments
 (0)