diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-10 13:11:11 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-10 13:11:11 -0600 |
| commit | 01959b16a21b22b5df5f16569c2a8e8f92beecef (patch) | |
| tree | 32afa5d747c5466345c59ec52161a7cba3d6d755 /vendor/rustls/src/server | |
| parent | ff30574117a996df332e23d1fb6f65259b316b5b (diff) | |
chore: vendor dependencies
Diffstat (limited to 'vendor/rustls/src/server')
| -rw-r--r-- | vendor/rustls/src/server/builder.rs | 127 | ||||
| -rw-r--r-- | vendor/rustls/src/server/common.rs | 35 | ||||
| -rw-r--r-- | vendor/rustls/src/server/handy.rs | 356 | ||||
| -rw-r--r-- | vendor/rustls/src/server/hs.rs | 763 | ||||
| -rw-r--r-- | vendor/rustls/src/server/server_conn.rs | 1288 | ||||
| -rw-r--r-- | vendor/rustls/src/server/test.rs | 369 | ||||
| -rw-r--r-- | vendor/rustls/src/server/tls12.rs | 1003 | ||||
| -rw-r--r-- | vendor/rustls/src/server/tls13.rs | 1535 |
8 files changed, 5476 insertions, 0 deletions
diff --git a/vendor/rustls/src/server/builder.rs b/vendor/rustls/src/server/builder.rs new file mode 100644 index 00000000..d1b7b24a --- /dev/null +++ b/vendor/rustls/src/server/builder.rs @@ -0,0 +1,127 @@ +use alloc::vec::Vec; +use core::marker::PhantomData; + +use pki_types::{CertificateDer, PrivateKeyDer}; + +use super::{ResolvesServerCert, ServerConfig, handy}; +use crate::builder::{ConfigBuilder, WantsVerifier}; +use crate::error::Error; +use crate::sign::{CertifiedKey, SingleCertAndKey}; +use crate::sync::Arc; +use crate::verify::{ClientCertVerifier, NoClientAuth}; +use crate::{NoKeyLog, compress, versions}; + +impl ConfigBuilder<ServerConfig, WantsVerifier> { + /// Choose how to verify client certificates. + pub fn with_client_cert_verifier( + self, + client_cert_verifier: Arc<dyn ClientCertVerifier>, + ) -> ConfigBuilder<ServerConfig, WantsServerCert> { + ConfigBuilder { + state: WantsServerCert { + versions: self.state.versions, + verifier: client_cert_verifier, + }, + provider: self.provider, + time_provider: self.time_provider, + side: PhantomData, + } + } + + /// Disable client authentication. + pub fn with_no_client_auth(self) -> ConfigBuilder<ServerConfig, WantsServerCert> { + self.with_client_cert_verifier(Arc::new(NoClientAuth)) + } +} + +/// A config builder state where the caller must supply how to provide a server certificate to +/// the connecting peer. +/// +/// For more information, see the [`ConfigBuilder`] documentation. +#[derive(Clone, Debug)] +pub struct WantsServerCert { + versions: versions::EnabledVersions, + verifier: Arc<dyn ClientCertVerifier>, +} + +impl ConfigBuilder<ServerConfig, WantsServerCert> { + /// Sets a single certificate chain and matching private key. This + /// certificate and key is used for all subsequent connections, + /// irrespective of things like SNI hostname. + /// + /// Note that the end-entity certificate must have the + /// [Subject Alternative Name](https://tools.ietf.org/html/rfc6125#section-4.1) + /// extension to describe, e.g., the valid DNS name. The `commonName` field is + /// disregarded. + /// + /// `cert_chain` is a vector of DER-encoded certificates. + /// `key_der` is a DER-encoded private key as PKCS#1, PKCS#8, or SEC1. The + /// `aws-lc-rs` and `ring` [`CryptoProvider`][crate::CryptoProvider]s support + /// all three encodings, but other `CryptoProviders` may not. + /// + /// This function fails if `key_der` is invalid, or if the + /// `SubjectPublicKeyInfo` from the private key does not match the public + /// key for the end-entity certificate from the `cert_chain`. + pub fn with_single_cert( + self, + cert_chain: Vec<CertificateDer<'static>>, + key_der: PrivateKeyDer<'static>, + ) -> Result<ServerConfig, Error> { + let certified_key = CertifiedKey::from_der(cert_chain, key_der, self.crypto_provider())?; + Ok(self.with_cert_resolver(Arc::new(SingleCertAndKey::from(certified_key)))) + } + + /// Sets a single certificate chain, matching private key and optional OCSP + /// response. This certificate and key is used for all + /// subsequent connections, irrespective of things like SNI hostname. + /// + /// `cert_chain` is a vector of DER-encoded certificates. + /// `key_der` is a DER-encoded private key as PKCS#1, PKCS#8, or SEC1. The + /// `aws-lc-rs` and `ring` [`CryptoProvider`][crate::CryptoProvider]s support + /// all three encodings, but other `CryptoProviders` may not. + /// `ocsp` is a DER-encoded OCSP response. Ignored if zero length. + /// + /// This function fails if `key_der` is invalid, or if the + /// `SubjectPublicKeyInfo` from the private key does not match the public + /// key for the end-entity certificate from the `cert_chain`. + pub fn with_single_cert_with_ocsp( + self, + cert_chain: Vec<CertificateDer<'static>>, + key_der: PrivateKeyDer<'static>, + ocsp: Vec<u8>, + ) -> Result<ServerConfig, Error> { + let mut certified_key = + CertifiedKey::from_der(cert_chain, key_der, self.crypto_provider())?; + certified_key.ocsp = Some(ocsp); + Ok(self.with_cert_resolver(Arc::new(SingleCertAndKey::from(certified_key)))) + } + + /// Sets a custom [`ResolvesServerCert`]. + pub fn with_cert_resolver(self, cert_resolver: Arc<dyn ResolvesServerCert>) -> ServerConfig { + ServerConfig { + provider: self.provider, + verifier: self.state.verifier, + cert_resolver, + ignore_client_order: false, + max_fragment_size: None, + #[cfg(feature = "std")] + session_storage: handy::ServerSessionMemoryCache::new(256), + #[cfg(not(feature = "std"))] + session_storage: Arc::new(handy::NoServerSessionStorage {}), + ticketer: Arc::new(handy::NeverProducesTickets {}), + alpn_protocols: Vec::new(), + versions: self.state.versions, + key_log: Arc::new(NoKeyLog {}), + enable_secret_extraction: false, + max_early_data_size: 0, + send_half_rtt_data: false, + send_tls13_tickets: 2, + #[cfg(feature = "tls12")] + require_ems: cfg!(feature = "fips"), + time_provider: self.time_provider, + cert_compressors: compress::default_cert_compressors().to_vec(), + cert_compression_cache: Arc::new(compress::CompressionCache::default()), + cert_decompressors: compress::default_cert_decompressors().to_vec(), + } + } +} diff --git a/vendor/rustls/src/server/common.rs b/vendor/rustls/src/server/common.rs new file mode 100644 index 00000000..8310998a --- /dev/null +++ b/vendor/rustls/src/server/common.rs @@ -0,0 +1,35 @@ +use pki_types::CertificateDer; + +use crate::sign; + +/// ActiveCertifiedKey wraps [`sign::CertifiedKey`] and tracks OSCP state in a single handshake. +pub(super) struct ActiveCertifiedKey<'a> { + key: &'a sign::CertifiedKey, + ocsp: Option<&'a [u8]>, +} + +impl ActiveCertifiedKey<'_> { + pub(super) fn from_certified_key(key: &sign::CertifiedKey) -> ActiveCertifiedKey<'_> { + ActiveCertifiedKey { + key, + ocsp: key.ocsp.as_deref(), + } + } + + /// Get the certificate chain + #[inline] + pub(super) fn get_cert(&self) -> &[CertificateDer<'static>] { + &self.key.cert + } + + /// Get the signing key + #[inline] + pub(super) fn get_key(&self) -> &dyn sign::SigningKey { + &*self.key.key + } + + #[inline] + pub(super) fn get_ocsp(&self) -> Option<&[u8]> { + self.ocsp + } +} diff --git a/vendor/rustls/src/server/handy.rs b/vendor/rustls/src/server/handy.rs new file mode 100644 index 00000000..ea3ec5d9 --- /dev/null +++ b/vendor/rustls/src/server/handy.rs @@ -0,0 +1,356 @@ +use alloc::vec::Vec; +use core::fmt::Debug; + +use crate::server::ClientHello; +use crate::sync::Arc; +use crate::{server, sign}; + +/// Something which never stores sessions. +#[derive(Debug)] +pub struct NoServerSessionStorage {} + +impl server::StoresServerSessions for NoServerSessionStorage { + fn put(&self, _id: Vec<u8>, _sec: Vec<u8>) -> bool { + false + } + fn get(&self, _id: &[u8]) -> Option<Vec<u8>> { + None + } + fn take(&self, _id: &[u8]) -> Option<Vec<u8>> { + None + } + fn can_cache(&self) -> bool { + false + } +} + +#[cfg(any(feature = "std", feature = "hashbrown"))] +mod cache { + use alloc::vec::Vec; + use core::fmt::{Debug, Formatter}; + + use crate::lock::Mutex; + use crate::sync::Arc; + use crate::{limited_cache, server}; + + /// An implementer of `StoresServerSessions` that stores everything + /// in memory. If enforces a limit on the number of stored sessions + /// to bound memory usage. + pub struct ServerSessionMemoryCache { + cache: Mutex<limited_cache::LimitedCache<Vec<u8>, Vec<u8>>>, + } + + impl ServerSessionMemoryCache { + /// Make a new ServerSessionMemoryCache. `size` is the maximum + /// number of stored sessions, and may be rounded-up for + /// efficiency. + #[cfg(feature = "std")] + pub fn new(size: usize) -> Arc<Self> { + Arc::new(Self { + cache: Mutex::new(limited_cache::LimitedCache::new(size)), + }) + } + + /// Make a new ServerSessionMemoryCache. `size` is the maximum + /// number of stored sessions, and may be rounded-up for + /// efficiency. + #[cfg(not(feature = "std"))] + pub fn new<M: crate::lock::MakeMutex>(size: usize) -> Arc<Self> { + Arc::new(Self { + cache: Mutex::new::<M>(limited_cache::LimitedCache::new(size)), + }) + } + } + + impl server::StoresServerSessions for ServerSessionMemoryCache { + fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool { + self.cache + .lock() + .unwrap() + .insert(key, value); + true + } + + fn get(&self, key: &[u8]) -> Option<Vec<u8>> { + self.cache + .lock() + .unwrap() + .get(key) + .cloned() + } + + fn take(&self, key: &[u8]) -> Option<Vec<u8>> { + self.cache.lock().unwrap().remove(key) + } + + fn can_cache(&self) -> bool { + true + } + } + + impl Debug for ServerSessionMemoryCache { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.debug_struct("ServerSessionMemoryCache") + .finish() + } + } + + #[cfg(test)] + mod tests { + use std::vec; + + use super::*; + use crate::server::StoresServerSessions; + + #[test] + fn test_serversessionmemorycache_accepts_put() { + let c = ServerSessionMemoryCache::new(4); + assert!(c.put(vec![0x01], vec![0x02])); + } + + #[test] + fn test_serversessionmemorycache_persists_put() { + let c = ServerSessionMemoryCache::new(4); + assert!(c.put(vec![0x01], vec![0x02])); + assert_eq!(c.get(&[0x01]), Some(vec![0x02])); + assert_eq!(c.get(&[0x01]), Some(vec![0x02])); + } + + #[test] + fn test_serversessionmemorycache_overwrites_put() { + let c = ServerSessionMemoryCache::new(4); + assert!(c.put(vec![0x01], vec![0x02])); + assert!(c.put(vec![0x01], vec![0x04])); + assert_eq!(c.get(&[0x01]), Some(vec![0x04])); + } + + #[test] + fn test_serversessionmemorycache_drops_to_maintain_size_invariant() { + let c = ServerSessionMemoryCache::new(2); + assert!(c.put(vec![0x01], vec![0x02])); + assert!(c.put(vec![0x03], vec![0x04])); + assert!(c.put(vec![0x05], vec![0x06])); + assert!(c.put(vec![0x07], vec![0x08])); + assert!(c.put(vec![0x09], vec![0x0a])); + + let count = c.get(&[0x01]).iter().count() + + c.get(&[0x03]).iter().count() + + c.get(&[0x05]).iter().count() + + c.get(&[0x07]).iter().count() + + c.get(&[0x09]).iter().count(); + + assert!(count < 5); + } + } +} + +#[cfg(any(feature = "std", feature = "hashbrown"))] +pub use cache::ServerSessionMemoryCache; + +/// Something which never produces tickets. +#[derive(Debug)] +pub(super) struct NeverProducesTickets {} + +impl server::ProducesTickets for NeverProducesTickets { + fn enabled(&self) -> bool { + false + } + fn lifetime(&self) -> u32 { + 0 + } + fn encrypt(&self, _bytes: &[u8]) -> Option<Vec<u8>> { + None + } + fn decrypt(&self, _bytes: &[u8]) -> Option<Vec<u8>> { + None + } +} + +/// An exemplar `ResolvesServerCert` implementation that always resolves to a single +/// [RFC 7250] raw public key. +/// +/// [RFC 7250]: https://tools.ietf.org/html/rfc7250 +#[derive(Clone, Debug)] +pub struct AlwaysResolvesServerRawPublicKeys(Arc<sign::CertifiedKey>); + +impl AlwaysResolvesServerRawPublicKeys { + /// Create a new `AlwaysResolvesServerRawPublicKeys` instance. + pub fn new(certified_key: Arc<sign::CertifiedKey>) -> Self { + Self(certified_key) + } +} + +impl server::ResolvesServerCert for AlwaysResolvesServerRawPublicKeys { + fn resolve(&self, _client_hello: ClientHello<'_>) -> Option<Arc<sign::CertifiedKey>> { + Some(self.0.clone()) + } + + fn only_raw_public_keys(&self) -> bool { + true + } +} + +#[cfg(any(feature = "std", feature = "hashbrown"))] +mod sni_resolver { + use alloc::string::{String, ToString}; + use core::fmt::Debug; + + use pki_types::{DnsName, ServerName}; + + use crate::error::Error; + use crate::hash_map::HashMap; + use crate::server::ClientHello; + use crate::sync::Arc; + use crate::webpki::{ParsedCertificate, verify_server_name}; + use crate::{server, sign}; + + /// Something that resolves do different cert chains/keys based + /// on client-supplied server name (via SNI). + #[derive(Debug)] + pub struct ResolvesServerCertUsingSni { + by_name: HashMap<String, Arc<sign::CertifiedKey>>, + } + + impl ResolvesServerCertUsingSni { + /// Create a new and empty (i.e., knows no certificates) resolver. + pub fn new() -> Self { + Self { + by_name: HashMap::new(), + } + } + + /// Add a new `sign::CertifiedKey` to be used for the given SNI `name`. + /// + /// This function fails if `name` is not a valid DNS name, or if + /// it's not valid for the supplied certificate, or if the certificate + /// chain is syntactically faulty. + pub fn add(&mut self, name: &str, ck: sign::CertifiedKey) -> Result<(), Error> { + let server_name = { + let checked_name = DnsName::try_from(name) + .map_err(|_| Error::General("Bad DNS name".into())) + .map(|name| name.to_lowercase_owned())?; + ServerName::DnsName(checked_name) + }; + + // Check the certificate chain for validity: + // - it should be non-empty list + // - the first certificate should be parsable as a x509v3, + // - the first certificate should quote the given server name + // (if provided) + // + // These checks are not security-sensitive. They are the + // *server* attempting to detect accidental misconfiguration. + + ck.end_entity_cert() + .and_then(ParsedCertificate::try_from) + .and_then(|cert| verify_server_name(&cert, &server_name))?; + + if let ServerName::DnsName(name) = server_name { + self.by_name + .insert(name.as_ref().to_string(), Arc::new(ck)); + } + Ok(()) + } + } + + impl server::ResolvesServerCert for ResolvesServerCertUsingSni { + fn resolve(&self, client_hello: ClientHello<'_>) -> Option<Arc<sign::CertifiedKey>> { + if let Some(name) = client_hello.server_name() { + self.by_name.get(name).cloned() + } else { + // This kind of resolver requires SNI + None + } + } + } + + #[cfg(test)] + mod tests { + use super::*; + use crate::server::ResolvesServerCert; + + #[test] + fn test_resolvesservercertusingsni_requires_sni() { + let rscsni = ResolvesServerCertUsingSni::new(); + assert!( + rscsni + .resolve(ClientHello { + server_name: &None, + signature_schemes: &[], + alpn: None, + server_cert_types: None, + client_cert_types: None, + cipher_suites: &[], + certificate_authorities: None, + named_groups: None, + }) + .is_none() + ); + } + + #[test] + fn test_resolvesservercertusingsni_handles_unknown_name() { + let rscsni = ResolvesServerCertUsingSni::new(); + let name = DnsName::try_from("hello.com") + .unwrap() + .to_owned(); + assert!( + rscsni + .resolve(ClientHello { + server_name: &Some(name), + signature_schemes: &[], + alpn: None, + server_cert_types: None, + client_cert_types: None, + cipher_suites: &[], + certificate_authorities: None, + named_groups: None, + }) + .is_none() + ); + } + } +} + +#[cfg(any(feature = "std", feature = "hashbrown"))] +pub use sni_resolver::ResolvesServerCertUsingSni; + +#[cfg(test)] +mod tests { + use std::vec; + + use super::*; + use crate::server::{ProducesTickets, StoresServerSessions}; + + #[test] + fn test_noserversessionstorage_drops_put() { + let c = NoServerSessionStorage {}; + assert!(!c.put(vec![0x01], vec![0x02])); + } + + #[test] + fn test_noserversessionstorage_denies_gets() { + let c = NoServerSessionStorage {}; + c.put(vec![0x01], vec![0x02]); + assert_eq!(c.get(&[]), None); + assert_eq!(c.get(&[0x01]), None); + assert_eq!(c.get(&[0x02]), None); + } + + #[test] + fn test_noserversessionstorage_denies_takes() { + let c = NoServerSessionStorage {}; + assert_eq!(c.take(&[]), None); + assert_eq!(c.take(&[0x01]), None); + assert_eq!(c.take(&[0x02]), None); + } + + #[test] + fn test_neverproducestickets_does_nothing() { + let npt = NeverProducesTickets {}; + assert!(!npt.enabled()); + assert_eq!(0, npt.lifetime()); + assert_eq!(None, npt.encrypt(&[])); + assert_eq!(None, npt.decrypt(&[])); + } +} diff --git a/vendor/rustls/src/server/hs.rs b/vendor/rustls/src/server/hs.rs new file mode 100644 index 00000000..d98336e4 --- /dev/null +++ b/vendor/rustls/src/server/hs.rs @@ -0,0 +1,763 @@ +use alloc::borrow::ToOwned; +use alloc::boxed::Box; +use alloc::vec::Vec; + +use pki_types::DnsName; + +use super::server_conn::ServerConnectionData; +#[cfg(feature = "tls12")] +use super::tls12; +use crate::common_state::{KxState, Protocol, State}; +use crate::conn::ConnectionRandoms; +use crate::crypto::SupportedKxGroup; +use crate::enums::{ + AlertDescription, CertificateType, CipherSuite, HandshakeType, ProtocolVersion, + SignatureAlgorithm, SignatureScheme, +}; +use crate::error::{Error, PeerIncompatible, PeerMisbehaved}; +use crate::hash_hs::{HandshakeHash, HandshakeHashBuffer}; +use crate::log::{debug, trace}; +use crate::msgs::enums::{Compression, ExtensionType, NamedGroup}; +#[cfg(feature = "tls12")] +use crate::msgs::handshake::SessionId; +use crate::msgs::handshake::{ + ClientHelloPayload, HandshakePayload, KeyExchangeAlgorithm, ProtocolName, Random, + ServerExtensions, ServerExtensionsInput, ServerNamePayload, SingleProtocolName, + TransportParameters, +}; +use crate::msgs::message::{Message, MessagePayload}; +use crate::msgs::persist; +use crate::server::common::ActiveCertifiedKey; +use crate::server::{ClientHello, ServerConfig, tls13}; +use crate::sync::Arc; +use crate::{SupportedCipherSuite, suites}; + +pub(super) type NextState<'a> = Box<dyn State<ServerConnectionData> + 'a>; +pub(super) type NextStateOrError<'a> = Result<NextState<'a>, Error>; +pub(super) type ServerContext<'a> = crate::common_state::Context<'a, ServerConnectionData>; + +pub(super) fn can_resume( + suite: SupportedCipherSuite, + sni: &Option<DnsName<'_>>, + using_ems: bool, + resumedata: &persist::ServerSessionValue, +) -> bool { + // The RFCs underspecify what happens if we try to resume to + // an unoffered/varying suite. We merely don't resume in weird cases. + // + // RFC 6066 says "A server that implements this extension MUST NOT accept + // the request to resume the session if the server_name extension contains + // a different name. Instead, it proceeds with a full handshake to + // establish a new session." + // + // RFC 8446: "The server MUST ensure that it selects + // a compatible PSK (if any) and cipher suite." + resumedata.cipher_suite == suite.suite() + && (resumedata.extended_ms == using_ems || (resumedata.extended_ms && !using_ems)) + && &resumedata.sni == sni +} + +#[derive(Default)] +pub(super) struct ExtensionProcessing { + // extensions to reply with + pub(super) extensions: Box<ServerExtensions<'static>>, + #[cfg(feature = "tls12")] + pub(super) send_ticket: bool, +} + +impl ExtensionProcessing { + pub(super) fn new(extra_exts: ServerExtensionsInput<'static>) -> Self { + let ServerExtensionsInput { + transport_parameters, + } = extra_exts; + + let mut extensions = Box::new(ServerExtensions::default()); + match transport_parameters { + Some(TransportParameters::Quic(v)) => extensions.transport_parameters = Some(v), + Some(TransportParameters::QuicDraft(v)) => { + extensions.transport_parameters_draft = Some(v) + } + None => {} + } + + Self { + extensions, + #[cfg(feature = "tls12")] + send_ticket: false, + } + } + + pub(super) fn process_common( + &mut self, + config: &ServerConfig, + cx: &mut ServerContext<'_>, + ocsp_response: &mut Option<&[u8]>, + hello: &ClientHelloPayload, + resumedata: Option<&persist::ServerSessionValue>, + ) -> Result<(), Error> { + // ALPN + let our_protocols = &config.alpn_protocols; + if let Some(their_protocols) = &hello.protocols { + cx.common.alpn_protocol = our_protocols + .iter() + .find(|ours| { + their_protocols + .iter() + .any(|theirs| theirs.as_ref() == ours.as_slice()) + }) + .map(|bytes| ProtocolName::from(bytes.clone())); + if let Some(selected_protocol) = &cx.common.alpn_protocol { + debug!("Chosen ALPN protocol {selected_protocol:?}"); + + self.extensions.selected_protocol = + Some(SingleProtocolName::new(selected_protocol.clone())); + } else if !our_protocols.is_empty() { + return Err(cx.common.send_fatal_alert( + AlertDescription::NoApplicationProtocol, + Error::NoApplicationProtocol, + )); + } + } + + if cx.common.is_quic() { + // QUIC has strict ALPN, unlike TLS's more backwards-compatible behavior. RFC 9001 + // says: "The server MUST treat the inability to select a compatible application + // protocol as a connection error of type 0x0178". We judge that ALPN was desired + // (rather than some out-of-band protocol negotiation mechanism) if and only if any ALPN + // protocols were configured locally or offered by the client. This helps prevent + // successful establishment of connections between peers that can't understand + // each other. + if cx.common.alpn_protocol.is_none() + && (!our_protocols.is_empty() || hello.protocols.is_some()) + { + return Err(cx.common.send_fatal_alert( + AlertDescription::NoApplicationProtocol, + Error::NoApplicationProtocol, + )); + } + + let transport_params = hello + .transport_parameters + .as_ref() + .or(hello + .transport_parameters_draft + .as_ref()); + match transport_params { + Some(params) => cx.common.quic.params = Some(params.to_owned().into_vec()), + None => { + return Err(cx + .common + .missing_extension(PeerMisbehaved::MissingQuicTransportParameters)); + } + } + } + + let for_resume = resumedata.is_some(); + // SNI + if let (false, Some(ServerNamePayload::SingleDnsName(_))) = (for_resume, &hello.server_name) + { + self.extensions.server_name_ack = Some(()); + } + + // Send status_request response if we have one. This is not allowed + // if we're resuming, and is only triggered if we have an OCSP response + // to send. + if !for_resume + && hello + .certificate_status_request + .is_some() + { + if ocsp_response.is_some() && !cx.common.is_tls13() { + // Only TLS1.2 sends confirmation in ServerHello + self.extensions + .certificate_status_request_ack = Some(()); + } + } else { + // Throw away any OCSP response so we don't try to send it later. + ocsp_response.take(); + } + + self.validate_server_cert_type_extension(hello, config, cx)?; + self.validate_client_cert_type_extension(hello, config, cx)?; + + Ok(()) + } + + #[cfg(feature = "tls12")] + pub(super) fn process_tls12( + &mut self, + config: &ServerConfig, + hello: &ClientHelloPayload, + using_ems: bool, + ) { + // Renegotiation. + // (We don't do reneg at all, but would support the secure version if we did.) + + use crate::msgs::base::PayloadU8; + let secure_reneg_offered = hello.renegotiation_info.is_some() + || hello + .cipher_suites + .contains(&CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + + if secure_reneg_offered { + self.extensions.renegotiation_info = Some(PayloadU8::new(Vec::new())); + } + + // Tickets: + // If we get any SessionTicket extension and have tickets enabled, + // we send an ack. + if hello.session_ticket.is_some() && config.ticketer.enabled() { + self.send_ticket = true; + self.extensions.session_ticket_ack = Some(()); + } + + // Confirm use of EMS if offered. + if using_ems { + self.extensions + .extended_master_secret_ack = Some(()); + } + } + + fn validate_server_cert_type_extension( + &mut self, + hello: &ClientHelloPayload, + config: &ServerConfig, + cx: &mut ServerContext<'_>, + ) -> Result<(), Error> { + let client_supports = hello + .server_certificate_types + .as_deref() + .unwrap_or_default(); + + self.process_cert_type_extension( + client_supports, + config + .cert_resolver + .only_raw_public_keys(), + ExtensionType::ServerCertificateType, + cx, + ) + } + + fn validate_client_cert_type_extension( + &mut self, + hello: &ClientHelloPayload, + config: &ServerConfig, + cx: &mut ServerContext<'_>, + ) -> Result<(), Error> { + let client_supports = hello + .client_certificate_types + .as_deref() + .unwrap_or_default(); + + self.process_cert_type_extension( + client_supports, + config + .verifier + .requires_raw_public_keys(), + ExtensionType::ClientCertificateType, + cx, + ) + } + + fn process_cert_type_extension( + &mut self, + client_supports: &[CertificateType], + requires_raw_keys: bool, + extension_type: ExtensionType, + cx: &mut ServerContext<'_>, + ) -> Result<(), Error> { + debug_assert!( + extension_type == ExtensionType::ClientCertificateType + || extension_type == ExtensionType::ServerCertificateType + ); + let raw_key_negotation_result = match ( + requires_raw_keys, + client_supports.contains(&CertificateType::RawPublicKey), + client_supports.contains(&CertificateType::X509), + ) { + (true, true, _) => Ok((extension_type, CertificateType::RawPublicKey)), + (false, _, true) => Ok((extension_type, CertificateType::X509)), + (false, true, false) => Err(Error::PeerIncompatible( + PeerIncompatible::IncorrectCertificateTypeExtension, + )), + (true, false, _) => Err(Error::PeerIncompatible( + PeerIncompatible::IncorrectCertificateTypeExtension, + )), + (false, false, false) => return Ok(()), + }; + + match raw_key_negotation_result { + Ok((ExtensionType::ClientCertificateType, cert_type)) => { + self.extensions.client_certificate_type = Some(cert_type); + } + Ok((ExtensionType::ServerCertificateType, cert_type)) => { + self.extensions.server_certificate_type = Some(cert_type); + } + Err(err) => { + return Err(cx + .common + .send_fatal_alert(AlertDescription::HandshakeFailure, err)); + } + Ok((_, _)) => unreachable!(), + } + Ok(()) + } +} + +pub(super) struct ExpectClientHello { + pub(super) config: Arc<ServerConfig>, + pub(super) extra_exts: ServerExtensionsInput<'static>, + pub(super) transcript: HandshakeHashOrBuffer, + #[cfg(feature = "tls12")] + pub(super) session_id: SessionId, + #[cfg(feature = "tls12")] + 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<'static>, + ) -> Self { + let mut transcript_buffer = HandshakeHashBuffer::new(); + + if config.verifier.offer_client_auth() { + transcript_buffer.set_client_auth_enabled(); + } + + Self { + config, + extra_exts, + transcript: HandshakeHashOrBuffer::Buffer(transcript_buffer), + #[cfg(feature = "tls12")] + session_id: SessionId::empty(), + #[cfg(feature = "tls12")] + using_ems: false, + done_retry: false, + send_tickets: 0, + } + } + + /// Continues handling of a `ClientHello` message once config and certificate are available. + pub(super) fn with_certified_key( + self, + mut sig_schemes: Vec<SignatureScheme>, + client_hello: &ClientHelloPayload, + m: &Message<'_>, + cx: &mut ServerContext<'_>, + ) -> NextStateOrError<'static> { + let tls13_enabled = self + .config + .supports_version(ProtocolVersion::TLSv1_3); + let tls12_enabled = self + .config + .supports_version(ProtocolVersion::TLSv1_2); + + // Are we doing TLS1.3? + let version = if let Some(versions) = &client_hello.supported_versions { + if versions.tls13 && tls13_enabled { + ProtocolVersion::TLSv1_3 + } else if !versions.tls12 || !tls12_enabled { + return Err(cx.common.send_fatal_alert( + AlertDescription::ProtocolVersion, + PeerIncompatible::Tls12NotOfferedOrEnabled, + )); + } else if cx.common.is_quic() { + return Err(cx.common.send_fatal_alert( + AlertDescription::ProtocolVersion, + PeerIncompatible::Tls13RequiredForQuic, + )); + } else { + ProtocolVersion::TLSv1_2 + } + } else if u16::from(client_hello.client_version) < u16::from(ProtocolVersion::TLSv1_2) { + return Err(cx.common.send_fatal_alert( + AlertDescription::ProtocolVersion, + PeerIncompatible::Tls12NotOffered, + )); + } else if !tls12_enabled && tls13_enabled { + return Err(cx.common.send_fatal_alert( + AlertDescription::ProtocolVersion, + PeerIncompatible::SupportedVersionsExtensionRequired, + )); + } else if cx.common.is_quic() { + return Err(cx.common.send_fatal_alert( + AlertDescription::ProtocolVersion, + PeerIncompatible::Tls13RequiredForQuic, + )); + } else { + ProtocolVersion::TLSv1_2 + }; + + cx.common.negotiated_version = Some(version); + + // We communicate to the upper layer what kind of key they should choose + // via the sigschemes value. Clients tend to treat this extension + // orthogonally to offered ciphersuites (even though, in TLS1.2 it is not). + // So: reduce the offered sigschemes to those compatible with the + // intersection of ciphersuites. + let client_suites = self + .config + .provider + .cipher_suites + .iter() + .copied() + .filter(|scs| { + client_hello + .cipher_suites + .contains(&scs.suite()) + }) + .collect::<Vec<_>>(); + + sig_schemes + .retain(|scheme| suites::compatible_sigscheme_for_suites(*scheme, &client_suites)); + + // We adhere to the TLS 1.2 RFC by not exposing this to the cert resolver if TLS version is 1.2 + let certificate_authorities = match version { + ProtocolVersion::TLSv1_2 => None, + _ => client_hello + .certificate_authority_names + .as_deref(), + }; + // Choose a certificate. + let certkey = { + let client_hello = ClientHello { + server_name: &cx.data.sni, + signature_schemes: &sig_schemes, + alpn: client_hello.protocols.as_ref(), + client_cert_types: client_hello + .client_certificate_types + .as_deref(), + server_cert_types: client_hello + .server_certificate_types + .as_deref(), + cipher_suites: &client_hello.cipher_suites, + certificate_authorities, + named_groups: client_hello.named_groups.as_deref(), + }; + trace!("Resolving server certificate: {client_hello:#?}"); + + let certkey = self + .config + .cert_resolver + .resolve(client_hello); + + certkey.ok_or_else(|| { + cx.common.send_fatal_alert( + AlertDescription::AccessDenied, + Error::General("no server certificate chain resolved".to_owned()), + ) + })? + }; + let certkey = ActiveCertifiedKey::from_certified_key(&certkey); + + let (suite, skxg) = self + .choose_suite_and_kx_group( + version, + certkey.get_key().algorithm(), + cx.common.protocol, + client_hello + .named_groups + .as_deref() + .unwrap_or_default(), + &client_hello.cipher_suites, + ) + .map_err(|incompat| { + cx.common + .send_fatal_alert(AlertDescription::HandshakeFailure, incompat) + })?; + + debug!("decided upon suite {suite:?}"); + cx.common.suite = Some(suite); + cx.common.kx_state = KxState::Start(skxg); + + // Start handshake hash. + let starting_hash = suite.hash_provider(); + let transcript = match self.transcript { + HandshakeHashOrBuffer::Buffer(inner) => inner.start_hash(starting_hash), + HandshakeHashOrBuffer::Hash(inner) + if inner.algorithm() == starting_hash.algorithm() => + { + inner + } + _ => { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::HandshakeHashVariedAfterRetry, + )); + } + }; + + // Save their Random. + let randoms = ConnectionRandoms::new( + client_hello.random, + Random::new(self.config.provider.secure_random)?, + ); + match suite { + SupportedCipherSuite::Tls13(suite) => tls13::CompleteClientHelloHandling { + config: self.config, + transcript, + suite, + randoms, + done_retry: self.done_retry, + send_tickets: self.send_tickets, + extra_exts: self.extra_exts, + } + .handle_client_hello(cx, certkey, m, client_hello, skxg, sig_schemes), + #[cfg(feature = "tls12")] + SupportedCipherSuite::Tls12(suite) => tls12::CompleteClientHelloHandling { + config: self.config, + transcript, + session_id: self.session_id, + suite, + using_ems: self.using_ems, + randoms, + send_ticket: self.send_tickets > 0, + extra_exts: self.extra_exts, + } + .handle_client_hello( + cx, + certkey, + m, + client_hello, + skxg, + sig_schemes, + tls13_enabled, + ), + } + } + + fn choose_suite_and_kx_group( + &self, + selected_version: ProtocolVersion, + sig_key_algorithm: SignatureAlgorithm, + protocol: Protocol, + client_groups: &[NamedGroup], + client_suites: &[CipherSuite], + ) -> Result<(SupportedCipherSuite, &'static dyn SupportedKxGroup), PeerIncompatible> { + // Determine which `KeyExchangeAlgorithm`s are theoretically possible, based + // on the offered and supported groups. + let mut ecdhe_possible = false; + let mut ffdhe_possible = false; + let mut ffdhe_offered = false; + let mut supported_groups = Vec::with_capacity(client_groups.len()); + + for offered_group in client_groups { + let supported = self + .config + .provider + .kx_groups + .iter() + .find(|skxg| { + skxg.usable_for_version(selected_version) && skxg.name() == *offered_group + }); + + match offered_group.key_exchange_algorithm() { + KeyExchangeAlgorithm::DHE => { + ffdhe_possible |= supported.is_some(); + ffdhe_offered = true; + } + KeyExchangeAlgorithm::ECDHE => { + ecdhe_possible |= supported.is_some(); + } + } + + supported_groups.push(supported); + } + + let first_supported_dhe_kxg = if selected_version == ProtocolVersion::TLSv1_2 { + // https://datatracker.ietf.org/doc/html/rfc7919#section-4 (paragraph 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 { + // In TLS1.3, the server may only directly negotiate a group. + None + }; + + if !ecdhe_possible && !ffdhe_possible { + return Err(PeerIncompatible::NoKxGroupsInCommon); + } + + let mut suitable_suites_iter = self + .config + .provider + .cipher_suites + .iter() + .filter(|suite| { + // Reduce our supported ciphersuites by the certified key's algorithm. + suite.usable_for_signature_algorithm(sig_key_algorithm) + // And version + && suite.version().version == selected_version + // And protocol + && suite.usable_for_protocol(protocol) + // And support one of key exchange groups + && (ecdhe_possible && suite.usable_for_kx_algorithm(KeyExchangeAlgorithm::ECDHE) + || ffdhe_possible && suite.usable_for_kx_algorithm(KeyExchangeAlgorithm::DHE)) + }); + + // RFC 7919 (https://datatracker.ietf.org/doc/html/rfc7919#section-4) requires us to send + // the InsufficientSecurity alert in case we don't recognize client's FFDHE groups (i.e., + // `suitable_suites` becomes empty). But that does not make a lot of sense (e.g., client + // proposes FFDHE4096 and we only support FFDHE2048), so we ignore that requirement here, + // and continue to send HandshakeFailure. + + let suite = if self.config.ignore_client_order { + suitable_suites_iter.find(|suite| client_suites.contains(&suite.suite())) + } else { + let suitable_suites = suitable_suites_iter.collect::<Vec<_>>(); + client_suites + .iter() + .find_map(|client_suite| { + suitable_suites + .iter() + .find(|x| *client_suite == x.suite()) + }) + .copied() + } + .ok_or(PeerIncompatible::NoCipherSuitesInCommon)?; + + // Finally, choose a key exchange group that is compatible with the selected cipher + // suite. + let maybe_skxg = supported_groups + .iter() + .find_map(|maybe_skxg| match maybe_skxg { + Some(skxg) => suite + .usable_for_kx_algorithm(skxg.name().key_exchange_algorithm()) + .then_some(*skxg), + None => None, + }); + + if selected_version == ProtocolVersion::TLSv1_3 { + // This unwrap is structurally guaranteed by the early return for `!ffdhe_possible && !ecdhe_possible` + return Ok((*suite, *maybe_skxg.unwrap())); + } + + // For TLS1.2, the server can unilaterally choose a DHE group if it has one and + // there was no better option. + match maybe_skxg { + Some(skxg) => Ok((*suite, *skxg)), + None if suite.usable_for_kx_algorithm(KeyExchangeAlgorithm::DHE) => { + // If kx for the selected cipher suite is DHE and no DHE groups are specified in the extension, + // the server is free to choose DHE params, we choose the first DHE kx group of the provider. + 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), + } + } +} + +impl State<ServerConnectionData> for ExpectClientHello { + fn handle<'m>( + self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> NextStateOrError<'m> + where + Self: 'm, + { + let (client_hello, sig_schemes) = process_client_hello(&m, self.done_retry, cx)?; + self.with_certified_key(sig_schemes, client_hello, &m, cx) + } + + fn into_owned(self: Box<Self>) -> NextState<'static> { + self + } +} + +/// Configuration-independent validation of a `ClientHello` message. +/// +/// This represents the first part of the `ClientHello` handling, where we do all validation that +/// doesn't depend on a `ServerConfig` being available and extract everything needed to build a +/// [`ClientHello`] value for a [`ResolvesServerCert`]. +/// +/// Note that this will modify `data.sni` even if config or certificate resolution fail. +/// +/// [`ResolvesServerCert`]: crate::server::ResolvesServerCert +pub(super) fn process_client_hello<'m>( + m: &'m Message<'m>, + done_retry: bool, + cx: &mut ServerContext<'_>, +) -> Result<(&'m ClientHelloPayload, Vec<SignatureScheme>), Error> { + let client_hello = + require_handshake_msg!(m, HandshakeType::ClientHello, HandshakePayload::ClientHello)?; + trace!("we got a clienthello {client_hello:?}"); + + if !client_hello + .compression_methods + .contains(&Compression::Null) + { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerIncompatible::NullCompressionRequired, + )); + } + + // No handshake messages should follow this one in this flight. + cx.common.check_aligned_handshake()?; + + // Extract and validate the SNI DNS name, if any, before giving it to + // the cert resolver. In particular, if it is invalid then we should + // send an Illegal Parameter alert instead of the Internal Error alert + // (or whatever) that we'd send if this were checked later or in a + // different way. + // + // [RFC6066][] specifies that literal IP addresses are illegal in + // `ServerName`s with a `name_type` of `host_name`. + // + // Some clients incorrectly send such extensions: we choose to + // successfully parse these (into `ServerNamePayload::IpAddress`) + // but then act like the client sent no `server_name` extension. + // + // [RFC6066]: https://datatracker.ietf.org/doc/html/rfc6066#section-3 + let sni = match &client_hello.server_name { + Some(ServerNamePayload::SingleDnsName(dns_name)) => Some(dns_name.to_lowercase_owned()), + Some(ServerNamePayload::IpAddress) => None, + Some(ServerNamePayload::Invalid) => { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::ServerNameMustContainOneHostName, + )); + } + None => None, + }; + + // save only the first SNI + if let (Some(sni), false) = (&sni, done_retry) { + // Save the SNI into the session. + // The SNI hostname is immutable once set. + assert!(cx.data.sni.is_none()); + cx.data.sni = Some(sni.clone()); + } else if cx.data.sni != sni { + return Err(PeerMisbehaved::ServerNameDifferedOnRetry.into()); + } + + let sig_schemes = client_hello + .signature_schemes + .as_ref() + .ok_or_else(|| { + cx.common.send_fatal_alert( + AlertDescription::HandshakeFailure, + PeerIncompatible::SignatureAlgorithmsExtensionRequired, + ) + })?; + + Ok((client_hello, sig_schemes.to_owned())) +} + +pub(crate) enum HandshakeHashOrBuffer { + Buffer(HandshakeHashBuffer), + Hash(HandshakeHash), +} diff --git a/vendor/rustls/src/server/server_conn.rs b/vendor/rustls/src/server/server_conn.rs new file mode 100644 index 00000000..d1024b14 --- /dev/null +++ b/vendor/rustls/src/server/server_conn.rs @@ -0,0 +1,1288 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::fmt; +use core::fmt::{Debug, Formatter}; +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; +#[cfg(feature = "std")] +use std::io; + +use pki_types::{DnsName, UnixTime}; + +use super::hs; +#[cfg(feature = "std")] +use crate::WantsVerifier; +use crate::builder::ConfigBuilder; +use crate::common_state::{CommonState, Side}; +#[cfg(feature = "std")] +use crate::common_state::{Protocol, State}; +use crate::conn::{ConnectionCommon, ConnectionCore, UnbufferedConnectionCommon}; +#[cfg(doc)] +use crate::crypto; +use crate::crypto::CryptoProvider; +use crate::enums::{CertificateType, CipherSuite, ProtocolVersion, SignatureScheme}; +use crate::error::Error; +use crate::kernel::KernelConnection; +use crate::log::trace; +use crate::msgs::base::Payload; +use crate::msgs::handshake::{ClientHelloPayload, ProtocolName, ServerExtensionsInput}; +use crate::msgs::message::Message; +use crate::suites::ExtractedSecrets; +use crate::sync::Arc; +#[cfg(feature = "std")] +use crate::time_provider::DefaultTimeProvider; +use crate::time_provider::TimeProvider; +use crate::vecbuf::ChunkVecBuffer; +use crate::{ + DistinguishedName, KeyLog, NamedGroup, WantsVersions, compress, sign, verify, versions, +}; + +/// A trait for the ability to store server session data. +/// +/// The keys and values are opaque. +/// +/// Inserted keys are randomly chosen by the library and have +/// no internal structure (in other words, you may rely on all +/// bits being uniformly random). Queried keys are untrusted data. +/// +/// Both the keys and values should be treated as +/// **highly sensitive data**, containing enough key material +/// to break all security of the corresponding sessions. +/// +/// Implementations can be lossy (in other words, forgetting +/// key/value pairs) without any negative security consequences. +/// +/// However, note that `take` **must** reliably delete a returned +/// value. If it does not, there may be security consequences. +/// +/// `put` and `take` are mutating operations; this isn't expressed +/// in the type system to allow implementations freedom in +/// how to achieve interior mutability. `Mutex` is a common +/// choice. +pub trait StoresServerSessions: Debug + Send + Sync { + /// Store session secrets encoded in `value` against `key`, + /// overwrites any existing value against `key`. Returns `true` + /// if the value was stored. + fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool; + + /// Find a value with the given `key`. Return it, or None + /// if it doesn't exist. + fn get(&self, key: &[u8]) -> Option<Vec<u8>>; + + /// Find a value with the given `key`. Return it and delete it; + /// or None if it doesn't exist. + fn take(&self, key: &[u8]) -> Option<Vec<u8>>; + + /// Whether the store can cache another session. This is used to indicate to clients + /// whether their session can be resumed; the implementation is not required to remember + /// a session even if it returns `true` here. + fn can_cache(&self) -> bool; +} + +/// A trait for the ability to encrypt and decrypt tickets. +pub trait ProducesTickets: Debug + Send + Sync { + /// Returns true if this implementation will encrypt/decrypt + /// tickets. Should return false if this is a dummy + /// implementation: the server will not send the SessionTicket + /// extension and will not call the other functions. + fn enabled(&self) -> bool; + + /// Returns the lifetime in seconds of tickets produced now. + /// The lifetime is provided as a hint to clients that the + /// ticket will not be useful after the given time. + /// + /// This lifetime must be implemented by key rolling and + /// erasure, *not* by storing a lifetime in the ticket. + /// + /// The objective is to limit damage to forward secrecy caused + /// by tickets, not just limiting their lifetime. + fn lifetime(&self) -> u32; + + /// Encrypt and authenticate `plain`, returning the resulting + /// ticket. Return None if `plain` cannot be encrypted for + /// some reason: an empty ticket will be sent and the connection + /// will continue. + fn encrypt(&self, plain: &[u8]) -> Option<Vec<u8>>; + + /// Decrypt `cipher`, validating its authenticity protection + /// and recovering the plaintext. `cipher` is fully attacker + /// controlled, so this decryption must be side-channel free, + /// panic-proof, and otherwise bullet-proof. If the decryption + /// fails, return None. + fn decrypt(&self, cipher: &[u8]) -> Option<Vec<u8>>; +} + +/// How to choose a certificate chain and signing key for use +/// in server authentication. +/// +/// This is suitable when selecting a certificate does not require +/// I/O or when the application is using blocking I/O anyhow. +/// +/// For applications that use async I/O and need to do I/O to choose +/// a certificate (for instance, fetching a certificate from a data store), +/// the [`Acceptor`] interface is more suitable. +pub trait ResolvesServerCert: Debug + Send + Sync { + /// Choose a certificate chain and matching key given simplified + /// ClientHello information. + /// + /// Return `None` to abort the handshake. + fn resolve(&self, client_hello: ClientHello<'_>) -> Option<Arc<sign::CertifiedKey>>; + + /// Return true when the server only supports raw public keys. + fn only_raw_public_keys(&self) -> bool { + false + } +} + +/// A struct representing the received Client Hello +#[derive(Debug)] +pub struct ClientHello<'a> { + pub(super) server_name: &'a Option<DnsName<'a>>, + pub(super) signature_schemes: &'a [SignatureScheme], + pub(super) alpn: Option<&'a Vec<ProtocolName>>, + pub(super) server_cert_types: Option<&'a [CertificateType]>, + pub(super) client_cert_types: Option<&'a [CertificateType]>, + pub(super) cipher_suites: &'a [CipherSuite], + /// The [certificate_authorities] extension, if it was sent by the client. + /// + /// [certificate_authorities]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.4 + pub(super) certificate_authorities: Option<&'a [DistinguishedName]>, + pub(super) named_groups: Option<&'a [NamedGroup]>, +} + +impl<'a> ClientHello<'a> { + /// Get the server name indicator. + /// + /// Returns `None` if the client did not supply a SNI. + pub fn server_name(&self) -> Option<&str> { + self.server_name + .as_ref() + .map(<DnsName<'_> as AsRef<str>>::as_ref) + } + + /// Get the compatible signature schemes. + /// + /// Returns standard-specified default if the client omitted this extension. + pub fn signature_schemes(&self) -> &[SignatureScheme] { + self.signature_schemes + } + + /// Get the ALPN protocol identifiers submitted by the client. + /// + /// Returns `None` if the client did not include an ALPN extension. + /// + /// Application Layer Protocol Negotiation (ALPN) is a TLS extension that lets a client + /// submit a set of identifiers that each a represent an application-layer protocol. + /// The server will then pick its preferred protocol from the set submitted by the client. + /// Each identifier is represented as a byte array, although common values are often ASCII-encoded. + /// See the official RFC-7301 specifications at <https://datatracker.ietf.org/doc/html/rfc7301> + /// for more information on ALPN. + /// + /// For example, a HTTP client might specify "http/1.1" and/or "h2". Other well-known values + /// are listed in the at IANA registry at + /// <https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids>. + /// + /// The server can specify supported ALPN protocols by setting [`ServerConfig::alpn_protocols`]. + /// During the handshake, the server will select the first protocol configured that the client supports. + pub fn alpn(&self) -> Option<impl Iterator<Item = &'a [u8]>> { + self.alpn.map(|protocols| { + protocols + .iter() + .map(|proto| proto.as_ref()) + }) + } + + /// Get cipher suites. + pub fn cipher_suites(&self) -> &[CipherSuite] { + self.cipher_suites + } + + /// Get the server certificate types offered in the ClientHello. + /// + /// Returns `None` if the client did not include a certificate type extension. + pub fn server_cert_types(&self) -> Option<&'a [CertificateType]> { + self.server_cert_types + } + + /// Get the client certificate types offered in the ClientHello. + /// + /// Returns `None` if the client did not include a certificate type extension. + pub fn client_cert_types(&self) -> Option<&'a [CertificateType]> { + self.client_cert_types + } + + /// Get the [certificate_authorities] extension sent by the client. + /// + /// Returns `None` if the client did not send this extension. + /// + /// [certificate_authorities]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.4 + pub fn certificate_authorities(&self) -> Option<&'a [DistinguishedName]> { + self.certificate_authorities + } + + /// Get the [`named_groups`] extension sent by the client. + /// + /// This means different things in different versions of TLS: + /// + /// Originally it was introduced as the "[`elliptic_curves`]" extension for TLS1.2. + /// It described the elliptic curves supported by a client for all purposes: key + /// exchange, signature verification (for server authentication), and signing (for + /// client auth). Later [RFC7919] extended this to include FFDHE "named groups", + /// but FFDHE groups in this context only relate to key exchange. + /// + /// In TLS1.3 it was renamed to "[`named_groups`]" and now describes all types + /// of key exchange mechanisms, and does not relate at all to elliptic curves + /// used for signatures. + /// + /// [`elliptic_curves`]: https://datatracker.ietf.org/doc/html/rfc4492#section-5.1.1 + /// [RFC7919]: https://datatracker.ietf.org/doc/html/rfc7919#section-2 + /// [`named_groups`]:https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.7 + pub fn named_groups(&self) -> Option<&'a [NamedGroup]> { + self.named_groups + } +} + +/// Common configuration for a set of server sessions. +/// +/// Making one of these is cheap, though one of the inputs may be expensive: gathering trust roots +/// from the operating system to add to the [`RootCertStore`] passed to a `ClientCertVerifier` +/// builder may take on the order of a few hundred milliseconds. +/// +/// These must be created via the [`ServerConfig::builder()`] or [`ServerConfig::builder_with_provider()`] +/// function. +/// +/// # Defaults +/// +/// * [`ServerConfig::max_fragment_size`]: the default is `None` (meaning 16kB). +/// * [`ServerConfig::session_storage`]: if the `std` feature is enabled, the default stores 256 +/// sessions in memory. If the `std` feature is not enabled, the default is to not store any +/// sessions. In a no-std context, by enabling the `hashbrown` feature you may provide your +/// own `session_storage` using [`ServerSessionMemoryCache`] and a `crate::lock::MakeMutex` +/// implementation. +/// * [`ServerConfig::alpn_protocols`]: the default is empty -- no ALPN protocol is negotiated. +/// * [`ServerConfig::key_log`]: key material is not logged. +/// * [`ServerConfig::send_tls13_tickets`]: 2 tickets are sent. +/// * [`ServerConfig::cert_compressors`]: depends on the crate features, see [`compress::default_cert_compressors()`]. +/// * [`ServerConfig::cert_compression_cache`]: caches the most recently used 4 compressions +/// * [`ServerConfig::cert_decompressors`]: depends on the crate features, see [`compress::default_cert_decompressors()`]. +/// +/// # Sharing resumption storage between `ServerConfig`s +/// +/// In a program using many `ServerConfig`s it may improve resumption rates +/// (which has a significant impact on connection performance) if those +/// configs share [`ServerConfig::session_storage`] or [`ServerConfig::ticketer`]. +/// +/// However, caution is needed: other fields influence the security of a session +/// and resumption between them can be surprising. If sharing +/// [`ServerConfig::session_storage`] or [`ServerConfig::ticketer`] between two +/// `ServerConfig`s, you should also evaluate the following fields and ensure +/// they are equivalent: +/// +/// * `ServerConfig::verifier` -- client authentication requirements, +/// * [`ServerConfig::cert_resolver`] -- server identities. +/// +/// To illustrate, imagine two `ServerConfig`s `A` and `B`. `A` requires +/// client authentication, `B` does not. If `A` and `B` shared a resumption store, +/// it would be possible for a session originated by `B` (that is, an unauthenticated client) +/// to be inserted into the store, and then resumed by `A`. This would give a false +/// impression to the user of `A` that the client was authenticated. This is possible +/// whether the resumption is performed statefully (via [`ServerConfig::session_storage`]) +/// or statelessly (via [`ServerConfig::ticketer`]). +/// +/// _Unlike_ `ClientConfig`, rustls does not enforce any policy here. +/// +/// [`RootCertStore`]: crate::RootCertStore +/// [`ServerSessionMemoryCache`]: crate::server::handy::ServerSessionMemoryCache +#[derive(Clone, Debug)] +pub struct ServerConfig { + /// Source of randomness and other crypto. + pub(super) provider: Arc<CryptoProvider>, + + /// Ignore the client's ciphersuite order. Instead, + /// choose the top ciphersuite in the server list + /// which is supported by the client. + pub ignore_client_order: bool, + + /// The maximum size of plaintext input to be emitted in a single TLS record. + /// A value of None is equivalent to the [TLS maximum] of 16 kB. + /// + /// rustls enforces an arbitrary minimum of 32 bytes for this field. + /// Out of range values are reported as errors from [ServerConnection::new]. + /// + /// Setting this value to a little less than the TCP MSS may improve latency + /// for stream-y workloads. + /// + /// [TLS maximum]: https://datatracker.ietf.org/doc/html/rfc8446#section-5.1 + /// [ServerConnection::new]: crate::server::ServerConnection::new + pub max_fragment_size: Option<usize>, + + /// How to store client sessions. + /// + /// See [ServerConfig#sharing-resumption-storage-between-serverconfigs] + /// for a warning related to this field. + pub session_storage: Arc<dyn StoresServerSessions>, + + /// How to produce tickets. + /// + /// See [ServerConfig#sharing-resumption-storage-between-serverconfigs] + /// for a warning related to this field. + pub ticketer: Arc<dyn ProducesTickets>, + + /// How to choose a server cert and key. This is usually set by + /// [ConfigBuilder::with_single_cert] or [ConfigBuilder::with_cert_resolver]. + /// For async applications, see also [Acceptor]. + pub cert_resolver: Arc<dyn ResolvesServerCert>, + + /// Protocol names we support, most preferred first. + /// If empty we don't do ALPN at all. + pub alpn_protocols: Vec<Vec<u8>>, + + /// Supported protocol versions, in no particular order. + /// The default is all supported versions. + pub(super) versions: versions::EnabledVersions, + + /// How to verify client certificates. + pub(super) verifier: Arc<dyn verify::ClientCertVerifier>, + + /// How to output key material for debugging. The default + /// does nothing. + pub key_log: Arc<dyn KeyLog>, + + /// Allows traffic secrets to be extracted after the handshake, + /// e.g. for kTLS setup. + pub enable_secret_extraction: bool, + + /// Amount of early data to accept for sessions created by + /// this config. Specify 0 to disable early data. The + /// default is 0. + /// + /// Read the early data via [`ServerConnection::early_data`]. + /// + /// The units for this are _both_ plaintext bytes, _and_ ciphertext + /// bytes, depending on whether the server accepts a client's early_data + /// or not. It is therefore recommended to include some slop in + /// this value to account for the unknown amount of ciphertext + /// expansion in the latter case. + pub max_early_data_size: u32, + + /// Whether the server should send "0.5RTT" data. This means the server + /// sends data after its first flight of handshake messages, without + /// waiting for the client to complete the handshake. + /// + /// This can improve TTFB latency for either server-speaks-first protocols, + /// or client-speaks-first protocols when paired with "0RTT" data. This + /// comes at the cost of a subtle weakening of the normal handshake + /// integrity guarantees that TLS provides. Note that the initial + /// `ClientHello` is indirectly authenticated because it is included + /// in the transcript used to derive the keys used to encrypt the data. + /// + /// This only applies to TLS1.3 connections. TLS1.2 connections cannot + /// do this optimisation and this setting is ignored for them. It is + /// also ignored for TLS1.3 connections that even attempt client + /// authentication. + /// + /// This defaults to false. This means the first application data + /// sent by the server comes after receiving and validating the client's + /// handshake up to the `Finished` message. This is the safest option. + pub send_half_rtt_data: bool, + + /// How many TLS1.3 tickets to send immediately after a successful + /// handshake. + /// + /// Because TLS1.3 tickets are single-use, this allows + /// a client to perform multiple resumptions. + /// + /// The default is 2. + /// + /// If this is 0, no tickets are sent and clients will not be able to + /// do any resumption. + pub send_tls13_tickets: usize, + + /// If set to `true`, requires the client to support the extended + /// master secret extraction method defined in [RFC 7627]. + /// + /// The default is `true` if the "fips" crate feature is enabled, + /// `false` otherwise. + /// + /// It must be set to `true` to meet FIPS requirement mentioned in section + /// **D.Q Transition of the TLS 1.2 KDF to Support the Extended Master + /// Secret** from [FIPS 140-3 IG.pdf]. + /// + /// [RFC 7627]: https://datatracker.ietf.org/doc/html/rfc7627 + /// [FIPS 140-3 IG.pdf]: https://csrc.nist.gov/csrc/media/Projects/cryptographic-module-validation-program/documents/fips%20140-3/FIPS%20140-3%20IG.pdf + #[cfg(feature = "tls12")] + pub require_ems: bool, + + /// Provides the current system time + pub time_provider: Arc<dyn TimeProvider>, + + /// How to compress the server's certificate chain. + /// + /// If a client supports this extension, and advertises support + /// for one of the compression algorithms included here, the + /// server certificate will be compressed according to [RFC8779]. + /// + /// This only applies to TLS1.3 connections. It is ignored for + /// TLS1.2 connections. + /// + /// [RFC8779]: https://datatracker.ietf.org/doc/rfc8879/ + pub cert_compressors: Vec<&'static dyn compress::CertCompressor>, + + /// Caching for compressed certificates. + /// + /// This is optional: [`compress::CompressionCache::Disabled`] gives + /// a cache that does no caching. + pub cert_compression_cache: Arc<compress::CompressionCache>, + + /// How to decompress the clients's certificate chain. + /// + /// If this is non-empty, the [RFC8779] certificate compression + /// extension is offered when requesting client authentication, + /// and any compressed certificates are transparently decompressed + /// during the handshake. + /// + /// This only applies to TLS1.3 connections. It is ignored for + /// TLS1.2 connections. + /// + /// [RFC8779]: https://datatracker.ietf.org/doc/rfc8879/ + pub cert_decompressors: Vec<&'static dyn compress::CertDecompressor>, +} + +impl ServerConfig { + /// Create a builder for a server configuration with + /// [the process-default `CryptoProvider`][CryptoProvider#using-the-per-process-default-cryptoprovider] + /// and safe protocol version defaults. + /// + /// For more information, see the [`ConfigBuilder`] documentation. + #[cfg(feature = "std")] + pub fn builder() -> ConfigBuilder<Self, WantsVerifier> { + Self::builder_with_protocol_versions(versions::DEFAULT_VERSIONS) + } + + /// Create a builder for a server configuration with + /// [the process-default `CryptoProvider`][CryptoProvider#using-the-per-process-default-cryptoprovider] + /// and the provided protocol versions. + /// + /// Panics if + /// - the supported versions are not compatible with the provider (eg. + /// the combination of ciphersuites supported by the provider and supported + /// versions lead to zero cipher suites being usable), + /// - if a `CryptoProvider` cannot be resolved using a combination of + /// the crate features and process default. + /// + /// For more information, see the [`ConfigBuilder`] documentation. + #[cfg(feature = "std")] + pub fn builder_with_protocol_versions( + versions: &[&'static versions::SupportedProtocolVersion], + ) -> ConfigBuilder<Self, WantsVerifier> { + // Safety assumptions: + // 1. that the provider has been installed (explicitly or implicitly) + // 2. that the process-level default provider is usable with the supplied protocol versions. + Self::builder_with_provider( + CryptoProvider::get_default_or_install_from_crate_features().clone(), + ) + .with_protocol_versions(versions) + .unwrap() + } + + /// Create a builder for a server configuration with a specific [`CryptoProvider`]. + /// + /// This will use the provider's configured ciphersuites. You must additionally choose + /// which protocol versions to enable, using `with_protocol_versions` or + /// `with_safe_default_protocol_versions` and handling the `Result` in case a protocol + /// version is not supported by the provider's ciphersuites. + /// + /// For more information, see the [`ConfigBuilder`] documentation. + #[cfg(feature = "std")] + pub fn builder_with_provider( + provider: Arc<CryptoProvider>, + ) -> ConfigBuilder<Self, WantsVersions> { + ConfigBuilder { + state: WantsVersions {}, + provider, + time_provider: Arc::new(DefaultTimeProvider), + side: PhantomData, + } + } + + /// Create a builder for a server configuration with no default implementation details. + /// + /// This API must be used by `no_std` users. + /// + /// You must provide a specific [`TimeProvider`]. + /// + /// You must provide a specific [`CryptoProvider`]. + /// + /// This will use the provider's configured ciphersuites. You must additionally choose + /// which protocol versions to enable, using `with_protocol_versions` or + /// `with_safe_default_protocol_versions` and handling the `Result` in case a protocol + /// version is not supported by the provider's ciphersuites. + /// + /// For more information, see the [`ConfigBuilder`] documentation. + pub fn builder_with_details( + provider: Arc<CryptoProvider>, + time_provider: Arc<dyn TimeProvider>, + ) -> ConfigBuilder<Self, WantsVersions> { + ConfigBuilder { + state: WantsVersions {}, + provider, + time_provider, + side: PhantomData, + } + } + + /// Return `true` if connections made with this `ServerConfig` will + /// operate in FIPS mode. + /// + /// This is different from [`CryptoProvider::fips()`]: [`CryptoProvider::fips()`] + /// is concerned only with cryptography, whereas this _also_ covers TLS-level + /// configuration that NIST recommends. + pub fn fips(&self) -> bool { + #[cfg(feature = "tls12")] + { + self.provider.fips() && self.require_ems + } + + #[cfg(not(feature = "tls12"))] + { + self.provider.fips() + } + } + + /// Return the crypto provider used to construct this client configuration. + pub fn crypto_provider(&self) -> &Arc<CryptoProvider> { + &self.provider + } + + /// We support a given TLS version if it's quoted in the configured + /// versions *and* at least one ciphersuite for this version is + /// also configured. + pub(crate) fn supports_version(&self, v: ProtocolVersion) -> bool { + self.versions.contains(v) + && self + .provider + .cipher_suites + .iter() + .any(|cs| cs.version().version == v) + } + + #[cfg(feature = "std")] + pub(crate) fn supports_protocol(&self, proto: Protocol) -> bool { + self.provider + .cipher_suites + .iter() + .any(|cs| cs.usable_for_protocol(proto)) + } + + pub(super) fn current_time(&self) -> Result<UnixTime, Error> { + self.time_provider + .current_time() + .ok_or(Error::FailedToGetCurrentTime) + } +} + +#[cfg(feature = "std")] +mod connection { + use alloc::boxed::Box; + use core::fmt; + use core::fmt::{Debug, Formatter}; + use core::ops::{Deref, DerefMut}; + use std::io; + + use super::{ + Accepted, Accepting, EarlyDataState, ServerConfig, ServerConnectionData, + ServerExtensionsInput, + }; + use crate::common_state::{CommonState, Context, Side}; + use crate::conn::{ConnectionCommon, ConnectionCore}; + use crate::error::Error; + use crate::server::hs; + use crate::suites::ExtractedSecrets; + use crate::sync::Arc; + use crate::vecbuf::ChunkVecBuffer; + + /// Allows reading of early data in resumed TLS1.3 connections. + /// + /// "Early data" is also known as "0-RTT data". + /// + /// This structure implements [`std::io::Read`]. + pub struct ReadEarlyData<'a> { + early_data: &'a mut EarlyDataState, + } + + impl<'a> ReadEarlyData<'a> { + fn new(early_data: &'a mut EarlyDataState) -> Self { + ReadEarlyData { early_data } + } + } + + impl io::Read for ReadEarlyData<'_> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.early_data.read(buf) + } + + #[cfg(read_buf)] + fn read_buf(&mut self, cursor: core::io::BorrowedCursor<'_>) -> io::Result<()> { + self.early_data.read_buf(cursor) + } + } + + /// This represents a single TLS server connection. + /// + /// Send TLS-protected data to the peer using the `io::Write` trait implementation. + /// Read data from the peer using the `io::Read` trait implementation. + pub struct ServerConnection { + pub(super) inner: ConnectionCommon<ServerConnectionData>, + } + + impl ServerConnection { + /// Make a new ServerConnection. `config` controls how + /// we behave in the TLS protocol. + pub fn new(config: Arc<ServerConfig>) -> Result<Self, Error> { + Ok(Self { + inner: ConnectionCommon::from(ConnectionCore::for_server( + config, + ServerExtensionsInput::default(), + )?), + }) + } + + /// Retrieves the server name, if any, used to select the certificate and + /// private key. + /// + /// This returns `None` until some time after the client's server name indication + /// (SNI) extension value is processed during the handshake. It will never be + /// `None` when the connection is ready to send or process application data, + /// unless the client does not support SNI. + /// + /// This is useful for application protocols that need to enforce that the + /// server name matches an application layer protocol hostname. For + /// example, HTTP/1.1 servers commonly expect the `Host:` header field of + /// every request on a connection to match the hostname in the SNI extension + /// when the client provides the SNI extension. + /// + /// The server name is also used to match sessions during session resumption. + pub fn server_name(&self) -> Option<&str> { + self.inner.core.get_sni_str() + } + + /// Application-controlled portion of the resumption ticket supplied by the client, if any. + /// + /// Recovered from the prior session's `set_resumption_data`. Integrity is guaranteed by rustls. + /// + /// Returns `Some` if and only if a valid resumption ticket has been received from the client. + pub fn received_resumption_data(&self) -> Option<&[u8]> { + self.inner + .core + .data + .received_resumption_data + .as_ref() + .map(|x| &x[..]) + } + + /// Set the resumption data to embed in future resumption tickets supplied to the client. + /// + /// Defaults to the empty byte string. Must be less than 2^15 bytes to allow room for other + /// data. Should be called while `is_handshaking` returns true to ensure all transmitted + /// resumption tickets are affected. + /// + /// Integrity will be assured by rustls, but the data will be visible to the client. If secrecy + /// from the client is desired, encrypt the data separately. + pub fn set_resumption_data(&mut self, data: &[u8]) { + assert!(data.len() < 2usize.pow(15)); + self.inner.core.data.resumption_data = data.into(); + } + + /// Explicitly discard early data, notifying the client + /// + /// Useful if invariants encoded in `received_resumption_data()` cannot be respected. + /// + /// Must be called while `is_handshaking` is true. + pub fn reject_early_data(&mut self) { + self.inner.core.reject_early_data() + } + + /// Returns an `io::Read` implementer you can read bytes from that are + /// received from a client as TLS1.3 0RTT/"early" data, during the handshake. + /// + /// This returns `None` in many circumstances, such as : + /// + /// - Early data is disabled if [`ServerConfig::max_early_data_size`] is zero (the default). + /// - The session negotiated with the client is not TLS1.3. + /// - The client just doesn't support early data. + /// - The connection doesn't resume an existing session. + /// - The client hasn't sent a full ClientHello yet. + pub fn early_data(&mut self) -> Option<ReadEarlyData<'_>> { + let data = &mut self.inner.core.data; + if data.early_data.was_accepted() { + Some(ReadEarlyData::new(&mut data.early_data)) + } else { + None + } + } + + /// Return true if the connection was made with a `ServerConfig` that is FIPS compatible. + /// + /// This is different from [`crate::crypto::CryptoProvider::fips()`]: + /// it is concerned only with cryptography, whereas this _also_ covers TLS-level + /// configuration that NIST recommends, as well as ECH HPKE suites if applicable. + pub fn fips(&self) -> bool { + self.inner.core.common_state.fips + } + + /// Extract secrets, so they can be used when configuring kTLS, for example. + /// Should be used with care as it exposes secret key material. + pub fn dangerous_extract_secrets(self) -> Result<ExtractedSecrets, Error> { + self.inner.dangerous_extract_secrets() + } + } + + impl Debug for ServerConnection { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("ServerConnection") + .finish() + } + } + + impl Deref for ServerConnection { + type Target = ConnectionCommon<ServerConnectionData>; + + fn deref(&self) -> &Self::Target { + &self.inner + } + } + + impl DerefMut for ServerConnection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } + } + + impl From<ServerConnection> for crate::Connection { + fn from(conn: ServerConnection) -> Self { + Self::Server(conn) + } + } + + /// Handle a server-side connection before configuration is available. + /// + /// `Acceptor` allows the caller to choose a [`ServerConfig`] after reading + /// the [`super::ClientHello`] of an incoming connection. This is useful for servers + /// that choose different certificates or cipher suites based on the + /// characteristics of the `ClientHello`. In particular it is useful for + /// servers that need to do some I/O to load a certificate and its private key + /// and don't want to use the blocking interface provided by + /// [`super::ResolvesServerCert`]. + /// + /// Create an Acceptor with [`Acceptor::default()`]. + /// + /// # Example + /// + /// ```no_run + /// # #[cfg(feature = "aws_lc_rs")] { + /// # fn choose_server_config( + /// # _: rustls::server::ClientHello, + /// # ) -> std::sync::Arc<rustls::ServerConfig> { + /// # unimplemented!(); + /// # } + /// # #[allow(unused_variables)] + /// # fn main() { + /// use rustls::server::{Acceptor, ServerConfig}; + /// let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); + /// for stream in listener.incoming() { + /// let mut stream = stream.unwrap(); + /// let mut acceptor = Acceptor::default(); + /// let accepted = loop { + /// acceptor.read_tls(&mut stream).unwrap(); + /// if let Some(accepted) = acceptor.accept().unwrap() { + /// break accepted; + /// } + /// }; + /// + /// // For some user-defined choose_server_config: + /// let config = choose_server_config(accepted.client_hello()); + /// let conn = accepted + /// .into_connection(config) + /// .unwrap(); + /// + /// // Proceed with handling the ServerConnection. + /// } + /// # } + /// # } + /// ``` + pub struct Acceptor { + inner: Option<ConnectionCommon<ServerConnectionData>>, + } + + impl Default for Acceptor { + /// Return an empty Acceptor, ready to receive bytes from a new client connection. + fn default() -> Self { + Self { + inner: Some( + ConnectionCore::new( + Box::new(Accepting), + ServerConnectionData::default(), + CommonState::new(Side::Server), + ) + .into(), + ), + } + } + } + + impl Acceptor { + /// Read TLS content from `rd`. + /// + /// Returns an error if this `Acceptor` has already yielded an [`Accepted`]. For more details, + /// refer to [`Connection::read_tls()`]. + /// + /// [`Connection::read_tls()`]: crate::Connection::read_tls + pub fn read_tls(&mut self, rd: &mut dyn io::Read) -> Result<usize, io::Error> { + match &mut self.inner { + Some(conn) => conn.read_tls(rd), + None => Err(io::Error::new( + io::ErrorKind::Other, + "acceptor cannot read after successful acceptance", + )), + } + } + + /// Check if a `ClientHello` message has been received. + /// + /// Returns `Ok(None)` if the complete `ClientHello` has not yet been received. + /// Do more I/O and then call this function again. + /// + /// Returns `Ok(Some(accepted))` if the connection has been accepted. Call + /// `accepted.into_connection()` to continue. Do not call this function again. + /// + /// Returns `Err((err, alert))` if an error occurred. If an alert is returned, the + /// application should call `alert.write()` to send the alert to the client. It should + /// not call `accept()` again. + pub fn accept(&mut self) -> Result<Option<Accepted>, (Error, AcceptedAlert)> { + let Some(mut connection) = self.inner.take() else { + return Err(( + Error::General("Acceptor polled after completion".into()), + AcceptedAlert::empty(), + )); + }; + + let message = match connection.first_handshake_message() { + Ok(Some(msg)) => msg, + Ok(None) => { + self.inner = Some(connection); + return Ok(None); + } + Err(err) => return Err((err, AcceptedAlert::from(connection))), + }; + + let mut cx = Context::from(&mut connection); + let sig_schemes = match hs::process_client_hello(&message, false, &mut cx) { + Ok((_, sig_schemes)) => sig_schemes, + Err(err) => { + return Err((err, AcceptedAlert::from(connection))); + } + }; + + Ok(Some(Accepted { + connection, + message, + sig_schemes, + })) + } + } + + /// Represents a TLS alert resulting from handling the client's `ClientHello` message. + /// + /// When [`Acceptor::accept()`] returns an error, it yields an `AcceptedAlert` such that the + /// application can communicate failure to the client via [`AcceptedAlert::write()`]. + pub struct AcceptedAlert(ChunkVecBuffer); + + impl AcceptedAlert { + pub(super) fn empty() -> Self { + Self(ChunkVecBuffer::new(None)) + } + + /// Send the alert to the client. + /// + /// To account for short writes this function should be called repeatedly until it + /// returns `Ok(0)` or an error. + pub fn write(&mut self, wr: &mut dyn io::Write) -> Result<usize, io::Error> { + self.0.write_to(wr) + } + + /// Send the alert to the client. + /// + /// This function will invoke the writer until the buffer is empty. + pub fn write_all(&mut self, wr: &mut dyn io::Write) -> Result<(), io::Error> { + while self.write(wr)? != 0 {} + Ok(()) + } + } + + impl From<ConnectionCommon<ServerConnectionData>> for AcceptedAlert { + fn from(conn: ConnectionCommon<ServerConnectionData>) -> Self { + Self(conn.core.common_state.sendable_tls) + } + } + + impl Debug for AcceptedAlert { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("AcceptedAlert").finish() + } + } +} + +#[cfg(feature = "std")] +pub use connection::{AcceptedAlert, Acceptor, ReadEarlyData, ServerConnection}; + +/// Unbuffered version of `ServerConnection` +/// +/// See the [`crate::unbuffered`] module docs for more details +pub struct UnbufferedServerConnection { + inner: UnbufferedConnectionCommon<ServerConnectionData>, +} + +impl UnbufferedServerConnection { + /// Make a new ServerConnection. `config` controls how we behave in the TLS protocol. + pub fn new(config: Arc<ServerConfig>) -> Result<Self, Error> { + Ok(Self { + inner: UnbufferedConnectionCommon::from(ConnectionCore::for_server( + config, + ServerExtensionsInput::default(), + )?), + }) + } + + /// Extract secrets, so they can be used when configuring kTLS, for example. + /// Should be used with care as it exposes secret key material. + #[deprecated = "dangerous_extract_secrets() does not support session tickets or \ + key updates, use dangerous_into_kernel_connection() instead"] + pub fn dangerous_extract_secrets(self) -> Result<ExtractedSecrets, Error> { + self.inner.dangerous_extract_secrets() + } + + /// Extract secrets and an [`KernelConnection`] object. + /// + /// This allows you use rustls to manage keys and then manage encryption and + /// decryption yourself (e.g. for kTLS). + /// + /// Should be used with care as it exposes secret key material. + /// + /// See the [`crate::kernel`] documentations for details on prerequisites + /// for calling this method. + pub fn dangerous_into_kernel_connection( + self, + ) -> Result<(ExtractedSecrets, KernelConnection<ServerConnectionData>), Error> { + self.inner + .core + .dangerous_into_kernel_connection() + } +} + +impl Deref for UnbufferedServerConnection { + type Target = UnbufferedConnectionCommon<ServerConnectionData>; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for UnbufferedServerConnection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl UnbufferedConnectionCommon<ServerConnectionData> { + pub(crate) fn pop_early_data(&mut self) -> Option<Vec<u8>> { + self.core.data.early_data.pop() + } + + pub(crate) fn peek_early_data(&self) -> Option<&[u8]> { + self.core.data.early_data.peek() + } +} + +/// Represents a `ClientHello` message received through the [`Acceptor`]. +/// +/// Contains the state required to resume the connection through [`Accepted::into_connection()`]. +pub struct Accepted { + connection: ConnectionCommon<ServerConnectionData>, + message: Message<'static>, + sig_schemes: Vec<SignatureScheme>, +} + +impl Accepted { + /// Get the [`ClientHello`] for this connection. + pub fn client_hello(&self) -> ClientHello<'_> { + let payload = Self::client_hello_payload(&self.message); + let ch = ClientHello { + server_name: &self.connection.core.data.sni, + signature_schemes: &self.sig_schemes, + alpn: payload.protocols.as_ref(), + server_cert_types: payload + .server_certificate_types + .as_deref(), + client_cert_types: payload + .client_certificate_types + .as_deref(), + cipher_suites: &payload.cipher_suites, + certificate_authorities: payload + .certificate_authority_names + .as_deref(), + named_groups: payload.named_groups.as_deref(), + }; + + trace!("Accepted::client_hello(): {ch:#?}"); + ch + } + + /// Convert the [`Accepted`] into a [`ServerConnection`]. + /// + /// Takes the state returned from [`Acceptor::accept()`] as well as the [`ServerConfig`] and + /// [`sign::CertifiedKey`] that should be used for the session. Returns an error if + /// configuration-dependent validation of the received `ClientHello` message fails. + #[cfg(feature = "std")] + pub fn into_connection( + mut self, + config: Arc<ServerConfig>, + ) -> Result<ServerConnection, (Error, AcceptedAlert)> { + if let Err(err) = self + .connection + .set_max_fragment_size(config.max_fragment_size) + { + // We have a connection here, but it won't contain an alert since the error + // is with the fragment size configured in the `ServerConfig`. + return Err((err, AcceptedAlert::empty())); + } + + self.connection.enable_secret_extraction = config.enable_secret_extraction; + + let state = hs::ExpectClientHello::new(config, ServerExtensionsInput::default()); + let mut cx = hs::ServerContext::from(&mut self.connection); + + let ch = Self::client_hello_payload(&self.message); + let new = match state.with_certified_key(self.sig_schemes, ch, &self.message, &mut cx) { + Ok(new) => new, + Err(err) => return Err((err, AcceptedAlert::from(self.connection))), + }; + + self.connection.replace_state(new); + Ok(ServerConnection { + inner: self.connection, + }) + } + + fn client_hello_payload<'a>(message: &'a Message<'_>) -> &'a ClientHelloPayload { + match &message.payload { + crate::msgs::message::MessagePayload::Handshake { parsed, .. } => match &parsed.0 { + crate::msgs::handshake::HandshakePayload::ClientHello(ch) => ch, + _ => unreachable!(), + }, + _ => unreachable!(), + } + } +} + +impl Debug for Accepted { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Accepted").finish() + } +} + +#[cfg(feature = "std")] +struct Accepting; + +#[cfg(feature = "std")] +impl State<ServerConnectionData> for Accepting { + fn handle<'m>( + self: Box<Self>, + _cx: &mut hs::ServerContext<'_>, + _m: Message<'m>, + ) -> Result<Box<dyn State<ServerConnectionData> + 'm>, Error> + where + Self: 'm, + { + Err(Error::General("unreachable state".into())) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +pub(super) enum EarlyDataState { + New, + Accepted { + received: ChunkVecBuffer, + left: usize, + }, + Rejected, +} + +impl Default for EarlyDataState { + fn default() -> Self { + Self::New + } +} + +impl EarlyDataState { + pub(super) fn reject(&mut self) { + *self = Self::Rejected; + } + + pub(super) fn accept(&mut self, max_size: usize) { + *self = Self::Accepted { + received: ChunkVecBuffer::new(Some(max_size)), + left: max_size, + }; + } + + #[cfg(feature = "std")] + fn was_accepted(&self) -> bool { + matches!(self, Self::Accepted { .. }) + } + + pub(super) fn was_rejected(&self) -> bool { + matches!(self, Self::Rejected) + } + + fn peek(&self) -> Option<&[u8]> { + match self { + Self::Accepted { received, .. } => received.peek(), + _ => None, + } + } + + fn pop(&mut self) -> Option<Vec<u8>> { + match self { + Self::Accepted { received, .. } => received.pop(), + _ => None, + } + } + + #[cfg(feature = "std")] + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + match self { + Self::Accepted { received, .. } => received.read(buf), + _ => Err(io::Error::from(io::ErrorKind::BrokenPipe)), + } + } + + #[cfg(read_buf)] + fn read_buf(&mut self, cursor: core::io::BorrowedCursor<'_>) -> io::Result<()> { + match self { + Self::Accepted { received, .. } => received.read_buf(cursor), + _ => Err(io::Error::from(io::ErrorKind::BrokenPipe)), + } + } + + pub(super) fn take_received_plaintext(&mut self, bytes: Payload<'_>) -> bool { + let available = bytes.bytes().len(); + let Self::Accepted { received, left } = self else { + return false; + }; + + if received.apply_limit(available) != available || available > *left { + return false; + } + + received.append(bytes.into_vec()); + *left -= available; + true + } +} + +impl Debug for EarlyDataState { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::New => write!(f, "EarlyDataState::New"), + Self::Accepted { received, left } => write!( + f, + "EarlyDataState::Accepted {{ received: {}, left: {} }}", + received.len(), + left + ), + Self::Rejected => write!(f, "EarlyDataState::Rejected"), + } + } +} + +impl ConnectionCore<ServerConnectionData> { + pub(crate) fn for_server( + config: Arc<ServerConfig>, + extra_exts: ServerExtensionsInput<'static>, + ) -> Result<Self, Error> { + let mut common = CommonState::new(Side::Server); + common.set_max_fragment_size(config.max_fragment_size)?; + common.enable_secret_extraction = config.enable_secret_extraction; + common.fips = config.fips(); + Ok(Self::new( + Box::new(hs::ExpectClientHello::new(config, extra_exts)), + ServerConnectionData::default(), + common, + )) + } + + #[cfg(feature = "std")] + pub(crate) fn reject_early_data(&mut self) { + assert!( + self.common_state.is_handshaking(), + "cannot retroactively reject early data" + ); + self.data.early_data.reject(); + } + + #[cfg(feature = "std")] + pub(crate) fn get_sni_str(&self) -> Option<&str> { + self.data.get_sni_str() + } +} + +/// State associated with a server connection. +#[derive(Default, Debug)] +pub struct ServerConnectionData { + pub(super) sni: Option<DnsName<'static>>, + pub(super) received_resumption_data: Option<Vec<u8>>, + pub(super) resumption_data: Vec<u8>, + pub(super) early_data: EarlyDataState, +} + +impl ServerConnectionData { + #[cfg(feature = "std")] + pub(super) fn get_sni_str(&self) -> Option<&str> { + self.sni.as_ref().map(AsRef::as_ref) + } +} + +impl crate::conn::SideData for ServerConnectionData {} + +#[cfg(feature = "std")] +#[cfg(test)] +mod tests { + use std::format; + + use super::*; + + // these branches not reachable externally, unless something else goes wrong. + #[test] + fn test_read_in_new_state() { + assert_eq!( + format!("{:?}", EarlyDataState::default().read(&mut [0u8; 5])), + "Err(Kind(BrokenPipe))" + ); + } + + #[cfg(read_buf)] + #[test] + fn test_read_buf_in_new_state() { + use core::io::BorrowedBuf; + + let mut buf = [0u8; 5]; + let mut buf: BorrowedBuf<'_> = buf.as_mut_slice().into(); + assert_eq!( + format!("{:?}", EarlyDataState::default().read_buf(buf.unfilled())), + "Err(Kind(BrokenPipe))" + ); + } +} diff --git a/vendor/rustls/src/server/test.rs b/vendor/rustls/src/server/test.rs new file mode 100644 index 00000000..9254dbc6 --- /dev/null +++ b/vendor/rustls/src/server/test.rs @@ -0,0 +1,369 @@ +use std::prelude::v1::*; +use std::vec; + +use super::ServerConnectionData; +use crate::common_state::Context; +use crate::enums::{CipherSuite, SignatureScheme}; +use crate::msgs::base::PayloadU16; +use crate::msgs::enums::{Compression, NamedGroup}; +use crate::msgs::handshake::{ + ClientExtensions, ClientHelloPayload, HandshakeMessagePayload, HandshakePayload, KeyShareEntry, + Random, ServerNamePayload, SessionId, SupportedProtocolVersions, +}; +use crate::msgs::message::{Message, MessagePayload}; +use crate::{CommonState, Error, PeerIncompatible, PeerMisbehaved, ProtocolVersion, Side}; + +#[test] +fn null_compression_required() { + assert_eq!( + test_process_client_hello(ClientHelloPayload { + compression_methods: vec![], + ..minimal_client_hello() + }), + Err(PeerIncompatible::NullCompressionRequired.into()), + ); +} + +#[test] +fn server_ignores_sni_with_ip_address() { + let mut ch = minimal_client_hello(); + ch.extensions.server_name = Some(ServerNamePayload::IpAddress); + std::println!("{:?}", ch.extensions); + assert_eq!(test_process_client_hello(ch), Ok(())); +} + +#[test] +fn server_rejects_sni_with_illegal_dns_name() { + let mut ch = minimal_client_hello(); + ch.extensions.server_name = Some(ServerNamePayload::Invalid); + std::println!("{:?}", ch.extensions); + assert_eq!( + test_process_client_hello(ch), + Err(PeerMisbehaved::ServerNameMustContainOneHostName.into()) + ); +} + +fn test_process_client_hello(hello: ClientHelloPayload) -> Result<(), Error> { + let m = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload(HandshakePayload::ClientHello( + hello, + ))), + }; + super::hs::process_client_hello( + &m, + false, + &mut Context { + common: &mut CommonState::new(Side::Server), + data: &mut ServerConnectionData::default(), + sendable_plaintext: None, + }, + ) + .map(|_| ()) +} + +#[macro_rules_attribute::apply(test_for_each_provider)] +mod tests { + use super::super::*; + use crate::common_state::KxState; + use crate::crypto::{ + ActiveKeyExchange, CryptoProvider, KeyExchangeAlgorithm, SupportedKxGroup, + }; + use crate::enums::CertificateType; + use crate::pki_types::pem::PemObject; + use crate::pki_types::{CertificateDer, PrivateKeyDer}; + use crate::server::{AlwaysResolvesServerRawPublicKeys, ServerConfig, ServerConnection}; + use crate::sign::CertifiedKey; + use crate::sync::Arc; + use crate::{CipherSuiteCommon, SupportedCipherSuite, Tls12CipherSuite, version}; + + #[cfg(feature = "tls12")] + #[test] + fn test_server_rejects_no_extended_master_secret_extension_when_require_ems_or_fips() { + let provider = super::provider::default_provider(); + let mut config = ServerConfig::builder_with_provider(provider.into()) + .with_protocol_versions(&[&version::TLS12]) + .unwrap() + .with_no_client_auth() + .with_single_cert(server_cert(), server_key()) + .unwrap(); + + if config.provider.fips() { + assert!(config.require_ems); + } else { + config.require_ems = true; + } + let mut conn = ServerConnection::new(config.into()).unwrap(); + + let mut ch = minimal_client_hello(); + ch.extensions + .extended_master_secret_request + .take(); + let ch = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ClientHello(ch), + )), + }; + conn.read_tls(&mut ch.into_wire_bytes().as_slice()) + .unwrap(); + + assert_eq!( + conn.process_new_packets(), + Err(Error::PeerIncompatible( + PeerIncompatible::ExtendedMasterSecretExtensionRequired + )) + ); + } + + #[cfg(feature = "tls12")] + #[test] + fn server_picks_ffdhe_group_when_clienthello_has_no_ffdhe_group_in_groups_ext() { + let config = ServerConfig::builder_with_provider(ffdhe_provider().into()) + .with_protocol_versions(&[&version::TLS12]) + .unwrap() + .with_no_client_auth() + .with_single_cert(server_cert(), server_key()) + .unwrap(); + + let mut ch = minimal_client_hello(); + ch.cipher_suites + .push(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256.suite()); + + server_chooses_ffdhe_group_for_client_hello( + ServerConnection::new(config.into()).unwrap(), + ch, + ); + } + + #[cfg(feature = "tls12")] + #[test] + fn server_picks_ffdhe_group_when_clienthello_has_no_groups_ext() { + let config = ServerConfig::builder_with_provider(ffdhe_provider().into()) + .with_protocol_versions(&[&version::TLS12]) + .unwrap() + .with_no_client_auth() + .with_single_cert(server_cert(), server_key()) + .unwrap(); + + let mut ch = minimal_client_hello(); + ch.cipher_suites + .push(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256.suite()); + ch.extensions.named_groups.take(); + + server_chooses_ffdhe_group_for_client_hello( + ServerConnection::new(config.into()).unwrap(), + ch, + ); + } + + #[cfg(feature = "tls12")] + #[test] + fn server_accepts_client_with_no_ecpoints_extension_and_only_ffdhe_cipher_suites() { + let config = ServerConfig::builder_with_provider(ffdhe_provider().into()) + .with_protocol_versions(&[&version::TLS12]) + .unwrap() + .with_no_client_auth() + .with_single_cert(server_cert(), server_key()) + .unwrap(); + + let mut ch = minimal_client_hello(); + ch.cipher_suites + .push(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256.suite()); + ch.extensions.ec_point_formats.take(); + + server_chooses_ffdhe_group_for_client_hello( + ServerConnection::new(config.into()).unwrap(), + ch, + ); + } + + fn server_chooses_ffdhe_group_for_client_hello( + mut conn: ServerConnection, + client_hello: ClientHelloPayload, + ) { + let ch = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ClientHello(client_hello), + )), + }; + conn.read_tls(&mut ch.into_wire_bytes().as_slice()) + .unwrap(); + conn.process_new_packets().unwrap(); + + let KxState::Start(skxg) = &conn.kx_state else { + panic!("unexpected kx_state"); + }; + assert_eq!(skxg.name(), FAKE_FFDHE_GROUP.name()); + } + + #[test] + fn test_server_requiring_rpk_client_rejects_x509_client() { + let mut ch = minimal_client_hello(); + ch.extensions.client_certificate_types = Some(vec![CertificateType::X509]); + let ch = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ClientHello(ch), + )), + }; + + let mut conn = ServerConnection::new(server_config_for_rpk().into()).unwrap(); + conn.read_tls(&mut ch.into_wire_bytes().as_slice()) + .unwrap(); + assert_eq!( + conn.process_new_packets().unwrap_err(), + PeerIncompatible::IncorrectCertificateTypeExtension.into(), + ); + } + + #[test] + fn test_rpk_only_server_rejects_x509_only_client() { + let mut ch = minimal_client_hello(); + ch.extensions.server_certificate_types = Some(vec![CertificateType::X509]); + let ch = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ClientHello(ch), + )), + }; + + let mut conn = ServerConnection::new(server_config_for_rpk().into()).unwrap(); + conn.read_tls(&mut ch.into_wire_bytes().as_slice()) + .unwrap(); + assert_eq!( + conn.process_new_packets().unwrap_err(), + PeerIncompatible::IncorrectCertificateTypeExtension.into(), + ); + } + + fn server_config_for_rpk() -> ServerConfig { + let x25519_provider = CryptoProvider { + kx_groups: vec![super::provider::kx_group::X25519], + ..super::provider::default_provider() + }; + ServerConfig::builder_with_provider(x25519_provider.into()) + .with_protocol_versions(&[&version::TLS13]) + .unwrap() + .with_no_client_auth() + .with_cert_resolver(Arc::new(AlwaysResolvesServerRawPublicKeys::new(Arc::new( + server_certified_key(), + )))) + } + + fn server_certified_key() -> CertifiedKey { + let key = super::provider::default_provider() + .key_provider + .load_private_key(server_key()) + .unwrap(); + let public_key_as_cert = vec![CertificateDer::from( + key.public_key() + .unwrap() + .as_ref() + .to_vec(), + )]; + CertifiedKey::new(public_key_as_cert, key) + } + + fn server_key() -> PrivateKeyDer<'static> { + PrivateKeyDer::from_pem_reader( + &mut include_bytes!("../../../test-ca/rsa-2048/end.key").as_slice(), + ) + .unwrap() + } + + fn server_cert() -> Vec<CertificateDer<'static>> { + vec![ + CertificateDer::from(&include_bytes!("../../../test-ca/rsa-2048/end.der")[..]), + CertificateDer::from(&include_bytes!("../../../test-ca/rsa-2048/inter.der")[..]), + ] + } + + fn ffdhe_provider() -> CryptoProvider { + CryptoProvider { + kx_groups: vec![FAKE_FFDHE_GROUP], + cipher_suites: vec![TLS_DHE_RSA_WITH_AES_128_GCM_SHA256], + ..super::provider::default_provider() + } + } + + static FAKE_FFDHE_GROUP: &'static dyn SupportedKxGroup = &FakeFfdheGroup; + + #[derive(Debug)] + struct FakeFfdheGroup; + + impl SupportedKxGroup for FakeFfdheGroup { + fn name(&self) -> NamedGroup { + NamedGroup::FFDHE2048 + } + + fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> { + Ok(Box::new(ActiveFakeFfdhe)) + } + } + + #[derive(Debug)] + struct ActiveFakeFfdhe; + + impl ActiveKeyExchange for ActiveFakeFfdhe { + #[cfg_attr(coverage_nightly, coverage(off))] + fn complete( + self: Box<Self>, + _peer_pub_key: &[u8], + ) -> Result<crate::crypto::SharedSecret, Error> { + todo!() + } + + fn pub_key(&self) -> &[u8] { + b"ActiveFakeFfdhe pub key" + } + + fn group(&self) -> NamedGroup { + NamedGroup::FFDHE2048 + } + } + + static TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&TLS12_DHE_RSA_WITH_AES_128_GCM_SHA256); + + static TLS12_DHE_RSA_WITH_AES_128_GCM_SHA256: Tls12CipherSuite = + match &super::provider::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 { + SupportedCipherSuite::Tls12(provider) => Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + ..provider.common + }, + kx: KeyExchangeAlgorithm::DHE, + ..**provider + }, + _ => unreachable!(), + }; +} + +fn minimal_client_hello() -> ClientHelloPayload { + ClientHelloPayload { + client_version: ProtocolVersion::TLSv1_3, + random: Random::from([0u8; 32]), + session_id: SessionId::empty(), + cipher_suites: vec![ + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS13_AES_128_GCM_SHA256, + ], + compression_methods: vec![Compression::Null], + extensions: Box::new(ClientExtensions { + signature_schemes: Some(vec![SignatureScheme::RSA_PSS_SHA256]), + named_groups: Some(vec![NamedGroup::X25519, NamedGroup::secp256r1]), + supported_versions: Some(SupportedProtocolVersions { + tls12: true, + tls13: true, + }), + key_shares: Some(vec![KeyShareEntry { + group: NamedGroup::X25519, + payload: PayloadU16::new(vec![0xab; 32]), + }]), + extended_master_secret_request: Some(()), + ..ClientExtensions::default() + }), + } +} diff --git a/vendor/rustls/src/server/tls12.rs b/vendor/rustls/src/server/tls12.rs new file mode 100644 index 00000000..d3dfa5c8 --- /dev/null +++ b/vendor/rustls/src/server/tls12.rs @@ -0,0 +1,1003 @@ +use alloc::boxed::Box; +use alloc::string::ToString; +use alloc::vec; +use alloc::vec::Vec; + +pub(super) use client_hello::CompleteClientHelloHandling; +use pki_types::UnixTime; +use subtle::ConstantTimeEq; + +use super::common::ActiveCertifiedKey; +use super::hs::{self, ServerContext}; +use super::server_conn::{ProducesTickets, ServerConfig, ServerConnectionData}; +use crate::check::inappropriate_message; +use crate::common_state::{CommonState, HandshakeFlightTls12, HandshakeKind, Side, State}; +use crate::conn::ConnectionRandoms; +use crate::conn::kernel::{Direction, KernelContext, KernelState}; +use crate::crypto::ActiveKeyExchange; +use crate::enums::{AlertDescription, ContentType, HandshakeType, ProtocolVersion}; +use crate::error::{Error, PeerIncompatible, PeerMisbehaved}; +use crate::hash_hs::HandshakeHash; +use crate::log::{debug, trace}; +use crate::msgs::base::Payload; +use crate::msgs::ccs::ChangeCipherSpecPayload; +use crate::msgs::codec::Codec; +use crate::msgs::handshake::{ + CertificateChain, ClientKeyExchangeParams, HandshakeMessagePayload, HandshakePayload, + NewSessionTicketPayload, NewSessionTicketPayloadTls13, SessionId, +}; +use crate::msgs::message::{Message, MessagePayload}; +use crate::msgs::persist; +use crate::suites::PartiallyExtractedSecrets; +use crate::sync::Arc; +use crate::tls12::{self, ConnectionSecrets, Tls12CipherSuite}; +use crate::{ConnectionTrafficSecrets, verify}; + +mod client_hello { + use pki_types::CertificateDer; + + use super::*; + use crate::common_state::KxState; + use crate::crypto::SupportedKxGroup; + use crate::enums::SignatureScheme; + use crate::msgs::enums::{ClientCertificateType, Compression}; + use crate::msgs::handshake::{ + CertificateRequestPayload, CertificateStatus, ClientHelloPayload, ClientSessionTicket, + Random, ServerExtensionsInput, ServerHelloPayload, ServerKeyExchange, + ServerKeyExchangeParams, ServerKeyExchangePayload, + }; + use crate::sign; + use crate::verify::DigitallySignedStruct; + + pub(in crate::server) struct CompleteClientHelloHandling { + pub(in crate::server) config: Arc<ServerConfig>, + pub(in crate::server) transcript: HandshakeHash, + pub(in crate::server) session_id: SessionId, + pub(in crate::server) suite: &'static Tls12CipherSuite, + pub(in crate::server) using_ems: bool, + pub(in crate::server) randoms: ConnectionRandoms, + pub(in crate::server) send_ticket: bool, + pub(in crate::server) extra_exts: ServerExtensionsInput<'static>, + } + + impl CompleteClientHelloHandling { + pub(in crate::server) fn handle_client_hello( + mut self, + cx: &mut ServerContext<'_>, + server_key: ActiveCertifiedKey<'_>, + chm: &Message<'_>, + client_hello: &ClientHelloPayload, + selected_kxg: &'static dyn SupportedKxGroup, + sigschemes_ext: Vec<SignatureScheme>, + tls13_enabled: bool, + ) -> hs::NextStateOrError<'static> { + // -- TLS1.2 only from hereon in -- + self.transcript.add_message(chm); + + if client_hello + .extended_master_secret_request + .is_some() + { + self.using_ems = true; + } else if self.config.require_ems { + return Err(cx.common.send_fatal_alert( + AlertDescription::HandshakeFailure, + PeerIncompatible::ExtendedMasterSecretExtensionRequired, + )); + } + + // "RFC 4492 specified that if this extension is missing, + // it means that only the uncompressed point format is + // supported" + // - <https://datatracker.ietf.org/doc/html/rfc8422#section-5.1.2> + let supported_ec_point_formats = client_hello + .ec_point_formats + .unwrap_or_default(); + + trace!("ecpoints {supported_ec_point_formats:?}"); + + if !supported_ec_point_formats.uncompressed { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerIncompatible::UncompressedEcPointsRequired, + )); + } + + // -- If TLS1.3 is enabled, signal the downgrade in the server random + if tls13_enabled { + self.randoms.server[24..].copy_from_slice(&tls12::DOWNGRADE_SENTINEL); + } + + // -- Check for resumption -- + // We can do this either by (in order of preference): + // 1. receiving a ticket that decrypts + // 2. receiving a sessionid that is in our cache + // + // If we receive a ticket, the sessionid won't be in our + // cache, so don't check. + // + // If either works, we end up with a ServerConnectionValue + // which is passed to start_resumption and concludes + // our handling of the ClientHello. + // + let mut ticket_received = false; + let resume_data = client_hello + .session_ticket + .as_ref() + .and_then(|ticket_ext| match ticket_ext { + ClientSessionTicket::Offer(ticket) => Some(ticket), + _ => None, + }) + .and_then(|ticket| { + ticket_received = true; + debug!("Ticket received"); + let data = self + .config + .ticketer + .decrypt(ticket.bytes()); + if data.is_none() { + debug!("Ticket didn't decrypt"); + } + data + }) + .or_else(|| { + // Perhaps resume? If we received a ticket, the sessionid + // does not correspond to a real session. + if client_hello.session_id.is_empty() || ticket_received { + return None; + } + + self.config + .session_storage + .get(client_hello.session_id.as_ref()) + }) + .and_then(|x| persist::ServerSessionValue::read_bytes(&x).ok()) + .filter(|resumedata| { + hs::can_resume(self.suite.into(), &cx.data.sni, self.using_ems, resumedata) + }); + + if let Some(data) = resume_data { + return self.start_resumption(cx, client_hello, &client_hello.session_id, data); + } + + // Now we have chosen a ciphersuite, we can make kx decisions. + let sigschemes = self + .suite + .resolve_sig_schemes(&sigschemes_ext); + + if sigschemes.is_empty() { + return Err(cx.common.send_fatal_alert( + AlertDescription::HandshakeFailure, + PeerIncompatible::NoSignatureSchemesInCommon, + )); + } + + let mut ocsp_response = server_key.get_ocsp(); + + // If we're not offered a ticket or a potential session ID, allocate a session ID. + if !self.config.session_storage.can_cache() { + self.session_id = SessionId::empty(); + } else if self.session_id.is_empty() && !ticket_received { + self.session_id = SessionId::random(self.config.provider.secure_random)?; + } + + cx.common.kx_state = KxState::Start(selected_kxg); + cx.common.handshake_kind = Some(HandshakeKind::Full); + + let mut flight = HandshakeFlightTls12::new(&mut self.transcript); + + self.send_ticket = emit_server_hello( + &mut flight, + &self.config, + cx, + self.session_id, + self.suite, + self.using_ems, + &mut ocsp_response, + client_hello, + None, + &self.randoms, + self.extra_exts, + )?; + emit_certificate(&mut flight, server_key.get_cert()); + if let Some(ocsp_response) = ocsp_response { + emit_cert_status(&mut flight, ocsp_response); + } + let server_kx = emit_server_kx( + &mut flight, + sigschemes, + selected_kxg, + server_key.get_key(), + &self.randoms, + )?; + let doing_client_auth = emit_certificate_req(&mut flight, &self.config)?; + emit_server_hello_done(&mut flight); + + flight.finish(cx.common); + + if doing_client_auth { + Ok(Box::new(ExpectCertificate { + config: self.config, + transcript: self.transcript, + randoms: self.randoms, + session_id: self.session_id, + suite: self.suite, + using_ems: self.using_ems, + server_kx, + send_ticket: self.send_ticket, + })) + } else { + Ok(Box::new(ExpectClientKx { + config: self.config, + transcript: self.transcript, + randoms: self.randoms, + session_id: self.session_id, + suite: self.suite, + using_ems: self.using_ems, + server_kx, + client_cert: None, + send_ticket: self.send_ticket, + })) + } + } + + fn start_resumption( + mut self, + cx: &mut ServerContext<'_>, + client_hello: &ClientHelloPayload, + id: &SessionId, + resumedata: persist::ServerSessionValue, + ) -> hs::NextStateOrError<'static> { + debug!("Resuming connection"); + + if resumedata.extended_ms && !self.using_ems { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::ResumptionAttemptedWithVariedEms, + )); + } + + self.session_id = *id; + let mut flight = HandshakeFlightTls12::new(&mut self.transcript); + self.send_ticket = emit_server_hello( + &mut flight, + &self.config, + cx, + self.session_id, + self.suite, + self.using_ems, + &mut None, + client_hello, + Some(&resumedata), + &self.randoms, + self.extra_exts, + )?; + flight.finish(cx.common); + + let secrets = ConnectionSecrets::new_resume( + self.randoms, + self.suite, + &resumedata.master_secret.0, + ); + self.config.key_log.log( + "CLIENT_RANDOM", + &secrets.randoms.client, + &secrets.master_secret, + ); + cx.common + .start_encryption_tls12(&secrets, Side::Server); + cx.common.peer_certificates = resumedata.client_cert_chain; + cx.common.handshake_kind = Some(HandshakeKind::Resumed); + + if self.send_ticket { + let now = self.config.current_time()?; + + emit_ticket( + &secrets, + &mut self.transcript, + self.using_ems, + cx, + &*self.config.ticketer, + now, + )?; + } + emit_ccs(cx.common); + cx.common + .record_layer + .start_encrypting(); + emit_finished(&secrets, &mut self.transcript, cx.common); + + Ok(Box::new(ExpectCcs { + config: self.config, + secrets, + transcript: self.transcript, + session_id: self.session_id, + using_ems: self.using_ems, + resuming: true, + send_ticket: self.send_ticket, + })) + } + } + + fn emit_server_hello( + flight: &mut HandshakeFlightTls12<'_>, + config: &ServerConfig, + cx: &mut ServerContext<'_>, + session_id: SessionId, + suite: &'static Tls12CipherSuite, + using_ems: bool, + ocsp_response: &mut Option<&[u8]>, + hello: &ClientHelloPayload, + resumedata: Option<&persist::ServerSessionValue>, + randoms: &ConnectionRandoms, + extra_exts: ServerExtensionsInput<'static>, + ) -> Result<bool, Error> { + let mut ep = hs::ExtensionProcessing::new(extra_exts); + ep.process_common(config, cx, ocsp_response, hello, resumedata)?; + ep.process_tls12(config, hello, using_ems); + + let sh = HandshakeMessagePayload(HandshakePayload::ServerHello(ServerHelloPayload { + legacy_version: ProtocolVersion::TLSv1_2, + random: Random::from(randoms.server), + session_id, + cipher_suite: suite.common.suite, + compression_method: Compression::Null, + extensions: ep.extensions, + })); + trace!("sending server hello {sh:?}"); + flight.add(sh); + + Ok(ep.send_ticket) + } + + fn emit_certificate( + flight: &mut HandshakeFlightTls12<'_>, + cert_chain: &[CertificateDer<'static>], + ) { + flight.add(HandshakeMessagePayload(HandshakePayload::Certificate( + CertificateChain(cert_chain.to_vec()), + ))); + } + + fn emit_cert_status(flight: &mut HandshakeFlightTls12<'_>, ocsp: &[u8]) { + flight.add(HandshakeMessagePayload( + HandshakePayload::CertificateStatus(CertificateStatus::new(ocsp)), + )); + } + + fn emit_server_kx( + flight: &mut HandshakeFlightTls12<'_>, + sigschemes: Vec<SignatureScheme>, + selected_group: &'static dyn SupportedKxGroup, + signing_key: &dyn sign::SigningKey, + randoms: &ConnectionRandoms, + ) -> Result<Box<dyn ActiveKeyExchange>, Error> { + let kx = selected_group.start()?; + let kx_params = ServerKeyExchangeParams::new(&*kx); + + let mut msg = Vec::new(); + msg.extend(randoms.client); + msg.extend(randoms.server); + kx_params.encode(&mut msg); + + let signer = signing_key + .choose_scheme(&sigschemes) + .ok_or_else(|| Error::General("incompatible signing key".to_string()))?; + let sigscheme = signer.scheme(); + let sig = signer.sign(&msg)?; + + let skx = ServerKeyExchangePayload::from(ServerKeyExchange { + params: kx_params, + dss: DigitallySignedStruct::new(sigscheme, sig), + }); + + flight.add(HandshakeMessagePayload( + HandshakePayload::ServerKeyExchange(skx), + )); + Ok(kx) + } + + fn emit_certificate_req( + flight: &mut HandshakeFlightTls12<'_>, + config: &ServerConfig, + ) -> Result<bool, Error> { + let client_auth = &config.verifier; + + if !client_auth.offer_client_auth() { + return Ok(false); + } + + let verify_schemes = client_auth.supported_verify_schemes(); + + let names = config + .verifier + .root_hint_subjects() + .to_vec(); + + let cr = CertificateRequestPayload { + certtypes: vec![ + ClientCertificateType::RSASign, + ClientCertificateType::ECDSASign, + ], + sigschemes: verify_schemes, + canames: names, + }; + + let creq = HandshakeMessagePayload(HandshakePayload::CertificateRequest(cr)); + + trace!("Sending CertificateRequest {creq:?}"); + flight.add(creq); + Ok(true) + } + + fn emit_server_hello_done(flight: &mut HandshakeFlightTls12<'_>) { + flight.add(HandshakeMessagePayload(HandshakePayload::ServerHelloDone)); + } +} + +// --- Process client's Certificate for client auth --- +struct ExpectCertificate { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + randoms: ConnectionRandoms, + session_id: SessionId, + suite: &'static Tls12CipherSuite, + using_ems: bool, + server_kx: Box<dyn ActiveKeyExchange>, + send_ticket: bool, +} + +impl State<ServerConnectionData> for ExpectCertificate { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + self.transcript.add_message(&m); + let cert_chain = require_handshake_msg_move!( + m, + HandshakeType::Certificate, + HandshakePayload::Certificate + )?; + + // If we can't determine if the auth is mandatory, abort + let mandatory = self + .config + .verifier + .client_auth_mandatory(); + + trace!("certs {cert_chain:?}"); + + let client_cert = match cert_chain.split_first() { + None if mandatory => { + return Err(cx.common.send_fatal_alert( + AlertDescription::CertificateRequired, + Error::NoCertificatesPresented, + )); + } + None => { + debug!("client auth requested but no certificate supplied"); + self.transcript.abandon_client_auth(); + None + } + Some((end_entity, intermediates)) => { + let now = self.config.current_time()?; + + self.config + .verifier + .verify_client_cert(end_entity, intermediates, now) + .map_err(|err| { + cx.common + .send_cert_verify_error_alert(err) + })?; + + Some(cert_chain) + } + }; + + Ok(Box::new(ExpectClientKx { + config: self.config, + transcript: self.transcript, + randoms: self.randoms, + session_id: self.session_id, + suite: self.suite, + using_ems: self.using_ems, + server_kx: self.server_kx, + client_cert, + send_ticket: self.send_ticket, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// --- Process client's KeyExchange --- +struct ExpectClientKx<'a> { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + randoms: ConnectionRandoms, + session_id: SessionId, + suite: &'static Tls12CipherSuite, + using_ems: bool, + server_kx: Box<dyn ActiveKeyExchange>, + client_cert: Option<CertificateChain<'a>>, + send_ticket: bool, +} + +impl State<ServerConnectionData> for ExpectClientKx<'_> { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let client_kx = require_handshake_msg!( + m, + HandshakeType::ClientKeyExchange, + HandshakePayload::ClientKeyExchange + )?; + self.transcript.add_message(&m); + let ems_seed = self + .using_ems + .then(|| self.transcript.current_hash()); + + // Complete key agreement, and set up encryption with the + // resulting premaster secret. + let peer_kx_params = tls12::decode_kx_params::<ClientKeyExchangeParams>( + self.suite.kx, + cx.common, + client_kx.bytes(), + )?; + let secrets = ConnectionSecrets::from_key_exchange( + self.server_kx, + peer_kx_params.pub_key(), + ems_seed, + self.randoms, + self.suite, + ) + .map_err(|err| { + cx.common + .send_fatal_alert(AlertDescription::IllegalParameter, err) + })?; + cx.common.kx_state.complete(); + + self.config.key_log.log( + "CLIENT_RANDOM", + &secrets.randoms.client, + &secrets.master_secret, + ); + cx.common + .start_encryption_tls12(&secrets, Side::Server); + + match self.client_cert { + Some(client_cert) => Ok(Box::new(ExpectCertificateVerify { + config: self.config, + secrets, + transcript: self.transcript, + session_id: self.session_id, + using_ems: self.using_ems, + client_cert, + send_ticket: self.send_ticket, + })), + _ => Ok(Box::new(ExpectCcs { + config: self.config, + secrets, + transcript: self.transcript, + session_id: self.session_id, + using_ems: self.using_ems, + resuming: false, + send_ticket: self.send_ticket, + })), + } + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + Box::new(ExpectClientKx { + config: self.config, + transcript: self.transcript, + randoms: self.randoms, + session_id: self.session_id, + suite: self.suite, + using_ems: self.using_ems, + server_kx: self.server_kx, + client_cert: self + .client_cert + .map(|cert| cert.into_owned()), + send_ticket: self.send_ticket, + }) + } +} + +// --- Process client's certificate proof --- +struct ExpectCertificateVerify<'a> { + config: Arc<ServerConfig>, + secrets: ConnectionSecrets, + transcript: HandshakeHash, + session_id: SessionId, + using_ems: bool, + client_cert: CertificateChain<'a>, + send_ticket: bool, +} + +impl State<ServerConnectionData> for ExpectCertificateVerify<'_> { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let rc = { + let sig = require_handshake_msg!( + m, + HandshakeType::CertificateVerify, + HandshakePayload::CertificateVerify + )?; + + match self.transcript.take_handshake_buf() { + Some(msgs) => { + let certs = &self.client_cert; + self.config + .verifier + .verify_tls12_signature(&msgs, &certs[0], sig) + } + None => { + // This should be unreachable; the handshake buffer was initialized with + // client authentication if the verifier wants to offer it. + // `transcript.abandon_client_auth()` can extract it, but its only caller in + // this flow will also set `ExpectClientKx::client_cert` to `None`, making it + // impossible to reach this state. + return Err(cx.common.send_fatal_alert( + AlertDescription::AccessDenied, + Error::General("client authentication not set up".into()), + )); + } + } + }; + + if let Err(e) = rc { + return Err(cx + .common + .send_cert_verify_error_alert(e)); + } + + trace!("client CertificateVerify OK"); + cx.common.peer_certificates = Some(self.client_cert.into_owned()); + + self.transcript.add_message(&m); + Ok(Box::new(ExpectCcs { + config: self.config, + secrets: self.secrets, + transcript: self.transcript, + session_id: self.session_id, + using_ems: self.using_ems, + resuming: false, + send_ticket: self.send_ticket, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + Box::new(ExpectCertificateVerify { + config: self.config, + secrets: self.secrets, + transcript: self.transcript, + session_id: self.session_id, + using_ems: self.using_ems, + client_cert: self.client_cert.into_owned(), + send_ticket: self.send_ticket, + }) + } +} + +// --- Process client's ChangeCipherSpec --- +struct ExpectCcs { + config: Arc<ServerConfig>, + secrets: ConnectionSecrets, + transcript: HandshakeHash, + session_id: SessionId, + using_ems: bool, + resuming: bool, + send_ticket: bool, +} + +impl State<ServerConnectionData> for ExpectCcs { + fn handle<'m>( + self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::ChangeCipherSpec(..) => {} + payload => { + return Err(inappropriate_message( + &payload, + &[ContentType::ChangeCipherSpec], + )); + } + } + + // CCS should not be received interleaved with fragmented handshake-level + // message. + cx.common.check_aligned_handshake()?; + + cx.common + .record_layer + .start_decrypting(); + Ok(Box::new(ExpectFinished { + config: self.config, + secrets: self.secrets, + transcript: self.transcript, + session_id: self.session_id, + using_ems: self.using_ems, + resuming: self.resuming, + send_ticket: self.send_ticket, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// --- Process client's Finished --- +fn get_server_connection_value_tls12( + secrets: &ConnectionSecrets, + using_ems: bool, + cx: &ServerContext<'_>, + time_now: UnixTime, +) -> persist::ServerSessionValue { + let version = ProtocolVersion::TLSv1_2; + + let mut v = persist::ServerSessionValue::new( + cx.data.sni.as_ref(), + version, + secrets.suite().common.suite, + secrets.master_secret(), + cx.common.peer_certificates.clone(), + cx.common.alpn_protocol.clone(), + cx.data.resumption_data.clone(), + time_now, + 0, + ); + + if using_ems { + v.set_extended_ms_used(); + } + + v +} + +fn emit_ticket( + secrets: &ConnectionSecrets, + transcript: &mut HandshakeHash, + using_ems: bool, + cx: &mut ServerContext<'_>, + ticketer: &dyn ProducesTickets, + now: UnixTime, +) -> Result<(), Error> { + let plain = get_server_connection_value_tls12(secrets, using_ems, cx, now).get_encoding(); + + // If we can't produce a ticket for some reason, we can't + // report an error. Send an empty one. + let ticket = ticketer + .encrypt(&plain) + .unwrap_or_default(); + let ticket_lifetime = ticketer.lifetime(); + + let m = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::NewSessionTicket(NewSessionTicketPayload::new( + ticket_lifetime, + ticket, + )), + )), + }; + + transcript.add_message(&m); + cx.common.send_msg(m, false); + Ok(()) +} + +fn emit_ccs(common: &mut CommonState) { + let m = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::ChangeCipherSpec(ChangeCipherSpecPayload {}), + }; + + common.send_msg(m, false); +} + +fn emit_finished( + secrets: &ConnectionSecrets, + transcript: &mut HandshakeHash, + common: &mut CommonState, +) { + let vh = transcript.current_hash(); + let verify_data = secrets.server_verify_data(&vh); + let verify_data_payload = Payload::new(verify_data); + + let f = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload(HandshakePayload::Finished( + verify_data_payload, + ))), + }; + + transcript.add_message(&f); + common.send_msg(f, true); +} + +struct ExpectFinished { + config: Arc<ServerConfig>, + secrets: ConnectionSecrets, + transcript: HandshakeHash, + session_id: SessionId, + using_ems: bool, + resuming: bool, + send_ticket: bool, +} + +impl State<ServerConnectionData> for ExpectFinished { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let finished = + require_handshake_msg!(m, HandshakeType::Finished, HandshakePayload::Finished)?; + + cx.common.check_aligned_handshake()?; + + let vh = self.transcript.current_hash(); + let expect_verify_data = self.secrets.client_verify_data(&vh); + + let _fin_verified = + match ConstantTimeEq::ct_eq(&expect_verify_data[..], finished.bytes()).into() { + true => verify::FinishedMessageVerified::assertion(), + false => { + return Err(cx + .common + .send_fatal_alert(AlertDescription::DecryptError, Error::DecryptError)); + } + }; + + // Save connection, perhaps + if !self.resuming && !self.session_id.is_empty() { + let now = self.config.current_time()?; + + let value = get_server_connection_value_tls12(&self.secrets, self.using_ems, cx, now); + + let worked = self + .config + .session_storage + .put(self.session_id.as_ref().to_vec(), value.get_encoding()); + #[cfg_attr(not(feature = "logging"), allow(clippy::if_same_then_else))] + if worked { + debug!("Session saved"); + } else { + debug!("Session not saved"); + } + } + + // Send our CCS and Finished. + self.transcript.add_message(&m); + if !self.resuming { + if self.send_ticket { + let now = self.config.current_time()?; + emit_ticket( + &self.secrets, + &mut self.transcript, + self.using_ems, + cx, + &*self.config.ticketer, + now, + )?; + } + emit_ccs(cx.common); + cx.common + .record_layer + .start_encrypting(); + emit_finished(&self.secrets, &mut self.transcript, cx.common); + } + + cx.common + .start_traffic(&mut cx.sendable_plaintext); + Ok(Box::new(ExpectTraffic { + secrets: self.secrets, + _fin_verified, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// --- Process traffic --- +struct ExpectTraffic { + secrets: ConnectionSecrets, + _fin_verified: verify::FinishedMessageVerified, +} + +impl ExpectTraffic {} + +impl State<ServerConnectionData> for ExpectTraffic { + fn handle<'m>( + self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::ApplicationData(payload) => cx + .common + .take_received_plaintext(payload), + payload => { + return Err(inappropriate_message( + &payload, + &[ContentType::ApplicationData], + )); + } + } + Ok(self) + } + + fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: Option<&[u8]>, + ) -> Result<(), Error> { + self.secrets + .export_keying_material(output, label, context); + Ok(()) + } + + fn extract_secrets(&self) -> Result<PartiallyExtractedSecrets, Error> { + self.secrets + .extract_secrets(Side::Server) + } + + fn into_external_state(self: Box<Self>) -> Result<Box<dyn KernelState + 'static>, Error> { + Ok(self) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +impl KernelState for ExpectTraffic { + fn update_secrets(&mut self, _: Direction) -> Result<ConnectionTrafficSecrets, Error> { + Err(Error::General( + "TLS 1.2 connections do not support traffic secret updates".into(), + )) + } + + fn handle_new_session_ticket( + &mut self, + _cx: &mut KernelContext<'_>, + _message: &NewSessionTicketPayloadTls13, + ) -> Result<(), Error> { + unreachable!( + "server connections should never have handle_new_session_ticket called on them" + ) + } +} diff --git a/vendor/rustls/src/server/tls13.rs b/vendor/rustls/src/server/tls13.rs new file mode 100644 index 00000000..b8b70e72 --- /dev/null +++ b/vendor/rustls/src/server/tls13.rs @@ -0,0 +1,1535 @@ +use alloc::boxed::Box; +use alloc::vec; +use alloc::vec::Vec; + +pub(super) use client_hello::CompleteClientHelloHandling; +use pki_types::{CertificateDer, UnixTime}; +use subtle::ConstantTimeEq; + +use super::hs::{self, HandshakeHashOrBuffer, ServerContext}; +use super::server_conn::ServerConnectionData; +use crate::check::{inappropriate_handshake_message, inappropriate_message}; +use crate::common_state::{ + CommonState, HandshakeFlightTls13, HandshakeKind, Protocol, Side, State, +}; +use crate::conn::ConnectionRandoms; +use crate::conn::kernel::{Direction, KernelContext, KernelState}; +use crate::enums::{AlertDescription, ContentType, HandshakeType, ProtocolVersion}; +use crate::error::{Error, InvalidMessage, PeerIncompatible, PeerMisbehaved}; +use crate::hash_hs::HandshakeHash; +use crate::log::{debug, trace, warn}; +use crate::msgs::codec::{Codec, Reader}; +use crate::msgs::enums::KeyUpdateRequest; +use crate::msgs::handshake::{ + CERTIFICATE_MAX_SIZE_LIMIT, CertificateChain, CertificatePayloadTls13, HandshakeMessagePayload, + HandshakePayload, NewSessionTicketPayloadTls13, +}; +use crate::msgs::message::{Message, MessagePayload}; +use crate::msgs::persist; +use crate::server::ServerConfig; +use crate::suites::PartiallyExtractedSecrets; +use crate::sync::Arc; +use crate::tls13::key_schedule::{ + KeyScheduleResumption, KeyScheduleTraffic, KeyScheduleTrafficWithClientFinishedPending, +}; +use crate::tls13::{ + Tls13CipherSuite, construct_client_verify_message, construct_server_verify_message, +}; +use crate::{ConnectionTrafficSecrets, compress, rand, verify}; + +mod client_hello { + use super::*; + use crate::compress::CertCompressor; + use crate::crypto::SupportedKxGroup; + use crate::enums::SignatureScheme; + use crate::msgs::base::{Payload, PayloadU8}; + use crate::msgs::ccs::ChangeCipherSpecPayload; + use crate::msgs::enums::{Compression, NamedGroup}; + use crate::msgs::handshake::{ + CertificatePayloadTls13, CertificateRequestExtensions, CertificateRequestPayloadTls13, + ClientHelloPayload, HelloRetryRequest, HelloRetryRequestExtensions, KeyShareEntry, Random, + ServerExtensions, ServerExtensionsInput, ServerHelloPayload, SessionId, + }; + use crate::server::common::ActiveCertifiedKey; + use crate::sign; + use crate::tls13::key_schedule::{ + KeyScheduleEarly, KeyScheduleHandshake, KeySchedulePreHandshake, + }; + use crate::verify::DigitallySignedStruct; + + #[derive(PartialEq)] + pub(super) enum EarlyDataDecision { + Disabled, + RequestedButRejected, + Accepted, + } + + pub(in crate::server) struct CompleteClientHelloHandling { + pub(in crate::server) config: Arc<ServerConfig>, + pub(in crate::server) transcript: HandshakeHash, + pub(in crate::server) suite: &'static Tls13CipherSuite, + pub(in crate::server) randoms: ConnectionRandoms, + pub(in crate::server) done_retry: bool, + pub(in crate::server) send_tickets: usize, + pub(in crate::server) extra_exts: ServerExtensionsInput<'static>, + } + + fn max_early_data_size(configured: u32) -> usize { + if configured != 0 { + configured as usize + } else { + // The relevant max_early_data_size may in fact be unknowable: if + // we (the server) have turned off early_data but the client has + // a stale ticket from when we allowed early_data: we'll naturally + // reject early_data but need an upper bound on the amount of data + // to drop. + // + // Use a single maximum-sized message. + 16384 + } + } + + impl CompleteClientHelloHandling { + fn check_binder( + &self, + suite: &'static Tls13CipherSuite, + client_hello: &Message<'_>, + psk: &[u8], + binder: &[u8], + ) -> bool { + let binder_plaintext = match &client_hello.payload { + MessagePayload::Handshake { parsed, encoded } => { + &encoded.bytes()[..encoded.bytes().len() - parsed.total_binder_length()] + } + _ => unreachable!(), + }; + + let handshake_hash = self + .transcript + .hash_given(binder_plaintext); + + let key_schedule = KeyScheduleEarly::new(suite, psk); + let real_binder = + key_schedule.resumption_psk_binder_key_and_sign_verify_data(&handshake_hash); + + ConstantTimeEq::ct_eq(real_binder.as_ref(), binder).into() + } + + fn attempt_tls13_ticket_decryption( + &mut self, + ticket: &[u8], + ) -> Option<persist::ServerSessionValue> { + if self.config.ticketer.enabled() { + self.config + .ticketer + .decrypt(ticket) + .and_then(|plain| persist::ServerSessionValue::read_bytes(&plain).ok()) + } else { + self.config + .session_storage + .take(ticket) + .and_then(|plain| persist::ServerSessionValue::read_bytes(&plain).ok()) + } + } + + pub(in crate::server) fn handle_client_hello( + mut self, + cx: &mut ServerContext<'_>, + server_key: ActiveCertifiedKey<'_>, + chm: &Message<'_>, + client_hello: &ClientHelloPayload, + selected_kxg: &'static dyn SupportedKxGroup, + mut sigschemes_ext: Vec<SignatureScheme>, + ) -> hs::NextStateOrError<'static> { + if client_hello.compression_methods.len() != 1 { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::OfferedIncorrectCompressions, + )); + } + + sigschemes_ext.retain(SignatureScheme::supported_in_tls13); + + let shares_ext = client_hello + .key_shares + .as_ref() + .ok_or_else(|| { + cx.common.send_fatal_alert( + AlertDescription::HandshakeFailure, + PeerIncompatible::KeyShareExtensionRequired, + ) + })?; + + if client_hello.has_keyshare_extension_with_duplicates() { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::OfferedDuplicateKeyShares, + )); + } + + if client_hello.has_certificate_compression_extension_with_duplicates() { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::OfferedDuplicateCertificateCompressions, + )); + } + + let cert_compressor = client_hello + .certificate_compression_algorithms + .as_ref() + .and_then(|offered| + // prefer server order when choosing a compression: the client's + // extension here does not denote any preference. + self.config + .cert_compressors + .iter() + .find(|compressor| offered.contains(&compressor.algorithm())) + .cloned()); + + let early_data_requested = client_hello + .early_data_request + .is_some(); + + // EarlyData extension is illegal in second ClientHello + if self.done_retry && early_data_requested { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::EarlyDataAttemptedInSecondClientHello, + ) + }); + } + + // See if there is a KeyShare for the selected kx group. + let chosen_share_and_kxg = shares_ext.iter().find_map(|share| { + (share.group == selected_kxg.name()).then_some((share, selected_kxg)) + }); + + let Some(chosen_share_and_kxg) = chosen_share_and_kxg else { + // We don't have a suitable key share. Send a HelloRetryRequest + // for the mutually_preferred_group. + self.transcript.add_message(chm); + + if self.done_retry { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::RefusedToFollowHelloRetryRequest, + )); + } + + emit_hello_retry_request( + &mut self.transcript, + self.suite, + client_hello.session_id, + cx.common, + selected_kxg.name(), + ); + emit_fake_ccs(cx.common); + + let skip_early_data = max_early_data_size(self.config.max_early_data_size); + + let next = Box::new(hs::ExpectClientHello { + config: self.config, + transcript: HandshakeHashOrBuffer::Hash(self.transcript), + #[cfg(feature = "tls12")] + session_id: SessionId::empty(), + #[cfg(feature = "tls12")] + using_ems: false, + done_retry: true, + send_tickets: self.send_tickets, + extra_exts: self.extra_exts, + }); + + return if early_data_requested { + Ok(Box::new(ExpectAndSkipRejectedEarlyData { + skip_data_left: skip_early_data, + next, + })) + } else { + Ok(next) + }; + }; + + let mut chosen_psk_index = None; + let mut resumedata = None; + + if let Some(psk_offer) = &client_hello.preshared_key_offer { + // "A client MUST provide a "psk_key_exchange_modes" extension if it + // offers a "pre_shared_key" extension. If clients offer + // "pre_shared_key" without a "psk_key_exchange_modes" extension, + // servers MUST abort the handshake." - RFC8446 4.2.9 + if client_hello + .preshared_key_modes + .is_none() + { + return Err(cx.common.send_fatal_alert( + AlertDescription::MissingExtension, + PeerMisbehaved::MissingPskModesExtension, + )); + } + + if psk_offer.binders.is_empty() { + return Err(cx.common.send_fatal_alert( + AlertDescription::DecodeError, + PeerMisbehaved::MissingBinderInPskExtension, + )); + } + + if psk_offer.binders.len() != psk_offer.identities.len() { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::PskExtensionWithMismatchedIdsAndBinders, + )); + } + + let now = self.config.current_time()?; + + for (i, psk_id) in psk_offer.identities.iter().enumerate() { + let maybe_resume_data = self + .attempt_tls13_ticket_decryption(&psk_id.identity.0) + .map(|resumedata| { + resumedata.set_freshness(psk_id.obfuscated_ticket_age, now) + }) + .filter(|resumedata| { + hs::can_resume(self.suite.into(), &cx.data.sni, false, resumedata) + }); + + let Some(resume) = maybe_resume_data else { + continue; + }; + + if !self.check_binder( + self.suite, + chm, + &resume.master_secret.0, + psk_offer.binders[i].as_ref(), + ) { + return Err(cx.common.send_fatal_alert( + AlertDescription::DecryptError, + PeerMisbehaved::IncorrectBinder, + )); + } + + chosen_psk_index = Some(i); + resumedata = Some(resume); + break; + } + } + + if !client_hello + .preshared_key_modes + .as_ref() + .map(|offer| offer.psk_dhe) + .unwrap_or_default() + { + debug!("Client unwilling to resume, PSK_DHE_KE not offered"); + self.send_tickets = 0; + chosen_psk_index = None; + resumedata = None; + } else { + self.send_tickets = self.config.send_tls13_tickets; + } + + if let Some(resume) = &resumedata { + cx.data.received_resumption_data = Some(resume.application_data.0.clone()); + cx.common + .peer_certificates + .clone_from(&resume.client_cert_chain); + } + + let full_handshake = resumedata.is_none(); + self.transcript.add_message(chm); + let key_schedule = emit_server_hello( + &mut self.transcript, + &self.randoms, + self.suite, + cx, + &client_hello.session_id, + chosen_share_and_kxg, + chosen_psk_index, + resumedata + .as_ref() + .map(|x| &x.master_secret.0[..]), + &self.config, + )?; + if !self.done_retry { + emit_fake_ccs(cx.common); + } + + if full_handshake { + cx.common + .handshake_kind + .get_or_insert(HandshakeKind::Full); + } else { + cx.common.handshake_kind = Some(HandshakeKind::Resumed); + } + + let mut ocsp_response = server_key.get_ocsp(); + let mut flight = HandshakeFlightTls13::new(&mut self.transcript); + let doing_early_data = emit_encrypted_extensions( + &mut flight, + self.suite, + cx, + &mut ocsp_response, + client_hello, + resumedata.as_ref(), + self.extra_exts, + &self.config, + )?; + + let doing_client_auth = if full_handshake { + let client_auth = emit_certificate_req_tls13(&mut flight, &self.config)?; + + if let Some(compressor) = cert_compressor { + emit_compressed_certificate_tls13( + &mut flight, + &self.config, + server_key.get_cert(), + ocsp_response, + compressor, + ); + } else { + emit_certificate_tls13(&mut flight, server_key.get_cert(), ocsp_response); + } + emit_certificate_verify_tls13( + &mut flight, + cx.common, + server_key.get_key(), + &sigschemes_ext, + )?; + client_auth + } else { + false + }; + + // If we're not doing early data, then the next messages we receive + // are encrypted with the handshake keys. + match doing_early_data { + EarlyDataDecision::Disabled => { + key_schedule.set_handshake_decrypter(None, cx.common); + cx.data.early_data.reject(); + } + EarlyDataDecision::RequestedButRejected => { + debug!( + "Client requested early_data, but not accepted: switching to handshake keys with trial decryption" + ); + key_schedule.set_handshake_decrypter( + Some(max_early_data_size(self.config.max_early_data_size)), + cx.common, + ); + cx.data.early_data.reject(); + } + EarlyDataDecision::Accepted => { + cx.data + .early_data + .accept(self.config.max_early_data_size as usize); + } + } + + cx.common.check_aligned_handshake()?; + let key_schedule_traffic = + emit_finished_tls13(flight, &self.randoms, cx, key_schedule, &self.config); + + if !doing_client_auth && self.config.send_half_rtt_data { + // Application data can be sent immediately after Finished, in one + // flight. However, if client auth is enabled, we don't want to send + // application data to an unauthenticated peer. + cx.common + .start_outgoing_traffic(&mut cx.sendable_plaintext); + } + + if doing_client_auth { + if self + .config + .cert_decompressors + .is_empty() + { + Ok(Box::new(ExpectCertificate { + config: self.config, + transcript: self.transcript, + suite: self.suite, + key_schedule: key_schedule_traffic, + send_tickets: self.send_tickets, + message_already_in_transcript: false, + })) + } else { + Ok(Box::new(ExpectCertificateOrCompressedCertificate { + config: self.config, + transcript: self.transcript, + suite: self.suite, + key_schedule: key_schedule_traffic, + send_tickets: self.send_tickets, + })) + } + } else if doing_early_data == EarlyDataDecision::Accepted && !cx.common.is_quic() { + // Not used for QUIC: RFC 9001 ยง8.3: Clients MUST NOT send the EndOfEarlyData + // message. A server MUST treat receipt of a CRYPTO frame in a 0-RTT packet as a + // connection error of type PROTOCOL_VIOLATION. + Ok(Box::new(ExpectEarlyData { + config: self.config, + transcript: self.transcript, + suite: self.suite, + key_schedule: key_schedule_traffic, + send_tickets: self.send_tickets, + })) + } else { + Ok(Box::new(ExpectFinished { + config: self.config, + transcript: self.transcript, + suite: self.suite, + key_schedule: key_schedule_traffic, + send_tickets: self.send_tickets, + })) + } + } + } + + fn emit_server_hello( + transcript: &mut HandshakeHash, + randoms: &ConnectionRandoms, + suite: &'static Tls13CipherSuite, + cx: &mut ServerContext<'_>, + session_id: &SessionId, + share_and_kxgroup: (&KeyShareEntry, &'static dyn SupportedKxGroup), + chosen_psk_idx: Option<usize>, + resuming_psk: Option<&[u8]>, + config: &ServerConfig, + ) -> Result<KeyScheduleHandshake, Error> { + // Prepare key exchange; the caller already found the matching SupportedKxGroup + let (share, kxgroup) = share_and_kxgroup; + debug_assert_eq!(kxgroup.name(), share.group); + let ckx = kxgroup + .start_and_complete(&share.payload.0) + .map_err(|err| { + cx.common + .send_fatal_alert(AlertDescription::IllegalParameter, err) + })?; + cx.common.kx_state.complete(); + + let extensions = Box::new(ServerExtensions { + key_share: Some(KeyShareEntry::new(ckx.group, ckx.pub_key)), + selected_version: Some(ProtocolVersion::TLSv1_3), + preshared_key: chosen_psk_idx.map(|idx| idx as u16), + ..Default::default() + }); + + let sh = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ServerHello(ServerHelloPayload { + legacy_version: ProtocolVersion::TLSv1_2, + random: Random::from(randoms.server), + session_id: *session_id, + cipher_suite: suite.common.suite, + compression_method: Compression::Null, + extensions, + }), + )), + }; + + cx.common.check_aligned_handshake()?; + + let client_hello_hash = transcript.hash_given(&[]); + + trace!("sending server hello {sh:?}"); + transcript.add_message(&sh); + cx.common.send_msg(sh, false); + + // Start key schedule + let key_schedule_pre_handshake = if let Some(psk) = resuming_psk { + let early_key_schedule = KeyScheduleEarly::new(suite, psk); + early_key_schedule.client_early_traffic_secret( + &client_hello_hash, + &*config.key_log, + &randoms.client, + cx.common, + ); + + KeySchedulePreHandshake::from(early_key_schedule) + } else { + KeySchedulePreHandshake::new(suite) + }; + + // Do key exchange + let key_schedule = key_schedule_pre_handshake.into_handshake(ckx.secret); + + let handshake_hash = transcript.current_hash(); + let key_schedule = key_schedule.derive_server_handshake_secrets( + handshake_hash, + &*config.key_log, + &randoms.client, + cx.common, + ); + + Ok(key_schedule) + } + + fn emit_fake_ccs(common: &mut CommonState) { + if common.is_quic() { + return; + } + let m = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::ChangeCipherSpec(ChangeCipherSpecPayload {}), + }; + common.send_msg(m, false); + } + + fn emit_hello_retry_request( + transcript: &mut HandshakeHash, + suite: &'static Tls13CipherSuite, + session_id: SessionId, + common: &mut CommonState, + group: NamedGroup, + ) { + let req = HelloRetryRequest { + legacy_version: ProtocolVersion::TLSv1_2, + session_id, + cipher_suite: suite.common.suite, + extensions: HelloRetryRequestExtensions { + key_share: Some(group), + supported_versions: Some(ProtocolVersion::TLSv1_3), + ..Default::default() + }, + }; + + let m = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::HelloRetryRequest(req), + )), + }; + + trace!("Requesting retry {m:?}"); + transcript.rollup_for_hrr(); + transcript.add_message(&m); + common.send_msg(m, false); + common.handshake_kind = Some(HandshakeKind::FullWithHelloRetryRequest); + } + + fn decide_if_early_data_allowed( + cx: &mut ServerContext<'_>, + client_hello: &ClientHelloPayload, + resumedata: Option<&persist::ServerSessionValue>, + suite: &'static Tls13CipherSuite, + config: &ServerConfig, + ) -> EarlyDataDecision { + let early_data_requested = client_hello + .early_data_request + .is_some(); + let rejected_or_disabled = match early_data_requested { + true => EarlyDataDecision::RequestedButRejected, + false => EarlyDataDecision::Disabled, + }; + + let Some(resume) = resumedata else { + // never any early data if not resuming. + return rejected_or_disabled; + }; + + /* Non-zero max_early_data_size controls whether early_data is allowed at all. + * We also require stateful resumption. */ + let early_data_configured = config.max_early_data_size > 0 && !config.ticketer.enabled(); + + /* "For PSKs provisioned via NewSessionTicket, a server MUST validate + * that the ticket age for the selected PSK identity (computed by + * subtracting ticket_age_add from PskIdentity.obfuscated_ticket_age + * modulo 2^32) is within a small tolerance of the time since the ticket + * was issued (see Section 8)." -- this is implemented in ServerSessionValue::set_freshness() + * and related. + * + * "In order to accept early data, the server [...] MUST verify that the + * following values are the same as those associated with the + * selected PSK: + * + * - The TLS version number + * - The selected cipher suite + * - The selected ALPN [RFC7301] protocol, if any" + * + * (RFC8446, 4.2.10) */ + let early_data_possible = early_data_requested + && resume.is_fresh() + && Some(resume.version) == cx.common.negotiated_version + && resume.cipher_suite == suite.common.suite + && resume.alpn.as_ref().map(|p| &p.0[..]) == cx.common.alpn_protocol.as_deref(); + + if early_data_configured && early_data_possible && !cx.data.early_data.was_rejected() { + EarlyDataDecision::Accepted + } else { + if cx.common.is_quic() { + // Clobber value set in tls13::emit_server_hello + cx.common.quic.early_secret = None; + } + + rejected_or_disabled + } + } + + fn emit_encrypted_extensions( + flight: &mut HandshakeFlightTls13<'_>, + suite: &'static Tls13CipherSuite, + cx: &mut ServerContext<'_>, + ocsp_response: &mut Option<&[u8]>, + hello: &ClientHelloPayload, + resumedata: Option<&persist::ServerSessionValue>, + extra_exts: ServerExtensionsInput<'static>, + config: &ServerConfig, + ) -> Result<EarlyDataDecision, Error> { + let mut ep = hs::ExtensionProcessing::new(extra_exts); + ep.process_common(config, cx, ocsp_response, hello, resumedata)?; + + let early_data = decide_if_early_data_allowed(cx, hello, resumedata, suite, config); + if early_data == EarlyDataDecision::Accepted { + ep.extensions.early_data_ack = Some(()); + } + + let ee = HandshakeMessagePayload(HandshakePayload::EncryptedExtensions(ep.extensions)); + + trace!("sending encrypted extensions {ee:?}"); + flight.add(ee); + Ok(early_data) + } + + fn emit_certificate_req_tls13( + flight: &mut HandshakeFlightTls13<'_>, + config: &ServerConfig, + ) -> Result<bool, Error> { + if !config.verifier.offer_client_auth() { + return Ok(false); + } + + let cr = CertificateRequestPayloadTls13 { + context: PayloadU8::empty(), + extensions: CertificateRequestExtensions { + signature_algorithms: Some( + config + .verifier + .supported_verify_schemes(), + ), + certificate_compression_algorithms: match config.cert_decompressors.as_slice() { + &[] => None, + decomps => Some( + decomps + .iter() + .map(|decomp| decomp.algorithm()) + .collect(), + ), + }, + authority_names: match config.verifier.root_hint_subjects() { + &[] => None, + authorities => Some(authorities.to_vec()), + }, + }, + }; + + let creq = HandshakeMessagePayload(HandshakePayload::CertificateRequestTls13(cr)); + + trace!("Sending CertificateRequest {creq:?}"); + flight.add(creq); + Ok(true) + } + + fn emit_certificate_tls13( + flight: &mut HandshakeFlightTls13<'_>, + cert_chain: &[CertificateDer<'static>], + ocsp_response: Option<&[u8]>, + ) { + let cert = HandshakeMessagePayload(HandshakePayload::CertificateTls13( + CertificatePayloadTls13::new(cert_chain.iter(), ocsp_response), + )); + + trace!("sending certificate {cert:?}"); + flight.add(cert); + } + + fn emit_compressed_certificate_tls13( + flight: &mut HandshakeFlightTls13<'_>, + config: &ServerConfig, + cert_chain: &[CertificateDer<'static>], + ocsp_response: Option<&[u8]>, + cert_compressor: &'static dyn CertCompressor, + ) { + let payload = CertificatePayloadTls13::new(cert_chain.iter(), ocsp_response); + + let Ok(entry) = config + .cert_compression_cache + .compression_for(cert_compressor, &payload) + else { + return emit_certificate_tls13(flight, cert_chain, ocsp_response); + }; + + let c = HandshakeMessagePayload(HandshakePayload::CompressedCertificate( + entry.compressed_cert_payload(), + )); + + trace!("sending compressed certificate {c:?}"); + flight.add(c); + } + + fn emit_certificate_verify_tls13( + flight: &mut HandshakeFlightTls13<'_>, + common: &mut CommonState, + signing_key: &dyn sign::SigningKey, + schemes: &[SignatureScheme], + ) -> Result<(), Error> { + let message = construct_server_verify_message(&flight.transcript.current_hash()); + + let signer = signing_key + .choose_scheme(schemes) + .ok_or_else(|| { + common.send_fatal_alert( + AlertDescription::HandshakeFailure, + PeerIncompatible::NoSignatureSchemesInCommon, + ) + })?; + + let scheme = signer.scheme(); + let sig = signer.sign(message.as_ref())?; + + let cv = DigitallySignedStruct::new(scheme, sig); + + let cv = HandshakeMessagePayload(HandshakePayload::CertificateVerify(cv)); + + trace!("sending certificate-verify {cv:?}"); + flight.add(cv); + Ok(()) + } + + fn emit_finished_tls13( + mut flight: HandshakeFlightTls13<'_>, + randoms: &ConnectionRandoms, + cx: &mut ServerContext<'_>, + key_schedule: KeyScheduleHandshake, + config: &ServerConfig, + ) -> KeyScheduleTrafficWithClientFinishedPending { + let handshake_hash = flight.transcript.current_hash(); + let verify_data = key_schedule.sign_server_finish(&handshake_hash); + let verify_data_payload = Payload::new(verify_data.as_ref()); + + let fin = HandshakeMessagePayload(HandshakePayload::Finished(verify_data_payload)); + + trace!("sending finished {fin:?}"); + flight.add(fin); + let hash_at_server_fin = flight.transcript.current_hash(); + flight.finish(cx.common); + + // Now move to application data keys. Read key change is deferred until + // the Finish message is received & validated. + key_schedule.into_traffic_with_client_finished_pending( + hash_at_server_fin, + &*config.key_log, + &randoms.client, + cx.common, + ) + } +} + +struct ExpectAndSkipRejectedEarlyData { + skip_data_left: usize, + next: Box<hs::ExpectClientHello>, +} + +impl State<ServerConnectionData> for ExpectAndSkipRejectedEarlyData { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + /* "The server then ignores early data by skipping all records with an external + * content type of "application_data" (indicating that they are encrypted), + * up to the configured max_early_data_size." + * (RFC8446, 14.2.10) */ + if let MessagePayload::ApplicationData(skip_data) = &m.payload { + if skip_data.bytes().len() <= self.skip_data_left { + self.skip_data_left -= skip_data.bytes().len(); + return Ok(self); + } + } + + self.next.handle(cx, m) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +struct ExpectCertificateOrCompressedCertificate { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + suite: &'static Tls13CipherSuite, + key_schedule: KeyScheduleTrafficWithClientFinishedPending, + send_tickets: usize, +} + +impl State<ServerConnectionData> for ExpectCertificateOrCompressedCertificate { + fn handle<'m>( + self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::CertificateTls13(..)), + .. + } => Box::new(ExpectCertificate { + config: self.config, + transcript: self.transcript, + suite: self.suite, + key_schedule: self.key_schedule, + send_tickets: self.send_tickets, + message_already_in_transcript: false, + }) + .handle(cx, m), + + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::CompressedCertificate(..)), + .. + } => Box::new(ExpectCompressedCertificate { + config: self.config, + transcript: self.transcript, + suite: self.suite, + key_schedule: self.key_schedule, + send_tickets: self.send_tickets, + }) + .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 ExpectCompressedCertificate { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + suite: &'static Tls13CipherSuite, + key_schedule: KeyScheduleTrafficWithClientFinishedPending, + send_tickets: usize, +} + +impl State<ServerConnectionData> for ExpectCompressedCertificate { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + 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 selected_decompressor = self + .config + .cert_decompressors + .iter() + .find(|item| item.algorithm() == compressed_cert.alg); + + let Some(decompressor) = selected_decompressor else { + 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!( + "Client 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( + HandshakePayload::CertificateTls13(cert_payload.into_owned()), + )), + }; + + Box::new(ExpectCertificate { + config: self.config, + transcript: self.transcript, + suite: self.suite, + key_schedule: self.key_schedule, + send_tickets: self.send_tickets, + message_already_in_transcript: true, + }) + .handle(cx, m) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +struct ExpectCertificate { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + suite: &'static Tls13CipherSuite, + key_schedule: KeyScheduleTrafficWithClientFinishedPending, + send_tickets: usize, + message_already_in_transcript: bool, +} + +impl State<ServerConnectionData> for ExpectCertificate { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + if !self.message_already_in_transcript { + self.transcript.add_message(&m); + } + let certp = require_handshake_msg_move!( + m, + HandshakeType::Certificate, + HandshakePayload::CertificateTls13 + )?; + + // We don't send any CertificateRequest extensions, so any extensions + // here are illegal. + if certp + .entries + .iter() + .any(|e| !e.extensions.only_contains(&[])) + { + return Err(PeerMisbehaved::UnsolicitedCertExtension.into()); + } + + let client_cert = certp.into_certificate_chain(); + + let mandatory = self + .config + .verifier + .client_auth_mandatory(); + + let Some((end_entity, intermediates)) = client_cert.split_first() else { + if !mandatory { + debug!("client auth requested but no certificate supplied"); + self.transcript.abandon_client_auth(); + return Ok(Box::new(ExpectFinished { + config: self.config, + suite: self.suite, + key_schedule: self.key_schedule, + transcript: self.transcript, + send_tickets: self.send_tickets, + })); + } + + return Err(cx.common.send_fatal_alert( + AlertDescription::CertificateRequired, + Error::NoCertificatesPresented, + )); + }; + + let now = self.config.current_time()?; + + self.config + .verifier + .verify_client_cert(end_entity, intermediates, now) + .map_err(|err| { + cx.common + .send_cert_verify_error_alert(err) + })?; + + Ok(Box::new(ExpectCertificateVerify { + config: self.config, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + client_cert: client_cert.into_owned(), + send_tickets: self.send_tickets, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +struct ExpectCertificateVerify { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + suite: &'static Tls13CipherSuite, + key_schedule: KeyScheduleTrafficWithClientFinishedPending, + client_cert: CertificateChain<'static>, + send_tickets: usize, +} + +impl State<ServerConnectionData> for ExpectCertificateVerify { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let rc = { + let sig = require_handshake_msg!( + m, + HandshakeType::CertificateVerify, + HandshakePayload::CertificateVerify + )?; + let handshake_hash = self.transcript.current_hash(); + self.transcript.abandon_client_auth(); + let certs = &self.client_cert; + let msg = construct_client_verify_message(&handshake_hash); + + self.config + .verifier + .verify_tls13_signature(msg.as_ref(), &certs[0], sig) + }; + + if let Err(e) = rc { + return Err(cx + .common + .send_cert_verify_error_alert(e)); + } + + trace!("client CertificateVerify OK"); + cx.common.peer_certificates = Some(self.client_cert); + + self.transcript.add_message(&m); + Ok(Box::new(ExpectFinished { + config: self.config, + suite: self.suite, + key_schedule: self.key_schedule, + transcript: self.transcript, + send_tickets: self.send_tickets, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// --- Process (any number of) early ApplicationData messages, +// followed by a terminating handshake EndOfEarlyData message --- + +struct ExpectEarlyData { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + suite: &'static Tls13CipherSuite, + key_schedule: KeyScheduleTrafficWithClientFinishedPending, + send_tickets: usize, +} + +impl State<ServerConnectionData> for ExpectEarlyData { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::ApplicationData(payload) => { + match cx + .data + .early_data + .take_received_plaintext(payload) + { + true => Ok(self), + false => Err(cx.common.send_fatal_alert( + AlertDescription::UnexpectedMessage, + PeerMisbehaved::TooMuchEarlyDataReceived, + )), + } + } + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::EndOfEarlyData), + .. + } => { + self.key_schedule + .update_decrypter(cx.common); + self.transcript.add_message(&m); + Ok(Box::new(ExpectFinished { + config: self.config, + suite: self.suite, + key_schedule: self.key_schedule, + transcript: self.transcript, + send_tickets: self.send_tickets, + })) + } + payload => Err(inappropriate_handshake_message( + &payload, + &[ContentType::ApplicationData, ContentType::Handshake], + &[HandshakeType::EndOfEarlyData], + )), + } + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// --- Process client's Finished --- +fn get_server_session_value( + suite: &'static Tls13CipherSuite, + resumption: &KeyScheduleResumption, + cx: &ServerContext<'_>, + nonce: &[u8], + time_now: UnixTime, + age_obfuscation_offset: u32, +) -> persist::ServerSessionValue { + let version = ProtocolVersion::TLSv1_3; + + let secret = resumption.derive_ticket_psk(nonce); + + persist::ServerSessionValue::new( + cx.data.sni.as_ref(), + version, + suite.common.suite, + secret.as_ref(), + cx.common.peer_certificates.clone(), + cx.common.alpn_protocol.clone(), + cx.data.resumption_data.clone(), + time_now, + age_obfuscation_offset, + ) +} + +struct ExpectFinished { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + suite: &'static Tls13CipherSuite, + key_schedule: KeyScheduleTrafficWithClientFinishedPending, + send_tickets: usize, +} + +impl ExpectFinished { + fn emit_ticket( + flight: &mut HandshakeFlightTls13<'_>, + suite: &'static Tls13CipherSuite, + cx: &ServerContext<'_>, + resumption: &KeyScheduleResumption, + config: &ServerConfig, + ) -> Result<(), Error> { + let secure_random = config.provider.secure_random; + let nonce = rand::random_vec(secure_random, 32)?; + let age_add = rand::random_u32(secure_random)?; + + let now = config.current_time()?; + + let plain = + get_server_session_value(suite, resumption, cx, &nonce, now, age_add).get_encoding(); + + let stateless = config.ticketer.enabled(); + let (ticket, lifetime) = if stateless { + let Some(ticket) = config.ticketer.encrypt(&plain) else { + return Ok(()); + }; + (ticket, config.ticketer.lifetime()) + } else { + let id = rand::random_vec(secure_random, 32)?; + let stored = config + .session_storage + .put(id.clone(), plain); + if !stored { + trace!("resumption not available; not issuing ticket"); + return Ok(()); + } + let stateful_lifetime = 24 * 60 * 60; // this is a bit of a punt + (id, stateful_lifetime) + }; + + let mut payload = NewSessionTicketPayloadTls13::new(lifetime, age_add, nonce, ticket); + + if config.max_early_data_size > 0 { + if !stateless { + payload.extensions.max_early_data_size = Some(config.max_early_data_size); + } else { + // We implement RFC8446 section 8.1: by enforcing that 0-RTT is + // only possible if using stateful resumption + warn!("early_data with stateless resumption is not allowed"); + } + } + + let t = HandshakeMessagePayload(HandshakePayload::NewSessionTicketTls13(payload)); + trace!("sending new ticket {t:?} (stateless: {stateless})"); + flight.add(t); + + Ok(()) + } +} + +impl State<ServerConnectionData> for ExpectFinished { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let finished = + require_handshake_msg!(m, HandshakeType::Finished, HandshakePayload::Finished)?; + + let handshake_hash = self.transcript.current_hash(); + let (key_schedule_before_finished, expect_verify_data) = self + .key_schedule + .sign_client_finish(&handshake_hash, cx.common); + + 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)); + } + }; + + // Note: future derivations include Client Finished, but not the + // main application data keying. + self.transcript.add_message(&m); + + cx.common.check_aligned_handshake()?; + + let (key_schedule_traffic, resumption) = + key_schedule_before_finished.into_traffic(self.transcript.current_hash()); + + let mut flight = HandshakeFlightTls13::new(&mut self.transcript); + for _ in 0..self.send_tickets { + Self::emit_ticket(&mut flight, self.suite, cx, &resumption, &self.config)?; + } + flight.finish(cx.common); + + // Application data may now flow, even if we have client auth enabled. + cx.common + .start_traffic(&mut cx.sendable_plaintext); + + Ok(match cx.common.is_quic() { + true => Box::new(ExpectQuicTraffic { + key_schedule: key_schedule_traffic, + _fin_verified: fin, + }), + false => Box::new(ExpectTraffic { + key_schedule: key_schedule_traffic, + _fin_verified: fin, + }), + }) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// --- Process traffic --- +struct ExpectTraffic { + key_schedule: KeyScheduleTraffic, + _fin_verified: verify::FinishedMessageVerified, +} + +impl ExpectTraffic { + 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); + } + + // Update our read-side keys. + self.key_schedule + .update_decrypter(common); + Ok(()) + } +} + +impl State<ServerConnectionData> for ExpectTraffic { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + 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(HandshakePayload::KeyUpdate(key_update)), + .. + } => self.handle_key_update(cx.common, &key_update)?, + payload => { + return Err(inappropriate_handshake_message( + &payload, + &[ContentType::ApplicationData, ContentType::Handshake], + &[HandshakeType::KeyUpdate], + )); + } + } + + Ok(self) + } + + 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::Server) + } + + fn send_key_update_request(&mut self, common: &mut CommonState) -> Result<(), Error> { + self.key_schedule + .request_key_update_and_update_encrypter(common) + } + + fn into_external_state(self: Box<Self>) -> Result<Box<dyn KernelState + 'static>, Error> { + Ok(self) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +impl KernelState for ExpectTraffic { + fn update_secrets(&mut self, dir: Direction) -> Result<ConnectionTrafficSecrets, Error> { + self.key_schedule + .refresh_traffic_secret(match dir { + Direction::Transmit => Side::Server, + Direction::Receive => Side::Client, + }) + } + + fn handle_new_session_ticket( + &mut self, + _cx: &mut KernelContext<'_>, + _message: &NewSessionTicketPayloadTls13, + ) -> Result<(), Error> { + unreachable!( + "server connections should never have handle_new_session_ticket called on them" + ) + } +} + +struct ExpectQuicTraffic { + key_schedule: KeyScheduleTraffic, + _fin_verified: verify::FinishedMessageVerified, +} + +impl State<ServerConnectionData> for ExpectQuicTraffic { + fn handle<'m>( + self: Box<Self>, + _cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + // reject all messages + Err(inappropriate_message(&m.payload, &[])) + } + + 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 into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +impl KernelState for ExpectQuicTraffic { + fn update_secrets(&mut self, _: Direction) -> Result<ConnectionTrafficSecrets, Error> { + Err(Error::General( + "QUIC connections do not support key updates".into(), + )) + } + + fn handle_new_session_ticket( + &mut self, + _cx: &mut KernelContext<'_>, + _message: &NewSessionTicketPayloadTls13, + ) -> Result<(), Error> { + unreachable!("handle_new_session_ticket should not be called for server-side connections") + } +} |
