gosling/
endpoint_client.rs

1// standard
2use std::clone::Clone;
3use std::convert::TryInto;
4use std::net::TcpStream;
5
6// extern crates
7use bson::doc;
8use bson::spec::BinarySubtype;
9use bson::{Binary, Bson};
10use honk_rpc::honk_rpc::{RequestCookie, Response, Session};
11use rand::rngs::OsRng;
12use rand::{rand_core, TryRngCore};
13use tor_interface::tor_crypto::*;
14
15// internal crates
16use crate::ascii_string::*;
17use crate::gosling::*;
18
19//
20// Endpoint Client
21//
22
23#[derive(thiserror::Error, Debug)]
24pub enum Error {
25    #[error("HonkRPC method failed: {0}")]
26    HonkRPCFailure(#[from] honk_rpc::honk_rpc::Error),
27
28    #[error("client received unexpected response: {0}")]
29    UnexpectedResponseReceived(String),
30
31    #[error("client is in invalid state: {0}")]
32    InvalidState(String),
33
34    #[error("incorrect usage: {0}")]
35    IncorrectUsage(String),
36
37    #[error("OsRng::try_fill_bytes() failed: {0}")]
38    OsRngTryFillBytesFailure(#[from] rand_core::OsError),
39}
40
41pub(crate) enum EndpointClientEvent {
42    HandshakeCompleted { stream: TcpStream },
43}
44
45#[derive(Debug, PartialEq)]
46enum EndpointClientState {
47    BeginHandshake,
48    WaitingForServerCookie,
49    WaitingForProofVerification,
50    HandshakeComplete,
51}
52
53pub(crate) struct EndpointClient {
54    // session data
55    rpc: Option<Session<TcpStream>>,
56    pub server_service_id: V3OnionServiceId,
57    pub requested_channel: AsciiString,
58    client_service_id: V3OnionServiceId,
59    client_ed25519_private: Ed25519PrivateKey,
60
61    // state machine data
62    state: EndpointClientState,
63    begin_handshake_request_cookie: Option<RequestCookie>,
64    send_response_request_cookie: Option<RequestCookie>,
65}
66
67impl EndpointClient {
68    fn get_state(&self) -> String {
69        format!("{{ state: {:?}, begin_handshake_request_cookie: {:?}, send_response_request_cookie: {:?} }}", self.state, self.begin_handshake_request_cookie, self.send_response_request_cookie)
70    }
71
72    pub fn new(
73        rpc: Session<TcpStream>,
74        server_service_id: V3OnionServiceId,
75        requested_channel: AsciiString,
76        client_ed25519_private: Ed25519PrivateKey,
77    ) -> Self {
78        Self {
79            rpc: Some(rpc),
80            server_service_id,
81            requested_channel,
82            client_service_id: V3OnionServiceId::from_private_key(&client_ed25519_private),
83            client_ed25519_private,
84
85            state: EndpointClientState::BeginHandshake,
86            begin_handshake_request_cookie: None,
87            send_response_request_cookie: None,
88        }
89    }
90
91    pub fn update(&mut self) -> Result<Option<EndpointClientEvent>, Error> {
92        if self.state == EndpointClientState::HandshakeComplete {
93            return Err(Error::IncorrectUsage("update() may not be called after HandshakeComplete has been returned from previous update() call".to_string()));
94        }
95
96        // update our rpc session
97        if let Some(rpc) = self.rpc.as_mut() {
98            rpc.update(None)?;
99
100            // client state machine
101            match (
102                &self.state,
103                self.begin_handshake_request_cookie,
104                self.send_response_request_cookie,
105            ) {
106                (&EndpointClientState::BeginHandshake, None, None) => {
107                    self.begin_handshake_request_cookie = Some(rpc.client_call(
108                        "gosling_endpoint",
109                        "begin_handshake",
110                        0,
111                        doc! {
112                            "version" : bson::Bson::String(GOSLING_PROTOCOL_VERSION.to_string()),
113                            "client_identity" : bson::Bson::String(self.client_service_id.to_string()),
114                            "channel" : bson::Bson::String(self.requested_channel.to_string()),
115                        },
116                    ).unwrap());
117                    self.state = EndpointClientState::WaitingForServerCookie;
118                    Ok(None)
119                }
120                (
121                    &EndpointClientState::WaitingForServerCookie,
122                    Some(begin_handshake_request_cookie),
123                    None, // send_response_request_cookie
124                ) => {
125                    if let Some(response) = rpc.client_next_response() {
126                        let result = match response {
127                            Response::Pending { cookie } => {
128                                if cookie == begin_handshake_request_cookie {
129                                    return Ok(None);
130                                } else {
131                                    return Err(Error::UnexpectedResponseReceived(
132                                        "received unexpected pending response".to_string(),
133                                    ));
134                                }
135                            }
136                            Response::Error { cookie, error_code } => {
137                                if cookie != begin_handshake_request_cookie {
138                                    return Err(Error::UnexpectedResponseReceived(format!(
139                                        "received unexpected error response; rpc error_code: {}",
140                                        error_code
141                                    )));
142                                }
143                                return Err(Error::UnexpectedResponseReceived(format!(
144                                    "received unexpected rpc error_code: {}",
145                                    error_code
146                                )));
147                            }
148                            Response::Success { cookie, result } => {
149                                if cookie == begin_handshake_request_cookie {
150                                    result
151                                } else {
152                                    return Err(Error::UnexpectedResponseReceived(
153                                        "received unexpected success response".to_string(),
154                                    ));
155                                }
156                            }
157                        };
158
159                        if let Some(bson::Bson::Document(result)) = result {
160                            if let Some(Bson::Binary(Binary {
161                                subtype: BinarySubtype::Generic,
162                                bytes: server_cookie,
163                            })) = result.get("server_cookie")
164                            {
165                                // build arguments for send_response()
166
167                                // client_cookie
168                                let mut client_cookie: ClientCookie = Default::default();
169                                OsRng.try_fill_bytes(&mut client_cookie)?;
170
171                                // client_identity_proof_signature
172                                let server_cookie: ServerCookie =
173                                    match server_cookie.clone().try_into() {
174                                        Ok(server_cookie) => server_cookie,
175                                        Err(_) => {
176                                            return Err(Error::UnexpectedResponseReceived(format!(
177                                                "unable to convert '{:?}' to server cookie",
178                                                server_cookie
179                                            )))
180                                        }
181                                    };
182                                let client_identity_proof = build_client_proof(
183                                    DomainSeparator::GoslingEndpoint,
184                                    &self.requested_channel,
185                                    &self.client_service_id,
186                                    &self.server_service_id,
187                                    &client_cookie,
188                                    &server_cookie,
189                                );
190                                let client_identity_proof_signature = self
191                                    .client_ed25519_private
192                                    .sign_message(&client_identity_proof);
193
194                                // build our args object for rpc call
195                                let args = doc! {
196                                    "client_cookie" : Bson::Binary(bson::Binary{subtype: BinarySubtype::Generic, bytes: client_cookie.to_vec()}),
197                                    "client_identity_proof_signature" : Bson::Binary(bson::Binary{subtype: BinarySubtype::Generic, bytes: client_identity_proof_signature.to_bytes().to_vec()}),
198                                };
199
200                                // make rpc call
201                                self.send_response_request_cookie = Some(
202                                    rpc.client_call("gosling_endpoint", "send_response", 0, args)
203                                        .unwrap(),
204                                );
205
206                                self.state = EndpointClientState::WaitingForProofVerification;
207                            } else {
208                                return Err(Error::UnexpectedResponseReceived(format!(
209                                    "begin_handshake() returned unexpected value: {}",
210                                    result
211                                )));
212                            }
213                        } else {
214                            return Err(Error::UnexpectedResponseReceived(format!(
215                                "begin_handshake() returned unexpected value: {:?}",
216                                result
217                            )));
218                        }
219                    }
220                    Ok(None)
221                }
222                (
223                    &EndpointClientState::WaitingForProofVerification,
224                    Some(_begin_handshake_request_cookie),
225                    Some(send_response_request_cookie),
226                ) => {
227                    if let Some(response) = rpc.client_next_response() {
228                        let result = match response {
229                            Response::Pending { cookie } => {
230                                if cookie == send_response_request_cookie {
231                                    return Ok(None);
232                                } else {
233                                    return Err(Error::UnexpectedResponseReceived(
234                                        "received unexpected pending response".to_string(),
235                                    ));
236                                }
237                            }
238                            Response::Error { cookie, error_code } => {
239                                if cookie == send_response_request_cookie {
240                                    return Err(Error::UnexpectedResponseReceived(format!(
241                                        "received unexpected error response; rpc error_code: {}",
242                                        error_code
243                                    )));
244                                }
245                                return Err(Error::UnexpectedResponseReceived(format!(
246                                    "received unexpected rpc error_code: {}",
247                                    error_code
248                                )));
249                            }
250                            Response::Success { cookie, result } => {
251                                if cookie == send_response_request_cookie {
252                                    result
253                                } else {
254                                    return Err(Error::UnexpectedResponseReceived(
255                                        "received unexpected success response".to_string(),
256                                    ));
257                                }
258                            }
259                        };
260
261                        if let Some(Bson::Document(result)) = result {
262                            if result.is_empty() {
263                                self.state = EndpointClientState::HandshakeComplete;
264                                let stream = std::mem::take(&mut self.rpc).unwrap().into_stream();
265                                return Ok(Some(EndpointClientEvent::HandshakeCompleted {
266                                    stream,
267                                }));
268                            } else {
269                                return Err(Error::UnexpectedResponseReceived(format!(
270                                    "received unexpected data from send_response(): {:?}",
271                                    result
272                                )));
273                            }
274                        } else {
275                            return Err(Error::UnexpectedResponseReceived(format!(
276                                "received unexpected data from send_response(): {:?}",
277                                result
278                            )));
279                        }
280                    }
281                    Ok(None)
282                }
283                _ => Err(Error::InvalidState(self.get_state())),
284            }
285        } else {
286            Err(Error::InvalidState(self.get_state()))
287        }
288    }
289}