//! The invariant pins, promoted to integration level (ADR-006: the //! regression-proof shapes): //! //! 1. The nine-scheme exact-list pin — exact vec equality, not membership //! (the shape the in-module seed test must not regress from). //! 2. The verifier-selection matrix + client-auth presentation matrix, //! exercised through the public API only. //! 3. The root-store fallback — the store is never empty. //! 4. The server `max_early_data_size` + request-but-don't-require shape. //! //! Fail-closed for unknown raw-key remotes is structural (the CA verifier //! is what `None` installs; a raw-key remote cannot satisfy it — the //! failure manifests at handshake, never via `TlsError`): the matrix //! asserts `None` installs `WebPkiServerVerifier`, which is the //! fail-closed structure. The verifier inside `rustls::ClientConfig` / //! `rustls::ServerConfig` is `pub(super)` at rustls 0.23.44, so the //! verifier identity is probed through the config's derived Debug output //! (the verifier's type name appears in it) and the helpers' own //! construction results. use alktls::{ build_client_auth, build_rustls_server_config, load_platform_root_cert_store, select_server_verifier, AcceptAnyCertVerifier, ConnectionCredentials, Ed25519SecretKey, RemoteIdentity, TlsClientConfig, TlsIdentity, }; fn provider() -> std::sync::Arc { std::sync::Arc::new(rustls::crypto::aws_lc_rs::default_provider()) } #[test] fn nine_schemes_exact_list_pin() { use rustls::server::danger::ClientCertVerifier; let schemes = AcceptAnyCertVerifier.supported_verify_schemes(); assert_eq!( schemes, vec![ rustls::SignatureScheme::ED25519, rustls::SignatureScheme::ECDSA_NISTP256_SHA256, rustls::SignatureScheme::ECDSA_NISTP384_SHA384, rustls::SignatureScheme::RSA_PSS_SHA256, rustls::SignatureScheme::RSA_PSS_SHA384, rustls::SignatureScheme::RSA_PSS_SHA512, rustls::SignatureScheme::RSA_PKCS1_SHA256, rustls::SignatureScheme::RSA_PKCS1_SHA384, rustls::SignatureScheme::RSA_PKCS1_SHA512, ], "the nine-scheme list is load-bearing (server.md) — exact equality, order included" ); } #[test] fn verifier_selection_matrix_full_construction() { let known_peer = ConnectionCredentials::new() .with_local_identity(TlsIdentity::RawKey(Ed25519SecretKey::generate())) .with_remote_identity(RemoteIdentity { fingerprint: "ed25519:aa".to_string(), }); let config = TlsClientConfig::new(&known_peer, b"alk/call") .expect("Some(remote_identity) cell must construct"); assert!( format!("{:?}", config.into_rustls_config()).contains("FingerprintPinVerifier"), "Some(fingerprint) must install FingerprintPinVerifier" ); let unknown_x509 = ConnectionCredentials::new(); let config = TlsClientConfig::new(&unknown_x509, b"alk/call") .expect("None(remote_identity) cell must construct (the public-X.509 state)"); assert!( format!("{:?}", config.into_rustls_config()).contains("WebPkiServerVerifier"), "None must install WebPkiServerVerifier — the CA verifier. Fail-closed is structural: \ an unknown raw-key remote cannot satisfy it and fails at handshake, never via TlsError" ); } #[test] fn client_auth_presentation_matrix_full_construction() { let dir = tempfile::tempdir().expect("tempdir"); let key_pair = rcgen::KeyPair::generate().expect("key gen"); let cert = rcgen::CertificateParams::default() .self_signed(&key_pair) .expect("self-signed cert"); let cert_path = dir.path().join("cert.pem"); let key_path = dir.path().join("key.pem"); std::fs::write(&cert_path, cert.pem()).expect("write cert"); std::fs::write(&key_path, key_pair.serialize_pem()).expect("write key"); let cells: Vec<(ConnectionCredentials, &str)> = vec![ ( ConnectionCredentials::new() .with_local_identity(TlsIdentity::RawKey(Ed25519SecretKey::generate())), "raw-key", ), ( ConnectionCredentials::new().with_local_identity(TlsIdentity::X509 { cert: cert_path, key: key_path, }), "x509", ), ( ConnectionCredentials::new().with_local_identity(TlsIdentity::SelfSigned), "self-signed", ), (ConnectionCredentials::new(), "none"), ]; for (credentials, label) in &cells { let config = TlsClientConfig::new(credentials, b"alk/call") .unwrap_or_else(|e| panic!("{label} cell must construct: {e}")); let resolver = &config.into_rustls_config().client_auth_cert_resolver; match *label { "raw-key" => assert!( resolver.has_certs() && !resolver.only_raw_public_keys(), "RawKey presents its SPKI under the X.509 offer (ADR-007: the \ extension is an offer format, not an identity statement)" ), "x509" => assert!( resolver.has_certs() && !resolver.only_raw_public_keys(), "X509 must present the loaded chain (not raw public keys)" ), _ => assert!( !resolver.has_certs(), "{label} presents nothing (NoClientCertResolver)" ), } } let acme = ConnectionCredentials::new().with_local_identity(TlsIdentity::Acme { domains: vec!["example.com".to_string()], cache_dir: dir.path().to_path_buf(), directory: alktls::AcmeDirectory::Production, contact: vec![], }); match TlsClientConfig::new(&acme, b"alk/call") { Ok(_) => panic!("Acme is a server-only identity — client path must be a config error"), Err(e) => assert!( matches!(e, alktls::TlsError::AcmeConfig(_)), "Acme on the client path must surface TlsError::AcmeConfig, got {e:?}" ), } } #[test] fn root_store_fallback_is_never_empty() { let roots = load_platform_root_cert_store().expect("root store must load"); assert!( !roots.is_empty(), "the webpki-roots fallback guarantees a non-empty store even when the platform store is \ empty (alknet ADR-088 §5): got {} anchors", roots.roots.len() ); } #[test] fn selection_and_auth_helpers_agree_with_the_full_config() { let sk = Ed25519SecretKey::generate(); let local = Some(TlsIdentity::RawKey(sk.clone())); let resolver = build_client_auth(&provider(), &local).expect("raw-key resolver builds"); assert!( resolver.has_certs() && !resolver.only_raw_public_keys(), "the raw-key resolver presents its SPKI under the X.509 offer (ADR-007)" ); let remote = Some(RemoteIdentity { fingerprint: "ed25519:aa".to_string(), }); let verifier = select_server_verifier(&provider(), &remote).expect("pin verifier selects"); assert!(format!("{verifier:?}").contains("FingerprintPinVerifier")); let _ = sk; } #[test] fn server_paths_carry_max_early_data_and_alpn() { let alpns = vec![b"alk/call".to_vec()]; for identity in [ TlsIdentity::RawKey(Ed25519SecretKey::generate()), TlsIdentity::SelfSigned, ] { let config = build_rustls_server_config(&identity, &alpns) .expect("server config builds per variant"); assert_eq!( config.max_early_data_size, u32::MAX, "max_early_data_size must be u32::MAX on every server path (the 0-RTT invariant)" ); assert_eq!(config.alpn_protocols, alpns); assert!( !format!("{config:?}").is_empty(), "the config carries its verifier through the derived Debug (request-but-don't-require \ shape asserted at unit level on the verifiers themselves)" ); } }