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::{DistributionPointName, Extension, remember_extension, set_extension_once};
18
19#[derive(Debug)]
25pub enum CertRevocationList<'a> {
26 #[cfg(feature = "alloc")]
28 Owned(OwnedCertRevocationList),
29 Borrowed(BorrowedCertRevocationList<'a>),
31}
32
33#[cfg(feature = "alloc")]
34impl From<OwnedCertRevocationList> for CertRevocationList<'_> {
35 fn from(crl: OwnedCertRevocationList) -> Self {
36 Self::Owned(crl)
37 }
38}
39
40impl<'a> From<BorrowedCertRevocationList<'a>> for CertRevocationList<'a> {
41 fn from(crl: BorrowedCertRevocationList<'a>) -> Self {
42 Self::Borrowed(crl)
43 }
44}
45
46impl CertRevocationList<'_> {
47 pub fn issuer(&self) -> &[u8] {
49 match self {
50 #[cfg(feature = "alloc")]
51 CertRevocationList::Owned(crl) => crl.issuer.as_ref(),
52 CertRevocationList::Borrowed(crl) => crl.issuer.as_slice_less_safe(),
53 }
54 }
55
56 pub fn issuing_distribution_point(&self) -> Option<&[u8]> {
58 match self {
59 #[cfg(feature = "alloc")]
60 CertRevocationList::Owned(crl) => crl.issuing_distribution_point.as_deref(),
61 CertRevocationList::Borrowed(crl) => crl
62 .issuing_distribution_point
63 .map(|idp| idp.as_slice_less_safe()),
64 }
65 }
66
67 pub fn find_serial(&self, serial: &[u8]) -> Result<Option<BorrowedRevokedCert<'_>>, Error> {
70 match self {
71 #[cfg(feature = "alloc")]
72 CertRevocationList::Owned(crl) => crl.find_serial(serial),
73 CertRevocationList::Borrowed(crl) => crl.find_serial(serial),
74 }
75 }
76
77 pub(crate) fn authoritative(&self, path: &PathNode<'_>) -> bool {
94 if self.issuer() != path.cert.issuer() {
97 return false;
98 }
99
100 let crl_idp = match self.issuing_distribution_point() {
101 Some(crl_idp) => {
104 match IssuingDistributionPoint::from_der(untrusted::Input::from(crl_idp)) {
105 Ok(crl_idp) => crl_idp,
106 Err(_) => return false, }
108 }
109 None => return true,
114 };
115
116 crl_idp.authoritative_for(path)
117 }
118
119 pub(crate) fn verify_signature(
122 &self,
123 supported_sig_algs: &[&dyn SignatureVerificationAlgorithm],
124 issuer_spki: untrusted::Input<'_>,
125 budget: &mut Budget,
126 ) -> Result<(), Error> {
127 signed_data::verify_signed_data(
128 supported_sig_algs,
129 issuer_spki,
130 &match self {
131 #[cfg(feature = "alloc")]
132 CertRevocationList::Owned(crl) => crl.signed_data.borrow(),
133 CertRevocationList::Borrowed(crl) => SignedData {
134 data: crl.signed_data.data,
135 algorithm: crl.signed_data.algorithm,
136 signature: crl.signed_data.signature,
137 },
138 },
139 budget,
140 )
141 .map_err(crl_signature_err)
142 }
143
144 pub(crate) fn check_expiration(&self, time: UnixTime) -> Result<(), Error> {
146 let next_update = match self {
147 #[cfg(feature = "alloc")]
148 CertRevocationList::Owned(crl) => crl.next_update,
149 CertRevocationList::Borrowed(crl) => crl.next_update,
150 };
151
152 if time >= next_update {
153 return Err(Error::CrlExpired { time, next_update });
154 }
155
156 Ok(())
157 }
158}
159
160#[cfg(feature = "alloc")]
164#[derive(Debug, Clone)]
165pub struct OwnedCertRevocationList {
166 revoked_certs: BTreeMap<Vec<u8>, OwnedRevokedCert>,
169
170 issuer: Vec<u8>,
171
172 issuing_distribution_point: Option<Vec<u8>>,
173
174 signed_data: signed_data::OwnedSignedData,
175
176 next_update: UnixTime,
177}
178
179#[cfg(feature = "alloc")]
180impl OwnedCertRevocationList {
181 pub fn from_der(crl_der: &[u8]) -> Result<Self, Error> {
194 BorrowedCertRevocationList::from_der(crl_der)?.to_owned()
195 }
196
197 fn find_serial(&self, serial: &[u8]) -> Result<Option<BorrowedRevokedCert<'_>>, Error> {
198 Ok(self
202 .revoked_certs
203 .get(serial)
204 .map(|owned_revoked_cert| owned_revoked_cert.borrow()))
205 }
206}
207
208#[derive(Debug)]
212pub struct BorrowedCertRevocationList<'a> {
213 signed_data: SignedData<'a>,
215
216 issuer: untrusted::Input<'a>,
219
220 issuing_distribution_point: Option<untrusted::Input<'a>>,
222
223 revoked_certs: untrusted::Input<'a>,
225
226 next_update: UnixTime,
227}
228
229impl<'a> BorrowedCertRevocationList<'a> {
230 pub fn from_der(crl_der: &'a [u8]) -> Result<Self, Error> {
241 der::read_all(untrusted::Input::from(crl_der))
242 }
243
244 #[cfg(feature = "alloc")]
247 pub fn to_owned(&self) -> Result<OwnedCertRevocationList, Error> {
248 let revoked_certs = self
251 .into_iter()
252 .collect::<Result<Vec<_>, _>>()?
253 .iter()
254 .map(|revoked_cert| (revoked_cert.serial_number.to_vec(), revoked_cert.to_owned()))
255 .collect::<BTreeMap<_, _>>();
256
257 Ok(OwnedCertRevocationList {
258 signed_data: self.signed_data.to_owned(),
259 issuer: self.issuer.as_slice_less_safe().to_vec(),
260 issuing_distribution_point: self
261 .issuing_distribution_point
262 .map(|idp| idp.as_slice_less_safe().to_vec()),
263 revoked_certs,
264 next_update: self.next_update,
265 })
266 }
267
268 fn remember_extension(&mut self, extension: &Extension<'a>) -> Result<(), Error> {
269 remember_extension(extension, |id| {
270 match id {
271 20 => {
273 extension.value.read_all(Error::InvalidCrlNumber, |der| {
279 let crl_number = der::nonnegative_integer(der)
280 .map_err(|_| Error::InvalidCrlNumber)?
281 .as_slice_less_safe();
282 if crl_number.len() <= 20 {
283 Ok(crl_number)
284 } else {
285 Err(Error::InvalidCrlNumber)
286 }
287 })?;
288 Ok(())
290 }
291
292 27 => Err(Error::UnsupportedDeltaCrl),
295
296 28 => {
299 set_extension_once(&mut self.issuing_distribution_point, || Ok(extension.value))
300 }
301
302 35 => Ok(()),
305
306 _ => extension.unsupported(),
308 }
309 })
310 }
311
312 fn find_serial(&self, serial: &[u8]) -> Result<Option<BorrowedRevokedCert<'_>>, Error> {
313 for revoked_cert_result in self {
314 match revoked_cert_result {
315 Err(e) => return Err(e),
316 Ok(revoked_cert) => {
317 if revoked_cert.serial_number.eq(serial) {
318 return Ok(Some(revoked_cert));
319 }
320 }
321 }
322 }
323
324 Ok(None)
325 }
326}
327
328impl<'a> FromDer<'a> for BorrowedCertRevocationList<'a> {
329 fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
340 let (tbs_cert_list, signed_data) = der::nested_limited(
341 reader,
342 Tag::Sequence,
343 Error::TrailingData(Self::TYPE_ID),
344 |signed_der| SignedData::from_der(signed_der, der::MAX_DER_SIZE),
345 der::MAX_DER_SIZE,
346 )?;
347
348 let crl = tbs_cert_list.read_all(Error::BadDer, |tbs_cert_list| {
349 if u8::from_der(tbs_cert_list)? != 1 {
360 return Err(Error::UnsupportedCrlVersion);
361 }
362
363 let signature = der::expect_tag(tbs_cert_list, Tag::Sequence)?;
367 if !public_values_eq(signature, signed_data.algorithm) {
368 return Err(Error::SignatureAlgorithmMismatch);
369 }
370
371 let issuer = der::expect_tag(tbs_cert_list, Tag::Sequence)?;
374
375 UnixTime::from_der(tbs_cert_list)?;
381
382 let next_update = UnixTime::from_der(tbs_cert_list)?;
387
388 let revoked_certs = if tbs_cert_list.peek(Tag::Sequence.into()) {
393 der::expect_tag_and_get_value_limited(
394 tbs_cert_list,
395 Tag::Sequence,
396 der::MAX_DER_SIZE,
397 )?
398 } else {
399 untrusted::Input::from(&[])
400 };
401
402 let mut crl = BorrowedCertRevocationList {
403 signed_data,
404 issuer,
405 revoked_certs,
406 issuing_distribution_point: None,
407 next_update,
408 };
409
410 der::nested(
419 tbs_cert_list,
420 Tag::ContextSpecificConstructed0,
421 Error::MalformedExtensions,
422 |tagged| {
423 der::nested_of_mut(
424 tagged,
425 Tag::Sequence,
426 Tag::Sequence,
427 Error::TrailingData(DerTypeId::CertRevocationListExtension),
428 false,
429 |extension| {
430 crl.remember_extension(&Extension::from_der(extension)?)
436 },
437 )
438 },
439 )?;
440
441 Ok(crl)
442 })?;
443
444 if let Some(der) = crl.issuing_distribution_point {
447 IssuingDistributionPoint::from_der(der)?;
448 }
449
450 Ok(crl)
451 }
452
453 const TYPE_ID: DerTypeId = DerTypeId::CertRevocationList;
454}
455
456impl<'a> IntoIterator for &'a BorrowedCertRevocationList<'a> {
457 type Item = Result<BorrowedRevokedCert<'a>, Error>;
458 type IntoIter = DerIterator<'a, BorrowedRevokedCert<'a>>;
459
460 fn into_iter(self) -> Self::IntoIter {
461 DerIterator::new(self.revoked_certs)
462 }
463}
464
465pub(crate) struct IssuingDistributionPoint<'a> {
466 distribution_point: Option<untrusted::Input<'a>>,
467 pub(crate) only_contains_user_certs: bool,
468 pub(crate) only_contains_ca_certs: bool,
469 pub(crate) only_some_reasons: Option<der::BitStringFlags<'a>>,
470 pub(crate) indirect_crl: bool,
471 pub(crate) only_contains_attribute_certs: bool,
472}
473
474impl<'a> IssuingDistributionPoint<'a> {
475 pub(crate) fn from_der(der: untrusted::Input<'a>) -> Result<Self, Error> {
476 const DISTRIBUTION_POINT_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED;
477 const ONLY_CONTAINS_USER_CERTS_TAG: u8 = CONTEXT_SPECIFIC | 1;
478 const ONLY_CONTAINS_CA_CERTS_TAG: u8 = CONTEXT_SPECIFIC | 2;
479 const ONLY_CONTAINS_SOME_REASONS_TAG: u8 = CONTEXT_SPECIFIC | 3;
480 const INDIRECT_CRL_TAG: u8 = CONTEXT_SPECIFIC | 4;
481 const ONLY_CONTAINS_ATTRIBUTE_CERTS_TAG: u8 = CONTEXT_SPECIFIC | 5;
482
483 let mut result = IssuingDistributionPoint {
484 distribution_point: None,
485 only_contains_user_certs: false,
486 only_contains_ca_certs: false,
487 only_some_reasons: None,
488 indirect_crl: false,
489 only_contains_attribute_certs: false,
490 };
491
492 fn decode_bool(value: untrusted::Input<'_>) -> Result<bool, Error> {
496 let mut reader = untrusted::Reader::new(value);
497 let value = reader.read_byte().map_err(der::end_of_input_err)?;
498 if !reader.at_end() {
499 return Err(Error::BadDer);
500 }
501 match value {
502 0xFF => Ok(true),
503 0x00 => Ok(false), _ => Err(Error::BadDer),
505 }
506 }
507
508 der::nested(
510 &mut untrusted::Reader::new(der),
511 Tag::Sequence,
512 Error::TrailingData(DerTypeId::IssuingDistributionPoint),
513 |der| {
514 while !der.at_end() {
515 let (tag, value) = der::read_tag_and_get_value(der)?;
516 match tag {
517 DISTRIBUTION_POINT_TAG => {
518 set_extension_once(&mut result.distribution_point, || Ok(value))?
519 }
520 ONLY_CONTAINS_USER_CERTS_TAG => {
521 result.only_contains_user_certs = decode_bool(value)?
522 }
523 ONLY_CONTAINS_CA_CERTS_TAG => {
524 result.only_contains_ca_certs = decode_bool(value)?
525 }
526 ONLY_CONTAINS_SOME_REASONS_TAG => {
527 set_extension_once(&mut result.only_some_reasons, || {
528 der::bit_string_flags(value)
529 })?
530 }
531 INDIRECT_CRL_TAG => result.indirect_crl = decode_bool(value)?,
532 ONLY_CONTAINS_ATTRIBUTE_CERTS_TAG => {
533 result.only_contains_attribute_certs = decode_bool(value)?
534 }
535 _ => return Err(Error::BadDer),
536 }
537 }
538
539 Ok(())
540 },
541 )?;
542
543 if result.only_contains_attribute_certs {
546 return Err(Error::MalformedExtensions);
547 }
548
549 if result.indirect_crl {
551 return Err(Error::UnsupportedIndirectCrl);
552 }
553
554 if result.only_some_reasons.is_some() {
556 return Err(Error::UnsupportedRevocationReasonsPartitioning);
557 }
558
559 use DistributionPointName::*;
561 match result.names() {
562 Ok(Some(FullName(_))) => Ok(result),
563 Ok(Some(NameRelativeToCrlIssuer)) | Ok(None) => {
564 Err(Error::UnsupportedCrlIssuingDistributionPoint)
565 }
566 Err(_) => Err(Error::MalformedExtensions),
567 }
568 }
569
570 pub(crate) fn names(&self) -> Result<Option<DistributionPointName<'a>>, Error> {
572 self.distribution_point
573 .map(|input| DistributionPointName::from_der(&mut untrusted::Reader::new(input)))
574 .transpose()
575 }
576
577 pub(crate) fn authoritative_for(&self, node: &PathNode<'a>) -> bool {
592 assert!(!self.only_contains_attribute_certs); if self.only_contains_ca_certs && node.role() != Role::Issuer
596 || self.only_contains_user_certs && node.role() != Role::EndEntity
597 {
598 return false;
599 }
600
601 let cert_dps = match node.cert.crl_distribution_points() {
602 None => return true,
605 Some(cert_dps) => cert_dps,
606 };
607
608 let mut idp_general_names = match self.names() {
609 Ok(Some(DistributionPointName::FullName(general_names))) => general_names,
610 _ => return false, };
612
613 for cert_dp in cert_dps {
614 let cert_dp = match cert_dp {
615 Ok(cert_dp) => cert_dp,
616 Err(_) => return false,
618 };
619
620 if cert_dp.crl_issuer.is_some() || cert_dp.reasons.is_some() {
623 return false;
624 }
625
626 let mut dp_general_names = match cert_dp.names() {
627 Ok(Some(DistributionPointName::FullName(general_names))) => general_names,
628 _ => return false, };
630
631 if Self::uri_name_in_common(&mut idp_general_names, &mut dp_general_names) {
634 return true;
635 }
636 }
637
638 false
639 }
640
641 fn uri_name_in_common(
642 idp_general_names: &mut DerIterator<'a, GeneralName<'a>>,
643 dp_general_names: &mut DerIterator<'a, GeneralName<'a>>,
644 ) -> bool {
645 use GeneralName::UniformResourceIdentifier;
646 for name in idp_general_names.flatten() {
647 let uri = match name {
648 UniformResourceIdentifier(uri) => uri,
649 _ => continue,
650 };
651
652 for other_name in (&mut *dp_general_names).flatten() {
653 match other_name {
654 UniformResourceIdentifier(other_uri)
655 if uri.as_slice_less_safe() == other_uri.as_slice_less_safe() =>
656 {
657 return true;
658 }
659 _ => continue,
660 }
661 }
662 }
663 false
664 }
665}
666
667#[cfg(feature = "alloc")]
674#[derive(Clone, Debug)]
675pub struct OwnedRevokedCert {
676 pub serial_number: Vec<u8>,
678
679 pub revocation_date: UnixTime,
681
682 pub reason_code: Option<RevocationReason>,
687
688 pub invalidity_date: Option<UnixTime>,
692}
693
694#[cfg(feature = "alloc")]
695impl OwnedRevokedCert {
696 pub fn borrow(&self) -> BorrowedRevokedCert<'_> {
698 BorrowedRevokedCert {
699 serial_number: &self.serial_number,
700 revocation_date: self.revocation_date,
701 reason_code: self.reason_code,
702 invalidity_date: self.invalidity_date,
703 }
704 }
705}
706
707#[derive(Debug)]
712pub struct BorrowedRevokedCert<'a> {
713 pub serial_number: &'a [u8],
715
716 pub revocation_date: UnixTime,
718
719 pub reason_code: Option<RevocationReason>,
724
725 pub invalidity_date: Option<UnixTime>,
729}
730
731impl<'a> BorrowedRevokedCert<'a> {
732 #[cfg(feature = "alloc")]
734 pub fn to_owned(&self) -> OwnedRevokedCert {
735 OwnedRevokedCert {
736 serial_number: self.serial_number.to_vec(),
737 revocation_date: self.revocation_date,
738 reason_code: self.reason_code,
739 invalidity_date: self.invalidity_date,
740 }
741 }
742
743 fn remember_extension(&mut self, extension: &Extension<'a>) -> Result<(), Error> {
744 remember_extension(extension, |id| {
745 match id {
746 21 => set_extension_once(&mut self.reason_code, || der::read_all(extension.value)),
748
749 24 => set_extension_once(&mut self.invalidity_date, || {
751 extension.value.read_all(Error::BadDer, UnixTime::from_der)
752 }),
753
754 29 => Err(Error::UnsupportedIndirectCrl),
762
763 _ => extension.unsupported(),
765 }
766 })
767 }
768}
769
770impl<'a> FromDer<'a> for BorrowedRevokedCert<'a> {
771 fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
772 der::nested(
773 reader,
774 Tag::Sequence,
775 Error::TrailingData(DerTypeId::RevokedCertEntry),
776 |der| {
777 let serial_number = lenient_certificate_serial_number(der)
787 .map_err(|_| Error::InvalidSerialNumber)?
788 .as_slice_less_safe();
789
790 let revocation_date = UnixTime::from_der(der)?;
791
792 let mut revoked_cert = BorrowedRevokedCert {
793 serial_number,
794 revocation_date,
795 reason_code: None,
796 invalidity_date: None,
797 };
798
799 if der.at_end() {
805 return Ok(revoked_cert);
806 }
807
808 let ext_seq = der::expect_tag(der, Tag::Sequence)?;
812 if ext_seq.is_empty() {
813 return Ok(revoked_cert);
814 }
815
816 let mut reader = untrusted::Reader::new(ext_seq);
817 loop {
818 der::nested(
819 &mut reader,
820 Tag::Sequence,
821 Error::TrailingData(DerTypeId::RevokedCertificateExtension),
822 |ext_der| {
823 revoked_cert.remember_extension(&Extension::from_der(ext_der)?)
829 },
830 )?;
831 if reader.at_end() {
832 break;
833 }
834 }
835
836 Ok(revoked_cert)
837 },
838 )
839 }
840
841 const TYPE_ID: DerTypeId = DerTypeId::RevokedCertificate;
842}
843
844#[derive(Debug, Clone, Copy, Eq, PartialEq)]
849#[allow(missing_docs)] pub enum RevocationReason {
851 Unspecified = 0,
854 KeyCompromise = 1,
855 CaCompromise = 2,
856 AffiliationChanged = 3,
857 Superseded = 4,
858 CessationOfOperation = 5,
859 CertificateHold = 6,
860 RemoveFromCrl = 8,
863 PrivilegeWithdrawn = 9,
864 AaCompromise = 10,
865}
866
867impl RevocationReason {
868 pub fn iter() -> impl Iterator<Item = Self> {
870 use RevocationReason::*;
871 [
872 Unspecified,
873 KeyCompromise,
874 CaCompromise,
875 AffiliationChanged,
876 Superseded,
877 CessationOfOperation,
878 CertificateHold,
879 RemoveFromCrl,
880 PrivilegeWithdrawn,
881 AaCompromise,
882 ]
883 .into_iter()
884 }
885}
886
887impl<'a> FromDer<'a> for RevocationReason {
888 fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
890 let input = der::expect_tag(reader, Tag::Enum)?;
891 Self::try_from(input.read_all(Error::BadDer, |reason| {
892 reason.read_byte().map_err(|_| Error::BadDer)
893 })?)
894 }
895
896 const TYPE_ID: DerTypeId = DerTypeId::RevocationReason;
897}
898
899impl TryFrom<u8> for RevocationReason {
900 type Error = Error;
901
902 fn try_from(value: u8) -> Result<Self, Self::Error> {
903 match value {
905 0 => Ok(Self::Unspecified),
906 1 => Ok(Self::KeyCompromise),
907 2 => Ok(Self::CaCompromise),
908 3 => Ok(Self::AffiliationChanged),
909 4 => Ok(Self::Superseded),
910 5 => Ok(Self::CessationOfOperation),
911 6 => Ok(Self::CertificateHold),
912 8 => Ok(Self::RemoveFromCrl),
914 9 => Ok(Self::PrivilegeWithdrawn),
915 10 => Ok(Self::AaCompromise),
916 _ => Err(Error::UnsupportedRevocationReason),
917 }
918 }
919}
920
921#[cfg(feature = "alloc")]
922#[cfg(test)]
923mod tests {
924 use std::time::Duration;
925
926 use pki_types::CertificateDer;
927 use std::prelude::v1::*;
928 use std::println;
929
930 use super::*;
931 use crate::cert::Cert;
932 use crate::end_entity::EndEntityCert;
933 use crate::verify_cert::PartialPath;
934
935 #[test]
936 fn parse_issuing_distribution_point_ext() {
937 let crl = include_bytes!("../../tests/crls/crl.idp.valid.der");
938 let crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
939
940 let crl_issuing_dp = crl
942 .issuing_distribution_point
943 .expect("missing crl distribution point DER");
944
945 #[cfg(feature = "alloc")]
946 {
947 let owned_crl = crl.to_owned().unwrap();
950 assert!(owned_crl.issuing_distribution_point.is_some());
951 }
952
953 let crl_issuing_dp = IssuingDistributionPoint::from_der(untrusted::Input::from(
954 crl_issuing_dp.as_slice_less_safe(),
955 ))
956 .expect("failed to parse issuing distribution point DER");
957
958 assert!(!crl_issuing_dp.only_contains_user_certs);
960 assert!(!crl_issuing_dp.only_contains_ca_certs);
961 assert!(!crl_issuing_dp.indirect_crl);
962
963 assert!(crl_issuing_dp.only_some_reasons.is_none());
966
967 let dp_name = crl_issuing_dp
969 .names()
970 .expect("failed to parse distribution point names")
971 .expect("missing distribution point name");
972 let uri = match dp_name {
973 DistributionPointName::NameRelativeToCrlIssuer => {
974 panic!("unexpected relative dp name")
975 }
976 DistributionPointName::FullName(general_names) => {
977 general_names.map(|general_name| match general_name {
978 Ok(GeneralName::UniformResourceIdentifier(uri)) => uri.as_slice_less_safe(),
979 _ => panic!("unexpected general name type"),
980 })
981 }
982 }
983 .collect::<Vec<_>>();
984 let expected = &["http://crl.trustcor.ca/sub/dv-ssl-rsa-s-0.crl".as_bytes()];
985 assert_eq!(uri, expected);
986 }
987
988 #[test]
989 fn test_issuing_distribution_point_only_user_certs() {
990 let crl = include_bytes!("../../tests/crls/crl.idp.only_user_certs.der");
991 let crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
992
993 let crl_issuing_dp = crl
995 .issuing_distribution_point
996 .expect("missing crl distribution point DER");
997 let crl_issuing_dp = IssuingDistributionPoint::from_der(crl_issuing_dp)
998 .expect("failed to parse issuing distribution point DER");
999
1000 assert!(crl_issuing_dp.only_contains_user_certs);
1002
1003 let ee = CertificateDer::from(
1005 &include_bytes!("../../tests/client_auth_revocation/no_crl_ku_chain.ee.der")[..],
1006 );
1007 let ee = EndEntityCert::try_from(&ee).unwrap();
1008 let ca = include_bytes!("../../tests/client_auth_revocation/no_crl_ku_chain.int.a.ca.der");
1009 let ca = Cert::from_der(untrusted::Input::from(&ca[..])).unwrap();
1010
1011 let mut path = PartialPath::new(&ee);
1012 path.push(ca).unwrap();
1013
1014 assert!(!crl_issuing_dp.authoritative_for(&path.node()));
1015 }
1016
1017 #[test]
1018 fn test_issuing_distribution_point_only_ca_certs() {
1019 let crl = include_bytes!("../../tests/crls/crl.idp.only_ca_certs.der");
1020 let crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
1021
1022 let crl_issuing_dp = crl
1024 .issuing_distribution_point
1025 .expect("missing crl distribution point DER");
1026 let crl_issuing_dp = IssuingDistributionPoint::from_der(crl_issuing_dp)
1027 .expect("failed to parse issuing distribution point DER");
1028
1029 assert!(crl_issuing_dp.only_contains_ca_certs);
1031
1032 let ee = CertificateDer::from(
1034 &include_bytes!("../../tests/client_auth_revocation/no_crl_ku_chain.ee.der")[..],
1035 );
1036 let ee = EndEntityCert::try_from(&ee).unwrap();
1037 let path = PartialPath::new(&ee);
1038
1039 assert!(!crl_issuing_dp.authoritative_for(&path.node()));
1040 }
1041
1042 #[test]
1043 fn test_issuing_distribution_point_indirect() {
1044 let crl = include_bytes!("../../tests/crls/crl.idp.indirect_crl.der");
1045 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1048 assert!(matches!(result, Err(Error::UnsupportedIndirectCrl)));
1049 }
1050
1051 #[test]
1052 fn test_issuing_distribution_only_attribute_certs() {
1053 let crl = include_bytes!("../../tests/crls/crl.idp.only_attribute_certs.der");
1054 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1057 assert!(matches!(result, Err(Error::MalformedExtensions)));
1058 }
1059
1060 #[test]
1061 fn test_issuing_distribution_only_some_reasons() {
1062 let crl = include_bytes!("../../tests/crls/crl.idp.only_some_reasons.der");
1063 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1066 assert!(matches!(
1067 result,
1068 Err(Error::UnsupportedRevocationReasonsPartitioning)
1069 ));
1070 }
1071
1072 #[test]
1073 fn test_issuing_distribution_invalid_bool() {
1074 let crl = include_bytes!("../../tests/crls/crl.idp.invalid.bool.der");
1077 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1079 assert!(matches!(result, Err(Error::BadDer)))
1080 }
1081
1082 #[test]
1083 fn test_issuing_distribution_explicit_false_bool() {
1084 let crl = include_bytes!("../../tests/crls/crl.idp.explicit.false.bool.der");
1087 let crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
1088
1089 let crl_issuing_dp = crl
1091 .issuing_distribution_point
1092 .expect("missing crl distribution point DER");
1093 assert!(IssuingDistributionPoint::from_der(crl_issuing_dp).is_ok());
1094 }
1095
1096 #[test]
1097 fn test_issuing_distribution_unknown_tag() {
1098 let crl = include_bytes!("../../tests/crls/crl.idp.unknown.tag.der");
1101 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1103 assert!(matches!(result, Err(Error::BadDer)));
1104 }
1105
1106 #[test]
1107 fn test_issuing_distribution_invalid_name() {
1108 let crl = include_bytes!("../../tests/crls/crl.idp.invalid.name.der");
1111
1112 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1114 assert!(matches!(result, Err(Error::MalformedExtensions)))
1115 }
1116
1117 #[test]
1118 fn test_issuing_distribution_relative_name() {
1119 let crl = include_bytes!("../../tests/crls/crl.idp.name_relative_to_issuer.der");
1120 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1123 assert!(matches!(
1124 result,
1125 Err(Error::UnsupportedCrlIssuingDistributionPoint)
1126 ))
1127 }
1128
1129 #[test]
1130 fn test_issuing_distribution_no_name() {
1131 let crl = include_bytes!("../../tests/crls/crl.idp.no_distribution_point_name.der");
1132 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1135 assert!(matches!(
1136 result,
1137 Err(Error::UnsupportedCrlIssuingDistributionPoint)
1138 ))
1139 }
1140
1141 #[test]
1142 fn revocation_reasons() {
1143 let testcases: Vec<(u8, RevocationReason)> = vec![
1146 (0, RevocationReason::Unspecified),
1147 (1, RevocationReason::KeyCompromise),
1148 (2, RevocationReason::CaCompromise),
1149 (3, RevocationReason::AffiliationChanged),
1150 (4, RevocationReason::Superseded),
1151 (5, RevocationReason::CessationOfOperation),
1152 (6, RevocationReason::CertificateHold),
1153 (8, RevocationReason::RemoveFromCrl),
1155 (9, RevocationReason::PrivilegeWithdrawn),
1156 (10, RevocationReason::AaCompromise),
1157 ];
1158 for tc in testcases.iter() {
1159 let (id, expected) = tc;
1160 let actual = <u8 as TryInto<RevocationReason>>::try_into(*id)
1161 .expect("unexpected reason code conversion error");
1162 assert_eq!(actual, *expected);
1163 #[cfg(feature = "alloc")]
1164 {
1165 println!("{:?}", actual);
1167 }
1168 }
1169
1170 let res = <u8 as TryInto<RevocationReason>>::try_into(7);
1172 assert!(matches!(res, Err(Error::UnsupportedRevocationReason)));
1173
1174 let expected = testcases
1176 .iter()
1177 .map(|(_, reason)| *reason)
1178 .collect::<Vec<_>>();
1179 let actual = RevocationReason::iter().collect::<Vec<_>>();
1180 assert_eq!(actual, expected);
1181 }
1182
1183 #[test]
1184 #[allow(clippy::redundant_clone, clippy::clone_on_copy)]
1186 fn test_derived_traits() {
1187 let crl =
1188 BorrowedCertRevocationList::from_der(include_bytes!("../../tests/crls/crl.valid.der"))
1189 .unwrap();
1190 println!("{:?}", crl); let owned_crl = crl.to_owned().unwrap();
1193 println!("{:?}", owned_crl); let _ = owned_crl.clone(); let mut revoked_certs = crl.into_iter();
1197 println!("{:?}", revoked_certs); let revoked_cert = revoked_certs.next().unwrap().unwrap();
1200 println!("{:?}", revoked_cert); let owned_revoked_cert = revoked_cert.to_owned();
1203 println!("{:?}", owned_revoked_cert); let _ = owned_revoked_cert.clone(); }
1206
1207 #[test]
1208 fn test_enum_conversions() {
1209 let crl =
1210 include_bytes!("../../tests/client_auth_revocation/ee_revoked_crl_ku_ee_depth.crl.der");
1211 let borrowed_crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
1212 let owned_crl = borrowed_crl.to_owned().unwrap();
1213
1214 let _crl = CertRevocationList::from(borrowed_crl);
1216 let _crl = CertRevocationList::from(owned_crl);
1218 }
1219
1220 #[test]
1221 fn test_crl_authoritative_issuer_mismatch() {
1222 let crl = include_bytes!("../../tests/crls/crl.valid.der");
1223 let crl = CertRevocationList::from(BorrowedCertRevocationList::from_der(&crl[..]).unwrap());
1224
1225 let ee = CertificateDer::from(
1226 &include_bytes!("../../tests/client_auth_revocation/no_ku_chain.ee.der")[..],
1227 );
1228 let ee = EndEntityCert::try_from(&ee).unwrap();
1229 let path = PartialPath::new(&ee);
1230
1231 assert!(!crl.authoritative(&path.node()));
1233 }
1234
1235 #[test]
1236 fn test_crl_authoritative_no_idp_no_cert_dp() {
1237 let crl =
1238 include_bytes!("../../tests/client_auth_revocation/ee_revoked_crl_ku_ee_depth.crl.der");
1239 let crl = CertRevocationList::from(BorrowedCertRevocationList::from_der(&crl[..]).unwrap());
1240
1241 let ee = CertificateDer::from(
1242 &include_bytes!("../../tests/client_auth_revocation/ku_chain.ee.der")[..],
1243 );
1244 let ee = EndEntityCert::try_from(&ee).unwrap();
1245 let path = PartialPath::new(&ee);
1246
1247 assert!(crl.authoritative(&path.node()));
1250 }
1251
1252 #[test]
1253 fn test_crl_expired() {
1254 let crl = include_bytes!("../../tests/crls/crl.valid.der");
1255 let crl = CertRevocationList::from(BorrowedCertRevocationList::from_der(&crl[..]).unwrap());
1256 let time = UnixTime::since_unix_epoch(Duration::from_secs(1_706_905_579));
1258 assert!(matches!(
1259 crl.check_expiration(time),
1260 Err(Error::CrlExpired { .. })
1261 ));
1262 }
1263
1264 #[test]
1265 fn test_crl_not_expired() {
1266 let crl = include_bytes!("../../tests/crls/crl.valid.der");
1267 let crl = CertRevocationList::from(BorrowedCertRevocationList::from_der(&crl[..]).unwrap());
1268 let expiration_time = 1_666_210_326;
1270 let time = UnixTime::since_unix_epoch(Duration::from_secs(expiration_time - 1000));
1271
1272 assert!(matches!(crl.check_expiration(time), Ok(())));
1273 }
1274
1275 #[test]
1276 fn test_construct_owned_crl() {
1277 let crl =
1280 include_bytes!("../../tests/client_auth_revocation/ee_revoked_crl_ku_ee_depth.crl.der");
1281 assert!(OwnedCertRevocationList::from_der(crl).is_ok())
1282 }
1283}