use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_methods_common};
use crate::offers::invoice_request::{INVOICE_REQUEST_PAYER_ID_TYPE, INVOICE_REQUEST_TYPES, IV_BYTES as INVOICE_REQUEST_IV_BYTES, InvoiceRequest, InvoiceRequestContents, InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef};
use crate::offers::merkle::{SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, WithoutSignatures, self};
+use crate::offers::nonce::Nonce;
use crate::offers::offer::{Amount, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity};
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
use crate::offers::payer::{PAYER_METADATA_TYPE, PayerTlvStream, PayerTlvStreamRef};
use crate::offers::refund::{IV_BYTES as REFUND_IV_BYTES, Refund, RefundContents};
-use crate::offers::signer;
+use crate::offers::signer::{Metadata, self};
use crate::util::ser::{HighZeroBytesDroppedBigSize, Iterable, Readable, SeekReadable, WithoutLength, Writeable, Writer};
use crate::util::string::PrintableString;
self.tagged_hash.as_digest().as_ref().clone()
}
- /// Verifies that the invoice was for a request or refund created using the given key. Returns
- /// the associated [`PaymentId`] to use when sending the payment.
+ /// Verifies that the invoice was for a request or refund created using the given key by
+ /// checking the payer metadata from the invoice request.
+ ///
+ /// Returns the associated [`PaymentId`] to use when sending the payment.
pub fn verify<T: secp256k1::Signing>(
&self, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
) -> Result<PaymentId, ()> {
- self.contents.verify(TlvStream::new(&self.bytes), key, secp_ctx)
+ let metadata = match &self.contents {
+ InvoiceContents::ForOffer { invoice_request, .. } => &invoice_request.inner.payer.0,
+ InvoiceContents::ForRefund { refund, .. } => &refund.payer.0,
+ };
+ self.contents.verify(TlvStream::new(&self.bytes), metadata, key, secp_ctx)
+ }
+
+ /// Verifies that the invoice was for a request or refund created using the given key by
+ /// checking a payment id and nonce included with the [`BlindedPath`] for which the invoice was
+ /// sent through.
+ pub fn verify_using_payer_data<T: secp256k1::Signing>(
+ &self, payment_id: PaymentId, nonce: Nonce, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
+ ) -> bool {
+ let metadata = Metadata::payer_data(payment_id, nonce, key);
+ match self.contents.verify(TlvStream::new(&self.bytes), &metadata, key, secp_ctx) {
+ Ok(extracted_payment_id) => payment_id == extracted_payment_id,
+ Err(()) => false,
+ }
}
pub(crate) fn as_tlv_stream(&self) -> FullInvoiceTlvStreamRef {
}
fn verify<T: secp256k1::Signing>(
- &self, tlv_stream: TlvStream<'_>, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
+ &self, tlv_stream: TlvStream<'_>, metadata: &Metadata, key: &ExpandedKey,
+ secp_ctx: &Secp256k1<T>
) -> Result<PaymentId, ()> {
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.derives_keys(),
+ INVOICE_REQUEST_PAYER_ID_TYPE => !metadata.derives_payer_keys(),
_ => true,
}
});
let tlv_stream = offer_records.chain(invreq_records);
- let (metadata, payer_id, iv_bytes) = match self {
- InvoiceContents::ForOffer { invoice_request, .. } => {
- (invoice_request.metadata(), invoice_request.payer_id(), INVOICE_REQUEST_IV_BYTES)
- },
- InvoiceContents::ForRefund { refund, .. } => {
- (refund.metadata(), refund.payer_id(), REFUND_IV_BYTES)
- },
+ let payer_id = self.payer_id();
+ let iv_bytes = match self {
+ InvoiceContents::ForOffer { .. } => INVOICE_REQUEST_IV_BYTES,
+ InvoiceContents::ForRefund { .. } => REFUND_IV_BYTES,
};
- signer::verify_payer_metadata(metadata, key, iv_bytes, payer_id, tlv_stream, secp_ctx)
- }
-
- fn derives_keys(&self) -> bool {
- match self {
- InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.derives_keys(),
- InvoiceContents::ForRefund { refund, .. } => refund.derives_keys(),
- }
+ signer::verify_payer_metadata(
+ metadata.as_ref(), key, iv_bytes, payer_id, tlv_stream, secp_ctx,
+ )
}
fn as_tlv_stream(&self) -> PartialInvoiceTlvStreamRef {
#[derive(Clone, Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub(super) struct InvoiceRequestContentsWithoutPayerId {
- payer: PayerContents,
+ pub(super) payer: PayerContents,
pub(super) offer: OfferContents,
chain: Option<ChainHash>,
amount_msats: Option<u64>,
self.inner.metadata()
}
- pub(super) fn derives_keys(&self) -> bool {
- self.inner.payer.0.derives_payer_keys()
- }
-
pub(super) fn chain(&self) -> ChainHash {
self.inner.chain()
}
Ok(payment_id) => assert_eq!(payment_id, PaymentId([1; 32])),
Err(()) => panic!("verification failed"),
}
+ assert!(!invoice.verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx));
// Fails verification with altered fields
let (
Ok(payment_id) => assert_eq!(payment_id, PaymentId([1; 32])),
Err(()) => panic!("verification failed"),
}
+ assert!(invoice.verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx));
// Fails verification with altered fields
let (
#[derive(Clone, Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub(super) struct RefundContents {
- payer: PayerContents,
+ pub(super) payer: PayerContents,
// offer fields
description: String,
absolute_expiry: Option<Duration>,
self.payer_note.as_ref().map(|payer_note| PrintableString(payer_note.as_str()))
}
- pub(super) fn derives_keys(&self) -> bool {
- self.payer.0.derives_payer_keys()
- }
-
pub(super) fn as_tlv_stream(&self) -> RefundTlvStreamRef {
let payer = PayerTlvStreamRef {
metadata: self.payer.0.as_bytes(),
Ok(payment_id) => assert_eq!(payment_id, PaymentId([1; 32])),
Err(()) => panic!("verification failed"),
}
+ assert!(!invoice.verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx));
let mut tlv_stream = refund.as_tlv_stream();
tlv_stream.2.amount = Some(2000);
Ok(payment_id) => assert_eq!(payment_id, PaymentId([1; 32])),
Err(()) => panic!("verification failed"),
}
+ assert!(invoice.verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx));
// Fails verification with altered fields
let mut tlv_stream = refund.as_tlv_stream();
/// This variant should only be used at verification time, never when building.
RecipientData(Nonce),
+ /// Metadata for deriving keys included as payer data in a blinded path.
+ ///
+ /// This variant should only be used at verification time, never when building.
+ PayerData([u8; PaymentId::LENGTH + Nonce::LENGTH]),
+
/// Metadata to be derived from message contents and given material.
///
/// This variant should only be used at building time.
}
impl Metadata {
+ pub fn payer_data(payment_id: PaymentId, nonce: Nonce, expanded_key: &ExpandedKey) -> Self {
+ let encrypted_payment_id = expanded_key.crypt_for_offer(payment_id.0, nonce);
+
+ let mut bytes = [0u8; PaymentId::LENGTH + Nonce::LENGTH];
+ bytes[..PaymentId::LENGTH].copy_from_slice(encrypted_payment_id.as_slice());
+ bytes[PaymentId::LENGTH..].copy_from_slice(nonce.as_slice());
+
+ Metadata::PayerData(bytes)
+ }
+
pub fn as_bytes(&self) -> Option<&Vec<u8>> {
match self {
Metadata::Bytes(bytes) => Some(bytes),
match self {
Metadata::Bytes(_) => false,
Metadata::RecipientData(_) => { debug_assert!(false); false },
+ Metadata::PayerData(_) => { debug_assert!(false); false },
Metadata::Derived(_) => true,
Metadata::DerivedSigningPubkey(_) => true,
}
// Nonce::LENGTH had been set explicitly.
Metadata::Bytes(bytes) => bytes.len() == PaymentId::LENGTH + Nonce::LENGTH,
Metadata::RecipientData(_) => false,
+ Metadata::PayerData(_) => true,
Metadata::Derived(_) => false,
Metadata::DerivedSigningPubkey(_) => true,
}
// been set explicitly.
Metadata::Bytes(bytes) => bytes.len() == Nonce::LENGTH,
Metadata::RecipientData(_) => true,
+ Metadata::PayerData(_) => false,
Metadata::Derived(_) => false,
Metadata::DerivedSigningPubkey(_) => true,
}
match self {
Metadata::Bytes(_) => self,
Metadata::RecipientData(_) => { debug_assert!(false); self },
+ Metadata::PayerData(_) => { debug_assert!(false); self },
Metadata::Derived(_) => self,
Metadata::DerivedSigningPubkey(material) => Metadata::Derived(material),
}
match self {
Metadata::Bytes(_) => (self, None),
Metadata::RecipientData(_) => { debug_assert!(false); (self, None) },
+ Metadata::PayerData(_) => { debug_assert!(false); (self, None) },
Metadata::Derived(mut metadata_material) => {
tlv_stream.write(&mut metadata_material.hmac).unwrap();
(Metadata::Bytes(metadata_material.derive_metadata()), None)
match self {
Metadata::Bytes(bytes) => &bytes,
Metadata::RecipientData(nonce) => &nonce.0,
+ Metadata::PayerData(bytes) => bytes.as_slice(),
Metadata::Derived(_) => { debug_assert!(false); &[] },
Metadata::DerivedSigningPubkey(_) => { debug_assert!(false); &[] },
}
match self {
Metadata::Bytes(bytes) => bytes.fmt(f),
Metadata::RecipientData(Nonce(bytes)) => bytes.fmt(f),
+ Metadata::PayerData(bytes) => bytes.fmt(f),
Metadata::Derived(_) => f.write_str("Derived"),
Metadata::DerivedSigningPubkey(_) => f.write_str("DerivedSigningPubkey"),
}
false
},
Metadata::RecipientData(_) => false,
+ Metadata::PayerData(_) => false,
Metadata::Derived(_) => false,
Metadata::DerivedSigningPubkey(_) => false,
}