use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;
use subtle::ConstantTimeEq;
use super::config::{ClientConfig, ClientSessionKey, ClientSessionStore};
use super::ech::EchStatus;
use super::hs::{
ClientHandler, ClientHelloInput, ClientSessionValue, ClientState, ExpectServerHello,
GroupAndKeyShare, process_alpn_protocol,
};
use super::{
ClientAuthDetails, ClientHelloDetails, Retrieved, ServerCertDetails, Tls13ClientSessionInput,
Tls13Session,
};
use crate::check::inappropriate_handshake_message;
use crate::common_state::{
EarlyDataEvent, Event, HandshakeFlightTls13, HandshakeKind, Output, OutputEvent, Side,
};
use crate::conn::kernel::KernelState;
use crate::conn::{ConnectionRandoms, Input, TrafficTemperCounters};
use crate::crypto::cipher::Payload;
use crate::crypto::hash::Hash;
use crate::crypto::kx::{ActiveKeyExchange, HybridKeyExchange, SharedSecret, StartedKeyExchange};
use crate::crypto::{Identity, SelectedCredential, SignatureScheme, Signer};
use crate::enums::{CertificateType, ContentType, HandshakeType, ProtocolVersion};
use crate::error::{
ApiMisuse, Error, InvalidMessage, PeerIncompatible, PeerMisbehaved, RejectedEch,
};
use crate::hash_hs::{HandshakeHash, HandshakeHashBuffer};
use crate::log::{debug, trace, warn};
use crate::msgs::{
CERTIFICATE_MAX_SIZE_LIMIT, CertificatePayloadTls13, ChangeCipherSpecPayload, ClientExtensions,
Codec, EchConfigPayload, ExtensionType, HandshakeMessagePayload, HandshakePayload,
KeyShareEntry, KeyUpdateRequest, MaybeEmpty, Message, MessagePayload,
NewSessionTicketPayloadTls13, PresharedKeyBinder, PresharedKeyIdentity, PresharedKeyOffer,
ServerExtensions, ServerHelloPayload, SizedPayload,
};
use crate::sealed::Sealed;
use crate::suites::PartiallyExtractedSecrets;
use crate::sync::Arc;
use crate::tls13::key_schedule::{
KeyScheduleEarlyClient, KeyScheduleHandshake, KeySchedulePreHandshake, KeyScheduleResumption,
KeyScheduleTrafficReceive, KeyScheduleTrafficSend,
};
use crate::tls13::{
Tls13CipherSuite, construct_client_verify_message, construct_server_verify_message,
};
use crate::verify::{self, DigitallySignedStruct, ServerIdentity, SignatureVerificationInput};
use crate::{ConnectionTrafficSecrets, KeyLog, compress, crypto};
#[expect(private_interfaces)]
pub(crate) enum Tls13State {
EncryptedExtensions(Box<ExpectEncryptedExtensions>),
CertificateOrCompressedCertificateOrCertReq(
Box<ExpectCertificateOrCompressedCertificateOrCertReq>,
),
CertificateOrCompressedCertificate(Box<ExpectCertificateOrCompressedCertificate>),
CertificateOrCertReq(Box<ExpectCertificateOrCertReq>),
Certificate(Box<ExpectCertificate>),
CertificateVerify(Box<ExpectCertificateVerify>),
Finished(Box<ExpectFinished>),
Traffic(Box<ExpectTraffic>),
QuicTraffic(Box<ExpectQuicTraffic>),
}
impl Tls13State {
pub(crate) fn handle<'m>(
self,
input: Input<'m>,
output: &mut dyn Output<'m>,
) -> Result<ClientState, Error> {
match self {
Self::EncryptedExtensions(e) => e.handle(input, output),
Self::CertificateOrCompressedCertificateOrCertReq(e) => e.handle(input, output),
Self::CertificateOrCompressedCertificate(e) => e.handle(input, output),
Self::CertificateOrCertReq(e) => e.handle(input, output),
Self::Certificate(e) => e.handle(input, output),
Self::CertificateVerify(e) => e.handle(input, output),
Self::Finished(e) => e.handle(input, output),
Self::Traffic(e) => e.handle(input, output),
Self::QuicTraffic(e) => e.handle(input, output),
}
}
}
pub(crate) static TLS13_HANDLER: &dyn ClientHandler<Tls13CipherSuite> = &Handler;
#[derive(Debug)]
struct Handler;
impl ClientHandler<Tls13CipherSuite> for Handler {
fn handle_server_hello(
&self,
suite: &'static Tls13CipherSuite,
server_hello: &ServerHelloPayload,
input: &Input<'_>,
mut st: ExpectServerHello,
output: &mut dyn Output<'_>,
) -> Result<ClientState, Error> {
let mut transcript = st
.transcript_buffer
.start_hash(suite.common.hash_provider);
transcript.add_message(&input.message);
let mut randoms = ConnectionRandoms::new(st.input.random, server_hello.random);
if !server_hello.only_contains(ALLOWED_PLAINTEXT_EXTS) {
return Err(PeerMisbehaved::UnexpectedCleartextExtension.into());
}
let their_key_share = server_hello
.key_share
.as_ref()
.ok_or(PeerMisbehaved::MissingKeyShare)?;
let ClientHelloInput {
config,
resuming,
mut sent_tls13_fake_ccs,
mut hello,
session_key,
protocol,
..
} = st.input;
let mut resuming_session = match resuming {
Some(Retrieved {
value: ClientSessionValue::Tls13(value),
..
}) => Some(value),
_ => None,
};
let our_key_share = st.offered_key_share.unwrap();
let our_key_share = KeyExchangeChoice::new(&config, output, our_key_share, their_key_share)
.map_err(|_| PeerMisbehaved::WrongGroupForKeyShare)?;
let (key_schedule_pre_handshake, in_early_traffic) =
match (server_hello.preshared_key, st.early_data_key_schedule) {
(Some(selected_psk), Some((early_key_schedule, in_early_traffic))) => {
match &resuming_session {
Some(resuming) => {
let Some(resuming_suite) = suite.can_resume_from(resuming.suite) else {
return Err(
PeerMisbehaved::ResumptionOfferedWithIncompatibleCipherSuite
.into(),
);
};
if in_early_traffic && resuming_suite != suite {
return Err(
PeerMisbehaved::EarlyDataOfferedWithVariedCipherSuite.into()
);
}
if selected_psk != 0 {
return Err(PeerMisbehaved::SelectedInvalidPsk.into());
}
debug!("Resuming using PSK");
}
_ => {
return Err(PeerMisbehaved::SelectedUnofferedPsk.into());
}
}
(
KeySchedulePreHandshake::from(early_key_schedule),
in_early_traffic,
)
}
_ => {
debug!("Not resuming");
output.emit(Event::EarlyData(EarlyDataEvent::Rejected));
resuming_session.take();
(
KeySchedulePreHandshake::new(Side::Client, protocol, suite),
false,
)
}
};
let shared_secret = our_key_share.complete(their_key_share.payload.bytes())?;
let key_schedule = key_schedule_pre_handshake.into_handshake(shared_secret);
if let Some(ech_state) = st.ech_state {
let Message {
payload:
MessagePayload::Handshake {
encoded: server_hello_encoded,
..
},
..
} = &input.message
else {
unreachable!("ServerHello is a handshake message");
};
st.ech_status = match ech_state.confirm_acceptance(
&key_schedule,
server_hello,
server_hello_encoded,
suite.common.hash_provider,
)? {
Some(mut accepted) => {
accepted
.transcript
.add_message(&input.message);
transcript = accepted.transcript;
randoms.client = accepted.random.0;
hello.sent_extensions = accepted.sent_extensions;
EchStatus::Accepted
}
None => EchStatus::Rejected,
};
output.emit(Event::EchStatus(st.ech_status));
}
config
.resumption
.store
.set_kx_hint(session_key.clone(), their_key_share.group);
let proof = input.check_aligned_handshake()?;
let hash_at_client_recvd_server_hello = transcript.current_hash();
let key_schedule = key_schedule.derive_client_handshake_secrets(
in_early_traffic,
hash_at_client_recvd_server_hello,
suite,
&*config.key_log,
&randoms.client,
output,
&proof,
);
if !key_schedule.protocol().is_quic() {
emit_fake_ccs(&mut sent_tls13_fake_ccs, output);
}
output.output(OutputEvent::HandshakeKind(
match (&resuming_session, st.done_retry) {
(Some(_), true) => HandshakeKind::ResumedWithHelloRetryRequest,
(None, true) => HandshakeKind::FullWithHelloRetryRequest,
(Some(_), false) => HandshakeKind::Resumed,
(None, false) => HandshakeKind::Full,
},
));
Ok(Box::new(ExpectEncryptedExtensions {
hs: HandshakeState {
config,
session_key,
randoms,
transcript,
key_schedule,
},
resuming_session,
suite,
hello,
ech_status: st.ech_status,
in_early_traffic,
})
.into())
}
}
impl Sealed for Handler {}
enum KeyExchangeChoice {
Whole(Box<dyn ActiveKeyExchange>),
Component(Box<dyn HybridKeyExchange>),
}
impl KeyExchangeChoice {
fn new(
config: &Arc<ClientConfig>,
output: &mut dyn Output<'_>,
our_key_share: GroupAndKeyShare,
their_key_share: &KeyShareEntry,
) -> Result<Self, ()> {
if our_key_share.share.group() == their_key_share.group {
output.output(OutputEvent::KeyExchangeGroup(our_key_share.group));
return Ok(Self::Whole(our_key_share.share.into_single()));
}
let (hybrid_key_share, actual_skxg) = our_key_share
.share
.as_hybrid_checked(&config.provider().kx_groups, ProtocolVersion::TLSv1_3)
.ok_or(())?;
if hybrid_key_share.component().0 != their_key_share.group {
return Err(());
}
let StartedKeyExchange::Hybrid(hybrid_key_share) = our_key_share.share else {
return Err(()); };
output.output(OutputEvent::KeyExchangeGroup(actual_skxg));
Ok(Self::Component(hybrid_key_share))
}
fn complete(self, peer_pub_key: &[u8]) -> Result<SharedSecret, Error> {
match self {
Self::Whole(akx) => akx.complete(peer_pub_key),
Self::Component(akx) => akx.complete_component(peer_pub_key),
}
}
}
pub(super) fn initial_key_share(
config: &ClientConfig,
session_key: &ClientSessionKey<'_>,
) -> Result<GroupAndKeyShare, Error> {
let group = config
.resumption
.store
.kx_hint(session_key)
.and_then(|group_name| {
config
.provider()
.find_kx_group(group_name, ProtocolVersion::TLSv1_3)
})
.unwrap_or_else(|| {
config
.provider()
.kx_groups
.iter()
.copied()
.next()
.expect("No kx groups configured")
});
GroupAndKeyShare::new(group)
}
pub(super) fn fill_in_psk_binder(
key_schedule: &KeyScheduleEarlyClient,
transcript: &HandshakeHashBuffer,
hmp: &mut HandshakeMessagePayload<'_>,
) {
let binder_plaintext = hmp.encoding_for_binder_signing();
let handshake_hash = transcript.hash_given(key_schedule.hash(), &binder_plaintext);
let real_binder = key_schedule.resumption_psk_binder_key_and_sign_verify_data(&handshake_hash);
if let HandshakePayload::ClientHello(ch) = &mut hmp.0 {
if let Some(PresharedKeyOffer {
binders,
identities,
}) = &mut ch.preshared_key_offer
{
debug_assert_eq!(identities.len(), 1);
debug_assert_eq!(binders.len(), 1);
debug_assert_eq!(binders[0].as_ref().len(), real_binder.as_ref().len());
binders[0] = PresharedKeyBinder::from(real_binder.as_ref().to_vec());
}
};
}
pub(super) fn prepare_resumption(
config: &ClientConfig,
output: &mut dyn Output<'_>,
resuming_session: &Retrieved<&Tls13Session>,
exts: &mut ClientExtensions<'_>,
doing_retry: bool,
) -> bool {
let resuming_suite = resuming_session.suite;
output.output(OutputEvent::CipherSuite(resuming_suite.into()));
let max_early_data_size = resuming_session.max_early_data_size;
let early_data_enabled = if config.enable_early_data && max_early_data_size > 0 && !doing_retry
{
output.emit(Event::EarlyData(EarlyDataEvent::Enable(
max_early_data_size as usize,
)));
exts.early_data_request = Some(());
true
} else {
false
};
let obfuscated_ticket_age = resuming_session.obfuscated_ticket_age();
let binder_len = resuming_suite
.common
.hash_provider
.output_len();
let binder = vec![0u8; binder_len];
let psk_identity =
PresharedKeyIdentity::new(resuming_session.ticket().to_vec(), obfuscated_ticket_age);
let psk_offer = PresharedKeyOffer::new(psk_identity, binder);
exts.preshared_key_offer = Some(psk_offer);
early_data_enabled
}
pub(super) fn derive_early_traffic_secret(
key_log: &dyn KeyLog,
output: &mut dyn Output<'_>,
hash_alg: &'static dyn Hash,
early_key_schedule: &KeyScheduleEarlyClient,
sent_tls13_fake_ccs: &mut bool,
transcript_buffer: &HandshakeHashBuffer,
client_random: &[u8; 32],
) {
if !early_key_schedule.protocol().is_quic() {
emit_fake_ccs(sent_tls13_fake_ccs, output);
}
let client_hello_hash = transcript_buffer.hash_given(hash_alg, &[]);
early_key_schedule.client_early_traffic_secret(
&client_hello_hash,
key_log,
client_random,
output,
);
output.output(OutputEvent::EarlyExporter(
early_key_schedule.early_exporter(&client_hello_hash, key_log, client_random),
));
output.emit(Event::EarlyData(EarlyDataEvent::Start));
trace!("Starting early data traffic");
}
pub(super) fn emit_fake_ccs(sent_tls13_fake_ccs: &mut bool, output: &mut dyn Output<'_>) {
if core::mem::replace(sent_tls13_fake_ccs, true) {
return;
}
output.send_msg(
Message {
version: ProtocolVersion::TLSv1_2,
payload: MessagePayload::ChangeCipherSpec(ChangeCipherSpecPayload {}),
},
false,
);
}
fn validate_encrypted_extensions(
hello: &ClientHelloDetails,
exts: &ServerExtensions<'_>,
) -> Result<(), Error> {
if hello.server_sent_unsolicited_extensions(exts, &[]) {
return Err(PeerMisbehaved::UnsolicitedEncryptedExtension.into());
}
if exts.contains_any(ALLOWED_PLAINTEXT_EXTS) || exts.contains_any(DISALLOWED_TLS13_EXTS) {
return Err(PeerMisbehaved::DisallowedEncryptedExtension.into());
}
Ok(())
}
struct ExpectEncryptedExtensions {
hs: HandshakeState,
resuming_session: Option<Tls13Session>,
suite: &'static Tls13CipherSuite,
hello: ClientHelloDetails,
ech_status: EchStatus,
in_early_traffic: bool,
}
impl ExpectEncryptedExtensions {
fn handle(
mut self: Box<Self>,
Input { message, .. }: Input<'_>,
output: &mut dyn Output<'_>,
) -> Result<ClientState, Error> {
let exts = require_handshake_msg!(
message,
HandshakeType::EncryptedExtensions,
HandshakePayload::EncryptedExtensions
)?;
debug!("TLS1.3 encrypted extensions: {exts:?}");
self.hs.transcript.add_message(&message);
validate_encrypted_extensions(&self.hello, exts)?;
let selected_alpn = exts
.selected_protocol
.as_ref()
.map(|protocol| protocol.as_ref());
process_alpn_protocol(
output,
&self.hello.alpn_protocols,
selected_alpn,
self.hs.config.check_selected_alpn,
)?;
if self
.hs
.key_schedule
.protocol()
.is_quic()
&& selected_alpn.is_none()
&& !self.hello.alpn_protocols.is_empty()
{
return Err(Error::NoApplicationProtocol);
}
check_cert_type(
self.hs
.config
.resolver()
.supported_certificate_types(),
exts.client_certificate_type,
)?;
check_cert_type(
self.hs
.config
.verifier()
.supported_certificate_types(),
exts.server_certificate_type,
)?;
let ech_retry_configs = match (self.ech_status, &exts.encrypted_client_hello_ack) {
(EchStatus::NotOffered | EchStatus::Accepted, Some(_)) => {
return Err(PeerMisbehaved::UnsolicitedEchExtension.into());
}
(EchStatus::Rejected, ext) => ext
.as_ref()
.map(|ext| ext.retry_configs.to_vec()),
_ => None,
};
let ech = Ech {
retry_configs: ech_retry_configs,
status: self.ech_status,
};
let quic_params = if let Some(quic) = output.quic() {
let Some(quic_params) = exts.transport_parameters.as_ref() else {
return Err(PeerMisbehaved::MissingQuicTransportParameters.into());
};
quic.transport_parameters(quic_params.clone().into_vec());
Some(SizedPayload::from(Payload::new(
quic_params.clone().into_vec(),
)))
} else {
None
};
match self.resuming_session {
Some(resuming_session) => {
if self.in_early_traffic {
match exts.early_data_ack {
Some(()) => output.emit(Event::EarlyData(EarlyDataEvent::Accepted)),
None => {
output.emit(Event::EarlyData(EarlyDataEvent::Rejected));
self.hs
.key_schedule
.set_handshake_encrypter(output.send());
self.in_early_traffic = false;
}
}
}
let cert_verified = verify::PeerVerified::assertion();
let sig_verified = verify::HandshakeSignatureValid::assertion();
Ok(Box::new(ExpectFinished {
hs: self.hs,
session_input: Tls13ClientSessionInput {
suite: self.suite,
peer_identity: resuming_session.peer_identity().clone(),
quic_params,
},
client_auth: None,
cert_verified,
sig_verified,
ech,
in_early_traffic: self.in_early_traffic,
})
.into())
}
_ => {
if exts.early_data_ack.is_some() {
return Err(PeerMisbehaved::EarlyDataExtensionWithoutResumption.into());
}
let expected_certificate_type = exts
.server_certificate_type
.unwrap_or_default();
Ok(if self.hello.offered_cert_compression {
Box::new(ExpectCertificateOrCompressedCertificateOrCertReq {
hs: self.hs,
suite: self.suite,
quic_params,
ech,
expected_certificate_type,
negotiated_client_type: exts.client_certificate_type,
})
.into()
} else {
Box::new(ExpectCertificateOrCertReq {
hs: self.hs,
suite: self.suite,
quic_params,
ech,
expected_certificate_type,
negotiated_client_type: exts.client_certificate_type,
})
.into()
})
}
}
}
}
impl From<Box<ExpectEncryptedExtensions>> for ClientState {
fn from(value: Box<ExpectEncryptedExtensions>) -> Self {
Self::Tls13(Tls13State::EncryptedExtensions(value))
}
}
fn check_cert_type(
client_supported: &[CertificateType],
server_negotiated: Option<CertificateType>,
) -> Result<(), Error> {
match server_negotiated {
None if client_supported.is_empty()
|| client_supported.contains(&CertificateType::X509) =>
{
Ok(())
}
Some(ct) if client_supported.contains(&ct) => Ok(()),
_ => Err(Error::PeerIncompatible(
PeerIncompatible::IncorrectCertificateTypeExtension,
)),
}
}
struct ExpectCertificateOrCompressedCertificateOrCertReq {
hs: HandshakeState,
suite: &'static Tls13CipherSuite,
quic_params: Option<SizedPayload<'static, u16, MaybeEmpty>>,
ech: Ech,
expected_certificate_type: CertificateType,
negotiated_client_type: Option<CertificateType>,
}
impl ExpectCertificateOrCompressedCertificateOrCertReq {
fn handle(
self: Box<Self>,
input: Input<'_>,
_output: &mut dyn Output<'_>,
) -> Result<ClientState, Error> {
match input.message.payload {
MessagePayload::Handshake {
parsed: HandshakeMessagePayload(HandshakePayload::CertificateTls13(..)),
..
} => ExpectCertificate {
hs: self.hs,
suite: self.suite,
quic_params: self.quic_params,
client_auth: None,
ech: self.ech,
expected_certificate_type: self.expected_certificate_type,
}
.handle_input(input),
MessagePayload::Handshake {
parsed: HandshakeMessagePayload(HandshakePayload::CompressedCertificate(..)),
..
} => ExpectCompressedCertificate {
hs: self.hs,
suite: self.suite,
quic_params: self.quic_params,
client_auth: None,
ech: self.ech,
expected_certificate_type: self.expected_certificate_type,
}
.handle_input(input),
MessagePayload::Handshake {
parsed: HandshakeMessagePayload(HandshakePayload::CertificateRequestTls13(..)),
..
} => ExpectCertificateRequest {
hs: self.hs,
suite: self.suite,
quic_params: self.quic_params,
offered_cert_compression: true,
ech: self.ech,
expected_certificate_type: self.expected_certificate_type,
negotiated_client_type: self.negotiated_client_type,
}
.handle_input(input),
payload => Err(inappropriate_handshake_message(
&payload,
&[ContentType::Handshake],
&[
HandshakeType::Certificate,
HandshakeType::CertificateRequest,
HandshakeType::CompressedCertificate,
],
)),
}
}
}
impl From<Box<ExpectCertificateOrCompressedCertificateOrCertReq>> for ClientState {
fn from(value: Box<ExpectCertificateOrCompressedCertificateOrCertReq>) -> Self {
Self::Tls13(Tls13State::CertificateOrCompressedCertificateOrCertReq(
value,
))
}
}
struct ExpectCertificateOrCompressedCertificate {
hs: HandshakeState,
suite: &'static Tls13CipherSuite,
quic_params: Option<SizedPayload<'static, u16, MaybeEmpty>>,
client_auth: Option<ClientAuthDetails>,
ech: Ech,
expected_certificate_type: CertificateType,
}
impl ExpectCertificateOrCompressedCertificate {
fn handle(
self: Box<Self>,
input: Input<'_>,
_output: &mut dyn Output<'_>,
) -> Result<ClientState, Error> {
match input.message.payload {
MessagePayload::Handshake {
parsed: HandshakeMessagePayload(HandshakePayload::CertificateTls13(..)),
..
} => ExpectCertificate {
hs: self.hs,
suite: self.suite,
quic_params: self.quic_params,
client_auth: self.client_auth,
ech: self.ech,
expected_certificate_type: self.expected_certificate_type,
}
.handle_input(input),
MessagePayload::Handshake {
parsed: HandshakeMessagePayload(HandshakePayload::CompressedCertificate(..)),
..
} => ExpectCompressedCertificate {
hs: self.hs,
suite: self.suite,
quic_params: self.quic_params,
client_auth: self.client_auth,
ech: self.ech,
expected_certificate_type: self.expected_certificate_type,
}
.handle_input(input),
payload => Err(inappropriate_handshake_message(
&payload,
&[ContentType::Handshake],
&[
HandshakeType::Certificate,
HandshakeType::CompressedCertificate,
],
)),
}
}
}
impl From<Box<ExpectCertificateOrCompressedCertificate>> for ClientState {
fn from(value: Box<ExpectCertificateOrCompressedCertificate>) -> Self {
Self::Tls13(Tls13State::CertificateOrCompressedCertificate(value))
}
}
struct ExpectCertificateOrCertReq {
hs: HandshakeState,
suite: &'static Tls13CipherSuite,
quic_params: Option<SizedPayload<'static, u16, MaybeEmpty>>,
ech: Ech,
expected_certificate_type: CertificateType,
negotiated_client_type: Option<CertificateType>,
}
impl ExpectCertificateOrCertReq {
fn handle(
self: Box<Self>,
input: Input<'_>,
_output: &mut dyn Output<'_>,
) -> Result<ClientState, Error> {
match input.message.payload {
MessagePayload::Handshake {
parsed: HandshakeMessagePayload(HandshakePayload::CertificateTls13(..)),
..
} => ExpectCertificate {
hs: self.hs,
suite: self.suite,
quic_params: self.quic_params,
client_auth: None,
ech: self.ech,
expected_certificate_type: self.expected_certificate_type,
}
.handle_input(input),
MessagePayload::Handshake {
parsed: HandshakeMessagePayload(HandshakePayload::CertificateRequestTls13(..)),
..
} => ExpectCertificateRequest {
hs: self.hs,
suite: self.suite,
quic_params: self.quic_params,
offered_cert_compression: false,
ech: self.ech,
expected_certificate_type: self.expected_certificate_type,
negotiated_client_type: self.negotiated_client_type,
}
.handle_input(input),
payload => Err(inappropriate_handshake_message(
&payload,
&[ContentType::Handshake],
&[
HandshakeType::Certificate,
HandshakeType::CertificateRequest,
],
)),
}
}
}
impl From<Box<ExpectCertificateOrCertReq>> for ClientState {
fn from(value: Box<ExpectCertificateOrCertReq>) -> Self {
Self::Tls13(Tls13State::CertificateOrCertReq(value))
}
}
struct ExpectCertificateRequest {
hs: HandshakeState,
suite: &'static Tls13CipherSuite,
quic_params: Option<SizedPayload<'static, u16, MaybeEmpty>>,
offered_cert_compression: bool,
ech: Ech,
expected_certificate_type: CertificateType,
negotiated_client_type: Option<CertificateType>,
}
impl ExpectCertificateRequest {
fn handle_input(mut self, Input { message, .. }: Input<'_>) -> Result<ClientState, Error> {
let certreq = &require_handshake_msg!(
message,
HandshakeType::CertificateRequest,
HandshakePayload::CertificateRequestTls13
)?;
self.hs.transcript.add_message(&message);
debug!("Got CertificateRequest {certreq:?}");
if !certreq.context.is_empty() {
warn!("Server sent non-empty certreq context");
return Err(InvalidMessage::InvalidCertRequest.into());
}
let compat_sigschemes = certreq
.extensions
.signature_algorithms
.as_deref()
.unwrap_or_default()
.iter()
.copied()
.filter(SignatureScheme::supported_in_tls13)
.collect::<Vec<SignatureScheme>>();
if compat_sigschemes.is_empty() {
return Err(PeerIncompatible::NoCertificateRequestSignatureSchemesInCommon.into());
}
let compat_compressor = certreq
.extensions
.certificate_compression_algorithms
.as_deref()
.and_then(|offered| {
self.hs
.config
.cert_compressors
.iter()
.find(|compressor| offered.contains(&compressor.algorithm()))
})
.copied();
let client_auth = ClientAuthDetails::resolve(
self.negotiated_client_type
.unwrap_or(CertificateType::X509),
self.hs.config.resolver().as_ref(),
certreq
.extensions
.authority_names
.as_deref(),
&compat_sigschemes,
Some(certreq.context.to_vec()),
compat_compressor,
);
Ok(if self.offered_cert_compression {
Box::new(ExpectCertificateOrCompressedCertificate {
hs: self.hs,
suite: self.suite,
quic_params: self.quic_params,
client_auth: Some(client_auth),
ech: self.ech,
expected_certificate_type: self.expected_certificate_type,
})
.into()
} else {
Box::new(ExpectCertificate {
hs: self.hs,
suite: self.suite,
quic_params: self.quic_params,
client_auth: Some(client_auth),
ech: self.ech,
expected_certificate_type: self.expected_certificate_type,
})
.into()
})
}
}
struct ExpectCompressedCertificate {
hs: HandshakeState,
suite: &'static Tls13CipherSuite,
quic_params: Option<SizedPayload<'static, u16, MaybeEmpty>>,
client_auth: Option<ClientAuthDetails>,
ech: Ech,
expected_certificate_type: CertificateType,
}
impl ExpectCompressedCertificate {
fn handle_input(mut self, Input { message, .. }: Input<'_>) -> Result<ClientState, Error> {
self.hs.transcript.add_message(&message);
let compressed_cert = require_handshake_msg_move!(
message,
HandshakeType::CompressedCertificate,
HandshakePayload::CompressedCertificate
)?;
let selected_decompressor = self
.hs
.config
.cert_decompressors
.iter()
.find(|item| item.algorithm() == compressed_cert.alg);
let Some(decompressor) = selected_decompressor else {
return Err(PeerMisbehaved::SelectedUnofferedCertCompression.into());
};
if compressed_cert.uncompressed_len as usize > CERTIFICATE_MAX_SIZE_LIMIT {
return Err(InvalidMessage::CertificatePayloadTooLarge.into());
}
let mut decompress_buffer = vec![0u8; compressed_cert.uncompressed_len as usize];
if let Err(compress::DecompressionFailed) =
decompressor.decompress(compressed_cert.compressed.bytes(), &mut decompress_buffer)
{
return Err(PeerMisbehaved::InvalidCertCompression.into());
}
let cert_payload = CertificatePayloadTls13::read_bytes(&decompress_buffer)?;
trace!(
"Server certificate decompressed using {:?} ({} bytes -> {})",
compressed_cert.alg,
compressed_cert.compressed.bytes().len(),
compressed_cert.uncompressed_len,
);
ExpectCertificate {
hs: self.hs,
suite: self.suite,
quic_params: self.quic_params,
client_auth: self.client_auth,
ech: self.ech,
expected_certificate_type: self.expected_certificate_type,
}
.handle_cert_payload(cert_payload)
}
}
struct ExpectCertificate {
hs: HandshakeState,
suite: &'static Tls13CipherSuite,
quic_params: Option<SizedPayload<'static, u16, MaybeEmpty>>,
client_auth: Option<ClientAuthDetails>,
ech: Ech,
expected_certificate_type: CertificateType,
}
impl ExpectCertificate {
fn handle_input(mut self, Input { message, .. }: Input<'_>) -> Result<ClientState, Error> {
self.hs.transcript.add_message(&message);
self.handle_cert_payload(require_handshake_msg_move!(
message,
HandshakeType::Certificate,
HandshakePayload::CertificateTls13
)?)
}
fn handle_cert_payload(
self,
cert_chain: CertificatePayloadTls13<'_>,
) -> Result<ClientState, Error> {
if !cert_chain.context.is_empty() {
return Err(InvalidMessage::InvalidCertRequest.into());
}
let end_entity_ocsp = cert_chain.end_entity_ocsp().to_vec();
let server_cert = ServerCertDetails::new(
cert_chain
.into_certificate_chain()
.into_owned(),
end_entity_ocsp,
);
Ok(Box::new(ExpectCertificateVerify {
hs: self.hs,
suite: self.suite,
quic_params: self.quic_params,
server_cert,
client_auth: self.client_auth,
ech: self.ech,
expected_certificate_type: self.expected_certificate_type,
})
.into())
}
}
impl ExpectCertificate {
fn handle(
self: Box<Self>,
input: Input<'_>,
_output: &mut dyn Output<'_>,
) -> Result<ClientState, Error> {
self.handle_input(input)
}
}
impl From<Box<ExpectCertificate>> for ClientState {
fn from(value: Box<ExpectCertificate>) -> Self {
Self::Tls13(Tls13State::Certificate(value))
}
}
struct ExpectCertificateVerify {
hs: HandshakeState,
suite: &'static Tls13CipherSuite,
quic_params: Option<SizedPayload<'static, u16, MaybeEmpty>>,
server_cert: ServerCertDetails,
client_auth: Option<ClientAuthDetails>,
ech: Ech,
expected_certificate_type: CertificateType,
}
impl ExpectCertificateVerify {
fn handle(
mut self: Box<Self>,
Input { message, .. }: Input<'_>,
_output: &mut dyn Output<'_>,
) -> Result<ClientState, Error> {
let cert_verify = require_handshake_msg!(
message,
HandshakeType::CertificateVerify,
HandshakePayload::CertificateVerify
)?;
trace!("Server cert is {:?}", self.server_cert.cert_chain);
let identity = Identity::from_peer(
self.server_cert.cert_chain.0,
self.expected_certificate_type,
)?
.ok_or(PeerMisbehaved::NoCertificatesPresented)?;
let cert_verified = self
.hs
.config
.verifier()
.verify_identity(&ServerIdentity {
identity: &identity,
server_name: &self.hs.session_key.server_name,
ocsp_response: &self.server_cert.ocsp_response,
now: self.hs.config.current_time()?,
})?;
let handshake_hash = self.hs.transcript.current_hash();
let sig_verified = self
.hs
.config
.verifier()
.verify_tls13_signature(&SignatureVerificationInput {
message: construct_server_verify_message(&handshake_hash).as_ref(),
signer: &identity.as_signer(),
signature: cert_verify,
})?;
self.hs.transcript.add_message(&message);
Ok(Box::new(ExpectFinished {
hs: self.hs,
session_input: Tls13ClientSessionInput {
suite: self.suite,
peer_identity: identity,
quic_params: self.quic_params,
},
client_auth: self.client_auth,
cert_verified,
sig_verified,
ech: self.ech,
in_early_traffic: false,
})
.into())
}
}
impl From<Box<ExpectCertificateVerify>> for ClientState {
fn from(value: Box<ExpectCertificateVerify>) -> Self {
Self::Tls13(Tls13State::CertificateVerify(value))
}
}
fn emit_compressed_certificate_tls13(
flight: &mut HandshakeFlightTls13<'_>,
credentials: &SelectedCredential,
auth_context: Option<Vec<u8>>,
compressor: &dyn compress::CertCompressor,
config: &ClientConfig,
) {
let mut cert_payload =
CertificatePayloadTls13::new(credentials.identity.as_certificates(), None);
cert_payload.context = auth_context
.clone()
.unwrap_or_default()
.into();
let Ok(compressed) = config
.cert_compression_cache
.compression_for(compressor, &cert_payload)
else {
return emit_certificate_tls13(flight, Some(credentials), auth_context);
};
flight.add(HandshakeMessagePayload(
HandshakePayload::CompressedCertificate(compressed.compressed_cert_payload()),
));
}
fn emit_certificate_tls13(
flight: &mut HandshakeFlightTls13<'_>,
credentials: Option<&SelectedCredential>,
auth_context: Option<Vec<u8>>,
) {
let mut cert_payload = match credentials {
Some(credentials) => {
CertificatePayloadTls13::new(credentials.identity.as_certificates(), None)
}
None => CertificatePayloadTls13::new([].into_iter(), None),
};
cert_payload.context = auth_context.unwrap_or_default().into();
flight.add(HandshakeMessagePayload(HandshakePayload::CertificateTls13(
cert_payload,
)));
}
fn emit_certverify_tls13(
flight: &mut HandshakeFlightTls13<'_>,
signer: Box<dyn Signer>,
) -> Result<(), Error> {
let message = construct_client_verify_message(&flight.transcript.current_hash());
let scheme = signer.scheme();
let sig = signer.sign(message.as_ref())?;
let dss = DigitallySignedStruct::new(scheme, sig);
flight.add(HandshakeMessagePayload(
HandshakePayload::CertificateVerify(dss),
));
Ok(())
}
fn emit_finished_tls13(
flight: &mut HandshakeFlightTls13<'_>,
verify_data: &crypto::hmac::PublicTag,
) {
let verify_data_payload = Payload::new(verify_data.as_ref());
flight.add(HandshakeMessagePayload(HandshakePayload::Finished(
verify_data_payload,
)));
}
fn emit_end_of_early_data_tls13(transcript: &mut HandshakeHash, output: &mut dyn Output<'_>) {
let m = Message {
version: ProtocolVersion::TLSv1_3,
payload: MessagePayload::handshake(HandshakeMessagePayload(
HandshakePayload::EndOfEarlyData,
)),
};
transcript.add_message(&m);
output.send_msg(m, true);
}
struct ExpectFinished {
hs: HandshakeState,
session_input: Tls13ClientSessionInput,
client_auth: Option<ClientAuthDetails>,
cert_verified: verify::PeerVerified,
sig_verified: verify::HandshakeSignatureValid,
ech: Ech,
in_early_traffic: bool,
}
impl ExpectFinished {
fn handle(
self: Box<Self>,
input: Input<'_>,
output: &mut dyn Output<'_>,
) -> Result<ClientState, Error> {
let mut st = *self;
let finished = require_handshake_msg!(
input.message,
HandshakeType::Finished,
HandshakePayload::Finished
)?;
let proof = input.check_aligned_handshake()?;
let handshake_hash = st.hs.transcript.current_hash();
let expect_verify_data = st
.hs
.key_schedule
.sign_server_finish(&handshake_hash, &proof);
let fin = match ConstantTimeEq::ct_eq(expect_verify_data.as_ref(), finished.bytes()).into()
{
true => verify::FinishedMessageVerified::assertion(),
false => {
return Err(PeerMisbehaved::IncorrectFinished.into());
}
};
st.hs
.transcript
.add_message(&input.message);
let hash_after_handshake = st.hs.transcript.current_hash();
if st.in_early_traffic {
if !st.hs.key_schedule.protocol().is_quic() {
emit_end_of_early_data_tls13(&mut st.hs.transcript, output);
}
output.emit(Event::EarlyData(EarlyDataEvent::Finished));
st.hs
.key_schedule
.set_handshake_encrypter(output.send());
}
let mut flight = HandshakeFlightTls13::new(&mut st.hs.transcript);
if let Some(client_auth) = st.client_auth {
match client_auth {
ClientAuthDetails::Empty {
auth_context_tls13: auth_context,
} => {
emit_certificate_tls13(&mut flight, None, auth_context);
}
ClientAuthDetails::Verify {
auth_context_tls13: auth_context,
..
} if st.ech.status == EchStatus::Rejected => {
emit_certificate_tls13(&mut flight, None, auth_context);
}
ClientAuthDetails::Verify {
credentials,
auth_context_tls13: auth_context,
compressor,
} => {
if let Some(compressor) = compressor {
emit_compressed_certificate_tls13(
&mut flight,
&credentials,
auth_context,
compressor,
&st.hs.config,
);
} else {
emit_certificate_tls13(&mut flight, Some(&credentials), auth_context);
}
emit_certverify_tls13(&mut flight, credentials.signer)?;
}
}
}
let (key_schedule_pre_finished, verify_data) = st
.hs
.key_schedule
.into_pre_finished_client_traffic(
hash_after_handshake,
flight.transcript.current_hash(),
&*st.hs.config.key_log,
&st.hs.randoms.client,
);
emit_finished_tls13(&mut flight, &verify_data);
flight.finish(output);
st.hs
.config
.resumption
.store
.remove_tls12_session(&st.hs.session_key);
let (key_schedule, exporter, resumption) =
key_schedule_pre_finished.into_traffic(output, st.hs.transcript.current_hash(), &proof);
let (key_schedule_send, key_schedule_recv) = key_schedule.split();
output.output(OutputEvent::PeerIdentity(
st.session_input.peer_identity.clone(),
));
output.output(OutputEvent::Exporter(Box::new(exporter)));
output
.send()
.update_key_schedule(Box::new(key_schedule_send));
output.start_traffic();
if st.ech.status == EchStatus::Rejected {
return Err(RejectedEch {
retry_configs: st.ech.retry_configs,
}
.into());
}
let protocol = key_schedule_recv.protocol();
let st = ExpectTraffic {
config: st.hs.config.clone(),
session_storage: st.hs.config.resumption.store.clone(),
session_key: st.hs.session_key,
session_input: st.session_input,
key_schedule_recv,
resumption,
counters: TrafficTemperCounters::default(),
_cert_verified: st.cert_verified,
_sig_verified: st.sig_verified,
_fin_verified: fin,
};
Ok(match protocol.is_quic() {
true => Box::new(ExpectQuicTraffic(st)).into(),
false => Box::new(st).into(),
})
}
}
impl From<Box<ExpectFinished>> for ClientState {
fn from(value: Box<ExpectFinished>) -> Self {
Self::Tls13(Tls13State::Finished(value))
}
}
struct HandshakeState {
config: Arc<ClientConfig>,
session_key: ClientSessionKey<'static>,
randoms: ConnectionRandoms,
transcript: HandshakeHash,
key_schedule: KeyScheduleHandshake,
}
pub(super) struct ExpectTraffic {
config: Arc<ClientConfig>,
session_storage: Arc<dyn ClientSessionStore>,
session_key: ClientSessionKey<'static>,
session_input: Tls13ClientSessionInput,
key_schedule_recv: KeyScheduleTrafficReceive,
resumption: KeyScheduleResumption,
counters: TrafficTemperCounters,
_cert_verified: verify::PeerVerified,
_sig_verified: verify::HandshakeSignatureValid,
_fin_verified: verify::FinishedMessageVerified,
}
impl ExpectTraffic {
fn handle_new_ticket_impl(&self, nst: &NewSessionTicketPayloadTls13) -> Result<(), Error> {
let secret = self
.resumption
.derive_ticket_psk(nst.nonce.bytes());
let now = self.config.current_time()?;
let value = Tls13Session::new(nst, self.session_input.clone(), secret.as_ref(), now);
if self
.key_schedule_recv
.protocol()
.is_quic()
{
if let Some(sz) = nst.extensions.max_early_data_size {
if sz != 0 && sz != 0xffff_ffff {
return Err(PeerMisbehaved::InvalidMaxEarlyDataSize.into());
}
}
}
self.session_storage
.insert_tls13_ticket(self.session_key.clone(), value);
Ok(())
}
fn handle_new_ticket_tls13(
&self,
output: &mut dyn Output<'_>,
nst: &NewSessionTicketPayloadTls13,
) -> Result<(), Error> {
let received = &mut output.receive().tls13_tickets_received;
*received = received.saturating_add(1);
self.handle_new_ticket_impl(nst)
}
fn handle_key_update(
&mut self,
input: Input<'_>,
output: &mut dyn Output<'_>,
key_update_request: &KeyUpdateRequest,
) -> Result<(), Error> {
if self
.key_schedule_recv
.protocol()
.is_quic()
{
return Err(PeerMisbehaved::KeyUpdateReceivedInQuicConnection.into());
}
let proof = input.check_aligned_handshake()?;
match *key_update_request {
KeyUpdateRequest::UpdateNotRequested => {}
KeyUpdateRequest::UpdateRequested => output.send().ensure_key_update_queued(),
_ => return Err(InvalidMessage::InvalidKeyUpdate.into()),
}
self.key_schedule_recv
.update_decrypter(output.receive(), &proof);
Ok(())
}
}
impl ExpectTraffic {
fn handle<'m>(
mut self: Box<Self>,
input: Input<'m>,
output: &mut dyn Output<'m>,
) -> Result<ClientState, Error> {
match input.message.payload {
MessagePayload::ApplicationData(payload) => {
self.counters.received_app_data();
output.received_plaintext(payload);
}
MessagePayload::Handshake {
parsed: HandshakeMessagePayload(HandshakePayload::NewSessionTicketTls13(new_ticket)),
..
} => {
self.counters
.received_handshake_message()?;
self.handle_new_ticket_tls13(output, &new_ticket)?
}
MessagePayload::Handshake {
parsed: HandshakeMessagePayload(HandshakePayload::KeyUpdate(key_update)),
..
} => {
self.counters
.received_handshake_message()?;
self.handle_key_update(input, output, &key_update)?
}
payload => {
return Err(inappropriate_handshake_message(
&payload,
&[ContentType::ApplicationData, ContentType::Handshake],
&[HandshakeType::NewSessionTicket, HandshakeType::KeyUpdate],
));
}
}
Ok(self.into())
}
pub(super) fn into_external_state(
self: Box<Self>,
send_keys: &Option<Box<KeyScheduleTrafficSend>>,
) -> Result<(PartiallyExtractedSecrets, Box<dyn KernelState + 'static>), Error> {
if !self.config.enable_secret_extraction {
return Err(ApiMisuse::SecretExtractionRequiresPriorOptIn.into());
}
let Some(send_keys) = send_keys else {
return Err(Error::Unreachable(
"send_keys required for TLS1.3 into_external_state",
));
};
Ok((
PartiallyExtractedSecrets {
tx: send_keys.extract()?,
rx: self.key_schedule_recv.extract()?,
},
self,
))
}
}
impl KernelState for ExpectTraffic {
fn update_rx_secret(&mut self) -> Result<ConnectionTrafficSecrets, Error> {
self.key_schedule_recv
.refresh_traffic_secret()
}
fn handle_new_session_ticket(
&self,
message: &NewSessionTicketPayloadTls13,
) -> Result<(), Error> {
self.handle_new_ticket_impl(message)
}
}
impl From<Box<ExpectTraffic>> for ClientState {
fn from(value: Box<ExpectTraffic>) -> Self {
Self::Tls13(Tls13State::Traffic(value))
}
}
pub(super) struct ExpectQuicTraffic(ExpectTraffic);
impl ExpectQuicTraffic {
fn handle(
self: Box<Self>,
Input { message, .. }: Input<'_>,
output: &mut dyn Output<'_>,
) -> Result<ClientState, Error> {
let nst = require_handshake_msg!(
message,
HandshakeType::NewSessionTicket,
HandshakePayload::NewSessionTicketTls13
)?;
self.0
.handle_new_ticket_tls13(output, nst)?;
Ok(self.into())
}
pub(super) fn into_external_state(
self: Box<Self>,
send_keys: &Option<Box<KeyScheduleTrafficSend>>,
) -> Result<(PartiallyExtractedSecrets, Box<dyn KernelState + 'static>), Error> {
if !self.0.config.enable_secret_extraction {
return Err(ApiMisuse::SecretExtractionRequiresPriorOptIn.into());
}
let Some(send_keys) = send_keys else {
return Err(Error::Unreachable(
"send_keys required for TLS1.3 into_external_state",
));
};
Ok((
PartiallyExtractedSecrets {
tx: send_keys.extract()?,
rx: self.0.key_schedule_recv.extract()?,
},
self,
))
}
}
impl KernelState for ExpectQuicTraffic {
fn update_rx_secret(&mut self) -> Result<ConnectionTrafficSecrets, Error> {
Err(Error::Unreachable(
"KeyUpdate is not supported for QUIC connections",
))
}
fn handle_new_session_ticket(&self, nst: &NewSessionTicketPayloadTls13) -> Result<(), Error> {
self.0.handle_new_ticket_impl(nst)
}
}
impl From<Box<ExpectQuicTraffic>> for ClientState {
fn from(value: Box<ExpectQuicTraffic>) -> Self {
Self::Tls13(Tls13State::QuicTraffic(value))
}
}
struct Ech {
status: EchStatus,
retry_configs: Option<Vec<EchConfigPayload>>,
}
const ALLOWED_PLAINTEXT_EXTS: &[ExtensionType] = &[
ExtensionType::KeyShare,
ExtensionType::PreSharedKey,
ExtensionType::SupportedVersions,
];
const DISALLOWED_TLS13_EXTS: &[ExtensionType] = &[
ExtensionType::ECPointFormats,
ExtensionType::SessionTicket,
ExtensionType::RenegotiationInfo,
ExtensionType::ExtendedMasterSecret,
];