Skip to content

Commit

Permalink
Elide nonce from payer metadata
Browse files Browse the repository at this point in the history
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 need in the
payer metadata according to the spec. This saves space and prevents
de-anonymization attacks as along as the nonce isn't revealed.
  • Loading branch information
jkczyz committed Jul 15, 2024
1 parent b9f0ece commit 9f7fd0d
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 39 deletions.
49 changes: 27 additions & 22 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4106,15 +4106,35 @@ where
/// whether or not the payment was successful.
///
/// [timer tick]: Self::timer_tick_occurred
pub fn send_payment_for_bolt12_invoice(&self, invoice: &Bolt12Invoice) -> Result<(), Bolt12PaymentError> {
let secp_ctx = &self.secp_ctx;
let expanded_key = &self.inbound_payment_key;
match invoice.verify(expanded_key, secp_ctx) {
pub fn send_payment_for_bolt12_invoice(
&self, invoice: &Bolt12Invoice, context: &OffersContext,
) -> Result<(), Bolt12PaymentError> {
match self.verify_bolt12_invoice(invoice, context) {
Ok(payment_id) => self.send_payment_for_verified_bolt12_invoice(invoice, payment_id),
Err(()) => Err(Bolt12PaymentError::UnexpectedInvoice),
}
}

fn verify_bolt12_invoice(
&self, invoice: &Bolt12Invoice, context: &OffersContext,
) -> Result<PaymentId, ()> {
let secp_ctx = &self.secp_ctx;
let expanded_key = &self.inbound_payment_key;

match context {
OffersContext::Unknown {} if invoice.is_for_refund_without_paths() => {
invoice.verify(expanded_key, secp_ctx)
},
OffersContext::OutboundPayment { payment_id, nonce } => {
invoice
.verify_using_payer_data(*payment_id, *nonce, expanded_key, secp_ctx)
.then(|| *payment_id)
.ok_or(())
},
_ => Err(()),
}
}

fn send_payment_for_verified_bolt12_invoice(&self, invoice: &Bolt12Invoice, payment_id: PaymentId) -> Result<(), Bolt12PaymentError> {
let best_block_height = self.best_block.read().unwrap().height;
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
Expand Down Expand Up @@ -10623,30 +10643,15 @@ where
}
},
OffersMessage::Invoice(invoice) => {
let payer_data = match context {
OffersContext::Unknown {} if invoice.is_for_refund_without_paths() => None,
OffersContext::OutboundPayment { payment_id, nonce } => Some((payment_id, nonce)),
_ => return ResponseInstruction::NoResponse,
let payment_id = match self.verify_bolt12_invoice(&invoice, &context) {
Ok(payment_id) => payment_id,
Err(()) => return ResponseInstruction::NoResponse,
};

let logger = WithContext::from(
&self.logger, None, None, Some(invoice.payment_hash()),
);

let payment_id = match payer_data {
Some((payment_id, nonce)) => {
if invoice.verify_using_payer_data(payment_id, nonce, expanded_key, secp_ctx) {
payment_id
} else {
return ResponseInstruction::NoResponse;
}
},
None => match invoice.verify(expanded_key, secp_ctx) {
Ok(payment_id) => payment_id,
Err(()) => return ResponseInstruction::NoResponse,
},
};

let result = {
let features = self.bolt12_invoice_features();
if invoice.invoice_features().requires_unknown_bits_from(&features) {
Expand Down
14 changes: 7 additions & 7 deletions lightning/src/ln/offers_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -921,10 +921,10 @@ fn pays_bolt12_invoice_asynchronously() {
let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
bob.onion_messenger.handle_onion_message(&alice_id, &onion_message);

let invoice = match get_event!(bob, Event::InvoiceReceived) {
Event::InvoiceReceived { payment_id: actual_payment_id, invoice, .. } => {
let (invoice, context) = match get_event!(bob, Event::InvoiceReceived) {
Event::InvoiceReceived { payment_id: actual_payment_id, invoice, context, .. } => {
assert_eq!(actual_payment_id, payment_id);
invoice
(invoice, context)
},
_ => panic!("No Event::InvoiceReceived"),
};
Expand All @@ -935,9 +935,9 @@ fn pays_bolt12_invoice_asynchronously() {
assert_eq!(path.introduction_node, IntroductionNode::NodeId(alice_id));
}

assert!(bob.node.send_payment_for_bolt12_invoice(&invoice).is_ok());
assert!(bob.node.send_payment_for_bolt12_invoice(&invoice, &context).is_ok());
assert_eq!(
bob.node.send_payment_for_bolt12_invoice(&invoice),
bob.node.send_payment_for_bolt12_invoice(&invoice, &context),
Err(Bolt12PaymentError::DuplicateInvoice),
);

Expand All @@ -948,7 +948,7 @@ fn pays_bolt12_invoice_asynchronously() {
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);

assert_eq!(
bob.node.send_payment_for_bolt12_invoice(&invoice),
bob.node.send_payment_for_bolt12_invoice(&invoice, &context),
Err(Bolt12PaymentError::DuplicateInvoice),
);

Expand All @@ -957,7 +957,7 @@ fn pays_bolt12_invoice_asynchronously() {
}

assert_eq!(
bob.node.send_payment_for_bolt12_invoice(&invoice),
bob.node.send_payment_for_bolt12_invoice(&invoice, &context),
Err(Bolt12PaymentError::UnexpectedInvoice),
);
}
Expand Down
4 changes: 0 additions & 4 deletions lightning/src/offers/invoice_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1483,10 +1483,6 @@ mod tests {
.unwrap()
.build().unwrap()
.sign(recipient_sign).unwrap();
match invoice.verify(&expanded_key, &secp_ctx) {
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
Expand Down
4 changes: 0 additions & 4 deletions lightning/src/offers/refund.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1106,10 +1106,6 @@ mod tests {
.unwrap()
.build().unwrap()
.sign(recipient_sign).unwrap();
match invoice.verify(&expanded_key, &secp_ctx) {
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
Expand Down
3 changes: 1 addition & 2 deletions lightning/src/offers/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,7 @@ impl MetadataMaterial {
self.hmac.input(DERIVED_METADATA_AND_KEYS_HMAC_INPUT);
self.maybe_include_encrypted_payment_id();

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

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

0 comments on commit 9f7fd0d

Please sign in to comment.