use crate::framing::Framing;
use crate::handshake;
use crate::protocol::{ArkToHost, HostToArk};
use crate::server::Attestation;
use crate::session::Session;
use crate::{
CRYPTO_DOMAIN_WIRE, CRYPTO_DOMAIN_WIRE_ARK_TO_HOST, CRYPTO_DOMAIN_WIRE_HOST_TO_ARK, Error,
};
use darkbio_crypto::{cbor, cose, xdsa, xhpke};
use darkbio_trust as trust;
use std::io::{Read, Write};
use std::time::{SystemTime, UNIX_EPOCH};
use tracing::{trace, warn};
pub(crate) const MAX_STALE_FRAMES: usize = 32;
pub trait Verifier {
type Info;
fn verify(&self, attestation: &Attestation) -> Result<(xdsa::PublicKey, Self::Info), String>;
}
impl Verifier for xdsa::PublicKey {
type Info = Attestation;
fn verify(&self, attestation: &Attestation) -> Result<(xdsa::PublicKey, Self::Info), String> {
Ok((self.clone(), attestation.clone()))
}
}
pub struct Roots<'a> {
pub hardware: &'a [xdsa::PublicKey], pub emulator: &'a [xdsa::PublicKey], }
impl Verifier for Roots<'_> {
type Info = trust::device::Device;
fn verify(&self, attestation: &Attestation) -> Result<(xdsa::PublicKey, Self::Info), String> {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|err| err.to_string())?
.as_secs();
let device = trust::device::verify(
attestation.as_bytes(),
self.hardware,
self.emulator,
Some(now),
)
.map_err(|err| err.to_string())?;
Ok((device.signer.clone(), device))
}
}
pub struct Client<R: Read, W: Write> {
framing: Framing<R, W>, session: Option<Session>, }
impl<R: Read, W: Write> Client<R, W> {
pub fn new(reader: R, writer: W) -> Self {
Self {
framing: Framing::new(reader, writer),
session: None,
}
}
pub fn handshake<V: Verifier>(&mut self, verifier: &V) -> Result<V::Info, Error> {
let host_xdsa_sk = xdsa::SecretKey::generate();
let host_xhpke_sk = xhpke::SecretKey::generate();
self.handshake_with(verifier, host_xdsa_sk, host_xhpke_sk, None)
}
fn handshake_with<V: Verifier>(
&mut self,
verifier: &V,
host_xdsa_sk: xdsa::SecretKey,
host_xhpke_sk: xhpke::SecretKey,
timestamp: Option<i64>,
) -> Result<V::Info, Error> {
self.session = None;
self.framing.send_reset()?;
let host_xdsa_pk = host_xdsa_sk.public_key();
let host_xhpke_pk = host_xhpke_sk.public_key();
let hello = cbor::encode(&handshake::HostHello {
host_signer: host_xdsa_pk.clone(),
host_crypto: host_xhpke_pk.clone(),
})
.map_err(|err| Error::HandshakeFailed(format!("failed to encode client hello: {}", err)))?;
self.framing.send_packet(&hello)?;
let host_xhpke_fp = host_xhpke_pk.fingerprint();
let mut stale = 0;
let size = loop {
let size = match self.framing.next_packet() {
Ok(Some(size)) => size,
Ok(None) | Err(Error::FrameDecodingFailed(_)) => 0,
Err(err) => return Err(err),
};
let recipient = cose::recipient(&self.framing.decobs_buffer[..size]);
if recipient.is_ok_and(|fp| fp == host_xhpke_fp) {
break size;
}
stale += 1;
if stale > MAX_STALE_FRAMES {
return Err(Error::HandshakeFailed(
"too many stale frames before server hello".into(),
));
}
warn!("skipping stale frame during handshake");
};
let auth = handshake::ArkHelloAuth {
host_signer: host_xdsa_pk.clone(),
host_crypto: host_xhpke_pk.clone(),
};
let sign1 = cose::decrypt(
&self.framing.decobs_buffer[..size],
&auth,
&host_xhpke_sk,
CRYPTO_DOMAIN_WIRE,
)
.map_err(|err| {
Error::HandshakeFailed(format!("failed to decrypt server hello: {}", err))
})?;
let unverified: handshake::ArkHello = cose::peek(&sign1).map_err(|err| {
Error::HandshakeFailed(format!("invalid server hello payload: {}", err))
})?;
let attestation = Attestation::new(unverified.ark_attest)?;
let (ark_identity, info) = verifier
.verify(&attestation)
.map_err(Error::HandshakeFailed)?;
let ark_hello: handshake::ArkHello =
cose::verify(&sign1, &auth, &ark_identity, CRYPTO_DOMAIN_WIRE, None).map_err(
|err| Error::HandshakeFailed(format!("server hello signature invalid: {}", err)),
)?;
let enc_a2h: [u8; xhpke::ENCAP_KEY_SIZE] = ark_hello
.a2h_encap
.try_into()
.map_err(|_| Error::HandshakeFailed("invalid a2h_encap size".into()))?;
let receiver = host_xhpke_sk
.new_receiver(&enc_a2h, CRYPTO_DOMAIN_WIRE_ARK_TO_HOST)
.map_err(|err| {
Error::HandshakeFailed(format!("client receiver setup failed: {}", err))
})?;
let ark_xhpke_pk = ark_hello.ark_crypto;
let (sender, enc_h2a) = ark_xhpke_pk
.new_sender(CRYPTO_DOMAIN_WIRE_HOST_TO_ARK)
.map_err(|err| {
Error::HandshakeFailed(format!("client sender setup failed: {}", err))
})?;
let ack = handshake::HostAck {
h2a_encap: enc_h2a.to_vec(),
};
let auth = handshake::HostAckAuth {
ark_signer: ark_identity,
ark_crypto: ark_xhpke_pk.clone(),
};
let ack = match timestamp {
Some(timestamp) => cose::seal_at(
&ack,
&auth,
&host_xdsa_sk,
&ark_xhpke_pk,
CRYPTO_DOMAIN_WIRE,
timestamp,
),
None => cose::seal(
&ack,
&auth,
&host_xdsa_sk,
&ark_xhpke_pk,
CRYPTO_DOMAIN_WIRE,
),
}
.map_err(|err| Error::HandshakeFailed(format!("failed to seal client ack: {}", err)))?;
self.framing.send_packet(&ack)?;
self.session = Some(Session { sender, receiver });
Ok(info)
}
pub fn next_message(&mut self) -> Result<ArkToHost, Error> {
let size = match self.framing.next_packet() {
Err(err) => {
self.session = None;
return Err(err);
}
Ok(None) => {
self.session = None;
return Err(Error::SessionReset);
}
Ok(Some(size)) => size,
};
let session = self
.session
.as_mut()
.ok_or_else(|| Error::EncryptionFailed("no active session".into()))?;
let res = match session.open(&self.framing.decobs_buffer[..size]) {
Err(err @ Error::EncryptionFailed(_)) => {
self.session = None;
return Err(err);
}
Err(err) => return Err(err),
Ok(res) => res,
};
trace!("read ark-to-host message ({} bytes encrypted)", size);
Ok(res)
}
pub fn send_message(&mut self, req: HostToArk) -> Result<(), Error> {
let session = self
.session
.as_mut()
.ok_or_else(|| Error::EncryptionFailed("no active session".into()))?;
let blob = match session.seal(&req, &mut self.framing.encode_buffer) {
Err(err @ Error::EncryptionFailed(_)) => {
self.session = None;
return Err(err);
}
Err(err) => return Err(err),
Ok(blob) => blob,
};
if let Err(err) = self.framing.send_packet(&blob) {
self.session = None;
return Err(err);
}
trace!("sent host-to-ark message ({} bytes)", blob.len());
Ok(())
}
#[doc(hidden)]
#[inline]
#[cfg(any(test, feature = "bench", feature = "fuzz"))]
#[cfg_attr(coverage_nightly, coverage(off))]
pub fn handshake_with_keys<V: Verifier>(
&mut self,
verifier: &V,
host_xdsa_sk: xdsa::SecretKey,
host_xhpke_sk: xhpke::SecretKey,
timestamp: i64,
) -> Result<V::Info, Error> {
self.handshake_with(verifier, host_xdsa_sk, host_xhpke_sk, Some(timestamp))
}
#[doc(hidden)]
#[inline]
#[cfg(any(test, feature = "bench", feature = "fuzz"))]
#[cfg_attr(coverage_nightly, coverage(off))]
pub fn next_packet_blob(&mut self) -> Result<Option<&[u8]>, Error> {
self.framing.next_packet_blob()
}
#[doc(hidden)]
#[inline]
#[cfg(any(test, feature = "bench", feature = "fuzz"))]
#[cfg_attr(coverage_nightly, coverage(off))]
pub fn send_packet_blob(&mut self, packet: &[u8]) -> Result<(), Error> {
self.framing.send_packet(packet)
}
#[doc(hidden)]
#[inline]
#[cfg(any(test, feature = "bench", feature = "fuzz"))]
#[cfg_attr(coverage_nightly, coverage(off))]
pub fn next_frame_blob(&mut self) -> Result<&[u8], Error> {
self.framing.next_frame_blob()
}
#[doc(hidden)]
#[inline]
#[cfg(any(test, feature = "bench", feature = "fuzz"))]
#[cfg_attr(coverage_nightly, coverage(off))]
pub fn send_frame_blob(&mut self, frame: &[u8]) -> Result<(), Error> {
self.framing.send_frame_blob(frame)
}
}