Coverage Report

Created: 2025-11-16 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rustls-webpki-0.102.8/src/cert.rs
Line
Count
Source
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")]
16
use pki_types::SubjectPublicKeyInfoDer;
17
use pki_types::{CertificateDer, DnsName};
18
19
use crate::der::{self, DerIterator, FromDer, Tag, CONSTRUCTED, CONTEXT_SPECIFIC};
20
use crate::error::{DerTypeId, Error};
21
use crate::public_values_eq;
22
use crate::signed_data::SignedData;
23
use crate::subject_name::{GeneralName, NameIterator, WildcardDnsNameRef};
24
use crate::x509::{remember_extension, set_extension_once, DistributionPointName, Extension};
25
26
/// A parsed X509 certificate.
27
pub 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
49
impl<'a> Cert<'a> {
50
0
    pub(crate) fn from_der(cert_der: untrusted::Input<'a>) -> Result<Self, Error> {
51
0
        let (tbs, signed_data) =
52
0
            cert_der.read_all(Error::TrailingData(DerTypeId::Certificate), |cert_der| {
53
0
                der::nested(
54
0
                    cert_der,
55
0
                    der::Tag::Sequence,
56
0
                    Error::TrailingData(DerTypeId::SignedData),
57
0
                    |der| {
58
                        // limited to SEQUENCEs of size 2^16 or less.
59
0
                        SignedData::from_der(der, der::TWO_BYTE_DER_SIZE)
60
0
                    },
61
                )
62
0
            })?;
63
64
0
        tbs.read_all(
65
0
            Error::TrailingData(DerTypeId::CertificateTbsCertificate),
66
0
            |tbs| {
67
0
                version3(tbs)?;
68
69
0
                let serial = lenient_certificate_serial_number(tbs)?;
70
71
0
                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
0
                if !public_values_eq(signature, signed_data.algorithm) {
76
0
                    return Err(Error::SignatureAlgorithmMismatch);
77
0
                }
78
79
0
                let issuer = der::expect_tag(tbs, der::Tag::Sequence)?;
80
0
                let validity = der::expect_tag(tbs, der::Tag::Sequence)?;
81
0
                let subject = der::expect_tag(tbs, der::Tag::Sequence)?;
82
0
                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
0
                let mut cert = Cert {
90
0
                    signed_data,
91
0
                    serial,
92
0
                    issuer,
93
0
                    validity,
94
0
                    subject,
95
0
                    spki,
96
0
97
0
                    basic_constraints: None,
98
0
                    key_usage: None,
99
0
                    eku: None,
100
0
                    name_constraints: None,
101
0
                    subject_alt_name: None,
102
0
                    crl_distribution_points: None,
103
0
104
0
                    der: CertificateDer::from(cert_der.as_slice_less_safe()),
105
0
                };
106
107
0
                if !tbs.at_end() {
108
0
                    der::nested(
109
0
                        tbs,
110
0
                        der::Tag::ContextSpecificConstructed3,
111
0
                        Error::TrailingData(DerTypeId::CertificateExtensions),
112
0
                        |tagged| {
113
0
                            der::nested_of_mut(
114
0
                                tagged,
115
0
                                der::Tag::Sequence,
116
0
                                der::Tag::Sequence,
117
0
                                Error::TrailingData(DerTypeId::Extension),
118
0
                                |extension| {
119
0
                                    remember_cert_extension(
120
0
                                        &mut cert,
121
0
                                        &Extension::from_der(extension)?,
122
                                    )
123
0
                                },
124
                            )
125
0
                        },
126
0
                    )?;
127
0
                }
128
129
0
                Ok(cert)
130
0
            },
131
        )
132
0
    }
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
0
    pub fn valid_dns_names(&self) -> impl Iterator<Item = &str> {
142
0
        NameIterator::new(Some(self.subject), self.subject_alt_name).filter_map(|result| {
143
0
            let presented_id = match result.ok()? {
144
0
                GeneralName::DnsName(presented) => presented,
145
0
                _ => return None,
146
            };
147
148
            // if the name could be converted to a DNS name, return it; otherwise,
149
            // keep going.
150
0
            let dns_str = core::str::from_utf8(presented_id.as_slice_less_safe()).ok()?;
151
0
            match DnsName::try_from(dns_str) {
152
0
                Ok(_) => Some(dns_str),
153
                Err(_) => {
154
0
                    match WildcardDnsNameRef::try_from_ascii(presented_id.as_slice_less_safe()) {
155
0
                        Ok(wildcard_dns_name) => Some(wildcard_dns_name.as_str()),
156
0
                        Err(_) => None,
157
                    }
158
                }
159
            }
160
0
        })
161
0
    }
162
163
    /// Raw DER encoded certificate serial number.
164
0
    pub fn serial(&self) -> &[u8] {
165
0
        self.serial.as_slice_less_safe()
166
0
    }
167
168
    /// Raw DER encoded certificate issuer.
169
0
    pub fn issuer(&self) -> &[u8] {
170
0
        self.issuer.as_slice_less_safe()
171
0
    }
172
173
    /// Raw DER encoded certificate subject.
174
0
    pub fn subject(&self) -> &[u8] {
175
0
        self.subject.as_slice_less_safe()
176
0
    }
177
178
    /// Get the RFC 5280-compliant [`SubjectPublicKeyInfoDer`] (SPKI) of this [`Cert`].
179
    #[cfg(feature = "alloc")]
180
0
    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
0
        SubjectPublicKeyInfoDer::from(der::asn1_wrap(
184
0
            Tag::Sequence,
185
0
            self.spki.as_slice_less_safe(),
186
        ))
187
0
    }
188
189
    /// Returns an iterator over the certificate's cRLDistributionPoints extension values, if any.
190
0
    pub(crate) fn crl_distribution_points(
191
0
        &self,
192
0
    ) -> Option<impl Iterator<Item = Result<CrlDistributionPoint<'a>, Error>>> {
193
0
        self.crl_distribution_points.map(DerIterator::new)
194
0
    }
195
196
    /// Raw DER encoded representation of the certificate.
197
0
    pub fn der(&self) -> CertificateDer<'a> {
198
0
        self.der.clone() // This is cheap, just cloning a reference.
199
0
    }
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.
204
0
fn version3(input: &mut untrusted::Reader<'_>) -> Result<(), Error> {
205
0
    der::nested(
206
0
        input,
207
0
        der::Tag::ContextSpecificConstructed0,
208
0
        Error::UnsupportedCertVersion,
209
0
        |input| {
210
0
            let version = u8::from_der(input)?;
211
0
            if version != 2 {
212
                // v3
213
0
                return Err(Error::UnsupportedCertVersion);
214
0
            }
215
0
            Ok(())
216
0
        },
217
    )
218
0
}
219
220
0
pub(crate) fn lenient_certificate_serial_number<'a>(
221
0
    input: &mut untrusted::Reader<'a>,
222
0
) -> 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
0
    der::expect_tag(input, Tag::Integer)
234
0
}
235
236
0
fn remember_cert_extension<'a>(
237
0
    cert: &mut Cert<'a>,
238
0
    extension: &Extension<'a>,
239
0
) -> 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
0
    remember_extension(extension, |id| {
245
0
        let out = match id {
246
            // id-ce-keyUsage 2.5.29.15.
247
0
            15 => &mut cert.key_usage,
248
249
            // id-ce-subjectAltName 2.5.29.17
250
0
            17 => &mut cert.subject_alt_name,
251
252
            // id-ce-basicConstraints 2.5.29.19
253
0
            19 => &mut cert.basic_constraints,
254
255
            // id-ce-nameConstraints 2.5.29.30
256
0
            30 => &mut cert.name_constraints,
257
258
            // id-ce-cRLDistributionPoints 2.5.29.31
259
0
            31 => &mut cert.crl_distribution_points,
260
261
            // id-ce-extKeyUsage 2.5.29.37
262
0
            37 => &mut cert.eku,
263
264
            // Unsupported extension
265
0
            _ => return extension.unsupported(),
266
        };
267
268
0
        set_extension_once(out, || {
269
0
            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
0
                15 => Ok(value.read_bytes_to_end()),
273
                // All other remembered certificate extensions are wrapped in a Sequence.
274
0
                _ => der::expect_tag(value, Tag::Sequence),
275
0
            })
276
0
        })
277
0
    })
278
0
}
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>
284
pub(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
297
impl<'a> CrlDistributionPoint<'a> {
298
    /// Return the distribution point names (if any).
299
0
    pub(crate) fn names(&self) -> Result<Option<DistributionPointName<'a>>, Error> {
300
0
        self.distribution_point
301
0
            .map(|input| DistributionPointName::from_der(&mut untrusted::Reader::new(input)))
302
0
            .transpose()
303
0
    }
304
}
305
306
impl<'a> FromDer<'a> for CrlDistributionPoint<'a> {
307
0
    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
0
        let mut result = CrlDistributionPoint {
312
0
            distribution_point: None,
313
0
            reasons: None,
314
0
            crl_issuer: None,
315
0
        };
316
317
0
        der::nested(
318
0
            reader,
319
0
            Tag::Sequence,
320
0
            Error::TrailingData(Self::TYPE_ID),
321
0
            |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
0
                while !der.at_end() {
327
0
                    let (tag, value) = der::read_tag_and_get_value(der)?;
328
0
                    match tag {
329
                        DISTRIBUTION_POINT_TAG => {
330
0
                            set_extension_once(&mut result.distribution_point, || Ok(value))?
331
                        }
332
0
                        REASONS_TAG => set_extension_once(&mut result.reasons, || {
333
0
                            der::bit_string_flags(value)
334
0
                        })?,
335
0
                        CRL_ISSUER_TAG => set_extension_once(&mut result.crl_issuer, || Ok(value))?,
336
0
                        _ => 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
0
                match (result.distribution_point, result.crl_issuer) {
344
0
                    (None, None) => Err(Error::MalformedExtensions),
345
0
                    _ => Ok(result),
346
                }
347
0
            },
348
        )
349
0
    }
350
351
    const TYPE_ID: DerTypeId = DerTypeId::CrlDistributionPoint;
352
}
353
354
#[cfg(test)]
355
mod 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
}