Skip to content

Commit adcfbd6

Browse files
committed
InvoiceRequest metadata verification
1 parent 24e53de commit adcfbd6

File tree

5 files changed

+52
-8
lines changed

5 files changed

+52
-8
lines changed

lightning/src/offers/invoice.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,10 @@ use core::time::Duration;
9696
use crate::io;
9797
use crate::ln::PaymentHash;
9898
use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures};
99+
use crate::ln::inbound_payment::ExpandedKey;
99100
use crate::ln::msgs::DecodeError;
100101
use crate::offers::invoice_request::{InvoiceRequest, InvoiceRequestContents, InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef};
101-
use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, WithoutSignatures, self};
102+
use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, TlvStream, WithoutSignatures, self};
102103
use crate::offers::offer::{Amount, OfferTlvStream, OfferTlvStreamRef};
103104
use crate::offers::parse::{ParseError, ParsedMessage, SemanticError};
104105
use crate::offers::payer::{PayerTlvStream, PayerTlvStreamRef};
@@ -450,6 +451,11 @@ impl Invoice {
450451
self.signature
451452
}
452453

454+
/// Verifies that the invoice was for a request or refund created using the given key.
455+
pub(crate) fn verify(&self, key: &ExpandedKey) -> bool {
456+
self.contents.verify(TlvStream::new(&self.bytes), key)
457+
}
458+
453459
#[cfg(test)]
454460
fn as_tlv_stream(&self) -> FullInvoiceTlvStreamRef {
455461
let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream) =
@@ -493,6 +499,15 @@ impl InvoiceContents {
493499
}
494500
}
495501

502+
fn verify(&self, tlv_stream: TlvStream<'_>, key: &ExpandedKey) -> bool {
503+
match self {
504+
InvoiceContents::ForOffer { invoice_request, .. } => {
505+
invoice_request.verify(tlv_stream, key)
506+
},
507+
_ => todo!(),
508+
}
509+
}
510+
496511
fn as_tlv_stream(&self) -> PartialInvoiceTlvStreamRef {
497512
let (payer, offer, invoice_request) = match self {
498513
InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.as_tlv_stream(),

lightning/src/offers/invoice_request.rs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ use crate::ln::inbound_payment::{ExpandedKey, Nonce};
6565
use crate::ln::msgs::DecodeError;
6666
use crate::offers::invoice::{BlindedPayInfo, InvoiceBuilder};
6767
use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, TlvStream, self};
68-
use crate::offers::offer::{Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef};
68+
use crate::offers::offer::{OFFER_TYPES, Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef};
6969
use crate::offers::parse::{ParseError, ParsedMessage, SemanticError};
70-
use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef};
71-
use crate::offers::signer::{MetadataMaterial, SigningPubkey};
70+
use crate::offers::payer::{PAYER_METADATA_TYPE, PayerContents, PayerTlvStream, PayerTlvStreamRef};
71+
use crate::offers::signer::{MetadataMaterial, SigningPubkey, self};
7272
use crate::onion_message::BlindedPath;
7373
use crate::util::ser::{HighZeroBytesDroppedBigSize, SeekReadable, WithoutLength, Writeable, Writer};
7474
use crate::util::string::PrintableString;
@@ -430,6 +430,20 @@ impl InvoiceRequestContents {
430430
self.chain.unwrap_or_else(|| self.offer.implied_chain())
431431
}
432432

433+
/// Verifies that the payer metadata was produced from the invoice request in the TLV stream.
434+
pub(super) fn verify(&self, tlv_stream: TlvStream<'_>, key: &ExpandedKey) -> bool {
435+
let offer_records = tlv_stream.clone().range(OFFER_TYPES);
436+
let invreq_records = tlv_stream.range(INVOICE_REQUEST_TYPES).filter(|record| {
437+
match record.r#type {
438+
PAYER_METADATA_TYPE => false, // Should be outside range
439+
INVOICE_REQUEST_PAYER_ID_TYPE => false,
440+
_ => true,
441+
}
442+
});
443+
let tlv_stream = offer_records.chain(invreq_records);
444+
signer::verify_metadata(&self.payer.0, key, tlv_stream)
445+
}
446+
433447
pub(super) fn as_tlv_stream(&self) -> PartialInvoiceRequestTlvStreamRef {
434448
let payer = PayerTlvStreamRef {
435449
metadata: Some(&self.payer.0),
@@ -467,12 +481,20 @@ impl Writeable for InvoiceRequestContents {
467481
}
468482
}
469483

470-
tlv_stream!(InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef, 80..160, {
484+
/// Valid type range for invoice_request TLV records.
485+
const INVOICE_REQUEST_TYPES: core::ops::Range<u64> = 80..160;
486+
487+
/// TLV record type for [`InvoiceRequest::payer_id`] and [`Refund::payer_id`].
488+
///
489+
/// [`Refund::payer_id`]: crate::offers::refund::Refund::payer_id
490+
const INVOICE_REQUEST_PAYER_ID_TYPE: u64 = 88;
491+
492+
tlv_stream!(InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef, INVOICE_REQUEST_TYPES, {
471493
(80, chain: ChainHash),
472494
(82, amount: (u64, HighZeroBytesDroppedBigSize)),
473495
(84, features: (InvoiceRequestFeatures, WithoutLength)),
474496
(86, quantity: (u64, HighZeroBytesDroppedBigSize)),
475-
(88, payer_id: PublicKey),
497+
(INVOICE_REQUEST_PAYER_ID_TYPE, payer_id: PublicKey),
476498
(89, payer_note: (String, WithoutLength)),
477499
});
478500

lightning/src/offers/merkle.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ fn tagged_branch_hash_from_engine(
143143

144144
/// [`Iterator`] over a sequence of bytes yielding [`TlvRecord`]s. The input is assumed to be a
145145
/// well-formed TLV stream.
146+
#[derive(Clone)]
146147
pub(super) struct TlvStream<'a> {
147148
data: io::Cursor<&'a [u8]>,
148149
}

lightning/src/offers/offer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ impl Quantity {
626626
}
627627

628628
/// Valid type range for offer TLV records.
629-
const OFFER_TYPES: core::ops::Range<u64> = 1..80;
629+
pub(super) const OFFER_TYPES: core::ops::Range<u64> = 1..80;
630630

631631
/// TLV record type for [`Offer::metadata`].
632632
const OFFER_METADATA_TYPE: u64 = 4;

lightning/src/offers/payer.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ use crate::prelude::*;
2020
#[derive(Clone, Debug)]
2121
pub(super) struct PayerContents(pub Vec<u8>);
2222

23+
/// TLV record type for [`InvoiceRequest::metadata`] and [`Refund::metadata`].
24+
///
25+
/// [`InvoiceRequest::metadata`]: crate::offers::invoice_request::InvoiceRequest::metadata
26+
/// [`Refund::metadata`]: crate::offers::refund::Refund::metadata
27+
pub(super) const PAYER_METADATA_TYPE: u64 = 0;
28+
2329
tlv_stream!(PayerTlvStream, PayerTlvStreamRef, 0..1, {
24-
(0, metadata: (Vec<u8>, WithoutLength)),
30+
(PAYER_METADATA_TYPE, metadata: (Vec<u8>, WithoutLength)),
2531
});

0 commit comments

Comments
 (0)