1use crate::crypto;
2use rustls::pki_types::pem::PemObject;
3use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime};
4use std::path::{Path, PathBuf};
5use std::sync::Arc;
6use std::{fs, io};
7
8#[cfg(all(
9 any(feature = "quinn", feature = "noq", feature = "quiche"),
10 any(feature = "aws-lc-rs", feature = "ring")
11))]
12use rustls::pki_types::PrivatePkcs8KeyDer;
13#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
14use std::sync::RwLock;
15
16#[derive(Debug, thiserror::Error)]
21#[non_exhaustive]
22pub enum Error {
23 #[error("failed to open certificate file")]
24 Open(#[source] std::io::Error),
25
26 #[error("failed to read file")]
27 ReadFile(#[source] std::io::Error),
28
29 #[error("failed to read certificates")]
30 Read(#[source] rustls::pki_types::pem::Error),
31
32 #[error("failed to parse private key")]
33 Key(#[source] rustls::pki_types::pem::Error),
34
35 #[error("no certificates found")]
36 Empty,
37
38 #[error("no roots found in {}", .0.display())]
39 EmptyRoots(PathBuf),
40
41 #[error(
42 "no trusted roots: provide --client-tls-root, enable --client-tls-system-roots, or use --client-tls-fingerprint / --client-tls-disable-verify"
43 )]
44 NoRoots,
45
46 #[error("invalid TLS fingerprint (expected hex-encoded SHA-256)")]
47 Fingerprint(#[source] hex::FromHexError),
48
49 #[error("invalid TLS fingerprint length: expected 32 bytes (SHA-256), got {0}")]
50 FingerprintLength(usize),
51
52 #[error(
53 "--client-tls-fingerprint cannot be combined with --client-tls-root or --client-tls-system-roots: fingerprint pinning bypasses CA verification"
54 )]
55 FingerprintWithRoots,
56
57 #[error("failed to add root certificate")]
58 AddRoot(#[source] rustls::Error),
59
60 #[error("failed to configure client certificate")]
61 ClientAuth(#[source] rustls::Error),
62
63 #[error("both --client-tls-cert and --client-tls-key must be provided")]
64 IncompleteClientAuth,
65
66 #[error("must provide both cert and key")]
67 CertKeyCountMismatch,
68
69 #[error("must provide at least one cert/key pair or generate entry")]
70 NoCertSource,
71
72 #[error("private key {} doesn't match certificate {}", key.display(), cert.display())]
73 KeyMismatch {
74 key: PathBuf,
75 cert: PathBuf,
76 #[source]
77 source: rustls::Error,
78 },
79
80 #[error(transparent)]
81 Rustls(#[from] rustls::Error),
82
83 #[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
84 #[error("failed to build client certificate verifier")]
85 ClientVerifier(#[source] rustls::server::VerifierBuilderError),
86
87 #[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
88 #[error(transparent)]
89 Rcgen(#[from] rcgen::Error),
90
91 #[error("no crypto provider available; enable aws-lc-rs or ring feature")]
92 NoCryptoProvider,
93}
94
95pub type Result<T> = std::result::Result<T, Error>;
97
98pub(crate) fn read_certs(path: &Path) -> Result<Vec<CertificateDer<'static>>> {
100 let file = fs::File::open(path).map_err(Error::Open)?;
101 let mut reader = io::BufReader::new(file);
102 CertificateDer::pem_reader_iter(&mut reader)
103 .collect::<std::result::Result<_, _>>()
104 .map_err(Error::Read)
105}
106
107#[serde_with::serde_as]
111#[derive(Clone, Default, Debug, clap::Args, serde::Serialize, serde::Deserialize)]
112#[serde(default, deny_unknown_fields)]
113#[group(id = "tls-client")]
114#[non_exhaustive]
115pub struct Client {
116 #[serde(skip_serializing_if = "Vec::is_empty")]
126 #[arg(id = "client-tls-root", long = "client-tls-root", env = "MOQ_CLIENT_TLS_ROOT")]
127 #[serde_as(as = "serde_with::OneOrMany<_>")]
128 pub root: Vec<PathBuf>,
129
130 #[serde(skip_serializing_if = "Option::is_none")]
137 #[arg(
138 id = "client-tls-system-roots",
139 long = "client-tls-system-roots",
140 env = "MOQ_CLIENT_TLS_SYSTEM_ROOTS",
141 default_missing_value = "true",
142 num_args = 0..=1,
143 require_equals = true,
144 value_parser = clap::value_parser!(bool),
145 )]
146 pub system_roots: Option<bool>,
147
148 #[serde(skip_serializing_if = "Vec::is_empty")]
159 #[arg(
160 id = "client-tls-fingerprint",
161 long = "client-tls-fingerprint",
162 env = "MOQ_CLIENT_TLS_FINGERPRINT"
163 )]
164 #[serde_as(as = "serde_with::OneOrMany<_>")]
165 pub fingerprint: Vec<String>,
166
167 #[serde(skip_serializing_if = "Option::is_none")]
172 #[arg(id = "client-tls-cert", long = "client-tls-cert", env = "MOQ_CLIENT_TLS_CERT")]
173 pub cert: Option<PathBuf>,
174
175 #[serde(skip_serializing_if = "Option::is_none")]
180 #[arg(id = "client-tls-key", long = "client-tls-key", env = "MOQ_CLIENT_TLS_KEY")]
181 pub key: Option<PathBuf>,
182
183 #[serde(skip_serializing_if = "Option::is_none")]
187 #[arg(
188 id = "client-tls-disable-verify",
189 long = "client-tls-disable-verify",
190 env = "MOQ_CLIENT_TLS_DISABLE_VERIFY",
191 default_missing_value = "true",
192 num_args = 0..=1,
193 require_equals = true,
194 value_parser = clap::value_parser!(bool),
195 )]
196 pub disable_verify: Option<bool>,
197
198 #[command(flatten)]
202 #[serde(skip)]
203 deprecated: Deprecated,
204}
205
206#[derive(Clone, Default, Debug, clap::Args)]
211struct Deprecated {
212 #[arg(long = "tls-root", hide = true)]
213 root: Vec<PathBuf>,
214
215 #[arg(
216 long = "tls-system-roots",
217 hide = true,
218 default_missing_value = "true",
219 num_args = 0..=1,
220 require_equals = true,
221 value_parser = clap::value_parser!(bool),
222 )]
223 system_roots: Option<bool>,
224
225 #[arg(long = "tls-fingerprint", hide = true)]
226 fingerprint: Vec<String>,
227
228 #[arg(
229 long = "tls-disable-verify",
230 hide = true,
231 default_missing_value = "true",
232 num_args = 0..=1,
233 require_equals = true,
234 value_parser = clap::value_parser!(bool),
235 )]
236 disable_verify: Option<bool>,
237}
238
239#[derive(Clone)]
246pub(crate) enum Verification {
247 Disabled,
249
250 Fingerprints(Vec<[u8; 32]>),
253
254 Roots(Vec<CertificateDer<'static>>),
257}
258
259impl Client {
260 pub(crate) fn warn_deprecated(&self) {
263 if !self.deprecated.root.is_empty() {
264 tracing::warn!("--tls-root is deprecated; use --client-tls-root");
265 }
266 if self.deprecated.system_roots.is_some() {
267 tracing::warn!("--tls-system-roots is deprecated; use --client-tls-system-roots");
268 }
269 if !self.deprecated.fingerprint.is_empty() {
270 tracing::warn!("--tls-fingerprint is deprecated; use --client-tls-fingerprint");
271 }
272 if self.deprecated.disable_verify.is_some() {
273 tracing::warn!("--tls-disable-verify is deprecated; use --client-tls-disable-verify");
274 }
275 }
276
277 pub(crate) fn effective_root(&self) -> Vec<PathBuf> {
279 let mut root = self.root.clone();
280 root.extend(self.deprecated.root.iter().cloned());
281 root
282 }
283
284 pub(crate) fn effective_fingerprint(&self) -> Vec<String> {
286 let mut fp = self.fingerprint.clone();
287 fp.extend(self.deprecated.fingerprint.iter().cloned());
288 fp
289 }
290
291 pub(crate) fn effective_system_roots(&self) -> Option<bool> {
293 self.system_roots.or(self.deprecated.system_roots)
294 }
295
296 pub(crate) fn effective_disable_verify(&self) -> Option<bool> {
298 self.disable_verify.or(self.deprecated.disable_verify)
299 }
300
301 pub(crate) fn verification(&self) -> Result<Verification> {
312 self.warn_deprecated();
313
314 if self.effective_disable_verify().unwrap_or_default() {
315 return Ok(Verification::Disabled);
316 }
317
318 let fingerprints = self.fingerprints()?;
319 if !fingerprints.is_empty() {
320 if !self.effective_root().is_empty() || self.effective_system_roots() == Some(true) {
321 return Err(Error::FingerprintWithRoots);
322 }
323 return Ok(Verification::Fingerprints(fingerprints));
324 }
325
326 let root = self.effective_root();
327 let system_roots = self.effective_system_roots().unwrap_or(root.is_empty());
330
331 let mut roots = Vec::new();
332 if system_roots {
333 let native = rustls_native_certs::load_native_certs();
334 for err in native.errors {
335 tracing::warn!(%err, "failed to load root cert");
336 }
337 roots.extend(native.certs);
338 }
339 for root in &root {
340 let certs = read_certs(root)?;
341 if certs.is_empty() {
342 return Err(Error::EmptyRoots(root.clone()));
343 }
344 roots.extend(certs);
345 }
346
347 if roots.is_empty() {
350 return Err(Error::NoRoots);
351 }
352
353 Ok(Verification::Roots(roots))
354 }
355
356 pub(crate) fn allows_http_bootstrap(&self) -> bool {
365 self.effective_fingerprint().is_empty() && !self.effective_disable_verify().unwrap_or_default()
366 }
367
368 fn fingerprints(&self) -> Result<Vec<[u8; 32]>> {
370 self.effective_fingerprint()
371 .iter()
372 .map(|fp| {
373 let bytes = hex::decode(fp.trim()).map_err(Error::Fingerprint)?;
374 bytes.try_into().map_err(|v: Vec<u8>| Error::FingerprintLength(v.len()))
375 })
376 .collect()
377 }
378
379 pub fn build(&self) -> Result<rustls::ClientConfig> {
384 let provider = crypto::provider();
385 let verification = self.verification()?;
386
387 let mut roots = rustls::RootCertStore::empty();
388 if let Verification::Roots(certs) = &verification {
389 for cert in certs {
390 roots.add(cert.clone()).map_err(Error::AddRoot)?;
391 }
392 }
393
394 let builder = rustls::ClientConfig::builder_with_provider(provider.clone())
397 .with_protocol_versions(&[&rustls::version::TLS13, &rustls::version::TLS12])?
398 .with_root_certificates(roots);
399
400 let mut tls = match (&self.cert, &self.key) {
401 (Some(cert_path), Some(key_path)) => {
402 let cert_pem = fs::read(cert_path).map_err(Error::ReadFile)?;
403 let chain: Vec<CertificateDer<'static>> = CertificateDer::pem_slice_iter(&cert_pem)
404 .collect::<std::result::Result<_, _>>()
405 .map_err(Error::Read)?;
406 if chain.is_empty() {
407 return Err(Error::Empty);
408 }
409 let key_pem = fs::read(key_path).map_err(Error::ReadFile)?;
410 let key = PrivateKeyDer::from_pem_slice(&key_pem).map_err(Error::Key)?;
411 builder.with_client_auth_cert(chain, key).map_err(Error::ClientAuth)?
412 }
413 (None, None) => builder.with_no_client_auth(),
414 _ => return Err(Error::IncompleteClientAuth),
415 };
416
417 match verification {
418 Verification::Disabled => {
419 tracing::warn!(
420 "TLS server certificate verification is disabled; A man-in-the-middle attack is possible."
421 );
422 tls.dangerous()
423 .set_certificate_verifier(Arc::new(NoCertificateVerification(provider)));
424 }
425 Verification::Fingerprints(fingerprints) => {
426 let fingerprints = fingerprints.into_iter().map(|fp| fp.to_vec()).collect();
427 let verifier = FingerprintVerifier::new(provider, fingerprints);
428 tls.dangerous().set_certificate_verifier(Arc::new(verifier));
429 }
430 Verification::Roots(_) => {}
432 }
433
434 Ok(tls)
435 }
436}
437
438#[serde_with::serde_as]
447#[derive(clap::Args, Clone, Default, Debug, serde::Serialize, serde::Deserialize)]
448#[serde(deny_unknown_fields)]
449#[group(id = "tls-server")]
450#[non_exhaustive]
451pub struct Server {
452 #[arg(long = "tls-cert", id = "tls-cert", env = "MOQ_SERVER_TLS_CERT")]
454 #[serde(default, skip_serializing_if = "Vec::is_empty")]
455 #[serde_as(as = "serde_with::OneOrMany<_>")]
456 pub cert: Vec<PathBuf>,
457
458 #[arg(long = "tls-key", id = "tls-key", env = "MOQ_SERVER_TLS_KEY")]
460 #[serde(default, skip_serializing_if = "Vec::is_empty")]
461 #[serde_as(as = "serde_with::OneOrMany<_>")]
462 pub key: Vec<PathBuf>,
463
464 #[arg(
467 long = "tls-generate",
468 id = "tls-generate",
469 value_delimiter = ',',
470 env = "MOQ_SERVER_TLS_GENERATE"
471 )]
472 #[serde(default, skip_serializing_if = "Vec::is_empty")]
473 #[serde_as(as = "serde_with::OneOrMany<_>")]
474 pub generate: Vec<String>,
475
476 #[arg(
488 long = "server-tls-root",
489 id = "server-tls-root",
490 value_delimiter = ',',
491 env = "MOQ_SERVER_TLS_ROOT"
492 )]
493 #[serde(default, skip_serializing_if = "Vec::is_empty")]
494 #[serde_as(as = "serde_with::OneOrMany<_>")]
495 pub root: Vec<PathBuf>,
496}
497
498impl Server {
499 pub fn load_roots(&self) -> Result<rustls::RootCertStore> {
501 let mut roots = rustls::RootCertStore::empty();
502 for path in &self.root {
503 let certs = read_certs(path)?;
504 if certs.is_empty() {
505 return Err(Error::Empty);
506 }
507 for cert in certs {
508 roots.add(cert).map_err(Error::AddRoot)?;
509 }
510 }
511 Ok(roots)
512 }
513
514 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
523 pub fn server_config(&self, alpn: Vec<Vec<u8>>) -> Result<Arc<rustls::ServerConfig>> {
524 server_config(self, alpn)
525 }
526}
527
528#[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
530fn server_config(config: &Server, alpn: Vec<Vec<u8>>) -> Result<Arc<rustls::ServerConfig>> {
531 let provider = crypto::provider();
532
533 let certs = ServeCerts::new(provider.clone());
534 certs.load_certs(config)?;
535 let certs = Arc::new(certs);
536
537 let builder =
539 rustls::ServerConfig::builder_with_provider(provider.clone()).with_safe_default_protocol_versions()?;
540
541 let mut tls = if config.root.is_empty() {
542 builder.with_no_client_auth().with_cert_resolver(certs)
543 } else {
544 let roots = config.load_roots()?;
545 let verifier = rustls::server::WebPkiClientVerifier::builder_with_provider(Arc::new(roots), provider)
546 .allow_unauthenticated()
547 .build()
548 .map_err(Error::ClientVerifier)?;
549 builder.with_client_cert_verifier(verifier).with_cert_resolver(certs)
550 };
551
552 tls.alpn_protocols = alpn;
553 Ok(Arc::new(tls))
554}
555
556pub struct PeerIdentity {
563 chain: Vec<CertificateDer<'static>>,
564}
565
566impl PeerIdentity {
567 #[cfg(any(feature = "quinn", feature = "noq"))]
571 pub(crate) fn from_any(identity: Option<Box<dyn std::any::Any>>) -> Option<Self> {
572 let chain = identity?.downcast::<Vec<CertificateDer<'static>>>().ok()?;
573 Some(Self { chain: *chain })
574 }
575
576 pub fn chain(&self) -> &[CertificateDer<'static>] {
582 &self.chain
583 }
584
585 pub fn expiry(&self) -> Option<std::time::SystemTime> {
588 use std::time::{Duration, UNIX_EPOCH};
589
590 let leaf = self.chain.first()?;
591 let (_, cert) = x509_parser::parse_x509_certificate(leaf).ok()?;
592 let secs = u64::try_from(cert.validity().not_after.timestamp()).ok()?;
593 Some(UNIX_EPOCH + Duration::from_secs(secs))
594 }
595}
596
597#[derive(Debug)]
599pub struct Info {
600 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
601 pub(crate) certs: Vec<Arc<rustls::sign::CertifiedKey>>,
602 pub fingerprints: Vec<String>,
603}
604
605#[derive(Debug)]
608struct NoCertificateVerification(crypto::Provider);
609
610impl rustls::client::danger::ServerCertVerifier for NoCertificateVerification {
611 fn verify_server_cert(
612 &self,
613 _end_entity: &CertificateDer<'_>,
614 _intermediates: &[CertificateDer<'_>],
615 _server_name: &ServerName<'_>,
616 _ocsp: &[u8],
617 _now: UnixTime,
618 ) -> std::result::Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
619 Ok(rustls::client::danger::ServerCertVerified::assertion())
620 }
621
622 fn verify_tls12_signature(
623 &self,
624 message: &[u8],
625 cert: &CertificateDer<'_>,
626 dss: &rustls::DigitallySignedStruct,
627 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
628 rustls::crypto::verify_tls12_signature(message, cert, dss, &self.0.signature_verification_algorithms)
629 }
630
631 fn verify_tls13_signature(
632 &self,
633 message: &[u8],
634 cert: &CertificateDer<'_>,
635 dss: &rustls::DigitallySignedStruct,
636 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
637 rustls::crypto::verify_tls13_signature(message, cert, dss, &self.0.signature_verification_algorithms)
638 }
639
640 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
641 self.0.signature_verification_algorithms.supported_schemes()
642 }
643}
644
645#[derive(Debug)]
648pub(crate) struct FingerprintVerifier {
649 provider: crypto::Provider,
650 fingerprints: Vec<Vec<u8>>,
651}
652
653impl FingerprintVerifier {
654 pub fn new(provider: crypto::Provider, fingerprints: Vec<Vec<u8>>) -> Self {
655 Self { provider, fingerprints }
656 }
657}
658
659impl rustls::client::danger::ServerCertVerifier for FingerprintVerifier {
660 fn verify_server_cert(
661 &self,
662 end_entity: &CertificateDer<'_>,
663 _intermediates: &[CertificateDer<'_>],
664 _server_name: &ServerName<'_>,
665 _ocsp: &[u8],
666 _now: UnixTime,
667 ) -> std::result::Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
668 let fingerprint = crypto::sha256(&self.provider, end_entity);
669 if self.fingerprints.iter().any(|fp| fingerprint.as_ref() == fp.as_slice()) {
670 Ok(rustls::client::danger::ServerCertVerified::assertion())
671 } else {
672 Err(rustls::Error::General("fingerprint mismatch".into()))
673 }
674 }
675
676 fn verify_tls12_signature(
677 &self,
678 message: &[u8],
679 cert: &CertificateDer<'_>,
680 dss: &rustls::DigitallySignedStruct,
681 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
682 rustls::crypto::verify_tls12_signature(message, cert, dss, &self.provider.signature_verification_algorithms)
683 }
684
685 fn verify_tls13_signature(
686 &self,
687 message: &[u8],
688 cert: &CertificateDer<'_>,
689 dss: &rustls::DigitallySignedStruct,
690 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
691 rustls::crypto::verify_tls13_signature(message, cert, dss, &self.provider.signature_verification_algorithms)
692 }
693
694 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
695 self.provider.signature_verification_algorithms.supported_schemes()
696 }
697}
698
699#[cfg(test)]
700#[cfg(all(any(feature = "quinn", feature = "noq", feature = "quiche"), feature = "aws-lc-rs"))]
701mod tests {
702 use super::*;
703 use rustls::client::danger::ServerCertVerifier;
704 use rustls::pki_types::ServerName;
705
706 fn self_signed() -> CertificateDer<'static> {
707 let key = rcgen::KeyPair::generate().unwrap();
708 let params = rcgen::CertificateParams::new(vec!["localhost".to_string()]).unwrap();
709 params.self_signed(&key).unwrap().into()
710 }
711
712 #[cfg(any(feature = "quinn", feature = "noq"))]
713 #[test]
714 fn peer_identity_expiry_reads_not_after() {
715 let not_after = ::time::OffsetDateTime::from_unix_timestamp(2_000_000_000).unwrap();
717
718 let key = rcgen::KeyPair::generate().unwrap();
719 let mut params = rcgen::CertificateParams::new(vec!["localhost".to_string()]).unwrap();
720 params.not_after = not_after;
721 let cert: CertificateDer<'static> = params.self_signed(&key).unwrap().into();
722
723 let identity: Box<dyn std::any::Any> = Box::new(vec![cert]);
725 let parsed = PeerIdentity::from_any(Some(identity)).expect("chain parsed");
726 let expiry = parsed.expiry().expect("expiry parsed");
727 assert_eq!(
728 expiry.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(),
729 2_000_000_000
730 );
731 }
732
733 #[cfg(any(feature = "quinn", feature = "noq"))]
734 #[test]
735 fn peer_identity_none_without_chain() {
736 assert!(PeerIdentity::from_any(None).is_none());
737 let bogus: Box<dyn std::any::Any> = Box::new(42u32);
739 assert!(PeerIdentity::from_any(Some(bogus)).is_none());
740 }
741
742 #[test]
743 fn fingerprint_verifier_matches_and_rejects() {
744 let provider = crypto::provider();
745 let cert = self_signed();
746 let fingerprint = crypto::sha256(&provider, cert.as_ref()).as_ref().to_vec();
747
748 let name = ServerName::try_from("localhost").unwrap();
749 let now = UnixTime::now();
750
751 let verifier = FingerprintVerifier::new(provider.clone(), vec![fingerprint]);
752 assert!(verifier.verify_server_cert(&cert, &[], &name, &[], now).is_ok());
753
754 let other = self_signed();
756 assert!(verifier.verify_server_cert(&other, &[], &name, &[], now).is_err());
757 }
758
759 #[test]
760 fn build_installs_fingerprint_verifier() {
761 let cert = self_signed();
762 let fingerprint = hex::encode(crypto::sha256(&crypto::provider(), cert.as_ref()));
763
764 let config = Client {
766 fingerprint: vec![fingerprint],
767 ..Default::default()
768 };
769 assert!(config.build().is_ok());
770 }
771
772 #[test]
773 fn build_rejects_invalid_fingerprint_hex() {
774 let config = Client {
775 fingerprint: vec!["not-hex".to_string()],
776 ..Default::default()
777 };
778 assert!(matches!(config.build(), Err(Error::Fingerprint(_))));
779 }
780
781 #[test]
782 fn build_rejects_wrong_length_fingerprint() {
783 let config = Client {
785 fingerprint: vec!["abcd".to_string()],
786 ..Default::default()
787 };
788 assert!(matches!(config.build(), Err(Error::FingerprintLength(2))));
789 }
790
791 #[test]
792 fn build_rejects_no_roots() {
793 let config = Client {
796 system_roots: Some(false),
797 ..Default::default()
798 };
799 assert!(matches!(config.build(), Err(Error::NoRoots)));
800 }
801
802 #[test]
803 fn build_allows_no_roots_when_verification_overridden() {
804 let config = Client {
806 system_roots: Some(false),
807 disable_verify: Some(true),
808 ..Default::default()
809 };
810 assert!(config.build().is_ok());
811
812 let cert = self_signed();
814 let fingerprint = hex::encode(crypto::sha256(&crypto::provider(), cert.as_ref()));
815 let config = Client {
816 system_roots: Some(false),
817 fingerprint: vec![fingerprint],
818 ..Default::default()
819 };
820 assert!(config.build().is_ok());
821 }
822
823 #[test]
824 fn build_rejects_fingerprint_with_roots() {
825 let cert = self_signed();
826 let fingerprint = hex::encode(crypto::sha256(&crypto::provider(), cert.as_ref()));
827
828 let with_system = Client {
831 fingerprint: vec![fingerprint.clone()],
832 system_roots: Some(true),
833 ..Default::default()
834 };
835 assert!(matches!(with_system.build(), Err(Error::FingerprintWithRoots)));
836
837 let with_custom = Client {
840 fingerprint: vec![fingerprint],
841 root: vec![PathBuf::from("/does-not-exist.pem")],
842 ..Default::default()
843 };
844 assert!(matches!(with_custom.build(), Err(Error::FingerprintWithRoots)));
845 }
846}
847
848#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
851#[derive(Debug)]
852pub(crate) struct ServeCerts {
853 pub info: Arc<RwLock<Info>>,
854 provider: crypto::Provider,
855}
856
857#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
858impl ServeCerts {
859 pub fn new(provider: crypto::Provider) -> Self {
860 Self {
861 info: Arc::new(RwLock::new(Info {
862 certs: Vec::new(),
863 fingerprints: Vec::new(),
864 })),
865 provider,
866 }
867 }
868
869 pub fn load_certs(&self, config: &Server) -> Result<()> {
870 if config.cert.len() != config.key.len() {
871 return Err(Error::CertKeyCountMismatch);
872 }
873 if config.cert.is_empty() && config.generate.is_empty() {
874 return Err(Error::NoCertSource);
875 }
876
877 let mut certs = Vec::new();
878
879 for (cert, key) in config.cert.iter().zip(config.key.iter()) {
881 certs.push(Arc::new(self.load(cert, key)?));
882 }
883
884 if !config.generate.is_empty() {
886 certs.push(Arc::new(self.generate(&config.generate)?));
887 }
888
889 self.set_certs(certs);
890 Ok(())
891 }
892
893 fn load(&self, chain_path: &Path, key_path: &Path) -> Result<rustls::sign::CertifiedKey> {
895 let chain = read_certs(chain_path)?;
896 if chain.is_empty() {
897 return Err(Error::Empty);
898 }
899
900 let key = PrivateKeyDer::from_pem_file(key_path).map_err(Error::Key)?;
902 let key = self.provider.key_provider.load_private_key(key)?;
903
904 let certified_key = rustls::sign::CertifiedKey::new(chain, key);
905
906 certified_key.keys_match().map_err(|source| Error::KeyMismatch {
907 key: key_path.to_path_buf(),
908 cert: chain_path.to_path_buf(),
909 source,
910 })?;
911
912 Ok(certified_key)
913 }
914
915 #[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
916 fn generate(&self, hostnames: &[String]) -> Result<rustls::sign::CertifiedKey> {
917 let key_pair = rcgen::KeyPair::generate()?;
918
919 let mut params = rcgen::CertificateParams::new(hostnames)?;
920
921 params.not_before = ::time::OffsetDateTime::now_utc() - ::time::Duration::days(1);
924 params.not_after = params.not_before + ::time::Duration::days(14);
925
926 let cert = params.self_signed(&key_pair)?;
928
929 let key_der = key_pair.serialized_der().to_vec();
931 let key_der = PrivatePkcs8KeyDer::from(key_der);
932 let key = self.provider.key_provider.load_private_key(key_der.into())?;
933
934 Ok(rustls::sign::CertifiedKey::new(vec![cert.into()], key))
936 }
937
938 #[cfg(not(any(feature = "aws-lc-rs", feature = "ring")))]
939 fn generate(&self, _hostnames: &[String]) -> Result<rustls::sign::CertifiedKey> {
940 Err(Error::NoCryptoProvider)
941 }
942
943 pub fn set_certs(&self, certs: Vec<Arc<rustls::sign::CertifiedKey>>) {
945 let fingerprints = certs
946 .iter()
947 .map(|ck| {
948 let fingerprint = crate::crypto::sha256(&self.provider, ck.cert[0].as_ref());
949 hex::encode(fingerprint)
950 })
951 .collect();
952
953 let mut info = self.info.write().expect("info write lock poisoned");
954 info.certs = certs;
955 info.fingerprints = fingerprints;
956 }
957
958 fn best_certificate(
960 &self,
961 client_hello: &rustls::server::ClientHello<'_>,
962 ) -> Option<Arc<rustls::sign::CertifiedKey>> {
963 let server_name = client_hello.server_name()?;
964 let dns_name = rustls::pki_types::ServerName::try_from(server_name).ok()?;
965
966 for ck in self.info.read().expect("info read lock poisoned").certs.iter() {
967 let leaf: webpki::EndEntityCert = ck
968 .end_entity_cert()
969 .expect("missing certificate")
970 .try_into()
971 .expect("failed to parse certificate");
972
973 if leaf.verify_is_valid_for_subject_name(&dns_name).is_ok() {
974 return Some(ck.clone());
975 }
976 }
977
978 None
979 }
980}
981
982#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
983impl rustls::server::ResolvesServerCert for ServeCerts {
984 fn resolve(&self, client_hello: rustls::server::ClientHello<'_>) -> Option<Arc<rustls::sign::CertifiedKey>> {
985 if let Some(cert) = self.best_certificate(&client_hello) {
986 return Some(cert);
987 }
988
989 tracing::warn!(server_name = ?client_hello.server_name(), "no SNI certificate found");
992
993 self.info
994 .read()
995 .expect("info read lock poisoned")
996 .certs
997 .first()
998 .cloned()
999 }
1000}
1001
1002#[cfg(any(feature = "quinn", feature = "noq"))]
1010pub(crate) async fn reload_certs(certs: Arc<ServeCerts>, tls_config: Server) {
1011 let paths: Vec<PathBuf> = tls_config.cert.iter().chain(tls_config.key.iter()).cloned().collect();
1012 if paths.is_empty() {
1013 return;
1014 }
1015
1016 let mut watcher = match crate::watch::FileWatcher::new(&paths) {
1017 Ok(watcher) => watcher,
1018 Err(err) => {
1019 tracing::error!(%err, "failed to watch certificate files; hot reload disabled");
1020 return;
1021 }
1022 };
1023
1024 loop {
1025 watcher.changed().await;
1026 tracing::info!("reloading server certificates");
1027
1028 if let Err(err) = certs.load_certs(&tls_config) {
1029 tracing::warn!(%err, "failed to reload server certificates");
1030 }
1031 }
1032}