use alloc::boxed::Box;
use alloc::sync::Arc;
use alloc::vec;
use alloc::vec::Vec;
use pki_types::ServerName;
use subtle::ConstantTimeEq;
use super::client_conn::ClientConnectionData;
use super::hs::ClientContext;
use crate::check::inappropriate_handshake_message;
use crate::client::common::{ClientAuthDetails, ClientHelloDetails, ServerCertDetails};
use crate::client::ech::{self, EchState, EchStatus};
use crate::client::{hs, ClientConfig, ClientSessionStore};
use crate::common_state::{CommonState, HandshakeKind, KxState, Protocol, Side, State};
use crate::conn::ConnectionRandoms;
use crate::crypto::ActiveKeyExchange;
use crate::enums::{
AlertDescription, ContentType, HandshakeType, ProtocolVersion, SignatureScheme,
};
use crate::error::{Error, InvalidMessage, PeerIncompatible, PeerMisbehaved};
use crate::hash_hs::{HandshakeHash, HandshakeHashBuffer};
use crate::log::{debug, trace, warn};
use crate::msgs::base::{Payload, PayloadU8};
use crate::msgs::ccs::ChangeCipherSpecPayload;
use crate::msgs::codec::{Codec, Reader};
use crate::msgs::enums::{ExtensionType, KeyUpdateRequest};
use crate::msgs::handshake::{
CertificatePayloadTls13, ClientExtension, EchConfigPayload, HandshakeMessagePayload,
HandshakePayload, HasServerExtensions, NewSessionTicketPayloadTls13, PresharedKeyIdentity,
PresharedKeyOffer, ServerExtension, ServerHelloPayload, CERTIFICATE_MAX_SIZE_LIMIT,
};
use crate::msgs::message::{Message, MessagePayload};
use crate::msgs::persist;
use crate::sign::{CertifiedKey, Signer};
use crate::suites::PartiallyExtractedSecrets;
use crate::tls13::key_schedule::{
KeyScheduleEarly, KeyScheduleHandshake, KeySchedulePreHandshake, KeyScheduleTraffic,
};
use crate::tls13::{
construct_client_verify_message, construct_server_verify_message, Tls13CipherSuite,
};
use crate::verify::{self, DigitallySignedStruct};
use crate::{compress, crypto, KeyLog};
static ALLOWED_PLAINTEXT_EXTS: &[ExtensionType] = &[
ExtensionType::KeyShare,
ExtensionType::PreSharedKey,
ExtensionType::SupportedVersions,
];
static DISALLOWED_TLS13_EXTS: &[ExtensionType] = &[
ExtensionType::ECPointFormats,
ExtensionType::SessionTicket,
ExtensionType::RenegotiationInfo,
ExtensionType::ExtendedMasterSecret,
];
pub(super) fn handle_server_hello(
config: Arc<ClientConfig>,
cx: &mut ClientContext<'_>,
server_hello: &ServerHelloPayload,
mut resuming_session: Option<persist::Tls13ClientSessionValue>,
server_name: ServerName<'static>,
mut randoms: ConnectionRandoms,
suite: &'static Tls13CipherSuite,
mut transcript: HandshakeHash,
early_key_schedule: Option<KeyScheduleEarly>,
mut hello: ClientHelloDetails,
our_key_share: Box<dyn ActiveKeyExchange>,
mut sent_tls13_fake_ccs: bool,
server_hello_msg: &Message<'_>,
ech_state: Option<EchState>,
) -> hs::NextStateOrError<'static> {
validate_server_hello(cx.common, server_hello)?;
let their_key_share = server_hello
.key_share()
.ok_or_else(|| {
cx.common.send_fatal_alert(
AlertDescription::MissingExtension,
PeerMisbehaved::MissingKeyShare,
)
})?;
if our_key_share.group() != their_key_share.group {
return Err({
cx.common.send_fatal_alert(
AlertDescription::IllegalParameter,
PeerMisbehaved::WrongGroupForKeyShare,
)
});
}
let key_schedule_pre_handshake = if let (Some(selected_psk), Some(early_key_schedule)) =
(server_hello.psk_index(), early_key_schedule)
{
if let Some(ref resuming) = resuming_session {
let resuming_suite = match suite.can_resume_from(resuming.suite()) {
Some(resuming) => resuming,
None => {
return Err({
cx.common.send_fatal_alert(
AlertDescription::IllegalParameter,
PeerMisbehaved::ResumptionOfferedWithIncompatibleCipherSuite,
)
});
}
};
if cx.data.early_data.is_enabled() && resuming_suite != suite {
return Err({
cx.common.send_fatal_alert(
AlertDescription::IllegalParameter,
PeerMisbehaved::EarlyDataOfferedWithVariedCipherSuite,
)
});
}
if selected_psk != 0 {
return Err({
cx.common.send_fatal_alert(
AlertDescription::IllegalParameter,
PeerMisbehaved::SelectedInvalidPsk,
)
});
}
debug!("Resuming using PSK");
} else {
return Err(PeerMisbehaved::SelectedUnofferedPsk.into());
}
KeySchedulePreHandshake::from(early_key_schedule)
} else {
debug!("Not resuming");
cx.data.early_data.rejected();
cx.common.early_traffic = false;
resuming_session.take();
KeySchedulePreHandshake::new(suite)
};
cx.common.kx_state.complete();
let shared_secret = our_key_share.complete(&their_key_share.payload.0)?;
let mut key_schedule = key_schedule_pre_handshake.into_handshake(shared_secret);
if let Some(ech_state) = ech_state {
cx.data.ech_status = match ech_state.confirm_acceptance(
&mut key_schedule,
server_hello,
suite.common.hash_provider,
)? {
Some(mut accepted) => {
accepted
.transcript
.add_message(server_hello_msg);
transcript = accepted.transcript;
randoms.client = accepted.random.0;
hello.sent_extensions = accepted.sent_extensions;
EchStatus::Accepted
}
None => EchStatus::Rejected,
};
}
config
.resumption
.store
.set_kx_hint(server_name.clone(), their_key_share.group);
cx.common.check_aligned_handshake()?;
let hash_at_client_recvd_server_hello = transcript.current_hash();
let key_schedule = key_schedule.derive_client_handshake_secrets(
cx.data.early_data.is_enabled(),
hash_at_client_recvd_server_hello,
suite,
&*config.key_log,
&randoms.client,
cx.common,
);
emit_fake_ccs(&mut sent_tls13_fake_ccs, cx.common);
Ok(Box::new(ExpectEncryptedExtensions {
config,
resuming_session,
server_name,
randoms,
suite,
transcript,
key_schedule,
hello,
}))
}
fn validate_server_hello(
common: &mut CommonState,
server_hello: &ServerHelloPayload,
) -> Result<(), Error> {
for ext in &server_hello.extensions {
if !ALLOWED_PLAINTEXT_EXTS.contains(&ext.ext_type()) {
return Err(common.send_fatal_alert(
AlertDescription::UnsupportedExtension,
PeerMisbehaved::UnexpectedCleartextExtension,
));
}
}
Ok(())
}
pub(super) fn initial_key_share(
config: &ClientConfig,
server_name: &ServerName<'_>,
kx_state: &mut KxState,
) -> Result<Box<dyn ActiveKeyExchange>, Error> {
let group = config
.resumption
.store
.kx_hint(server_name)
.and_then(|group_name| config.find_kx_group(group_name))
.unwrap_or_else(|| {
config
.provider
.kx_groups
.iter()
.copied()
.next()
.expect("No kx groups configured")
});
*kx_state = KxState::Start(group);
group.start()
}
pub(super) fn fill_in_psk_binder(
resuming: &persist::Tls13ClientSessionValue,
transcript: &HandshakeHashBuffer,
hmp: &mut HandshakeMessagePayload<'_>,
) -> KeyScheduleEarly {
let suite = resuming.suite();
let suite_hash = suite.common.hash_provider;
let binder_plaintext = hmp.encoding_for_binder_signing();
let handshake_hash = transcript.hash_given(suite_hash, &binder_plaintext);
let key_schedule = KeyScheduleEarly::new(suite, resuming.secret());
let real_binder = key_schedule.resumption_psk_binder_key_and_sign_verify_data(&handshake_hash);
if let HandshakePayload::ClientHello(ref mut ch) = hmp.payload {
ch.set_psk_binder(real_binder.as_ref());
};
key_schedule
}
pub(super) fn prepare_resumption(
config: &ClientConfig,
cx: &mut ClientContext<'_>,
resuming_session: &persist::Retrieved<&persist::Tls13ClientSessionValue>,
exts: &mut Vec<ClientExtension>,
doing_retry: bool,
) {
let resuming_suite = resuming_session.suite();
cx.common.suite = Some(resuming_suite.into());
cx.data.resumption_ciphersuite = Some(resuming_suite.into());
let max_early_data_size = resuming_session.max_early_data_size();
if config.enable_early_data && max_early_data_size > 0 && !doing_retry {
cx.data
.early_data
.enable(max_early_data_size as usize);
exts.push(ClientExtension::EarlyData);
}
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_ext = PresharedKeyOffer::new(psk_identity, binder);
exts.push(ClientExtension::PresharedKey(psk_ext));
}
pub(super) fn derive_early_traffic_secret(
key_log: &dyn KeyLog,
cx: &mut ClientContext<'_>,
resuming_suite: &'static Tls13CipherSuite,
early_key_schedule: &KeyScheduleEarly,
sent_tls13_fake_ccs: &mut bool,
transcript_buffer: &HandshakeHashBuffer,
client_random: &[u8; 32],
) {
emit_fake_ccs(sent_tls13_fake_ccs, cx.common);
let client_hello_hash = transcript_buffer.hash_given(resuming_suite.common.hash_provider, &[]);
early_key_schedule.client_early_traffic_secret(
&client_hello_hash,
key_log,
client_random,
cx.common,
);
cx.common.early_traffic = true;
trace!("Starting early data traffic");
}
pub(super) fn emit_fake_ccs(sent_tls13_fake_ccs: &mut bool, common: &mut CommonState) {
if common.is_quic() {
return;
}
if core::mem::replace(sent_tls13_fake_ccs, true) {
return;
}
let m = Message {
version: ProtocolVersion::TLSv1_2,
payload: MessagePayload::ChangeCipherSpec(ChangeCipherSpecPayload {}),
};
common.send_msg(m, false);
}
fn validate_encrypted_extensions(
common: &mut CommonState,
hello: &ClientHelloDetails,
exts: &Vec<ServerExtension>,
) -> Result<(), Error> {
if exts.has_duplicate_extension() {
return Err(common.send_fatal_alert(
AlertDescription::DecodeError,
PeerMisbehaved::DuplicateEncryptedExtensions,
));
}
if hello.server_sent_unsolicited_extensions(exts, &[]) {
return Err(common.send_fatal_alert(
AlertDescription::UnsupportedExtension,
PeerMisbehaved::UnsolicitedEncryptedExtension,
));
}
for ext in exts {
if ALLOWED_PLAINTEXT_EXTS.contains(&ext.ext_type())
|| DISALLOWED_TLS13_EXTS.contains(&ext.ext_type())
{
return Err(common.send_fatal_alert(
AlertDescription::UnsupportedExtension,
PeerMisbehaved::DisallowedEncryptedExtension,
));
}
}
Ok(())
}
struct ExpectEncryptedExtensions {
config: Arc<ClientConfig>,
resuming_session: Option<persist::Tls13ClientSessionValue>,
server_name: ServerName<'static>,
randoms: ConnectionRandoms,
suite: &'static Tls13CipherSuite,
transcript: HandshakeHash,
key_schedule: KeyScheduleHandshake,
hello: ClientHelloDetails,
}
impl State<ClientConnectionData> for ExpectEncryptedExtensions {
fn handle<'m>(
mut self: Box<Self>,
cx: &mut ClientContext<'_>,
m: Message<'m>,
) -> hs::NextStateOrError<'m>
where
Self: 'm,
{
let exts = require_handshake_msg!(
m,
HandshakeType::EncryptedExtensions,
HandshakePayload::EncryptedExtensions
)?;
debug!("TLS1.3 encrypted extensions: {:?}", exts);
self.transcript.add_message(&m);
validate_encrypted_extensions(cx.common, &self.hello, exts)?;
hs::process_alpn_protocol(cx.common, &self.config, exts.alpn_protocol())?;
let ech_retry_configs = match (cx.data.ech_status, exts.server_ech_extension()) {
(EchStatus::NotOffered | EchStatus::Accepted, Some(_)) => {
return Err(cx.common.send_fatal_alert(
AlertDescription::UnsupportedExtension,
PeerMisbehaved::UnsolicitedEchExtension,
))
}
(EchStatus::Rejected, ext) => ext.map(|ext| ext.retry_configs.to_vec()),
_ => None,
};
if cx.common.is_quic() {
match exts.quic_params_extension() {
Some(params) => cx.common.quic.params = Some(params),
None => {
return Err(cx
.common
.missing_extension(PeerMisbehaved::MissingQuicTransportParameters));
}
}
}
if let Some(resuming_session) = self.resuming_session {
let was_early_traffic = cx.common.early_traffic;
if was_early_traffic {
if exts.early_data_extension_offered() {
cx.data.early_data.accepted();
} else {
cx.data.early_data.rejected();
cx.common.early_traffic = false;
}
}
if was_early_traffic && !cx.common.early_traffic {
self.key_schedule
.set_handshake_encrypter(cx.common);
}
cx.common.peer_certificates = Some(
resuming_session
.server_cert_chain()
.clone(),
);
cx.common.handshake_kind = Some(HandshakeKind::Resumed);
let cert_verified = verify::ServerCertVerified::assertion();
let sig_verified = verify::HandshakeSignatureValid::assertion();
Ok(Box::new(ExpectFinished {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
client_auth: None,
cert_verified,
sig_verified,
ech_retry_configs,
}))
} else {
if exts.early_data_extension_offered() {
return Err(PeerMisbehaved::EarlyDataExtensionWithoutResumption.into());
}
cx.common
.handshake_kind
.get_or_insert(HandshakeKind::Full);
Ok(if self.hello.offered_cert_compression {
Box::new(ExpectCertificateOrCompressedCertificateOrCertReq {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
ech_retry_configs,
})
} else {
Box::new(ExpectCertificateOrCertReq {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
ech_retry_configs,
})
})
}
}
fn into_owned(self: Box<Self>) -> hs::NextState<'static> {
self
}
}
struct ExpectCertificateOrCompressedCertificateOrCertReq {
config: Arc<ClientConfig>,
server_name: ServerName<'static>,
randoms: ConnectionRandoms,
suite: &'static Tls13CipherSuite,
transcript: HandshakeHash,
key_schedule: KeyScheduleHandshake,
ech_retry_configs: Option<Vec<EchConfigPayload>>,
}
impl State<ClientConnectionData> for ExpectCertificateOrCompressedCertificateOrCertReq {
fn handle<'m>(
self: Box<Self>,
cx: &mut ClientContext<'_>,
m: Message<'m>,
) -> hs::NextStateOrError<'m>
where
Self: 'm,
{
match m.payload {
MessagePayload::Handshake {
parsed:
HandshakeMessagePayload {
payload: HandshakePayload::CertificateTls13(..),
..
},
..
} => Box::new(ExpectCertificate {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
client_auth: None,
message_already_in_transcript: false,
ech_retry_configs: self.ech_retry_configs,
})
.handle(cx, m),
MessagePayload::Handshake {
parsed:
HandshakeMessagePayload {
payload: HandshakePayload::CompressedCertificate(..),
..
},
..
} => Box::new(ExpectCompressedCertificate {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
client_auth: None,
ech_retry_configs: self.ech_retry_configs,
})
.handle(cx, m),
MessagePayload::Handshake {
parsed:
HandshakeMessagePayload {
payload: HandshakePayload::CertificateRequestTls13(..),
..
},
..
} => Box::new(ExpectCertificateRequest {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
offered_cert_compression: true,
ech_retry_configs: self.ech_retry_configs,
})
.handle(cx, m),
payload => Err(inappropriate_handshake_message(
&payload,
&[ContentType::Handshake],
&[
HandshakeType::Certificate,
HandshakeType::CertificateRequest,
HandshakeType::CompressedCertificate,
],
)),
}
}
fn into_owned(self: Box<Self>) -> hs::NextState<'static> {
self
}
}
struct ExpectCertificateOrCompressedCertificate {
config: Arc<ClientConfig>,
server_name: ServerName<'static>,
randoms: ConnectionRandoms,
suite: &'static Tls13CipherSuite,
transcript: HandshakeHash,
key_schedule: KeyScheduleHandshake,
client_auth: Option<ClientAuthDetails>,
ech_retry_configs: Option<Vec<EchConfigPayload>>,
}
impl State<ClientConnectionData> for ExpectCertificateOrCompressedCertificate {
fn handle<'m>(
self: Box<Self>,
cx: &mut ClientContext<'_>,
m: Message<'m>,
) -> hs::NextStateOrError<'m>
where
Self: 'm,
{
match m.payload {
MessagePayload::Handshake {
parsed:
HandshakeMessagePayload {
payload: HandshakePayload::CertificateTls13(..),
..
},
..
} => Box::new(ExpectCertificate {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
client_auth: self.client_auth,
message_already_in_transcript: false,
ech_retry_configs: self.ech_retry_configs,
})
.handle(cx, m),
MessagePayload::Handshake {
parsed:
HandshakeMessagePayload {
payload: HandshakePayload::CompressedCertificate(..),
..
},
..
} => Box::new(ExpectCompressedCertificate {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
client_auth: self.client_auth,
ech_retry_configs: self.ech_retry_configs,
})
.handle(cx, m),
payload => Err(inappropriate_handshake_message(
&payload,
&[ContentType::Handshake],
&[
HandshakeType::Certificate,
HandshakeType::CompressedCertificate,
],
)),
}
}
fn into_owned(self: Box<Self>) -> hs::NextState<'static> {
self
}
}
struct ExpectCertificateOrCertReq {
config: Arc<ClientConfig>,
server_name: ServerName<'static>,
randoms: ConnectionRandoms,
suite: &'static Tls13CipherSuite,
transcript: HandshakeHash,
key_schedule: KeyScheduleHandshake,
ech_retry_configs: Option<Vec<EchConfigPayload>>,
}
impl State<ClientConnectionData> for ExpectCertificateOrCertReq {
fn handle<'m>(
self: Box<Self>,
cx: &mut ClientContext<'_>,
m: Message<'m>,
) -> hs::NextStateOrError<'m>
where
Self: 'm,
{
match m.payload {
MessagePayload::Handshake {
parsed:
HandshakeMessagePayload {
payload: HandshakePayload::CertificateTls13(..),
..
},
..
} => Box::new(ExpectCertificate {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
client_auth: None,
message_already_in_transcript: false,
ech_retry_configs: self.ech_retry_configs,
})
.handle(cx, m),
MessagePayload::Handshake {
parsed:
HandshakeMessagePayload {
payload: HandshakePayload::CertificateRequestTls13(..),
..
},
..
} => Box::new(ExpectCertificateRequest {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
offered_cert_compression: false,
ech_retry_configs: self.ech_retry_configs,
})
.handle(cx, m),
payload => Err(inappropriate_handshake_message(
&payload,
&[ContentType::Handshake],
&[
HandshakeType::Certificate,
HandshakeType::CertificateRequest,
],
)),
}
}
fn into_owned(self: Box<Self>) -> hs::NextState<'static> {
self
}
}
struct ExpectCertificateRequest {
config: Arc<ClientConfig>,
server_name: ServerName<'static>,
randoms: ConnectionRandoms,
suite: &'static Tls13CipherSuite,
transcript: HandshakeHash,
key_schedule: KeyScheduleHandshake,
offered_cert_compression: bool,
ech_retry_configs: Option<Vec<EchConfigPayload>>,
}
impl State<ClientConnectionData> for ExpectCertificateRequest {
fn handle<'m>(
mut self: Box<Self>,
cx: &mut ClientContext<'_>,
m: Message<'m>,
) -> hs::NextStateOrError<'m>
where
Self: 'm,
{
let certreq = &require_handshake_msg!(
m,
HandshakeType::CertificateRequest,
HandshakePayload::CertificateRequestTls13
)?;
self.transcript.add_message(&m);
debug!("Got CertificateRequest {:?}", certreq);
if !certreq.context.0.is_empty() {
warn!("Server sent non-empty certreq context");
return Err(cx.common.send_fatal_alert(
AlertDescription::DecodeError,
InvalidMessage::InvalidCertRequest,
));
}
let no_sigschemes = Vec::new();
let compat_sigschemes = certreq
.sigalgs_extension()
.unwrap_or(&no_sigschemes)
.iter()
.cloned()
.filter(SignatureScheme::supported_in_tls13)
.collect::<Vec<SignatureScheme>>();
if compat_sigschemes.is_empty() {
return Err(cx.common.send_fatal_alert(
AlertDescription::HandshakeFailure,
PeerIncompatible::NoCertificateRequestSignatureSchemesInCommon,
));
}
let compat_compressor = certreq
.certificate_compression_extension()
.and_then(|offered| {
self.config
.cert_compressors
.iter()
.find(|compressor| offered.contains(&compressor.algorithm()))
})
.cloned();
let client_auth = ClientAuthDetails::resolve(
self.config
.client_auth_cert_resolver
.as_ref(),
certreq.authorities_extension(),
&compat_sigschemes,
Some(certreq.context.0.clone()),
compat_compressor,
);
Ok(if self.offered_cert_compression {
Box::new(ExpectCertificateOrCompressedCertificate {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
client_auth: Some(client_auth),
ech_retry_configs: self.ech_retry_configs,
})
} else {
Box::new(ExpectCertificate {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
client_auth: Some(client_auth),
message_already_in_transcript: false,
ech_retry_configs: self.ech_retry_configs,
})
})
}
fn into_owned(self: Box<Self>) -> hs::NextState<'static> {
self
}
}
struct ExpectCompressedCertificate {
config: Arc<ClientConfig>,
server_name: ServerName<'static>,
randoms: ConnectionRandoms,
suite: &'static Tls13CipherSuite,
transcript: HandshakeHash,
key_schedule: KeyScheduleHandshake,
client_auth: Option<ClientAuthDetails>,
ech_retry_configs: Option<Vec<EchConfigPayload>>,
}
impl State<ClientConnectionData> for ExpectCompressedCertificate {
fn handle<'m>(
mut self: Box<Self>,
cx: &mut ClientContext<'_>,
m: Message<'m>,
) -> hs::NextStateOrError<'m>
where
Self: 'm,
{
self.transcript.add_message(&m);
let compressed_cert = require_handshake_msg_move!(
m,
HandshakeType::CompressedCertificate,
HandshakePayload::CompressedCertificate
)?;
let decompressor = match self
.config
.cert_decompressors
.iter()
.find(|item| item.algorithm() == compressed_cert.alg)
{
Some(dec) => dec,
None => {
return Err(cx.common.send_fatal_alert(
AlertDescription::BadCertificate,
PeerMisbehaved::SelectedUnofferedCertCompression,
));
}
};
if compressed_cert.uncompressed_len as usize > CERTIFICATE_MAX_SIZE_LIMIT {
return Err(cx.common.send_fatal_alert(
AlertDescription::BadCertificate,
InvalidMessage::MessageTooLarge,
));
}
let mut decompress_buffer = vec![0u8; compressed_cert.uncompressed_len as usize];
if let Err(compress::DecompressionFailed) =
decompressor.decompress(compressed_cert.compressed.0.bytes(), &mut decompress_buffer)
{
return Err(cx.common.send_fatal_alert(
AlertDescription::BadCertificate,
PeerMisbehaved::InvalidCertCompression,
));
}
let cert_payload =
match CertificatePayloadTls13::read(&mut Reader::init(&decompress_buffer)) {
Ok(cm) => cm,
Err(err) => {
return Err(cx
.common
.send_fatal_alert(AlertDescription::BadCertificate, err));
}
};
trace!(
"Server certificate decompressed using {:?} ({} bytes -> {})",
compressed_cert.alg,
compressed_cert
.compressed
.0
.bytes()
.len(),
compressed_cert.uncompressed_len,
);
let m = Message {
version: ProtocolVersion::TLSv1_3,
payload: MessagePayload::handshake(HandshakeMessagePayload {
typ: HandshakeType::Certificate,
payload: HandshakePayload::CertificateTls13(cert_payload.into_owned()),
}),
};
Box::new(ExpectCertificate {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
client_auth: self.client_auth,
message_already_in_transcript: true,
ech_retry_configs: self.ech_retry_configs,
})
.handle(cx, m)
}
fn into_owned(self: Box<Self>) -> hs::NextState<'static> {
self
}
}
struct ExpectCertificate {
config: Arc<ClientConfig>,
server_name: ServerName<'static>,
randoms: ConnectionRandoms,
suite: &'static Tls13CipherSuite,
transcript: HandshakeHash,
key_schedule: KeyScheduleHandshake,
client_auth: Option<ClientAuthDetails>,
message_already_in_transcript: bool,
ech_retry_configs: Option<Vec<EchConfigPayload>>,
}
impl State<ClientConnectionData> for ExpectCertificate {
fn handle<'m>(
mut self: Box<Self>,
cx: &mut ClientContext<'_>,
m: Message<'m>,
) -> hs::NextStateOrError<'m>
where
Self: 'm,
{
if !self.message_already_in_transcript {
self.transcript.add_message(&m);
}
let cert_chain = require_handshake_msg_move!(
m,
HandshakeType::Certificate,
HandshakePayload::CertificateTls13
)?;
if !cert_chain.context.0.is_empty() {
return Err(cx.common.send_fatal_alert(
AlertDescription::DecodeError,
InvalidMessage::InvalidCertRequest,
));
}
if cert_chain.any_entry_has_duplicate_extension()
|| cert_chain.any_entry_has_unknown_extension()
{
return Err(cx.common.send_fatal_alert(
AlertDescription::UnsupportedExtension,
PeerMisbehaved::BadCertChainExtensions,
));
}
let end_entity_ocsp = cert_chain.end_entity_ocsp();
let server_cert = ServerCertDetails::new(
cert_chain
.into_certificate_chain()
.into_owned(),
end_entity_ocsp,
);
Ok(Box::new(ExpectCertificateVerify {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
server_cert,
client_auth: self.client_auth,
ech_retry_configs: self.ech_retry_configs,
}))
}
fn into_owned(self: Box<Self>) -> hs::NextState<'static> {
self
}
}
struct ExpectCertificateVerify<'a> {
config: Arc<ClientConfig>,
server_name: ServerName<'static>,
randoms: ConnectionRandoms,
suite: &'static Tls13CipherSuite,
transcript: HandshakeHash,
key_schedule: KeyScheduleHandshake,
server_cert: ServerCertDetails<'a>,
client_auth: Option<ClientAuthDetails>,
ech_retry_configs: Option<Vec<EchConfigPayload>>,
}
impl State<ClientConnectionData> for ExpectCertificateVerify<'_> {
fn handle<'m>(
mut self: Box<Self>,
cx: &mut ClientContext<'_>,
m: Message<'m>,
) -> hs::NextStateOrError<'m>
where
Self: 'm,
{
let cert_verify = require_handshake_msg!(
m,
HandshakeType::CertificateVerify,
HandshakePayload::CertificateVerify
)?;
trace!("Server cert is {:?}", self.server_cert.cert_chain);
let (end_entity, intermediates) = self
.server_cert
.cert_chain
.split_first()
.ok_or(Error::NoCertificatesPresented)?;
let now = self.config.current_time()?;
let cert_verified = self
.config
.verifier
.verify_server_cert(
end_entity,
intermediates,
&self.server_name,
&self.server_cert.ocsp_response,
now,
)
.map_err(|err| {
cx.common
.send_cert_verify_error_alert(err)
})?;
let handshake_hash = self.transcript.current_hash();
let sig_verified = self
.config
.verifier
.verify_tls13_signature(
&construct_server_verify_message(&handshake_hash),
end_entity,
cert_verify,
)
.map_err(|err| {
cx.common
.send_cert_verify_error_alert(err)
})?;
cx.common.peer_certificates = Some(self.server_cert.cert_chain.into_owned());
self.transcript.add_message(&m);
Ok(Box::new(ExpectFinished {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
client_auth: self.client_auth,
cert_verified,
sig_verified,
ech_retry_configs: self.ech_retry_configs,
}))
}
fn into_owned(self: Box<Self>) -> hs::NextState<'static> {
Box::new(ExpectCertificateVerify {
config: self.config,
server_name: self.server_name,
randoms: self.randoms,
suite: self.suite,
transcript: self.transcript,
key_schedule: self.key_schedule,
server_cert: self.server_cert.into_owned(),
client_auth: self.client_auth,
ech_retry_configs: self.ech_retry_configs,
})
}
}
fn emit_compressed_certificate_tls13(
transcript: &mut HandshakeHash,
certkey: &CertifiedKey,
auth_context: Option<Vec<u8>>,
compressor: &dyn compress::CertCompressor,
config: &ClientConfig,
common: &mut CommonState,
) {
let mut cert_payload = CertificatePayloadTls13::new(certkey.cert.iter(), None);
cert_payload.context = PayloadU8::new(auth_context.clone().unwrap_or_default());
let compressed = match config
.cert_compression_cache
.compression_for(compressor, &cert_payload)
{
Ok(compressed) => compressed,
Err(_) => return emit_certificate_tls13(transcript, Some(certkey), auth_context, common),
};
let m = Message {
version: ProtocolVersion::TLSv1_3,
payload: MessagePayload::handshake(HandshakeMessagePayload {
typ: HandshakeType::CompressedCertificate,
payload: HandshakePayload::CompressedCertificate(compressed.compressed_cert_payload()),
}),
};
transcript.add_message(&m);
common.send_msg(m, true);
}
fn emit_certificate_tls13(
transcript: &mut HandshakeHash,
certkey: Option<&CertifiedKey>,
auth_context: Option<Vec<u8>>,
common: &mut CommonState,
) {
let certs = certkey
.map(|ck| ck.cert.as_ref())
.unwrap_or(&[][..]);
let mut cert_payload = CertificatePayloadTls13::new(certs.iter(), None);
cert_payload.context = PayloadU8::new(auth_context.unwrap_or_default());
let m = Message {
version: ProtocolVersion::TLSv1_3,
payload: MessagePayload::handshake(HandshakeMessagePayload {
typ: HandshakeType::Certificate,
payload: HandshakePayload::CertificateTls13(cert_payload),
}),
};
transcript.add_message(&m);
common.send_msg(m, true);
}
fn emit_certverify_tls13(
transcript: &mut HandshakeHash,
signer: &dyn Signer,
common: &mut CommonState,
) -> Result<(), Error> {
let message = construct_client_verify_message(&transcript.current_hash());
let scheme = signer.scheme();
let sig = signer.sign(&message)?;
let dss = DigitallySignedStruct::new(scheme, sig);
let m = Message {
version: ProtocolVersion::TLSv1_3,
payload: MessagePayload::handshake(HandshakeMessagePayload {
typ: HandshakeType::CertificateVerify,
payload: HandshakePayload::CertificateVerify(dss),
}),
};
transcript.add_message(&m);
common.send_msg(m, true);
Ok(())
}
fn emit_finished_tls13(
transcript: &mut HandshakeHash,
verify_data: &crypto::hmac::Tag,
common: &mut CommonState,
) {
let verify_data_payload = Payload::new(verify_data.as_ref());
let m = Message {
version: ProtocolVersion::TLSv1_3,
payload: MessagePayload::handshake(HandshakeMessagePayload {
typ: HandshakeType::Finished,
payload: HandshakePayload::Finished(verify_data_payload),
}),
};
transcript.add_message(&m);
common.send_msg(m, true);
}
fn emit_end_of_early_data_tls13(transcript: &mut HandshakeHash, common: &mut CommonState) {
if common.is_quic() {
return;
}
let m = Message {
version: ProtocolVersion::TLSv1_3,
payload: MessagePayload::handshake(HandshakeMessagePayload {
typ: HandshakeType::EndOfEarlyData,
payload: HandshakePayload::EndOfEarlyData,
}),
};
transcript.add_message(&m);
common.send_msg(m, true);
}
struct ExpectFinished {
config: Arc<ClientConfig>,
server_name: ServerName<'static>,
randoms: ConnectionRandoms,
suite: &'static Tls13CipherSuite,
transcript: HandshakeHash,
key_schedule: KeyScheduleHandshake,
client_auth: Option<ClientAuthDetails>,
cert_verified: verify::ServerCertVerified,
sig_verified: verify::HandshakeSignatureValid,
ech_retry_configs: Option<Vec<EchConfigPayload>>,
}
impl State<ClientConnectionData> for ExpectFinished {
fn handle<'m>(
self: Box<Self>,
cx: &mut ClientContext<'_>,
m: Message<'m>,
) -> hs::NextStateOrError<'m>
where
Self: 'm,
{
let mut st = *self;
let finished =
require_handshake_msg!(m, HandshakeType::Finished, HandshakePayload::Finished)?;
let handshake_hash = st.transcript.current_hash();
let expect_verify_data = st
.key_schedule
.sign_server_finish(&handshake_hash);
let fin = match ConstantTimeEq::ct_eq(expect_verify_data.as_ref(), finished.bytes()).into()
{
true => verify::FinishedMessageVerified::assertion(),
false => {
return Err(cx
.common
.send_fatal_alert(AlertDescription::DecryptError, Error::DecryptError));
}
};
st.transcript.add_message(&m);
let hash_after_handshake = st.transcript.current_hash();
if cx.common.early_traffic {
emit_end_of_early_data_tls13(&mut st.transcript, cx.common);
cx.common.early_traffic = false;
cx.data.early_data.finished();
st.key_schedule
.set_handshake_encrypter(cx.common);
}
if let Some(client_auth) = st.client_auth {
match client_auth {
ClientAuthDetails::Empty {
auth_context_tls13: auth_context,
} => {
emit_certificate_tls13(&mut st.transcript, None, auth_context, cx.common);
}
ClientAuthDetails::Verify {
auth_context_tls13: auth_context,
..
} if cx.data.ech_status == EchStatus::Rejected => {
emit_certificate_tls13(&mut st.transcript, None, auth_context, cx.common);
}
ClientAuthDetails::Verify {
certkey,
signer,
auth_context_tls13: auth_context,
compressor,
} => {
if let Some(compressor) = compressor {
emit_compressed_certificate_tls13(
&mut st.transcript,
&certkey,
auth_context,
compressor,
&st.config,
cx.common,
);
} else {
emit_certificate_tls13(
&mut st.transcript,
Some(&certkey),
auth_context,
cx.common,
);
}
emit_certverify_tls13(&mut st.transcript, signer.as_ref(), cx.common)?;
}
}
}
let (key_schedule_pre_finished, verify_data) = st
.key_schedule
.into_pre_finished_client_traffic(
hash_after_handshake,
st.transcript.current_hash(),
&*st.config.key_log,
&st.randoms.client,
);
emit_finished_tls13(&mut st.transcript, &verify_data, cx.common);
st.config
.resumption
.store
.remove_tls12_session(&st.server_name);
cx.common.check_aligned_handshake()?;
let key_schedule_traffic = key_schedule_pre_finished.into_traffic(cx.common);
cx.common
.start_traffic(&mut cx.sendable_plaintext);
if cx.data.ech_status == EchStatus::Rejected {
return Err(ech::fatal_alert_required(st.ech_retry_configs, cx.common));
}
let st = ExpectTraffic {
config: Arc::clone(&st.config),
session_storage: Arc::clone(&st.config.resumption.store),
server_name: st.server_name,
suite: st.suite,
transcript: st.transcript,
key_schedule: key_schedule_traffic,
_cert_verified: st.cert_verified,
_sig_verified: st.sig_verified,
_fin_verified: fin,
};
Ok(match cx.common.is_quic() {
true => Box::new(ExpectQuicTraffic(st)),
false => Box::new(st),
})
}
fn into_owned(self: Box<Self>) -> hs::NextState<'static> {
self
}
}
struct ExpectTraffic {
config: Arc<ClientConfig>,
session_storage: Arc<dyn ClientSessionStore>,
server_name: ServerName<'static>,
suite: &'static Tls13CipherSuite,
transcript: HandshakeHash,
key_schedule: KeyScheduleTraffic,
_cert_verified: verify::ServerCertVerified,
_sig_verified: verify::HandshakeSignatureValid,
_fin_verified: verify::FinishedMessageVerified,
}
impl ExpectTraffic {
fn handle_new_ticket_tls13(
&mut self,
cx: &mut ClientContext<'_>,
nst: &NewSessionTicketPayloadTls13,
) -> Result<(), Error> {
if nst.has_duplicate_extension() {
return Err(cx.common.send_fatal_alert(
AlertDescription::IllegalParameter,
PeerMisbehaved::DuplicateNewSessionTicketExtensions,
));
}
let handshake_hash = self.transcript.current_hash();
let secret = self
.key_schedule
.resumption_master_secret_and_derive_ticket_psk(&handshake_hash, &nst.nonce.0);
let now = self.config.current_time()?;
#[allow(unused_mut)]
let mut value = persist::Tls13ClientSessionValue::new(
self.suite,
nst.ticket.0.clone(),
secret.as_ref(),
cx.common
.peer_certificates
.clone()
.unwrap_or_default(),
now,
nst.lifetime,
nst.age_add,
nst.max_early_data_size()
.unwrap_or_default(),
);
if cx.common.is_quic() {
if let Some(sz) = nst.max_early_data_size() {
if sz != 0 && sz != 0xffff_ffff {
return Err(PeerMisbehaved::InvalidMaxEarlyDataSize.into());
}
}
if let Some(ref quic_params) = &cx.common.quic.params {
value.set_quic_params(quic_params);
}
}
self.session_storage
.insert_tls13_ticket(self.server_name.clone(), value);
Ok(())
}
fn handle_key_update(
&mut self,
common: &mut CommonState,
key_update_request: &KeyUpdateRequest,
) -> Result<(), Error> {
if let Protocol::Quic = common.protocol {
return Err(common.send_fatal_alert(
AlertDescription::UnexpectedMessage,
PeerMisbehaved::KeyUpdateReceivedInQuicConnection,
));
}
common.check_aligned_handshake()?;
if common.should_update_key(key_update_request)? {
self.key_schedule
.update_encrypter_and_notify(common);
}
self.key_schedule
.update_decrypter(common);
Ok(())
}
}
impl State<ClientConnectionData> for ExpectTraffic {
fn handle<'m>(
mut self: Box<Self>,
cx: &mut ClientContext<'_>,
m: Message<'m>,
) -> hs::NextStateOrError<'m>
where
Self: 'm,
{
match m.payload {
MessagePayload::ApplicationData(payload) => cx
.common
.take_received_plaintext(payload),
MessagePayload::Handshake {
parsed:
HandshakeMessagePayload {
payload: HandshakePayload::NewSessionTicketTls13(ref new_ticket),
..
},
..
} => self.handle_new_ticket_tls13(cx, new_ticket)?,
MessagePayload::Handshake {
parsed:
HandshakeMessagePayload {
payload: HandshakePayload::KeyUpdate(ref key_update),
..
},
..
} => self.handle_key_update(cx.common, key_update)?,
payload => {
return Err(inappropriate_handshake_message(
&payload,
&[ContentType::ApplicationData, ContentType::Handshake],
&[HandshakeType::NewSessionTicket, HandshakeType::KeyUpdate],
));
}
}
Ok(self)
}
fn send_key_update_request(&mut self, common: &mut CommonState) -> Result<(), Error> {
self.key_schedule
.request_key_update_and_update_encrypter(common)
}
fn export_keying_material(
&self,
output: &mut [u8],
label: &[u8],
context: Option<&[u8]>,
) -> Result<(), Error> {
self.key_schedule
.export_keying_material(output, label, context)
}
fn extract_secrets(&self) -> Result<PartiallyExtractedSecrets, Error> {
self.key_schedule
.extract_secrets(Side::Client)
}
fn into_owned(self: Box<Self>) -> hs::NextState<'static> {
self
}
}
struct ExpectQuicTraffic(ExpectTraffic);
impl State<ClientConnectionData> for ExpectQuicTraffic {
fn handle<'m>(
mut self: Box<Self>,
cx: &mut ClientContext<'_>,
m: Message<'m>,
) -> hs::NextStateOrError<'m>
where
Self: 'm,
{
let nst = require_handshake_msg!(
m,
HandshakeType::NewSessionTicket,
HandshakePayload::NewSessionTicketTls13
)?;
self.0
.handle_new_ticket_tls13(cx, nst)?;
Ok(self)
}
fn export_keying_material(
&self,
output: &mut [u8],
label: &[u8],
context: Option<&[u8]>,
) -> Result<(), Error> {
self.0
.export_keying_material(output, label, context)
}
fn into_owned(self: Box<Self>) -> hs::NextState<'static> {
self
}
}