1use std::clone::Clone;
3use std::convert::TryInto;
4use std::net::TcpStream;
5
6use bson::doc;
8use bson::spec::BinarySubtype;
9use bson::{Binary, Bson};
10use honk_rpc::honk_rpc::{RequestCookie, Response, Session};
11use rand::rngs::OsRng;
12use rand::{rand_core, TryRngCore};
13use tor_interface::tor_crypto::*;
14
15use crate::ascii_string::*;
17use crate::gosling::*;
18
19#[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 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: 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 if let Some(rpc) = self.rpc.as_mut() {
98 rpc.update(None)?;
99
100 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, ) => {
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 let mut client_cookie: ClientCookie = Default::default();
169 OsRng.try_fill_bytes(&mut client_cookie)?;
170
171 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 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 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}