ADR-008: server-path possession verification — the verifying verifier is the default (OQ-TLS-09 resolved)
Close review 001 §S-1: the default client-cert verifier never checked the client's CertificateVerify, so anyone holding a peer's *public* cert/SPKI bytes (public by design — peers publish them to be dialable) could complete a handshake as that peer, and the auth layer could not detect it. The consumer designs are known (X.509 and raw-key TCP/QUIC endpoints with identity-bearing clients), so implementing now — the zero-consumer moment — avoids the guaranteed breaking republish of flipping the default later. - VerifyPresentedCertVerifier (new): request, don't require, verify possession — permissive verify_client_cert (self-signed chains and bare SPKIs stay valid presentation) + CertificateVerify routing by presented cert kind (Ed25519 SPKI -> verify_tls13_signature_with_ raw_key both TLS versions; X.509 -> standard route), the same routing FingerprintPinVerifier implements. Nine-scheme list verbatim (shared fn, exact-list pin covers both). - Default on every TlsServerConfig path — X509 / RawKey / SelfSigned / ACME (the verifier install is crate-side rustls in new_acme, not rustls-acme's). - AcceptAnyCertVerifier stays public as the explicit no-pop escape hatch, no longer installed by any crate path. - tests/impersonation_posture.rs: four pins — default rejects the attacker (X.509: UnsupportedSignatureAlgorithmForPublicKeyContext; raw-key: BadSignature), escape hatch still accepts + extracts the victim's fingerprint (both cert types). - tests/handshake_behavior.rs: suites 4/4b — possession-checked legit clients (raw-key pin vs raw-key server; X.509 client vs X.509 server) complete and the server extracts the fingerprint; suite 3b doc updated. - Docs: ADR-008 written; OQ-TLS-09 -> resolved (option (b)); ADR-007 §Limits deferral retired to not-planned; server.md/client.md invariants/README/overview synced. Verification: cargo test 81 / --features tcp 95 / --all-features 104 green; clippy -D warnings clean (default + all-features); fmt clean; cargo doc warning-free.
This commit is contained in:
@@ -21,7 +21,7 @@ are authoritative; the Phase 0 doc's statuses are the historical record.
|
||||
| OQ-TLS-06 | ACME task shutdown surface | resolved (detached-only for v1) | low |
|
||||
| OQ-TLS-07 | iroh key surface | **resolved** (ADR-005, byte access pinned) | low |
|
||||
| OQ-TLS-08 | `quinn` → `noq` feature rename | **resolved** (ADR-003) | high |
|
||||
| OQ-TLS-09 | Server-path proof-of-possession | **open** | high |
|
||||
| OQ-TLS-09 | Server-path proof-of-possession | **resolved** (ADR-008) | high |
|
||||
| OQ-TLS-10 | RFC 7250 over TCP: cert-type negotiation gap | **resolved** (ADR-007) | high |
|
||||
|
||||
## Identity & types
|
||||
@@ -112,37 +112,23 @@ are authoritative; the Phase 0 doc's statuses are the historical record.
|
||||
impersonation probe: a handshake with the victim's cert bytes + an
|
||||
attacker signer completes, and the server extracts the victim's
|
||||
fingerprint)
|
||||
- **Status**: open (recorded 2026-09-10)
|
||||
- **Priority**: high
|
||||
- **Question**: `AcceptAnyCertVerifier` never checks the client's
|
||||
CertificateVerify signature — the presented identity is spoofable by
|
||||
anyone holding the public cert/SPKI bytes, and the auth layer cannot
|
||||
detect it (the fingerprint it is handed *is* the victim's). alknet
|
||||
ADR-034 inherited the request-but-don't-require shape without
|
||||
recording this property. Who enforces possession?
|
||||
- **Options**:
|
||||
- **(a) Keep request-but-don't-require; the auth layer owns
|
||||
challenge-response** over the established channel, bound to the
|
||||
presented public key. No crate change; the S-1 doc note on
|
||||
`AcceptAnyCertVerifier` is the honest description until then.
|
||||
- **(b) Add a `VerifyPresentedCertVerifier` sibling**
|
||||
(request-and-verify): same nine-scheme list, permissive
|
||||
`verify_client_cert`, signature methods delegate to
|
||||
`rustls::crypto::verify_tls{12,13}_signature(_with_raw_key)` — the
|
||||
routing the client-side `FingerprintPinVerifier` already
|
||||
implements. Additive; must land before the first consumer (an API
|
||||
shape decision).
|
||||
- **(c) Make the auth layer's fingerprint resolution
|
||||
possession-checked** (a possession proof accompanies each
|
||||
fingerprint resolution). Cross-crate; the auth layer does not exist
|
||||
yet.
|
||||
- **Constraints**: the spoofable posture is pinned by
|
||||
`tests/impersonation_posture.rs` (both cert types) — any decision
|
||||
must fail or update that test together with the
|
||||
`AcceptAnyCertVerifier` doc note.
|
||||
- **Cross-references**: src/server.rs (`AcceptAnyCertVerifier`),
|
||||
src/client.rs (`FingerprintPinVerifier`),
|
||||
docs/reviews/001-implementation-review.md §S-1, alknet ADR-034
|
||||
- **Status**: resolved (2026-09-11) — option (b),
|
||||
[ADR-008](decisions/008-server-path-possession-verification.md).
|
||||
[`VerifyPresentedCertVerifier`](decisions/008-server-path-possession-verification.md)
|
||||
is the default client-cert verifier on every `TlsServerConfig` path
|
||||
(including the ACME path — the verifier install is crate-side
|
||||
rustls, not rustls-acme's): request, don't require, **verify the
|
||||
CertificateVerify against the presented cert's public key**. The
|
||||
extracted fingerprint is possession-checked (attacker-supplied
|
||||
presentations fail the handshake —
|
||||
`tests/impersonation_posture.rs` pins both directions); who a
|
||||
fingerprint maps to remains the auth layer's concern (ADR-005).
|
||||
`AcceptAnyCertVerifier` stays public as the explicit no-pop escape
|
||||
hatch, its posture pinned by the same test file.
|
||||
- **Cross-references**: src/server.rs (`VerifyPresentedCertVerifier`,
|
||||
`AcceptAnyCertVerifier`), src/client.rs (`FingerprintPinVerifier` —
|
||||
the routing being mirrored), ADR-008, review 001 §S-1, alknet
|
||||
ADR-034
|
||||
|
||||
### OQ-TLS-10: How do RFC 7250 raw-key peers negotiate over rustls-driven TCP+TLS?
|
||||
|
||||
@@ -205,6 +191,7 @@ are authoritative; the Phase 0 doc's statuses are the historical record.
|
||||
|
||||
## Deferred / Blocked
|
||||
|
||||
- OQ-TLS-09 (server-path proof-of-possession): open by design — the
|
||||
decision needs the rewrite's auth-layer design in hand (option (c))
|
||||
or an API-shape call before the first consumer (option (b)).
|
||||
None — all recorded OQs are resolved. Future questions (e.g. the
|
||||
raw-key-only server-verifier sibling if a foreign strict RFC 7250
|
||||
server interop ever appears — see ADR-007 §Limits) get new OQ entries
|
||||
rather than reopening these.
|
||||
Reference in New Issue
Block a user