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 match revoked_cert_result {
318 Err(e) => return Err(e),
319 Ok(revoked_cert) => {
320 if revoked_cert.serial_number.eq(serial) {
321 return Ok(Some(revoked_cert));
322 }
323 }
324 }
325 }
326
327 Ok(None)
328 }
329}
330
331impl<'a> FromDer<'a> for BorrowedCertRevocationList<'a> {
332 fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
343 let (tbs_cert_list, signed_data) = der::nested_limited(
344 reader,
345 Tag::Sequence,
346 Error::TrailingData(Self::TYPE_ID),
347 |signed_der| SignedData::from_der(signed_der, der::MAX_DER_SIZE),
348 der::MAX_DER_SIZE,
349 )?;
350
351 let crl = tbs_cert_list.read_all(Error::BadDer, |tbs_cert_list| {
352 if u8::from_der(tbs_cert_list)? != 1 {
363 return Err(Error::UnsupportedCrlVersion);
364 }
365
366 let signature = der::expect_tag(tbs_cert_list, Tag::Sequence)?;
370 if !public_values_eq(signature, signed_data.algorithm) {
371 return Err(Error::SignatureAlgorithmMismatch);
372 }
373
374 let issuer = der::expect_tag(tbs_cert_list, Tag::Sequence)?;
377
378 UnixTime::from_der(tbs_cert_list)?;
384
385 let next_update = UnixTime::from_der(tbs_cert_list)?;
390
391 let revoked_certs = if tbs_cert_list.peek(Tag::Sequence.into()) {
396 der::expect_tag_and_get_value_limited(
397 tbs_cert_list,
398 Tag::Sequence,
399 der::MAX_DER_SIZE,
400 )?
401 } else {
402 untrusted::Input::from(&[])
403 };
404
405 let mut crl = BorrowedCertRevocationList {
406 signed_data,
407 issuer,
408 revoked_certs,
409 issuing_distribution_point: None,
410 next_update,
411 };
412
413 der::nested(
422 tbs_cert_list,
423 Tag::ContextSpecificConstructed0,
424 Error::MalformedExtensions,
425 |tagged| {
426 der::nested_of_mut(
427 tagged,
428 Tag::Sequence,
429 Tag::Sequence,
430 Error::TrailingData(DerTypeId::CertRevocationListExtension),
431 false,
432 |extension| {
433 crl.remember_extension(&Extension::from_der(extension)?)
439 },
440 )
441 },
442 )?;
443
444 Ok(crl)
445 })?;
446
447 if let Some(der) = crl.issuing_distribution_point {
450 IssuingDistributionPoint::from_der(der)?;
451 }
452
453 Ok(crl)
454 }
455
456 const TYPE_ID: DerTypeId = DerTypeId::CertRevocationList;
457}
458
459impl<'a> IntoIterator for &'a BorrowedCertRevocationList<'a> {
460 type Item = Result<BorrowedRevokedCert<'a>, Error>;
461 type IntoIter = DerIterator<'a, BorrowedRevokedCert<'a>>;
462
463 fn into_iter(self) -> Self::IntoIter {
464 DerIterator::new(self.revoked_certs)
465 }
466}
467
468pub(crate) struct IssuingDistributionPoint<'a> {
469 distribution_point: Option<untrusted::Input<'a>>,
470 pub(crate) only_contains_user_certs: bool,
471 pub(crate) only_contains_ca_certs: bool,
472 pub(crate) only_some_reasons: Option<der::BitStringFlags<'a>>,
473 pub(crate) indirect_crl: bool,
474 pub(crate) only_contains_attribute_certs: bool,
475}
476
477impl<'a> IssuingDistributionPoint<'a> {
478 pub(crate) fn from_der(der: untrusted::Input<'a>) -> Result<Self, Error> {
479 const DISTRIBUTION_POINT_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED;
480 const ONLY_CONTAINS_USER_CERTS_TAG: u8 = CONTEXT_SPECIFIC | 1;
481 const ONLY_CONTAINS_CA_CERTS_TAG: u8 = CONTEXT_SPECIFIC | 2;
482 const ONLY_CONTAINS_SOME_REASONS_TAG: u8 = CONTEXT_SPECIFIC | 3;
483 const INDIRECT_CRL_TAG: u8 = CONTEXT_SPECIFIC | 4;
484 const ONLY_CONTAINS_ATTRIBUTE_CERTS_TAG: u8 = CONTEXT_SPECIFIC | 5;
485
486 let mut result = IssuingDistributionPoint {
487 distribution_point: None,
488 only_contains_user_certs: false,
489 only_contains_ca_certs: false,
490 only_some_reasons: None,
491 indirect_crl: false,
492 only_contains_attribute_certs: false,
493 };
494
495 fn decode_bool(value: untrusted::Input<'_>) -> Result<bool, Error> {
499 let mut reader = untrusted::Reader::new(value);
500 let value = reader.read_byte().map_err(der::end_of_input_err)?;
501 if !reader.at_end() {
502 return Err(Error::BadDer);
503 }
504 match value {
505 0xFF => Ok(true),
506 0x00 => Ok(false), _ => Err(Error::BadDer),
508 }
509 }
510
511 der::nested(
513 &mut untrusted::Reader::new(der),
514 Tag::Sequence,
515 Error::TrailingData(DerTypeId::IssuingDistributionPoint),
516 |der| {
517 while !der.at_end() {
518 let (tag, value) = der::read_tag_and_get_value(der)?;
519 match tag {
520 DISTRIBUTION_POINT_TAG => {
521 set_extension_once(&mut result.distribution_point, || Ok(value))?
522 }
523 ONLY_CONTAINS_USER_CERTS_TAG => {
524 result.only_contains_user_certs = decode_bool(value)?
525 }
526 ONLY_CONTAINS_CA_CERTS_TAG => {
527 result.only_contains_ca_certs = decode_bool(value)?
528 }
529 ONLY_CONTAINS_SOME_REASONS_TAG => {
530 set_extension_once(&mut result.only_some_reasons, || {
531 der::bit_string_flags(value)
532 })?
533 }
534 INDIRECT_CRL_TAG => result.indirect_crl = decode_bool(value)?,
535 ONLY_CONTAINS_ATTRIBUTE_CERTS_TAG => {
536 result.only_contains_attribute_certs = decode_bool(value)?
537 }
538 _ => return Err(Error::BadDer),
539 }
540 }
541
542 Ok(())
543 },
544 )?;
545
546 if result.only_contains_attribute_certs {
549 return Err(Error::MalformedExtensions);
550 }
551
552 if result.indirect_crl {
554 return Err(Error::UnsupportedIndirectCrl);
555 }
556
557 if result.only_some_reasons.is_some() {
559 return Err(Error::UnsupportedRevocationReasonsPartitioning);
560 }
561
562 use DistributionPointName::*;
564 match result.names() {
565 Ok(Some(FullName(_))) => Ok(result),
566 Ok(Some(NameRelativeToCrlIssuer)) | Ok(None) => {
567 Err(Error::UnsupportedCrlIssuingDistributionPoint)
568 }
569 Err(_) => Err(Error::MalformedExtensions),
570 }
571 }
572
573 pub(crate) fn names(&self) -> Result<Option<DistributionPointName<'a>>, Error> {
575 self.distribution_point
576 .map(|input| DistributionPointName::from_der(&mut untrusted::Reader::new(input)))
577 .transpose()
578 }
579
580 pub(crate) fn authoritative_for(&self, node: &PathNode<'a>) -> bool {
595 assert!(!self.only_contains_attribute_certs); if self.only_contains_ca_certs && node.role() != Role::Issuer
599 || self.only_contains_user_certs && node.role() != Role::EndEntity
600 {
601 return false; }
603
604 let cert_dps = match node.cert.crl_distribution_points() {
605 None => return true,
608 Some(cert_dps) => cert_dps,
609 };
610
611 for cert_dp in cert_dps {
612 let Ok(cert_dp) = cert_dp else {
613 continue; };
615
616 if cert_dp.crl_issuer.is_some() || cert_dp.reasons.is_some() {
619 continue; }
621
622 let Ok(Some(DistributionPointName::FullName(dp_general_names))) = cert_dp.names()
623 else {
624 continue; };
626
627 for dp_name in dp_general_names {
630 let dp_uri = match dp_name {
631 Ok(GeneralName::UniformResourceIdentifier(dp_uri)) => dp_uri,
632 Ok(_) => continue, Err(_) => continue, };
635
636 let Ok(Some(DistributionPointName::FullName(idp_general_names))) = self.names()
637 else {
638 return false; };
640
641 for idp_name in idp_general_names.flatten() {
642 match idp_name {
643 GeneralName::UniformResourceIdentifier(idp_uri)
644 if dp_uri.as_slice_less_safe() == idp_uri.as_slice_less_safe() =>
645 {
646 return true; }
648 _ => continue, }
650 }
651 }
652 }
653
654 false
655 }
656}
657
658#[cfg(feature = "alloc")]
665#[derive(Clone, Debug)]
666pub struct OwnedRevokedCert {
667 pub serial_number: Vec<u8>,
669
670 pub revocation_date: UnixTime,
672
673 pub reason_code: Option<RevocationReason>,
678
679 pub invalidity_date: Option<UnixTime>,
683}
684
685#[cfg(feature = "alloc")]
686impl OwnedRevokedCert {
687 pub fn borrow(&self) -> BorrowedRevokedCert<'_> {
689 BorrowedRevokedCert {
690 serial_number: &self.serial_number,
691 revocation_date: self.revocation_date,
692 reason_code: self.reason_code,
693 invalidity_date: self.invalidity_date,
694 }
695 }
696}
697
698#[derive(Debug)]
703pub struct BorrowedRevokedCert<'a> {
704 pub serial_number: &'a [u8],
706
707 pub revocation_date: UnixTime,
709
710 pub reason_code: Option<RevocationReason>,
715
716 pub invalidity_date: Option<UnixTime>,
720}
721
722impl<'a> BorrowedRevokedCert<'a> {
723 #[cfg(feature = "alloc")]
725 pub fn to_owned(&self) -> OwnedRevokedCert {
726 OwnedRevokedCert {
727 serial_number: self.serial_number.to_vec(),
728 revocation_date: self.revocation_date,
729 reason_code: self.reason_code,
730 invalidity_date: self.invalidity_date,
731 }
732 }
733
734 fn remember_extension(&mut self, extension: &Extension<'a>) -> Result<(), Error> {
735 remember_extension(extension, UnknownExtensionPolicy::default(), |id| {
736 match id {
737 21 => set_extension_once(&mut self.reason_code, || der::read_all(extension.value)),
739
740 24 => set_extension_once(&mut self.invalidity_date, || {
742 extension.value.read_all(Error::BadDer, UnixTime::from_der)
743 }),
744
745 29 => Err(Error::UnsupportedIndirectCrl),
753
754 _ => extension.unsupported(UnknownExtensionPolicy::default()),
756 }
757 })
758 }
759}
760
761impl<'a> FromDer<'a> for BorrowedRevokedCert<'a> {
762 fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
763 der::nested(
764 reader,
765 Tag::Sequence,
766 Error::TrailingData(DerTypeId::RevokedCertEntry),
767 |der| {
768 let serial_number = lenient_certificate_serial_number(der)
778 .map_err(|_| Error::InvalidSerialNumber)?
779 .as_slice_less_safe();
780
781 let revocation_date = UnixTime::from_der(der)?;
782
783 let mut revoked_cert = BorrowedRevokedCert {
784 serial_number,
785 revocation_date,
786 reason_code: None,
787 invalidity_date: None,
788 };
789
790 if der.at_end() {
796 return Ok(revoked_cert);
797 }
798
799 let ext_seq = der::expect_tag(der, Tag::Sequence)?;
803 if ext_seq.is_empty() {
804 return Ok(revoked_cert);
805 }
806
807 let mut reader = untrusted::Reader::new(ext_seq);
808 loop {
809 der::nested(
810 &mut reader,
811 Tag::Sequence,
812 Error::TrailingData(DerTypeId::RevokedCertificateExtension),
813 |ext_der| {
814 revoked_cert.remember_extension(&Extension::from_der(ext_der)?)
820 },
821 )?;
822 if reader.at_end() {
823 break;
824 }
825 }
826
827 Ok(revoked_cert)
828 },
829 )
830 }
831
832 const TYPE_ID: DerTypeId = DerTypeId::RevokedCertificate;
833}
834
835#[derive(Debug, Clone, Copy, Eq, PartialEq)]
840#[allow(missing_docs)] pub enum RevocationReason {
842 Unspecified = 0,
845 KeyCompromise = 1,
846 CaCompromise = 2,
847 AffiliationChanged = 3,
848 Superseded = 4,
849 CessationOfOperation = 5,
850 CertificateHold = 6,
851 RemoveFromCrl = 8,
854 PrivilegeWithdrawn = 9,
855 AaCompromise = 10,
856}
857
858impl RevocationReason {
859 pub fn iter() -> impl Iterator<Item = Self> {
861 use RevocationReason::*;
862 [
863 Unspecified,
864 KeyCompromise,
865 CaCompromise,
866 AffiliationChanged,
867 Superseded,
868 CessationOfOperation,
869 CertificateHold,
870 RemoveFromCrl,
871 PrivilegeWithdrawn,
872 AaCompromise,
873 ]
874 .into_iter()
875 }
876}
877
878impl<'a> FromDer<'a> for RevocationReason {
879 fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
881 let input = der::expect_tag(reader, Tag::Enum)?;
882 Self::try_from(input.read_all(Error::BadDer, |reason| {
883 reason.read_byte().map_err(|_| Error::BadDer)
884 })?)
885 }
886
887 const TYPE_ID: DerTypeId = DerTypeId::RevocationReason;
888}
889
890impl TryFrom<u8> for RevocationReason {
891 type Error = Error;
892
893 fn try_from(value: u8) -> Result<Self, Self::Error> {
894 match value {
896 0 => Ok(Self::Unspecified),
897 1 => Ok(Self::KeyCompromise),
898 2 => Ok(Self::CaCompromise),
899 3 => Ok(Self::AffiliationChanged),
900 4 => Ok(Self::Superseded),
901 5 => Ok(Self::CessationOfOperation),
902 6 => Ok(Self::CertificateHold),
903 8 => Ok(Self::RemoveFromCrl),
905 9 => Ok(Self::PrivilegeWithdrawn),
906 10 => Ok(Self::AaCompromise),
907 _ => Err(Error::UnsupportedRevocationReason),
908 }
909 }
910}
911
912#[cfg(feature = "alloc")]
913#[cfg(test)]
914mod tests {
915 use std::time::Duration;
916
917 use pki_types::CertificateDer;
918 use std::println;
919
920 use super::*;
921 use crate::cert::Cert;
922 use crate::end_entity::EndEntityCert;
923 use crate::verify_cert::PartialPath;
924
925 #[test]
926 fn parse_issuing_distribution_point_ext() {
927 let crl = include_bytes!("../../tests/crls/crl.idp.valid.der");
928 let crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
929
930 let crl_issuing_dp = crl
932 .issuing_distribution_point
933 .expect("missing crl distribution point DER");
934
935 #[cfg(feature = "alloc")]
936 {
937 let owned_crl = crl.to_owned().unwrap();
940 assert!(owned_crl.issuing_distribution_point.is_some());
941 }
942
943 let crl_issuing_dp = IssuingDistributionPoint::from_der(untrusted::Input::from(
944 crl_issuing_dp.as_slice_less_safe(),
945 ))
946 .expect("failed to parse issuing distribution point DER");
947
948 assert!(!crl_issuing_dp.only_contains_user_certs);
950 assert!(!crl_issuing_dp.only_contains_ca_certs);
951 assert!(!crl_issuing_dp.indirect_crl);
952
953 assert!(crl_issuing_dp.only_some_reasons.is_none());
956
957 let dp_name = crl_issuing_dp
959 .names()
960 .expect("failed to parse distribution point names")
961 .expect("missing distribution point name");
962 let uri = match dp_name {
963 DistributionPointName::NameRelativeToCrlIssuer => {
964 panic!("unexpected relative dp name")
965 }
966 DistributionPointName::FullName(general_names) => {
967 general_names.map(|general_name| match general_name {
968 Ok(GeneralName::UniformResourceIdentifier(uri)) => uri.as_slice_less_safe(),
969 _ => panic!("unexpected general name type"),
970 })
971 }
972 }
973 .collect::<Vec<_>>();
974 let expected = &["http://crl.trustcor.ca/sub/dv-ssl-rsa-s-0.crl".as_bytes()];
975 assert_eq!(uri, expected);
976 }
977
978 #[test]
979 fn test_issuing_distribution_point_only_user_certs() {
980 let crl = include_bytes!("../../tests/crls/crl.idp.only_user_certs.der");
981 let crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
982
983 let crl_issuing_dp = crl
985 .issuing_distribution_point
986 .expect("missing crl distribution point DER");
987 let crl_issuing_dp = IssuingDistributionPoint::from_der(crl_issuing_dp)
988 .expect("failed to parse issuing distribution point DER");
989
990 assert!(crl_issuing_dp.only_contains_user_certs);
992
993 let ee = CertificateDer::from(
995 &include_bytes!("../../tests/client_auth_revocation/no_crl_ku_chain.ee.der")[..],
996 );
997 let ee = EndEntityCert::try_from(&ee).unwrap();
998 let ca = include_bytes!("../../tests/client_auth_revocation/no_crl_ku_chain.int.a.ca.der");
999 let ca = Cert::from_der(untrusted::Input::from(&ca[..])).unwrap();
1000
1001 let mut path = PartialPath::new(&ee);
1002 path.push(ca).unwrap();
1003
1004 assert!(!crl_issuing_dp.authoritative_for(&path.node()));
1005 }
1006
1007 #[test]
1008 fn test_issuing_distribution_point_only_ca_certs() {
1009 let crl = include_bytes!("../../tests/crls/crl.idp.only_ca_certs.der");
1010 let crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
1011
1012 let crl_issuing_dp = crl
1014 .issuing_distribution_point
1015 .expect("missing crl distribution point DER");
1016 let crl_issuing_dp = IssuingDistributionPoint::from_der(crl_issuing_dp)
1017 .expect("failed to parse issuing distribution point DER");
1018
1019 assert!(crl_issuing_dp.only_contains_ca_certs);
1021
1022 let ee = CertificateDer::from(
1024 &include_bytes!("../../tests/client_auth_revocation/no_crl_ku_chain.ee.der")[..],
1025 );
1026 let ee = EndEntityCert::try_from(&ee).unwrap();
1027 let path = PartialPath::new(&ee);
1028
1029 assert!(!crl_issuing_dp.authoritative_for(&path.node()));
1030 }
1031
1032 #[test]
1033 fn test_issuing_distribution_point_indirect() {
1034 let crl = include_bytes!("../../tests/crls/crl.idp.indirect_crl.der");
1035 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1038 assert!(matches!(result, Err(Error::UnsupportedIndirectCrl)));
1039 }
1040
1041 #[test]
1042 fn test_issuing_distribution_only_attribute_certs() {
1043 let crl = include_bytes!("../../tests/crls/crl.idp.only_attribute_certs.der");
1044 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1047 assert!(matches!(result, Err(Error::MalformedExtensions)));
1048 }
1049
1050 #[test]
1051 fn test_issuing_distribution_only_some_reasons() {
1052 let crl = include_bytes!("../../tests/crls/crl.idp.only_some_reasons.der");
1053 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1056 assert!(matches!(
1057 result,
1058 Err(Error::UnsupportedRevocationReasonsPartitioning)
1059 ));
1060 }
1061
1062 #[test]
1063 fn test_issuing_distribution_invalid_bool() {
1064 let crl = include_bytes!("../../tests/crls/crl.idp.invalid.bool.der");
1067 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1069 assert!(matches!(result, Err(Error::BadDer)))
1070 }
1071
1072 #[test]
1073 fn test_issuing_distribution_explicit_false_bool() {
1074 let crl = include_bytes!("../../tests/crls/crl.idp.explicit.false.bool.der");
1077 let crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
1078
1079 let crl_issuing_dp = crl
1081 .issuing_distribution_point
1082 .expect("missing crl distribution point DER");
1083 assert!(IssuingDistributionPoint::from_der(crl_issuing_dp).is_ok());
1084 }
1085
1086 #[test]
1087 fn test_issuing_distribution_unknown_tag() {
1088 let crl = include_bytes!("../../tests/crls/crl.idp.unknown.tag.der");
1091 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1093 assert!(matches!(result, Err(Error::BadDer)));
1094 }
1095
1096 #[test]
1097 fn test_issuing_distribution_invalid_name() {
1098 let crl = include_bytes!("../../tests/crls/crl.idp.invalid.name.der");
1101
1102 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1104 assert!(matches!(result, Err(Error::MalformedExtensions)))
1105 }
1106
1107 #[test]
1108 fn test_issuing_distribution_relative_name() {
1109 let crl = include_bytes!("../../tests/crls/crl.idp.name_relative_to_issuer.der");
1110 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1113 assert!(matches!(
1114 result,
1115 Err(Error::UnsupportedCrlIssuingDistributionPoint)
1116 ))
1117 }
1118
1119 #[test]
1120 fn test_issuing_distribution_no_name() {
1121 let crl = include_bytes!("../../tests/crls/crl.idp.no_distribution_point_name.der");
1122 let result = BorrowedCertRevocationList::from_der(&crl[..]);
1125 assert!(matches!(
1126 result,
1127 Err(Error::UnsupportedCrlIssuingDistributionPoint)
1128 ))
1129 }
1130
1131 #[test]
1132 fn revocation_reasons() {
1133 let testcases: Vec<(u8, RevocationReason)> = vec![
1136 (0, RevocationReason::Unspecified),
1137 (1, RevocationReason::KeyCompromise),
1138 (2, RevocationReason::CaCompromise),
1139 (3, RevocationReason::AffiliationChanged),
1140 (4, RevocationReason::Superseded),
1141 (5, RevocationReason::CessationOfOperation),
1142 (6, RevocationReason::CertificateHold),
1143 (8, RevocationReason::RemoveFromCrl),
1145 (9, RevocationReason::PrivilegeWithdrawn),
1146 (10, RevocationReason::AaCompromise),
1147 ];
1148 for tc in testcases.iter() {
1149 let (id, expected) = tc;
1150 let actual = <u8 as TryInto<RevocationReason>>::try_into(*id)
1151 .expect("unexpected reason code conversion error");
1152 assert_eq!(actual, *expected);
1153 #[cfg(feature = "alloc")]
1154 {
1155 println!("{actual:?}");
1157 }
1158 }
1159
1160 let res = <u8 as TryInto<RevocationReason>>::try_into(7);
1162 assert!(matches!(res, Err(Error::UnsupportedRevocationReason)));
1163
1164 let expected = testcases
1166 .iter()
1167 .map(|(_, reason)| *reason)
1168 .collect::<Vec<_>>();
1169 let actual = RevocationReason::iter().collect::<Vec<_>>();
1170 assert_eq!(actual, expected);
1171 }
1172
1173 #[test]
1174 #[allow(clippy::redundant_clone, clippy::clone_on_copy)]
1176 fn test_derived_traits() {
1177 let crl =
1178 BorrowedCertRevocationList::from_der(include_bytes!("../../tests/crls/crl.valid.der"))
1179 .unwrap();
1180 println!("{crl:?}"); let owned_crl = crl.to_owned().unwrap();
1183 println!("{owned_crl:?}"); let _ = owned_crl.clone(); let mut revoked_certs = crl.into_iter();
1187 println!("{revoked_certs:?}"); let revoked_cert = revoked_certs.next().unwrap().unwrap();
1190 println!("{revoked_cert:?}"); let owned_revoked_cert = revoked_cert.to_owned();
1193 println!("{owned_revoked_cert:?}"); let _ = owned_revoked_cert.clone(); }
1196
1197 #[test]
1198 fn test_enum_conversions() {
1199 let crl =
1200 include_bytes!("../../tests/client_auth_revocation/ee_revoked_crl_ku_ee_depth.crl.der");
1201 let borrowed_crl = BorrowedCertRevocationList::from_der(&crl[..]).unwrap();
1202 let owned_crl = borrowed_crl.to_owned().unwrap();
1203
1204 let _crl = CertRevocationList::from(borrowed_crl);
1206 let _crl = CertRevocationList::from(owned_crl);
1208 }
1209
1210 #[test]
1211 fn test_crl_authoritative_issuer_mismatch() {
1212 let crl = include_bytes!("../../tests/crls/crl.valid.der");
1213 let crl = CertRevocationList::from(BorrowedCertRevocationList::from_der(&crl[..]).unwrap());
1214
1215 let ee = CertificateDer::from(
1216 &include_bytes!("../../tests/client_auth_revocation/no_ku_chain.ee.der")[..],
1217 );
1218 let ee = EndEntityCert::try_from(&ee).unwrap();
1219 let path = PartialPath::new(&ee);
1220
1221 assert!(!crl.authoritative(&path.node()));
1223 }
1224
1225 #[test]
1226 fn test_crl_authoritative_no_idp_no_cert_dp() {
1227 let crl =
1228 include_bytes!("../../tests/client_auth_revocation/ee_revoked_crl_ku_ee_depth.crl.der");
1229 let crl = CertRevocationList::from(BorrowedCertRevocationList::from_der(&crl[..]).unwrap());
1230
1231 let ee = CertificateDer::from(
1232 &include_bytes!("../../tests/client_auth_revocation/ku_chain.ee.der")[..],
1233 );
1234 let ee = EndEntityCert::try_from(&ee).unwrap();
1235 let path = PartialPath::new(&ee);
1236
1237 assert!(crl.authoritative(&path.node()));
1240 }
1241
1242 #[test]
1243 fn test_crl_expired() {
1244 let crl = include_bytes!("../../tests/crls/crl.valid.der");
1245 let crl = CertRevocationList::from(BorrowedCertRevocationList::from_der(&crl[..]).unwrap());
1246 let time = UnixTime::since_unix_epoch(Duration::from_secs(1_706_905_579));
1248 assert!(matches!(
1249 crl.check_expiration(time),
1250 Err(Error::CrlExpired { .. })
1251 ));
1252 }
1253
1254 #[test]
1255 fn test_crl_not_expired() {
1256 let crl = include_bytes!("../../tests/crls/crl.valid.der");
1257 let crl = CertRevocationList::from(BorrowedCertRevocationList::from_der(&crl[..]).unwrap());
1258 let expiration_time = 1_666_210_326;
1260 let time = UnixTime::since_unix_epoch(Duration::from_secs(expiration_time - 1000));
1261
1262 assert!(matches!(crl.check_expiration(time), Ok(())));
1263 }
1264
1265 #[test]
1266 fn test_construct_owned_crl() {
1267 let crl =
1270 include_bytes!("../../tests/client_auth_revocation/ee_revoked_crl_ku_ee_depth.crl.der");
1271 assert!(OwnedCertRevocationList::from_der(crl).is_ok())
1272 }
1273
1274 #[test]
1275 fn test_crl_issuing_distribution_point_illegal_bit_string() {
1276 let crl = &[
1277 0x30, 0x65, 0x30, 0x50, 0x02, 0x01, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
1278 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x0c, 0x31, 0x0a, 0x30, 0x08,
1279 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x01, 0x41, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x31,
1280 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x30,
1281 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0xa0, 0x10, 0x30, 0x0e,
1282 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x1c, 0x04, 0x05, 0x30, 0x03, 0x83, 0x01, 0x00,
1283 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
1284 0x00, 0x03, 0x02, 0x00, 0x00,
1285 ];
1286 assert_eq!(
1287 BorrowedCertRevocationList::from_der(crl).err(),
1288 Some(Error::UnsupportedRevocationReasonsPartitioning)
1289 );
1290 }
1291}