polyc-crypto 2026.7.1

Provenance signatures (commonware-cryptography ed25519) for polychrome tool calls.
Documentation
//! Shared CA-bundle PEM loading for TLS trust stores.
//!
//! Every trust-store consumer (the substrate `ateapi` client, the #1167
//! control-plane<->harness mTLS pair) needs the same fail-closed
//! "read this PEM file, add every certificate in it to a root store, error
//! loudly if the file is unreadable or carries none" shape. One shared
//! loader means that failure-loudness behavior can't drift between call
//! sites.

use rustls::RootCertStore;
use rustls::pki_types::{CertificateDer, pem::PemObject};

/// A CA bundle failed to load into a [`RootCertStore`].
#[derive(Debug, thiserror::Error)]
pub enum CaLoadError {
    /// The file couldn't be opened or read.
    #[error("read CA file {path}: {source}")]
    Read {
        /// The path that failed to read.
        path: String,
        /// The underlying I/O/PEM-decode error.
        #[source]
        source: rustls::pki_types::pem::Error,
    },
    /// A certificate in the file was structurally invalid.
    #[error("parse CA certificate in {path}: {source}")]
    Parse {
        /// The file the invalid certificate came from.
        path: String,
        /// The underlying PEM-decode error.
        #[source]
        source: rustls::pki_types::pem::Error,
    },
    /// A certificate was well-formed but rustls rejected it (e.g. an
    /// unsupported signature algorithm).
    #[error("add CA certificate from {path} to root store: {source}")]
    Reject {
        /// The file the rejected certificate came from.
        path: String,
        /// The underlying rustls error.
        #[source]
        source: rustls::Error,
    },
    /// The file parsed but named zero certificates — an explicitly
    /// configured CA that's empty or malformed in a way that doesn't error.
    #[error("CA file {path} contained no certificates")]
    Empty {
        /// The empty file's path.
        path: String,
    },
}

/// Load every certificate in the PEM file `path` into `roots`.
///
/// Fail-closed: refusing to silently produce a verifier that trusts nothing
/// (which would reject every real connection with no signal *why*) is the
/// caller's job — this function only reports the file-level problem
/// (unreadable, unparseable, empty) so the caller can refuse to start rather
/// than guess.
///
/// # Errors
///
/// Returns [`CaLoadError`] if the file can't be read, contains an
/// unparseable or rustls-rejected certificate, or names no certificates at
/// all.
pub fn load_ca_into(roots: &mut RootCertStore, path: &str) -> Result<(), CaLoadError> {
    let mut added = 0usize;
    for cert in CertificateDer::pem_file_iter(path).map_err(|source| CaLoadError::Read {
        path: path.to_owned(),
        source,
    })? {
        let cert = cert.map_err(|source| CaLoadError::Parse {
            path: path.to_owned(),
            source,
        })?;
        roots.add(cert).map_err(|source| CaLoadError::Reject {
            path: path.to_owned(),
            source,
        })?;
        added += 1;
    }
    if added == 0 {
        return Err(CaLoadError::Empty {
            path: path.to_owned(),
        });
    }
    Ok(())
}

/// Load every certificate in the in-memory PEM bundle `pem` into `roots`.
///
/// Same fail-closed contract and error shape as [`load_ca_into`], for a CA
/// bundle already held in memory (e.g. read from a Kubernetes Secret) rather
/// than a file on disk. `label` identifies the source in error messages
/// (there's no path to report).
///
/// # Errors
///
/// Returns [`CaLoadError`] if `pem` contains an unparseable or
/// rustls-rejected certificate, or names no certificates at all.
pub fn load_ca_pem_into(
    roots: &mut RootCertStore,
    label: &str,
    pem: &[u8],
) -> Result<(), CaLoadError> {
    let mut added = 0usize;
    for cert in CertificateDer::pem_slice_iter(pem) {
        let cert = cert.map_err(|source| CaLoadError::Parse {
            path: label.to_owned(),
            source,
        })?;
        roots.add(cert).map_err(|source| CaLoadError::Reject {
            path: label.to_owned(),
            source,
        })?;
        added += 1;
    }
    if added == 0 {
        return Err(CaLoadError::Empty {
            path: label.to_owned(),
        });
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    #![allow(clippy::pedantic, clippy::nursery, missing_docs)]
    use super::*;

    #[test]
    fn missing_file_is_a_read_error() {
        let mut roots = RootCertStore::empty();
        let err = load_ca_into(&mut roots, "/nonexistent/ca.pem").unwrap_err();
        assert!(matches!(err, CaLoadError::Read { .. }), "got {err:?}");
        assert!(err.to_string().contains("ca.pem"), "got: {err}");
    }

    #[test]
    fn empty_file_is_an_empty_error() {
        let dir = std::env::temp_dir();
        let path = dir.join(format!("polyc-crypto-tls-test-{}.pem", std::process::id()));
        std::fs::write(&path, b"").unwrap();
        let mut roots = RootCertStore::empty();
        let err = load_ca_into(&mut roots, path.to_str().unwrap()).unwrap_err();
        std::fs::remove_file(&path).ok();
        assert!(matches!(err, CaLoadError::Empty { .. }), "got {err:?}");
    }

    #[test]
    fn empty_pem_bytes_is_an_empty_error() {
        let mut roots = RootCertStore::empty();
        let err = load_ca_pem_into(&mut roots, "test CA", b"").unwrap_err();
        assert!(matches!(err, CaLoadError::Empty { .. }), "got {err:?}");
        assert!(err.to_string().contains("test CA"), "got: {err}");
    }
}