Phase 0 (core/connection-credentials): purely additive — add ConnectionCredentials + RemoteIdentity to alknet-core. No call crate changes. ~40 lines, zero breakage. Phase 1 (tls/*): greenfield alknet-tls crate in 5 tasks: - tls/crate-init: Cargo.toml, deps, module skeleton - tls/server-extract: TlsServerConfig + server TLS code from endpoint.rs - tls/client-extract: TlsClientConfig + client TLS code from call_client.rs - tls/tests: 32 TLS tests moved and adapted - tls/review-tls: phase gate review checkpoint All old code stays duplicated — purely additive phases. Prunes in 4-5.
5.9 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|
| tls/client-extract | Extract client-side TLS code from alknet-call/call_client.rs into alknet-tls | pending |
|
narrow | medium | component | implementation |
Description
Phase 1, Task 3 of the crate extraction. Extract the client-side TLS setup code from
crates/alknet-call/src/client/call_client.rs (lines 189-320) into
crates/alknet-tls/src/client.rs. Reuse the shared Ed25519SigningKey from
signing.rs and load_cert_chain/load_private_key from pem.rs (already extracted
in the previous task).
The old code stays in call_client.rs (duplicated) — no breakage. The new crate is
self-contained and builds standalone.
Types to extract
From call_client.rs lines 189-320:
| Type/Function | Lines | Destination |
|---|---|---|
build_quinn_client_config() |
189-211 | client.rs |
build_client_auth() |
213-246 | client.rs |
select_server_verifier() |
248-278 | client.rs |
load_platform_root_cert_store() |
280-297 | client.rs |
load_cert_chain() |
299-308 | skip — already in pem.rs from server-extract |
load_private_key() |
310-321 | skip — already in pem.rs from server-extract |
Also extract from call_client.rs lines 323-567 (the struct impls):
| Type/Function | Lines | Destination |
|---|---|---|
RawKeyClientCertResolver struct + impls |
323-374 | client.rs |
NoClientCertResolver struct + impls |
376-403 | client.rs |
FingerprintPinVerifier struct + impls |
405-507 | client.rs |
Ed25519SigningKey struct + impls |
509-567 | skip — already in signing.rs from server-extract |
New public API type
Wrap the extracted client-side code in a public API type:
// client.rs
/// Client-side TLS configuration, transport-agnostic.
/// Wraps a `rustls::ClientConfig` built from `ConnectionCredentials`.
pub struct TlsClientConfig {
pub(crate) rustls_config: rustls::ClientConfig,
}
impl TlsClientConfig {
/// Build a client config from `ConnectionCredentials` and an ALPN.
/// Selects the server cert verifier by `remote_identity` presence
/// (ADR-034 §3): `Some` → fingerprint pin, `None` → CA verification.
pub fn new(
credentials: &alknet_core::credentials::ConnectionCredentials,
alpn: &[u8],
) -> Result<Self, TlsError> { ... }
/// Convert to a `quinn::ClientConfig` for QUIC transport.
#[cfg(feature = "quinn")]
pub fn for_quinn(self) -> Result<quinn::ClientConfig, TlsError> { ... }
}
Adaptations
- Error types: Replace
Stringerror returns withTlsError. The current code returnsResult<_, String>from most functions — convert toResult<_, TlsError>. - Imports: Update
alknet_core::config::*andalknet_core::fingerprint::*imports. Usecrate::signing::Ed25519SigningKey(not a local copy). Usecrate::pem::load_cert_chain/crate::pem::load_private_key(not local copies). load_platform_root_cert_store: Add thewebpki-rootsfallback (ADR-088 §5) — when the platform store is empty, merge built-inwebpki-rootssoNoRootAnchorsis unreachable in practice. This is new code, not extracted.FingerprintPinVerifier: Theverify_tls12_signatureandverify_tls13_signaturemethods usealknet_core::fingerprint::extract_ed25519_raw_key_from_spki— keep that import.- Feature gates: All client-side TLS code is gated on
#[cfg(feature = "quinn")]. TheTlsClientConfig::new()constructor itself is not feature-gated (it builds arustls::ClientConfig, which is transport-agnostic). Onlyfor_quinn()is gated.
What stays in call
The old code in call_client.rs lines 189-567 is not deleted — it stays as a duplicate.
The prune happens in Phase 5. This task only adds code to alknet-tls.
Acceptance Criteria
crates/alknet-tls/src/client.rscontainsTlsClientConfig,build_quinn_client_config(asTlsClientConfig::new),build_client_auth,select_server_verifier,load_platform_root_cert_store,FingerprintPinVerifier,RawKeyClientCertResolver,NoClientCertResolverTlsClientConfig::new()accepts&ConnectionCredentials+&[u8]and returnsResult<Self, TlsError>TlsClientConfig::for_quinn()converts toquinn::ClientConfig(feature-gated)load_platform_root_cert_storeincludeswebpki-rootsfallback (ADR-088 §5)- Client code uses
crate::signing::Ed25519SigningKey(not a local copy) - Client code uses
crate::pem::load_cert_chain/crate::pem::load_private_key(not local copies) - All error returns use
TlsError(notString) - Feature gates correct:
quinnforfor_quinn()and quinn-specific helpers cargo check -p alknet-tlssucceeds (all feature combos)cargo clippy -p alknet-tlssucceeds with no warningscargo test -p alknet-corestill passes (old code untouched)cargo test -p alknet-callstill passes (old code untouched)
References
- docs/research/alknet-crate-extraction/findings.md — Phase 1, client-side extraction
- docs/architecture/decisions/088-webpki-roots-fallback.md — ADR-088 §5
- docs/architecture/decisions/034-outgoing-only-x509-and-three-peer-roles.md — ADR-034 §3
- crates/alknet-call/src/client/call_client.rs — lines 189-567 (source code to extract)
- crates/alknet-core/src/fingerprint.rs —
extract_ed25519_raw_key_from_spki,fingerprint_from_cert_der
Notes
This is the smaller extraction (~130 lines of implementation). The main work is adapting error types (String → TlsError) and reusing the shared
Ed25519SigningKeyandload_cert_chain/load_private_keyfrom the server-extract task. Thewebpki-rootsfallback inload_platform_root_cert_storeis new code (not extracted) per ADR-088 §5. The old code incall_client.rsis NOT deleted — that's Phase 5.
Summary
To be filled on completion