ADR-007: RFC 7250 cert-type negotiation — the offer follows the identity (OQ-TLS-10 resolved)
Resolve the cert-type negotiation gap (review 001 §U-3, OQ-TLS-10) by deviation from alknet: the gap was a defect in the prior art (alknet's code never delivered its spec's raw-key-over-TCP promise — ADR-082 "works for both QUIC and TCP+TLS"), not behavior to preserve. - FingerprintPinVerifier::requires_raw_public_keys() derives from the pin format: ed25519: -> true (offer [RawPublicKey]), SHA256: -> false (X.509 offer). Crate pin client now completes against the crate raw-key server; SHA256: pins negotiate unchanged. - RawKeyClientCertResolver presents the SPKI under the X.509 offer (only_raw_public_keys() == false): a raw-only client offer can only negotiate against a requires_raw server verifier, and AcceptAnyCertVerifier correctly stays on the default (accepts both cert types). The server extracts the ed25519: fingerprint from the SPKI bytes either way. - Fail-closed preserved and strengthened: an ed25519: pin against an X.509 server now aborts at negotiation (suite 2b), never a downgrade; no API change (no public signature affected; the fix is invisible to consumers apart from working handshakes). - tests/handshake_behavior.rs: suite 3 now runs crate-native (no custom iroh-shaped verifier), new negotiation fail-closed suite, suite 3b inverted to end-to-end success; invariant_pins.rs resolver-offer assertions flipped; unused imports dropped. - Docs: ADR-007 written; OQ-TLS-10 -> resolved-by-deviation; client.md/server.md/overview/README/task postscript synced (incl. the strict-foreign-server limit in ADR-007 §Limits). Verification: cargo test 81 / --features tcp 94 / --all-features 105 green; clippy -D warnings clean (default + all-features); fmt clean; cargo doc warning-free.
This commit is contained in:
+24
-11
@@ -48,7 +48,7 @@ exactly the two inputs the client config consumes:
|
||||
|
||||
| Local identity | Presented |
|
||||
|----------------|-----------|
|
||||
| `RawKey` | RFC 7250 raw public key (SPKI DER; `only_raw_public_keys()` auto-detected from the DER) |
|
||||
| `RawKey` | the Ed25519 SPKI as the client cert, under the X.509 offer (ADR-007 — the cert-type extension is an offer format, not an identity statement; the server extracts the `ed25519:` fingerprint either way) |
|
||||
| `X509` | the cert chain + key, loaded from disk |
|
||||
| `SelfSigned` / `None` | nothing (`NoClientCertResolver`) — documented, resolved in OQ-TLS-02 |
|
||||
| `Acme` | **config error** (`TlsError::AcmeConfig`) — server-only identity |
|
||||
@@ -82,15 +82,21 @@ All three outcomes are **executed** in
|
||||
`tests/handshake_behavior.rs` (real rustls handshakes over a duplex
|
||||
pair, `tcp`-gated).
|
||||
|
||||
**RFC 7250 over TCP is a negotiation gap, not a verified path**
|
||||
(OQ-TLS-10): `FingerprintPinVerifier` keeps rustls' trait-default
|
||||
`requires_raw_public_keys() == false`, so a crate-built pin client
|
||||
cannot reach a crate-built raw-key server — the handshake fails
|
||||
closed (`HandshakeFailure`). The executed raw-key-over-TCP pin
|
||||
(`raw_key_server_path_completes_with_requires_raw_verifier`) uses the
|
||||
iroh-shaped verifier (`requires_raw_public_keys() == true`) any
|
||||
raw-key-over-TCP consumer must bring. Raw-key peers that ride
|
||||
iroh/noq use those transports' own TLS and are unaffected.
|
||||
**RFC 7250 over TCP is the crate's own composition now** (ADR-007,
|
||||
resolving OQ-TLS-10): `FingerprintPinVerifier` derives its cert-type
|
||||
offer from the pin format — an `ed25519:<hex>` pin offers
|
||||
`server_certificate_types = [RawPublicKey]` and completes against a
|
||||
crate-built raw-key server (`raw_key_server_path_completes_with_crate_pin_client`);
|
||||
a `SHA256:<hex>` pin keeps the X.509 offer. A raw-key *client*
|
||||
identity presents its SPKI under the X.509 offer and
|
||||
`AcceptAnyCertVerifier` accepts it end-to-end
|
||||
(`raw_key_client_presents_spki_and_server_extracts_fingerprint`). A
|
||||
pin-format/cert-kind mismatch fails closed at negotiation, never a
|
||||
downgrade (`ed25519_pin_against_x509_server_fails_closed_at_negotiation`).
|
||||
Raw-key peers that ride iroh/noq use those transports' own TLS and are
|
||||
unaffected. The strict-server limit (a foreign server demanding
|
||||
raw-only client certs still rejects an X.509-offer presentation) is
|
||||
recorded in ADR-007 §Limits.
|
||||
|
||||
## `FingerprintPinVerifier`
|
||||
|
||||
@@ -109,6 +115,12 @@ key. This verifier checks proof-of-possession; the server-side
|
||||
`AcceptAnyCertVerifier` does not (see
|
||||
[server.md](server.md), OQ-TLS-09).
|
||||
|
||||
The cert-type offer follows the pin format (ADR-007):
|
||||
`requires_raw_public_keys()` returns `true` for `ed25519:` pins
|
||||
(the peer presents an RFC 7250 raw key), `false` for `SHA256:` pins
|
||||
(the peer presents an X.509 cert) — a mismatched pairing fails closed
|
||||
at negotiation.
|
||||
|
||||
## The root-store fallback (alknet ADR-088 §5)
|
||||
|
||||
The `None` + X.509 CA path loads the platform's native root certs
|
||||
@@ -148,4 +160,5 @@ X.509 endpoint needs it regardless of transport.
|
||||
- alknet ADR-034 (verifier selection), ADR-088 §5 (root-store
|
||||
fallback), ADR-091 (`ConnectionCredentials`)
|
||||
- ADR-002 (`TlsError`), ADR-004 (accessors), ADR-005 (credential
|
||||
types), ADR-006 (tests — the verifier-selection matrix)
|
||||
types), ADR-006 (tests — the verifier-selection matrix), ADR-007
|
||||
(the cert-type negotiation / offer-follows-identity rule)
|
||||
Reference in New Issue
Block a user