1#[cfg(feature = "__rustls")]
48use rustls::{
49 client::HandshakeSignatureValid, client::ServerCertVerified, client::ServerCertVerifier,
50 DigitallySignedStruct, Error as TLSError, ServerName,
51};
52use std::{
53 fmt,
54 io::{BufRead, BufReader},
55};
56
57#[derive(Clone)]
59pub struct Certificate {
60 #[cfg(feature = "native-tls-crate")]
61 native: native_tls_crate::Certificate,
62 #[cfg(feature = "__rustls")]
63 original: Cert,
64}
65
66#[cfg(feature = "__rustls")]
67#[derive(Clone)]
68enum Cert {
69 Der(Vec<u8>),
70 Pem(Vec<u8>),
71}
72
73#[derive(Clone)]
75pub struct Identity {
76 #[cfg_attr(not(any(feature = "native-tls", feature = "__rustls")), allow(unused))]
77 inner: ClientCert,
78}
79
80#[derive(Clone)]
81enum ClientCert {
82 #[cfg(feature = "native-tls")]
83 Pkcs12(native_tls_crate::Identity),
84 #[cfg(feature = "native-tls")]
85 Pkcs8(native_tls_crate::Identity),
86 #[cfg(feature = "__rustls")]
87 Pem {
88 key: rustls::PrivateKey,
89 certs: Vec<rustls::Certificate>,
90 },
91}
92
93impl Certificate {
94 pub fn from_der(der: &[u8]) -> crate::Result<Certificate> {
111 Ok(Certificate {
112 #[cfg(feature = "native-tls-crate")]
113 native: native_tls_crate::Certificate::from_der(der).map_err(crate::error::builder)?,
114 #[cfg(feature = "__rustls")]
115 original: Cert::Der(der.to_owned()),
116 })
117 }
118
119 pub fn from_pem(pem: &[u8]) -> crate::Result<Certificate> {
136 Ok(Certificate {
137 #[cfg(feature = "native-tls-crate")]
138 native: native_tls_crate::Certificate::from_pem(pem).map_err(crate::error::builder)?,
139 #[cfg(feature = "__rustls")]
140 original: Cert::Pem(pem.to_owned()),
141 })
142 }
143
144 pub fn from_pem_bundle(pem_bundle: &[u8]) -> crate::Result<Vec<Certificate>> {
162 let mut reader = BufReader::new(pem_bundle);
163
164 Self::read_pem_certs(&mut reader)?
165 .iter()
166 .map(|cert_vec| Certificate::from_der(&cert_vec))
167 .collect::<crate::Result<Vec<Certificate>>>()
168 }
169
170 #[cfg(feature = "native-tls-crate")]
171 pub(crate) fn add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder) {
172 tls.add_root_certificate(self.native);
173 }
174
175 #[cfg(feature = "__rustls")]
176 pub(crate) fn add_to_rustls(
177 self,
178 root_cert_store: &mut rustls::RootCertStore,
179 ) -> crate::Result<()> {
180 use std::io::Cursor;
181
182 match self.original {
183 Cert::Der(buf) => root_cert_store
184 .add(&rustls::Certificate(buf))
185 .map_err(crate::error::builder)?,
186 Cert::Pem(buf) => {
187 let mut reader = Cursor::new(buf);
188 let certs = Self::read_pem_certs(&mut reader)?;
189 for c in certs {
190 root_cert_store
191 .add(&rustls::Certificate(c))
192 .map_err(crate::error::builder)?;
193 }
194 }
195 }
196 Ok(())
197 }
198
199 fn read_pem_certs(reader: &mut impl BufRead) -> crate::Result<Vec<Vec<u8>>> {
200 rustls_pemfile::certs(reader)
201 .map_err(|_| crate::error::builder("invalid certificate encoding"))
202 }
203}
204
205impl Identity {
206 #[cfg(feature = "native-tls")]
238 pub fn from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity> {
239 Ok(Identity {
240 inner: ClientCert::Pkcs12(
241 native_tls_crate::Identity::from_pkcs12(der, password)
242 .map_err(crate::error::builder)?,
243 ),
244 })
245 }
246
247 #[cfg(feature = "native-tls")]
272 pub fn from_pkcs8_pem(pem: &[u8], key: &[u8]) -> crate::Result<Identity> {
273 Ok(Identity {
274 inner: ClientCert::Pkcs8(
275 native_tls_crate::Identity::from_pkcs8(pem, key).map_err(crate::error::builder)?,
276 ),
277 })
278 }
279
280 #[cfg(feature = "__rustls")]
306 pub fn from_pem(buf: &[u8]) -> crate::Result<Identity> {
307 use std::io::Cursor;
308
309 let (key, certs) = {
310 let mut pem = Cursor::new(buf);
311 let mut sk = Vec::<rustls::PrivateKey>::new();
312 let mut certs = Vec::<rustls::Certificate>::new();
313
314 for item in std::iter::from_fn(|| rustls_pemfile::read_one(&mut pem).transpose()) {
315 match item.map_err(|_| {
316 crate::error::builder(TLSError::General(String::from(
317 "Invalid identity PEM file",
318 )))
319 })? {
320 rustls_pemfile::Item::X509Certificate(cert) => {
321 certs.push(rustls::Certificate(cert))
322 }
323 rustls_pemfile::Item::PKCS8Key(key) => sk.push(rustls::PrivateKey(key)),
324 rustls_pemfile::Item::RSAKey(key) => sk.push(rustls::PrivateKey(key)),
325 rustls_pemfile::Item::ECKey(key) => sk.push(rustls::PrivateKey(key)),
326 _ => {
327 return Err(crate::error::builder(TLSError::General(String::from(
328 "No valid certificate was found",
329 ))))
330 }
331 }
332 }
333
334 if let (Some(sk), false) = (sk.pop(), certs.is_empty()) {
335 (sk, certs)
336 } else {
337 return Err(crate::error::builder(TLSError::General(String::from(
338 "private key or certificate not found",
339 ))));
340 }
341 };
342
343 Ok(Identity {
344 inner: ClientCert::Pem { key, certs },
345 })
346 }
347
348 #[cfg(feature = "native-tls")]
349 pub(crate) fn add_to_native_tls(
350 self,
351 tls: &mut native_tls_crate::TlsConnectorBuilder,
352 ) -> crate::Result<()> {
353 match self.inner {
354 ClientCert::Pkcs12(id) | ClientCert::Pkcs8(id) => {
355 tls.identity(id);
356 Ok(())
357 }
358 #[cfg(feature = "__rustls")]
359 ClientCert::Pem { .. } => Err(crate::error::builder("incompatible TLS identity type")),
360 }
361 }
362
363 #[cfg(feature = "__rustls")]
364 pub(crate) fn add_to_rustls(
365 self,
366 config_builder: rustls::ConfigBuilder<
367 rustls::ClientConfig,
368 rustls::client::WantsTransparencyPolicyOrClientCert,
369 >,
370 ) -> crate::Result<rustls::ClientConfig> {
371 match self.inner {
372 ClientCert::Pem { key, certs } => config_builder
373 .with_client_auth_cert(certs, key)
374 .map_err(crate::error::builder),
375 #[cfg(feature = "native-tls")]
376 ClientCert::Pkcs12(..) | ClientCert::Pkcs8(..) => {
377 Err(crate::error::builder("incompatible TLS identity type"))
378 }
379 }
380 }
381}
382
383impl fmt::Debug for Certificate {
384 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
385 f.debug_struct("Certificate").finish()
386 }
387}
388
389impl fmt::Debug for Identity {
390 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
391 f.debug_struct("Identity").finish()
392 }
393}
394
395#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
397pub struct Version(InnerVersion);
398
399#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
400#[non_exhaustive]
401enum InnerVersion {
402 Tls1_0,
403 Tls1_1,
404 Tls1_2,
405 Tls1_3,
406}
407
408impl Version {
411 pub const TLS_1_0: Version = Version(InnerVersion::Tls1_0);
413 pub const TLS_1_1: Version = Version(InnerVersion::Tls1_1);
415 pub const TLS_1_2: Version = Version(InnerVersion::Tls1_2);
417 pub const TLS_1_3: Version = Version(InnerVersion::Tls1_3);
419
420 #[cfg(feature = "default-tls")]
421 pub(crate) fn to_native_tls(self) -> Option<native_tls_crate::Protocol> {
422 match self.0 {
423 InnerVersion::Tls1_0 => Some(native_tls_crate::Protocol::Tlsv10),
424 InnerVersion::Tls1_1 => Some(native_tls_crate::Protocol::Tlsv11),
425 InnerVersion::Tls1_2 => Some(native_tls_crate::Protocol::Tlsv12),
426 InnerVersion::Tls1_3 => None,
427 }
428 }
429
430 #[cfg(feature = "__rustls")]
431 pub(crate) fn from_rustls(version: rustls::ProtocolVersion) -> Option<Self> {
432 match version {
433 rustls::ProtocolVersion::SSLv2 => None,
434 rustls::ProtocolVersion::SSLv3 => None,
435 rustls::ProtocolVersion::TLSv1_0 => Some(Self(InnerVersion::Tls1_0)),
436 rustls::ProtocolVersion::TLSv1_1 => Some(Self(InnerVersion::Tls1_1)),
437 rustls::ProtocolVersion::TLSv1_2 => Some(Self(InnerVersion::Tls1_2)),
438 rustls::ProtocolVersion::TLSv1_3 => Some(Self(InnerVersion::Tls1_3)),
439 _ => None,
440 }
441 }
442}
443
444pub(crate) enum TlsBackend {
445 #[allow(dead_code)]
447 #[cfg(feature = "default-tls")]
448 Default,
449 #[cfg(feature = "native-tls")]
450 BuiltNativeTls(native_tls_crate::TlsConnector),
451 #[cfg(feature = "__rustls")]
452 Rustls,
453 #[cfg(feature = "__rustls")]
454 BuiltRustls(rustls::ClientConfig),
455 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
456 UnknownPreconfigured,
457}
458
459impl fmt::Debug for TlsBackend {
460 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
461 match self {
462 #[cfg(feature = "default-tls")]
463 TlsBackend::Default => write!(f, "Default"),
464 #[cfg(feature = "native-tls")]
465 TlsBackend::BuiltNativeTls(_) => write!(f, "BuiltNativeTls"),
466 #[cfg(feature = "__rustls")]
467 TlsBackend::Rustls => write!(f, "Rustls"),
468 #[cfg(feature = "__rustls")]
469 TlsBackend::BuiltRustls(_) => write!(f, "BuiltRustls"),
470 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
471 TlsBackend::UnknownPreconfigured => write!(f, "UnknownPreconfigured"),
472 }
473 }
474}
475
476impl Default for TlsBackend {
477 fn default() -> TlsBackend {
478 #[cfg(all(feature = "default-tls", not(feature = "http3")))]
479 {
480 TlsBackend::Default
481 }
482
483 #[cfg(any(
484 all(feature = "__rustls", not(feature = "default-tls")),
485 feature = "http3"
486 ))]
487 {
488 TlsBackend::Rustls
489 }
490 }
491}
492
493#[cfg(feature = "__rustls")]
494pub(crate) struct NoVerifier;
495
496#[cfg(feature = "__rustls")]
497impl ServerCertVerifier for NoVerifier {
498 fn verify_server_cert(
499 &self,
500 _end_entity: &rustls::Certificate,
501 _intermediates: &[rustls::Certificate],
502 _server_name: &ServerName,
503 _scts: &mut dyn Iterator<Item = &[u8]>,
504 _ocsp_response: &[u8],
505 _now: std::time::SystemTime,
506 ) -> Result<ServerCertVerified, TLSError> {
507 Ok(ServerCertVerified::assertion())
508 }
509
510 fn verify_tls12_signature(
511 &self,
512 _message: &[u8],
513 _cert: &rustls::Certificate,
514 _dss: &DigitallySignedStruct,
515 ) -> Result<HandshakeSignatureValid, TLSError> {
516 Ok(HandshakeSignatureValid::assertion())
517 }
518
519 fn verify_tls13_signature(
520 &self,
521 _message: &[u8],
522 _cert: &rustls::Certificate,
523 _dss: &DigitallySignedStruct,
524 ) -> Result<HandshakeSignatureValid, TLSError> {
525 Ok(HandshakeSignatureValid::assertion())
526 }
527}
528
529#[derive(Clone)]
532pub struct TlsInfo {
533 pub(crate) peer_certificate: Option<Vec<u8>>,
534}
535
536impl TlsInfo {
537 pub fn peer_certificate(&self) -> Option<&[u8]> {
539 self.peer_certificate.as_ref().map(|der| &der[..])
540 }
541}
542
543impl std::fmt::Debug for TlsInfo {
544 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
545 f.debug_struct("TlsInfo").finish()
546 }
547}
548
549#[cfg(test)]
550mod tests {
551 use super::*;
552
553 #[cfg(feature = "default-tls")]
554 #[test]
555 fn certificate_from_der_invalid() {
556 Certificate::from_der(b"not der").unwrap_err();
557 }
558
559 #[cfg(feature = "default-tls")]
560 #[test]
561 fn certificate_from_pem_invalid() {
562 Certificate::from_pem(b"not pem").unwrap_err();
563 }
564
565 #[cfg(feature = "native-tls")]
566 #[test]
567 fn identity_from_pkcs12_der_invalid() {
568 Identity::from_pkcs12_der(b"not der", "nope").unwrap_err();
569 }
570
571 #[cfg(feature = "native-tls")]
572 #[test]
573 fn identity_from_pkcs8_pem_invalid() {
574 Identity::from_pkcs8_pem(b"not pem", b"not key").unwrap_err();
575 }
576
577 #[cfg(feature = "__rustls")]
578 #[test]
579 fn identity_from_pem_invalid() {
580 Identity::from_pem(b"not pem").unwrap_err();
581 }
582
583 #[cfg(feature = "__rustls")]
584 #[test]
585 fn identity_from_pem_pkcs1_key() {
586 let pem = b"-----BEGIN CERTIFICATE-----\n\
587 -----END CERTIFICATE-----\n\
588 -----BEGIN RSA PRIVATE KEY-----\n\
589 -----END RSA PRIVATE KEY-----\n";
590
591 Identity::from_pem(pem).unwrap();
592 }
593
594 #[test]
595 fn certificates_from_pem_bundle() {
596 const PEM_BUNDLE: &[u8] = b"
597 -----BEGIN CERTIFICATE-----
598 MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5
599 MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
600 Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
601 A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
602 Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl
603 ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j
604 QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr
605 ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr
606 BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM
607 YyRIHN8wfdVoOw==
608 -----END CERTIFICATE-----
609
610 -----BEGIN CERTIFICATE-----
611 MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5
612 MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
613 Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
614 A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
615 Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi
616 9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk
617 M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB
618 /zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB
619 MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw
620 CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW
621 1KyLa2tJElMzrdfkviT8tQp21KW8EA==
622 -----END CERTIFICATE-----
623 ";
624
625 assert!(Certificate::from_pem_bundle(PEM_BUNDLE).is_ok())
626 }
627}