1use std::{
48 fmt,
49 io::{BufRead, BufReader},
50};
51
52#[cfg(feature = "__rustls")]
54pub struct CertificateRevocationList {
55 #[cfg(feature = "__rustls")]
56 inner: Vec<u8>,
57}
58
59#[derive(Clone)]
61pub struct Certificate {
62 #[cfg(feature = "__native-tls")]
63 native: native_tls_crate::Certificate,
64 #[cfg(feature = "__rustls")]
65 original: Cert,
66}
67
68#[cfg(feature = "__rustls")]
69#[derive(Clone)]
70enum Cert {
71 Der(Vec<u8>),
72 Pem(Vec<u8>),
73}
74
75#[derive(Clone)]
77pub struct Identity {
78 #[cfg_attr(
79 not(any(feature = "__native-tls", feature = "__rustls")),
80 allow(unused)
81 )]
82 inner: ClientCert,
83}
84
85enum ClientCert {
86 #[cfg(feature = "__native-tls")]
87 Pkcs12(native_tls_crate::Identity),
88 #[cfg(feature = "__native-tls")]
89 Pkcs8(native_tls_crate::Identity),
90 #[cfg(feature = "__rustls")]
91 Pem { key: Vec<u8>, certs: Vec<Vec<u8>> },
92}
93
94impl Clone for ClientCert {
95 fn clone(&self) -> Self {
96 match self {
97 #[cfg(feature = "__native-tls")]
98 Self::Pkcs8(i) => Self::Pkcs8(i.clone()),
99 #[cfg(feature = "__native-tls")]
100 Self::Pkcs12(i) => Self::Pkcs12(i.clone()),
101 #[cfg(feature = "__rustls")]
102 ClientCert::Pem { key, certs } => ClientCert::Pem {
103 key: key.clone(),
104 certs: certs.clone(),
105 },
106 #[cfg_attr(
107 any(feature = "__native-tls", feature = "__rustls"),
108 allow(unreachable_patterns)
109 )]
110 _ => unreachable!(),
111 }
112 }
113}
114
115impl Certificate {
116 pub fn from_der(der: &[u8]) -> crate::Result<Certificate> {
133 Ok(Certificate {
134 #[cfg(feature = "__native-tls")]
135 native: native_tls_crate::Certificate::from_der(der).map_err(crate::error::builder)?,
136 #[cfg(feature = "__rustls")]
137 original: Cert::Der(der.to_owned()),
138 })
139 }
140
141 pub fn from_pem(pem: &[u8]) -> crate::Result<Certificate> {
158 Ok(Certificate {
159 #[cfg(feature = "__native-tls")]
160 native: native_tls_crate::Certificate::from_pem(pem).map_err(crate::error::builder)?,
161 #[cfg(feature = "__rustls")]
162 original: Cert::Pem(pem.to_owned()),
163 })
164 }
165
166 pub fn from_pem_bundle(pem_bundle: &[u8]) -> crate::Result<Vec<Certificate>> {
184 let mut reader = BufReader::new(pem_bundle);
185
186 Self::read_pem_certs(&mut reader)?
187 .iter()
188 .map(|cert_vec| Certificate::from_der(cert_vec))
189 .collect::<crate::Result<Vec<Certificate>>>()
190 }
191
192 #[cfg(feature = "__native-tls")]
193 pub(crate) fn add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder) {
194 tls.add_root_certificate(self.native);
195 }
196
197 #[cfg(all(feature = "__rustls", target_vendor = "apple"))]
198 pub(crate) fn ders(&self) -> crate::Result<Vec<Vec<u8>>> {
199 match &self.original {
200 Cert::Der(der) => Ok(vec![der.clone()]),
201 Cert::Pem(pem) => Self::read_pem_certs(&mut &pem[..]),
202 }
203 }
204
205 #[cfg(feature = "__rustls")]
206 pub(crate) fn add_to_boring(
207 self,
208 store: &mut boring::x509::store::X509StoreBuilder,
209 ) -> crate::Result<()> {
210 let certs = match self.original {
211 Cert::Der(der) => vec![der],
212 Cert::Pem(pem) => Self::read_pem_certs(&mut &pem[..])?,
213 };
214 if certs.is_empty() {
215 return Err(crate::error::builder("no certificates found"));
216 }
217 for der in certs {
218 store
219 .add_cert(boring::x509::X509::from_der(&der).map_err(crate::error::builder)?)
220 .map_err(crate::error::builder)?;
221 }
222 Ok(())
223 }
224
225 fn read_pem_certs(reader: &mut impl BufRead) -> crate::Result<Vec<Vec<u8>>> {
226 let mut buf = Vec::new();
227 reader
228 .read_to_end(&mut buf)
229 .map_err(crate::error::builder)?;
230 Ok(pem::parse_many(buf)
231 .map_err(crate::error::builder)?
232 .into_iter()
233 .filter(|p| p.tag() == "CERTIFICATE")
234 .map(|p| p.into_contents())
235 .collect())
236 }
237}
238
239impl Identity {
240 #[cfg(feature = "__native-tls")]
272 pub fn from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity> {
273 Ok(Identity {
274 inner: ClientCert::Pkcs12(
275 native_tls_crate::Identity::from_pkcs12(der, password)
276 .map_err(crate::error::builder)?,
277 ),
278 })
279 }
280
281 #[cfg(feature = "__native-tls")]
306 pub fn from_pkcs8_pem(pem: &[u8], key: &[u8]) -> crate::Result<Identity> {
307 Ok(Identity {
308 inner: ClientCert::Pkcs8(
309 native_tls_crate::Identity::from_pkcs8(pem, key).map_err(crate::error::builder)?,
310 ),
311 })
312 }
313
314 #[cfg(feature = "__rustls")]
340 pub fn from_pem(buf: &[u8]) -> crate::Result<Identity> {
341 let blocks = pem::parse_many(buf).map_err(crate::error::builder)?;
342 let mut certs = Vec::new();
343 let mut key = None;
344 for block in blocks {
345 match block.tag() {
346 "CERTIFICATE" => certs.push(block.into_contents()),
347 "PRIVATE KEY" | "RSA PRIVATE KEY" | "EC PRIVATE KEY" => {
348 key = Some(pem::encode(&block).into_bytes())
349 }
350 _ => return Err(crate::error::builder("invalid identity PEM section")),
351 }
352 }
353 let key = key.ok_or_else(|| crate::error::builder("private key not found"))?;
354 if certs.is_empty() {
355 return Err(crate::error::builder("certificate not found"));
356 }
357 Ok(Identity {
358 inner: ClientCert::Pem { key, certs },
359 })
360 }
361
362 #[cfg(feature = "__native-tls")]
363 pub(crate) fn add_to_native_tls(
364 self,
365 tls: &mut native_tls_crate::TlsConnectorBuilder,
366 ) -> crate::Result<()> {
367 match self.inner {
368 ClientCert::Pkcs12(id) | ClientCert::Pkcs8(id) => {
369 tls.identity(id);
370 Ok(())
371 }
372 #[cfg(feature = "__rustls")]
373 ClientCert::Pem { .. } => Err(crate::error::builder("incompatible TLS identity type")),
374 }
375 }
376
377 #[cfg(feature = "__rustls")]
378 pub(crate) fn add_to_boring(
379 self,
380 tls: &mut boring::ssl::SslContextBuilder,
381 ) -> crate::Result<()> {
382 match self.inner {
383 ClientCert::Pem { key, certs } => {
384 let mut certs = certs.into_iter();
385 let cert = boring::x509::X509::from_der(
386 &certs
387 .next()
388 .ok_or_else(|| crate::error::builder("certificate not found"))?,
389 )
390 .map_err(crate::error::builder)?;
391 tls.set_certificate(&cert).map_err(crate::error::builder)?;
392 for cert in certs {
393 tls.add_extra_chain_cert(
394 boring::x509::X509::from_der(&cert).map_err(crate::error::builder)?,
395 )
396 .map_err(crate::error::builder)?;
397 }
398 let key = boring::pkey::PKey::private_key_from_pem(&key)
399 .map_err(crate::error::builder)?;
400 tls.set_private_key(&key).map_err(crate::error::builder)?;
401 tls.check_private_key().map_err(crate::error::builder)
402 }
403 #[cfg(feature = "__native-tls")]
404 _ => Err(crate::error::builder("incompatible TLS identity type")),
405 }
406 }
407}
408
409#[cfg(feature = "__rustls")]
410impl CertificateRevocationList {
411 #[cfg(feature = "__rustls")]
432 pub fn from_pem(pem: &[u8]) -> crate::Result<CertificateRevocationList> {
433 let block = pem::parse(pem).map_err(crate::error::builder)?;
434 if block.tag() != "X509 CRL" {
435 return Err(crate::error::builder("invalid crl encoding"));
436 }
437 Ok(CertificateRevocationList {
438 inner: block.into_contents(),
439 })
440 }
441
442 #[cfg(feature = "__rustls")]
464 pub fn from_pem_bundle(pem_bundle: &[u8]) -> crate::Result<Vec<CertificateRevocationList>> {
465 Ok(pem::parse_many(pem_bundle)
466 .map_err(crate::error::builder)?
467 .into_iter()
468 .filter(|p| p.tag() == "X509 CRL")
469 .map(|p| CertificateRevocationList {
470 inner: p.into_contents(),
471 })
472 .collect())
473 }
474
475 pub(crate) fn add_to_boring(
476 &self,
477 store: &mut boring::x509::store::X509StoreBuilder,
478 ) -> crate::Result<()> {
479 use foreign_types::ForeignType;
480 let mut input = self.inner.as_ptr();
481 let len = self.inner.len().try_into().map_err(crate::error::builder)?;
482 let ok = unsafe {
485 let crl = boring_sys::d2i_X509_CRL(std::ptr::null_mut(), &mut input, len);
486 if crl.is_null() {
487 return Err(crate::error::builder(boring::error::ErrorStack::get()));
488 }
489 let ok = boring_sys::X509_STORE_add_crl(store.as_ptr(), crl);
490 boring_sys::X509_CRL_free(crl);
491 ok
492 };
493 if ok != 1 {
494 return Err(crate::error::builder(boring::error::ErrorStack::get()));
495 }
496 Ok(())
497 }
498}
499
500impl fmt::Debug for Certificate {
501 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
502 f.debug_struct("Certificate").finish()
503 }
504}
505
506impl fmt::Debug for Identity {
507 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
508 f.debug_struct("Identity").finish()
509 }
510}
511
512#[cfg(feature = "__rustls")]
513impl fmt::Debug for CertificateRevocationList {
514 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
515 f.debug_struct("CertificateRevocationList").finish()
516 }
517}
518
519#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
521pub struct Version(InnerVersion);
522
523#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
524#[non_exhaustive]
525enum InnerVersion {
526 Tls1_0,
527 Tls1_1,
528 Tls1_2,
529 Tls1_3,
530}
531
532impl Version {
535 pub const TLS_1_0: Version = Version(InnerVersion::Tls1_0);
537 pub const TLS_1_1: Version = Version(InnerVersion::Tls1_1);
539 pub const TLS_1_2: Version = Version(InnerVersion::Tls1_2);
541 pub const TLS_1_3: Version = Version(InnerVersion::Tls1_3);
543
544 #[cfg(feature = "__native-tls")]
545 pub(crate) fn to_native_tls(self) -> Option<native_tls_crate::Protocol> {
546 match self.0 {
547 InnerVersion::Tls1_0 => Some(native_tls_crate::Protocol::Tlsv10),
548 InnerVersion::Tls1_1 => Some(native_tls_crate::Protocol::Tlsv11),
549 InnerVersion::Tls1_2 => Some(native_tls_crate::Protocol::Tlsv12),
550 InnerVersion::Tls1_3 => Some(native_tls_crate::Protocol::Tlsv13),
551 }
552 }
553
554 #[cfg(feature = "__rustls")]
555 pub(crate) fn to_boring(self) -> boring::ssl::SslVersion {
556 use boring::ssl::SslVersion;
557 match self.0 {
558 InnerVersion::Tls1_0 => SslVersion::TLS1,
559 InnerVersion::Tls1_1 => SslVersion::TLS1_1,
560 InnerVersion::Tls1_2 => SslVersion::TLS1_2,
561 InnerVersion::Tls1_3 => SslVersion::TLS1_3,
562 }
563 }
564
565 #[cfg(feature = "__rustls")]
566 pub(crate) fn from_boring(version: boring::ssl::SslVersion) -> Option<Self> {
567 [Self::TLS_1_0, Self::TLS_1_1, Self::TLS_1_2, Self::TLS_1_3]
568 .into_iter()
569 .find(|v| v.to_boring() == version)
570 }
571}
572
573pub(crate) enum TlsBackend {
574 #[allow(dead_code)]
576 #[cfg(feature = "__native-tls")]
577 NativeTls,
578 #[cfg(feature = "__native-tls")]
579 BuiltNativeTls(native_tls_crate::TlsConnector),
580 #[cfg(feature = "__rustls")]
581 Boring,
582 #[cfg(feature = "__rustls")]
583 BuiltBoring(boring::ssl::SslConnector),
584 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
585 UnknownPreconfigured,
586}
587
588impl fmt::Debug for TlsBackend {
589 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
590 match self {
591 #[cfg(feature = "__native-tls")]
592 TlsBackend::NativeTls => write!(f, "NativeTls"),
593 #[cfg(feature = "__native-tls")]
594 TlsBackend::BuiltNativeTls(_) => write!(f, "BuiltNativeTls"),
595 #[cfg(feature = "__rustls")]
596 TlsBackend::Boring => write!(f, "Boring"),
597 #[cfg(feature = "__rustls")]
598 TlsBackend::BuiltBoring(_) => write!(f, "BuiltBoring"),
599 #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
600 TlsBackend::UnknownPreconfigured => write!(f, "UnknownPreconfigured"),
601 }
602 }
603}
604
605#[allow(clippy::derivable_impls)]
606impl Default for TlsBackend {
607 fn default() -> TlsBackend {
608 #[cfg(any(
609 all(feature = "__rustls", not(feature = "__native-tls")),
610 feature = "http3"
611 ))]
612 {
613 TlsBackend::Boring
614 }
615
616 #[cfg(all(feature = "__native-tls", not(feature = "http3")))]
617 {
618 TlsBackend::NativeTls
619 }
620 }
621}
622
623#[derive(Clone)]
626pub struct TlsInfo {
627 pub(crate) peer_certificate: Option<Vec<u8>>,
628 pub(crate) version: Option<Version>,
629}
630
631impl TlsInfo {
632 pub fn peer_certificate(&self) -> Option<&[u8]> {
634 self.peer_certificate.as_ref().map(|der| &der[..])
635 }
636
637 pub fn version(&self) -> Option<Version> {
642 self.version
643 }
644}
645
646impl std::fmt::Debug for TlsInfo {
647 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
648 f.debug_struct("TlsInfo")
649 .field("version", &self.version)
650 .finish()
651 }
652}
653
654#[cfg(test)]
655mod tests {
656 use super::*;
657
658 #[cfg(feature = "__native-tls")]
659 #[test]
660 fn certificate_from_der_invalid() {
661 Certificate::from_der(b"not der").unwrap_err();
662 }
663
664 #[cfg(feature = "__native-tls")]
665 #[test]
666 fn certificate_from_pem_invalid() {
667 Certificate::from_pem(b"not pem").unwrap_err();
668 }
669
670 #[cfg(feature = "__native-tls")]
671 #[test]
672 fn identity_from_pkcs12_der_invalid() {
673 Identity::from_pkcs12_der(b"not der", "nope").unwrap_err();
674 }
675
676 #[cfg(feature = "__native-tls")]
677 #[test]
678 fn identity_from_pkcs8_pem_invalid() {
679 Identity::from_pkcs8_pem(b"not pem", b"not key").unwrap_err();
680 }
681
682 #[cfg(feature = "__rustls")]
683 #[test]
684 fn identity_from_pem_invalid() {
685 Identity::from_pem(b"not pem").unwrap_err();
686 }
687
688 #[cfg(feature = "__rustls")]
689 #[test]
690 fn identity_from_pem_pkcs1_key() {
691 let pem = b"-----BEGIN CERTIFICATE-----\n\
692 -----END CERTIFICATE-----\n\
693 -----BEGIN RSA PRIVATE KEY-----\n\
694 -----END RSA PRIVATE KEY-----\n";
695
696 Identity::from_pem(pem).unwrap();
697 }
698
699 #[test]
700 fn certificates_from_pem_bundle() {
701 const PEM_BUNDLE: &[u8] = b"
702 -----BEGIN CERTIFICATE-----
703 MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5
704 MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
705 Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
706 A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
707 Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl
708 ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j
709 QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr
710 ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr
711 BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM
712 YyRIHN8wfdVoOw==
713 -----END CERTIFICATE-----
714
715 -----BEGIN CERTIFICATE-----
716 MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5
717 MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
718 Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
719 A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
720 Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi
721 9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk
722 M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB
723 /zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB
724 MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw
725 CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW
726 1KyLa2tJElMzrdfkviT8tQp21KW8EA==
727 -----END CERTIFICATE-----
728 ";
729
730 assert!(Certificate::from_pem_bundle(PEM_BUNDLE).is_ok())
731 }
732
733 #[cfg(feature = "__rustls")]
734 #[test]
735 fn crl_from_pem() {
736 let pem = b"-----BEGIN X509 CRL-----\n-----END X509 CRL-----\n";
737
738 CertificateRevocationList::from_pem(pem).unwrap();
739 }
740
741 #[cfg(feature = "__rustls")]
742 #[test]
743 fn invalid_crl_from_pem() {
744 CertificateRevocationList::from_pem(b"Invalid").unwrap_err();
745 }
746
747 #[cfg(feature = "__rustls")]
748 #[test]
749 fn crl_from_pem_bundle() {
750 let pem_bundle = std::fs::read("tests/support/crl.pem").unwrap();
751
752 let result = CertificateRevocationList::from_pem_bundle(&pem_bundle);
753
754 assert!(result.is_ok());
755 let result = result.unwrap();
756 assert_eq!(result.len(), 1);
757 }
758}