Coverage Report

Created: 2026-01-30 06:08

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