@@ -56,23 +56,51 @@ impl Readable for BlindedMessagePath {
5656impl BlindedMessagePath {
5757 /// Create a one-hop blinded path for a message.
5858 pub fn one_hop < ES : Deref , T : secp256k1:: Signing + secp256k1:: Verification > (
59- recipient_node_id : PublicKey , context : MessageContext , entropy_source : ES ,
60- secp_ctx : & Secp256k1 < T > ,
59+ recipient_node_id : PublicKey , context : MessageContext ,
60+ expanded_key : inbound_payment :: ExpandedKey , entropy_source : ES , secp_ctx : & Secp256k1 < T > ,
6161 ) -> Result < Self , ( ) >
6262 where
6363 ES :: Target : EntropySource ,
6464 {
65- Self :: new ( & [ ] , recipient_node_id, context, entropy_source, secp_ctx)
65+ Self :: new ( & [ ] , recipient_node_id, context, entropy_source, expanded_key , secp_ctx)
6666 }
6767
6868 /// Create a path for an onion message, to be forwarded along `node_pks`. The last node
6969 /// pubkey in `node_pks` will be the destination node.
7070 ///
7171 /// Errors if no hops are provided or if `node_pk`(s) are invalid.
72- // TODO: make all payloads the same size with padding + add dummy hops
7372 pub fn new < ES : Deref , T : secp256k1:: Signing + secp256k1:: Verification > (
7473 intermediate_nodes : & [ MessageForwardNode ] , recipient_node_id : PublicKey ,
75- context : MessageContext , entropy_source : ES , secp_ctx : & Secp256k1 < T > ,
74+ context : MessageContext , entropy_source : ES , expanded_key : inbound_payment:: ExpandedKey ,
75+ secp_ctx : & Secp256k1 < T > ,
76+ ) -> Result < Self , ( ) >
77+ where
78+ ES :: Target : EntropySource ,
79+ {
80+ BlindedMessagePath :: new_with_dummy_hops (
81+ intermediate_nodes,
82+ 0 ,
83+ recipient_node_id,
84+ context,
85+ entropy_source,
86+ expanded_key,
87+ secp_ctx,
88+ )
89+ }
90+
91+ /// Create a path for an onion message, to be forwarded along `node_pks`.
92+ ///
93+ /// Additionally allows appending a number of dummy hops before the final hop,
94+ /// increasing the total path length and enhancing privacy by obscuring the true
95+ /// distance between sender and recipient.
96+ ///
97+ /// The last node pubkey in `node_pks` will be the destination node.
98+ ///
99+ /// Errors if no hops are provided or if `node_pk`(s) are invalid.
100+ pub fn new_with_dummy_hops < ES : Deref , T : secp256k1:: Signing + secp256k1:: Verification > (
101+ intermediate_nodes : & [ MessageForwardNode ] , dummy_hops_count : u8 ,
102+ recipient_node_id : PublicKey , context : MessageContext , entropy_source : ES ,
103+ expanded_key : inbound_payment:: ExpandedKey , secp_ctx : & Secp256k1 < T > ,
76104 ) -> Result < Self , ( ) >
77105 where
78106 ES :: Target : EntropySource ,
@@ -89,9 +117,12 @@ impl BlindedMessagePath {
89117 blinding_point : PublicKey :: from_secret_key ( secp_ctx, & blinding_secret) ,
90118 blinded_hops : blinded_hops (
91119 secp_ctx,
120+ entropy_source,
121+ expanded_key,
92122 intermediate_nodes,
93123 recipient_node_id,
94124 context,
125+ dummy_hops_count,
95126 & blinding_secret,
96127 )
97128 . map_err ( |_| ( ) ) ?,
@@ -260,7 +291,7 @@ pub(crate) struct ForwardTlvs {
260291}
261292
262293/// A blank struct, representing dummy tlv prior to authentication.
263- ///
294+ ///
264295/// For more details, see [`DummyTlv`].
265296pub ( crate ) struct UnauthenticatedDummyTlv { }
266297
@@ -288,15 +319,15 @@ impl Verification for UnauthenticatedDummyTlv {
288319/// Represents the dummy TLV encoded immediately before the actual [`ReceiveTlvs`] in a blinded path.
289320/// These TLVs are intended for the final node and are recursively authenticated and verified until
290321/// the real [`ReceiveTlvs`] is reached.
291- ///
322+ ///
292323/// Their purpose is to arbitrarily extend the path length, obscuring the receiver's position in the
293324/// route and thereby enhancing privacy.
294- ///
325+ ///
295326/// ## Authentication
296327/// Authentication provides an additional layer of security, ensuring that the path is legitimate
297328/// and terminates in valid [`ReceiveTlvs`] data. Verification begins with the first dummy hop and
298329/// continues recursively until the final [`ReceiveTlvs`] is reached.
299- ///
330+ ///
300331/// This prevents an attacker from crafting a bogus blinded path consisting solely of dummy tlv
301332/// without any valid payload, which could otherwise waste resources through recursive
302333/// processing — a potential vector for DoS-like attacks.
@@ -563,13 +594,18 @@ impl_writeable_tlv_based!(DNSResolverContext, {
563594pub ( crate ) const MESSAGE_PADDING_ROUND_OFF : usize = 100 ;
564595
565596/// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`.
566- pub ( super ) fn blinded_hops < T : secp256k1:: Signing + secp256k1:: Verification > (
567- secp_ctx : & Secp256k1 < T > , intermediate_nodes : & [ MessageForwardNode ] ,
568- recipient_node_id : PublicKey , context : MessageContext , session_priv : & SecretKey ,
569- ) -> Result < Vec < BlindedHop > , secp256k1:: Error > {
597+ pub ( super ) fn blinded_hops < ES : Deref , T : secp256k1:: Signing + secp256k1:: Verification > (
598+ secp_ctx : & Secp256k1 < T > , entropy_source : ES , expanded_key : inbound_payment:: ExpandedKey ,
599+ intermediate_nodes : & [ MessageForwardNode ] , recipient_node_id : PublicKey ,
600+ context : MessageContext , dummy_hops_count : u8 , session_priv : & SecretKey ,
601+ ) -> Result < Vec < BlindedHop > , secp256k1:: Error >
602+ where
603+ ES :: Target : EntropySource ,
604+ {
570605 let pks = intermediate_nodes
571606 . iter ( )
572607 . map ( |node| node. node_id )
608+ . chain ( ( 0 ..dummy_hops_count) . map ( |_| recipient_node_id) )
573609 . chain ( core:: iter:: once ( recipient_node_id) ) ;
574610 let is_compact = intermediate_nodes. iter ( ) . any ( |node| node. short_channel_id . is_some ( ) ) ;
575611
@@ -584,6 +620,12 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
584620 . map ( |next_hop| {
585621 ControlTlvs :: Forward ( ForwardTlvs { next_hop, next_blinding_override : None } )
586622 } )
623+ . chain ( ( 0 ..dummy_hops_count) . map ( |_| {
624+ let dummy_tlv = UnauthenticatedDummyTlv { } ;
625+ let nonce = Nonce :: from_entropy_source ( & * entropy_source) ;
626+ let hmac = dummy_tlv. hmac_data ( nonce, & expanded_key) ;
627+ ControlTlvs :: Dummy ( DummyTlv { dummy_tlv, authentication : ( hmac, nonce) } )
628+ } ) )
587629 . chain ( core:: iter:: once ( ControlTlvs :: Receive ( ReceiveTlvs { context : Some ( context) } ) ) ) ;
588630
589631 if is_compact {
0 commit comments