crates/installer/nordisk-pairing/src/mtls.rs
//! **The installer's mutual TLS — valid during bootstrap only.**
//!
//! The owner, 2026-09-24: the installer has a temporary mTLS certificate the
//! installing machines know, so they can contact the installer, which feeds
//! them what they need; and it is valid ONLY during bootstrap.
//!
//! One installer CA per MEDIUM (it was per lay until 2026-09-24 evening, and
//! that made every lay a new medium): minted by the laptop when the medium is
//! composed, its key kept beside the medium's receipt on the laptop (0600)
//! and pushed to each new front's door — never onto the medium, never onto a
//! box. It issues exactly three kinds of certificate:
//!
//! * **the door's** — the front's server certificate, for its utility address;
//! every box PINS the installer CA, so a box talks to this door or to nothing
//! (the join used to be plain HTTP, and a box believed whatever answered);
//! * **the bootstrap client certificate** — ON THE MEDIUM, with its key, the
//! same for every box. A key on a medium is only as secret as the medium
//! (it is uploaded to the cloud's storage), so this certificate is weak on
//! purpose: it may only JOIN, the door accepts it only for a slot that has
//! not joined yet, and it is refused outright once every slot has joined;
//! * **a box's own certificate** — signed at the first join from a request for
//! a key the box minted itself (the private half never leaves the box). From
//! then on that box speaks with it, and the bootstrap certificate is refused
//! for its slot.
//!
//! **"Valid during bootstrap only" is the DOOR's rule, not an expiry.** When
//! every slot has joined the door refuses the bootstrap certificate outright
//! (`join-withdrawn`), and a slot that has joined refuses it for good
//! (`join-spent`) — per lay, because each lay's door has its own ledger. The
//! CA's own lifetime is the caller's choice, tied to the medium: long enough
//! that one medium serves many lays, and the medium is recomposed (with a new
//! CA) before it ends ([`cert_left`]). Pairing inside the tunnel then runs on
//! gunnar's own issuer, as it always has.
//!
//! Pure Rust (`rcgen` + `rustls` on `ring`, X25519MLKEM768 first —
//! [`crate::pqtls`]), blocking sockets: an OS-less
//! PID 1 and a laptop both drive it without an async runtime.
use rcgen::{BasicConstraints, CertificateParams, CertificateSigningRequestParams, DistinguishedName, DnType, ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose, SanType};
use rustls::pki_types::pem::PemObject;
use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName};
use std::net::{IpAddr, Ipv4Addr};
use std::sync::Arc;
use std::time::Duration;
/// The CN every bootstrap client certificate carries. A box's own certificate
/// carries its NAME; the door tells them apart by this.
pub const BOOTSTRAP_CN: &str = "bootstrap";
/// The CN of the installer's own client certificate — the ONLY client a VM's
/// bootstrap listener serves (0.3.0). A box name can never be this.
pub const INSTALLER_CN: &str = "installer";
/// **The one name every VM's bootstrap listener answers to** — a DNS SAN on
/// the bootstrap certificate and on every box certificate. The installer
/// checks the chain against the installer CA and this name, never the
/// address: the cloud hands the address out after the medium is composed.
pub const VM_SERVER_NAME: &str = "bootstrap.installer";
fn vm_sans() -> Vec<SanType> {
match VM_SERVER_NAME.to_string().try_into() {
Ok(n) => vec![SanType::DnsName(n)],
Err(_) => Vec::new(),
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MtlsError(pub String);
impl std::fmt::Display for MtlsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "REFUSED [pairing-mtls] {}", self.0)
}
}
impl std::error::Error for MtlsError {}
fn e(what: &str) -> impl Fn(rcgen::Error) -> MtlsError + '_ {
move |err| MtlsError(format!("{what}: {err}"))
}
/// A certificate and its private key, both PEM.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Issued {
pub cert_pem: String,
pub key_pem: String,
}
/// **The installer CA.** `key_pem` stays on the front.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InstallerCa {
pub cert_pem: String,
pub key_pem: String,
}
fn window(valid_for: Duration) -> (time::OffsetDateTime, time::OffsetDateTime) {
// Five minutes of back-dating: a fresh box's clock is set from the front
// only after it has joined, and a certificate "not yet valid" by a few
// seconds of skew would refuse the very join that fixes the clock.
window_back(valid_for, time::Duration::minutes(5))
}
fn window_back(valid_for: Duration, back: time::Duration) -> (time::OffsetDateTime, time::OffsetDateTime) {
let now = time::OffsetDateTime::now_utc();
(now - back, now + time::Duration::seconds(valid_for.as_secs() as i64))
}
/// **How far the DOOR's certificate is back-dated: a day.** The door issues
/// it when it starts, from the front's clock, and the BOX checks it with its
/// own clock — which has not been set yet (it is set from the front AFTER the
/// join, `gunnar-clock` fed by the door). A box whose clock runs behind the
/// front's by more than the five minutes the other certificates allow would
/// refuse the only door that can fix it. Nothing about the door certificate
/// is weaker for it: the CA is pinned, and the address is in the SAN.
pub const DOOR_BACKDATE: Duration = Duration::from_secs(24 * 3600);
/// **How long a certificate (PEM) has left**, from its `notAfter`; zero once
/// it has ended. `now_unix` is injected so a caller's test does not wait. The
/// laptop asks it of a medium's installer CA (re-mint and recompose before it
/// ends), the door of its CA (its own certificate lives as long as the CA).
pub fn cert_left(pem: &str, now_unix: i64) -> Result<Duration, MtlsError> {
let (_, p) = x509_parser::pem::parse_x509_pem(pem.trim().as_bytes()).map_err(|err| MtlsError(format!("not a PEM certificate: {err}")))?;
let cert = p.parse_x509().map_err(|err| MtlsError(format!("not an X.509 certificate: {err}")))?;
let end = cert.validity().not_after.timestamp();
Ok(Duration::from_secs(end.saturating_sub(now_unix).max(0) as u64))
}
/// Seconds since the epoch, now (for [`cert_left`]).
pub fn now_unix() -> i64 {
time::OffsetDateTime::now_utc().unix_timestamp()
}
impl InstallerCa {
/// Mint the CA for one lay of `estate`, valid for `valid_for` (hours).
pub fn mint(estate: &str, valid_for: Duration) -> Result<InstallerCa, MtlsError> {
let key = KeyPair::generate().map_err(e("the CA key"))?;
let mut p = ca_params(&format!("nordisk-pairing installer CA — {estate}"));
(p.not_before, p.not_after) = window(valid_for);
let cert = p.self_signed(&key).map_err(e("the CA certificate"))?;
Ok(InstallerCa { cert_pem: cert.pem(), key_pem: key.serialize_pem() })
}
/// The CA as an `rcgen` issuer, rebuilt from its PEM and key.
fn issuer(&self) -> Result<rcgen::Issuer<'static, KeyPair>, MtlsError> {
let key = KeyPair::from_pem(&self.key_pem).map_err(e("the CA key"))?;
rcgen::Issuer::from_ca_cert_pem(&self.cert_pem, key).map_err(e("the CA certificate"))
}
fn leaf(&self, cn: &str, sans: Vec<SanType>, purpose: Vec<ExtendedKeyUsagePurpose>, valid_for: Duration) -> Result<Issued, MtlsError> {
self.leaf_back(cn, sans, purpose, window(valid_for))
}
fn leaf_back(&self, cn: &str, sans: Vec<SanType>, purpose: Vec<ExtendedKeyUsagePurpose>, validity: (time::OffsetDateTime, time::OffsetDateTime)) -> Result<Issued, MtlsError> {
let ca = self.issuer()?;
let key = KeyPair::generate().map_err(e("a leaf key"))?;
let mut p = CertificateParams::default();
let mut dn = DistinguishedName::new();
dn.push(DnType::CommonName, cn);
p.distinguished_name = dn;
p.subject_alt_names = sans;
p.extended_key_usages = purpose;
(p.not_before, p.not_after) = validity;
let cert = p.signed_by(&key, &ca).map_err(e("a leaf certificate"))?;
Ok(Issued { cert_pem: cert.pem(), key_pem: key.serialize_pem() })
}
/// The door's server certificate, for the front's utility address —
/// back-dated by [`DOOR_BACKDATE`], because a box checks it with a clock
/// nobody has set yet.
pub fn issue_door(&self, utility: Ipv4Addr, valid_for: Duration) -> Result<Issued, MtlsError> {
let back = time::Duration::seconds(DOOR_BACKDATE.as_secs() as i64);
self.leaf_back("nordisk-pairing door", vec![SanType::IpAddress(IpAddr::V4(utility))], vec![ExtendedKeyUsagePurpose::ServerAuth], window_back(valid_for, back))
}
/// **The bootstrap certificate the MEDIUM carries** — the same for every
/// VM of the medium. Since 0.3.0 it is first of all what each VM's
/// bootstrap listener SERVES the installer with (server auth, the fixed
/// name [`VM_SERVER_NAME`]). Client auth stays for the 0.2 join against a
/// front-hosted join service, which holger may still run.
pub fn issue_bootstrap(&self, valid_for: Duration) -> Result<Issued, MtlsError> {
self.leaf(BOOTSTRAP_CN, vm_sans(), vec![ExtendedKeyUsagePurpose::ServerAuth, ExtendedKeyUsagePurpose::ClientAuth], valid_for)
}
/// **The installer's own certificate** (0.3.0, owner 2026-09-24): the
/// client certificate the installer on the operator's machine shows every
/// VM it connects OUT to. CN [`INSTALLER_CN`] — the one role a VM's
/// bootstrap listener accepts. Minted per medium with the CA and kept
/// beside it on the operator's machine (0600); it never leaves that
/// machine. Back-dated a day: a VM checks it with a clock nobody has set.
pub fn issue_installer(&self, valid_for: Duration) -> Result<Issued, MtlsError> {
let back = time::Duration::seconds(DOOR_BACKDATE.as_secs() as i64);
self.leaf_back(INSTALLER_CN, Vec::new(), vec![ExtendedKeyUsagePurpose::ClientAuth], window_back(valid_for, back))
}
/// **A box's own certificate**, from its signing request: the CN is the
/// NAME the door gave the box (whatever the request asked for), the key is
/// the box's.
pub fn sign_box(&self, csr_pem: &str, name: &str, valid_for: Duration) -> Result<String, MtlsError> {
if name == BOOTSTRAP_CN || name == INSTALLER_CN {
return Err(MtlsError(format!("`{name}` is not a box name")));
}
let ca = self.issuer()?;
let mut csr = CertificateSigningRequestParams::from_pem(csr_pem).map_err(e("the box's signing request"))?;
let mut dn = DistinguishedName::new();
dn.push(DnType::CommonName, name);
csr.params.distinguished_name = dn;
// The VM name too: a VM that holds its own certificate serves its
// bootstrap listener with it after a restart (0.3.0).
csr.params.subject_alt_names = vm_sans();
csr.params.is_ca = IsCa::NoCa;
csr.params.extended_key_usages = vec![ExtendedKeyUsagePurpose::ServerAuth, ExtendedKeyUsagePurpose::ClientAuth];
(csr.params.not_before, csr.params.not_after) = window(valid_for);
Ok(csr.signed_by(&ca).map_err(e("the box's certificate"))?.pem())
}
}
impl InstallerCa {
/// **A box's certificate renewed** (0.5.12): the SAME key and name as `current`, a new
/// lifetime of `valid_for`, signed by this CA. No request is needed: the key is the box's
/// own and stays on it.
pub fn renew_box(&self, current: &str, valid_for: Duration) -> Result<String, MtlsError> {
let (spki, cn) = key_and_name(current)?;
if cn == BOOTSTRAP_CN || cn == INSTALLER_CN {
return Err(MtlsError(format!("`{cn}` is not a box name")));
}
let ca = self.issuer()?;
let public = rcgen::SubjectPublicKeyInfo::from_der(&spki).map_err(e("the box's public key"))?;
let mut p = CertificateParams::default();
let mut dn = DistinguishedName::new();
dn.push(DnType::CommonName, cn);
p.distinguished_name = dn;
p.subject_alt_names = vm_sans();
p.is_ca = IsCa::NoCa;
p.extended_key_usages = vec![ExtendedKeyUsagePurpose::ServerAuth, ExtendedKeyUsagePurpose::ClientAuth];
(p.not_before, p.not_after) = window(valid_for);
Ok(p.signed_by(&public, &ca).map_err(e("the renewed certificate"))?.pem())
}
}
impl InstallerCa {
/// **The CA's own certificate renewed** (0.5.13, a planned roll before its end): the SAME
/// key and name, a new lifetime. Every pin of the old certificate still holds — a trust
/// anchor is its name and key — so nothing the boxes or the shop hold changes.
pub fn renew_ca(&self, valid_for: Duration) -> Result<InstallerCa, MtlsError> {
let key = KeyPair::from_pem(&self.key_pem).map_err(e("the CA key"))?;
let (_, cn) = key_and_name(&self.cert_pem)?;
let mut p = ca_params(&cn);
(p.not_before, p.not_after) = window(valid_for);
let cert = p.self_signed(&key).map_err(e("the renewed CA certificate"))?;
Ok(InstallerCa { cert_pem: cert.pem(), key_pem: self.key_pem.clone() })
}
}
/// Pure: **why `boot` is not a bootstrap pair a VM keeps for its next join** (0.5.13), or
/// `None`: a certificate named [`BOOTSTRAP_CN`] whose key is the one handed with it.
pub fn bootstrap_refusal(boot: &Issued) -> Option<String> {
let (spki, cn) = match key_and_name(&boot.cert_pem) {
Ok(v) => v,
Err(err) => return Some(format!("REFUSED [rebootstrap-unreadable] the bootstrap certificate: {err}")),
};
if cn != BOOTSTRAP_CN {
return Some(format!("REFUSED [rebootstrap-not-bootstrap] the certificate names `{cn}`, not a bootstrap certificate (`{BOOTSTRAP_CN}`)"));
}
match KeyPair::from_pem(&boot.key_pem) {
Ok(k) if rcgen::PublicKeyData::subject_public_key_info(&k) == spki => None,
Ok(_) => Some("REFUSED [rebootstrap-other-key] the key handed is not the bootstrap certificate's".into()),
Err(err) => Some(format!("REFUSED [rebootstrap-unreadable] the bootstrap key: {err}")),
}
}
/// The DER public key and the CN a certificate (PEM) carries.
fn key_and_name(pem: &str) -> Result<(Vec<u8>, String), MtlsError> {
let (_, p) = x509_parser::pem::parse_x509_pem(pem.trim().as_bytes()).map_err(|err| MtlsError(format!("not a PEM certificate: {err}")))?;
let cert = p.parse_x509().map_err(|err| MtlsError(format!("not an X.509 certificate: {err}")))?;
let cn = cert.subject().iter_common_name().next().and_then(|a| a.as_str().ok()).ok_or_else(|| MtlsError("the certificate names no CN".into()))?;
Ok((cert.public_key().raw.to_vec(), cn.to_string()))
}
/// The installer CA's parameters: its one CN, a CA of path length 0 that signs
/// certificates. [`InstallerCa::mint`] and [`InstallerCa::renew_ca`] build the same
/// subject from it, so a renewed CA keeps the name every pin holds (rcgen 0.14 no
/// longer rebuilds parameters from a certificate).
fn ca_params(cn: &str) -> CertificateParams {
let mut p = CertificateParams::default();
let mut dn = DistinguishedName::new();
dn.push(DnType::CommonName, cn);
p.distinguished_name = dn;
p.is_ca = IsCa::Ca(BasicConstraints::Constrained(0));
p.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::DigitalSignature];
p
}
/// (notBefore, notAfter) of a certificate (PEM), unix seconds.
fn life(pem: &str) -> Result<(i64, i64), MtlsError> {
let (_, p) = x509_parser::pem::parse_x509_pem(pem.trim().as_bytes()).map_err(|err| MtlsError(format!("not a PEM certificate: {err}")))?;
let cert = p.parse_x509().map_err(|err| MtlsError(format!("not an X.509 certificate: {err}")))?;
Ok((cert.validity().not_before.timestamp(), cert.validity().not_after.timestamp()))
}
/// The share of a certificate's life after which it is renewed: two thirds.
pub const RENEW_AT: (i64, i64) = (2, 3);
/// Pure: **is a certificate due for renewal at `now_unix`?** At two thirds of its life (or past
/// its end).
pub fn renewal_due(pem: &str, now_unix: i64) -> Result<bool, MtlsError> {
let (from, to) = life(pem)?;
let at = from + (to - from).max(0) * RENEW_AT.0 / RENEW_AT.1;
Ok(now_unix >= at)
}
/// Pure: **why `new` is not a renewal of `current`**, or `None`: the same key, the same name, an
/// end no earlier.
pub fn renewal_refusal(current: &str, new: &str) -> Option<String> {
let (old_key, old_cn) = match key_and_name(current) {
Ok(v) => v,
Err(e) => return Some(format!("REFUSED [renew-unreadable] this VM's own certificate: {e}")),
};
let (new_key, new_cn) = match key_and_name(new) {
Ok(v) => v,
Err(e) => return Some(format!("REFUSED [renew-unreadable] the renewed certificate: {e}")),
};
if new_key != old_key {
return Some("REFUSED [renew-other-key] the certificate is for another key, not this VM's own".into());
}
if new_cn != old_cn {
return Some(format!("REFUSED [renew-other-name] the certificate names `{new_cn}`, this VM is `{old_cn}`"));
}
match (life(current), life(new)) {
(Ok((_, old_end)), Ok((_, new_end))) if new_end >= old_end => None,
_ => Some("REFUSED [renew-not-later] the certificate ends before the one this VM holds".into()),
}
}
/// **The CN of a certificate** (PEM) — what an attached estate's certificate
/// names (`<estate>/<box>`).
pub fn cert_cn(pem: &str) -> Result<String, MtlsError> {
let (_, p) = x509_parser::pem::parse_x509_pem(pem.trim().as_bytes()).map_err(|err| MtlsError(format!("not a PEM certificate: {err}")))?;
let cert = p.parse_x509().map_err(|err| MtlsError(format!("not an X.509 certificate: {err}")))?;
let cn = cert.subject().iter_common_name().next().and_then(|a| a.as_str().ok()).ok_or_else(|| MtlsError("the certificate names no CN".into()))?;
Ok(cn.to_string())
}
impl InstallerCa {
/// **An attached estate's certificate** (f-attach-enrol): the central
/// monetize's ATTACH CA signs the estate's own CSR — CN `cn`
/// (`<estate>/<box>`), the estate's control-plane host as a SAN (a DNS
/// name, or an IP), client AND server auth: the estate shows it calling the
/// central, and the central's dial into it verifies it.
pub fn sign_attached(&self, csr_pem: &str, cn: &str, host: &str, valid_for: Duration) -> Result<String, MtlsError> {
let ca = self.issuer()?;
let mut csr = CertificateSigningRequestParams::from_pem(csr_pem).map_err(e("the estate's signing request"))?;
let mut dn = DistinguishedName::new();
dn.push(DnType::CommonName, cn);
csr.params.distinguished_name = dn;
csr.params.subject_alt_names = match host.parse::<IpAddr>() {
Ok(ip) => vec![SanType::IpAddress(ip)],
Err(_) => vec![SanType::DnsName(host.try_into().map_err(e("the estate's host name"))?)],
};
csr.params.is_ca = IsCa::NoCa;
csr.params.extended_key_usages = vec![ExtendedKeyUsagePurpose::ServerAuth, ExtendedKeyUsagePurpose::ClientAuth];
(csr.params.not_before, csr.params.not_after) = window(valid_for);
Ok(csr.signed_by(&ca).map_err(e("the estate's certificate"))?.pem())
}
}
/// **A box's own key and its signing request.** The key stays on the box.
pub fn box_request() -> Result<(String, String), MtlsError> {
let key = KeyPair::generate().map_err(e("the box key"))?;
request_for(key)
}
/// A signing request for a key the box ALREADY holds (PEM) — a box that
/// minted its key on an earlier boot asks for the same key, not a new one.
pub fn box_request_for(key_pem: &str) -> Result<(String, String), MtlsError> {
request_for(KeyPair::from_pem(key_pem).map_err(e("the box key"))?)
}
fn request_for(key: KeyPair) -> Result<(String, String), MtlsError> {
let mut p = CertificateParams::default();
let mut dn = DistinguishedName::new();
dn.push(DnType::CommonName, "box");
p.distinguished_name = dn;
let csr = p.serialize_request(&key).map_err(e("the signing request"))?;
Ok((csr.pem().map_err(e("the signing request as PEM"))?, key.serialize_pem()))
}
fn certs(pem: &str) -> Result<Vec<CertificateDer<'static>>, MtlsError> {
let v: Vec<_> = CertificateDer::pem_slice_iter(pem.as_bytes()).collect::<Result<_, _>>().map_err(|err| MtlsError(format!("a certificate PEM: {err:?}")))?;
if v.is_empty() {
return Err(MtlsError("a certificate PEM with no certificate in it".into()));
}
Ok(v)
}
fn key(pem: &str) -> Result<PrivateKeyDer<'static>, MtlsError> {
PrivateKeyDer::from_pem_slice(pem.as_bytes()).map_err(|err| MtlsError(format!("a private key PEM: {err:?}")))
}
/// ring, with X25519MLKEM768 offered first ([`crate::pqtls`]): the feed
/// crosses this TLS, and with it the estate's WireGuard pre-shared keys.
fn provider() -> Arc<rustls::crypto::CryptoProvider> {
Arc::new(crate::pqtls::provider())
}
fn roots(ca_pem: &str) -> Result<rustls::RootCertStore, MtlsError> {
let mut r = rustls::RootCertStore::empty();
for c in certs(ca_pem)? {
r.add(c).map_err(|err| MtlsError(format!("the installer CA as a root: {err}")))?;
}
Ok(r)
}
/// **The door's TLS**: its own certificate, and a client certificate from the
/// installer CA REQUIRED on every connection.
pub fn server_config(ca_pem: &str, door: &Issued) -> Result<Arc<rustls::ServerConfig>, MtlsError> {
let verifier = rustls::server::WebPkiClientVerifier::builder_with_provider(Arc::new(roots(ca_pem)?), provider())
.build()
.map_err(|err| MtlsError(format!("the client verifier: {err}")))?;
let cfg = rustls::ServerConfig::builder_with_provider(provider())
.with_protocol_versions(&[&rustls::version::TLS13])
.map_err(|err| MtlsError(format!("TLS 1.3: {err}")))?
.with_client_cert_verifier(verifier)
.with_single_cert(certs(&door.cert_pem)?, key(&door.key_pem)?)
.map_err(|err| MtlsError(format!("the door's certificate: {err}")))?;
Ok(Arc::new(cfg))
}
/// **A box's TLS**: the installer CA pinned as the ONLY root, and the client
/// certificate it presents (the bootstrap one, or its own after the first join).
pub fn client_config(ca_pem: &str, client: &Issued) -> Result<Arc<rustls::ClientConfig>, MtlsError> {
let cfg = rustls::ClientConfig::builder_with_provider(provider())
.with_protocol_versions(&[&rustls::version::TLS13])
.map_err(|err| MtlsError(format!("TLS 1.3: {err}")))?
.with_root_certificates(roots(ca_pem)?)
.with_client_auth_cert(certs(&client.cert_pem)?, key(&client.key_pem)?)
.map_err(|err| MtlsError(format!("the client certificate: {err}")))?;
Ok(Arc::new(cfg))
}
/// The server name a box checks the door's certificate against: its address.
pub fn door_name(utility: Ipv4Addr) -> ServerName<'static> {
ServerName::IpAddress(IpAddr::V4(utility).into())
}
/// The CN of the certificate a client presented — `bootstrap` or a box name.
/// The chain was already verified against the installer CA by the handshake.
pub fn peer_cn(conn: &rustls::ServerConnection) -> Option<String> {
cn_of(conn.peer_certificates()?.first()?)
}
/// **The name the installer checks a VM's certificate against**
/// ([`VM_SERVER_NAME`]) — never the address it dialled (0.3.0).
pub fn vm_name() -> ServerName<'static> {
ServerName::try_from(VM_SERVER_NAME).expect("VM_SERVER_NAME is a valid DNS name")
}
/// The CN of the certificate a VM SERVED the installer — `bootstrap`, or the
/// box's own name once it holds one. Verified against the installer CA and
/// [`VM_SERVER_NAME`] by the handshake.
pub fn server_cn(conn: &rustls::ClientConnection) -> Option<String> {
cn_of(conn.peer_certificates()?.first()?)
}
fn cn_of(der: &CertificateDer<'_>) -> Option<String> {
let (_, cert) = x509_parser::parse_x509_certificate(der.as_ref()).ok()?;
let cn = cert.subject().iter_common_name().next()?.as_str().ok()?.to_string();
Some(cn)
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::{Read, Write};
const H: Duration = Duration::from_secs(3600);
/// A real TLS 1.3 handshake over loopback: the door sees `bootstrap`, then
/// the box's own name; a certificate from ANOTHER CA is refused, and a box
/// that pins the installer CA refuses a door it did not issue.
#[test]
fn a_box_joins_with_the_bootstrap_cert_then_speaks_as_itself_and_strangers_are_refused_both_ways() {
let ca = InstallerCa::mint("fleet.test", H).unwrap();
let door = ca.issue_door(Ipv4Addr::LOCALHOST, H).unwrap();
let boot = ca.issue_bootstrap(H).unwrap();
let (csr, box_key) = box_request().unwrap();
let mine = Issued { cert_pem: ca.sign_box(&csr, "main", H).unwrap(), key_pem: box_key };
let other = InstallerCa::mint("another", H).unwrap();
let stranger = other.issue_bootstrap(H).unwrap();
let serve = |client: Issued, pin: String| -> Result<String, String> {
let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = l.local_addr().unwrap();
let scfg = server_config(&ca.cert_pem, &door).unwrap();
let t = std::thread::spawn(move || {
let (s, _) = l.accept().unwrap();
let conn = rustls::ServerConnection::new(scfg).unwrap();
let mut tls = rustls::StreamOwned::new(conn, s);
let mut b = [0u8; 5];
match tls.read_exact(&mut b) {
Ok(()) => {
let cn = peer_cn(&tls.conn).unwrap_or_default();
let _ = tls.write_all(cn.as_bytes());
tls.conn.send_close_notify();
let _ = tls.flush();
Ok(cn)
}
Err(err) => Err(err.to_string()),
}
});
let ccfg = client_config(&pin, &client).map_err(|e| e.to_string())?;
let s = std::net::TcpStream::connect(addr).unwrap();
let conn = rustls::ClientConnection::new(ccfg, door_name(Ipv4Addr::LOCALHOST)).unwrap();
let mut tls = rustls::StreamOwned::new(conn, s);
let sent = tls.write_all(b"hello").and_then(|_| tls.flush());
let mut back = String::new();
let read = tls.read_to_string(&mut back);
let server = t.join().unwrap();
match (sent, read, server) {
(Ok(()), Ok(_), Ok(cn)) if back == cn => Ok(cn),
(s, r, sv) => Err(format!("sent {s:?} read {r:?} server {sv:?}")),
}
};
assert_eq!(serve(boot.clone(), ca.cert_pem.clone()), Ok(BOOTSTRAP_CN.to_string()));
assert_eq!(serve(mine, ca.cert_pem.clone()), Ok("main".to_string()), "the box speaks as the name the door gave it");
assert!(serve(stranger, ca.cert_pem.clone()).is_err(), "a certificate from another CA is refused by the door");
assert!(serve(boot, other.cert_pem.clone()).is_err(), "a box that pins the installer CA refuses a door it did not issue");
}
/// **The installer's mutual TLS is post-quantum** (2026-10-06): the feed
/// crosses it — and with the feed every WireGuard pre-shared key of the
/// estate — so the door and a box negotiate X25519MLKEM768
/// ([`crate::pqtls`]), and a recording of the link opens for nobody later.
#[test]
fn the_door_and_a_box_negotiate_x25519mlkem768() {
let ca = InstallerCa::mint("fleet.test", H).unwrap();
let door = ca.issue_door(Ipv4Addr::LOCALHOST, H).unwrap();
let boot = ca.issue_bootstrap(H).unwrap();
let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = l.local_addr().unwrap();
let scfg = server_config(&ca.cert_pem, &door).unwrap();
let t = std::thread::spawn(move || {
let (s, _) = l.accept().unwrap();
let mut tls = rustls::StreamOwned::new(rustls::ServerConnection::new(scfg).unwrap(), s);
let mut b = [0u8; 5];
tls.read_exact(&mut b).unwrap();
tls.write_all(b"done!").unwrap();
tls.flush().unwrap();
tls.conn.negotiated_key_exchange_group().map(|g| g.name())
});
let ccfg = client_config(&ca.cert_pem, &boot).unwrap();
let conn = rustls::ClientConnection::new(ccfg, door_name(Ipv4Addr::LOCALHOST)).unwrap();
let mut tls = rustls::StreamOwned::new(conn, std::net::TcpStream::connect(addr).unwrap());
tls.write_all(b"hello").unwrap();
tls.flush().unwrap();
let mut b = [0u8; 5];
tls.read_exact(&mut b).unwrap();
let client = tls.conn.negotiated_key_exchange_group().map(|g| g.name());
assert_eq!((t.join().unwrap(), client), (Some(rustls::NamedGroup::X25519MLKEM768), Some(rustls::NamedGroup::X25519MLKEM768)));
}
/// The door's certificate is valid from a day back (a box checks it with
/// a clock nobody has set yet); the other leaves keep five minutes. And
/// what a certificate has left is read off it.
#[test]
fn the_door_certificate_is_back_dated_a_day_and_what_a_certificate_has_left_is_read_off_it() {
let ca = InstallerCa::mint("fleet.test", 90 * 24 * H).unwrap();
let now = now_unix();
let nb = |pem: &str| {
let der = certs(pem).unwrap().remove(0);
let (_, c) = x509_parser::parse_x509_certificate(der.as_ref()).unwrap();
c.validity().not_before.timestamp()
};
let door = ca.issue_door(Ipv4Addr::LOCALHOST, H).unwrap();
let back = now - nb(&door.cert_pem);
assert!((24 * 3600 - 60..=24 * 3600 + 60).contains(&back), "the door is back-dated a day, got {back} s");
let boot = ca.issue_bootstrap(H).unwrap();
assert!(now - nb(&boot.cert_pem) <= 5 * 60 + 60, "the bootstrap certificate keeps five minutes");
let left = cert_left(&ca.cert_pem, now).unwrap();
assert!(left > 89 * 24 * H && left <= 90 * 24 * H, "{left:?}");
assert_eq!(cert_left(&ca.cert_pem, now + 91 * 24 * 3600).unwrap(), Duration::ZERO, "ended");
assert!(cert_left("not a pem", now).is_err());
}
#[test]
fn the_box_request_is_signed_with_the_door_given_name_and_bootstrap_is_not_a_name() {
let ca = InstallerCa::mint("fleet.test", H).unwrap();
let (csr, _) = box_request().unwrap();
let pem = ca.sign_box(&csr, "twin", H).unwrap();
let der = certs(&pem).unwrap().remove(0);
let (_, cert) = x509_parser::parse_x509_certificate(der.as_ref()).unwrap();
assert_eq!(cert.subject().iter_common_name().next().unwrap().as_str().unwrap(), "twin");
assert!(ca.sign_box(&csr, BOOTSTRAP_CN, H).is_err());
assert!(ca.sign_box("not a csr", "twin", H).is_err());
}
/// ★ **The installer CA renews ITSELF before its end** (0.5.13, the 180-day gap): the same
/// key and name, a later end; a door and a box certificate issued by the RENEWED CA are taken
/// by peers that still pin the OLD certificate — the boxes' seeds and the shop's custody need
/// nothing.
#[test]
fn a_renewed_ca_keeps_its_key_and_name_and_the_old_pin_still_holds() {
let ca = InstallerCa::mint("fleet.test", H).unwrap();
let renewed = ca.renew_ca(180 * 24 * H).unwrap();
assert_eq!(renewed.key_pem, ca.key_pem, "the same key");
assert_ne!(renewed.cert_pem, ca.cert_pem);
assert_eq!(key_and_name(&renewed.cert_pem).unwrap(), key_and_name(&ca.cert_pem).unwrap(), "the same key and name");
assert!(cert_left(&renewed.cert_pem, 0).unwrap() > cert_left(&ca.cert_pem, 0).unwrap(), "a later end");
let door = renewed.issue_door(Ipv4Addr::LOCALHOST, H).unwrap();
let (csr, k) = box_request().unwrap();
let mine = Issued { cert_pem: renewed.sign_box(&csr, "main", H).unwrap(), key_pem: k };
let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = l.local_addr().unwrap();
let scfg = server_config(&ca.cert_pem, &door).unwrap();
let t = std::thread::spawn(move || {
let (s, _) = l.accept().unwrap();
let mut tls = rustls::StreamOwned::new(rustls::ServerConnection::new(scfg).unwrap(), s);
let mut b = [0u8; 5];
tls.read_exact(&mut b).map(|_| peer_cn(&tls.conn).unwrap_or_default()).map_err(|e| e.to_string())
});
let ccfg = client_config(&ca.cert_pem, &mine).unwrap();
let s = std::net::TcpStream::connect(addr).unwrap();
let mut tls = rustls::StreamOwned::new(rustls::ClientConnection::new(ccfg, door_name(Ipv4Addr::LOCALHOST)).unwrap(), s);
tls.write_all(b"hello").and_then(|_| tls.flush()).expect("the box takes the renewed CA's door under the old pin");
assert_eq!(t.join().unwrap(), Ok("main".to_string()), "the door takes the renewed CA's box under the old pin");
}
/// One mTLS handshake: a box holding `mine` dials a door holding `door`, both pinning
/// `pin`. The door's answer: the box's CN, or why either side refused.
fn handshake(pin: &str, door: Issued, mine: Issued) -> Result<String, String> {
let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = l.local_addr().unwrap();
let scfg = server_config(pin, &door).map_err(|e| e.to_string())?;
let t = std::thread::spawn(move || {
let (s, _) = l.accept().map_err(|e| e.to_string())?;
let mut tls = rustls::StreamOwned::new(rustls::ServerConnection::new(scfg).unwrap(), s);
let mut b = [0u8; 5];
tls.read_exact(&mut b).map(|_| peer_cn(&tls.conn).unwrap_or_default()).map_err(|e| e.to_string())
});
let ccfg = client_config(pin, &mine).map_err(|e| e.to_string())?;
let s = std::net::TcpStream::connect(addr).unwrap();
let mut tls = rustls::StreamOwned::new(rustls::ClientConnection::new(ccfg, door_name(Ipv4Addr::LOCALHOST)).unwrap(), s);
let sent = tls.write_all(b"hello").and_then(|_| tls.flush()).map_err(|e| format!("the box: {e}"));
drop(tls);
let heard = t.join().unwrap();
sent.and(heard)
}
/// ★ **S21: day 200 with the original installer CA** (its 180 days are over) — a trust
/// anchor is its name and key: certificates the EXPIRED CA issues still pass under the pin
/// both sides hold, so an estate whose front missed the renewal still joins and re-admits;
/// the CA is due, renews (same key, a later end) and the old pin keeps holding.
#[test]
fn day_200_with_the_original_ca_still_joins_and_the_ca_renews_under_the_old_pin() {
let fresh = InstallerCa::mint("fleet.test", H).unwrap();
let key = KeyPair::from_pem(&fresh.key_pem).unwrap();
let mut p = ca_params(&key_and_name(&fresh.cert_pem).unwrap().1);
let now = time::OffsetDateTime::now_utc();
(p.not_before, p.not_after) = (now - time::Duration::days(200), now - time::Duration::days(20));
let expired = InstallerCa { cert_pem: p.self_signed(&key).unwrap().pem(), key_pem: fresh.key_pem.clone() };
let now_unix = now.unix_timestamp();
assert_eq!(cert_left(&expired.cert_pem, now_unix).unwrap(), Duration::ZERO, "the CA certificate is past its end");
assert!(renewal_due(&expired.cert_pem, now_unix).unwrap(), "and due");
let issue = |ca: &InstallerCa| {
let (csr, k) = box_request().unwrap();
(ca.issue_door(Ipv4Addr::LOCALHOST, H).unwrap(), Issued { cert_pem: ca.sign_box(&csr, "main", H).unwrap(), key_pem: k })
};
let (door, mine) = issue(&expired);
assert_eq!(handshake(&expired.cert_pem, door, mine), Ok("main".to_string()), "an expired anchor still anchors");
let renewed = expired.renew_ca(180 * 24 * H).unwrap();
assert!(cert_left(&renewed.cert_pem, now_unix).unwrap() > 170 * 24 * H, "renewed past day 200: a new lifetime");
assert!(!renewal_due(&renewed.cert_pem, now_unix).unwrap());
let (door, mine) = issue(&renewed);
assert_eq!(handshake(&expired.cert_pem, door, mine), Ok("main".to_string()), "the boxes' old pin takes the renewed CA's certificates");
}
}