use alloc::borrow::Cow;
use alloc::vec::Vec;
use core::fmt::Debug;
use core::marker::PhantomData;
#[cfg(feature = "webpki")]
use pki_types::PrivateKeyDer;
use pki_types::{DnsName, FipsStatus, UnixTime};
use super::{ServerSessionKey, handy};
use crate::builder::{ConfigBuilder, WantsVerifier};
#[cfg(doc)]
use crate::crypto;
use crate::crypto::kx::NamedGroup;
use crate::crypto::{
CipherSuite, CryptoProvider, SelectedCredential, SignatureScheme, TicketProducer,
};
#[cfg(feature = "webpki")]
use crate::crypto::{Credentials, Identity, SingleCredential};
use crate::enums::{ApplicationProtocol, CertificateType, ProtocolVersion};
use crate::error::{Error, PeerMisbehaved};
use crate::msgs::{ClientHelloPayload, ClientTicketRequest, ServerNamePayload};
use crate::suites::Suite;
use crate::sync::Arc;
use crate::time_provider::{DefaultTimeProvider, TimeProvider};
use crate::verify::{ClientVerifier, DistinguishedName, NoClientAuth};
use crate::{KeyLog, NoKeyLog, Tls12CipherSuite, Tls13CipherSuite, compress};
#[derive(Clone, Debug)]
pub struct ServerConfig {
pub(crate) provider: Arc<CryptoProvider>,
pub cipher_suite_selector: &'static dyn CipherSuiteSelector,
pub max_fragment_size: Option<usize>,
pub session_storage: Arc<dyn StoresServerSessions>,
pub ticketer: Option<Arc<dyn TicketProducer>>,
pub cert_resolver: Arc<dyn ServerCredentialResolver>,
pub alpn_protocols: Vec<ApplicationProtocol<'static>>,
pub(super) verifier: Arc<dyn ClientVerifier>,
pub key_log: Arc<dyn KeyLog>,
pub enable_secret_extraction: bool,
pub max_early_data_size: u32,
pub send_half_rtt_data: bool,
pub send_tls13_tickets: Tls13Tickets,
pub require_ems: bool,
pub time_provider: Arc<dyn TimeProvider>,
pub cert_compressors: Vec<&'static dyn compress::CertCompressor>,
pub cert_compression_cache: Arc<compress::CompressionCache>,
pub cert_decompressors: Vec<&'static dyn compress::CertDecompressor>,
pub invalid_sni_policy: InvalidSniPolicy,
}
impl ServerConfig {
pub fn builder(provider: Arc<CryptoProvider>) -> ConfigBuilder<Self, WantsVerifier> {
Self::builder_with_details(provider, Arc::new(DefaultTimeProvider))
}
pub fn builder_with_details(
provider: Arc<CryptoProvider>,
time_provider: Arc<dyn TimeProvider>,
) -> ConfigBuilder<Self, WantsVerifier> {
ConfigBuilder {
state: WantsVerifier {
client_ech_mode: None,
},
provider,
time_provider,
side: PhantomData,
}
}
pub fn fips(&self) -> FipsStatus {
match self.require_ems {
true => self.provider.fips(),
false => FipsStatus::Unvalidated,
}
}
pub fn provider(&self) -> &Arc<CryptoProvider> {
&self.provider
}
pub(crate) fn supports_version(&self, v: ProtocolVersion) -> bool {
self.provider.supports_version(v)
}
pub(super) fn current_time(&self) -> Result<UnixTime, Error> {
self.time_provider
.current_time()
.ok_or(Error::FailedToGetCurrentTime)
}
}
#[expect(clippy::exhaustive_structs)]
#[derive(Clone, Copy, Debug)]
pub struct Tls13Tickets {
pub default: usize,
pub max: usize,
}
impl Tls13Tickets {
pub(super) fn resolve(&self, requested: Option<&ClientTicketRequest>, resuming: bool) -> usize {
let Some(req) = requested else {
return self.default;
};
Ord::min(
usize::from(match resuming {
true => req.resumption_count,
false => req.new_session_count,
}),
self.max,
)
}
}
impl Default for Tls13Tickets {
fn default() -> Self {
Self { default: 2, max: 2 }
}
}
pub trait StoresServerSessions: Debug + Send + Sync {
fn put(&self, key: ServerSessionKey<'_>, value: Vec<u8>) -> bool;
fn get(&self, key: ServerSessionKey<'_>) -> Option<Vec<u8>>;
fn take(&self, key: ServerSessionKey<'_>) -> Option<Vec<u8>>;
fn can_cache(&self) -> bool;
}
pub trait ServerCredentialResolver: Debug + Send + Sync {
fn resolve(&self, client_hello: &ClientHello<'_>) -> Result<SelectedCredential, Error>;
fn supported_certificate_types(&self) -> &'static [CertificateType] {
&[CertificateType::X509]
}
}
#[derive(Debug)]
pub struct ClientHello<'a> {
pub(super) server_name: Option<Cow<'a, DnsName<'a>>>,
pub(super) signature_schemes: &'a [SignatureScheme],
pub(super) alpn: Option<&'a Vec<ApplicationProtocol<'a>>>,
pub(super) server_cert_types: Option<&'a [CertificateType]>,
pub(super) client_cert_types: Option<&'a [CertificateType]>,
pub(super) cipher_suites: &'a [CipherSuite],
pub(super) certificate_authorities: Option<&'a [DistinguishedName]>,
pub(super) named_groups: Option<&'a [NamedGroup]>,
}
impl<'a> ClientHello<'a> {
#[cfg(test)]
pub(super) fn empty() -> Self {
Self {
server_name: None,
signature_schemes: &[],
alpn: None,
server_cert_types: None,
client_cert_types: None,
cipher_suites: &[],
certificate_authorities: None,
named_groups: None,
}
}
pub(super) fn new(
payload: &'a ClientHelloPayload,
signature_schemes: Option<&'a [SignatureScheme]>,
server_name: Option<Cow<'a, DnsName<'a>>>,
version: Option<ProtocolVersion>,
) -> Self {
Self {
server_name,
signature_schemes: signature_schemes.unwrap_or_else(|| {
payload
.signature_schemes
.as_deref()
.unwrap_or_default()
}),
alpn: payload.protocols.as_ref(),
server_cert_types: payload
.server_certificate_types
.as_deref(),
client_cert_types: payload
.client_certificate_types
.as_deref(),
cipher_suites: &payload.cipher_suites,
certificate_authorities: match version {
Some(ProtocolVersion::TLSv1_2) => None,
_ => payload
.certificate_authority_names
.as_deref(),
},
named_groups: payload.named_groups.as_deref(),
}
}
pub fn server_name(&self) -> Option<&DnsName<'_>> {
self.server_name.as_deref()
}
pub fn signature_schemes(&self) -> &[SignatureScheme] {
self.signature_schemes
}
pub fn alpn(&self) -> Option<impl Iterator<Item = &'a [u8]> + use<'a>> {
self.alpn.map(|protocols| {
protocols
.iter()
.map(|proto| proto.as_ref())
})
}
pub fn cipher_suites(&self) -> &[CipherSuite] {
self.cipher_suites
}
pub fn server_cert_types(&self) -> Option<&'a [CertificateType]> {
self.server_cert_types
}
pub fn client_cert_types(&self) -> Option<&'a [CertificateType]> {
self.client_cert_types
}
pub fn certificate_authorities(&self) -> Option<&'a [DistinguishedName]> {
self.certificate_authorities
}
pub fn named_groups(&self) -> Option<&'a [NamedGroup]> {
self.named_groups
}
}
#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum InvalidSniPolicy {
RejectAll,
#[default]
IgnoreIpAddresses,
IgnoreAll,
}
impl InvalidSniPolicy {
pub(super) fn accept(
&self,
payload: Option<&ServerNamePayload<'_>>,
) -> Result<Option<DnsName<'static>>, Error> {
let Some(payload) = payload else {
return Ok(None);
};
if let Some(server_name) = payload.to_dns_name_normalized() {
return Ok(Some(server_name));
}
match (self, payload) {
(Self::IgnoreAll, _) => Ok(None),
(Self::IgnoreIpAddresses, ServerNamePayload::IpAddress) => Ok(None),
_ => Err(Error::PeerMisbehaved(
PeerMisbehaved::ServerNameMustContainOneHostName,
)),
}
}
}
impl ConfigBuilder<ServerConfig, WantsVerifier> {
pub fn with_client_cert_verifier(
self,
client_cert_verifier: Arc<dyn ClientVerifier>,
) -> ConfigBuilder<ServerConfig, WantsServerCert> {
ConfigBuilder {
state: WantsServerCert {
verifier: client_cert_verifier,
},
provider: self.provider,
time_provider: self.time_provider,
side: PhantomData,
}
}
pub fn with_no_client_auth(self) -> ConfigBuilder<ServerConfig, WantsServerCert> {
self.with_client_cert_verifier(Arc::new(NoClientAuth))
}
}
#[derive(Clone, Debug)]
pub struct WantsServerCert {
verifier: Arc<dyn ClientVerifier>,
}
impl ConfigBuilder<ServerConfig, WantsServerCert> {
#[cfg(feature = "webpki")]
pub fn with_single_cert(
self,
identity: Arc<Identity<'static>>,
key_der: PrivateKeyDer<'static>,
) -> Result<ServerConfig, Error> {
let credentials = Credentials::from_der(identity, key_der, self.provider())?;
self.with_server_credential_resolver(Arc::new(SingleCredential::from(credentials)))
}
#[cfg(feature = "webpki")]
pub fn with_single_cert_with_ocsp(
self,
identity: Arc<Identity<'static>>,
key_der: PrivateKeyDer<'static>,
ocsp: Arc<[u8]>,
) -> Result<ServerConfig, Error> {
let mut credentials = Credentials::from_der(identity, key_der, self.provider())?;
if !ocsp.is_empty() {
credentials.ocsp = Some(ocsp);
}
self.with_server_credential_resolver(Arc::new(SingleCredential::from(credentials)))
}
pub fn with_server_credential_resolver(
self,
cert_resolver: Arc<dyn ServerCredentialResolver>,
) -> Result<ServerConfig, Error> {
self.provider.consistency_check()?;
let require_ems = !matches!(self.provider.fips(), FipsStatus::Unvalidated);
Ok(ServerConfig {
provider: self.provider,
cipher_suite_selector: &PreferClientOrder,
max_fragment_size: None,
session_storage: handy::ServerSessionMemoryCache::new(256),
ticketer: None,
cert_resolver,
alpn_protocols: Vec::new(),
verifier: self.state.verifier,
key_log: Arc::new(NoKeyLog {}),
enable_secret_extraction: false,
max_early_data_size: 0,
send_half_rtt_data: false,
send_tls13_tickets: Tls13Tickets::default(),
require_ems,
time_provider: self.time_provider,
cert_compressors: compress::default_cert_compressors().to_vec(),
cert_compression_cache: Arc::new(compress::CompressionCache::default()),
cert_decompressors: compress::default_cert_decompressors().to_vec(),
invalid_sni_policy: InvalidSniPolicy::default(),
})
}
}
#[expect(clippy::exhaustive_structs)]
#[derive(Debug)]
pub struct PreferClientOrder;
impl CipherSuiteSelector for PreferClientOrder {
fn select_tls12_cipher_suite(
&self,
client: &mut dyn Iterator<Item = &'static Tls12CipherSuite>,
server: &[&'static Tls12CipherSuite],
) -> Option<&'static Tls12CipherSuite> {
self.select(client, server)
}
fn select_tls13_cipher_suite(
&self,
client: &mut dyn Iterator<Item = &'static Tls13CipherSuite>,
server: &[&'static Tls13CipherSuite],
) -> Option<&'static Tls13CipherSuite> {
self.select(client, server)
}
}
impl PreferClientOrder {
fn select<T: Suite>(
&self,
client: &mut dyn Iterator<Item = &'static T>,
_server: &[&'static T],
) -> Option<&'static T> {
client.next()
}
}
#[expect(clippy::exhaustive_structs)]
#[derive(Debug)]
pub struct PreferServerOrder;
impl CipherSuiteSelector for PreferServerOrder {
fn select_tls12_cipher_suite(
&self,
client: &mut dyn Iterator<Item = &'static Tls12CipherSuite>,
server: &[&'static Tls12CipherSuite],
) -> Option<&'static Tls12CipherSuite> {
client
.filter_map(|cs| {
server
.iter()
.position(|&ss| ss == cs)
.map(|pos| (pos, cs))
})
.min_by_key(|&(pos, _)| pos)
.map(|(_, cs)| cs)
}
fn select_tls13_cipher_suite(
&self,
client: &mut dyn Iterator<Item = &'static Tls13CipherSuite>,
server: &[&'static Tls13CipherSuite],
) -> Option<&'static Tls13CipherSuite> {
client
.filter_map(|cs| {
server
.iter()
.position(|&ss| ss == cs)
.map(|pos| (pos, cs))
})
.min_by_key(|&(pos, _)| pos)
.map(|(_, cs)| cs)
}
}
impl<T: CipherSuiteSelector + ?Sized> VersionSuiteSelector<Tls12CipherSuite> for T {
fn select(
&self,
client: &mut dyn Iterator<Item = &'static Tls12CipherSuite>,
server: &[&'static Tls12CipherSuite],
) -> Option<&'static Tls12CipherSuite> {
self.select_tls12_cipher_suite(client, server)
}
}
impl<T: CipherSuiteSelector + ?Sized> VersionSuiteSelector<Tls13CipherSuite> for T {
fn select(
&self,
client: &mut dyn Iterator<Item = &'static Tls13CipherSuite>,
server: &[&'static Tls13CipherSuite],
) -> Option<&'static Tls13CipherSuite> {
self.select_tls13_cipher_suite(client, server)
}
}
pub(super) trait VersionSuiteSelector<T> {
fn select(
&self,
client: &mut dyn Iterator<Item = &'static T>,
server: &[&'static T],
) -> Option<&'static T>;
}
pub trait CipherSuiteSelector: Debug + Send + Sync {
fn select_tls12_cipher_suite(
&self,
client: &mut dyn Iterator<Item = &'static Tls12CipherSuite>,
server: &[&'static Tls12CipherSuite],
) -> Option<&'static Tls12CipherSuite>;
fn select_tls13_cipher_suite(
&self,
client: &mut dyn Iterator<Item = &'static Tls13CipherSuite>,
server: &[&'static Tls13CipherSuite],
) -> Option<&'static Tls13CipherSuite>;
}