docs-pin batch: self-signed validity, pin format-exactness, non-Clone decision, negotiation note (C-1, C-4, N-3, N-4)

- C-1: SelfSignedCert + generate_self_signed_cert document the
  rcgen-default validity (1975→4096, never expires in practice), no
  SANs (CA verification fails as designed — pairs with SHA256: pinning),
  and the additive not_before/not_after route if tighter validity is
  ever wanted
- C-4: RemoteIdentity::fingerprint + FingerprintPinVerifier document
  case-/format-exactness (pins produced by fingerprint_from_cert_der,
  lowercase hex, exact ed25519:/SHA256: prefixes); written from the
  ADR-007-corrected chain — same-format-but-wrong pins fail closed at
  the pin compare, cross-format mismatches fail earlier at cert-type
  negotiation (the review's original pin-compare mechanism superseded)
- N-3: TlsServerConfig doc records the keep-non-Clone-for-v1 decision
  (API identical across feature configurations; conditional Clone is
  its own trap); N-3 closed in review 001
- N-4: "Client-cert-type negotiation" section on
  VerifyPresentedCertVerifier (cross-referenced from
  AcceptAnyCertVerifier): requires_raw_public_keys() stays trait-default
  false on both server verifiers — do not fix to true (rejects X.509
  clients); the raw-only-offer rejection can only arise from a foreign
  resolver with only_raw_public_keys() == true — interop boundary of
  the request-not-require shape, fail-closed, not a downgrade; pinned
  by server_verifiers_keep_requires_raw_public_keys_default_false
- Work item 5: FingerprintPinVerifier's pop cross-ref now names both
  server verifiers (default VerifyPresentedCertVerifier verifies per
  ADR-008; AcceptAnyCertVerifier escape hatch does not)

Verification: cargo test (default, 69 lib tests) and --all-features
(78 lib tests, 1 new), clippy --all-targets --all-features -D warnings,
fmt --check, doc --no-deps warning-free — all green
This commit is contained in:
2026-09-12 04:28:58 +00:00
parent 9bdc32d848
commit 7713a6e210
5 changed files with 229 additions and 9 deletions
+93 -3
View File
@@ -1,7 +1,7 @@
---
id: docs-pin-c1-c4-n3-n4
name: Docs + pin batch — self-signed validity, fingerprint pin format-exactness, Clone-under-no-acme, cert-type negotiation note (C-1, C-4, N-3, N-4)
status: pending
status: completed
depends_on: []
scope: narrow
risk: low
@@ -151,8 +151,98 @@ question):
## Notes
> Agent fills this during implementation.
- C-1: doc blocks added on both `SelfSignedCert` and
`generate_self_signed_cert` (src/server.rs): the cert is valid
1975→4096 (rcgen 0.13's `CertificateParams::default()`) — never
expires in practice — carries no SANs, so CA verification of it
fails as expected; it pairs with `SHA256:` fingerprint pinning (a
pin that outlives any plausible deployment by design) and can never
*fail* the way an ACME/ops-managed cert can; `not_before`/`not_after`
are additive if tighter validity is ever wanted. The `cert_chain`
field doc repeats the validity/no-SAN facts briefly.
- C-4: two doc blocks written from the ADR-007-corrected chain:
- `RemoteIdentity::fingerprint` (src/credentials.rs): the pin must
be produced by `fingerprint_from_cert_der`, case- and
format-exact (lowercase hex, `ed25519:` / `SHA256:` prefixes);
same-format-but-wrong pins (uppercase hex, lowercase `sha256:`,
garbage) construct and fail closed at the pin compare; mismatched
*format* fails earlier, at cert-type negotiation (ADR-007), before
the pin compare is reached — same fail-closed verdict, earlier
failure point.
- `FingerprintPinVerifier` (src/client.rs): a "The pin is case- and
format-exact" section covering the same chain from the verifier's
side (exact-string compare; the pin format also selects the
server cert-type offer per ADR-007, so a wrong-format pairing
aborts at negotiation).
- N-3: one decision paragraph added to `TlsServerConfig`'s doc
(src/server.rs): not `Clone` for v1, kept so the API is identical
across feature configurations (conditional `Clone` would be an API
difference between feature sets); share via `Arc`, revisit only if a
concrete consumer demands it. N-3 closed in review 001
(`docs/reviews/001-implementation-review.md` §N-3 remediation
status, plus a status-block note).
- N-4: a "Client-cert-type negotiation (ADR-007, review 001 §N-4)"
section added on `VerifyPresentedCertVerifier` (the default
verifier), stating: `requires_raw_public_keys()` stays on the trait
default `false` on both server verifiers — do not "fix" it to `true`
(that would reject every X.509 client); the crate's own
`RawKeyClientCertResolver` presents the SPKI under the X.509 offer
unconditionally, so a raw-only client offer rejection
(`IncorrectCertificateTypeExtension`, rustls
`server/hs.rs::process_cert_type_extension`'s `(false, true, false)`
arm) can only arise from a *foreign* resolver with
`only_raw_public_keys() == true` — an interop boundary of the
request-not-require shape, fail-closed, not a downgrade; the
raw-key *server* side (`RawKeyCertResolver`,
`server_certificate_types`) is the other knob and unaffected.
`AcceptAnyCertVerifier` carries a cross-reference paragraph
("the same shape and rationale apply verbatim") instead of a
duplicate. No mechanism re-derivation — ADR-007 stays the
authoritative record. Pinned by a new unit test
`server_verifiers_keep_requires_raw_public_keys_default_false`
(src/server.rs tests) asserting the trait-default `false` on both
verifiers.
- Work item 5 (pop cross-ref): `FingerprintPinVerifier`'s closing
paragraph now says the **default** `VerifyPresentedCertVerifier`
verifies possession (ADR-008, resolving OQ-TLS-09) and the
`AcceptAnyCertVerifier` escape hatch does not — the pre-ADR-008
sentence that named only `AcceptAnyCertVerifier` is gone.
- Work item 6 (finding citations): every doc block names its finding
(§C-1, §C-4, §N-3, §N-4) and review 001's status block records the
batch closure, including the §C-4 supersession note.
- Review 001 updates: §N-3 marked "closed by decision"; Status block
gained a "Status update (2026-09-12, docs-pin batch)" paragraph.
- ADR-007 doc-link style note: rustdoc renders the
`../docs/architecture/decisions/007-cert-type-negotiation.md`
relative links from `src/credentials.rs` / `src/server.rs`; verified
present in the rendered HTML (`cargo doc --no-deps` warning-free,
links resolve to the repo file path).
## Summary
> Agent fills this on completion.
All four doc/doc+pin findings closed as rustdoc (plus one decided
question), no code-behavior change beyond the optional unit pin:
1. `src/server.rs` — C-1 validity/no-SAN facts on `SelfSignedCert` +
`generate_self_signed_cert`; N-3 one-sentence non-Clone decision on
`TlsServerConfig`; N-4 "Client-cert-type negotiation" section on
`VerifyPresentedCertVerifier` + cross-ref on `AcceptAnyCertVerifier`;
new pinned unit test
`server_verifiers_keep_requires_raw_public_keys_default_false`
(asserts the trait-default `false` on both server verifiers).
2. `src/credentials.rs` — C-4 format-exactness block on
`RemoteIdentity::fingerprint` (pin produced by
`fingerprint_from_cert_der`; same-format-but-wrong → pin-compare
rejection; cross-format → ADR-007 negotiation abort).
3. `src/client.rs` — C-4 "case- and format-exact" section on
`FingerprintPinVerifier` (with the ADR-007 offer-selection chain);
work item 5: the pop cross-ref now names both server verifiers
(default verifies per ADR-008, escape hatch does not).
4. `docs/reviews/001-implementation-review.md` — §N-3 closed by
decision; Status block records the C-1/C-4/N-3/N-4 closure and the
§C-4 mechanism supersession.
Verification: `cargo test` (default, 69 lib + integration) and
`cargo test --all-features` (78 lib tests, new pin test passing),
`cargo clippy --all-targets --all-features -- -D warnings`,
`cargo fmt --check`, `cargo doc --no-deps` warning-free — all green.