review-001 decomposition: 6 remediation tasks from verified findings
Verify every review-001 finding before decomposition: - S-1 re-confirmed by fresh executable probe (X.509 + raw-key impersonation both complete the handshake with the victim's fingerprint extracted server-side) - U-2/U-3 uncovered-line inventory re-derived from cargo llvm-cov (--all-features); matches Part C exactly - rcgen 1975/4096 defaults, rustls cert-type negotiation arm, packaging list, doc texts: all verified against sources Tasks (all verified, none speculative): - fix-accept-any-cert-verifier-posture (S-1 + N-1 + OQ-TLS-09 + probe) - handshake-tests (U-3 suites 1-3; suite 4 lives in the S-1 task) - coverage-cheap-closes (U-2's seven groups) - acme-event-loop-test (U-1; depends on coverage-cheap-closes for the tracing-capture pattern) - config-validation-and-trivia (C-2 ALPN dedup, C-3 empty-domains, N-6 excludes, N-7 https doc line) - docs-pin-c1-c4-n3-n4 (C-1, C-4, N-3 decision note, N-4 negotiation note) Graph: acme-event-loop-test is generation 2; the rest run in generation 1. taskgraph validate: 14 tasks, no cycles. Verification: cargo test (default + --all-features) green before and after; probe file deleted after its run.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
---
|
||||
id: handshake-tests
|
||||
name: Handshake-level test suite — pin, fail-closed, raw-key path executed (U-3)
|
||||
status: pending
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
tags: [tests, tcp, review-001, u3]
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Every existing test asserts config *construction*; the crate's actual
|
||||
TLS behavior (server accepts, pin verifies, fail-closed manifests) is
|
||||
never executed — no handshake-level test exists. These are the
|
||||
highest-value missing tests in the crate: they turn "fail closed" and
|
||||
"pin" from documentation into executed behavior. The ADR-006 boundary
|
||||
(handshakes belong to transport crates) is not violated by a
|
||||
duplex-pair handshake: no external transport dep, just tokio +
|
||||
tokio-rustls under the existing `tcp` feature (dev/test only — the
|
||||
feature gate already exists for `for_tcp_tls`).
|
||||
|
||||
The four handshake-level gaps (review 001 §U-3):
|
||||
|
||||
1. **The pin path end-to-end**: client config with
|
||||
`FingerprintPinVerifier` handshaking a server whose presentation
|
||||
matches the pin → ok; mismatched pin → fails.
|
||||
2. **The fail-closed path**: `remote_identity: None` + raw-key server
|
||||
→ handshake fails (the structural claim, executed).
|
||||
3. **The RFC 7250 raw-key server path end-to-end**: `RawKeyCertResolver`
|
||||
+ client → handshake completes with the raw-key cert type negotiated
|
||||
(currently only `only_raw_public_keys()` is asserted).
|
||||
4. **S-1's impersonation probe made permanent** — covered by
|
||||
`fix-accept-any-cert-verifier-posture` (kept separate: different
|
||||
remediation owner, doc+ADR+probe).
|
||||
|
||||
## Work
|
||||
|
||||
1. Add `tests/handshake_behavior.rs` (`#![cfg(feature = "tcp")]`):
|
||||
tokio duplex pair + `for_tcp_tls()` acceptor / `into_rustls_config()`
|
||||
connector — or raw `ServerConnection`/`ClientConnection` with
|
||||
`complete_io` (the review-probe shape; fewer moving parts).
|
||||
2. Suite (1): server = `TlsIdentity::RawKey(sk)`, client pin =
|
||||
`fingerprint_from_cert_der(spki)` → handshake ok + app-data
|
||||
round-trip; wrong pin → error.
|
||||
3. Suite (2): server = RawKey, credentials without `remote_identity` →
|
||||
handshake fails (`UnknownCertificateType`/alert — assert failure,
|
||||
not the specific error text unless rustls pins it).
|
||||
4. Suite (3): raw-key server + raw-key client (RFC 7250 both sides):
|
||||
handshake ok; server `peer_certificates()` is an SPKI; client
|
||||
presented X509+RawPublicKey cert types per N-4's correction (the
|
||||
client resolver already offers the right shape — see N-4).
|
||||
5. Keep every test within the crate's public API + rustls types; no new
|
||||
dependencies.
|
||||
|
||||
## Verification
|
||||
|
||||
- [ ] All three executed handshake tests pass under
|
||||
`cargo test --features tcp` (and `--all-features`)
|
||||
- [ ] The fail-closed test actually fails the handshake (assert error,
|
||||
not success)
|
||||
- [ ] `cargo test` (default) still green — the file is feature-gated
|
||||
and contributes nothing without `tcp`
|
||||
- [ ] `cargo clippy --all-targets --all-features -- -D warnings`,
|
||||
`cargo fmt --check` green
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Pin / fail-closed / raw-key are executed behaviors in the suite,
|
||||
not just structural assertions
|
||||
- [ ] No new dependencies; the default build stays lean
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/001-implementation-review.md §U-3, §N-4 (the cert-type
|
||||
negotiation correction — read before writing suite (3))
|
||||
- src/client.rs (`FingerprintPinVerifier`, `select_server_verifier`),
|
||||
src/server.rs (`RawKeyCertResolver`)
|
||||
- ADR-006 (the handshake scope boundary — and why the duplex-pair shape
|
||||
stays inside it)
|
||||
- tasks/integration-suite.md (the existing suite this extends)
|
||||
|
||||
## Notes
|
||||
|
||||
> Agent fills this during implementation.
|
||||
|
||||
## Summary
|
||||
|
||||
> Agent fills this on completion.
|
||||
Reference in New Issue
Block a user