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(
620 long = "server-tls-root",
621 id = "server-tls-root",
622 value_delimiter = ',',
623 env = "MOQ_SERVER_TLS_ROOT"
624 )]
625 #[serde(default, skip_serializing_if = "Vec::is_empty")]
626 #[serde_as(as = "serde_with::OneOrMany<_>")]
627 pub root: Vec<PathBuf>,
628}
629
630impl Server {
631 pub fn load_roots(&self) -> Result<rustls::RootCertStore> {
633 let mut roots = rustls::RootCertStore::empty();
634 for path in &self.root {
635 let certs = read_certs(path)?;
636 if certs.is_empty() {
637 return Err(Error::Empty);
638 }
639 for cert in certs {
640 roots.add(cert).map_err(Error::AddRoot)?;
641 }
642 }
643 Ok(roots)
644 }
645
646 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
655 pub fn server_config(&self, alpn: Vec<Vec<u8>>) -> Result<Arc<rustls::ServerConfig>> {
656 server_config(self, alpn)
657 }
658}
659
660#[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
662fn server_config(config: &Server, alpn: Vec<Vec<u8>>) -> Result<Arc<rustls::ServerConfig>> {
663 let provider = crypto::provider();
664
665 let certs = ServeCerts::new(provider.clone());
666 certs.load_certs(config)?;
667 let certs = Arc::new(certs);
668
669 let builder =
671 rustls::ServerConfig::builder_with_provider(provider.clone()).with_safe_default_protocol_versions()?;
672
673 let mut tls = if config.root.is_empty() {
674 builder.with_no_client_auth().with_cert_resolver(certs)
675 } else {
676 let roots = config.load_roots()?;
677 let verifier = rustls::server::WebPkiClientVerifier::builder_with_provider(Arc::new(roots), provider)
678 .allow_unauthenticated()
679 .build()
680 .map_err(Error::ClientVerifier)?;
681 builder.with_client_cert_verifier(verifier).with_cert_resolver(certs)
682 };
683
684 tls.alpn_protocols = alpn;
685 Ok(Arc::new(tls))
686}
687
688#[derive(Clone)]
695pub struct PeerIdentity {
696 chain: Vec<CertificateDer<'static>>,
697}
698
699impl PeerIdentity {
700 #[cfg(any(feature = "quinn", feature = "noq"))]
704 pub(crate) fn from_any(identity: Option<Box<dyn std::any::Any>>) -> Option<Self> {
705 let chain = identity?.downcast::<Vec<CertificateDer<'static>>>().ok()?;
706 Some(Self { chain: *chain })
707 }
708
709 #[cfg(feature = "quiche")]
711 pub(crate) fn from_chain(chain: Vec<CertificateDer<'static>>) -> Self {
712 Self { chain }
713 }
714
715 pub fn chain(&self) -> &[CertificateDer<'static>] {
721 &self.chain
722 }
723
724 pub fn expiry(&self) -> Option<std::time::SystemTime> {
727 use std::time::{Duration, UNIX_EPOCH};
728
729 let leaf = self.chain.first()?;
730 let (_, cert) = x509_parser::parse_x509_certificate(leaf).ok()?;
731 let secs = u64::try_from(cert.validity().not_after.timestamp()).ok()?;
732 Some(UNIX_EPOCH + Duration::from_secs(secs))
733 }
734}
735
736#[derive(Debug, Default)]
738pub(crate) struct Info {
739 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
740 pub(crate) certs: Vec<Arc<rustls::sign::CertifiedKey>>,
741 pub(crate) fingerprints: Vec<String>,
742}
743
744#[derive(Clone, Debug)]
750pub struct Certificates {
751 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
752 info: Arc<RwLock<Info>>,
753}
754
755impl Certificates {
756 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
757 pub(crate) fn new(info: Arc<RwLock<Info>>) -> Self {
758 Self { info }
759 }
760
761 pub(crate) fn empty() -> Self {
763 Self {
764 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
765 info: Arc::new(RwLock::new(Info::default())),
766 }
767 }
768
769 pub fn fingerprints(&self) -> Vec<String> {
775 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
776 {
777 let info = self.info.read().unwrap_or_else(std::sync::PoisonError::into_inner);
780 info.fingerprints.clone()
781 }
782 #[cfg(not(any(feature = "noq", feature = "quinn", feature = "quiche")))]
783 Vec::new()
784 }
785}
786
787#[derive(Debug)]
790struct NoCertificateVerification(crypto::Provider);
791
792impl rustls::client::danger::ServerCertVerifier for NoCertificateVerification {
793 fn verify_server_cert(
794 &self,
795 _end_entity: &CertificateDer<'_>,
796 _intermediates: &[CertificateDer<'_>],
797 _server_name: &ServerName<'_>,
798 _ocsp: &[u8],
799 _now: UnixTime,
800 ) -> std::result::Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
801 Ok(rustls::client::danger::ServerCertVerified::assertion())
802 }
803
804 fn verify_tls12_signature(
805 &self,
806 message: &[u8],
807 cert: &CertificateDer<'_>,
808 dss: &rustls::DigitallySignedStruct,
809 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
810 rustls::crypto::verify_tls12_signature(message, cert, dss, &self.0.signature_verification_algorithms)
811 }
812
813 fn verify_tls13_signature(
814 &self,
815 message: &[u8],
816 cert: &CertificateDer<'_>,
817 dss: &rustls::DigitallySignedStruct,
818 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
819 rustls::crypto::verify_tls13_signature(message, cert, dss, &self.0.signature_verification_algorithms)
820 }
821
822 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
823 self.0.signature_verification_algorithms.supported_schemes()
824 }
825}
826
827#[derive(Debug)]
830pub(crate) struct FingerprintVerifier {
831 provider: crypto::Provider,
832 fingerprints: Vec<Vec<u8>>,
833}
834
835impl FingerprintVerifier {
836 pub fn new(provider: crypto::Provider, fingerprints: Vec<Vec<u8>>) -> Self {
837 Self { provider, fingerprints }
838 }
839}
840
841impl rustls::client::danger::ServerCertVerifier for FingerprintVerifier {
842 fn verify_server_cert(
843 &self,
844 end_entity: &CertificateDer<'_>,
845 _intermediates: &[CertificateDer<'_>],
846 _server_name: &ServerName<'_>,
847 _ocsp: &[u8],
848 _now: UnixTime,
849 ) -> std::result::Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
850 let fingerprint = crypto::sha256(&self.provider, end_entity);
851 if self.fingerprints.iter().any(|fp| fingerprint.as_ref() == fp.as_slice()) {
852 Ok(rustls::client::danger::ServerCertVerified::assertion())
853 } else {
854 Err(rustls::Error::General("fingerprint mismatch".into()))
855 }
856 }
857
858 fn verify_tls12_signature(
859 &self,
860 message: &[u8],
861 cert: &CertificateDer<'_>,
862 dss: &rustls::DigitallySignedStruct,
863 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
864 rustls::crypto::verify_tls12_signature(message, cert, dss, &self.provider.signature_verification_algorithms)
865 }
866
867 fn verify_tls13_signature(
868 &self,
869 message: &[u8],
870 cert: &CertificateDer<'_>,
871 dss: &rustls::DigitallySignedStruct,
872 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
873 rustls::crypto::verify_tls13_signature(message, cert, dss, &self.provider.signature_verification_algorithms)
874 }
875
876 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
877 self.provider.signature_verification_algorithms.supported_schemes()
878 }
879}
880
881#[cfg(test)]
882#[cfg(all(any(feature = "quinn", feature = "noq", feature = "quiche"), feature = "aws-lc-rs"))]
883mod tests {
884 use super::*;
885 use rustls::client::danger::ServerCertVerifier;
886 use rustls::pki_types::ServerName;
887
888 fn self_signed() -> CertificateDer<'static> {
889 let key = rcgen::KeyPair::generate().unwrap();
890 let params = rcgen::CertificateParams::new(vec!["localhost".to_string()]).unwrap();
891 params.self_signed(&key).unwrap().into()
892 }
893
894 #[cfg(any(feature = "quinn", feature = "noq"))]
895 #[test]
896 fn peer_identity_expiry_reads_not_after() {
897 let not_after = ::time::OffsetDateTime::from_unix_timestamp(2_000_000_000).unwrap();
899
900 let key = rcgen::KeyPair::generate().unwrap();
901 let mut params = rcgen::CertificateParams::new(vec!["localhost".to_string()]).unwrap();
902 params.not_after = not_after;
903 let cert: CertificateDer<'static> = params.self_signed(&key).unwrap().into();
904
905 let identity: Box<dyn std::any::Any> = Box::new(vec![cert]);
907 let parsed = PeerIdentity::from_any(Some(identity)).expect("chain parsed");
908 let expiry = parsed.expiry().expect("expiry parsed");
909 assert_eq!(
910 expiry.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(),
911 2_000_000_000
912 );
913 }
914
915 #[cfg(any(feature = "quinn", feature = "noq"))]
916 #[test]
917 fn peer_identity_none_without_chain() {
918 assert!(PeerIdentity::from_any(None).is_none());
919 let bogus: Box<dyn std::any::Any> = Box::new(42u32);
921 assert!(PeerIdentity::from_any(Some(bogus)).is_none());
922 }
923
924 #[test]
925 fn fingerprint_verifier_matches_and_rejects() {
926 let provider = crypto::provider();
927 let cert = self_signed();
928 let fingerprint = crypto::sha256(&provider, cert.as_ref()).as_ref().to_vec();
929
930 let name = ServerName::try_from("localhost").unwrap();
931 let now = UnixTime::now();
932
933 let verifier = FingerprintVerifier::new(provider.clone(), vec![fingerprint]);
934 assert!(verifier.verify_server_cert(&cert, &[], &name, &[], now).is_ok());
935
936 let other = self_signed();
938 assert!(verifier.verify_server_cert(&other, &[], &name, &[], now).is_err());
939 }
940
941 #[test]
942 fn build_installs_fingerprint_verifier() {
943 let cert = self_signed();
944 let fingerprint = hex::encode(crypto::sha256(&crypto::provider(), cert.as_ref()));
945
946 let config = Client {
948 fingerprint: vec![fingerprint],
949 ..Default::default()
950 };
951 assert!(config.build().is_ok());
952 }
953
954 #[test]
955 fn build_rejects_invalid_fingerprint_hex() {
956 let config = Client {
957 fingerprint: vec!["not-hex".to_string()],
958 ..Default::default()
959 };
960 assert!(matches!(config.build(), Err(Error::Fingerprint(_))));
961 }
962
963 #[test]
964 fn build_rejects_wrong_length_fingerprint() {
965 let config = Client {
967 fingerprint: vec!["abcd".to_string()],
968 ..Default::default()
969 };
970 assert!(matches!(config.build(), Err(Error::FingerprintLength(2))));
971 }
972
973 #[test]
974 fn build_rejects_no_roots() {
975 let config = Client {
978 system_roots: Some(false),
979 ..Default::default()
980 };
981 assert!(matches!(config.build(), Err(Error::NoRoots)));
982 }
983
984 #[test]
985 fn build_allows_no_roots_when_verification_overridden() {
986 let config = Client {
988 system_roots: Some(false),
989 disable_verify: Some(true),
990 ..Default::default()
991 };
992 assert!(config.build().is_ok());
993
994 let cert = self_signed();
996 let fingerprint = hex::encode(crypto::sha256(&crypto::provider(), cert.as_ref()));
997 let config = Client {
998 system_roots: Some(false),
999 fingerprint: vec![fingerprint],
1000 ..Default::default()
1001 };
1002 assert!(config.build().is_ok());
1003 }
1004
1005 #[test]
1006 fn build_rejects_fingerprint_with_roots() {
1007 let cert = self_signed();
1008 let fingerprint = hex::encode(crypto::sha256(&crypto::provider(), cert.as_ref()));
1009
1010 let with_system = Client {
1013 fingerprint: vec![fingerprint.clone()],
1014 system_roots: Some(true),
1015 ..Default::default()
1016 };
1017 assert!(matches!(with_system.build(), Err(Error::FingerprintWithRoots)));
1018
1019 let with_custom = Client {
1022 fingerprint: vec![fingerprint],
1023 root: vec![PathBuf::from("/does-not-exist.pem")],
1024 ..Default::default()
1025 };
1026 assert!(matches!(with_custom.build(), Err(Error::FingerprintWithRoots)));
1027 }
1028
1029 fn self_signed_root() -> (tempfile::NamedTempFile, PathBuf) {
1032 use std::io::Write;
1033 let key = rcgen::KeyPair::generate().unwrap();
1034 let params = rcgen::CertificateParams::new(vec!["localhost".to_string()]).unwrap();
1035 let cert = params.self_signed(&key).unwrap();
1036 let mut file = tempfile::NamedTempFile::new().unwrap();
1037 file.write_all(cert.pem().as_bytes()).unwrap();
1038 let path = file.path().to_path_buf();
1039 (file, path)
1040 }
1041
1042 #[test]
1043 fn build_uses_platform_verifier_by_default() {
1044 assert!(Client::default().build().is_ok());
1047 }
1048
1049 #[test]
1050 fn build_with_custom_roots_only() {
1051 let (_keep, path) = self_signed_root();
1054 let config = Client {
1055 root: vec![path],
1056 ..Default::default()
1057 };
1058 assert!(config.build().is_ok());
1059 }
1060
1061 #[test]
1062 fn build_with_custom_and_system_roots() {
1063 let (_keep, path) = self_signed_root();
1066 let config = Client {
1067 root: vec![path],
1068 system_roots: Some(true),
1069 ..Default::default()
1070 };
1071 assert!(config.build().is_ok());
1072 }
1073}
1074
1075#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
1078#[derive(Debug)]
1079pub(crate) struct ServeCerts {
1080 pub info: Arc<RwLock<Info>>,
1081 provider: crypto::Provider,
1082}
1083
1084#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
1085impl ServeCerts {
1086 pub fn new(provider: crypto::Provider) -> Self {
1087 Self {
1088 info: Arc::new(RwLock::new(Info::default())),
1089 provider,
1090 }
1091 }
1092
1093 pub fn load_certs(&self, config: &Server) -> Result<()> {
1094 if config.cert.len() != config.key.len() {
1095 return Err(Error::CertKeyCountMismatch);
1096 }
1097 if config.cert.is_empty() && config.generate.is_empty() {
1098 return Err(Error::NoCertSource);
1099 }
1100
1101 let mut certs = Vec::new();
1102
1103 for (cert, key) in config.cert.iter().zip(config.key.iter()) {
1105 certs.push(Arc::new(self.load(cert, key)?));
1106 }
1107
1108 if !config.generate.is_empty() {
1110 certs.push(Arc::new(self.generate(&config.generate)?));
1111 }
1112
1113 self.set_certs(certs);
1114 Ok(())
1115 }
1116
1117 fn load(&self, chain_path: &Path, key_path: &Path) -> Result<rustls::sign::CertifiedKey> {
1119 let chain = read_certs(chain_path)?;
1120 if chain.is_empty() {
1121 return Err(Error::Empty);
1122 }
1123
1124 let key = PrivateKeyDer::from_pem_file(key_path).map_err(Error::Key)?;
1126 let key = self.provider.key_provider.load_private_key(key)?;
1127
1128 let certified_key = rustls::sign::CertifiedKey::new(chain, key);
1129
1130 certified_key.keys_match().map_err(|source| Error::KeyMismatch {
1131 key: key_path.to_path_buf(),
1132 cert: chain_path.to_path_buf(),
1133 source,
1134 })?;
1135
1136 Ok(certified_key)
1137 }
1138
1139 #[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
1140 fn generate(&self, hostnames: &[String]) -> Result<rustls::sign::CertifiedKey> {
1141 let key_pair = rcgen::KeyPair::generate()?;
1142
1143 let mut params = rcgen::CertificateParams::new(hostnames)?;
1144
1145 params.not_before = ::time::OffsetDateTime::now_utc() - ::time::Duration::days(1);
1148 params.not_after = params.not_before + ::time::Duration::days(14);
1149
1150 let cert = params.self_signed(&key_pair)?;
1152
1153 let key_der = key_pair.serialized_der().to_vec();
1155 let key_der = PrivatePkcs8KeyDer::from(key_der);
1156 let key = self.provider.key_provider.load_private_key(key_der.into())?;
1157
1158 Ok(rustls::sign::CertifiedKey::new(vec![cert.into()], key))
1160 }
1161
1162 #[cfg(not(any(feature = "aws-lc-rs", feature = "ring")))]
1163 fn generate(&self, _hostnames: &[String]) -> Result<rustls::sign::CertifiedKey> {
1164 Err(Error::NoCryptoProvider)
1165 }
1166
1167 pub fn set_certs(&self, certs: Vec<Arc<rustls::sign::CertifiedKey>>) {
1169 let fingerprints = certs
1170 .iter()
1171 .map(|ck| {
1172 let fingerprint = crate::crypto::sha256(&self.provider, ck.cert[0].as_ref());
1173 hex::encode(fingerprint)
1174 })
1175 .collect();
1176
1177 let mut info = self.info.write().expect("info write lock poisoned");
1178 info.certs = certs;
1179 info.fingerprints = fingerprints;
1180 }
1181
1182 fn best_certificate(
1184 &self,
1185 client_hello: &rustls::server::ClientHello<'_>,
1186 ) -> Option<Arc<rustls::sign::CertifiedKey>> {
1187 let server_name = client_hello.server_name()?;
1188 let dns_name = rustls::pki_types::ServerName::try_from(server_name).ok()?;
1189
1190 for ck in self.info.read().expect("info read lock poisoned").certs.iter() {
1191 let leaf: webpki::EndEntityCert = ck
1192 .end_entity_cert()
1193 .expect("missing certificate")
1194 .try_into()
1195 .expect("failed to parse certificate");
1196
1197 if leaf.verify_is_valid_for_subject_name(&dns_name).is_ok() {
1198 return Some(ck.clone());
1199 }
1200 }
1201
1202 None
1203 }
1204}
1205
1206#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
1207impl rustls::server::ResolvesServerCert for ServeCerts {
1208 fn resolve(&self, client_hello: rustls::server::ClientHello<'_>) -> Option<Arc<rustls::sign::CertifiedKey>> {
1209 if let Some(cert) = self.best_certificate(&client_hello) {
1210 return Some(cert);
1211 }
1212
1213 tracing::warn!(server_name = ?client_hello.server_name(), "no SNI certificate found");
1216
1217 self.info
1218 .read()
1219 .expect("info read lock poisoned")
1220 .certs
1221 .first()
1222 .cloned()
1223 }
1224}
1225
1226#[cfg(any(feature = "quinn", feature = "noq"))]
1234pub(crate) async fn reload_certs(certs: Arc<ServeCerts>, tls_config: Server) {
1235 let paths: Vec<PathBuf> = tls_config.cert.iter().chain(tls_config.key.iter()).cloned().collect();
1236 if paths.is_empty() {
1237 return;
1238 }
1239
1240 let mut watcher = match crate::watch::FileWatcher::new(&paths) {
1241 Ok(watcher) => watcher,
1242 Err(err) => {
1243 tracing::error!(%err, "failed to watch certificate files; hot reload disabled");
1244 return;
1245 }
1246 };
1247
1248 loop {
1249 watcher.changed().await;
1250 tracing::info!("reloading server certificates");
1251
1252 if let Err(err) = certs.load_certs(&tls_config) {
1253 tracing::warn!(%err, "failed to reload server certificates");
1254 }
1255 }
1256}