1#[cfg(feature = "alloc")]
2use alloc::collections::BTreeMap;
3#[cfg(feature = "alloc")]
4use alloc::vec::Vec;
5use core::fmt::Debug;
6
7use pki_types::{SignatureVerificationAlgorithm, UnixTime};
8
9use crate::cert::lenient_certificate_serial_number;
10use crate::crl::crl_signature_err;
11use crate::der::{self, CONSTRUCTED, CONTEXT_SPECIFIC, DerIterator, FromDer, Tag};
12use crate::error::{DerTypeId, Error};
13use crate::public_values_eq;
14use crate::signed_data::{self, SignedData};
15use crate::subject_name::GeneralName;
16use crate::verify_cert::{Budget, PathNode, Role};
17use crate::x509::{
18 DistributionPointName, Extension, UnknownExtensionPolicy, remember_extension,
19 set_extension_once,
20};
21
22#[derive(Debug)]
28pub enum CertRevocationList<'a> {
29 #[cfg(feature = "alloc")]
31 Owned(OwnedCertRevocationList),
32 Borrowed(BorrowedCertRevocationList<'a>),
34}
35
36#[cfg(feature = "alloc")]
37impl From<OwnedCertRevocationList> for CertRevocationList<'_> {
38 fn from(crl: OwnedCertRevocationList) -> Self {
39 Self::Owned(crl)
40 }
41}
42
43impl<'a> From<BorrowedCertRevocationList<'a>> for CertRevocationList<'a> {
44 fn from(crl: BorrowedCertRevocationList<'a>) -> Self {
45 Self::Borrowed(crl)
46 }
47}
48
49impl CertRevocationList<'_> {
50 pub fn issuer(&self) -> &[u8] {
52 match self {
53 #[cfg(feature = "alloc")]
54 CertRevocationList::Owned(crl) => crl.issuer.as_ref(),
55 CertRevocationList::Borrowed(crl) => crl.issuer.as_slice_less_safe(),
56 }
57 }
58
59 pub fn issuing_distribution_point(&self) -> Option<&[u8]> {
61 match self {
62 #[cfg(feature = "alloc")]
63 CertRevocationList::Owned(crl) => crl.issuing_distribution_point.as_deref(),
64 CertRevocationList::Borrowed(crl) => crl
65 .issuing_distribution_point
66 .map(|idp| idp.as_slice_less_safe()),
67 }
68 }
69
70 pub fn find_serial(&self, serial: &[u8]) -> Result<Option<BorrowedRevokedCert<'_>>, Error> {
73 match self {
74 #[cfg(feature = "alloc")]
75 CertRevocationList::Owned(crl) => crl.find_serial(serial),
76 CertRevocationList::Borrowed(crl) => crl.find_serial(serial),
77 }
78 }
79
80 pub(crate) fn authoritative(&self, path: &PathNode<'_>) -> bool {
97 if self.issuer() != path.cert.issuer() {
100 return false;
101 }
102
103 let crl_idp = match self.issuing_distribution_point() {
104 Some(crl_idp) => {
107 match IssuingDistributionPoint::from_der(untrusted::Input::from(crl_idp)) {
108 Ok(crl_idp) => crl_idp,
109 Err(_) => return false, }
111 }
112 None => return true,
117 };
118
119 crl_idp.authoritative_for(path)
120 }
121
122 pub(crate) fn verify_signature(
125 &self,
126 supported_sig_algs: &[&dyn SignatureVerificationAlgorithm],
127 issuer_spki: untrusted::Input<'_>,
128 budget: &mut Budget,
129 ) -> Result<(), Error> {
130 signed_data::verify_signed_data(
131 supported_sig_algs,
132 issuer_spki,
133 &match self {
134 #[cfg(feature = "alloc")]
135 CertRevocationList::Owned(crl) => crl.signed_data.borrow(),
136 CertRevocationList::Borrowed(crl) => SignedData {
137 data: crl.signed_data.data,
138 algorithm: crl.signed_data.algorithm,
139 signature: crl.signed_data.signature,
140 },
141 },
142 budget,
143 )
144 .map_err(crl_signature_err)
145 }
146
147 pub(crate) fn check_expiration(&self, time: UnixTime) -> Result<(), Error> {
149 let next_update = match self {
150 #[cfg(feature = "alloc")]
151 CertRevocationList::Owned(crl) => crl.next_update,
152 CertRevocationList::Borrowed(crl) => crl.next_update,
153 };
154
155 if time >= next_update {
156 return Err(Error::CrlExpired { time, next_update });
157 }
158
159 Ok(())
160 }
161}
162
163#[cfg(feature = "alloc")]
167#[derive(Debug, Clone)]
168pub struct OwnedCertRevocationList {
169 revoked_certs: BTreeMap<Vec<u8>, OwnedRevokedCert>,
172
173 issuer: Vec<u8>,
174
175 issuing_distribution_point: Option<Vec<u8>>,
176
177 signed_data: signed_data::OwnedSignedData,
178
179 next_update: UnixTime,
180}
181
182#[cfg(feature = "alloc")]
183impl OwnedCertRevocationList {
184 pub fn from_der(crl_der: &[u8]) -> Result<Self, Error> {
197 BorrowedCertRevocationList::from_der(crl_der)?.to_owned()
198 }
199
200 fn find_serial(&self, serial: &[u8]) -> Result<Option<BorrowedRevokedCert<'_>>, Error> {
201 Ok(self
205 .revoked_certs
206 .get(serial)
207 .map(|owned_revoked_cert| owned_revoked_cert.borrow()))
208 }
209}
210
211#[derive(Debug)]
215pub struct BorrowedCertRevocationList<'a> {
216 signed_data: SignedData<'a>,
218
219 issuer: untrusted::Input<'a>,
222
223 issuing_distribution_point: Option<untrusted::Input<'a>>,
225
226 revoked_certs: untrusted::Input<'a>,
228
229 next_update: UnixTime,
230}
231
232impl<'a> BorrowedCertRevocationList<'a> {
233 pub fn from_der(crl_der: &'a [u8]) -> Result<Self, Error> {
244 der::read_all(untrusted::Input::from(crl_der))
245 }
246
247 #[cfg(feature = "alloc")]
250 pub fn to_owned(&self) -> Result<OwnedCertRevocationList, Error> {
251 let revoked_certs = self
254 .into_iter()
255 .collect::<Result<Vec<_>, _>>()?
256 .iter()
257 .map(|revoked_cert| (revoked_cert.serial_number.to_vec(), revoked_cert.to_owned()))
258 .collect::<BTreeMap<_, _>>();
259
260 Ok(OwnedCertRevocationList {
261 signed_data: self.signed_data.to_owned(),
262 issuer: self.issuer.as_slice_less_safe().to_vec(),
263 issuing_distribution_point: self
264 .issuing_distribution_point
265 .map(|idp| idp.as_slice_less_safe().to_vec()),
266 revoked_certs,
267 next_update: self.next_update,
268 })
269 }
270
271 fn remember_extension(&mut self, extension: &Extension<'a>) -> Result<(), Error> {
272 remember_extension(extension, UnknownExtensionPolicy::default(), |id| {
273 match id {
274 20 => {
276 extension.value.read_all(Error::InvalidCrlNumber, |der| {
282 let crl_number = der::nonnegative_integer(der)
283 .map_err(|_| Error::InvalidCrlNumber)?
284 .as_slice_less_safe();
285 if crl_number.len() <= 20 {
286 Ok(crl_number)
287 } else {
288 Err(Error::InvalidCrlNumber)
289 }
290 })?;
291 Ok(())
293 }
294
295 27 => Err(Error::UnsupportedDeltaCrl),
298
299 28 => {
302 set_extension_once(&mut self.issuing_distribution_point, || Ok(extension.value))
303 }
304
305 35 => Ok(()),
308
309 _ => extension.unsupported(UnknownExtensionPolicy::default()),
311 }
312 })
313 }
314
315 fn find_serial(&self, serial: &[u8]) -> Result<Option<BorrowedRevokedCert<'_>>, Error> {
316 for revoked_cert_result in self {
317 let revoked_cert = revoked_cert_result?;
318 if revoked_cert.serial_number.eq(serial) {
319 return Ok(Some(revoked_cert));
320 }
321 }
322
323 Ok(None)
324 }
325}
326
327impl<'a> FromDer<'a> for BorrowedCertRevocationList<'a> {
328 fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
339 let (tbs_cert_list, signed_data) = der::nested_limited(
340 reader,
341 Tag::Sequence,
342 Error::TrailingData(Self::TYPE_ID),
343 |signed_der| SignedData::from_der(signed_der, der::MAX_DER_SIZE),
344 der::MAX_DER_SIZE,
345 )?;
346
347 let crl = tbs_cert_list.read_all(Error::BadDer, |tbs_cert_list| {
348 if u8::from_der(tbs_cert_list)? != 1 {
359 return Err(Error::UnsupportedCrlVersion);
360 }
361
362 let signature = der::expect_tag(tbs_cert_list, Tag::Sequence)?;
366 if !public_values_eq(signature, signed_data.algorithm) {
367 return Err(Error::SignatureAlgorithmMismatch);
368 }
369
370 let issuer = der::expect_tag(tbs_cert_list, Tag::Sequence)?;
373
374 UnixTime::from_der(tbs_cert_list)?;
380
381 let next_update = UnixTime::from_der(tbs_cert_list)?;
386
387 let revoked_certs = if tbs_cert_list.peek(Tag::Sequence.into()) {
392 der::expect_tag_and_get_value_limited(
393 tbs_cert_list,
394 Tag::Sequence,
395 der::MAX_DER_SIZE,
396 )?
397 } else {
398 untrusted::Input::from(&[])
399 };
400
401 let mut crl = BorrowedCertRevocationList {
402 signed_data,
403 issuer,
404 revoked_certs,
405 issuing_distribution_point: None,
406 next_update,
407 };
408
409 der::nested(
418 tbs_cert_list,
419 Tag::ContextSpecificConstructed0,
420 Error::MalformedExtensions,
421 |tagged| {
422 der::nested_of_mut(
423 tagged,
424 Tag::Sequence,
425 Tag::Sequence,
426 Error::TrailingData(DerTypeId::CertRevocationListExtension),
427 false,
428 |extension| {
429 crl.remember_extension(&Extension::from_der(extension)?)
435 },
436 )
437 },
438 )?;
439
440 Ok(crl)
441 })?;
442
443 if let Some(der) = crl.issuing_distribution_point {
446 IssuingDistributionPoint::from_der(der)?;
447 }
448
449 Ok(crl)
450 }
451
452 const TYPE_ID: DerTypeId = DerTypeId::CertRevocationList;
453}
454
455impl<'a> IntoIterator for &'a BorrowedCertRevocationList<'a> {
456 type Item = Result<BorrowedRevokedCert<'a>, Error>;
457 type IntoIter = DerIterator<'a, BorrowedRevokedCert<'a>>;
458
459 fn into_iter(self) -> Self::IntoIter {
460 DerIterator::new(self.revoked_certs)
461 }
462}
463
464pub(crate) struct IssuingDistributionPoint<'a> {
465 distribution_point: Option<untrusted::Input<'a>>,
466 pub(crate) only_contains_user_certs: bool,
467 pub(crate) only_contains_ca_certs: bool,
468 pub(crate) only_some_reasons: Option<der::BitStringFlags<'a>>,
469 pub(crate) indirect_crl: bool,
470 pub(crate) only_contains_attribute_certs: bool,
471}
472
473impl<'a> IssuingDistributionPoint<'a> {
474 pub(crate) fn from_der(der: untrusted::Input<'a>) -> Result<Self, Error> {
475 const DISTRIBUTION_POINT_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED;
476 const ONLY_CONTAINS_USER_CERTS_TAG: u8 = CONTEXT_SPECIFIC | 1;
477 const ONLY_CONTAINS_CA_CERTS_TAG: u8 = CONTEXT_SPECIFIC | 2;
478 const ONLY_CONTAINS_SOME_REASONS_TAG: u8 = CONTEXT_SPECIFIC | 3;
479 const INDIRECT_CRL_TAG: u8 = CONTEXT_SPECIFIC | 4;
480 const ONLY_CONTAINS_ATTRIBUTE_CERTS_TAG: u8 = CONTEXT_SPECIFIC | 5;
481
482 let mut result = IssuingDistributionPoint {
483 distribution_point: None,
484 only_contains_user_certs: false,
485 only_contains_ca_certs: false,
486 only_some_reasons: None,
487 indirect_crl: false,
488 only_contains_attribute_certs: false,
489 };
490
491 fn decode_bool(value: untrusted::Input<'_>) -> Result<bool, Error> {
495 let mut reader = untrusted::Reader::new(value);
496 let value = reader.read_byte().map_err(der::end_of_input_err)?;
497 if !reader.at_end() {
498 return Err(Error::BadDer);
499 }
500 match value {
501 0xFF => Ok(true),
502 0x00 => Ok(false), _ => Err(Error::BadDer),
504 }
505 }
506
507 der::nested(
509 &mut untrusted::Reader::new(der),
510 Tag::Sequence,
511 Error::TrailingData(DerTypeId::IssuingDistributionPoint),
512 |der| {
513 while !der.at_end() {
514 let (tag, value) = der::read_tag_and_get_value(der)?;
515 match tag {
516 DISTRIBUTION_POINT_TAG => {
517 set_extension_once(&mut result.distribution_point, || Ok(value))?
518 }
519 ONLY_CONTAINS_USER_CERTS_TAG => {
520 result.only_contains_user_certs = decode_bool(value)?
521 }
522 ONLY_CONTAINS_CA_CERTS_TAG => {
523 result.only_contains_ca_certs = decode_bool(value)?
524 }
525 ONLY_CONTAINS_SOME_REASONS_TAG => {
526 set_extension_once(&mut result.only_some_reasons, || {
527 der::bit_string_flags(value)
528 })?
529 }
530 INDIRECT_CRL_TAG => result.indirect_crl = decode_bool(value)?,
531 ONLY_CONTAINS_ATTRIBUTE_CERTS_TAG => {
532 result.only_contains_attribute_certs = decode_bool(value)?
533 }
534 _ => return Err(Error::BadDer),
535 }
536 }
537
538 Ok(())
539 },
540 )?;
541
542 if result.only_contains_attribute_certs {
545 return Err(Error::MalformedExtensions);
546 }
547
548 if result.indirect_crl {
550 return Err(Error::UnsupportedIndirectCrl);
551 }
552
553 if result.only_some_reasons.is_some() {
555 return Err(Error::UnsupportedRevocationReasonsPartitioning);
556 }
557
558 use DistributionPointName::*;
560 match result.names() {
561 Ok(Some(FullName(_))) => Ok(result),
562 Ok(Some(NameRelativeToCrlIssuer)) | Ok(None) => {
563 Err(Error::UnsupportedCrlIssuingDistributionPoint)
564 }
565 Err(_) => Err(Error::MalformedExtensions),
566 }
567 }
568
569 pub(crate) fn names(&self) -> Result<Option<DistributionPointName<'a>>, Error> {
571 self.distribution_point
572 .map(|input| DistributionPointName::from_der(&mut untrusted::Reader::new(input)))
573 .transpose()
574 }
575
576 pub(crate) fn authoritative_for(&self, node: &PathNode<'a>) -> bool {
591 assert!(!self.only_contains_attribute_certs); if self.only_contains_ca_certs && node.role() != Role::Issuer
595 || self.only_contains_user_certs && node.role() != Role::EndEntity
596 {
597 return false; }
599
600 let cert_dps = match node.cert.crl_distribution_points() {
601 None => return true,
604 Some(cert_dps) => cert_dps,
605 };
606
607 for cert_dp in cert_dps {
608 let Ok(cert_dp) = cert_dp else {
609 continue; };
611
612 if cert_dp.crl_issuer.is_some() || cert_dp.reasons.is_some() {
615 continue; }
617
618 let Ok(Some(DistributionPointName::FullName(dp_general_names))) = cert_dp.names()
619 else {
620 continue; };
622
623 for dp_name in dp_general_names {
626 let dp_uri = match dp_name {
627 Ok(GeneralName::UniformResourceIdentifier(dp_uri)) => dp_uri,
628 Ok(_) => continue, Err(_) => continue, };
631
632 let Ok(Some(DistributionPointName::FullName(idp_general_names))) = self.names()
633 else {
634 return false; };
636
637 for idp_name in idp_general_names.flatten() {
638 match idp_name {
639 GeneralName::UniformResourceIdentifier(idp_uri)
640 if dp_uri.as_slice_less_safe() == idp_uri.as_slice_less_safe() =>
641 {
642 return true; }
644 _ => continue, }
646 }
647 }
648 }
649
650 false
651 }
652}
653
654#[cfg(feature = "alloc")]
661#[derive(Clone, Debug)]
662pub struct OwnedRevokedCert {
663 pub serial_number: Vec<u8>,
665
666 pub revocation_date: UnixTime,
668
669 pub reason_code: Option<RevocationReason>,
674
675 pub invalidity_date: Option<UnixTime>,
679}
680
681#[cfg(feature = "alloc")]
682impl OwnedRevokedCert {
683 pub fn borrow(&self) -> BorrowedRevokedCert<'_> {
685 BorrowedRevokedCert {
686 serial_number: &self.serial_number,
687 revocation_date: self.revocation_date,
688 reason_code: self.reason_code,
689 invalidity_date: self.invalidity_date,
690 }
691 }
692}
693
694#[derive(Debug)]
699pub struct BorrowedRevokedCert<'a> {
700 pub serial_number: &'a [u8],
702
703 pub revocation_date: UnixTime,
705
706 pub reason_code: Option<RevocationReason>,
711
712 pub invalidity_date: Option<UnixTime>,
716}
717
718impl<'a> BorrowedRevokedCert<'a> {
719 #[cfg(feature = "alloc")]
721 pub fn to_owned(&self) -> OwnedRevokedCert {
722 OwnedRevokedCert {
723 serial_number: self.serial_number.to_vec(),
724 revocation_date: self.revocation_date,
725 reason_code: self.reason_code,
726 invalidity_date: self.invalidity_date,
727 }
728 }
729
730 fn remember_extension(&mut self, extension: &Extension<'a>) -> Result<(), Error> {
731 remember_extension(extension, UnknownExtensionPolicy::default(), |id| {
732 match id {
733 21 => set_extension_once(&mut self.reason_code, || der::read_all(extension.value)),
735
736 24 => set_extension_once(&mut self.invalidity_date, || {
738 extension.value.read_all(Error::BadDer, UnixTime::from_der)
739 }),
740
741 29 => Err(Error::UnsupportedIndirectCrl),
749
750 _ => extension.unsupported(UnknownExtensionPolicy::default()),
752 }
753 })
754 }
755}
756
757impl<'a> FromDer<'a> for BorrowedRevokedCert<'a> {
758 fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
759 der::nested(
760 reader,
761 Tag::Sequence,
762 Error::TrailingData(DerTypeId::RevokedCertEntry),
763 |der| {
764 let serial_number = lenient_certificate_serial_number(der)
774 .map_err(|_| Error::InvalidSerialNumber)?
775 .as_slice_less_safe();
776
777 let revocation_date = UnixTime::from_der(der)?;
778
779 let mut revoked_cert = BorrowedRevokedCert {
780 serial_number,
781 revocation_date,
782 reason_code: None,
783 invalidity_date: None,
784 };
785
786 if der.at_end() {
792 return Ok(revoked_cert);
793 }
794
795 let ext_seq = der::expect_tag(der, Tag::Sequence)?;
799 if ext_seq.is_empty() {
800 return Ok(revoked_cert);
801 }
802
803 let mut reader = untrusted::Reader::new(ext_seq);
804 loop {
805 der::nested(
806 &mut reader,
807 Tag::Sequence,
808 Error::TrailingData(DerTypeId::RevokedCertificateExtension),
809 |ext_der| {
810 revoked_cert.remember_extension(&Extension::from_der(ext_der)?)
816 },
817 )?;
818 if reader.at_end() {
819 break;
820 }
821 }
822
823 Ok(revoked_cert)
824 },
825 )
826 }
827
828 const TYPE_ID: DerTypeId = DerTypeId::RevokedCertificate;
829}
830
831#[derive(Debug, Clone, Copy, Eq, PartialEq)]
836#[allow(missing_docs)] pub enum RevocationReason {
838 Unspecified = 0,
841 KeyCompromise = 1,
842 CaCompromise = 2,
843 AffiliationChanged = 3,
844 Superseded = 4,
845 CessationOfOperation = 5,
846 CertificateHold = 6,
847 RemoveFromCrl = 8,
850 PrivilegeWithdrawn = 9,
851 AaCompromise = 10,
852}
853
854impl RevocationReason {
855 pub fn iter() -> impl Iterator<Item = Self> {
857 use RevocationReason::*;
858 [
859 Unspecified,
860 KeyCompromise,
861 CaCompromise,
862 AffiliationChanged,
863 Superseded,
864 CessationOfOperation,
865 CertificateHold,
866 RemoveFromCrl,
867 PrivilegeWithdrawn,
868 AaCompromise,
869 ]
870 .into_iter()
871 }
872}
873
874impl<'a> FromDer<'a> for RevocationReason {
875 fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
877 let input = der::expect_tag(reader, Tag::Enum)?;
878 Self::try_from(input.read_all(Error::BadDer, |reason| {
879 reason.read_byte().map_err(|_| Error::BadDer)
880 })?)
881 }
882
883 const TYPE_ID: DerTypeId = DerTypeId::RevocationReason;
884}
885
886impl TryFrom<u8> for RevocationReason {
887 type Error = Error;
888
889 fn try_from(value: u8) -> Result<Self, Self::Error> {
890 match value {
892 0 => Ok(Self::Unspecified),
893 1 => Ok(Self::KeyCompromise),
894 2 => Ok(Self::CaCompromise),
895 3 => Ok(Self::AffiliationChanged),
896 4 => Ok(Self::Superseded),
897 5 => Ok(Self::CessationOfOperation),
898 6 => Ok(Self::CertificateHold),
899 8 => Ok(Self::RemoveFromCrl),
901 9 => Ok(Self::PrivilegeWithdrawn),
902 10 => Ok(Self::AaCompromise),
903 _ => Err(Error::UnsupportedRevocationReason),
904 }
905 }
906}
907
908#[cfg(feature = "alloc")]
909#[cfg(test)]
910mod tests {
911 use std::time::Duration;
912
913 use pki_types::CertificateDer;
914 use std::println;
915
916 use super::*;
917 use crate::cert::Cert;
918 use crate::end_entity::EndEntityCert;
919 use crate::verify_cert::PartialPath;
920
921 #[test]
922 fn parse_issuing_distribution_point_ext() {
923 let crl = include_bytes!("../../tests/crls/crl.idp.valid.der");
924 let crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
925
926 let crl_issuing_dp = crl
928 .issuing_distribution_point
929 .expect("missing crl distribution point DER");
930
931 #[cfg(feature = "alloc")]
932 {
933 let owned_crl = crl.to_owned().unwrap();
936 assert!(owned_crl.issuing_distribution_point.is_some());
937 }
938
939 let crl_issuing_dp = IssuingDistributionPoint::from_der(untrusted::Input::from(
940 crl_issuing_dp.as_slice_less_safe(),
941 ))
942 .expect("failed to parse issuing distribution point DER");
943
944 assert!(!crl_issuing_dp.only_contains_user_certs);
946 assert!(!crl_issuing_dp.only_contains_ca_certs);
947 assert!(!crl_issuing_dp.indirect_crl);
948
949 assert!(crl_issuing_dp.only_some_reasons.is_none());
952
953 let dp_name = crl_issuing_dp
955 .names()
956 .expect("failed to parse distribution point names")
957 .expect("missing distribution point name");
958 let uri = match dp_name {
959 DistributionPointName::NameRelativeToCrlIssuer => {
960 panic!("unexpected relative dp name")
961 }
962 DistributionPointName::FullName(general_names) => {
963 general_names.map(|general_name| match general_name {
964 Ok(GeneralName::UniformResourceIdentifier(uri)) => uri.as_slice_less_safe(),
965 _ => panic!("unexpected general name type"),
966 })
967 }
968 }
969 .collect::<Vec<_>>();
970 let expected = &["http://crl.trustcor.ca/sub/dv-ssl-rsa-s-0.crl".as_bytes()];
971 assert_eq!(uri, expected);
972 }
973
974 #[test]
975 fn test_issuing_distribution_point_only_user_certs() {
976 let crl = include_bytes!("../../tests/crls/crl.idp.only_user_certs.der");
977 let crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
978
979 let crl_issuing_dp = crl
981 .issuing_distribution_point
982 .expect("missing crl distribution point DER");
983 let crl_issuing_dp = IssuingDistributionPoint::from_der(crl_issuing_dp)
984 .expect("failed to parse issuing distribution point DER");
985
986 assert!(crl_issuing_dp.only_contains_user_certs);
988
989 let ee = CertificateDer::from(
991 &include_bytes!("../../tests/client_auth_revocation/no_crl_ku_chain.ee.der")[..],
992 );
993 let ee = EndEntityCert::try_from(&ee).unwrap();
994 let ca = include_bytes!("../../tests/client_auth_revocation/no_crl_ku_chain.int.a.ca.der");
995 let ca = Cert::from_der(untrusted::Input::from(&ca[..])).unwrap();
996
997 let mut path = PartialPath::new(&ee);
998 path.push(ca).unwrap();
999
1000 assert!(!crl_issuing_dp.authoritative_for(&path.node()));
1001 }
1002
1003 #[test]
1004 fn test_issuing_distribution_point_only_ca_certs() {
1005 let crl = include_bytes!("../../tests/crls/crl.idp.only_ca_certs.der");
1006 let crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
1007
1008 let crl_issuing_dp = crl
1010 .issuing_distribution_point
1011 .expect("missing crl distribution point DER");
1012 let crl_issuing_dp = IssuingDistributionPoint::from_der(crl_issuing_dp)
1013 .expect("failed to parse issuing distribution point DER");
1014
1015 assert!(crl_issuing_dp.only_contains_ca_certs);
1017
1018 let ee = CertificateDer::from(
1020 &include_bytes!("../../tests/client_auth_revocation/no_crl_ku_chain.ee.der")[..],
1021 );
1022 let ee = EndEntityCert::try_from(&ee).unwrap();
1023 let path = PartialPath::new(&ee);
1024
1025 assert!(!crl_issuing_dp.authoritative_for(&path.node()));
1026 }
1027
1028 #[test]
1029 fn test_issuing_distribution_point_indirect() {
1030 let crl = include_bytes!("../../tests/crls/crl.idp.indirect_crl.der");
1031 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1034 assert!(matches!(result, Err(Error::UnsupportedIndirectCrl)));
1035 }
1036
1037 #[test]
1038 fn test_issuing_distribution_only_attribute_certs() {
1039 let crl = include_bytes!("../../tests/crls/crl.idp.only_attribute_certs.der");
1040 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1043 assert!(matches!(result, Err(Error::MalformedExtensions)));
1044 }
1045
1046 #[test]
1047 fn test_issuing_distribution_only_some_reasons() {
1048 let crl = include_bytes!("../../tests/crls/crl.idp.only_some_reasons.der");
1049 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1052 assert!(matches!(
1053 result,
1054 Err(Error::UnsupportedRevocationReasonsPartitioning)
1055 ));
1056 }
1057
1058 #[test]
1059 fn test_issuing_distribution_invalid_bool() {
1060 let crl = include_bytes!("../../tests/crls/crl.idp.invalid.bool.der");
1063 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1065 assert!(matches!(result, Err(Error::BadDer)))
1066 }
1067
1068 #[test]
1069 fn test_issuing_distribution_explicit_false_bool() {
1070 let crl = include_bytes!("../../tests/crls/crl.idp.explicit.false.bool.der");
1073 let crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
1074
1075 let crl_issuing_dp = crl
1077 .issuing_distribution_point
1078 .expect("missing crl distribution point DER");
1079 assert!(IssuingDistributionPoint::from_der(crl_issuing_dp).is_ok());
1080 }
1081
1082 #[test]
1083 fn test_issuing_distribution_unknown_tag() {
1084 let crl = include_bytes!("../../tests/crls/crl.idp.unknown.tag.der");
1087 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1089 assert!(matches!(result, Err(Error::BadDer)));
1090 }
1091
1092 #[test]
1093 fn test_issuing_distribution_invalid_name() {
1094 let crl = include_bytes!("../../tests/crls/crl.idp.invalid.name.der");
1097
1098 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1100 assert!(matches!(result, Err(Error::MalformedExtensions)))
1101 }
1102
1103 #[test]
1104 fn test_issuing_distribution_relative_name() {
1105 let crl = include_bytes!("../../tests/crls/crl.idp.name_relative_to_issuer.der");
1106 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1109 assert!(matches!(
1110 result,
1111 Err(Error::UnsupportedCrlIssuingDistributionPoint)
1112 ))
1113 }
1114
1115 #[test]
1116 fn test_issuing_distribution_no_name() {
1117 let crl = include_bytes!("../../tests/crls/crl.idp.no_distribution_point_name.der");
1118 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1121 assert!(matches!(
1122 result,
1123 Err(Error::UnsupportedCrlIssuingDistributionPoint)
1124 ))
1125 }
1126
1127 #[test]
1128 fn revocation_reasons() {
1129 let testcases: Vec<(u8, RevocationReason)> = vec![
1132 (0, RevocationReason::Unspecified),
1133 (1, RevocationReason::KeyCompromise),
1134 (2, RevocationReason::CaCompromise),
1135 (3, RevocationReason::AffiliationChanged),
1136 (4, RevocationReason::Superseded),
1137 (5, RevocationReason::CessationOfOperation),
1138 (6, RevocationReason::CertificateHold),
1139 (8, RevocationReason::RemoveFromCrl),
1141 (9, RevocationReason::PrivilegeWithdrawn),
1142 (10, RevocationReason::AaCompromise),
1143 ];
1144 for tc in testcases.iter() {
1145 let (id, expected) = tc;
1146 let actual = <u8 as TryInto<RevocationReason>>::try_into(*id)
1147 .expect("unexpected reason code conversion error");
1148 assert_eq!(actual, *expected);
1149 #[cfg(feature = "alloc")]
1150 {
1151 println!("{actual:?}");
1153 }
1154 }
1155
1156 let res = <u8 as TryInto<RevocationReason>>::try_into(7);
1158 assert!(matches!(res, Err(Error::UnsupportedRevocationReason)));
1159
1160 let expected = testcases
1162 .iter()
1163 .map(|(_, reason)| *reason)
1164 .collect::<Vec<_>>();
1165 let actual = RevocationReason::iter().collect::<Vec<_>>();
1166 assert_eq!(actual, expected);
1167 }
1168
1169 #[test]
1170 #[allow(clippy::redundant_clone, clippy::clone_on_copy)]
1172 fn test_derived_traits() {
1173 let crl =
1174 BorrowedCertRevocationList::from_der(include_bytes!("../../tests/crls/crl.valid.der"))
1175 .unwrap();
1176 println!("{crl:?}"); let owned_crl = crl.to_owned().unwrap();
1179 println!("{owned_crl:?}"); let _ = owned_crl.clone(); let mut revoked_certs = crl.into_iter();
1183 println!("{revoked_certs:?}"); let revoked_cert = revoked_certs.next().unwrap().unwrap();
1186 println!("{revoked_cert:?}"); let owned_revoked_cert = revoked_cert.to_owned();
1189 println!("{owned_revoked_cert:?}"); let _ = owned_revoked_cert.clone(); }
1192
1193 #[test]
1194 fn test_enum_conversions() {
1195 let crl =
1196 include_bytes!("../../tests/client_auth_revocation/ee_revoked_crl_ku_ee_depth.crl.der");
1197 let borrowed_crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
1198 let owned_crl = borrowed_crl.to_owned().unwrap();
1199
1200 let _crl = CertRevocationList::from(borrowed_crl);
1202 let _crl = CertRevocationList::from(owned_crl);
1204 }
1205
1206 #[test]
1207 fn test_crl_authoritative_issuer_mismatch() {
1208 let crl = include_bytes!("../../tests/crls/crl.valid.der");
1209 let crl = CertRevocationList::from(BorrowedCertRevocationList::from_der(&crl[..]).unwrap());
1210
1211 let ee = CertificateDer::from(
1212 &include_bytes!("../../tests/client_auth_revocation/no_ku_chain.ee.der")[..],
1213 );
1214 let ee = EndEntityCert::try_from(&ee).unwrap();
1215 let path = PartialPath::new(&ee);
1216
1217 assert!(!crl.authoritative(&path.node()));
1219 }
1220
1221 #[test]
1222 fn test_crl_authoritative_no_idp_no_cert_dp() {
1223 let crl =
1224 include_bytes!("../../tests/client_auth_revocation/ee_revoked_crl_ku_ee_depth.crl.der");
1225 let crl = CertRevocationList::from(BorrowedCertRevocationList::from_der(&crl[..]).unwrap());
1226
1227 let ee = CertificateDer::from(
1228 &include_bytes!("../../tests/client_auth_revocation/ku_chain.ee.der")[..],
1229 );
1230 let ee = EndEntityCert::try_from(&ee).unwrap();
1231 let path = PartialPath::new(&ee);
1232
1233 assert!(crl.authoritative(&path.node()));
1236 }
1237
1238 #[test]
1239 fn test_crl_expired() {
1240 let crl = include_bytes!("../../tests/crls/crl.valid.der");
1241 let crl = CertRevocationList::from(BorrowedCertRevocationList::from_der(&crl[..]).unwrap());
1242 let time = UnixTime::since_unix_epoch(Duration::from_secs(1_706_905_579));
1244 assert!(matches!(
1245 crl.check_expiration(time),
1246 Err(Error::CrlExpired { .. })
1247 ));
1248 }
1249
1250 #[test]
1251 fn test_crl_not_expired() {
1252 let crl = include_bytes!("../../tests/crls/crl.valid.der");
1253 let crl = CertRevocationList::from(BorrowedCertRevocationList::from_der(&crl[..]).unwrap());
1254 let expiration_time = 1_666_210_326;
1256 let time = UnixTime::since_unix_epoch(Duration::from_secs(expiration_time - 1000));
1257
1258 assert!(matches!(crl.check_expiration(time), Ok(())));
1259 }
1260
1261 #[test]
1262 fn test_construct_owned_crl() {
1263 let crl =
1266 include_bytes!("../../tests/client_auth_revocation/ee_revoked_crl_ku_ee_depth.crl.der");
1267 assert!(OwnedCertRevocationList::from_der(crl).is_ok())
1268 }
1269
1270 #[test]
1271 fn test_crl_issuing_distribution_point_illegal_bit_string() {
1272 let crl = &[
1273 0x30, 0x65, 0x30, 0x50, 0x02, 0x01, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
1274 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x0c, 0x31, 0x0a, 0x30, 0x08,
1275 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x01, 0x41, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x31,
1276 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x30,
1277 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0xa0, 0x10, 0x30, 0x0e,
1278 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x1c, 0x04, 0x05, 0x30, 0x03, 0x83, 0x01, 0x00,
1279 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
1280 0x00, 0x03, 0x02, 0x00, 0x00,
1281 ];
1282 assert_eq!(
1283 BorrowedCertRevocationList::from_der(crl).err(),
1284 Some(Error::UnsupportedRevocationReasonsPartitioning)
1285 );
1286 }
1287}