Skip to main content

darkbio_wire/
lib.rs

1// wire-rs: encrypted protocol between Ark and host
2// Copyright 2025 Dark Bio AG. All rights reserved.
3
4// Pull in the README as the package doc
5#![doc = include_str!("../README.md")]
6
7pub mod protocol;
8
9mod framing;
10mod handshake;
11mod session;
12mod side_ark;
13mod side_host;
14
15pub use protocol::{ArkToHost, HostToArk};
16pub use side_ark::{ArkSide, Attestation, Attester};
17pub use side_host::{HostSide, Roots, Verifier};
18
19use std::io;
20
21/// Maximum limit for a frame size, above which it will be discarded from the
22/// wire protocol.
23pub const MAX_FRAME_SIZE: usize = 2 * 1024 * 1024;
24
25/// Largest protobuf message the wire carries, being what still fits a frame
26/// after the session's sealing and the COBS framing overheads are added.
27pub const MAX_MESSAGE_SIZE: usize = {
28    let mut size = MAX_FRAME_SIZE;
29    while darkbio_cobs::encode_buffer(size + session::Session::SEAL_OVERHEAD) > MAX_FRAME_SIZE {
30        size -= 1;
31    }
32    size
33};
34
35/// Domain separator for the COSE envelopes of the handshake, sealing the Ark's
36/// hello and the host's ack (the host's hello is plain CBOR). It binds their
37/// signatures and encryption to the wire, so a handshake signed by the Ark's
38/// identity key cannot be replayed into other protocols using the same key.
39pub(crate) const CRYPTO_DOMAIN_WIRE: &[u8] = b"wire-v1";
40
41/// HPKE info string for the ark-to-host encryption context of an established
42/// session (message traffic after the handshake, not the handshake itself).
43pub(crate) const CRYPTO_DOMAIN_WIRE_ARK_TO_HOST: &[u8] = b"wire-v1:ark-to-host";
44
45/// HPKE info string for the host-to-ark encryption context of an established
46/// session (message traffic after the handshake, not the handshake itself).
47pub(crate) const CRYPTO_DOMAIN_WIRE_HOST_TO_ARK: &[u8] = b"wire-v1:host-to-ark";
48
49/// Things that can go wrong in the wire transport.
50#[derive(Debug, thiserror::Error)]
51pub enum Error {
52    #[error("wire packet too large: {0} bytes, max {MAX_MESSAGE_SIZE} bytes")]
53    PacketTooLarge(usize),
54
55    #[error("wire packet encode failed: {0}")]
56    PacketEncodingFailed(prost::EncodeError),
57
58    #[error("wire packet decode failed: {0}")]
59    PacketDecodingFailed(prost::DecodeError),
60
61    #[error("wire frame too large: {0} bytes, max {MAX_FRAME_SIZE} bytes")]
62    FrameTooLarge(usize),
63
64    #[error("wire frame encode failed: {0}")]
65    FrameEncodingFailed(darkbio_cobs::EncodeError),
66
67    #[error("wire frame decode failed: {0}")]
68    FrameDecodingFailed(darkbio_cobs::DecodeError),
69
70    #[error("wire send failed: {0}")]
71    SendFailed(io::Error),
72
73    #[error("wire receive failed: {0}")]
74    RecvFailed(io::Error),
75
76    #[error("wire terminated")]
77    Terminated,
78
79    #[error("attestation is not for a hardware or emulator")]
80    InvalidAttestation,
81
82    #[error("wire handshake failed: {0}")]
83    HandshakeFailed(String),
84
85    #[error("wire encryption failed: {0}")]
86    EncryptionFailed(String),
87}
88
89#[cfg(test)]
90pub(crate) mod testing {
91    use std::sync::Once;
92
93    static INIT: Once = Once::new();
94
95    // init_tracing sets up a test logger to push log messages to stderr.
96    pub fn init_tracing() {
97        INIT.call_once(|| {
98            tracing_subscriber::fmt()
99                .with_env_filter(
100                    tracing_subscriber::EnvFilter::from_default_env()
101                        .add_directive(tracing::Level::TRACE.into()),
102                )
103                .with_ansi(true)
104                .with_test_writer()
105                .init();
106        });
107    }
108}