diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 8b41c53a635..1439e11da6c 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -326,7 +326,7 @@ impl NodeSigner for KeyProvider { Ok(SharedSecret::new(other_key, &node_secret)) } - fn get_inbound_payment_key(&self) -> ExpandedKey { + fn get_expanded_key(&self) -> ExpandedKey { #[rustfmt::skip] let random_bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, self.node_secret[31]]; ExpandedKey::new(random_bytes) diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 5281a933526..435bb24dae1 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -405,7 +405,7 @@ impl NodeSigner for KeyProvider { Ok(SharedSecret::new(other_key, &node_secret)) } - fn get_inbound_payment_key(&self) -> ExpandedKey { + fn get_expanded_key(&self) -> ExpandedKey { self.inbound_payment_key } diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index a5782dacd42..fd3294fab28 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -228,7 +228,7 @@ impl NodeSigner for KeyProvider { Ok(SharedSecret::new(other_key, &node_secret)) } - fn get_inbound_payment_key(&self) -> ExpandedKey { + fn get_expanded_key(&self) -> ExpandedKey { unreachable!() } diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs index 239ff3f0c98..3a03f96679d 100644 --- a/lightning-background-processor/src/lib.rs +++ b/lightning-background-processor/src/lib.rs @@ -1174,7 +1174,7 @@ mod tests { use lightning::routing::gossip::{NetworkGraph, P2PGossipSync}; use lightning::routing::router::{CandidateRouteHop, DefaultRouter, Path, RouteHop}; use lightning::routing::scoring::{ChannelUsage, LockableScore, ScoreLookUp, ScoreUpdate}; - use lightning::sign::{ChangeDestinationSourceSync, InMemorySigner, KeysManager}; + use lightning::sign::{ChangeDestinationSourceSync, InMemorySigner, KeysManager, NodeSigner}; use lightning::types::features::{ChannelFeatures, NodeFeatures}; use lightning::types::payment::PaymentHash; use lightning::util::config::UserConfig; @@ -1650,6 +1650,7 @@ mod tests { let msg_router = Arc::new(DefaultMessageRouter::new( network_graph.clone(), Arc::clone(&keys_manager), + keys_manager.get_expanded_key(), )); let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Bitcoin)); let kv_store = diff --git a/lightning-dns-resolver/src/lib.rs b/lightning-dns-resolver/src/lib.rs index 6de6b77be90..6288053006e 100644 --- a/lightning-dns-resolver/src/lib.rs +++ b/lightning-dns-resolver/src/lib.rs @@ -232,7 +232,14 @@ mod test { secp_ctx: &Secp256k1, ) -> Result, ()> { let keys = KeysManager::new(&[0; 32], 42, 43); - Ok(vec![BlindedMessagePath::one_hop(recipient, context, &keys, secp_ctx).unwrap()]) + Ok(vec![BlindedMessagePath::one_hop( + recipient, + context, + keys.get_expanded_key(), + &keys, + secp_ctx, + ) + .unwrap()]) } } impl Deref for DirectlyConnectedRouter { @@ -334,8 +341,14 @@ mod test { let (msg, context) = payer.resolver.resolve_name(payment_id, name.clone(), &*payer_keys).unwrap(); let query_context = MessageContext::DNSResolver(context); - let reply_path = - BlindedMessagePath::one_hop(payer_id, query_context, &*payer_keys, &secp_ctx).unwrap(); + let reply_path = BlindedMessagePath::one_hop( + payer_id, + query_context, + payer_keys.get_expanded_key(), + &*payer_keys, + &secp_ctx, + ) + .unwrap(); payer.pending_messages.lock().unwrap().push(( DNSResolverMessage::DNSSECQuery(msg), MessageSendInstructions::WithSpecifiedReplyPath { diff --git a/lightning-liquidity/tests/common/mod.rs b/lightning-liquidity/tests/common/mod.rs index 2259d1eae06..95b3ca4580b 100644 --- a/lightning-liquidity/tests/common/mod.rs +++ b/lightning-liquidity/tests/common/mod.rs @@ -5,7 +5,7 @@ #![allow(unused_macros)] use lightning::chain::Filter; -use lightning::sign::EntropySource; +use lightning::sign::{EntropySource, NodeSigner}; use bitcoin::blockdata::constants::{genesis_block, ChainHash}; use bitcoin::blockdata::transaction::Transaction; @@ -418,8 +418,11 @@ pub(crate) fn create_liquidity_node( scorer.clone(), Default::default(), )); - let msg_router = - Arc::new(DefaultMessageRouter::new(Arc::clone(&network_graph), Arc::clone(&keys_manager))); + let msg_router = Arc::new(DefaultMessageRouter::new( + Arc::clone(&network_graph), + Arc::clone(&keys_manager), + keys_manager.get_expanded_key(), + )); let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Bitcoin)); let kv_store = Arc::new(FilesystemStore::new(format!("{}_persister_{}", &persist_dir, i).into())); diff --git a/lightning/src/blinded_path/message.rs b/lightning/src/blinded_path/message.rs index 0e3977ab68e..07df61ee448 100644 --- a/lightning/src/blinded_path/message.rs +++ b/lightning/src/blinded_path/message.rs @@ -11,6 +11,7 @@ use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey}; +use crate::offers::signer; #[allow(unused_imports)] use crate::prelude::*; @@ -19,9 +20,9 @@ use crate::blinded_path::{BlindedHop, BlindedPath, Direction, IntroductionNode, use crate::crypto::streams::ChaChaPolyReadAdapter; use crate::io; use crate::io::Cursor; -use crate::ln::channelmanager::PaymentId; +use crate::ln::channelmanager::{PaymentId, Verification}; use crate::ln::msgs::DecodeError; -use crate::ln::onion_utils; +use crate::ln::{inbound_payment, onion_utils}; use crate::offers::nonce::Nonce; use crate::onion_message::packet::ControlTlvs; use crate::routing::gossip::{NodeId, ReadOnlyNetworkGraph}; @@ -55,23 +56,51 @@ impl Readable for BlindedMessagePath { impl BlindedMessagePath { /// Create a one-hop blinded path for a message. pub fn one_hop( - recipient_node_id: PublicKey, context: MessageContext, entropy_source: ES, - secp_ctx: &Secp256k1, + recipient_node_id: PublicKey, context: MessageContext, + expanded_key: inbound_payment::ExpandedKey, entropy_source: ES, secp_ctx: &Secp256k1, ) -> Result where ES::Target: EntropySource, { - Self::new(&[], recipient_node_id, context, entropy_source, secp_ctx) + Self::new(&[], recipient_node_id, context, entropy_source, expanded_key, secp_ctx) } /// Create a path for an onion message, to be forwarded along `node_pks`. The last node /// pubkey in `node_pks` will be the destination node. /// /// Errors if no hops are provided or if `node_pk`(s) are invalid. - // TODO: make all payloads the same size with padding + add dummy hops pub fn new( intermediate_nodes: &[MessageForwardNode], recipient_node_id: PublicKey, - context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1, + context: MessageContext, entropy_source: ES, expanded_key: inbound_payment::ExpandedKey, + secp_ctx: &Secp256k1, + ) -> Result + where + ES::Target: EntropySource, + { + BlindedMessagePath::new_with_dummy_hops( + intermediate_nodes, + 0, + recipient_node_id, + context, + entropy_source, + expanded_key, + secp_ctx, + ) + } + + /// Create a path for an onion message, to be forwarded along `node_pks`. + /// + /// Additionally allows appending a number of dummy hops before the final hop, + /// increasing the total path length and enhancing privacy by obscuring the true + /// distance between sender and recipient. + /// + /// The last node pubkey in `node_pks` will be the destination node. + /// + /// Errors if no hops are provided or if `node_pk`(s) are invalid. + pub fn new_with_dummy_hops( + intermediate_nodes: &[MessageForwardNode], dummy_hops_count: u8, + recipient_node_id: PublicKey, context: MessageContext, entropy_source: ES, + expanded_key: inbound_payment::ExpandedKey, secp_ctx: &Secp256k1, ) -> Result where ES::Target: EntropySource, @@ -88,9 +117,12 @@ impl BlindedMessagePath { blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret), blinded_hops: blinded_hops( secp_ctx, + entropy_source, + expanded_key, intermediate_nodes, recipient_node_id, context, + dummy_hops_count, &blinding_secret, ) .map_err(|_| ())?, @@ -258,6 +290,63 @@ pub(crate) struct ForwardTlvs { pub(crate) next_blinding_override: Option, } +/// A blank struct, representing dummy tlv prior to authentication. +/// +/// For more details, see [`DummyTlv`]. +pub(crate) struct UnauthenticatedDummyTlv {} + +impl Writeable for UnauthenticatedDummyTlv { + fn write(&self, _writer: &mut W) -> Result<(), io::Error> { + Ok(()) + } +} + +impl Verification for UnauthenticatedDummyTlv { + /// Constructs an HMAC to include in [`OffersContext`] for the data along with the given + /// [`Nonce`]. + fn hmac_data(&self, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey) -> Hmac { + signer::hmac_for_dummy_tlv(self, nonce, expanded_key) + } + + /// Authenticates the data using an HMAC and a [`Nonce`] taken from an [`OffersContext`]. + fn verify_data( + &self, hmac: Hmac, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey, + ) -> Result<(), ()> { + signer::verify_dummy_tlv(self, hmac, nonce, expanded_key) + } +} + +/// Represents the dummy TLV encoded immediately before the actual [`ReceiveTlvs`] in a blinded path. +/// These TLVs are intended for the final node and are recursively authenticated and verified until +/// the real [`ReceiveTlvs`] is reached. +/// +/// Their purpose is to arbitrarily extend the path length, obscuring the receiver's position in the +/// route and thereby enhancing privacy. +/// +/// ## Authentication +/// Authentication provides an additional layer of security, ensuring that the path is legitimate +/// and terminates in valid [`ReceiveTlvs`] data. Verification begins with the first dummy hop and +/// continues recursively until the final [`ReceiveTlvs`] is reached. +/// +/// This prevents an attacker from crafting a bogus blinded path consisting solely of dummy tlv +/// without any valid payload, which could otherwise waste resources through recursive +/// processing — a potential vector for DoS-like attacks. +pub(crate) struct DummyTlv { + pub(crate) dummy_tlv: UnauthenticatedDummyTlv, + /// An HMAC of `tlvs` along with a nonce used to construct it. + pub(crate) authentication: (Hmac, Nonce), +} + +impl Writeable for DummyTlv { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { + encode_tlv_stream!(writer, { + (65539, self.authentication, required), + }); + + Ok(()) + } +} + /// Similar to [`ForwardTlvs`], but these TLVs are for the final node. pub(crate) struct ReceiveTlvs { /// If `context` is `Some`, it is used to identify the blinded path that this onion message is @@ -505,13 +594,18 @@ impl_writeable_tlv_based!(DNSResolverContext, { pub(crate) const MESSAGE_PADDING_ROUND_OFF: usize = 100; /// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`. -pub(super) fn blinded_hops( - secp_ctx: &Secp256k1, intermediate_nodes: &[MessageForwardNode], - recipient_node_id: PublicKey, context: MessageContext, session_priv: &SecretKey, -) -> Result, secp256k1::Error> { +pub(super) fn blinded_hops( + secp_ctx: &Secp256k1, entropy_source: ES, expanded_key: inbound_payment::ExpandedKey, + intermediate_nodes: &[MessageForwardNode], recipient_node_id: PublicKey, + context: MessageContext, dummy_hops_count: u8, session_priv: &SecretKey, +) -> Result, secp256k1::Error> +where + ES::Target: EntropySource, +{ let pks = intermediate_nodes .iter() .map(|node| node.node_id) + .chain((0..dummy_hops_count).map(|_| recipient_node_id)) .chain(core::iter::once(recipient_node_id)); let is_compact = intermediate_nodes.iter().any(|node| node.short_channel_id.is_some()); @@ -526,6 +620,12 @@ pub(super) fn blinded_hops( .map(|next_hop| { ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override: None }) }) + .chain((0..dummy_hops_count).map(|_| { + let dummy_tlv = UnauthenticatedDummyTlv {}; + let nonce = Nonce::from_entropy_source(&*entropy_source); + let hmac = dummy_tlv.hmac_data(nonce, &expanded_key); + ControlTlvs::Dummy(DummyTlv { dummy_tlv, authentication: (hmac, nonce) }) + })) .chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { context: Some(context) }))); if is_compact { diff --git a/lightning/src/blinded_path/payment.rs b/lightning/src/blinded_path/payment.rs index f015886933a..ae26d51b1f2 100644 --- a/lightning/src/blinded_path/payment.rs +++ b/lightning/src/blinded_path/payment.rs @@ -351,10 +351,7 @@ 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, - } + ReceiveTlvs { authentication: (self.hmac_data(nonce, expanded_key), nonce), tlvs: self } } } diff --git a/lightning/src/ln/async_payments_tests.rs b/lightning/src/ln/async_payments_tests.rs index a956f2ebae2..9277775b38d 100644 --- a/lightning/src/ln/async_payments_tests.rs +++ b/lightning/src/ln/async_payments_tests.rs @@ -668,7 +668,7 @@ fn amount_doesnt_match_invreq() { valid_invreq = Some(invoice_request.clone()); *invoice_request = offer .request_invoice( - &nodes[0].keys_manager.get_inbound_payment_key(), + &nodes[0].keys_manager.get_expanded_key(), Nonce::from_entropy_source(nodes[0].keys_manager), &secp_ctx, payment_id, diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index 18a1089d647..43de09a936c 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -83,7 +83,7 @@ pub fn blinded_payment_path( }; let nonce = Nonce([42u8; 16]); - let expanded_key = keys_manager.get_inbound_payment_key(); + let expanded_key = keys_manager.get_expanded_key(); let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let mut secp_ctx = Secp256k1::new(); @@ -168,7 +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_inbound_payment_key(); + let expanded_key = chanmon_cfgs[1].keys_manager.get_expanded_key(); let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let mut secp_ctx = Secp256k1::new(); @@ -222,7 +222,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_inbound_payment_key(); + let expanded_key = chanmon_cfgs[3].keys_manager.get_expanded_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, @@ -1342,7 +1342,7 @@ 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_inbound_payment_key(); + let expanded_key = chanmon_cfgs[1].keys_manager.get_expanded_key(); let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPaymentPath::new( @@ -1396,7 +1396,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_inbound_payment_key(); + let expanded_key = chanmon_cfgs[1].keys_manager.get_expanded_key(); let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let mut secp_ctx = Secp256k1::new(); @@ -1622,7 +1622,7 @@ fn route_blinding_spec_test_vector() { } Ok(SharedSecret::new(other_key, &node_secret)) } - fn get_inbound_payment_key(&self) -> ExpandedKey { unreachable!() } + fn get_expanded_key(&self) -> ExpandedKey { unreachable!() } fn get_node_id(&self, _recipient: Recipient) -> Result { unreachable!() } fn sign_invoice( &self, _invoice: &RawBolt11Invoice, _recipient: Recipient, @@ -1931,7 +1931,7 @@ fn test_trampoline_inbound_payment_decoding() { } Ok(SharedSecret::new(other_key, &node_secret)) } - fn get_inbound_payment_key(&self) -> ExpandedKey { unreachable!() } + fn get_expanded_key(&self) -> ExpandedKey { unreachable!() } fn get_node_id(&self, _recipient: Recipient) -> Result { unreachable!() } fn sign_invoice( &self, _invoice: &RawBolt11Invoice, _recipient: Recipient, @@ -2016,7 +2016,7 @@ fn do_test_trampoline_single_hop_receive(success: bool) { }; let nonce = Nonce([42u8; 16]); - let expanded_key = nodes[2].keys_manager.get_inbound_payment_key(); + 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(); diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 170d8261d5a..71285938c6b 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -256,7 +256,7 @@ pub enum PendingHTLCRouting { requires_blinded_error: bool, /// Set if we are receiving a keysend to a blinded path, meaning we created the /// [`PaymentSecret`] and should verify it using our - /// [`NodeSigner::get_inbound_payment_key`]. + /// [`NodeSigner::get_expanded_key`]. has_recipient_created_payment_secret: bool, /// The [`InvoiceRequest`] associated with the [`Offer`] corresponding to this payment. invoice_request: Option, @@ -475,12 +475,12 @@ impl Ord for ClaimableHTLC { pub trait Verification { /// Constructs an HMAC to include in [`OffersContext`] for the data along with the given /// [`Nonce`]. - fn hmac_for_offer_payment( + fn hmac_data( &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( + fn verify_data( &self, hmac: Hmac, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey, ) -> Result<(), ()>; } @@ -488,7 +488,7 @@ pub trait Verification { impl Verification for PaymentHash { /// Constructs an HMAC to include in [`OffersContext::InboundPayment`] for the payment hash /// along with the given [`Nonce`]. - fn hmac_for_offer_payment( + fn hmac_data( &self, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey, ) -> Hmac { signer::hmac_for_payment_hash(*self, nonce, expanded_key) @@ -496,7 +496,7 @@ impl Verification for PaymentHash { /// Authenticates the payment id using an HMAC and a [`Nonce`] taken from an /// [`OffersContext::InboundPayment`]. - fn verify_for_offer_payment( + fn verify_data( &self, hmac: Hmac, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey, ) -> Result<(), ()> { signer::verify_payment_hash(*self, hmac, nonce, expanded_key) @@ -504,13 +504,13 @@ impl Verification for PaymentHash { } impl Verification for UnauthenticatedReceiveTlvs { - fn hmac_for_offer_payment( + fn hmac_data( &self, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey, ) -> Hmac { signer::hmac_for_payment_tlvs(self, nonce, expanded_key) } - fn verify_for_offer_payment( + fn verify_data( &self, hmac: Hmac, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey, ) -> Result<(), ()> { signer::verify_payment_tlvs(self, hmac, nonce, expanded_key) @@ -550,7 +550,7 @@ impl PaymentId { impl Verification for PaymentId { /// Constructs an HMAC to include in [`OffersContext::OutboundPayment`] for the payment id /// along with the given [`Nonce`]. - fn hmac_for_offer_payment( + fn hmac_data( &self, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey, ) -> Hmac { signer::hmac_for_offer_payment_id(*self, nonce, expanded_key) @@ -558,7 +558,7 @@ impl Verification for PaymentId { /// Authenticates the payment id using an HMAC and a [`Nonce`] taken from an /// [`OffersContext::OutboundPayment`]. - fn verify_for_offer_payment( + fn verify_data( &self, hmac: Hmac, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey, ) -> Result<(), ()> { signer::verify_offer_payment_id(*self, hmac, nonce, expanded_key) @@ -3576,7 +3576,7 @@ where ) -> Self { let mut secp_ctx = Secp256k1::new(); secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes()); - let expanded_inbound_key = node_signer.get_inbound_payment_key(); + let expanded_inbound_key = node_signer.get_expanded_key(); ChannelManager { default_configuration: config.clone(), chain_hash: ChainHash::using_genesis_block(params.network), @@ -10560,7 +10560,7 @@ where }; let invoice_request = builder.build_and_sign()?; - let hmac = payment_id.hmac_for_offer_payment(nonce, expanded_key); + let hmac = payment_id.hmac_data(nonce, expanded_key); let context = MessageContext::Offers( OffersContext::OutboundPayment { payment_id, nonce, hmac: Some(hmac) } ); @@ -10664,7 +10664,7 @@ where let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?; let nonce = Nonce::from_entropy_source(entropy); - let hmac = payment_hash.hmac_for_offer_payment(nonce, expanded_key); + let hmac = payment_hash.hmac_data(nonce, expanded_key); let context = MessageContext::Offers(OffersContext::InboundPayment { payment_hash: invoice.payment_hash(), nonce, hmac }); @@ -12444,7 +12444,7 @@ where .release_invoice_requests_awaiting_invoice() { let RetryableInvoiceRequest { invoice_request, nonce, .. } = retryable_invoice_request; - let hmac = payment_id.hmac_for_offer_payment(nonce, &self.inbound_payment_key); + let hmac = payment_id.hmac_data(nonce, &self.inbound_payment_key); let context = MessageContext::Offers(OffersContext::OutboundPayment { payment_id, nonce, @@ -12627,7 +12627,7 @@ where match response { Ok(invoice) => { let nonce = Nonce::from_entropy_source(&*self.entropy_source); - let hmac = payment_hash.hmac_for_offer_payment(nonce, expanded_key); + let hmac = payment_hash.hmac_data(nonce, expanded_key); let context = MessageContext::Offers(OffersContext::InboundPayment { payment_hash, nonce, hmac }); Some((OffersMessage::Invoice(invoice), responder.respond_with_reply_path(context))) }, @@ -12664,7 +12664,7 @@ where OffersMessage::StaticInvoice(invoice) => { let payment_id = match context { Some(OffersContext::OutboundPayment { payment_id, nonce, hmac: Some(hmac) }) => { - if payment_id.verify_for_offer_payment(hmac, nonce, expanded_key).is_err() { + if payment_id.verify_data(hmac, nonce, expanded_key).is_err() { return None } payment_id @@ -12677,7 +12677,7 @@ where OffersMessage::InvoiceError(invoice_error) => { let payment_hash = match context { Some(OffersContext::InboundPayment { payment_hash, nonce, hmac }) => { - match payment_hash.verify_for_offer_payment(hmac, nonce, expanded_key) { + match payment_hash.verify_data(hmac, nonce, expanded_key) { Ok(_) => Some(payment_hash), Err(_) => None, } @@ -12690,7 +12690,7 @@ where match context { Some(OffersContext::OutboundPayment { payment_id, nonce, hmac: Some(hmac) }) => { - if let Ok(()) = payment_id.verify_for_offer_payment(hmac, nonce, expanded_key) { + if let Ok(()) = payment_id.verify_data(hmac, nonce, expanded_key) { self.abandon_payment_with_reason( payment_id, PaymentFailureReason::InvoiceRequestRejected, ); @@ -14569,7 +14569,7 @@ where }, None)); } - let expanded_inbound_key = args.node_signer.get_inbound_payment_key(); + let expanded_inbound_key = args.node_signer.get_expanded_key(); let mut claimable_payments = hash_map_with_capacity(claimable_htlcs_list.len()); if let Some(purposes) = claimable_htlc_purposes { diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 8a9a5cb1762..875143f8bb4 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -29,7 +29,7 @@ use crate::onion_message::messenger::OnionMessenger; use crate::ln::onion_utils::LocalHTLCFailureReason; use crate::routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate}; use crate::routing::router::{self, PaymentParameters, Route, RouteParameters}; -use crate::sign::{EntropySource, RandomBytes}; +use crate::sign::{EntropySource, NodeSigner, RandomBytes}; use crate::util::config::{MaxDustHTLCExposure, UserConfig}; use crate::util::logger::Logger; use crate::util::scid_utils; @@ -725,7 +725,7 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> { signer_provider: self.keys_manager, fee_estimator: &test_utils::TestFeeEstimator::new(253), router: &test_utils::TestRouter::new(Arc::clone(&network_graph), &self.logger, &scorer), - message_router: &test_utils::TestMessageRouter::new(network_graph, self.keys_manager), + message_router: &test_utils::TestMessageRouter::new(network_graph, self.keys_manager, self.keys_manager.get_expanded_key()), chain_monitor: self.chain_monitor, tx_broadcaster: &broadcaster, logger: &self.logger, @@ -3353,7 +3353,7 @@ pub fn create_node_cfgs_with_persisters<'a>(node_count: usize, chanmon_cfgs: &'a tx_broadcaster: &chanmon_cfgs[i].tx_broadcaster, fee_estimator: &chanmon_cfgs[i].fee_estimator, router: test_utils::TestRouter::new(network_graph.clone(), &chanmon_cfgs[i].logger, &chanmon_cfgs[i].scorer), - message_router: test_utils::TestMessageRouter::new(network_graph.clone(), &chanmon_cfgs[i].keys_manager), + message_router: test_utils::TestMessageRouter::new(network_graph.clone(), &chanmon_cfgs[i].keys_manager, chanmon_cfgs[i].keys_manager.get_expanded_key()), chain_monitor, keys_manager: &chanmon_cfgs[i].keys_manager, node_seed: seed, diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 0c0bb0713cb..2130819a5a9 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -50,7 +50,7 @@ use crate::routing::gossip::{NetworkGraph, NetworkUpdate}; use crate::routing::router::{ get_route, Path, PaymentParameters, Route, RouteHop, RouteParameters, }; -use crate::sign::{EntropySource, OutputSpender, SignerProvider}; +use crate::sign::{EntropySource, NodeSigner, OutputSpender, SignerProvider}; use crate::types::features::{ChannelFeatures, ChannelTypeFeatures, NodeFeatures}; use crate::types::payment::{PaymentHash, PaymentSecret}; use crate::util::config::{ @@ -5112,7 +5112,11 @@ pub fn test_key_derivation_params() { let scorer = RwLock::new(test_utils::TestScorer::new()); let router = test_utils::TestRouter::new(network_graph.clone(), &chanmon_cfgs[0].logger, &scorer); - let message_router = test_utils::TestMessageRouter::new(network_graph.clone(), &keys_manager); + let message_router = test_utils::TestMessageRouter::new( + network_graph.clone(), + &keys_manager, + keys_manager.get_expanded_key(), + ); let node = NodeCfg { chain_source: &chanmon_cfgs[0].chain_source, logger: &chanmon_cfgs[0].logger, diff --git a/lightning/src/ln/inbound_payment.rs b/lightning/src/ln/inbound_payment.rs index 53b212428ca..6a0b52d40b5 100644 --- a/lightning/src/ln/inbound_payment.rs +++ b/lightning/src/ln/inbound_payment.rs @@ -37,9 +37,9 @@ const AMT_MSAT_LEN: usize = 8; // retrieve said payment type bits. const METHOD_TYPE_OFFSET: usize = 5; -/// A set of keys that were HKDF-expanded. Returned by [`NodeSigner::get_inbound_payment_key`]. +/// A set of keys that were HKDF-expanded. Returned by [`NodeSigner::get_expanded_key`]. /// -/// [`NodeSigner::get_inbound_payment_key`]: crate::sign::NodeSigner::get_inbound_payment_key +/// [`NodeSigner::get_expanded_key`]: crate::sign::NodeSigner::get_expanded_key #[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)] pub struct ExpandedKey { /// The key used to encrypt the bytes containing the payment metadata (i.e. the amount and @@ -133,7 +133,7 @@ fn min_final_cltv_expiry_delta_from_metadata(bytes: [u8; METADATA_LEN]) -> u16 { /// `ChannelManager` is required. Useful for generating invoices for [phantom node payments] without /// a `ChannelManager`. /// -/// `keys` is generated by calling [`NodeSigner::get_inbound_payment_key`]. It is recommended to +/// `keys` is generated by calling [`NodeSigner::get_expanded_key`]. It is recommended to /// cache this value and not regenerate it for each new inbound payment. /// /// `current_time` is a Unix timestamp representing the current time. @@ -142,7 +142,7 @@ fn min_final_cltv_expiry_delta_from_metadata(bytes: [u8; METADATA_LEN]) -> u16 { /// on versions of LDK prior to 0.0.114. /// /// [phantom node payments]: crate::sign::PhantomKeysManager -/// [`NodeSigner::get_inbound_payment_key`]: crate::sign::NodeSigner::get_inbound_payment_key +/// [`NodeSigner::get_expanded_key`]: crate::sign::NodeSigner::get_expanded_key pub fn create( keys: &ExpandedKey, min_value_msat: Option, invoice_expiry_delta_secs: u32, entropy_source: &ES, current_time: u64, min_final_cltv_expiry_delta: Option, @@ -322,7 +322,7 @@ fn construct_payment_secret( /// For payments including a custom `min_final_cltv_expiry_delta`, the metadata is constructed as: /// payment method (3 bits) || payment amount (8 bytes - 3 bits) || min_final_cltv_expiry_delta (2 bytes) || expiry (6 bytes) /// -/// In both cases the result is then encrypted using a key derived from [`NodeSigner::get_inbound_payment_key`]. +/// In both cases the result is then encrypted using a key derived from [`NodeSigner::get_expanded_key`]. /// /// Then on payment receipt, we verify in this method that the payment preimage and payment secret /// match what was constructed. @@ -343,7 +343,7 @@ fn construct_payment_secret( /// /// See [`ExpandedKey`] docs for more info on the individual keys used. /// -/// [`NodeSigner::get_inbound_payment_key`]: crate::sign::NodeSigner::get_inbound_payment_key +/// [`NodeSigner::get_expanded_key`]: crate::sign::NodeSigner::get_expanded_key /// [`create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment /// [`create_inbound_payment_for_hash`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash pub(super) fn verify( diff --git a/lightning/src/ln/invoice_utils.rs b/lightning/src/ln/invoice_utils.rs index f2e8284a617..876f5eccb27 100644 --- a/lightning/src/ln/invoice_utils.rs +++ b/lightning/src/ln/invoice_utils.rs @@ -195,7 +195,7 @@ where }, }; - let keys = node_signer.get_inbound_payment_key(); + let keys = node_signer.get_expanded_key(); let (payment_hash, payment_secret) = if let Some(payment_hash) = payment_hash { let payment_secret = create_from_hash( &keys, diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs index 95a1fbaaa10..8fce1f20e3b 100644 --- a/lightning/src/ln/max_payment_path_len_tests.rs +++ b/lightning/src/ln/max_payment_path_len_tests.rs @@ -173,7 +173,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_inbound_payment_key(); + let expanded_key = chanmon_cfgs[2].keys_manager.get_expanded_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 2e9c3b90957..1c19c3a15d5 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -3443,8 +3443,8 @@ where }, ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Receive(receive_tlvs) } => { let ReceiveTlvs { tlvs, authentication: (hmac, nonce) } = receive_tlvs; - let expanded_key = node_signer.get_inbound_payment_key(); - if tlvs.verify_for_offer_payment(hmac, nonce, &expanded_key).is_err() { + let expanded_key = node_signer.get_expanded_key(); + if tlvs.verify_data(hmac, nonce, &expanded_key).is_err() { return Err(DecodeError::InvalidValue); } @@ -3595,8 +3595,8 @@ where readable: BlindedTrampolineTlvs::Receive(receive_tlvs), } => { let ReceiveTlvs { tlvs, authentication: (hmac, nonce) } = receive_tlvs; - let expanded_key = node_signer.get_inbound_payment_key(); - if tlvs.verify_for_offer_payment(hmac, nonce, &expanded_key).is_err() { + let expanded_key = node_signer.get_expanded_key(); + if tlvs.verify_data(hmac, nonce, &expanded_key).is_err() { return Err(DecodeError::InvalidValue); } diff --git a/lightning/src/ln/offers_tests.rs b/lightning/src/ln/offers_tests.rs index f8649111a0c..bc9abd59914 100644 --- a/lightning/src/ln/offers_tests.rs +++ b/lightning/src/ln/offers_tests.rs @@ -2223,7 +2223,7 @@ fn fails_paying_invoice_with_unknown_required_features() { let payment_paths = invoice.payment_paths().to_vec(); let payment_hash = invoice.payment_hash(); - let expanded_key = alice.keys_manager.get_inbound_payment_key(); + let expanded_key = alice.keys_manager.get_expanded_key(); let secp_ctx = Secp256k1::new(); let created_at = alice.node.duration_since_epoch(); diff --git a/lightning/src/offers/signer.rs b/lightning/src/offers/signer.rs index 329b90d2076..82b5b6a3f78 100644 --- a/lightning/src/offers/signer.rs +++ b/lightning/src/offers/signer.rs @@ -9,6 +9,7 @@ //! Utilities for signing offer messages and verifying metadata. +use crate::blinded_path::message::UnauthenticatedDummyTlv; use crate::blinded_path::payment::UnauthenticatedReceiveTlvs; use crate::ln::channelmanager::PaymentId; use crate::ln::inbound_payment::{ExpandedKey, IV_LEN}; @@ -570,3 +571,26 @@ pub(crate) fn verify_held_htlc_available_context( Err(()) } } + +pub(crate) fn hmac_for_dummy_tlv( + tlvs: &UnauthenticatedDummyTlv, nonce: Nonce, expanded_key: &ExpandedKey, +) -> Hmac { + const IV_BYTES: &[u8; IV_LEN] = b"LDK Msgs Dummies"; + let mut hmac = expanded_key.hmac_for_offer(); + hmac.input(IV_BYTES); + hmac.input(&nonce.0); + hmac.input(PAYMENT_TLVS_HMAC_INPUT); + tlvs.write(&mut hmac).unwrap(); + + Hmac::from_engine(hmac) +} + +pub(crate) fn verify_dummy_tlv( + tlvs: &UnauthenticatedDummyTlv, hmac: Hmac, nonce: Nonce, expanded_key: &ExpandedKey, +) -> Result<(), ()> { + if hmac_for_dummy_tlv(tlvs, nonce, expanded_key) == hmac { + Ok(()) + } else { + Err(()) + } +} diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index b28819ee692..749a9bdb063 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -26,6 +26,7 @@ use crate::blinded_path::message::{ use crate::blinded_path::utils::is_padded; use crate::blinded_path::EmptyNodeIdLookUp; use crate::events::{Event, EventsProvider}; +use crate::ln::inbound_payment::ExpandedKey; use crate::ln::msgs::{self, BaseMessageHandler, DecodeError, OnionMessageHandler}; use crate::routing::gossip::{NetworkGraph, P2PGossipSync}; use crate::routing::test_utils::{add_channel, add_or_update_node}; @@ -279,8 +280,11 @@ fn create_nodes_using_cfgs(cfgs: Vec) -> Vec { let node_signer = Arc::new(TestNodeSigner::new(secret_key)); let node_id_lookup = Arc::new(EmptyNodeIdLookUp {}); - let message_router = - Arc::new(DefaultMessageRouter::new(network_graph.clone(), entropy_source.clone())); + let message_router = Arc::new(DefaultMessageRouter::new( + network_graph.clone(), + entropy_source.clone(), + node_signer.get_expanded_key(), + )); let offers_message_handler = Arc::new(TestOffersMessageHandler {}); let async_payments_message_handler = Arc::new(TestAsyncPaymentsMessageHandler {}); let dns_resolver_message_handler = Arc::new(TestDNSResolverMessageHandler {}); @@ -409,8 +413,38 @@ fn one_blinded_hop() { let secp_ctx = Secp256k1::new(); let context = MessageContext::Custom(Vec::new()); let entropy = &*nodes[1].entropy_source; + let expanded_key = ExpandedKey::new([42; 32]); let blinded_path = - BlindedMessagePath::new(&[], nodes[1].node_id, context, entropy, &secp_ctx).unwrap(); + BlindedMessagePath::new(&[], nodes[1].node_id, context, entropy, expanded_key, &secp_ctx) + .unwrap(); + let destination = Destination::BlindedPath(blinded_path); + let instructions = MessageSendInstructions::WithoutReplyPath { destination }; + nodes[0].messenger.send_onion_message(test_msg, instructions).unwrap(); + nodes[1].custom_message_handler.expect_message(TestCustomMessage::Pong); + pass_along_path(&nodes); +} + +#[test] +fn blinded_path_with_dummy() { + let nodes = create_nodes(2); + let test_msg = TestCustomMessage::Pong; + + let secp_ctx = Secp256k1::new(); + let context = MessageContext::Custom(Vec::new()); + let entropy = &*nodes[1].entropy_source; + let expanded_key = ExpandedKey::new([42; 32]); + let blinded_path = BlindedMessagePath::new_with_dummy_hops( + &[], + 5, + nodes[1].node_id, + context, + entropy, + expanded_key, + &secp_ctx, + ) + .unwrap(); + // Ensure that dummy hops are added to the blinded path. + assert_eq!(blinded_path.blinded_hops().len(), 6); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; nodes[0].messenger.send_onion_message(test_msg, instructions).unwrap(); @@ -428,9 +462,16 @@ fn two_unblinded_two_blinded() { [MessageForwardNode { node_id: nodes[3].node_id, short_channel_id: None }]; let context = MessageContext::Custom(Vec::new()); let entropy = &*nodes[4].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[4].node_id, context, entropy, &secp_ctx) - .unwrap(); + let expanded_key = ExpandedKey::new([42; 32]); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[4].node_id, + context, + entropy, + expanded_key, + &secp_ctx, + ) + .unwrap(); let path = OnionMessagePath { intermediate_nodes: vec![nodes[1].node_id, nodes[2].node_id], destination: Destination::BlindedPath(blinded_path), @@ -454,9 +495,16 @@ fn three_blinded_hops() { ]; let context = MessageContext::Custom(Vec::new()); let entropy = &*nodes[3].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[3].node_id, context, entropy, &secp_ctx) - .unwrap(); + let expanded_key = ExpandedKey::new([42; 32]); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[3].node_id, + context, + entropy, + expanded_key, + &secp_ctx, + ) + .unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -481,8 +529,10 @@ fn async_response_over_one_blinded_hop() { let secp_ctx = Secp256k1::new(); let context = MessageContext::Custom(Vec::new()); let entropy = &*nodes[1].entropy_source; + let expanded_key = ExpandedKey::new([42; 32]); let reply_path = - BlindedMessagePath::new(&[], nodes[1].node_id, context, entropy, &secp_ctx).unwrap(); + BlindedMessagePath::new(&[], nodes[1].node_id, context, entropy, expanded_key, &secp_ctx) + .unwrap(); // 4. Create a responder using the reply path for Alice. let responder = Some(Responder::new(reply_path)); @@ -520,9 +570,16 @@ fn async_response_with_reply_path_succeeds() { // Alice receives a message from Bob with an added reply_path for responding back. let message = TestCustomMessage::Ping; let context = MessageContext::Custom(Vec::new()); - let reply_path = - BlindedMessagePath::new(&[], bob.node_id, context, &*bob.entropy_source, &secp_ctx) - .unwrap(); + let expanded_key = ExpandedKey::new([42; 32]); + let reply_path = BlindedMessagePath::new( + &[], + bob.node_id, + context, + &*bob.entropy_source, + expanded_key, + &secp_ctx, + ) + .unwrap(); // Alice asynchronously responds to Bob, expecting a response back from him. let responder = Responder::new(reply_path); @@ -561,9 +618,16 @@ fn async_response_with_reply_path_fails() { // Alice receives a message from Bob with an added reply_path for responding back. let message = TestCustomMessage::Ping; let context = MessageContext::Custom(Vec::new()); - let reply_path = - BlindedMessagePath::new(&[], bob.node_id, context, &*bob.entropy_source, &secp_ctx) - .unwrap(); + let expanded_key = ExpandedKey::new([42; 32]); + let reply_path = BlindedMessagePath::new( + &[], + bob.node_id, + context, + &*bob.entropy_source, + expanded_key, + &secp_ctx, + ) + .unwrap(); // Alice tries to asynchronously respond to Bob, but fails because the nodes are unannounced and // disconnected. Thus, a reply path could no be created for the response. @@ -604,6 +668,7 @@ fn test_blinded_path_padding_for_full_length_path() { let test_msg = TestCustomMessage::Pong; let secp_ctx = Secp256k1::new(); + let expanded_key = ExpandedKey::new([42; 32]); let intermediate_nodes = [ MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }, MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: None }, @@ -611,11 +676,13 @@ fn test_blinded_path_padding_for_full_length_path() { // Update the context to create a larger final receive TLVs, ensuring that // the hop sizes vary before padding. let context = MessageContext::Custom(vec![0u8; 42]); - let blinded_path = BlindedMessagePath::new( + let blinded_path = BlindedMessagePath::new_with_dummy_hops( &intermediate_nodes, + 5, nodes[3].node_id, context, &*nodes[3].entropy_source, + expanded_key, &secp_ctx, ) .unwrap(); @@ -635,6 +702,7 @@ fn test_blinded_path_no_padding_for_compact_path() { // Check that for a compact blinded path, no padding is applied. let nodes = create_nodes(4); let secp_ctx = Secp256k1::new(); + let expanded_key = ExpandedKey::new([42; 32]); // Include some short_channel_id, so that MessageRouter uses this to create compact blinded paths. let intermediate_nodes = [ @@ -644,11 +712,13 @@ fn test_blinded_path_no_padding_for_compact_path() { // Update the context to create a larger final receive TLVs, ensuring that // the hop sizes vary before padding. let context = MessageContext::Custom(vec![0u8; 42]); - let blinded_path = BlindedMessagePath::new( + let blinded_path = BlindedMessagePath::new_with_dummy_hops( &intermediate_nodes, + 5, nodes[3].node_id, context, &*nodes[3].entropy_source, + expanded_key, &secp_ctx, ) .unwrap(); @@ -664,15 +734,22 @@ fn we_are_intro_node() { let test_msg = TestCustomMessage::Pong; let secp_ctx = Secp256k1::new(); + let expanded_key = ExpandedKey::new([42; 32]); let intermediate_nodes = [ MessageForwardNode { node_id: nodes[0].node_id, short_channel_id: None }, MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }, ]; let context = MessageContext::Custom(Vec::new()); let entropy = &*nodes[2].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[2].node_id, context, entropy, &secp_ctx) - .unwrap(); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[2].node_id, + context, + entropy, + expanded_key, + &secp_ctx, + ) + .unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -685,9 +762,15 @@ fn we_are_intro_node() { [MessageForwardNode { node_id: nodes[0].node_id, short_channel_id: None }]; let context = MessageContext::Custom(Vec::new()); let entropy = &*nodes[1].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[1].node_id, context, entropy, &secp_ctx) - .unwrap(); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[1].node_id, + context, + entropy, + expanded_key, + &secp_ctx, + ) + .unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -704,13 +787,20 @@ fn invalid_blinded_path_error() { let test_msg = TestCustomMessage::Pong; let secp_ctx = Secp256k1::new(); + let expanded_key = ExpandedKey::new([42; 32]); let intermediate_nodes = [MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }]; let context = MessageContext::Custom(Vec::new()); let entropy = &*nodes[2].entropy_source; - let mut blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[2].node_id, context, entropy, &secp_ctx) - .unwrap(); + let mut blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[2].node_id, + context, + entropy, + expanded_key, + &secp_ctx, + ) + .unwrap(); blinded_path.clear_blinded_hops(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -724,6 +814,7 @@ fn reply_path() { let mut nodes = create_nodes(4); let test_msg = TestCustomMessage::Ping; let secp_ctx = Secp256k1::new(); + let expanded_key = ExpandedKey::new([42; 32]); // Destination::Node let path = OnionMessagePath { @@ -737,9 +828,15 @@ fn reply_path() { ]; let context = MessageContext::Custom(Vec::new()); let entropy = &*nodes[0].entropy_source; - let reply_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[0].node_id, context, entropy, &secp_ctx) - .unwrap(); + let reply_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[0].node_id, + context, + entropy, + expanded_key, + &secp_ctx, + ) + .unwrap(); nodes[0] .messenger .send_onion_message_using_path(path, test_msg.clone(), Some(reply_path)) @@ -758,9 +855,15 @@ fn reply_path() { ]; let context = MessageContext::Custom(Vec::new()); let entropy = &*nodes[3].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[3].node_id, context, entropy, &secp_ctx) - .unwrap(); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[3].node_id, + context, + entropy, + expanded_key, + &secp_ctx, + ) + .unwrap(); let destination = Destination::BlindedPath(blinded_path); let intermediate_nodes = [ MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: None }, @@ -768,9 +871,15 @@ fn reply_path() { ]; let context = MessageContext::Custom(Vec::new()); let entropy = &*nodes[0].entropy_source; - let reply_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[0].node_id, context, entropy, &secp_ctx) - .unwrap(); + let reply_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[0].node_id, + context, + entropy, + expanded_key, + &secp_ctx, + ) + .unwrap(); let instructions = MessageSendInstructions::WithSpecifiedReplyPath { destination, reply_path }; nodes[0].messenger.send_onion_message(test_msg, instructions).unwrap(); @@ -866,9 +975,16 @@ fn requests_peer_connection_for_buffered_messages() { [MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }]; let context = MessageContext::Custom(Vec::new()); let entropy = &*nodes[0].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[2].node_id, context, entropy, &secp_ctx) - .unwrap(); + let expanded_key = ExpandedKey::new([42; 32]); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[2].node_id, + context, + entropy, + expanded_key, + &secp_ctx, + ) + .unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -908,9 +1024,16 @@ fn drops_buffered_messages_waiting_for_peer_connection() { [MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }]; let context = MessageContext::Custom(Vec::new()); let entropy = &*nodes[0].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[2].node_id, context, entropy, &secp_ctx) - .unwrap(); + let expanded_key = ExpandedKey::new([42; 32]); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[2].node_id, + context, + entropy, + expanded_key, + &secp_ctx, + ) + .unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -966,9 +1089,16 @@ fn intercept_offline_peer_oms() { [MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }]; let context = MessageContext::Custom(Vec::new()); let entropy = &*nodes[2].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[2].node_id, context, entropy, &secp_ctx) - .unwrap(); + let expanded_key = ExpandedKey::new([42; 32]); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[2].node_id, + context, + entropy, + expanded_key, + &secp_ctx, + ) + .unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 6009a276976..f39e776d051 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -20,8 +20,8 @@ use super::async_payments::AsyncPaymentsMessage; use super::async_payments::AsyncPaymentsMessageHandler; use super::dns_resolution::{DNSResolverMessage, DNSResolverMessageHandler}; use super::offers::{OffersMessage, OffersMessageHandler}; -use super::packet::OnionMessageContents; use super::packet::ParsedOnionMessageContents; +use super::packet::{DummyControlTlvs, OnionMessageContents}; use super::packet::{ ForwardControlTlvs, Packet, Payload, ReceiveControlTlvs, BIG_PACKET_HOP_DATA_LEN, SMALL_PACKET_HOP_DATA_LEN, @@ -29,16 +29,17 @@ use super::packet::{ #[cfg(async_payments)] use crate::blinded_path::message::AsyncPaymentsContext; use crate::blinded_path::message::{ - BlindedMessagePath, DNSResolverContext, ForwardTlvs, MessageContext, MessageForwardNode, - NextMessageHop, OffersContext, ReceiveTlvs, + BlindedMessagePath, DNSResolverContext, DummyTlv, ForwardTlvs, MessageContext, + MessageForwardNode, NextMessageHop, OffersContext, ReceiveTlvs, }; use crate::blinded_path::utils; use crate::blinded_path::{IntroductionNode, NodeIdLookUp}; use crate::events::{Event, EventHandler, EventsProvider, ReplayEvent}; +use crate::ln::channelmanager::Verification; use crate::ln::msgs::{ self, BaseMessageHandler, MessageSendEvent, OnionMessage, OnionMessageHandler, SocketAddress, }; -use crate::ln::onion_utils; +use crate::ln::{inbound_payment, onion_utils}; use crate::routing::gossip::{NetworkGraph, NodeId, ReadOnlyNetworkGraph}; use crate::sign::{EntropySource, NodeSigner, Recipient}; use crate::types::features::{InitFeatures, NodeFeatures}; @@ -191,7 +192,7 @@ where /// # use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey, self}; /// # use lightning::blinded_path::EmptyNodeIdLookUp; /// # use lightning::blinded_path::message::{BlindedMessagePath, MessageForwardNode, MessageContext}; -/// # use lightning::sign::{EntropySource, KeysManager}; +/// # use lightning::sign::{EntropySource, KeysManager, NodeSigner}; /// # use lightning::ln::peer_handler::IgnoringMessageHandler; /// # use lightning::onion_message::messenger::{Destination, MessageRouter, MessageSendInstructions, OnionMessagePath, OnionMessenger}; /// # use lightning::onion_message::packet::OnionMessageContents; @@ -272,8 +273,10 @@ where /// MessageForwardNode { node_id: hop_node_id3, short_channel_id: None }, /// MessageForwardNode { node_id: hop_node_id4, short_channel_id: None }, /// ]; +/// /// let context = MessageContext::Custom(Vec::new()); -/// let blinded_path = BlindedMessagePath::new(&hops, your_node_id, context, &keys_manager, &secp_ctx).unwrap(); +/// let expanded_key = keys_manager.get_expanded_key(); +/// let blinded_path = BlindedMessagePath::new(&hops, your_node_id, context, &keys_manager, expanded_key, &secp_ctx).unwrap(); /// /// // Send a custom onion message to a blinded path. /// let destination = Destination::BlindedPath(blinded_path); @@ -539,6 +542,7 @@ where { network_graph: G, entropy_source: ES, + inbound_payment_key: inbound_payment::ExpandedKey, } impl>, L: Deref, ES: Deref> DefaultMessageRouter @@ -547,8 +551,10 @@ where ES::Target: EntropySource, { /// Creates a [`DefaultMessageRouter`] using the given [`NetworkGraph`]. - pub fn new(network_graph: G, entropy_source: ES) -> Self { - Self { network_graph, entropy_source } + pub fn new( + network_graph: G, entropy_source: ES, expanded_key: inbound_payment::ExpandedKey, + ) -> Self { + Self { network_graph, entropy_source, inbound_payment_key: expanded_key } } fn create_blinded_paths_from_iter< @@ -556,7 +562,8 @@ where T: secp256k1::Signing + secp256k1::Verification, >( network_graph: &G, recipient: PublicKey, context: MessageContext, peers: I, - entropy_source: &ES, secp_ctx: &Secp256k1, compact_paths: bool, + entropy_source: &ES, expanded_key: &inbound_payment::ExpandedKey, secp_ctx: &Secp256k1, + compact_paths: bool, ) -> Result, ()> { // Limit the number of blinded paths that are computed. const MAX_PATHS: usize = 3; @@ -565,6 +572,19 @@ where // recipient's node_id. const MIN_PEER_CHANNELS: usize = 3; + // Add a random number (0 to 5) of dummy hops to each non-compact blinded path + // to make it harder to infer the recipient's position. + // + // # Note on compact paths: + // + // Compact paths are optimized for minimal size. Adding dummy hops to them + // would increase their size and negate their primary advantage. + // Therefore, we avoid adding dummy hops to compact paths. + let dummy_hops_count = compact_paths.then_some(0).unwrap_or_else(|| { + let random_byte = entropy_source.get_secure_random_bytes()[0]; + random_byte % 6 + }); + let network_graph = network_graph.deref().read_only(); let is_recipient_announced = network_graph.nodes().contains_key(&NodeId::from_pubkey(&recipient)); @@ -595,7 +615,15 @@ where let paths = peer_info .into_iter() .map(|(peer, _, _)| { - BlindedMessagePath::new(&[peer], recipient, context.clone(), entropy, secp_ctx) + BlindedMessagePath::new_with_dummy_hops( + &[peer], + dummy_hops_count, + recipient, + context.clone(), + entropy, + *expanded_key, + secp_ctx, + ) }) .take(MAX_PATHS) .collect::, _>>(); @@ -604,8 +632,15 @@ where Ok(paths) if !paths.is_empty() => Ok(paths), _ => { if is_recipient_announced { - BlindedMessagePath::new(&[], recipient, context, &**entropy_source, secp_ctx) - .map(|path| vec![path]) + BlindedMessagePath::new( + &[], + recipient, + context, + &**entropy_source, + *expanded_key, + secp_ctx, + ) + .map(|path| vec![path]) } else { Err(()) } @@ -664,7 +699,7 @@ where pub(crate) fn create_blinded_paths( network_graph: &G, recipient: PublicKey, context: MessageContext, peers: Vec, - entropy_source: &ES, secp_ctx: &Secp256k1, + entropy_source: &ES, expanded_key: &inbound_payment::ExpandedKey, secp_ctx: &Secp256k1, ) -> Result, ()> { let peers = peers.into_iter().map(|node_id| MessageForwardNode { node_id, short_channel_id: None }); @@ -674,6 +709,7 @@ where context, peers.into_iter(), entropy_source, + expanded_key, secp_ctx, false, ) @@ -681,7 +717,8 @@ where pub(crate) fn create_compact_blinded_paths( network_graph: &G, recipient: PublicKey, context: MessageContext, - peers: Vec, entropy_source: &ES, secp_ctx: &Secp256k1, + peers: Vec, entropy_source: &ES, + expanded_key: &inbound_payment::ExpandedKey, secp_ctx: &Secp256k1, ) -> Result, ()> { Self::create_blinded_paths_from_iter( network_graph, @@ -689,6 +726,7 @@ where context, peers.into_iter(), entropy_source, + expanded_key, secp_ctx, true, ) @@ -717,6 +755,7 @@ where context, peers, &self.entropy_source, + &self.inbound_payment_key, secp_ctx, ) } @@ -731,6 +770,7 @@ where context, peers, &self.entropy_source, + &self.inbound_payment_key, secp_ctx, ) } @@ -1074,6 +1114,44 @@ where msg.onion_routing_packet.hmac, (control_tlvs_ss, custom_handler.deref(), logger.deref()), ); + + // Constructs the next onion message using packet data and blinding logic. + let compute_onion_message = |packet_pubkey: PublicKey, + next_hop_hmac: [u8; 32], + new_packet_bytes: Vec, + blinding_point_opt: Option| + -> Result { + let new_pubkey = + match onion_utils::next_hop_pubkey(&secp_ctx, packet_pubkey, &onion_decode_ss) { + Ok(pk) => pk, + Err(e) => { + log_trace!(logger, "Failed to compute next hop packet pubkey: {}", e); + return Err(()); + }, + }; + let outgoing_packet = Packet { + version: 0, + public_key: new_pubkey, + hop_data: new_packet_bytes, + hmac: next_hop_hmac, + }; + let blinding_point = match blinding_point_opt { + Some(bp) => bp, + None => match onion_utils::next_hop_pubkey( + &secp_ctx, + msg.blinding_point, + control_tlvs_ss.as_ref(), + ) { + Ok(bp) => bp, + Err(e) => { + log_trace!(logger, "Failed to compute next blinding point: {}", e); + return Err(()); + }, + }, + }; + Ok(OnionMessage { blinding_point, onion_routing_packet: outgoing_packet }) + }; + match next_hop { Ok(( Payload::Receive { @@ -1115,6 +1193,21 @@ where Err(()) }, }, + Ok(( + Payload::Dummy(DummyControlTlvs::Unblinded(DummyTlv { dummy_tlv, authentication })), + Some((next_hop_hmac, new_packet_bytes)), + )) => { + let expanded_key = node_signer.get_expanded_key(); + dummy_tlv.verify_data(authentication.0, authentication.1, &expanded_key)?; + + let onion_message = compute_onion_message( + msg.onion_routing_packet.public_key, + next_hop_hmac, + new_packet_bytes, + None, + )?; + peel_onion_message(&onion_message, secp_ctx, node_signer, logger, custom_handler) + }, Ok(( Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs { next_hop, @@ -1122,47 +1215,12 @@ where })), Some((next_hop_hmac, new_packet_bytes)), )) => { - // TODO: we need to check whether `next_hop` is our node, in which case this is a dummy - // blinded hop and this onion message is destined for us. In this situation, we should keep - // unwrapping the onion layers to get to the final payload. Since we don't have the option - // of creating blinded paths with dummy hops currently, we should be ok to not handle this - // for now. - let packet_pubkey = msg.onion_routing_packet.public_key; - let new_pubkey_opt = - onion_utils::next_hop_pubkey(&secp_ctx, packet_pubkey, &onion_decode_ss); - let new_pubkey = match new_pubkey_opt { - Ok(pk) => pk, - Err(e) => { - log_trace!(logger, "Failed to compute next hop packet pubkey: {}", e); - return Err(()); - }, - }; - let outgoing_packet = Packet { - version: 0, - public_key: new_pubkey, - hop_data: new_packet_bytes, - hmac: next_hop_hmac, - }; - let onion_message = OnionMessage { - blinding_point: match next_blinding_override { - Some(blinding_point) => blinding_point, - None => { - match onion_utils::next_hop_pubkey( - &secp_ctx, - msg.blinding_point, - control_tlvs_ss.as_ref(), - ) { - Ok(bp) => bp, - Err(e) => { - log_trace!(logger, "Failed to compute next blinding point: {}", e); - return Err(()); - }, - } - }, - }, - onion_routing_packet: outgoing_packet, - }; - + let onion_message = compute_onion_message( + msg.onion_routing_packet.public_key, + next_hop_hmac, + new_packet_bytes, + next_blinding_override, + )?; Ok(PeeledOnion::Forward(next_hop, onion_message)) }, Err(e) => { diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index 632cbc9c8a3..db480203b2d 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -17,7 +17,9 @@ use super::async_payments::AsyncPaymentsMessage; use super::dns_resolution::DNSResolverMessage; use super::messenger::CustomOnionMessageHandler; use super::offers::OffersMessage; -use crate::blinded_path::message::{BlindedMessagePath, ForwardTlvs, NextMessageHop, ReceiveTlvs}; +use crate::blinded_path::message::{ + BlindedMessagePath, DummyTlv, ForwardTlvs, NextMessageHop, ReceiveTlvs, UnauthenticatedDummyTlv, +}; use crate::crypto::streams::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter}; use crate::ln::msgs::DecodeError; use crate::ln::onion_utils; @@ -111,6 +113,8 @@ impl LengthReadable for Packet { pub(super) enum Payload { /// This payload is for an intermediate hop. Forward(ForwardControlTlvs), + /// This payload is dummy, and is inteded to be peeled. + Dummy(DummyControlTlvs), /// This payload is for the final hop. Receive { control_tlvs: ReceiveControlTlvs, reply_path: Option, message: T }, } @@ -204,6 +208,11 @@ pub(super) enum ForwardControlTlvs { Unblinded(ForwardTlvs), } +pub(super) enum DummyControlTlvs { + /// See [`ForwardControlTlvs::Unblinded`] + Unblinded(DummyTlv), +} + /// Receive control TLVs in their blinded and unblinded form. pub(super) enum ReceiveControlTlvs { /// See [`ForwardControlTlvs::Blinded`]. @@ -234,6 +243,10 @@ impl Writeable for (Payload, [u8; 32]) { let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &control_tlvs); _encode_varint_length_prefixed_tlv!(w, { (4, write_adapter, required) }) }, + Payload::Dummy(DummyControlTlvs::Unblinded(control_tlvs)) => { + let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &control_tlvs); + _encode_varint_length_prefixed_tlv!(w, { (4, write_adapter, required) }) + }, Payload::Receive { control_tlvs: ReceiveControlTlvs::Unblinded(control_tlvs), reply_path, @@ -310,6 +323,9 @@ impl ReadableArgs<(Sh } Ok(Payload::Forward(ForwardControlTlvs::Unblinded(tlvs))) }, + Some(ChaChaPolyReadAdapter { readable: ControlTlvs::Dummy(tlvs) }) => { + Ok(Payload::Dummy(DummyControlTlvs::Unblinded(tlvs))) + }, Some(ChaChaPolyReadAdapter { readable: ControlTlvs::Receive(tlvs) }) => { Ok(Payload::Receive { control_tlvs: ReceiveControlTlvs::Unblinded(tlvs), @@ -328,6 +344,8 @@ impl ReadableArgs<(Sh pub(crate) enum ControlTlvs { /// This onion message is intended to be forwarded. Forward(ForwardTlvs), + /// This onion message is a dummy, and is intended to be peeled. + Dummy(DummyTlv), /// This onion message is intended to be received. Receive(ReceiveTlvs), } @@ -343,6 +361,7 @@ impl Readable for ControlTlvs { (4, next_node_id, option), (8, next_blinding_override, option), (65537, context, option), + (65539, authentication, option), }); let next_hop = match (short_channel_id, next_node_id) { @@ -353,7 +372,8 @@ impl Readable for ControlTlvs { }; let valid_fwd_fmt = next_hop.is_some(); - let valid_recv_fmt = next_hop.is_none() && next_blinding_override.is_none(); + let valid_recv_fmt = + next_hop.is_none() && next_blinding_override.is_none() && authentication.is_none(); let payload_fmt = if valid_fwd_fmt { ControlTlvs::Forward(ForwardTlvs { @@ -363,7 +383,10 @@ impl Readable for ControlTlvs { } else if valid_recv_fmt { ControlTlvs::Receive(ReceiveTlvs { context }) } else { - return Err(DecodeError::InvalidValue); + ControlTlvs::Dummy(DummyTlv { + dummy_tlv: UnauthenticatedDummyTlv {}, + authentication: authentication.ok_or(DecodeError::InvalidValue)?, + }) }; Ok(payload_fmt) @@ -374,6 +397,7 @@ impl Writeable for ControlTlvs { fn write(&self, w: &mut W) -> Result<(), io::Error> { match self { Self::Forward(tlvs) => tlvs.write(w), + Self::Dummy(tlvs) => tlvs.write(w), Self::Receive(tlvs) => tlvs.write(w), } } diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index f35a407634a..57af75d1ef1 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -820,19 +820,24 @@ pub trait EntropySource { /// A trait that can handle cryptographic operations at the scope level of a node. pub trait NodeSigner { - /// Get the [`ExpandedKey`] for use in encrypting and decrypting inbound payment data. + /// Get the [`ExpandedKey`] which provides cryptographic material for various Lightning Network operations. + /// + /// This key set is used for: + /// - Encrypting and decrypting inbound payment metadata + /// - Authenticating payment hashes (both LDK-provided and user-provided) + /// - Supporting BOLT 12 Offers functionality (signing and encryption) + /// - Authenticating spontaneous payments' metadata /// /// If the implementor of this trait supports [phantom node payments], then every node that is /// intended to be included in the phantom invoice route hints must return the same value from /// this method. - // This is because LDK avoids storing inbound payment data by encrypting payment data in the - // payment hash and/or payment secret, therefore for a payment to be receivable by multiple - // nodes, they must share the key that encrypts this payment data. /// - /// This method must return the same value each time it is called. + /// This method must return the same value each time it is called, as LDK avoids storing inbound + /// payment data by encrypting it in the payment hash and/or payment secret. Consistency is also + /// required for signature and encryption verification in Offers and spontaneous payments. /// /// [phantom node payments]: PhantomKeysManager - fn get_inbound_payment_key(&self) -> ExpandedKey; + fn get_expanded_key(&self) -> ExpandedKey; /// Get node id based on the provided [`Recipient`]. /// @@ -2113,7 +2118,7 @@ impl NodeSigner for KeysManager { Ok(SharedSecret::new(other_key, &node_secret)) } - fn get_inbound_payment_key(&self) -> ExpandedKey { + fn get_expanded_key(&self) -> ExpandedKey { self.inbound_payment_key.clone() } @@ -2274,7 +2279,7 @@ impl NodeSigner for PhantomKeysManager { Ok(SharedSecret::new(other_key, &node_secret)) } - fn get_inbound_payment_key(&self) -> ExpandedKey { + fn get_expanded_key(&self) -> ExpandedKey { self.inbound_payment_key.clone() } diff --git a/lightning/src/util/dyn_signer.rs b/lightning/src/util/dyn_signer.rs index 939e40cf7c4..2efedbb2355 100644 --- a/lightning/src/util/dyn_signer.rs +++ b/lightning/src/util/dyn_signer.rs @@ -214,7 +214,7 @@ inner, fn sign_bolt12_invoice(, invoice: &crate::offers::invoice::UnsignedBolt12Invoice ) -> Result, - fn get_inbound_payment_key(,) -> ExpandedKey + fn get_expanded_key(,) -> ExpandedKey ); delegate!(DynKeysInterface, SignerProvider, @@ -278,7 +278,7 @@ delegate!(DynPhantomKeysInterface, NodeSigner, fn sign_invoice(, invoice: &RawBolt11Invoice, recipient: Recipient) -> Result, fn sign_bolt12_invoice(, invoice: &crate::offers::invoice::UnsignedBolt12Invoice ) -> Result, - fn get_inbound_payment_key(,) -> ExpandedKey + fn get_expanded_key(,) -> ExpandedKey ); impl SignerProvider for DynPhantomKeysInterface { diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index f90bfb97ef7..c658a4ab87a 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -25,11 +25,11 @@ use crate::events::bump_transaction::{Utxo, WalletSource}; #[cfg(any(test, feature = "_externalize_tests"))] use crate::ln::chan_utils::CommitmentTransaction; use crate::ln::channel_state::ChannelDetails; -use crate::ln::channelmanager; use crate::ln::inbound_payment::ExpandedKey; use crate::ln::msgs::{BaseMessageHandler, MessageSendEvent}; use crate::ln::script::ShutdownScript; use crate::ln::types::ChannelId; +use crate::ln::{channelmanager, inbound_payment}; use crate::ln::{msgs, wire}; use crate::offers::invoice::UnsignedBolt12Invoice; use crate::onion_message::messenger::{ @@ -324,8 +324,9 @@ pub struct TestMessageRouter<'a> { impl<'a> TestMessageRouter<'a> { pub fn new( network_graph: Arc>, entropy_source: &'a TestKeysInterface, + expanded_key: inbound_payment::ExpandedKey, ) -> Self { - Self { inner: DefaultMessageRouter::new(network_graph, entropy_source) } + Self { inner: DefaultMessageRouter::new(network_graph, entropy_source, expanded_key) } } } @@ -1486,8 +1487,8 @@ impl TestNodeSigner { } impl NodeSigner for TestNodeSigner { - fn get_inbound_payment_key(&self) -> ExpandedKey { - unreachable!() + fn get_expanded_key(&self) -> ExpandedKey { + ExpandedKey::new([42; 32]) } fn get_node_id(&self, recipient: Recipient) -> Result { @@ -1559,8 +1560,8 @@ impl NodeSigner for TestKeysInterface { self.backing.ecdh(recipient, other_key, tweak) } - fn get_inbound_payment_key(&self) -> ExpandedKey { - self.backing.get_inbound_payment_key() + fn get_expanded_key(&self) -> ExpandedKey { + self.backing.get_expanded_key() } fn sign_invoice(