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 Server<R: Read, W: Write, A: Attester> {
73 framing: Framing<R, W>, signer: xdsa::SecretKey, attester: A, session: Option<Session>, #[cfg(any(test, feature = "bench", feature = "fuzz"))]
80 timestamp: Option<i64>, }
82
83impl<R: Read, W: Write, A: Attester> Server<R, W, A> {
84 pub fn new(reader: R, writer: W, signer: xdsa::SecretKey, attester: A) -> Self {
90 Self {
91 framing: Framing::new(reader, writer),
92 signer,
93 attester,
94 session: None,
95 #[cfg(any(test, feature = "bench", feature = "fuzz"))]
96 timestamp: None,
97 }
98 }
99
100 #[doc(hidden)]
104 #[inline]
105 #[cfg(any(test, feature = "bench", feature = "fuzz"))]
106 #[cfg_attr(coverage_nightly, coverage(off))]
107 pub fn new_at(
108 reader: R,
109 writer: W,
110 signer: xdsa::SecretKey,
111 attester: A,
112 timestamp: i64,
113 ) -> Self {
114 let mut server = Self::new(reader, writer, signer, attester);
115 server.timestamp = Some(timestamp);
116 server
117 }
118
119 pub fn next_message(&mut self) -> Result<HostToArk, Error> {
125 loop {
128 let size = match self.framing.next_packet() {
130 Err(Error::Terminated) => return Err(Error::Terminated),
132 Err(Error::RecvFailed(err)) => return Err(Error::RecvFailed(err)),
133
134 Err(err) => {
139 if self.session.take().is_some() {
140 warn!("failed to decode cobs packet, resetting session: {}", err);
141 } else {
142 warn!("failed to decode cobs packet: {}", err);
143 }
144 self.send_dropped();
145 continue;
146 }
147 Ok(None) => {
149 self.session = None;
150
151 match self.handshake() {
152 Err(Error::Terminated) => return Err(Error::Terminated),
154 Err(Error::RecvFailed(err)) => return Err(Error::RecvFailed(err)),
155
156 Err(err) => {
159 warn!("wire handshake failed: {}", err);
160 self.send_dropped();
161 continue;
162 }
163 Ok(session) => {
165 info!("new wire session established");
166 self.session = Some(session);
167 continue;
168 }
169 }
170 }
171 Ok(Some(size)) => size,
173 };
174 let session = match self.session.as_mut() {
177 None => {
178 warn!("dropping data outside session");
179 self.send_dropped();
180 continue;
181 }
182 Some(s) => s,
183 };
184 let req = match session.open(&self.framing.decobs_buffer[..size]) {
186 Err(Error::EncryptionFailed(err)) => {
189 warn!("decryption failed, resetting session: {}", err);
190 self.session = None;
191 self.send_dropped();
192 continue;
193 }
194 Err(err) => return Err(err),
195 Ok(req) => req,
196 };
197 trace!("read host-to-ark message ({} bytes encrypted)", size);
198 return Ok(req);
199 }
200 }
201
202 fn send_dropped(&mut self) {
205 if let Err(err) = self.framing.send_dropped() {
206 warn!("failed to signal dropped session: {}", err);
207 }
208 }
209
210 pub fn send_message(&mut self, res: ArkToHost) -> Result<(), Error> {
215 let session = self
218 .session
219 .as_mut()
220 .ok_or_else(|| Error::EncryptionFailed("no active session".into()))?;
221
222 let blob = match session.seal(&res, &mut self.framing.encode_buffer) {
223 Err(err @ Error::EncryptionFailed(_)) => {
224 self.session = None;
225 self.send_dropped();
226 return Err(err);
227 }
228 Err(err) => return Err(err),
229 Ok(blob) => blob,
230 };
231 if let Err(err) = self.framing.send_packet(&blob) {
234 self.session = None;
235 self.send_dropped();
236 return Err(err);
237 }
238 trace!("sent ark-to-host message ({} bytes)", blob.len());
239 Ok(())
240 }
241
242 fn handshake(&mut self) -> Result<Session, Error> {
249 loop {
250 let size = loop {
252 if let Some(n) = self.framing.next_packet()? {
253 break n;
254 }
255 };
256 let host_hello: handshake::HostHello =
257 cbor::decode(&self.framing.decobs_buffer[..size]).map_err(|err| {
258 Error::HandshakeFailed(format!("invalid client hello: {}", err))
259 })?;
260
261 let ark_crypto_key = xhpke::SecretKey::generate();
263 let ark_crypto_pub = ark_crypto_key.public_key();
264
265 let (sender, a2h_encap) = host_hello
266 .host_crypto
267 .new_sender(CRYPTO_DOMAIN_WIRE_ARK_TO_HOST)
268 .map_err(|err| {
269 Error::HandshakeFailed(format!("server sender setup failed: {}", err))
270 })?;
271
272 let ark_hello = handshake::ArkHello {
274 ark_attest: self.attester.attest().into_bytes(),
275 ark_crypto: ark_crypto_pub.clone(),
276 a2h_encap: a2h_encap.to_vec(),
277 };
278 let auth = handshake::ArkHelloAuth {
279 host_signer: host_hello.host_signer.clone(),
280 host_crypto: host_hello.host_crypto.clone(),
281 };
282 #[cfg(not(any(test, feature = "bench", feature = "fuzz")))]
283 let sealed = cose::seal(
284 &ark_hello,
285 &auth,
286 &self.signer,
287 &host_hello.host_crypto,
288 CRYPTO_DOMAIN_WIRE,
289 );
290 #[cfg(any(test, feature = "bench", feature = "fuzz"))]
291 let sealed = match self.timestamp {
292 Some(timestamp) => cose::seal_at(
293 &ark_hello,
294 &auth,
295 &self.signer,
296 &host_hello.host_crypto,
297 CRYPTO_DOMAIN_WIRE,
298 timestamp,
299 ),
300 None => cose::seal(
301 &ark_hello,
302 &auth,
303 &self.signer,
304 &host_hello.host_crypto,
305 CRYPTO_DOMAIN_WIRE,
306 ),
307 };
308 let ark_hello = sealed.map_err(|err| {
309 Error::HandshakeFailed(format!("failed to seal server hello: {}", err))
310 })?;
311
312 self.framing.send_packet(&ark_hello)?;
313
314 let Some(size) = self.framing.next_packet()? else {
317 warn!("session reset during handshake");
318 continue;
319 };
320 let host_ack: handshake::HostAck = cose::open(
321 &self.framing.decobs_buffer[..size],
322 &handshake::HostAckAuth {
323 ark_signer: self.signer.public_key(),
324 ark_crypto: ark_crypto_pub.clone(),
325 },
326 &ark_crypto_key,
327 &host_hello.host_signer,
328 CRYPTO_DOMAIN_WIRE,
329 None, )
331 .map_err(|err| Error::HandshakeFailed(format!("invalid client ack: {}", err)))?;
332
333 let enc_h2a: [u8; xhpke::ENCAP_KEY_SIZE] = host_ack
335 .h2a_encap
336 .try_into()
337 .map_err(|_| Error::HandshakeFailed("invalid h2a_encap size".into()))?;
338
339 let receiver = ark_crypto_key
340 .new_receiver(&enc_h2a, CRYPTO_DOMAIN_WIRE_HOST_TO_ARK)
341 .map_err(|err| {
342 Error::HandshakeFailed(format!("server receiver setup failed: {}", err))
343 })?;
344
345 return Ok(Session { sender, receiver });
347 }
348 }
349}
350
351#[cfg(all(test, unix))]
352#[cfg_attr(coverage_nightly, coverage(off))]
353mod tests {
354 use super::*;
355 use crate::testing;
356 use crate::{Client, Verifier};
357 use darkbio_cobs as cobs;
358 use std::io::Write;
359 use std::os::unix::net::UnixStream;
360
361 fn self_attestation(signer: &xdsa::SecretKey) -> Attestation {
364 use darkbio_crypto::cwt::claims::{self, eat};
365
366 let claims = darkbio_trust::device::HardwareClaims {
367 sub: claims::Subject { sub: "".into() },
368 cnf: claims::Confirm::new(signer.public_key()),
369 nbf: claims::NotBefore { nbf: 0 },
370 iat: claims::IssuedAt { iat: 0 },
371 oem: eat::Oemid::new_pen(0),
372 hwm: eat::HwModel { hw_model: vec![] },
373 hwv: eat::HwVersion::new("".into()),
374 };
375 let cwt = cwt::issue(
376 &claims,
377 signer,
378 darkbio_trust::CRYPTO_DOMAIN_DEVICE_ATTESTATION,
379 )
380 .unwrap();
381 Attestation::new(cwt).unwrap()
382 }
383
384 fn cobs_frame(data: &[u8]) -> Vec<u8> {
386 let mut buf = vec![0u8; cobs::encode_buffer(data.len())];
387 let n = cobs::encode(data, &mut buf).unwrap();
388 buf.truncate(n);
389 buf.push(0x00);
390 buf
391 }
392
393 #[test]
398 fn test_message_round_trip() {
399 testing::init_tracing();
400
401 let signer_key = xdsa::SecretKey::generate();
402 let signer_pub = signer_key.public_key();
403 let attestation = self_attestation(&signer_key);
404 let presented = attestation.clone();
405
406 let (host_sock, ark_sock) = UnixStream::pair().unwrap();
407 let ark_reader = ark_sock.try_clone().unwrap();
408 let ark_writer = ark_sock;
409
410 let ark_thread = std::thread::spawn(move || {
412 let mut server = Server::new(ark_reader, ark_writer, signer_key, attestation);
413 let mut ids = Vec::new();
414 for _ in 0..2 {
415 let req = server.next_message().unwrap();
416 ids.push(req.id);
417 server
418 .send_message(ArkToHost {
419 id: req.id,
420 err: None,
421 content: None,
422 })
423 .unwrap();
424 }
425 ids
426 });
427
428 let mut raw_sock = host_sock.try_clone().unwrap();
430
431 let mut client = Client::new(host_sock.try_clone().unwrap(), host_sock);
433 let attest = client.handshake(&signer_pub).unwrap();
434 assert_eq!(attest.as_bytes(), presented.as_bytes());
435 client
436 .send_message(HostToArk {
437 id: Some(1),
438 content: None,
439 })
440 .unwrap();
441 let res = client.next_message().unwrap();
442 assert_eq!(res.id, Some(1));
443
444 raw_sock
448 .write_all(&cobs_frame(b"interrupted transfer"))
449 .unwrap();
450 let result = client.next_message();
451 assert!(matches!(result, Err(Error::SessionReset)), "{result:?}");
452 let result = client.send_message(HostToArk {
453 id: Some(2),
454 content: None,
455 });
456 assert!(
457 matches!(result, Err(Error::EncryptionFailed(_))),
458 "{result:?}"
459 );
460
461 client.handshake(&signer_pub).unwrap();
463 client
464 .send_message(HostToArk {
465 id: Some(2),
466 content: None,
467 })
468 .unwrap();
469 let res = client.next_message().unwrap();
470 assert_eq!(res.id, Some(2));
471
472 let ids = ark_thread.join().unwrap();
473 assert_eq!(ids, vec![Some(1), Some(2)]);
474 }
475
476 #[test]
478 fn test_verifier_rejects() {
479 testing::init_tracing();
480
481 struct Untrusting;
483
484 impl Verifier for Untrusting {
485 type Info = ();
486
487 fn verify(&self, _: &Attestation) -> Result<(xdsa::PublicKey, Self::Info), String> {
488 Err("attestation rejected".into())
489 }
490 }
491
492 let signer_key = xdsa::SecretKey::generate();
493
494 let (host_sock, ark_sock) = UnixStream::pair().unwrap();
495 let ark_reader = ark_sock.try_clone().unwrap();
496 let ark_writer = ark_sock;
497
498 let ark_thread = std::thread::spawn(move || {
501 let attestation = self_attestation(&signer_key);
502 let mut server = Server::new(ark_reader, ark_writer, signer_key, attestation);
503 server.next_message()
504 });
505
506 let mut client = Client::new(host_sock.try_clone().unwrap(), host_sock);
508 let result = client.handshake(&Untrusting);
509 assert!(result.is_err());
510
511 drop(client);
513 assert!(ark_thread.join().unwrap().is_err());
514 }
515
516 #[test]
520 fn test_roots_verifier() {
521 testing::init_tracing();
522
523 use crate::Roots;
524 use darkbio_crypto::cwt;
525 use darkbio_crypto::cwt::claims::{self, eat};
526 use darkbio_trust::device::{EmulatorClaims, HardwareClaims};
527 use darkbio_trust::{CRYPTO_DOMAIN_DEVICE_ATTESTATION, Realm};
528 use std::time::{SystemTime, UNIX_EPOCH};
529
530 let now = SystemTime::now()
531 .duration_since(UNIX_EPOCH)
532 .unwrap()
533 .as_secs();
534
535 fn handshake(
538 signer_key: xdsa::SecretKey,
539 attestation: Attestation,
540 hardware: &[xdsa::PublicKey],
541 emulator: &[xdsa::PublicKey],
542 ) -> Result<darkbio_trust::device::Device, Error> {
543 let (host_sock, ark_sock) = UnixStream::pair().unwrap();
544 let ark_reader = ark_sock.try_clone().unwrap();
545 let ark_writer = ark_sock;
546
547 let ark_thread = std::thread::spawn(move || {
548 let mut server = Server::new(ark_reader, ark_writer, signer_key, attestation);
549 server.next_message()
550 });
551 let mut client = Client::new(host_sock.try_clone().unwrap(), host_sock);
552 let result = client.handshake(&Roots { hardware, emulator });
553
554 drop(client);
556 let _ = ark_thread.join().unwrap();
557 result
558 }
559
560 let hardware_root = xdsa::SecretKey::generate();
561 let emulator_root = xdsa::SecretKey::generate();
562 let hardware_roots = [hardware_root.public_key()];
563 let emulator_roots = [emulator_root.public_key()];
564
565 let signer_key = xdsa::SecretKey::generate();
567 let attestation = cwt::issue(
568 &HardwareClaims {
569 sub: claims::Subject {
570 sub: "ark-1234".into(),
571 },
572 cnf: claims::Confirm::new(signer_key.public_key()),
573 nbf: claims::NotBefore { nbf: now - 10 },
574 iat: claims::IssuedAt { iat: now - 10 },
575 oem: eat::Oemid::new_pen(65145),
576 hwm: eat::HwModel {
577 hw_model: b"Ark I".to_vec(),
578 },
579 hwv: eat::HwVersion::new("Ark I - 1.0.0".into()),
580 },
581 &hardware_root,
582 CRYPTO_DOMAIN_DEVICE_ATTESTATION,
583 )
584 .map(|cwt| Attestation::new(cwt).unwrap())
585 .unwrap();
586 let device = handshake(signer_key, attestation.clone(), &hardware_roots, &[]).unwrap();
587 assert_eq!(device.realm, Realm::Hardware);
588 assert_eq!(device.serial, "ark-1234");
589
590 let signer_key = xdsa::SecretKey::generate();
592 assert!(handshake(signer_key, attestation, &[], &emulator_roots).is_err());
593
594 let signer_key = xdsa::SecretKey::generate();
596 let attestation = cwt::issue(
597 &EmulatorClaims {
598 sub: claims::Subject {
599 sub: "emu-1234".into(),
600 },
601 cnf: claims::Confirm::new(signer_key.public_key()),
602 nbf: claims::NotBefore { nbf: now - 10 },
603 exp: claims::Expiration { exp: now + 1000 },
604 iat: claims::IssuedAt { iat: now - 10 },
605 oem: eat::Oemid::new_pen(65145),
606 hwm: eat::HwModel {
607 hw_model: b"Ark I".to_vec(),
608 },
609 hwv: eat::HwVersion::new("Ark I - 1.0.0".into()),
610 },
611 &emulator_root,
612 CRYPTO_DOMAIN_DEVICE_ATTESTATION,
613 )
614 .map(|cwt| Attestation::new(cwt).unwrap())
615 .unwrap();
616 let device = handshake(signer_key, attestation, &hardware_roots, &emulator_roots).unwrap();
617 assert_eq!(device.realm, Realm::Emulator);
618 assert_eq!(device.expiry, Some(now + 1000));
619
620 let signer_key = xdsa::SecretKey::generate();
622 let attestation = cwt::issue(
623 &HardwareClaims {
624 sub: claims::Subject { sub: "".into() },
625 cnf: claims::Confirm::new(signer_key.public_key()),
626 nbf: claims::NotBefore { nbf: 0 },
627 iat: claims::IssuedAt { iat: 0 },
628 oem: eat::Oemid::new_pen(0),
629 hwm: eat::HwModel { hw_model: vec![] },
630 hwv: eat::HwVersion::new("".into()),
631 },
632 &signer_key,
633 CRYPTO_DOMAIN_DEVICE_ATTESTATION,
634 )
635 .map(|cwt| Attestation::new(cwt).unwrap())
636 .unwrap();
637 assert!(handshake(signer_key, attestation, &hardware_roots, &emulator_roots).is_err());
638 }
639
640 #[test]
643 fn test_attestation_shapes() {
644 use darkbio_crypto::cwt::claims;
645 use darkbio_trust::CRYPTO_DOMAIN_DEVICE_ATTESTATION;
646
647 let signer = xdsa::SecretKey::generate();
648 let _ = self_attestation(&signer);
649
650 let emulator = darkbio_trust::device::EmulatorClaims {
651 sub: claims::Subject { sub: "".into() },
652 cnf: claims::Confirm::new(signer.public_key()),
653 nbf: claims::NotBefore { nbf: 0 },
654 exp: claims::Expiration { exp: u64::MAX },
655 iat: claims::IssuedAt { iat: 0 },
656 oem: claims::eat::Oemid::new_pen(0),
657 hwm: claims::eat::HwModel { hw_model: vec![] },
658 hwv: claims::eat::HwVersion::new("".into()),
659 };
660 let cwt = cwt::issue(&emulator, &signer, CRYPTO_DOMAIN_DEVICE_ATTESTATION).unwrap();
661 Attestation::new(cwt).expect("emulator attestation refused");
662
663 let cloud = darkbio_trust::cloud::SignerClaims {
664 iss: claims::Issuer { iss: "".into() },
665 sub: claims::Subject { sub: "".into() },
666 nbf: claims::NotBefore { nbf: 0 },
667 exp: claims::Expiration { exp: 1 },
668 cnf: claims::Confirm::new(signer.public_key()),
669 };
670 let cwt = cwt::issue(&cloud, &signer, CRYPTO_DOMAIN_DEVICE_ATTESTATION).unwrap();
671 let result = Attestation::new(cwt).map(|_| ());
672 assert!(
673 matches!(result, Err(Error::InvalidAttestation)),
674 "{result:?}"
675 );
676 let result = Attestation::new(b"junk".to_vec()).map(|_| ());
677 assert!(
678 matches!(result, Err(Error::InvalidAttestation)),
679 "{result:?}"
680 );
681 }
682}