1use crate::LogId;
5use crate::transport::DEFAULT_HANDSHAKE_TIMEOUT;
6use crate::transport::framing::FrameReader;
7use crate::transport::handshake;
8use crate::transport::io::check_deadline;
9use crate::transport::outbound::{Outbound, Side};
10use crate::transport::sealing;
11use crate::transport::sender::Sender;
12use crate::transport::server::Attestation;
13use crate::transport::{
14 CRYPTO_DOMAIN_WIRE, CRYPTO_DOMAIN_WIRE_ARK_TO_HOST, CRYPTO_DOMAIN_WIRE_HOST_TO_ARK, Closer,
15 Error, Read, Stream, Write,
16};
17use darkbio_crypto::{cbor, cose, xdsa, xhpke};
18use darkbio_trust as trust;
19use std::sync::{Arc, Mutex};
20use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
21use tracing::{debug, info, trace, warn};
22
23pub trait Verifier {
27 type Info;
29
30 fn verify(&self, attestation: &Attestation) -> Result<(xdsa::PublicKey, Self::Info), String>;
35}
36
37impl Verifier for xdsa::PublicKey {
40 type Info = Attestation;
41
42 fn verify(&self, attestation: &Attestation) -> Result<(xdsa::PublicKey, Self::Info), String> {
43 Ok((self.clone(), attestation.clone()))
44 }
45}
46
47pub struct Roots<'a> {
51 pub hardware: &'a [xdsa::PublicKey], pub emulator: &'a [xdsa::PublicKey], }
54
55impl Verifier for Roots<'_> {
56 type Info = trust::device::Device;
57
58 fn verify(&self, attestation: &Attestation) -> Result<(xdsa::PublicKey, Self::Info), String> {
59 let now = SystemTime::now()
60 .duration_since(UNIX_EPOCH)
61 .map_err(|err| err.to_string())?
62 .as_secs();
63
64 let device = trust::device::verify(
65 attestation.as_bytes(),
66 self.hardware,
67 self.emulator,
68 Some(now),
69 )
70 .map_err(|err| err.to_string())?;
71 Ok((device.signer.clone(), device))
72 }
73}
74pub struct Client<R: Read, W: Write> {
85 handshake_timeout: Duration, reader: FrameReader<R>, receiver: Option<xhpke::Receiver>, sealer: Option<Arc<Mutex<xhpke::Sender>>>, outbound: Arc<Outbound<W>>, log_id: LogId, }
93
94impl<R: Read, W: Write> Client<R, W> {
95 pub fn new(stream: Stream<R, W>) -> Self {
100 let (reader, writer, close, timeout) = stream.into_parts();
101
102 let outbound = Arc::new(Outbound::new(writer, Side::Client, close.clone(), timeout));
103
104 Self {
105 handshake_timeout: DEFAULT_HANDSHAKE_TIMEOUT,
106 reader: FrameReader::new(reader, close),
107 receiver: None,
108 sealer: None,
109 outbound,
110 log_id: LogId::default(),
111 }
112 }
113
114 pub fn set_handshake_timeout(mut self, timeout: Duration) -> Self {
123 self.handshake_timeout = timeout;
124 self
125 }
126
127 pub fn closer(&self) -> Closer {
129 self.outbound.closer()
130 }
131
132 pub fn close(&self) {
136 self.outbound.close();
137 }
138
139 pub fn connect<V: Verifier>(&mut self, verifier: &V) -> Result<(Sender<W>, V::Info), Error> {
158 let deadline = Instant::now() + self.handshake_timeout;
160
161 let host_xdsa_sk = xdsa::SecretKey::generate();
163 let host_xhpke_sk = xhpke::SecretKey::generate();
164
165 self.handshake(verifier, host_xdsa_sk, host_xhpke_sk, None, deadline)
166 }
167
168 fn handshake<V: Verifier>(
172 &mut self,
173 verifier: &V,
174 host_xdsa_sk: xdsa::SecretKey,
175 host_xhpke_sk: xhpke::SecretKey,
176 timestamp: Option<i64>,
177 deadline: Instant,
178 ) -> Result<(Sender<W>, V::Info), Error> {
179 let host_xdsa_pk = host_xdsa_sk.public_key();
180 let host_xhpke_pk = host_xhpke_sk.public_key();
181 debug!("starting wire handshake");
182
183 let hello = cbor::encode(&handshake::HostHello {
185 host_signer: host_xdsa_pk.clone(),
186 host_crypto: host_xhpke_pk.clone(),
187 })
188 .map_err(|err| {
189 self.end_session();
190 Error::HandshakeFailed(format!("failed to encode client hello: {}", err))
191 })?;
192
193 self.receiver = None;
196 self.sealer = None;
197 self.outbound.send_reset(deadline)?;
198 self.outbound.send_packet(&hello, Some(deadline))?;
199
200 let recipient = host_xhpke_pk.fingerprint();
204 let packet = loop {
205 let packet = match self.reader.next_packet(Some(deadline)) {
206 Ok(Some(packet)) => packet,
207 Ok(None) | Err(Error::FrameDecodingFailed(_) | Error::FrameTooLarge(_)) => &[],
208 Err(err) => return Err(err),
209 };
210 if cose::recipient(packet).is_ok_and(|fp| fp == recipient) {
211 break packet;
212 }
213 debug!("skipping stale frame during handshake");
214 };
215 let auth = handshake::ArkHelloAuth {
216 host_signer: host_xdsa_pk.clone(),
217 host_crypto: host_xhpke_pk.clone(),
218 };
219
220 let sign1 =
222 cose::decrypt(packet, &auth, &host_xhpke_sk, CRYPTO_DOMAIN_WIRE).map_err(|err| {
223 Error::HandshakeFailed(format!("failed to decrypt server hello: {}", err))
224 })?;
225
226 let unverified: handshake::ArkHello = cose::peek(&sign1).map_err(|err| {
228 Error::HandshakeFailed(format!("invalid server hello payload: {}", err))
229 })?;
230
231 let attestation = Attestation::new(unverified.ark_attest)?;
234 let (ark_identity, info) = verifier
235 .verify(&attestation)
236 .map_err(Error::HandshakeFailed)?;
237
238 let ark_hello: handshake::ArkHello =
240 cose::verify(&sign1, &auth, &ark_identity, CRYPTO_DOMAIN_WIRE, None).map_err(
241 |err| Error::HandshakeFailed(format!("server hello signature invalid: {}", err)),
242 )?;
243
244 let enc_a2h: [u8; xhpke::ENCAP_KEY_SIZE] = ark_hello
246 .a2h_encap
247 .try_into()
248 .map_err(|_| Error::HandshakeFailed("invalid a2h_encap size".into()))?;
249
250 let receiver = host_xhpke_sk
251 .new_receiver(&enc_a2h, CRYPTO_DOMAIN_WIRE_ARK_TO_HOST)
252 .map_err(|err| {
253 Error::HandshakeFailed(format!("client receiver setup failed: {}", err))
254 })?;
255
256 let ark_xhpke_pk = ark_hello.ark_crypto;
258 let (sender, enc_h2a) = ark_xhpke_pk
259 .new_sender(CRYPTO_DOMAIN_WIRE_HOST_TO_ARK)
260 .map_err(|err| {
261 Error::HandshakeFailed(format!("client sender setup failed: {}", err))
262 })?;
263
264 let ack = handshake::HostAck {
266 h2a_encap: enc_h2a.to_vec(),
267 };
268 let auth = handshake::HostAckAuth {
269 ark_signer: ark_identity,
270 ark_crypto: ark_xhpke_pk.clone(),
271 };
272 let ack = match timestamp {
273 Some(timestamp) => cose::seal_at(
274 &ack,
275 &auth,
276 &host_xdsa_sk,
277 &ark_xhpke_pk,
278 CRYPTO_DOMAIN_WIRE,
279 timestamp,
280 ),
281 None => cose::seal(
282 &ack,
283 &auth,
284 &host_xdsa_sk,
285 &ark_xhpke_pk,
286 CRYPTO_DOMAIN_WIRE,
287 ),
288 }
289 .map_err(|err| Error::HandshakeFailed(format!("failed to seal client ack: {}", err)))?;
290
291 self.outbound.send_packet(&ack, Some(deadline))?;
292 check_deadline(deadline).map_err(Error::RecvFailed)?;
293
294 let sender = self.new_session(sender, receiver);
296 info!(
297 "wire session {} established with ark {}",
298 self.log_id,
299 hex(&auth.ark_signer.fingerprint())
300 );
301 Ok((sender, info))
302 }
303
304 pub fn recv(&mut self) -> Result<Vec<u8>, Error> {
318 let receiver = self
319 .receiver
320 .as_mut()
321 .ok_or_else(|| Error::EncryptionFailed("no active session".into()))?;
322
323 let packet = match self.reader.next_packet(None) {
327 Err(err) => {
328 if matches!(err, Error::FrameDecodingFailed(_) | Error::FrameTooLarge(_)) {
329 warn!("ending session {}: {}", self.log_id, err);
330 }
331 self.end_session();
332 return Err(err);
333 }
334 Ok(None) => {
335 info!("wire session {} reset by ark", self.log_id);
336 self.end_session();
337 return Err(Error::SessionReset);
338 }
339 Ok(Some(packet)) => packet,
340 };
341 let sealer = self
342 .sealer
343 .as_ref()
344 .expect("receiver has a sending context");
345 let opened = sealing::open(receiver, packet);
346 let undecryptable = opened.is_err();
347 let message = match self.outbound.finish_receive(sealer, opened) {
348 Err(err) => {
349 if undecryptable {
350 warn!("ending session {}: {}", self.log_id, err);
351 } else {
352 debug!(
353 "discarding message read after session {} ended",
354 self.log_id
355 );
356 }
357 self.end_session();
358 return Err(err);
359 }
360 Ok(message) => message,
361 };
362 trace!("received ark-to-host message ({} bytes)", packet.len());
363 Ok(message)
364 }
365
366 fn new_session(&mut self, sender: xhpke::Sender, receiver: xhpke::Receiver) -> Sender<W> {
376 let sealer = Arc::new(Mutex::new(sender));
377 let sender = self.outbound.bind(&sealer);
378 self.log_id = sender.log_id();
379
380 self.receiver = Some(receiver);
381 self.sealer = Some(sealer);
382
383 sender
384 }
385
386 fn end_session(&mut self) {
396 if let Some(sealer) = self.sealer.as_ref() {
397 self.outbound.end(sealer);
398 }
399 self.receiver = None;
400 self.sealer = None;
401 }
402
403 #[doc(hidden)]
406 #[inline]
407 #[cfg(any(test, feature = "bench", feature = "fuzz"))]
408 #[cfg_attr(coverage_nightly, coverage(off))]
409 pub fn handshake_with_keys<V: Verifier>(
410 &mut self,
411 verifier: &V,
412 host_xdsa_sk: xdsa::SecretKey,
413 host_xhpke_sk: xhpke::SecretKey,
414 timestamp: i64,
415 ) -> Result<(Sender<W>, V::Info), Error> {
416 self.handshake(
417 verifier,
418 host_xdsa_sk,
419 host_xhpke_sk,
420 Some(timestamp),
421 Instant::now() + self.handshake_timeout,
422 )
423 }
424
425 #[doc(hidden)]
427 #[inline]
428 #[cfg(any(test, feature = "bench", feature = "fuzz"))]
429 #[cfg_attr(coverage_nightly, coverage(off))]
430 pub fn next_packet_blob(&mut self) -> Result<Option<&[u8]>, Error> {
431 self.reader.next_packet(None)
432 }
433
434 #[doc(hidden)]
436 #[inline]
437 #[cfg(any(test, feature = "bench", feature = "fuzz"))]
438 #[cfg_attr(coverage_nightly, coverage(off))]
439 pub fn send_packet_blob(&mut self, packet: &[u8]) -> Result<(), Error> {
440 self.outbound.send_packet(packet, None)
441 }
442
443 #[doc(hidden)]
445 #[inline]
446 #[cfg(any(test, feature = "bench", feature = "fuzz"))]
447 #[cfg_attr(coverage_nightly, coverage(off))]
448 pub fn next_frame_blob(&mut self) -> Result<&[u8], Error> {
449 self.reader.next_frame_blob()
450 }
451
452 #[doc(hidden)]
454 #[inline]
455 #[cfg(any(test, feature = "bench", feature = "fuzz"))]
456 #[cfg_attr(coverage_nightly, coverage(off))]
457 pub fn send_frame_blob(&mut self, frame: &[u8]) -> Result<(), Error> {
458 self.outbound.send_frame_blob(frame)
459 }
460}
461
462impl<R: Read, W: Write> Drop for Client<R, W> {
463 fn drop(&mut self) {
467 self.outbound.close();
468 self.end_session();
469 }
470}
471
472fn hex(fingerprint: &xdsa::Fingerprint) -> String {
474 fingerprint
475 .to_bytes()
476 .iter()
477 .map(|byte| format!("{byte:02x}"))
478 .collect()
479}
480
481#[cfg(test)]
482#[cfg_attr(coverage_nightly, coverage(off))]
483mod tests {
484 use super::*;
485 use crate::transport::DEFAULT_WRITE_TIMEOUT;
486 use crate::transport::framing::FrameWriter;
487 use crate::transport::mock::{payload, self_attestation};
488 use crate::transport::server::Server;
489 use crate::transport::testing::Memory;
490 use crate::{memory, testing};
491 use std::io::{self, Read as _};
492 use std::sync::mpsc;
493 use std::thread;
494 use std::time::{Duration, Instant};
495
496 fn contexts() -> (xhpke::Sender, xhpke::Receiver) {
498 let secret = xhpke::SecretKey::generate();
499 let (sender, encap) = secret.public_key().new_sender(b"test").unwrap();
500 let receiver = secret.new_receiver(&encap, b"test").unwrap();
501 (sender, receiver)
502 }
503
504 struct BlockedFlush {
508 entered: Option<mpsc::Sender<()>>,
509 release: mpsc::Receiver<()>,
510 deadline: Option<Instant>,
511 }
512
513 impl Write for BlockedFlush {
514 fn set_write_deadline(&mut self, deadline: Instant) -> io::Result<()> {
515 self.deadline = Some(deadline);
516 Ok(())
517 }
518 }
519
520 impl io::Write for BlockedFlush {
521 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
522 testing::remaining(self.deadline.expect("write deadline installed"))?;
523 Ok(bytes.len())
524 }
525
526 fn flush(&mut self) -> io::Result<()> {
527 let deadline = self.deadline.expect("write deadline installed");
528 testing::remaining(deadline)?;
529 if let Some(entered) = self.entered.take() {
530 entered.send(()).unwrap();
531 self.release
532 .recv_timeout(testing::remaining(deadline)?)
533 .map_err(|_| io::Error::from(io::ErrorKind::TimedOut))?;
534 }
535 Ok(())
536 }
537 }
538
539 #[test]
542 fn test_end_waits_for_flush() {
543 testing::init_tracing();
544
545 let (entered_tx, entered) = mpsc::channel();
546 let (release, release_rx) = mpsc::channel();
547 let mut client = Client::new(Stream::new(
548 Memory::new(io::empty()),
549 BlockedFlush {
550 entered: Some(entered_tx),
551 release: release_rx,
552 deadline: None,
553 },
554 || {},
555 ));
556 let (crypto, receiver) = contexts();
557 let sender = client.new_session(crypto, receiver);
558 let sending = {
559 let sender = sender.clone();
560 thread::spawn(move || sender.send(&payload(1)))
561 };
562 entered.recv_timeout(Duration::from_secs(5)).unwrap();
563
564 let (started_tx, started) = mpsc::channel();
565 let (ended_tx, ended) = mpsc::channel();
566 let ending = thread::spawn(move || {
567 started_tx.send(()).unwrap();
568 client.end_session();
569 ended_tx.send(()).unwrap();
570 client
571 });
572 started.recv_timeout(Duration::from_secs(5)).unwrap();
573 let early = ended.recv_timeout(Duration::from_millis(50));
574 release.send(()).unwrap();
575 sending.join().unwrap().unwrap();
576 let mut client = ending.join().unwrap();
577 assert!(matches!(early, Err(mpsc::RecvTimeoutError::Timeout)));
578 ended.recv_timeout(Duration::from_secs(5)).unwrap();
579 assert!(matches!(
580 sender.send(&payload(2)),
581 Err(Error::EncryptionFailed(_))
582 ));
583
584 let (crypto, receiver) = contexts();
585 let fresh = client.new_session(crypto, receiver);
586 fresh.send(&payload(3)).unwrap();
587 assert!(matches!(
588 sender.send(&payload(4)),
589 Err(Error::EncryptionFailed(_))
590 ));
591 }
592
593 #[test]
597 fn test_recv_during_blocked_flush() {
598 testing::init_tracing();
599
600 let (mut peer, receiver) = contexts();
601 let packet = sealing::seal(&mut peer, &payload(1)).unwrap();
602 let mut bytes = Vec::new();
603 FrameWriter::new(Memory::new(&mut bytes), Closer::new(|| {}))
604 .send_packet(&packet, Instant::now() + DEFAULT_WRITE_TIMEOUT)
605 .unwrap();
606 let (entered_tx, entered) = mpsc::channel();
607 let (release, release_rx) = mpsc::channel();
608 let mut client = Client::new(Stream::new(
609 Memory::new(io::Cursor::new(bytes)),
610 BlockedFlush {
611 entered: Some(entered_tx),
612 release: release_rx,
613 deadline: None,
614 },
615 || {},
616 ));
617 let sender = client.new_session(contexts().0, receiver);
618 let sending = thread::spawn(move || sender.send(&payload(2)));
619 entered.recv_timeout(Duration::from_secs(5)).unwrap();
620
621 let (received_tx, received) = mpsc::channel();
622 let receiving = thread::spawn(move || {
623 received_tx.send(client.recv()).unwrap();
624 client
625 });
626 let result = received.recv_timeout(Duration::from_secs(5));
627 release.send(()).unwrap();
629 sending.join().unwrap().unwrap();
630 let _client = receiving.join().unwrap();
631 assert_eq!(result.unwrap().unwrap(), payload(1));
632 }
633
634 #[test]
639 fn test_owner_drop() {
640 testing::init_tracing();
641
642 let (mut peer, receiver) = contexts();
643 let packet = sealing::seal(&mut peer, &payload(2)).unwrap();
644 let mut bytes = Vec::new();
645 FrameWriter::new(Memory::new(&mut bytes), Closer::new(|| {}))
646 .send_packet(&packet, Instant::now() + DEFAULT_WRITE_TIMEOUT)
647 .unwrap();
648 let mut client = Client::new(Stream::new(
649 Memory::new(&bytes[..]),
650 Memory::new(Vec::new()),
651 || {},
652 ));
653 let (crypto, old_receiver) = contexts();
654 let stale = client.new_session(crypto, old_receiver);
655 let old_sealer = client.sealer.as_ref().unwrap().clone();
656
657 let fresh = client.new_session(contexts().0, receiver);
658 client.outbound.end(&old_sealer);
659 drop(old_sealer);
660 assert!(matches!(
661 stale.send(&payload(1)),
662 Err(Error::EncryptionFailed(_))
663 ));
664 assert_eq!(client.recv().unwrap(), payload(2));
665 fresh.send(&payload(3)).unwrap();
666
667 let outbound = client.outbound.clone();
668 let sealer = client.sealer.as_ref().unwrap().clone();
669 drop(client);
670 assert!(outbound.finish_receive(&sealer, Ok(Vec::new())).is_err());
671 assert!(matches!(
672 fresh.send(&payload(4)),
673 Err(Error::EncryptionFailed(_))
674 ));
675 }
676
677 #[test]
681 fn test_senders() {
682 testing::init_tracing();
683
684 let (host, ark_stream) = memory::duplex(64 * 1024);
686
687 let signer = xdsa::SecretKey::generate();
688 let identity = signer.public_key();
689 let attestation = self_attestation(&signer);
690 let ark = thread::spawn(move || {
691 let mut server = Server::new(ark_stream, signer, attestation);
692 let mut sender = None;
693 for _ in 0..100 {
694 let req = testing::served(&mut server, &mut sender).unwrap();
695 sender.as_ref().unwrap().send(&req).unwrap();
696 }
697 });
698 let mut client = Client::new(host);
699 let (sender, _) = client.connect(&identity).unwrap();
700
701 let senders: Vec<_> = (0..4)
703 .map(|thread| {
704 let sender = sender.clone();
705 thread::spawn(move || {
706 for i in 0..25 {
707 sender.send(&payload(thread * 100 + i)).unwrap();
708 }
709 })
710 })
711 .collect();
712 let mut echoes: Vec<Vec<u8>> = (0..100).map(|_| client.recv().unwrap()).collect();
713 for sender in senders {
714 sender.join().unwrap();
715 }
716 ark.join().unwrap();
717
718 echoes.sort_unstable();
719 let mut expected: Vec<Vec<u8>> = (0..4)
720 .flat_map(|thread| (0..25).map(move |i| payload(thread * 100 + i)))
721 .collect();
722 expected.sort_unstable();
723 assert_eq!(echoes, expected);
724 }
725
726 #[test]
729 fn test_sender_outlives_client() {
730 testing::init_tracing();
731
732 let (mut reader, writer) = testing::pipe();
733 let (sender, receiver) = contexts();
734 let mut client = Client::new(Stream::new(Memory::new(io::empty()), writer, || {}));
735 let sender = client.new_session(sender, receiver);
736 sender.send(&payload(1)).unwrap();
737 drop(client);
738
739 let result = sender.send(&payload(2));
740 assert!(matches!(result, Err(Error::Terminated)), "{result:?}");
741 let mut bytes = Vec::new();
743 reader
744 .set_read_deadline(Some(Instant::now() + DEFAULT_WRITE_TIMEOUT))
745 .unwrap();
746 reader.read_to_end(&mut bytes).unwrap();
747 assert!(!bytes.is_empty());
748 }
749}