1use crate::LogId;
11use crate::transport::DEFAULT_HANDSHAKE_TIMEOUT;
12use crate::transport::framing::FrameReader;
13use crate::transport::handshake;
14use crate::transport::io::deadline_after;
15use crate::transport::outbound::{Outbound, Side};
16use crate::transport::sealing;
17use crate::transport::sender::Sender;
18use crate::transport::server::Attestation;
19use crate::transport::{
20 CRYPTO_DOMAIN_WIRE, CRYPTO_DOMAIN_WIRE_ARK_TO_HOST, CRYPTO_DOMAIN_WIRE_HOST_TO_ARK, Closer,
21 Error, Read, Stream, Write,
22};
23use darkbio_crypto::{cbor, cose, xdsa, xhpke};
24use darkbio_trust as trust;
25use std::fmt;
26use std::sync::{Arc, Mutex};
27use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
28use tracing::{debug, info, trace, warn};
29
30pub trait Verifier {
36 type Info;
38
39 fn verify(
46 &self,
47 attestation: &Attestation,
48 now: SystemTime,
49 ) -> Result<(xdsa::PublicKey, Self::Info), String>;
50}
51
52impl Verifier for xdsa::PublicKey {
57 type Info = Attestation;
58
59 fn verify(
60 &self,
61 attestation: &Attestation,
62 _: SystemTime,
63 ) -> Result<(xdsa::PublicKey, Self::Info), String> {
64 Ok((self.clone(), attestation.clone()))
65 }
66}
67
68#[derive(Debug)]
74pub struct Roots<'a> {
75 pub hardware: &'a [xdsa::PublicKey],
77 pub emulator: &'a [xdsa::PublicKey],
79}
80
81impl Verifier for Roots<'_> {
82 type Info = trust::device::Device;
83
84 fn verify(
85 &self,
86 attestation: &Attestation,
87 now: SystemTime,
88 ) -> Result<(xdsa::PublicKey, Self::Info), String> {
89 let now = now
91 .duration_since(UNIX_EPOCH)
92 .map_err(|err| err.to_string())?
93 .as_secs();
94
95 let device = trust::device::verify(
97 attestation.as_bytes(),
98 self.hardware,
99 self.emulator,
100 Some(now),
101 )
102 .map_err(|err| err.to_string())?;
103 Ok((device.identity.clone(), device))
104 }
105}
106
107pub struct Client<R: Read, W: Write> {
120 handshake_timeout: Duration,
122
123 reader: FrameReader<R>,
125 receiver: Option<xhpke::Receiver>,
127 sealer: Option<Arc<Mutex<xhpke::Sender>>>,
129 outbound: Arc<Outbound<W>>,
131 log_id: LogId,
133
134 #[cfg(test)]
136 pub(super) ack_hook: Option<Box<dyn FnOnce() + Send>>,
137}
138
139impl<R: Read, W: Write> Client<R, W> {
140 pub fn new(stream: Stream<R, W>) -> Self {
147 let (reader, writer, close, timeout) = stream.into_parts();
148
149 let outbound = Arc::new(Outbound::new(writer, Side::Client, close.clone(), timeout));
150
151 Self {
152 handshake_timeout: DEFAULT_HANDSHAKE_TIMEOUT,
153 reader: FrameReader::new(reader, close),
154 receiver: None,
155 sealer: None,
156 outbound,
157 log_id: LogId::default(),
158 #[cfg(test)]
159 ack_hook: None,
160 }
161 }
162
163 pub fn set_handshake_timeout(mut self, timeout: Duration) -> Self {
174 self.handshake_timeout = timeout;
175 self
176 }
177
178 pub fn closer(&self) -> Closer {
180 self.outbound.closer()
181 }
182
183 pub fn close(&self) {
188 self.outbound.close();
189 }
190
191 pub fn connect<V: Verifier>(&mut self, verifier: &V) -> Result<(Sender<W>, V::Info), Error> {
221 let deadline = deadline_after(&self.outbound.clock, self.handshake_timeout);
223
224 let host_xdsa_sk = xdsa::SecretKey::generate();
226 let host_xhpke_sk = xhpke::SecretKey::generate();
227
228 self.handshake(verifier, host_xdsa_sk, host_xhpke_sk, None, deadline)
229 }
230
231 fn handshake<V: Verifier>(
237 &mut self,
238 verifier: &V,
239 host_xdsa_sk: xdsa::SecretKey,
240 host_xhpke_sk: xhpke::SecretKey,
241 timestamp: Option<i64>,
242 deadline: Instant,
243 ) -> Result<(Sender<W>, V::Info), Error> {
244 let host_xdsa_pk = host_xdsa_sk.public_key();
246 let host_xhpke_pk = host_xhpke_sk.public_key();
247 debug!("starting wire handshake");
248
249 let hello = cbor::encode(&handshake::HostHello {
251 host_signer: host_xdsa_pk.clone(),
252 host_crypto: host_xhpke_pk.clone(),
253 })
254 .map_err(|err| {
255 self.end_session();
256 Error::HandshakeFailed(format!("failed to encode client hello: {}", err))
257 })?;
258
259 self.receiver = None;
262 self.sealer = None;
263 self.outbound.send_reset(deadline)?;
264 self.outbound.send_packet(&hello, Some(deadline))?;
265
266 let recipient = host_xhpke_pk.fingerprint();
270 let packet = loop {
271 let packet = match self.reader.next_packet(Some(deadline)) {
272 Ok(Some(packet)) => packet,
273 Ok(None) | Err(Error::FrameDecodingFailed(_) | Error::FrameTooLarge(_)) => &[],
274 Err(err) => return Err(err),
275 };
276 if cose::recipient(packet).is_ok_and(|fp| fp == recipient) {
277 break packet;
278 }
279 debug!("skipping stale frame during handshake");
280 };
281 let auth = handshake::ArkHelloAuth {
282 host_signer: host_xdsa_pk.clone(),
283 host_crypto: host_xhpke_pk.clone(),
284 };
285
286 let sign1 =
288 cose::decrypt(packet, &auth, &host_xhpke_sk, CRYPTO_DOMAIN_WIRE).map_err(|err| {
289 Error::HandshakeFailed(format!("failed to decrypt server hello: {}", err))
290 })?;
291
292 let unverified: handshake::ArkHello = cose::peek(&sign1).map_err(|err| {
294 Error::HandshakeFailed(format!("invalid server hello payload: {}", err))
295 })?;
296
297 let attestation = Attestation::new(unverified.ark_attest)?;
300 let (ark_identity, info) = verifier
301 .verify(&attestation, self.outbound.clock.system_time())
302 .map_err(Error::HandshakeFailed)?;
303
304 let ark_hello: handshake::ArkHello = cose::verify_at(
306 &sign1,
307 &auth,
308 &ark_identity,
309 CRYPTO_DOMAIN_WIRE,
310 None,
311 0, )
313 .map_err(|err| {
314 Error::HandshakeFailed(format!("server hello signature invalid: {}", err))
315 })?;
316
317 let enc_a2h: [u8; xhpke::ENCAP_KEY_SIZE] = ark_hello
319 .a2h_encap
320 .try_into()
321 .map_err(|_| Error::HandshakeFailed("invalid a2h_encap size".into()))?;
322 let receiver = host_xhpke_sk
323 .new_receiver(&enc_a2h, CRYPTO_DOMAIN_WIRE_ARK_TO_HOST)
324 .map_err(|err| {
325 Error::HandshakeFailed(format!("client receiver setup failed: {}", err))
326 })?;
327
328 let ark_xhpke_pk = ark_hello.ark_crypto;
330 let (sender, enc_h2a) = ark_xhpke_pk
331 .new_sender(CRYPTO_DOMAIN_WIRE_HOST_TO_ARK)
332 .map_err(|err| {
333 Error::HandshakeFailed(format!("client sender setup failed: {}", err))
334 })?;
335
336 let ack = handshake::HostAck {
338 h2a_encap: enc_h2a.to_vec(),
339 };
340 let auth = handshake::HostAckAuth {
341 ark_signer: ark_identity,
342 ark_crypto: ark_xhpke_pk.clone(),
343 };
344 let ack = cose::seal_at(
346 &ack,
347 &auth,
348 &host_xdsa_sk,
349 &ark_xhpke_pk,
350 CRYPTO_DOMAIN_WIRE,
351 &cose::Padding::None,
352 timestamp.unwrap_or_else(|| handshake::timestamp(&self.outbound.clock)),
353 )
354 .map_err(|err| Error::HandshakeFailed(format!("failed to seal client ack: {}", err)))?;
355
356 self.outbound.send_packet(&ack, Some(deadline))?;
359
360 #[cfg(test)]
362 if let Some(hook) = self.ack_hook.take() {
363 hook();
364 }
365
366 let sender = self.new_session(sender, receiver);
368 info!(
369 "wire session {} established with ark {}",
370 self.log_id,
371 hex(&auth.ark_signer.fingerprint())
372 );
373 Ok((sender, info))
374 }
375
376 pub fn recv(&mut self) -> Result<Vec<u8>, Error> {
392 let receiver = self
394 .receiver
395 .as_mut()
396 .ok_or_else(|| Error::EncryptionFailed("no active session".into()))?;
397
398 let packet = match self.reader.next_packet(None) {
402 Err(err) => {
403 if matches!(err, Error::FrameDecodingFailed(_) | Error::FrameTooLarge(_)) {
404 warn!("ending session {}: {}", self.log_id, err);
405 }
406 self.end_session();
407 return Err(err);
408 }
409 Ok(None) => {
410 info!("wire session {} reset by ark", self.log_id);
411 self.end_session();
412 return Err(Error::SessionReset);
413 }
414 Ok(Some(packet)) => packet,
415 };
416
417 let sealer = self
420 .sealer
421 .as_ref()
422 .expect("receiver has a sending context");
423 let opened = sealing::open(receiver, packet);
424 let undecryptable = opened.is_err();
425 let message = match self.outbound.finish_receive(sealer, opened) {
426 Err(err) => {
427 if undecryptable {
428 warn!("ending session {}: {}", self.log_id, err);
429 } else {
430 debug!(
431 "discarding message read after session {} ended",
432 self.log_id
433 );
434 }
435 self.end_session();
436 return Err(err);
437 }
438 Ok(message) => message,
439 };
440 trace!("received ark-to-host message ({} bytes)", packet.len());
441 Ok(message)
442 }
443
444 fn new_session(&mut self, sender: xhpke::Sender, receiver: xhpke::Receiver) -> Sender<W> {
455 let sealer = Arc::new(Mutex::new(sender));
456 let sender = self.outbound.bind(&sealer);
457 self.log_id = sender.log_id();
458
459 self.receiver = Some(receiver);
460 self.sealer = Some(sealer);
461
462 sender
463 }
464
465 fn end_session(&mut self) {
477 if let Some(sealer) = self.sealer.as_ref() {
478 self.outbound.end(sealer);
479 }
480 self.receiver = None;
481 self.sealer = None;
482 }
483
484 #[cfg(any(test, feature = "fuzz"))]
487 pub(crate) fn watch_writer(&self) -> impl FnOnce() + use<R, W> {
488 let outbound = self.outbound.clone();
489 move || outbound.wait_writers(1)
490 }
491
492 #[doc(hidden)]
496 #[inline]
497 #[cfg(any(test, feature = "bench", feature = "fuzz"))]
498 #[cfg_attr(coverage_nightly, coverage(off))]
499 pub fn handshake_with_keys<V: Verifier>(
500 &mut self,
501 verifier: &V,
502 host_xdsa_sk: xdsa::SecretKey,
503 host_xhpke_sk: xhpke::SecretKey,
504 timestamp: i64,
505 ) -> Result<(Sender<W>, V::Info), Error> {
506 self.handshake(
507 verifier,
508 host_xdsa_sk,
509 host_xhpke_sk,
510 Some(timestamp),
511 deadline_after(&self.outbound.clock, self.handshake_timeout),
512 )
513 }
514
515 #[doc(hidden)]
517 #[inline]
518 #[cfg(any(test, feature = "bench", feature = "fuzz"))]
519 #[cfg_attr(coverage_nightly, coverage(off))]
520 pub fn next_packet_blob(&mut self) -> Result<Option<&[u8]>, Error> {
521 self.reader.next_packet(None)
522 }
523
524 #[doc(hidden)]
526 #[inline]
527 #[cfg(any(test, feature = "bench", feature = "fuzz"))]
528 #[cfg_attr(coverage_nightly, coverage(off))]
529 pub fn send_packet_blob(&mut self, packet: &[u8]) -> Result<(), Error> {
530 self.outbound.send_packet(packet, None)
531 }
532
533 #[doc(hidden)]
535 #[inline]
536 #[cfg(any(test, feature = "bench", feature = "fuzz"))]
537 #[cfg_attr(coverage_nightly, coverage(off))]
538 pub fn next_frame_blob(&mut self) -> Result<&[u8], Error> {
539 self.reader.next_frame_blob()
540 }
541
542 #[doc(hidden)]
544 #[inline]
545 #[cfg(any(test, feature = "bench", feature = "fuzz"))]
546 #[cfg_attr(coverage_nightly, coverage(off))]
547 pub fn send_frame_blob(&mut self, frame: &[u8]) -> Result<(), Error> {
548 self.outbound.send_frame_blob(frame)
549 }
550}
551
552impl<R: Read, W: Write> Drop for Client<R, W> {
553 fn drop(&mut self) {
560 self.outbound.close();
561 self.end_session();
562 }
563}
564
565impl<R: Read, W: Write> fmt::Debug for Client<R, W> {
566 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
569 f.debug_struct("Client")
570 .field("session", &self.log_id)
571 .field("connected", &self.sealer.is_some())
572 .field("handshake_timeout", &self.handshake_timeout)
573 .finish_non_exhaustive()
574 }
575}
576
577fn hex(fingerprint: &xdsa::Fingerprint) -> String {
579 fingerprint
580 .to_bytes()
581 .iter()
582 .map(|byte| format!("{byte:02x}"))
583 .collect()
584}
585
586#[cfg(test)]
588#[cfg_attr(coverage_nightly, coverage(off))]
589mod tests {
590 use super::*;
591 use crate::transport::DEFAULT_WRITE_TIMEOUT;
592 use crate::transport::framing::FrameWriter;
593 use crate::transport::mock::{payload, self_attestation};
594 use crate::transport::server::Server;
595 use crate::transport::testing::{Memory, test_clock};
596 use crate::{memory, testing};
597 use darkbio_clock::Clock;
598 use std::io::{self, Read as _};
599 use std::sync::mpsc;
600 use std::thread;
601 use std::time::Instant;
602
603 fn contexts() -> (xhpke::Sender, xhpke::Receiver) {
606 let secret = xhpke::SecretKey::generate();
607 let (sender, encap) = secret.public_key().new_sender(b"test").unwrap();
608 let receiver = secret.new_receiver(&encap, b"test").unwrap();
609 (sender, receiver)
610 }
611
612 struct BlockedFlush {
618 clock: Clock,
620 entered: Option<mpsc::Sender<()>>,
622 release: testing::Gate,
624 deadline: Option<Instant>,
626 }
627
628 impl Write for BlockedFlush {
629 fn clock(&self) -> Clock {
630 self.clock.clone()
631 }
632
633 fn set_write_deadline(&mut self, deadline: Instant) -> io::Result<()> {
634 self.deadline = Some(deadline);
635 Ok(())
636 }
637 }
638
639 impl io::Write for BlockedFlush {
640 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
641 testing::remaining(
642 &self.clock,
643 self.deadline.expect("write deadline installed"),
644 )?;
645 Ok(bytes.len())
646 }
647
648 fn flush(&mut self) -> io::Result<()> {
649 let deadline = self.deadline.expect("write deadline installed");
650 testing::remaining(&self.clock, deadline)?;
651 if let Some(entered) = self.entered.take() {
652 entered.send(()).unwrap();
653 self.release.wait(Some(deadline))?;
654 }
655 Ok(())
656 }
657 }
658
659 #[test]
661 fn test_end_waits_for_flush() {
662 testing::init_tracing();
664 let tester = test_clock();
665 let clock = tester.clock();
666 let (entered_tx, entered) = mpsc::channel();
667 let release = testing::Gate::new(&clock);
668 let mut client = Client::new(Stream::new(
669 Memory::new(io::empty(), &clock),
670 BlockedFlush {
671 clock: clock.clone(),
672 entered: Some(entered_tx),
673 release: release.clone(),
674 deadline: None,
675 },
676 || {},
677 ));
678 let (crypto, receiver) = contexts();
679 let sender = client.new_session(crypto, receiver);
680 let sending = {
681 let sender = sender.clone();
682 thread::spawn(move || sender.send(&payload(1)))
683 };
684 entered.recv().unwrap();
685
686 let (started_tx, started) = mpsc::channel();
688 let (ended_tx, ended) = mpsc::channel();
689 let outbound = client.outbound.clone();
690 let ending = thread::spawn(move || {
691 started_tx.send(()).unwrap();
692 client.end_session();
693 ended_tx.send(()).unwrap();
694 client
695 });
696 started.recv().unwrap();
697 tester.wait_registered(1);
698 outbound.wait_writers(1);
699 assert!(ended.try_recv().is_err());
700
701 release.open();
703 sending.join().unwrap().unwrap();
704 let mut client = ending.join().unwrap();
705 ended.recv().unwrap();
706 assert!(matches!(
707 sender.send(&payload(2)),
708 Err(Error::EncryptionFailed(_))
709 ));
710
711 let (crypto, receiver) = contexts();
713 let fresh = client.new_session(crypto, receiver);
714 fresh.send(&payload(3)).unwrap();
715 assert!(matches!(
716 sender.send(&payload(4)),
717 Err(Error::EncryptionFailed(_))
718 ));
719 }
720
721 #[test]
727 fn test_recv_during_blocked_flush() {
728 testing::init_tracing();
730 let tester = test_clock();
731 let clock = tester.clock();
732 let (mut peer, receiver) = contexts();
733 let packet = sealing::seal(&mut peer, &payload(1)).unwrap();
734 let mut bytes = Vec::new();
735 FrameWriter::new(Memory::new(&mut bytes, &clock), Closer::new(&clock, || {}))
736 .send_packet(&packet, clock.now() + DEFAULT_WRITE_TIMEOUT)
737 .unwrap();
738 let (entered_tx, entered) = mpsc::channel();
739 let release = testing::Gate::new(&clock);
740 let mut client = Client::new(Stream::new(
741 Memory::new(io::Cursor::new(bytes), &clock),
742 BlockedFlush {
743 clock: clock.clone(),
744 entered: Some(entered_tx),
745 release: release.clone(),
746 deadline: None,
747 },
748 || {},
749 ));
750 let sender = client.new_session(contexts().0, receiver);
751 let sending = thread::spawn(move || sender.send(&payload(2)));
752 entered.recv().unwrap();
753
754 tester.wait_registered(1);
756 let (received_tx, received) = mpsc::channel();
757 let receiving = thread::spawn(move || {
758 received_tx.send(client.recv()).unwrap();
759 client
760 });
761 let result = received.recv();
762
763 release.open();
765 sending.join().unwrap().unwrap();
766 let _client = receiving.join().unwrap();
767 assert_eq!(result.unwrap().unwrap(), payload(1));
768 }
769
770 #[test]
773 fn test_owner_drop() {
774 testing::init_tracing();
776 let tester = test_clock();
777 let clock = tester.clock();
778 let (mut peer, receiver) = contexts();
779 let packet = sealing::seal(&mut peer, &payload(2)).unwrap();
780 let mut bytes = Vec::new();
781 FrameWriter::new(Memory::new(&mut bytes, &clock), Closer::new(&clock, || {}))
782 .send_packet(&packet, clock.now() + DEFAULT_WRITE_TIMEOUT)
783 .unwrap();
784 let mut client = Client::new(Stream::new(
785 Memory::new(&bytes[..], &clock),
786 Memory::new(Vec::new(), &clock),
787 || {},
788 ));
789 let (crypto, old_receiver) = contexts();
790 let stale = client.new_session(crypto, old_receiver);
791 let old_sealer = client.sealer.as_ref().unwrap().clone();
792
793 let fresh = client.new_session(contexts().0, receiver);
795 client.outbound.end(&old_sealer);
796 drop(old_sealer);
797 assert!(matches!(
798 stale.send(&payload(1)),
799 Err(Error::EncryptionFailed(_))
800 ));
801 assert_eq!(client.recv().unwrap(), payload(2));
802 fresh.send(&payload(3)).unwrap();
803
804 let outbound = client.outbound.clone();
807 let sealer = client.sealer.as_ref().unwrap().clone();
808 drop(client);
809 assert!(outbound.finish_receive(&sealer, Ok(Vec::new())).is_err());
810 assert!(matches!(
811 fresh.send(&payload(4)),
812 Err(Error::EncryptionFailed(_))
813 ));
814 }
815
816 #[test]
821 fn test_senders() {
822 testing::init_tracing();
823
824 let tester = test_clock();
827 let (host, ark_stream) = memory::duplex(64 * 1024, &tester.clock());
828 let signer = xdsa::SecretKey::generate();
829 let identity = signer.public_key();
830 let attestation = self_attestation(&signer);
831 let ark = thread::spawn(move || {
832 let mut server = Server::new(ark_stream, signer, attestation);
833 let mut sender = None;
834 for _ in 0..100 {
835 let req = testing::served(&mut server, &mut sender).unwrap();
836 sender.as_ref().unwrap().send(&req).unwrap();
837 }
838 });
839 let mut client = Client::new(host);
840 let (sender, _) = client.connect(&identity).unwrap();
841
842 let senders: Vec<_> = (0..4)
844 .map(|thread| {
845 let sender = sender.clone();
846 thread::spawn(move || {
847 for i in 0..25 {
848 sender.send(&payload(thread * 100 + i)).unwrap();
849 }
850 })
851 })
852 .collect();
853 let mut echoes: Vec<Vec<u8>> = (0..100).map(|_| client.recv().unwrap()).collect();
854 for sender in senders {
855 sender.join().unwrap();
856 }
857 ark.join().unwrap();
858
859 echoes.sort_unstable();
861 let mut expected: Vec<Vec<u8>> = (0..4)
862 .flat_map(|thread| (0..25).map(move |i| payload(thread * 100 + i)))
863 .collect();
864 expected.sort_unstable();
865 assert_eq!(echoes, expected);
866 }
867
868 #[test]
871 fn test_sender_outlives_client() {
872 testing::init_tracing();
874 let tester = test_clock();
875 let clock = tester.clock();
876 let (mut reader, writer) = testing::pipe(&clock);
877 let (sender, receiver) = contexts();
878 let mut client = Client::new(Stream::new(Memory::new(io::empty(), &clock), writer, || {}));
879 let sender = client.new_session(sender, receiver);
880 sender.send(&payload(1)).unwrap();
881 drop(client);
882
883 let result = sender.send(&payload(2));
885 assert!(matches!(result, Err(Error::Terminated)), "{result:?}");
886 let mut bytes = Vec::new();
888 reader
889 .set_read_deadline(Some(clock.now() + DEFAULT_WRITE_TIMEOUT))
890 .unwrap();
891 reader.read_to_end(&mut bytes).unwrap();
892 assert!(!bytes.is_empty());
893 }
894}