From b924252720dc9a61b94fcafcb7a097d369827dca Mon Sep 17 00:00:00 2001 From: shaavan Date: Mon, 22 Sep 2025 15:54:01 +0530 Subject: [PATCH 1/2] Introduce ReceiveAuthKey-based verification in Blinded Payment Paths Extends the work started in [PR#3917](https://github.com/lightningdevkit/rust-lightning/pull/3917) by adding ReceiveAuthKey-based verification for Blinded Payment Paths. This reduces space previously taken by individual ReceiveTlvs and aligns the verification logic with that used for Blinded Message Paths. --- fuzz/src/chanmon_consistency.rs | 5 +-- fuzz/src/full_stack.rs | 6 ++-- fuzz/src/invoice_request_deser.rs | 4 ++- fuzz/src/refund_deser.rs | 4 ++- lightning/src/blinded_path/payment.rs | 23 +++++++------ lightning/src/ln/blinded_payment_tests.rs | 31 +++++++++++------ lightning/src/ln/channelmanager.rs | 13 ++++--- .../src/ln/max_payment_path_len_tests.rs | 2 ++ lightning/src/ln/msgs.rs | 27 +++++++++++---- lightning/src/ln/onion_payment.rs | 34 +++++++++++++++---- lightning/src/ln/onion_utils.rs | 15 ++++++-- lightning/src/offers/flow.rs | 2 ++ lightning/src/routing/router.rs | 22 ++++++------ lightning/src/util/test_utils.rs | 6 ++-- 14 files changed, 133 insertions(+), 61 deletions(-) diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 40a840eb164..8637ef8ad3c 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -128,8 +128,9 @@ impl Router for FuzzRouter { } fn create_blinded_payment_paths( - &self, _recipient: PublicKey, _first_hops: Vec, _tlvs: ReceiveTlvs, - _amount_msats: Option, _secp_ctx: &Secp256k1, + &self, _recipient: PublicKey, _receive_auth_key: ReceiveAuthKey, + _first_hops: Vec, _tlvs: ReceiveTlvs, _amount_msats: Option, + _secp_ctx: &Secp256k1, ) -> Result, ()> { unreachable!() } diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index ee5f4572eb2..f37a1ac17bb 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -62,7 +62,6 @@ use lightning::sign::{ }; use lightning::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; use lightning::util::config::{ChannelConfig, UserConfig}; -use lightning::util::errors::APIError; use lightning::util::hash_tables::*; use lightning::util::logger::Logger; use lightning::util::ser::{Readable, Writeable}; @@ -157,8 +156,9 @@ impl Router for FuzzRouter { } fn create_blinded_payment_paths( - &self, _recipient: PublicKey, _first_hops: Vec, _tlvs: ReceiveTlvs, - _amount_msats: Option, _secp_ctx: &Secp256k1, + &self, _recipient: PublicKey, _receive_auth_key: ReceiveAuthKey, + _first_hops: Vec, _tlvs: ReceiveTlvs, _amount_msats: Option, + _secp_ctx: &Secp256k1, ) -> Result, ()> { unreachable!() } diff --git a/fuzz/src/invoice_request_deser.rs b/fuzz/src/invoice_request_deser.rs index 96d8515f0b5..93618d1c9dc 100644 --- a/fuzz/src/invoice_request_deser.rs +++ b/fuzz/src/invoice_request_deser.rs @@ -21,7 +21,7 @@ use lightning::offers::invoice_request::{InvoiceRequest, InvoiceRequestFields}; use lightning::offers::nonce::Nonce; use lightning::offers::offer::OfferId; use lightning::offers::parse::Bolt12SemanticError; -use lightning::sign::EntropySource; +use lightning::sign::{EntropySource, ReceiveAuthKey}; use lightning::types::features::BlindedHopFeatures; use lightning::types::payment::{PaymentHash, PaymentSecret}; use lightning::types::string::UntrustedString; @@ -85,6 +85,7 @@ fn build_response( let expanded_key = ExpandedKey::new([42; 32]); let entropy_source = Randomness {}; let nonce = Nonce::from_entropy_source(&entropy_source); + let receive_auth_key = ReceiveAuthKey([41; 32]); let invoice_request_fields = if let Ok(ver) = invoice_request.clone().verify_using_metadata(&expanded_key, secp_ctx) { @@ -136,6 +137,7 @@ fn build_response( let payment_path = BlindedPaymentPath::new( &intermediate_nodes, pubkey(42), + receive_auth_key, payee_tlvs, u64::MAX, MIN_FINAL_CLTV_EXPIRY_DELTA, diff --git a/fuzz/src/refund_deser.rs b/fuzz/src/refund_deser.rs index 6151d810344..2dea67ca087 100644 --- a/fuzz/src/refund_deser.rs +++ b/fuzz/src/refund_deser.rs @@ -20,7 +20,7 @@ use lightning::offers::invoice::UnsignedBolt12Invoice; use lightning::offers::nonce::Nonce; use lightning::offers::parse::Bolt12SemanticError; use lightning::offers::refund::Refund; -use lightning::sign::EntropySource; +use lightning::sign::{EntropySource, ReceiveAuthKey}; use lightning::types::features::BlindedHopFeatures; use lightning::types::payment::{PaymentHash, PaymentSecret}; use lightning::util::ser::Writeable; @@ -71,6 +71,7 @@ fn build_response( ) -> Result { let expanded_key = ExpandedKey::new([42; 32]); let entropy_source = Randomness {}; + let receive_auth_key = ReceiveAuthKey([41; 32]); let nonce = Nonce::from_entropy_source(&entropy_source); let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {}); let payee_tlvs = UnauthenticatedReceiveTlvs { @@ -103,6 +104,7 @@ fn build_response( let payment_path = BlindedPaymentPath::new( &intermediate_nodes, pubkey(42), + receive_auth_key, payee_tlvs, u64::MAX, MIN_FINAL_CLTV_EXPIRY_DELTA, diff --git a/lightning/src/blinded_path/payment.rs b/lightning/src/blinded_path/payment.rs index 4ae10f75961..107f80735d1 100644 --- a/lightning/src/blinded_path/payment.rs +++ b/lightning/src/blinded_path/payment.rs @@ -16,7 +16,7 @@ use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey}; use crate::blinded_path::utils::{self, BlindedPathWithPadding}; use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode, NodeIdLookUp}; -use crate::crypto::streams::ChaChaPolyReadAdapter; +use crate::crypto::streams::ChaChaDualPolyReadAdapter; use crate::io; use crate::io::Cursor; use crate::ln::channel_state::CounterpartyForwardingInfo; @@ -28,7 +28,7 @@ use crate::offers::invoice_request::InvoiceRequestFields; use crate::offers::nonce::Nonce; use crate::offers::offer::OfferId; use crate::routing::gossip::{NodeId, ReadOnlyNetworkGraph}; -use crate::sign::{EntropySource, NodeSigner, Recipient}; +use crate::sign::{EntropySource, NodeSigner, ReceiveAuthKey, Recipient}; use crate::types::features::BlindedHopFeatures; use crate::types::payment::PaymentSecret; use crate::types::routing::RoutingFees; @@ -93,8 +93,8 @@ pub struct BlindedPaymentPath { impl BlindedPaymentPath { /// Create a one-hop blinded path for a payment. pub fn one_hop( - payee_node_id: PublicKey, payee_tlvs: ReceiveTlvs, min_final_cltv_expiry_delta: u16, - entropy_source: ES, secp_ctx: &Secp256k1, + payee_node_id: PublicKey, receive_auth_key: ReceiveAuthKey, payee_tlvs: ReceiveTlvs, + min_final_cltv_expiry_delta: u16, entropy_source: ES, secp_ctx: &Secp256k1, ) -> Result where ES::Target: EntropySource, @@ -105,6 +105,7 @@ impl BlindedPaymentPath { Self::new( &[], payee_node_id, + receive_auth_key, payee_tlvs, htlc_maximum_msat, min_final_cltv_expiry_delta, @@ -121,8 +122,8 @@ impl BlindedPaymentPath { // TODO: make all payloads the same size with padding + add dummy hops pub fn new( intermediate_nodes: &[PaymentForwardNode], payee_node_id: PublicKey, - payee_tlvs: ReceiveTlvs, htlc_maximum_msat: u64, min_final_cltv_expiry_delta: u16, - entropy_source: ES, secp_ctx: &Secp256k1, + receive_auth_key: ReceiveAuthKey, payee_tlvs: ReceiveTlvs, htlc_maximum_msat: u64, + min_final_cltv_expiry_delta: u16, entropy_source: ES, secp_ctx: &Secp256k1, ) -> Result where ES::Target: EntropySource, @@ -150,6 +151,7 @@ impl BlindedPaymentPath { payee_node_id, payee_tlvs, &blinding_secret, + receive_auth_key, ), }, payinfo: blinded_payinfo, @@ -226,12 +228,13 @@ impl BlindedPaymentPath { let control_tlvs_ss = node_signer.ecdh(Recipient::Node, &self.inner_path.blinding_point, None)?; let rho = onion_utils::gen_rho_from_shared_secret(&control_tlvs_ss.secret_bytes()); + let receive_auth_key = node_signer.get_receive_auth_key(); let encrypted_control_tlvs = &self.inner_path.blinded_hops.get(0).ok_or(())?.encrypted_payload; let mut s = Cursor::new(encrypted_control_tlvs); let mut reader = FixedLengthReader::new(&mut s, encrypted_control_tlvs.len() as u64); - match ChaChaPolyReadAdapter::read(&mut reader, rho) { - Ok(ChaChaPolyReadAdapter { readable, .. }) => Ok((readable, control_tlvs_ss)), + match ChaChaDualPolyReadAdapter::read(&mut reader, (rho, receive_auth_key.0)) { + Ok(ChaChaDualPolyReadAdapter { readable, .. }) => Ok((readable, control_tlvs_ss)), _ => Err(()), } } @@ -660,12 +663,12 @@ pub(crate) const PAYMENT_PADDING_ROUND_OFF: usize = 30; /// Construct blinded payment hops for the given `intermediate_nodes` and payee info. pub(super) fn blinded_hops( secp_ctx: &Secp256k1, intermediate_nodes: &[PaymentForwardNode], payee_node_id: PublicKey, - payee_tlvs: ReceiveTlvs, session_priv: &SecretKey, + payee_tlvs: ReceiveTlvs, session_priv: &SecretKey, local_node_receive_key: ReceiveAuthKey, ) -> Vec { let pks = intermediate_nodes .iter() .map(|node| (node.node_id, None)) - .chain(core::iter::once((payee_node_id, None))); + .chain(core::iter::once((payee_node_id, Some(local_node_receive_key)))); let tlvs = intermediate_nodes .iter() .map(|node| BlindedPaymentTlvsRef::Forward(&node.tlvs)) diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index 25fa5e71e5d..f770c00f1e8 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -88,12 +88,13 @@ pub fn blinded_payment_path( let nonce = Nonce([42u8; 16]); let expanded_key = keys_manager.get_expanded_key(); + let receive_auth_key = keys_manager.get_receive_auth_key(); let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let mut secp_ctx = Secp256k1::new(); BlindedPaymentPath::new( - &intermediate_nodes[..], *node_ids.last().unwrap(), payee_tlvs, - intro_node_max_htlc_opt.unwrap_or_else(|| channel_upds.last().unwrap().htlc_maximum_msat), + &intermediate_nodes[..], *node_ids.last().unwrap(), receive_auth_key, + payee_tlvs, intro_node_max_htlc_opt.unwrap_or_else(|| channel_upds.last().unwrap().htlc_maximum_msat), TEST_FINAL_CLTV as u16, keys_manager, &secp_ctx ).unwrap() } @@ -173,11 +174,13 @@ fn do_one_hop_blinded_path(success: bool) { }; let nonce = Nonce([42u8; 16]); let expanded_key = chanmon_cfgs[1].keys_manager.get_expanded_key(); + let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_key(); let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPaymentPath::new( - &[], nodes[1].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, + &[], nodes[1].node.get_our_node_id(), receive_auth_key, + payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, &chanmon_cfgs[1].keys_manager, &secp_ctx ).unwrap(); @@ -227,9 +230,11 @@ fn mpp_to_one_hop_blinded_path() { }; let nonce = Nonce([42u8; 16]); let expanded_key = chanmon_cfgs[3].keys_manager.get_expanded_key(); + let receive_auth_key = chanmon_cfgs[3].keys_manager.get_receive_auth_key(); let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let blinded_path = BlindedPaymentPath::new( - &[], nodes[3].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, + &[], nodes[3].node.get_our_node_id(), receive_auth_key, + payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, &chanmon_cfgs[3].keys_manager, &secp_ctx ).unwrap(); @@ -1337,10 +1342,12 @@ fn custom_tlvs_to_blinded_path() { }; let nonce = Nonce([42u8; 16]); let expanded_key = chanmon_cfgs[1].keys_manager.get_expanded_key(); + let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_key(); let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPaymentPath::new( - &[], nodes[1].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, + &[], nodes[1].node.get_our_node_id(), receive_auth_key, + payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, &chanmon_cfgs[1].keys_manager, &secp_ctx ).unwrap(); @@ -1391,11 +1398,13 @@ fn fails_receive_tlvs_authentication() { }; let nonce = Nonce([42u8; 16]); let expanded_key = chanmon_cfgs[1].keys_manager.get_expanded_key(); + let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_key(); let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPaymentPath::new( - &[], nodes[1].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, + &[], nodes[1].node.get_our_node_id(), receive_auth_key, + payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, &chanmon_cfgs[1].keys_manager, &secp_ctx ).unwrap(); @@ -1426,7 +1435,8 @@ fn fails_receive_tlvs_authentication() { let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPaymentPath::new( - &[], nodes[1].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, + &[], nodes[1].node.get_our_node_id(), receive_auth_key, + payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, &chanmon_cfgs[1].keys_manager, &secp_ctx ).unwrap(); @@ -1629,7 +1639,7 @@ fn route_blinding_spec_test_vector() { &self, _invoice: &RawBolt11Invoice, _recipient: Recipient, ) -> Result { unreachable!() } fn get_peer_storage_key(&self) -> PeerStorageKey { unreachable!() } - fn get_receive_auth_key(&self) -> ReceiveAuthKey { unreachable!() } + fn get_receive_auth_key(&self) -> ReceiveAuthKey { ReceiveAuthKey([41; 32]) } fn sign_bolt12_invoice( &self, _invoice: &UnsignedBolt12Invoice, ) -> Result { unreachable!() } @@ -1942,7 +1952,7 @@ fn test_trampoline_inbound_payment_decoding() { &self, _invoice: &RawBolt11Invoice, _recipient: Recipient, ) -> Result { unreachable!() } fn get_peer_storage_key(&self) -> PeerStorageKey { unreachable!() } - fn get_receive_auth_key(&self) -> ReceiveAuthKey { unreachable!() } + fn get_receive_auth_key(&self) -> ReceiveAuthKey { ReceiveAuthKey([41; 32]) } fn sign_bolt12_invoice( &self, _invoice: &UnsignedBolt12Invoice, ) -> Result { unreachable!() } @@ -2027,8 +2037,9 @@ fn do_test_trampoline_single_hop_receive(success: bool) { let expanded_key = nodes[2].keys_manager.get_expanded_key(); let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let carol_unblinded_tlvs = payee_tlvs.encode(); + let receive_auth_key = nodes[2].keys_manager.get_receive_auth_key(); - let path = [((carol_node_id, None), WithoutLength(&carol_unblinded_tlvs))]; + let path = [((carol_node_id, Some(receive_auth_key)), WithoutLength(&carol_unblinded_tlvs))]; blinded_path::utils::construct_blinded_hops( &secp_ctx, path.into_iter(), &carol_alice_trampoline_session_priv, ) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 4365407a2d0..cd23821b713 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -4912,7 +4912,7 @@ where let current_height: u32 = self.best_block.read().unwrap().height; create_recv_pending_htlc_info(decoded_hop, shared_secret, msg.payment_hash, msg.amount_msat, msg.cltv_expiry, None, allow_underpay, msg.skimmed_fee_msat, - current_height) + current_height, &*self.logger) }, onion_utils::Hop::Forward { .. } | onion_utils::Hop::BlindedForward { .. } => { create_fwd_pending_htlc_info(msg, decoded_hop, shared_secret, next_packet_pubkey_opt) @@ -7025,6 +7025,7 @@ where false, None, current_height, + &*self.logger, ); match create_res { Ok(info) => phantom_receives.push(( @@ -17912,7 +17913,7 @@ mod tests { use crate::util::config::{ChannelConfig, ChannelConfigUpdate}; use crate::util::errors::APIError; use crate::util::ser::Writeable; - use crate::util::test_utils; + use crate::util::test_utils::{self, TestLogger}; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; use core::sync::atomic::Ordering; @@ -18737,6 +18738,7 @@ mod tests { let node_chanmgr = create_node_chanmgrs(1, &node_cfg, &[None]); let node = create_network(1, &node_cfg, &node_chanmgr); let sender_intended_amt_msat = 100; + let logger = TestLogger::new(); let extra_fee_msat = 10; let hop_data = onion_utils::Hop::Receive { hop_data: msgs::InboundOnionReceivePayload { @@ -18758,7 +18760,7 @@ mod tests { if let Err(crate::ln::channelmanager::InboundHTLCErr { reason, .. }) = create_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]), sender_intended_amt_msat - extra_fee_msat - 1, 42, None, true, Some(extra_fee_msat), - current_height) + current_height, &logger) { assert_eq!(reason, LocalHTLCFailureReason::FinalIncorrectHTLCAmount); } else { panic!(); } @@ -18781,7 +18783,7 @@ mod tests { let current_height: u32 = node[0].node.best_block.read().unwrap().height; assert!(create_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]), sender_intended_amt_msat - extra_fee_msat, 42, None, true, Some(extra_fee_msat), - current_height).is_ok()); + current_height, &logger).is_ok()); } #[test] @@ -18791,6 +18793,7 @@ mod tests { let node_cfg = create_node_cfgs(1, &chanmon_cfg); let node_chanmgr = create_node_chanmgrs(1, &node_cfg, &[None]); let node = create_network(1, &node_cfg, &node_chanmgr); + let logger = TestLogger::new(); let current_height: u32 = node[0].node.best_block.read().unwrap().height; let result = create_recv_pending_htlc_info(onion_utils::Hop::Receive { @@ -18806,7 +18809,7 @@ mod tests { custom_tlvs: Vec::new(), }, shared_secret: SharedSecret::from_bytes([0; 32]), - }, [0; 32], PaymentHash([0; 32]), 100, TEST_FINAL_CLTV + 1, None, true, None, current_height); + }, [0; 32], PaymentHash([0; 32]), 100, TEST_FINAL_CLTV + 1, None, true, None, current_height, &logger); // Should not return an error as this condition: // https://github.com/lightning/bolts/blob/4dcc377209509b13cf89a4b91fde7d478f5b46d8/04-onion-routing.md?plain=1#L334 diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs index 8425e1928ad..366f54b9c80 100644 --- a/lightning/src/ln/max_payment_path_len_tests.rs +++ b/lightning/src/ln/max_payment_path_len_tests.rs @@ -223,11 +223,13 @@ fn one_hop_blinded_path_with_custom_tlv() { }; let nonce = Nonce([42u8; 16]); let expanded_key = chanmon_cfgs[2].keys_manager.get_expanded_key(); + let receive_auth_key = chanmon_cfgs[2].keys_manager.get_receive_auth_key(); let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPaymentPath::new( &[], nodes[2].node.get_our_node_id(), + receive_auth_key, payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index fd806a27951..5318f3d062a 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -59,7 +59,7 @@ use core::str::FromStr; #[cfg(feature = "std")] use std::net::SocketAddr; -use crate::crypto::streams::ChaChaPolyReadAdapter; +use crate::crypto::streams::ChaChaDualPolyReadAdapter; use crate::util::base32; use crate::util::logger; use crate::util::ser::{ @@ -2342,6 +2342,7 @@ mod fuzzy_internal_msgs { pub keysend_preimage: Option, pub invoice_request: Option, pub custom_tlvs: Vec<(u64, Vec)>, + pub payment_tlvs_authenticated: bool, } pub enum InboundOnionPayload { @@ -3659,10 +3660,11 @@ where .ecdh(Recipient::Node, &blinding_point, None) .map_err(|_| DecodeError::InvalidValue)?; let rho = onion_utils::gen_rho_from_shared_secret(&enc_tlvs_ss.secret_bytes()); + let receive_auth_key = node_signer.get_receive_auth_key(); let mut s = Cursor::new(&enc_tlvs); let mut reader = FixedLengthReader::new(&mut s, enc_tlvs.len() as u64); - match ChaChaPolyReadAdapter::read(&mut reader, rho)? { - ChaChaPolyReadAdapter { + match ChaChaDualPolyReadAdapter::read(&mut reader, (rho, receive_auth_key.0))? { + ChaChaDualPolyReadAdapter { readable: BlindedPaymentTlvs::Forward(ForwardTlvs { short_channel_id, @@ -3671,11 +3673,13 @@ where features, next_blinding_override, }), + used_aad, } => { if amt.is_some() || cltv_value.is_some() || total_msat.is_some() || keysend_preimage.is_some() || invoice_request.is_some() + || used_aad { return Err(DecodeError::InvalidValue); } @@ -3688,7 +3692,10 @@ where next_blinding_override, })) }, - ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Receive(receive_tlvs) } => { + ChaChaDualPolyReadAdapter { + readable: BlindedPaymentTlvs::Receive(receive_tlvs), + used_aad, + } => { let ReceiveTlvs { tlvs, authentication: (hmac, nonce) } = receive_tlvs; let expanded_key = node_signer.get_expanded_key(); if tlvs.verify_for_offer_payment(hmac, nonce, &expanded_key).is_err() { @@ -3714,6 +3721,7 @@ where keysend_preimage, invoice_request, custom_tlvs, + payment_tlvs_authenticated: used_aad, })) }, } @@ -3758,6 +3766,7 @@ where { fn read(r: &mut R, args: (Option, NS)) -> Result { let (update_add_blinding_point, node_signer) = args; + let receive_auth_key = node_signer.get_receive_auth_key(); let mut amt = None; let mut cltv_value = None; @@ -3811,8 +3820,8 @@ where let rho = onion_utils::gen_rho_from_shared_secret(&enc_tlvs_ss.secret_bytes()); let mut s = Cursor::new(&enc_tlvs); let mut reader = FixedLengthReader::new(&mut s, enc_tlvs.len() as u64); - match ChaChaPolyReadAdapter::read(&mut reader, rho)? { - ChaChaPolyReadAdapter { + match ChaChaDualPolyReadAdapter::read(&mut reader, (rho, receive_auth_key.0))? { + ChaChaDualPolyReadAdapter { readable: BlindedTrampolineTlvs::Forward(TrampolineForwardTlvs { next_trampoline, @@ -3821,11 +3830,13 @@ where features, next_blinding_override, }), + used_aad, } => { if amt.is_some() || cltv_value.is_some() || total_msat.is_some() || keysend_preimage.is_some() || invoice_request.is_some() + || used_aad { return Err(DecodeError::InvalidValue); } @@ -3838,8 +3849,9 @@ where next_blinding_override, })) }, - ChaChaPolyReadAdapter { + ChaChaDualPolyReadAdapter { readable: BlindedTrampolineTlvs::Receive(receive_tlvs), + used_aad, } => { let ReceiveTlvs { tlvs, authentication: (hmac, nonce) } = receive_tlvs; let expanded_key = node_signer.get_expanded_key(); @@ -3866,6 +3878,7 @@ where keysend_preimage, invoice_request, custom_tlvs, + payment_tlvs_authenticated: used_aad, })) }, } diff --git a/lightning/src/ln/onion_payment.rs b/lightning/src/ln/onion_payment.rs index 0934c6c812b..e8532ed5cef 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -245,11 +245,26 @@ pub(super) fn create_fwd_pending_htlc_info( } #[rustfmt::skip] -pub(super) fn create_recv_pending_htlc_info( +pub(super) fn create_recv_pending_htlc_info( hop_data: onion_utils::Hop, shared_secret: [u8; 32], payment_hash: PaymentHash, amt_msat: u64, cltv_expiry: u32, phantom_shared_secret: Option<[u8; 32]>, allow_underpay: bool, - counterparty_skimmed_fee_msat: Option, current_height: u32 -) -> Result { + counterparty_skimmed_fee_msat: Option, current_height: u32, logger: L +) -> Result +where + L::Target: Logger, +{ + let check_authentication = |payment_tlvs_authenticated: bool| -> Result<(), InboundHTLCErr> { + if !payment_tlvs_authenticated { + log_warn!(logger, "Received an unauthenticated receive payment message"); + return Err(InboundHTLCErr { + reason: LocalHTLCFailureReason::UnauthenticatedPayload, + err_data: Vec::new(), + msg: "Received unauthenticated receive payment htlc", + }); + } + Ok(()) + }; + let ( payment_data, keysend_preimage, custom_tlvs, onion_amt_msat, onion_cltv_expiry, payment_metadata, payment_context, requires_blinded_error, has_recipient_created_payment_secret, @@ -264,8 +279,10 @@ pub(super) fn create_recv_pending_htlc_info( onion_utils::Hop::BlindedReceive { hop_data: msgs::InboundOnionBlindedReceivePayload { sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, payment_secret, intro_node_blinding_point, payment_constraints, payment_context, keysend_preimage, - custom_tlvs, invoice_request + custom_tlvs, invoice_request, payment_tlvs_authenticated }, .. } => { + check_authentication(payment_tlvs_authenticated)?; + check_blinded_payment_constraints( sender_intended_htlc_amt_msat, cltv_expiry, &payment_constraints ) @@ -293,9 +310,11 @@ pub(super) fn create_recv_pending_htlc_info( trampoline_hop_data: msgs::InboundOnionBlindedReceivePayload { sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, payment_secret, intro_node_blinding_point, payment_constraints, payment_context, keysend_preimage, - custom_tlvs, invoice_request + custom_tlvs, invoice_request, payment_tlvs_authenticated }, .. } => { + check_authentication(payment_tlvs_authenticated)?; + check_blinded_payment_constraints( sender_intended_htlc_amt_msat, cltv_expiry, &payment_constraints, ) @@ -438,7 +457,7 @@ where L::Target: Logger, { let (hop, next_packet_details_opt) = - decode_incoming_update_add_htlc_onion(msg, node_signer, logger, secp_ctx + decode_incoming_update_add_htlc_onion(msg, node_signer, logger.deref(), secp_ctx ).map_err(|(msg, failure_reason)| { let (reason, err_data) = match msg { HTLCFailureMsg::Malformed(_) => (failure_reason, Vec::new()), @@ -480,7 +499,8 @@ where let shared_secret = hop.shared_secret().secret_bytes(); create_recv_pending_htlc_info( hop, shared_secret, msg.payment_hash, msg.amount_msat, msg.cltv_expiry, - None, allow_skimmed_fees, msg.skimmed_fee_msat, cur_height, + None, allow_skimmed_fees, msg.skimmed_fee_msat, + cur_height, logger.deref() )? } }) diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 6bba2b59e10..ffb67aba0ff 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -1680,6 +1680,13 @@ pub enum LocalHTLCFailureReason { PeerOffline, /// The HTLC was failed because the channel balance was overdrawn. ChannelBalanceOverdrawn, + /// The payload we received was not authenticated by our node. + /// + /// This can occur if we constructed an invalid [`BlindedPaymentPath`] + /// for our counterparty to send a payment over. + /// + /// [`BlindedPaymentPath`]: crate::blinded_path::payment::BlindedPaymentPath + UnauthenticatedPayload, } impl LocalHTLCFailureReason { @@ -1720,7 +1727,7 @@ impl LocalHTLCFailureReason { Self::CLTVExpiryTooFar => 21, Self::InvalidOnionPayload | Self::InvalidTrampolinePayload => PERM | 22, Self::MPPTimeout => 23, - Self::InvalidOnionBlinding => BADONION | PERM | 24, + Self::InvalidOnionBlinding | Self::UnauthenticatedPayload => BADONION | PERM | 24, Self::UnknownFailureCode { code } => *code, } } @@ -1880,7 +1887,8 @@ ser_failure_reasons!( (39, HTLCMinimum), (40, HTLCMaximum), (41, PeerOffline), - (42, ChannelBalanceOverdrawn) + (42, ChannelBalanceOverdrawn), + (43, UnauthenticatedPayload) ); impl From<&HTLCFailReason> for HTLCHandlingFailureReason { @@ -2041,7 +2049,8 @@ impl HTLCFailReason { LocalHTLCFailureReason::InvalidOnionPayload | LocalHTLCFailureReason::InvalidTrampolinePayload => debug_assert!(data.len() <= 11), LocalHTLCFailureReason::MPPTimeout => debug_assert!(data.is_empty()), - LocalHTLCFailureReason::InvalidOnionBlinding => debug_assert_eq!(data.len(), 32), + LocalHTLCFailureReason::InvalidOnionBlinding + | LocalHTLCFailureReason::UnauthenticatedPayload => debug_assert_eq!(data.len(), 32), LocalHTLCFailureReason::UnknownFailureCode { code } => { // We set some bogus BADONION failure codes in tests, so allow unknown BADONION. if code & BADONION == 0 { diff --git a/lightning/src/offers/flow.rs b/lightning/src/offers/flow.rs index 615b2991f17..9c2a02f3d06 100644 --- a/lightning/src/offers/flow.rs +++ b/lightning/src/offers/flow.rs @@ -331,6 +331,7 @@ where let expanded_key = &self.inbound_payment_key; let entropy = &*entropy_source; let secp_ctx = &self.secp_ctx; + let receive_auth_key = self.receive_auth_key; let payee_node_id = self.get_our_node_id(); @@ -351,6 +352,7 @@ where router.create_blinded_payment_paths( payee_node_id, + receive_auth_key, usable_channels, payee_tlvs, amount_msats, diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index e3443b5e45a..f7fc21fa25a 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -28,7 +28,7 @@ use crate::routing::gossip::{ DirectedChannelInfo, EffectiveCapacity, NetworkGraph, NodeId, ReadOnlyNetworkGraph, }; use crate::routing::scoring::{ChannelUsage, LockableScore, ScoreLookUp}; -use crate::sign::EntropySource; +use crate::sign::{EntropySource, ReceiveAuthKey}; use crate::sync::Mutex; use crate::types::features::{ BlindedHopFeatures, Bolt11InvoiceFeatures, Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures, @@ -129,8 +129,8 @@ where fn create_blinded_payment_paths< T: secp256k1::Signing + secp256k1::Verification > ( - &self, recipient: PublicKey, first_hops: Vec, tlvs: ReceiveTlvs, - amount_msats: Option, secp_ctx: &Secp256k1 + &self, recipient: PublicKey, receive_auth_key: ReceiveAuthKey, first_hops: Vec, + tlvs: ReceiveTlvs, amount_msats: Option, secp_ctx: &Secp256k1 ) -> Result, ()> { // Limit the number of blinded paths that are computed. const MAX_PAYMENT_PATHS: usize = 3; @@ -197,7 +197,7 @@ where }) .map(|forward_node| { BlindedPaymentPath::new( - &[forward_node], recipient, tlvs.clone(), u64::MAX, MIN_FINAL_CLTV_EXPIRY_DELTA, + &[forward_node], recipient, receive_auth_key, tlvs.clone(), u64::MAX, MIN_FINAL_CLTV_EXPIRY_DELTA, &*self.entropy_source, secp_ctx ) }) @@ -209,7 +209,7 @@ where _ => { if network_graph.nodes().contains_key(&NodeId::from_pubkey(&recipient)) { BlindedPaymentPath::new( - &[], recipient, tlvs, u64::MAX, MIN_FINAL_CLTV_EXPIRY_DELTA, &*self.entropy_source, + &[], recipient, receive_auth_key, tlvs, u64::MAX, MIN_FINAL_CLTV_EXPIRY_DELTA, &*self.entropy_source, secp_ctx ).map(|path| vec![path]) } else { @@ -243,8 +243,9 @@ impl Router for FixedRouter { } fn create_blinded_payment_paths( - &self, _recipient: PublicKey, _first_hops: Vec, _tlvs: ReceiveTlvs, - _amount_msats: Option, _secp_ctx: &Secp256k1, + &self, _recipient: PublicKey, _receive_auth_key: ReceiveAuthKey, + _first_hops: Vec, _tlvs: ReceiveTlvs, _amount_msats: Option, + _secp_ctx: &Secp256k1, ) -> Result, ()> { // Should be unreachable as this router is only intended to provide a one-time payment route. debug_assert!(false); @@ -281,10 +282,11 @@ pub trait Router { /// Creates [`BlindedPaymentPath`]s for payment to the `recipient` node. The channels in `first_hops` /// are assumed to be with the `recipient`'s peers. The payment secret and any constraints are - /// given in `tlvs`. + /// given in `tlvs`. The `receive_auth_key` is required to authenticate the blinded payment paths. fn create_blinded_payment_paths( - &self, recipient: PublicKey, first_hops: Vec, tlvs: ReceiveTlvs, - amount_msats: Option, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, receive_auth_key: ReceiveAuthKey, + first_hops: Vec, tlvs: ReceiveTlvs, amount_msats: Option, + secp_ctx: &Secp256k1, ) -> Result, ()>; } diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index aacc38e366a..d8ce690a452 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -288,13 +288,15 @@ impl<'a> Router for TestRouter<'a> { } fn create_blinded_payment_paths( - &self, recipient: PublicKey, first_hops: Vec, tlvs: ReceiveTlvs, - amount_msats: Option, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, receive_auth_key: ReceiveAuthKey, + first_hops: Vec, tlvs: ReceiveTlvs, amount_msats: Option, + secp_ctx: &Secp256k1, ) -> Result, ()> { let mut expected_paths = self.next_blinded_payment_paths.lock().unwrap(); if expected_paths.is_empty() { self.router.create_blinded_payment_paths( recipient, + receive_auth_key, first_hops, tlvs, amount_msats, From a93113cf984d604683951d7c7b61de80b3f68a78 Mon Sep 17 00:00:00 2001 From: shaavan Date: Thu, 25 Sep 2025 14:17:55 +0530 Subject: [PATCH 2/2] Cleanup: Remove redundant (hmac, nonce) from codebase Now that we have introduced an alternate mechanism for authentication in the codebase, we can safely remove the now redundant (hmac, nonce) fields from the Payment ReceiveTlvs's while maintaining the security of the onion messages. --- fuzz/src/invoice_request_deser.rs | 9 +- fuzz/src/refund_deser.rs | 11 +-- lightning/src/blinded_path/payment.rs | 89 +++++-------------- lightning/src/ln/async_payments_tests.rs | 5 +- lightning/src/ln/blinded_payment_tests.rs | 43 +++------ lightning/src/ln/channelmanager.rs | 41 +-------- .../src/ln/max_payment_path_len_tests.rs | 8 +- lightning/src/ln/msgs.rs | 29 ++---- lightning/src/offers/flow.rs | 69 +++++--------- lightning/src/offers/signer.rs | 31 +------ lightning/src/routing/router.rs | 4 +- 11 files changed, 77 insertions(+), 262 deletions(-) diff --git a/fuzz/src/invoice_request_deser.rs b/fuzz/src/invoice_request_deser.rs index 93618d1c9dc..a21303debd7 100644 --- a/fuzz/src/invoice_request_deser.rs +++ b/fuzz/src/invoice_request_deser.rs @@ -12,13 +12,12 @@ use bitcoin::secp256k1::{self, Keypair, Parity, PublicKey, Secp256k1, SecretKey} use core::convert::TryFrom; use lightning::blinded_path::payment::{ BlindedPaymentPath, Bolt12OfferContext, ForwardTlvs, PaymentConstraints, PaymentContext, - PaymentForwardNode, PaymentRelay, UnauthenticatedReceiveTlvs, + PaymentForwardNode, PaymentRelay, ReceiveTlvs, }; use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA; use lightning::ln::inbound_payment::ExpandedKey; use lightning::offers::invoice::UnsignedBolt12Invoice; use lightning::offers::invoice_request::{InvoiceRequest, InvoiceRequestFields}; -use lightning::offers::nonce::Nonce; use lightning::offers::offer::OfferId; use lightning::offers::parse::Bolt12SemanticError; use lightning::sign::{EntropySource, ReceiveAuthKey}; @@ -84,7 +83,6 @@ fn build_response( ) -> Result { let expanded_key = ExpandedKey::new([42; 32]); let entropy_source = Randomness {}; - let nonce = Nonce::from_entropy_source(&entropy_source); let receive_auth_key = ReceiveAuthKey([41; 32]); let invoice_request_fields = @@ -107,7 +105,7 @@ fn build_response( offer_id: OfferId([42; 32]), invoice_request: invoice_request_fields, }); - let payee_tlvs = UnauthenticatedReceiveTlvs { + let payee_tlvs = ReceiveTlvs { payment_secret: PaymentSecret([42; 32]), payment_constraints: PaymentConstraints { max_cltv_expiry: 1_000_000, @@ -115,7 +113,6 @@ fn build_response( }, payment_context, }; - let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let intermediate_nodes = [PaymentForwardNode { tlvs: ForwardTlvs { short_channel_id: 43, @@ -125,7 +122,7 @@ fn build_response( fee_base_msat: 1, }, payment_constraints: PaymentConstraints { - max_cltv_expiry: payee_tlvs.tlvs().payment_constraints.max_cltv_expiry + 40, + max_cltv_expiry: payee_tlvs.payment_constraints.max_cltv_expiry + 40, htlc_minimum_msat: 100, }, features: BlindedHopFeatures::empty(), diff --git a/fuzz/src/refund_deser.rs b/fuzz/src/refund_deser.rs index 2dea67ca087..446ac704455 100644 --- a/fuzz/src/refund_deser.rs +++ b/fuzz/src/refund_deser.rs @@ -12,12 +12,10 @@ use bitcoin::secp256k1::{self, Keypair, PublicKey, Secp256k1, SecretKey}; use core::convert::TryFrom; use lightning::blinded_path::payment::{ BlindedPaymentPath, Bolt12RefundContext, ForwardTlvs, PaymentConstraints, PaymentContext, - PaymentForwardNode, PaymentRelay, UnauthenticatedReceiveTlvs, + PaymentForwardNode, PaymentRelay, ReceiveTlvs, }; use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA; -use lightning::ln::inbound_payment::ExpandedKey; use lightning::offers::invoice::UnsignedBolt12Invoice; -use lightning::offers::nonce::Nonce; use lightning::offers::parse::Bolt12SemanticError; use lightning::offers::refund::Refund; use lightning::sign::{EntropySource, ReceiveAuthKey}; @@ -69,12 +67,10 @@ fn privkey(byte: u8) -> SecretKey { fn build_response( refund: &Refund, signing_pubkey: PublicKey, secp_ctx: &Secp256k1, ) -> Result { - let expanded_key = ExpandedKey::new([42; 32]); let entropy_source = Randomness {}; let receive_auth_key = ReceiveAuthKey([41; 32]); - let nonce = Nonce::from_entropy_source(&entropy_source); let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {}); - let payee_tlvs = UnauthenticatedReceiveTlvs { + let payee_tlvs = ReceiveTlvs { payment_secret: PaymentSecret([42; 32]), payment_constraints: PaymentConstraints { max_cltv_expiry: 1_000_000, @@ -82,7 +78,6 @@ fn build_response( }, payment_context, }; - let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let intermediate_nodes = [PaymentForwardNode { tlvs: ForwardTlvs { short_channel_id: 43, @@ -92,7 +87,7 @@ fn build_response( fee_base_msat: 1, }, payment_constraints: PaymentConstraints { - max_cltv_expiry: payee_tlvs.tlvs().payment_constraints.max_cltv_expiry + 40, + max_cltv_expiry: payee_tlvs.payment_constraints.max_cltv_expiry + 40, htlc_minimum_msat: 100, }, features: BlindedHopFeatures::empty(), diff --git a/lightning/src/blinded_path/payment.rs b/lightning/src/blinded_path/payment.rs index 107f80735d1..4f8fbd51284 100644 --- a/lightning/src/blinded_path/payment.rs +++ b/lightning/src/blinded_path/payment.rs @@ -9,8 +9,6 @@ //! Data structures and methods for constructing [`BlindedPaymentPath`]s to send a payment over. -use bitcoin::hashes::hmac::Hmac; -use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey}; @@ -20,8 +18,6 @@ use crate::crypto::streams::ChaChaDualPolyReadAdapter; use crate::io; use crate::io::Cursor; use crate::ln::channel_state::CounterpartyForwardingInfo; -use crate::ln::channelmanager::Verification; -use crate::ln::inbound_payment::ExpandedKey; use crate::ln::msgs::DecodeError; use crate::ln::onion_utils; use crate::offers::invoice_request::InvoiceRequestFields; @@ -137,7 +133,7 @@ impl BlindedPaymentPath { let blinded_payinfo = compute_payinfo( intermediate_nodes, - &payee_tlvs.tlvs, + &payee_tlvs, htlc_maximum_msat, min_final_cltv_expiry_delta, )?; @@ -328,26 +324,8 @@ pub struct TrampolineForwardTlvs { /// Data to construct a [`BlindedHop`] for receiving a payment. This payload is custom to LDK and /// may not be valid if received by another lightning implementation. -/// -/// Can only be constructed by calling [`UnauthenticatedReceiveTlvs::authenticate`]. #[derive(Clone, Debug)] pub struct ReceiveTlvs { - /// The TLVs for which the HMAC in `authentication` is derived. - pub(crate) tlvs: UnauthenticatedReceiveTlvs, - /// An HMAC of `tlvs` along with a nonce used to construct it. - pub(crate) authentication: (Hmac, Nonce), -} - -impl ReceiveTlvs { - /// Returns the underlying TLVs. - pub fn tlvs(&self) -> &UnauthenticatedReceiveTlvs { - &self.tlvs - } -} - -/// An unauthenticated [`ReceiveTlvs`]. -#[derive(Clone, Debug)] -pub struct UnauthenticatedReceiveTlvs { /// Used to authenticate the sender of a payment to the receiver and tie MPP HTLCs together. pub payment_secret: PaymentSecret, /// Constraints for the receiver of this payment. @@ -356,17 +334,6 @@ pub struct UnauthenticatedReceiveTlvs { pub payment_context: PaymentContext, } -impl UnauthenticatedReceiveTlvs { - /// Creates an authenticated [`ReceiveTlvs`], which includes an HMAC and the provide [`Nonce`] - /// that can be use later to verify it authenticity. - pub fn authenticate(self, nonce: Nonce, expanded_key: &ExpandedKey) -> ReceiveTlvs { - ReceiveTlvs { - authentication: (self.hmac_for_offer_payment(nonce, expanded_key), nonce), - tlvs: self, - } - } -} - /// Data to construct a [`BlindedHop`] for sending a payment over. /// /// [`BlindedHop`]: crate::blinded_path::BlindedHop @@ -539,19 +506,12 @@ impl Writeable for TrampolineForwardTlvs { } } +// Note: Authentication TLV field was removed in LDK v0.2 following the +// introduction of `ReceiveAuthKey`-based authentication for inbound +// `BlindedPaymentPaths`s. Because we do not support receiving to those +// contexts anymore (they will fail the `ReceiveAuthKey`-based +// authentication checks), we can reuse those fields here. impl Writeable for ReceiveTlvs { - fn write(&self, w: &mut W) -> Result<(), io::Error> { - encode_tlv_stream!(w, { - (12, self.tlvs.payment_constraints, required), - (65536, self.tlvs.payment_secret, required), - (65537, self.tlvs.payment_context, required), - (65539, self.authentication, required), - }); - Ok(()) - } -} - -impl Writeable for UnauthenticatedReceiveTlvs { fn write(&self, w: &mut W) -> Result<(), io::Error> { encode_tlv_stream!(w, { (12, self.payment_constraints, required), @@ -586,7 +546,6 @@ impl Readable for BlindedPaymentTlvs { (14, features, (option, encoding: (BlindedHopFeatures, WithoutLength))), (65536, payment_secret, option), (65537, payment_context, option), - (65539, authentication, option), }); if let Some(short_channel_id) = scid { @@ -605,12 +564,9 @@ impl Readable for BlindedPaymentTlvs { return Err(DecodeError::InvalidValue); } Ok(BlindedPaymentTlvs::Receive(ReceiveTlvs { - tlvs: UnauthenticatedReceiveTlvs { - payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?, - payment_constraints: payment_constraints.0.unwrap(), - payment_context: payment_context.ok_or(DecodeError::InvalidValue)?, - }, - authentication: authentication.ok_or(DecodeError::InvalidValue)?, + payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?, + payment_constraints: payment_constraints.0.unwrap(), + payment_context: payment_context.ok_or(DecodeError::InvalidValue)?, })) } } @@ -626,7 +582,6 @@ impl Readable for BlindedTrampolineTlvs { (14, features, (option, encoding: (BlindedHopFeatures, WithoutLength))), (65536, payment_secret, option), (65537, payment_context, option), - (65539, authentication, option), }); if let Some(next_trampoline) = next_trampoline { @@ -645,19 +600,15 @@ impl Readable for BlindedTrampolineTlvs { return Err(DecodeError::InvalidValue); } Ok(BlindedTrampolineTlvs::Receive(ReceiveTlvs { - tlvs: UnauthenticatedReceiveTlvs { - payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?, - payment_constraints: payment_constraints.0.unwrap(), - payment_context: payment_context.ok_or(DecodeError::InvalidValue)?, - }, - authentication: authentication.ok_or(DecodeError::InvalidValue)?, + payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?, + payment_constraints: payment_constraints.0.unwrap(), + payment_context: payment_context.ok_or(DecodeError::InvalidValue)?, })) } } } -/// Represents the padding round off size (in bytes) that -/// is used to pad payment bilnded path's [`BlindedHop`] +/// Represents the padding round-off size (in bytes) used to pad payment blinded path's [`BlindedHop`]. pub(crate) const PAYMENT_PADDING_ROUND_OFF: usize = 30; /// Construct blinded payment hops for the given `intermediate_nodes` and payee info. @@ -737,7 +688,7 @@ where } pub(super) fn compute_payinfo( - intermediate_nodes: &[PaymentForwardNode], payee_tlvs: &UnauthenticatedReceiveTlvs, + intermediate_nodes: &[PaymentForwardNode], payee_tlvs: &ReceiveTlvs, payee_htlc_maximum_msat: u64, min_final_cltv_expiry_delta: u16, ) -> Result { let (aggregated_base_fee, aggregated_prop_fee) = @@ -860,7 +811,7 @@ impl_writeable_tlv_based!(Bolt12RefundContext, {}); mod tests { use crate::blinded_path::payment::{ Bolt12RefundContext, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentForwardNode, - PaymentRelay, UnauthenticatedReceiveTlvs, + PaymentRelay, ReceiveTlvs, }; use crate::ln::functional_test_utils::TEST_FINAL_CLTV; use crate::types::features::BlindedHopFeatures; @@ -910,7 +861,7 @@ mod tests { htlc_maximum_msat: u64::max_value(), }, ]; - let recv_tlvs = UnauthenticatedReceiveTlvs { + let recv_tlvs = ReceiveTlvs { payment_secret: PaymentSecret([0; 32]), payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), @@ -928,7 +879,7 @@ mod tests { #[test] fn compute_payinfo_1_hop() { - let recv_tlvs = UnauthenticatedReceiveTlvs { + let recv_tlvs = ReceiveTlvs { payment_secret: PaymentSecret([0; 32]), payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), @@ -985,7 +936,7 @@ mod tests { htlc_maximum_msat: u64::max_value(), }, ]; - let recv_tlvs = UnauthenticatedReceiveTlvs { + let recv_tlvs = ReceiveTlvs { payment_secret: PaymentSecret([0; 32]), payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 3 }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), @@ -1044,7 +995,7 @@ mod tests { htlc_maximum_msat: u64::max_value(), }, ]; - let recv_tlvs = UnauthenticatedReceiveTlvs { + let recv_tlvs = ReceiveTlvs { payment_secret: PaymentSecret([0; 32]), payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), @@ -1113,7 +1064,7 @@ mod tests { htlc_maximum_msat: 10_000, }, ]; - let recv_tlvs = UnauthenticatedReceiveTlvs { + let recv_tlvs = ReceiveTlvs { payment_secret: PaymentSecret([0; 32]), payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), diff --git a/lightning/src/ln/async_payments_tests.rs b/lightning/src/ln/async_payments_tests.rs index d56670f4d67..78250e69f83 100644 --- a/lightning/src/ln/async_payments_tests.rs +++ b/lightning/src/ln/async_payments_tests.rs @@ -272,7 +272,6 @@ fn pass_async_payments_oms( fn create_static_invoice_builder<'a>( recipient: &Node, offer: &'a Offer, offer_nonce: Nonce, relative_expiry: Option, ) -> StaticInvoiceBuilder<'a> { - let entropy = recipient.keys_manager; let amount_msat = offer.amount().and_then(|amount| match amount { Amount::Bitcoin { amount_msats } => Some(amount_msats), Amount::Currency { .. } => None, @@ -296,7 +295,6 @@ fn create_static_invoice_builder<'a>( .flow .create_static_invoice_builder( &recipient.router, - entropy, offer, offer_nonce, payment_secret, @@ -1860,7 +1858,7 @@ fn expired_static_invoice_payment_path() { .advance_path_by_one(&nodes[1].keys_manager, &nodes[1].node, &secp_ctx) .unwrap(); match blinded_path.decrypt_intro_payload(&nodes[2].keys_manager).unwrap().0 { - BlindedPaymentTlvs::Receive(tlvs) => tlvs.tlvs.payment_constraints.max_cltv_expiry, + BlindedPaymentTlvs::Receive(tlvs) => tlvs.payment_constraints.max_cltv_expiry, _ => panic!(), } }; @@ -3106,7 +3104,6 @@ fn intercepted_hold_htlc() { .flow .test_create_blinded_payment_paths( &recipient.router, - recipient.keys_manager, first_hops, None, payment_secret, diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index f770c00f1e8..ab569637176 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -17,7 +17,7 @@ use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, schnorr}; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use crate::blinded_path; -use crate::blinded_path::payment::{BlindedPaymentPath, Bolt12RefundContext, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentForwardNode, PaymentRelay, UnauthenticatedReceiveTlvs, PAYMENT_PADDING_ROUND_OFF}; +use crate::blinded_path::payment::{BlindedPaymentPath, Bolt12RefundContext, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentForwardNode, PaymentRelay, ReceiveTlvs, PAYMENT_PADDING_ROUND_OFF}; use crate::blinded_path::utils::is_padded; use crate::events::{Event, HTLCHandlingFailureType, PaymentFailureReason}; use crate::ln::types::ChannelId; @@ -33,7 +33,6 @@ use crate::ln::onion_payment; use crate::ln::onion_utils::{self, LocalHTLCFailureReason}; use crate::ln::outbound_payment::{Retry, IDEMPOTENCY_TIMEOUT_TICKS}; use crate::offers::invoice::UnsignedBolt12Invoice; -use crate::offers::nonce::Nonce; use crate::prelude::*; use crate::routing::router::{BlindedTail, Path, Payee, PaymentParameters, RouteHop, RouteParameters, TrampolineHop}; use crate::sign::{NodeSigner, PeerStorageKey, ReceiveAuthKey, Recipient}; @@ -76,7 +75,7 @@ pub fn blinded_payment_path( }); } - let payee_tlvs = UnauthenticatedReceiveTlvs { + let payee_tlvs = ReceiveTlvs { payment_secret, payment_constraints: PaymentConstraints { max_cltv_expiry: u32::max_value(), @@ -86,10 +85,7 @@ pub fn blinded_payment_path( payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), }; - let nonce = Nonce([42u8; 16]); - let expanded_key = keys_manager.get_expanded_key(); let receive_auth_key = keys_manager.get_receive_auth_key(); - let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let mut secp_ctx = Secp256k1::new(); BlindedPaymentPath::new( @@ -164,7 +160,7 @@ fn do_one_hop_blinded_path(success: bool) { let amt_msat = 5000; let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[1], Some(amt_msat), None); - let payee_tlvs = UnauthenticatedReceiveTlvs { + let payee_tlvs = ReceiveTlvs { payment_secret, payment_constraints: PaymentConstraints { max_cltv_expiry: u32::max_value(), @@ -172,10 +168,7 @@ fn do_one_hop_blinded_path(success: bool) { }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), }; - let nonce = Nonce([42u8; 16]); - let expanded_key = chanmon_cfgs[1].keys_manager.get_expanded_key(); let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_key(); - let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPaymentPath::new( @@ -220,7 +213,7 @@ fn mpp_to_one_hop_blinded_path() { let amt_msat = 15_000_000; let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[3], Some(amt_msat), None); - let payee_tlvs = UnauthenticatedReceiveTlvs { + let payee_tlvs = ReceiveTlvs { payment_secret, payment_constraints: PaymentConstraints { max_cltv_expiry: u32::max_value(), @@ -228,10 +221,7 @@ fn mpp_to_one_hop_blinded_path() { }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), }; - let nonce = Nonce([42u8; 16]); - let expanded_key = chanmon_cfgs[3].keys_manager.get_expanded_key(); let receive_auth_key = chanmon_cfgs[3].keys_manager.get_receive_auth_key(); - let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let blinded_path = BlindedPaymentPath::new( &[], nodes[3].node.get_our_node_id(), receive_auth_key, payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, @@ -1332,7 +1322,7 @@ fn custom_tlvs_to_blinded_path() { let amt_msat = 5000; let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[1], Some(amt_msat), None); - let payee_tlvs = UnauthenticatedReceiveTlvs { + let payee_tlvs = ReceiveTlvs { payment_secret, payment_constraints: PaymentConstraints { max_cltv_expiry: u32::max_value(), @@ -1340,10 +1330,8 @@ fn custom_tlvs_to_blinded_path() { }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), }; - let nonce = Nonce([42u8; 16]); - let expanded_key = chanmon_cfgs[1].keys_manager.get_expanded_key(); let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_key(); - let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); + let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPaymentPath::new( &[], nodes[1].node.get_our_node_id(), receive_auth_key, @@ -1388,7 +1376,7 @@ fn fails_receive_tlvs_authentication() { let amt_msat = 5000; let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[1], Some(amt_msat), None); - let payee_tlvs = UnauthenticatedReceiveTlvs { + let payee_tlvs = ReceiveTlvs { payment_secret, payment_constraints: PaymentConstraints { max_cltv_expiry: u32::max_value(), @@ -1396,10 +1384,7 @@ fn fails_receive_tlvs_authentication() { }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), }; - let nonce = Nonce([42u8; 16]); - let expanded_key = chanmon_cfgs[1].keys_manager.get_expanded_key(); let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_key(); - let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPaymentPath::new( @@ -1421,7 +1406,7 @@ fn fails_receive_tlvs_authentication() { // Swap in a different nonce to force authentication to fail. let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[1], Some(amt_msat), None); - let payee_tlvs = UnauthenticatedReceiveTlvs { + let payee_tlvs = ReceiveTlvs { payment_secret, payment_constraints: PaymentConstraints { max_cltv_expiry: u32::max_value(), @@ -1429,13 +1414,12 @@ fn fails_receive_tlvs_authentication() { }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), }; - let nonce = Nonce([43u8; 16]); - let mut payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); - payee_tlvs.authentication.1 = Nonce([0u8; 16]); + // Use a mismatched ReceiveAuthKey to force auth failure: + let mismatched_receive_auth_key = ReceiveAuthKey([0u8; 32]); let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPaymentPath::new( - &[], nodes[1].node.get_our_node_id(), receive_auth_key, + &[], nodes[1].node.get_our_node_id(), mismatched_receive_auth_key, payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, &chanmon_cfgs[1].keys_manager, &secp_ctx ).unwrap(); @@ -2024,7 +2008,7 @@ fn do_test_trampoline_single_hop_receive(success: bool) { let carol_alice_trampoline_session_priv = secret_from_hex("a0f4b8d7b6c2d0ffdfaf718f76e9decaef4d9fb38a8c4addb95c4007cc3eee03"); let carol_blinding_point = PublicKey::from_secret_key(&secp_ctx, &carol_alice_trampoline_session_priv); let carol_blinded_hops = if success { - let payee_tlvs = UnauthenticatedReceiveTlvs { + let payee_tlvs = ReceiveTlvs { payment_secret, payment_constraints: PaymentConstraints { max_cltv_expiry: u32::max_value(), @@ -2033,9 +2017,6 @@ fn do_test_trampoline_single_hop_receive(success: bool) { payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), }; - let nonce = Nonce([42u8; 16]); - let expanded_key = nodes[2].keys_manager.get_expanded_key(); - let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let carol_unblinded_tlvs = payee_tlvs.encode(); let receive_auth_key = nodes[2].keys_manager.get_receive_auth_key(); diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index cd23821b713..8a5c66f02ba 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -35,9 +35,7 @@ use bitcoin::{secp256k1, Sequence, SignedAmount}; use crate::blinded_path::message::{ AsyncPaymentsContext, BlindedMessagePath, MessageForwardNode, OffersContext, }; -use crate::blinded_path::payment::{ - AsyncBolt12OfferContext, Bolt12OfferContext, PaymentContext, UnauthenticatedReceiveTlvs, -}; +use crate::blinded_path::payment::{AsyncBolt12OfferContext, Bolt12OfferContext, PaymentContext}; use crate::blinded_path::NodeIdLookUp; use crate::chain; use crate::chain::chaininterface::{ @@ -101,7 +99,6 @@ use crate::offers::nonce::Nonce; use crate::offers::offer::{Offer, OfferFromHrn}; use crate::offers::parse::Bolt12SemanticError; use crate::offers::refund::Refund; -use crate::offers::signer; use crate::offers::static_invoice::StaticInvoice; use crate::onion_message::async_payments::{ AsyncPaymentsMessage, AsyncPaymentsMessageHandler, HeldHtlcAvailable, OfferPaths, @@ -563,34 +560,6 @@ impl Ord for ClaimableHTLC { } } -/// A trait defining behavior for creating and verifing the HMAC for authenticating a given data. -pub trait Verification { - /// Constructs an HMAC to include in [`OffersContext`] for the data along with the given - /// [`Nonce`]. - fn hmac_for_offer_payment( - &self, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey, - ) -> Hmac; - - /// Authenticates the data using an HMAC and a [`Nonce`] taken from an [`OffersContext`]. - fn verify_for_offer_payment( - &self, hmac: Hmac, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey, - ) -> Result<(), ()>; -} - -impl Verification for UnauthenticatedReceiveTlvs { - fn hmac_for_offer_payment( - &self, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey, - ) -> Hmac { - signer::hmac_for_payment_tlvs(self, nonce, expanded_key) - } - - fn verify_for_offer_payment( - &self, hmac: Hmac, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey, - ) -> Result<(), ()> { - signer::verify_payment_tlvs(self, hmac, nonce, expanded_key) - } -} - /// A user-provided identifier in [`ChannelManager::send_payment`] used to uniquely identify /// a payment and ensure idempotency in LDK. /// @@ -5440,12 +5409,10 @@ where fn check_refresh_async_receive_offer_cache(&self, timer_tick_occurred: bool) { let peers = self.get_peers_for_blinded_path(); let channels = self.list_usable_channels(); - let entropy = &*self.entropy_source; let router = &*self.router; let refresh_res = self.flow.check_refresh_async_receive_offer_cache( peers, channels, - entropy, router, timer_tick_occurred, ); @@ -12964,11 +12931,8 @@ where &self, amount_msats: Option, payment_secret: PaymentSecret, payment_context: PaymentContext, relative_expiry_seconds: u32, ) -> Result, ()> { - let entropy = &*self.entropy_source; - self.flow.test_create_blinded_payment_paths( &self.router, - entropy, self.list_usable_channels(), amount_msats, payment_secret, @@ -14847,9 +14811,8 @@ where }, }; - let entropy = &*self.entropy_source; let (response, context) = self.flow.create_response_for_invoice_request( - &self.node_signer, &self.router, entropy, invoice_request, amount_msats, + &self.node_signer, &self.router, invoice_request, amount_msats, payment_hash, payment_secret, self.list_usable_channels() ); diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs index 366f54b9c80..f67ad442c29 100644 --- a/lightning/src/ln/max_payment_path_len_tests.rs +++ b/lightning/src/ln/max_payment_path_len_tests.rs @@ -12,7 +12,7 @@ use crate::blinded_path::payment::{ BlindedPayInfo, BlindedPaymentPath, Bolt12RefundContext, PaymentConstraints, PaymentContext, - UnauthenticatedReceiveTlvs, + ReceiveTlvs, }; use crate::blinded_path::BlindedHop; use crate::events::Event; @@ -24,7 +24,6 @@ use crate::ln::msgs::{BaseMessageHandler, OnionMessageHandler}; use crate::ln::onion_utils; use crate::ln::onion_utils::MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY; use crate::ln::outbound_payment::{RecipientOnionFields, Retry, RetryableSendFailure}; -use crate::offers::nonce::Nonce; use crate::prelude::*; use crate::routing::router::{ PaymentParameters, RouteParameters, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, @@ -213,7 +212,7 @@ fn one_hop_blinded_path_with_custom_tlv() { let amt_msat = 100_000; let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), None); - let payee_tlvs = UnauthenticatedReceiveTlvs { + let payee_tlvs = ReceiveTlvs { payment_secret, payment_constraints: PaymentConstraints { max_cltv_expiry: u32::max_value(), @@ -221,10 +220,7 @@ fn one_hop_blinded_path_with_custom_tlv() { }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), }; - let nonce = Nonce([42u8; 16]); - let expanded_key = chanmon_cfgs[2].keys_manager.get_expanded_key(); let receive_auth_key = chanmon_cfgs[2].keys_manager.get_receive_auth_key(); - let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPaymentPath::new( &[], diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 5318f3d062a..6dd147da6d9 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -32,11 +32,8 @@ use bitcoin::secp256k1::PublicKey; use bitcoin::{secp256k1, Transaction, Witness}; use crate::blinded_path::message::BlindedMessagePath; -use crate::blinded_path::payment::{ - BlindedPaymentTlvs, ForwardTlvs, ReceiveTlvs, UnauthenticatedReceiveTlvs, -}; +use crate::blinded_path::payment::{BlindedPaymentTlvs, ForwardTlvs, ReceiveTlvs}; use crate::blinded_path::payment::{BlindedTrampolineTlvs, TrampolineForwardTlvs}; -use crate::ln::channelmanager::Verification; use crate::ln::onion_utils; use crate::ln::types::ChannelId; use crate::offers::invoice_request::InvoiceRequest; @@ -3696,17 +3693,9 @@ where readable: BlindedPaymentTlvs::Receive(receive_tlvs), used_aad, } => { - let ReceiveTlvs { tlvs, authentication: (hmac, nonce) } = receive_tlvs; - let expanded_key = node_signer.get_expanded_key(); - if tlvs.verify_for_offer_payment(hmac, nonce, &expanded_key).is_err() { - return Err(DecodeError::InvalidValue); - } + let ReceiveTlvs { payment_secret, payment_constraints, payment_context } = + receive_tlvs; - let UnauthenticatedReceiveTlvs { - payment_secret, - payment_constraints, - payment_context, - } = tlvs; if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue); } @@ -3853,17 +3842,9 @@ where readable: BlindedTrampolineTlvs::Receive(receive_tlvs), used_aad, } => { - let ReceiveTlvs { tlvs, authentication: (hmac, nonce) } = receive_tlvs; - let expanded_key = node_signer.get_expanded_key(); - if tlvs.verify_for_offer_payment(hmac, nonce, &expanded_key).is_err() { - return Err(DecodeError::InvalidValue); - } + let ReceiveTlvs { payment_secret, payment_constraints, payment_context } = + receive_tlvs; - let UnauthenticatedReceiveTlvs { - payment_secret, - payment_constraints, - payment_context, - } = tlvs; if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue); } diff --git a/lightning/src/offers/flow.rs b/lightning/src/offers/flow.rs index 9c2a02f3d06..24cd25aebee 100644 --- a/lightning/src/offers/flow.rs +++ b/lightning/src/offers/flow.rs @@ -23,7 +23,7 @@ use crate::blinded_path::message::{ }; use crate::blinded_path::payment::{ AsyncBolt12OfferContext, BlindedPaymentPath, Bolt12OfferContext, Bolt12RefundContext, - PaymentConstraints, PaymentContext, UnauthenticatedReceiveTlvs, + PaymentConstraints, PaymentContext, ReceiveTlvs, }; use crate::chain::channelmonitor::LATENCY_GRACE_PERIOD_BLOCKS; @@ -319,17 +319,14 @@ where /// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to /// [`Router::create_blinded_payment_paths`]. - fn create_blinded_payment_paths( - &self, router: &R, entropy_source: ES, usable_channels: Vec, - amount_msats: Option, payment_secret: PaymentSecret, payment_context: PaymentContext, + fn create_blinded_payment_paths( + &self, router: &R, usable_channels: Vec, amount_msats: Option, + payment_secret: PaymentSecret, payment_context: PaymentContext, relative_expiry_seconds: u32, ) -> Result, ()> where - ES::Target: EntropySource, R::Target: Router, { - let expanded_key = &self.inbound_payment_key; - let entropy = &*entropy_source; let secp_ctx = &self.secp_ctx; let receive_auth_key = self.receive_auth_key; @@ -342,13 +339,11 @@ where .saturating_add(LATENCY_GRACE_PERIOD_BLOCKS) .saturating_add(self.best_block.read().unwrap().height); - let payee_tlvs = UnauthenticatedReceiveTlvs { + let payee_tlvs = ReceiveTlvs { payment_secret, payment_constraints: PaymentConstraints { max_cltv_expiry, htlc_minimum_msat: 1 }, payment_context, }; - let nonce = Nonce::from_entropy_source(entropy); - let payee_tlvs = payee_tlvs.authenticate(nonce, expanded_key); router.create_blinded_payment_paths( payee_node_id, @@ -363,18 +358,16 @@ where #[cfg(test)] /// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to /// [`Router::create_blinded_payment_paths`]. - pub(crate) fn test_create_blinded_payment_paths( - &self, router: &R, entropy_source: ES, usable_channels: Vec, - amount_msats: Option, payment_secret: PaymentSecret, payment_context: PaymentContext, + pub(crate) fn test_create_blinded_payment_paths( + &self, router: &R, usable_channels: Vec, amount_msats: Option, + payment_secret: PaymentSecret, payment_context: PaymentContext, relative_expiry_seconds: u32, ) -> Result, ()> where - ES::Target: EntropySource, R::Target: Router, { self.create_blinded_payment_paths( router, - entropy_source, usable_channels, amount_msats, payment_secret, @@ -816,17 +809,15 @@ where /// Creates a [`StaticInvoiceBuilder`] from the corresponding [`Offer`] and [`Nonce`] that were /// created via [`Self::create_async_receive_offer_builder`]. - pub fn create_static_invoice_builder<'a, ES: Deref, R: Deref>( - &self, router: &R, entropy_source: ES, offer: &'a Offer, offer_nonce: Nonce, - payment_secret: PaymentSecret, relative_expiry_secs: u32, - usable_channels: Vec, peers: Vec, + pub fn create_static_invoice_builder<'a, R: Deref>( + &self, router: &R, offer: &'a Offer, offer_nonce: Nonce, payment_secret: PaymentSecret, + relative_expiry_secs: u32, usable_channels: Vec, + peers: Vec, ) -> Result, Bolt12SemanticError> where - ES::Target: EntropySource, R::Target: Router, { let expanded_key = &self.inbound_payment_key; - let entropy = &*entropy_source; let secp_ctx = &self.secp_ctx; let payment_context = @@ -842,7 +833,6 @@ where let payment_paths = self .create_blinded_payment_paths( router, - entropy, usable_channels, amount_msat, payment_secret, @@ -910,7 +900,6 @@ where let payment_paths = self .create_blinded_payment_paths( router, - entropy, usable_channels, Some(amount_msats), payment_secret, @@ -949,17 +938,15 @@ where /// An [`OffersMessage::InvoiceError`] will be generated if: /// - We fail to generate valid payment paths to include in the [`Bolt12Invoice`]. /// - We fail to generate a valid signed [`Bolt12Invoice`] for the [`InvoiceRequest`]. - pub fn create_response_for_invoice_request( - &self, signer: &NS, router: &R, entropy_source: ES, - invoice_request: VerifiedInvoiceRequest, amount_msats: u64, payment_hash: PaymentHash, - payment_secret: PaymentSecret, usable_channels: Vec, + pub fn create_response_for_invoice_request( + &self, signer: &NS, router: &R, invoice_request: VerifiedInvoiceRequest, amount_msats: u64, + payment_hash: PaymentHash, payment_secret: PaymentSecret, + usable_channels: Vec, ) -> (OffersMessage, Option) where - ES::Target: EntropySource, NS::Target: NodeSigner, R::Target: Router, { - let entropy = &*entropy_source; let secp_ctx = &self.secp_ctx; let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32; @@ -971,7 +958,6 @@ where let payment_paths = match self.create_blinded_payment_paths( router, - entropy, usable_channels, Some(amount_msats), payment_secret, @@ -1334,12 +1320,11 @@ where /// the cache can self-regulate the number of messages sent out. /// /// Errors if we failed to create blinded reply paths when sending an [`OfferPathsRequest`] message. - pub fn check_refresh_async_receive_offer_cache( - &self, peers: Vec, usable_channels: Vec, entropy: ES, - router: R, timer_tick_occurred: bool, + pub fn check_refresh_async_receive_offer_cache( + &self, peers: Vec, usable_channels: Vec, router: R, + timer_tick_occurred: bool, ) -> Result<(), ()> where - ES::Target: EntropySource, R::Target: Router, { // Terminate early if this node does not intend to receive async payments. @@ -1353,7 +1338,7 @@ where self.check_refresh_async_offers(peers.clone(), timer_tick_occurred)?; if timer_tick_occurred { - self.check_refresh_static_invoices(peers, usable_channels, entropy, router); + self.check_refresh_static_invoices(peers, usable_channels, router); } Ok(()) @@ -1410,11 +1395,9 @@ where /// Enqueue onion messages that will used to request invoice refresh from the static invoice /// server, based on the offers provided by the cache. - fn check_refresh_static_invoices( - &self, peers: Vec, usable_channels: Vec, entropy: ES, - router: R, + fn check_refresh_static_invoices( + &self, peers: Vec, usable_channels: Vec, router: R, ) where - ES::Target: EntropySource, R::Target: Router, { let mut serve_static_invoice_msgs = Vec::new(); @@ -1429,7 +1412,6 @@ where offer_nonce, peers.clone(), usable_channels.clone(), - &*entropy, &*router, ) { Ok((invoice, path)) => (invoice, path), @@ -1595,7 +1577,6 @@ where offer_nonce, peers, usable_channels, - &*entropy, router, ) { Ok(res) => res, @@ -1630,12 +1611,11 @@ where /// Creates a [`StaticInvoice`] and a blinded path for the server to forward invoice requests from /// payers to our node. - fn create_static_invoice_for_server( + fn create_static_invoice_for_server( &self, offer: &Offer, offer_nonce: Nonce, peers: Vec, - usable_channels: Vec, entropy: ES, router: R, + usable_channels: Vec, router: R, ) -> Result<(StaticInvoice, BlindedMessagePath), ()> where - ES::Target: EntropySource, R::Target: Router, { let expanded_key = &self.inbound_payment_key; @@ -1662,7 +1642,6 @@ where let invoice = self .create_static_invoice_builder( &router, - &*entropy, &offer, offer_nonce, payment_secret, diff --git a/lightning/src/offers/signer.rs b/lightning/src/offers/signer.rs index 645949ff866..e51a120b6d7 100644 --- a/lightning/src/offers/signer.rs +++ b/lightning/src/offers/signer.rs @@ -9,7 +9,6 @@ //! Utilities for signing offer messages and verifying metadata. -use crate::blinded_path::payment::UnauthenticatedReceiveTlvs; use crate::ln::channelmanager::PaymentId; use crate::ln::inbound_payment::{ExpandedKey, IV_LEN}; use crate::offers::merkle::TlvRecord; @@ -41,16 +40,14 @@ const WITH_ENCRYPTED_PAYMENT_ID_HMAC_INPUT: &[u8; 16] = &[4; 16]; // The following `HMAC_INPUT` constants were previously used for authenticating fields in // `OffersContext`, but were removed in LDK v0.2 with the introduction of `ReceiveAuthKey`-based // authentication. -// Their corresponding values (`[5; 16]` and `[7; 16]`) are now reserved and must not +// Their corresponding values (`[5; 16]`, `[7; 16]` and `[8; 16]`) are now reserved and must not // be reused to ensure type confusion attacks are impossible. // // Reserved HMAC_INPUT values — do not reuse: // // const OFFER_PAYMENT_ID_HMAC_INPUT: &[u8; 16] = &[5; 16]; // const PAYMENT_HASH_HMAC_INPUT: &[u8; 16] = &[7; 16]; - -// HMAC input for `ReceiveTlvs`. The HMAC is used in `blinded_path::payment::PaymentContext`. -const PAYMENT_TLVS_HMAC_INPUT: &[u8; 16] = &[8; 16]; +// const PAYMENT_TLVS_HMAC_INPUT: &[u8; 16] = &[8; 16]; /// Message metadata which possibly is derived from [`MetadataMaterial`] such that it can be /// verified. @@ -449,27 +446,3 @@ fn hmac_for_message<'a>( Ok(hmac) } - -pub(crate) fn hmac_for_payment_tlvs( - receive_tlvs: &UnauthenticatedReceiveTlvs, nonce: Nonce, expanded_key: &ExpandedKey, -) -> Hmac { - const IV_BYTES: &[u8; IV_LEN] = b"LDK Payment TLVs"; - let mut hmac = expanded_key.hmac_for_offer(); - hmac.input(IV_BYTES); - hmac.input(&nonce.0); - hmac.input(PAYMENT_TLVS_HMAC_INPUT); - receive_tlvs.write(&mut hmac).unwrap(); - - Hmac::from_engine(hmac) -} - -pub(crate) fn verify_payment_tlvs( - receive_tlvs: &UnauthenticatedReceiveTlvs, hmac: Hmac, nonce: Nonce, - expanded_key: &ExpandedKey, -) -> Result<(), ()> { - if hmac_for_payment_tlvs(receive_tlvs, nonce, expanded_key) == hmac { - Ok(()) - } else { - Err(()) - } -} diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index f7fc21fa25a..3eb14063f64 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -180,7 +180,9 @@ where let cltv_expiry_delta = payment_relay.cltv_expiry_delta as u32; let payment_constraints = PaymentConstraints { - max_cltv_expiry: tlvs.tlvs().payment_constraints.max_cltv_expiry + cltv_expiry_delta, + max_cltv_expiry: tlvs.payment_constraints + .max_cltv_expiry + .saturating_add(cltv_expiry_delta), htlc_minimum_msat: details.inbound_htlc_minimum_msat.unwrap_or(0), }; Some(PaymentForwardNode {