1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! # solti-tls
//!
//! Shared TLS / mTLS configuration for Solti network-facing crates.
//!
//! Builders hold *intent*, a [`PemSource`] - filesystem path or in-memory PEM bytes for each cert/key.
//! Defer all I/O and parsing to [`ServerTlsConfig::into_rustls_config`] / [`ClientTlsConfig::into_rustls_config`], which produce a ready [`rustls::ServerConfig`] / [`rustls::ClientConfig`].
//!
//! ## Architecture
//!
//! ```text
//! ┌──────────────────────┐ ┌──────────────────────┐
//! │ PemSource::Path │ │ PemSource::Bytes │ intent: where PEM lives
//! │ (read at I/O time) │ │ (in-memory buffer) │
//! └──────────┬───────────┘ └──────────┬───────────┘
//! └───────────────┬───────────────┘
//! ▼
//! ┌───────────────────────────────────────────────┐
//! │ ServerTlsConfig / ClientTlsConfig │ built via *Builder
//! │ cert · key · client_ca/ca · alpn │ (pure, cloneable intent)
//! └───────────────────────┬───────────────────────┘
//! │ into_rustls_config()
//! │ 1. ensure_default_provider() (install ring once)
//! │ 2. PemSource::read() (I/O happens here)
//! │ 3. load_certs_from_pem / load_key_from_pem
//! │ 4. RootCertStore + (mTLS) WebPki*Verifier
//! │ 5. apply ALPN
//! ▼
//! ┌───────────────────────────────────────────────┐
//! │ rustls::ServerConfig / rustls::ClientConfig │
//! └───────────────────────────────────────────────┘
//! ```
//!
//! No I/O, provider install, or parsing happens until `into_rustls_config()` is called; the builders are pure intent.
//!
//! ## Features
//!
//! | Area | Description | Key types |
//! |-----------------|---------------------------------------------------------------------|-------------------------------------------------|
//! | **PEM sources** | Path-on-disk or in-memory bytes; read lazily at config-build time. | [`PemSource`] |
//! | **Server TLS** | Server cert/key, optional client-CA (mTLS), ALPN. | [`ServerTlsConfig`], [`ServerTlsConfigBuilder`] |
//! | **Client TLS** | Trust roots (CA), optional client cert/key (mTLS), ALPN. | [`ClientTlsConfig`], [`ClientTlsConfigBuilder`] |
//! | **Parsing** | Pure PEM→DER helpers for certs and the first private key. | [`load_certs_from_pem`], [`load_key_from_pem`] |
//! | **Provider** | Idempotent process-wide installation of the `ring` crypto provider. | [`ensure_default_provider`] |
//! | **Errors** | One `#[non_exhaustive]` enum across I/O, parse, and `rustls`. | [`TlsError`] |
//!
//! ## Security model
//!
//! **What IS verified**
//! - *Server chain*: a client built from [`ClientTlsConfig`] verifies the server's certificate chains to the CA bundle you pass to `ca(..)` (`rustls`' `WebPkiServerVerifier`).
//! Trust roots come **only** from your PEM - there is no OS / `rustls-native-certs` trust integration.
//! - *Client chain (mTLS)*: when `require_client_ca(..)` is set, the server demands and verifies a client cert chaining to that CA.
//! Client auth is then **mandatory**: connections without a valid client cert are rejected at the handshake.
//!
//! **What is NOT verified here (the caller's responsibility)**
//! - *Hostname / SAN*: `into_rustls_config()` does not check the server hostname.
//! SAN/identity matching happens when you call `TlsConnector::connect(server_name, ..)`: you must pass the correct [`ServerName`](rustls::pki_types::ServerName).
//! A wrong or placeholder name silently disables identity checking even though the chain still validates.
//! - *Revocation*: no OCSP / CRL / stapling; a revoked-but-unexpired cert is accepted.
//! Short cert lifetimes are the intended mitigation.
//! - *TLS versions & suites*: `rustls` safe defaults only (the workspace enables TLS 1.2 + 1.3); no explicit minimum-version or cipher policy.
//! - *Crypto provider*: `ring` is installed via [`ensure_default_provider`]; `aws-lc-rs` is not selectable (install your own provider first if needed).
//!
//! ## Example
//!
//! ```rust
//! use solti_tls::ServerTlsConfig;
//!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Use real PEM files in production; placeholder bytes keep this hermetic.
//! let cfg = ServerTlsConfig::builder()
//! .cert_pem_bytes(b"-----BEGIN CERTIFICATE-----\n...".to_vec())
//! .key_pem_bytes(b"-----BEGIN PRIVATE KEY-----\n...".to_vec())
//! .with_alpn(["h2"]) // gRPC; ["h2", "http/1.1"] for HTTP
//! .build()?; // validates required fields, no I/O yet
//!
//! assert_eq!(cfg.alpn, vec![b"h2".to_vec()]);
//! // cfg.into_rustls_config()? would now read + parse the PEM into a
//! // rustls::ServerConfig (requires real cert/key material).
//! # Ok(()) }
//! ```
//!
//! ## Also
//! - [`ServerTlsConfig`] / [`ClientTlsConfig`] - the two entry points.
//! - [`PemSource`] - path-vs-bytes intent shared by both.
//! - [`TlsError`] - every failure mode of `build` / `into_rustls_config`.
/// Compiles the runnable Rust code blocks in `README.md` as doctests.
;
pub use TlsError;
pub use PemSource;
pub use ensure_default_provider;
pub use ;
pub use ;
pub use ;