use alloc::borrow::{Cow, ToOwned};
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::borrow::Borrow;
use core::fmt;
use pki_types::DnsName;
use super::config::{CipherSuiteSelector, VersionSuiteSelector};
use super::{ClientHello, CommonServerSessionValue, ServerConfig, tls12, tls13};
use crate::SupportedCipherSuite;
use crate::common_state::{Event, Output, OutputEvent, Protocol};
use crate::conn::{ConnectionRandoms, Input};
use crate::crypto::hash::Hash;
use crate::crypto::kx::{KeyExchangeAlgorithm, NamedGroup, SupportedKxGroup};
use crate::crypto::{CipherSuite, CryptoProvider, SelectedCredential, SignatureScheme};
use crate::enums::{ApplicationProtocol, CertificateType, HandshakeType, ProtocolVersion};
use crate::error::{ApiMisuse, Error, PeerIncompatible, PeerMisbehaved};
use crate::hash_hs::{HandshakeHash, HandshakeHashBuffer};
use crate::kernel::KernelState;
use crate::log::{debug, trace};
use crate::msgs::{
ClientHelloPayload, Compression, HandshakeAlignedProof, HandshakeMessagePayload,
HandshakePayload, Message, MessagePayload, Random, ServerExtensions, ServerExtensionsInput,
ServerNamePayload, SessionId, SingleProtocolName, TransportParameters,
};
use crate::sealed::Sealed;
use crate::suites::{PartiallyExtractedSecrets, Suite};
use crate::sync::Arc;
use crate::tls12::Tls12CipherSuite;
use crate::tls13::Tls13CipherSuite;
use crate::tls13::key_schedule::KeyScheduleTrafficSend;
pub(crate) enum ServerState {
ReadClientHello(ReadClientHello),
ChooseConfig(Box<ChooseConfig>),
ClientHello(Box<ExpectClientHello>),
Tls12(tls12::Tls12State),
Tls13(tls13::Tls13State),
}
impl ServerState {
pub(crate) fn set_resumption_data(&mut self, resumption_data: &[u8]) -> Result<(), Error> {
match self {
Self::ReadClientHello(e) => e.set_resumption_data(resumption_data),
Self::ChooseConfig(e) => e.set_resumption_data(resumption_data),
Self::ClientHello(e) => e.set_resumption_data(resumption_data),
_ => Err(ApiMisuse::ResumptionDataProvidedTooLate.into()),
}
}
}
impl crate::conn::StateMachine for ServerState {
fn handle<'m>(self, input: Input<'m>, output: &mut dyn Output<'m>) -> Result<Self, Error> {
match self {
Self::ReadClientHello(r) => r.handle(input, output),
Self::ChooseConfig(_) => {
Err(Error::Unreachable("ChooseConfig cannot process a message"))
}
Self::ClientHello(e) => e.handle(input, output),
Self::Tls12(sm) => sm.handle(input, output),
Self::Tls13(sm) => sm.handle(input, output),
}
}
fn wants_input(&self) -> bool {
!matches!(self, Self::ChooseConfig(_))
}
fn is_traffic(&self) -> bool {
matches!(
self,
Self::Tls12(tls12::Tls12State::Traffic(..))
| Self::Tls13(tls13::Tls13State::Traffic(..) | tls13::Tls13State::QuicTraffic(..))
)
}
fn handle_decrypt_error(&mut self) {}
fn into_external_state(
self,
send_keys: &Option<Box<KeyScheduleTrafficSend>>,
) -> Result<(PartiallyExtractedSecrets, Box<dyn KernelState + 'static>), Error> {
match self {
Self::Tls13(tls13::Tls13State::Traffic(e)) => e.into_external_state(send_keys),
Self::Tls12(tls12::Tls12State::Traffic(e)) => e.into_external_state(send_keys),
_ => Err(Error::HandshakeNotComplete),
}
}
}
pub(super) struct Tls12Extensions {
pub(super) alpn_protocol: Option<ApplicationProtocol<'static>>,
pub(super) send_ticket: bool,
}
impl Tls12Extensions {
pub(super) fn new(
extra_exts: ServerExtensionsInput,
ocsp_response: &mut Option<&[u8]>,
resumedata: Option<&CommonServerSessionValue<'_>>,
hello: &ClientHelloPayload,
output: &mut dyn Output<'_>,
using_ems: bool,
config: &ServerConfig,
) -> Result<(Self, Box<ServerExtensions<'static>>), Error> {
let ep = ExtensionProcessing::new(hello, config);
let (alpn_protocol, mut extensions) =
ep.process_common(extra_exts, output, ocsp_response, resumedata)?;
if hello.renegotiation_info.is_some()
|| hello
.cipher_suites
.contains(&CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
{
extensions.renegotiation_info = Some(Vec::new().into());
}
let send_ticket = if hello.session_ticket.is_some() && config.ticketer.is_some() {
extensions.session_ticket_ack = Some(());
true
} else {
false
};
if using_ems {
extensions.extended_master_secret_ack = Some(());
}
if let Some([_, ..]) = ocsp_response {
extensions.certificate_status_request_ack = Some(());
}
let out = Self {
alpn_protocol,
send_ticket,
};
Ok((out, extensions))
}
}
pub(super) struct Tls13Extensions {
pub(super) certificate_types: CertificateTypes,
pub(super) alpn_protocol: Option<ApplicationProtocol<'static>>,
}
impl Tls13Extensions {
pub(super) fn new(
extra_exts: ServerExtensionsInput,
ocsp_response: &mut Option<&[u8]>,
resumedata: Option<&CommonServerSessionValue<'_>>,
hello: &ClientHelloPayload,
output: &mut dyn Output<'_>,
config: &ServerConfig,
) -> Result<(Self, Box<ServerExtensions<'static>>), Error> {
let ep = ExtensionProcessing::new(hello, config);
let (alpn_protocol, mut extensions) =
ep.process_common(extra_exts, output, ocsp_response, resumedata)?;
let expected_client_type = select_cert_type(
hello
.client_certificate_types
.as_deref(),
config
.verifier
.supported_certificate_types(),
)?;
let expected_server_type = select_cert_type(
hello
.server_certificate_types
.as_deref(),
config
.cert_resolver
.supported_certificate_types(),
)?;
if hello.client_certificate_types.is_some() && config.verifier.offer_client_auth() {
extensions.client_certificate_type = Some(expected_client_type);
}
if hello.server_certificate_types.is_some() {
extensions.server_certificate_type = Some(expected_server_type);
}
let out = Self {
certificate_types: CertificateTypes {
client: expected_client_type,
},
alpn_protocol,
};
Ok((out, extensions))
}
}
struct ExtensionProcessing<'a> {
config: &'a ServerConfig,
hello: &'a ClientHelloPayload,
}
impl<'a> ExtensionProcessing<'a> {
fn new(client_hello: &'a ClientHelloPayload, config: &'a ServerConfig) -> Self {
Self {
config,
hello: client_hello,
}
}
fn process_common(
self,
extra_exts: ServerExtensionsInput,
output: &mut dyn Output<'_>,
ocsp_response: &mut Option<&[u8]>,
resumedata: Option<&CommonServerSessionValue<'_>>,
) -> Result<
(
Option<ApplicationProtocol<'static>>,
Box<ServerExtensions<'static>>,
),
Error,
> {
let Self { config, hello } = self;
let mut extensions = Box::new(ServerExtensions::default());
let ServerExtensionsInput {
transport_parameters,
} = extra_exts;
if let Some(TransportParameters::Quic(v)) = transport_parameters {
extensions.transport_parameters = Some(v);
}
let our_protocols = &config.alpn_protocols;
let chosen_protocol = if let Some(their_protocols) = &hello.protocols {
if let Some(selected_protocol) = our_protocols.iter().find(|ours| {
their_protocols
.iter()
.any(|theirs| theirs.as_ref() == ours.as_ref())
}) {
debug!("Chosen ALPN protocol {selected_protocol:?}");
Some(selected_protocol)
} else if !our_protocols.is_empty() {
return Err(Error::NoApplicationProtocol);
} else {
None
}
} else {
None
};
if let Some(protocol) = &chosen_protocol {
extensions.selected_protocol = Some(SingleProtocolName::new((*protocol).to_owned()));
output.output(OutputEvent::ApplicationProtocol((*protocol).to_owned()));
}
if let Some(quic) = output.quic() {
if chosen_protocol.is_none() && (!our_protocols.is_empty() || hello.protocols.is_some())
{
return Err(Error::NoApplicationProtocol);
}
match hello.transport_parameters.as_ref() {
Some(params) => quic.transport_parameters(params.to_owned().into_vec()),
None => {
return Err(PeerMisbehaved::MissingQuicTransportParameters.into());
}
}
}
let for_resume = resumedata.is_some();
if let (false, Some(ServerNamePayload::SingleDnsName(_))) = (for_resume, &hello.server_name)
{
extensions.server_name_ack = Some(());
}
if for_resume
|| hello
.certificate_status_request
.is_none()
{
ocsp_response.take();
}
Ok((chosen_protocol.map(|p| p.to_owned()), extensions))
}
}
fn select_cert_type(
client: Option<&[CertificateType]>,
server: &[CertificateType],
) -> Result<CertificateType, Error> {
if server.is_empty() {
return Err(ApiMisuse::NoSupportedCertificateTypes.into());
}
let client = match client {
Some([]) => {
return Err(PeerIncompatible::IncorrectCertificateTypeExtension.into());
}
Some(c) => c,
None => {
return match server.contains(&CertificateType::X509) {
true => Ok(CertificateType::X509),
false => Err(PeerIncompatible::IncorrectCertificateTypeExtension.into()),
};
}
};
for &ct in client {
if server.contains(&ct) {
return Ok(ct);
}
}
Err(PeerIncompatible::IncorrectCertificateTypeExtension.into())
}
pub(super) struct CertificateTypes {
pub(super) client: CertificateType,
}
pub(crate) struct ReadClientHello {
protocol: Protocol,
resumption_data: Vec<u8>,
}
impl ReadClientHello {
pub(crate) fn new(protocol: Protocol) -> Self {
Self {
protocol,
resumption_data: Vec::new(),
}
}
pub(crate) fn handle<'m>(
self,
input: Input<'m>,
_output: &mut dyn Output<'_>,
) -> Result<ServerState, Error> {
ClientHelloInput::from_input(&input)?;
Ok(Box::new(ChooseConfig {
client_hello: Input {
message: input.message.into_owned(),
aligned_handshake: input.aligned_handshake,
},
resumption_data: self.resumption_data,
protocol: self.protocol,
})
.into())
}
fn set_resumption_data(&mut self, resumption_data: &[u8]) -> Result<(), Error> {
self.resumption_data = resumption_data.to_vec();
Ok(())
}
}
impl From<ReadClientHello> for ServerState {
fn from(value: ReadClientHello) -> Self {
Self::ReadClientHello(value)
}
}
pub(crate) struct ChooseConfig {
protocol: Protocol,
resumption_data: Vec<u8>,
client_hello: Input<'static>,
}
impl ChooseConfig {
pub(crate) fn use_config(
self,
config: Arc<ServerConfig>,
extra_exts: ServerExtensionsInput,
output: &mut dyn Output<'_>,
) -> Result<ServerState, Error> {
ExpectClientHello::new(config, extra_exts, self.resumption_data, self.protocol)
.with_input(ClientHelloInput::from_input(&self.client_hello)?, output)
}
pub(crate) fn client_hello(&self) -> ClientHello<'_> {
let MessagePayload::Handshake {
parsed: HandshakeMessagePayload(HandshakePayload::ClientHello(client_hello)),
..
} = &self.client_hello.message.payload
else {
unreachable!();
};
let server_name = client_hello
.server_name
.as_ref()
.and_then(ServerNamePayload::to_dns_name_normalized)
.map(Cow::Owned);
ClientHello::new(client_hello, None, server_name, None)
}
fn set_resumption_data(&mut self, resumption_data: &[u8]) -> Result<(), Error> {
self.resumption_data = resumption_data.to_vec();
Ok(())
}
}
impl From<Box<ChooseConfig>> for ServerState {
fn from(value: Box<ChooseConfig>) -> Self {
Self::ChooseConfig(value)
}
}
pub(crate) struct ExpectClientHello {
pub(super) config: Arc<ServerConfig>,
pub(super) protocol: Protocol,
pub(super) extra_exts: ServerExtensionsInput,
pub(super) transcript: HandshakeHashOrBuffer,
pub(super) session_id: SessionId,
pub(super) sni: Option<DnsName<'static>>,
pub(super) resumption_data: Vec<u8>,
pub(super) using_ems: bool,
pub(super) done_retry: bool,
pub(super) send_tickets: usize,
}
impl ExpectClientHello {
pub(super) fn new(
config: Arc<ServerConfig>,
extra_exts: ServerExtensionsInput,
resumption_data: Vec<u8>,
protocol: Protocol,
) -> Self {
let mut transcript_buffer = HandshakeHashBuffer::new();
if config.verifier.offer_client_auth() {
transcript_buffer.set_client_auth_enabled();
}
Self {
config,
protocol,
extra_exts,
transcript: HandshakeHashOrBuffer::Buffer(transcript_buffer),
session_id: SessionId::empty(),
sni: None,
resumption_data,
using_ems: false,
done_retry: false,
send_tickets: 0,
}
}
pub(super) fn with_input(
self,
input: ClientHelloInput<'_>,
output: &mut dyn Output<'_>,
) -> Result<ServerState, Error> {
let tls13_enabled = self
.config
.supports_version(ProtocolVersion::TLSv1_3);
let tls12_enabled = self
.config
.supports_version(ProtocolVersion::TLSv1_2);
if let Some(versions) = &input.client_hello.supported_versions {
if versions.tls13 && tls13_enabled {
self.with_version::<Tls13CipherSuite>(input, output)
} else if !versions.tls12 || !tls12_enabled {
Err(PeerIncompatible::Tls12NotOfferedOrEnabled.into())
} else if self.protocol.is_quic() {
Err(PeerIncompatible::Tls13RequiredForQuic.into())
} else {
self.with_version::<Tls12CipherSuite>(input, output)
}
} else if u16::from(input.client_hello.client_version) < u16::from(ProtocolVersion::TLSv1_2)
{
Err(PeerIncompatible::Tls12NotOffered.into())
} else if !tls12_enabled && tls13_enabled {
Err(PeerIncompatible::SupportedVersionsExtensionRequired.into())
} else if self.protocol.is_quic() {
Err(PeerIncompatible::Tls13RequiredForQuic.into())
} else {
self.with_version::<Tls12CipherSuite>(input, output)
}
}
fn with_version<T: Suite + 'static>(
mut self,
input: ClientHelloInput<'_>,
output: &mut dyn Output<'_>,
) -> Result<ServerState, Error>
where
CryptoProvider: Borrow<[&'static T]>,
SupportedCipherSuite: From<&'static T>,
dyn CipherSuiteSelector: VersionSuiteSelector<T>,
{
output.output(OutputEvent::ProtocolVersion(T::VERSION));
let sni = self
.config
.invalid_sni_policy
.accept(input.client_hello.server_name.as_ref())?;
output.emit(Event::ReceivedServerName(sni.clone()));
if self.done_retry {
let ch_sni = input
.client_hello
.server_name
.as_ref()
.and_then(ServerNamePayload::to_dns_name_normalized);
if self.sni != ch_sni {
return Err(PeerMisbehaved::ServerNameDifferedOnRetry.into());
}
}
let suites = <CryptoProvider as Borrow<[&'static T]>>::borrow(&self.config.provider);
let client_suites = suites
.iter()
.filter(|&&scs| {
input
.client_hello
.cipher_suites
.contains(&scs.suite())
})
.collect::<Vec<_>>();
let mut sig_schemes = input.sig_schemes.to_owned();
if T::VERSION == ProtocolVersion::TLSv1_2 {
sig_schemes.retain(|scheme| {
client_suites
.iter()
.any(|&suite| suite.usable_for_signature_scheme(*scheme))
});
} else if T::VERSION == ProtocolVersion::TLSv1_3 {
sig_schemes.retain(SignatureScheme::supported_in_tls13);
}
let credentials = self
.config
.cert_resolver
.resolve(&ClientHello::new(
input.client_hello,
Some(&sig_schemes),
sni.as_ref().map(Cow::Borrowed),
Some(T::VERSION),
))?;
self.sni = sni;
let (suite, skxg) = self.choose_suite_and_kx_group(
suites,
credentials.signer.scheme(),
input
.client_hello
.named_groups
.as_deref()
.unwrap_or_default(),
&input.client_hello.cipher_suites,
)?;
debug!("decided upon suite {suite:?}");
output.output(OutputEvent::CipherSuite(suite.into()));
suite
.server_handler()
.handle_client_hello(suite, skxg, credentials, input, self, output)
}
fn choose_suite_and_kx_group<T: Suite + 'static>(
&self,
suites: &[&'static T],
sig_scheme: SignatureScheme,
client_groups: &[NamedGroup],
client_suites: &[CipherSuite],
) -> Result<(&'static T, &'static dyn SupportedKxGroup), PeerIncompatible>
where
SupportedCipherSuite: From<&'static T>,
dyn CipherSuiteSelector: VersionSuiteSelector<T>,
{
let mut ecdhe_possible = false;
let mut ffdhe_possible = false;
let mut ffdhe_offered = false;
let mut supported_groups: Vec<&'static dyn SupportedKxGroup> =
Vec::with_capacity(client_groups.len());
for offered_group in client_groups {
let supported = self
.config
.provider
.find_kx_group(*offered_group, T::VERSION);
match offered_group.key_exchange_algorithm() {
KeyExchangeAlgorithm::DHE => {
ffdhe_possible |= supported.is_some();
ffdhe_offered = true;
}
KeyExchangeAlgorithm::ECDHE => {
ecdhe_possible |= supported.is_some();
}
}
if let Some(supported) = supported {
supported_groups.push(supported);
}
}
let first_supported_dhe_kxg = if T::VERSION == ProtocolVersion::TLSv1_2 {
let first_supported_dhe_kxg = self
.config
.provider
.kx_groups
.iter()
.find(|skxg| skxg.name().key_exchange_algorithm() == KeyExchangeAlgorithm::DHE);
ffdhe_possible |= !ffdhe_offered && first_supported_dhe_kxg.is_some();
first_supported_dhe_kxg
} else {
None
};
if !ecdhe_possible && !ffdhe_possible {
return Err(PeerIncompatible::NoKxGroupsInCommon);
}
let mut client_suites = client_suites
.iter()
.filter_map(|&suite| {
let &suite = suites
.iter()
.find(|ss| ss.suite() == suite)?;
(suite.usable_for_signature_scheme(sig_scheme)
&& (ecdhe_possible && suite.usable_for_kx_algorithm(KeyExchangeAlgorithm::ECDHE)
|| ffdhe_possible && suite.usable_for_kx_algorithm(KeyExchangeAlgorithm::DHE)))
.then_some(suite)
});
let suite = self
.config
.cipher_suite_selector
.select(&mut client_suites, suites)
.ok_or(PeerIncompatible::NoCipherSuitesInCommon)?;
let maybe_skxg = supported_groups
.iter()
.find(|kx_group| {
suite.usable_for_kx_algorithm(kx_group.name().key_exchange_algorithm())
});
if T::VERSION == ProtocolVersion::TLSv1_3 {
return Ok((suite, *maybe_skxg.unwrap()));
}
match maybe_skxg {
Some(skxg) => Ok((suite, *skxg)),
None if suite.usable_for_kx_algorithm(KeyExchangeAlgorithm::DHE) => {
if let Some(server_selected_ffdhe_skxg) = first_supported_dhe_kxg {
Ok((suite, *server_selected_ffdhe_skxg))
} else {
Err(PeerIncompatible::NoKxGroupsInCommon)
}
}
None => Err(PeerIncompatible::NoKxGroupsInCommon),
}
}
pub(super) fn randoms(&self, input: &ClientHelloInput<'_>) -> Result<ConnectionRandoms, Error> {
Ok(ConnectionRandoms::new(
input.client_hello.random,
Random::new(self.config.provider.secure_random)?,
))
}
}
impl ExpectClientHello {
pub(crate) fn handle<'m>(
self,
input: Input<'m>,
output: &mut dyn Output<'_>,
) -> Result<ServerState, Error> {
let input = ClientHelloInput::from_input(&input)?;
self.with_input(input, output)
}
fn set_resumption_data(&mut self, resumption_data: &[u8]) -> Result<(), Error> {
self.resumption_data = resumption_data.to_vec();
Ok(())
}
}
impl From<Box<ExpectClientHello>> for ServerState {
fn from(value: Box<ExpectClientHello>) -> Self {
Self::ClientHello(value)
}
}
pub(crate) trait ServerHandler<T>: fmt::Debug + Sealed + Send + Sync {
fn handle_client_hello(
&self,
suite: &'static T,
kx_group: &'static dyn SupportedKxGroup,
credentials: SelectedCredential,
input: ClientHelloInput<'_>,
st: ExpectClientHello,
output: &mut dyn Output<'_>,
) -> Result<ServerState, Error>;
}
pub(crate) struct ClientHelloInput<'a> {
pub(super) message: &'a Message<'a>,
pub(super) client_hello: &'a ClientHelloPayload,
pub(super) sig_schemes: &'a [SignatureScheme],
pub(super) proof: HandshakeAlignedProof,
}
impl<'a> ClientHelloInput<'a> {
pub(super) fn from_input(input: &'a Input<'a>) -> Result<Self, Error> {
let client_hello = require_handshake_msg!(
input.message,
HandshakeType::ClientHello,
HandshakePayload::ClientHello
)?;
trace!("we got a clienthello {client_hello:?}");
if !client_hello
.compression_methods
.contains(&Compression::Null)
{
return Err(PeerIncompatible::NullCompressionRequired.into());
}
let proof = input.check_aligned_handshake()?;
let sig_schemes = client_hello
.signature_schemes
.as_deref()
.ok_or(PeerIncompatible::SignatureAlgorithmsExtensionRequired)?;
Ok(ClientHelloInput {
message: &input.message,
client_hello,
sig_schemes,
proof,
})
}
}
pub(crate) enum HandshakeHashOrBuffer {
Buffer(HandshakeHashBuffer),
Hash(HandshakeHash),
}
impl HandshakeHashOrBuffer {
pub(super) fn start(self, hash: &'static dyn Hash) -> Result<HandshakeHash, Error> {
match self {
Self::Buffer(inner) => Ok(inner.start_hash(hash)),
Self::Hash(inner) if inner.algorithm() == hash.algorithm() => Ok(inner),
_ => Err(PeerMisbehaved::HandshakeHashVariedAfterRetry.into()),
}
}
}