task 2: handshake-level suites — pin, fail-closed, RFC 7250 paths executed (U-3)
tests/handshake_behavior.rs (tcp-gated, tokio duplex + tokio-rustls, no new deps) turns the fail-closed / pin / raw-key language into executed behavior: - pin match: X.509 server + SHA256 pin -> handshake completes, app data round-trips, server extracts the client cert fingerprint - pin mismatch: wrong pin -> handshake error (the pin IS the anchor) - fail closed: remote_identity None + raw-key server -> HandshakeFailure - raw-key server path end-to-end: completes with the iroh-shaped client verifier (requires_raw_public_keys == true); presented cert asserted to be the SPKI carrying the raw Ed25519 key - N-4's interop trap executed: raw-key client resolver vs AcceptAnyCertVerifier -> IncorrectCertificateTypeExtension alert Major finding, recorded as OQ-TLS-10 (open): a crate-built pin client cannot reach a crate-built raw-key server over rustls TCP+TLS — the raw-key resolver requires the client to offer [RawPublicKey] server cert types, sent only when the client verifier overrides requires_raw_public_keys() == true. FingerprintPinVerifier keeps the trait default false (AcceptAnyCertVerifier too); iroh's verifier overrides true on both sides. Gap inherited from alknet (behavior-preserving); pinned both ways by the suite. client.md / server.md carry the interop notes; task file updated (premise adjustments documented in Notes, summary filled). Verification: 81 default / 91 tcp / 99 all-features tests green (+5 new), clippy -D warnings clean both configs, fmt clean, cargo doc warning-free, taskgraph validate 14 tasks.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: handshake-tests
|
||||
name: Handshake-level test suite — pin, fail-closed, raw-key path executed (U-3)
|
||||
status: pending
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: medium
|
||||
@@ -84,8 +84,69 @@ The four handshake-level gaps (review 001 §U-3):
|
||||
|
||||
## Notes
|
||||
|
||||
> Agent fills this during implementation.
|
||||
> **Major finding during implementation (recorded as OQ-TLS-10):** the
|
||||
> task's suite premises 1 and 3 did not hold as written. A temporary
|
||||
> probe (the review's methodology, deleted after the run) found:
|
||||
>
|
||||
> - **A crate-built pin client cannot complete a handshake against a
|
||||
> crate-built raw-key server** (HandshakeFailure). Mechanism,
|
||||
> verified in the rustls 0.23.44 sources
|
||||
> (`server/hs.rs::process_cert_type_extension`,
|
||||
> `client/hs.rs::process_cert_type_extension`): the raw-key server
|
||||
> resolver requires the client to offer
|
||||
> `server_certificate_types = [RawPublicKey]`, which rustls sends
|
||||
> only when the client verifier overrides
|
||||
> `requires_raw_public_keys() == true`. `FingerprintPinVerifier`
|
||||
> keeps the trait default `false` (as does
|
||||
> `AcceptAnyCertVerifier`). iroh's verifier overrides `true` on both
|
||||
> sides (`iroh/src/tls/verifier.rs`) — the working prior art. The
|
||||
> gap is inherited from alknet (no override there either) —
|
||||
> behavior-preserving, but the raw-key-over-TCP interop the
|
||||
> extraction implies does not exist yet.
|
||||
> - **Suite 3's premise adjusted**: the raw-key server path completes
|
||||
> only with an iroh-shaped verifier (`requires_raw_public_keys()
|
||||
> == true`); the suite pins that working shape (with
|
||||
> `NoClientCertResolver` client auth) and asserts the presented
|
||||
> SPKI carries the raw Ed25519 key.
|
||||
> - **Suite 1's premise adjusted**: the crate pin client ↔ raw-key
|
||||
> server shape was replaced by the pin client ↔ X.509 server
|
||||
> (`SHA256:` pin, which the crate's verifier can negotiate), pin
|
||||
> match + mismatch. The executed fail-closed (suite 2) is
|
||||
> mechanically the same HandshakeFailure (raw-key server needs a
|
||||
> raw-key offer the CA-path client never makes) — the alert is
|
||||
> asserted, the no-downgrade outcome is what's pinned.
|
||||
> - **N-4's trap executed as suite 3b**: raw-key client resolver ↔
|
||||
> `AcceptAnyCertVerifier` fails (client offers
|
||||
> `[RawPublicKey]` → `IncorrectCertificateTypeExtension`).
|
||||
>
|
||||
> OQ-TLS-10 recorded (open, options a/b/c, deferral rationale);
|
||||
> client.md and server.md carry the interop notes.
|
||||
|
||||
## Summary
|
||||
|
||||
> Agent fills this on completion.
|
||||
**Landed as `tests/handshake_behavior.rs`** (`tcp`-gated, 5 suites;
|
||||
no new deps — tokio duplex + tokio-rustls under the existing feature):
|
||||
|
||||
1. `pin_match_completes_and_server_extracts_client_fingerprint` —
|
||||
X.509 server, client pins the matching `SHA256:` fingerprint →
|
||||
handshake completes, app data round-trips, server extracts the
|
||||
client cert's fingerprint (request-but-don't-require, executed).
|
||||
2. `pin_mismatch_fails_the_handshake` — wrong pin → handshake error
|
||||
carrying the pin verifier's mismatch message.
|
||||
3. `unknown_raw_key_remote_fails_closed` — `remote_identity: None` +
|
||||
raw-key server → HandshakeFailure (fail closed, executed).
|
||||
4. `raw_key_server_path_completes_with_requires_raw_verifier` — the
|
||||
RFC 7250 server path end-to-end with the iroh-shaped client
|
||||
verifier: handshake completes, no client cert presented, and the
|
||||
presented server cert is verified to be the SPKI carrying the raw
|
||||
Ed25519 public key.
|
||||
5. `raw_key_client_resolver_fails_against_accept_any_cert_verifier` —
|
||||
N-4's interop trap executed (raw-key client cert types rejected).
|
||||
|
||||
Plus **OQ-TLS-10** (open — the cert-type negotiation gap above),
|
||||
server.md/client.md synced, frontmatter → completed.
|
||||
|
||||
**Verification:** 81 default / 91 tcp / 99 all-features tests green
|
||||
(+5 new handshake suites), clippy `-D warnings` clean (default +
|
||||
all-features), fmt clean, `cargo doc` warning-free, `taskgraph
|
||||
validate` 14 tasks.
|
||||
Reference in New Issue
Block a user