Skip to main content

darkbio_trust/
lib.rs

1// trust-rs: dark bio ecosystem roots of trust
2// Copyright 2026 Dark Bio AG. All rights reserved.
3//
4// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file.
6
7// Pull in the README as the package doc
8#![doc = include_str!("../README.md")]
9// The crate only composes the cryptography crate and never needs unsafe itself
10#![forbid(unsafe_code)]
11
12pub mod cloud;
13pub mod device;
14pub mod roots;
15
16/// The cryptography crate this one builds on, re-exported so consumers can
17/// name its types at the exact version this crate was compiled against.
18pub use darkbio_crypto as crypto;
19
20use darkbio_crypto::{cwt, xdsa};
21use std::fmt;
22use std::time::Duration;
23
24/// Domain separator of device attestations, binding the signature of a root
25/// to the attestation format so it cannot be replayed into other protocols
26/// using the same key.
27pub const CRYPTO_DOMAIN_DEVICE_ATTESTATION: &[u8] = b"device-attestation-v1";
28
29/// Domain separator of cloud attestations, binding the signature of a cloud
30/// root to the attestation format so it cannot be replayed into other protocols
31/// using the same key.
32pub const CRYPTO_DOMAIN_CLOUD_ATTESTATION: &[u8] = b"cloud-attestation-v1";
33
34/// Longest validity period an emulator attestation may carry, bounding how
35/// long an emulated device stays attested.
36pub const EMULATOR_ATTESTATION_MAX_VALIDITY: Duration = Duration::from_secs(3600 * 24 * 30);
37
38/// Longest validity period a cloud attestation may carry.
39pub const CLOUD_ATTESTATION_MAX_VALIDITY: Duration = Duration::from_secs(3600 * 24 * 90);
40
41/// Environment represents the deployments of the ecosystem, which devices are
42/// built for and clouds run in, each with its own roots. Every environment can
43/// be named in every build, but its root keys are only embedded when the crate
44/// feature of the same name is enabled. An environment without keys has empty
45/// root sets, so nothing verifies under it.
46#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
47pub enum Environment {
48    /// The environment serving actual users, with devices manufactured for it.
49    Release,
50    /// The pre-release verification environment.
51    Staging,
52    /// The development deployments.
53    Develop,
54}
55
56impl fmt::Display for Environment {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        f.write_str(match self {
59            Environment::Release => "release",
60            Environment::Staging => "staging",
61            Environment::Develop => "develop",
62        })
63    }
64}
65
66/// Realm separates the hardware device universe from the emulated one.
67/// Hardware devices are attested once at manufacturing by the hardware roots and
68/// never expire, emulated devices are attested online by the emulator roots
69/// and always expire. The two realms never share trust.
70#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
71pub enum Realm {
72    /// Hardware Arks, attested once at manufacturing under the device root of
73    /// their series.
74    Hardware,
75    /// Emulated Arks, attested online under the emulator root with an expiry.
76    Emulator,
77}
78
79/// Failures during attestation verification.
80#[derive(Debug, thiserror::Error)]
81pub enum Error {
82    /// The token names a signer outside the selected roots. This identifier
83    /// and its optional root metadata have not been authenticated.
84    #[error("attestation signed by {}, which is not among the trusted roots", describe_signer(.fingerprint, .root))]
85    UntrustedSigner {
86        /// Fingerprint of the signer, taken from the unverified header.
87        fingerprint: xdsa::Fingerprint,
88        /// Published root metadata matching the claimed signer fingerprint.
89        root: Option<roots::Root>,
90    },
91    /// The claimed self-signer differs from the embedded identity key.
92    #[error("attestation is not self-signed")]
93    NotSelfSigned,
94    /// The signed validity period is empty, inverted or longer than the cap.
95    /// Enforced even when verification skips the clock check.
96    #[error("invalid attestation validity; expected a positive duration of at most {} days", max.as_secs() / 86400)]
97    InvalidValidity {
98        /// The longest validity period accepted for the attestation's kind.
99        max: Duration,
100    },
101    /// Signature, encoding, domain, claim-shape or clock-check failure.
102    #[error("cwt: {0}")]
103    Cwt(#[from] cwt::Error),
104}
105
106/// Enforces the lifetime cap independently of checks against the current time.
107fn check_validity(nbf: u64, exp: u64, max: Duration) -> Result<(), Error> {
108    if nbf >= exp || exp - nbf > max.as_secs() {
109        return Err(Error::InvalidValidity { max });
110    }
111    Ok(())
112}
113
114fn describe_signer(fingerprint: &xdsa::Fingerprint, root: &Option<roots::Root>) -> String {
115    match root {
116        Some(info) => format!("the {info} ({})", hex::encode(fingerprint.to_bytes())),
117        None => format!("unknown key {}", hex::encode(fingerprint.to_bytes())),
118    }
119}
120
121impl Error {
122    pub(crate) fn untrusted_signer(fingerprint: xdsa::Fingerprint) -> Self {
123        Self::UntrustedSigner {
124            root: roots::identify(&fingerprint),
125            fingerprint,
126        }
127    }
128}