diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 9f03de47d23..17a46ff3ac8 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, _local_node_receive_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 97a74871ea4..6ddcd782225 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -157,8 +157,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, _local_node_receive_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..a21303debd7 100644 --- a/fuzz/src/invoice_request_deser.rs +++ b/fuzz/src/invoice_request_deser.rs @@ -12,16 +12,15 @@ 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; +use lightning::sign::{EntropySource, ReceiveAuthKey}; use lightning::types::features::BlindedHopFeatures; use lightning::types::payment::{PaymentHash, PaymentSecret}; use lightning::types::string::UntrustedString; @@ -84,7 +83,7 @@ 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 = if let Ok(ver) = invoice_request.clone().verify_using_metadata(&expanded_key, secp_ctx) { @@ -106,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, @@ -114,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, @@ -124,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(), @@ -136,6 +134,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..446ac704455 100644 --- a/fuzz/src/refund_deser.rs +++ b/fuzz/src/refund_deser.rs @@ -12,15 +12,13 @@ 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; +use lightning::sign::{EntropySource, ReceiveAuthKey}; use lightning::types::features::BlindedHopFeatures; use lightning::types::payment::{PaymentHash, PaymentSecret}; use lightning::util::ser::Writeable; @@ -69,11 +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 nonce = Nonce::from_entropy_source(&entropy_source); + let receive_auth_key = ReceiveAuthKey([41; 32]); 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, @@ -81,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, @@ -91,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(), @@ -103,6 +99,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 37d7a1dba7d..13ade222f5b 100644 --- a/lightning/src/blinded_path/payment.rs +++ b/lightning/src/blinded_path/payment.rs @@ -9,26 +9,22 @@ //! 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}; 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; -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; 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 +89,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, local_node_receive_key: ReceiveAuthKey, payee_tlvs: ReceiveTlvs, + min_final_cltv_expiry_delta: u16, entropy_source: ES, secp_ctx: &Secp256k1, ) -> Result where ES::Target: EntropySource, @@ -105,6 +101,7 @@ impl BlindedPaymentPath { Self::new( &[], payee_node_id, + local_node_receive_key, payee_tlvs, htlc_maximum_msat, min_final_cltv_expiry_delta, @@ -121,8 +118,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, + local_node_receive_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, @@ -136,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, )?; @@ -150,6 +147,7 @@ impl BlindedPaymentPath { payee_node_id, payee_tlvs, &blinding_secret, + local_node_receive_key, ), }, payinfo: blinded_payinfo, @@ -226,12 +224,19 @@ 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)), + let ChaChaDualPolyReadAdapter { readable, used_aad } = + ChaChaDualPolyReadAdapter::read(&mut reader, (rho, receive_auth_key.0)) + .map_err(|_| ())?; + + match (&readable, used_aad) { + (BlindedPaymentTlvs::Forward(_), false) | (BlindedPaymentTlvs::Receive(_), true) => { + Ok((readable, control_tlvs_ss)) + }, _ => Err(()), } } @@ -325,26 +330,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. @@ -353,17 +340,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 @@ -536,19 +512,12 @@ impl Writeable for TrampolineForwardTlvs { } } +// Note: The `authentication` TLV field was removed in LDK v0.3 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 that field 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), @@ -583,7 +552,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 { @@ -602,12 +570,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)?, })) } } @@ -623,7 +588,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 { @@ -642,30 +606,26 @@ 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. 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)) @@ -734,7 +694,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) = @@ -857,7 +817,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; @@ -907,7 +867,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 {}), @@ -925,7 +885,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 {}), @@ -982,7 +942,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 {}), @@ -1041,7 +1001,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 {}), @@ -1110,7 +1070,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 8959e347bf7..3f65b89821d 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -15,7 +15,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; @@ -31,7 +31,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}; @@ -74,7 +73,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(), @@ -84,14 +83,12 @@ pub fn blinded_payment_path( payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), }; - let nonce = Nonce([42u8; 16]); - let expanded_key = keys_manager.get_expanded_key(); - let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); + let receive_auth_key = keys_manager.get_receive_auth_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() } @@ -161,7 +158,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(), @@ -169,13 +166,12 @@ 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 payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); + let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_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(); @@ -215,7 +211,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(), @@ -223,11 +219,10 @@ 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 payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); + let receive_auth_key = chanmon_cfgs[3].keys_manager.get_receive_auth_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(); @@ -1325,7 +1320,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(), @@ -1333,12 +1328,12 @@ 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 payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); + let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_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(); @@ -1379,7 +1374,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(), @@ -1387,13 +1382,12 @@ 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 payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); + let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_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(); @@ -1410,7 +1404,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(), @@ -1418,13 +1412,13 @@ 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(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, + &[], 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(); @@ -1627,7 +1621,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!() } @@ -1940,7 +1934,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!() } @@ -2197,7 +2191,7 @@ fn do_test_trampoline_single_hop_receive(success: bool) { let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), None); // Create a 1-hop blinded path for Carol. - let payee_tlvs = UnauthenticatedReceiveTlvs { + let payee_tlvs = ReceiveTlvs { payment_secret, payment_constraints: PaymentConstraints { max_cltv_expiry: u32::max_value(), @@ -2205,10 +2199,8 @@ 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 blinded_path = BlindedPaymentPath::new(&[], carol_node_id, payee_tlvs, u64::MAX, 0, nodes[2].keys_manager, &secp_ctx).unwrap(); + let receive_auth_key = nodes[2].keys_manager.get_receive_auth_key(); + let blinded_path = BlindedPaymentPath::new(&[], carol_node_id, receive_auth_key, payee_tlvs, u64::MAX, 0, nodes[2].keys_manager, &secp_ctx).unwrap(); let route = Route { paths: vec![Path { diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 644920557d2..271ecaa9b24 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::{ @@ -100,7 +98,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, @@ -574,34 +571,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. /// @@ -5642,12 +5611,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, ); @@ -13364,11 +13331,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, @@ -15272,7 +15236,6 @@ where InvoiceRequestVerifiedFromOffer::DerivedKeys(request) => { let result = self.flow.create_invoice_builder_from_invoice_request_with_keys( &self.router, - &*self.entropy_source, &request, self.list_usable_channels(), get_payment_info, @@ -15297,7 +15260,6 @@ where InvoiceRequestVerifiedFromOffer::ExplicitKeys(request) => { let result = self.flow.create_invoice_builder_from_invoice_request_without_keys( &self.router, - &*self.entropy_source, &request, self.list_usable_channels(), get_payment_info, diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs index 8425e1928ad..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,13 +220,12 @@ 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 payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); + let receive_auth_key = chanmon_cfgs[2].keys_manager.get_receive_auth_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 c0c8239f621..67bf764c397 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; @@ -59,7 +56,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::{ @@ -3655,10 +3652,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, @@ -3667,11 +3665,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); } @@ -3684,18 +3684,17 @@ where next_blinding_override, })) }, - ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Receive(receive_tlvs) } => { - 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() { + ChaChaDualPolyReadAdapter { + readable: BlindedPaymentTlvs::Receive(receive_tlvs), + used_aad, + } => { + if !used_aad { return Err(DecodeError::InvalidValue); } - let UnauthenticatedReceiveTlvs { - payment_secret, - payment_constraints, - payment_context, - } = tlvs; + let ReceiveTlvs { payment_secret, payment_constraints, payment_context } = + receive_tlvs; + if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue); } @@ -3754,6 +3753,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; @@ -3807,8 +3807,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, @@ -3817,11 +3817,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); } @@ -3834,20 +3836,17 @@ 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(); - if tlvs.verify_for_offer_payment(hmac, nonce, &expanded_key).is_err() { + if !used_aad { return Err(DecodeError::InvalidValue); } - let UnauthenticatedReceiveTlvs { - payment_secret, - payment_constraints, - payment_context, - } = tlvs; + let ReceiveTlvs { payment_secret, payment_constraints, payment_context } = + receive_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 88f0cc5079c..94a4534c61a 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; @@ -317,18 +317,16 @@ 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; let payee_node_id = self.get_our_node_id(); @@ -339,16 +337,15 @@ 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, + receive_auth_key, usable_channels, payee_tlvs, amount_msats, @@ -359,18 +356,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, @@ -826,17 +821,15 @@ where /// created via [`Self::create_async_receive_offer_builder`]. /// /// This is not exported to bindings users as builder patterns don't map outside of move semantics. - 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 = @@ -852,7 +845,6 @@ where let payment_paths = self .create_blinded_payment_paths( router, - entropy, usable_channels, amount_msat, payment_secret, @@ -925,7 +917,6 @@ where let payment_paths = self .create_blinded_payment_paths( router, - entropy, usable_channels, Some(amount_msats), payment_secret, @@ -970,18 +961,14 @@ where /// Returns a [`Bolt12SemanticError`] if: /// - Valid blinded payment paths could not be generated for the [`Bolt12Invoice`]. /// - The [`InvoiceBuilder`] could not be created from the [`InvoiceRequest`]. - pub fn create_invoice_builder_from_invoice_request_with_keys<'a, ES: Deref, R: Deref, F>( - &self, router: &R, entropy_source: ES, - invoice_request: &'a VerifiedInvoiceRequest, + pub fn create_invoice_builder_from_invoice_request_with_keys<'a, R: Deref, F>( + &self, router: &R, invoice_request: &'a VerifiedInvoiceRequest, usable_channels: Vec, get_payment_info: F, ) -> Result<(InvoiceBuilder<'a, DerivedSigningPubkey>, MessageContext), Bolt12SemanticError> where - ES::Target: EntropySource, - R::Target: Router, F: Fn(u64, u32) -> Result<(PaymentHash, PaymentSecret), Bolt12SemanticError>, { - let entropy = &*entropy_source; let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32; let amount_msats = @@ -997,7 +984,6 @@ where let payment_paths = self .create_blinded_payment_paths( router, - entropy, usable_channels, Some(amount_msats), payment_secret, @@ -1035,17 +1021,14 @@ where /// Returns a [`Bolt12SemanticError`] if: /// - Valid blinded payment paths could not be generated for the [`Bolt12Invoice`]. /// - The [`InvoiceBuilder`] could not be created from the [`InvoiceRequest`]. - pub fn create_invoice_builder_from_invoice_request_without_keys<'a, ES: Deref, R: Deref, F>( - &self, router: &R, entropy_source: ES, - invoice_request: &'a VerifiedInvoiceRequest, + pub fn create_invoice_builder_from_invoice_request_without_keys<'a, R: Deref, F>( + &self, router: &R, invoice_request: &'a VerifiedInvoiceRequest, usable_channels: Vec, get_payment_info: F, ) -> Result<(InvoiceBuilder<'a, ExplicitSigningPubkey>, MessageContext), Bolt12SemanticError> where - ES::Target: EntropySource, R::Target: Router, F: Fn(u64, u32) -> Result<(PaymentHash, PaymentSecret), Bolt12SemanticError>, { - let entropy = &*entropy_source; let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32; let amount_msats = @@ -1061,7 +1044,6 @@ where let payment_paths = self .create_blinded_payment_paths( router, - entropy, usable_channels, Some(amount_msats), payment_secret, @@ -1392,12 +1374,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. @@ -1411,7 +1392,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(()) @@ -1468,11 +1449,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(); @@ -1487,7 +1466,6 @@ where offer_nonce, peers.clone(), usable_channels.clone(), - &*entropy, &*router, ) { Ok((invoice, path)) => (invoice, path), @@ -1653,7 +1631,6 @@ where offer_nonce, peers, usable_channels, - &*entropy, router, ) { Ok(res) => res, @@ -1688,12 +1665,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; @@ -1720,7 +1696,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 8434b17698d..e969237d10f 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, local_node_receive_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; @@ -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 { @@ -197,7 +199,7 @@ where }) .map(|forward_node| { BlindedPaymentPath::new( - &[forward_node], recipient, tlvs.clone(), u64::MAX, MIN_FINAL_CLTV_EXPIRY_DELTA, + &[forward_node], recipient, local_node_receive_key, tlvs.clone(), u64::MAX, MIN_FINAL_CLTV_EXPIRY_DELTA, &*self.entropy_source, secp_ctx ) }) @@ -209,7 +211,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, local_node_receive_key, tlvs, u64::MAX, MIN_FINAL_CLTV_EXPIRY_DELTA, &*self.entropy_source, secp_ctx ).map(|path| vec![path]) } else { @@ -243,8 +245,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, _local_node_receive_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 +284,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 `local_node_receive_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, local_node_receive_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 ad8ea224205..c0023a2b74b 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -289,13 +289,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, local_node_receive_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, + local_node_receive_key, first_hops, tlvs, amount_msats,