From b191fd48d502a3476ac0063c3d694092505198f4 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Thu, 19 Oct 2023 17:49:13 -0500 Subject: [PATCH] Check offer expiry when building invoice in no-std Building an invoice will fail if the underlying offer or refund has already expired. The check was skipped in no-std since there is no system clock. However, the invoice creation time can be used instead. This prevents responding to an invoice request if the offer has already expired. --- lightning/src/offers/invoice.rs | 22 ++++++++++++++++++++++ lightning/src/offers/offer.rs | 17 ++++++++++------- lightning/src/offers/refund.rs | 17 ++++++++++------- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/lightning/src/offers/invoice.rs b/lightning/src/offers/invoice.rs index 908d2d4be..1165cfd39 100644 --- a/lightning/src/offers/invoice.rs +++ b/lightning/src/offers/invoice.rs @@ -339,6 +339,12 @@ impl<'a> InvoiceBuilder<'a, ExplicitSigningPubkey> { } } + #[cfg(not(feature = "std"))] { + if self.invoice.is_offer_or_refund_expired_no_std(self.invoice.created_at()) { + return Err(Bolt12SemanticError::AlreadyExpired); + } + } + let InvoiceBuilder { invreq_bytes, invoice, .. } = self; Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice)) } @@ -355,6 +361,12 @@ impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> { } } + #[cfg(not(feature = "std"))] { + if self.invoice.is_offer_or_refund_expired_no_std(self.invoice.created_at()) { + return Err(Bolt12SemanticError::AlreadyExpired); + } + } + let InvoiceBuilder { invreq_bytes, invoice, signing_pubkey_strategy: DerivedSigningPubkey(keys) } = self; @@ -727,6 +739,16 @@ impl InvoiceContents { } } + #[cfg(not(feature = "std"))] + fn is_offer_or_refund_expired_no_std(&self, duration_since_epoch: Duration) -> bool { + match self { + InvoiceContents::ForOffer { invoice_request, .. } => + invoice_request.inner.offer.is_expired_no_std(duration_since_epoch), + InvoiceContents::ForRefund { refund, .. } => + refund.is_expired_no_std(duration_since_epoch), + } + } + fn offer_chains(&self) -> Option> { match self { InvoiceContents::ForOffer { invoice_request, .. } => diff --git a/lightning/src/offers/offer.rs b/lightning/src/offers/offer.rs index ab95d5b19..8e0d46261 100644 --- a/lightning/src/offers/offer.rs +++ b/lightning/src/offers/offer.rs @@ -609,13 +609,16 @@ impl OfferContents { #[cfg(feature = "std")] pub(super) fn is_expired(&self) -> bool { - match self.absolute_expiry { - Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() { - Ok(elapsed) => elapsed > seconds_from_epoch, - Err(_) => false, - }, - None => false, - } + SystemTime::UNIX_EPOCH + .elapsed() + .map(|duration_since_epoch| self.is_expired_no_std(duration_since_epoch)) + .unwrap_or(false) + } + + pub(super) fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool { + self.absolute_expiry + .map(|absolute_expiry| duration_since_epoch > absolute_expiry) + .unwrap_or(false) } pub fn issuer(&self) -> Option { diff --git a/lightning/src/offers/refund.rs b/lightning/src/offers/refund.rs index ecafb2bb5..0a95f7251 100644 --- a/lightning/src/offers/refund.rs +++ b/lightning/src/offers/refund.rs @@ -540,13 +540,16 @@ impl RefundContents { #[cfg(feature = "std")] pub(super) fn is_expired(&self) -> bool { - match self.absolute_expiry { - Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() { - Ok(elapsed) => elapsed > seconds_from_epoch, - Err(_) => false, - }, - None => false, - } + SystemTime::UNIX_EPOCH + .elapsed() + .map(|duration_since_epoch| self.is_expired_no_std(duration_since_epoch)) + .unwrap_or(false) + } + + pub(super) fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool { + self.absolute_expiry + .map(|absolute_expiry| duration_since_epoch > absolute_expiry) + .unwrap_or(false) } pub fn issuer(&self) -> Option { -- 2.39.5