1#[cfg(feature = "__rustls")]
48use rustls::{
49 client::danger::HandshakeSignatureValid, client::danger::ServerCertVerified,
50 client::danger::ServerCertVerifier, crypto::WebPkiSupportedAlgorithms,
51 server::ParsedCertificate, DigitallySignedStruct, Error as TLSError, RootCertStore,
52 SignatureScheme,
53};
54use rustls_pki_types::pem::PemObject;
55#[cfg(feature = "__rustls")]
56use rustls_pki_types::{ServerName, UnixTime};
57use std::{
58 fmt,
59 io::{BufRead, BufReader},
60};
61
62#[cfg(feature = "__rustls")]
64pub struct CertificateRevocationList {
65 #[cfg(feature = "__rustls")]
66 inner: rustls_pki_types::CertificateRevocationListDer<'static>,
67}
68
69#[derive(Clone)]
71pub struct Certificate {
72 #[cfg(feature = "default-tls")]
73 native: native_tls_crate::Certificate,
74 #[cfg(feature = "__rustls")]
75 original: Cert,
76}
77
78#[cfg(feature = "__rustls")]
79#[derive(Clone)]
80enum Cert {
81 Der(Vec<u8>),
82 Pem(Vec<u8>),
83}
84
85#[derive(Clone)]
87pub struct Identity {
88 #[cfg_attr(not(any(feature = "native-tls", feature = "__rustls")), allow(unused))]
89 inner: ClientCert,
90}
91
92enum ClientCert {
93 #[cfg(feature = "native-tls")]
94 Pkcs12(native_tls_crate::Identity),
95 #[cfg(feature = "native-tls")]
96 Pkcs8(native_tls_crate::Identity),
97 #[cfg(feature = "__rustls")]
98 Pem {
99 key: rustls_pki_types::PrivateKeyDer<'static>,
100 certs: Vec<rustls_pki_types::CertificateDer<'static>>,
101 },
102}
103
104impl Clone for ClientCert {
105 fn clone(&self) -> Self {
106 match self {
107 #[cfg(feature = "native-tls")]
108 Self::Pkcs8(i) => Self::Pkcs8(i.clone()),
109 #[cfg(feature = "native-tls")]
110 Self::Pkcs12(i) => Self::Pkcs12(i.clone()),
111 #[cfg(feature = "__rustls")]
112 ClientCert::Pem { key, certs } => ClientCert::Pem {
113 key: key.clone_key(),
114 certs: certs.clone(),
115 },
116 #[cfg_attr(
117 any(feature = "native-tls", feature = "__rustls"),
118 allow(unreachable_patterns)
119 )]
120 _ => unreachable!(),
121 }
122 }
123}
124
125impl Certificate {
126 pub fn from_der(der: &[u8]) -> crate::Result<Certificate> {
143 Ok(Certificate {
144 #[cfg(feature = "default-tls")]
145 native: native_tls_crate::Certificate::from_der(der).map_err(crate::error::builder)?,
146 #[cfg(feature = "__rustls")]
147 original: Cert::Der(der.to_owned()),
148 })
149 }
150
151 pub fn from_pem(pem: &[u8]) -> crate::Result<Certificate> {
168 Ok(Certificate {
169 #[cfg(feature = "default-tls")]
170 native: native_tls_crate::Certificate::from_pem(pem).map_err(crate::error::builder)?,
171 #[cfg(feature = "__rustls")]
172 original: Cert::Pem(pem.to_owned()),
173 })
174 }
175
176 pub fn from_pem_bundle(pem_bundle: &[u8]) -> crate::Result<Vec<Certificate>> {
194 let mut reader = BufReader::new(pem_bundle);
195
196 Self::read_pem_certs(&mut reader)?
197 .iter()
198 .map(|cert_vec| Certificate::from_der(cert_vec))
199 .collect::<crate::Result<Vec<Certificate>>>()
200 }
201
202 #[cfg(feature = "default-tls")]
203 pub(crate) fn add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder) {
204 tls.add_root_certificate(self.native);
205 }
206
207 #[cfg(feature = "__rustls")]
208 pub(crate) fn add_to_rustls(
209 self,
210 root_cert_store: &mut rustls::RootCertStore,
211 ) -> crate::Result<()> {
212 use std::io::Cursor;
213
214 match self.original {
215 Cert::Der(buf) => root_cert_store
216 .add(buf.into())
217 .map_err(crate::error::builder)?,
218 Cert::Pem(buf) => {
219 let mut reader = Cursor::new(buf);
220 let certs = Self::read_pem_certs(&mut reader)?;
221 for c in certs {
222 root_cert_store
223 .add(c.into())
224 .map_err(crate::error::builder)?;
225 }
226 }
227 }
228 Ok(())
229 }
230
231 fn read_pem_certs(reader: &mut impl BufRead) -> crate::Result<Vec<Vec<u8>>> {
232 rustls_pki_types::CertificateDer::pem_reader_iter(reader)
233 .map(|result| match result {
234 Ok(cert) => Ok(cert.as_ref().to_vec()),
235 Err(_) => Err(crate::error::builder("invalid certificate encoding")),
236 })
237 .collect()
238 }
239}
240
241impl Identity {
242 #[cfg(feature = "native-tls")]
274 pub fn from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity> {
275 Ok(Identity {
276 inner: ClientCert::Pkcs12(
277 native_tls_crate::Identity::from_pkcs12(der, password)
278 .map_err(crate::error::builder)?,
279 ),
280 })
281 }
282
283 #[cfg(feature = "native-tls")]
308 pub fn from_pkcs8_pem(pem: &[u8], key: &[u8]) -> crate::Result<Identity> {
309 Ok(Identity {
310 inner: ClientCert::Pkcs8(
311 native_tls_crate::Identity::from_pkcs8(pem, key).map_err(crate::error::builder)?,
312 ),
313 })
314 }
315
316 #[cfg(feature = "__rustls")]
342 pub fn from_pem(buf: &[u8]) -> crate::Result<Identity> {
343 use rustls_pki_types::{pem::SectionKind, PrivateKeyDer};
344 use std::io::Cursor;
345
346 let (key, certs) = {
347 let mut pem = Cursor::new(buf);
348 let mut sk = Vec::<rustls_pki_types::PrivateKeyDer>::new();
349 let mut certs = Vec::<rustls_pki_types::CertificateDer>::new();
350
351 while let Some((kind, data)) =
352 rustls_pki_types::pem::from_buf(&mut pem).map_err(|_| {
353 crate::error::builder(TLSError::General(String::from(
354 "Invalid identity PEM file",
355 )))
356 })?
357 {
358 match kind {
359 SectionKind::Certificate => certs.push(data.into()),
360 SectionKind::PrivateKey => sk.push(PrivateKeyDer::Pkcs8(data.into())),
361 SectionKind::RsaPrivateKey => sk.push(PrivateKeyDer::Pkcs1(data.into())),
362 SectionKind::EcPrivateKey => sk.push(PrivateKeyDer::Sec1(data.into())),
363 _ => {
364 return Err(crate::error::builder(TLSError::General(String::from(
365 "No valid certificate was found",
366 ))))
367 }
368 }
369 }
370
371 if let (Some(sk), false) = (sk.pop(), certs.is_empty()) {
372 (sk, certs)
373 } else {
374 return Err(crate::error::builder(TLSError::General(String::from(
375 "private key or certificate not found",
376 ))));
377 }
378 };
379
380 Ok(Identity {
381 inner: ClientCert::Pem { key, certs },
382 })
383 }
384
385 #[cfg(feature = "native-tls")]
386 pub(crate) fn add_to_native_tls(
387 self,
388 tls: &mut native_tls_crate::TlsConnectorBuilder,
389 ) -> crate::Result<()> {
390 match self.inner {
391 ClientCert::Pkcs12(id) | ClientCert::Pkcs8(id) => {
392 tls.identity(id);
393 Ok(())
394 }
395 #[cfg(feature = "__rustls")]
396 ClientCert::Pem { .. } => Err(crate::error::builder("incompatible TLS identity type")),
397 }
398 }
399
400 #[cfg(feature = "__rustls")]
401 pub(crate) fn add_to_rustls(
402 self,
403 config_builder: rustls::ConfigBuilder<
404 rustls::ClientConfig,
405 rustls::client::WantsClientCert,
407 >,
408 ) -> crate::Result<rustls::ClientConfig> {
409 match self.inner {
410 ClientCert::Pem { key, certs } => config_builder
411 .with_client_auth_cert(certs, key)
412 .map_err(crate::error::builder),
413 #[cfg(feature = "native-tls")]
414 ClientCert::Pkcs12(..) | ClientCert::Pkcs8(..) => {
415 Err(crate::error::builder("incompatible TLS identity type"))
416 }
417 }
418 }
419}
420
421#[cfg(feature = "__rustls")]
422impl CertificateRevocationList {
423 #[cfg(feature = "__rustls")]
444 pub fn from_pem(pem: &[u8]) -> crate::Result<CertificateRevocationList> {
445 Ok(CertificateRevocationList {
446 #[cfg(feature = "__rustls")]
447 inner: rustls_pki_types::CertificateRevocationListDer::from(pem.to_vec()),
448 })
449 }
450
451 #[cfg(feature = "__rustls")]
473 pub fn from_pem_bundle(pem_bundle: &[u8]) -> crate::Result<Vec<CertificateRevocationList>> {
474 rustls_pki_types::CertificateRevocationListDer::pem_slice_iter(pem_bundle)
475 .map(|result| match result {
476 Ok(crl) => Ok(CertificateRevocationList { inner: crl }),
477 Err(_) => Err(crate::error::builder("invalid crl encoding")),
478 })
479 .collect::<crate::Result<Vec<CertificateRevocationList>>>()
480 }
481
482 #[cfg(feature = "__rustls")]
483 pub(crate) fn as_rustls_crl<'a>(&self) -> rustls_pki_types::CertificateRevocationListDer<'a> {
484 self.inner.clone()
485 }
486}
487
488impl fmt::Debug for Certificate {
489 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
490 f.debug_struct("Certificate").finish()
491 }
492}
493
494impl fmt::Debug for Identity {
495 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
496 f.debug_struct("Identity").finish()
497 }
498}
499
500#[cfg(feature = "__rustls")]
501impl fmt::Debug for CertificateRevocationList {
502 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
503 f.debug_struct("CertificateRevocationList").finish()
504 }
505}
506
507#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
509pub struct Version(InnerVersion);
510
511#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
512#[non_exhaustive]
513enum InnerVersion {
514 Tls1_0,
515 Tls1_1,
516 Tls1_2,
517 Tls1_3,
518}
519
520impl Version {
523 pub const TLS_1_0: Version = Version(InnerVersion::Tls1_0);
525 pub const TLS_1_1: Version = Version(InnerVersion::Tls1_1);
527 pub const TLS_1_2: Version = Version(InnerVersion::Tls1_2);
529 pub const TLS_1_3: Version = Version(InnerVersion::Tls1_3);
531
532 #[cfg(feature = "default-tls")]
533 pub(crate) fn to_native_tls(self) -> Option<native_tls_crate::Protocol> {
534 match self.0 {
535 InnerVersion::Tls1_0 => Some(native_tls_crate::Protocol::Tlsv10),
536 InnerVersion::Tls1_1 => Some(native_tls_crate::Protocol::Tlsv11),
537 InnerVersion::Tls1_2 => Some(native_tls_crate::Protocol::Tlsv12),
538 InnerVersion::Tls1_3 => None,
539 }
540 }
541
542 #[cfg(feature = "__rustls")]
543 pub(crate) fn from_rustls(version: rustls::ProtocolVersion) -> Option<Self> {
544 match version {
545 rustls::ProtocolVersion::SSLv2 => None,
546 rustls::ProtocolVersion::SSLv3 => None,
547 rustls::ProtocolVersion::TLSv1_0 => Some(Self(InnerVersion::Tls1_0)),
548 rustls::ProtocolVersion::TLSv1_1 => Some(Self(InnerVersion::Tls1_1)),
549 rustls::ProtocolVersion::TLSv1_2 => Some(Self(InnerVersion::Tls1_2)),
550 rustls::ProtocolVersion::TLSv1_3 => Some(Self(InnerVersion::Tls1_3)),
551 _ => None,
552 }
553 }
554}
555
556pub(crate) enum TlsBackend {
557 #[allow(dead_code)]
559 #[cfg(feature = "default-tls")]
560 Default,
561 #[cfg(feature = "native-tls")]
562 BuiltNativeTls(native_tls_crate::TlsConnector),
563 #[cfg(feature = "__rustls")]
564 Rustls,
565 #[cfg(feature = "__rustls")]
566 BuiltRustls(rustls::ClientConfig),
567 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
568 UnknownPreconfigured,
569}
570
571impl fmt::Debug for TlsBackend {
572 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
573 match self {
574 #[cfg(feature = "default-tls")]
575 TlsBackend::Default => write!(f, "Default"),
576 #[cfg(feature = "native-tls")]
577 TlsBackend::BuiltNativeTls(_) => write!(f, "BuiltNativeTls"),
578 #[cfg(feature = "__rustls")]
579 TlsBackend::Rustls => write!(f, "Rustls"),
580 #[cfg(feature = "__rustls")]
581 TlsBackend::BuiltRustls(_) => write!(f, "BuiltRustls"),
582 #[cfg(any(feature = "native-tls", feature = "__rustls",))]
583 TlsBackend::UnknownPreconfigured => write!(f, "UnknownPreconfigured"),
584 }
585 }
586}
587
588#[allow(clippy::derivable_impls)]
589impl Default for TlsBackend {
590 fn default() -> TlsBackend {
591 #[cfg(all(feature = "default-tls", not(feature = "http3")))]
592 {
593 TlsBackend::Default
594 }
595
596 #[cfg(any(
597 all(feature = "__rustls", not(feature = "default-tls")),
598 feature = "http3"
599 ))]
600 {
601 TlsBackend::Rustls
602 }
603 }
604}
605
606#[cfg(feature = "__rustls")]
607#[derive(Debug)]
608pub(crate) struct NoVerifier;
609
610#[cfg(feature = "__rustls")]
611impl ServerCertVerifier for NoVerifier {
612 fn verify_server_cert(
613 &self,
614 _end_entity: &rustls_pki_types::CertificateDer,
615 _intermediates: &[rustls_pki_types::CertificateDer],
616 _server_name: &ServerName,
617 _ocsp_response: &[u8],
618 _now: UnixTime,
619 ) -> Result<ServerCertVerified, TLSError> {
620 Ok(ServerCertVerified::assertion())
621 }
622
623 fn verify_tls12_signature(
624 &self,
625 _message: &[u8],
626 _cert: &rustls_pki_types::CertificateDer,
627 _dss: &DigitallySignedStruct,
628 ) -> Result<HandshakeSignatureValid, TLSError> {
629 Ok(HandshakeSignatureValid::assertion())
630 }
631
632 fn verify_tls13_signature(
633 &self,
634 _message: &[u8],
635 _cert: &rustls_pki_types::CertificateDer,
636 _dss: &DigitallySignedStruct,
637 ) -> Result<HandshakeSignatureValid, TLSError> {
638 Ok(HandshakeSignatureValid::assertion())
639 }
640
641 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
642 vec![
643 SignatureScheme::RSA_PKCS1_SHA1,
644 SignatureScheme::ECDSA_SHA1_Legacy,
645 SignatureScheme::RSA_PKCS1_SHA256,
646 SignatureScheme::ECDSA_NISTP256_SHA256,
647 SignatureScheme::RSA_PKCS1_SHA384,
648 SignatureScheme::ECDSA_NISTP384_SHA384,
649 SignatureScheme::RSA_PKCS1_SHA512,
650 SignatureScheme::ECDSA_NISTP521_SHA512,
651 SignatureScheme::RSA_PSS_SHA256,
652 SignatureScheme::RSA_PSS_SHA384,
653 SignatureScheme::RSA_PSS_SHA512,
654 SignatureScheme::ED25519,
655 SignatureScheme::ED448,
656 ]
657 }
658}
659
660#[cfg(feature = "__rustls")]
661#[derive(Debug)]
662pub(crate) struct IgnoreHostname {
663 roots: RootCertStore,
664 signature_algorithms: WebPkiSupportedAlgorithms,
665}
666
667#[cfg(feature = "__rustls")]
668impl IgnoreHostname {
669 pub(crate) fn new(
670 roots: RootCertStore,
671 signature_algorithms: WebPkiSupportedAlgorithms,
672 ) -> Self {
673 Self {
674 roots,
675 signature_algorithms,
676 }
677 }
678}
679
680#[cfg(feature = "__rustls")]
681impl ServerCertVerifier for IgnoreHostname {
682 fn verify_server_cert(
683 &self,
684 end_entity: &rustls_pki_types::CertificateDer<'_>,
685 intermediates: &[rustls_pki_types::CertificateDer<'_>],
686 _server_name: &ServerName<'_>,
687 _ocsp_response: &[u8],
688 now: UnixTime,
689 ) -> Result<ServerCertVerified, TLSError> {
690 let cert = ParsedCertificate::try_from(end_entity)?;
691
692 rustls::client::verify_server_cert_signed_by_trust_anchor(
693 &cert,
694 &self.roots,
695 intermediates,
696 now,
697 self.signature_algorithms.all,
698 )?;
699 Ok(ServerCertVerified::assertion())
700 }
701
702 fn verify_tls12_signature(
703 &self,
704 message: &[u8],
705 cert: &rustls_pki_types::CertificateDer<'_>,
706 dss: &DigitallySignedStruct,
707 ) -> Result<HandshakeSignatureValid, TLSError> {
708 rustls::crypto::verify_tls12_signature(message, cert, dss, &self.signature_algorithms)
709 }
710
711 fn verify_tls13_signature(
712 &self,
713 message: &[u8],
714 cert: &rustls_pki_types::CertificateDer<'_>,
715 dss: &DigitallySignedStruct,
716 ) -> Result<HandshakeSignatureValid, TLSError> {
717 rustls::crypto::verify_tls13_signature(message, cert, dss, &self.signature_algorithms)
718 }
719
720 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
721 self.signature_algorithms.supported_schemes()
722 }
723}
724
725#[derive(Clone)]
728pub struct TlsInfo {
729 pub(crate) peer_certificate: Option<Vec<u8>>,
730}
731
732impl TlsInfo {
733 pub fn peer_certificate(&self) -> Option<&[u8]> {
735 self.peer_certificate.as_ref().map(|der| &der[..])
736 }
737}
738
739impl std::fmt::Debug for TlsInfo {
740 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
741 f.debug_struct("TlsInfo").finish()
742 }
743}
744
745#[cfg(test)]
746mod tests {
747 use super::*;
748
749 #[cfg(feature = "default-tls")]
750 #[test]
751 fn certificate_from_der_invalid() {
752 Certificate::from_der(b"not der").unwrap_err();
753 }
754
755 #[cfg(feature = "default-tls")]
756 #[test]
757 fn certificate_from_pem_invalid() {
758 Certificate::from_pem(b"not pem").unwrap_err();
759 }
760
761 #[cfg(feature = "native-tls")]
762 #[test]
763 fn identity_from_pkcs12_der_invalid() {
764 Identity::from_pkcs12_der(b"not der", "nope").unwrap_err();
765 }
766
767 #[cfg(feature = "native-tls")]
768 #[test]
769 fn identity_from_pkcs8_pem_invalid() {
770 Identity::from_pkcs8_pem(b"not pem", b"not key").unwrap_err();
771 }
772
773 #[cfg(feature = "__rustls")]
774 #[test]
775 fn identity_from_pem_invalid() {
776 Identity::from_pem(b"not pem").unwrap_err();
777 }
778
779 #[cfg(feature = "__rustls")]
780 #[test]
781 fn identity_from_pem_pkcs1_key() {
782 let pem = b"-----BEGIN CERTIFICATE-----\n\
783 -----END CERTIFICATE-----\n\
784 -----BEGIN RSA PRIVATE KEY-----\n\
785 -----END RSA PRIVATE KEY-----\n";
786
787 Identity::from_pem(pem).unwrap();
788 }
789
790 #[test]
791 fn certificates_from_pem_bundle() {
792 const PEM_BUNDLE: &[u8] = b"
793 -----BEGIN CERTIFICATE-----
794 MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5
795 MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
796 Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
797 A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
798 Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl
799 ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j
800 QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr
801 ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr
802 BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM
803 YyRIHN8wfdVoOw==
804 -----END CERTIFICATE-----
805
806 -----BEGIN CERTIFICATE-----
807 MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5
808 MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
809 Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
810 A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
811 Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi
812 9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk
813 M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB
814 /zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB
815 MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw
816 CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW
817 1KyLa2tJElMzrdfkviT8tQp21KW8EA==
818 -----END CERTIFICATE-----
819 ";
820
821 assert!(Certificate::from_pem_bundle(PEM_BUNDLE).is_ok())
822 }
823
824 #[cfg(feature = "__rustls")]
825 #[test]
826 fn crl_from_pem() {
827 let pem = b"-----BEGIN X509 CRL-----\n-----END X509 CRL-----\n";
828
829 CertificateRevocationList::from_pem(pem).unwrap();
830 }
831
832 #[cfg(feature = "__rustls")]
833 #[test]
834 fn crl_from_pem_bundle() {
835 let pem_bundle = std::fs::read("tests/support/crl.pem").unwrap();
836
837 let result = CertificateRevocationList::from_pem_bundle(&pem_bundle);
838
839 assert!(result.is_ok());
840 let result = result.unwrap();
841 assert_eq!(result.len(), 1);
842 }
843}