- client.rs: TLS 1.2 non-Ed25519 (X.509 ECDSA) routing pin on FingerprintPinVerifier::verify_tls12_signature's else-arm; deterministic webpki-roots fallback tests over a new private fill_root_store_from_native(&CertificateResult) seam (empty store → all webpki-roots anchors, errors + valid cert, no-fallback case) - server.rs: VerifyPresentedCertVerifier::verify_tls12_signature unit tests (Ed25519 raw-key route, X.509 route, wrong-key and mismatched-message rejections), the escape hatch's TLS 1.2 no-pop pin, and the default verifier's nine-scheme list pin - pem.rs: parse-error arm test (garbage-but-keyed file → CertLoad) - fingerprint.rs: fixed vacuous bad_bit_string_lengths_extract_nothing (outer SEQUENCE lengths exceeded the actual bytes, so parsing never reached the line-67 bit-string checks); added the non-OID-tag / missing-BIT-STRING matrix - no public-API growth; remaining uncovered lines are the ACME event loop (owned by acme-event-loop-test) and llvm-cov attribution artifacts documented in the task's Notes Verification: cargo llvm-cov --all-features 98.26% lines; cargo test (80 lib + 13 integration), cargo test --all-features (89 lib + 29 integration), clippy -D warnings (default + all-features), fmt --check
11 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| coverage-cheap-closes | Cheap coverage closes — non-Ed25519 pin arms, PEM parse-error arm, fallback seam, escape-hatch methods (U-2, re-baselined) | completed | narrow | low | component | implementation |
|
Description
The coverage inventory (review 001 Part C) — re-baselined
2026-09-12 by a fresh cargo llvm-cov --all-features run after
ADR-007/ADR-008 landed (commits 49d4432/ac440f3): the new
handshake and impersonation suites already closed two of the original
groups, and ADR-008's new verifier opened one new gap. Current
per-line ground truth (all line numbers re-verified against the
current tree):
- client.rs:316 —
FingerprintPinVerifier:: verify_tls12_signature's non-Ed25519 else-arm (rustls::crypto::verify_tls12_signature) is never executed. (The original group-2 text pointed at the TLS 1.3 twin at 297/316-322 — that TLS 1.3 arm is now covered by the handshake suite's pin suites; only the TLS 1.2 else-arm remains.) Pinned test: an ECDSA-P256 rcgen cert +dss_with_scheme(P256/SHA256) againstverify_tls12_signature, assert ok, then a forged sig → err. Mirrors the existing Ed25519 TLS 1.3 routing pin (fingerprint_pin_verifier_routes_ed25519_spki_tls13_signature_ through_raw_key_path, client.rs:472). - server.rs:349-366 —
VerifyPresentedCertVerifier:: verify_tls12_signature's body (ADR-008's default verifier; did not exist at decomposition time) is entirely uncovered: no TLS 1.2 handshake exists in the suite. Same pop-routing shape as the pin verifier (Ed25519-SPKI →verify_tls13_signature_with_raw_key, elseverify_tls12_signature). A direct unit-call test (Ed25519 SPKI + valid sig → ok; wrong-key or mismatched-message dss → err) closes it. This group must be included or the ≥98% acceptance bar is unreachable while the ACME loop stays open (owned byacme-event-loop-test). - pem.rs:30 — the
Err(e) => Err(io::Error::other(e))arm ofload_private_key(a parse failure, distinct from "no key found") has no test: garbage-but-keyed file (b"-----BEGIN PRIVATE KEY-----\n!!!\n-----END PRIVATE KEY-----\n") exercises it. Verified against rustls-pemfile 2.2.0: malformed base64 in a keyed section is a realError::Base64Decoding-classErrfromprivate_key— the prescribed input works. - client.rs:145-148 — the webpki-roots fallback push loop is
covered only nondeterministically (passes vacuously when the
platform store is non-empty). Deterministic remediation: a
#[cfg(test)]-visible helper taking the "native certs" as a parameter (or an injectableload_native_certsseam) so the empty-platform case is testable without root. This is a load-bearing invariant whose fallback branch has no deterministic test. (rustls_native_certs::CertificateResulthas publiccerts/errorsfields, so the seam shape is a plain refactor — see Work item 2.) Thetracing::warn!for native-cert errors at client.rs:139 sits in the same uncovered pocket. - server.rs:468-475 —
AcceptAnyCertVerifier(now the escape-hatch verifier post-ADR-008, moved to server.rs:429) is only partially covered by the impersonation suite's escape-hatch tests:verify_tls12_signature's body andsupported_verify_schemes()(468-475) are never called. Two-line test each (call, assertOk/ assert the nine-scheme list), making the no-pop posture explicit in the suite. (The original group-6 range 287-303 is nowVerifyPresentedCertVerifier's code — the file shifted under the ADR-008 commit.) Note: the original "coordinate with OQ-TLS-09's resolution" caveat is stale — OQ-TLS-09 is resolved (ADR-008), the escape-hatch posture is permanent and pinned bytests/impersonation_posture.rs. - fingerprint.rs:67 — the second disjunct
(
len() != 33 || [0] != 0x00) matrix case (34-byte bit-string + unused-bits ≠ 0 vs len ≠ 33). Cosmetic.
Already closed by the ADR-007/ADR-008 suites (do not redo): the
client resolvers' resolve calls (original group 1 —
RawKeyClientCertResolver::resolve and NoClientCertResolver::resolve
are exercised by the handshake suites) and RawKeyCertResolver::resolve
(original group 5 — exercised end-to-end by
tests/handshake_behavior.rs, e.g.
raw_key_client_presents_spki_and_server_extracts_fingerprint).
FingerprintPinVerifier::verify_tls13_signature's non-Ed25519 arm
(original group 2's TLS 1.3 half) is also covered.
Work
- Write the unit tests (in-module
#[cfg(test)]where the items are private-visible, tests/ where public API suffices). - Item 4 is the only one touching non-test code: extract a
test-visible seam for the fallback loop. Keep the seam
#[cfg(test)]-visible or behind a plainpub(crate)fn — do not grow the public API. - Run llvm-cov and confirm the ranges close.
Verification
cargo llvm-cov --all-featuresshows the six groups covered (client.rs 316/145-148, pem.rs 30, server.rs 349-366/468-475, fingerprint.rs 67)- The fallback test deterministically exercises the push loop (platform-store-independent)
cargo test,cargo test --all-features, clippy, fmt green
Acceptance Criteria
- Line coverage ≥ 98% (from 95.81% currently; 95.32% at decomposition) with every load-bearing uncovered group closed
- No public-API growth
References
- docs/reviews/001-implementation-review.md §U-2, Part C (the inventory rows for the closed groups are stale — this task's Description is the current baseline)
- src/client.rs, src/pem.rs, src/server.rs, src/fingerprint.rs
- tests/handshake_behavior.rs (the suites that closed original groups
1 and 5), tests/impersonation_posture.rs (the escape-hatch pins —
group 5's
verify_tls13_signaturecoverage comes from here)
Notes
Agent fills this during implementation.
Work notes (2026-09-12, verified against the current tree before and after):
- Line drift since the re-baseline: the two doc-pin commits
(
9bdc32d,7713a6e) shifted the ADR-008 ranges. Current ground truth at task start: client.rs TLS 1.2 pin else-arm = 334; server.rsVerifyPresentedCertVerifier::verify_tls12_signature= 414-431;AcceptAnyCertVerifier::verify_tls12_signature= 538-545 (supported_verify_schemes()at 547-549 was already covered). - Group 1 (client.rs:334): the else-arm needs a parseable X.509
cert —
verify_tls12_signaturemaps the cert throughwebpki::EndEntityCert. Test: rcgen ECDSA-P256 cert +any_ecdsa_typesigner +ECDSA_NISTP256_SHA256DSS → ok; forged sig → err. (rcgen'sKeyPair::generate()is already P-256; its message-signing ispub(crate), so the signer comes fromrustls::crypto::aws_lc_rs::sign::any_ecdsa_type.) - Group 2 (server.rs:414-431): three direct unit-call tests — Ed25519 SPKI → raw-key path ok + forged → err; X.509 → standard path ok + valid-sig-under-different-key → err; Ed25519 sig over a mismatched message → err.
- Group 3 (pem.rs:30): the prescribed garbage-but-keyed input
verified against rustls-pemfile 2.2.0 first (returns
Err(InvalidCharacter(33))) — works as prescribed. - Group 4: seam extracted as
fill_root_store_from_native(&CertificateResult)(plain private fn, no API growth). Tests constructCertificateResult::default()and push into its publiccerts/errorsfields (probed: construction works despite#[non_exhaustive]— it derivesDefaultandError's fields are public). Three tests: empty → all webpki-roots anchors (count-checked, deterministic); errors + valid cert → valid cert loads (also covers the client.rs:139tracing::warn!pocket); valid cert → used verbatim, no fallback. - Group 5 (server.rs:538-545):
verify_tls12_signatureasserts unconditionally (pinned with garbage cert + garbage sig → Ok); added the nine-scheme pin forVerifyPresentedCertVerifier:: supported_verify_schemes()(its list delegates tonine_supported_verify_schemes();AcceptAnyCertVerifier's list was already pinned). - Group 6 (fingerprint.rs:67): the task's second-disjunct concern
was already half-covered, but the existing
bad_bit_string_lengths_extract_nothingpassed vacuously — its crafted SPKIs declared outer SEQUENCE lengths longer than the actual bytes (0x2b/0x2d/0x2d vs actual 0x29/0x2b/0x2a), so parsing failed at the outer TLV and never reached the bit-string checks. Fixed the three declared lengths and added a tag-matrix test (non-OID tag inside AlgorithmIdentifier; well-formed SPKI with no BIT STRING after the alg-id). - Coverage leftovers, documented as out of scope: the two
panic!arms of the error-path tests were converted to.err().expect(..)(closes client.rs:949 and server.rs:1029 — the config types are deliberately notDebug, so.err()avoidsexpect_err'sT: Debugbound). fingerprint.rs 125/143 (the} else { None }tail arms ofexpect_sequence/expect_bit_string) remain "uncovered" in llvm-cov output — proven (temporary eprintln + region dump) to be an llvm-cov region attribution artifact: the else arms execute (region counts 19 and- but llvm attributes the region to the
read_tlv()?line, not theNoneliteral. The parser is behavior-preservation code; restructuring it for a tooling artifact is not warranted.
- but llvm attributes the region to the
- Remaining uncovered lines crate-wide are the ACME event loop
(owned by
acme-event-loop-test) and the ACME-only task arms.
Summary
Agent fills this on completion.
Closed all six re-baselined U-2 coverage groups. Tests added:
client.rs:fingerprint_pin_verifier_verify_tls12_signature_routes_x509_through_standard_path(ECDSA-P256 → standard TLS 1.2 path ok + forged → err);empty_platform_store_deterministically_falls_back_to_webpki_roots,native_cert_errors_are_logged_and_valid_certs_still_load,native_certs_fill_the_store_without_the_fallback(deterministic fallback pins over the newfill_root_store_from_nativeseam) +dss_with_scheme/native_resulttest helpers.server.rs: threeVerifyPresentedCertVerifier::verify_tls12_signaturetests (Ed25519 raw-key route ok/forged-err, X.509 route ok/wrong-key-err, mismatched-message err), the escape hatch'sverify_tls12_signatureno-pop pin, and theVerifyPresentedCertVerifiernine-scheme list pin +dss_with_schemehelper.pem.rs:load_private_key_parse_error_yields_cert_load_error(garbage-but-keyed input →TlsError::CertLoad).fingerprint.rs: corrected the vacuousbad_bit_string_lengths_extract_nothing(wrong outer lengths) so the line-67 disjunct genuinely executes, and addedwrong_tag_before_oid_and_missing_bit_string_extract_nothing.
Only non-test change: load_platform_root_cert_store now delegates
to a private fill_root_store_from_native(&CertificateResult) seam —
no public-API growth.
Verification: cargo llvm-cov --all-features line coverage 98.26%
(from 95.90%), all six groups covered; cargo test (80 lib + 13
integration), cargo test --all-features (89 lib + 29 integration),
cargo clippy --all-targets -- -D warnings (default and
--all-features), cargo fmt --check — all green.