Files
alknet/docs/architecture/decisions/084-aws-lc-rs-crypto-provider.md
T
glm-5.2 7610ec1f31 docs(arch): workspace scope correction (ADR-085) + tls spec review fixes
ADR-085 records the actual workspace scope: the mono-repo is the core
networking toolkit (substrate: core, tls, call, channels; deployment
shapes: hub, worker; foundational handlers: tty, http, ssh, tunnel,
socks5, fs, sftp; vault). Consumer repos (docker, agent) are separate
repos depending on the published core crates. The overview's crate
graph had been describing the wrong scope since ADR-003 — a flat
~12-crate workspace including DNS/messaging/NAPI while omitting
channels, hub, worker, and tls. This stale scope was a causal factor
in the 'assembly layer' hedging pattern: when the overview implies
everything lives in one repo but the architecture needs a hub/worker
composition layer not in the graph, the gap gets filled with
'assembly layer' as an escape hatch. The overview is rewritten to
match the real boundary.

TLS spec review fixes (from architecture review):
- C3: hub/worker/hub-worker terminology pointers (tls README + endpoint.md)
- W1: server-only statement + OQ-64 (client-side TLS helper, deferred)
- W2: ACME task lifecycle semantics (returns immediately, no first-cert await)
- W3: remove stale EndpointError::TlsConfig variant
- W4: update stale ALPN section for two-config hub
- W5: add alknet-tls to hub dep graph (assembly-layer dep)
- W6: trim inline rationale -> point to ADR-084
- W7: ADR-084 status dependency note

New open questions:
- OQ-62: ALPN list sharing for two-config hub (open, high)
- OQ-63: TlsError shape (open, high)
- OQ-64: client-side TLS helper (deferred, blocked on OQ-55)
2026-07-14 12:46:16 +00:00

142 lines
6.6 KiB
Markdown

# ADR-084: aws-lc-rs as the TLS Crypto Provider
## Status
Accepted (depends on ADR-082/083, which are still Proposed — this ADR
records a decision those ADRs list as an invariant. If ADR-082/083 are
revised in a way that changes the config-construction paths, the
provider decision here still stands; only the "where it is applied"
referent would update.)
## Context
The TLS stack in alknet uses `rustls`, which requires a crypto provider.
Rustls 0.23 made the crypto provider explicit — it is no longer a
process-global default that gets set once and forgotten. Each
`ServerConfig` / `ClientConfig` is built with a specific provider via
`builder_with_provider(Arc<dyn CryptoProvider>)`.
The current code (`alknet-core/endpoint.rs`, ADR-027, and the
`alknet-tls` extraction in ADR-082) uses
`rustls::crypto::aws_lc_rs::default_provider()` on all server config
paths (X509, RawKey, SelfSigned, ACME). This choice was made during
ADR-027's implementation to match iroh's `tls-aws-lc-rs` feature, but
was never recorded as a decision. ADR-082's behavior-preservation
invariants list says "do not switch to `ring` or the process-default
provider without an ADR" — this ADR is that record.
### Why this is load-bearing
The crypto provider determines:
- **FIPS status**: `aws-lc-rs` has a FIPS-certified build mode (via
AWS-LC). `ring` does not. If alknet ever needs FIPS compliance (e.g.,
for regulated deployments), `aws-lc-rs` is the path; `ring` would be a
dead end.
- **Platform support**: `aws-lc-rs` supports a broad set of platforms
via its C/C++ build. `ring` has a different (historically narrower)
platform matrix. Switching providers changes which platforms compile.
- **Cipher suite defaults**: `default_provider()` returns a provider
with a specific set of cipher suites and signature algorithms.
`AcceptAnyCertVerifier`'s `supported_verify_schemes()` (ED25519 +
ECDSA P-256/P-384 + RSA PSS/PKCS1) must be supported by the provider —
`aws_lc_rs` supports all of these; a provider that dropped one would
silently change which client cert signature algorithms the server
accepts.
- **iroh compatibility**: iroh uses `aws-lc-rs` via its
`tls-aws-lc-rs` feature. If alknet's quinn/TCP+TLS path used a
different provider, the same Ed25519 key would produce TLS handshakes
with different crypto internals on the quinn path vs the iroh path —
a consistency risk for the fingerprint normalization (ADR-030 §6).
### The options
- **`aws_lc_rs::default_provider()`** (current): FIPS-capable, broad
platform support, matches iroh. The choice already in the code.
- **`ring::default_provider()`**: simpler pure-Rust crate, no FIPS,
different platform matrix. Would break iroh provider consistency.
- **Process-default provider** (`rustls::crypto::CryptoProvider::get_default()`):
relies on whoever set the process global first. Non-deterministic in a
library context — alknet crates shouldn't depend on the binary having
set the right global. Explicit per-config is the library-correct
pattern (and is what the current code does).
## Decision
**`rustls::crypto::aws_lc_rs::default_provider()` is alknet's TLS crypto
provider on all server and client config paths.** This applies to
`alknet-tls` (the `TlsServerConfig` construction paths: X509, RawKey,
SelfSigned, ACME) and to `alknet-call`'s client-side verifier
construction (which builds a `rustls::ClientConfig` with the same
provider for consistency).
The provider is constructed explicitly per config
(`Arc::new(rustls::crypto::aws_lc_rs::default_provider())` passed to
`builder_with_provider`), not via the process-default global. This
keeps alknet crates correct as libraries — they don't depend on the
binary having set a global.
### What this means for `alknet-tls`
The `TlsServerConfig::new` paths (X509, RawKey, SelfSigned, ACME) all
construct their `rustls::ServerConfig` with
`builder_with_provider(Arc::new(aws_lc_rs::default_provider()))`. This
is already the case in the current code (ADR-082's
behavior-preservation invariants record this); this ADR makes the
*decision* explicit so that a future switch requires a new ADR.
### What this means for `alknet-call`
The client-side `rustls::ClientConfig` (used by `CallClient` for
outgoing connections, with either `FingerprintPinVerifier` or
`WebPkiServerVerifier`) must use the same provider. This ensures the
client and server agree on cipher suites and signature algorithms — a
mismatch could cause handshake failures or silent signature-algorithm
changes.
## Consequences
**Positive:**
- One crypto provider across all TLS paths (quinn, TCP+TLS, iroh's
built-in TLS, client-side). Consistent cipher suites, signature
algorithms, and FIPS status.
- FIPS-capable: if a regulated deployment needs FIPS, `aws-lc-rs`'s
FIPS build mode is available without an ADR (it's a build-flag, not a
provider change).
- Matches iroh's `tls-aws-lc-rs` feature — no provider mismatch between
alknet's quinn/TCP+TLS path and iroh's built-in TLS path.
**Negative:**
- `aws-lc-rs` is a C/C++ crate (built via `aws-lc`), not pure Rust. This
adds a C build dependency. `ring` is also C-backed, so this is not a
regression vs the alternative — but a pure-Rust provider (e.g., a
future `rustls` provider) would be lighter. Not switching now; the
FIPS and iroh-consistency benefits outweigh the build complexity.
- A future switch to a different provider requires a new ADR (this is
already stated in ADR-082's invariants; this ADR makes the *current*
choice a recorded decision, not just an invariant).
## Door type
**One-way.** The provider choice is baked into every TLS config path.
Switching providers after consumers exist requires updating every config
construction site and verifying cipher-suite + signature-algorithm
compatibility. The FIPS and iroh-consistency constraints make `ring`
and the process-default provider non-viable — the decision is
`aws_lc_rs` unless a future ADR records a different choice with
rationale.
## References
- ADR-027: TLS Identity Redesign (where `aws_lc_rs` was first used, to
match iroh's `tls-aws-lc-rs` feature)
- ADR-082: alknet-tls extraction (behavior-preservation invariants:
`aws_lc_rs::default_provider()` on all paths; "do not switch to `ring`
or the process-default provider without an ADR")
- ADR-030 §6: fingerprint normalization (requires provider consistency
across quinn/iroh/TCP+TLS — the same Ed25519 key must produce the same
fingerprint regardless of transport)
- `crates/alknet-core/src/endpoint.rs` — the current code using
`aws_lc_rs::default_provider()` on all server config paths
- `crates/alknet-call/src/client/call_client.rs` — the client-side
`rustls::ClientConfig` construction (must use the same provider)