gosling/
context.rs

1// standard
2use std::clone::Clone;
3use std::collections::{BTreeMap, HashMap, VecDeque};
4use std::net::TcpStream;
5use std::time::Duration;
6
7// extern crates
8use honk_rpc::honk_rpc::*;
9use tor_interface::tor_crypto::*;
10use tor_interface::tor_provider::*;
11
12// internal crates
13use crate::ascii_string::*;
14use crate::endpoint_client;
15use crate::endpoint_client::*;
16use crate::endpoint_server;
17use crate::endpoint_server::*;
18use crate::identity_client;
19use crate::identity_client::*;
20use crate::identity_server;
21use crate::identity_server::*;
22
23/// A handle to an in-progres identity or endpoint handshake
24pub type HandshakeHandle = usize;
25const DEFAULT_ENDPOINT_TIMEOUT: Duration = Duration::from_secs(60);
26const DEFAULT_ENDPOINT_MAX_MESSAGE_SIZE: i32 = 384;
27
28/// The error type for the [`Context`] type.
29#[derive(thiserror::Error, Debug)]
30pub enum Error {
31    /// An invalid argument was provided to a function
32    #[error("invalid argument: {0}")]
33    InvalidArgument(String),
34
35    /// Function requiring tor connectivity called before bootstrap
36    #[error(
37        "context is not connected, must call bootstrap() and wait for TorBootstrapCompleted event"
38    )]
39    TorNotConnected(),
40
41    /// Provided handle does not map to an in-flight handshake
42    #[error("handshake handle {0} not found")]
43    HandshakeHandleNotFound(HandshakeHandle),
44
45    /// Requesting an invalid operation
46    #[error("incorrect usage: {0}")]
47    IncorrectUsage(String),
48
49    /// An underlying `std::io::Error`
50    #[error(transparent)]
51    Io(#[from] std::io::Error),
52
53    /// An underlying `honk_rpc::honk_rpc::Error`
54    #[error(transparent)]
55    HonkRpc(#[from] honk_rpc::honk_rpc::Error),
56
57    /// An underlying `tor_interface::tor_crypto::Error`
58    #[error(transparent)]
59    TorCrypto(#[from] tor_interface::tor_crypto::Error),
60
61    /// An underlying `tor_interface::tor_provider::Error`
62    #[error(transparent)]
63    TorProvider(#[from] tor_interface::tor_provider::Error),
64
65    /// Failure ocurred in outgoing identity handshake
66    #[error(transparent)]
67    IdentityClientError(#[from] identity_client::Error),
68
69    /// Failure ocurred in incoming identity handshake
70    #[error(transparent)]
71    IdentityServerError(#[from] identity_server::Error),
72
73    /// Failure ocurred in outgoing endpoint handshake
74    #[error(transparent)]
75    EndpointClientError(#[from] endpoint_client::Error),
76
77    /// Failure ocurred in incoming endpoint handshake
78    #[error(transparent)]
79    EndpointServerError(#[from] endpoint_server::Error),
80}
81
82/// The gosling protocol implementation.
83///
84/// The `Context` object provides various methods for starting and progressing identity and endpoint handshakes. The general usage pattern developers will follow is to construct a `Context` object, connect to the Tor Network using [`Context::bootstrap()`], optionally start an identity or endpoint servers, and listen for and handle incoming identity and endpoint clients using [`Context::update()`] and the various associated methods. Depending on the application's requirements, the developer can also initiate identity and endpoint handshakes as necessary.
85///
86/// The Gosling Protocol specification can be found here:
87/// - [https://gosling.technology/gosling-spec.xhtml](https://gosling.technology/gosling-spec.xhtml)
88pub struct Context {
89    // our tor instance
90    tor_provider: Box<dyn TorProvider>,
91    bootstrap_complete: bool,
92    identity_port: u16,
93    endpoint_port: u16,
94    identity_timeout: Duration,
95    identity_max_message_size: i32,
96    endpoint_timeout: Duration,
97
98    //
99    // Servers and Clients for in-process handshakes
100    //
101    next_handshake_handle: HandshakeHandle,
102    identity_clients: BTreeMap<HandshakeHandle, IdentityClient>,
103    identity_servers: BTreeMap<HandshakeHandle, IdentityServer>,
104    endpoint_clients: BTreeMap<HandshakeHandle, EndpointClient>,
105    endpoint_servers: BTreeMap<HandshakeHandle, EndpointServer>,
106
107    //
108    // Listeners for incoming connections
109    //
110    identity_listener: Option<OnionListener>,
111    identity_server_published: bool,
112    // maps the endpoint service id to the (enpdoint name, alowed client, onion listener tuple, published)
113    endpoint_listeners: HashMap<V3OnionServiceId, (String, V3OnionServiceId, OnionListener, bool)>,
114
115    //
116    // Server Config Data
117    //
118
119    // Private key behind the identity onion service
120    identity_private_key: Ed25519PrivateKey,
121    // Identity server's service id
122    identity_service_id: V3OnionServiceId,
123}
124
125/// Events to signal completion of asynchronous [`Context`] operations
126#[derive(Debug)]
127pub enum ContextEvent {
128    //
129    // Tor Events
130    //
131
132    /// Tor bootstrap progress
133    TorBootstrapStatusReceived {
134        /// Bootstrap percent compeletion
135        progress: u32,
136        /// A short string indicating the completed bootstrap step
137        tag: String,
138        /// A longer human-readable summary of the bootstrap progress
139        summary: String,
140    },
141
142    /// Tor bootstrap completed
143    TorBootstrapCompleted,
144
145    /// Human-readable logs from the [`Context`]'s [`TorProvider`]
146    TorLogReceived {
147        /// Human-readable debug log
148        line: String,
149    },
150
151    //
152    // Identity Client Events
153    //
154
155    /// An identity client has received a challenge request from an identy server
156    ///
157    /// To continue the handshake, the client must call [`Context::identity_client_handle_challenge_received()`]
158    IdentityClientChallengeReceived {
159        /// The handle of the in-progress handshake
160        handle: HandshakeHandle,
161        /// An application specific challenge object used by the identity client to create a challenge response object
162        endpoint_challenge: bson::document::Document,
163    },
164
165    /// An identity client has successfully completed an identity handshake and may now access the requested endpoint server.
166    IdentityClientHandshakeCompleted {
167        /// The handle of the completed handshake
168        handle: HandshakeHandle,
169        /// The onion-service service-id of the identity server the client has completed an identity handshake with
170        identity_service_id: V3OnionServiceId,
171        /// The onion-service service-id of the requested endpoint server
172        endpoint_service_id: V3OnionServiceId,
173        /// The ASCII-encoded name of the requested endpoint server
174        endpoint_name: String,
175        /// The private x25519 client-auth key required to access the requested endpoint server
176        client_auth_private_key: X25519PrivateKey,
177    },
178
179    /// An incoming identit handshake has failed
180    IdentityClientHandshakeFailed {
181        /// The handle of the failed handshake
182        handle: HandshakeHandle,
183        /// The failure reason
184        reason: Error,
185    },
186
187    /// The identity server's onion-service has been published and may be reachable by identity clients
188    IdentityServerPublished,
189
190    /// An identity server has received an incoming connection and the handshake is ready to begin
191    IdentityServerHandshakeStarted {
192        /// The handle of the new handshake
193        handle: HandshakeHandle,
194    },
195
196    /// An identity server has received a request for an endpoint from an identity client.
197    ///
198    /// To continue the handshake, the server must call [`Context::identity_server_handle_endpoint_request_received()`]
199    IdentityServerEndpointRequestReceived {
200        /// The handle of the in-progress handshake
201        handle: HandshakeHandle,
202        /// The alleged onion-service service-id of the connecting client
203        client_service_id: V3OnionServiceId,
204        /// The ASCII-encoded name of the requested endpoint server
205        requested_endpoint: String,
206    },
207
208    /// An identity server has received a challenge response from an identity client.
209    ///
210    /// To continue the handshake, the server must call [`Context::identity_server_handle_challenge_response_received()`]
211    IdentityServerChallengeResponseReceived {
212        /// The handle of the in-progress handshake
213        handle: HandshakeHandle,
214        /// An application specific challenge response object created by the identity client in response to the identity server's challenge object
215        challenge_response: bson::document::Document,
216    },
217
218    /// An identity server's handshake has completed.
219    IdentityServerHandshakeCompleted {
220        /// The handle of the completed handshake
221        handle: HandshakeHandle,
222        /// The ed25519 private key of requested endpoint server
223        endpoint_private_key: Ed25519PrivateKey,
224        /// The ASCII-encoded name of the requested endpoint server
225        endpoint_name: String,
226        /// The onion-service service-id of the authenticated client
227        client_service_id: V3OnionServiceId,
228        /// The public x25519 client-auth key used to encrypt the endpoint server's onion-service descriptor
229        client_auth_public_key: X25519PublicKey,
230    },
231
232    /// An identity server has rejected an identity client's endpoint-request.
233    ///
234    /// There are multiple potential reasons why a handshake may be rejected and this event provides a breakdown on which part(s) failed specifically.
235    IdentityServerHandshakeRejected {
236        /// The handle of the rejected handshake
237        handle: HandshakeHandle,
238        /// `false` if the client was rejected based on their onion-service service-id
239        client_allowed: bool,
240        /// `false` if the requested endpoint name was not understood by the server
241        client_requested_endpoint_valid: bool,
242        /// `false` if the client failed its authentication proof (i.e. potential attempt at identity client impersonation)
243        client_proof_signature_valid: bool,
244        /// `false` if the client fails its x25519 key-ownership proof (i.e. potential attempt at use an x25519 public key not owned by the client)
245        client_auth_signature_valid: bool,
246        /// `false` if the client's challenge response was not suitable
247        challenge_response_valid: bool,
248    },
249
250    /// An incoming identity handshake has failed.
251    IdentityServerHandshakeFailed {
252        /// The handle of the failed handshake
253        handle: HandshakeHandle,
254        /// The failure reason
255        reason: Error,
256    },
257
258    //
259    // Endpoint Client Events
260    //
261
262    /// An endpoint client has successfully completed an endpoint handshake and may now communicate freely with the endpoint server.
263    EndpointClientHandshakeCompleted {
264        /// The handle of the completed handshake
265        handle: HandshakeHandle,
266        /// The onion-service service-id of the endpoint server the client has connected to
267        endpoint_service_id: V3OnionServiceId,
268        /// The ASCII-encoded name of the requested channel on the endpoint server
269        channel_name: String,
270        /// The resulting TCP connection to the endpoint server
271        stream: TcpStream,
272    },
273
274    /// An outgoing endpoint handshake has failed.
275    EndpointClientHandshakeFailed {
276        /// The handle of the failed handshake
277        handle: HandshakeHandle,
278        /// The failure reason
279        reason: Error,
280    },
281
282    //
283    // Endpint Server Events
284    //
285
286    /// The endpoint server’s onion-service has been published and may be reachable by endpoint clients.
287    EndpointServerPublished {
288        /// The onion-service service-id of the published endpoint server
289        endpoint_service_id: V3OnionServiceId,
290        /// The name of the published endpoint server
291        endpoint_name: String,
292    },
293
294    /// An endpoint server has received an incoming connection and the handshake is ready to begin.
295    EndpointServerHandshakeStarted {
296        /// The handle of the new handshake
297        handle: HandshakeHandle,
298    },
299
300    /// An endpoint server has received a request for a channel from an endpoint client.
301    ///
302    /// To continue the handshake, the server must call [`Context::endpoint_server_handle_channel_request_received()`]
303    EndpointServerChannelRequestReceived {
304        /// The handle of the in-progress handshake
305        handle: HandshakeHandle,
306        /// The alleged onion-service service-id of the connecting client
307        client_service_id: V3OnionServiceId,
308        /// The ASCII-encoded name of the requested channel
309        requested_channel: String,
310    },
311
312    /// An endpoint server's handshake has completed
313    EndpointServerHandshakeCompleted {
314        /// The handle of the completed handshake
315        handle: HandshakeHandle,
316        /// The onion-service service-id of the endpoint server which an endpoint client has connected to
317        endpoint_service_id: V3OnionServiceId,
318        /// The onion-service service-id of the connected client
319        client_service_id: V3OnionServiceId,
320        /// The ASCII-encoded name of the client's requested channel
321        channel_name: String,
322        /// The resulting TCP connection to tohe endpoint clientt
323        stream: TcpStream,
324    },
325
326    /// An endpoint server has rejected an endpoint client's channel request.
327    ///
328    /// There are multiple potential reasons why a handshake may be rejected and this event provides a breakdown on which part(s) failed specifically.
329    EndpointServerHandshakeRejected {
330        /// The handle of the rejected handshake
331        handle: HandshakeHandle,
332        /// `false` if the client was rejected based on their onion-service service-id
333        client_allowed: bool,
334        /// `false` if the requested channel name was not understood by the server
335        client_requested_channel_valid: bool,
336        /// `false` if the client failed its authentication proof (i.e. potential attempt at endpoint client impersonation)
337        client_proof_signature_valid: bool,
338    },
339
340    /// An incoming endpoint handshake has failed.
341    EndpointServerHandshakeFailed {
342        /// The handle of the failed handshake
343        handle: HandshakeHandle,
344        /// The failure reason
345        reason: Error,
346    },
347}
348
349impl Context {
350    /// Construct a new `Context` object.
351    ///
352    /// # Parameters
353    /// - `tor_provider`: an implementation of the [`TorProvider`] trait which provides our Tor Network connectivity
354    /// - `identity_port`: the virt-port this `Context`'s identity server's onion-service will listen on for new identity handshakes.
355    /// - `endpoint_port`: the virt-port this `Context`'s endpoint servers' onion-services will listen on for new endpoint handshakes.
356    /// - `identity_timeout`: the maximum amount of time this `Context`' will allow an identity handshake to delay between steps before rejecting the request.
357    /// - `identity_max_message_size`: the maximum size of the underlying Honk-RPC BSON message this `Context`'s identity handshake will send and accept.
358    /// - `endpoint_timeout`: the maximum amount of time this `Context`' will allow an endpoint handshake to delay between steps before rejecting the request.
359    /// - `identity_private_key`: the ed25519 private key used to start this `Context`'s identity server's onion-service
360    /// # Returns
361    /// A newly constructed `Context`.
362    pub fn new(
363        tor_provider: Box<dyn TorProvider>,
364        identity_port: u16,
365        endpoint_port: u16,
366        identity_timeout: Duration,
367        identity_max_message_size: i32,
368        endpoint_timeout: Option<Duration>,
369        identity_private_key: Ed25519PrivateKey,
370    ) -> Result<Self, Error> {
371        let identity_service_id = V3OnionServiceId::from_private_key(&identity_private_key);
372
373        Ok(Self {
374            tor_provider,
375            bootstrap_complete: false,
376            identity_port,
377            identity_max_message_size,
378            endpoint_port,
379            identity_timeout,
380            endpoint_timeout: match endpoint_timeout {
381                Some(timeout) => timeout,
382                None => DEFAULT_ENDPOINT_TIMEOUT,
383            },
384
385            next_handshake_handle: Default::default(),
386            identity_clients: Default::default(),
387            identity_servers: Default::default(),
388            endpoint_clients: Default::default(),
389            endpoint_servers: Default::default(),
390
391            identity_listener: None,
392            identity_server_published: false,
393            endpoint_listeners: Default::default(),
394
395            identity_private_key,
396            identity_service_id,
397        })
398    }
399
400    /// Initiate bootstrap of the `Context`'s owned [`TorProvider`]. Bootstrap status is communicated through [`ContextEvent`]s returned from the [`Context::update()`] method.
401    pub fn bootstrap(&mut self) -> Result<(), Error> {
402        self.tor_provider.bootstrap()?;
403        Ok(())
404    }
405
406    /// Initiate an identity handshake with an identity server. Handshake progression is communicated through  [`ContextEvent`]s returned from the [`Context::update()`] method.
407    ///
408    /// # Parameters
409    /// - `identitity_server_id`: the long term identity onion-service service-id of a remote peer
410    /// - `endpoint`: the ASCII-encoded requested endpoint
411    /// # Returns
412    /// A `HandshakeHandle` used to refer to this particular identity handshake.
413    pub fn identity_client_begin_handshake(
414        &mut self,
415        identity_server_id: V3OnionServiceId,
416        endpoint: String,
417    ) -> Result<HandshakeHandle, Error> {
418        let endpoint = match AsciiString::new(endpoint) {
419            Ok(endpoint) => endpoint,
420            Err(_) => {
421                return Err(Error::InvalidArgument(
422                    "endpoint must be an ASCII string".to_string(),
423                ))
424            }
425        };
426
427        if !self.bootstrap_complete {
428            return Err(Error::TorNotConnected());
429        }
430
431        // open tcp stream to remove ident server
432        let stream: TcpStream = self
433            .tor_provider
434            .connect(
435                (identity_server_id.clone(), self.identity_port).into(),
436                None,
437            )?
438            .into();
439        stream.set_nonblocking(true)?;
440        let mut client_rpc = Session::new(stream);
441        client_rpc.set_max_wait_time(self.identity_timeout);
442        client_rpc.set_max_message_size(self.identity_max_message_size)?;
443
444        let ident_client = IdentityClient::new(
445            client_rpc,
446            identity_server_id,
447            endpoint,
448            self.identity_private_key.clone(),
449            X25519PrivateKey::generate(),
450        )?;
451
452        let handshake_handle = self.next_handshake_handle;
453        self.next_handshake_handle += 1;
454        self.identity_clients.insert(handshake_handle, ident_client);
455
456        Ok(handshake_handle)
457    }
458
459    /// Abort an in-process outgoing identity handshake.
460    ///
461    /// # Parameters
462    /// - `handle`: the handle of the in-progress outoing identity handshake to abort
463    pub fn identity_client_abort_handshake(
464        &mut self,
465        handle: HandshakeHandle,
466    ) -> Result<(), Error> {
467        if let Some(_identity_client) = self.identity_clients.remove(&handle) {
468            Ok(())
469        } else {
470            Err(Error::HandshakeHandleNotFound(handle))
471        }
472    }
473
474    /// Handle an identity server's endpoint challenge. Callers must construct an identity client's endpoint challenge-response. The particulars of creating and verifying the challenge-response BSON documents are undefined and application-specific.
475    ///
476    /// # Parameters
477    /// - `handle`: the handle of the in-progress outgoing identity handshake
478    /// - `challenge_response`: an application-specific BSON document which somehow responds to an identity server's challenge.
479    pub fn identity_client_handle_challenge_received(
480        &mut self,
481        handle: HandshakeHandle,
482        challenge_response: bson::document::Document,
483    ) -> Result<(), Error> {
484        if let Some(identity_client) = self.identity_clients.get_mut(&handle) {
485            identity_client.send_response(challenge_response)?;
486            Ok(())
487        } else {
488            Err(Error::HandshakeHandleNotFound(handle))
489        }
490    }
491
492    /// Start this `Context`'s identity server. Publish status is communicated through [`ContextEvent`]s returned from the [`Context::update()`] method.
493    pub fn identity_server_start(&mut self) -> Result<(), Error> {
494        if !self.bootstrap_complete {
495            return Err(Error::TorNotConnected());
496        }
497        if self.identity_listener.is_some() {
498            return Err(Error::IncorrectUsage(
499                "identity server already started".to_string(),
500            ));
501        }
502
503        let identity_listener =
504            self.tor_provider
505                .listener(&self.identity_private_key, self.identity_port, None)?;
506        identity_listener.set_nonblocking(true)?;
507
508        self.identity_listener = Some(identity_listener);
509        Ok(())
510    }
511
512    /// Stops this `Context`'s identity server and ends any in-progress incoming identity handshakes.
513    pub fn identity_server_stop(&mut self) -> Result<(), Error> {
514        if self.identity_listener.is_none() {
515            return Err(Error::IncorrectUsage(
516                "identity server is not started".to_string(),
517            ));
518        }
519
520        // clear out current identity listener
521        self.identity_listener = None;
522        // clear out published flag
523        self.identity_server_published = false;
524        // clear out any in-process identity handshakes
525        self.identity_servers = Default::default();
526        Ok(())
527    }
528
529    /// Handle an identity client's incoming endpoint request. Callers must determine whether the connected identity client is allowed to access the requested endpoint, decide whether the requested endpoint is supported by this `Context`, and build an endpoint challenge for the identity client. The particulars of creating the endpoint challenge is undefined and application-specific.
530    ///
531    /// # Parameters
532    /// - `handle`: the handle of the in-progress incoming identity handshake
533    /// - `client_allowed`: whether the connected identity client is allowed to access the requested endpoint
534    /// - `endpoint_supported`: whether the requested endpoint is supported
535    /// - `endpoint_challenge`: an application-specific BSON document which the connected identity client must respond to
536    pub fn identity_server_handle_endpoint_request_received(
537        &mut self,
538        handle: HandshakeHandle,
539        client_allowed: bool,
540        endpoint_supported: bool,
541        endpoint_challenge: bson::document::Document,
542    ) -> Result<(), Error> {
543        if let Some(identity_server) = self.identity_servers.get_mut(&handle) {
544            Ok(identity_server.handle_endpoint_request_received(
545                client_allowed,
546                endpoint_supported,
547                endpoint_challenge,
548            )?)
549        } else {
550            Err(Error::HandshakeHandleNotFound(handle))
551        }
552    }
553
554    // confirm that a received endpoint challenge response is valid
555
556    /// Handle an identity client's incoming endpoint challenge-response. Callers must determine whether the connected identity client's challenge-response is valid. The particulars of verifying the challenge-response is undefined and application-specific.
557    ///
558    /// # Parameters
559    /// - `handle`: the handle of the in-progress incoming identity handshake
560    /// - `challenge_response_valid`: whether the received challenge-response is valid
561    pub fn identity_server_handle_challenge_response_received(
562        &mut self,
563        handle: HandshakeHandle,
564        challenge_response_valid: bool,
565    ) -> Result<(), Error> {
566        if let Some(identity_server) = self.identity_servers.get_mut(&handle) {
567            Ok(identity_server.handle_challenge_response_received(challenge_response_valid)?)
568        } else {
569            Err(Error::HandshakeHandleNotFound(handle))
570        }
571    }
572
573    /// Initiate an endpoint handshake with an identity server. An endpoint client acquires the `endpoint_server_id` and `client_auth_key` by completing an identity handshake or through some other side-channnel. Handshake progression is communicated through [`ContextEvent`]s returned from the [`Context::update()`] method.
574    ///
575    /// # Parameters
576    /// - `endpoint_server_id`: the endpoint onion-service service-id of a remote peer
577    /// - `client_uath_key`: the x25519 private-key required to decrypt the endpoint server's onion-service descriptor
578    /// - `channel`: the ASCII-encoded requested channel
579    pub fn endpoint_client_begin_handshake(
580        &mut self,
581        endpoint_server_id: V3OnionServiceId,
582        client_auth_key: X25519PrivateKey,
583        channel: String,
584    ) -> Result<HandshakeHandle, Error> {
585        let channel = match AsciiString::new(channel) {
586            Ok(channel) => channel,
587            Err(_) => {
588                return Err(Error::InvalidArgument(
589                    "channel must be an ASCII string".to_string(),
590                ))
591            }
592        };
593
594        if !self.bootstrap_complete {
595            return Err(Error::TorNotConnected());
596        }
597
598        self.tor_provider
599            .add_client_auth(&endpoint_server_id, &client_auth_key)?;
600        let stream: TcpStream = self
601            .tor_provider
602            .connect(
603                (endpoint_server_id.clone(), self.endpoint_port).into(),
604                None,
605            )?
606            .into();
607        stream.set_nonblocking(true)?;
608
609        let mut session = Session::new(stream);
610        session.set_max_wait_time(self.endpoint_timeout);
611        session.set_max_message_size(DEFAULT_ENDPOINT_MAX_MESSAGE_SIZE)?;
612
613        let endpoint_client = EndpointClient::new(
614            session,
615            endpoint_server_id,
616            channel,
617            self.identity_private_key.clone(),
618        );
619
620        let handshake_handle = self.next_handshake_handle;
621        self.next_handshake_handle += 1;
622        self.endpoint_clients
623            .insert(handshake_handle, endpoint_client);
624        Ok(handshake_handle)
625    }
626
627    /// Abort an in-process outgoing endpoint handshake
628    ///
629    /// # Parameters
630    /// - `handle`: the handle of the in-progress outoing identity handshake to abort
631    pub fn endpoint_client_abort_handshake(
632        &mut self,
633        handle: HandshakeHandle,
634    ) -> Result<(), Error> {
635        if let Some(_endpoint_client) = self.endpoint_clients.remove(&handle) {
636            Ok(())
637        } else {
638            Err(Error::HandshakeHandleNotFound(handle))
639        }
640    }
641
642    /// Start one of this `Context`'s endpoint servers. Publish status is communicated through [`ContextEvent`]s returned from the [`Context::update()`] method.
643    ///
644    /// # Parameters
645    /// - `endpoint_private_key`: the ed25519 private key used to start this endpoint server's onion-service
646    /// - `endpoint_name`: the ASCII-encoded endpoint name
647    /// - `client_identity`: the onion-service service-id of the client which will be connecting to this endpoint server
648    /// - `client_auth`: the x25519 public-key used to encrypt the endpoint server's onion-service descriptor
649    pub fn endpoint_server_start(
650        &mut self,
651        endpoint_private_key: Ed25519PrivateKey,
652        endpoint_name: String,
653        client_identity: V3OnionServiceId,
654        client_auth: X25519PublicKey,
655    ) -> Result<(), Error> {
656        if !self.bootstrap_complete {
657            return Err(Error::TorNotConnected());
658        }
659
660        let endpoint_public_key = Ed25519PublicKey::from_private_key(&endpoint_private_key);
661        let endpoint_service_id = V3OnionServiceId::from_public_key(&endpoint_public_key);
662
663        if endpoint_service_id == self.identity_service_id {
664            return Err(Error::InvalidArgument(
665                "endpoint server must be different from identity server".to_string(),
666            ));
667        }
668
669        if self.endpoint_listeners.contains_key(&endpoint_service_id) {
670            return Err(Error::IncorrectUsage(
671                "endpoint server already started".to_string(),
672            ));
673        }
674
675        let endpoint_listener = self.tor_provider.listener(
676            &endpoint_private_key,
677            self.endpoint_port,
678            Some(&[client_auth]),
679        )?;
680        endpoint_listener.set_nonblocking(true)?;
681
682        self.endpoint_listeners.insert(
683            endpoint_service_id,
684            (endpoint_name, client_identity, endpoint_listener, false),
685        );
686        Ok(())
687    }
688
689    /// Handle an endpoint client's incoming channel request. Callers must determine whether the requested channel is supported by this `Context`. The particulars of making this determination is undefined and application-specific.
690    ///
691    /// # Parameters
692    /// - `handle`: the handle of the in-progress incoming endpoint handshake
693    /// - `channel_supported`: whether the requested channel is supported
694    pub fn endpoint_server_handle_channel_request_received(
695        &mut self,
696        handle: HandshakeHandle,
697        channel_supported: bool,
698    ) -> Result<(), Error> {
699        if let Some(endpoint_server) = self.endpoint_servers.get_mut(&handle) {
700            Ok(endpoint_server.handle_channel_request_received(channel_supported)?)
701        } else {
702            Err(Error::HandshakeHandleNotFound(handle))
703        }
704    }
705
706    /// Stop one of this `Context`'s endpoint servers and ends any of its in-progress incoming endpoint handshakes.
707    ///
708    /// # Parameters
709    /// - `endpoint_identity`: the onion-service service-id of the enpdoint server to stop
710    pub fn endpoint_server_stop(
711        &mut self,
712        endpoint_identity: V3OnionServiceId,
713    ) -> Result<(), Error> {
714        if !self.bootstrap_complete {
715            return Err(Error::TorNotConnected());
716        }
717
718        if let Some(_listener) = self.endpoint_listeners.remove(&endpoint_identity) {
719            Ok(())
720        } else {
721            Err(Error::InvalidArgument(format!(
722                "endpoint server with service id {} not found",
723                endpoint_identity
724            )))
725        }
726    }
727
728    fn identity_server_handle_accept(
729        identity_listener: &OnionListener,
730        identity_timeout: Duration,
731        identity_max_message_size: i32,
732        identity_private_key: &Ed25519PrivateKey,
733    ) -> Result<Option<IdentityServer>, Error> {
734        if let Some(stream) = identity_listener.accept()? {
735            let stream: TcpStream = stream.into();
736            if stream.set_nonblocking(true).is_err() {
737                return Ok(None);
738            }
739
740            let mut server_rpc = Session::new(stream);
741            server_rpc.set_max_wait_time(identity_timeout);
742            server_rpc.set_max_message_size(identity_max_message_size)?;
743            let service_id = V3OnionServiceId::from_private_key(identity_private_key);
744            let identity_server = IdentityServer::new(server_rpc, service_id);
745
746            Ok(Some(identity_server))
747        } else {
748            Ok(None)
749        }
750    }
751
752    fn endpoint_server_handle_accept(
753        endpoint_listener: &OnionListener,
754        endpoint_timeout: Duration,
755        client_service_id: &V3OnionServiceId,
756        endpoint_service_id: &V3OnionServiceId,
757    ) -> Result<Option<EndpointServer>, Error> {
758        if let Some(stream) = endpoint_listener.accept()? {
759            let stream: TcpStream = stream.into();
760            if stream.set_nonblocking(true).is_err() {
761                return Ok(None);
762            }
763
764            let mut server_rpc = Session::new(stream);
765            server_rpc.set_max_wait_time(endpoint_timeout);
766            server_rpc.set_max_message_size(DEFAULT_ENDPOINT_MAX_MESSAGE_SIZE)?;
767
768            let endpoint_server = EndpointServer::new(
769                server_rpc,
770                client_service_id.clone(),
771                endpoint_service_id.clone(),
772            );
773
774            Ok(Some(endpoint_server))
775        } else {
776            Ok(None)
777        }
778    }
779
780    /// A direct pass-through to the underlying [`TorProvider`]'s [`TorProvider::connect()`] method.
781    pub fn connect(
782        &mut self,
783        target_addr: TargetAddr,
784        circuit_token: Option<CircuitToken>,
785    ) -> Result<OnionStream, Error> {
786        Ok(self.tor_provider.connect(target_addr, circuit_token)?)
787    }
788
789    /// A direct pass-through to the underlying [`TorProvider`]'s [`TorProvider::generate_token()`] method.
790    pub fn generate_circuit_token(&mut self) -> CircuitToken {
791        self.tor_provider.generate_token()
792    }
793
794    /// A direct pass-through to the underlying [`TorProvider`]'s [`TorProvider::release_token()`] method.
795    pub fn release_circuit_token(&mut self, circuit_token: CircuitToken) {
796        self.tor_provider.release_token(circuit_token)
797    }
798
799    /// This function updates the `Context`'s underlying [`TorProvider`], handles new handshakes requests, and updates in-progress handshakes. This function needs to be regularly called to process the returned [`ContextEvent`]s.
800    pub fn update(&mut self) -> Result<VecDeque<ContextEvent>, Error> {
801        // events to return
802        let mut events: VecDeque<ContextEvent> = Default::default();
803
804        // first handle new identity connections
805        if let Some(identity_listener) = &self.identity_listener {
806            match Self::identity_server_handle_accept(
807                identity_listener,
808                self.identity_timeout,
809                self.identity_max_message_size,
810                &self.identity_private_key,
811            ) {
812                Ok(Some(identity_server)) => {
813                    let handle = self.next_handshake_handle;
814                    self.next_handshake_handle += 1;
815                    self.identity_servers.insert(handle, identity_server);
816                    events.push_back(ContextEvent::IdentityServerHandshakeStarted { handle });
817                }
818                Ok(None) => {}
819                // identity listener failed, remove it
820                // TODO: signal caller identity listener is down
821                Err(_) => self.identity_listener = None,
822            }
823        }
824
825        // next handle new endpoint connections
826        self.endpoint_listeners.retain(
827            |endpoint_service_id, (_endpoint_name, allowed_client, listener, _published)| -> bool {
828                match Self::endpoint_server_handle_accept(
829                    listener,
830                    self.endpoint_timeout,
831                    allowed_client,
832                    endpoint_service_id,
833                ) {
834                    Ok(Some(endpoint_server)) => {
835                        let handle = self.next_handshake_handle;
836                        self.next_handshake_handle += 1;
837                        self.endpoint_servers.insert(handle, endpoint_server);
838                        events.push_back(ContextEvent::EndpointServerHandshakeStarted { handle });
839                        true
840                    }
841                    Ok(None) => true,
842                    // endpoint listener failed, remove it
843                    // TODO: signal caller endpoint listener is down
844                    Err(_) => false,
845                }
846            },
847        );
848
849        // consume tor events
850        // TODO: so curently the only failure mode of this function is a result of the
851        // LegacyTorClient failing; we should probably consider a LegacyTorClient failure fatal, since
852        // reading the LegacyTorClient::update() function it seems the only failure modes are a
853        // failure to DEL_ONION (which realistically speaking could only be due to a logic
854        // error on our part by deleting an onion that doesn't exist, or a parse error of
855        // the response) and a failure to read async events which is either again a parsing
856        // bug on our end or a malformed/buggy tor daemon which we also cannot recover
857        // from.
858        for event in self.tor_provider.update()?.drain(..) {
859            match event {
860                TorEvent::BootstrapStatus {
861                    progress,
862                    tag,
863                    summary,
864                } => {
865                    events.push_back(ContextEvent::TorBootstrapStatusReceived {
866                        progress,
867                        tag,
868                        summary,
869                    });
870                }
871                TorEvent::BootstrapComplete => {
872                    events.push_back(ContextEvent::TorBootstrapCompleted);
873                    self.bootstrap_complete = true;
874                }
875                TorEvent::LogReceived { line } => {
876                    events.push_back(ContextEvent::TorLogReceived { line });
877                }
878                TorEvent::OnionServicePublished { service_id } => {
879                    if service_id == self.identity_service_id {
880                        if !self.identity_server_published {
881                            events.push_back(ContextEvent::IdentityServerPublished);
882                            self.identity_server_published = true;
883                        }
884                    } else if let Some((endpoint_name, _, _, published)) =
885                        self.endpoint_listeners.get_mut(&service_id)
886                    {
887                        // ingore duplicate publish events
888                        if !*published {
889                            events.push_back(ContextEvent::EndpointServerPublished {
890                                endpoint_service_id: service_id,
891                                endpoint_name: endpoint_name.clone(),
892                            });
893                            *published = true;
894                        }
895                    }
896                }
897            }
898        }
899
900        // update the ident client handshakes
901        self.identity_clients
902            .retain(|handle, identity_client| -> bool {
903                let handle = *handle;
904                match identity_client.update() {
905                    Ok(Some(IdentityClientEvent::ChallengeReceived { endpoint_challenge })) => {
906                        events.push_back(ContextEvent::IdentityClientChallengeReceived {
907                            handle,
908                            endpoint_challenge,
909                        });
910                        true
911                    }
912                    Ok(Some(IdentityClientEvent::HandshakeCompleted {
913                        identity_service_id,
914                        endpoint_service_id,
915                        endpoint_name,
916                        client_auth_private_key,
917                    })) => {
918                        events.push_back(ContextEvent::IdentityClientHandshakeCompleted {
919                            handle,
920                            identity_service_id,
921                            endpoint_service_id,
922                            endpoint_name,
923                            client_auth_private_key,
924                        });
925                        false
926                    }
927                    Err(err) => {
928                        events.push_back(ContextEvent::IdentityClientHandshakeFailed {
929                            handle,
930                            reason: err.into(),
931                        });
932                        false
933                    }
934                    Ok(None) => true,
935                }
936            });
937
938        // update the ident server handshakes
939        self.identity_servers
940            .retain(|handle, identity_server| -> bool {
941                let handle = *handle;
942                match identity_server.update() {
943                    Ok(Some(IdentityServerEvent::EndpointRequestReceived {
944                        client_service_id,
945                        requested_endpoint,
946                    })) => {
947                        events.push_back(ContextEvent::IdentityServerEndpointRequestReceived {
948                            handle,
949                            client_service_id,
950                            requested_endpoint: requested_endpoint.to_string(),
951                        });
952                        true
953                    }
954                    Ok(Some(IdentityServerEvent::ChallengeResponseReceived {
955                        challenge_response,
956                    })) => {
957                        events.push_back(ContextEvent::IdentityServerChallengeResponseReceived {
958                            handle,
959                            challenge_response,
960                        });
961                        true
962                    }
963                    Ok(Some(IdentityServerEvent::HandshakeCompleted {
964                        endpoint_private_key,
965                        endpoint_name,
966                        client_service_id,
967                        client_auth_public_key,
968                    })) => {
969                        events.push_back(ContextEvent::IdentityServerHandshakeCompleted {
970                            handle,
971                            endpoint_private_key,
972                            endpoint_name: endpoint_name.to_string(),
973                            client_service_id,
974                            client_auth_public_key,
975                        });
976                        false
977                    }
978                    Ok(Some(IdentityServerEvent::HandshakeRejected {
979                        client_allowed,
980                        client_requested_endpoint_valid,
981                        client_proof_signature_valid,
982                        client_auth_signature_valid,
983                        challenge_response_valid,
984                    })) => {
985                        events.push_back(ContextEvent::IdentityServerHandshakeRejected {
986                            handle,
987                            client_allowed,
988                            client_requested_endpoint_valid,
989                            client_proof_signature_valid,
990                            client_auth_signature_valid,
991                            challenge_response_valid,
992                        });
993                        false
994                    }
995                    Err(err) => {
996                        events.push_back(ContextEvent::IdentityServerHandshakeFailed {
997                            handle,
998                            reason: err.into(),
999                        });
1000                        false
1001                    }
1002                    Ok(None) => true,
1003                }
1004            });
1005
1006        // update the endpoint client handshakes
1007        self.endpoint_clients
1008            .retain(|handle, endpoint_client| -> bool {
1009                let handle = *handle;
1010                match endpoint_client.update() {
1011                    Ok(Some(EndpointClientEvent::HandshakeCompleted { stream })) => {
1012                        events.push_back(ContextEvent::EndpointClientHandshakeCompleted {
1013                            handle,
1014                            endpoint_service_id: endpoint_client.server_service_id.clone(),
1015                            channel_name: endpoint_client.requested_channel.to_string(),
1016                            stream,
1017                        });
1018                        false
1019                    }
1020                    Err(err) => {
1021                        events.push_back(ContextEvent::EndpointClientHandshakeFailed {
1022                            handle,
1023                            reason: err.into(),
1024                        });
1025                        false
1026                    }
1027                    Ok(None) => true,
1028                }
1029            });
1030
1031        // update the endpoint server handshakes
1032        self.endpoint_servers
1033            .retain(|handle, endpoint_server| -> bool {
1034                let handle = *handle;
1035                match endpoint_server.update() {
1036                    Ok(Some(EndpointServerEvent::ChannelRequestReceived {
1037                        requested_channel,
1038                        client_service_id,
1039                    })) => {
1040                        events.push_back(ContextEvent::EndpointServerChannelRequestReceived {
1041                            handle,
1042                            client_service_id,
1043                            requested_channel: requested_channel.to_string(),
1044                        });
1045                        true
1046                    }
1047                    Ok(Some(EndpointServerEvent::HandshakeCompleted {
1048                        client_service_id,
1049                        channel_name,
1050                        stream,
1051                    })) => {
1052                        events.push_back(ContextEvent::EndpointServerHandshakeCompleted {
1053                            handle,
1054                            endpoint_service_id: endpoint_server.server_identity.clone(),
1055                            client_service_id,
1056                            channel_name: channel_name.to_string(),
1057                            stream,
1058                        });
1059                        false
1060                    }
1061                    Ok(Some(EndpointServerEvent::HandshakeRejected {
1062                        client_allowed,
1063                        client_requested_channel_valid,
1064                        client_proof_signature_valid,
1065                    })) => {
1066                        events.push_back(ContextEvent::EndpointServerHandshakeRejected {
1067                            handle,
1068                            client_allowed,
1069                            client_requested_channel_valid,
1070                            client_proof_signature_valid,
1071                        });
1072                        false
1073                    }
1074                    Err(err) => {
1075                        events.push_back(ContextEvent::EndpointServerHandshakeFailed {
1076                            handle,
1077                            reason: err.into(),
1078                        });
1079                        false
1080                    }
1081                    Ok(None) => true,
1082                }
1083            });
1084
1085        Ok(events)
1086    }
1087}