Skip to content

Commit 3c81fdb

Browse files
committed
Elide metadata from Offer with derived keys
When an Offer uses blinded paths, its metadata consists of a nonce used to derive its signing keys. Now that the blinded paths contain this nonce, elide the metadata as it is now redundant. This saves space and also makes it impossible to derive the signing keys if an invoice request is received with the incorrect nonce. The nonce shouldn't be revealed in this case either to prevent de-anonymization attacks.
1 parent c7afd5c commit 3c81fdb

File tree

3 files changed

+27
-21
lines changed

3 files changed

+27
-21
lines changed

lightning/src/offers/invoice.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1776,7 +1776,7 @@ mod tests {
17761776
.sign(payer_sign).unwrap();
17771777

17781778
if let Err(e) = invoice_request.clone()
1779-
.verify(&expanded_key, &secp_ctx).unwrap()
1779+
.verify_using_nonce(nonce, &expanded_key, &secp_ctx).unwrap()
17801780
.respond_using_derived_keys_no_std(payment_paths(), payment_hash(), now()).unwrap()
17811781
.build_and_sign(&secp_ctx)
17821782
{

lightning/src/offers/offer.rs

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -399,13 +399,13 @@ macro_rules! offer_builder_methods { (
399399
}
400400

401401
let (derived_metadata, keys) = metadata.derive_from(tlv_stream, $self.secp_ctx);
402-
metadata = derived_metadata;
403-
if let Some(keys) = keys {
404-
$self.offer.signing_pubkey = Some(keys.public_key());
402+
match keys {
403+
Some(keys) => $self.offer.signing_pubkey = Some(keys.public_key()),
404+
None => $self.offer.metadata = Some(derived_metadata),
405405
}
406+
} else {
407+
$self.offer.metadata = Some(metadata);
406408
}
407-
408-
$self.offer.metadata = Some(metadata);
409409
}
410410

411411
let mut bytes = Vec::new();
@@ -667,9 +667,9 @@ impl Offer {
667667

668668
#[cfg(async_payments)]
669669
pub(super) fn verify<T: secp256k1::Signing>(
670-
&self, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
670+
&self, nonce: Nonce, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
671671
) -> Result<(OfferId, Option<Keypair>), ()> {
672-
self.contents.verify(&self.bytes, key, secp_ctx)
672+
self.contents.verify_using_nonce(&self.bytes, nonce, key, secp_ctx)
673673
}
674674
}
675675

@@ -1296,6 +1296,7 @@ mod tests {
12961296
let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx)
12971297
.amount_msats(1000)
12981298
.build().unwrap();
1299+
assert!(offer.metadata().is_some());
12991300
assert_eq!(offer.signing_pubkey(), Some(node_id));
13001301

13011302
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1362,16 +1363,9 @@ mod tests {
13621363
.amount_msats(1000)
13631364
.path(blinded_path)
13641365
.build().unwrap();
1366+
assert!(offer.metadata().is_none());
13651367
assert_ne!(offer.signing_pubkey(), Some(node_id));
13661368

1367-
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1368-
.build().unwrap()
1369-
.sign(payer_sign).unwrap();
1370-
match invoice_request.verify(&expanded_key, &secp_ctx) {
1371-
Ok(invoice_request) => assert_eq!(invoice_request.offer_id, offer.id()),
1372-
Err(_) => panic!("unexpected error"),
1373-
}
1374-
13751369
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
13761370
.build().unwrap()
13771371
.sign(payer_sign).unwrap();

lightning/src/offers/static_invoice.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_me
2222
use crate::offers::merkle::{
2323
self, SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash,
2424
};
25+
use crate::offers::nonce::Nonce;
2526
use crate::offers::offer::{
2627
Amount, Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef, Quantity,
2728
};
@@ -97,7 +98,7 @@ impl<'a> StaticInvoiceBuilder<'a> {
9798
pub fn for_offer_using_derived_keys<T: secp256k1::Signing>(
9899
offer: &'a Offer, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
99100
message_paths: Vec<BlindedPath>, created_at: Duration, expanded_key: &ExpandedKey,
100-
secp_ctx: &Secp256k1<T>,
101+
nonce: Nonce, secp_ctx: &Secp256k1<T>,
101102
) -> Result<Self, Bolt12SemanticError> {
102103
if offer.chains().len() > 1 {
103104
return Err(Bolt12SemanticError::UnexpectedChain);
@@ -111,7 +112,7 @@ impl<'a> StaticInvoiceBuilder<'a> {
111112
offer.signing_pubkey().ok_or(Bolt12SemanticError::MissingSigningPubkey)?;
112113

113114
let keys = offer
114-
.verify(&expanded_key, &secp_ctx)
115+
.verify(nonce, &expanded_key, &secp_ctx)
115116
.map_err(|()| Bolt12SemanticError::InvalidMetadata)?
116117
.1
117118
.ok_or(Bolt12SemanticError::MissingSigningPubkey)?;
@@ -623,6 +624,7 @@ mod tests {
623624
vec![blinded_path()],
624625
now,
625626
&expanded_key,
627+
nonce,
626628
&secp_ctx,
627629
)
628630
.unwrap()
@@ -662,6 +664,7 @@ mod tests {
662664
vec![blinded_path()],
663665
now,
664666
&expanded_key,
667+
nonce,
665668
&secp_ctx,
666669
)
667670
.unwrap()
@@ -672,7 +675,7 @@ mod tests {
672675
invoice.write(&mut buffer).unwrap();
673676

674677
assert_eq!(invoice.bytes, buffer.as_slice());
675-
assert!(invoice.metadata().is_some());
678+
assert_eq!(invoice.metadata(), None);
676679
assert_eq!(invoice.amount(), None);
677680
assert_eq!(invoice.description(), None);
678681
assert_eq!(invoice.offer_features(), &OfferFeatures::empty());
@@ -698,13 +701,12 @@ mod tests {
698701
);
699702

700703
let paths = vec![blinded_path()];
701-
let metadata = vec![42; 16];
702704
assert_eq!(
703705
invoice.as_tlv_stream(),
704706
(
705707
OfferTlvStreamRef {
706708
chains: None,
707-
metadata: Some(&metadata),
709+
metadata: None,
708710
currency: None,
709711
amount: None,
710712
description: None,
@@ -762,6 +764,7 @@ mod tests {
762764
vec![blinded_path()],
763765
now,
764766
&expanded_key,
767+
nonce,
765768
&secp_ctx,
766769
)
767770
.unwrap()
@@ -782,6 +785,7 @@ mod tests {
782785
vec![blinded_path()],
783786
now,
784787
&expanded_key,
788+
nonce,
785789
&secp_ctx,
786790
)
787791
.unwrap()
@@ -815,6 +819,7 @@ mod tests {
815819
vec![blinded_path()],
816820
now,
817821
&expanded_key,
822+
nonce,
818823
&secp_ctx,
819824
) {
820825
assert_eq!(e, Bolt12SemanticError::MissingPaths);
@@ -829,6 +834,7 @@ mod tests {
829834
Vec::new(),
830835
now,
831836
&expanded_key,
837+
nonce,
832838
&secp_ctx,
833839
) {
834840
assert_eq!(e, Bolt12SemanticError::MissingPaths);
@@ -849,6 +855,7 @@ mod tests {
849855
vec![blinded_path()],
850856
now,
851857
&expanded_key,
858+
nonce,
852859
&secp_ctx,
853860
) {
854861
assert_eq!(e, Bolt12SemanticError::MissingPaths);
@@ -886,6 +893,7 @@ mod tests {
886893
vec![blinded_path()],
887894
now,
888895
&expanded_key,
896+
nonce,
889897
&secp_ctx,
890898
) {
891899
assert_eq!(e, Bolt12SemanticError::MissingSigningPubkey);
@@ -906,6 +914,7 @@ mod tests {
906914
vec![blinded_path()],
907915
now,
908916
&expanded_key,
917+
nonce,
909918
&secp_ctx,
910919
) {
911920
assert_eq!(e, Bolt12SemanticError::InvalidMetadata);
@@ -937,6 +946,7 @@ mod tests {
937946
vec![blinded_path()],
938947
now,
939948
&expanded_key,
949+
nonce,
940950
&secp_ctx,
941951
) {
942952
assert_eq!(e, Bolt12SemanticError::UnexpectedChain);
@@ -967,6 +977,7 @@ mod tests {
967977
vec![blinded_path()],
968978
now,
969979
&expanded_key,
980+
nonce,
970981
&secp_ctx,
971982
)
972983
.unwrap()
@@ -1007,6 +1018,7 @@ mod tests {
10071018
vec![blinded_path()],
10081019
now,
10091020
&expanded_key,
1021+
nonce,
10101022
&secp_ctx,
10111023
)
10121024
.unwrap()

0 commit comments

Comments
 (0)