1#[cfg(test)]
3use std::net::{SocketAddr, TcpListener, TcpStream};
4
5#[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
14use 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)]
27pub(crate) enum RpcError {
29 BadVersion,
31 RequestCookieRequired,
33 InvalidArg,
35 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#[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 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 let client_ed25519_private = Ed25519PrivateKey::generate();
113
114 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 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 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 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 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}