Skip to content

Commit ac6d4cb

Browse files
committed
Include OfferId in VerifiedInvoiceRequest
Extract the OfferId from the offer metadata sent back in the InvoiceRequest and include it in VerifiedInvoiceRequest. This can be used to correspond the eventual payment for the invoice response by including it in the invoice's blinded payment paths.
1 parent b9970ff commit ac6d4cb

File tree

4 files changed

+65
-33
lines changed

4 files changed

+65
-33
lines changed

lightning/src/offers/invoice.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ impl UnsignedBolt12Invoice {
546546
let mut bytes = Vec::new();
547547
unsigned_tlv_stream.write(&mut bytes).unwrap();
548548

549-
let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
549+
let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
550550

551551
Self { bytes, contents, tagged_hash }
552552
}
@@ -1225,7 +1225,7 @@ impl TryFrom<Vec<u8>> for UnsignedBolt12Invoice {
12251225
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream)
12261226
)?;
12271227

1228-
let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
1228+
let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
12291229

12301230
Ok(UnsignedBolt12Invoice { bytes, contents, tagged_hash })
12311231
}
@@ -1370,7 +1370,7 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
13701370
None => return Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSignature)),
13711371
Some(signature) => signature,
13721372
};
1373-
let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
1373+
let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
13741374
let pubkey = contents.fields().signing_pubkey;
13751375
merkle::verify_signature(&signature, &tagged_hash, pubkey)?;
13761376

@@ -1601,7 +1601,7 @@ mod tests {
16011601
assert_eq!(invoice.invoice_features(), &Bolt12InvoiceFeatures::empty());
16021602
assert_eq!(invoice.signing_pubkey(), recipient_pubkey());
16031603

1604-
let message = TaggedHash::new(SIGNATURE_TAG, &invoice.bytes);
1604+
let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &invoice.bytes);
16051605
assert!(merkle::verify_signature(&invoice.signature, &message, recipient_pubkey()).is_ok());
16061606

16071607
let digest = Message::from_slice(&invoice.signable_hash()).unwrap();
@@ -1698,7 +1698,7 @@ mod tests {
16981698
assert_eq!(invoice.invoice_features(), &Bolt12InvoiceFeatures::empty());
16991699
assert_eq!(invoice.signing_pubkey(), recipient_pubkey());
17001700

1701-
let message = TaggedHash::new(SIGNATURE_TAG, &invoice.bytes);
1701+
let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &invoice.bytes);
17021702
assert!(merkle::verify_signature(&invoice.signature, &message, recipient_pubkey()).is_ok());
17031703

17041704
assert_eq!(

lightning/src/offers/invoice_request.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
7272
use crate::ln::msgs::DecodeError;
7373
use crate::offers::invoice::BlindedPayInfo;
7474
use crate::offers::merkle::{SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, self};
75-
use crate::offers::offer::{Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef};
75+
use crate::offers::offer::{Offer, OfferContents, OfferId, OfferTlvStream, OfferTlvStreamRef};
7676
use crate::offers::parse::{Bolt12ParseError, ParsedMessage, Bolt12SemanticError};
7777
use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef};
7878
use crate::offers::signer::{Metadata, MetadataMaterial};
@@ -529,7 +529,7 @@ impl UnsignedInvoiceRequest {
529529
let mut bytes = Vec::new();
530530
unsigned_tlv_stream.write(&mut bytes).unwrap();
531531

532-
let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
532+
let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
533533

534534
Self { bytes, contents, tagged_hash }
535535
}
@@ -607,6 +607,9 @@ pub struct InvoiceRequest {
607607
/// ways to respond depending on whether the signing keys were derived.
608608
#[derive(Clone, Debug)]
609609
pub struct VerifiedInvoiceRequest {
610+
/// The identifier of the [`Offer`] for which the [`InvoiceRequest`] was made.
611+
pub offer_id: OfferId,
612+
610613
/// The verified request.
611614
inner: InvoiceRequest,
612615

@@ -764,8 +767,9 @@ macro_rules! invoice_request_verify_method { ($self: ident, $self_type: ty) => {
764767
#[cfg(c_bindings)]
765768
secp_ctx: &Secp256k1<secp256k1::All>,
766769
) -> Result<VerifiedInvoiceRequest, ()> {
767-
let keys = $self.contents.inner.offer.verify(&$self.bytes, key, secp_ctx)?;
770+
let (offer_id, keys) = $self.contents.inner.offer.verify(&$self.bytes, key, secp_ctx)?;
768771
Ok(VerifiedInvoiceRequest {
772+
offer_id,
769773
#[cfg(not(c_bindings))]
770774
inner: $self,
771775
#[cfg(c_bindings)]
@@ -1022,7 +1026,7 @@ impl TryFrom<Vec<u8>> for UnsignedInvoiceRequest {
10221026
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
10231027
)?;
10241028

1025-
let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
1029+
let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
10261030

10271031
Ok(UnsignedInvoiceRequest { bytes, contents, tagged_hash })
10281032
}
@@ -1046,7 +1050,7 @@ impl TryFrom<Vec<u8>> for InvoiceRequest {
10461050
None => return Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSignature)),
10471051
Some(signature) => signature,
10481052
};
1049-
let message = TaggedHash::new(SIGNATURE_TAG, &bytes);
1053+
let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
10501054
merkle::verify_signature(&signature, &message, contents.payer_id)?;
10511055

10521056
Ok(InvoiceRequest { bytes, contents, signature })
@@ -1192,7 +1196,7 @@ mod tests {
11921196
assert_eq!(invoice_request.payer_id(), payer_pubkey());
11931197
assert_eq!(invoice_request.payer_note(), None);
11941198

1195-
let message = TaggedHash::new(SIGNATURE_TAG, &invoice_request.bytes);
1199+
let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &invoice_request.bytes);
11961200
assert!(merkle::verify_signature(&invoice_request.signature, &message, payer_pubkey()).is_ok());
11971201

11981202
assert_eq!(
@@ -1297,7 +1301,7 @@ mod tests {
12971301
let mut bytes = Vec::new();
12981302
tlv_stream.write(&mut bytes).unwrap();
12991303

1300-
let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes);
1304+
let message = TaggedHash::from_valid_tlv_stream_bytes(INVOICE_SIGNATURE_TAG, &bytes);
13011305
let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap();
13021306
signature_tlv_stream.signature = Some(&signature);
13031307

@@ -1320,7 +1324,7 @@ mod tests {
13201324
let mut bytes = Vec::new();
13211325
tlv_stream.write(&mut bytes).unwrap();
13221326

1323-
let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes);
1327+
let message = TaggedHash::from_valid_tlv_stream_bytes(INVOICE_SIGNATURE_TAG, &bytes);
13241328
let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap();
13251329
signature_tlv_stream.signature = Some(&signature);
13261330

@@ -1369,7 +1373,7 @@ mod tests {
13691373
let mut bytes = Vec::new();
13701374
tlv_stream.write(&mut bytes).unwrap();
13711375

1372-
let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes);
1376+
let message = TaggedHash::from_valid_tlv_stream_bytes(INVOICE_SIGNATURE_TAG, &bytes);
13731377
let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap();
13741378
signature_tlv_stream.signature = Some(&signature);
13751379

@@ -1392,7 +1396,7 @@ mod tests {
13921396
let mut bytes = Vec::new();
13931397
tlv_stream.write(&mut bytes).unwrap();
13941398

1395-
let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes);
1399+
let message = TaggedHash::from_valid_tlv_stream_bytes(INVOICE_SIGNATURE_TAG, &bytes);
13961400
let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap();
13971401
signature_tlv_stream.signature = Some(&signature);
13981402

lightning/src/offers/merkle.rs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,20 @@ pub struct TaggedHash {
3838
}
3939

4040
impl TaggedHash {
41+
/// Creates a tagged hash with the given parameters.
42+
///
43+
/// Panics if `bytes` is not a well-formed TLV stream containing at least one TLV record.
44+
pub(super) fn from_valid_tlv_stream_bytes(tag: &'static str, bytes: &[u8]) -> Self {
45+
let tlv_stream = TlvStream::new(bytes);
46+
Self::from_tlv_stream(tag, tlv_stream)
47+
}
48+
4149
/// Creates a tagged hash with the given parameters.
4250
///
4351
/// Panics if `tlv_stream` is not a well-formed TLV stream containing at least one TLV record.
44-
pub(super) fn new(tag: &'static str, tlv_stream: &[u8]) -> Self {
52+
pub(super) fn from_tlv_stream<'a, I: core::iter::Iterator<Item = TlvRecord<'a>>>(
53+
tag: &'static str, tlv_stream: I
54+
) -> Self {
4555
let tag_hash = sha256::Hash::hash(tag.as_bytes());
4656
let merkle_root = root_hash(tlv_stream);
4757
let digest = Message::from_slice(tagged_hash(tag_hash, merkle_root).as_byte_array()).unwrap();
@@ -141,9 +151,10 @@ pub(super) fn verify_signature(
141151

142152
/// Computes a merkle root hash for the given data, which must be a well-formed TLV stream
143153
/// containing at least one TLV record.
144-
fn root_hash(data: &[u8]) -> sha256::Hash {
154+
fn root_hash<'a, I: core::iter::Iterator<Item = TlvRecord<'a>>>(tlv_stream: I) -> sha256::Hash {
155+
let mut tlv_stream = tlv_stream.peekable();
145156
let nonce_tag = tagged_hash_engine(sha256::Hash::from_engine({
146-
let first_tlv_record = TlvStream::new(&data[..]).next().unwrap();
157+
let first_tlv_record = tlv_stream.peek().unwrap();
147158
let mut engine = sha256::Hash::engine();
148159
engine.input("LnNonce".as_bytes());
149160
engine.input(first_tlv_record.record_bytes);
@@ -153,8 +164,7 @@ fn root_hash(data: &[u8]) -> sha256::Hash {
153164
let branch_tag = tagged_hash_engine(sha256::Hash::hash("LnBranch".as_bytes()));
154165

155166
let mut leaves = Vec::new();
156-
let tlv_stream = TlvStream::new(&data[..]);
157-
for record in tlv_stream.skip_signatures() {
167+
for record in TlvStream::skip_signatures(tlv_stream) {
158168
leaves.push(tagged_hash_from_engine(leaf_tag.clone(), &record.record_bytes));
159169
leaves.push(tagged_hash_from_engine(nonce_tag.clone(), &record.type_bytes));
160170
}
@@ -231,8 +241,10 @@ impl<'a> TlvStream<'a> {
231241
.take_while(move |record| take_range.contains(&record.r#type))
232242
}
233243

234-
fn skip_signatures(self) -> core::iter::Filter<TlvStream<'a>, fn(&TlvRecord) -> bool> {
235-
self.filter(|record| !SIGNATURE_TYPES.contains(&record.r#type))
244+
fn skip_signatures(
245+
tlv_stream: impl core::iter::Iterator<Item = TlvRecord<'a>>
246+
) -> impl core::iter::Iterator<Item = TlvRecord<'a>> {
247+
tlv_stream.filter(|record| !SIGNATURE_TYPES.contains(&record.r#type))
236248
}
237249
}
238250

@@ -280,7 +292,7 @@ impl<'a> Writeable for WithoutSignatures<'a> {
280292
#[inline]
281293
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
282294
let tlv_stream = TlvStream::new(self.0);
283-
for record in tlv_stream.skip_signatures() {
295+
for record in TlvStream::skip_signatures(tlv_stream) {
284296
writer.write_all(record.record_bytes)?;
285297
}
286298
Ok(())
@@ -308,15 +320,15 @@ mod tests {
308320
macro_rules! tlv2 { () => { "02080000010000020003" } }
309321
macro_rules! tlv3 { () => { "03310266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c0351800000000000000010000000000000002" } }
310322
assert_eq!(
311-
super::root_hash(&<Vec<u8>>::from_hex(tlv1!()).unwrap()),
323+
super::root_hash(TlvStream::new(&<Vec<u8>>::from_hex(tlv1!()).unwrap())),
312324
sha256::Hash::from_slice(&<Vec<u8>>::from_hex("b013756c8fee86503a0b4abdab4cddeb1af5d344ca6fc2fa8b6c08938caa6f93").unwrap()).unwrap(),
313325
);
314326
assert_eq!(
315-
super::root_hash(&<Vec<u8>>::from_hex(concat!(tlv1!(), tlv2!())).unwrap()),
327+
super::root_hash(TlvStream::new(&<Vec<u8>>::from_hex(concat!(tlv1!(), tlv2!())).unwrap())),
316328
sha256::Hash::from_slice(&<Vec<u8>>::from_hex("c3774abbf4815aa54ccaa026bff6581f01f3be5fe814c620a252534f434bc0d1").unwrap()).unwrap(),
317329
);
318330
assert_eq!(
319-
super::root_hash(&<Vec<u8>>::from_hex(concat!(tlv1!(), tlv2!(), tlv3!())).unwrap()),
331+
super::root_hash(TlvStream::new(&<Vec<u8>>::from_hex(concat!(tlv1!(), tlv2!(), tlv3!())).unwrap())),
320332
sha256::Hash::from_slice(&<Vec<u8>>::from_hex("ab2e79b1283b0b31e0b035258de23782df6b89a38cfa7237bde69aed1a658c5d").unwrap()).unwrap(),
321333
);
322334
}
@@ -348,7 +360,7 @@ mod tests {
348360
"lnr1qqyqqqqqqqqqqqqqqcp4256ypqqkgzshgysy6ct5dpjk6ct5d93kzmpq23ex2ct5d9ek293pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpjkppqvjx204vgdzgsqpvcp4mldl3plscny0rt707gvpdh6ndydfacz43euzqhrurageg3n7kafgsek6gz3e9w52parv8gs2hlxzk95tzeswywffxlkeyhml0hh46kndmwf4m6xma3tkq2lu04qz3slje2rfthc89vss",
349361
);
350362
assert_eq!(
351-
super::root_hash(&invoice_request.bytes[..]),
363+
super::root_hash(TlvStream::new(&invoice_request.bytes[..])),
352364
sha256::Hash::from_slice(&<Vec<u8>>::from_hex("608407c18ad9a94d9ea2bcdbe170b6c20c462a7833a197621c916f78cf18e624").unwrap()).unwrap(),
353365
);
354366
assert_eq!(

lightning/src/offers/offer.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,13 @@ impl OfferId {
122122
const ID_TAG: &'static str = "LDK Offer ID";
123123

124124
fn from_valid_offer_tlv_stream(bytes: &[u8]) -> Self {
125-
let tagged_hash = TaggedHash::new(Self::ID_TAG, &bytes);
125+
let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(Self::ID_TAG, bytes);
126+
Self(tagged_hash.to_bytes())
127+
}
128+
129+
fn from_valid_invreq_tlv_stream(bytes: &[u8]) -> Self {
130+
let tlv_stream = TlvStream::new(bytes).range(OFFER_TYPES);
131+
let tagged_hash = TaggedHash::from_tlv_stream(Self::ID_TAG, tlv_stream);
126132
Self(tagged_hash.to_bytes())
127133
}
128134
}
@@ -887,7 +893,7 @@ impl OfferContents {
887893
/// Verifies that the offer metadata was produced from the offer in the TLV stream.
888894
pub(super) fn verify<T: secp256k1::Signing>(
889895
&self, bytes: &[u8], key: &ExpandedKey, secp_ctx: &Secp256k1<T>
890-
) -> Result<Option<KeyPair>, ()> {
896+
) -> Result<(OfferId, Option<KeyPair>), ()> {
891897
match self.metadata() {
892898
Some(metadata) => {
893899
let tlv_stream = TlvStream::new(bytes).range(OFFER_TYPES).filter(|record| {
@@ -899,9 +905,13 @@ impl OfferContents {
899905
_ => true,
900906
}
901907
});
902-
signer::verify_recipient_metadata(
908+
let keys = signer::verify_recipient_metadata(
903909
metadata, key, IV_BYTES, self.signing_pubkey(), tlv_stream, secp_ctx
904-
)
910+
)?;
911+
912+
let offer_id = OfferId::from_valid_invreq_tlv_stream(bytes);
913+
914+
Ok((offer_id, keys))
905915
},
906916
None => Err(()),
907917
}
@@ -1246,7 +1256,10 @@ mod tests {
12461256
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
12471257
.build().unwrap()
12481258
.sign(payer_sign).unwrap();
1249-
assert!(invoice_request.verify(&expanded_key, &secp_ctx).is_ok());
1259+
match invoice_request.verify(&expanded_key, &secp_ctx) {
1260+
Ok(invoice_request) => assert_eq!(invoice_request.offer_id, offer.id()),
1261+
Err(_) => panic!("unexpected error"),
1262+
}
12501263

12511264
// Fails verification with altered offer field
12521265
let mut tlv_stream = offer.as_tlv_stream();
@@ -1305,7 +1318,10 @@ mod tests {
13051318
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
13061319
.build().unwrap()
13071320
.sign(payer_sign).unwrap();
1308-
assert!(invoice_request.verify(&expanded_key, &secp_ctx).is_ok());
1321+
match invoice_request.verify(&expanded_key, &secp_ctx) {
1322+
Ok(invoice_request) => assert_eq!(invoice_request.offer_id, offer.id()),
1323+
Err(_) => panic!("unexpected error"),
1324+
}
13091325

13101326
// Fails verification with altered offer field
13111327
let mut tlv_stream = offer.as_tlv_stream();

0 commit comments

Comments
 (0)