Skip to content

Commit df5d7ea

Browse files
committed
Elide nonce from payer metadata
InvoiceRequest and Refund have payer_metadata which consists of an encrypted payment id and a nonce used to derive its signing keys and authenticate any corresponding invoices. Now that the blinded paths include this data in their OffersContext, remove the nonce as it is now redundant. Keep the encrypted payment id as some data is needed in the payer metadata according to the spec. This saves space and prevents de-anonymization attacks as along as the nonce isn't revealed.
1 parent 4ed37d8 commit df5d7ea

File tree

5 files changed

+45
-44
lines changed

5 files changed

+45
-44
lines changed

lightning/src/ln/channelmanager.rs

+27-22
Original file line numberDiff line numberDiff line change
@@ -4203,15 +4203,35 @@ where
42034203
/// whether or not the payment was successful.
42044204
///
42054205
/// [timer tick]: Self::timer_tick_occurred
4206-
pub fn send_payment_for_bolt12_invoice(&self, invoice: &Bolt12Invoice) -> Result<(), Bolt12PaymentError> {
4207-
let secp_ctx = &self.secp_ctx;
4208-
let expanded_key = &self.inbound_payment_key;
4209-
match invoice.verify(expanded_key, secp_ctx) {
4206+
pub fn send_payment_for_bolt12_invoice(
4207+
&self, invoice: &Bolt12Invoice, context: &OffersContext,
4208+
) -> Result<(), Bolt12PaymentError> {
4209+
match self.verify_bolt12_invoice(invoice, context) {
42104210
Ok(payment_id) => self.send_payment_for_verified_bolt12_invoice(invoice, payment_id),
42114211
Err(()) => Err(Bolt12PaymentError::UnexpectedInvoice),
42124212
}
42134213
}
42144214

4215+
fn verify_bolt12_invoice(
4216+
&self, invoice: &Bolt12Invoice, context: &OffersContext,
4217+
) -> Result<PaymentId, ()> {
4218+
let secp_ctx = &self.secp_ctx;
4219+
let expanded_key = &self.inbound_payment_key;
4220+
4221+
match context {
4222+
OffersContext::Unknown {} if invoice.is_for_refund_without_paths() => {
4223+
invoice.verify(expanded_key, secp_ctx)
4224+
},
4225+
OffersContext::OutboundPayment { payment_id, nonce } => {
4226+
invoice
4227+
.verify_using_payer_data(*payment_id, *nonce, expanded_key, secp_ctx)
4228+
.then(|| *payment_id)
4229+
.ok_or(())
4230+
},
4231+
_ => Err(()),
4232+
}
4233+
}
4234+
42154235
fn send_payment_for_verified_bolt12_invoice(&self, invoice: &Bolt12Invoice, payment_id: PaymentId) -> Result<(), Bolt12PaymentError> {
42164236
let best_block_height = self.best_block.read().unwrap().height;
42174237
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
@@ -10807,24 +10827,9 @@ where
1080710827
}
1080810828
},
1080910829
OffersMessage::Invoice(invoice) => {
10810-
let payer_data = match context {
10811-
OffersContext::Unknown {} if invoice.is_for_refund_without_paths() => None,
10812-
OffersContext::OutboundPayment { payment_id, nonce } => Some((payment_id, nonce)),
10813-
_ => return ResponseInstruction::NoResponse,
10814-
};
10815-
10816-
let payment_id = match payer_data {
10817-
Some((payment_id, nonce)) => {
10818-
if invoice.verify_using_payer_data(payment_id, nonce, expanded_key, secp_ctx) {
10819-
payment_id
10820-
} else {
10821-
return ResponseInstruction::NoResponse;
10822-
}
10823-
},
10824-
None => match invoice.verify(expanded_key, secp_ctx) {
10825-
Ok(payment_id) => payment_id,
10826-
Err(()) => return ResponseInstruction::NoResponse,
10827-
},
10830+
let payment_id = match self.verify_bolt12_invoice(&invoice, &context) {
10831+
Ok(payment_id) => payment_id,
10832+
Err(()) => return ResponseInstruction::NoResponse,
1082810833
};
1082910834

1083010835
let result = {

lightning/src/ln/offers_tests.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -1085,10 +1085,10 @@ fn pays_bolt12_invoice_asynchronously() {
10851085
let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
10861086
bob.onion_messenger.handle_onion_message(&alice_id, &onion_message);
10871087

1088-
let invoice = match get_event!(bob, Event::InvoiceReceived) {
1089-
Event::InvoiceReceived { payment_id: actual_payment_id, invoice, .. } => {
1088+
let (invoice, context) = match get_event!(bob, Event::InvoiceReceived) {
1089+
Event::InvoiceReceived { payment_id: actual_payment_id, invoice, context, .. } => {
10901090
assert_eq!(actual_payment_id, payment_id);
1091-
invoice
1091+
(invoice, context)
10921092
},
10931093
_ => panic!("No Event::InvoiceReceived"),
10941094
};
@@ -1099,9 +1099,9 @@ fn pays_bolt12_invoice_asynchronously() {
10991099
assert_eq!(path.introduction_node, IntroductionNode::NodeId(alice_id));
11001100
}
11011101

1102-
assert!(bob.node.send_payment_for_bolt12_invoice(&invoice).is_ok());
1102+
assert!(bob.node.send_payment_for_bolt12_invoice(&invoice, &context).is_ok());
11031103
assert_eq!(
1104-
bob.node.send_payment_for_bolt12_invoice(&invoice),
1104+
bob.node.send_payment_for_bolt12_invoice(&invoice, &context),
11051105
Err(Bolt12PaymentError::DuplicateInvoice),
11061106
);
11071107

@@ -1112,7 +1112,7 @@ fn pays_bolt12_invoice_asynchronously() {
11121112
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
11131113

11141114
assert_eq!(
1115-
bob.node.send_payment_for_bolt12_invoice(&invoice),
1115+
bob.node.send_payment_for_bolt12_invoice(&invoice, &context),
11161116
Err(Bolt12PaymentError::DuplicateInvoice),
11171117
);
11181118

@@ -1121,7 +1121,7 @@ fn pays_bolt12_invoice_asynchronously() {
11211121
}
11221122

11231123
assert_eq!(
1124-
bob.node.send_payment_for_bolt12_invoice(&invoice),
1124+
bob.node.send_payment_for_bolt12_invoice(&invoice, &context),
11251125
Err(Bolt12PaymentError::UnexpectedInvoice),
11261126
);
11271127
}

lightning/src/offers/invoice_request.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -1487,10 +1487,7 @@ mod tests {
14871487
.unwrap()
14881488
.build().unwrap()
14891489
.sign(recipient_sign).unwrap();
1490-
match invoice.verify(&expanded_key, &secp_ctx) {
1491-
Ok(payment_id) => assert_eq!(payment_id, PaymentId([1; 32])),
1492-
Err(()) => panic!("verification failed"),
1493-
}
1490+
assert!(invoice.verify(&expanded_key, &secp_ctx).is_err());
14941491
assert!(invoice.verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx));
14951492

14961493
// Fails verification with altered fields
@@ -1514,7 +1511,7 @@ mod tests {
15141511
signature_tlv_stream.write(&mut encoded_invoice).unwrap();
15151512

15161513
let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap();
1517-
assert!(invoice.verify(&expanded_key, &secp_ctx).is_err());
1514+
assert!(!invoice.verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx));
15181515

15191516
// Fails verification with altered payer id
15201517
let (
@@ -1537,7 +1534,7 @@ mod tests {
15371534
signature_tlv_stream.write(&mut encoded_invoice).unwrap();
15381535

15391536
let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap();
1540-
assert!(invoice.verify(&expanded_key, &secp_ctx).is_err());
1537+
assert!(!invoice.verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx));
15411538
}
15421539

15431540
#[test]

lightning/src/offers/refund.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,15 @@ macro_rules! refund_builder_methods { (
191191
///
192192
/// Also, sets the metadata when [`RefundBuilder::build`] is called such that it can be used by
193193
/// [`Bolt12Invoice::verify`] to determine if the invoice was produced for the refund given an
194-
/// [`ExpandedKey`].
194+
/// [`ExpandedKey`]. However, if [`RefundBuilder::path`] is called, then the metadata must be
195+
/// included in each [`BlindedPath`] instead. In this case, use
196+
/// [`Bolt12Invoice::verify_using_payer_data`].
195197
///
196198
/// The `payment_id` is encrypted in the metadata and should be unique. This ensures that only
197199
/// one invoice will be paid for the refund and that payments can be uniquely identified.
198200
///
199201
/// [`Bolt12Invoice::verify`]: crate::offers::invoice::Bolt12Invoice::verify
202+
/// [`Bolt12Invoice::verify_using_payer_data`]: crate::offers::invoice::Bolt12Invoice::verify_using_payer_data
200203
/// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
201204
pub fn deriving_payer_id(
202205
node_id: PublicKey, expanded_key: &ExpandedKey, nonce: Nonce,
@@ -1107,10 +1110,7 @@ mod tests {
11071110
.unwrap()
11081111
.build().unwrap()
11091112
.sign(recipient_sign).unwrap();
1110-
match invoice.verify(&expanded_key, &secp_ctx) {
1111-
Ok(payment_id) => assert_eq!(payment_id, PaymentId([1; 32])),
1112-
Err(()) => panic!("verification failed"),
1113-
}
1113+
assert!(invoice.verify(&expanded_key, &secp_ctx).is_err());
11141114
assert!(invoice.verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx));
11151115

11161116
// Fails verification with altered fields
@@ -1125,7 +1125,7 @@ mod tests {
11251125
.unwrap()
11261126
.build().unwrap()
11271127
.sign(recipient_sign).unwrap();
1128-
assert!(invoice.verify(&expanded_key, &secp_ctx).is_err());
1128+
assert!(!invoice.verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx));
11291129

11301130
// Fails verification with altered payer_id
11311131
let mut tlv_stream = refund.as_tlv_stream();
@@ -1140,7 +1140,7 @@ mod tests {
11401140
.unwrap()
11411141
.build().unwrap()
11421142
.sign(recipient_sign).unwrap();
1143-
assert!(invoice.verify(&expanded_key, &secp_ctx).is_err());
1143+
assert!(!invoice.verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx));
11441144
}
11451145

11461146
#[test]

lightning/src/offers/signer.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,7 @@ impl MetadataMaterial {
249249
self.hmac.input(DERIVED_METADATA_AND_KEYS_HMAC_INPUT);
250250
self.maybe_include_encrypted_payment_id();
251251

252-
let mut bytes = self.encrypted_payment_id.map(|id| id.to_vec()).unwrap_or(vec![]);
253-
bytes.extend_from_slice(self.nonce.as_slice());
252+
let bytes = self.encrypted_payment_id.map(|id| id.to_vec()).unwrap_or(vec![]);
254253

255254
let hmac = Hmac::from_engine(self.hmac);
256255
let privkey = SecretKey::from_slice(hmac.as_byte_array()).unwrap();

0 commit comments

Comments
 (0)