From 6a546189e4cc06d59c456d15d1d1fa63e855757d Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 3 Jul 2024 13:47:07 -0500 Subject: [PATCH] Add OffersContext::InvoiceRequest To authenticate that an InvoiceRequest is for a valid Offer, include the nonce from the Offer::metadata in the Offer::paths. This can be used to prevent de-anonymization attacks where an attacker sends requests using self-constructed paths to nodes near the Offer::paths' introduction nodes. --- lightning/src/blinded_path/message.rs | 20 +++++++++++++++++++- lightning/src/ln/channelmanager.rs | 3 ++- lightning/src/offers/nonce.rs | 15 +++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lightning/src/blinded_path/message.rs b/lightning/src/blinded_path/message.rs index c910689ce..74d31c5b9 100644 --- a/lightning/src/blinded_path/message.rs +++ b/lightning/src/blinded_path/message.rs @@ -22,6 +22,7 @@ use crate::io; use crate::io::Cursor; use crate::ln::channelmanager::PaymentId; use crate::ln::onion_utils; +use crate::offers::nonce::Nonce; use crate::onion_message::packet::ControlTlvs; use crate::sign::{NodeSigner, Recipient}; use crate::crypto::streams::ChaChaPolyReadAdapter; @@ -112,6 +113,20 @@ pub enum OffersContext { /// This variant is used when a message is sent without using a [`BlindedPath`] or over one /// created prior to LDK version 0.0.124. Unknown {}, + /// Context used by a [`BlindedPath`] within an [`Offer`]. + /// + /// This variant is intended to be received when handling an [`InvoiceRequest`]. + /// + /// [`Offer`]: crate::offers::offer::Offer + /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest + InvoiceRequest { + /// A nonce used for authenticating that an [`InvoiceRequest`] is for a valid [`Offer`] and + /// for deriving the offer's signing keys. + /// + /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest + /// [`Offer`]: crate::offers::offer::Offer + nonce: Nonce, + }, /// Context used by a [`BlindedPath`] within a [`Refund`] or as a reply path for an /// [`InvoiceRequest`]. /// @@ -138,7 +153,10 @@ impl_writeable_tlv_based_enum!(MessageContext, impl_writeable_tlv_based_enum!(OffersContext, (0, Unknown) => {}, - (1, OutboundPayment) => { + (1, InvoiceRequest) => { + (0, nonce, required), + }, + (2, OutboundPayment) => { (0, payment_id, required), }, ); diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 34267aded..9f05877b9 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -8786,7 +8786,8 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => { let secp_ctx = &$self.secp_ctx; let nonce = Nonce::from_entropy_source(entropy); - let path = $self.create_blinded_paths_using_absolute_expiry(OffersContext::Unknown {}, absolute_expiry) + let context = OffersContext::InvoiceRequest { nonce }; + let path = $self.create_blinded_paths_using_absolute_expiry(context, absolute_expiry) .and_then(|paths| paths.into_iter().next().ok_or(())) .map_err(|_| Bolt12SemanticError::MissingPaths)?; let builder = OfferBuilder::deriving_signing_pubkey(node_id, expanded_key, nonce, secp_ctx) diff --git a/lightning/src/offers/nonce.rs b/lightning/src/offers/nonce.rs index 965a39d84..be4c1d3d2 100644 --- a/lightning/src/offers/nonce.rs +++ b/lightning/src/offers/nonce.rs @@ -9,7 +9,10 @@ //! A number used only once. +use crate::io::{self, Read}; +use crate::ln::msgs::DecodeError; use crate::sign::EntropySource; +use crate::util::ser::{Readable, Writeable, Writer}; use core::ops::Deref; #[allow(unused_imports)] @@ -62,3 +65,15 @@ impl TryFrom<&[u8]> for Nonce { Ok(Self(copied_bytes)) } } + +impl Writeable for Nonce { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.0.write(w) + } +} + +impl Readable for Nonce { + fn read(r: &mut R) -> Result { + Ok(Nonce(Readable::read(r)?)) + } +} -- 2.39.5