@@ -69,7 +69,6 @@ impl BlindedMessagePath {
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 ,
7574 context : MessageContext , entropy_source : ES , secp_ctx : & Secp256k1 < T > ,
@@ -89,9 +88,53 @@ impl BlindedMessagePath {
8988 blinding_point : PublicKey :: from_secret_key ( secp_ctx, & blinding_secret) ,
9089 blinded_hops : blinded_hops (
9190 secp_ctx,
91+ entropy_source,
92+ None ,
9293 intermediate_nodes,
9394 recipient_node_id,
9495 context,
96+ 0 ,
97+ & blinding_secret,
98+ )
99+ . map_err ( |_| ( ) ) ?,
100+ } ) )
101+ }
102+
103+ /// Create a path for an onion message, to be forwarded along `node_pks`.
104+ ///
105+ /// Additionally allows appending a number of dummy hops before the final hop,
106+ /// increasing the total path length and enhancing privacy by obscuring the true
107+ /// distance between sender and recipient.
108+ ///
109+ /// The last node pubkey in `node_pks` will be the destination node.
110+ ///
111+ /// Errors if no hops are provided or if `node_pk`(s) are invalid.
112+ pub fn new_with_dummy_hops < ES : Deref , T : secp256k1:: Signing + secp256k1:: Verification > (
113+ intermediate_nodes : & [ MessageForwardNode ] , dummy_hops_count : u8 ,
114+ recipient_node_id : PublicKey , context : MessageContext , entropy_source : ES ,
115+ expanded_key : & inbound_payment:: ExpandedKey , secp_ctx : & Secp256k1 < T > ,
116+ ) -> Result < Self , ( ) >
117+ where
118+ ES :: Target : EntropySource ,
119+ {
120+ let introduction_node = IntroductionNode :: NodeId (
121+ intermediate_nodes. first ( ) . map_or ( recipient_node_id, |n| n. node_id ) ,
122+ ) ;
123+ let blinding_secret_bytes = entropy_source. get_secure_random_bytes ( ) ;
124+ let blinding_secret =
125+ SecretKey :: from_slice ( & blinding_secret_bytes[ ..] ) . expect ( "RNG is busted" ) ;
126+
127+ Ok ( Self ( BlindedPath {
128+ introduction_node,
129+ blinding_point : PublicKey :: from_secret_key ( secp_ctx, & blinding_secret) ,
130+ blinded_hops : blinded_hops (
131+ secp_ctx,
132+ entropy_source,
133+ Some ( expanded_key) ,
134+ intermediate_nodes,
135+ recipient_node_id,
136+ context,
137+ dummy_hops_count,
95138 & blinding_secret,
96139 )
97140 . map_err ( |_| ( ) ) ?,
@@ -600,13 +643,23 @@ impl_writeable_tlv_based!(DNSResolverContext, {
600643pub ( crate ) const MESSAGE_PADDING_ROUND_OFF : usize = 100 ;
601644
602645/// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`.
603- pub ( super ) fn blinded_hops < T : secp256k1:: Signing + secp256k1:: Verification > (
604- secp_ctx : & Secp256k1 < T > , intermediate_nodes : & [ MessageForwardNode ] ,
605- recipient_node_id : PublicKey , context : MessageContext , session_priv : & SecretKey ,
606- ) -> Result < Vec < BlindedHop > , secp256k1:: Error > {
646+ pub ( super ) fn blinded_hops < ES : Deref , T : secp256k1:: Signing + secp256k1:: Verification > (
647+ secp_ctx : & Secp256k1 < T > , entropy_source : ES ,
648+ expanded_key : Option < & inbound_payment:: ExpandedKey > , intermediate_nodes : & [ MessageForwardNode ] ,
649+ recipient_node_id : PublicKey , context : MessageContext , dummy_hops_count : u8 ,
650+ session_priv : & SecretKey ,
651+ ) -> Result < Vec < BlindedHop > , secp256k1:: Error >
652+ where
653+ ES :: Target : EntropySource ,
654+ {
655+ if expanded_key. is_none ( ) && dummy_hops_count > 0 {
656+ debug_assert ! ( false , "Dummy hops are not supported without expanded keys" ) ;
657+ }
658+
607659 let pks = intermediate_nodes
608660 . iter ( )
609661 . map ( |node| node. node_id )
662+ . chain ( ( 0 ..dummy_hops_count) . map ( |_| recipient_node_id) )
610663 . chain ( core:: iter:: once ( recipient_node_id) ) ;
611664 let is_compact = intermediate_nodes. iter ( ) . any ( |node| node. short_channel_id . is_some ( ) ) ;
612665
@@ -621,6 +674,14 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
621674 . map ( |next_hop| {
622675 ControlTlvs :: Forward ( ForwardTlvs { next_hop, next_blinding_override : None } )
623676 } )
677+ . chain ( ( 0 ..1 ) . filter ( |_| dummy_hops_count > 0 ) . map ( |_| {
678+ let dummy_tlv = UnauthenticatedDummyTlv { } ;
679+ let nonce = Nonce :: from_entropy_source ( & * entropy_source) ;
680+ let hmac = dummy_tlv. hmac_data ( nonce, expanded_key. unwrap ( ) ) ;
681+ let tlv = PrimaryDummyTlv { dummy_tlv, authentication : ( hmac, nonce) } ;
682+ ControlTlvs :: Dummy ( DummyTlv :: Primary ( tlv) )
683+ } ) )
684+ . chain ( ( 1 ..dummy_hops_count) . map ( |_| ControlTlvs :: Dummy ( DummyTlv :: Subsequent ) ) )
624685 . chain ( core:: iter:: once ( ControlTlvs :: Receive ( ReceiveTlvs { context : Some ( context) } ) ) ) ;
625686
626687 if is_compact {
0 commit comments