re-baseline the four pending remediation tasks against the ADR-007/008 tree

The tasks were decomposed (d7db6b1) before 49d4432/ac440f3 landed;
both commits touched exactly the areas the tasks reference. Fixes
grounded in verified sources (vendored rustls-acme 0.12.1, rustls-pemfile
2.2.0, rustls-native-certs 0.8.4) and a fresh cargo llvm-cov run:

- acme-event-loop-test: the termination assertion was impossible —
  rustls-acme's Stream for AcmeState never yields None
  (state.rs:407-412, poll_next_infinite + 2^16s backoff); the review's
  U-1 exit-condition premise is withdrawn and corrected in place.
  Replaced with timeout-bounded event collection, a reachable-arm
  inventory (Order warn, AccountCacheStore, Load/Parse error arms,
  DeployedCachedCert/CertCacheStore via deterministic DirCache file
  pre-seeding), and an explicit mark for the full-fake-CA arms.
  server.rs:135 flagged as unreachable dead code (delete or accept).
- coverage-cheap-closes: re-baselined per-line ground truth — original
  groups 1 and 5 are already closed by the handshake suites; group 2's
  TLS 1.3 half is covered, leaving the TLS 1.2 else-arm (client.rs:316);
  new group added for VerifyPresentedCertVerifier::verify_tls12_signature
  (server.rs:349-366, opened by ADR-008; required for the >=98% bar);
  AcceptAnyCertVerifier refs moved to server.rs:468-475 with the stale
  OQ-TLS-09 coordination caveat retired.
- docs-pin-c1-c4-n3-n4: N-4 re-scoped (the mechanism analysis already
  lives in ADR-007 + the resolver doc block; what remains is a short
  server-verifier note covering both verifiers); C-4 updated for
  ADR-007's negotiation-earlier failure point; added the
  FingerprintPinVerifier pop cross-ref update (post-ADR-008 the default
  verifier does verify possession).
- config-validation-and-trivia: added the feature-gate mechanics note
  for the C-3 test (a non-gated test passes vacuously under default
  features); refreshed drifted line refs with a re-grep advisory.
- review 001: Status block records OQ-TLS-09/-10 resolutions; U-1
  carries the termination correction; U-2 carries the supersession
  note. ADR-008 gains the suite-number-to-test-name mapping.

Verification: taskgraph validate 14 tasks; cargo doc --no-deps
warning-free; all edits docs-only (no code paths touched).
This commit is contained in:
2026-09-12 02:42:20 +00:00
parent ac440f3a9a
commit 23893d6236
6 changed files with 299 additions and 121 deletions
+84 -56
View File
@@ -25,7 +25,7 @@ question):
tighter validity is ever wanted, `not_before`/`not_after` are
additive params.
2. **C-4**`RemoteIdentity::fingerprint` accepts any string and the
pin comparison is exact-string (src/client.rs:272): uppercase-hex
pin comparison is exact-string (src/client.rs:293): uppercase-hex
pins, `sha256:` lowercase prefix, and cross-prefix pins
(`SHA256:`-of-an-ed25519-remote) construct fine and reject at
handshake — fail-closed, never a downgrade, but a config-author
@@ -33,7 +33,19 @@ question):
`FingerprintPinVerifier`): pins must be produced by
`fingerprint_from_cert_der` — case- and format-exact
(`ed25519:<lowercase hex>` / `SHA256:<lowercase hex>`); malformed
pins fail closed at handshake.
pins fail closed at handshake. **Update for ADR-007 (commit
`49d4432`): the pin format is no longer only a verification
selector — it also selects the cert-type offer (`ed25519:` → offer
`[RawPublicKey]`, `SHA256:` → default X.509 offer), and a
cross-format mismatch now fails earlier, at cert-type negotiation,
before the pin compare is even reached (pinned by
`tests/handshake_behavior.rs`:
`ed25519_pin_against_x509_server_fails_closed_at_negotiation`).
The review's original C-4 mechanism ("rejected at pin comparison")
is superseded — same fail-closed verdict, earlier failure point.
Write the doc line from that chain: mismatched *format* fails at
negotiation (a config error, ADR-007); same-format-but-wrong pins
(case, garbage) construct and fail at the pin compare.**
3. **N-3**`TlsServerConfig` is not `Clone` even under
`default = []` where the struct has no `JoinHandle` field. The
all-features posture is what the API freeze pins (conditional
@@ -41,53 +53,59 @@ question):
configurations). Decision: keep non-Clone for v1; record the
reasoning in the type's doc (one sentence) and close the question
— revisit only with a concrete consumer demanding it.
4. **N-4**the cert-type negotiation interaction needs an explicit
doc note on `AcceptAnyCertVerifier`. **Read the corrected mechanism
first** — the review's §N-4 original parenthetical ("alknet's own
client resolver offers both types, which is why production works")
is inaccurate; the review's §Status block and §N-4 now carry the
correction (found during the S-1 remediation, commit `e86b8ba`).
The accurate chain, from the rustls sources (0.23.41 AND 0.23.44):
- rustls sends `client_certificate_types = [RawPublicKey]` (only)
iff the *resolver's* `only_raw_public_keys()` is true
(client/hs.rs:331-334 in 0.23.41, :355-358 in 0.23.44) — no
"offers both types" behavior exists in either version.
- Against `AcceptAnyCertVerifier`
(`requires_raw_public_keys() == false`), a client offering only
`[RawPublicKey]` hits the `(false, true, false)` arm of
`process_cert_type_extension` (server/hs.rs:274-288 in 0.23.44)
`IncorrectCertificateTypeExtension` → handshake failure.
- A client with an X.509-typed resolver (or no resolver) offers no
cert-type extension / `[X509]` → the `(false, _, true)` or
`(false, false, false)` arm → default X.509 → works, and the
server passes presented bytes through unparsed to a custom
`verify_client_cert`.
- So `requires_raw_public_keys()` must stay `false` (do not "fix"
it to `true` — that would break X.509 clients), and the doc note
must state that a raw-key *client* resolver makes rustls offer
`[RawPublicKey]` only, which this verifier rejects
(fail-closed, not a downgrade). Note the S-1 doc on the same
type (landed, commit `e86b8ba`) already carries the no-pop
section; add the negotiation note as a sibling section, and note
that a raw-key *server* presentation (`RawKeyCertResolver`,
`only_raw_public_keys() == true` on the server cert side) is a
different knob (the `server_certificate_types` extension) that
is unaffected. Without the note, the next reader may "fix" the
verifier to `true` and break X.509 clients, or mis-diagnose the
raw-only-client failure as a bug.
4. **N-4****re-scoped 2026-09-12: the mechanism analysis this item
originally asked for is already documented.** ADR-007
(`docs/architecture/decisions/007-cert-type-negotiation.md`,
commit `49d4432`) records the whole negotiation chain, and
`RawKeyClientCertResolver`'s doc block (src/client.rs:152-171)
carries the client-side mechanism note (`only_raw_public_keys() ==
false` → X.509 offer; the `(false, true, false)` arm reasoning).
Do not re-derive or duplicate that analysis. What remains is a
short server-verifier note (a sibling section in the
`AcceptAnyCertVerifier` and `VerifyPresentedCertVerifier` docs, or
one shared paragraph referenced from both):
- `requires_raw_public_keys()` stays `false` on **both** server
verifiers — do not "fix" it to `true` (that would reject X.509
clients; the request-but-don't-require shape accepts both cert
types).
- Post-ADR-007, the crate's own `RawKeyClientCertResolver` presents
the SPKI under the **X.509 offer** unconditionally
(`client.rs:187`, `raw_public_keys: false`), so a raw-key client
against this crate's servers never sends a raw-only offer. The
`IncorrectCertificateTypeExtension` rejection of a raw-only
client offer (`(false, true, false)` arm) can now only arise
from a *foreign* rustls resolver that sets
`only_raw_public_keys() == true` — state it as an interop
boundary of the request-not-require shape, fail-closed, not a
downgrade.
- A raw-key *server* presentation (`RawKeyCertResolver`,
`only_raw_public_keys() == true` on the server-cert side) is the
other knob (`server_certificate_types`) and is unaffected.
Without the note, the next reader may "fix" the verifier to `true`
and break X.509 clients, or mis-diagnose a foreign raw-only client
failure as a bug.
## Work
1. C-1 doc lines (`generate_self_signed_cert` + `SelfSignedCert`).
2. C-4 doc lines (`RemoteIdentity::fingerprint`,
`FingerprintPinVerifier` — format-exactness + fail-closed).
`FingerprintPinVerifier` — format-exactness + fail-closed, with
the ADR-007 negotiation-earlier failure point per the Description).
3. N-3: the one-sentence decision note on `TlsServerConfig`'s doc;
close N-3 in the review's finding list (no code change).
4. N-4: the negotiation note on `AcceptAnyCertVerifier`'s doc. If
desired, add a pinned unit test asserting
`requires_raw_public_keys() == false` stays default (it's a trait
default — the test just documents the choice).
5. Cross-check the review's finding numbering so each doc change
4. N-4: the short server-verifier negotiation note per the re-scoped
item 4 in the Description (a sibling section on
`AcceptAnyCertVerifier` + `VerifyPresentedCertVerifier`, or one
shared paragraph referenced from both). If desired, add a pinned
unit test asserting `requires_raw_public_keys() == false` stays
default (it's a trait default — the test just documents the
choice).
5. While in these docs: `FingerprintPinVerifier`'s pop cross-ref
(src/client.rs:253-255) currently says only "the **server-side**
`AcceptAnyCertVerifier` does not [verify possession]" — post-ADR-008
the *default* server verifier `VerifyPresentedCertVerifier` does;
mention both (the default verifies, the escape hatch does not).
6. Cross-check the review's finding numbering so each doc change
cites its finding.
## Verification
@@ -102,24 +120,34 @@ question):
- [ ] A config author reading the rustdoc cannot mis-case a pin, pin
the wrong prefix, or expect a never-expiring dev cert to expire
- [ ] The raw-only-client negotiation trap is documented before a
consumer hits it
- [ ] The raw-only-client interop boundary is documented before a
consumer hits it (short note per the re-scope; the full
mechanism lives in ADR-007)
## References
- docs/reviews/001-implementation-review.md §C-1, §C-4, §N-3, §N-4
(§N-4 — read the correction block; the original parenthetical is
inaccurate), and the §Status block (the correction summary)
- src/server.rs:226-256, src/client.rs:225-236, src/credentials.rs:35-39,
src/identity.rs (SelfSigned doc)
- rustls 0.23.44 `server/hs.rs` `process_cert_type_extension`
(server/hs.rs:263-288 — the `(requires_raw_keys, offers_raw,
offers_x509)` negotiation table N-4 documents) and
`client/hs.rs:350-358` (the `[RawPublicKey]`-only offer rule) —
both pinned in rustls 0.23.41 too (client/hs.rs:326-334)
- tests/impersonation_posture.rs (the S-1 probe — its fixed resolver
pins `only_raw_public_keys() == false` for the X.509-typed offer;
do not confuse that with a raw-key client resolver)
(§N-4 — the correction blocks are historical context now; ADR-007
is the authoritative record of the negotiation mechanism), and the
§Status block
- src/server.rs:223-257 (the self-signed helper + `SelfSignedCert`),
src/server.rs:18-24 (`TlsServerConfig` — where the N-3 note goes),
src/client.rs:253-255 (the pop cross-ref), src/credentials.rs:36-38
(`RemoteIdentity::fingerprint`), src/identity.rs (SelfSigned doc)
- ADR-007 (`docs/architecture/decisions/007-cert-type-negotiation.md`)
— the authoritative negotiation record; ADR-008 (the verifier
default change that makes `VerifyPresentedCertVerifier` part of the
N-4 note)
- rustls 0.23.44 `server/hs.rs::process_cert_type_extension` (the
negotiation table) and `client/hs.rs` (the `[RawPublicKey]`-only
offer rule) — consult only if extending the analysis beyond what
ADR-007 records
- tests/impersonation_posture.rs (its fixed resolver pins
`only_raw_public_keys() == false` — post-ADR-007 that is also the
crate resolver's unconditional shape),
tests/handshake_behavior.rs
(`ed25519_pin_against_x509_server_fails_closed_at_negotiation`
the negotiation fail-closed pin)
## Notes