crate-init: crate skeleton — module map, feature gates, TlsError, re-export block deferred per port
- src/lib.rs: crate docs, ADR-002 TlsError verbatim (six variants, #[non_exhaustive], typed #[source] chains), module declarations, compile-assertion tests for the enum + noq-gated NoqWrap - seven module files with doc headers only (ports land per task) - Cargo.toml verified against ADR-003 verbatim (no edits needed) - deviation: VerifierBuild source is rustls::client::VerifierBuilderError (rustls::webpki is private at pinned 0.23.44; same type, public path) Verification: cargo build/test (default, noq, tcp, acme, all-features), clippy -D warnings, fmt --check, doc --no-deps — all green
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
//! Client-side TLS configuration: [`TlsClientConfig`],
|
||||
//! [`FingerprintPinVerifier`], [`RawKeyClientCertResolver`],
|
||||
//! [`NoClientCertResolver`], [`select_server_verifier`],
|
||||
//! [`build_client_auth`], [`load_platform_root_cert_store`].
|
||||
@@ -0,0 +1,3 @@
|
||||
//! Transport-level credential bundle for outbound connections:
|
||||
//! [`ConnectionCredentials`], [`RemoteIdentity`] (ADR-005, moved from
|
||||
//! alknet-core `credentials.rs`).
|
||||
@@ -0,0 +1,10 @@
|
||||
//! TLS certificate fingerprint extraction: [`fingerprint_from_cert_der`],
|
||||
//! [`extract_ed25519_raw_key_from_spki`], and the private manual DER parser
|
||||
//! (ADR-005, moved from alknet-core).
|
||||
//!
|
||||
//! Fingerprint formats (alknet ADR-030 §6):
|
||||
//!
|
||||
//! - **Ed25519 raw key** (RFC 7250 SPKI): `ed25519:<hex of 32-byte pub key>`
|
||||
//! — the fingerprint IS the trust anchor.
|
||||
//! - **X.509 cert**: `SHA256:<hex of DER>` — the fallback for everything
|
||||
//! else.
|
||||
@@ -0,0 +1,2 @@
|
||||
//! Identity types: [`TlsIdentity`], [`Ed25519SecretKey`], [`AcmeDirectory`]
|
||||
//! (ADR-005, moved from alknet-core `config.rs`).
|
||||
+90
-14
@@ -1,27 +1,103 @@
|
||||
//! alktls: shared TLS setup types — server and client rustls configs,
|
||||
//! cert resolvers, verifiers, and shared signing helpers.
|
||||
//! alktls: shared TLS setup types — server and client `rustls` configs,
|
||||
//! cert resolvers, verifiers, ACME state-machine wiring, and the identity /
|
||||
//! credential / fingerprint types that drive config construction.
|
||||
//!
|
||||
//! Phase 0 (exploration) — the crate shape is being established in
|
||||
//! `docs/research/phase-0.md`. This placeholder keeps the scaffold
|
||||
//! building until the crate skeleton lands in Phase 1.
|
||||
//! The crate owns **config construction**: given an identity and an ALPN
|
||||
//! list, produce a `rustls::ServerConfig` or `rustls::ClientConfig` and hand
|
||||
//! it to whichever transport wrapper the deployment runs (`noq` for QUIC,
|
||||
//! `tokio-rustls` for TCP+TLS). It does not dial, accept, dispatch, or
|
||||
//! resolve peer identities — those belong to the dial seam, the accept
|
||||
//! loop, and the auth layer.
|
||||
//!
|
||||
//! Core types:
|
||||
//!
|
||||
//! - [`TlsServerConfig`] — built once per identity + ALPN list, shared
|
||||
//! across transports via [`Arc`](std::sync::Arc) (not `Clone`; it holds
|
||||
//! the ACME task's `JoinHandle`).
|
||||
//! - [`TlsClientConfig`] — built per dial from a [`ConnectionCredentials`] +
|
||||
//! ALPN; consumed by its accessors.
|
||||
//! - [`TlsError`] — the config-construction error type. Handshake outcomes
|
||||
//! and ACME runtime errors are not `TlsError`s.
|
||||
//!
|
||||
//! Transport-specific accessors are feature-gated: `noq` (`for_noq`),
|
||||
//! `tcp` (`for_tcp_tls`), `acme` (the ACME path). `default = []`.
|
||||
|
||||
pub mod client;
|
||||
pub mod credentials;
|
||||
pub mod fingerprint;
|
||||
pub mod identity;
|
||||
pub mod pem;
|
||||
pub mod server;
|
||||
pub mod signing;
|
||||
|
||||
// The re-export block (the documented public API surface) lands per
|
||||
// module as each port task completes; the final shape is ADR-006's
|
||||
// module map + overview.md §What the crate is.
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum TlsError {
|
||||
/// Cert or key file read / PEM parse. `rustls_pemfile` funnels its
|
||||
/// own error type into `io::Error`, so one `io::Error` source covers
|
||||
/// the whole loading path.
|
||||
#[error("loading cert/key material: {0}")]
|
||||
CertLoad(#[from] std::io::Error),
|
||||
|
||||
/// Self-signed cert generation (rcgen). Server `SelfSigned` path.
|
||||
#[error("generating self-signed cert: {0}")]
|
||||
SelfSigned(#[from] rcgen::Error),
|
||||
|
||||
/// rustls server or client config construction
|
||||
/// (`with_safe_default_protocol_versions`, `with_single_cert`,
|
||||
/// `CertifiedKey::from_der`, `RootCertStore::add`).
|
||||
#[error("building rustls config: {0}")]
|
||||
Rustls(#[from] rustls::Error),
|
||||
|
||||
/// `WebPkiServerVerifier::builder(_with_provider)..build()` — the
|
||||
/// unknown-X.509-remote client path. Re-exported by rustls at
|
||||
/// `rustls::client` (the `rustls::webpki` module is private at the
|
||||
/// pinned 0.23.44; same type, public path).
|
||||
#[error("building webpki verifier: {0}")]
|
||||
VerifierBuild(#[from] rustls::client::VerifierBuilderError),
|
||||
|
||||
/// QUIC config wrapping — the one path where `for_noq()` fails
|
||||
/// (`NoInitialCipherSuite`, not a `rustls::Error`). noq-gated.
|
||||
#[cfg(feature = "noq")]
|
||||
#[error("wrapping rustls config for noq: {0}")]
|
||||
NoqWrap(#[from] noq_proto::crypto::rustls::NoInitialCipherSuite),
|
||||
|
||||
/// Config-mismatch errors that are not wrapped third-party
|
||||
/// errors: ACME feature not enabled but `Acme` configured
|
||||
/// (server), or `Acme` identity used for client auth. A config
|
||||
/// error, not a wrapped third-party error.
|
||||
#[error("TLS config error: {0}")]
|
||||
Config(String),
|
||||
#[error("I/O error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("certificate error: {0}")]
|
||||
Cert(String),
|
||||
AcmeConfig(String),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::TlsError;
|
||||
|
||||
#[test]
|
||||
fn error_type_constructs() {
|
||||
let e = TlsError::Config("placeholder".into());
|
||||
assert!(e.to_string().contains("placeholder"));
|
||||
fn tls_error_matches_the_adr_002_variant_set() {
|
||||
let io: TlsError = std::io::Error::other("x").into();
|
||||
assert!(matches!(io, TlsError::CertLoad(_)));
|
||||
let rcgen: TlsError = rcgen::Error::CouldNotParseCertificate.into();
|
||||
assert!(matches!(rcgen, TlsError::SelfSigned(_)));
|
||||
let rustls: TlsError = rustls::Error::General("x".into()).into();
|
||||
assert!(matches!(rustls, TlsError::Rustls(_)));
|
||||
let builder: TlsError = rustls::client::VerifierBuilderError::NoRootAnchors.into();
|
||||
assert!(matches!(builder, TlsError::VerifierBuild(_)));
|
||||
let acme = TlsError::AcmeConfig("acme feature not enabled".into());
|
||||
assert!(acme.to_string().contains("acme feature not enabled"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "noq")]
|
||||
#[test]
|
||||
fn tls_error_noq_wrap_variant_exists() {
|
||||
let _ = matches!(
|
||||
Option::<TlsError>::None,
|
||||
Some(TlsError::NoqWrap(_)) | Some(_)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
//! PEM loading helpers: [`load_cert_chain`], [`load_private_key`].
|
||||
//! One copy used by both server and client.
|
||||
@@ -0,0 +1,3 @@
|
||||
//! Server-side TLS configuration: [`TlsServerConfig`],
|
||||
//! [`RawKeyCertResolver`], [`AcceptAnyCertVerifier`], [`SelfSignedCert`],
|
||||
//! [`generate_self_signed_cert`], and the ACME path (feature `acme`).
|
||||
@@ -0,0 +1,3 @@
|
||||
//! Ed25519 signing key usable as both a rustls `SigningKey` and `Signer`:
|
||||
//! [`Ed25519SigningKey`]. One copy used by both server
|
||||
//! (`RawKeyCertResolver`) and client (`RawKeyClientCertResolver`).
|
||||
+54
-2
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: crate-init
|
||||
name: Crate skeleton — module files, feature gates, error type, re-exports
|
||||
status: pending
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
@@ -77,7 +77,59 @@ land now so subsequent tasks are additive, not structural.
|
||||
> Agent fills this during implementation. Document any decisions,
|
||||
> deviations from architecture, or relevant context discovered.
|
||||
|
||||
### Decisions / deviations
|
||||
|
||||
1. **`VerifierBuild` source path: `rustls::client::VerifierBuilderError`,
|
||||
not `rustls::webpki::VerifierBuilderError`.** The ADR-002 sketch used
|
||||
the `rustls::webpki` path, but at the pinned rustls 0.23.44 that
|
||||
module is private — the type is re-exported publicly at
|
||||
`rustls::client` (verified in the 0.23.44 source: `mod webpki;` +
|
||||
`pub use crate::webpki::{VerifierBuilderError, ...}` inside
|
||||
`pub mod client`). Same type, public path; noted in the variant's
|
||||
doc comment. ADR-002 needs a one-line amendment (recorded for the
|
||||
review-impl docs-sync pass).
|
||||
2. **Re-export block deferred per module.** `lib.rs` carries the module
|
||||
declarations now; the `pub use` block lands incrementally with each
|
||||
port task (a re-export of a not-yet-ported type cannot compile, and
|
||||
every intermediate commit must stay green). The final shape is
|
||||
ADR-006's map + overview.md's surface; port-client (the last module
|
||||
port) completes it.
|
||||
3. **The `noq`-gated compile assertion** cannot construct
|
||||
`NoInitialCipherSuite` from outside noq-proto (private field), so the
|
||||
test is an existence proof via `matches!` on `Option<TlsError>` —
|
||||
compiles only when the variant + its `#[from]` source exist.
|
||||
4. **`Cargo.toml` unchanged** — the scaffold's TOML already matched
|
||||
ADR-003's block exactly (features `default = []` / `noq` / `tcp` /
|
||||
`acme`; `noq` with `default-features = false, features = ["rustls"]`).
|
||||
Verified against ADR-003 line-by-line; no edits needed.
|
||||
|
||||
## Summary
|
||||
|
||||
> Agent fills this on completion. Brief description of what was
|
||||
> implemented, files changed, and any follow-up needed.
|
||||
> implemented, files changed, and any follow-up needed.
|
||||
|
||||
### What landed
|
||||
|
||||
- `src/lib.rs`: crate docs + the ADR-002 `TlsError` verbatim (six
|
||||
variants, `#[non_exhaustive]`, `#[source]` chains; `NoqWrap`
|
||||
noq-gated) + module declarations + a compile-assertion test for the
|
||||
enum (`#[from]` conversions + `AcmeConfig` display) and the noq-gated
|
||||
`NoqWrap` existence test.
|
||||
- The seven module files (`identity`, `credentials`, `fingerprint`,
|
||||
`server`, `client`, `pem`, `signing`) with `//!` doc headers only —
|
||||
no bodies, per "what does NOT land here".
|
||||
- Feature matrix verified: default, `noq`, `tcp`, `acme`,
|
||||
`--all-features`.
|
||||
|
||||
### Verification
|
||||
|
||||
- `cargo build` (default) ✓; `cargo test` ✓ (1 test);
|
||||
`cargo test --features noq` ✓ (2 tests); `cargo check --features
|
||||
tcp|acme` ✓; `cargo test --all-features` ✓; `cargo clippy
|
||||
--all-targets --all-features -- -D warnings` ✓; `cargo fmt --check` ✓;
|
||||
`cargo doc --no-deps` ✓.
|
||||
|
||||
### Follow-up
|
||||
|
||||
- ADR-002 amendment note for the `VerifierBuilderError` path
|
||||
(review-impl docs sync).
|
||||
Reference in New Issue
Block a user