Close review 001 §S-1: the default client-cert verifier never checked the client's CertificateVerify, so anyone holding a peer's *public* cert/SPKI bytes (public by design — peers publish them to be dialable) could complete a handshake as that peer, and the auth layer could not detect it. The consumer designs are known (X.509 and raw-key TCP/QUIC endpoints with identity-bearing clients), so implementing now — the zero-consumer moment — avoids the guaranteed breaking republish of flipping the default later. - VerifyPresentedCertVerifier (new): request, don't require, verify possession — permissive verify_client_cert (self-signed chains and bare SPKIs stay valid presentation) + CertificateVerify routing by presented cert kind (Ed25519 SPKI -> verify_tls13_signature_with_ raw_key both TLS versions; X.509 -> standard route), the same routing FingerprintPinVerifier implements. Nine-scheme list verbatim (shared fn, exact-list pin covers both). - Default on every TlsServerConfig path — X509 / RawKey / SelfSigned / ACME (the verifier install is crate-side rustls in new_acme, not rustls-acme's). - AcceptAnyCertVerifier stays public as the explicit no-pop escape hatch, no longer installed by any crate path. - tests/impersonation_posture.rs: four pins — default rejects the attacker (X.509: UnsupportedSignatureAlgorithmForPublicKeyContext; raw-key: BadSignature), escape hatch still accepts + extracts the victim's fingerprint (both cert types). - tests/handshake_behavior.rs: suites 4/4b — possession-checked legit clients (raw-key pin vs raw-key server; X.509 client vs X.509 server) complete and the server extracts the fingerprint; suite 3b doc updated. - Docs: ADR-008 written; OQ-TLS-09 -> resolved (option (b)); ADR-007 §Limits deferral retired to not-planned; server.md/client.md invariants/README/overview synced. Verification: cargo test 81 / --features tcp 95 / --all-features 104 green; clippy -D warnings clean (default + all-features); fmt clean; cargo doc warning-free.
197 lines
9.1 KiB
Markdown
197 lines
9.1 KiB
Markdown
---
|
|
status: draft
|
|
last_updated: 2026-09-11
|
|
---
|
|
|
|
# Open Questions
|
|
|
|
Centralized tracker for alktls. Promoted from Phase 0
|
|
(`docs/research/phase-0.md` OQ-TLS-01..08) on 2026-09-10. Statuses here
|
|
are authoritative; the Phase 0 doc's statuses are the historical record.
|
|
|
|
## Statuses at a glance
|
|
|
|
| OQ | Topic | Status | Priority |
|
|
|----|-------|--------|----------|
|
|
| OQ-TLS-01 | Config-type ownership | **resolved** (ADR-005) | high |
|
|
| OQ-TLS-02 | `SelfSigned` on the client path | resolved (documented behavior kept) | low |
|
|
| OQ-TLS-03 | `for_tcp_tls()` adoption | **resolved** (ADR-004) | medium |
|
|
| OQ-TLS-04 | Accessor borrow-vs-consume | **resolved** (ADR-004) | medium |
|
|
| OQ-TLS-05 | Test surface | **resolved** (ADR-006) | low |
|
|
| OQ-TLS-06 | ACME task shutdown surface | resolved (detached-only for v1) | low |
|
|
| OQ-TLS-07 | iroh key surface | **resolved** (ADR-005, byte access pinned) | low |
|
|
| OQ-TLS-08 | `quinn` → `noq` feature rename | **resolved** (ADR-003) | high |
|
|
| OQ-TLS-09 | Server-path proof-of-possession | **resolved** (ADR-008) | high |
|
|
| OQ-TLS-10 | RFC 7250 over TCP: cert-type negotiation gap | **resolved** (ADR-007) | high |
|
|
|
|
## Identity & types
|
|
|
|
### OQ-TLS-01: Where do the config types live, and what moves?
|
|
|
|
- **Origin**: docs/research/phase-0.md §Gaps #3
|
|
- **Status**: resolved (2026-09-10)
|
|
- **Priority**: high
|
|
- **Resolution**: alktls owns the identity types (`TlsIdentity`,
|
|
`Ed25519SecretKey`, `AcmeDirectory`), the credential bundle
|
|
(`ConnectionCredentials`, `RemoteIdentity`), and the fingerprint
|
|
helpers. The auth layer (`PeerEntry`, `AuthPolicy`,
|
|
`IdentityProvider`) stays out — peer-level identity is not TLS.
|
|
Decision recorded in [ADR-005](decisions/005-config-types-move-into-alktls.md).
|
|
- **Cross-references**: ADR-001, ADR-005
|
|
|
|
### OQ-TLS-02: `SelfSigned` on the client path — encode or document?
|
|
|
|
- **Origin**: docs/research/phase-0.md §Gaps #4
|
|
- **Status**: resolved (2026-09-10)
|
|
- **Priority**: low
|
|
- **Resolution**: keep the current behavior (present nothing via
|
|
`NoClientCertResolver`) and document it on the identity type and in
|
|
the [client spec](client.md). Type-level enforcement (config error,
|
|
like `Acme`) was rejected: `SelfSigned` as a *local* identity
|
|
meaning "present nothing" is coherent — the dev cert exists for the
|
|
server side, and presenting a self-signed client cert would add
|
|
nothing the fingerprint path uses.
|
|
- **Consequences**: a future dev client-auth use case would need a
|
|
type-level change (additive, not a one-way door).
|
|
- **Cross-references**: ADR-001 (identity model), client spec
|
|
|
|
### OQ-TLS-07: iroh relationship in the rewrite
|
|
|
|
- **Origin**: docs/research/phase-0.md OQ-TLS-07
|
|
- **Status**: resolved (2026-09-10)
|
|
- **Priority**: low
|
|
- **Resolution**: the requirement is pinned and verified —
|
|
`Ed25519SecretKey` keeps 32-byte raw access (`from_bytes` /
|
|
`as_bytes`) with the same byte-level surface iroh's
|
|
`iroh_base::SecretKey` consumes (`from_bytes` / `to_bytes`; 32 raw
|
|
bytes in/out — verified against iroh 1.1 in Phase 0). iroh stays
|
|
key-not-config; no `for_iroh()`. The rewrite's iroh dial consumes
|
|
the type alktls owns (ADR-005); no alktls-side work remains.
|
|
- **Cross-references**: ADR-003, ADR-005
|
|
|
|
## API surface
|
|
|
|
### OQ-TLS-03: `for_tcp_tls()` — adopt the spec accessor?
|
|
|
|
- **Origin**: docs/research/phase-0.md §Gaps #2
|
|
- **Status**: resolved (2026-09-10)
|
|
- **Priority**: medium
|
|
- **Resolution**: adopted — `for_tcp_tls(&self) ->
|
|
tokio_rustls::TlsAcceptor`, feature-gated on `tcp`, infallible.
|
|
Decision and rationale in [ADR-004](decisions/004-accessor-surface.md).
|
|
- **Cross-references**: ADR-004
|
|
|
|
### OQ-TLS-04: `for_quinn()` — `self` or `&self`?
|
|
|
|
- **Origin**: docs/research/phase-0.md OQ-TLS-04
|
|
- **Status**: resolved (2026-09-10)
|
|
- **Priority**: medium
|
|
- **Resolution**: server accessors take `&self` (one config feeds N
|
|
transports — the ADR-082 story, directly expressible); client
|
|
accessors consume `self` (per-dial build, zero-cost
|
|
`into_rustls_config`). Full rationale in
|
|
[ADR-004](decisions/004-accessor-surface.md).
|
|
- **Cross-references**: ADR-003, ADR-004
|
|
|
|
### OQ-TLS-08: `quinn` feature vs `noq` feature — and the MSRV floor
|
|
|
|
- **Origin**: docs/research/phase-0.md §Prior art: noq
|
|
- **Status**: resolved (2026-09-10)
|
|
- **Priority**: high
|
|
- **Resolution**: the feature is `noq` (no `quinn` feature ever
|
|
published; rename is pre-consumer and free). noq 1.2 pinned
|
|
(`"1.2"`, default-features off, `rustls` feature), bump
|
|
deliberately. The MSRV half of the OQ was separately resolved the
|
|
same day (ecosystem floor 1.88; `time` pin dropped). Decision in
|
|
[ADR-003](decisions/003-noq-replaces-quinn.md).
|
|
- **Cross-references**: ADR-002, ADR-003, ADR-004
|
|
|
|
### OQ-TLS-09: Which layer owns proof-of-possession on the server path?
|
|
|
|
- **Origin**: docs/reviews/001-implementation-review.md §S-1 (the
|
|
impersonation probe: a handshake with the victim's cert bytes + an
|
|
attacker signer completes, and the server extracts the victim's
|
|
fingerprint)
|
|
- **Status**: resolved (2026-09-11) — option (b),
|
|
[ADR-008](decisions/008-server-path-possession-verification.md).
|
|
[`VerifyPresentedCertVerifier`](decisions/008-server-path-possession-verification.md)
|
|
is the default client-cert verifier on every `TlsServerConfig` path
|
|
(including the ACME path — the verifier install is crate-side
|
|
rustls, not rustls-acme's): request, don't require, **verify the
|
|
CertificateVerify against the presented cert's public key**. The
|
|
extracted fingerprint is possession-checked (attacker-supplied
|
|
presentations fail the handshake —
|
|
`tests/impersonation_posture.rs` pins both directions); who a
|
|
fingerprint maps to remains the auth layer's concern (ADR-005).
|
|
`AcceptAnyCertVerifier` stays public as the explicit no-pop escape
|
|
hatch, its posture pinned by the same test file.
|
|
- **Cross-references**: src/server.rs (`VerifyPresentedCertVerifier`,
|
|
`AcceptAnyCertVerifier`), src/client.rs (`FingerprintPinVerifier` —
|
|
the routing being mirrored), ADR-008, review 001 §S-1, alknet
|
|
ADR-034
|
|
|
|
### OQ-TLS-10: How do RFC 7250 raw-key peers negotiate over rustls-driven TCP+TLS?
|
|
|
|
- **Origin**: `tasks/handshake-tests.md` (review 001 §U-3 execution):
|
|
the executed handshake suites found that the crate's own pin client
|
|
cannot complete a handshake against the crate's own raw-key server,
|
|
and a raw-key client resolver cannot present itself to
|
|
`AcceptAnyCertVerifier` — both fail with rustls' HandshakeFailure
|
|
alert (cert-type negotiation), not a verification outcome. Verified
|
|
empirically and against the rustls 0.23.44 sources
|
|
(`server/hs.rs::process_cert_type_extension`,
|
|
`client/hs.rs::process_cert_type_extension`); pinned by
|
|
`tests/handshake_behavior.rs`.
|
|
- **Status**: resolved (2026-09-11) — by deviation from alknet,
|
|
[ADR-007](decisions/007-cert-type-negotiation.md). Option (b) plus a
|
|
resolver change: `FingerprintPinVerifier::requires_raw_public_keys()`
|
|
derives from the pin format (`ed25519:` → `true`, `SHA256:` →
|
|
`false`), and `RawKeyClientCertResolver` presents the SPKI under the
|
|
X.509 offer (`only_raw_public_keys() == false`). The gap was a bug in
|
|
the prior art (alknet's code never delivered its spec's raw-key-over-
|
|
TCP promise), not an alknet behavior to preserve — so the
|
|
behavior-preservation invariant does not cover it. The two pin
|
|
formats negotiate independently (per-connection verifier, never
|
|
shared); fail-closed is preserved and strengthened (an `ed25519:`
|
|
pin against an X.509 server aborts at negotiation, never a
|
|
downgrade). The original "defer until the first consumer" rationale
|
|
was circular — the crate is the component that must enable the
|
|
raw-key-over-TCP consumer, and the fix required no API change.
|
|
- **Cross-references**: src/client.rs (`FingerprintPinVerifier`,
|
|
`RawKeyClientCertResolver`), src/server.rs (`RawKeyCertResolver`,
|
|
`AcceptAnyCertVerifier`), review 001 §N-4, ADR-007,
|
|
iroh `iroh/src/tls/verifier.rs` (the working prior art)
|
|
|
|
## Quality / process
|
|
|
|
### OQ-TLS-05: Test surface for the invariants
|
|
|
|
- **Origin**: docs/research/phase-0.md §Gaps #5
|
|
- **Status**: resolved (2026-09-10)
|
|
- **Priority**: low
|
|
- **Resolution**: port the in-module seed tests AND add `tests/`
|
|
integration tests for the cross-module surfaces; pin the exact
|
|
nine-scheme list (regression-proof). Decision in
|
|
[ADR-006](decisions/006-module-layout-and-tests.md).
|
|
- **Cross-references**: ADR-006
|
|
|
|
### OQ-TLS-06: Does the ACME state machine need a shutdown surface?
|
|
|
|
- **Origin**: docs/research/phase-0.md OQ-TLS-06
|
|
- **Status**: resolved (2026-09-10)
|
|
- **Priority**: low
|
|
- **Resolution**: detached-only — the handle is stored (keeping
|
|
`TlsServerConfig` non-Clone) and never aborted; the ACME task runs
|
|
for the process lifetime. Documented on the type.
|
|
- **Consequences**: a `shutdown()` (abort + await) surface would be
|
|
additive and cheap if the rewrite's graceful-shutdown design wants
|
|
one later — not a one-way door.
|
|
- **Cross-references**: ADR-001 (one-ACME-machine rule),
|
|
[server.md](server.md)
|
|
|
|
## Deferred / Blocked
|
|
|
|
None — all recorded OQs are resolved. Future questions (e.g. the
|
|
raw-key-only server-verifier sibling if a foreign strict RFC 7250
|
|
server interop ever appears — see ADR-007 §Limits) get new OQ entries
|
|
rather than reopening these. |