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