Skip to main content

ferritls_rustls/
lib.rs

1//! # ferritls-rustls
2//!
3//! A [rustls](https://docs.rs/rustls) 0.23 `CryptoProvider` backed by
4//! [`ferritls_core`]: a pure-Rust cryptographic core with no C, no
5//! assembly and no `unsafe` code, organized as a FIPS 140-3 module
6//! boundary.
7//!
8//! This crate sits **outside** the FIPS boundary: it only assembles
9//! rustls traits from core primitives and contains no cryptography
10//! itself, which isolates rustls version churn to this layer.
11//!
12//! ## Getting started
13//!
14//! Install the provider once per process, then use rustls as usual:
15//!
16//! ```no_run
17//! ferritls_rustls::default_provider()
18//!     .install_default()
19//!     .expect("ferritls is the first CryptoProvider installed");
20//!
21//! // `ClientConfig::builder()` / `ServerConfig::builder()` now pick up
22//! // the process-default provider:
23//! let config = rustls::ClientConfig::builder()
24//!     .with_root_certificates(rustls::RootCertStore::empty())
25//!     .with_no_client_auth();
26//! ```
27//!
28//! `install_default()` fails if another provider has already claimed the
29//! process default (for example a `ring`/`aws-lc-rs` default pulled in
30//! by another dependency — see the [reqwest notes](#using-with-reqwest)
31//! below).
32//!
33//! To avoid process-global state, pass the provider explicitly:
34//!
35//! ```no_run
36//! use std::sync::Arc;
37//!
38//! let provider = Arc::new(ferritls_rustls::default_provider());
39//!
40//! let client_config = rustls::ClientConfig::builder_with_provider(provider.clone())
41//!     .with_safe_default_protocol_versions()
42//!     .expect("rustls defaults")
43//!     .with_root_certificates(rustls::RootCertStore::empty())
44//!     .with_no_client_auth();
45//! ```
46//!
47//! Server side (certificate chain and private key loaded from your own
48//! storage; PKCS#8 / SEC1 DER and PEM via `rustls::pki_types`):
49//!
50//! ```no_run
51//! # use std::sync::Arc;
52//! # fn example(
53//! #     certs: Vec<rustls::pki_types::CertificateDer<'static>>,
54//! #     key: rustls::pki_types::PrivateKeyDer<'static>,
55//! # ) {
56//! let provider = Arc::new(ferritls_rustls::default_provider());
57//!
58//! let server_config = rustls::ServerConfig::builder_with_provider(provider)
59//!     .with_safe_default_protocol_versions()
60//!     .expect("rustls defaults")
61//!     .with_no_client_auth()
62//!     .with_single_cert(certs, key)
63//!     .expect("valid server certificate chain");
64//! # }
65//! ```
66//!
67//! Signing keys are loaded through the provider's `key_provider`; the
68//! supported private-key formats are ECDSA (SEC1/PKCS#8), RSA (PKCS#8)
69//! and Ed25519 (PKCS#8).
70//!
71//! ## Capabilities
72//!
73//! **Cipher suites (TLS 1.3 only)** — order equals preference:
74//!
75//! | Suite | Default mode | Approved mode | QUIC |
76//! |---|---|---|---|
77//! | `TLS_AES_128_GCM_SHA256` | yes | yes | yes |
78//! | `TLS_AES_256_GCM_SHA384` | yes | yes | yes |
79//! | `TLS_CHACHA20_POLY1305_SHA256` | yes | no (not NIST-approved) | yes |
80//! | `TLS_AES_128_CCM_SHA256` | yes | yes | no |
81//! | `TLS_AES_128_CCM_8_SHA256` | yes | no (8-byte tag outside SP 800-52r2) | no |
82//!
83//! **Key exchange groups**: X25519, secp256r1, secp384r1, the
84//! X25519MLKEM768 hybrid (0x11EC, draft-ietf-tls-ecdhe-mlkem) and the
85//! pure ML-KEM groups MLKEM512/768/1024 (0x0200–0x0202,
86//! draft-ietf-tls-mlkem-key-agreement). Approved mode offers the
87//! SP 800-52r2 approved subset (P-256/384 + X25519MLKEM768 + ML-KEM).
88//!
89//! **Signature verification**: ECDSA P-256/384 (SHA-256/384/512),
90//! RSA-PSS and RSA PKCS#1 v1.5 (SHA-256/384/512), Ed25519 (default
91//! mode only — EdDSA is not a FIPS-approved signature algorithm).
92//!
93//! ## Approved ("FIPS") mode
94//!
95//! [`fips_mode_provider()`] returns a provider restricted to
96//! NIST-approved algorithms: AES-GCM/CCM suites, P-256/384 + ML-KEM key
97//! exchange, ECDSA/RSA verification, an SP 800-90A CTR-DRBG random
98//! source (reseeded with OS entropy on every generate) and power-on
99//! self-test KATs. The same restriction is available at compile time
100//! via the `fips` cargo feature.
101//!
102//! ```no_run
103//! let provider = ferritls_rustls::fips_mode_provider();
104//! ```
105//!
106//! **`fips()` semantics (important)**: ferritls is **not yet
107//! CMVP-validated**, so every `fips()` hook on this provider returns
108//! `false`. [`fips_mode_provider()`] means "runs in the approved
109//! algorithm mode", not a certification claim. Once validation lands, a
110//! separate `fips-validated` feature on a locked source snapshot will
111//! flip that semantics (see `docs/FIPS.md`, phase C).
112//!
113//! ## QUIC
114//!
115//! The AES-128-GCM, AES-256-GCM and ChaCha20-Poly1305 suites implement
116//! `rustls::quic::Algorithm` (RFC 9001 packet protection: packet keys,
117//! header protection and multipath `for_path`), so they can be used
118//! with QUIC stacks such as quinn:
119//!
120//! ```no_run
121//! let provider = std::sync::Arc::new(ferritls_rustls::default_provider());
122//! let mut config = rustls::ClientConfig::builder_with_provider(provider)
123//!     .with_safe_default_protocol_versions()
124//!     .expect("rustls defaults")
125//!     .with_root_certificates(rustls::RootCertStore::empty())
126//!     .with_no_client_auth();
127//! config.alpn_protocols = vec![b"h3".to_vec()];
128//! // Feed `config` to your QUIC stack (e.g. quinn::crypto::rustls).
129//! ```
130//!
131//! The CCM suites report `quic: None` (RFC 9001 §5.1 mandates AES-GCM
132//! as the baseline; rustls has no CCM traffic-secrets variant either).
133//!
134//! ## Using with reqwest
135//!
136//! reqwest 0.12 and 0.13 both depend on `rustls ^0.23` — the same
137//! version line as ferritls — so Cargo unifies everything into a single
138//! rustls instance and both generations work out of the box. Pick the
139//! **no-provider feature variants**; otherwise reqwest compiles in
140//! ring/aws-lc, which may claim the process default provider first
141//! (after which `install_default()` fails):
142//!
143//! ```toml
144//! # reqwest 0.13 (rustls is the default TLS backend; system roots via
145//! # rustls-platform-verifier)
146//! reqwest = { version = "0.13", default-features = false,
147//!             features = ["rustls-no-provider", "http2", "charset"] }
148//!
149//! # reqwest 0.12 (rustls is opt-in; bundled webpki root store)
150//! reqwest = { version = "0.12", default-features = false,
151//!             features = ["rustls-tls-webpki-roots-no-provider", "http2", "charset"] }
152//! # For system root stores use rustls-tls-native-roots-no-provider.
153//! ```
154//!
155//! ```ignore
156//! // Install before creating the first Client:
157//! ferritls_rustls::default_provider().install_default()?;
158//! let client = reqwest::Client::new();
159//! ```
160//!
161//! To avoid process-global state, build a `ClientConfig` explicitly and
162//! inject it via `use_preconfigured_tls` (supported in both 0.12 and
163//! 0.13). The trust roots and **ALPN** must then be set on the
164//! `ClientConfig` yourself: reqwest does not modify a preconfigured
165//! config, and a missing ALPN list silently disables HTTP/2. Note that
166//! ferritls currently ships TLS 1.3 suites only.
167//!
168//! ## Build requirements (`simd` default feature)
169//!
170//! The default `simd` feature of `ferritls-core` uses the standard
171//! library's `core::simd` (portable_simd) for explicit vectorization of
172//! the AES and ChaCha20 hot paths. It is safe code from `std` (no new
173//! dependencies), but on the **stable toolchain** it requires the
174//! `RUSTC_BOOTSTRAP=1` environment variable to compile the feature gate:
175//!
176//! ```bash
177//! export RUSTC_BOOTSTRAP=1                                     # (a) keep simd
178//! # or, in your Cargo.toml:                                    # (b) scalar fallback
179//! # ferritls-core = { version = "0.9", default-features = false }
180//! ```
181//!
182//! Vector width follows the compile target (SSE2/NEON baseline; build
183//! with `RUSTFLAGS="-C target-feature=+avx2"` or `-C target-cpu=native`
184//! to widen to AVX2/AVX-512). Nightly toolchains need no variable, and
185//! the requirement disappears once portable_simd stabilizes.
186//!
187//! ## Hardware acceleration (optional)
188//!
189//! [`ferritls-backend-x86_64`](https://docs.rs/ferritls-backend-x86_64)
190//! is a separate, out-of-boundary crate (x86_64 only) providing AES-NI +
191//! CLMUL whole-message AES-GCM kernels and SHA-NI SHA-256, with runtime
192//! CPU detection and install-time KATs. It is opt-in from the
193//! application side via `ferritls_core::ops::install()`; nothing changes
194//! if it is not installed, and approved mode always stays on the
195//! software path.
196//!
197//! ## Crate layout
198//!
199//! | Module | Contents |
200//! |---|---|
201//! | [`provider`] | [`default_provider`] / [`fips_mode_provider`] assembly |
202//! | [`cipher`] | TLS 1.3 suite adapters (AEAD, hash, HKDF, QUIC wiring) |
203//! | [`kx`] | `SupportedKxGroup` adapters (ECDH, ML-KEM, hybrid) |
204//! | [`verify`] | webpki signature-verification algorithms |
205//! | [`sign`] | `SigningKey`/`Signer` adapters and the rustls key provider |
206//! | [`random`] | `SecureRandom` implementations (OS entropy / CTR-DRBG) |
207//! | [`quic`] | `rustls::quic::Algorithm` packet/header protection |
208
209#![forbid(unsafe_code)]
210
211pub mod cipher;
212pub mod kx;
213pub mod provider;
214pub mod quic;
215pub mod random;
216pub mod sign;
217pub mod verify;
218
219pub use provider::{default_provider, fips_mode_provider};