use std::sync::Arc;
#[cfg(feature = "acme")]
use tracing::{debug, error, warn};
#[cfg(feature = "acme")]
use crate::identity::AcmeDirectory;
use crate::identity::{Ed25519SecretKey, TlsIdentity};
use crate::signing::Ed25519SigningKey;
use crate::fingerprint::extract_ed25519_raw_key_from_spki;
use crate::TlsError;
#[allow(dead_code)]
pub struct TlsServerConfig {
pub(crate) rustls_config: rustls::ServerConfig,
#[cfg(feature = "acme")]
pub(crate) acme_handle: Option<tokio::task::JoinHandle<()>>,
}
impl TlsServerConfig {
pub async fn new(tls_identity: &TlsIdentity, alpns: &[Vec<u8>]) -> Result<Self, TlsError> {
match tls_identity {
TlsIdentity::Acme {
domains,
cache_dir,
directory,
contact,
} => {
#[cfg(feature = "acme")]
{
Self::new_acme(domains, cache_dir, directory, contact, alpns).await
}
#[cfg(not(feature = "acme"))]
{
let _ = (domains, cache_dir, directory, contact, alpns);
Err(TlsError::AcmeConfig(
"ACME feature not enabled but TlsIdentity::Acme configured".to_string(),
))
}
}
_ => {
let server_config = build_rustls_server_config(tls_identity, alpns)?;
Ok(Self {
rustls_config: server_config,
#[cfg(feature = "acme")]
acme_handle: None,
})
}
}
}
#[cfg(feature = "acme")]
async fn new_acme(
domains: &[String],
cache_dir: &std::path::Path,
directory: &AcmeDirectory,
contact: &[String],
alpns: &[Vec<u8>],
) -> Result<Self, TlsError> {
use rustls_acme::caches::DirCache;
use rustls_acme::{AcmeConfig, EventError, EventOk};
if domains.is_empty() {
return Err(TlsError::AcmeConfig(
"TlsIdentity::Acme requires a non-empty domain list".to_string(),
));
}
let acme_config = AcmeConfig::new(domains.to_vec())
.cache(DirCache::new(cache_dir.to_path_buf()))
.directory(directory.url())
.contact(contact.iter().map(|c| c.as_str()));
let state = acme_config.state();
let resolver = state.resolver();
let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
let mut config = rustls::ServerConfig::builder_with_provider(provider.clone())
.with_safe_default_protocol_versions()?
.with_client_cert_verifier(Arc::new(VerifyPresentedCertVerifier::new(&provider)))
.with_cert_resolver(resolver);
config.max_early_data_size = u32::MAX;
let mut alpn = alpns.to_vec();
if !alpn.contains(&b"acme-tls/1".to_vec()) {
alpn.push(b"acme-tls/1".to_vec());
}
config.alpn_protocols = alpn;
let domains_owned: Vec<String> = domains.to_vec();
let handle = tokio::spawn(async move {
use futures::StreamExt;
let mut state = state;
while let Some(event) = state.next().await {
match event {
Ok(EventOk::DeployedCachedCert) => {
debug!(domains = ?domains_owned, "ACME: deployed cached certificate");
}
Ok(EventOk::DeployedNewCert) => {
debug!(domains = ?domains_owned, "ACME: deployed new certificate");
}
Ok(EventOk::CertCacheStore) => {
debug!(domains = ?domains_owned, "ACME: certificate stored to cache");
}
Ok(EventOk::AccountCacheStore) => {
debug!(domains = ?domains_owned, "ACME: account stored to cache");
}
Err(EventError::CertCacheLoad(e)) => {
error!(domains = ?domains_owned, error = ?e, "ACME: certificate cache load failed");
}
Err(EventError::AccountCacheLoad(e)) => {
error!(domains = ?domains_owned, error = ?e, "ACME: account cache load failed");
}
Err(EventError::CertCacheStore(e)) => {
warn!(domains = ?domains_owned, error = ?e, "ACME: certificate cache store failed");
}
Err(EventError::AccountCacheStore(e)) => {
warn!(domains = ?domains_owned, error = ?e, "ACME: account cache store failed");
}
Err(EventError::CachedCertParse(e)) => {
error!(domains = ?domains_owned, error = ?e, "ACME: cached certificate parse failed");
}
Err(EventError::Order(e)) => {
warn!(domains = ?domains_owned, error = ?e, "ACME: certificate order failed, will retry");
}
Err(EventError::NewCertParse(e)) => {
error!(domains = ?domains_owned, error = ?e, "ACME: new certificate parse failed");
}
}
}
});
Ok(Self {
rustls_config: config,
acme_handle: Some(handle),
})
}
#[cfg(feature = "noq")]
pub fn for_noq(&self) -> Result<noq::ServerConfig, TlsError> {
use noq::crypto::rustls::QuicServerConfig;
let quic_server_config = QuicServerConfig::try_from(self.rustls_config.clone())?;
Ok(noq::ServerConfig::with_crypto(Arc::new(quic_server_config)))
}
#[cfg(feature = "tcp")]
pub fn for_tcp_tls(&self) -> tokio_rustls::TlsAcceptor {
tokio_rustls::TlsAcceptor::from(Arc::new(self.rustls_config.clone()))
}
pub fn rustls_config(&self) -> &rustls::ServerConfig {
&self.rustls_config
}
}
pub fn build_rustls_server_config(
tls_identity: &TlsIdentity,
alpns: &[Vec<u8>],
) -> Result<rustls::ServerConfig, TlsError> {
let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
let client_verifier: Arc<dyn rustls::server::danger::ClientCertVerifier> =
Arc::new(VerifyPresentedCertVerifier::new(&provider));
match tls_identity {
TlsIdentity::X509 { cert, key } => {
let cert_chain = crate::pem::load_cert_chain(cert)?;
let private_key = crate::pem::load_private_key(key)?;
let mut config = rustls::ServerConfig::builder_with_provider(provider)
.with_safe_default_protocol_versions()?
.with_client_cert_verifier(client_verifier)
.with_single_cert(cert_chain, private_key)?;
config.alpn_protocols = alpns.to_vec();
config.max_early_data_size = u32::MAX;
Ok(config)
}
TlsIdentity::RawKey(secret_key) => {
let resolver = Arc::new(RawKeyCertResolver::new(secret_key));
let mut config = rustls::ServerConfig::builder_with_provider(provider)
.with_safe_default_protocol_versions()?
.with_client_cert_verifier(client_verifier)
.with_cert_resolver(resolver);
config.alpn_protocols = alpns.to_vec();
config.max_early_data_size = u32::MAX;
Ok(config)
}
TlsIdentity::SelfSigned => {
let cert = generate_self_signed_cert()?;
let mut config = rustls::ServerConfig::builder_with_provider(provider)
.with_safe_default_protocol_versions()?
.with_client_cert_verifier(client_verifier)
.with_single_cert(cert.cert_chain, cert.private_key)?;
config.alpn_protocols = alpns.to_vec();
config.max_early_data_size = u32::MAX;
Ok(config)
}
TlsIdentity::Acme { .. } => Err(TlsError::AcmeConfig(
"TlsIdentity::Acme is handled by TlsServerConfig::new_acme, not \
build_rustls_server_config"
.to_string(),
)),
}
}
pub struct SelfSignedCert {
pub cert_chain: Vec<rustls::pki_types::CertificateDer<'static>>,
pub private_key: rustls::pki_types::PrivateKeyDer<'static>,
}
pub fn generate_self_signed_cert() -> Result<SelfSignedCert, TlsError> {
use rcgen::{CertificateParams, KeyPair};
let key_pair = KeyPair::generate()?;
let params = CertificateParams::default();
let cert = params.self_signed(&key_pair)?;
let cert_der = cert.der().clone();
let key_der = rustls::pki_types::PrivateKeyDer::Pkcs8(
rustls::pki_types::PrivatePkcs8KeyDer::from(key_pair.serialize_der()),
);
Ok(SelfSignedCert {
cert_chain: vec![cert_der],
private_key: key_der,
})
}
pub struct VerifyPresentedCertVerifier {
supported: rustls::crypto::WebPkiSupportedAlgorithms,
}
impl VerifyPresentedCertVerifier {
pub fn new(provider: &Arc<rustls::crypto::CryptoProvider>) -> Self {
Self {
supported: provider.signature_verification_algorithms,
}
}
}
impl std::fmt::Debug for VerifyPresentedCertVerifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VerifyPresentedCertVerifier").finish()
}
}
impl rustls::server::danger::ClientCertVerifier for VerifyPresentedCertVerifier {
fn offer_client_auth(&self) -> bool {
true
}
fn client_auth_mandatory(&self) -> bool {
false
}
fn root_hint_subjects(&self) -> &[rustls::DistinguishedName] {
&[]
}
fn verify_client_cert(
&self,
_end_entity: &rustls::pki_types::CertificateDer<'_>,
_intermediates: &[rustls::pki_types::CertificateDer<'_>],
_now: rustls::pki_types::UnixTime,
) -> Result<rustls::server::danger::ClientCertVerified, rustls::Error> {
Ok(rustls::server::danger::ClientCertVerified::assertion())
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &rustls::pki_types::CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
if extract_ed25519_raw_key_from_spki(cert.as_ref()).is_some() {
let spki = rustls::pki_types::SubjectPublicKeyInfoDer::from(cert.as_ref().to_vec());
rustls::crypto::verify_tls13_signature_with_raw_key(
message,
&spki,
dss,
&self.supported,
)
} else {
rustls::crypto::verify_tls13_signature(message, cert, dss, &self.supported)
}
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &rustls::pki_types::CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
if extract_ed25519_raw_key_from_spki(cert.as_ref()).is_some() {
let spki = rustls::pki_types::SubjectPublicKeyInfoDer::from(cert.as_ref().to_vec());
rustls::crypto::verify_tls13_signature_with_raw_key(
message,
&spki,
dss,
&self.supported,
)
} else {
rustls::crypto::verify_tls12_signature(message, cert, dss, &self.supported)
}
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
nine_supported_verify_schemes()
}
}
fn nine_supported_verify_schemes() -> Vec<rustls::SignatureScheme> {
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,
]
}
pub struct AcceptAnyCertVerifier;
impl std::fmt::Debug for AcceptAnyCertVerifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AcceptAnyCertVerifier").finish()
}
}
impl rustls::server::danger::ClientCertVerifier for AcceptAnyCertVerifier {
fn offer_client_auth(&self) -> bool {
true
}
fn client_auth_mandatory(&self) -> bool {
false
}
fn root_hint_subjects(&self) -> &[rustls::DistinguishedName] {
&[]
}
fn verify_client_cert(
&self,
_end_entity: &rustls::pki_types::CertificateDer<'_>,
_intermediates: &[rustls::pki_types::CertificateDer<'_>],
_now: rustls::pki_types::UnixTime,
) -> Result<rustls::server::danger::ClientCertVerified, rustls::Error> {
Ok(rustls::server::danger::ClientCertVerified::assertion())
}
fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &rustls::pki_types::CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &rustls::pki_types::CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
nine_supported_verify_schemes()
}
}
pub struct RawKeyCertResolver {
key: Arc<rustls::sign::CertifiedKey>,
}
impl RawKeyCertResolver {
pub fn new(secret_key: &Ed25519SecretKey) -> Self {
let signing_key = Arc::new(Ed25519SigningKey::new(secret_key.clone()));
let public_key = signing_key.spki_public_key();
let cert = rustls::pki_types::CertificateDer::from(public_key.to_vec());
let certified_key = rustls::sign::CertifiedKey::new(vec![cert], signing_key);
Self {
key: Arc::new(certified_key),
}
}
}
impl rustls::server::ResolvesServerCert for RawKeyCertResolver {
fn resolve(
&self,
_client_hello: rustls::server::ClientHello<'_>,
) -> Option<Arc<rustls::sign::CertifiedKey>> {
Some(Arc::clone(&self.key))
}
fn only_raw_public_keys(&self) -> bool {
true
}
}
impl std::fmt::Debug for RawKeyCertResolver {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RawKeyCertResolver").finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn raw_key_cert_resolver_only_raw_public_keys() {
use rustls::server::ResolvesServerCert;
let sk = Ed25519SecretKey::generate();
let resolver = RawKeyCertResolver::new(&sk);
assert!(resolver.only_raw_public_keys());
}
#[test]
fn self_signed_cert_generation_produces_cert_and_key() {
let cert = generate_self_signed_cert().expect("self-signed cert generates");
assert!(!cert.cert_chain.is_empty());
assert!(!cert.private_key.secret_der().is_empty());
}
#[tokio::test]
async fn tls_setup_x509_returns_no_acme_state() {
use rcgen::{CertificateParams, KeyPair};
let key_pair = KeyPair::generate().unwrap();
let params = CertificateParams::default();
let cert = params.self_signed(&key_pair).unwrap();
let cert_pem = cert.pem();
let key_pem = key_pair.serialize_pem();
let dir = tempfile::tempdir().unwrap();
let cert_path = dir.path().join("cert.pem");
let key_path = dir.path().join("key.pem");
std::fs::write(&cert_path, cert_pem).unwrap();
std::fs::write(&key_path, key_pem).unwrap();
let tls_identity = TlsIdentity::X509 {
cert: cert_path,
key: key_path,
};
let setup = TlsServerConfig::new(&tls_identity, &[b"alktls/test".to_vec()])
.await
.expect("X509 tls setup should succeed");
let _ = setup.rustls_config;
#[cfg(feature = "acme")]
assert!(setup.acme_handle.is_none());
}
#[test]
fn build_rustls_server_config_raw_key_succeeds() {
let sk = Ed25519SecretKey::generate();
let identity = TlsIdentity::RawKey(sk);
let alpns = vec![b"alktls/test".to_vec(), b"alktls/call".to_vec()];
let config = build_rustls_server_config(&identity, &alpns).expect("raw key config builds");
assert_eq!(config.alpn_protocols, alpns);
assert_eq!(config.max_early_data_size, u32::MAX);
}
#[test]
fn build_rustls_server_config_self_signed_succeeds() {
let identity = TlsIdentity::SelfSigned;
let alpns = vec![b"alktls/test".to_vec()];
let config =
build_rustls_server_config(&identity, &alpns).expect("self-signed config builds");
assert_eq!(config.alpn_protocols, alpns);
assert_eq!(config.max_early_data_size, u32::MAX);
}
#[test]
fn build_rustls_server_config_acme_returns_config_error() {
let identity = TlsIdentity::Acme {
domains: vec!["example.com".to_string()],
cache_dir: std::path::PathBuf::from("/tmp/alktls-acme-test"),
directory: crate::identity::AcmeDirectory::Staging,
contact: vec!["mailto:dev@example.com".to_string()],
};
let err = build_rustls_server_config(&identity, &[])
.expect_err("Acme identity must not reach the plain builder");
assert!(
matches!(err, TlsError::AcmeConfig(_)),
"the defensive Acme arm must surface as TlsError::AcmeConfig, got {err:?}"
);
}
#[cfg(feature = "noq")]
#[test]
fn for_noq_round_trips_raw_key_config() {
let sk = Ed25519SecretKey::generate();
let rustls_config =
build_rustls_server_config(&TlsIdentity::RawKey(sk), &[b"alktls/test".to_vec()])
.expect("rustls config builds");
let config = TlsServerConfig {
rustls_config,
#[cfg(feature = "acme")]
acme_handle: None,
};
let noq_config = config.for_noq().expect("noq config converts");
let _ = noq_config;
}
#[test]
fn accept_any_cert_verifier_offers_and_does_not_require_client_auth() {
use rustls::server::danger::ClientCertVerifier;
let verifier = AcceptAnyCertVerifier;
assert!(verifier.offer_client_auth());
assert!(!verifier.client_auth_mandatory());
assert!(verifier.root_hint_subjects().is_empty());
}
fn dss_with_scheme(
scheme: rustls::SignatureScheme,
signature: Vec<u8>,
) -> rustls::DigitallySignedStruct {
use rustls::internal::msgs::codec::{Codec, Reader};
let mut encoded = Vec::new();
scheme.encode(&mut encoded);
(signature.len() as u16).encode(&mut encoded);
encoded.extend_from_slice(&signature);
rustls::DigitallySignedStruct::read(&mut Reader::init(&encoded))
.expect("DigitallySignedStruct decodes from its wire encoding")
}
#[test]
fn verify_presented_cert_verifier_tls12_signature_routes_ed25519_spki_through_raw_key_path() {
use rustls::server::danger::ClientCertVerifier;
let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
let verifier = VerifyPresentedCertVerifier::new(&provider);
let sk = Ed25519SecretKey::generate();
let raw_key = sk.public().to_bytes();
let spki_der =
rustls::sign::public_key_to_spki(&rustls::pki_types::alg_id::ED25519, raw_key).to_vec();
let message = b"alktls verify-presented tls12 raw-key routing";
let signature = sk.sign(message).to_bytes().to_vec();
let dss = dss_with_scheme(rustls::SignatureScheme::ED25519, signature);
let cert = rustls::pki_types::CertificateDer::from(spki_der);
let result = verifier.verify_tls12_signature(message, &cert, &dss);
assert!(
result.is_ok(),
"TLS 1.2 signature for an Ed25519 SPKI presentation must route \
through the raw-key path and verify, got: {result:?}"
);
let forged = dss_with_scheme(rustls::SignatureScheme::ED25519, vec![0u8; 64]);
let tampered = verifier.verify_tls12_signature(b"tampered", &cert, &forged);
assert!(
tampered.is_err(),
"a signature that does not verify must fail the possession check"
);
}
#[test]
fn verify_presented_cert_verifier_tls12_signature_routes_x509_through_standard_path() {
use rustls::server::danger::ClientCertVerifier;
let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
let verifier = VerifyPresentedCertVerifier::new(&provider);
let key_pair = rcgen::KeyPair::generate().expect("ECDSA P-256 key gen");
let cert = rcgen::CertificateParams::default()
.self_signed(&key_pair)
.expect("self-signed cert");
let cert_der = cert.der().clone();
let signing_key = rustls::crypto::aws_lc_rs::sign::any_ecdsa_type(
&rustls::pki_types::PrivateKeyDer::Pkcs8(rustls::pki_types::PrivatePkcs8KeyDer::from(
key_pair.serialize_der(),
)),
)
.expect("ECDSA signing key loads");
let message = b"alktls verify-presented tls12 x509 routing";
let signature = signing_key
.choose_scheme(&[rustls::SignatureScheme::ECDSA_NISTP256_SHA256])
.expect("ECDSA_NISTP256_SHA256 must be offered")
.sign(message)
.expect("signing must succeed");
let dss = dss_with_scheme(rustls::SignatureScheme::ECDSA_NISTP256_SHA256, signature);
let result = verifier.verify_tls12_signature(message, &cert_der, &dss);
assert!(
result.is_ok(),
"TLS 1.2 signature for an X.509 presentation must route through the \
standard verification path and verify, got: {result:?}"
);
let wrong_key = rcgen::KeyPair::generate().expect("other key gen");
let other_signing_key = rustls::crypto::aws_lc_rs::sign::any_ecdsa_type(
&rustls::pki_types::PrivateKeyDer::Pkcs8(rustls::pki_types::PrivatePkcs8KeyDer::from(
wrong_key.serialize_der(),
)),
)
.expect("other ECDSA signing key loads");
let wrong_signature = other_signing_key
.choose_scheme(&[rustls::SignatureScheme::ECDSA_NISTP256_SHA256])
.expect("ECDSA_NISTP256_SHA256 must be offered")
.sign(message)
.expect("signing must succeed");
let wrong_key_dss = dss_with_scheme(
rustls::SignatureScheme::ECDSA_NISTP256_SHA256,
wrong_signature,
);
let tampered = verifier.verify_tls12_signature(message, &cert_der, &wrong_key_dss);
assert!(
tampered.is_err(),
"a valid signature under a different key must fail the possession check"
);
}
#[test]
fn verify_presented_cert_verifier_tls12_signature_rejects_mismatched_message() {
use rustls::server::danger::ClientCertVerifier;
let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
let verifier = VerifyPresentedCertVerifier::new(&provider);
let sk = Ed25519SecretKey::generate();
let raw_key = sk.public().to_bytes();
let spki_der =
rustls::sign::public_key_to_spki(&rustls::pki_types::alg_id::ED25519, raw_key).to_vec();
let signature = sk.sign(b"the real message").to_bytes().to_vec();
let dss = dss_with_scheme(rustls::SignatureScheme::ED25519, signature);
let cert = rustls::pki_types::CertificateDer::from(spki_der);
let result = verifier.verify_tls12_signature(b"a different message", &cert, &dss);
assert!(
result.is_err(),
"a signature over a different message must fail the possession check"
);
}
#[test]
fn server_verifiers_keep_requires_raw_public_keys_default_false() {
use rustls::server::danger::ClientCertVerifier;
assert!(
!AcceptAnyCertVerifier.requires_raw_public_keys(),
"the request-not-require shape must accept both cert types; requires_raw_public_keys() == true would reject every X.509 client (ADR-007, review 001 N-4)"
);
let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
let verifier = VerifyPresentedCertVerifier::new(&provider);
assert!(
!verifier.requires_raw_public_keys(),
"the default verifier must keep the trait default; a raw-only demand is the interop boundary documented under ADR-007"
);
}
#[test]
fn accept_any_cert_verifier_verifies_any_client_cert() {
use rustls::pki_types::{CertificateDer, UnixTime};
use rustls::server::danger::ClientCertVerifier;
let verifier = AcceptAnyCertVerifier;
let cert = CertificateDer::from(b"fake-cert-der".to_vec());
let result = verifier.verify_client_cert(&cert, &[], UnixTime::now());
assert!(
result.is_ok(),
"AcceptAnyCertVerifier must accept any client cert"
);
}
#[test]
fn accept_any_cert_verifier_supported_schemes_are_the_nine_pinned() {
use rustls::server::danger::ClientCertVerifier;
let verifier = AcceptAnyCertVerifier;
let schemes = verifier.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,
]
);
}
#[test]
fn accept_any_cert_verifier_debug_is_implemented() {
let verifier = AcceptAnyCertVerifier;
let s = format!("{verifier:?}");
assert!(s.contains("AcceptAnyCertVerifier"));
}
#[test]
fn accept_any_cert_verifier_tls12_signature_asserts_without_possession_check() {
use rustls::server::danger::ClientCertVerifier;
let verifier = AcceptAnyCertVerifier;
let cert = rustls::pki_types::CertificateDer::from(b"not even a cert".to_vec());
let dss = dss_with_scheme(rustls::SignatureScheme::ED25519, vec![0u8; 64]);
let result = verifier.verify_tls12_signature(b"any message", &cert, &dss);
assert!(
result.is_ok(),
"the escape hatch must assert TLS 1.2 signature validity \
unconditionally (the documented no-pop posture), got: {result:?}"
);
}
#[test]
fn verify_presented_cert_verifier_supported_schemes_are_the_nine_pinned() {
use rustls::server::danger::ClientCertVerifier;
let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
let verifier = VerifyPresentedCertVerifier::new(&provider);
assert_eq!(
verifier.supported_verify_schemes(),
nine_supported_verify_schemes(),
"the default verifier must report the same nine-scheme list as the \
escape hatch (the load-bearing list)"
);
}
#[test]
fn raw_key_cert_resolver_debug_is_implemented() {
let sk = Ed25519SecretKey::generate();
let resolver = RawKeyCertResolver::new(&sk);
let s = format!("{resolver:?}");
assert!(s.contains("RawKeyCertResolver"));
}
#[cfg(feature = "tcp")]
#[tokio::test]
async fn new_x509_for_tcp_tls_and_rustls_config_round_trip() {
use rcgen::{CertificateParams, KeyPair};
let key_pair = KeyPair::generate().unwrap();
let cert = CertificateParams::default().self_signed(&key_pair).unwrap();
let dir = tempfile::tempdir().unwrap();
let cert_path = dir.path().join("cert.pem");
let key_path = dir.path().join("key.pem");
std::fs::write(&cert_path, cert.pem()).unwrap();
std::fs::write(&key_path, key_pair.serialize_pem()).unwrap();
let identity = TlsIdentity::X509 {
cert: cert_path,
key: key_path,
};
let alpn = vec![b"alktls/test".to_vec()];
let setup = TlsServerConfig::new(&identity, &alpn)
.await
.expect("X509 config builds");
let rc = setup.rustls_config();
assert_eq!(rc.alpn_protocols, alpn);
assert_eq!(rc.max_early_data_size, u32::MAX);
let _acceptor = setup.for_tcp_tls();
}
#[cfg(feature = "tcp")]
#[tokio::test]
async fn new_raw_key_for_tcp_tls_and_rustls_config_round_trip() {
let identity = TlsIdentity::RawKey(Ed25519SecretKey::generate());
let alpn = vec![b"alktls/test".to_vec()];
let setup = TlsServerConfig::new(&identity, &alpn)
.await
.expect("raw key config builds");
let rc = setup.rustls_config();
assert_eq!(rc.alpn_protocols, alpn);
assert_eq!(rc.max_early_data_size, u32::MAX);
let _acceptor = setup.for_tcp_tls();
}
#[cfg(feature = "tcp")]
#[tokio::test]
async fn new_self_signed_for_tcp_tls_and_rustls_config_round_trip() {
let identity = TlsIdentity::SelfSigned;
let alpn = vec![b"alktls/test".to_vec()];
let setup = TlsServerConfig::new(&identity, &alpn)
.await
.expect("self-signed config builds");
let rc = setup.rustls_config();
assert_eq!(rc.alpn_protocols, alpn);
assert_eq!(rc.max_early_data_size, u32::MAX);
let _acceptor = setup.for_tcp_tls();
}
#[cfg(feature = "acme")]
#[tokio::test]
async fn new_acme_spawns_and_appends_acme_tls_alpn() {
let dir = tempfile::tempdir().unwrap();
let identity = TlsIdentity::Acme {
domains: vec!["localhost".to_string()],
cache_dir: dir.path().join("cache"),
directory: crate::identity::AcmeDirectory::Custom(
"http://127.0.0.1:9/directory".to_string(),
),
contact: vec!["mailto:dev@example.com".to_string()],
};
let alpn = vec![b"alktls/test".to_vec()];
let setup = TlsServerConfig::new(&identity, &alpn)
.await
.expect("ACME config builds without awaiting the order");
let rc = setup.rustls_config();
assert_eq!(rc.max_early_data_size, u32::MAX);
assert_eq!(
rc.alpn_protocols,
vec![b"alktls/test".to_vec(), b"acme-tls/1".to_vec()]
);
assert!(
setup.acme_handle.is_some(),
"the event-loop task must be spawned and its handle stored"
);
}
#[cfg(feature = "acme")]
#[tokio::test]
async fn new_acme_caller_supplied_acme_tls_alpn_is_not_duplicated() {
let dir = tempfile::tempdir().unwrap();
let identity = TlsIdentity::Acme {
domains: vec!["localhost".to_string()],
cache_dir: dir.path().join("cache"),
directory: crate::identity::AcmeDirectory::Custom(
"http://127.0.0.1:9/directory".to_string(),
),
contact: vec!["mailto:dev@example.com".to_string()],
};
let alpn = vec![b"alktls/test".to_vec(), b"acme-tls/1".to_vec()];
let setup = TlsServerConfig::new(&identity, &alpn)
.await
.expect("ACME config builds without awaiting the order");
assert_eq!(
setup.rustls_config().alpn_protocols,
vec![b"alktls/test".to_vec(), b"acme-tls/1".to_vec()],
"a caller-supplied acme-tls/1 must yield exactly one entry"
);
}
#[cfg(feature = "acme")]
#[tokio::test]
async fn new_acme_empty_domains_returns_config_error() {
let dir = tempfile::tempdir().unwrap();
let identity = TlsIdentity::Acme {
domains: vec![],
cache_dir: dir.path().join("cache"),
directory: crate::identity::AcmeDirectory::Staging,
contact: vec!["mailto:dev@example.com".to_string()],
};
let err = TlsServerConfig::new(&identity, &[b"alktls/test".to_vec()])
.await
.err()
.expect("empty domain list must not construct an ACME config");
assert!(
matches!(err, TlsError::AcmeConfig(_)),
"empty domains must surface as TlsError::AcmeConfig, got {err:?}"
);
}
#[cfg(not(feature = "acme"))]
#[tokio::test]
async fn new_acme_identity_without_feature_returns_config_error() {
let identity = TlsIdentity::Acme {
domains: vec!["example.com".to_string()],
cache_dir: std::path::PathBuf::from("/tmp/alktls-acme-test"),
directory: crate::identity::AcmeDirectory::Staging,
contact: vec!["mailto:dev@example.com".to_string()],
};
let err = match TlsServerConfig::new(&identity, &[]).await {
Ok(_) => panic!("Acme identity must fail without the acme feature"),
Err(e) => e,
};
assert!(
matches!(err, TlsError::AcmeConfig(_)),
"expected TlsError::AcmeConfig, got {err:?}"
);
}
}