1use anyhow::{Context, Result};
45use std::collections::HashSet;
46use std::path::Path;
47use std::sync::Arc;
48
49pub async fn load_rustls_config(
54 cert_path: &Path,
55 key_path: &Path,
56) -> Result<axum_server::tls_rustls::RustlsConfig> {
57 let cert = tokio::fs::read(cert_path)
58 .await
59 .with_context(|| format!("failed to read TLS cert from {}", cert_path.display()))?;
60 let key = tokio::fs::read(key_path)
61 .await
62 .with_context(|| format!("failed to read TLS key from {}", key_path.display()))?;
63 let config = axum_server::tls_rustls::RustlsConfig::from_pem(cert, key)
64 .await
65 .context(
66 "failed to parse TLS cert/key — ensure PEM-encoded (cert may be fullchain; \
67 key must be PKCS#8 or RSA)",
68 )?;
69 Ok(config)
70}
71
72pub async fn load_mtls_rustls_config(
78 cert_path: &Path,
79 key_path: &Path,
80 allowlist_path: &Path,
81) -> Result<axum_server::tls_rustls::RustlsConfig> {
82 let allowlist = load_fingerprint_allowlist(allowlist_path).await?;
83 if allowlist.is_empty() {
84 anyhow::bail!(
85 "mTLS allowlist at {} is empty — refuse to start rather than silently accept all peers",
86 allowlist_path.display()
87 );
88 }
89
90 let cert_pem = tokio::fs::read(cert_path)
91 .await
92 .with_context(|| format!("failed to read TLS cert from {}", cert_path.display()))?;
93 let key_pem = tokio::fs::read(key_path)
94 .await
95 .with_context(|| format!("failed to read TLS key from {}", key_path.display()))?;
96
97 let certs: Vec<rustls::pki_types::CertificateDer<'static>> =
98 rustls_pki_pem_iter_certs(&cert_pem)?;
99 let key = rustls_pki_pem_parse_private_key(&key_pem)?;
100
101 let verifier = Arc::new(FingerprintAllowlistVerifier { allowlist });
102 let server_config = rustls::ServerConfig::builder()
103 .with_client_cert_verifier(verifier)
104 .with_single_cert(certs, key)
105 .context("failed to build rustls ServerConfig for mTLS")?;
106
107 Ok(axum_server::tls_rustls::RustlsConfig::from_config(
108 Arc::new(server_config),
109 ))
110}
111
112pub async fn load_fingerprint_allowlist(path: &Path) -> Result<HashSet<[u8; 32]>> {
115 let text = tokio::fs::read_to_string(path)
116 .await
117 .with_context(|| format!("failed to read mTLS allowlist from {}", path.display()))?;
118 let mut set = HashSet::new();
119 for (lineno, raw) in text.lines().enumerate() {
120 let line = raw.trim();
121 if line.is_empty() || line.starts_with('#') {
122 continue;
123 }
124 let line = line.split('#').next().unwrap_or("").trim();
129 if line.is_empty() {
130 continue;
131 }
132 let hex_part = line.strip_prefix("sha256:").unwrap_or(line);
134 if let Some(bad) = hex_part
142 .chars()
143 .find(|c| !c.is_ascii_hexdigit() && *c != ':')
144 {
145 anyhow::bail!(
146 "mTLS allowlist line {}: unexpected character {:?} — \
147 entries must be 64 hex chars with optional `:` separators",
148 lineno + 1,
149 bad
150 );
151 }
152 let hex_clean: String = hex_part.chars().filter(|c| *c != ':').collect();
153 if hex_clean.len() != 64 {
154 anyhow::bail!(
155 "mTLS allowlist line {}: expected 64 hex chars (optionally with `:` separators), got {}",
156 lineno + 1,
157 hex_clean.len()
158 );
159 }
160 let mut bytes = [0u8; 32];
161 for i in 0..32 {
162 bytes[i] = u8::from_str_radix(&hex_clean[i * 2..i * 2 + 2], 16)
163 .with_context(|| format!("mTLS allowlist line {}: invalid hex", lineno + 1))?;
164 }
165 set.insert(bytes);
166 }
167 Ok(set)
168}
169
170pub fn rustls_pki_pem_iter_certs(
171 pem: &[u8],
172) -> Result<Vec<rustls::pki_types::CertificateDer<'static>>> {
173 use rustls::pki_types::pem::PemObject as _;
174 let mut cursor = std::io::Cursor::new(pem);
175 let certs: Vec<_> = rustls::pki_types::CertificateDer::pem_reader_iter(&mut cursor)
176 .collect::<std::result::Result<Vec<_>, _>>()
177 .context("failed to parse TLS cert PEM")?;
178 if certs.is_empty() {
179 anyhow::bail!("TLS cert PEM contained no certificates");
180 }
181 Ok(certs)
182}
183
184pub fn rustls_pki_pem_parse_private_key(
185 pem: &[u8],
186) -> Result<rustls::pki_types::PrivateKeyDer<'static>> {
187 use rustls::pki_types::pem::PemObject as _;
188 let mut cursor = std::io::Cursor::new(pem);
189 let key = rustls::pki_types::PrivateKeyDer::from_pem_reader(&mut cursor)
190 .context("failed to parse TLS key PEM — expected PKCS#8, RSA, or SEC1")?;
191 Ok(key)
192}
193
194#[derive(Debug)]
198pub struct FingerprintAllowlistVerifier {
199 pub allowlist: HashSet<[u8; 32]>,
200}
201
202impl rustls::server::danger::ClientCertVerifier for FingerprintAllowlistVerifier {
203 fn offer_client_auth(&self) -> bool {
204 true
205 }
206
207 fn client_auth_mandatory(&self) -> bool {
208 true
209 }
210
211 fn root_hint_subjects(&self) -> &[rustls::DistinguishedName] {
212 &[]
213 }
214
215 fn verify_client_cert(
216 &self,
217 end_entity: &rustls::pki_types::CertificateDer<'_>,
218 _intermediates: &[rustls::pki_types::CertificateDer<'_>],
219 _now: rustls::pki_types::UnixTime,
220 ) -> std::result::Result<rustls::server::danger::ClientCertVerified, rustls::Error> {
221 use sha2::{Digest, Sha256};
222 let fp: [u8; 32] = Sha256::digest(end_entity.as_ref()).into();
223 if self.allowlist.contains(&fp) {
224 Ok(rustls::server::danger::ClientCertVerified::assertion())
225 } else {
226 Err(rustls::Error::General(format!(
227 "client cert fingerprint {} not in mTLS allowlist",
228 hex_short(&fp)
229 )))
230 }
231 }
232
233 fn verify_tls12_signature(
234 &self,
235 message: &[u8],
236 cert: &rustls::pki_types::CertificateDer<'_>,
237 dss: &rustls::DigitallySignedStruct,
238 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
239 rustls::crypto::verify_tls12_signature(
240 message,
241 cert,
242 dss,
243 &rustls::crypto::ring::default_provider().signature_verification_algorithms,
244 )
245 }
246
247 fn verify_tls13_signature(
248 &self,
249 message: &[u8],
250 cert: &rustls::pki_types::CertificateDer<'_>,
251 dss: &rustls::DigitallySignedStruct,
252 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
253 rustls::crypto::verify_tls13_signature(
254 message,
255 cert,
256 dss,
257 &rustls::crypto::ring::default_provider().signature_verification_algorithms,
258 )
259 }
260
261 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
262 rustls::crypto::ring::default_provider()
263 .signature_verification_algorithms
264 .supported_schemes()
265 }
266}
267
268pub fn hex_short(fp: &[u8; 32]) -> String {
269 use std::fmt::Write as _;
270 let mut s = String::with_capacity(12);
271 for b in &fp[..6] {
272 let _ = write!(s, "{b:02x}");
273 }
274 s.push('…');
275 s
276}
277
278pub async fn build_rustls_client_config(
285 cert_path: &Path,
286 key_path: &Path,
287) -> Result<rustls::ClientConfig> {
288 let cert_pem = tokio::fs::read(cert_path)
289 .await
290 .with_context(|| format!("failed to read client cert from {}", cert_path.display()))?;
291 let key_pem = tokio::fs::read(key_path)
292 .await
293 .with_context(|| format!("failed to read client key from {}", key_path.display()))?;
294
295 let certs = rustls_pki_pem_iter_certs(&cert_pem)?;
296 let key = rustls_pki_pem_parse_private_key(&key_pem)?;
297
298 let config = rustls::ClientConfig::builder()
303 .dangerous()
304 .with_custom_certificate_verifier(Arc::new(DangerousAnyServerVerifier))
305 .with_client_auth_cert(certs, key)
306 .context("failed to build rustls ClientConfig with client cert")?;
307 Ok(config)
308}
309
310#[derive(Debug)]
314pub struct DangerousAnyServerVerifier;
315
316impl rustls::client::danger::ServerCertVerifier for DangerousAnyServerVerifier {
317 fn verify_server_cert(
318 &self,
319 _end_entity: &rustls::pki_types::CertificateDer<'_>,
320 _intermediates: &[rustls::pki_types::CertificateDer<'_>],
321 _server_name: &rustls::pki_types::ServerName<'_>,
322 _ocsp_response: &[u8],
323 _now: rustls::pki_types::UnixTime,
324 ) -> std::result::Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
325 Ok(rustls::client::danger::ServerCertVerified::assertion())
326 }
327
328 fn verify_tls12_signature(
329 &self,
330 message: &[u8],
331 cert: &rustls::pki_types::CertificateDer<'_>,
332 dss: &rustls::DigitallySignedStruct,
333 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
334 rustls::crypto::verify_tls12_signature(
335 message,
336 cert,
337 dss,
338 &rustls::crypto::ring::default_provider().signature_verification_algorithms,
339 )
340 }
341
342 fn verify_tls13_signature(
343 &self,
344 message: &[u8],
345 cert: &rustls::pki_types::CertificateDer<'_>,
346 dss: &rustls::DigitallySignedStruct,
347 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
348 rustls::crypto::verify_tls13_signature(
349 message,
350 cert,
351 dss,
352 &rustls::crypto::ring::default_provider().signature_verification_algorithms,
353 )
354 }
355
356 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
357 rustls::crypto::ring::default_provider()
358 .signature_verification_algorithms
359 .supported_schemes()
360 }
361}
362
363#[cfg(test)]
369mod tests {
370 use super::*;
371 use rustls::server::danger::ClientCertVerifier;
372
373 fn write_tmp(body: &str) -> tempfile::NamedTempFile {
376 let tmp = tempfile::NamedTempFile::new().unwrap();
377 std::fs::write(tmp.path(), body).unwrap();
378 tmp
379 }
380
381 #[tokio::test]
386 async fn test_allowlist_empty_file_errors() {
387 let tmp = write_tmp("");
391 let set = load_fingerprint_allowlist(tmp.path()).await.unwrap();
392 assert!(set.is_empty());
393 }
394
395 #[tokio::test]
396 async fn test_allowlist_only_comments_errors() {
397 let tmp = write_tmp("# header\n# more\n # indented\n");
400 let set = load_fingerprint_allowlist(tmp.path()).await.unwrap();
401 assert!(set.is_empty());
402 }
403
404 #[tokio::test]
405 async fn test_allowlist_single_valid_fp() {
406 let fp = "a".repeat(64);
407 let tmp = write_tmp(&format!("{fp}\n"));
408 let set = load_fingerprint_allowlist(tmp.path()).await.unwrap();
409 assert_eq!(set.len(), 1);
410 assert!(set.contains(&[0xaa; 32]));
411 }
412
413 #[tokio::test]
414 async fn test_allowlist_with_colons() {
415 let fp = format!("{}:{}", "b".repeat(32), "b".repeat(32));
416 let tmp = write_tmp(&format!("{fp}\n"));
417 let set = load_fingerprint_allowlist(tmp.path()).await.unwrap();
418 assert_eq!(set.len(), 1);
419 assert!(set.contains(&[0xbb; 32]));
420 }
421
422 #[tokio::test]
423 async fn test_allowlist_sha256_prefix() {
424 let fp = format!("sha256:{}", "c".repeat(64));
425 let tmp = write_tmp(&format!("{fp}\n"));
426 let set = load_fingerprint_allowlist(tmp.path()).await.unwrap();
427 assert_eq!(set.len(), 1);
428 assert!(set.contains(&[0xcc; 32]));
429 }
430
431 #[tokio::test]
433 async fn test_allowlist_inline_comment() {
434 let fp = "d".repeat(64);
435 let body = format!("{fp} # node-1 mTLS\n");
436 let tmp = write_tmp(&body);
437 let set = load_fingerprint_allowlist(tmp.path()).await.unwrap();
438 assert_eq!(set.len(), 1);
439 assert!(set.contains(&[0xdd; 32]));
440 }
441
442 #[tokio::test]
443 async fn test_allowlist_too_short_errors() {
444 let tmp = write_tmp(&"a".repeat(63));
445 let err = load_fingerprint_allowlist(tmp.path()).await.unwrap_err();
446 assert!(
447 err.to_string().contains("expected 64 hex chars"),
448 "got: {err}"
449 );
450 }
451
452 #[tokio::test]
453 async fn test_allowlist_too_long_errors() {
454 let tmp = write_tmp(&"a".repeat(65));
455 let err = load_fingerprint_allowlist(tmp.path()).await.unwrap_err();
456 assert!(
457 err.to_string().contains("expected 64 hex chars"),
458 "got: {err}"
459 );
460 }
461
462 #[tokio::test]
463 async fn test_allowlist_invalid_hex_errors() {
464 let mut s = "a".repeat(63);
466 s.push('z');
467 let tmp = write_tmp(&s);
468 let err = load_fingerprint_allowlist(tmp.path()).await.unwrap_err();
469 assert!(
470 err.to_string().contains("unexpected character"),
471 "got: {err}"
472 );
473 }
474
475 #[tokio::test]
478 async fn test_allowlist_embedded_whitespace_errors() {
479 let body = format!("{} {}\n", "a".repeat(32), "a".repeat(32));
480 let tmp = write_tmp(&body);
481 let err = load_fingerprint_allowlist(tmp.path()).await.unwrap_err();
482 assert!(
483 err.to_string().contains("unexpected character"),
484 "got: {err}"
485 );
486 }
487
488 #[tokio::test]
489 async fn test_allowlist_tab_in_hex_errors() {
490 let body = format!("{}\t{}\n", "a".repeat(32), "a".repeat(32));
491 let tmp = write_tmp(&body);
492 let err = load_fingerprint_allowlist(tmp.path()).await.unwrap_err();
493 assert!(
494 err.to_string().contains("unexpected character"),
495 "got: {err}"
496 );
497 }
498
499 #[tokio::test]
500 async fn test_allowlist_blank_lines_skipped() {
501 let fp = "a".repeat(64);
502 let body = format!("\n\n \n{fp}\n\n \n");
503 let tmp = write_tmp(&body);
504 let set = load_fingerprint_allowlist(tmp.path()).await.unwrap();
505 assert_eq!(set.len(), 1);
506 }
507
508 #[tokio::test]
509 async fn test_allowlist_multiple_entries() {
510 let fp_a = "a".repeat(64);
511 let fp_b = "b".repeat(64);
512 let fp_c = format!("{}:{}", "c".repeat(32), "c".repeat(32));
513 let body = format!(
514 "# header\n\
515 {fp_a}\n\
516 sha256:{fp_b}\n\
517 {fp_c}\n"
518 );
519 let tmp = write_tmp(&body);
520 let set = load_fingerprint_allowlist(tmp.path()).await.unwrap();
521 assert_eq!(set.len(), 3);
522 assert!(set.contains(&[0xaa; 32]));
523 assert!(set.contains(&[0xbb; 32]));
524 assert!(set.contains(&[0xcc; 32]));
525 }
526
527 #[tokio::test]
528 async fn test_allowlist_duplicate_entries_dedup() {
529 let fp = "e".repeat(64);
530 let body = format!("{fp}\n{fp}\n{fp}\n");
531 let tmp = write_tmp(&body);
532 let set = load_fingerprint_allowlist(tmp.path()).await.unwrap();
533 assert_eq!(set.len(), 1);
535 assert!(set.contains(&[0xee; 32]));
536 }
537
538 #[test]
543 fn test_pem_iter_certs_empty_errors() {
544 let err = rustls_pki_pem_iter_certs(b"").unwrap_err();
545 assert!(
548 err.to_string().contains("no certificates")
549 || err.to_string().contains("failed to parse"),
550 "got: {err}"
551 );
552 }
553
554 #[test]
555 fn test_pem_iter_certs_garbage_errors() {
556 let err = rustls_pki_pem_iter_certs(b"not a pem file\n").unwrap_err();
557 assert!(
558 err.to_string().contains("no certificates")
559 || err.to_string().contains("failed to parse"),
560 "got: {err}"
561 );
562 }
563
564 #[test]
565 fn test_pem_iter_certs_single_cert() {
566 let pem = std::fs::read("tests/fixtures/tls/valid_cert.pem")
567 .expect("regenerate fixtures via tests/fixtures/tls/regenerate.sh");
568 let certs = rustls_pki_pem_iter_certs(&pem).unwrap();
569 assert_eq!(
570 certs.len(),
571 1,
572 "expected exactly one cert in valid_cert.pem"
573 );
574 }
575
576 #[test]
577 fn test_pem_iter_certs_chain() {
578 let pem = std::fs::read("tests/fixtures/tls/cert_chain.pem")
579 .expect("regenerate fixtures via tests/fixtures/tls/regenerate.sh");
580 let certs = rustls_pki_pem_iter_certs(&pem).unwrap();
581 assert!(
582 certs.len() >= 2,
583 "expected leaf + intermediate, got {}",
584 certs.len()
585 );
586 }
587
588 #[test]
589 fn test_pem_parse_pkcs8_key() {
590 let pem = std::fs::read("tests/fixtures/tls/valid_key_pkcs8.pem")
591 .expect("regenerate fixtures via tests/fixtures/tls/regenerate.sh");
592 let key = rustls_pki_pem_parse_private_key(&pem).unwrap();
593 let _ = key;
596 }
597
598 #[test]
599 fn test_pem_parse_rsa_key() {
600 let pem = std::fs::read("tests/fixtures/tls/valid_key_rsa.pem")
601 .expect("regenerate fixtures via tests/fixtures/tls/regenerate.sh");
602 let key = rustls_pki_pem_parse_private_key(&pem).unwrap();
603 let _ = key;
604 }
605
606 #[test]
607 fn test_pem_parse_sec1_key() {
608 let pem = std::fs::read("tests/fixtures/tls/valid_key_sec1.pem")
609 .expect("regenerate fixtures via tests/fixtures/tls/regenerate.sh");
610 let key = rustls_pki_pem_parse_private_key(&pem).unwrap();
611 let _ = key;
612 }
613
614 #[test]
615 fn test_pem_parse_garbage_errors() {
616 let err = rustls_pki_pem_parse_private_key(b"not a pem file\n").unwrap_err();
617 assert!(err.to_string().contains("failed to parse TLS key PEM"));
618 }
619
620 #[test]
625 fn test_hex_short_format() {
626 let mut fp = [0u8; 32];
628 fp[0] = 0xde;
629 fp[1] = 0xad;
630 fp[2] = 0xbe;
631 fp[3] = 0xef;
632 fp[4] = 0x12;
633 fp[5] = 0x34;
634 for (i, slot) in fp.iter_mut().enumerate().skip(6) {
636 *slot = (i as u8).wrapping_mul(7);
637 }
638 assert_eq!(hex_short(&fp), "deadbeef1234…");
639 }
640
641 #[test]
642 fn test_hex_short_truncates_to_6_bytes() {
643 let fp = [0xff; 32];
644 let s = hex_short(&fp);
645 let hex_only = s.trim_end_matches('…');
647 assert_eq!(hex_only.len(), 12, "expected 6 bytes = 12 hex chars");
648 assert_eq!(hex_only, "ffffffffffff");
649 }
650
651 #[test]
656 fn test_verifier_accepts_allowlisted_fp() {
657 use sha2::{Digest, Sha256};
658 let fake_cert = b"fake certificate DER bytes for fingerprint test";
662 let fp: [u8; 32] = Sha256::digest(fake_cert).into();
663 let mut allowlist = HashSet::new();
664 allowlist.insert(fp);
665 let verifier = FingerprintAllowlistVerifier { allowlist };
666 let cert = rustls::pki_types::CertificateDer::from(fake_cert.to_vec());
667 let now = rustls::pki_types::UnixTime::now();
668 let result = verifier.verify_client_cert(&cert, &[], now);
669 assert!(result.is_ok(), "expected accept, got: {result:?}");
670 }
671
672 #[test]
673 fn test_verifier_rejects_unknown_fp() {
674 let allowlist = HashSet::new();
675 let verifier = FingerprintAllowlistVerifier { allowlist };
676 let cert = rustls::pki_types::CertificateDer::from(b"unknown".to_vec());
677 let now = rustls::pki_types::UnixTime::now();
678 let err = verifier.verify_client_cert(&cert, &[], now).unwrap_err();
679 assert!(
680 err.to_string().contains("not in mTLS allowlist"),
681 "got: {err}"
682 );
683 }
684
685 #[test]
686 fn test_verifier_error_includes_truncated_fp() {
687 let allowlist = HashSet::new();
688 let verifier = FingerprintAllowlistVerifier { allowlist };
689 let cert_bytes = b"some cert that won't be in the allowlist";
690 let cert = rustls::pki_types::CertificateDer::from(cert_bytes.to_vec());
691 let now = rustls::pki_types::UnixTime::now();
692 let err = verifier.verify_client_cert(&cert, &[], now).unwrap_err();
693 let msg = err.to_string();
694 use sha2::{Digest, Sha256};
696 let fp: [u8; 32] = Sha256::digest(cert_bytes).into();
697 let short = hex_short(&fp);
698 assert!(msg.contains(&short), "expected fp {short} in: {msg}");
699 assert!(msg.contains('…'), "expected truncation marker in: {msg}");
702 }
703
704 #[test]
705 fn test_verifier_offer_client_auth_returns_true() {
706 let verifier = FingerprintAllowlistVerifier {
707 allowlist: HashSet::new(),
708 };
709 assert!(verifier.offer_client_auth());
710 }
711
712 #[test]
713 fn test_verifier_client_auth_mandatory_returns_true() {
714 let verifier = FingerprintAllowlistVerifier {
715 allowlist: HashSet::new(),
716 };
717 assert!(verifier.client_auth_mandatory());
718 assert_eq!(verifier.root_hint_subjects().len(), 0);
721 }
722
723 fn bogus_dss() -> rustls::DigitallySignedStruct {
729 use rustls::internal::msgs::codec::{Codec, Reader};
730 let mut wire = Vec::with_capacity(4 + 64);
732 wire.extend_from_slice(&[0x08, 0x07]);
733 wire.extend_from_slice(&[0x00, 0x40]);
734 wire.extend_from_slice(&[0u8; 64]);
735 let mut reader = Reader::init(&wire);
736 rustls::DigitallySignedStruct::read(&mut reader)
737 .expect("hand-rolled wire bytes must round-trip the Codec")
738 }
739
740 #[test]
746 fn test_verifier_signature_methods_run() {
747 let _ = rustls::crypto::ring::default_provider().install_default();
748 let verifier = FingerprintAllowlistVerifier {
749 allowlist: HashSet::new(),
750 };
751 let schemes = verifier.supported_verify_schemes();
753 assert!(
754 !schemes.is_empty(),
755 "ring provider must expose at least one signature scheme"
756 );
757
758 let cert = rustls::pki_types::CertificateDer::from(vec![0u8; 32]);
760 let dss = bogus_dss();
761 let _ = verifier.verify_tls12_signature(b"bogus message", &cert, &dss);
762 let _ = verifier.verify_tls13_signature(b"bogus message", &cert, &dss);
763 }
764
765 #[test]
772 fn test_dangerous_any_server_verifier_accepts_any_cert() {
773 use rustls::client::danger::ServerCertVerifier;
774 let _ = rustls::crypto::ring::default_provider().install_default();
775 let verifier = DangerousAnyServerVerifier;
776 let cert = rustls::pki_types::CertificateDer::from(b"any bytes here".to_vec());
777 let server_name = rustls::pki_types::ServerName::try_from("example.com").unwrap();
778 let now = rustls::pki_types::UnixTime::now();
779 let result = verifier.verify_server_cert(&cert, &[], &server_name, &[], now);
780 assert!(
781 result.is_ok(),
782 "DangerousAnyServerVerifier accepts any cert (compensating mTLS control)"
783 );
784 }
785
786 #[test]
787 fn test_dangerous_any_server_verifier_signature_methods_run() {
788 use rustls::client::danger::ServerCertVerifier;
789 let _ = rustls::crypto::ring::default_provider().install_default();
790 let verifier = DangerousAnyServerVerifier;
791 let schemes = verifier.supported_verify_schemes();
792 assert!(!schemes.is_empty());
793
794 let cert = rustls::pki_types::CertificateDer::from(vec![0u8; 32]);
795 let dss = bogus_dss();
796 let _ = verifier.verify_tls12_signature(b"bogus message", &cert, &dss);
797 let _ = verifier.verify_tls13_signature(b"bogus message", &cert, &dss);
798 }
799
800 #[tokio::test]
807 async fn test_build_rustls_client_config_happy_path() {
808 let _ = rustls::crypto::ring::default_provider().install_default();
809 let cert = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
810 .join("tests/fixtures/tls/valid_cert.pem");
811 let key = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
812 .join("tests/fixtures/tls/valid_key_pkcs8.pem");
813 let config = build_rustls_client_config(&cert, &key)
814 .await
815 .expect("client config build with valid cert+key");
816 drop(config);
819 }
820
821 #[tokio::test]
822 async fn test_build_rustls_client_config_missing_cert_errors() {
823 let cert = std::path::PathBuf::from("/does/not/exist/cert.pem");
824 let key = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
825 .join("tests/fixtures/tls/valid_key_pkcs8.pem");
826 let err = build_rustls_client_config(&cert, &key)
827 .await
828 .expect_err("missing client cert must error");
829 assert!(
830 err.to_string().contains("failed to read client cert"),
831 "got: {err}"
832 );
833 }
834}