1use std::time::Duration;
2
3use ap_noise::{InitiatorHandshake, MultiDeviceTransport, Psk};
4use ap_proxy_client::IncomingMessage;
5use ap_proxy_protocol::{IdentityFingerprint, RendezvousCode};
6use base64::{Engine, engine::general_purpose::STANDARD};
7use rand::RngCore;
8
9use crate::compat::{now_millis, timeout};
10use crate::proxy::ProxyClient;
11use tokio::sync::{mpsc, oneshot};
12use tracing::{debug, warn};
13
14use crate::traits::{IdentityProvider, SessionStore};
15use crate::{
16 error::RemoteClientError,
17 types::{
18 CredentialData, CredentialQuery, CredentialRequestPayload, CredentialResponsePayload,
19 ProtocolMessage,
20 },
21};
22
23const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
24
25#[derive(Debug, Clone)]
31pub enum RemoteClientNotification {
32 Connecting,
34 Connected {
36 fingerprint: IdentityFingerprint,
38 },
39 ReconnectingToSession {
41 fingerprint: IdentityFingerprint,
43 },
44 RendezvousResolving {
46 code: String,
48 },
49 RendezvousResolved {
51 fingerprint: IdentityFingerprint,
53 },
54 PskMode {
56 fingerprint: IdentityFingerprint,
58 },
59 HandshakeStart,
61 HandshakeProgress {
63 message: String,
65 },
66 HandshakeComplete,
68 HandshakeFingerprint {
70 fingerprint: String,
72 },
73 FingerprintVerified,
75 FingerprintRejected {
77 reason: String,
79 },
80 Ready {
82 can_request_credentials: bool,
84 },
85 CredentialRequestSent {
87 query: CredentialQuery,
89 },
90 CredentialReceived {
92 credential: CredentialData,
94 },
95 Error {
97 message: String,
99 context: Option<String>,
101 },
102 Disconnected {
104 reason: Option<String>,
106 },
107}
108
109#[derive(Debug)]
111pub struct RemoteClientFingerprintReply {
112 pub approved: bool,
114}
115
116#[derive(Debug)]
118pub enum RemoteClientRequest {
119 VerifyFingerprint {
121 fingerprint: String,
123 reply: oneshot::Sender<RemoteClientFingerprintReply>,
125 },
126}
127
128type SessionList = Vec<(IdentityFingerprint, Option<String>, u64, u64)>;
134
135enum RemoteClientCommand {
137 PairWithHandshake {
138 rendezvous_code: String,
139 verify_fingerprint: bool,
140 reply: oneshot::Sender<Result<IdentityFingerprint, RemoteClientError>>,
141 },
142 PairWithPsk {
143 psk: Psk,
144 remote_fingerprint: IdentityFingerprint,
145 reply: oneshot::Sender<Result<(), RemoteClientError>>,
146 },
147 LoadCachedSession {
148 remote_fingerprint: IdentityFingerprint,
149 reply: oneshot::Sender<Result<(), RemoteClientError>>,
150 },
151 RequestCredential {
152 query: CredentialQuery,
153 reply: oneshot::Sender<Result<CredentialData, RemoteClientError>>,
154 },
155 ListSessions {
156 reply: oneshot::Sender<SessionList>,
157 },
158 HasSession {
159 fingerprint: IdentityFingerprint,
160 reply: oneshot::Sender<bool>,
161 },
162}
163
164#[derive(Clone)]
177pub struct RemoteClient {
178 command_tx: mpsc::Sender<RemoteClientCommand>,
179}
180
181impl RemoteClient {
182 pub async fn connect(
191 identity_provider: Box<dyn IdentityProvider>,
192 session_store: Box<dyn SessionStore>,
193 mut proxy_client: Box<dyn ProxyClient>,
194 notification_tx: mpsc::Sender<RemoteClientNotification>,
195 request_tx: mpsc::Sender<RemoteClientRequest>,
196 ) -> Result<Self, RemoteClientError> {
197 let own_fingerprint = identity_provider.fingerprint().await;
198
199 debug!("Connecting to proxy with identity {:?}", own_fingerprint);
200
201 notification_tx
202 .send(RemoteClientNotification::Connecting)
203 .await
204 .ok();
205
206 let incoming_rx = proxy_client.connect().await?;
207
208 notification_tx
209 .send(RemoteClientNotification::Connected {
210 fingerprint: own_fingerprint,
211 })
212 .await
213 .ok();
214
215 debug!("Connected to proxy successfully");
216
217 let (command_tx, command_rx) = mpsc::channel(32);
219
220 let inner = RemoteClientInner {
222 session_store,
223 proxy_client,
224 transport: None,
225 remote_fingerprint: None,
226 };
227
228 #[cfg(target_arch = "wasm32")]
230 wasm_bindgen_futures::spawn_local(inner.run_event_loop(
231 incoming_rx,
232 command_rx,
233 notification_tx,
234 request_tx,
235 ));
236 #[cfg(not(target_arch = "wasm32"))]
237 tokio::spawn(inner.run_event_loop(incoming_rx, command_rx, notification_tx, request_tx));
238
239 Ok(Self { command_tx })
240 }
241
242 pub async fn pair_with_handshake(
249 &self,
250 rendezvous_code: String,
251 verify_fingerprint: bool,
252 ) -> Result<IdentityFingerprint, RemoteClientError> {
253 let (tx, rx) = oneshot::channel();
254 self.command_tx
255 .send(RemoteClientCommand::PairWithHandshake {
256 rendezvous_code,
257 verify_fingerprint,
258 reply: tx,
259 })
260 .await
261 .map_err(|_| RemoteClientError::ChannelClosed)?;
262 rx.await.map_err(|_| RemoteClientError::ChannelClosed)?
263 }
264
265 pub async fn pair_with_psk(
270 &self,
271 psk: Psk,
272 remote_fingerprint: IdentityFingerprint,
273 ) -> Result<(), RemoteClientError> {
274 let (tx, rx) = oneshot::channel();
275 self.command_tx
276 .send(RemoteClientCommand::PairWithPsk {
277 psk,
278 remote_fingerprint,
279 reply: tx,
280 })
281 .await
282 .map_err(|_| RemoteClientError::ChannelClosed)?;
283 rx.await.map_err(|_| RemoteClientError::ChannelClosed)?
284 }
285
286 pub async fn load_cached_session(
291 &self,
292 remote_fingerprint: IdentityFingerprint,
293 ) -> Result<(), RemoteClientError> {
294 let (tx, rx) = oneshot::channel();
295 self.command_tx
296 .send(RemoteClientCommand::LoadCachedSession {
297 remote_fingerprint,
298 reply: tx,
299 })
300 .await
301 .map_err(|_| RemoteClientError::ChannelClosed)?;
302 rx.await.map_err(|_| RemoteClientError::ChannelClosed)?
303 }
304
305 pub async fn request_credential(
307 &self,
308 query: &CredentialQuery,
309 ) -> Result<CredentialData, RemoteClientError> {
310 let (tx, rx) = oneshot::channel();
311 self.command_tx
312 .send(RemoteClientCommand::RequestCredential {
313 query: query.clone(),
314 reply: tx,
315 })
316 .await
317 .map_err(|_| RemoteClientError::ChannelClosed)?;
318 rx.await.map_err(|_| RemoteClientError::ChannelClosed)?
319 }
320
321 pub async fn list_sessions(
323 &self,
324 ) -> Result<Vec<(IdentityFingerprint, Option<String>, u64, u64)>, RemoteClientError> {
325 let (tx, rx) = oneshot::channel();
326 self.command_tx
327 .send(RemoteClientCommand::ListSessions { reply: tx })
328 .await
329 .map_err(|_| RemoteClientError::ChannelClosed)?;
330 rx.await.map_err(|_| RemoteClientError::ChannelClosed)
331 }
332
333 pub async fn has_session(
335 &self,
336 fingerprint: IdentityFingerprint,
337 ) -> Result<bool, RemoteClientError> {
338 let (tx, rx) = oneshot::channel();
339 self.command_tx
340 .send(RemoteClientCommand::HasSession {
341 fingerprint,
342 reply: tx,
343 })
344 .await
345 .map_err(|_| RemoteClientError::ChannelClosed)?;
346 rx.await.map_err(|_| RemoteClientError::ChannelClosed)
347 }
348}
349
350struct RemoteClientInner {
356 session_store: Box<dyn SessionStore>,
357 proxy_client: Box<dyn ProxyClient>,
358 transport: Option<MultiDeviceTransport>,
359 remote_fingerprint: Option<IdentityFingerprint>,
360}
361
362impl RemoteClientInner {
363 async fn run_event_loop(
365 mut self,
366 mut incoming_rx: mpsc::UnboundedReceiver<IncomingMessage>,
367 mut command_rx: mpsc::Receiver<RemoteClientCommand>,
368 notification_tx: mpsc::Sender<RemoteClientNotification>,
369 request_tx: mpsc::Sender<RemoteClientRequest>,
370 ) {
371 loop {
372 tokio::select! {
373 msg = incoming_rx.recv() => {
374 match msg {
375 Some(_) => {
376 debug!("Received message while idle");
378 }
379 None => {
380 notification_tx.send(RemoteClientNotification::Disconnected {
382 reason: Some("Proxy connection closed".to_string()),
383 }).await.ok();
384 return;
385 }
386 }
387 }
388 cmd = command_rx.recv() => {
389 match cmd {
390 Some(cmd) => {
391 self.handle_command(
392 cmd,
393 &mut incoming_rx,
394 ¬ification_tx,
395 &request_tx,
396 ).await;
397 }
398 None => {
399 debug!("All RemoteClient handles dropped, shutting down event loop");
401 self.proxy_client.disconnect().await.ok();
402 return;
403 }
404 }
405 }
406 }
407 }
408 }
409
410 async fn handle_command(
412 &mut self,
413 cmd: RemoteClientCommand,
414 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
415 notification_tx: &mpsc::Sender<RemoteClientNotification>,
416 request_tx: &mpsc::Sender<RemoteClientRequest>,
417 ) {
418 match cmd {
419 RemoteClientCommand::PairWithHandshake {
420 rendezvous_code,
421 verify_fingerprint,
422 reply,
423 } => {
424 let result = self
425 .do_pair_with_handshake(
426 rendezvous_code,
427 verify_fingerprint,
428 incoming_rx,
429 notification_tx,
430 request_tx,
431 )
432 .await;
433 let _ = reply.send(result);
434 }
435 RemoteClientCommand::PairWithPsk {
436 psk,
437 remote_fingerprint,
438 reply,
439 } => {
440 let result = self
441 .do_pair_with_psk(psk, remote_fingerprint, incoming_rx, notification_tx)
442 .await;
443 let _ = reply.send(result);
444 }
445 RemoteClientCommand::LoadCachedSession {
446 remote_fingerprint,
447 reply,
448 } => {
449 let result = self
450 .do_load_cached_session(remote_fingerprint, notification_tx)
451 .await;
452 let _ = reply.send(result);
453 }
454 RemoteClientCommand::RequestCredential { query, reply } => {
455 let result = self
456 .do_request_credential(query, incoming_rx, notification_tx)
457 .await;
458 let _ = reply.send(result);
459 }
460 RemoteClientCommand::ListSessions { reply } => {
461 let sessions = self.session_store.list_sessions().await;
462 let _ = reply.send(sessions);
463 }
464 RemoteClientCommand::HasSession { fingerprint, reply } => {
465 let has = self.session_store.has_session(&fingerprint).await;
466 let _ = reply.send(has);
467 }
468 }
469 }
470
471 async fn do_pair_with_handshake(
474 &mut self,
475 rendezvous_code: String,
476 verify_fingerprint: bool,
477 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
478 notification_tx: &mpsc::Sender<RemoteClientNotification>,
479 request_tx: &mpsc::Sender<RemoteClientRequest>,
480 ) -> Result<IdentityFingerprint, RemoteClientError> {
481 notification_tx
483 .send(RemoteClientNotification::RendezvousResolving {
484 code: rendezvous_code.clone(),
485 })
486 .await
487 .ok();
488
489 let remote_fingerprint =
490 Self::resolve_rendezvous(self.proxy_client.as_ref(), incoming_rx, &rendezvous_code)
491 .await?;
492
493 notification_tx
494 .send(RemoteClientNotification::RendezvousResolved {
495 fingerprint: remote_fingerprint,
496 })
497 .await
498 .ok();
499
500 notification_tx
502 .send(RemoteClientNotification::HandshakeStart)
503 .await
504 .ok();
505
506 let (transport, fingerprint_str) = Self::perform_handshake(
507 self.proxy_client.as_ref(),
508 incoming_rx,
509 remote_fingerprint,
510 None,
511 )
512 .await?;
513
514 notification_tx
515 .send(RemoteClientNotification::HandshakeComplete)
516 .await
517 .ok();
518
519 notification_tx
521 .send(RemoteClientNotification::HandshakeFingerprint {
522 fingerprint: fingerprint_str.clone(),
523 })
524 .await
525 .ok();
526
527 if verify_fingerprint {
528 let (fp_tx, fp_rx) = oneshot::channel();
530 request_tx
531 .send(RemoteClientRequest::VerifyFingerprint {
532 fingerprint: fingerprint_str,
533 reply: fp_tx,
534 })
535 .await
536 .map_err(|_| RemoteClientError::ChannelClosed)?;
537
538 match timeout(Duration::from_secs(60), fp_rx).await {
540 Ok(Ok(RemoteClientFingerprintReply { approved: true })) => {
541 notification_tx
542 .send(RemoteClientNotification::FingerprintVerified)
543 .await
544 .ok();
545 }
546 Ok(Ok(RemoteClientFingerprintReply { approved: false })) => {
547 self.proxy_client.disconnect().await.ok();
548 notification_tx
549 .send(RemoteClientNotification::FingerprintRejected {
550 reason: "User rejected fingerprint verification".to_string(),
551 })
552 .await
553 .ok();
554 return Err(RemoteClientError::FingerprintRejected);
555 }
556 Ok(Err(_)) => {
557 return Err(RemoteClientError::ChannelClosed);
558 }
559 Err(_) => {
560 self.proxy_client.disconnect().await.ok();
561 return Err(RemoteClientError::Timeout(
562 "Fingerprint verification timeout".to_string(),
563 ));
564 }
565 }
566 }
567
568 self.finalize_pairing(transport, remote_fingerprint, notification_tx)
570 .await?;
571
572 Ok(remote_fingerprint)
573 }
574
575 async fn do_pair_with_psk(
578 &mut self,
579 psk: Psk,
580 remote_fingerprint: IdentityFingerprint,
581 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
582 notification_tx: &mpsc::Sender<RemoteClientNotification>,
583 ) -> Result<(), RemoteClientError> {
584 notification_tx
585 .send(RemoteClientNotification::PskMode {
586 fingerprint: remote_fingerprint,
587 })
588 .await
589 .ok();
590
591 notification_tx
593 .send(RemoteClientNotification::HandshakeStart)
594 .await
595 .ok();
596
597 let (transport, _fingerprint_str) = Self::perform_handshake(
598 self.proxy_client.as_ref(),
599 incoming_rx,
600 remote_fingerprint,
601 Some(psk),
602 )
603 .await?;
604
605 notification_tx
606 .send(RemoteClientNotification::HandshakeComplete)
607 .await
608 .ok();
609
610 notification_tx
612 .send(RemoteClientNotification::FingerprintVerified)
613 .await
614 .ok();
615
616 self.finalize_pairing(transport, remote_fingerprint, notification_tx)
618 .await?;
619
620 Ok(())
621 }
622
623 async fn do_load_cached_session(
626 &mut self,
627 remote_fingerprint: IdentityFingerprint,
628 notification_tx: &mpsc::Sender<RemoteClientNotification>,
629 ) -> Result<(), RemoteClientError> {
630 if !self.session_store.has_session(&remote_fingerprint).await {
631 return Err(RemoteClientError::SessionNotFound);
632 }
633
634 notification_tx
635 .send(RemoteClientNotification::ReconnectingToSession {
636 fingerprint: remote_fingerprint,
637 })
638 .await
639 .ok();
640
641 let transport = self
642 .session_store
643 .load_transport_state(&remote_fingerprint)
644 .await?
645 .ok_or(RemoteClientError::SessionNotFound)?;
646
647 notification_tx
648 .send(RemoteClientNotification::HandshakeComplete)
649 .await
650 .ok();
651
652 notification_tx
654 .send(RemoteClientNotification::FingerprintVerified)
655 .await
656 .ok();
657
658 self.session_store
660 .update_last_connected(&remote_fingerprint)
661 .await?;
662
663 self.session_store
665 .save_transport_state(&remote_fingerprint, transport.clone())
666 .await?;
667
668 self.transport = Some(transport);
669 self.remote_fingerprint = Some(remote_fingerprint);
670
671 notification_tx
672 .send(RemoteClientNotification::Ready {
673 can_request_credentials: true,
674 })
675 .await
676 .ok();
677
678 debug!("Reconnected to cached session");
679 Ok(())
680 }
681
682 async fn finalize_pairing(
685 &mut self,
686 transport: MultiDeviceTransport,
687 remote_fingerprint: IdentityFingerprint,
688 notification_tx: &mpsc::Sender<RemoteClientNotification>,
689 ) -> Result<(), RemoteClientError> {
690 self.session_store.cache_session(remote_fingerprint).await?;
692
693 self.session_store
695 .save_transport_state(&remote_fingerprint, transport.clone())
696 .await?;
697
698 self.transport = Some(transport);
700 self.remote_fingerprint = Some(remote_fingerprint);
701
702 notification_tx
704 .send(RemoteClientNotification::Ready {
705 can_request_credentials: true,
706 })
707 .await
708 .ok();
709
710 debug!("Connection established successfully");
711 Ok(())
712 }
713
714 async fn do_request_credential(
717 &mut self,
718 query: CredentialQuery,
719 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
720 notification_tx: &mpsc::Sender<RemoteClientNotification>,
721 ) -> Result<CredentialData, RemoteClientError> {
722 let remote_fingerprint = self
723 .remote_fingerprint
724 .ok_or(RemoteClientError::NotInitialized)?;
725
726 #[allow(clippy::string_slice)]
728 let request_id = format!("req-{}-{}", now_millis(), &uuid_v4()[..8]);
729
730 debug!("Requesting credential for query: {:?}", query);
731
732 let request = CredentialRequestPayload {
734 request_type: "credential_request".to_string(),
735 query: query.clone(),
736 timestamp: now_millis(),
737 request_id: request_id.clone(),
738 };
739
740 let request_json = serde_json::to_string(&request)?;
741
742 let encrypted_data = {
743 let transport = self
744 .transport
745 .as_mut()
746 .ok_or(RemoteClientError::SecureChannelNotEstablished)?;
747 let encrypted_packet = transport
748 .encrypt(request_json.as_bytes())
749 .map_err(|e| RemoteClientError::NoiseProtocol(e.to_string()))?;
750 STANDARD.encode(encrypted_packet.encode())
751 };
752
753 let msg = ProtocolMessage::CredentialRequest {
754 encrypted: encrypted_data,
755 };
756
757 let msg_json = serde_json::to_string(&msg)?;
759 self.proxy_client
760 .send_to(remote_fingerprint, msg_json.into_bytes())
761 .await?;
762
763 notification_tx
765 .send(RemoteClientNotification::CredentialRequestSent {
766 query: query.clone(),
767 })
768 .await
769 .ok();
770
771 match timeout(
773 DEFAULT_TIMEOUT,
774 self.receive_credential_response(&request_id, incoming_rx, notification_tx),
775 )
776 .await
777 {
778 Ok(result) => result,
779 Err(_) => Err(RemoteClientError::Timeout(format!(
780 "Timeout waiting for credential response for query: {query:?}"
781 ))),
782 }
783 }
784
785 async fn receive_credential_response(
790 &mut self,
791 request_id: &str,
792 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
793 notification_tx: &mpsc::Sender<RemoteClientNotification>,
794 ) -> Result<CredentialData, RemoteClientError> {
795 loop {
796 match incoming_rx.recv().await {
797 Some(IncomingMessage::Send { payload, .. }) => {
798 if let Ok(text) = String::from_utf8(payload)
799 && let Ok(ProtocolMessage::CredentialResponse { encrypted }) =
800 serde_json::from_str::<ProtocolMessage>(&text)
801 {
802 match self
803 .decrypt_credential_response(&encrypted, request_id, notification_tx)
804 .await
805 {
806 Ok(credential) => return Ok(credential),
807 Err(RemoteClientError::CredentialRequestFailed(ref msg))
808 if msg.contains("request_id mismatch") =>
809 {
810 debug!("Skipping stale credential response: {msg}");
812 continue;
813 }
814 Err(e) => return Err(e),
815 }
816 }
817 }
818 Some(_) => {
819 }
821 None => {
822 return Err(RemoteClientError::ChannelClosed);
823 }
824 }
825 }
826 }
827
828 async fn decrypt_credential_response(
830 &mut self,
831 encrypted: &str,
832 request_id: &str,
833 notification_tx: &mpsc::Sender<RemoteClientNotification>,
834 ) -> Result<CredentialData, RemoteClientError> {
835 let encrypted_bytes = STANDARD
836 .decode(encrypted)
837 .map_err(|e| RemoteClientError::Serialization(format!("Invalid base64: {e}")))?;
838
839 let packet = ap_noise::TransportPacket::decode(&encrypted_bytes)
840 .map_err(|e| RemoteClientError::NoiseProtocol(format!("Invalid packet: {e}")))?;
841
842 let transport = self
843 .transport
844 .as_mut()
845 .ok_or(RemoteClientError::SecureChannelNotEstablished)?;
846
847 let decrypted = transport
848 .decrypt(&packet)
849 .map_err(|e| RemoteClientError::NoiseProtocol(e.to_string()))?;
850
851 let response: CredentialResponsePayload = serde_json::from_slice(&decrypted)?;
852
853 if response.request_id.as_deref() != Some(request_id) {
855 warn!(
856 "Ignoring response with mismatched request_id: {:?}",
857 response.request_id
858 );
859 return Err(RemoteClientError::CredentialRequestFailed(
860 "Response request_id mismatch".to_string(),
861 ));
862 }
863
864 if let Some(error) = response.error {
865 return Err(RemoteClientError::CredentialRequestFailed(error));
866 }
867
868 if let Some(credential) = response.credential {
869 notification_tx
870 .send(RemoteClientNotification::CredentialReceived {
871 credential: credential.clone(),
872 })
873 .await
874 .ok();
875 Ok(credential)
876 } else {
877 Err(RemoteClientError::CredentialRequestFailed(
878 "Response contains neither credential nor error".to_string(),
879 ))
880 }
881 }
882
883 async fn resolve_rendezvous(
887 proxy_client: &dyn ProxyClient,
888 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
889 rendezvous_code: &str,
890 ) -> Result<IdentityFingerprint, RemoteClientError> {
891 proxy_client
893 .request_identity(RendezvousCode::from_string(rendezvous_code.to_string()))
894 .await
895 .map_err(|e| RemoteClientError::RendezvousResolutionFailed(e.to_string()))?;
896
897 let timeout_duration = Duration::from_secs(10);
899 match timeout(timeout_duration, async {
900 while let Some(msg) = incoming_rx.recv().await {
901 if let IncomingMessage::IdentityInfo { fingerprint, .. } = msg {
902 return Some(fingerprint);
903 }
904 }
905 None
906 })
907 .await
908 {
909 Ok(Some(fingerprint)) => Ok(fingerprint),
910 Ok(None) => Err(RemoteClientError::RendezvousResolutionFailed(
911 "Connection closed while waiting for identity response".to_string(),
912 )),
913 Err(_) => Err(RemoteClientError::RendezvousResolutionFailed(
914 "Timeout waiting for identity response. The rendezvous code may be invalid, expired, or the target client may be disconnected.".to_string(),
915 )),
916 }
917 }
918
919 async fn perform_handshake(
921 proxy_client: &dyn ProxyClient,
922 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
923 remote_fingerprint: IdentityFingerprint,
924 psk: Option<Psk>,
925 ) -> Result<(MultiDeviceTransport, String), RemoteClientError> {
926 let psk_id = psk.as_ref().map(|p| p.id());
928
929 let mut handshake = if let Some(psk) = psk {
931 InitiatorHandshake::with_psk(psk)
932 } else {
933 InitiatorHandshake::new()
934 };
935
936 let init_packet = handshake.send_start()?;
938
939 let msg = ProtocolMessage::HandshakeInit {
941 data: STANDARD.encode(init_packet.encode()?),
942 ciphersuite: format!("{:?}", handshake.ciphersuite()),
943 psk_id,
944 };
945
946 let msg_json = serde_json::to_string(&msg)?;
947 proxy_client
948 .send_to(remote_fingerprint, msg_json.into_bytes())
949 .await?;
950
951 debug!("Sent handshake init");
952
953 let response_timeout = Duration::from_secs(10);
955 let response: String = timeout(response_timeout, async {
956 loop {
957 if let Some(incoming) = incoming_rx.recv().await {
958 match incoming {
959 IncomingMessage::Send { payload, .. } => {
960 if let Ok(text) = String::from_utf8(payload)
962 && let Ok(ProtocolMessage::HandshakeResponse { data, .. }) =
963 serde_json::from_str::<ProtocolMessage>(&text)
964 {
965 return Ok::<String, RemoteClientError>(data);
966 }
967 }
968 _ => continue,
969 }
970 }
971 }
972 })
973 .await
974 .map_err(|_| RemoteClientError::Timeout("Waiting for handshake response".to_string()))??;
975
976 let response_bytes = STANDARD
978 .decode(&response)
979 .map_err(|e| RemoteClientError::Serialization(format!("Invalid base64: {e}")))?;
980
981 let response_packet = ap_noise::HandshakePacket::decode(&response_bytes)
982 .map_err(|e| RemoteClientError::NoiseProtocol(format!("Invalid packet: {e}")))?;
983
984 handshake.receive_finish(&response_packet)?;
986 let (transport, fingerprint) = handshake.finalize()?;
987
988 debug!("Handshake complete");
989 Ok((transport, fingerprint.to_string()))
990 }
991}
992
993fn uuid_v4() -> String {
994 let mut bytes = [0u8; 16];
996 let mut rng = rand::thread_rng();
997 rng.fill_bytes(&mut bytes);
998
999 bytes[6] = (bytes[6] & 0x0f) | 0x40;
1001 bytes[8] = (bytes[8] & 0x3f) | 0x80;
1002
1003 format!(
1004 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
1005 bytes[0],
1006 bytes[1],
1007 bytes[2],
1008 bytes[3],
1009 bytes[4],
1010 bytes[5],
1011 bytes[6],
1012 bytes[7],
1013 bytes[8],
1014 bytes[9],
1015 bytes[10],
1016 bytes[11],
1017 bytes[12],
1018 bytes[13],
1019 bytes[14],
1020 bytes[15]
1021 )
1022}