use crate::check::inappropriate_message;
use crate::common_state::{CommonState, Side, State};
use crate::conn::ConnectionRandoms;
use crate::enums::ProtocolVersion;
use crate::enums::{AlertDescription, ContentType, HandshakeType};
use crate::error::{Error, PeerIncompatible, PeerMisbehaved};
use crate::hash_hs::HandshakeHash;
use crate::key::Certificate;
#[cfg(feature = "logging")]
use crate::log::{debug, trace};
use crate::msgs::base::Payload;
use crate::msgs::ccs::ChangeCipherSpecPayload;
use crate::msgs::codec::Codec;
use crate::msgs::handshake::{ClientECDHParams, HandshakeMessagePayload, HandshakePayload};
use crate::msgs::handshake::{NewSessionTicketPayload, SessionId};
use crate::msgs::message::{Message, MessagePayload};
use crate::msgs::persist;
#[cfg(feature = "secret_extraction")]
use crate::suites::PartiallyExtractedSecrets;
use crate::tls12::{self, ConnectionSecrets, Tls12CipherSuite};
use crate::{kx, ticketer, verify};
use super::common::ActiveCertifiedKey;
use super::hs::{self, ServerContext};
use super::server_conn::{ProducesTickets, ServerConfig, ServerConnectionData};
use ring::constant_time;
use std::sync::Arc;
pub(super) use client_hello::CompleteClientHelloHandling;
mod client_hello {
use crate::enums::SignatureScheme;
use crate::msgs::enums::ECPointFormat;
use crate::msgs::enums::{ClientCertificateType, Compression};
use crate::msgs::handshake::ClientExtension;
use crate::msgs::handshake::ServerECDHParams;
use crate::msgs::handshake::{CertificateRequestPayload, ClientSessionTicket, Random};
use crate::msgs::handshake::{CertificateStatus, ECDHEServerKeyExchange};
use crate::msgs::handshake::{ClientHelloPayload, ServerHelloPayload};
use crate::msgs::handshake::{ServerExtension, ServerKeyExchangePayload};
use crate::sign;
use crate::verify::DigitallySignedStruct;
use super::*;
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: Vec<ServerExtension>,
}
impl CompleteClientHelloHandling {
pub(in crate::server) fn handle_client_hello(
mut self,
cx: &mut ServerContext<'_>,
server_key: ActiveCertifiedKey,
chm: &Message,
client_hello: &ClientHelloPayload,
sigschemes_ext: Vec<SignatureScheme>,
tls13_enabled: bool,
) -> hs::NextStateOrError {
self.transcript.add_message(chm);
if client_hello.ems_support_offered() {
self.using_ems = true;
}
let groups_ext = client_hello
.get_namedgroups_extension()
.ok_or_else(|| {
cx.common.send_fatal_alert(
AlertDescription::HandshakeFailure,
PeerIncompatible::NamedGroupsExtensionRequired,
)
})?;
let ecpoints_ext = client_hello
.get_ecpoints_extension()
.ok_or_else(|| {
cx.common.send_fatal_alert(
AlertDescription::HandshakeFailure,
PeerIncompatible::EcPointsExtensionRequired,
)
})?;
trace!("namedgroups {:?}", groups_ext);
trace!("ecpoints {:?}", ecpoints_ext);
if !ecpoints_ext.contains(&ECPointFormat::Uncompressed) {
return Err(cx.common.send_fatal_alert(
AlertDescription::IllegalParameter,
PeerIncompatible::UncompressedEcPointsRequired,
));
}
if tls13_enabled {
self.randoms.server[24..].copy_from_slice(&tls12::DOWNGRADE_SENTINEL);
}
let mut ticket_received = false;
let resume_data = client_hello
.get_ticket_extension()
.and_then(|ticket_ext| match ticket_ext {
ClientExtension::SessionTicket(ClientSessionTicket::Offer(ticket)) => {
Some(ticket)
}
_ => None,
})
.and_then(|ticket| {
ticket_received = true;
debug!("Ticket received");
let data = self.config.ticketer.decrypt(&ticket.0);
if data.is_none() {
debug!("Ticket didn't decrypt");
}
data
})
.or_else(|| {
if client_hello.session_id.is_empty() || ticket_received {
return None;
}
self.config
.session_storage
.get(&client_hello.session_id.get_encoding())
})
.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);
}
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 group = self
.config
.kx_groups
.iter()
.find(|skxg| groups_ext.contains(&skxg.name))
.cloned()
.ok_or_else(|| {
cx.common.send_fatal_alert(
AlertDescription::HandshakeFailure,
PeerIncompatible::NoKxGroupsInCommon,
)
})?;
let ecpoint = ECPointFormat::SUPPORTED
.iter()
.find(|format| ecpoints_ext.contains(format))
.cloned()
.ok_or_else(|| {
cx.common.send_fatal_alert(
AlertDescription::HandshakeFailure,
PeerIncompatible::NoEcPointFormatsInCommon,
)
})?;
debug_assert_eq!(ecpoint, ECPointFormat::Uncompressed);
let (mut ocsp_response, mut sct_list) =
(server_key.get_ocsp(), server_key.get_sct_list());
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.send_ticket = emit_server_hello(
&self.config,
&mut self.transcript,
cx,
self.session_id,
self.suite,
self.using_ems,
&mut ocsp_response,
&mut sct_list,
client_hello,
None,
&self.randoms,
self.extra_exts,
)?;
emit_certificate(&mut self.transcript, cx.common, server_key.get_cert());
if let Some(ocsp_response) = ocsp_response {
emit_cert_status(&mut self.transcript, cx.common, ocsp_response);
}
let server_kx = emit_server_kx(
&mut self.transcript,
cx.common,
sigschemes,
group,
server_key.get_key(),
&self.randoms,
)?;
let doing_client_auth = emit_certificate_req(&self.config, &mut self.transcript, cx)?;
emit_server_hello_done(&mut self.transcript, 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 {
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;
self.send_ticket = emit_server_hello(
&self.config,
&mut self.transcript,
cx,
self.session_id,
self.suite,
self.using_ems,
&mut None,
&mut None,
client_hello,
Some(&resumedata),
&self.randoms,
self.extra_exts,
)?;
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;
if self.send_ticket {
emit_ticket(
&secrets,
&mut self.transcript,
self.using_ems,
cx,
&*self.config.ticketer,
)?;
}
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(
config: &ServerConfig,
transcript: &mut HandshakeHash,
cx: &mut ServerContext<'_>,
session_id: SessionId,
suite: &'static Tls12CipherSuite,
using_ems: bool,
ocsp_response: &mut Option<&[u8]>,
sct_list: &mut Option<&[u8]>,
hello: &ClientHelloPayload,
resumedata: Option<&persist::ServerSessionValue>,
randoms: &ConnectionRandoms,
extra_exts: Vec<ServerExtension>,
) -> Result<bool, Error> {
let mut ep = hs::ExtensionProcessing::new();
ep.process_common(
config,
cx,
ocsp_response,
sct_list,
hello,
resumedata,
extra_exts,
)?;
ep.process_tls12(config, hello, using_ems);
let sh = Message {
version: ProtocolVersion::TLSv1_2,
payload: MessagePayload::handshake(HandshakeMessagePayload {
typ: HandshakeType::ServerHello,
payload: 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.exts,
}),
}),
};
trace!("sending server hello {:?}", sh);
transcript.add_message(&sh);
cx.common.send_msg(sh, false);
Ok(ep.send_ticket)
}
fn emit_certificate(
transcript: &mut HandshakeHash,
common: &mut CommonState,
cert_chain: &[Certificate],
) {
let c = Message {
version: ProtocolVersion::TLSv1_2,
payload: MessagePayload::handshake(HandshakeMessagePayload {
typ: HandshakeType::Certificate,
payload: HandshakePayload::Certificate(cert_chain.to_owned()),
}),
};
transcript.add_message(&c);
common.send_msg(c, false);
}
fn emit_cert_status(transcript: &mut HandshakeHash, common: &mut CommonState, ocsp: &[u8]) {
let st = CertificateStatus::new(ocsp.to_owned());
let c = Message {
version: ProtocolVersion::TLSv1_2,
payload: MessagePayload::handshake(HandshakeMessagePayload {
typ: HandshakeType::CertificateStatus,
payload: HandshakePayload::CertificateStatus(st),
}),
};
transcript.add_message(&c);
common.send_msg(c, false);
}
fn emit_server_kx(
transcript: &mut HandshakeHash,
common: &mut CommonState,
sigschemes: Vec<SignatureScheme>,
skxg: &'static kx::SupportedKxGroup,
signing_key: &dyn sign::SigningKey,
randoms: &ConnectionRandoms,
) -> Result<kx::KeyExchange, Error> {
let kx = kx::KeyExchange::start(skxg).ok_or(Error::FailedToGetRandomBytes)?;
let secdh = ServerECDHParams::new(skxg.name, kx.pubkey.as_ref());
let mut msg = Vec::new();
msg.extend(randoms.client);
msg.extend(randoms.server);
secdh.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::ECDHE(ECDHEServerKeyExchange {
params: secdh,
dss: DigitallySignedStruct::new(sigscheme, sig),
});
let m = Message {
version: ProtocolVersion::TLSv1_2,
payload: MessagePayload::handshake(HandshakeMessagePayload {
typ: HandshakeType::ServerKeyExchange,
payload: HandshakePayload::ServerKeyExchange(skx),
}),
};
transcript.add_message(&m);
common.send_msg(m, false);
Ok(kx)
}
fn emit_certificate_req(
config: &ServerConfig,
transcript: &mut HandshakeHash,
cx: &mut ServerContext<'_>,
) -> 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
.client_auth_root_subjects()
.to_vec();
let cr = CertificateRequestPayload {
certtypes: vec![
ClientCertificateType::RSASign,
ClientCertificateType::ECDSASign,
],
sigschemes: verify_schemes,
canames: names,
};
let m = Message {
version: ProtocolVersion::TLSv1_2,
payload: MessagePayload::handshake(HandshakeMessagePayload {
typ: HandshakeType::CertificateRequest,
payload: HandshakePayload::CertificateRequest(cr),
}),
};
trace!("Sending CertificateRequest {:?}", m);
transcript.add_message(&m);
cx.common.send_msg(m, false);
Ok(true)
}
fn emit_server_hello_done(transcript: &mut HandshakeHash, common: &mut CommonState) {
let m = Message {
version: ProtocolVersion::TLSv1_2,
payload: MessagePayload::handshake(HandshakeMessagePayload {
typ: HandshakeType::ServerHelloDone,
payload: HandshakePayload::ServerHelloDone,
}),
};
transcript.add_message(&m);
common.send_msg(m, false);
}
}
struct ExpectCertificate {
config: Arc<ServerConfig>,
transcript: HandshakeHash,
randoms: ConnectionRandoms,
session_id: SessionId,
suite: &'static Tls12CipherSuite,
using_ems: bool,
server_kx: kx::KeyExchange,
send_ticket: bool,
}
impl State<ServerConnectionData> for ExpectCertificate {
fn handle(mut self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
self.transcript.add_message(&m);
let cert_chain = require_handshake_msg_move!(
m,
HandshakeType::Certificate,
HandshakePayload::Certificate
)?;
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 = std::time::SystemTime::now();
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,
}))
}
}
struct ExpectClientKx {
config: Arc<ServerConfig>,
transcript: HandshakeHash,
randoms: ConnectionRandoms,
session_id: SessionId,
suite: &'static Tls12CipherSuite,
using_ems: bool,
server_kx: kx::KeyExchange,
client_cert: Option<Vec<Certificate>>,
send_ticket: bool,
}
impl State<ServerConnectionData> for ExpectClientKx {
fn handle(mut self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
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.get_current_hash());
let peer_kx_params =
tls12::decode_ecdh_params::<ClientECDHParams>(cx.common, &client_kx.0)?;
let secrets = ConnectionSecrets::from_key_exchange(
self.server_kx,
&peer_kx_params.public.0,
ems_seed,
self.randoms,
self.suite,
)?;
self.config.key_log.log(
"CLIENT_RANDOM",
&secrets.randoms.client,
&secrets.master_secret,
);
cx.common
.start_encryption_tls12(&secrets, Side::Server);
if let Some(client_cert) = self.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,
}))
} else {
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,
}))
}
}
}
struct ExpectCertificateVerify {
config: Arc<ServerConfig>,
secrets: ConnectionSecrets,
transcript: HandshakeHash,
session_id: SessionId,
using_ems: bool,
client_cert: Vec<Certificate>,
send_ticket: bool,
}
impl State<ServerConnectionData> for ExpectCertificateVerify {
fn handle(mut self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
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 => {
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);
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,
}))
}
}
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(self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
match m.payload {
MessagePayload::ChangeCipherSpec(..) => {}
payload => {
return Err(inappropriate_message(
&payload,
&[ContentType::ChangeCipherSpec],
))
}
}
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 get_server_connection_value_tls12(
secrets: &ConnectionSecrets,
using_ems: bool,
cx: &ServerContext<'_>,
time_now: ticketer::TimeBase,
) -> persist::ServerSessionValue {
let version = ProtocolVersion::TLSv1_2;
let secret = secrets.get_master_secret();
let mut v = persist::ServerSessionValue::new(
cx.data.sni.as_ref(),
version,
secrets.suite().common.suite,
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,
) -> Result<(), Error> {
let time_now = ticketer::TimeBase::now()?;
let plain = get_server_connection_value_tls12(secrets, using_ems, cx, time_now).get_encoding();
let ticket = ticketer
.encrypt(&plain)
.unwrap_or_default();
let ticket_lifetime = ticketer.lifetime();
let m = Message {
version: ProtocolVersion::TLSv1_2,
payload: MessagePayload::handshake(HandshakeMessagePayload {
typ: HandshakeType::NewSessionTicket,
payload: 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.get_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 {
typ: HandshakeType::Finished,
payload: 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(mut self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
let finished =
require_handshake_msg!(m, HandshakeType::Finished, HandshakePayload::Finished)?;
cx.common.check_aligned_handshake()?;
let vh = self.transcript.get_current_hash();
let expect_verify_data = self.secrets.client_verify_data(&vh);
let _fin_verified =
constant_time::verify_slices_are_equal(&expect_verify_data, &finished.0)
.map_err(|_| {
cx.common
.send_fatal_alert(AlertDescription::DecryptError, Error::DecryptError)
})
.map(|_| verify::FinishedMessageVerified::assertion())?;
if !self.resuming && !self.session_id.is_empty() {
let time_now = ticketer::TimeBase::now()?;
let value =
get_server_connection_value_tls12(&self.secrets, self.using_ems, cx, time_now);
let worked = self
.config
.session_storage
.put(self.session_id.get_encoding(), value.get_encoding());
if worked {
debug!("Session saved");
} else {
debug!("Session not saved");
}
}
self.transcript.add_message(&m);
if !self.resuming {
if self.send_ticket {
emit_ticket(
&self.secrets,
&mut self.transcript,
self.using_ems,
cx,
&*self.config.ticketer,
)?;
}
emit_ccs(cx.common);
cx.common
.record_layer
.start_encrypting();
emit_finished(&self.secrets, &mut self.transcript, cx.common);
}
cx.common.start_traffic();
Ok(Box::new(ExpectTraffic {
secrets: self.secrets,
_fin_verified,
}))
}
}
struct ExpectTraffic {
secrets: ConnectionSecrets,
_fin_verified: verify::FinishedMessageVerified,
}
impl ExpectTraffic {}
impl State<ServerConnectionData> for ExpectTraffic {
fn handle(self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
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(())
}
#[cfg(feature = "secret_extraction")]
fn extract_secrets(&self) -> Result<PartiallyExtractedSecrets, Error> {
self.secrets
.extract_secrets(Side::Server)
}
}