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