From 0d4b9ad6ce2d42de49a81db2cf2e1a31f0719692 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 17 Jul 2024 12:53:25 +0930 Subject: [PATCH] offers: check they use the blinded path if one is specified. Now we can specify a blinded path in an offer, we have to check they used it (or didn't use it, if we didn't have one!). Signed-off-by: Rusty Russell --- plugins/offers.c | 4 +-- plugins/offers_inv_hook.c | 13 ++++++++- plugins/offers_inv_hook.h | 3 +- plugins/offers_invreq_hook.c | 54 +++++++++++++++++++++++++++++++++--- plugins/offers_invreq_hook.h | 3 +- 5 files changed, 68 insertions(+), 9 deletions(-) diff --git a/plugins/offers.c b/plugins/offers.c index 48ea9b838..47136f7b6 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -255,7 +255,7 @@ static struct command_result *onion_message_recv(struct command *cmd, if (reply_path) return handle_invoice_request(cmd, invreqbin, - reply_path); + reply_path, secret); else plugin_log(cmd->plugin, LOG_DBG, "invoice_request without reply_path"); @@ -265,7 +265,7 @@ static struct command_result *onion_message_recv(struct command *cmd, if (invtok) { const u8 *invbin = json_tok_bin_from_hex(tmpctx, buf, invtok); if (invbin) - return handle_invoice(cmd, invbin, reply_path); + return handle_invoice(cmd, invbin, reply_path, secret); } return command_hook_success(cmd); diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index f21ccfe8f..09aab00fa 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -209,7 +209,8 @@ static struct command_result *listinvreqs_error(struct command *cmd, struct command_result *handle_invoice(struct command *cmd, const u8 *invbin, - struct blinded_path *reply_path STEALS) + struct blinded_path *reply_path STEALS, + const struct secret *secret) { size_t len = tal_count(invbin); struct inv *inv = tal(cmd, struct inv); @@ -227,6 +228,16 @@ struct command_result *handle_invoice(struct command *cmd, } invoice_invreq_id(inv->inv, &inv->invreq_id); + /* We never publish invoice_requests with a reply path, so replies via + * a path are invalid */ + if (secret) { + if (command_dev_apis(cmd)) + return fail_inv(cmd, inv, "Unexpected blinded path"); + /* Normally, "I don't know what you're talking about!" */ + return fail_inv(cmd, inv, "Unknown invoice_request %s", + fmt_sha256(tmpctx, &inv->invreq_id)); + } + /* BOLT-offers #12: * A reader of an invoice: * - MUST reject the invoice if `invoice_amount` is not present. diff --git a/plugins/offers_inv_hook.h b/plugins/offers_inv_hook.h index 164b85388..45ecb24ab 100644 --- a/plugins/offers_inv_hook.h +++ b/plugins/offers_inv_hook.h @@ -6,6 +6,7 @@ /* We got an onionmessage with an invoice! reply_path could be NULL. */ struct command_result *handle_invoice(struct command *cmd, const u8 *invbin, - struct blinded_path *reply_path STEALS); + struct blinded_path *reply_path STEALS, + const struct secret *secret); #endif /* LIGHTNING_PLUGINS_OFFERS_INV_HOOK_H */ diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index dd819c5a1..1ce35e779 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,9 @@ struct invreq { /* The preimage for the invoice. */ struct preimage preimage; + + /* Optional secret. */ + const struct secret *secret; }; static struct command_result *WARN_UNUSED_RESULT @@ -820,7 +824,50 @@ static struct command_result *listoffers_done(struct command *cmd, if (arr->size == 0) return fail_invreq(cmd, ir, "Unknown offer"); + /* Now, since we looked up by hash, we know that the entire offer + * is faithfully mirrored in this invreq. */ + + /* BOLT-offers #4: + * + * The final recipient: + *... + * - MUST ignore the message if the `path_id` does not match + * the blinded route it created + */ offertok = arr + 1; + if (ir->secret) { + struct sha256 offer_id; + const u8 *blinding_path_secret; + struct blinded_path **offer_paths; + + if (!ir->invreq->offer_paths) { + /* You should not have used a blinded path for invreq */ + if (command_dev_apis(cmd)) + return fail_invreq(cmd, ir, "Unexpected blinded path"); + return fail_invreq(cmd, ir, "Unknown offer"); + } + /* We generated this without the paths, so temporarily remove them */ + offer_paths = ir->invreq->offer_paths; + ir->invreq->offer_paths = NULL; + invreq_offer_id(ir->invreq, &offer_id); + ir->invreq->offer_paths = offer_paths; + blinding_path_secret = invoice_path_id(tmpctx, + &offerblinding_base, &offer_id); + if (!memeq(ir->secret, tal_bytelen(ir->secret), + blinding_path_secret, tal_bytelen(blinding_path_secret))) { + /* You used the wrong blinded path for invreq */ + if (command_dev_apis(cmd)) + return fail_invreq(cmd, ir, "Wrong blinded path"); + return fail_invreq(cmd, ir, "Unknown offer"); + } + } else { + if (ir->invreq->offer_paths) { + /* You should have used a blinded path for invreq */ + if (command_dev_apis(cmd)) + return fail_invreq(cmd, ir, "Expected blinded path"); + return fail_invreq(cmd, ir, "Unknown offer"); + } + } activetok = json_get_member(buf, offertok, "active"); if (!activetok) { @@ -833,8 +880,6 @@ static struct command_result *listoffers_done(struct command *cmd, if (!active) return fail_invreq(cmd, ir, "Offer no longer available"); - /* Now, since we looked up by hash, we know that the entire offer - * is faithfully mirrored in this invreq. */ b12tok = json_get_member(buf, offertok, "bolt12"); if (!b12tok) { return fail_internalerr(cmd, ir, @@ -972,7 +1017,8 @@ 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) + struct blinded_path *reply_path, + const struct secret *secret) { struct out_req *req; int bad_feature; @@ -981,7 +1027,7 @@ struct command_result *handle_invoice_request(struct command *cmd, struct invreq *ir = tal(cmd, struct invreq); ir->reply_path = tal_steal(ir, reply_path); - + ir->secret = tal_dup_or_null(ir, struct secret, secret); ir->invreq = fromwire_tlv_invoice_request(cmd, &cursor, &len); /* BOLT-offers #12: diff --git a/plugins/offers_invreq_hook.h b/plugins/offers_invreq_hook.h index 055c4655c..a0d63761c 100644 --- a/plugins/offers_invreq_hook.h +++ b/plugins/offers_invreq_hook.h @@ -6,5 +6,6 @@ /* We got an onionmessage with an invreq! */ struct command_result *handle_invoice_request(struct command *cmd, const u8 *invreqbin, - struct blinded_path *reply_path STEALS); + struct blinded_path *reply_path STEALS, + const struct secret *secret TAKES); #endif /* LIGHTNING_PLUGINS_OFFERS_INVREQ_HOOK_H */