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}