gosling/
gosling.rs

1// standard
2#[cfg(test)]
3use std::net::{SocketAddr, TcpListener, TcpStream};
4
5// extern crates
6#[cfg(test)]
7use bson::doc;
8use data_encoding::HEXLOWER;
9#[cfg(test)]
10use honk_rpc::honk_rpc::Session;
11use num_enum::TryFromPrimitive;
12use tor_interface::tor_crypto::*;
13
14// internal crates
15use crate::ascii_string::*;
16#[cfg(test)]
17use crate::endpoint_client::*;
18#[cfg(test)]
19use crate::endpoint_server::*;
20#[cfg(test)]
21use crate::identity_client::*;
22#[cfg(test)]
23use crate::identity_server::*;
24
25#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
26#[repr(i32)]
27/// cbindgen:ignore
28pub(crate) enum RpcError {
29    // bad gosling version
30    BadVersion,
31    // cookie required
32    RequestCookieRequired,
33    // invalid or missing arguments
34    InvalidArg,
35    // generic runtime error
36    Failure,
37}
38
39pub(crate) const GOSLING_PROTOCOL_VERSION: &str = "0.1.0";
40
41pub(crate) const CLIENT_COOKIE_SIZE: usize = 32usize;
42pub(crate) const SERVER_COOKIE_SIZE: usize = 32usize;
43
44pub(crate) type ClientCookie = [u8; CLIENT_COOKIE_SIZE];
45pub(crate) type ServerCookie = [u8; SERVER_COOKIE_SIZE];
46pub(crate) type ClientProof = Vec<u8>;
47
48pub(crate) enum DomainSeparator {
49    GoslingIdentity,
50    GoslingEndpoint,
51}
52
53impl From<DomainSeparator> for &[u8] {
54    fn from(sep: DomainSeparator) -> &'static [u8] {
55        match sep {
56            DomainSeparator::GoslingIdentity => b"gosling-identity",
57            DomainSeparator::GoslingEndpoint => b"gosling-endpoint",
58        }
59    }
60}
61
62pub(crate) fn build_client_proof(
63    domain_separator: DomainSeparator,
64    request: &AsciiString,
65    client_service_id: &V3OnionServiceId,
66    server_service_id: &V3OnionServiceId,
67    client_cookie: &ClientCookie,
68    server_cookie: &ServerCookie,
69) -> ClientProof {
70    let mut client_proof: ClientProof = Default::default();
71
72    client_proof.extend_from_slice(domain_separator.into());
73    client_proof.push(0u8);
74    client_proof.extend_from_slice(request.as_bytes());
75    client_proof.push(0u8);
76    client_proof.extend_from_slice(client_service_id.to_string().as_bytes());
77    client_proof.push(0u8);
78    client_proof.extend_from_slice(server_service_id.to_string().as_bytes());
79    client_proof.push(0u8);
80    client_proof.extend_from_slice(HEXLOWER.encode(client_cookie).as_bytes());
81    client_proof.push(0u8);
82    client_proof.extend_from_slice(HEXLOWER.encode(server_cookie).as_bytes());
83
84    client_proof
85}
86
87//
88// Tests
89//
90
91#[cfg(test)]
92fn identity_test(
93    client_blocked: bool,
94    client_requested_endpoint: &str,
95    client_requested_endpoint_valid: bool,
96    server_challenge: bson::document::Document,
97    client_response: bson::document::Document,
98    server_expected_response: bson::document::Document,
99    should_fail: bool,
100) -> anyhow::Result<()> {
101    // test sockets
102    let socket_addr = SocketAddr::from(([127, 0, 0, 1], 0u16));
103    let listener = TcpListener::bind(socket_addr)?;
104    let socket_addr = listener.local_addr()?;
105
106    let stream1 = TcpStream::connect(socket_addr)?;
107    stream1.set_nonblocking(true)?;
108    let (stream2, _socket_addr) = listener.accept()?;
109    stream2.set_nonblocking(true)?;
110
111    // client setup
112    let client_ed25519_private = Ed25519PrivateKey::generate();
113
114    // server setup
115    let server_ed25519_private = Ed25519PrivateKey::generate();
116    let server_ed25519_public = Ed25519PublicKey::from_private_key(&server_ed25519_private);
117    let server_service_id = V3OnionServiceId::from_public_key(&server_ed25519_public);
118
119    let client_requested_endpoint = match AsciiString::new(client_requested_endpoint.to_string()) {
120        Ok(ascii) => ascii,
121        Err(_) => {
122            assert!(should_fail);
123            return Ok(());
124        }
125    };
126
127    // rpc setup
128    let client_rpc = Session::new(stream1);
129    let mut ident_client = IdentityClient::new(
130        client_rpc,
131        server_service_id.clone(),
132        client_requested_endpoint.clone(),
133        client_ed25519_private,
134        X25519PrivateKey::generate(),
135    )
136    .unwrap();
137
138    let server_rpc = Session::new(stream2);
139    let mut ident_server = IdentityServer::new(server_rpc, server_service_id.clone());
140
141    let mut failure_ocurred = false;
142    let mut server_complete = false;
143    let mut client_complete = false;
144    while !server_complete && !client_complete {
145        if !server_complete {
146            match ident_server.update() {
147                Ok(Some(IdentityServerEvent::EndpointRequestReceived {
148                    client_service_id,
149                    requested_endpoint,
150                })) => {
151                    println!(
152                        "server challenge send: client_service_id {}, requested_endpoint: {}",
153                        client_service_id.to_string(),
154                        requested_endpoint
155                    );
156                    let client_allowed = !client_blocked;
157                    ident_server.handle_endpoint_request_received(
158                        client_allowed,
159                        client_requested_endpoint_valid,
160                        server_challenge.clone(),
161                    )?;
162                }
163                Ok(Some(IdentityServerEvent::ChallengeResponseReceived { challenge_response })) => {
164                    println!("server challenge repsonse received");
165                    ident_server.handle_challenge_response_received(
166                        challenge_response == server_expected_response,
167                    )?;
168                }
169                Ok(Some(IdentityServerEvent::HandshakeCompleted {
170                    endpoint_private_key: _,
171                    endpoint_name,
172                    client_service_id,
173                    client_auth_public_key: _,
174                })) => {
175                    assert!(endpoint_name == client_requested_endpoint);
176                    println!(
177                        "server complete! client_service_id : {}",
178                        client_service_id.to_string()
179                    );
180                    server_complete = true;
181                }
182                Ok(Some(IdentityServerEvent::HandshakeRejected {
183                    client_allowed,
184                    client_requested_endpoint_valid,
185                    client_proof_signature_valid,
186                    client_auth_signature_valid,
187                    challenge_response_valid,
188                })) => {
189                    println!("server complete! client request rejected");
190                    println!(" client_allowed: {}", client_allowed);
191                    println!(
192                        " client_requested_endpoint_valid: {}",
193                        client_requested_endpoint_valid
194                    );
195                    println!(
196                        " client_proof_signature_valid: {}",
197                        client_proof_signature_valid
198                    );
199                    println!(
200                        " client_auth_signature_valid: {}",
201                        client_auth_signature_valid
202                    );
203                    println!(" client_response_valid: {}", challenge_response_valid);
204                    server_complete = true;
205                    failure_ocurred = true;
206                }
207                Ok(None) => {}
208                Err(err) => {
209                    println!("server failure: {:?}", err);
210                    server_complete = true;
211                    failure_ocurred = true;
212                }
213            }
214        }
215
216        if !client_complete {
217            match ident_client.update() {
218                Ok(Some(IdentityClientEvent::ChallengeReceived { endpoint_challenge })) => {
219                    println!(
220                        "client challenge request received: endpoint_challenge: {}",
221                        endpoint_challenge
222                    );
223                    ident_client.send_response(client_response.clone())?;
224                }
225                Ok(Some(IdentityClientEvent::HandshakeCompleted {
226                    identity_service_id,
227                    endpoint_service_id,
228                    endpoint_name,
229                    client_auth_private_key: _,
230                })) => {
231                    assert!(identity_service_id == server_service_id);
232                    assert!(endpoint_name == client_requested_endpoint.clone().to_string());
233                    println!(
234                        "client complete! endpoint_server : {}",
235                        endpoint_service_id.to_string()
236                    );
237                    client_complete = true;
238                }
239                Ok(None) => {}
240                Err(err) => {
241                    println!("client failure: {:?}", err);
242                    client_complete = true;
243                    failure_ocurred = true;
244                }
245            }
246        }
247    }
248
249    assert!(failure_ocurred == should_fail);
250    Ok(())
251}
252
253#[test]
254fn test_identity_handshake() -> anyhow::Result<()> {
255    println!("Sucessful ---");
256    {
257        let client_blocked: bool = false;
258        let client_requested_endpoint: &str = "endpoint";
259        let client_requested_endpoint_valid: bool = true;
260        let server_challenge: bson::document::Document = doc!("msg": "Speak friend and enter");
261        let client_response: bson::document::Document = doc!("msg": "Mellon");
262        let server_expected_response: bson::document::Document = doc!("msg": "Mellon");
263        let should_fail: bool = false;
264        identity_test(
265            client_blocked,
266            client_requested_endpoint,
267            client_requested_endpoint_valid,
268            server_challenge,
269            client_response,
270            server_expected_response,
271            should_fail,
272        )?;
273    }
274    println!("Bad Endpoint ---");
275    {
276        let client_blocked: bool = false;
277        let client_requested_endpoint: &str = "endpoint";
278        let client_requested_endpoint_valid: bool = false;
279        let server_challenge: bson::document::Document = doc!("msg": "Speak friend and enter");
280        let client_response: bson::document::Document = doc!("msg": "Mellon");
281        let server_expected_response: bson::document::Document = doc!("msg": "Mellon");
282        let should_fail: bool = true;
283        identity_test(
284            client_blocked,
285            client_requested_endpoint,
286            client_requested_endpoint_valid,
287            server_challenge,
288            client_response,
289            server_expected_response,
290            should_fail,
291        )?;
292    }
293    println!("Bad Challenge Response ---");
294    {
295        let client_blocked: bool = false;
296        let client_requested_endpoint: &str = "endpoint";
297        let client_requested_endpoint_valid: bool = true;
298        let server_challenge: bson::document::Document = doc!("msg": "Speak friend and enter");
299        let client_response: bson::document::Document = doc!("msg": "Friend?");
300        let server_expected_response: bson::document::Document = doc!("msg": "Mellon");
301        let should_fail: bool = true;
302        identity_test(
303            client_blocked,
304            client_requested_endpoint,
305            client_requested_endpoint_valid,
306            server_challenge,
307            client_response,
308            server_expected_response,
309            should_fail,
310        )?;
311    }
312    println!("Blocked Client ---");
313    {
314        let client_blocked: bool = true;
315        let client_requested_endpoint: &str = "endpoint";
316        let client_requested_endpoint_valid: bool = true;
317        let server_challenge: bson::document::Document = doc!("msg": "Speak friend and enter");
318        let client_response: bson::document::Document = doc!("msg": "Mellon");
319        let server_expected_response: bson::document::Document = doc!("msg": "Mellon");
320        let should_fail: bool = true;
321        identity_test(
322            client_blocked,
323            client_requested_endpoint,
324            client_requested_endpoint_valid,
325            server_challenge,
326            client_response,
327            server_expected_response,
328            should_fail,
329        )?;
330    }
331    println!("Non-ASCII endpoint ---");
332    {
333        let client_blocked: bool = false;
334        let client_requested_endpoint: &str = "𝕦𝕥𝕗𝟠";
335        let client_requested_endpoint_valid: bool = true;
336        let server_challenge: bson::document::Document = doc!("msg": "Speak friend and enter");
337        let client_response: bson::document::Document = doc!("msg": "Mellon");
338        let server_expected_response: bson::document::Document = doc!("msg": "Mellon");
339        let should_fail: bool = true;
340        identity_test(
341            client_blocked,
342            client_requested_endpoint,
343            client_requested_endpoint_valid,
344            server_challenge,
345            client_response,
346            server_expected_response,
347            should_fail,
348        )?;
349    }
350    Ok(())
351}
352
353#[cfg(test)]
354fn endpoint_test(
355    should_fail: bool,
356    client_allowed: bool,
357    channel: &str,
358    channel_allowed: bool,
359) -> anyhow::Result<()> {
360    // test sockets
361    let socket_addr = SocketAddr::from(([127, 0, 0, 1], 0u16));
362    let listener = TcpListener::bind(socket_addr)?;
363    let socket_addr = listener.local_addr()?;
364
365    let stream1 = TcpStream::connect(socket_addr)?;
366    stream1.set_nonblocking(true)?;
367    let (stream2, _socket_addr) = listener.accept()?;
368    stream2.set_nonblocking(true)?;
369
370    // server+client setup
371    let server_ed25519_private = Ed25519PrivateKey::generate();
372    let server_ed25519_public = Ed25519PublicKey::from_private_key(&server_ed25519_private);
373    let server_service_id = V3OnionServiceId::from_public_key(&server_ed25519_public);
374
375    let client_ed25519_private = Ed25519PrivateKey::generate();
376    let client_ed25519_public = Ed25519PublicKey::from_private_key(&client_ed25519_private);
377    let client_service_id = V3OnionServiceId::from_public_key(&client_ed25519_public);
378
379    // ensure our client is in the allow list
380    let allowed_client = if client_allowed {
381        client_service_id.clone()
382    } else {
383        let ed25519_private = Ed25519PrivateKey::generate();
384        let ed25519_public = Ed25519PublicKey::from_private_key(&ed25519_private);
385        V3OnionServiceId::from_public_key(&ed25519_public)
386    };
387
388    let server_rpc = Session::new(stream1);
389
390    let mut endpoint_server = EndpointServer::new(
391        server_rpc,
392        allowed_client.clone(),
393        server_service_id.clone(),
394    );
395
396    let client_rpc = Session::new(stream2);
397
398    let channel = match AsciiString::new(channel.to_string()) {
399        Ok(channel) => channel,
400        Err(_) => {
401            assert!(should_fail);
402            return Ok(());
403        }
404    };
405
406    let mut endpoint_client = EndpointClient::new(
407        client_rpc,
408        server_service_id.clone(),
409        channel.clone(),
410        client_ed25519_private,
411    );
412
413    let mut failure_ocurred = false;
414    let mut server_complete = false;
415    let mut client_complete = false;
416    while !server_complete && !client_complete {
417        if !server_complete {
418            match endpoint_server.update() {
419                Ok(Some(EndpointServerEvent::ChannelRequestReceived {
420                    client_service_id: ret_client_service_id,
421                    requested_channel,
422                })) => {
423                    assert_eq!(ret_client_service_id, client_service_id);
424                    assert!(requested_channel == channel);
425                    endpoint_server.handle_channel_request_received(channel_allowed)?;
426                }
427                Ok(Some(EndpointServerEvent::HandshakeCompleted {
428                    client_service_id: ret_client_service_id,
429                    channel_name: ret_channel,
430                    stream: _,
431                })) => {
432                    assert!(ret_client_service_id == client_service_id);
433                    assert!(ret_channel == channel);
434                    server_complete = true;
435                }
436                Ok(Some(EndpointServerEvent::HandshakeRejected {
437                    client_allowed,
438                    client_requested_channel_valid,
439                    client_proof_signature_valid,
440                })) => {
441                    println!("handshake rejected: client_allowed: {}, client_requested_channel_valid: {}, client_proof_signature_valid: {}", client_allowed, client_requested_channel_valid, client_proof_signature_valid);
442                    server_complete = true;
443                    failure_ocurred = true;
444                }
445                Ok(None) => {}
446                Err(err) => {
447                    println!("server failure: {:?}", err);
448                    server_complete = true;
449                    failure_ocurred = true;
450                }
451            }
452        }
453
454        if !client_complete {
455            match endpoint_client.update() {
456                Ok(Some(EndpointClientEvent::HandshakeCompleted { stream: _ })) => {
457                    client_complete = true;
458                }
459                Ok(None) => {}
460                Err(err) => {
461                    println!("client failure: {:?}", err);
462                    client_complete = true;
463                    failure_ocurred = true;
464                }
465            }
466        }
467    }
468
469    println!("server_complete: {}", server_complete);
470    println!("client_complete: {}", client_complete);
471
472    assert!(should_fail == failure_ocurred);
473
474    Ok(())
475}
476
477#[test]
478fn test_endpoint_handshake() -> anyhow::Result<()> {
479    println!("Success ---");
480    {
481        let should_fail = false;
482        let client_allowed = true;
483        let channel = "channel";
484        let channel_allowed = true;
485        endpoint_test(should_fail, client_allowed, channel, channel_allowed)?;
486    }
487    println!("Client Not Allowed ---");
488    {
489        let should_fail = true;
490        let client_allowed = false;
491        let channel = "channel";
492        let channel_allowed = true;
493        endpoint_test(should_fail, client_allowed, channel, channel_allowed)?;
494    }
495    println!("Channel Not Allowed ---");
496    {
497        let should_fail = true;
498        let client_allowed = true;
499        let channel = "channel";
500        let channel_allowed = false;
501        endpoint_test(should_fail, client_allowed, channel, channel_allowed)?;
502    }
503    println!("Client and Channel Not Allowed ---");
504    {
505        let should_fail = true;
506        let client_allowed = false;
507        let channel = "channel";
508        let channel_allowed = false;
509        endpoint_test(should_fail, client_allowed, channel, channel_allowed)?;
510    }
511    println!("Non-Ascii Channel ---");
512    {
513        let should_fail = true;
514        let client_allowed = true;
515        let channel = "𝕦𝕥𝕗𝟠";
516        let channel_allowed = true;
517        endpoint_test(should_fail, client_allowed, channel, channel_allowed)?;
518    }
519
520    Ok(())
521}