use alloc::sync::Arc;
use parking_lot::RwLock as ParkingRwLock;
use rustls::client::ClientConfig;
use rustls::crypto::{CryptoProvider, SupportedKxGroup};
use rustls::pki_types::{CertificateDer, CertificateRevocationListDer, PrivateKeyDer};
use rustls::server::{ProducesTickets, ResolvesServerCert, ServerConfig, WebPkiClientVerifier};
use rustls::sign::CertifiedKey;
use rustls::{RootCertStore, SupportedCipherSuite};
use super::chain::{self, Purpose, VerifiedChainBuilder};
use super::cipher;
use super::constants::{VERIFY_X509_PARTIAL_CHAIN, VERIFY_X509_STRICT};
use super::providers::CryptoExt;
const X509_V_FLAG_CRL_CHECK: i32 = 4;
#[derive(Debug)]
pub struct ProtocolSettings {
pub versions: &'static [&'static rustls::SupportedProtocolVersion],
pub kx_groups: Option<Vec<&'static dyn rustls::crypto::SupportedKxGroup>>,
pub cipher_suites: Option<Vec<rustls::SupportedCipherSuite>>,
pub alpn_protocols: Vec<Vec<u8>>,
}
#[derive(Debug)]
pub struct ServerConfigOptions {
pub protocol_settings: ProtocolSettings,
pub cert_chain: Vec<CertificateDer<'static>>,
pub private_key: PrivateKeyDer<'static>,
pub root_store: Option<RootCertStore>,
pub ca_certs_der: Vec<Vec<u8>>,
pub request_client_cert: bool,
pub use_deferred_validation: bool,
pub cert_resolver: Option<Arc<dyn ResolvesServerCert>>,
pub deferred_cert_error: Option<Arc<ParkingRwLock<Option<String>>>>,
pub session_storage: Option<Arc<rustls::server::ServerSessionMemoryCache>>,
pub ticketer: Option<Arc<dyn ProducesTickets>>,
}
#[derive(Debug)]
pub struct ClientConfigOptions {
pub protocol_settings: ProtocolSettings,
pub root_store: Option<RootCertStore>,
pub ca_certs_der: Vec<Vec<u8>>,
pub cert_chain: Option<Vec<CertificateDer<'static>>>,
pub private_key: Option<PrivateKeyDer<'static>>,
pub verify_server_cert: bool,
pub check_hostname: bool,
pub check_common_name: bool,
pub verify_flags: i32,
pub session_store: Option<Arc<dyn rustls::client::ClientSessionStore>>,
pub crls: Vec<CertificateRevocationListDer<'static>>,
}
fn create_custom_crypto_provider(
cipher_suites: Option<Vec<SupportedCipherSuite>>,
kx_groups: Option<Vec<&'static dyn SupportedKxGroup>>,
) -> Arc<CryptoProvider> {
let default_provider = CryptoExt::get_provider();
Arc::new(CryptoProvider {
cipher_suites: cipher_suites.unwrap_or_else(|| default_provider.cipher_suites.clone()),
kx_groups: kx_groups.unwrap_or_else(|| default_provider.kx_groups.clone()),
signature_verification_algorithms: default_provider.signature_verification_algorithms,
secure_random: default_provider.secure_random,
key_provider: default_provider.key_provider,
})
}
pub fn create_server_config(options: ServerConfigOptions) -> Result<chain::ServerConfig, String> {
let custom_provider = create_custom_crypto_provider(
options.protocol_settings.cipher_suites.clone(),
options.protocol_settings.kx_groups.clone(),
);
let chain_builder = Arc::new(VerifiedChainBuilder {
purpose: if options.request_client_cert {
Purpose::ClientAuth
} else {
Purpose::Unverified
},
roots: options
.root_store
.clone()
.unwrap_or_else(RootCertStore::empty),
root_der: options.ca_certs_der,
crls: Vec::new(),
only_end_entity_revocation: false,
supported: custom_provider.signature_verification_algorithms,
allow_trusted_leaf: false,
verify_flags: 0,
});
let client_cert_verifier: Option<Arc<dyn rustls::server::danger::ClientCertVerifier>> =
if let Some(root_store) = options.root_store {
if options.request_client_cert {
let base_verifier = WebPkiClientVerifier::builder_with_provider(
Arc::new(root_store),
custom_provider.clone(),
)
.build()
.map_err(|e| format!("Failed to create client verifier: {e}"))?;
if options.use_deferred_validation {
if let Some(deferred_error) = options.deferred_cert_error {
use super::verify::DeferredClientCertVerifier;
let deferred_verifier =
DeferredClientCertVerifier::new(base_verifier, deferred_error);
Some(Arc::new(deferred_verifier))
} else {
Some(base_verifier)
}
} else {
Some(base_verifier)
}
} else {
None
}
} else {
None
};
let builder = ServerConfig::builder_with_provider(custom_provider)
.with_protocol_versions(options.protocol_settings.versions)
.map_err(|e| format!("Failed to create server config builder: {e}"))?;
let builder = if let Some(verifier) = client_cert_verifier {
builder.with_client_cert_verifier(verifier)
} else {
builder.with_no_client_auth()
};
let mut config = if let Some(resolver) = options.cert_resolver {
builder.with_cert_resolver(resolver)
} else {
builder
.with_single_cert(options.cert_chain, options.private_key)
.map_err(|e| format!("Failed to set server certificate: {e}"))?
};
apply_alpn_with_fallback(
&mut config.alpn_protocols,
&options.protocol_settings.alpn_protocols,
);
if let Some(session_storage) = options.session_storage {
config.session_storage = session_storage;
}
if let Some(ticketer) = options.ticketer {
config.ticketer = ticketer.clone();
}
Ok((Arc::new(config), chain_builder))
}
fn build_webpki_verifier_with_crls(
root_store: Arc<RootCertStore>,
crls: Vec<CertificateRevocationListDer<'static>>,
verify_flags: i32,
) -> Result<Arc<dyn rustls::client::danger::ServerCertVerifier>, String> {
use rustls::client::WebPkiServerVerifier;
let mut verifier_builder = WebPkiServerVerifier::builder(root_store);
let crl_check_requested = verify_flags & X509_V_FLAG_CRL_CHECK != 0;
let has_crls = !crls.is_empty();
if has_crls || crl_check_requested {
verifier_builder = verifier_builder.with_crls(crls);
if verify_flags & X509_V_FLAG_CRL_CHECK != 0 {
verifier_builder = verifier_builder.only_check_end_entity_revocation();
}
}
let webpki_verifier = verifier_builder
.build()
.map_err(|e| format!("Failed to build WebPkiServerVerifier: {e}"))?;
Ok(webpki_verifier as Arc<dyn rustls::client::danger::ServerCertVerifier>)
}
fn apply_verifier_wrappers(
verifier: Arc<dyn rustls::client::danger::ServerCertVerifier>,
verify_flags: i32,
has_crls: bool,
ca_certs_der: Vec<Vec<u8>>,
check_common_name: bool,
) -> Arc<dyn rustls::client::danger::ServerCertVerifier> {
let crl_check_requested = verify_flags & X509_V_FLAG_CRL_CHECK != 0;
let verifier = if crl_check_requested {
use super::verify::CRLCheckVerifier;
Arc::new(CRLCheckVerifier::new(
verifier,
has_crls,
crl_check_requested,
))
} else {
verifier
};
let verifier = if !ca_certs_der.is_empty() {
use super::verify::PartialChainVerifier;
Arc::new(PartialChainVerifier::new(
verifier,
ca_certs_der,
verify_flags,
check_common_name,
))
} else {
verifier
};
if verify_flags & VERIFY_X509_STRICT != 0 {
Arc::new(super::verify::StrictCertVerifier::new(
verifier,
verify_flags,
))
} else {
verifier
}
}
fn apply_alpn_with_fallback(config_alpn: &mut Vec<Vec<u8>>, alpn_protocols: &[Vec<u8>]) {
if !alpn_protocols.is_empty() {
*config_alpn = alpn_protocols.to_vec();
config_alpn.push(vec![0u8]); }
}
pub fn create_client_config(options: ClientConfigOptions) -> Result<chain::ClientConfig, String> {
let custom_provider = create_custom_crypto_provider(
options.protocol_settings.cipher_suites.clone(),
options.protocol_settings.kx_groups.clone(),
);
let chain_builder = Arc::new(VerifiedChainBuilder {
purpose: if options.verify_server_cert {
Purpose::ServerAuth
} else {
Purpose::Unverified
},
roots: options
.root_store
.clone()
.unwrap_or_else(RootCertStore::empty),
root_der: options.ca_certs_der.clone(),
crls: options.crls.clone(),
only_end_entity_revocation: options.verify_flags & X509_V_FLAG_CRL_CHECK != 0,
supported: custom_provider.signature_verification_algorithms,
allow_trusted_leaf: options.check_hostname
|| options.verify_flags & VERIFY_X509_PARTIAL_CHAIN != 0,
verify_flags: options.verify_flags,
});
let verifier: Arc<dyn rustls::client::danger::ServerCertVerifier> = if options
.verify_server_cert
{
let root_store = options
.root_store
.ok_or("Root store required for server verification")?;
let root_store_arc = Arc::new(root_store);
if root_store_arc.is_empty() {
use super::verify::EmptyRootStoreVerifier;
Arc::new(EmptyRootStoreVerifier)
} else {
let has_crls = !options.crls.is_empty();
if options.check_hostname {
let base_verifier = build_webpki_verifier_with_crls(
root_store_arc,
options.crls,
options.verify_flags,
)?;
let verifier = apply_verifier_wrappers(
base_verifier,
options.verify_flags,
has_crls,
options.ca_certs_der.clone(),
options.check_common_name,
);
if options.check_common_name {
use super::verify::CommonNameFallbackVerifier;
Arc::new(CommonNameFallbackVerifier::new(verifier))
} else {
verifier
}
} else {
use super::verify::HostnameIgnoringVerifier;
let webpki_verifier = build_webpki_verifier_with_crls(
root_store_arc,
options.crls,
options.verify_flags,
)?;
let crl_check_requested = options.verify_flags & X509_V_FLAG_CRL_CHECK != 0;
let verifier = if crl_check_requested {
use super::verify::CRLCheckVerifier;
Arc::new(CRLCheckVerifier::new(
webpki_verifier,
has_crls,
crl_check_requested,
)) as Arc<dyn rustls::client::danger::ServerCertVerifier>
} else {
webpki_verifier
};
let verifier = if options.verify_flags & VERIFY_X509_PARTIAL_CHAIN != 0 {
use super::verify::PartialChainVerifier;
Arc::new(PartialChainVerifier::new(
verifier,
options.ca_certs_der.clone(),
options.verify_flags,
options.check_common_name,
)) as Arc<dyn rustls::client::danger::ServerCertVerifier>
} else {
verifier
};
let hostname_ignoring_verifier: Arc<
dyn rustls::client::danger::ServerCertVerifier,
> = Arc::new(HostnameIgnoringVerifier::new_with_verifier(verifier));
if options.verify_flags & VERIFY_X509_STRICT != 0 {
Arc::new(super::verify::StrictCertVerifier::new(
hostname_ignoring_verifier,
options.verify_flags,
))
} else {
hostname_ignoring_verifier
}
}
}
} else {
use super::verify::NoVerifier;
Arc::new(NoVerifier)
};
let builder = ClientConfig::builder_with_provider(custom_provider)
.with_protocol_versions(options.protocol_settings.versions)
.map_err(|e| format!("Failed to create client config builder: {e}"))?
.dangerous()
.with_custom_certificate_verifier(verifier);
let mut config =
if let (Some(cert_chain), Some(private_key)) = (options.cert_chain, options.private_key) {
builder
.with_client_auth_cert(cert_chain, private_key)
.map_err(|e| format!("Failed to set client certificate: {e}"))?
} else {
builder.with_no_client_auth()
};
apply_alpn_with_fallback(
&mut config.alpn_protocols,
&options.protocol_settings.alpn_protocols,
);
if let Some(session_store) = options.session_store {
use rustls::client::Resumption;
config.resumption = Resumption::store(session_store);
}
Ok((Arc::new(config), chain_builder))
}
#[derive(Debug)]
pub struct MultiCertResolver {
cert_keys: Vec<Arc<CertifiedKey>>,
}
impl MultiCertResolver {
pub fn new(cert_keys: Vec<Arc<CertifiedKey>>) -> Self {
Self { cert_keys }
}
}
impl ResolvesServerCert for MultiCertResolver {
fn resolve(&self, client_hello: rustls::server::ClientHello<'_>) -> Option<Arc<CertifiedKey>> {
let client_schemes = client_hello.signature_schemes();
for cert_key in &self.cert_keys {
if let Some(_scheme) = cert_key.key.choose_scheme(client_schemes) {
return Some(cert_key.clone());
}
}
self.cert_keys.first().cloned()
}
}
pub fn curve_name_to_kx_group(curve: &str) -> Result<Vec<&'static dyn SupportedKxGroup>, String> {
cipher::kx_group_by_openssl_name(curve)
.map(|group| vec![group])
.ok_or_else(|| format!("unknown curve name '{curve}'"))
}