generation 3: port server + client — the two big ports

port-server (src/server.rs, 614 lines):
- TlsServerConfig with ADR-004 accessors: for_noq(&self) (borrow +
  inner-clone + NoqWrap via #[from]), for_tcp_tls(&self) adopted
  (infallible TlsAcceptor), rustls_config(&self) adopted
- acme_handle renamed per ADR-006; the defensive Acme arm returns
  TlsError::AcmeConfig (unreachable! eliminated)
- error sites remapped to typed variants (Rustls/SelfSigned/AcmeConfig)
- ACME path verbatim: DirCache + directory + contacts + resolver +
  acme-tls/1 + spawned event loop (tracing lines ported), returns
  immediately, detached handle
- 14 tests incl. the nine-scheme exact-list pin, ACME
  spawn/return/ALPN assertion (blackhole URL — no network I/O),
  no-feature AcmeConfig, for_tcp_tls/rustls_config round-trips

port-client (src/credentials.rs + src/client.rs):
- ConnectionCredentials/RemoteIdentity wholesale with load-bearing
  Option-semantics docs (None = public-X.509 state, Some = pin)
- verifier selection matrix + client-auth presentation matrix
  test-pinned at unit level; enable_early_data=true pinned;
  root-store fallback asserted non-empty
- FingerprintPinVerifier: pin match/mismatch + raw-key signature
  routing (verify_tls13_signature_with_raw_key) asserted
- resolvers/verifier made pub for the re-export block

Cargo.toml (two required deltas):
- noq feature gains aws-lc-rs: lockfile resolves noq-proto 1.3.0
  where ServerConfig::with_crypto is #[cfg(any(aws-lc-rs, ring))];
  ADR-003's TOML block was written against the 1.2 API. Amendment
  note recorded in tasks/port-server.md for the review-impl sync
- acme = [dep:rustls-acme, dep:futures] — ADR-006 already gates
  the futures dep on acme; the scaffold omitted it

lib.rs: re-export block complete (all eight modules)

Verification: cargo test 68, --all-features 75, --features noq 72,
clippy -D warnings, fmt --check, doc --no-deps (0 warnings) — green
This commit is contained in:
2026-09-10 14:51:44 +00:00
parent 4bdc12e84f
commit 0cd565fc28
8 changed files with 1673 additions and 30 deletions
+77 -11
View File
@@ -1,7 +1,7 @@
---
id: port-client
name: Port client side — TlsClientConfig, verifier selection, client auth (src/client.rs)
status: pending
status: completed
depends_on: [port-identity-types, port-fingerprint, port-pem-signing]
scope: broad
risk: medium
@@ -73,24 +73,24 @@ co-location prevents semantic drift (ADR-005).
## Verification
- [ ] Selection-matrix test: all four client-auth presentations × both
- [x] Selection-matrix test: all four client-auth presentations × both
verifier branches construct and select the expected resolver
types (inspect via the config's client-auth/verifier state where
the API permits; otherwise assert construction success/error
kind per cell)
- [ ] `Acme` local identity → `TlsError::AcmeConfig`
- [ ] `enable_early_data == true` pinned
- [ ] Root store non-empty (fallback exercised)
- [ ] FingerprintPinVerifier unit tests ported (pin match, mismatch,
- [x] `Acme` local identity → `TlsError::AcmeConfig`
- [x] `enable_early_data == true` pinned
- [x] Root store non-empty (fallback exercised)
- [x] FingerprintPinVerifier unit tests ported (pin match, mismatch,
raw-key signature routing)
- [ ] `cargo test` (default), `cargo test --all-features`,
- [x] `cargo test` (default), `cargo test --all-features`,
`cargo clippy --all-targets -- -D warnings`, `cargo fmt --check`
## Acceptance Criteria
- [ ] `for_noq(self)` / `into_rustls_config(self)` per ADR-004
- [ ] The selection matrix has no fourth path (fail-closed structural)
- [ ] `lib.rs` re-exports the client surface + `ConnectionCredentials`
- [x] `for_noq(self)` / `into_rustls_config(self)` per ADR-004
- [x] The selection matrix has no fourth path (fail-closed structural)
- [x] `lib.rs` re-exports the client surface + `ConnectionCredentials`
/ `RemoteIdentity`
## References
@@ -105,6 +105,72 @@ co-location prevents semantic drift (ADR-005).
> Agent fills this during implementation.
- Verifier selection / client-auth presentation are inspected through
the `rustls::ClientConfig` Debug output (`ClientConfig` derives
`Debug` and embeds the verifier's debug name —
`FingerprintPinVerifier` / `WebPkiServerVerifier`) and the public
`client_auth_cert_resolver` field; the `verifier` field itself is
`pub(super)` at rustls 0.23.44, so the debug-string probe is the
structural assertion shape.
- `select_server_verifier`'s `WebPkiServerVerifier` build error maps to
`TlsError::VerifierBuild` via `#[from] rustls::client::VerifierBuilderError`
(the `rustls::webpki` module is private at 0.23.44; same type,
public path — lib.rs's variant doc already records this).
- The signature-routing unit tests construct `DigitallySignedStruct`
from its wire encoding through the doc-hidden
`rustls::internal::msgs` surface (`Codec::read`; `new` is
`pub(crate)`), then drive `verify_tls12_signature` /
`verify_tls13_signature` on the SPKI-as-`CertificateDer` exactly as
rustls presents it in an RFC 7250 handshake — pin match + signature
possession-proof both asserted.
- The `for_noq()` wrap relies on `TlsError::NoqWrap(#[from])`
(`NoInitialCipherSuite``?`), ADR-002/ADR-003.
- The extracted `TlsClientConfig`'s `#[allow(dead_code)]` is kept
(the server-side `rustls_config()` accessor shape is ADR-004's;
no consumer touches the field within this crate yet).
## Summary
> Agent fills this on completion.
> Agent fills this on completion.
Ported `src/credentials.rs` (wholesale from alknet-core, doc comments
rewired to this crate's ADRs — ADR-005, alknet ADR-091/034/030
semantics preserved verbatim in substance) and `src/client.rs` (port of
alknet-tls `client.rs` with the task's error-mapping deltas and import
rewires). Visibility: `RawKeyClientCertResolver`, `NoClientCertResolver`,
`FingerprintPinVerifier` made `pub` (lib.rs re-exports them);
`build_client_auth`, `select_server_verifier`, `load_platform_root_cert_store`
`pub`. `lib.rs` gained the client + credentials re-export lines; the
pending-modules comment now notes only the server line remains (its port
task owns it).
Deltas vs the extraction:
- `TlsError` mapping per ADR-002: `with_safe_default_protocol_versions` /
`CertifiedKey::from_der` / `RootCertStore::add` errors →
`TlsError::Rustls` (`#[from] rustls::Error`); the
`WebPkiServerVerifier` build error → `TlsError::VerifierBuild`
(`#[from] rustls::client::VerifierBuilderError`); the Acme
client-auth error string → `TlsError::AcmeConfig`. No
`Config(String)` catch-all.
- `for_quinn``for_noq` (ADR-003/004): consuming, noq-gated; the
wrap error flows through `TlsError::NoqWrap(#[from])` — no
`map_err` stringification.
- Imports rewired to `crate::{credentials, fingerprint, identity}`;
the `PeerEntry` reference in the extraction's `NoClientCertResolver`
doc became the peer-id resolution language (auth layer stays out,
ADR-005).
- Invariants pinned by tests: `enable_early_data = true` + exact ALPN;
aws-lc-rs default provider (9-suite set + `crypto_provider()`
identity); root store non-empty; verifier-selection matrix
(`Some``FingerprintPinVerifier`, `None``WebPkiServerVerifier`);
client-auth presentation matrix (RawKey → RFC 7250 raw pub keys,
X509 → loaded chain, SelfSigned/None → nothing, Acme →
`TlsError::AcmeConfig`); FingerprintPinVerifier pin match/mismatch
(Ed25519 SPKI + SHA256 X.509) and raw-key signature routing
(TLS 1.2 + 1.3, forged-signature rejection).
Verification: `cargo test` (56 pass), `cargo test --all-features`
(59 pass, incl. both `for_noq` tests), `cargo clippy --all-targets
--all-features -- -D warnings` (clean), `cargo fmt --check` (clean),
`cargo check --features noq` / `--features tcp` / default (clean).