1use std::clone::Clone;
3use std::convert::TryInto;
4use std::net::TcpStream;
5
6use bson::doc;
8use bson::spec::BinarySubtype;
9use bson::{Binary, Bson};
10use honk_rpc::honk_rpc::{
11 get_message_overhead, get_response_section_size, ApiSet, ErrorCode, RequestCookie, Session,
12};
13use rand::rngs::OsRng;
14use rand::{rand_core, TryRngCore};
15use tor_interface::tor_crypto::*;
16
17use crate::ascii_string::*;
19use crate::gosling::*;
20
21#[derive(thiserror::Error, Debug)]
26pub enum Error {
27 #[error("HonkRPC method failed: {0}")]
28 HonkRPCFailure(#[from] honk_rpc::honk_rpc::Error),
29
30 #[error("server is in invalid state: {0}")]
31 InvalidState(String),
32
33 #[error("incorrect usage: {0}")]
34 IncorrectUsage(String),
35
36 #[error("client sent invalid request")]
37 BadClient,
38
39 #[error("provided endpoint challenge too large; encoded size would be {0} but session's maximum honk-rpc message size is {1}")]
40 EndpointChallengeTooLarge(usize, usize),
41
42 #[error("OsRng::try_fill_bytes() failed: {0}")]
43 OsRngTryFillBytesFailure(#[from] rand_core::OsError),
44}
45
46pub(crate) enum IdentityServerEvent {
47 EndpointRequestReceived {
48 client_service_id: V3OnionServiceId,
49 requested_endpoint: AsciiString,
50 },
51
52 ChallengeResponseReceived {
53 challenge_response: bson::document::Document,
54 },
55
56 HandshakeCompleted {
57 endpoint_private_key: Ed25519PrivateKey,
58 endpoint_name: AsciiString,
59 client_service_id: V3OnionServiceId,
60 client_auth_public_key: X25519PublicKey,
61 },
62
63 HandshakeRejected {
64 client_allowed: bool,
66 client_requested_endpoint_valid: bool,
68 client_proof_signature_valid: bool,
70 client_auth_signature_valid: bool,
72 challenge_response_valid: bool,
74 },
75}
76
77#[derive(Debug, PartialEq)]
78enum IdentityServerState {
79 WaitingForBeginHandshake,
81 GettingChallenge,
82 ChallengeReady,
83 WaitingForSendResponse,
84 GettingChallengeVerification,
85 ChallengeVerificationReady,
86 ChallengeVerificationResponseSent,
87 HandshakeComplete,
88 HandshakeFailed,
90}
91
92pub(crate) struct IdentityServer {
93 rpc: Option<Session<TcpStream>>,
95 server_identity: V3OnionServiceId,
96
97 state: IdentityServerState,
99 begin_handshake_request_cookie: Option<RequestCookie>,
100 client_identity: Option<V3OnionServiceId>,
101 requested_endpoint: Option<AsciiString>,
102 server_cookie: Option<ServerCookie>,
103 endpoint_challenge: Option<bson::document::Document>,
104 send_response_request_cookie: Option<RequestCookie>,
105 client_auth_key: Option<X25519PublicKey>,
106 challenge_response: Option<bson::document::Document>,
107 endpoint_private_key: Option<Ed25519PrivateKey>,
108
109 client_allowed: bool,
113 client_requested_endpoint_valid: bool,
115 client_proof_signature_valid: bool,
117 client_auth_signature_valid: bool,
119 challenge_response_valid: bool,
121}
122
123impl IdentityServer {
124 fn get_state(&self) -> String {
125 format!("{{ state: {:?}, begin_handshake_request_cookie: {:?}, client_identity: {:?}, requested_endpoint: {:?}, server_cookie: {:?}, endpoint_challenge: {:?}, send_response_request_cookie: {:?}, client_auth_key: {:?}, challenge_response: {:?}, endpoint_private_key: {:?} }}", self.state, self.begin_handshake_request_cookie, self.client_identity, self.requested_endpoint, self.server_cookie, self.endpoint_challenge, self.send_response_request_cookie, self.client_auth_key, self.challenge_response, self.endpoint_private_key)
126 }
127
128 pub fn new(rpc: Session<TcpStream>, server_identity: V3OnionServiceId) -> Self {
129 IdentityServer {
130 rpc: Some(rpc),
132 server_identity,
133
134 state: IdentityServerState::WaitingForBeginHandshake,
136 begin_handshake_request_cookie: None,
137 client_identity: None,
138 requested_endpoint: None,
139 server_cookie: None,
140 endpoint_challenge: None,
141 send_response_request_cookie: None,
142 client_auth_key: None,
143 challenge_response: None,
144 endpoint_private_key: None,
145
146 client_allowed: false,
148 client_requested_endpoint_valid: false,
149 client_proof_signature_valid: false,
150 client_auth_signature_valid: false,
151 challenge_response_valid: false,
152 }
153 }
154
155 pub fn update(&mut self) -> Result<Option<IdentityServerEvent>, Error> {
156 if let Some(mut rpc) = std::mem::take(&mut self.rpc) {
159 match rpc.update(Some(&mut [self])) {
160 Ok(()) => {
161 self.rpc = Some(rpc);
162 }
163 Err(err) => {
164 self.rpc = Some(rpc);
165 return Err(err.into());
166 }
167 }
168 }
169
170 match(&self.state,
171 self.begin_handshake_request_cookie,
172 self.client_identity.as_ref(),
173 self.requested_endpoint.as_ref(),
174 self.server_cookie.as_ref(),
175 self.endpoint_challenge.as_ref(),
176 self.send_response_request_cookie,
177 self.client_auth_key.as_ref(),
178 self.challenge_response.as_mut(),
179 self.endpoint_private_key.as_ref()) {
180 (&IdentityServerState::WaitingForBeginHandshake,
181 None, None, None, None, None, None, None, None, None) => {
191 },
193 (&IdentityServerState::WaitingForBeginHandshake,
194 Some(_begin_handshake_request_cookie),
195 Some(client_identity),
196 Some(requested_endpoint),
197 None, None, None, None, None, None) => {
204 self.state = IdentityServerState::GettingChallenge;
205 return Ok(Some(IdentityServerEvent::EndpointRequestReceived{client_service_id: client_identity.clone(), requested_endpoint: requested_endpoint.clone()}));
206 },
207 (&IdentityServerState::WaitingForSendResponse,
208 Some(_begin_handshake_request_cookie),
209 Some(_client_identity),
210 Some(_requested_endpoint),
211 Some(_server_cookie),
212 Some(_endpoint_challenge),
213 None, None, None, None) => {
218 },
220 (&IdentityServerState::WaitingForSendResponse,
221 Some(_begin_handshake_request_cookie),
222 Some(_client_identity),
223 Some(_requested_endpoint),
224 Some(_server_cookie),
225 Some(_endpoint_challenge),
226 Some(_send_response_request_cookie),
227 Some(_client_auth_key),
228 Some(challenge_response),
229 None) => {
231 self.state = IdentityServerState::GettingChallengeVerification;
232 return Ok(Some(IdentityServerEvent::ChallengeResponseReceived{
233 challenge_response: std::mem::take(challenge_response),
234 }));
235 },
236 (&IdentityServerState::ChallengeVerificationResponseSent,
237 Some(_begin_handshake_request_cookie),
238 Some(client_identity),
239 Some(requested_endpoint),
240 Some(_server_cookie),
241 Some(_endpoint_challenge),
242 Some(_send_response_request_cookie),
243 Some(client_auth_key),
244 Some(_challenge_response),
245 Some(endpoint_private_key))
246 => {
247 self.state = IdentityServerState::HandshakeComplete;
248 return Ok(Some(IdentityServerEvent::HandshakeCompleted{
249 endpoint_private_key: endpoint_private_key.clone(),
250 endpoint_name: requested_endpoint.clone(),
251 client_service_id: client_identity.clone(),
252 client_auth_public_key: client_auth_key.clone(),
253 }));
254 },
255 (&IdentityServerState::ChallengeVerificationResponseSent,
256 Some(_begin_handshake_request_cookie),
257 Some(_client_identity),
258 Some(_requested_endpoint),
259 Some(_server_cookie),
260 Some(_endpoint_challenge),
261 Some(_send_response_request_cookie),
262 Some(_client_auth_key),
263 Some(_challenge_response),
264 None) => {
266 self.state = IdentityServerState::HandshakeComplete;
267 return Ok(Some(IdentityServerEvent::HandshakeRejected{
268 client_allowed: self.client_allowed,
269 client_requested_endpoint_valid: self.client_requested_endpoint_valid,
270 client_proof_signature_valid: self.client_proof_signature_valid,
271 client_auth_signature_valid: self.client_auth_signature_valid,
272 challenge_response_valid: self.challenge_response_valid,
273 }));
274 },
275 _ => {
276 if self.state == IdentityServerState::HandshakeFailed {
277 return Err(Error::BadClient);
278 } else {
279 return Err(Error::InvalidState(self.get_state()));
280 }
281 }
282 }
283
284 Ok(None)
285 }
286
287 pub fn handle_endpoint_request_received(
288 &mut self,
289 client_allowed: bool,
290 endpoint_valid: bool,
291 endpoint_challenge: bson::document::Document,
292 ) -> Result<(), Error> {
293 match (
294 &self.state,
295 self.rpc.as_ref(),
296 self.begin_handshake_request_cookie,
297 self.client_identity.as_ref(),
298 self.requested_endpoint.as_ref(),
299 self.server_cookie.as_ref(),
300 self.endpoint_challenge.as_ref(),
301 self.client_auth_key.as_ref(),
302 self.challenge_response.as_ref(),
303 self.endpoint_private_key.as_ref(),
304 ) {
305 (
306 &IdentityServerState::GettingChallenge,
307 Some(rpc),
308 Some(_begin_handshake_request_cookie),
309 Some(_client_identity),
310 Some(_endpoint_name),
311 None, None, None, None, None, ) => {
317 let mut server_cookie: ServerCookie = Default::default();
318 OsRng.try_fill_bytes(&mut server_cookie)?;
319
320 let result = doc!{
323 "server_cookie" : Bson::Binary(Binary{subtype: BinarySubtype::Generic, bytes: server_cookie.to_vec()}),
324 "endpoint_challenge" : endpoint_challenge.clone(),
325 };
326 let response_section_size = get_response_section_size(Some(Bson::Document(result)))?;
327 let message_size = get_message_overhead()? + response_section_size;
328 let max_message_size = rpc.get_max_message_size();
329 if message_size > max_message_size {
330 Err(Error::EndpointChallengeTooLarge(message_size, max_message_size))
331 } else {
332 self.server_cookie = Some(server_cookie);
333 self.endpoint_challenge = Some(endpoint_challenge);
334 self.client_allowed = client_allowed;
335 self.client_requested_endpoint_valid = endpoint_valid;
336 self.state = IdentityServerState::ChallengeReady;
337 Ok(())
338 }
339 }
340 _ => {
341 Err(Error::IncorrectUsage("handle_endpoint_request_received() may only be called after EndpointRequestReceived has been returned from update(), and it may only be called once".to_string()))
342 }
343 }
344 }
345
346 pub fn handle_challenge_response_received(
347 &mut self,
348 challenge_response_valid: bool,
349 ) -> Result<(), Error> {
350 match (
351 &self.state,
352 self.begin_handshake_request_cookie,
353 self.client_identity.as_ref(),
354 self.requested_endpoint.as_ref(),
355 self.server_cookie.as_ref(),
356 self.endpoint_challenge.as_ref(),
357 self.client_auth_key.as_ref(),
358 self.challenge_response.as_ref(),
359 self.endpoint_private_key.as_ref(),
360 ) {
361 (
362 &IdentityServerState::GettingChallengeVerification,
363 Some(_begin_handshake_request_cookie),
364 Some(_client_identity),
365 Some(_requested_endpoint),
366 Some(_server_cookie),
367 Some(_endpoint_challenge),
368 Some(_client_auth_key),
369 Some(_challenge_response),
370 None,
371 ) =>
372 {
374 self.challenge_response_valid = challenge_response_valid;
375 self.state = IdentityServerState::ChallengeVerificationReady;
376 Ok(())
377 }
378 _ => {
379 Err(Error::IncorrectUsage("handle_challenge_response_received() may only be called after ChallengeResponseReceived event has been returned from update(), and it may only be called once".to_string()))
380 }
381 }
382 }
383}
384
385impl ApiSet for IdentityServer {
386 fn namespace(&self) -> &str {
387 "gosling_identity"
388 }
389
390 fn exec_function(
391 &mut self,
392 name: &str,
393 version: i32,
394 mut args: bson::document::Document,
395 request_cookie: Option<RequestCookie>,
396 ) -> Option<Result<Option<bson::Bson>, ErrorCode>> {
397 let request_cookie = match request_cookie {
398 Some(request_cookie) => request_cookie,
399 None => {
400 return Some(Err(ErrorCode::Runtime(
401 RpcError::RequestCookieRequired as i32,
402 )))
403 }
404 };
405
406 match (
407 name,
408 version,
409 &self.state,
410 self.begin_handshake_request_cookie,
411 self.client_identity.as_ref(),
412 self.requested_endpoint.as_ref(),
413 self.server_cookie.as_ref(),
414 self.endpoint_challenge.as_ref(),
415 self.client_auth_key.as_ref(),
416 self.challenge_response.as_ref(),
417 self.endpoint_private_key.as_ref(),
418 ) {
419 (
421 "begin_handshake",
422 0,
423 &IdentityServerState::WaitingForBeginHandshake,
424 None, None, None, None, None, None, None, None, ) => {
433 let valid_version = match args.remove("version") {
434 Some(Bson::String(value)) => value == GOSLING_PROTOCOL_VERSION,
435 _ => false,
436 };
437 if !valid_version {
438 self.state = IdentityServerState::HandshakeFailed;
439 return Some(Err(ErrorCode::Runtime(RpcError::BadVersion as i32)));
440 }
441
442 if let (Some(Bson::String(client_identity)), Some(Bson::String(endpoint_name))) =
443 (args.remove("client_identity"), args.remove("endpoint"))
444 {
445 let client_identity = match V3OnionServiceId::from_string(&client_identity) {
447 Ok(client_identity) => client_identity,
448 Err(_) => {
449 self.state = IdentityServerState::HandshakeFailed;
450 return Some(Err(ErrorCode::Runtime(RpcError::InvalidArg as i32)));
451 }
452 };
453
454 let endpoint_name = match AsciiString::new(endpoint_name) {
456 Ok(endpoint_name) => endpoint_name,
457 Err(_) => {
458 self.state = IdentityServerState::HandshakeFailed;
459 return Some(Err(ErrorCode::Runtime(RpcError::InvalidArg as i32)));
460 }
461 };
462
463 self.begin_handshake_request_cookie = Some(request_cookie);
465
466 self.client_identity = Some(client_identity);
468 self.requested_endpoint = Some(endpoint_name);
469 None
470 } else {
471 self.state = IdentityServerState::HandshakeFailed;
472 Some(Err(ErrorCode::Runtime(RpcError::InvalidArg as i32)))
473 }
474 }
475 (
477 "send_response",
478 0,
479 &IdentityServerState::WaitingForSendResponse,
480 Some(_begin_handshake_request_cookie),
481 Some(client_identity),
482 Some(requested_endpoint),
483 Some(server_cookie),
484 Some(_endpoint_challenge),
485 None, None, None, ) => {
489 if let (
491 Some(Bson::Binary(Binary {
492 subtype: BinarySubtype::Generic,
493 bytes: client_cookie,
494 })),
495 Some(Bson::Binary(Binary {
496 subtype: BinarySubtype::Generic,
497 bytes: client_identity_proof_signature,
498 })),
499 Some(Bson::Binary(Binary {
500 subtype: BinarySubtype::Generic,
501 bytes: client_authorization_key,
502 })),
503 Some(Bson::Boolean(client_authorization_key_signbit)),
504 Some(Bson::Binary(Binary {
505 subtype: BinarySubtype::Generic,
506 bytes: client_authorization_signature,
507 })),
508 Some(Bson::Document(challenge_response)),
509 ) = (
510 args.remove("client_cookie"),
511 args.remove("client_identity_proof_signature"),
512 args.remove("client_authorization_key"),
513 args.remove("client_authorization_key_signbit"),
514 args.remove("client_authorization_signature"),
515 args.remove("challenge_response"),
516 ) {
517 let client_cookie: ClientCookie = match client_cookie.try_into() {
519 Ok(client_cookie) => client_cookie,
520 Err(_) => {
521 self.state = IdentityServerState::HandshakeFailed;
522 return Some(Err(ErrorCode::Runtime(RpcError::InvalidArg as i32)));
523 }
524 };
525
526 let client_identity_proof_signature: [u8; ED25519_SIGNATURE_SIZE] =
528 match client_identity_proof_signature.try_into() {
529 Ok(client_identity_proof_signature) => client_identity_proof_signature,
530 Err(_) => {
531 self.state = IdentityServerState::HandshakeFailed;
532 return Some(Err(ErrorCode::Runtime(RpcError::InvalidArg as i32)));
533 }
534 };
535 let client_identity_proof_signature =
536 match Ed25519Signature::from_raw(&client_identity_proof_signature) {
537 Ok(client_identity_proof_signature) => client_identity_proof_signature,
538 Err(_) => {
539 self.state = IdentityServerState::HandshakeFailed;
540 return Some(Err(ErrorCode::Runtime(RpcError::InvalidArg as i32)));
541 }
542 };
543
544 let client_authorization_key: [u8; X25519_PUBLIC_KEY_SIZE] =
546 match client_authorization_key.try_into() {
547 Ok(client_authorization_key) => client_authorization_key,
548 Err(_) => {
549 self.state = IdentityServerState::HandshakeFailed;
550 return Some(Err(ErrorCode::Runtime(RpcError::InvalidArg as i32)));
551 }
552 };
553 let client_authorization_key =
554 X25519PublicKey::from_raw(&client_authorization_key);
555
556 let client_authorization_key_signbit: SignBit =
558 client_authorization_key_signbit.into();
559
560 let client_authorization_signature: [u8; ED25519_SIGNATURE_SIZE] =
562 match client_authorization_signature.try_into() {
563 Ok(client_authorization_signature) => client_authorization_signature,
564 Err(_) => {
565 self.state = IdentityServerState::HandshakeFailed;
566 return Some(Err(ErrorCode::Runtime(RpcError::InvalidArg as i32)));
567 }
568 };
569 let client_authorization_signature =
570 match Ed25519Signature::from_raw(&client_authorization_signature) {
571 Ok(client_authorization_signature) => client_authorization_signature,
572 Err(_) => {
573 self.state = IdentityServerState::HandshakeFailed;
574 return Some(Err(ErrorCode::Runtime(RpcError::InvalidArg as i32)));
575 }
576 };
577
578 self.send_response_request_cookie = Some(request_cookie);
580
581 if let Ok(client_identity_key) =
583 Ed25519PublicKey::from_service_id(client_identity)
584 {
585 let client_proof = build_client_proof(
587 DomainSeparator::GoslingIdentity,
588 requested_endpoint,
589 client_identity,
590 &self.server_identity,
591 &client_cookie,
592 server_cookie,
593 );
594 self.client_proof_signature_valid = client_identity_proof_signature
595 .verify(&client_proof, &client_identity_key);
596 }
597
598 self.client_auth_signature_valid = client_authorization_signature
600 .verify_x25519(
601 client_identity.as_bytes(),
602 &client_authorization_key,
603 client_authorization_key_signbit,
604 );
605
606 self.client_auth_key = Some(client_authorization_key);
608
609 self.challenge_response = Some(challenge_response);
611
612 None
613 } else {
614 self.state = IdentityServerState::HandshakeFailed;
615 Some(Err(ErrorCode::Runtime(RpcError::InvalidArg as i32)))
616 }
617 }
618 _ => {
619 self.state = IdentityServerState::HandshakeFailed;
620 Some(Err(ErrorCode::Runtime(RpcError::Failure as i32)))
621 }
622 }
623 }
624
625 fn next_result(&mut self) -> Option<(RequestCookie, Result<Option<bson::Bson>, ErrorCode>)> {
626 match (
627 &self.state,
628 self.begin_handshake_request_cookie,
629 self.client_identity.as_ref(),
630 self.requested_endpoint.as_ref(),
631 self.server_cookie.as_ref(),
632 self.endpoint_challenge.as_mut(),
633 self.send_response_request_cookie,
634 self.client_auth_key.as_ref(),
635 self.challenge_response.as_ref(),
636 ) {
637 (
639 &IdentityServerState::ChallengeReady,
640 Some(begin_handshake_request_cookie),
641 Some(_client_identity),
642 Some(_requested_endpoint),
643 Some(server_cookie),
644 Some(endpoint_challenge),
645 None, None, None,
648 ) =>
649 {
651 self.state = IdentityServerState::WaitingForSendResponse;
652 Some((
653 begin_handshake_request_cookie,
654 Ok(Some(Bson::Document(doc! {
655 "server_cookie" : Bson::Binary(Binary{subtype: BinarySubtype::Generic, bytes: server_cookie.to_vec()}),
656 "endpoint_challenge" : std::mem::take(endpoint_challenge),
657 }))),
658 ))
659 }
660 (&IdentityServerState::ChallengeReady, _, _, _, _, _, _, _, _) => unreachable!(),
661 (
662 &IdentityServerState::ChallengeVerificationReady,
663 Some(_begin_handshake_request_cookie),
664 Some(_client_identity),
665 Some(_requested_endpoint),
666 Some(_server_cookie),
667 Some(_endpoint_challenge),
668 Some(send_response_request_cookie),
669 Some(_client_auth_key),
670 Some(_challenge_response),
671 ) => {
672 let mut success = true;
673 success &= self.client_allowed;
674 success &= self.client_requested_endpoint_valid;
675 success &= self.client_proof_signature_valid;
676 success &= self.client_auth_signature_valid;
677 success &= self.challenge_response_valid;
678
679 self.state = IdentityServerState::ChallengeVerificationResponseSent;
680 if success {
681 let endpoint_private_key = Ed25519PrivateKey::generate();
682 let endpoint_service_id =
683 V3OnionServiceId::from_private_key(&endpoint_private_key);
684 self.endpoint_private_key = Some(endpoint_private_key);
685 Some((
686 send_response_request_cookie,
687 Ok(Some(Bson::String(endpoint_service_id.to_string()))),
688 ))
689 } else {
690 Some((
691 send_response_request_cookie,
692 Err(ErrorCode::Runtime(RpcError::Failure as i32)),
693 ))
694 }
695 }
696 _ => None,
697 }
698 }
699}