1#[cfg(feature = "alloc")]
16use pki_types::SubjectPublicKeyInfoDer;
17use pki_types::{CertificateDer, DnsName};
18
19use crate::der::{self, CONSTRUCTED, CONTEXT_SPECIFIC, DerIterator, FromDer, Tag};
20use crate::error::{DerTypeId, Error};
21use crate::public_values_eq;
22use crate::signed_data::SignedData;
23use crate::subject_name::{GeneralName, NameIterator, WildcardDnsNameRef};
24use crate::x509::{
25 DistributionPointName, Extension, UnknownExtensionPolicy, remember_extension,
26 set_extension_once,
27};
28
29pub struct Cert<'a> {
31 pub(crate) serial: untrusted::Input<'a>,
32 pub(crate) signed_data: SignedData<'a>,
33 pub(crate) issuer: untrusted::Input<'a>,
34 pub(crate) validity: untrusted::Input<'a>,
35 pub(crate) subject: untrusted::Input<'a>,
36 pub(crate) spki: untrusted::Input<'a>,
37
38 pub(crate) basic_constraints: Option<untrusted::Input<'a>>,
39 pub(crate) key_usage: Option<untrusted::Input<'a>>,
44 pub(crate) eku: Option<untrusted::Input<'a>>,
45 pub(crate) name_constraints: Option<untrusted::Input<'a>>,
46 pub(crate) subject_alt_name: Option<untrusted::Input<'a>>,
47 pub(crate) crl_distribution_points: Option<untrusted::Input<'a>>,
48
49 der: CertificateDer<'a>,
50}
51
52impl<'a> Cert<'a> {
53 pub(crate) fn for_trust_anchor(cert_der: untrusted::Input<'a>) -> Result<Self, Error> {
54 Self::from_input(cert_der, UnknownExtensionPolicy::IgnoreCritical)
55 }
56
57 pub(crate) fn from_der(cert_der: untrusted::Input<'a>) -> Result<Self, Error> {
58 Self::from_input(cert_der, UnknownExtensionPolicy::default())
59 }
60
61 fn from_input(
62 cert_der: untrusted::Input<'a>,
63 ext_policy: UnknownExtensionPolicy,
64 ) -> Result<Self, Error> {
65 let (tbs, signed_data) =
66 cert_der.read_all(Error::TrailingData(DerTypeId::Certificate), |cert_der| {
67 der::nested(
68 cert_der,
69 der::Tag::Sequence,
70 Error::TrailingData(DerTypeId::SignedData),
71 |der| {
72 SignedData::from_der(der, der::TWO_BYTE_DER_SIZE)
74 },
75 )
76 })?;
77
78 tbs.read_all(
79 Error::TrailingData(DerTypeId::CertificateTbsCertificate),
80 |tbs| {
81 version3(tbs)?;
82
83 let serial = lenient_certificate_serial_number(tbs)?;
84
85 let signature = der::expect_tag(tbs, der::Tag::Sequence)?;
86 if !public_values_eq(signature, signed_data.algorithm) {
90 return Err(Error::SignatureAlgorithmMismatch);
91 }
92
93 let issuer = der::expect_tag(tbs, der::Tag::Sequence)?;
94 let validity = der::expect_tag(tbs, der::Tag::Sequence)?;
95 let subject = der::expect_tag(tbs, der::Tag::Sequence)?;
96 let spki = der::expect_tag(tbs, der::Tag::Sequence)?;
97
98 let mut cert = Cert {
104 signed_data,
105 serial,
106 issuer,
107 validity,
108 subject,
109 spki,
110
111 basic_constraints: None,
112 key_usage: None,
113 eku: None,
114 name_constraints: None,
115 subject_alt_name: None,
116 crl_distribution_points: None,
117
118 der: CertificateDer::from(cert_der.as_slice_less_safe()),
119 };
120
121 const ALLOW_EMPTY: bool = true;
130
131 if !tbs.at_end() {
132 der::nested(
133 tbs,
134 der::Tag::ContextSpecificConstructed3,
135 Error::TrailingData(DerTypeId::CertificateExtensions),
136 |tagged| {
137 der::nested_of_mut(
138 tagged,
139 der::Tag::Sequence,
140 der::Tag::Sequence,
141 Error::TrailingData(DerTypeId::Extension),
142 ALLOW_EMPTY,
143 |extension| {
144 remember_cert_extension(
145 &mut cert,
146 &Extension::from_der(extension)?,
147 ext_policy,
148 )
149 },
150 )
151 },
152 )?;
153 }
154
155 Ok(cert)
156 },
157 )
158 }
159
160 pub fn valid_dns_names(&self) -> impl Iterator<Item = &'a str> {
168 NameIterator::new(self.subject_alt_name).filter_map(|result| {
169 let presented_id = match result.ok()? {
170 GeneralName::DnsName(presented) => presented,
171 _ => return None,
172 };
173
174 let dns_str = core::str::from_utf8(presented_id.as_slice_less_safe()).ok()?;
177 match DnsName::try_from(dns_str) {
178 Ok(_) => Some(dns_str),
179 Err(_) => {
180 match WildcardDnsNameRef::try_from_ascii(presented_id.as_slice_less_safe()) {
181 Ok(wildcard_dns_name) => Some(wildcard_dns_name.as_str()),
182 Err(_) => None,
183 }
184 }
185 }
186 })
187 }
188
189 pub fn valid_uri_names(&self) -> impl Iterator<Item = &'a str> {
194 NameIterator::new(self.subject_alt_name).filter_map(|result| {
195 let presented_id = match result.ok()? {
196 GeneralName::UniformResourceIdentifier(presented) => presented,
197 _ => return None,
198 };
199
200 core::str::from_utf8(presented_id.as_slice_less_safe()).ok()
203 })
204 }
205
206 pub fn serial(&self) -> &[u8] {
213 self.serial.as_slice_less_safe()
214 }
215
216 pub fn issuer(&self) -> &[u8] {
220 self.issuer.as_slice_less_safe()
221 }
222
223 pub fn subject(&self) -> &[u8] {
227 self.subject.as_slice_less_safe()
228 }
229
230 #[cfg(feature = "alloc")]
234 pub fn subject_public_key_info(&self) -> SubjectPublicKeyInfoDer<'static> {
235 SubjectPublicKeyInfoDer::from(der::asn1_wrap(
238 Tag::Sequence,
239 self.spki.as_slice_less_safe(),
240 ))
241 }
242
243 pub(crate) fn crl_distribution_points(
245 &self,
246 ) -> Option<impl Iterator<Item = Result<CrlDistributionPoint<'a>, Error>>> {
247 self.crl_distribution_points.map(DerIterator::new)
248 }
249
250 pub fn der(&self) -> CertificateDer<'a> {
252 self.der.clone() }
254}
255
256fn version3(input: &mut untrusted::Reader<'_>) -> Result<(), Error> {
259 der::nested(
260 input,
261 der::Tag::ContextSpecificConstructed0,
262 Error::UnsupportedCertVersion,
263 |input| {
264 let version = u8::from_der(input)?;
265 if version != 2 {
266 return Err(Error::UnsupportedCertVersion);
268 }
269 Ok(())
270 },
271 )
272}
273
274pub(crate) fn lenient_certificate_serial_number<'a>(
275 input: &mut untrusted::Reader<'a>,
276) -> Result<untrusted::Input<'a>, Error> {
277 der::expect_tag(input, Tag::Integer)
288}
289
290fn remember_cert_extension<'a>(
291 cert: &mut Cert<'a>,
292 extension: &Extension<'a>,
293 ext_policy: UnknownExtensionPolicy,
294) -> Result<(), Error> {
295 remember_extension(extension, ext_policy, |id| {
300 let out = match id {
301 15 => &mut cert.key_usage,
303
304 17 => &mut cert.subject_alt_name,
306
307 19 => &mut cert.basic_constraints,
309
310 30 => &mut cert.name_constraints,
312
313 31 => &mut cert.crl_distribution_points,
315
316 37 => &mut cert.eku,
318
319 _ => return extension.unsupported(ext_policy),
321 };
322
323 set_extension_once(out, || {
324 extension.value.read_all(Error::BadDer, |value| match id {
325 15 => Ok(value.read_bytes_to_end()),
328 _ => der::expect_tag(value, Tag::Sequence),
330 })
331 })
332 })
333}
334
335pub(crate) struct CrlDistributionPoint<'a> {
340 distribution_point: Option<untrusted::Input<'a>>,
342
343 pub(crate) reasons: Option<der::BitStringFlags<'a>>,
346
347 pub(crate) crl_issuer: Option<untrusted::Input<'a>>,
350}
351
352impl<'a> CrlDistributionPoint<'a> {
353 pub(crate) fn names(&self) -> Result<Option<DistributionPointName<'a>>, Error> {
355 self.distribution_point
356 .map(|input| DistributionPointName::from_der(&mut untrusted::Reader::new(input)))
357 .transpose()
358 }
359}
360
361impl<'a> FromDer<'a> for CrlDistributionPoint<'a> {
362 fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
363 let mut result = CrlDistributionPoint {
367 distribution_point: None,
368 reasons: None,
369 crl_issuer: None,
370 };
371
372 der::nested(
373 reader,
374 Tag::Sequence,
375 Error::TrailingData(Self::TYPE_ID),
376 |der| {
377 const DISTRIBUTION_POINT_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED;
378 const REASONS_TAG: u8 = CONTEXT_SPECIFIC | 1;
379 const CRL_ISSUER_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED | 2;
380
381 while !der.at_end() {
382 let (tag, value) = der::read_tag_and_get_value(der)?;
383 match tag {
384 DISTRIBUTION_POINT_TAG => {
385 set_extension_once(&mut result.distribution_point, || Ok(value))?
386 }
387 REASONS_TAG => set_extension_once(&mut result.reasons, || {
388 der::bit_string_flags(value)
389 })?,
390 CRL_ISSUER_TAG => set_extension_once(&mut result.crl_issuer, || Ok(value))?,
391 _ => return Err(Error::BadDer),
392 }
393 }
394
395 match (result.distribution_point, result.crl_issuer) {
399 (None, None) => Err(Error::MalformedExtensions),
400 _ => Ok(result),
401 }
402 },
403 )
404 }
405
406 const TYPE_ID: DerTypeId = DerTypeId::CrlDistributionPoint;
407}
408
409#[cfg(test)]
410mod tests {
411 #[cfg(feature = "alloc")]
412 use alloc::vec::Vec;
413
414 use super::*;
415 #[cfg(feature = "alloc")]
416 use crate::crl::RevocationReason;
417
418 #[test]
419 fn test_serial_read() {
423 let ee = include_bytes!("../tests/misc/serial_neg_ee.der");
424 let cert = Cert::from_der(untrusted::Input::from(ee)).expect("failed to parse certificate");
425 assert_eq!(cert.serial.as_slice_less_safe(), &[255, 33, 82, 65, 17]);
426
427 let ee = include_bytes!("../tests/misc/serial_large_positive.der");
428 let cert = Cert::from_der(untrusted::Input::from(ee)).expect("failed to parse certificate");
429 assert_eq!(
430 cert.serial.as_slice_less_safe(),
431 &[
432 0, 230, 9, 254, 122, 234, 0, 104, 140, 224, 36, 180, 237, 32, 27, 31, 239, 82, 180,
433 68, 209
434 ]
435 )
436 }
437
438 #[cfg(feature = "alloc")]
439 #[test]
440 fn test_spki_read() {
441 let ee = include_bytes!("../tests/ed25519/ee.der");
442 let cert = Cert::from_der(untrusted::Input::from(ee)).expect("failed to parse certificate");
443 let expected_spki = [
448 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00, 0xfe, 0x5a,
449 0x1e, 0x36, 0x6c, 0x17, 0x27, 0x5b, 0xf1, 0x58, 0x1e, 0x3a, 0x0e, 0xe6, 0x56, 0x29,
450 0x8d, 0x9e, 0x1b, 0x3f, 0xd3, 0x3f, 0x96, 0x46, 0xef, 0xbf, 0x04, 0x6b, 0xc7, 0x3d,
451 0x47, 0x5c,
452 ];
453 assert_eq!(expected_spki, *cert.subject_public_key_info())
454 }
455
456 #[test]
457 #[cfg(feature = "alloc")]
458 fn test_crl_distribution_point_netflix() {
459 let ee = include_bytes!("../tests/netflix/ee.der");
460 let inter = include_bytes!("../tests/netflix/inter.der");
461 let ee_cert = Cert::from_der(untrusted::Input::from(ee)).expect("failed to parse EE cert");
462 let cert =
463 Cert::from_der(untrusted::Input::from(inter)).expect("failed to parse certificate");
464
465 assert!(ee_cert.crl_distribution_points.is_none());
467
468 let crl_distribution_points = cert
470 .crl_distribution_points()
471 .expect("missing distribution points extension")
472 .collect::<Result<Vec<_>, Error>>()
473 .expect("failed to parse distribution points");
474
475 assert_eq!(crl_distribution_points.len(), 1);
477 let crl_distribution_point = crl_distribution_points
478 .first()
479 .expect("missing distribution point");
480
481 assert!(crl_distribution_point.reasons.is_none());
483
484 assert!(crl_distribution_point.crl_issuer.is_none());
486
487 let distribution_point_name = crl_distribution_point
489 .names()
490 .expect("failed to parse distribution point names")
491 .expect("missing distribution point name");
492
493 let names = match distribution_point_name {
496 DistributionPointName::NameRelativeToCrlIssuer => {
497 panic!("unexpected name relative to crl issuer")
498 }
499 DistributionPointName::FullName(names) => names,
500 };
501
502 let names = names
504 .collect::<Result<Vec<_>, Error>>()
505 .expect("failed to parse general names");
506
507 assert_eq!(names.len(), 1);
509 let name = names.first().expect("missing general name");
510
511 match name {
513 GeneralName::UniformResourceIdentifier(uri) => {
514 assert_eq!(
515 uri.as_slice_less_safe(),
516 "http://s.symcb.com/pca3-g3.crl".as_bytes()
517 );
518 }
519 _ => panic!("unexpected general name type"),
520 }
521 }
522
523 #[test]
524 #[cfg(feature = "alloc")]
525 fn test_crl_distribution_point_with_reasons() {
526 let der = include_bytes!("../tests/crl_distrib_point/with_reasons.der");
527 let cert =
528 Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
529
530 let crl_distribution_points = cert
532 .crl_distribution_points()
533 .expect("missing distribution points extension")
534 .collect::<Result<Vec<_>, Error>>()
535 .expect("failed to parse distribution points");
536
537 assert_eq!(crl_distribution_points.len(), 1);
539 let crl_distribution_point = crl_distribution_points
540 .first()
541 .expect("missing distribution point");
542
543 let reasons = crl_distribution_point
545 .reasons
546 .as_ref()
547 .expect("missing revocation reasons");
548 let expected = &[
549 RevocationReason::KeyCompromise,
550 RevocationReason::AffiliationChanged,
551 ];
552 for reason in RevocationReason::iter() {
553 #[allow(clippy::as_conversions)]
554 match expected.contains(&reason) {
556 true => assert!(reasons.bit_set(reason as usize)),
557 false => assert!(!reasons.bit_set(reason as usize)),
558 }
559 }
560 }
561
562 #[test]
563 #[cfg(feature = "alloc")]
564 fn test_crl_distribution_point_with_crl_issuer() {
565 let der = include_bytes!("../tests/crl_distrib_point/with_crl_issuer.der");
566 let cert =
567 Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
568
569 let crl_distribution_points = cert
571 .crl_distribution_points()
572 .expect("missing distribution points extension")
573 .collect::<Result<Vec<_>, Error>>()
574 .expect("failed to parse distribution points");
575
576 assert_eq!(crl_distribution_points.len(), 1);
578 let crl_distribution_point = crl_distribution_points
579 .first()
580 .expect("missing distribution point");
581
582 assert!(crl_distribution_point.crl_issuer.is_some());
584 assert!(crl_distribution_point.distribution_point.is_none());
585 assert!(crl_distribution_point.reasons.is_none());
586 }
587
588 #[test]
589 #[cfg(feature = "alloc")]
590 fn test_crl_distribution_point_bad_der() {
591 let der = include_bytes!("../tests/crl_distrib_point/unknown_tag.der");
594 let cert =
595 Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
596
597 let result = cert
600 .crl_distribution_points()
601 .expect("missing distribution points extension")
602 .collect::<Result<Vec<_>, Error>>();
603 assert!(matches!(result, Err(Error::BadDer)));
604 }
605
606 #[test]
607 #[cfg(feature = "alloc")]
608 fn test_crl_distribution_point_only_reasons() {
609 let der = include_bytes!("../tests/crl_distrib_point/only_reasons.der");
612 let cert =
613 Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
614
615 let result = cert
618 .crl_distribution_points()
619 .expect("missing distribution points extension")
620 .collect::<Result<Vec<_>, Error>>();
621 assert!(matches!(result, Err(Error::MalformedExtensions)));
622 }
623
624 #[test]
625 #[cfg(feature = "alloc")]
626 fn test_crl_distribution_point_name_relative_to_issuer() {
627 let der = include_bytes!("../tests/crl_distrib_point/dp_name_relative_to_issuer.der");
628 let cert =
629 Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
630
631 let crl_distribution_points = cert
633 .crl_distribution_points()
634 .expect("missing distribution points extension")
635 .collect::<Result<Vec<_>, Error>>()
636 .expect("failed to parse distribution points");
637
638 assert_eq!(crl_distribution_points.len(), 1);
640 let crl_distribution_point = crl_distribution_points
641 .first()
642 .expect("missing distribution point");
643
644 assert!(crl_distribution_point.crl_issuer.is_none());
645 assert!(crl_distribution_point.reasons.is_none());
646
647 let distribution_point_name = crl_distribution_point
649 .names()
650 .expect("failed to parse distribution point names")
651 .expect("missing distribution point name");
652
653 assert!(matches!(
655 distribution_point_name,
656 DistributionPointName::NameRelativeToCrlIssuer
657 ));
658 }
659
660 #[test]
661 #[cfg(feature = "alloc")]
662 fn test_crl_distribution_point_unknown_name_tag() {
663 let der = include_bytes!("../tests/crl_distrib_point/unknown_dp_name_tag.der");
666 let cert =
667 Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
668
669 let crl_distribution_points = cert
671 .crl_distribution_points()
672 .expect("missing distribution points extension")
673 .collect::<Result<Vec<_>, Error>>()
674 .expect("failed to parse distribution points");
675
676 assert_eq!(crl_distribution_points.len(), 1);
678 let crl_distribution_point = crl_distribution_points
679 .first()
680 .expect("missing distribution point");
681
682 let result = crl_distribution_point.names();
684 assert!(matches!(result, Err(Error::BadDer)))
685 }
686
687 #[test]
688 #[cfg(feature = "alloc")]
689 fn test_crl_distribution_point_multiple() {
690 let der = include_bytes!("../tests/crl_distrib_point/multiple_distribution_points.der");
691 let cert =
692 Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
693
694 let crl_distribution_points = cert
696 .crl_distribution_points()
697 .expect("missing distribution points extension")
698 .collect::<Result<Vec<_>, Error>>()
699 .expect("failed to parse distribution points");
700
701 let (point_a, point_b) = (
703 crl_distribution_points
704 .first()
705 .expect("missing first distribution point"),
706 crl_distribution_points
707 .get(1)
708 .expect("missing second distribution point"),
709 );
710
711 fn get_names<'a>(
712 point: &'a CrlDistributionPoint<'a>,
713 ) -> impl Iterator<Item = Result<GeneralName<'a>, Error>> {
714 match point
715 .names()
716 .expect("failed to parse distribution point names")
717 .expect("missing distribution point name")
718 {
719 DistributionPointName::NameRelativeToCrlIssuer => {
720 panic!("unexpected relative name")
721 }
722 DistributionPointName::FullName(names) => names,
723 }
724 }
725
726 fn uri_bytes<'a>(name: &'a GeneralName<'a>) -> &'a [u8] {
727 match name {
728 GeneralName::UniformResourceIdentifier(uri) => uri.as_slice_less_safe(),
729 _ => panic!("unexpected name type"),
730 }
731 }
732
733 let expected_names = [
735 "http://example.com/crl.1.der".as_bytes(),
736 "http://example.com/crl.2.der".as_bytes(),
737 "http://example.com/crl.3.der".as_bytes(),
738 ];
739 let all_names = get_names(point_a)
740 .chain(get_names(point_b))
741 .collect::<Result<Vec<_>, Error>>()
742 .expect("failed to parse names");
743
744 assert_eq!(
745 all_names.iter().map(uri_bytes).collect::<Vec<_>>(),
746 expected_names
747 );
748 }
749}