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 super::notify;
15use crate::traits::{ConnectionInfo, ConnectionStore, ConnectionUpdate, IdentityProvider};
16use crate::{
17 error::ClientError,
18 types::{
19 CredentialData, CredentialQuery, CredentialRequestPayload, CredentialResponsePayload,
20 ProtocolMessage,
21 },
22};
23
24const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
25
26#[derive(Debug, Clone)]
32pub enum RemoteClientNotification {
33 Connecting,
35 Connected {
37 fingerprint: IdentityFingerprint,
39 },
40 ReconnectingToSession {
42 fingerprint: IdentityFingerprint,
44 },
45 RendezvousResolving {
47 code: String,
49 },
50 RendezvousResolved {
52 fingerprint: IdentityFingerprint,
54 },
55 PskMode {
57 fingerprint: IdentityFingerprint,
59 },
60 HandshakeStart,
62 HandshakeProgress {
64 message: String,
66 },
67 HandshakeComplete,
69 HandshakeFingerprint {
71 fingerprint: String,
73 },
74 FingerprintVerified,
76 FingerprintRejected {
78 reason: String,
80 },
81 Ready {
83 can_request_credentials: bool,
85 },
86 CredentialRequestSent {
88 query: CredentialQuery,
90 },
91 CredentialReceived {
93 credential: CredentialData,
95 },
96 Error {
98 message: String,
100 context: Option<String>,
102 },
103 Disconnected {
105 reason: Option<String>,
107 },
108}
109
110#[derive(Debug)]
112pub struct RemoteClientFingerprintReply {
113 pub approved: bool,
115}
116
117#[derive(Debug)]
119pub enum RemoteClientRequest {
120 VerifyFingerprint {
122 fingerprint: String,
124 reply: oneshot::Sender<RemoteClientFingerprintReply>,
126 },
127}
128
129enum RemoteClientCommand {
135 PairWithHandshake {
136 rendezvous_code: String,
137 verify_fingerprint: bool,
138 reply: oneshot::Sender<Result<IdentityFingerprint, ClientError>>,
139 },
140 PairWithPsk {
141 psk: Psk,
142 remote_fingerprint: IdentityFingerprint,
143 reply: oneshot::Sender<Result<(), ClientError>>,
144 },
145 LoadCachedConnection {
146 remote_fingerprint: IdentityFingerprint,
147 reply: oneshot::Sender<Result<(), ClientError>>,
148 },
149 RequestCredential {
150 query: CredentialQuery,
151 reply: oneshot::Sender<Result<CredentialData, ClientError>>,
152 },
153 ListConnections {
154 reply: oneshot::Sender<Vec<ConnectionInfo>>,
155 },
156 HasConnection {
157 fingerprint: IdentityFingerprint,
158 reply: oneshot::Sender<bool>,
159 },
160}
161
162pub struct RemoteClientHandle {
177 pub client: RemoteClient,
178 pub notifications: mpsc::Receiver<RemoteClientNotification>,
179 pub requests: mpsc::Receiver<RemoteClientRequest>,
180}
181
182#[derive(Clone)]
183pub struct RemoteClient {
184 command_tx: mpsc::Sender<RemoteClientCommand>,
185}
186
187impl RemoteClient {
188 pub async fn connect(
197 identity_provider: Box<dyn IdentityProvider>,
198 connection_store: Box<dyn ConnectionStore>,
199 mut proxy_client: Box<dyn ProxyClient>,
200 ) -> Result<RemoteClientHandle, ClientError> {
201 let identity_keypair = identity_provider.identity().await;
202 let own_fingerprint = identity_keypair.identity().fingerprint();
203
204 debug!("Connecting to proxy with identity {:?}", own_fingerprint);
205
206 let (notification_tx, notification_rx) = mpsc::channel(32);
207 let (request_tx, request_rx) = mpsc::channel(32);
208
209 notify!(notification_tx, RemoteClientNotification::Connecting);
210
211 let incoming_rx = proxy_client.connect(identity_keypair).await?;
212
213 notify!(
214 notification_tx,
215 RemoteClientNotification::Connected {
216 fingerprint: own_fingerprint,
217 }
218 );
219
220 debug!("Connected to proxy successfully");
221
222 let (command_tx, command_rx) = mpsc::channel(32);
224
225 let inner = RemoteClientInner {
227 connection_store,
228 proxy_client,
229 transport: None,
230 remote_fingerprint: None,
231 };
232
233 #[cfg(target_arch = "wasm32")]
235 wasm_bindgen_futures::spawn_local(inner.run_event_loop(
236 incoming_rx,
237 command_rx,
238 notification_tx,
239 request_tx,
240 ));
241 #[cfg(not(target_arch = "wasm32"))]
242 tokio::spawn(inner.run_event_loop(incoming_rx, command_rx, notification_tx, request_tx));
243
244 Ok(RemoteClientHandle {
245 client: Self { command_tx },
246 notifications: notification_rx,
247 requests: request_rx,
248 })
249 }
250
251 pub async fn pair_with_handshake(
258 &self,
259 rendezvous_code: String,
260 verify_fingerprint: bool,
261 ) -> Result<IdentityFingerprint, ClientError> {
262 let (tx, rx) = oneshot::channel();
263 self.command_tx
264 .send(RemoteClientCommand::PairWithHandshake {
265 rendezvous_code,
266 verify_fingerprint,
267 reply: tx,
268 })
269 .await
270 .map_err(|_| ClientError::ChannelClosed)?;
271 rx.await.map_err(|_| ClientError::ChannelClosed)?
272 }
273
274 pub async fn pair_with_psk(
279 &self,
280 psk: Psk,
281 remote_fingerprint: IdentityFingerprint,
282 ) -> Result<(), ClientError> {
283 let (tx, rx) = oneshot::channel();
284 self.command_tx
285 .send(RemoteClientCommand::PairWithPsk {
286 psk,
287 remote_fingerprint,
288 reply: tx,
289 })
290 .await
291 .map_err(|_| ClientError::ChannelClosed)?;
292 rx.await.map_err(|_| ClientError::ChannelClosed)?
293 }
294
295 pub async fn load_cached_connection(
300 &self,
301 remote_fingerprint: IdentityFingerprint,
302 ) -> Result<(), ClientError> {
303 let (tx, rx) = oneshot::channel();
304 self.command_tx
305 .send(RemoteClientCommand::LoadCachedConnection {
306 remote_fingerprint,
307 reply: tx,
308 })
309 .await
310 .map_err(|_| ClientError::ChannelClosed)?;
311 rx.await.map_err(|_| ClientError::ChannelClosed)?
312 }
313
314 pub async fn request_credential(
316 &self,
317 query: &CredentialQuery,
318 ) -> Result<CredentialData, ClientError> {
319 let (tx, rx) = oneshot::channel();
320 self.command_tx
321 .send(RemoteClientCommand::RequestCredential {
322 query: query.clone(),
323 reply: tx,
324 })
325 .await
326 .map_err(|_| ClientError::ChannelClosed)?;
327 rx.await.map_err(|_| ClientError::ChannelClosed)?
328 }
329
330 pub async fn list_connections(&self) -> Result<Vec<ConnectionInfo>, ClientError> {
332 let (tx, rx) = oneshot::channel();
333 self.command_tx
334 .send(RemoteClientCommand::ListConnections { reply: tx })
335 .await
336 .map_err(|_| ClientError::ChannelClosed)?;
337 rx.await.map_err(|_| ClientError::ChannelClosed)
338 }
339
340 pub async fn has_connection(
342 &self,
343 fingerprint: IdentityFingerprint,
344 ) -> Result<bool, ClientError> {
345 let (tx, rx) = oneshot::channel();
346 self.command_tx
347 .send(RemoteClientCommand::HasConnection {
348 fingerprint,
349 reply: tx,
350 })
351 .await
352 .map_err(|_| ClientError::ChannelClosed)?;
353 rx.await.map_err(|_| ClientError::ChannelClosed)
354 }
355}
356
357struct RemoteClientInner {
363 connection_store: Box<dyn ConnectionStore>,
364 proxy_client: Box<dyn ProxyClient>,
365 transport: Option<MultiDeviceTransport>,
366 remote_fingerprint: Option<IdentityFingerprint>,
367}
368
369impl RemoteClientInner {
370 async fn run_event_loop(
372 mut self,
373 mut incoming_rx: mpsc::UnboundedReceiver<IncomingMessage>,
374 mut command_rx: mpsc::Receiver<RemoteClientCommand>,
375 notification_tx: mpsc::Sender<RemoteClientNotification>,
376 request_tx: mpsc::Sender<RemoteClientRequest>,
377 ) {
378 loop {
379 tokio::select! {
380 msg = incoming_rx.recv() => {
381 match msg {
382 Some(_) => {
383 debug!("Received message while idle");
385 }
386 None => {
387 notify!(notification_tx, RemoteClientNotification::Disconnected {
389 reason: Some("Proxy connection closed".to_string()),
390 });
391 return;
392 }
393 }
394 }
395 cmd = command_rx.recv() => {
396 match cmd {
397 Some(cmd) => {
398 self.handle_command(
399 cmd,
400 &mut incoming_rx,
401 ¬ification_tx,
402 &request_tx,
403 ).await;
404 }
405 None => {
406 debug!("All RemoteClient handles dropped, shutting down event loop");
408 self.proxy_client.disconnect().await.ok();
409 return;
410 }
411 }
412 }
413 }
414 }
415 }
416
417 async fn handle_command(
419 &mut self,
420 cmd: RemoteClientCommand,
421 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
422 notification_tx: &mpsc::Sender<RemoteClientNotification>,
423 request_tx: &mpsc::Sender<RemoteClientRequest>,
424 ) {
425 match cmd {
426 RemoteClientCommand::PairWithHandshake {
427 rendezvous_code,
428 verify_fingerprint,
429 reply,
430 } => {
431 let result = self
432 .do_pair_with_handshake(
433 rendezvous_code,
434 verify_fingerprint,
435 incoming_rx,
436 notification_tx,
437 request_tx,
438 )
439 .await;
440 let _ = reply.send(result);
441 }
442 RemoteClientCommand::PairWithPsk {
443 psk,
444 remote_fingerprint,
445 reply,
446 } => {
447 let result = self
448 .do_pair_with_psk(psk, remote_fingerprint, incoming_rx, notification_tx)
449 .await;
450 let _ = reply.send(result);
451 }
452 RemoteClientCommand::LoadCachedConnection {
453 remote_fingerprint,
454 reply,
455 } => {
456 let result = self
457 .do_load_cached_connection(remote_fingerprint, notification_tx)
458 .await;
459 let _ = reply.send(result);
460 }
461 RemoteClientCommand::RequestCredential { query, reply } => {
462 let result = self
463 .do_request_credential(query, incoming_rx, notification_tx)
464 .await;
465 let _ = reply.send(result);
466 }
467 RemoteClientCommand::ListConnections { reply } => {
468 let connections = self.connection_store.list().await;
469 let _ = reply.send(connections);
470 }
471 RemoteClientCommand::HasConnection { fingerprint, reply } => {
472 let has = self.connection_store.get(&fingerprint).await.is_some();
473 let _ = reply.send(has);
474 }
475 }
476 }
477
478 async fn do_pair_with_handshake(
481 &mut self,
482 rendezvous_code: String,
483 verify_fingerprint: bool,
484 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
485 notification_tx: &mpsc::Sender<RemoteClientNotification>,
486 request_tx: &mpsc::Sender<RemoteClientRequest>,
487 ) -> Result<IdentityFingerprint, ClientError> {
488 notify!(
490 notification_tx,
491 RemoteClientNotification::RendezvousResolving {
492 code: rendezvous_code.clone(),
493 }
494 );
495
496 let remote_fingerprint =
497 Self::resolve_rendezvous(self.proxy_client.as_ref(), incoming_rx, &rendezvous_code)
498 .await?;
499
500 notify!(
501 notification_tx,
502 RemoteClientNotification::RendezvousResolved {
503 fingerprint: remote_fingerprint,
504 }
505 );
506
507 notify!(notification_tx, RemoteClientNotification::HandshakeStart);
509
510 let (transport, fingerprint_str) = Self::perform_handshake(
511 self.proxy_client.as_ref(),
512 incoming_rx,
513 remote_fingerprint,
514 None,
515 )
516 .await?;
517
518 notify!(notification_tx, RemoteClientNotification::HandshakeComplete);
519
520 notify!(
522 notification_tx,
523 RemoteClientNotification::HandshakeFingerprint {
524 fingerprint: fingerprint_str.clone(),
525 }
526 );
527
528 if verify_fingerprint {
529 let (fp_tx, fp_rx) = oneshot::channel();
531 if request_tx.capacity() == 0 {
532 warn!("Request channel full, waiting for consumer to drain");
533 }
534 request_tx
535 .send(RemoteClientRequest::VerifyFingerprint {
536 fingerprint: fingerprint_str,
537 reply: fp_tx,
538 })
539 .await
540 .map_err(|_| ClientError::ChannelClosed)?;
541
542 match timeout(Duration::from_secs(60), fp_rx).await {
544 Ok(Ok(RemoteClientFingerprintReply { approved: true })) => {
545 notify!(
546 notification_tx,
547 RemoteClientNotification::FingerprintVerified
548 );
549 }
550 Ok(Ok(RemoteClientFingerprintReply { approved: false })) => {
551 self.proxy_client.disconnect().await.ok();
552 notify!(
553 notification_tx,
554 RemoteClientNotification::FingerprintRejected {
555 reason: "User rejected fingerprint verification".to_string(),
556 }
557 );
558 return Err(ClientError::FingerprintRejected);
559 }
560 Ok(Err(_)) => {
561 return Err(ClientError::ChannelClosed);
562 }
563 Err(_) => {
564 self.proxy_client.disconnect().await.ok();
565 return Err(ClientError::Timeout(
566 "Fingerprint verification timeout".to_string(),
567 ));
568 }
569 }
570 }
571
572 self.finalize_pairing(transport, remote_fingerprint, notification_tx)
574 .await?;
575
576 Ok(remote_fingerprint)
577 }
578
579 async fn do_pair_with_psk(
582 &mut self,
583 psk: Psk,
584 remote_fingerprint: IdentityFingerprint,
585 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
586 notification_tx: &mpsc::Sender<RemoteClientNotification>,
587 ) -> Result<(), ClientError> {
588 notify!(
589 notification_tx,
590 RemoteClientNotification::PskMode {
591 fingerprint: remote_fingerprint,
592 }
593 );
594
595 notify!(notification_tx, RemoteClientNotification::HandshakeStart);
597
598 let (transport, _fingerprint_str) = Self::perform_handshake(
599 self.proxy_client.as_ref(),
600 incoming_rx,
601 remote_fingerprint,
602 Some(psk),
603 )
604 .await?;
605
606 notify!(notification_tx, RemoteClientNotification::HandshakeComplete);
607
608 notify!(
610 notification_tx,
611 RemoteClientNotification::FingerprintVerified
612 );
613
614 self.finalize_pairing(transport, remote_fingerprint, notification_tx)
616 .await?;
617
618 Ok(())
619 }
620
621 async fn do_load_cached_connection(
624 &mut self,
625 remote_fingerprint: IdentityFingerprint,
626 notification_tx: &mpsc::Sender<RemoteClientNotification>,
627 ) -> Result<(), ClientError> {
628 let connection = self
629 .connection_store
630 .get(&remote_fingerprint)
631 .await
632 .ok_or(ClientError::ConnectionNotFound)?;
633
634 let transport = connection
635 .transport_state
636 .ok_or(ClientError::ConnectionNotFound)?;
637
638 notify!(
639 notification_tx,
640 RemoteClientNotification::ReconnectingToSession {
641 fingerprint: remote_fingerprint,
642 }
643 );
644
645 notify!(notification_tx, RemoteClientNotification::HandshakeComplete);
646
647 notify!(
649 notification_tx,
650 RemoteClientNotification::FingerprintVerified
651 );
652
653 self.connection_store
655 .update(ConnectionUpdate {
656 fingerprint: remote_fingerprint,
657 last_connected_at: crate::compat::now_seconds(),
658 })
659 .await?;
660
661 self.transport = Some(transport);
662 self.remote_fingerprint = Some(remote_fingerprint);
663
664 notify!(
665 notification_tx,
666 RemoteClientNotification::Ready {
667 can_request_credentials: true,
668 }
669 );
670
671 debug!("Reconnected to cached connection");
672 Ok(())
673 }
674
675 async fn finalize_pairing(
678 &mut self,
679 transport: MultiDeviceTransport,
680 remote_fingerprint: IdentityFingerprint,
681 notification_tx: &mpsc::Sender<RemoteClientNotification>,
682 ) -> Result<(), ClientError> {
683 let now = crate::compat::now_seconds();
684 self.connection_store
685 .save(ConnectionInfo {
686 fingerprint: remote_fingerprint,
687 name: None,
688 cached_at: now,
689 last_connected_at: now,
690 transport_state: Some(transport.clone()),
691 })
692 .await?;
693
694 self.transport = Some(transport);
696 self.remote_fingerprint = Some(remote_fingerprint);
697
698 notify!(
700 notification_tx,
701 RemoteClientNotification::Ready {
702 can_request_credentials: true,
703 }
704 );
705
706 debug!("Connection established successfully");
707 Ok(())
708 }
709
710 async fn do_request_credential(
713 &mut self,
714 query: CredentialQuery,
715 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
716 notification_tx: &mpsc::Sender<RemoteClientNotification>,
717 ) -> Result<CredentialData, ClientError> {
718 let remote_fingerprint = self.remote_fingerprint.ok_or(ClientError::NotInitialized)?;
719
720 #[allow(clippy::string_slice)]
722 let request_id = format!("req-{}-{}", now_millis(), &uuid_v4()[..8]);
723
724 debug!("Requesting credential for query: {:?}", query);
725
726 let request = CredentialRequestPayload {
728 request_type: "credential_request".to_string(),
729 query: query.clone(),
730 timestamp: now_millis(),
731 request_id: request_id.clone(),
732 };
733
734 let request_json = serde_json::to_string(&request)?;
735
736 let encrypted_data = {
737 let transport = self
738 .transport
739 .as_mut()
740 .ok_or(ClientError::SecureChannelNotEstablished)?;
741 let encrypted_packet = transport
742 .encrypt(request_json.as_bytes())
743 .map_err(|e| ClientError::NoiseProtocol(e.to_string()))?;
744 STANDARD.encode(encrypted_packet.encode())
745 };
746
747 let msg = ProtocolMessage::CredentialRequest {
748 encrypted: encrypted_data,
749 };
750
751 let msg_json = serde_json::to_string(&msg)?;
753 self.proxy_client
754 .send_to(remote_fingerprint, msg_json.into_bytes())
755 .await?;
756
757 notify!(
759 notification_tx,
760 RemoteClientNotification::CredentialRequestSent {
761 query: query.clone(),
762 }
763 );
764
765 match timeout(
767 DEFAULT_TIMEOUT,
768 self.receive_credential_response(&request_id, incoming_rx, notification_tx),
769 )
770 .await
771 {
772 Ok(result) => result,
773 Err(_) => Err(ClientError::Timeout(format!(
774 "Timeout waiting for credential response for query: {query:?}"
775 ))),
776 }
777 }
778
779 async fn receive_credential_response(
784 &mut self,
785 request_id: &str,
786 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
787 notification_tx: &mpsc::Sender<RemoteClientNotification>,
788 ) -> Result<CredentialData, ClientError> {
789 loop {
790 match incoming_rx.recv().await {
791 Some(IncomingMessage::Send { payload, .. }) => {
792 if let Ok(text) = String::from_utf8(payload)
793 && let Ok(ProtocolMessage::CredentialResponse { encrypted }) =
794 serde_json::from_str::<ProtocolMessage>(&text)
795 {
796 match self
797 .decrypt_credential_response(&encrypted, request_id, notification_tx)
798 .await
799 {
800 Ok(credential) => return Ok(credential),
801 Err(ClientError::CredentialRequestFailed(ref msg))
802 if msg.contains("request_id mismatch") =>
803 {
804 debug!("Skipping stale credential response: {msg}");
806 continue;
807 }
808 Err(e) => return Err(e),
809 }
810 }
811 }
812 Some(_) => {
813 }
815 None => {
816 return Err(ClientError::ChannelClosed);
817 }
818 }
819 }
820 }
821
822 async fn decrypt_credential_response(
824 &mut self,
825 encrypted: &str,
826 request_id: &str,
827 notification_tx: &mpsc::Sender<RemoteClientNotification>,
828 ) -> Result<CredentialData, ClientError> {
829 let encrypted_bytes = STANDARD
830 .decode(encrypted)
831 .map_err(|e| ClientError::Serialization(format!("Invalid base64: {e}")))?;
832
833 let packet = ap_noise::TransportPacket::decode(&encrypted_bytes)
834 .map_err(|e| ClientError::NoiseProtocol(format!("Invalid packet: {e}")))?;
835
836 let transport = self
837 .transport
838 .as_mut()
839 .ok_or(ClientError::SecureChannelNotEstablished)?;
840
841 let decrypted = transport
842 .decrypt(&packet)
843 .map_err(|e| ClientError::NoiseProtocol(e.to_string()))?;
844
845 let response: CredentialResponsePayload = serde_json::from_slice(&decrypted)?;
846
847 if response.request_id.as_deref() != Some(request_id) {
849 warn!(
850 "Ignoring response with mismatched request_id: {:?}",
851 response.request_id
852 );
853 return Err(ClientError::CredentialRequestFailed(
854 "Response request_id mismatch".to_string(),
855 ));
856 }
857
858 if let Some(error) = response.error {
859 return Err(ClientError::CredentialRequestFailed(error));
860 }
861
862 if let Some(credential) = response.credential {
863 notify!(
864 notification_tx,
865 RemoteClientNotification::CredentialReceived {
866 credential: credential.clone(),
867 }
868 );
869 Ok(credential)
870 } else {
871 Err(ClientError::CredentialRequestFailed(
872 "Response contains neither credential nor error".to_string(),
873 ))
874 }
875 }
876
877 async fn resolve_rendezvous(
881 proxy_client: &dyn ProxyClient,
882 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
883 rendezvous_code: &str,
884 ) -> Result<IdentityFingerprint, ClientError> {
885 proxy_client
887 .request_identity(RendezvousCode::from_string(rendezvous_code.to_string()))
888 .await
889 .map_err(|e| ClientError::RendezvousResolutionFailed(e.to_string()))?;
890
891 let timeout_duration = Duration::from_secs(10);
893 match timeout(timeout_duration, async {
894 while let Some(msg) = incoming_rx.recv().await {
895 if let IncomingMessage::IdentityInfo { fingerprint, .. } = msg {
896 return Some(fingerprint);
897 }
898 }
899 None
900 })
901 .await
902 {
903 Ok(Some(fingerprint)) => Ok(fingerprint),
904 Ok(None) => Err(ClientError::RendezvousResolutionFailed(
905 "Connection closed while waiting for identity response".to_string(),
906 )),
907 Err(_) => Err(ClientError::RendezvousResolutionFailed(
908 "Timeout waiting for identity response. The rendezvous code may be invalid, expired, or the target client may be disconnected.".to_string(),
909 )),
910 }
911 }
912
913 async fn perform_handshake(
915 proxy_client: &dyn ProxyClient,
916 incoming_rx: &mut mpsc::UnboundedReceiver<IncomingMessage>,
917 remote_fingerprint: IdentityFingerprint,
918 psk: Option<Psk>,
919 ) -> Result<(MultiDeviceTransport, String), ClientError> {
920 let psk_id = psk.as_ref().map(|p| p.id());
922
923 let mut handshake = if let Some(psk) = psk {
925 InitiatorHandshake::with_psk(psk)
926 } else {
927 InitiatorHandshake::new()
928 };
929
930 let init_packet = handshake.send_start()?;
932
933 let msg = ProtocolMessage::HandshakeInit {
935 data: STANDARD.encode(init_packet.encode()?),
936 ciphersuite: format!("{:?}", handshake.ciphersuite()),
937 psk_id,
938 };
939
940 let msg_json = serde_json::to_string(&msg)?;
941 proxy_client
942 .send_to(remote_fingerprint, msg_json.into_bytes())
943 .await?;
944
945 debug!("Sent handshake init");
946
947 let response_timeout = Duration::from_secs(10);
949 let response: String = timeout(response_timeout, async {
950 loop {
951 if let Some(incoming) = incoming_rx.recv().await {
952 match incoming {
953 IncomingMessage::Send { payload, .. } => {
954 if let Ok(text) = String::from_utf8(payload)
956 && let Ok(ProtocolMessage::HandshakeResponse { data, .. }) =
957 serde_json::from_str::<ProtocolMessage>(&text)
958 {
959 return Ok::<String, ClientError>(data);
960 }
961 }
962 _ => continue,
963 }
964 }
965 }
966 })
967 .await
968 .map_err(|_| ClientError::Timeout("Waiting for handshake response".to_string()))??;
969
970 let response_bytes = STANDARD
972 .decode(&response)
973 .map_err(|e| ClientError::Serialization(format!("Invalid base64: {e}")))?;
974
975 let response_packet = ap_noise::HandshakePacket::decode(&response_bytes)
976 .map_err(|e| ClientError::NoiseProtocol(format!("Invalid packet: {e}")))?;
977
978 handshake.receive_finish(&response_packet)?;
980 let (transport, fingerprint) = handshake.finalize()?;
981
982 debug!("Handshake complete");
983 Ok((transport, fingerprint.to_string()))
984 }
985}
986
987fn uuid_v4() -> String {
988 let mut bytes = [0u8; 16];
990 let mut rng = rand::thread_rng();
991 rng.fill_bytes(&mut bytes);
992
993 bytes[6] = (bytes[6] & 0x0f) | 0x40;
995 bytes[8] = (bytes[8] & 0x3f) | 0x80;
996
997 format!(
998 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
999 bytes[0],
1000 bytes[1],
1001 bytes[2],
1002 bytes[3],
1003 bytes[4],
1004 bytes[5],
1005 bytes[6],
1006 bytes[7],
1007 bytes[8],
1008 bytes[9],
1009 bytes[10],
1010 bytes[11],
1011 bytes[12],
1012 bytes[13],
1013 bytes[14],
1014 bytes[15]
1015 )
1016}