summaryrefslogtreecommitdiff
path: root/vendor/rustls/src/server
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-10 13:11:11 -0600
committermo khan <mo@mokhan.ca>2025-07-10 13:11:11 -0600
commit01959b16a21b22b5df5f16569c2a8e8f92beecef (patch)
tree32afa5d747c5466345c59ec52161a7cba3d6d755 /vendor/rustls/src/server
parentff30574117a996df332e23d1fb6f65259b316b5b (diff)
chore: vendor dependencies
Diffstat (limited to 'vendor/rustls/src/server')
-rw-r--r--vendor/rustls/src/server/builder.rs127
-rw-r--r--vendor/rustls/src/server/common.rs35
-rw-r--r--vendor/rustls/src/server/handy.rs356
-rw-r--r--vendor/rustls/src/server/hs.rs763
-rw-r--r--vendor/rustls/src/server/server_conn.rs1288
-rw-r--r--vendor/rustls/src/server/test.rs369
-rw-r--r--vendor/rustls/src/server/tls12.rs1003
-rw-r--r--vendor/rustls/src/server/tls13.rs1535
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")
+ }
+}