//! alktls: shared TLS setup types — server and client `rustls` configs, //! cert resolvers, verifiers, ACME state-machine wiring, and the identity / //! credential / fingerprint types that drive config construction. //! //! The crate owns **config construction**: given an identity and an ALPN //! list, produce a `rustls::ServerConfig` or `rustls::ClientConfig` and hand //! it to whichever transport wrapper the deployment runs (`noq` for QUIC, //! `tokio-rustls` for TCP+TLS). It does not dial, accept, dispatch, or //! resolve peer identities — those belong to the dial seam, the accept //! loop, and the auth layer. //! //! Core types: //! //! - [`TlsServerConfig`] — built once per identity + ALPN list, shared //! across transports via [`Arc`](std::sync::Arc) (not `Clone`; it holds //! the ACME task's `JoinHandle`). //! - [`TlsClientConfig`] — built per dial from a [`ConnectionCredentials`] + //! ALPN; consumed by its accessors. //! - [`TlsError`] — the config-construction error type. Handshake outcomes //! and ACME runtime errors are not `TlsError`s. //! //! Transport-specific accessors are feature-gated: `noq` (`for_noq`), //! `tcp` (`for_tcp_tls`), `acme` (the ACME path). `default = []`. pub mod client; pub mod credentials; pub mod fingerprint; pub mod identity; pub mod pem; pub mod server; pub mod signing; pub use fingerprint::{extract_ed25519_raw_key_from_spki, fingerprint_from_cert_der}; pub use pem::{load_cert_chain, load_private_key}; pub use signing::Ed25519SigningKey; pub use identity::{AcmeDirectory, Ed25519SecretKey, TlsIdentity}; pub use client::{ build_client_auth, load_platform_root_cert_store, select_server_verifier, FingerprintPinVerifier, NoClientCertResolver, RawKeyClientCertResolver, TlsClientConfig, }; pub use credentials::{ConnectionCredentials, RemoteIdentity}; pub use server::{ build_rustls_server_config, generate_self_signed_cert, AcceptAnyCertVerifier, RawKeyCertResolver, SelfSignedCert, TlsServerConfig, }; #[derive(Debug, thiserror::Error)] #[non_exhaustive] pub enum TlsError { /// Cert or key file read / PEM parse. `rustls_pemfile` funnels its /// own error type into `io::Error`, so one `io::Error` source covers /// the whole loading path. #[error("loading cert/key material: {0}")] CertLoad(#[from] std::io::Error), /// Self-signed cert generation (rcgen). Server `SelfSigned` path. #[error("generating self-signed cert: {0}")] SelfSigned(#[from] rcgen::Error), /// rustls server or client config construction /// (`with_safe_default_protocol_versions`, `with_single_cert`, /// `CertifiedKey::from_der`, `RootCertStore::add`). #[error("building rustls config: {0}")] Rustls(#[from] rustls::Error), /// `WebPkiServerVerifier::builder(_with_provider)..build()` — the /// unknown-X.509-remote client path. Re-exported by rustls at /// `rustls::client` (the `rustls::webpki` module is private at the /// pinned 0.23.44; same type, public path). #[error("building webpki verifier: {0}")] VerifierBuild(#[from] rustls::client::VerifierBuilderError), /// QUIC config wrapping — the one path where `for_noq()` fails /// (`NoInitialCipherSuite`, not a `rustls::Error`). noq-gated. #[cfg(feature = "noq")] #[error("wrapping rustls config for noq: {0}")] NoqWrap(#[from] noq_proto::crypto::rustls::NoInitialCipherSuite), /// Config-mismatch errors that are not wrapped third-party /// errors: ACME feature not enabled but `Acme` configured /// (server), or `Acme` identity used for client auth. A config /// error, not a wrapped third-party error. #[error("TLS config error: {0}")] AcmeConfig(String), } #[cfg(test)] mod tests { use super::TlsError; #[test] fn tls_error_matches_the_adr_002_variant_set() { let io: TlsError = std::io::Error::other("x").into(); assert!(matches!(io, TlsError::CertLoad(_))); let rcgen: TlsError = rcgen::Error::CouldNotParseCertificate.into(); assert!(matches!(rcgen, TlsError::SelfSigned(_))); let rustls: TlsError = rustls::Error::General("x".into()).into(); assert!(matches!(rustls, TlsError::Rustls(_))); let builder: TlsError = rustls::client::VerifierBuilderError::NoRootAnchors.into(); assert!(matches!(builder, TlsError::VerifierBuild(_))); let acme = TlsError::AcmeConfig("acme feature not enabled".into()); assert!(acme.to_string().contains("acme feature not enabled")); } #[cfg(feature = "noq")] #[test] fn tls_error_noq_wrap_variant_exists() { let _ = matches!( Option::::None, Some(TlsError::NoqWrap(_)) | Some(_) ); } }