1use crate::crypto;
12use rustls::pki_types::pem::PemObject;
13use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime};
14use std::path::{Path, PathBuf};
15use std::sync::Arc;
16use std::{fs, io};
17
18#[cfg(all(
19 any(feature = "quinn", feature = "noq", feature = "quiche"),
20 any(feature = "aws-lc-rs", feature = "ring")
21))]
22use rustls::pki_types::PrivatePkcs8KeyDer;
23#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
24use std::sync::RwLock;
25
26#[derive(Debug, thiserror::Error)]
31#[non_exhaustive]
32pub enum Error {
33 #[error("failed to open certificate file")]
35 Open(#[source] std::io::Error),
36
37 #[error("failed to read file")]
39 ReadFile(#[source] std::io::Error),
40
41 #[error("failed to read certificates")]
43 Read(#[source] rustls::pki_types::pem::Error),
44
45 #[error("failed to parse private key")]
47 Key(#[source] rustls::pki_types::pem::Error),
48
49 #[error("no certificates found")]
51 Empty,
52
53 #[error("no roots found in {}", .0.display())]
55 EmptyRoots(PathBuf),
56
57 #[error(
59 "no trusted roots: provide --client-tls-root, enable --client-tls-system-roots, or use --client-tls-fingerprint / --client-tls-disable-verify"
60 )]
61 NoRoots,
62
63 #[error("invalid TLS fingerprint (expected hex-encoded SHA-256)")]
65 Fingerprint(#[source] hex::FromHexError),
66
67 #[error("invalid TLS fingerprint length: expected 32 bytes (SHA-256), got {0}")]
69 FingerprintLength(usize),
70
71 #[error(
74 "--client-tls-fingerprint cannot be combined with --client-tls-root or --client-tls-system-roots: fingerprint pinning bypasses CA verification"
75 )]
76 FingerprintWithRoots,
77
78 #[error("failed to add root certificate")]
80 AddRoot(#[source] rustls::Error),
81
82 #[cfg(target_os = "android")]
84 #[error("failed to initialize the Android platform verifier")]
85 AndroidInit(#[source] jni::errors::Error),
86
87 #[error("failed to configure client certificate")]
89 ClientAuth(#[source] rustls::Error),
90
91 #[error("both --client-tls-cert and --client-tls-key must be provided")]
93 IncompleteClientAuth,
94
95 #[error("must provide both cert and key")]
97 CertKeyCountMismatch,
98
99 #[error("must provide at least one cert/key pair or generate entry")]
101 NoCertSource,
102
103 #[error("private key {} doesn't match certificate {}", key.display(), cert.display())]
105 KeyMismatch {
106 key: PathBuf,
108 cert: PathBuf,
110 #[source]
112 source: rustls::Error,
113 },
114
115 #[error(transparent)]
117 Rustls(#[from] rustls::Error),
118
119 #[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
121 #[error("failed to build client certificate verifier")]
122 ClientVerifier(#[source] rustls::server::VerifierBuilderError),
123
124 #[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
126 #[error(transparent)]
127 Rcgen(#[from] rcgen::Error),
128
129 #[error("no crypto provider available; enable aws-lc-rs or ring feature")]
131 NoCryptoProvider,
132}
133
134pub type Result<T> = std::result::Result<T, Error>;
136
137pub(crate) fn read_certs(path: &Path) -> Result<Vec<CertificateDer<'static>>> {
139 let file = fs::File::open(path).map_err(Error::Open)?;
140 let mut reader = io::BufReader::new(file);
141 CertificateDer::pem_reader_iter(&mut reader)
142 .collect::<std::result::Result<_, _>>()
143 .map_err(Error::Read)
144}
145
146#[serde_with::serde_as]
150#[derive(Clone, Default, Debug, clap::Args, serde::Serialize, serde::Deserialize)]
151#[serde(default, deny_unknown_fields)]
152#[group(id = "tls-client")]
153#[non_exhaustive]
154pub struct Client {
155 #[serde(skip_serializing_if = "Vec::is_empty")]
165 #[arg(id = "client-tls-root", long = "client-tls-root", env = "MOQ_CLIENT_TLS_ROOT")]
166 #[serde_as(as = "serde_with::OneOrMany<_>")]
167 pub root: Vec<PathBuf>,
168
169 #[serde(skip_serializing_if = "Option::is_none")]
176 #[arg(
177 id = "client-tls-system-roots",
178 long = "client-tls-system-roots",
179 env = "MOQ_CLIENT_TLS_SYSTEM_ROOTS",
180 default_missing_value = "true",
181 num_args = 0..=1,
182 require_equals = true,
183 value_parser = clap::value_parser!(bool),
184 )]
185 pub system_roots: Option<bool>,
186
187 #[serde(skip_serializing_if = "Vec::is_empty")]
198 #[arg(
199 id = "client-tls-fingerprint",
200 long = "client-tls-fingerprint",
201 env = "MOQ_CLIENT_TLS_FINGERPRINT"
202 )]
203 #[serde_as(as = "serde_with::OneOrMany<_>")]
204 pub fingerprint: Vec<String>,
205
206 #[serde(skip_serializing_if = "Option::is_none")]
211 #[arg(id = "client-tls-cert", long = "client-tls-cert", env = "MOQ_CLIENT_TLS_CERT")]
212 pub cert: Option<PathBuf>,
213
214 #[serde(skip_serializing_if = "Option::is_none")]
219 #[arg(id = "client-tls-key", long = "client-tls-key", env = "MOQ_CLIENT_TLS_KEY")]
220 pub key: Option<PathBuf>,
221
222 #[serde(skip_serializing_if = "Option::is_none")]
226 #[arg(
227 id = "client-tls-disable-verify",
228 long = "client-tls-disable-verify",
229 env = "MOQ_CLIENT_TLS_DISABLE_VERIFY",
230 default_missing_value = "true",
231 num_args = 0..=1,
232 require_equals = true,
233 value_parser = clap::value_parser!(bool),
234 )]
235 pub disable_verify: Option<bool>,
236
237 #[serde(skip_serializing_if = "Option::is_none")]
242 #[arg(
243 id = "client-tls-host-name",
244 long = "client-tls-host-name",
245 env = "MOQ_CLIENT_TLS_HOST_NAME"
246 )]
247 pub host_name: Option<String>,
248
249 #[command(flatten)]
253 #[serde(skip)]
254 deprecated: Deprecated,
255}
256
257#[derive(Clone, Default, Debug, clap::Args)]
262struct Deprecated {
263 #[arg(long = "tls-root", hide = true)]
264 root: Vec<PathBuf>,
265
266 #[arg(
267 long = "tls-system-roots",
268 hide = true,
269 default_missing_value = "true",
270 num_args = 0..=1,
271 require_equals = true,
272 value_parser = clap::value_parser!(bool),
273 )]
274 system_roots: Option<bool>,
275
276 #[arg(long = "tls-fingerprint", hide = true)]
277 fingerprint: Vec<String>,
278
279 #[arg(
280 long = "tls-disable-verify",
281 hide = true,
282 default_missing_value = "true",
283 num_args = 0..=1,
284 require_equals = true,
285 value_parser = clap::value_parser!(bool),
286 )]
287 disable_verify: Option<bool>,
288}
289
290#[derive(Clone)]
297pub(crate) enum Verification {
298 Disabled,
300
301 Fingerprints(Vec<[u8; 32]>),
304
305 Roots {
310 custom: Vec<CertificateDer<'static>>,
311 system: bool,
312 },
313}
314
315impl Client {
316 pub(crate) fn warn_deprecated(&self) {
319 if !self.deprecated.root.is_empty() {
320 tracing::warn!("--tls-root is deprecated; use --client-tls-root");
321 }
322 if self.deprecated.system_roots.is_some() {
323 tracing::warn!("--tls-system-roots is deprecated; use --client-tls-system-roots");
324 }
325 if !self.deprecated.fingerprint.is_empty() {
326 tracing::warn!("--tls-fingerprint is deprecated; use --client-tls-fingerprint");
327 }
328 if self.deprecated.disable_verify.is_some() {
329 tracing::warn!("--tls-disable-verify is deprecated; use --client-tls-disable-verify");
330 }
331 }
332
333 pub(crate) fn effective_root(&self) -> Vec<PathBuf> {
335 let mut root = self.root.clone();
336 root.extend(self.deprecated.root.iter().cloned());
337 root
338 }
339
340 pub(crate) fn effective_fingerprint(&self) -> Vec<String> {
342 let mut fp = self.fingerprint.clone();
343 fp.extend(self.deprecated.fingerprint.iter().cloned());
344 fp
345 }
346
347 pub(crate) fn effective_system_roots(&self) -> Option<bool> {
349 self.system_roots.or(self.deprecated.system_roots)
350 }
351
352 pub(crate) fn effective_disable_verify(&self) -> Option<bool> {
354 self.disable_verify.or(self.deprecated.disable_verify)
355 }
356
357 pub(crate) fn verification(&self) -> Result<Verification> {
368 self.warn_deprecated();
369
370 if self.effective_disable_verify().unwrap_or_default() {
371 return Ok(Verification::Disabled);
372 }
373
374 let fingerprints = self.fingerprints()?;
375 if !fingerprints.is_empty() {
376 if !self.effective_root().is_empty() || self.effective_system_roots() == Some(true) {
377 return Err(Error::FingerprintWithRoots);
378 }
379 return Ok(Verification::Fingerprints(fingerprints));
380 }
381
382 let root = self.effective_root();
383 let system = self.effective_system_roots().unwrap_or(root.is_empty());
386
387 let mut custom = Vec::new();
388 for root in &root {
389 let certs = read_certs(root)?;
390 if certs.is_empty() {
391 return Err(Error::EmptyRoots(root.clone()));
392 }
393 custom.extend(certs);
394 }
395
396 if !system && custom.is_empty() {
401 return Err(Error::NoRoots);
402 }
403
404 Ok(Verification::Roots { custom, system })
405 }
406
407 pub(crate) fn allows_http_bootstrap(&self) -> bool {
416 self.effective_fingerprint().is_empty() && !self.effective_disable_verify().unwrap_or_default()
417 }
418
419 fn fingerprints(&self) -> Result<Vec<[u8; 32]>> {
421 self.effective_fingerprint()
422 .iter()
423 .map(|fp| {
424 let bytes = hex::decode(fp.trim()).map_err(Error::Fingerprint)?;
425 bytes.try_into().map_err(|v: Vec<u8>| Error::FingerprintLength(v.len()))
426 })
427 .collect()
428 }
429
430 pub fn build(&self) -> Result<rustls::ClientConfig> {
435 let provider = crypto::provider();
436 let verification = self.verification()?;
437
438 let builder = rustls::ClientConfig::builder_with_provider(provider.clone())
441 .with_protocol_versions(&[&rustls::version::TLS13, &rustls::version::TLS12])?;
442
443 let builder = match &verification {
446 Verification::Roots { custom, system: true } => Self::system_verifier(builder, custom, &provider)?,
447 Verification::Roots { custom, system: false } => builder.with_root_certificates(root_store(custom)?),
448 Verification::Disabled | Verification::Fingerprints(_) => {
449 builder.with_root_certificates(rustls::RootCertStore::empty())
450 }
451 };
452
453 let mut tls = self.with_client_auth(builder)?;
454
455 match verification {
456 Verification::Disabled => {
457 tracing::warn!(
458 "TLS server certificate verification is disabled; A man-in-the-middle attack is possible."
459 );
460 tls.dangerous()
461 .set_certificate_verifier(Arc::new(NoCertificateVerification(provider)));
462 }
463 Verification::Fingerprints(fingerprints) => {
464 let fingerprints = fingerprints.into_iter().map(|fp| fp.to_vec()).collect();
465 let verifier = FingerprintVerifier::new(provider, fingerprints);
466 tls.dangerous().set_certificate_verifier(Arc::new(verifier));
467 }
468 Verification::Roots { .. } => {}
470 }
471
472 Ok(tls)
473 }
474
475 fn system_verifier(
483 builder: rustls::ConfigBuilder<rustls::ClientConfig, rustls::WantsVerifier>,
484 custom: &[CertificateDer<'static>],
485 provider: &crypto::Provider,
486 ) -> Result<rustls::ConfigBuilder<rustls::ClientConfig, rustls::client::WantsClientCert>> {
487 #[cfg(target_os = "android")]
492 {
493 if ANDROID_INITIALIZED.load(std::sync::atomic::Ordering::Acquire) && custom.is_empty() {
494 let verifier = rustls_platform_verifier::Verifier::new(provider.clone())?;
495 return Ok(builder.dangerous().with_custom_certificate_verifier(Arc::new(verifier)));
496 }
497
498 let mut roots = rustls::RootCertStore::empty();
499 roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
500 for cert in custom {
501 roots.add(cert.clone()).map_err(Error::AddRoot)?;
502 }
503 Ok(builder.with_root_certificates(roots))
504 }
505
506 #[cfg(not(target_os = "android"))]
507 {
508 let verifier = if custom.is_empty() {
509 rustls_platform_verifier::Verifier::new(provider.clone())?
510 } else {
511 rustls_platform_verifier::Verifier::new_with_extra_roots(custom.iter().cloned(), provider.clone())?
512 };
513 Ok(builder.dangerous().with_custom_certificate_verifier(Arc::new(verifier)))
514 }
515 }
516
517 fn with_client_auth(
519 &self,
520 builder: rustls::ConfigBuilder<rustls::ClientConfig, rustls::client::WantsClientCert>,
521 ) -> Result<rustls::ClientConfig> {
522 Ok(match (&self.cert, &self.key) {
523 (Some(cert_path), Some(key_path)) => {
524 let cert_pem = fs::read(cert_path).map_err(Error::ReadFile)?;
525 let chain: Vec<CertificateDer<'static>> = CertificateDer::pem_slice_iter(&cert_pem)
526 .collect::<std::result::Result<_, _>>()
527 .map_err(Error::Read)?;
528 if chain.is_empty() {
529 return Err(Error::Empty);
530 }
531 let key_pem = fs::read(key_path).map_err(Error::ReadFile)?;
532 let key = PrivateKeyDer::from_pem_slice(&key_pem).map_err(Error::Key)?;
533 builder.with_client_auth_cert(chain, key).map_err(Error::ClientAuth)?
534 }
535 (None, None) => builder.with_no_client_auth(),
536 _ => return Err(Error::IncompleteClientAuth),
537 })
538 }
539}
540
541fn root_store(custom: &[CertificateDer<'static>]) -> Result<rustls::RootCertStore> {
543 let mut roots = rustls::RootCertStore::empty();
544 for cert in custom {
545 roots.add(cert.clone()).map_err(Error::AddRoot)?;
546 }
547 Ok(roots)
548}
549
550#[cfg(target_os = "android")]
552static ANDROID_INITIALIZED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
553
554#[cfg(target_os = "android")]
566pub fn init_android(env: &mut jni::Env, context: jni::objects::JObject) -> Result<()> {
567 rustls_platform_verifier::android::init_with_env(env, context).map_err(Error::AndroidInit)?;
568 ANDROID_INITIALIZED.store(true, std::sync::atomic::Ordering::Release);
569 Ok(())
570}
571
572#[serde_with::serde_as]
581#[derive(clap::Args, Clone, Default, Debug, serde::Serialize, serde::Deserialize)]
582#[serde(deny_unknown_fields)]
583#[group(id = "tls-server")]
584#[non_exhaustive]
585pub struct Server {
586 #[arg(long = "tls-cert", id = "tls-cert", env = "MOQ_SERVER_TLS_CERT")]
588 #[serde(default, skip_serializing_if = "Vec::is_empty")]
589 #[serde_as(as = "serde_with::OneOrMany<_>")]
590 pub cert: Vec<PathBuf>,
591
592 #[arg(long = "tls-key", id = "tls-key", env = "MOQ_SERVER_TLS_KEY")]
594 #[serde(default, skip_serializing_if = "Vec::is_empty")]
595 #[serde_as(as = "serde_with::OneOrMany<_>")]
596 pub key: Vec<PathBuf>,
597
598 #[arg(
601 long = "tls-generate",
602 id = "tls-generate",
603 value_delimiter = ',',
604 env = "MOQ_SERVER_TLS_GENERATE"
605 )]
606 #[serde(default, skip_serializing_if = "Vec::is_empty")]
607 #[serde_as(as = "serde_with::OneOrMany<_>")]
608 pub generate: Vec<String>,
609
610 #[arg(
622 long = "server-tls-root",
623 id = "server-tls-root",
624 value_delimiter = ',',
625 env = "MOQ_SERVER_TLS_ROOT"
626 )]
627 #[serde(default, skip_serializing_if = "Vec::is_empty")]
628 #[serde_as(as = "serde_with::OneOrMany<_>")]
629 pub root: Vec<PathBuf>,
630}
631
632impl Server {
633 pub fn load_roots(&self) -> Result<rustls::RootCertStore> {
635 let mut roots = rustls::RootCertStore::empty();
636 for path in &self.root {
637 let certs = read_certs(path)?;
638 if certs.is_empty() {
639 return Err(Error::Empty);
640 }
641 for cert in certs {
642 roots.add(cert).map_err(Error::AddRoot)?;
643 }
644 }
645 Ok(roots)
646 }
647
648 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
657 pub fn server_config(&self, alpn: Vec<Vec<u8>>) -> Result<Arc<rustls::ServerConfig>> {
658 server_config(self, alpn)
659 }
660}
661
662#[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
664fn server_config(config: &Server, alpn: Vec<Vec<u8>>) -> Result<Arc<rustls::ServerConfig>> {
665 let provider = crypto::provider();
666
667 let certs = ServeCerts::new(provider.clone());
668 certs.load_certs(config)?;
669 let certs = Arc::new(certs);
670
671 let builder =
673 rustls::ServerConfig::builder_with_provider(provider.clone()).with_safe_default_protocol_versions()?;
674
675 let mut tls = if config.root.is_empty() {
676 builder.with_no_client_auth().with_cert_resolver(certs)
677 } else {
678 let roots = config.load_roots()?;
679 let verifier = rustls::server::WebPkiClientVerifier::builder_with_provider(Arc::new(roots), provider)
680 .allow_unauthenticated()
681 .build()
682 .map_err(Error::ClientVerifier)?;
683 builder.with_client_cert_verifier(verifier).with_cert_resolver(certs)
684 };
685
686 tls.alpn_protocols = alpn;
687 Ok(Arc::new(tls))
688}
689
690#[derive(Clone)]
697pub struct PeerIdentity {
698 chain: Vec<CertificateDer<'static>>,
699}
700
701impl PeerIdentity {
702 #[cfg(any(feature = "quinn", feature = "noq"))]
706 pub(crate) fn from_any(identity: Option<Box<dyn std::any::Any>>) -> Option<Self> {
707 let chain = identity?.downcast::<Vec<CertificateDer<'static>>>().ok()?;
708 Some(Self { chain: *chain })
709 }
710
711 pub fn chain(&self) -> &[CertificateDer<'static>] {
717 &self.chain
718 }
719
720 pub fn expiry(&self) -> Option<std::time::SystemTime> {
723 use std::time::{Duration, UNIX_EPOCH};
724
725 let leaf = self.chain.first()?;
726 let (_, cert) = x509_parser::parse_x509_certificate(leaf).ok()?;
727 let secs = u64::try_from(cert.validity().not_after.timestamp()).ok()?;
728 Some(UNIX_EPOCH + Duration::from_secs(secs))
729 }
730}
731
732#[derive(Debug, Default)]
734pub(crate) struct Info {
735 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
736 pub(crate) certs: Vec<Arc<rustls::sign::CertifiedKey>>,
737 pub(crate) fingerprints: Vec<String>,
738}
739
740#[derive(Clone, Debug)]
746pub struct Certificates {
747 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
748 info: Arc<RwLock<Info>>,
749}
750
751impl Certificates {
752 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
753 pub(crate) fn new(info: Arc<RwLock<Info>>) -> Self {
754 Self { info }
755 }
756
757 pub(crate) fn empty() -> Self {
759 Self {
760 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
761 info: Arc::new(RwLock::new(Info::default())),
762 }
763 }
764
765 pub fn fingerprints(&self) -> Vec<String> {
771 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
772 {
773 let info = self.info.read().unwrap_or_else(std::sync::PoisonError::into_inner);
776 info.fingerprints.clone()
777 }
778 #[cfg(not(any(feature = "noq", feature = "quinn", feature = "quiche")))]
779 Vec::new()
780 }
781}
782
783#[derive(Debug)]
786struct NoCertificateVerification(crypto::Provider);
787
788impl rustls::client::danger::ServerCertVerifier for NoCertificateVerification {
789 fn verify_server_cert(
790 &self,
791 _end_entity: &CertificateDer<'_>,
792 _intermediates: &[CertificateDer<'_>],
793 _server_name: &ServerName<'_>,
794 _ocsp: &[u8],
795 _now: UnixTime,
796 ) -> std::result::Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
797 Ok(rustls::client::danger::ServerCertVerified::assertion())
798 }
799
800 fn verify_tls12_signature(
801 &self,
802 message: &[u8],
803 cert: &CertificateDer<'_>,
804 dss: &rustls::DigitallySignedStruct,
805 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
806 rustls::crypto::verify_tls12_signature(message, cert, dss, &self.0.signature_verification_algorithms)
807 }
808
809 fn verify_tls13_signature(
810 &self,
811 message: &[u8],
812 cert: &CertificateDer<'_>,
813 dss: &rustls::DigitallySignedStruct,
814 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
815 rustls::crypto::verify_tls13_signature(message, cert, dss, &self.0.signature_verification_algorithms)
816 }
817
818 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
819 self.0.signature_verification_algorithms.supported_schemes()
820 }
821}
822
823#[derive(Debug)]
826pub(crate) struct FingerprintVerifier {
827 provider: crypto::Provider,
828 fingerprints: Vec<Vec<u8>>,
829}
830
831impl FingerprintVerifier {
832 pub fn new(provider: crypto::Provider, fingerprints: Vec<Vec<u8>>) -> Self {
833 Self { provider, fingerprints }
834 }
835}
836
837impl rustls::client::danger::ServerCertVerifier for FingerprintVerifier {
838 fn verify_server_cert(
839 &self,
840 end_entity: &CertificateDer<'_>,
841 _intermediates: &[CertificateDer<'_>],
842 _server_name: &ServerName<'_>,
843 _ocsp: &[u8],
844 _now: UnixTime,
845 ) -> std::result::Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
846 let fingerprint = crypto::sha256(&self.provider, end_entity);
847 if self.fingerprints.iter().any(|fp| fingerprint.as_ref() == fp.as_slice()) {
848 Ok(rustls::client::danger::ServerCertVerified::assertion())
849 } else {
850 Err(rustls::Error::General("fingerprint mismatch".into()))
851 }
852 }
853
854 fn verify_tls12_signature(
855 &self,
856 message: &[u8],
857 cert: &CertificateDer<'_>,
858 dss: &rustls::DigitallySignedStruct,
859 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
860 rustls::crypto::verify_tls12_signature(message, cert, dss, &self.provider.signature_verification_algorithms)
861 }
862
863 fn verify_tls13_signature(
864 &self,
865 message: &[u8],
866 cert: &CertificateDer<'_>,
867 dss: &rustls::DigitallySignedStruct,
868 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
869 rustls::crypto::verify_tls13_signature(message, cert, dss, &self.provider.signature_verification_algorithms)
870 }
871
872 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
873 self.provider.signature_verification_algorithms.supported_schemes()
874 }
875}
876
877#[cfg(test)]
878#[cfg(all(any(feature = "quinn", feature = "noq", feature = "quiche"), feature = "aws-lc-rs"))]
879mod tests {
880 use super::*;
881 use rustls::client::danger::ServerCertVerifier;
882 use rustls::pki_types::ServerName;
883
884 fn self_signed() -> CertificateDer<'static> {
885 let key = rcgen::KeyPair::generate().unwrap();
886 let params = rcgen::CertificateParams::new(vec!["localhost".to_string()]).unwrap();
887 params.self_signed(&key).unwrap().into()
888 }
889
890 #[cfg(any(feature = "quinn", feature = "noq"))]
891 #[test]
892 fn peer_identity_expiry_reads_not_after() {
893 let not_after = ::time::OffsetDateTime::from_unix_timestamp(2_000_000_000).unwrap();
895
896 let key = rcgen::KeyPair::generate().unwrap();
897 let mut params = rcgen::CertificateParams::new(vec!["localhost".to_string()]).unwrap();
898 params.not_after = not_after;
899 let cert: CertificateDer<'static> = params.self_signed(&key).unwrap().into();
900
901 let identity: Box<dyn std::any::Any> = Box::new(vec![cert]);
903 let parsed = PeerIdentity::from_any(Some(identity)).expect("chain parsed");
904 let expiry = parsed.expiry().expect("expiry parsed");
905 assert_eq!(
906 expiry.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(),
907 2_000_000_000
908 );
909 }
910
911 #[cfg(any(feature = "quinn", feature = "noq"))]
912 #[test]
913 fn peer_identity_none_without_chain() {
914 assert!(PeerIdentity::from_any(None).is_none());
915 let bogus: Box<dyn std::any::Any> = Box::new(42u32);
917 assert!(PeerIdentity::from_any(Some(bogus)).is_none());
918 }
919
920 #[test]
921 fn fingerprint_verifier_matches_and_rejects() {
922 let provider = crypto::provider();
923 let cert = self_signed();
924 let fingerprint = crypto::sha256(&provider, cert.as_ref()).as_ref().to_vec();
925
926 let name = ServerName::try_from("localhost").unwrap();
927 let now = UnixTime::now();
928
929 let verifier = FingerprintVerifier::new(provider.clone(), vec![fingerprint]);
930 assert!(verifier.verify_server_cert(&cert, &[], &name, &[], now).is_ok());
931
932 let other = self_signed();
934 assert!(verifier.verify_server_cert(&other, &[], &name, &[], now).is_err());
935 }
936
937 #[test]
938 fn build_installs_fingerprint_verifier() {
939 let cert = self_signed();
940 let fingerprint = hex::encode(crypto::sha256(&crypto::provider(), cert.as_ref()));
941
942 let config = Client {
944 fingerprint: vec![fingerprint],
945 ..Default::default()
946 };
947 assert!(config.build().is_ok());
948 }
949
950 #[test]
951 fn build_rejects_invalid_fingerprint_hex() {
952 let config = Client {
953 fingerprint: vec!["not-hex".to_string()],
954 ..Default::default()
955 };
956 assert!(matches!(config.build(), Err(Error::Fingerprint(_))));
957 }
958
959 #[test]
960 fn build_rejects_wrong_length_fingerprint() {
961 let config = Client {
963 fingerprint: vec!["abcd".to_string()],
964 ..Default::default()
965 };
966 assert!(matches!(config.build(), Err(Error::FingerprintLength(2))));
967 }
968
969 #[test]
970 fn build_rejects_no_roots() {
971 let config = Client {
974 system_roots: Some(false),
975 ..Default::default()
976 };
977 assert!(matches!(config.build(), Err(Error::NoRoots)));
978 }
979
980 #[test]
981 fn build_allows_no_roots_when_verification_overridden() {
982 let config = Client {
984 system_roots: Some(false),
985 disable_verify: Some(true),
986 ..Default::default()
987 };
988 assert!(config.build().is_ok());
989
990 let cert = self_signed();
992 let fingerprint = hex::encode(crypto::sha256(&crypto::provider(), cert.as_ref()));
993 let config = Client {
994 system_roots: Some(false),
995 fingerprint: vec![fingerprint],
996 ..Default::default()
997 };
998 assert!(config.build().is_ok());
999 }
1000
1001 #[test]
1002 fn build_rejects_fingerprint_with_roots() {
1003 let cert = self_signed();
1004 let fingerprint = hex::encode(crypto::sha256(&crypto::provider(), cert.as_ref()));
1005
1006 let with_system = Client {
1009 fingerprint: vec![fingerprint.clone()],
1010 system_roots: Some(true),
1011 ..Default::default()
1012 };
1013 assert!(matches!(with_system.build(), Err(Error::FingerprintWithRoots)));
1014
1015 let with_custom = Client {
1018 fingerprint: vec![fingerprint],
1019 root: vec![PathBuf::from("/does-not-exist.pem")],
1020 ..Default::default()
1021 };
1022 assert!(matches!(with_custom.build(), Err(Error::FingerprintWithRoots)));
1023 }
1024
1025 fn self_signed_root() -> (tempfile::NamedTempFile, PathBuf) {
1028 use std::io::Write;
1029 let key = rcgen::KeyPair::generate().unwrap();
1030 let params = rcgen::CertificateParams::new(vec!["localhost".to_string()]).unwrap();
1031 let cert = params.self_signed(&key).unwrap();
1032 let mut file = tempfile::NamedTempFile::new().unwrap();
1033 file.write_all(cert.pem().as_bytes()).unwrap();
1034 let path = file.path().to_path_buf();
1035 (file, path)
1036 }
1037
1038 #[test]
1039 fn build_uses_platform_verifier_by_default() {
1040 assert!(Client::default().build().is_ok());
1043 }
1044
1045 #[test]
1046 fn build_with_custom_roots_only() {
1047 let (_keep, path) = self_signed_root();
1050 let config = Client {
1051 root: vec![path],
1052 ..Default::default()
1053 };
1054 assert!(config.build().is_ok());
1055 }
1056
1057 #[test]
1058 fn build_with_custom_and_system_roots() {
1059 let (_keep, path) = self_signed_root();
1062 let config = Client {
1063 root: vec![path],
1064 system_roots: Some(true),
1065 ..Default::default()
1066 };
1067 assert!(config.build().is_ok());
1068 }
1069}
1070
1071#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
1074#[derive(Debug)]
1075pub(crate) struct ServeCerts {
1076 pub info: Arc<RwLock<Info>>,
1077 provider: crypto::Provider,
1078}
1079
1080#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
1081impl ServeCerts {
1082 pub fn new(provider: crypto::Provider) -> Self {
1083 Self {
1084 info: Arc::new(RwLock::new(Info::default())),
1085 provider,
1086 }
1087 }
1088
1089 pub fn load_certs(&self, config: &Server) -> Result<()> {
1090 if config.cert.len() != config.key.len() {
1091 return Err(Error::CertKeyCountMismatch);
1092 }
1093 if config.cert.is_empty() && config.generate.is_empty() {
1094 return Err(Error::NoCertSource);
1095 }
1096
1097 let mut certs = Vec::new();
1098
1099 for (cert, key) in config.cert.iter().zip(config.key.iter()) {
1101 certs.push(Arc::new(self.load(cert, key)?));
1102 }
1103
1104 if !config.generate.is_empty() {
1106 certs.push(Arc::new(self.generate(&config.generate)?));
1107 }
1108
1109 self.set_certs(certs);
1110 Ok(())
1111 }
1112
1113 fn load(&self, chain_path: &Path, key_path: &Path) -> Result<rustls::sign::CertifiedKey> {
1115 let chain = read_certs(chain_path)?;
1116 if chain.is_empty() {
1117 return Err(Error::Empty);
1118 }
1119
1120 let key = PrivateKeyDer::from_pem_file(key_path).map_err(Error::Key)?;
1122 let key = self.provider.key_provider.load_private_key(key)?;
1123
1124 let certified_key = rustls::sign::CertifiedKey::new(chain, key);
1125
1126 certified_key.keys_match().map_err(|source| Error::KeyMismatch {
1127 key: key_path.to_path_buf(),
1128 cert: chain_path.to_path_buf(),
1129 source,
1130 })?;
1131
1132 Ok(certified_key)
1133 }
1134
1135 #[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
1136 fn generate(&self, hostnames: &[String]) -> Result<rustls::sign::CertifiedKey> {
1137 let key_pair = rcgen::KeyPair::generate()?;
1138
1139 let mut params = rcgen::CertificateParams::new(hostnames)?;
1140
1141 params.not_before = ::time::OffsetDateTime::now_utc() - ::time::Duration::days(1);
1144 params.not_after = params.not_before + ::time::Duration::days(14);
1145
1146 let cert = params.self_signed(&key_pair)?;
1148
1149 let key_der = key_pair.serialized_der().to_vec();
1151 let key_der = PrivatePkcs8KeyDer::from(key_der);
1152 let key = self.provider.key_provider.load_private_key(key_der.into())?;
1153
1154 Ok(rustls::sign::CertifiedKey::new(vec![cert.into()], key))
1156 }
1157
1158 #[cfg(not(any(feature = "aws-lc-rs", feature = "ring")))]
1159 fn generate(&self, _hostnames: &[String]) -> Result<rustls::sign::CertifiedKey> {
1160 Err(Error::NoCryptoProvider)
1161 }
1162
1163 pub fn set_certs(&self, certs: Vec<Arc<rustls::sign::CertifiedKey>>) {
1165 let fingerprints = certs
1166 .iter()
1167 .map(|ck| {
1168 let fingerprint = crate::crypto::sha256(&self.provider, ck.cert[0].as_ref());
1169 hex::encode(fingerprint)
1170 })
1171 .collect();
1172
1173 let mut info = self.info.write().expect("info write lock poisoned");
1174 info.certs = certs;
1175 info.fingerprints = fingerprints;
1176 }
1177
1178 fn best_certificate(
1180 &self,
1181 client_hello: &rustls::server::ClientHello<'_>,
1182 ) -> Option<Arc<rustls::sign::CertifiedKey>> {
1183 let server_name = client_hello.server_name()?;
1184 let dns_name = rustls::pki_types::ServerName::try_from(server_name).ok()?;
1185
1186 for ck in self.info.read().expect("info read lock poisoned").certs.iter() {
1187 let leaf: webpki::EndEntityCert = ck
1188 .end_entity_cert()
1189 .expect("missing certificate")
1190 .try_into()
1191 .expect("failed to parse certificate");
1192
1193 if leaf.verify_is_valid_for_subject_name(&dns_name).is_ok() {
1194 return Some(ck.clone());
1195 }
1196 }
1197
1198 None
1199 }
1200}
1201
1202#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
1203impl rustls::server::ResolvesServerCert for ServeCerts {
1204 fn resolve(&self, client_hello: rustls::server::ClientHello<'_>) -> Option<Arc<rustls::sign::CertifiedKey>> {
1205 if let Some(cert) = self.best_certificate(&client_hello) {
1206 return Some(cert);
1207 }
1208
1209 tracing::warn!(server_name = ?client_hello.server_name(), "no SNI certificate found");
1212
1213 self.info
1214 .read()
1215 .expect("info read lock poisoned")
1216 .certs
1217 .first()
1218 .cloned()
1219 }
1220}
1221
1222#[cfg(any(feature = "quinn", feature = "noq"))]
1230pub(crate) async fn reload_certs(certs: Arc<ServeCerts>, tls_config: Server) {
1231 let paths: Vec<PathBuf> = tls_config.cert.iter().chain(tls_config.key.iter()).cloned().collect();
1232 if paths.is_empty() {
1233 return;
1234 }
1235
1236 let mut watcher = match crate::watch::FileWatcher::new(&paths) {
1237 Ok(watcher) => watcher,
1238 Err(err) => {
1239 tracing::error!(%err, "failed to watch certificate files; hot reload disabled");
1240 return;
1241 }
1242 };
1243
1244 loop {
1245 watcher.changed().await;
1246 tracing::info!("reloading server certificates");
1247
1248 if let Err(err) = certs.load_certs(&tls_config) {
1249 tracing::warn!(%err, "failed to reload server certificates");
1250 }
1251 }
1252}