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(
80 "--client-tls-disable-verify cannot be combined with --client-tls-fingerprint, --client-tls-root or --client-tls-system-roots: it accepts every certificate, so the trust material would be ignored"
81 )]
82 DisableVerifyWithTrust,
83
84 #[error("failed to add root certificate")]
86 AddRoot(#[source] rustls::Error),
87
88 #[cfg(target_os = "android")]
90 #[error("failed to initialize the Android platform verifier")]
91 AndroidInit(#[source] jni::errors::Error),
92
93 #[error("failed to configure client certificate")]
95 ClientAuth(#[source] rustls::Error),
96
97 #[error("both --client-tls-cert and --client-tls-key must be provided")]
99 IncompleteClientAuth,
100
101 #[error("must provide both cert and key")]
103 CertKeyCountMismatch,
104
105 #[error("must provide at least one cert/key pair or generate entry")]
107 NoCertSource,
108
109 #[error("private key {} doesn't match certificate {}", key.display(), cert.display())]
111 KeyMismatch {
112 key: PathBuf,
114 cert: PathBuf,
116 #[source]
118 source: rustls::Error,
119 },
120
121 #[error(transparent)]
123 Rustls(#[from] rustls::Error),
124
125 #[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
127 #[error("failed to build client certificate verifier")]
128 ClientVerifier(#[source] rustls::server::VerifierBuilderError),
129
130 #[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
132 #[error(transparent)]
133 Rcgen(#[from] rcgen::Error),
134
135 #[error("no crypto provider available; enable aws-lc-rs or ring feature")]
137 NoCryptoProvider,
138}
139
140pub type Result<T> = std::result::Result<T, Error>;
142
143pub fn parse_fingerprint(value: &str) -> Result<[u8; 32]> {
145 let bytes = hex::decode(value.trim()).map_err(Error::Fingerprint)?;
146 bytes.try_into().map_err(|v: Vec<u8>| Error::FingerprintLength(v.len()))
147}
148
149pub(crate) fn read_certs(path: &Path) -> Result<Vec<CertificateDer<'static>>> {
151 let file = fs::File::open(path).map_err(Error::Open)?;
152 let mut reader = io::BufReader::new(file);
153 CertificateDer::pem_reader_iter(&mut reader)
154 .collect::<std::result::Result<_, _>>()
155 .map_err(Error::Read)
156}
157
158#[serde_with::serde_as]
162#[derive(Clone, Default, Debug, clap::Args, serde::Serialize, serde::Deserialize)]
163#[serde(default, deny_unknown_fields)]
164#[group(id = "tls-client")]
165#[non_exhaustive]
166pub struct Client {
167 #[serde(skip_serializing_if = "Vec::is_empty")]
177 #[arg(id = "client-tls-root", long = "client-tls-root", env = "MOQ_CLIENT_TLS_ROOT")]
178 #[serde_as(as = "serde_with::OneOrMany<_>")]
179 pub root: Vec<PathBuf>,
180
181 #[serde(skip_serializing_if = "Option::is_none")]
188 #[arg(
189 id = "client-tls-system-roots",
190 long = "client-tls-system-roots",
191 env = "MOQ_CLIENT_TLS_SYSTEM_ROOTS",
192 default_missing_value = "true",
193 num_args = 0..=1,
194 require_equals = true,
195 value_parser = clap::value_parser!(bool),
196 )]
197 pub system_roots: Option<bool>,
198
199 #[serde(skip_serializing_if = "Vec::is_empty")]
210 #[arg(
211 id = "client-tls-fingerprint",
212 long = "client-tls-fingerprint",
213 env = "MOQ_CLIENT_TLS_FINGERPRINT"
214 )]
215 #[serde_as(as = "serde_with::OneOrMany<_>")]
216 pub fingerprint: Vec<String>,
217
218 #[serde(skip_serializing_if = "Option::is_none")]
223 #[arg(id = "client-tls-cert", long = "client-tls-cert", env = "MOQ_CLIENT_TLS_CERT")]
224 pub cert: Option<PathBuf>,
225
226 #[serde(skip_serializing_if = "Option::is_none")]
231 #[arg(id = "client-tls-key", long = "client-tls-key", env = "MOQ_CLIENT_TLS_KEY")]
232 pub key: Option<PathBuf>,
233
234 #[serde(skip_serializing_if = "Option::is_none")]
238 #[arg(
239 id = "client-tls-disable-verify",
240 long = "client-tls-disable-verify",
241 env = "MOQ_CLIENT_TLS_DISABLE_VERIFY",
242 default_missing_value = "true",
243 num_args = 0..=1,
244 require_equals = true,
245 value_parser = clap::value_parser!(bool),
246 )]
247 pub disable_verify: Option<bool>,
248
249 #[serde(skip_serializing_if = "Option::is_none")]
254 #[arg(
255 id = "client-tls-host-name",
256 long = "client-tls-host-name",
257 env = "MOQ_CLIENT_TLS_HOST_NAME"
258 )]
259 pub host_name: Option<String>,
260
261 #[command(flatten)]
265 #[serde(skip)]
266 deprecated: Deprecated,
267}
268
269#[derive(Clone, Default, Debug, clap::Args)]
274struct Deprecated {
275 #[arg(long = "tls-root", hide = true)]
276 root: Vec<PathBuf>,
277
278 #[arg(
279 long = "tls-system-roots",
280 hide = true,
281 default_missing_value = "true",
282 num_args = 0..=1,
283 require_equals = true,
284 value_parser = clap::value_parser!(bool),
285 )]
286 system_roots: Option<bool>,
287
288 #[arg(long = "tls-fingerprint", hide = true)]
289 fingerprint: Vec<String>,
290
291 #[arg(
292 long = "tls-disable-verify",
293 hide = true,
294 default_missing_value = "true",
295 num_args = 0..=1,
296 require_equals = true,
297 value_parser = clap::value_parser!(bool),
298 )]
299 disable_verify: Option<bool>,
300}
301
302#[derive(Clone)]
309pub(crate) enum Verification {
310 Disabled,
312
313 Fingerprints(Vec<[u8; 32]>),
316
317 Roots {
322 custom: Vec<CertificateDer<'static>>,
323 system: bool,
324 },
325}
326
327impl Client {
328 pub(crate) fn warn_deprecated(&self) {
331 if !self.deprecated.root.is_empty() {
332 tracing::warn!("--tls-root is deprecated; use --client-tls-root");
333 }
334 if self.deprecated.system_roots.is_some() {
335 tracing::warn!("--tls-system-roots is deprecated; use --client-tls-system-roots");
336 }
337 if !self.deprecated.fingerprint.is_empty() {
338 tracing::warn!("--tls-fingerprint is deprecated; use --client-tls-fingerprint");
339 }
340 if self.deprecated.disable_verify.is_some() {
341 tracing::warn!("--tls-disable-verify is deprecated; use --client-tls-disable-verify");
342 }
343 }
344
345 pub(crate) fn effective_root(&self) -> Vec<PathBuf> {
347 let mut root = self.root.clone();
348 root.extend(self.deprecated.root.iter().cloned());
349 root
350 }
351
352 pub(crate) fn effective_fingerprint(&self) -> Vec<String> {
354 let mut fp = self.fingerprint.clone();
355 fp.extend(self.deprecated.fingerprint.iter().cloned());
356 fp
357 }
358
359 pub(crate) fn effective_system_roots(&self) -> Option<bool> {
361 self.system_roots.or(self.deprecated.system_roots)
362 }
363
364 pub(crate) fn effective_disable_verify(&self) -> Option<bool> {
366 self.disable_verify.or(self.deprecated.disable_verify)
367 }
368
369 pub(crate) fn verification(&self) -> Result<Verification> {
386 self.warn_deprecated();
387
388 let fingerprints = self.fingerprints()?;
389 let roots = self.effective_root();
390 let system_roots = self.effective_system_roots();
391
392 if self.effective_disable_verify().unwrap_or_default() {
393 if !fingerprints.is_empty() || !roots.is_empty() || system_roots == Some(true) {
394 return Err(Error::DisableVerifyWithTrust);
395 }
396 return Ok(Verification::Disabled);
397 }
398
399 if !fingerprints.is_empty() {
400 if !roots.is_empty() || system_roots == Some(true) {
401 return Err(Error::FingerprintWithRoots);
402 }
403 return Ok(Verification::Fingerprints(fingerprints));
404 }
405
406 let system = system_roots.unwrap_or(roots.is_empty());
409
410 let mut custom = Vec::new();
411 for root in &roots {
412 let certs = read_certs(root)?;
413 if certs.is_empty() {
414 return Err(Error::EmptyRoots(root.clone()));
415 }
416 custom.extend(certs);
417 }
418
419 if !system && custom.is_empty() {
424 return Err(Error::NoRoots);
425 }
426
427 Ok(Verification::Roots { custom, system })
428 }
429
430 pub(crate) fn allows_http_bootstrap(&self) -> bool {
439 self.effective_fingerprint().is_empty() && !self.effective_disable_verify().unwrap_or_default()
440 }
441
442 fn fingerprints(&self) -> Result<Vec<[u8; 32]>> {
444 self.effective_fingerprint()
445 .iter()
446 .map(|fp| parse_fingerprint(fp))
447 .collect()
448 }
449
450 pub fn build(&self) -> Result<rustls::ClientConfig> {
455 let provider = crypto::provider();
456 let verification = self.verification()?;
457
458 let builder = rustls::ClientConfig::builder_with_provider(provider.clone())
461 .with_protocol_versions(&[&rustls::version::TLS13, &rustls::version::TLS12])?;
462
463 let builder = match &verification {
466 Verification::Roots { custom, system: true } => Self::system_verifier(builder, custom, &provider)?,
467 Verification::Roots { custom, system: false } => builder.with_root_certificates(root_store(custom)?),
468 Verification::Disabled | Verification::Fingerprints(_) => {
469 builder.with_root_certificates(rustls::RootCertStore::empty())
470 }
471 };
472
473 let mut tls = self.with_client_auth(builder)?;
474
475 match verification {
476 Verification::Disabled => {
477 tracing::warn!(
478 "TLS server certificate verification is disabled; A man-in-the-middle attack is possible."
479 );
480 tls.dangerous()
481 .set_certificate_verifier(Arc::new(NoCertificateVerification(provider)));
482 }
483 Verification::Fingerprints(fingerprints) => {
484 let fingerprints = fingerprints.into_iter().map(|fp| fp.to_vec()).collect();
485 let verifier = FingerprintVerifier::new(provider, fingerprints);
486 tls.dangerous().set_certificate_verifier(Arc::new(verifier));
487 }
488 Verification::Roots { .. } => {}
490 }
491
492 Ok(tls)
493 }
494
495 fn system_verifier(
503 builder: rustls::ConfigBuilder<rustls::ClientConfig, rustls::WantsVerifier>,
504 custom: &[CertificateDer<'static>],
505 provider: &crypto::Provider,
506 ) -> Result<rustls::ConfigBuilder<rustls::ClientConfig, rustls::client::WantsClientCert>> {
507 #[cfg(target_os = "android")]
512 {
513 if ANDROID_INITIALIZED.load(std::sync::atomic::Ordering::Acquire) && custom.is_empty() {
514 let verifier = rustls_platform_verifier::Verifier::new(provider.clone())?;
515 return Ok(builder.dangerous().with_custom_certificate_verifier(Arc::new(verifier)));
516 }
517
518 let mut roots = rustls::RootCertStore::empty();
519 roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
520 for cert in custom {
521 roots.add(cert.clone()).map_err(Error::AddRoot)?;
522 }
523 Ok(builder.with_root_certificates(roots))
524 }
525
526 #[cfg(not(target_os = "android"))]
527 {
528 let verifier = if custom.is_empty() {
529 rustls_platform_verifier::Verifier::new(provider.clone())?
530 } else {
531 rustls_platform_verifier::Verifier::new_with_extra_roots(custom.iter().cloned(), provider.clone())?
532 };
533 Ok(builder.dangerous().with_custom_certificate_verifier(Arc::new(verifier)))
534 }
535 }
536
537 fn with_client_auth(
539 &self,
540 builder: rustls::ConfigBuilder<rustls::ClientConfig, rustls::client::WantsClientCert>,
541 ) -> Result<rustls::ClientConfig> {
542 Ok(match (&self.cert, &self.key) {
543 (Some(cert_path), Some(key_path)) => {
544 let cert_pem = fs::read(cert_path).map_err(Error::ReadFile)?;
545 let chain: Vec<CertificateDer<'static>> = CertificateDer::pem_slice_iter(&cert_pem)
546 .collect::<std::result::Result<_, _>>()
547 .map_err(Error::Read)?;
548 if chain.is_empty() {
549 return Err(Error::Empty);
550 }
551 let key_pem = fs::read(key_path).map_err(Error::ReadFile)?;
552 let key = PrivateKeyDer::from_pem_slice(&key_pem).map_err(Error::Key)?;
553 builder.with_client_auth_cert(chain, key).map_err(Error::ClientAuth)?
554 }
555 (None, None) => builder.with_no_client_auth(),
556 _ => return Err(Error::IncompleteClientAuth),
557 })
558 }
559}
560
561fn root_store(custom: &[CertificateDer<'static>]) -> Result<rustls::RootCertStore> {
563 let mut roots = rustls::RootCertStore::empty();
564 for cert in custom {
565 roots.add(cert.clone()).map_err(Error::AddRoot)?;
566 }
567 Ok(roots)
568}
569
570#[cfg(target_os = "android")]
572static ANDROID_INITIALIZED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
573
574#[cfg(target_os = "android")]
586pub fn init_android(env: &mut jni::Env, context: jni::objects::JObject) -> Result<()> {
587 rustls_platform_verifier::android::init_with_env(env, context).map_err(Error::AndroidInit)?;
588 ANDROID_INITIALIZED.store(true, std::sync::atomic::Ordering::Release);
589 Ok(())
590}
591
592#[serde_with::serde_as]
601#[derive(clap::Args, Clone, Default, Debug, serde::Serialize, serde::Deserialize)]
602#[serde(deny_unknown_fields)]
603#[group(id = "tls-server")]
604#[non_exhaustive]
605pub struct Server {
606 #[arg(long = "tls-cert", id = "tls-cert", env = "MOQ_SERVER_TLS_CERT")]
608 #[serde(default, skip_serializing_if = "Vec::is_empty")]
609 #[serde_as(as = "serde_with::OneOrMany<_>")]
610 pub cert: Vec<PathBuf>,
611
612 #[arg(long = "tls-key", id = "tls-key", env = "MOQ_SERVER_TLS_KEY")]
614 #[serde(default, skip_serializing_if = "Vec::is_empty")]
615 #[serde_as(as = "serde_with::OneOrMany<_>")]
616 pub key: Vec<PathBuf>,
617
618 #[arg(
621 long = "tls-generate",
622 id = "tls-generate",
623 value_delimiter = ',',
624 env = "MOQ_SERVER_TLS_GENERATE"
625 )]
626 #[serde(default, skip_serializing_if = "Vec::is_empty")]
627 #[serde_as(as = "serde_with::OneOrMany<_>")]
628 pub generate: Vec<String>,
629
630 #[arg(
640 long = "server-tls-root",
641 id = "server-tls-root",
642 value_delimiter = ',',
643 env = "MOQ_SERVER_TLS_ROOT"
644 )]
645 #[serde(default, skip_serializing_if = "Vec::is_empty")]
646 #[serde_as(as = "serde_with::OneOrMany<_>")]
647 pub root: Vec<PathBuf>,
648}
649
650impl Server {
651 pub fn load_roots(&self) -> Result<rustls::RootCertStore> {
653 let mut roots = rustls::RootCertStore::empty();
654 for path in &self.root {
655 let certs = read_certs(path)?;
656 if certs.is_empty() {
657 return Err(Error::Empty);
658 }
659 for cert in certs {
660 roots.add(cert).map_err(Error::AddRoot)?;
661 }
662 }
663 Ok(roots)
664 }
665
666 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
675 pub fn server_config(&self, alpn: Vec<Vec<u8>>) -> Result<Arc<rustls::ServerConfig>> {
676 server_config(self, alpn)
677 }
678}
679
680#[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
682fn server_config(config: &Server, alpn: Vec<Vec<u8>>) -> Result<Arc<rustls::ServerConfig>> {
683 let provider = crypto::provider();
684
685 let certs = ServeCerts::new(provider.clone());
686 certs.load_certs(config)?;
687 let certs = Arc::new(certs);
688
689 let builder =
691 rustls::ServerConfig::builder_with_provider(provider.clone()).with_safe_default_protocol_versions()?;
692
693 let mut tls = if config.root.is_empty() {
694 builder.with_no_client_auth().with_cert_resolver(certs)
695 } else {
696 let roots = config.load_roots()?;
697 let verifier = rustls::server::WebPkiClientVerifier::builder_with_provider(Arc::new(roots), provider)
698 .allow_unauthenticated()
699 .build()
700 .map_err(Error::ClientVerifier)?;
701 builder.with_client_cert_verifier(verifier).with_cert_resolver(certs)
702 };
703
704 tls.alpn_protocols = alpn;
705 Ok(Arc::new(tls))
706}
707
708#[derive(Clone)]
715pub struct PeerIdentity {
716 chain: Vec<CertificateDer<'static>>,
717}
718
719impl PeerIdentity {
720 #[cfg(any(feature = "quinn", feature = "noq"))]
724 pub(crate) fn from_any(identity: Option<Box<dyn std::any::Any>>) -> Option<Self> {
725 let chain = identity?.downcast::<Vec<CertificateDer<'static>>>().ok()?;
726 Some(Self { chain: *chain })
727 }
728
729 #[cfg(feature = "quiche")]
731 pub(crate) fn from_chain(chain: Vec<CertificateDer<'static>>) -> Self {
732 Self { chain }
733 }
734
735 pub fn chain(&self) -> &[CertificateDer<'static>] {
741 &self.chain
742 }
743
744 pub fn expiry(&self) -> Option<std::time::SystemTime> {
747 use std::time::{Duration, UNIX_EPOCH};
748
749 let leaf = self.chain.first()?;
750 let (_, cert) = x509_parser::parse_x509_certificate(leaf).ok()?;
751 let secs = u64::try_from(cert.validity().not_after.timestamp()).ok()?;
752 Some(UNIX_EPOCH + Duration::from_secs(secs))
753 }
754}
755
756#[derive(Debug, Default)]
758pub(crate) struct Info {
759 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
760 pub(crate) certs: Vec<Arc<rustls::sign::CertifiedKey>>,
761 pub(crate) fingerprints: Vec<String>,
762}
763
764#[derive(Clone, Debug)]
770pub struct Certificates {
771 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
772 info: Arc<RwLock<Info>>,
773}
774
775impl Certificates {
776 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
777 pub(crate) fn new(info: Arc<RwLock<Info>>) -> Self {
778 Self { info }
779 }
780
781 pub(crate) fn empty() -> Self {
783 Self {
784 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
785 info: Arc::new(RwLock::new(Info::default())),
786 }
787 }
788
789 pub fn fingerprints(&self) -> Vec<String> {
795 #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))]
796 {
797 let info = self.info.read().unwrap_or_else(std::sync::PoisonError::into_inner);
800 info.fingerprints.clone()
801 }
802 #[cfg(not(any(feature = "noq", feature = "quinn", feature = "quiche")))]
803 Vec::new()
804 }
805}
806
807#[derive(Debug)]
810struct NoCertificateVerification(crypto::Provider);
811
812impl rustls::client::danger::ServerCertVerifier for NoCertificateVerification {
813 fn verify_server_cert(
814 &self,
815 _end_entity: &CertificateDer<'_>,
816 _intermediates: &[CertificateDer<'_>],
817 _server_name: &ServerName<'_>,
818 _ocsp: &[u8],
819 _now: UnixTime,
820 ) -> std::result::Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
821 Ok(rustls::client::danger::ServerCertVerified::assertion())
822 }
823
824 fn verify_tls12_signature(
825 &self,
826 message: &[u8],
827 cert: &CertificateDer<'_>,
828 dss: &rustls::DigitallySignedStruct,
829 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
830 rustls::crypto::verify_tls12_signature(message, cert, dss, &self.0.signature_verification_algorithms)
831 }
832
833 fn verify_tls13_signature(
834 &self,
835 message: &[u8],
836 cert: &CertificateDer<'_>,
837 dss: &rustls::DigitallySignedStruct,
838 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
839 rustls::crypto::verify_tls13_signature(message, cert, dss, &self.0.signature_verification_algorithms)
840 }
841
842 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
843 self.0.signature_verification_algorithms.supported_schemes()
844 }
845}
846
847#[derive(Debug)]
850pub(crate) struct FingerprintVerifier {
851 provider: crypto::Provider,
852 fingerprints: Vec<Vec<u8>>,
853}
854
855impl FingerprintVerifier {
856 pub fn new(provider: crypto::Provider, fingerprints: Vec<Vec<u8>>) -> Self {
857 Self { provider, fingerprints }
858 }
859}
860
861impl rustls::client::danger::ServerCertVerifier for FingerprintVerifier {
862 fn verify_server_cert(
863 &self,
864 end_entity: &CertificateDer<'_>,
865 _intermediates: &[CertificateDer<'_>],
866 _server_name: &ServerName<'_>,
867 _ocsp: &[u8],
868 _now: UnixTime,
869 ) -> std::result::Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
870 let fingerprint = crypto::sha256(&self.provider, end_entity);
871 if self.fingerprints.iter().any(|fp| fingerprint.as_ref() == fp.as_slice()) {
872 Ok(rustls::client::danger::ServerCertVerified::assertion())
873 } else {
874 Err(rustls::Error::General("fingerprint mismatch".into()))
875 }
876 }
877
878 fn verify_tls12_signature(
879 &self,
880 message: &[u8],
881 cert: &CertificateDer<'_>,
882 dss: &rustls::DigitallySignedStruct,
883 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
884 rustls::crypto::verify_tls12_signature(message, cert, dss, &self.provider.signature_verification_algorithms)
885 }
886
887 fn verify_tls13_signature(
888 &self,
889 message: &[u8],
890 cert: &CertificateDer<'_>,
891 dss: &rustls::DigitallySignedStruct,
892 ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
893 rustls::crypto::verify_tls13_signature(message, cert, dss, &self.provider.signature_verification_algorithms)
894 }
895
896 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
897 self.provider.signature_verification_algorithms.supported_schemes()
898 }
899}
900
901#[cfg(test)]
902#[cfg(all(any(feature = "quinn", feature = "noq", feature = "quiche"), feature = "aws-lc-rs"))]
903mod tests {
904 #[test]
907 fn disable_verify_rejects_trust_material() {
908 let insecure = Client {
909 disable_verify: Some(true),
910 ..Default::default()
911 };
912 assert!(matches!(insecure.verification(), Ok(Verification::Disabled)));
913
914 let with_fingerprint = Client {
915 disable_verify: Some(true),
916 fingerprint: vec!["ab".repeat(32)],
917 ..Default::default()
918 };
919 assert!(matches!(
920 with_fingerprint.verification(),
921 Err(Error::DisableVerifyWithTrust)
922 ));
923
924 let with_root = Client {
925 disable_verify: Some(true),
926 root: vec!["/tmp/root.pem".into()],
927 ..Default::default()
928 };
929 assert!(matches!(with_root.verification(), Err(Error::DisableVerifyWithTrust)));
930
931 let with_system_roots = Client {
932 disable_verify: Some(true),
933 system_roots: Some(true),
934 ..Default::default()
935 };
936 assert!(matches!(
937 with_system_roots.verification(),
938 Err(Error::DisableVerifyWithTrust)
939 ));
940
941 let without_system_roots = Client {
942 disable_verify: Some(true),
943 system_roots: Some(false),
944 ..Default::default()
945 };
946 assert!(matches!(
947 without_system_roots.verification(),
948 Ok(Verification::Disabled)
949 ));
950 }
951
952 use super::*;
953 use rustls::client::danger::ServerCertVerifier;
954 use rustls::pki_types::ServerName;
955
956 fn self_signed() -> CertificateDer<'static> {
957 let key = rcgen::KeyPair::generate().unwrap();
958 let params = rcgen::CertificateParams::new(vec!["localhost".to_string()]).unwrap();
959 params.self_signed(&key).unwrap().into()
960 }
961
962 #[cfg(any(feature = "quinn", feature = "noq"))]
963 #[test]
964 fn peer_identity_expiry_reads_not_after() {
965 let not_after = ::time::OffsetDateTime::from_unix_timestamp(2_000_000_000).unwrap();
967
968 let key = rcgen::KeyPair::generate().unwrap();
969 let mut params = rcgen::CertificateParams::new(vec!["localhost".to_string()]).unwrap();
970 params.not_after = not_after;
971 let cert: CertificateDer<'static> = params.self_signed(&key).unwrap().into();
972
973 let identity: Box<dyn std::any::Any> = Box::new(vec![cert]);
975 let parsed = PeerIdentity::from_any(Some(identity)).expect("chain parsed");
976 let expiry = parsed.expiry().expect("expiry parsed");
977 assert_eq!(
978 expiry.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(),
979 2_000_000_000
980 );
981 }
982
983 #[cfg(any(feature = "quinn", feature = "noq"))]
984 #[test]
985 fn peer_identity_none_without_chain() {
986 assert!(PeerIdentity::from_any(None).is_none());
987 let bogus: Box<dyn std::any::Any> = Box::new(42u32);
989 assert!(PeerIdentity::from_any(Some(bogus)).is_none());
990 }
991
992 #[test]
993 fn fingerprint_verifier_matches_and_rejects() {
994 let provider = crypto::provider();
995 let cert = self_signed();
996 let fingerprint = crypto::sha256(&provider, cert.as_ref()).as_ref().to_vec();
997
998 let name = ServerName::try_from("localhost").unwrap();
999 let now = UnixTime::now();
1000
1001 let verifier = FingerprintVerifier::new(provider.clone(), vec![fingerprint]);
1002 assert!(verifier.verify_server_cert(&cert, &[], &name, &[], now).is_ok());
1003
1004 let other = self_signed();
1006 assert!(verifier.verify_server_cert(&other, &[], &name, &[], now).is_err());
1007 }
1008
1009 #[test]
1010 fn build_installs_fingerprint_verifier() {
1011 let cert = self_signed();
1012 let fingerprint = hex::encode(crypto::sha256(&crypto::provider(), cert.as_ref()));
1013
1014 let config = Client {
1016 fingerprint: vec![fingerprint],
1017 ..Default::default()
1018 };
1019 assert!(config.build().is_ok());
1020 }
1021
1022 #[test]
1023 fn build_rejects_invalid_fingerprint_hex() {
1024 let config = Client {
1025 fingerprint: vec!["not-hex".to_string()],
1026 ..Default::default()
1027 };
1028 assert!(matches!(config.build(), Err(Error::Fingerprint(_))));
1029 }
1030
1031 #[test]
1032 fn build_rejects_wrong_length_fingerprint() {
1033 let config = Client {
1035 fingerprint: vec!["abcd".to_string()],
1036 ..Default::default()
1037 };
1038 assert!(matches!(config.build(), Err(Error::FingerprintLength(2))));
1039 }
1040
1041 #[test]
1042 fn build_rejects_no_roots() {
1043 let config = Client {
1046 system_roots: Some(false),
1047 ..Default::default()
1048 };
1049 assert!(matches!(config.build(), Err(Error::NoRoots)));
1050 }
1051
1052 #[test]
1053 fn build_allows_no_roots_when_verification_overridden() {
1054 let config = Client {
1056 system_roots: Some(false),
1057 disable_verify: Some(true),
1058 ..Default::default()
1059 };
1060 assert!(config.build().is_ok());
1061
1062 let cert = self_signed();
1064 let fingerprint = hex::encode(crypto::sha256(&crypto::provider(), cert.as_ref()));
1065 let config = Client {
1066 system_roots: Some(false),
1067 fingerprint: vec![fingerprint],
1068 ..Default::default()
1069 };
1070 assert!(config.build().is_ok());
1071 }
1072
1073 #[test]
1074 fn build_rejects_fingerprint_with_roots() {
1075 let cert = self_signed();
1076 let fingerprint = hex::encode(crypto::sha256(&crypto::provider(), cert.as_ref()));
1077
1078 let with_system = Client {
1081 fingerprint: vec![fingerprint.clone()],
1082 system_roots: Some(true),
1083 ..Default::default()
1084 };
1085 assert!(matches!(with_system.build(), Err(Error::FingerprintWithRoots)));
1086
1087 let with_custom = Client {
1090 fingerprint: vec![fingerprint],
1091 root: vec![PathBuf::from("/does-not-exist.pem")],
1092 ..Default::default()
1093 };
1094 assert!(matches!(with_custom.build(), Err(Error::FingerprintWithRoots)));
1095 }
1096
1097 fn self_signed_root() -> (tempfile::NamedTempFile, PathBuf) {
1100 use std::io::Write;
1101 let key = rcgen::KeyPair::generate().unwrap();
1102 let params = rcgen::CertificateParams::new(vec!["localhost".to_string()]).unwrap();
1103 let cert = params.self_signed(&key).unwrap();
1104 let mut file = tempfile::NamedTempFile::new().unwrap();
1105 file.write_all(cert.pem().as_bytes()).unwrap();
1106 let path = file.path().to_path_buf();
1107 (file, path)
1108 }
1109
1110 #[test]
1111 fn build_uses_platform_verifier_by_default() {
1112 assert!(Client::default().build().is_ok());
1115 }
1116
1117 #[test]
1118 fn build_with_custom_roots_only() {
1119 let (_keep, path) = self_signed_root();
1122 let config = Client {
1123 root: vec![path],
1124 ..Default::default()
1125 };
1126 assert!(config.build().is_ok());
1127 }
1128
1129 #[test]
1130 fn build_with_custom_and_system_roots() {
1131 let (_keep, path) = self_signed_root();
1134 let config = Client {
1135 root: vec![path],
1136 system_roots: Some(true),
1137 ..Default::default()
1138 };
1139 assert!(config.build().is_ok());
1140 }
1141}
1142
1143#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
1146#[derive(Debug)]
1147pub(crate) struct ServeCerts {
1148 pub info: Arc<RwLock<Info>>,
1149 provider: crypto::Provider,
1150}
1151
1152#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
1153impl ServeCerts {
1154 pub fn new(provider: crypto::Provider) -> Self {
1155 Self {
1156 info: Arc::new(RwLock::new(Info::default())),
1157 provider,
1158 }
1159 }
1160
1161 pub fn load_certs(&self, config: &Server) -> Result<()> {
1162 if config.cert.len() != config.key.len() {
1163 return Err(Error::CertKeyCountMismatch);
1164 }
1165 if config.cert.is_empty() && config.generate.is_empty() {
1166 return Err(Error::NoCertSource);
1167 }
1168
1169 let mut certs = Vec::new();
1170
1171 for (cert, key) in config.cert.iter().zip(config.key.iter()) {
1173 certs.push(Arc::new(self.load(cert, key)?));
1174 }
1175
1176 if !config.generate.is_empty() {
1178 certs.push(Arc::new(self.generate(&config.generate)?));
1179 }
1180
1181 self.set_certs(certs);
1182 Ok(())
1183 }
1184
1185 fn load(&self, chain_path: &Path, key_path: &Path) -> Result<rustls::sign::CertifiedKey> {
1187 let chain = read_certs(chain_path)?;
1188 if chain.is_empty() {
1189 return Err(Error::Empty);
1190 }
1191
1192 let key = PrivateKeyDer::from_pem_file(key_path).map_err(Error::Key)?;
1194 let key = self.provider.key_provider.load_private_key(key)?;
1195
1196 let certified_key = rustls::sign::CertifiedKey::new(chain, key);
1197
1198 certified_key.keys_match().map_err(|source| Error::KeyMismatch {
1199 key: key_path.to_path_buf(),
1200 cert: chain_path.to_path_buf(),
1201 source,
1202 })?;
1203
1204 Ok(certified_key)
1205 }
1206
1207 #[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
1208 fn generate(&self, hostnames: &[String]) -> Result<rustls::sign::CertifiedKey> {
1209 let key_pair = rcgen::KeyPair::generate()?;
1210
1211 let mut params = rcgen::CertificateParams::new(hostnames)?;
1212
1213 params.not_before = ::time::OffsetDateTime::now_utc() - ::time::Duration::days(1);
1216 params.not_after = params.not_before + ::time::Duration::days(14);
1217
1218 let cert = params.self_signed(&key_pair)?;
1220
1221 let key_der = key_pair.serialized_der().to_vec();
1223 let key_der = PrivatePkcs8KeyDer::from(key_der);
1224 let key = self.provider.key_provider.load_private_key(key_der.into())?;
1225
1226 Ok(rustls::sign::CertifiedKey::new(vec![cert.into()], key))
1228 }
1229
1230 #[cfg(not(any(feature = "aws-lc-rs", feature = "ring")))]
1231 fn generate(&self, _hostnames: &[String]) -> Result<rustls::sign::CertifiedKey> {
1232 Err(Error::NoCryptoProvider)
1233 }
1234
1235 pub fn set_certs(&self, certs: Vec<Arc<rustls::sign::CertifiedKey>>) {
1237 let fingerprints = certs
1238 .iter()
1239 .map(|ck| {
1240 let fingerprint = crate::crypto::sha256(&self.provider, ck.cert[0].as_ref());
1241 hex::encode(fingerprint)
1242 })
1243 .collect();
1244
1245 let mut info = self.info.write().expect("info write lock poisoned");
1246 info.certs = certs;
1247 info.fingerprints = fingerprints;
1248 }
1249
1250 fn best_certificate(
1252 &self,
1253 client_hello: &rustls::server::ClientHello<'_>,
1254 ) -> Option<Arc<rustls::sign::CertifiedKey>> {
1255 let server_name = client_hello.server_name()?;
1256 let dns_name = rustls::pki_types::ServerName::try_from(server_name).ok()?;
1257
1258 for ck in self.info.read().expect("info read lock poisoned").certs.iter() {
1259 let leaf: webpki::EndEntityCert = ck
1260 .end_entity_cert()
1261 .expect("missing certificate")
1262 .try_into()
1263 .expect("failed to parse certificate");
1264
1265 if leaf.verify_is_valid_for_subject_name(&dns_name).is_ok() {
1266 return Some(ck.clone());
1267 }
1268 }
1269
1270 None
1271 }
1272}
1273
1274#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche"))]
1275impl rustls::server::ResolvesServerCert for ServeCerts {
1276 fn resolve(&self, client_hello: rustls::server::ClientHello<'_>) -> Option<Arc<rustls::sign::CertifiedKey>> {
1277 if let Some(cert) = self.best_certificate(&client_hello) {
1278 return Some(cert);
1279 }
1280
1281 tracing::warn!(server_name = ?client_hello.server_name(), "no SNI certificate found");
1284
1285 self.info
1286 .read()
1287 .expect("info read lock poisoned")
1288 .certs
1289 .first()
1290 .cloned()
1291 }
1292}
1293
1294#[cfg(any(feature = "quinn", feature = "noq"))]
1302pub(crate) async fn reload_certs(certs: Arc<ServeCerts>, tls_config: Server) {
1303 let paths: Vec<PathBuf> = tls_config.cert.iter().chain(tls_config.key.iter()).cloned().collect();
1304 if paths.is_empty() {
1305 return;
1306 }
1307
1308 let mut watcher = match crate::watch::FileWatcher::new(&paths) {
1309 Ok(watcher) => watcher,
1310 Err(err) => {
1311 tracing::error!(%err, "failed to watch certificate files; hot reload disabled");
1312 return;
1313 }
1314 };
1315
1316 loop {
1317 watcher.changed().await;
1318 tracing::info!("reloading server certificates");
1319
1320 if let Err(err) = certs.load_certs(&tls_config) {
1321 tracing::warn!(%err, "failed to reload server certificates");
1322 }
1323 }
1324}