webpki/
cert.rs

1// Copyright 2015 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
10// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15#[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::{DistributionPointName, Extension, remember_extension, set_extension_once};
25
26/// A parsed X509 certificate.
27pub struct Cert<'a> {
28    pub(crate) serial: untrusted::Input<'a>,
29    pub(crate) signed_data: SignedData<'a>,
30    pub(crate) issuer: untrusted::Input<'a>,
31    pub(crate) validity: untrusted::Input<'a>,
32    pub(crate) subject: untrusted::Input<'a>,
33    pub(crate) spki: untrusted::Input<'a>,
34
35    pub(crate) basic_constraints: Option<untrusted::Input<'a>>,
36    // key usage (KU) extension (if any). When validating certificate revocation lists (CRLs) this
37    // field will be consulted to determine if the cert is allowed to sign CRLs. For cert validation
38    // this field is ignored (for more detail see in `verify_cert.rs` and
39    // `check_issuer_independent_properties`).
40    pub(crate) key_usage: Option<untrusted::Input<'a>>,
41    pub(crate) eku: Option<untrusted::Input<'a>>,
42    pub(crate) name_constraints: Option<untrusted::Input<'a>>,
43    pub(crate) subject_alt_name: Option<untrusted::Input<'a>>,
44    pub(crate) crl_distribution_points: Option<untrusted::Input<'a>>,
45
46    der: CertificateDer<'a>,
47}
48
49impl<'a> Cert<'a> {
50    pub(crate) fn from_der(cert_der: untrusted::Input<'a>) -> Result<Self, Error> {
51        let (tbs, signed_data) =
52            cert_der.read_all(Error::TrailingData(DerTypeId::Certificate), |cert_der| {
53                der::nested(
54                    cert_der,
55                    der::Tag::Sequence,
56                    Error::TrailingData(DerTypeId::SignedData),
57                    |der| {
58                        // limited to SEQUENCEs of size 2^16 or less.
59                        SignedData::from_der(der, der::TWO_BYTE_DER_SIZE)
60                    },
61                )
62            })?;
63
64        tbs.read_all(
65            Error::TrailingData(DerTypeId::CertificateTbsCertificate),
66            |tbs| {
67                version3(tbs)?;
68
69                let serial = lenient_certificate_serial_number(tbs)?;
70
71                let signature = der::expect_tag(tbs, der::Tag::Sequence)?;
72                // TODO: In mozilla::pkix, the comparison is done based on the
73                // normalized value (ignoring whether or not there is an optional NULL
74                // parameter for RSA-based algorithms), so this may be too strict.
75                if !public_values_eq(signature, signed_data.algorithm) {
76                    return Err(Error::SignatureAlgorithmMismatch);
77                }
78
79                let issuer = der::expect_tag(tbs, der::Tag::Sequence)?;
80                let validity = der::expect_tag(tbs, der::Tag::Sequence)?;
81                let subject = der::expect_tag(tbs, der::Tag::Sequence)?;
82                let spki = der::expect_tag(tbs, der::Tag::Sequence)?;
83
84                // In theory there could be fields [1] issuerUniqueID and [2]
85                // subjectUniqueID, but in practice there never are, and to keep the
86                // code small and simple we don't accept any certificates that do
87                // contain them.
88
89                let mut cert = Cert {
90                    signed_data,
91                    serial,
92                    issuer,
93                    validity,
94                    subject,
95                    spki,
96
97                    basic_constraints: None,
98                    key_usage: None,
99                    eku: None,
100                    name_constraints: None,
101                    subject_alt_name: None,
102                    crl_distribution_points: None,
103
104                    der: CertificateDer::from(cert_der.as_slice_less_safe()),
105                };
106
107                if !tbs.at_end() {
108                    der::nested(
109                        tbs,
110                        der::Tag::ContextSpecificConstructed3,
111                        Error::TrailingData(DerTypeId::CertificateExtensions),
112                        |tagged| {
113                            der::nested_of_mut(
114                                tagged,
115                                der::Tag::Sequence,
116                                der::Tag::Sequence,
117                                Error::TrailingData(DerTypeId::Extension),
118                                |extension| {
119                                    remember_cert_extension(
120                                        &mut cert,
121                                        &Extension::from_der(extension)?,
122                                    )
123                                },
124                            )
125                        },
126                    )?;
127                }
128
129                Ok(cert)
130            },
131        )
132    }
133
134    /// Returns a list of valid DNS names provided in the subject alternative names extension
135    ///
136    /// This function must not be used to implement custom DNS name verification.
137    /// Checking that a certificate is valid for a given subject name should always be done with
138    /// [EndEntityCert::verify_is_valid_for_subject_name].
139    ///
140    /// [EndEntityCert::verify_is_valid_for_subject_name]: crate::EndEntityCert::verify_is_valid_for_subject_name
141    pub fn valid_dns_names(&self) -> impl Iterator<Item = &str> {
142        NameIterator::new(self.subject_alt_name).filter_map(|result| {
143            let presented_id = match result.ok()? {
144                GeneralName::DnsName(presented) => presented,
145                _ => return None,
146            };
147
148            // if the name could be converted to a DNS name, return it; otherwise,
149            // keep going.
150            let dns_str = core::str::from_utf8(presented_id.as_slice_less_safe()).ok()?;
151            match DnsName::try_from(dns_str) {
152                Ok(_) => Some(dns_str),
153                Err(_) => {
154                    match WildcardDnsNameRef::try_from_ascii(presented_id.as_slice_less_safe()) {
155                        Ok(wildcard_dns_name) => Some(wildcard_dns_name.as_str()),
156                        Err(_) => None,
157                    }
158                }
159            }
160        })
161    }
162
163    /// Raw DER encoded certificate serial number.
164    pub fn serial(&self) -> &[u8] {
165        self.serial.as_slice_less_safe()
166    }
167
168    /// Raw DER encoded certificate issuer.
169    pub fn issuer(&self) -> &[u8] {
170        self.issuer.as_slice_less_safe()
171    }
172
173    /// Raw DER encoded certificate subject.
174    pub fn subject(&self) -> &[u8] {
175        self.subject.as_slice_less_safe()
176    }
177
178    /// Get the RFC 5280-compliant [`SubjectPublicKeyInfoDer`] (SPKI) of this [`Cert`].
179    #[cfg(feature = "alloc")]
180    pub fn subject_public_key_info(&self) -> SubjectPublicKeyInfoDer<'static> {
181        // Our SPKI representation contains only the content of the RFC 5280 SEQUENCE
182        // So we wrap the SPKI contents back into a properly-encoded ASN.1 SEQUENCE
183        SubjectPublicKeyInfoDer::from(der::asn1_wrap(
184            Tag::Sequence,
185            self.spki.as_slice_less_safe(),
186        ))
187    }
188
189    /// Returns an iterator over the certificate's cRLDistributionPoints extension values, if any.
190    pub(crate) fn crl_distribution_points(
191        &self,
192    ) -> Option<impl Iterator<Item = Result<CrlDistributionPoint<'a>, Error>>> {
193        self.crl_distribution_points.map(DerIterator::new)
194    }
195
196    /// Raw DER encoded representation of the certificate.
197    pub fn der(&self) -> CertificateDer<'a> {
198        self.der.clone() // This is cheap, just cloning a reference.
199    }
200}
201
202// mozilla::pkix supports v1, v2, v3, and v4, including both the implicit
203// (correct) and explicit (incorrect) encoding of v1. We allow only v3.
204fn version3(input: &mut untrusted::Reader<'_>) -> Result<(), Error> {
205    der::nested(
206        input,
207        der::Tag::ContextSpecificConstructed0,
208        Error::UnsupportedCertVersion,
209        |input| {
210            let version = u8::from_der(input)?;
211            if version != 2 {
212                // v3
213                return Err(Error::UnsupportedCertVersion);
214            }
215            Ok(())
216        },
217    )
218}
219
220pub(crate) fn lenient_certificate_serial_number<'a>(
221    input: &mut untrusted::Reader<'a>,
222) -> Result<untrusted::Input<'a>, Error> {
223    // https://tools.ietf.org/html/rfc5280#section-4.1.2.2:
224    // * Conforming CAs MUST NOT use serialNumber values longer than 20 octets."
225    // * "The serial number MUST be a positive integer [...]"
226    //
227    // However, we don't enforce these constraints, as there are widely-deployed trust anchors
228    // and many X.509 implementations in common use that violate these constraints. This is called
229    // out by the same section of RFC 5280 as cited above:
230    //   Note: Non-conforming CAs may issue certificates with serial numbers
231    //   that are negative or zero.  Certificate users SHOULD be prepared to
232    //   gracefully handle such certificates.
233    der::expect_tag(input, Tag::Integer)
234}
235
236fn remember_cert_extension<'a>(
237    cert: &mut Cert<'a>,
238    extension: &Extension<'a>,
239) -> Result<(), Error> {
240    // We don't do anything with certificate policies so we can safely ignore
241    // all policy-related stuff. We assume that the policy-related extensions
242    // are not marked critical.
243
244    remember_extension(extension, |id| {
245        let out = match id {
246            // id-ce-keyUsage 2.5.29.15.
247            15 => &mut cert.key_usage,
248
249            // id-ce-subjectAltName 2.5.29.17
250            17 => &mut cert.subject_alt_name,
251
252            // id-ce-basicConstraints 2.5.29.19
253            19 => &mut cert.basic_constraints,
254
255            // id-ce-nameConstraints 2.5.29.30
256            30 => &mut cert.name_constraints,
257
258            // id-ce-cRLDistributionPoints 2.5.29.31
259            31 => &mut cert.crl_distribution_points,
260
261            // id-ce-extKeyUsage 2.5.29.37
262            37 => &mut cert.eku,
263
264            // Unsupported extension
265            _ => return extension.unsupported(),
266        };
267
268        set_extension_once(out, || {
269            extension.value.read_all(Error::BadDer, |value| match id {
270                // Unlike the other extensions we remember KU is a BitString and not a Sequence. We
271                // read the raw bytes here and parse at the time of use.
272                15 => Ok(value.read_bytes_to_end()),
273                // All other remembered certificate extensions are wrapped in a Sequence.
274                _ => der::expect_tag(value, Tag::Sequence),
275            })
276        })
277    })
278}
279
280/// A certificate revocation list (CRL) distribution point, describing a source of
281/// CRL information for a given certificate as described in RFC 5280 section 4.2.3.13[^1].
282///
283/// [^1]: <https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.13>
284pub(crate) struct CrlDistributionPoint<'a> {
285    /// distributionPoint describes the location of CRL information.
286    distribution_point: Option<untrusted::Input<'a>>,
287
288    /// reasons holds a bit flag set of certificate revocation reasons associated with the
289    /// CRL distribution point.
290    pub(crate) reasons: Option<der::BitStringFlags<'a>>,
291
292    /// when the CRL issuer is not the certificate issuer, crl_issuer identifies the issuer of the
293    /// CRL.
294    pub(crate) crl_issuer: Option<untrusted::Input<'a>>,
295}
296
297impl<'a> CrlDistributionPoint<'a> {
298    /// Return the distribution point names (if any).
299    pub(crate) fn names(&self) -> Result<Option<DistributionPointName<'a>>, Error> {
300        self.distribution_point
301            .map(|input| DistributionPointName::from_der(&mut untrusted::Reader::new(input)))
302            .transpose()
303    }
304}
305
306impl<'a> FromDer<'a> for CrlDistributionPoint<'a> {
307    fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
308        // RFC 5280 section §4.2.1.13:
309        //   A DistributionPoint consists of three fields, each of which is optional:
310        //   distributionPoint, reasons, and cRLIssuer.
311        let mut result = CrlDistributionPoint {
312            distribution_point: None,
313            reasons: None,
314            crl_issuer: None,
315        };
316
317        der::nested(
318            reader,
319            Tag::Sequence,
320            Error::TrailingData(Self::TYPE_ID),
321            |der| {
322                const DISTRIBUTION_POINT_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED;
323                const REASONS_TAG: u8 = CONTEXT_SPECIFIC | 1;
324                const CRL_ISSUER_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED | 2;
325
326                while !der.at_end() {
327                    let (tag, value) = der::read_tag_and_get_value(der)?;
328                    match tag {
329                        DISTRIBUTION_POINT_TAG => {
330                            set_extension_once(&mut result.distribution_point, || Ok(value))?
331                        }
332                        REASONS_TAG => set_extension_once(&mut result.reasons, || {
333                            der::bit_string_flags(value)
334                        })?,
335                        CRL_ISSUER_TAG => set_extension_once(&mut result.crl_issuer, || Ok(value))?,
336                        _ => return Err(Error::BadDer),
337                    }
338                }
339
340                // RFC 5280 section §4.2.1.13:
341                //   a DistributionPoint MUST NOT consist of only the reasons field; either distributionPoint or
342                //   cRLIssuer MUST be present.
343                match (result.distribution_point, result.crl_issuer) {
344                    (None, None) => Err(Error::MalformedExtensions),
345                    _ => Ok(result),
346                }
347            },
348        )
349    }
350
351    const TYPE_ID: DerTypeId = DerTypeId::CrlDistributionPoint;
352}
353
354#[cfg(test)]
355mod tests {
356    use super::*;
357    #[cfg(feature = "alloc")]
358    use crate::crl::RevocationReason;
359    use std::prelude::v1::*;
360
361    #[test]
362    // Note: cert::parse_cert is crate-local visibility, and EndEntityCert doesn't expose the
363    //       inner Cert, or the serial number. As a result we test that the raw serial value
364    //       is read correctly here instead of in tests/integration.rs.
365    fn test_serial_read() {
366        let ee = include_bytes!("../tests/misc/serial_neg_ee.der");
367        let cert = Cert::from_der(untrusted::Input::from(ee)).expect("failed to parse certificate");
368        assert_eq!(cert.serial.as_slice_less_safe(), &[255, 33, 82, 65, 17]);
369
370        let ee = include_bytes!("../tests/misc/serial_large_positive.der");
371        let cert = Cert::from_der(untrusted::Input::from(ee)).expect("failed to parse certificate");
372        assert_eq!(
373            cert.serial.as_slice_less_safe(),
374            &[
375                0, 230, 9, 254, 122, 234, 0, 104, 140, 224, 36, 180, 237, 32, 27, 31, 239, 82, 180,
376                68, 209
377            ]
378        )
379    }
380
381    #[cfg(feature = "alloc")]
382    #[test]
383    fn test_spki_read() {
384        let ee = include_bytes!("../tests/ed25519/ee.der");
385        let cert = Cert::from_der(untrusted::Input::from(ee)).expect("failed to parse certificate");
386        // How did I get this lovely string of hex bytes?
387        // openssl x509 -in tests/ed25519/ee.der -pubkey -noout > pubkey.pem
388        // openssl ec -pubin -in pubkey.pem -outform DER -out pubkey.der
389        // xxd -plain -cols 1 pubkey.der
390        let expected_spki = [
391            0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00, 0xfe, 0x5a,
392            0x1e, 0x36, 0x6c, 0x17, 0x27, 0x5b, 0xf1, 0x58, 0x1e, 0x3a, 0x0e, 0xe6, 0x56, 0x29,
393            0x8d, 0x9e, 0x1b, 0x3f, 0xd3, 0x3f, 0x96, 0x46, 0xef, 0xbf, 0x04, 0x6b, 0xc7, 0x3d,
394            0x47, 0x5c,
395        ];
396        assert_eq!(expected_spki, *cert.subject_public_key_info())
397    }
398
399    #[test]
400    #[cfg(feature = "alloc")]
401    fn test_crl_distribution_point_netflix() {
402        let ee = include_bytes!("../tests/netflix/ee.der");
403        let inter = include_bytes!("../tests/netflix/inter.der");
404        let ee_cert = Cert::from_der(untrusted::Input::from(ee)).expect("failed to parse EE cert");
405        let cert =
406            Cert::from_der(untrusted::Input::from(inter)).expect("failed to parse certificate");
407
408        // The end entity certificate shouldn't have a distribution point.
409        assert!(ee_cert.crl_distribution_points.is_none());
410
411        // We expect to be able to parse the intermediate certificate's CRL distribution points.
412        let crl_distribution_points = cert
413            .crl_distribution_points()
414            .expect("missing distribution points extension")
415            .collect::<Result<Vec<_>, Error>>()
416            .expect("failed to parse distribution points");
417
418        // There should be one distribution point present.
419        assert_eq!(crl_distribution_points.len(), 1);
420        let crl_distribution_point = crl_distribution_points
421            .first()
422            .expect("missing distribution point");
423
424        // The distribution point shouldn't have revocation reasons listed.
425        assert!(crl_distribution_point.reasons.is_none());
426
427        // The distribution point shouldn't have a CRL issuer listed.
428        assert!(crl_distribution_point.crl_issuer.is_none());
429
430        // We should be able to parse the distribution point name.
431        let distribution_point_name = crl_distribution_point
432            .names()
433            .expect("failed to parse distribution point names")
434            .expect("missing distribution point name");
435
436        // We expect the distribution point name to be a sequence of GeneralNames, not a name
437        // relative to the CRL issuer.
438        let names = match distribution_point_name {
439            DistributionPointName::NameRelativeToCrlIssuer => {
440                panic!("unexpected name relative to crl issuer")
441            }
442            DistributionPointName::FullName(names) => names,
443        };
444
445        // The general names should parse.
446        let names = names
447            .collect::<Result<Vec<_>, Error>>()
448            .expect("failed to parse general names");
449
450        // There should be one general name.
451        assert_eq!(names.len(), 1);
452        let name = names.first().expect("missing general name");
453
454        // The general name should be a URI matching the expected value.
455        match name {
456            GeneralName::UniformResourceIdentifier(uri) => {
457                assert_eq!(
458                    uri.as_slice_less_safe(),
459                    "http://s.symcb.com/pca3-g3.crl".as_bytes()
460                );
461            }
462            _ => panic!("unexpected general name type"),
463        }
464    }
465
466    #[test]
467    #[cfg(feature = "alloc")]
468    fn test_crl_distribution_point_with_reasons() {
469        let der = include_bytes!("../tests/crl_distrib_point/with_reasons.der");
470        let cert =
471            Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
472
473        // We expect to be able to parse the intermediate certificate's CRL distribution points.
474        let crl_distribution_points = cert
475            .crl_distribution_points()
476            .expect("missing distribution points extension")
477            .collect::<Result<Vec<_>, Error>>()
478            .expect("failed to parse distribution points");
479
480        // There should be one distribution point present.
481        assert_eq!(crl_distribution_points.len(), 1);
482        let crl_distribution_point = crl_distribution_points
483            .first()
484            .expect("missing distribution point");
485
486        // The distribution point should include the expected revocation reasons, and no others.
487        let reasons = crl_distribution_point
488            .reasons
489            .as_ref()
490            .expect("missing revocation reasons");
491        let expected = &[
492            RevocationReason::KeyCompromise,
493            RevocationReason::AffiliationChanged,
494        ];
495        for reason in RevocationReason::iter() {
496            #[allow(clippy::as_conversions)]
497            // revocation reason is u8, infallible to convert to usize.
498            match expected.contains(&reason) {
499                true => assert!(reasons.bit_set(reason as usize)),
500                false => assert!(!reasons.bit_set(reason as usize)),
501            }
502        }
503    }
504
505    #[test]
506    #[cfg(feature = "alloc")]
507    fn test_crl_distribution_point_with_crl_issuer() {
508        let der = include_bytes!("../tests/crl_distrib_point/with_crl_issuer.der");
509        let cert =
510            Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
511
512        // We expect to be able to parse the intermediate certificate's CRL distribution points.
513        let crl_distribution_points = cert
514            .crl_distribution_points()
515            .expect("missing distribution points extension")
516            .collect::<Result<Vec<_>, Error>>()
517            .expect("failed to parse distribution points");
518
519        // There should be one distribution point present.
520        assert_eq!(crl_distribution_points.len(), 1);
521        let crl_distribution_point = crl_distribution_points
522            .first()
523            .expect("missing distribution point");
524
525        // The CRL issuer should be present, but not anything else.
526        assert!(crl_distribution_point.crl_issuer.is_some());
527        assert!(crl_distribution_point.distribution_point.is_none());
528        assert!(crl_distribution_point.reasons.is_none());
529    }
530
531    #[test]
532    #[cfg(feature = "alloc")]
533    fn test_crl_distribution_point_bad_der() {
534        // Created w/
535        //   ascii2der -i tests/crl_distrib_point/unknown_tag.der.txt -o tests/crl_distrib_point/unknown_tag.der
536        let der = include_bytes!("../tests/crl_distrib_point/unknown_tag.der");
537        let cert =
538            Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
539
540        // We expect there to be a distribution point extension, but parsing it should fail
541        // due to the unknown tag in the SEQUENCE.
542        let result = cert
543            .crl_distribution_points()
544            .expect("missing distribution points extension")
545            .collect::<Result<Vec<_>, Error>>();
546        assert!(matches!(result, Err(Error::BadDer)));
547    }
548
549    #[test]
550    #[cfg(feature = "alloc")]
551    fn test_crl_distribution_point_only_reasons() {
552        // Created w/
553        //   ascii2der -i tests/crl_distrib_point/only_reasons.der.txt -o tests/crl_distrib_point/only_reasons.der
554        let der = include_bytes!("../tests/crl_distrib_point/only_reasons.der");
555        let cert =
556            Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
557
558        // We expect there to be a distribution point extension, but parsing it should fail
559        // because no distribution points or cRLIssuer are set in the SEQUENCE, just reason codes.
560        let result = cert
561            .crl_distribution_points()
562            .expect("missing distribution points extension")
563            .collect::<Result<Vec<_>, Error>>();
564        assert!(matches!(result, Err(Error::MalformedExtensions)));
565    }
566
567    #[test]
568    #[cfg(feature = "alloc")]
569    fn test_crl_distribution_point_name_relative_to_issuer() {
570        let der = include_bytes!("../tests/crl_distrib_point/dp_name_relative_to_issuer.der");
571        let cert =
572            Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
573
574        // We expect to be able to parse the intermediate certificate's CRL distribution points.
575        let crl_distribution_points = cert
576            .crl_distribution_points()
577            .expect("missing distribution points extension")
578            .collect::<Result<Vec<_>, Error>>()
579            .expect("failed to parse distribution points");
580
581        // There should be one distribution point present.
582        assert_eq!(crl_distribution_points.len(), 1);
583        let crl_distribution_point = crl_distribution_points
584            .first()
585            .expect("missing distribution point");
586
587        assert!(crl_distribution_point.crl_issuer.is_none());
588        assert!(crl_distribution_point.reasons.is_none());
589
590        // We should be able to parse the distribution point name.
591        let distribution_point_name = crl_distribution_point
592            .names()
593            .expect("failed to parse distribution point names")
594            .expect("missing distribution point name");
595
596        // We expect the distribution point name to be a name relative to the CRL issuer.
597        assert!(matches!(
598            distribution_point_name,
599            DistributionPointName::NameRelativeToCrlIssuer
600        ));
601    }
602
603    #[test]
604    #[cfg(feature = "alloc")]
605    fn test_crl_distribution_point_unknown_name_tag() {
606        // Created w/
607        //   ascii2der -i tests/crl_distrib_point/unknown_dp_name_tag.der.txt > tests/crl_distrib_point/unknown_dp_name_tag.der
608        let der = include_bytes!("../tests/crl_distrib_point/unknown_dp_name_tag.der");
609        let cert =
610            Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
611
612        // We expect to be able to parse the intermediate certificate's CRL distribution points.
613        let crl_distribution_points = cert
614            .crl_distribution_points()
615            .expect("missing distribution points extension")
616            .collect::<Result<Vec<_>, Error>>()
617            .expect("failed to parse distribution points");
618
619        // There should be one distribution point present.
620        assert_eq!(crl_distribution_points.len(), 1);
621        let crl_distribution_point = crl_distribution_points
622            .first()
623            .expect("missing distribution point");
624
625        // Parsing the distrubition point names should fail due to the unknown name tag.
626        let result = crl_distribution_point.names();
627        assert!(matches!(result, Err(Error::BadDer)))
628    }
629
630    #[test]
631    #[cfg(feature = "alloc")]
632    fn test_crl_distribution_point_multiple() {
633        let der = include_bytes!("../tests/crl_distrib_point/multiple_distribution_points.der");
634        let cert =
635            Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
636
637        // We expect to be able to parse the intermediate certificate's CRL distribution points.
638        let crl_distribution_points = cert
639            .crl_distribution_points()
640            .expect("missing distribution points extension")
641            .collect::<Result<Vec<_>, Error>>()
642            .expect("failed to parse distribution points");
643
644        // There should be two distribution points present.
645        let (point_a, point_b) = (
646            crl_distribution_points
647                .first()
648                .expect("missing first distribution point"),
649            crl_distribution_points
650                .get(1)
651                .expect("missing second distribution point"),
652        );
653
654        fn get_names<'a>(
655            point: &'a CrlDistributionPoint<'a>,
656        ) -> impl Iterator<Item = Result<GeneralName<'a>, Error>> {
657            match point
658                .names()
659                .expect("failed to parse distribution point names")
660                .expect("missing distribution point name")
661            {
662                DistributionPointName::NameRelativeToCrlIssuer => {
663                    panic!("unexpected relative name")
664                }
665                DistributionPointName::FullName(names) => names,
666            }
667        }
668
669        fn uri_bytes<'a>(name: &'a GeneralName<'a>) -> &'a [u8] {
670            match name {
671                GeneralName::UniformResourceIdentifier(uri) => uri.as_slice_less_safe(),
672                _ => panic!("unexpected name type"),
673            }
674        }
675
676        // We expect to find three URIs across the two distribution points.
677        let expected_names = [
678            "http://example.com/crl.1.der".as_bytes(),
679            "http://example.com/crl.2.der".as_bytes(),
680            "http://example.com/crl.3.der".as_bytes(),
681        ];
682        let all_names = get_names(point_a)
683            .chain(get_names(point_b))
684            .collect::<Result<Vec<_>, Error>>()
685            .expect("failed to parse names");
686
687        assert_eq!(
688            all_names.iter().map(uri_bytes).collect::<Vec<_>>(),
689            expected_names
690        );
691    }
692}