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