44//! to automagically provide encrypted and authenticated channels.
55//!
66
7+ #[ cfg( feature = "tor" ) ]
8+ use crate :: tor:: TorProxy ;
79use crate :: {
810 error:: Error ,
911 message,
@@ -36,7 +38,41 @@ impl KKTransport {
3638 let timeout = Duration :: from_secs ( 20 ) ;
3739 let mut stream = TcpStream :: connect_timeout ( & addr, timeout) ?;
3840 stream. set_read_timeout ( Some ( timeout) ) ?;
41+ let channel = KKTransport :: perform_client_handshake (
42+ & mut stream,
43+ my_noise_privkey,
44+ their_noise_pubkey,
45+ ) ?;
46+ Ok ( KKTransport { stream, channel } )
47+ }
48+
49+ #[ cfg( feature = "tor" ) ]
50+ /// Connect to server at given tor address using the provided SOCKS5 proxy,
51+ /// and enact Noise handshake with given private key.
52+ /// Sets a read timeout of 20 seconds.
53+ pub fn tor_connect (
54+ addr : & str ,
55+ proxy : & TorProxy ,
56+ my_noise_privkey : & SecretKey ,
57+ their_noise_pubkey : & PublicKey ,
58+ ) -> Result < KKTransport , Error > {
59+ let mut stream = socks:: Socks5Stream :: connect ( & proxy. address , addr) ?. into_inner ( ) ;
60+ let timeout = Duration :: from_secs ( 20 ) ;
61+ stream. set_read_timeout ( Some ( timeout) ) ?;
62+ let channel = KKTransport :: perform_client_handshake (
63+ & mut stream,
64+ my_noise_privkey,
65+ their_noise_pubkey,
66+ ) ?;
67+ Ok ( KKTransport { stream, channel } )
68+ }
3969
70+ // Used by connect() and tor_connect() to perform the handshake
71+ fn perform_client_handshake (
72+ stream : & mut TcpStream ,
73+ my_noise_privkey : & SecretKey ,
74+ their_noise_pubkey : & PublicKey ,
75+ ) -> Result < KKChannel , Error > {
4076 let ( cli_act_1, msg_1) =
4177 KKHandshakeActOne :: initiator ( my_noise_privkey, their_noise_pubkey) ?;
4278
@@ -49,8 +85,7 @@ impl KKTransport {
4985
5086 let msg_act_2 = KKMessageActTwo ( msg_2) ;
5187 let cli_act_2 = KKHandshakeActTwo :: initiator ( cli_act_1, & msg_act_2) ?;
52- let channel = KKChannel :: from_handshake ( cli_act_2) ?;
53- Ok ( KKTransport { stream, channel } )
88+ KKChannel :: from_handshake ( cli_act_2) . map_err ( |e| e. into ( ) )
5489 }
5590
5691 /// Accept an incoming connection and immediately perform the noise KK handshake
@@ -189,7 +224,78 @@ impl KKTransport {
189224mod tests {
190225 use super :: * ;
191226 use sodiumoxide:: crypto:: box_:: curve25519xsalsa20poly1305:: gen_keypair;
192- use std:: { collections:: BTreeMap , str:: FromStr , thread} ;
227+ use std:: { collections:: BTreeMap , fs, process:: Command , str:: FromStr , thread} ;
228+
229+ #[ test]
230+ #[ cfg( feature = "tor" ) ]
231+ fn test_transport_kk_tor ( ) {
232+ let ( ( client_pubkey, client_privkey) , ( server_pubkey, server_privkey) ) =
233+ ( gen_keypair ( ) , gen_keypair ( ) ) ;
234+ let listener = TcpListener :: bind ( "127.0.0.1:0" ) . unwrap ( ) ;
235+ let server_addr = listener. local_addr ( ) . unwrap ( ) ;
236+
237+ let datadir = "scratch_test_datadir" ;
238+ // Clean from previous run
239+ fs:: remove_dir_all ( & datadir) . unwrap_or_else ( |_| ( ) ) ;
240+ fs:: create_dir ( & datadir) . unwrap ( ) ;
241+ let mut file = fs:: File :: create ( format ! ( "{}/torrc" , datadir) ) . unwrap ( ) ;
242+ let torrc = format ! (
243+ r#"HiddenServiceDir {0}/hidden_service/
244+ HiddenServicePort 19051 127.0.0.1:{1}
245+ DataDirectory {0}/server
246+ Log notice file {0}/server/log
247+ SOCKSPort 0"# ,
248+ datadir,
249+ server_addr. port( ) ,
250+ ) ;
251+ file. write_all ( torrc. as_bytes ( ) ) . unwrap ( ) ;
252+ let mut hidden_service_process = Command :: new ( "tor" )
253+ . args ( & [ "-f" , & format ! ( "{}/torrc" , datadir) ] )
254+ . spawn ( )
255+ . expect ( "Tor failed to start" ) ;
256+
257+ let msg = "Test message" . as_bytes ( ) ;
258+
259+ // hidden_service_process won't be killed if we panic here, so
260+ // instead of unwrapping directly I'm using `?` in a closure
261+ // and unwrapping the result after killing tor.
262+ // This way if there's an error we don't leave dangling tors around
263+ let c = || -> Result < _ , Box < dyn std:: error:: Error > > {
264+ let client_proxy = TorProxy :: start_tor ( format ! ( "{}/client/" , datadir) . into ( ) , None ) ;
265+
266+ // server thread
267+ let server_thread = thread:: spawn ( move || {
268+ let my_noise_privkey = server_privkey;
269+ let their_noise_pubkey = client_pubkey;
270+ let mut server_transport =
271+ KKTransport :: accept ( & listener, & my_noise_privkey, & [ their_noise_pubkey] ) ?;
272+ server_transport. read ( )
273+ } ) ;
274+
275+ // Giving tor a bit of time to start...
276+ std:: thread:: sleep ( std:: time:: Duration :: from_secs ( 30 ) ) ;
277+ let hidden_service_onion =
278+ fs:: read_to_string ( format ! ( "{}/hidden_service/hostname" , datadir) ) ?;
279+ let hidden_service_address = format ! ( "{}:19051" , hidden_service_onion. trim_end( ) ) ;
280+
281+ // client thread
282+ let mut cli_channel = KKTransport :: tor_connect (
283+ & hidden_service_address,
284+ & client_proxy,
285+ & client_privkey,
286+ & server_pubkey,
287+ ) ?;
288+ cli_channel. write ( & msg) ?;
289+
290+ Ok ( server_thread
291+ . join ( )
292+ . map_err ( |_| String :: from ( "Error joining thread" ) ) ??)
293+ } ;
294+
295+ let received_msg = c ( ) ;
296+ hidden_service_process. kill ( ) . unwrap_or_else ( |_| { } ) ;
297+ assert_eq ! ( msg, received_msg. unwrap( ) . as_slice( ) ) ;
298+ }
193299
194300 #[ test]
195301 fn test_transport_kk ( ) {
0 commit comments