1use crate::framing::Framing;
5use crate::handshake;
6use crate::protocol::{ArkToHost, HostToArk};
7use crate::session::Session;
8use crate::{
9 CRYPTO_DOMAIN_WIRE, CRYPTO_DOMAIN_WIRE_ARK_TO_HOST, CRYPTO_DOMAIN_WIRE_HOST_TO_ARK, Error,
10};
11use darkbio_crypto::{cbor, cose, cwt, xdsa, xhpke};
12use darkbio_trust as trust;
13use std::io::{Read, Write};
14use tracing::{info, trace, warn};
15
16#[derive(Clone)]
21pub struct Attestation(Vec<u8>);
22
23impl Attestation {
24 pub fn new(cwt: Vec<u8>) -> Result<Self, Error> {
26 if cwt::peek::<trust::device::HardwareClaims>(&cwt).is_err()
27 && cwt::peek::<trust::device::EmulatorClaims>(&cwt).is_err()
28 {
29 return Err(Error::InvalidAttestation);
30 }
31 Ok(Self(cwt))
32 }
33
34 pub fn as_bytes(&self) -> &[u8] {
36 &self.0
37 }
38
39 pub fn into_bytes(self) -> Vec<u8> {
41 self.0
42 }
43}
44
45pub trait Attester {
49 fn attest(&mut self) -> Attestation;
53}
54
55impl Attester for Attestation {
57 fn attest(&mut self) -> Attestation {
58 self.clone()
59 }
60}
61
62pub struct ArkSide<R: Read, W: Write, A: Attester> {
69 framing: Framing<R, W>, signer: xdsa::SecretKey, attester: A, session: Option<Session>, }
75
76impl<R: Read, W: Write, A: Attester> ArkSide<R, W, A> {
77 pub fn new(reader: R, writer: W, signer: xdsa::SecretKey, attester: A) -> Self {
83 Self {
84 framing: Framing::new(reader, writer),
85 signer,
86 attester,
87 session: None,
88 }
89 }
90
91 pub fn next_message(&mut self) -> Result<HostToArk, Error> {
97 loop {
100 let size = match self.framing.next_packet() {
102 Err(Error::Terminated) => return Err(Error::Terminated),
104 Err(Error::RecvFailed(err)) => return Err(Error::RecvFailed(err)),
105
106 Err(err) => {
111 if self.session.take().is_some() {
112 warn!("failed to decode cobs packet, resetting session: {}", err);
113 } else {
114 warn!("failed to decode cobs packet: {}", err);
115 }
116 continue;
117 }
118 Ok(None) => {
120 self.session = None;
121
122 match self.handshake() {
123 Err(Error::Terminated) => return Err(Error::Terminated),
125 Err(Error::RecvFailed(err)) => return Err(Error::RecvFailed(err)),
126
127 Err(err) => {
129 warn!("wire handshake failed: {}", err);
130 continue;
131 }
132 Ok(session) => {
134 info!("new wire session established");
135 self.session = Some(session);
136 continue;
137 }
138 }
139 }
140 Ok(Some(size)) => size,
142 };
143 let session = match self.session.as_mut() {
145 None => {
146 warn!("dropping data outside session");
147 continue;
148 }
149 Some(s) => s,
150 };
151 let req = match session.open(&self.framing.decobs_buffer[..size]) {
153 Err(Error::EncryptionFailed(err)) => {
156 warn!("decryption failed, resetting session: {}", err);
157 self.session = None;
158 continue;
159 }
160 Err(err) => return Err(err),
161 Ok(req) => req,
162 };
163 trace!("read host-to-ark message ({} bytes encrypted)", size);
164 return Ok(req);
165 }
166 }
167
168 pub fn send_message(&mut self, res: ArkToHost) -> Result<(), Error> {
173 let session = self
176 .session
177 .as_mut()
178 .ok_or_else(|| Error::EncryptionFailed("no active session".into()))?;
179
180 let blob = match session.seal(&res, &mut self.framing.encode_buffer) {
181 Err(err @ Error::EncryptionFailed(_)) => {
182 self.session = None;
183 return Err(err);
184 }
185 Err(err) => return Err(err),
186 Ok(blob) => blob,
187 };
188 if let Err(err) = self.framing.send_packet(&blob) {
191 self.session = None;
192 return Err(err);
193 }
194 trace!("sent ark-to-host message ({} bytes)", blob.len());
195 Ok(())
196 }
197
198 fn handshake(&mut self) -> Result<Session, Error> {
205 loop {
206 let size = loop {
208 if let Some(n) = self.framing.next_packet()? {
209 break n;
210 }
211 };
212 let host_hello: handshake::HostHello =
213 cbor::decode(&self.framing.decobs_buffer[..size]).map_err(|err| {
214 Error::HandshakeFailed(format!("invalid host hello: {}", err))
215 })?;
216
217 let ark_crypto_key = xhpke::SecretKey::generate();
219 let ark_crypto_pub = ark_crypto_key.public_key();
220
221 let (sender, a2h_encap) = host_hello
222 .host_crypto
223 .new_sender(CRYPTO_DOMAIN_WIRE_ARK_TO_HOST)
224 .map_err(|err| {
225 Error::HandshakeFailed(format!("ark sender setup failed: {}", err))
226 })?;
227
228 let ark_hello = cose::seal(
230 &handshake::ArkHello {
231 ark_attest: self.attester.attest().into_bytes(),
232 ark_crypto: ark_crypto_pub.clone(),
233 a2h_encap: a2h_encap.to_vec(),
234 },
235 &handshake::ArkHelloAuth {
236 host_signer: host_hello.host_signer.clone(),
237 host_crypto: host_hello.host_crypto.clone(),
238 },
239 &self.signer,
240 &host_hello.host_crypto,
241 CRYPTO_DOMAIN_WIRE,
242 )
243 .map_err(|err| Error::HandshakeFailed(format!("failed to seal ark hello: {}", err)))?;
244
245 self.framing.send_packet(&ark_hello)?;
246
247 let Some(size) = self.framing.next_packet()? else {
250 warn!("session reset during handshake");
251 continue;
252 };
253 let host_ack: handshake::HostAck = cose::open(
254 &self.framing.decobs_buffer[..size],
255 &handshake::HostAckAuth {
256 ark_signer: self.signer.public_key(),
257 ark_crypto: ark_crypto_pub.clone(),
258 },
259 &ark_crypto_key,
260 &host_hello.host_signer,
261 CRYPTO_DOMAIN_WIRE,
262 None, )
264 .map_err(|err| Error::HandshakeFailed(format!("invalid host ack: {}", err)))?;
265
266 let enc_h2a: [u8; xhpke::ENCAP_KEY_SIZE] = host_ack
268 .h2a_encap
269 .try_into()
270 .map_err(|_| Error::HandshakeFailed("invalid h2a_encap size".into()))?;
271
272 let receiver = ark_crypto_key
273 .new_receiver(&enc_h2a, CRYPTO_DOMAIN_WIRE_HOST_TO_ARK)
274 .map_err(|err| {
275 Error::HandshakeFailed(format!("ark receiver setup failed: {}", err))
276 })?;
277
278 return Ok(Session { sender, receiver });
280 }
281 }
282}
283
284#[cfg(all(test, unix))]
285mod tests {
286 use super::*;
287 use crate::testing;
288 use crate::{HostSide, Verifier};
289 use darkbio_cobs as cobs;
290 use std::io::{self, Write};
291 use std::os::unix::net::UnixStream;
292 use std::sync::Arc;
293 use std::sync::atomic::{AtomicBool, Ordering};
294
295 fn self_attestation(signer: &xdsa::SecretKey) -> Attestation {
298 use darkbio_crypto::cwt::claims::{self, eat};
299
300 let claims = darkbio_trust::device::HardwareClaims {
301 sub: claims::Subject { sub: "".into() },
302 cnf: claims::Confirm::new(signer.public_key()),
303 nbf: claims::NotBefore { nbf: 0 },
304 iat: claims::IssuedAt { iat: 0 },
305 oem: eat::Oemid::new_pen(0),
306 hwm: eat::HwModel { hw_model: vec![] },
307 hwv: eat::HwVersion::new("".into()),
308 };
309 let cwt = cwt::issue(
310 &claims,
311 signer,
312 darkbio_trust::CRYPTO_DOMAIN_DEVICE_ATTESTATION,
313 )
314 .unwrap();
315 Attestation::new(cwt).unwrap()
316 }
317
318 fn cobs_frame(data: &[u8]) -> Vec<u8> {
320 let mut buf = vec![0u8; cobs::encode_buffer(data.len())];
321 let n = cobs::encode(data, &mut buf).unwrap();
322 buf.truncate(n);
323 buf.push(0x00);
324 buf
325 }
326
327 #[test]
331 fn test_message_round_trip() {
332 testing::init_tracing();
333
334 let signer_key = xdsa::SecretKey::generate();
335 let signer_pub = signer_key.public_key();
336 let attestation = self_attestation(&signer_key);
337 let presented = attestation.clone();
338
339 let (host_sock, ark_sock) = UnixStream::pair().unwrap();
340 let ark_reader = ark_sock.try_clone().unwrap();
341 let ark_writer = ark_sock;
342
343 let ark_thread = std::thread::spawn(move || {
345 let mut ark = ArkSide::new(ark_reader, ark_writer, signer_key, attestation);
346 let req = ark.next_message().unwrap();
347 ark.send_message(ArkToHost {
348 id: req.id,
349 err: None,
350 content: None,
351 })
352 .unwrap();
353 req
354 });
355
356 let mut host = HostSide::new(host_sock.try_clone().unwrap(), host_sock);
358 let attest = host.handshake(&signer_pub).unwrap();
359 assert_eq!(
360 attest.as_bytes(),
361 presented.as_bytes(),
362 "attestation mismatch"
363 );
364
365 host.send_message(HostToArk {
366 id: Some(42),
367 content: None,
368 })
369 .unwrap();
370
371 let req = ark_thread.join().unwrap();
372 assert_eq!(req.id, Some(42), "request mismatch");
373
374 let res = host.next_message().unwrap();
375 assert_eq!(res.id, Some(42), "response mismatch");
376 }
377
378 #[test]
382 fn test_reset_mid_transfer() {
383 testing::init_tracing();
384
385 let signer_key = xdsa::SecretKey::generate();
386 let signer_pub = signer_key.public_key();
387
388 let (host_sock, ark_sock) = UnixStream::pair().unwrap();
389 let ark_reader = ark_sock.try_clone().unwrap();
390 let ark_writer = ark_sock;
391
392 let ark_thread = std::thread::spawn(move || {
394 let attestation = self_attestation(&signer_key);
395 let mut ark = ArkSide::new(ark_reader, ark_writer, signer_key, attestation);
396 let mut ids = Vec::new();
397 for _ in 0..2 {
398 let req = ark.next_message().unwrap();
399 ids.push(req.id);
400 ark.send_message(ArkToHost {
401 id: req.id,
402 err: None,
403 content: None,
404 })
405 .unwrap();
406 }
407 ids
408 });
409
410 let mut raw_sock = host_sock.try_clone().unwrap();
412
413 let mut host = HostSide::new(host_sock.try_clone().unwrap(), host_sock);
415 host.handshake(&signer_pub).unwrap();
416 host.send_message(HostToArk {
417 id: Some(1),
418 content: None,
419 })
420 .unwrap();
421 let res = host.next_message().unwrap();
422 assert_eq!(res.id, Some(1), "session 1 response mismatch");
423
424 raw_sock
428 .write_all(&cobs_frame(b"interrupted transfer"))
429 .unwrap();
430
431 host.handshake(&signer_pub).unwrap();
433 host.send_message(HostToArk {
434 id: Some(2),
435 content: None,
436 })
437 .unwrap();
438 let res = host.next_message().unwrap();
439 assert_eq!(res.id, Some(2), "session 2 response mismatch");
440
441 let ids = ark_thread.join().unwrap();
442 assert_eq!(
443 ids,
444 vec![Some(1), Some(2)],
445 "ark received wrong message ids"
446 );
447 }
448
449 #[test]
454 fn test_reset_unread_response() {
455 testing::init_tracing();
456
457 let signer_key = xdsa::SecretKey::generate();
458 let signer_pub = signer_key.public_key();
459
460 let (host_sock, ark_sock) = UnixStream::pair().unwrap();
461 let ark_reader = ark_sock.try_clone().unwrap();
462 let ark_writer = ark_sock;
463
464 let ark_thread = std::thread::spawn(move || {
466 let attestation = self_attestation(&signer_key);
467 let mut ark = ArkSide::new(ark_reader, ark_writer, signer_key, attestation);
468 let mut ids = Vec::new();
469 for _ in 0..2 {
470 let req = ark.next_message().unwrap();
471 ids.push(req.id);
472 ark.send_message(ArkToHost {
473 id: req.id,
474 err: None,
475 content: None,
476 })
477 .unwrap();
478 }
479 ids
480 });
481
482 let mut host = HostSide::new(host_sock.try_clone().unwrap(), host_sock);
485 host.handshake(&signer_pub).unwrap();
486 host.send_message(HostToArk {
487 id: Some(1),
488 content: None,
489 })
490 .unwrap();
491
492 host.handshake(&signer_pub).unwrap();
495 host.send_message(HostToArk {
496 id: Some(2),
497 content: None,
498 })
499 .unwrap();
500 let res = host.next_message().unwrap();
501 assert_eq!(res.id, Some(2), "session 2 response mismatch");
502
503 let ids = ark_thread.join().unwrap();
504 assert_eq!(
505 ids,
506 vec![Some(1), Some(2)],
507 "ark received wrong message ids"
508 );
509 }
510
511 #[test]
516 fn test_reset_mid_handshake() {
517 testing::init_tracing();
518
519 let signer_key = xdsa::SecretKey::generate();
520 let signer_pub = signer_key.public_key();
521
522 let (host_sock, ark_sock) = UnixStream::pair().unwrap();
523 let ark_reader = ark_sock.try_clone().unwrap();
524 let ark_writer = ark_sock;
525
526 let ark_thread = std::thread::spawn(move || {
528 let attestation = self_attestation(&signer_key);
529 let mut ark = ArkSide::new(ark_reader, ark_writer, signer_key, attestation);
530 let req = ark.next_message().unwrap();
531 ark.send_message(ArkToHost {
532 id: req.id,
533 err: None,
534 content: None,
535 })
536 .unwrap();
537 req
538 });
539
540 let host_read = host_sock.try_clone().unwrap();
541 let mut host_write = host_sock;
542
543 host_write.write_all(&[0x00, 0x00]).unwrap(); let host_signer_key = xdsa::SecretKey::generate();
548 let host_crypto_key = xhpke::SecretKey::generate();
549 let hello = cbor::encode(&handshake::HostHello {
550 host_signer: host_signer_key.public_key(),
551 host_crypto: host_crypto_key.public_key(),
552 })
553 .unwrap();
554 host_write.write_all(&cobs_frame(&hello)).unwrap(); let mut host = HostSide::new(host_read, host_write);
561 host.handshake(&signer_pub).unwrap();
562 host.send_message(HostToArk {
563 id: Some(99),
564 content: None,
565 })
566 .unwrap();
567
568 let req = ark_thread.join().unwrap();
569 assert_eq!(req.id, Some(99), "request mismatch");
570
571 let res = host.next_message().unwrap();
572 assert_eq!(res.id, Some(99), "response mismatch");
573 }
574
575 #[test]
579 fn test_reset_malformed_hello() {
580 testing::init_tracing();
581
582 let signer_key = xdsa::SecretKey::generate();
583 let signer_pub = signer_key.public_key();
584
585 let (host_sock, ark_sock) = UnixStream::pair().unwrap();
586 let ark_reader = ark_sock.try_clone().unwrap();
587 let ark_writer = ark_sock;
588
589 let ark_thread = std::thread::spawn(move || {
591 let attestation = self_attestation(&signer_key);
592 let mut ark = ArkSide::new(ark_reader, ark_writer, signer_key, attestation);
593 let req = ark.next_message().unwrap();
594 ark.send_message(ArkToHost {
595 id: req.id,
596 err: None,
597 content: None,
598 })
599 .unwrap();
600 req
601 });
602
603 let mut raw_sock = host_sock.try_clone().unwrap();
605
606 raw_sock.write_all(&[0x00, 0x00]).unwrap();
610 raw_sock.write_all(&cobs_frame(b"not a hello")).unwrap();
611
612 let mut host = HostSide::new(host_sock.try_clone().unwrap(), host_sock);
614 host.handshake(&signer_pub).unwrap();
615 host.send_message(HostToArk {
616 id: Some(99),
617 content: None,
618 })
619 .unwrap();
620
621 let req = ark_thread.join().unwrap();
622 assert_eq!(req.id, Some(99), "request mismatch");
623
624 let res = host.next_message().unwrap();
625 assert_eq!(res.id, Some(99), "response mismatch");
626 }
627
628 #[test]
630 fn test_verifier_rejects() {
631 testing::init_tracing();
632
633 struct Untrusting;
635
636 impl Verifier for Untrusting {
637 type Info = ();
638
639 fn verify(&self, _: &Attestation) -> Result<(xdsa::PublicKey, Self::Info), String> {
640 Err("attestation rejected".into())
641 }
642 }
643
644 let signer_key = xdsa::SecretKey::generate();
645
646 let (host_sock, ark_sock) = UnixStream::pair().unwrap();
647 let ark_reader = ark_sock.try_clone().unwrap();
648 let ark_writer = ark_sock;
649
650 let ark_thread = std::thread::spawn(move || {
653 let attestation = self_attestation(&signer_key);
654 let mut ark = ArkSide::new(ark_reader, ark_writer, signer_key, attestation);
655 ark.next_message()
656 });
657
658 let mut host = HostSide::new(host_sock.try_clone().unwrap(), host_sock);
660 let result = host.handshake(&Untrusting);
661 assert!(result.is_err(), "expected rejected handshake");
662
663 drop(host);
665 assert!(
666 ark_thread.join().unwrap().is_err(),
667 "expected torn down wire"
668 );
669 }
670
671 #[test]
675 fn test_roots_verifier() {
676 testing::init_tracing();
677
678 use crate::Roots;
679 use darkbio_crypto::cwt;
680 use darkbio_crypto::cwt::claims::{self, eat};
681 use darkbio_trust::device::{EmulatorClaims, HardwareClaims};
682 use darkbio_trust::{CRYPTO_DOMAIN_DEVICE_ATTESTATION, Realm};
683 use std::time::{SystemTime, UNIX_EPOCH};
684
685 let now = SystemTime::now()
686 .duration_since(UNIX_EPOCH)
687 .unwrap()
688 .as_secs();
689
690 fn handshake(
693 signer_key: xdsa::SecretKey,
694 attestation: Attestation,
695 hardware: &[xdsa::PublicKey],
696 emulator: &[xdsa::PublicKey],
697 ) -> Result<darkbio_trust::device::Device, Error> {
698 let (host_sock, ark_sock) = UnixStream::pair().unwrap();
699 let ark_reader = ark_sock.try_clone().unwrap();
700 let ark_writer = ark_sock;
701
702 let ark_thread = std::thread::spawn(move || {
703 let mut ark = ArkSide::new(ark_reader, ark_writer, signer_key, attestation);
704 ark.next_message()
705 });
706 let mut host = HostSide::new(host_sock.try_clone().unwrap(), host_sock);
707 let result = host.handshake(&Roots { hardware, emulator });
708
709 drop(host);
711 let _ = ark_thread.join().unwrap();
712 result
713 }
714
715 let hardware_root = xdsa::SecretKey::generate();
716 let emulator_root = xdsa::SecretKey::generate();
717 let hardware_roots = [hardware_root.public_key()];
718 let emulator_roots = [emulator_root.public_key()];
719
720 let signer_key = xdsa::SecretKey::generate();
722 let attestation = cwt::issue(
723 &HardwareClaims {
724 sub: claims::Subject {
725 sub: "ark-1234".into(),
726 },
727 cnf: claims::Confirm::new(signer_key.public_key()),
728 nbf: claims::NotBefore { nbf: now - 10 },
729 iat: claims::IssuedAt { iat: now - 10 },
730 oem: eat::Oemid::new_pen(65145),
731 hwm: eat::HwModel {
732 hw_model: b"Ark I".to_vec(),
733 },
734 hwv: eat::HwVersion::new("Ark I - 1.0.0".into()),
735 },
736 &hardware_root,
737 CRYPTO_DOMAIN_DEVICE_ATTESTATION,
738 )
739 .map(|cwt| Attestation::new(cwt).unwrap())
740 .unwrap();
741 let device = handshake(signer_key, attestation.clone(), &hardware_roots, &[]).unwrap();
742 assert_eq!(device.realm, Realm::Hardware, "realm mismatch");
743 assert_eq!(device.serial, "ark-1234", "serial mismatch");
744
745 let signer_key = xdsa::SecretKey::generate();
747 assert!(
748 handshake(signer_key, attestation, &[], &emulator_roots).is_err(),
749 "hardware attestation accepted under emulator roots"
750 );
751
752 let signer_key = xdsa::SecretKey::generate();
754 let attestation = cwt::issue(
755 &EmulatorClaims {
756 sub: claims::Subject {
757 sub: "emu-1234".into(),
758 },
759 cnf: claims::Confirm::new(signer_key.public_key()),
760 nbf: claims::NotBefore { nbf: now - 10 },
761 exp: claims::Expiration { exp: now + 1000 },
762 iat: claims::IssuedAt { iat: now - 10 },
763 oem: eat::Oemid::new_pen(65145),
764 hwm: eat::HwModel {
765 hw_model: b"Ark I".to_vec(),
766 },
767 hwv: eat::HwVersion::new("Ark I - 1.0.0".into()),
768 },
769 &emulator_root,
770 CRYPTO_DOMAIN_DEVICE_ATTESTATION,
771 )
772 .map(|cwt| Attestation::new(cwt).unwrap())
773 .unwrap();
774 let device = handshake(signer_key, attestation, &hardware_roots, &emulator_roots).unwrap();
775 assert_eq!(device.realm, Realm::Emulator, "realm mismatch");
776 assert_eq!(device.expiry, Some(now + 1000), "expiry mismatch");
777
778 let signer_key = xdsa::SecretKey::generate();
780 let attestation = cwt::issue(
781 &HardwareClaims {
782 sub: claims::Subject { sub: "".into() },
783 cnf: claims::Confirm::new(signer_key.public_key()),
784 nbf: claims::NotBefore { nbf: 0 },
785 iat: claims::IssuedAt { iat: 0 },
786 oem: eat::Oemid::new_pen(0),
787 hwm: eat::HwModel { hw_model: vec![] },
788 hwv: eat::HwVersion::new("".into()),
789 },
790 &signer_key,
791 CRYPTO_DOMAIN_DEVICE_ATTESTATION,
792 )
793 .map(|cwt| Attestation::new(cwt).unwrap())
794 .unwrap();
795 assert!(
796 handshake(signer_key, attestation, &hardware_roots, &emulator_roots).is_err(),
797 "self-signed attestation accepted"
798 );
799 }
800
801 #[test]
804 fn test_attestation_shapes() {
805 use darkbio_crypto::cwt::claims;
806 use darkbio_trust::CRYPTO_DOMAIN_DEVICE_ATTESTATION;
807
808 let signer = xdsa::SecretKey::generate();
809 let _ = self_attestation(&signer);
810
811 let emulator = darkbio_trust::device::EmulatorClaims {
812 sub: claims::Subject { sub: "".into() },
813 cnf: claims::Confirm::new(signer.public_key()),
814 nbf: claims::NotBefore { nbf: 0 },
815 exp: claims::Expiration { exp: u64::MAX },
816 iat: claims::IssuedAt { iat: 0 },
817 oem: claims::eat::Oemid::new_pen(0),
818 hwm: claims::eat::HwModel { hw_model: vec![] },
819 hwv: claims::eat::HwVersion::new("".into()),
820 };
821 let cwt = cwt::issue(&emulator, &signer, CRYPTO_DOMAIN_DEVICE_ATTESTATION).unwrap();
822 Attestation::new(cwt).expect("emulator attestation refused");
823
824 let cloud = darkbio_trust::cloud::SignerClaims {
825 iss: claims::Issuer { iss: "".into() },
826 sub: claims::Subject { sub: "".into() },
827 nbf: claims::NotBefore { nbf: 0 },
828 exp: claims::Expiration { exp: 1 },
829 cnf: claims::Confirm::new(signer.public_key()),
830 };
831 let cwt = cwt::issue(&cloud, &signer, CRYPTO_DOMAIN_DEVICE_ATTESTATION).unwrap();
832 assert!(
833 matches!(Attestation::new(cwt), Err(Error::InvalidAttestation)),
834 "cloud attestation accepted as device attestation"
835 );
836 assert!(
837 matches!(
838 Attestation::new(b"junk".to_vec()),
839 Err(Error::InvalidAttestation)
840 ),
841 "junk accepted as device attestation"
842 );
843 }
844
845 #[test]
849 fn test_malformed_attestation_rejected() {
850 testing::init_tracing();
851
852 struct Unreachable;
854
855 impl Verifier for Unreachable {
856 type Info = ();
857
858 fn verify(&self, _: &Attestation) -> Result<(xdsa::PublicKey, Self::Info), String> {
859 panic!("verifier consulted with a malformed attestation")
860 }
861 }
862
863 let signer_key = xdsa::SecretKey::generate();
864
865 let (host_sock, ark_sock) = UnixStream::pair().unwrap();
866 let ark_reader = ark_sock.try_clone().unwrap();
867 let ark_writer = ark_sock;
868
869 let ark_thread = std::thread::spawn(move || {
870 let attestation = Attestation(b"junk".to_vec());
871 let mut ark = ArkSide::new(ark_reader, ark_writer, signer_key, attestation);
872 ark.next_message()
873 });
874 let mut host = HostSide::new(host_sock.try_clone().unwrap(), host_sock);
875 assert!(
876 matches!(host.handshake(&Unreachable), Err(Error::InvalidAttestation)),
877 "malformed attestation not rejected"
878 );
879
880 drop(host);
882 assert!(
883 ark_thread.join().unwrap().is_err(),
884 "expected torn down wire"
885 );
886 }
887
888 #[test]
892 fn test_send_failure_drops_session() {
893 testing::init_tracing();
894
895 struct Faulty {
897 inner: UnixStream,
898 fail: Arc<AtomicBool>,
899 }
900
901 impl Write for Faulty {
902 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
903 if self.fail.load(Ordering::Relaxed) {
904 return Err(io::ErrorKind::BrokenPipe.into());
905 }
906 self.inner.write(buf)
907 }
908
909 fn flush(&mut self) -> io::Result<()> {
910 self.inner.flush()
911 }
912 }
913
914 let signer_key = xdsa::SecretKey::generate();
915 let signer_pub = signer_key.public_key();
916
917 let (host_sock, ark_sock) = UnixStream::pair().unwrap();
918 let ark_reader = ark_sock.try_clone().unwrap();
919 let ark_writer = ark_sock;
920
921 let ark_thread = std::thread::spawn(move || {
923 let attestation = self_attestation(&signer_key);
924 let mut ark = ArkSide::new(ark_reader, ark_writer, signer_key, attestation);
925 let req = ark.next_message().unwrap();
926 ark.send_message(ArkToHost {
927 id: req.id,
928 err: None,
929 content: None,
930 })
931 .unwrap();
932 req
933 });
934
935 let fail = Arc::new(AtomicBool::new(false));
936 let writer = Faulty {
937 inner: host_sock.try_clone().unwrap(),
938 fail: fail.clone(),
939 };
940 let mut host = HostSide::new(host_sock, writer);
941 host.handshake(&signer_pub).unwrap();
942
943 fail.store(true, Ordering::Relaxed);
946 let result = host.send_message(HostToArk {
947 id: Some(1),
948 content: None,
949 });
950 assert!(
951 matches!(result, Err(Error::SendFailed(_))),
952 "expected send failure"
953 );
954 let result = host.send_message(HostToArk {
955 id: Some(2),
956 content: None,
957 });
958 assert!(
959 matches!(result, Err(Error::EncryptionFailed(_))),
960 "expected dropped session"
961 );
962
963 fail.store(false, Ordering::Relaxed);
965 host.handshake(&signer_pub).unwrap();
966 host.send_message(HostToArk {
967 id: Some(3),
968 content: None,
969 })
970 .unwrap();
971
972 let req = ark_thread.join().unwrap();
973 assert_eq!(req.id, Some(3), "request mismatch");
974
975 let res = host.next_message().unwrap();
976 assert_eq!(res.id, Some(3), "response mismatch");
977 }
978}