alktls 0.1.0

Shared TLS setup types: server and client rustls configs, cert resolvers, verifiers, and ACME state-machine wiring, transport-agnostic and shareable across transports.
Documentation
//! PEM loading helpers: [`load_cert_chain`], [`load_private_key`].
//! One copy used by both server and client.

use std::io;
use std::path::Path;

use crate::TlsError;

pub fn load_cert_chain(
    path: &Path,
) -> Result<Vec<rustls::pki_types::CertificateDer<'static>>, TlsError> {
    let bytes = std::fs::read(path).map_err(TlsError::CertLoad)?;
    let mut reader = io::BufReader::new(bytes.as_slice());
    rustls_pemfile::certs(&mut reader)
        .collect::<Result<Vec<_>, _>>()
        .map_err(|e| TlsError::CertLoad(io::Error::other(e)))
}

pub fn load_private_key(
    path: &Path,
) -> Result<rustls::pki_types::PrivateKeyDer<'static>, TlsError> {
    let bytes = std::fs::read(path)?;
    let mut reader = io::BufReader::new(bytes.as_slice());
    match rustls_pemfile::private_key(&mut reader) {
        Ok(Some(key)) => Ok(key),
        Ok(None) => Err(io::Error::new(
            io::ErrorKind::InvalidData,
            "no private key found in file",
        )),
        Err(e) => Err(io::Error::other(e)),
    }
    .map_err(TlsError::CertLoad)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn load_private_key_returns_error_when_no_key_present() {
        let dir = tempfile::tempdir().unwrap();
        let empty = dir.path().join("empty.key");
        std::fs::write(&empty, b"# no key here\njust a comment\n").unwrap();
        let err = load_private_key(&empty);
        assert!(
            matches!(err, Err(TlsError::CertLoad(_))),
            "empty key file must yield CertLoad error, got {err:?}"
        );
    }

    #[test]
    fn load_private_key_parse_error_yields_cert_load_error() {
        let dir = tempfile::tempdir().unwrap();
        let garbage = dir.path().join("garbage.key");
        std::fs::write(
            &garbage,
            b"-----BEGIN PRIVATE KEY-----\n!!!\n-----END PRIVATE KEY-----\n",
        )
        .unwrap();
        let err = load_private_key(&garbage);
        assert!(
            matches!(err, Err(TlsError::CertLoad(_))),
            "malformed base64 in a keyed section is a parse error (not 'no key \
             found') and must yield CertLoad, got {err:?}"
        );
    }

    #[test]
    fn load_private_key_returns_error_when_file_missing() {
        let err = load_private_key(Path::new("/nonexistent/alktls-coverage/missing.key"));
        assert!(
            matches!(err, Err(TlsError::CertLoad(_))),
            "missing key file must yield CertLoad error, got {err:?}"
        );
    }

    #[test]
    fn load_cert_chain_returns_error_when_file_missing() {
        let err = load_cert_chain(Path::new("/nonexistent/alktls-coverage/missing.pem"));
        assert!(
            matches!(err, Err(TlsError::CertLoad(_))),
            "missing cert file must yield CertLoad error, got {err:?}"
        );
    }
}