1#[cfg(all(unix, not(feature = "fips")))]
4use std::os::unix::fs::OpenOptionsExt;
5#[cfg(not(feature = "fips"))]
6use std::{
7 fmt::{Debug, Formatter},
8 fs::{File, OpenOptions},
9 io::{self, Write},
10 path::Path,
11};
12use std::{
13 path::PathBuf,
14 sync::{Arc, Mutex, OnceLock},
15};
16
17#[cfg(not(feature = "fips"))]
18use rustls::KeyLog;
19use rustls::{
20 client::{
21 danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
22 Resumption,
23 },
24 crypto::CryptoProvider,
25 pki_types::{CertificateDer, ServerName, UnixTime},
26 version::{TLS12, TLS13},
27 ClientConfig, DigitallySignedStruct, RootCertStore, ServerConfig, SignatureScheme, SupportedProtocolVersion,
28};
29#[cfg(not(feature = "fips"))]
30use saluki_common::collections::FastHashMap;
31use saluki_error::{generic_error, GenericError};
32use tracing::debug;
33#[cfg(not(feature = "fips"))]
34use tracing::warn;
35
36static DEFAULT_CRYPTO_PROVIDER_SET: OnceLock<()> = OnceLock::new();
38
39static DEFAULT_ROOT_CERT_STORE_MUTEX: Mutex<()> = Mutex::new(());
41static DEFAULT_ROOT_CERT_STORE: OnceLock<Arc<RootCertStore>> = OnceLock::new();
42#[cfg(not(feature = "fips"))]
43static KEY_LOG_FILES: OnceLock<Mutex<FastHashMap<PathBuf, Option<Arc<NssKeyLogFile>>>>> = OnceLock::new();
44
45const DEFAULT_MAX_TLS12_RESUMPTION_SESSIONS: usize = 8;
47const TLS12_PLUS_PROTOCOL_VERSIONS: &[&SupportedProtocolVersion] = &[&TLS13, &TLS12];
48const TLS13_PROTOCOL_VERSIONS: &[&SupportedProtocolVersion] = &[&TLS13];
49
50#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
52pub enum TlsMinimumVersion {
53 #[default]
55 Tls12,
56
57 Tls13,
59}
60
61impl TlsMinimumVersion {
62 const fn protocol_versions(self) -> &'static [&'static SupportedProtocolVersion] {
63 match self {
64 Self::Tls12 => TLS12_PLUS_PROTOCOL_VERSIONS,
65 Self::Tls13 => TLS13_PROTOCOL_VERSIONS,
66 }
67 }
68}
69
70#[derive(Debug)]
76struct AcceptAllServerCertVerifier {
77 provider: Arc<CryptoProvider>,
78}
79
80impl ServerCertVerifier for AcceptAllServerCertVerifier {
81 fn verify_server_cert(
82 &self, _end_entity: &CertificateDer<'_>, _intermediates: &[CertificateDer<'_>], _server_name: &ServerName<'_>,
83 _ocsp_response: &[u8], _now: UnixTime,
84 ) -> Result<ServerCertVerified, rustls::Error> {
85 Ok(ServerCertVerified::assertion())
86 }
87
88 fn verify_tls12_signature(
89 &self, message: &[u8], cert: &CertificateDer<'_>, dss: &DigitallySignedStruct,
90 ) -> Result<HandshakeSignatureValid, rustls::Error> {
91 rustls::crypto::verify_tls12_signature(message, cert, dss, &self.provider.signature_verification_algorithms)
92 }
93
94 fn verify_tls13_signature(
95 &self, message: &[u8], cert: &CertificateDer<'_>, dss: &DigitallySignedStruct,
96 ) -> Result<HandshakeSignatureValid, rustls::Error> {
97 rustls::crypto::verify_tls13_signature(message, cert, dss, &self.provider.signature_verification_algorithms)
98 }
99
100 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
101 self.provider.signature_verification_algorithms.supported_schemes()
102 }
103}
104
105#[cfg(not(feature = "fips"))]
106struct NssKeyLogFile {
107 path: PathBuf,
108 file: Mutex<File>,
109}
110
111#[cfg(not(feature = "fips"))]
112impl NssKeyLogFile {
113 fn open_shared<P: Into<PathBuf>>(path: P) -> Option<Arc<Self>> {
114 let path = path.into();
115 let mut key_log_files = match KEY_LOG_FILES.get_or_init(|| Mutex::new(FastHashMap::default())).lock() {
116 Ok(key_log_files) => key_log_files,
117 Err(_) => {
118 warn!("Failed to acquire TLS key log file registry lock; TLS key logging disabled.");
119 return None;
120 }
121 };
122
123 if let Some(cached) = key_log_files.get(&path) {
126 return cached.clone();
127 }
128
129 let key_log_file = match open_key_log_file(&path) {
130 Ok(file) => {
131 warn!(
132 path = %path.display(),
133 "TLS key logging enabled; TLS session secrets will be written to disk."
134 );
135 Some(Arc::new(Self {
136 path: path.clone(),
137 file: Mutex::new(file),
138 }))
139 }
140 Err(e) => {
141 warn!(
142 path = %path.display(),
143 error = %e,
144 "Failed to open TLS key log file for appending; TLS key logging disabled."
145 );
146 None
147 }
148 };
149
150 key_log_files.insert(path, key_log_file.clone());
151 key_log_file
152 }
153}
154
155#[cfg(not(feature = "fips"))]
156impl Debug for NssKeyLogFile {
157 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
158 f.debug_struct("NssKeyLogFile").field("path", &self.path).finish()
159 }
160}
161
162#[cfg(feature = "fips")]
163static FIPS_KEY_LOG_WARNED_PATHS: OnceLock<Mutex<saluki_common::collections::FastHashSet<PathBuf>>> = OnceLock::new();
164
165#[cfg(feature = "fips")]
166fn fips_key_log_warn_once(path: PathBuf) {
167 let warned =
168 FIPS_KEY_LOG_WARNED_PATHS.get_or_init(|| Mutex::new(saluki_common::collections::FastHashSet::default()));
169 let Ok(mut warned) = warned.lock() else {
170 return;
171 };
172 if warned.insert(path.clone()) {
173 tracing::warn!(
174 path = %path.display(),
175 "FIPS build: TLS key logging is disabled because exporting TLS secrets is not FIPS-compliant."
176 );
177 }
178}
179
180#[cfg(not(feature = "fips"))]
181impl KeyLog for NssKeyLogFile {
182 fn log(&self, label: &str, client_random: &[u8], secret: &[u8]) {
183 let line = match build_nss_key_log_line(label, client_random, secret) {
184 Ok(line) => line,
185 Err(e) => {
186 debug!(path = %self.path.display(), error = %e, "Failed to format TLS key log line.");
187 return;
188 }
189 };
190
191 match self.file.lock() {
192 Ok(mut file) => {
193 if let Err(e) = file.write_all(&line) {
194 debug!(path = %self.path.display(), error = %e, "Failed to write TLS key log line.");
195 }
196 }
197 Err(_) => {
198 debug!(path = %self.path.display(), "TLS key log file lock poisoned; dropping TLS key log line.");
199 }
200 }
201 }
202}
203
204#[cfg(not(feature = "fips"))]
205fn open_key_log_file(path: &Path) -> io::Result<File> {
206 let mut options = OpenOptions::new();
207 options.write(true).create(true).append(true);
208
209 #[cfg(unix)]
210 options.mode(0o600);
211
212 options.open(path)
213}
214
215#[cfg(not(feature = "fips"))]
216fn build_nss_key_log_line(label: &str, client_random: &[u8], secret: &[u8]) -> io::Result<Vec<u8>> {
217 let mut line = Vec::new();
218 write!(line, "{label} ")?;
219 write_hex(&mut line, client_random)?;
220 write!(line, " ")?;
221 write_hex(&mut line, secret)?;
222 writeln!(line)?;
223
224 Ok(line)
225}
226
227#[cfg(not(feature = "fips"))]
228fn write_hex(writer: &mut impl Write, bytes: &[u8]) -> io::Result<()> {
229 for byte in bytes {
230 write!(writer, "{byte:02x}")?;
231 }
232
233 Ok(())
234}
235
236pub struct ClientTLSConfigBuilder {
245 key_log_file_path: Option<PathBuf>,
246 max_tls12_resumption_sessions: Option<usize>,
247 min_tls_version: TlsMinimumVersion,
248 root_cert_store: Option<RootCertStore>,
249 danger_accept_invalid_certs: bool,
250}
251
252impl ClientTLSConfigBuilder {
253 pub fn new() -> Self {
254 Self {
255 key_log_file_path: None,
256 max_tls12_resumption_sessions: None,
257 min_tls_version: TlsMinimumVersion::default(),
258 root_cert_store: None,
259 danger_accept_invalid_certs: false,
260 }
261 }
262
263 pub fn with_key_log_file<P: Into<PathBuf>>(mut self, path: P) -> Self {
274 self.key_log_file_path = Some(path.into());
275 self
276 }
277
278 pub fn with_max_tls12_resumption_sessions(mut self, max: usize) -> Self {
282 self.max_tls12_resumption_sessions = Some(max);
283 self
284 }
285
286 pub fn with_root_cert_store(mut self, store: RootCertStore) -> Self {
290 self.root_cert_store = Some(store);
291 self
292 }
293
294 pub fn with_min_tls_version(mut self, version: TlsMinimumVersion) -> Self {
298 self.min_tls_version = version;
299 self
300 }
301
302 pub fn danger_accept_invalid_certs(mut self) -> Self {
308 self.danger_accept_invalid_certs = true;
309 self
310 }
311
312 pub fn build(self) -> Result<ClientConfig, GenericError> {
320 let max_tls12_resumption_sessions = self
321 .max_tls12_resumption_sessions
322 .unwrap_or(DEFAULT_MAX_TLS12_RESUMPTION_SESSIONS);
323 let protocol_versions = self.min_tls_version.protocol_versions();
324
325 let mut config = if self.danger_accept_invalid_certs {
326 let crypto_provider = CryptoProvider::get_default()
327 .map(Arc::clone)
328 .ok_or_else(|| generic_error!("Default cryptography provider not yet installed."))?;
329 let verifier = Arc::new(AcceptAllServerCertVerifier {
330 provider: crypto_provider,
331 });
332
333 ClientConfig::builder_with_protocol_versions(protocol_versions)
334 .dangerous()
335 .with_custom_certificate_verifier(verifier)
336 .with_no_client_auth()
337 } else {
338 let root_cert_store = self.root_cert_store.map(Arc::new).map(Ok).unwrap_or_else(|| {
339 DEFAULT_ROOT_CERT_STORE
340 .get()
341 .map(Arc::clone)
342 .ok_or(generic_error!("Default TLS root certificate store not initialized."))
343 })?;
344
345 ClientConfig::builder_with_protocol_versions(protocol_versions)
346 .with_root_certificates(root_cert_store)
347 .with_no_client_auth()
348 };
349
350 if let Some(path) = self.key_log_file_path {
351 #[cfg(feature = "fips")]
352 fips_key_log_warn_once(path);
353
354 #[cfg(not(feature = "fips"))]
355 if let Some(key_log) = NssKeyLogFile::open_shared(path) {
356 config.key_log = key_log;
357 }
358 }
359
360 config.resumption = Resumption::in_memory_sessions(max_tls12_resumption_sessions);
364
365 ensure_client_config_fips_compliant(&config)?;
366
367 Ok(config)
368 }
369}
370
371pub fn ensure_client_config_fips_compliant(config: &ClientConfig) -> Result<(), GenericError> {
379 #[cfg(feature = "fips")]
380 if !config.fips() {
381 return Err(generic_error!("Client TLS configuration is not FIPS compliant."));
382 }
383
384 #[cfg(not(feature = "fips"))]
385 let _ = config;
386
387 Ok(())
388}
389
390pub fn ensure_server_config_fips_compliant(config: &mut ServerConfig) -> Result<(), GenericError> {
399 #[cfg(feature = "fips")]
400 {
401 config.key_log = Arc::new(rustls::NoKeyLog);
402 config.enable_secret_extraction = false;
403
404 if !config.fips() {
405 return Err(generic_error!("Server TLS configuration is not FIPS compliant."));
406 }
407 }
408
409 #[cfg(not(feature = "fips"))]
410 let _ = config;
411
412 Ok(())
413}
414
415pub fn initialize_default_crypto_provider() -> Result<(), GenericError> {
424 if DEFAULT_CRYPTO_PROVIDER_SET.get().is_some() {
425 return Err(generic_error!("Default TLS cryptography provider already initialized."));
426 }
427
428 default_crypto_provider().install_default().map_err(|_| {
429 generic_error!(
430 "Failed to install the default TLS cryptography provider. This is likely due to a conflicting provider already being installed."
431 )
432 })?;
433
434 DEFAULT_CRYPTO_PROVIDER_SET
436 .set(())
437 .expect("should be impossible for DEFAULT_CRYPTO_PROVIDER_SET to be initialized twice");
438
439 Ok(())
440}
441
442#[cfg(not(windows))]
443fn default_crypto_provider() -> CryptoProvider {
444 let provider = rustls::crypto::aws_lc_rs::default_provider();
445
446 #[cfg(feature = "fips")]
447 {
448 let mut provider = provider;
449 provider.cipher_suites.retain(|suite| suite.fips());
450 provider.kx_groups.retain(|group| group.fips());
451 provider
452 }
453
454 #[cfg(not(feature = "fips"))]
455 provider
456}
457
458#[cfg(windows)]
459fn default_crypto_provider() -> CryptoProvider {
460 rustls_cng_crypto::default_provider()
461}
462
463pub fn load_platform_root_certificates() -> Result<(), GenericError> {
529 let _guard = DEFAULT_ROOT_CERT_STORE_MUTEX
530 .lock()
531 .map_err(|_| generic_error!("Default TLS root certificate store update lock poisoned."))?;
532 if DEFAULT_ROOT_CERT_STORE.get().is_some() {
533 return Err(generic_error!(
534 "Default TLS root certificate store already initialized."
535 ));
536 }
537
538 let root_cert_store = load_platform_root_certificates_inner()?;
539
540 DEFAULT_ROOT_CERT_STORE
543 .set(Arc::new(root_cert_store))
544 .expect("should be impossible for DEFAULT_ROOT_CERT_STORE to be initialized twice");
545
546 Ok(())
547}
548
549pub fn load_platform_root_certificates_inner() -> Result<RootCertStore, GenericError> {
561 let mut root_cert_store = RootCertStore::empty();
562
563 let mut result = rustls_native_certs::load_native_certs();
564
565 result.errors.retain(|err| {
569 !matches!(
570 &err.kind,
571 rustls_native_certs::ErrorKind::Io { inner, .. } if inner.kind() == std::io::ErrorKind::NotFound,
572 )
573 });
574
575 let (added, failed) = root_cert_store.add_parsable_certificates(result.certs);
577 if failed == 0 && added > 0 {
578 debug!(
579 "Added {} certificates from environment to the default root certificate store.",
580 added
581 );
582 } else if failed > 0 && added > 0 {
583 debug!("Added {} certificates from environment to the default root certificate store, but failed to add {} certificates.", added, failed);
584 } else {
585 if !result.errors.is_empty() {
593 let joined_errors = result
594 .errors
595 .iter()
596 .map(|e| e.to_string())
597 .collect::<Vec<_>>()
598 .join(", ");
599
600 return Err(generic_error!(
601 "Failed to load certificates from platform's native certificate store: {}",
602 joined_errors
603 ));
604 }
605 }
606
607 Ok(root_cert_store)
608}
609
610#[cfg(test)]
611mod tests {
612 #[cfg(not(feature = "fips"))]
613 use std::fs;
614 #[cfg(all(unix, not(feature = "fips")))]
615 use std::os::unix::fs::PermissionsExt;
616 #[cfg(feature = "fips")]
617 use std::sync::{
618 atomic::{AtomicBool, Ordering},
619 Arc,
620 };
621
622 use rcgen::{generate_simple_self_signed, CertifiedKey};
623 #[cfg(feature = "fips")]
624 use rustls::KeyLog;
625 use rustls::{
626 pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer},
627 ProtocolVersion, RootCertStore, ServerConfig,
628 };
629
630 #[cfg(not(feature = "fips"))]
631 use super::{build_nss_key_log_line, open_key_log_file};
632 use super::{
633 ensure_client_config_fips_compliant, ensure_server_config_fips_compliant, ClientTLSConfigBuilder,
634 TlsMinimumVersion,
635 };
636
637 #[test]
638 fn tls12_minimum_enables_tls12_and_tls13() {
639 let versions = TlsMinimumVersion::Tls12.protocol_versions();
640
641 assert_eq!(versions.len(), 2);
642 assert_eq!(versions[0].version, ProtocolVersion::TLSv1_3);
643 assert_eq!(versions[1].version, ProtocolVersion::TLSv1_2);
644 }
645
646 #[test]
647 fn tls13_minimum_enables_tls13_only() {
648 let versions = TlsMinimumVersion::Tls13.protocol_versions();
649
650 assert_eq!(versions.len(), 1);
651 assert_eq!(versions[0].version, ProtocolVersion::TLSv1_3);
652 }
653
654 #[test]
655 fn client_config_fips_validation_accepts_builder_config() {
656 let _ = super::initialize_default_crypto_provider();
657
658 let config = ClientTLSConfigBuilder::new()
659 .with_root_cert_store(RootCertStore::empty())
660 .build()
661 .expect("client TLS config should build");
662
663 ensure_client_config_fips_compliant(&config).expect("client TLS config should pass FIPS validation");
664 }
665
666 #[test]
667 fn server_config_fips_validation_accepts_basic_server_config() {
668 let _ = super::initialize_default_crypto_provider();
669 let CertifiedKey { cert, signing_key } =
670 generate_simple_self_signed(["localhost".to_owned()]).expect("self-signed cert should be generated");
671 let cert_chain = vec![cert.der().clone()];
672 let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(signing_key.serialize_der()));
673 let mut config = ServerConfig::builder()
674 .with_no_client_auth()
675 .with_single_cert(cert_chain, key)
676 .expect("server TLS config should build");
677
678 ensure_server_config_fips_compliant(&mut config).expect("server TLS config should pass FIPS validation");
679 }
680
681 #[cfg(feature = "fips")]
682 #[test]
683 fn server_config_fips_validation_disables_secret_extraction() {
684 #[derive(Debug)]
685 struct TestKeyLog(Arc<AtomicBool>);
686
687 impl KeyLog for TestKeyLog {
688 fn log(&self, _label: &str, _client_random: &[u8], _secret: &[u8]) {
689 self.0.store(true, Ordering::Relaxed);
690 }
691 }
692
693 let _ = super::initialize_default_crypto_provider();
694 let CertifiedKey { cert, signing_key } =
695 generate_simple_self_signed(["localhost".to_owned()]).expect("self-signed cert should be generated");
696 let cert_chain = vec![cert.der().clone()];
697 let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(signing_key.serialize_der()));
698 let key_log_used = Arc::new(AtomicBool::new(false));
699 let mut config = ServerConfig::builder()
700 .with_no_client_auth()
701 .with_single_cert(cert_chain, key)
702 .expect("server TLS config should build");
703 config.key_log = Arc::new(TestKeyLog(Arc::clone(&key_log_used)));
704 config.enable_secret_extraction = true;
705
706 ensure_server_config_fips_compliant(&mut config).expect("server TLS config should pass FIPS validation");
707
708 config.key_log.log("CLIENT_RANDOM", &[0xab, 0xcd], &[0x01, 0x23]);
709
710 assert!(!key_log_used.load(Ordering::Relaxed));
711 assert!(!config.enable_secret_extraction);
712 }
713
714 #[test]
715 #[cfg(not(feature = "fips"))]
716 fn nss_key_log_lines_are_written_in_hex_format() {
717 let output =
718 build_nss_key_log_line("CLIENT_RANDOM", &[0xab, 0xcd], &[0x01, 0x23]).expect("key log line should build");
719
720 assert_eq!(output, b"CLIENT_RANDOM abcd 0123\n");
721 }
722
723 #[test]
724 #[cfg(not(feature = "fips"))]
725 fn client_config_uses_configured_key_log_file() {
726 let _ = super::initialize_default_crypto_provider();
727 let tempdir = tempfile::tempdir().expect("temporary directory should be created");
728 let key_log_path = tempdir.path().join("sslkeylogfile");
729
730 let config = ClientTLSConfigBuilder::new()
731 .with_root_cert_store(RootCertStore::empty())
732 .with_key_log_file(&key_log_path)
733 .build()
734 .expect("client TLS config should build");
735
736 config.key_log.log("CLIENT_RANDOM", &[0xab, 0xcd], &[0x01, 0x23]);
737
738 let contents = fs::read_to_string(&key_log_path).expect("key log file should be readable");
739 assert_eq!(contents, "CLIENT_RANDOM abcd 0123\n");
740 }
741
742 #[test]
743 #[cfg(not(feature = "fips"))]
744 fn client_config_ignores_unwritable_key_log_file() {
745 let _ = super::initialize_default_crypto_provider();
746 let tempdir = tempfile::tempdir().expect("temporary directory should be created");
747 let key_log_path = tempdir.path().join("missing").join("sslkeylogfile");
748
749 let config = ClientTLSConfigBuilder::new()
750 .with_root_cert_store(RootCertStore::empty())
751 .with_key_log_file(&key_log_path)
752 .build()
753 .expect("client TLS config should build even when the key log file cannot be opened");
754
755 config.key_log.log("CLIENT_RANDOM", &[0xab, 0xcd], &[0x01, 0x23]);
756
757 assert!(!key_log_path.exists());
758 }
759
760 #[test]
761 #[cfg(not(feature = "fips"))]
762 fn client_configs_append_to_shared_key_log_file() {
763 let _ = super::initialize_default_crypto_provider();
764 let tempdir = tempfile::tempdir().expect("temporary directory should be created");
765 let key_log_path = tempdir.path().join("shared-sslkeylogfile");
766
767 let first_config = ClientTLSConfigBuilder::new()
768 .with_root_cert_store(RootCertStore::empty())
769 .with_key_log_file(&key_log_path)
770 .build()
771 .expect("first client TLS config should build");
772 let second_config = ClientTLSConfigBuilder::new()
773 .with_root_cert_store(RootCertStore::empty())
774 .with_key_log_file(&key_log_path)
775 .build()
776 .expect("second client TLS config should build");
777
778 first_config.key_log.log("CLIENT_RANDOM", &[0xab, 0xcd], &[0x01, 0x23]);
779 second_config.key_log.log("CLIENT_RANDOM", &[0xef, 0x01], &[0x45, 0x67]);
780
781 let contents = fs::read_to_string(&key_log_path).expect("key log file should be readable");
782 assert_eq!(contents, "CLIENT_RANDOM abcd 0123\nCLIENT_RANDOM ef01 4567\n");
783 }
784
785 #[cfg(all(unix, not(feature = "fips")))]
786 #[test]
787 fn key_log_file_is_created_with_owner_only_permissions() {
788 let tempdir = tempfile::tempdir().expect("temporary directory should be created");
789 let key_log_path = tempdir.path().join("sslkeylogfile");
790
791 let file = open_key_log_file(&key_log_path).expect("key log file should open");
792 drop(file);
793
794 let mode = fs::metadata(&key_log_path)
795 .expect("key log file metadata should be readable")
796 .permissions()
797 .mode()
798 & 0o777;
799 assert_eq!(mode, 0o600);
800 }
801
802 #[cfg(windows)]
803 #[test]
804 fn windows_default_crypto_provider_builds_client_config() {
805 let _ = super::initialize_default_crypto_provider();
806
807 ClientTLSConfigBuilder::new()
808 .with_root_cert_store(RootCertStore::empty())
809 .build()
810 .expect("Windows CNG-backed TLS config should build");
811 }
812
813 #[test]
814 #[cfg(feature = "fips")]
815 fn key_log_file_ignored_in_fips_mode() {
816 let _ = super::initialize_default_crypto_provider();
817 let tempdir = tempfile::tempdir().expect("temporary directory should be created");
818 let key_log_path = tempdir.path().join("fips-sslkeylogfile");
819
820 let config = ClientTLSConfigBuilder::new()
823 .with_root_cert_store(RootCertStore::empty())
824 .with_key_log_file(&key_log_path)
825 .build()
826 .expect("TLS config should build in FIPS mode even when a key log file is configured");
827
828 config.key_log.log("CLIENT_RANDOM", &[0xab, 0xcd], &[0x01, 0x23]);
830
831 assert!(!key_log_path.exists(), "FIPS builds must not create a TLS key log file");
832 }
833}