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::ClientError,
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, ClientError>>,
141 },
142 PairWithPsk {
143 psk: Psk,
144 remote_fingerprint: IdentityFingerprint,
145 reply: oneshot::Sender<Result<(), ClientError>>,
146 },
147 LoadCachedSession {
148 remote_fingerprint: IdentityFingerprint,
149 reply: oneshot::Sender<Result<(), ClientError>>,
150 },
151 RequestCredential {
152 query: CredentialQuery,
153 reply: oneshot::Sender<Result<CredentialData, ClientError>>,
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, ClientError> {
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, ClientError> {
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(|_| ClientError::ChannelClosed)?;
262 rx.await.map_err(|_| ClientError::ChannelClosed)?
263 }
264
265 pub async fn pair_with_psk(
270 &self,
271 psk: Psk,
272 remote_fingerprint: IdentityFingerprint,
273 ) -> Result<(), ClientError> {
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(|_| ClientError::ChannelClosed)?;
283 rx.await.map_err(|_| ClientError::ChannelClosed)?
284 }
285
286 pub async fn load_cached_session(
291 &self,
292 remote_fingerprint: IdentityFingerprint,
293 ) -> Result<(), ClientError> {
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(|_| ClientError::ChannelClosed)?;
302 rx.await.map_err(|_| ClientError::ChannelClosed)?
303 }
304
305 pub async fn request_credential(
307 &self,
308 query: &CredentialQuery,
309 ) -> Result<CredentialData, ClientError> {
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(|_| ClientError::ChannelClosed)?;
318 rx.await.map_err(|_| ClientError::ChannelClosed)?
319 }
320
321 pub async fn list_sessions(
323 &self,
324 ) -> Result<Vec<(IdentityFingerprint, Option<String>, u64, u64)>, ClientError> {
325 let (tx, rx) = oneshot::channel();
326 self.command_tx
327 .send(RemoteClientCommand::ListSessions { reply: tx })
328 .await
329 .map_err(|_| ClientError::ChannelClosed)?;
330 rx.await.map_err(|_| ClientError::ChannelClosed)
331 }
332
333 pub async fn has_session(&self, fingerprint: IdentityFingerprint) -> Result<bool, ClientError> {
335 let (tx, rx) = oneshot::channel();
336 self.command_tx
337 .send(RemoteClientCommand::HasSession {
338 fingerprint,
339 reply: tx,
340 })
341 .await
342 .map_err(|_| ClientError::ChannelClosed)?;
343 rx.await.map_err(|_| ClientError::ChannelClosed)
344 }
345}
346
347struct RemoteClientInner {
353 session_store: Box<dyn SessionStore>,
354 proxy_client: Box<dyn ProxyClient>,
355 transport: Option<MultiDeviceTransport>,
356 remote_fingerprint: Option<IdentityFingerprint>,
357}
358
359impl RemoteClientInner {
360 async fn run_event_loop(
362 mut self,
363 mut incoming_rx: mpsc::UnboundedReceiver<IncomingMessage>,
364 mut command_rx: mpsc::Receiver<RemoteClientCommand>,
365 notification_tx: mpsc::Sender<RemoteClientNotification>,
366 request_tx: mpsc::Sender<RemoteClientRequest>,
367 ) {
368 loop {
369 tokio::select! {
370 msg = incoming_rx.recv() => {
371 match msg {
372 Some(_) => {
373 debug!("Received message while idle");
375 }
376 None => {
377 notification_tx.send(RemoteClientNotification::Disconnected {
379 reason: Some("Proxy connection closed".to_string()),
380 }).await.ok();
381 return;
382 }
383 }
384 }
385 cmd = command_rx.recv() => {
386 match cmd {
387 Some(cmd) => {
388 self.handle_command(
389 cmd,
390 &mut incoming_rx,
391 ¬ification_tx,
392 &request_tx,
393 ).await;
394 }
395 None => {
396 debug!("All RemoteClient handles dropped, shutting down event loop");
398 self.proxy_client.disconnect().await.ok();
399 return;
400 }
401 }
402 }
403 }
404 }
405 }
406
407 async fn handle_command(
409 &mut self,
410 cmd: RemoteClientCommand,
411 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
412 notification_tx: &mpsc::Sender<RemoteClientNotification>,
413 request_tx: &mpsc::Sender<RemoteClientRequest>,
414 ) {
415 match cmd {
416 RemoteClientCommand::PairWithHandshake {
417 rendezvous_code,
418 verify_fingerprint,
419 reply,
420 } => {
421 let result = self
422 .do_pair_with_handshake(
423 rendezvous_code,
424 verify_fingerprint,
425 incoming_rx,
426 notification_tx,
427 request_tx,
428 )
429 .await;
430 let _ = reply.send(result);
431 }
432 RemoteClientCommand::PairWithPsk {
433 psk,
434 remote_fingerprint,
435 reply,
436 } => {
437 let result = self
438 .do_pair_with_psk(psk, remote_fingerprint, incoming_rx, notification_tx)
439 .await;
440 let _ = reply.send(result);
441 }
442 RemoteClientCommand::LoadCachedSession {
443 remote_fingerprint,
444 reply,
445 } => {
446 let result = self
447 .do_load_cached_session(remote_fingerprint, notification_tx)
448 .await;
449 let _ = reply.send(result);
450 }
451 RemoteClientCommand::RequestCredential { query, reply } => {
452 let result = self
453 .do_request_credential(query, incoming_rx, notification_tx)
454 .await;
455 let _ = reply.send(result);
456 }
457 RemoteClientCommand::ListSessions { reply } => {
458 let sessions = self.session_store.list_sessions().await;
459 let _ = reply.send(sessions);
460 }
461 RemoteClientCommand::HasSession { fingerprint, reply } => {
462 let has = self.session_store.has_session(&fingerprint).await;
463 let _ = reply.send(has);
464 }
465 }
466 }
467
468 async fn do_pair_with_handshake(
471 &mut self,
472 rendezvous_code: String,
473 verify_fingerprint: bool,
474 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
475 notification_tx: &mpsc::Sender<RemoteClientNotification>,
476 request_tx: &mpsc::Sender<RemoteClientRequest>,
477 ) -> Result<IdentityFingerprint, ClientError> {
478 notification_tx
480 .send(RemoteClientNotification::RendezvousResolving {
481 code: rendezvous_code.clone(),
482 })
483 .await
484 .ok();
485
486 let remote_fingerprint =
487 Self::resolve_rendezvous(self.proxy_client.as_ref(), incoming_rx, &rendezvous_code)
488 .await?;
489
490 notification_tx
491 .send(RemoteClientNotification::RendezvousResolved {
492 fingerprint: remote_fingerprint,
493 })
494 .await
495 .ok();
496
497 notification_tx
499 .send(RemoteClientNotification::HandshakeStart)
500 .await
501 .ok();
502
503 let (transport, fingerprint_str) = Self::perform_handshake(
504 self.proxy_client.as_ref(),
505 incoming_rx,
506 remote_fingerprint,
507 None,
508 )
509 .await?;
510
511 notification_tx
512 .send(RemoteClientNotification::HandshakeComplete)
513 .await
514 .ok();
515
516 notification_tx
518 .send(RemoteClientNotification::HandshakeFingerprint {
519 fingerprint: fingerprint_str.clone(),
520 })
521 .await
522 .ok();
523
524 if verify_fingerprint {
525 let (fp_tx, fp_rx) = oneshot::channel();
527 request_tx
528 .send(RemoteClientRequest::VerifyFingerprint {
529 fingerprint: fingerprint_str,
530 reply: fp_tx,
531 })
532 .await
533 .map_err(|_| ClientError::ChannelClosed)?;
534
535 match timeout(Duration::from_secs(60), fp_rx).await {
537 Ok(Ok(RemoteClientFingerprintReply { approved: true })) => {
538 notification_tx
539 .send(RemoteClientNotification::FingerprintVerified)
540 .await
541 .ok();
542 }
543 Ok(Ok(RemoteClientFingerprintReply { approved: false })) => {
544 self.proxy_client.disconnect().await.ok();
545 notification_tx
546 .send(RemoteClientNotification::FingerprintRejected {
547 reason: "User rejected fingerprint verification".to_string(),
548 })
549 .await
550 .ok();
551 return Err(ClientError::FingerprintRejected);
552 }
553 Ok(Err(_)) => {
554 return Err(ClientError::ChannelClosed);
555 }
556 Err(_) => {
557 self.proxy_client.disconnect().await.ok();
558 return Err(ClientError::Timeout(
559 "Fingerprint verification timeout".to_string(),
560 ));
561 }
562 }
563 }
564
565 self.finalize_pairing(transport, remote_fingerprint, notification_tx)
567 .await?;
568
569 Ok(remote_fingerprint)
570 }
571
572 async fn do_pair_with_psk(
575 &mut self,
576 psk: Psk,
577 remote_fingerprint: IdentityFingerprint,
578 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
579 notification_tx: &mpsc::Sender<RemoteClientNotification>,
580 ) -> Result<(), ClientError> {
581 notification_tx
582 .send(RemoteClientNotification::PskMode {
583 fingerprint: remote_fingerprint,
584 })
585 .await
586 .ok();
587
588 notification_tx
590 .send(RemoteClientNotification::HandshakeStart)
591 .await
592 .ok();
593
594 let (transport, _fingerprint_str) = Self::perform_handshake(
595 self.proxy_client.as_ref(),
596 incoming_rx,
597 remote_fingerprint,
598 Some(psk),
599 )
600 .await?;
601
602 notification_tx
603 .send(RemoteClientNotification::HandshakeComplete)
604 .await
605 .ok();
606
607 notification_tx
609 .send(RemoteClientNotification::FingerprintVerified)
610 .await
611 .ok();
612
613 self.finalize_pairing(transport, remote_fingerprint, notification_tx)
615 .await?;
616
617 Ok(())
618 }
619
620 async fn do_load_cached_session(
623 &mut self,
624 remote_fingerprint: IdentityFingerprint,
625 notification_tx: &mpsc::Sender<RemoteClientNotification>,
626 ) -> Result<(), ClientError> {
627 if !self.session_store.has_session(&remote_fingerprint).await {
628 return Err(ClientError::SessionNotFound);
629 }
630
631 notification_tx
632 .send(RemoteClientNotification::ReconnectingToSession {
633 fingerprint: remote_fingerprint,
634 })
635 .await
636 .ok();
637
638 let transport = self
639 .session_store
640 .load_transport_state(&remote_fingerprint)
641 .await?
642 .ok_or(ClientError::SessionNotFound)?;
643
644 notification_tx
645 .send(RemoteClientNotification::HandshakeComplete)
646 .await
647 .ok();
648
649 notification_tx
651 .send(RemoteClientNotification::FingerprintVerified)
652 .await
653 .ok();
654
655 self.session_store
657 .update_last_connected(&remote_fingerprint)
658 .await?;
659
660 self.session_store
662 .save_transport_state(&remote_fingerprint, transport.clone())
663 .await?;
664
665 self.transport = Some(transport);
666 self.remote_fingerprint = Some(remote_fingerprint);
667
668 notification_tx
669 .send(RemoteClientNotification::Ready {
670 can_request_credentials: true,
671 })
672 .await
673 .ok();
674
675 debug!("Reconnected to cached session");
676 Ok(())
677 }
678
679 async fn finalize_pairing(
682 &mut self,
683 transport: MultiDeviceTransport,
684 remote_fingerprint: IdentityFingerprint,
685 notification_tx: &mpsc::Sender<RemoteClientNotification>,
686 ) -> Result<(), ClientError> {
687 self.session_store.cache_session(remote_fingerprint).await?;
689
690 self.session_store
692 .save_transport_state(&remote_fingerprint, transport.clone())
693 .await?;
694
695 self.transport = Some(transport);
697 self.remote_fingerprint = Some(remote_fingerprint);
698
699 notification_tx
701 .send(RemoteClientNotification::Ready {
702 can_request_credentials: true,
703 })
704 .await
705 .ok();
706
707 debug!("Connection established successfully");
708 Ok(())
709 }
710
711 async fn do_request_credential(
714 &mut self,
715 query: CredentialQuery,
716 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
717 notification_tx: &mpsc::Sender<RemoteClientNotification>,
718 ) -> Result<CredentialData, ClientError> {
719 let remote_fingerprint = self.remote_fingerprint.ok_or(ClientError::NotInitialized)?;
720
721 #[allow(clippy::string_slice)]
723 let request_id = format!("req-{}-{}", now_millis(), &uuid_v4()[..8]);
724
725 debug!("Requesting credential for query: {:?}", query);
726
727 let request = CredentialRequestPayload {
729 request_type: "credential_request".to_string(),
730 query: query.clone(),
731 timestamp: now_millis(),
732 request_id: request_id.clone(),
733 };
734
735 let request_json = serde_json::to_string(&request)?;
736
737 let encrypted_data = {
738 let transport = self
739 .transport
740 .as_mut()
741 .ok_or(ClientError::SecureChannelNotEstablished)?;
742 let encrypted_packet = transport
743 .encrypt(request_json.as_bytes())
744 .map_err(|e| ClientError::NoiseProtocol(e.to_string()))?;
745 STANDARD.encode(encrypted_packet.encode())
746 };
747
748 let msg = ProtocolMessage::CredentialRequest {
749 encrypted: encrypted_data,
750 };
751
752 let msg_json = serde_json::to_string(&msg)?;
754 self.proxy_client
755 .send_to(remote_fingerprint, msg_json.into_bytes())
756 .await?;
757
758 notification_tx
760 .send(RemoteClientNotification::CredentialRequestSent {
761 query: query.clone(),
762 })
763 .await
764 .ok();
765
766 match timeout(
768 DEFAULT_TIMEOUT,
769 self.receive_credential_response(&request_id, incoming_rx, notification_tx),
770 )
771 .await
772 {
773 Ok(result) => result,
774 Err(_) => Err(ClientError::Timeout(format!(
775 "Timeout waiting for credential response for query: {query:?}"
776 ))),
777 }
778 }
779
780 async fn receive_credential_response(
785 &mut self,
786 request_id: &str,
787 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
788 notification_tx: &mpsc::Sender<RemoteClientNotification>,
789 ) -> Result<CredentialData, ClientError> {
790 loop {
791 match incoming_rx.recv().await {
792 Some(IncomingMessage::Send { payload, .. }) => {
793 if let Ok(text) = String::from_utf8(payload)
794 && let Ok(ProtocolMessage::CredentialResponse { encrypted }) =
795 serde_json::from_str::<ProtocolMessage>(&text)
796 {
797 match self
798 .decrypt_credential_response(&encrypted, request_id, notification_tx)
799 .await
800 {
801 Ok(credential) => return Ok(credential),
802 Err(ClientError::CredentialRequestFailed(ref msg))
803 if msg.contains("request_id mismatch") =>
804 {
805 debug!("Skipping stale credential response: {msg}");
807 continue;
808 }
809 Err(e) => return Err(e),
810 }
811 }
812 }
813 Some(_) => {
814 }
816 None => {
817 return Err(ClientError::ChannelClosed);
818 }
819 }
820 }
821 }
822
823 async fn decrypt_credential_response(
825 &mut self,
826 encrypted: &str,
827 request_id: &str,
828 notification_tx: &mpsc::Sender<RemoteClientNotification>,
829 ) -> Result<CredentialData, ClientError> {
830 let encrypted_bytes = STANDARD
831 .decode(encrypted)
832 .map_err(|e| ClientError::Serialization(format!("Invalid base64: {e}")))?;
833
834 let packet = ap_noise::TransportPacket::decode(&encrypted_bytes)
835 .map_err(|e| ClientError::NoiseProtocol(format!("Invalid packet: {e}")))?;
836
837 let transport = self
838 .transport
839 .as_mut()
840 .ok_or(ClientError::SecureChannelNotEstablished)?;
841
842 let decrypted = transport
843 .decrypt(&packet)
844 .map_err(|e| ClientError::NoiseProtocol(e.to_string()))?;
845
846 let response: CredentialResponsePayload = serde_json::from_slice(&decrypted)?;
847
848 if response.request_id.as_deref() != Some(request_id) {
850 warn!(
851 "Ignoring response with mismatched request_id: {:?}",
852 response.request_id
853 );
854 return Err(ClientError::CredentialRequestFailed(
855 "Response request_id mismatch".to_string(),
856 ));
857 }
858
859 if let Some(error) = response.error {
860 return Err(ClientError::CredentialRequestFailed(error));
861 }
862
863 if let Some(credential) = response.credential {
864 notification_tx
865 .send(RemoteClientNotification::CredentialReceived {
866 credential: credential.clone(),
867 })
868 .await
869 .ok();
870 Ok(credential)
871 } else {
872 Err(ClientError::CredentialRequestFailed(
873 "Response contains neither credential nor error".to_string(),
874 ))
875 }
876 }
877
878 async fn resolve_rendezvous(
882 proxy_client: &dyn ProxyClient,
883 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
884 rendezvous_code: &str,
885 ) -> Result<IdentityFingerprint, ClientError> {
886 proxy_client
888 .request_identity(RendezvousCode::from_string(rendezvous_code.to_string()))
889 .await
890 .map_err(|e| ClientError::RendezvousResolutionFailed(e.to_string()))?;
891
892 let timeout_duration = Duration::from_secs(10);
894 match timeout(timeout_duration, async {
895 while let Some(msg) = incoming_rx.recv().await {
896 if let IncomingMessage::IdentityInfo { fingerprint, .. } = msg {
897 return Some(fingerprint);
898 }
899 }
900 None
901 })
902 .await
903 {
904 Ok(Some(fingerprint)) => Ok(fingerprint),
905 Ok(None) => Err(ClientError::RendezvousResolutionFailed(
906 "Connection closed while waiting for identity response".to_string(),
907 )),
908 Err(_) => Err(ClientError::RendezvousResolutionFailed(
909 "Timeout waiting for identity response. The rendezvous code may be invalid, expired, or the target client may be disconnected.".to_string(),
910 )),
911 }
912 }
913
914 async fn perform_handshake(
916 proxy_client: &dyn ProxyClient,
917 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
918 remote_fingerprint: IdentityFingerprint,
919 psk: Option<Psk>,
920 ) -> Result<(MultiDeviceTransport, String), ClientError> {
921 let psk_id = psk.as_ref().map(|p| p.id());
923
924 let mut handshake = if let Some(psk) = psk {
926 InitiatorHandshake::with_psk(psk)
927 } else {
928 InitiatorHandshake::new()
929 };
930
931 let init_packet = handshake.send_start()?;
933
934 let msg = ProtocolMessage::HandshakeInit {
936 data: STANDARD.encode(init_packet.encode()?),
937 ciphersuite: format!("{:?}", handshake.ciphersuite()),
938 psk_id,
939 };
940
941 let msg_json = serde_json::to_string(&msg)?;
942 proxy_client
943 .send_to(remote_fingerprint, msg_json.into_bytes())
944 .await?;
945
946 debug!("Sent handshake init");
947
948 let response_timeout = Duration::from_secs(10);
950 let response: String = timeout(response_timeout, async {
951 loop {
952 if let Some(incoming) = incoming_rx.recv().await {
953 match incoming {
954 IncomingMessage::Send { payload, .. } => {
955 if let Ok(text) = String::from_utf8(payload)
957 && let Ok(ProtocolMessage::HandshakeResponse { data, .. }) =
958 serde_json::from_str::<ProtocolMessage>(&text)
959 {
960 return Ok::<String, ClientError>(data);
961 }
962 }
963 _ => continue,
964 }
965 }
966 }
967 })
968 .await
969 .map_err(|_| ClientError::Timeout("Waiting for handshake response".to_string()))??;
970
971 let response_bytes = STANDARD
973 .decode(&response)
974 .map_err(|e| ClientError::Serialization(format!("Invalid base64: {e}")))?;
975
976 let response_packet = ap_noise::HandshakePacket::decode(&response_bytes)
977 .map_err(|e| ClientError::NoiseProtocol(format!("Invalid packet: {e}")))?;
978
979 handshake.receive_finish(&response_packet)?;
981 let (transport, fingerprint) = handshake.finalize()?;
982
983 debug!("Handshake complete");
984 Ok((transport, fingerprint.to_string()))
985 }
986}
987
988fn uuid_v4() -> String {
989 let mut bytes = [0u8; 16];
991 let mut rng = rand::thread_rng();
992 rng.fill_bytes(&mut bytes);
993
994 bytes[6] = (bytes[6] & 0x0f) | 0x40;
996 bytes[8] = (bytes[8] & 0x3f) | 0x80;
997
998 format!(
999 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
1000 bytes[0],
1001 bytes[1],
1002 bytes[2],
1003 bytes[3],
1004 bytes[4],
1005 bytes[5],
1006 bytes[6],
1007 bytes[7],
1008 bytes[8],
1009 bytes[9],
1010 bytes[10],
1011 bytes[11],
1012 bytes[12],
1013 bytes[13],
1014 bytes[14],
1015 bytes[15]
1016 )
1017}