X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Foffers%2Finvoice_request.rs;h=f5cb439b58a006d67b5895fb61a932610ba528f0;hb=d94227cc13d2dee0ce1a4f24629d6e58a8a8416d;hp=a7cdbfc0f155157cfa7b7f8e1976953ee750d080;hpb=022eadc4dbf0f60179674f936d604cade6c5dd9e;p=rust-lightning diff --git a/lightning/src/offers/invoice_request.rs b/lightning/src/offers/invoice_request.rs index a7cdbfc0..f5cb439b 100644 --- a/lightning/src/offers/invoice_request.rs +++ b/lightning/src/offers/invoice_request.rs @@ -11,12 +11,12 @@ //! //! An [`InvoiceRequest`] can be built from a parsed [`Offer`] as an "offer to be paid". It is //! typically constructed by a customer and sent to the merchant who had published the corresponding -//! offer. The recipient of the request responds with an [`Invoice`]. +//! offer. The recipient of the request responds with a [`Bolt12Invoice`]. //! //! For an "offer for money" (e.g., refund, ATM withdrawal), where an offer doesn't exist as a //! precursor, see [`Refund`]. //! -//! [`Invoice`]: crate::offers::invoice::Invoice +//! [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice //! [`Refund`]: crate::offers::refund::Refund //! //! ``` @@ -58,19 +58,19 @@ use bitcoin::secp256k1::{KeyPair, Message, PublicKey, Secp256k1, self}; use bitcoin::secp256k1::schnorr::Signature; use core::convert::{Infallible, TryFrom}; use core::ops::Deref; -use crate::chain::keysinterface::EntropySource; +use crate::sign::EntropySource; use crate::io; +use crate::blinded_path::BlindedPath; use crate::ln::PaymentHash; use crate::ln::features::InvoiceRequestFeatures; use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce}; use crate::ln::msgs::DecodeError; -use crate::offers::invoice::{BlindedPayInfo, InvoiceBuilder}; -use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, TlvStream, self}; -use crate::offers::offer::{OFFER_TYPES, Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef}; +use crate::offers::invoice::{BlindedPayInfo, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder}; +use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, self}; +use crate::offers::offer::{Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef}; use crate::offers::parse::{ParseError, ParsedMessage, SemanticError}; -use crate::offers::payer::{PAYER_METADATA_TYPE, PayerContents, PayerTlvStream, PayerTlvStreamRef}; -use crate::offers::signer::{Metadata, MetadataMaterial, self}; -use crate::onion_message::BlindedPath; +use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef}; +use crate::offers::signer::{Metadata, MetadataMaterial}; use crate::util::ser::{HighZeroBytesDroppedBigSize, SeekReadable, WithoutLength, Writeable, Writer}; use crate::util::string::PrintableString; @@ -78,12 +78,14 @@ use crate::prelude::*; const SIGNATURE_TAG: &'static str = concat!("lightning", "invoice_request", "signature"); -const IV_BYTES: &[u8; IV_LEN] = b"LDK Invreq ~~~~~"; +pub(super) const IV_BYTES: &[u8; IV_LEN] = b"LDK Invreq ~~~~~"; /// Builds an [`InvoiceRequest`] from an [`Offer`] for the "offer to be paid" flow. /// /// See [module-level documentation] for usage. /// +/// This is not exported to bindings users as builder patterns don't map outside of move semantics. +/// /// [module-level documentation]: self pub struct InvoiceRequestBuilder<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> { offer: &'a Offer, @@ -94,12 +96,18 @@ pub struct InvoiceRequestBuilder<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signi } /// Indicates how [`InvoiceRequest::payer_id`] will be set. +/// +/// This is not exported to bindings users as builder patterns don't map outside of move semantics. pub trait PayerIdStrategy {} /// [`InvoiceRequest::payer_id`] will be explicitly set. +/// +/// This is not exported to bindings users as builder patterns don't map outside of move semantics. pub struct ExplicitPayerId {} /// [`InvoiceRequest::payer_id`] will be derived. +/// +/// This is not exported to bindings users as builder patterns don't map outside of move semantics. pub struct DerivedPayerId {} impl PayerIdStrategy for ExplicitPayerId {} @@ -239,7 +247,7 @@ impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a fn build_without_checks(mut self) -> (UnsignedInvoiceRequest<'a>, Option, Option<&'b Secp256k1>) { - // Create the metadata for stateless verification of an Invoice. + // Create the metadata for stateless verification of a Bolt12Invoice. let mut keys = None; let secp_ctx = self.secp_ctx.clone(); if self.invoice_request.payer.0.has_derivation_material() { @@ -340,6 +348,8 @@ pub struct UnsignedInvoiceRequest<'a> { impl<'a> UnsignedInvoiceRequest<'a> { /// Signs the invoice request using the given function. + /// + /// This is not exported to bindings users as functions are not yet mapped. pub fn sign(self, sign: F) -> Result> where F: FnOnce(&Message) -> Result @@ -371,12 +381,12 @@ impl<'a> UnsignedInvoiceRequest<'a> { } } -/// An `InvoiceRequest` is a request for an [`Invoice`] formulated from an [`Offer`]. +/// An `InvoiceRequest` is a request for a [`Bolt12Invoice`] formulated from an [`Offer`]. /// /// An offer may provide choices such as quantity, amount, chain, features, etc. An invoice request /// specifies these such that its recipient can send an invoice for payment. /// -/// [`Invoice`]: crate::offers::invoice::Invoice +/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice /// [`Offer`]: crate::offers::offer::Offer #[derive(Clone, Debug)] #[cfg_attr(test, derive(PartialEq))] @@ -386,9 +396,9 @@ pub struct InvoiceRequest { signature: Signature, } -/// The contents of an [`InvoiceRequest`], which may be shared with an [`Invoice`]. +/// The contents of an [`InvoiceRequest`], which may be shared with an [`Bolt12Invoice`]. /// -/// [`Invoice`]: crate::offers::invoice::Invoice +/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice #[derive(Clone, Debug)] #[cfg_attr(test, derive(PartialEq))] pub(super) struct InvoiceRequestContents { @@ -465,11 +475,13 @@ impl InvoiceRequest { /// See [`InvoiceRequest::respond_with_no_std`] for further details where the aforementioned /// creation time is used for the `created_at` parameter. /// + /// This is not exported to bindings users as builder patterns don't map outside of move semantics. + /// /// [`Duration`]: core::time::Duration #[cfg(feature = "std")] pub fn respond_with( - &self, payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, payment_hash: PaymentHash - ) -> Result { + &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash + ) -> Result, SemanticError> { let created_at = std::time::SystemTime::now() .duration_since(std::time::SystemTime::UNIX_EPOCH) .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH"); @@ -480,8 +492,8 @@ impl InvoiceRequest { /// Creates an [`InvoiceBuilder`] for the request with the given required fields. /// /// Unless [`InvoiceBuilder::relative_expiry`] is set, the invoice will expire two hours after - /// `created_at`, which is used to set [`Invoice::created_at`]. Useful for `no-std` builds where - /// [`std::time::SystemTime`] is not available. + /// `created_at`, which is used to set [`Bolt12Invoice::created_at`]. Useful for `no-std` builds + /// where [`std::time::SystemTime`] is not available. /// /// The caller is expected to remember the preimage of `payment_hash` in order to claim a payment /// for the invoice. @@ -493,11 +505,13 @@ impl InvoiceRequest { /// /// Errors if the request contains unknown required features. /// - /// [`Invoice::created_at`]: crate::offers::invoice::Invoice::created_at + /// This is not exported to bindings users as builder patterns don't map outside of move semantics. + /// + /// [`Bolt12Invoice::created_at`]: crate::offers::invoice::Bolt12Invoice::created_at pub fn respond_with_no_std( - &self, payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, payment_hash: PaymentHash, + &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash, created_at: core::time::Duration - ) -> Result { + ) -> Result, SemanticError> { if self.features().requires_unknown_bits() { return Err(SemanticError::UnknownRequiredFeatures); } @@ -505,11 +519,64 @@ impl InvoiceRequest { InvoiceBuilder::for_offer(self, payment_paths, created_at, payment_hash) } - /// Verifies that the request was for an offer created using the given key. + /// Creates an [`InvoiceBuilder`] for the request using the given required fields and that uses + /// derived signing keys from the originating [`Offer`] to sign the [`Bolt12Invoice`]. Must use + /// the same [`ExpandedKey`] as the one used to create the offer. + /// + /// See [`InvoiceRequest::respond_with`] for further details. + /// + /// This is not exported to bindings users as builder patterns don't map outside of move semantics. + /// + /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice + #[cfg(feature = "std")] + pub fn verify_and_respond_using_derived_keys( + &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash, + expanded_key: &ExpandedKey, secp_ctx: &Secp256k1 + ) -> Result, SemanticError> { + let created_at = std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH) + .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH"); + + self.verify_and_respond_using_derived_keys_no_std( + payment_paths, payment_hash, created_at, expanded_key, secp_ctx + ) + } + + /// Creates an [`InvoiceBuilder`] for the request using the given required fields and that uses + /// derived signing keys from the originating [`Offer`] to sign the [`Bolt12Invoice`]. Must use + /// the same [`ExpandedKey`] as the one used to create the offer. + /// + /// See [`InvoiceRequest::respond_with_no_std`] for further details. + /// + /// This is not exported to bindings users as builder patterns don't map outside of move semantics. + /// + /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice + pub fn verify_and_respond_using_derived_keys_no_std( + &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash, + created_at: core::time::Duration, expanded_key: &ExpandedKey, secp_ctx: &Secp256k1 + ) -> Result, SemanticError> { + if self.features().requires_unknown_bits() { + return Err(SemanticError::UnknownRequiredFeatures); + } + + let keys = match self.verify(expanded_key, secp_ctx) { + Err(()) => return Err(SemanticError::InvalidMetadata), + Ok(None) => return Err(SemanticError::InvalidMetadata), + Ok(Some(keys)) => keys, + }; + + InvoiceBuilder::for_offer_using_keys(self, payment_paths, created_at, payment_hash, keys) + } + + /// Verifies that the request was for an offer created using the given key. Returns the derived + /// keys need to sign an [`Bolt12Invoice`] for the request if they could be extracted from the + /// metadata. + /// + /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice pub fn verify( &self, key: &ExpandedKey, secp_ctx: &Secp256k1 - ) -> bool { - self.contents.inner.offer.verify(TlvStream::new(&self.bytes), key, secp_ctx) + ) -> Result, ()> { + self.contents.inner.offer.verify(&self.bytes, key, secp_ctx) } #[cfg(test)] @@ -528,24 +595,16 @@ impl InvoiceRequestContents { self.inner.metadata() } + pub(super) fn derives_keys(&self) -> bool { + self.inner.payer.0.derives_keys() + } + pub(super) fn chain(&self) -> ChainHash { self.inner.chain() } - /// Verifies that the payer metadata was produced from the invoice request in the TLV stream. - pub(super) fn verify( - &self, tlv_stream: TlvStream<'_>, key: &ExpandedKey, secp_ctx: &Secp256k1 - ) -> bool { - let offer_records = tlv_stream.clone().range(OFFER_TYPES); - let invreq_records = tlv_stream.range(INVOICE_REQUEST_TYPES).filter(|record| { - match record.r#type { - PAYER_METADATA_TYPE => false, // Should be outside range - INVOICE_REQUEST_PAYER_ID_TYPE => !self.inner.payer.0.derives_keys(), - _ => true, - } - }); - let tlv_stream = offer_records.chain(invreq_records); - signer::verify_metadata(self.metadata(), key, IV_BYTES, self.payer_id, tlv_stream, secp_ctx) + pub(super) fn payer_id(&self) -> PublicKey { + self.payer_id } pub(super) fn as_tlv_stream(&self) -> PartialInvoiceRequestTlvStreamRef { @@ -602,12 +661,12 @@ impl Writeable for InvoiceRequestContents { } /// Valid type range for invoice_request TLV records. -const INVOICE_REQUEST_TYPES: core::ops::Range = 80..160; +pub(super) const INVOICE_REQUEST_TYPES: core::ops::Range = 80..160; /// TLV record type for [`InvoiceRequest::payer_id`] and [`Refund::payer_id`]. /// /// [`Refund::payer_id`]: crate::offers::refund::Refund::payer_id -const INVOICE_REQUEST_PAYER_ID_TYPE: u64 = 88; +pub(super) const INVOICE_REQUEST_PAYER_ID_TYPE: u64 = 88; tlv_stream!(InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef, INVOICE_REQUEST_TYPES, { (80, chain: ChainHash), @@ -726,11 +785,11 @@ mod tests { use core::num::NonZeroU64; #[cfg(feature = "std")] use core::time::Duration; - use crate::chain::keysinterface::KeyMaterial; + use crate::sign::KeyMaterial; use crate::ln::features::InvoiceRequestFeatures; use crate::ln::inbound_payment::ExpandedKey; use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT}; - use crate::offers::invoice::{Invoice, SIGNATURE_TAG as INVOICE_SIGNATURE_TAG}; + use crate::offers::invoice::{Bolt12Invoice, SIGNATURE_TAG as INVOICE_SIGNATURE_TAG}; use crate::offers::merkle::{SignError, SignatureTlvStreamRef, self}; use crate::offers::offer::{Amount, OfferBuilder, OfferTlvStreamRef, Quantity}; use crate::offers::parse::{ParseError, SemanticError}; @@ -871,7 +930,7 @@ mod tests { let mut encoded_invoice = bytes; signature_tlv_stream.write(&mut encoded_invoice).unwrap(); - let invoice = Invoice::try_from(encoded_invoice).unwrap(); + let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap(); assert!(!invoice.verify(&expanded_key, &secp_ctx)); // Fails verification with altered metadata @@ -895,7 +954,7 @@ mod tests { let mut encoded_invoice = bytes; signature_tlv_stream.write(&mut encoded_invoice).unwrap(); - let invoice = Invoice::try_from(encoded_invoice).unwrap(); + let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap(); assert!(!invoice.verify(&expanded_key, &secp_ctx)); } @@ -941,7 +1000,7 @@ mod tests { let mut encoded_invoice = bytes; signature_tlv_stream.write(&mut encoded_invoice).unwrap(); - let invoice = Invoice::try_from(encoded_invoice).unwrap(); + let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap(); assert!(!invoice.verify(&expanded_key, &secp_ctx)); // Fails verification with altered payer id @@ -965,7 +1024,7 @@ mod tests { let mut encoded_invoice = bytes; signature_tlv_stream.write(&mut encoded_invoice).unwrap(); - let invoice = Invoice::try_from(encoded_invoice).unwrap(); + let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap(); assert!(!invoice.verify(&expanded_key, &secp_ctx)); }