rustls-0.21.2/rustls/src/server/tls12.rs (827 lines of code) (raw):

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::ServerECDHParams; use crate::msgs::handshake::{CertificateRequestPayload, ClientSessionTicket, Random}; use crate::msgs::handshake::{CertificateStatus, ECDHEServerKeyExchange}; use crate::msgs::handshake::{ClientExtension, SessionId}; 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 { // -- TLS1.2 only from hereon in -- 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 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 .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(|| { // 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.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); } // 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 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 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.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); } } // --- 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: 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 )?; // 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 = 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, })) } } // --- Process client's KeyExchange --- 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()); // Complete key agreement, and set up encryption with the // resulting premaster secret. 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, })) } } } // --- Process client's certificate proof --- 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 => { // 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); 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, })) } } // --- 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(self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError { 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, })) } } // --- Process client's Finished --- 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(); // 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 { 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())?; // Save connection, perhaps 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"); } } // Send our CCS and Finished. 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, })) } } // --- Process traffic --- 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) } }