Coverage Report

Created: 2026-03-31 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rustls-webpki-0.103.10/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 = &'a 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 = &'a 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
    #[cfg(feature = "alloc")]
396
    use alloc::vec::Vec;
397
398
    use super::*;
399
    #[cfg(feature = "alloc")]
400
    use crate::crl::RevocationReason;
401
402
    #[test]
403
    // Note: cert::parse_cert is crate-local visibility, and EndEntityCert doesn't expose the
404
    //       inner Cert, or the serial number. As a result we test that the raw serial value
405
    //       is read correctly here instead of in tests/integration.rs.
406
    fn test_serial_read() {
407
        let ee = include_bytes!("../tests/misc/serial_neg_ee.der");
408
        let cert = Cert::from_der(untrusted::Input::from(ee)).expect("failed to parse certificate");
409
        assert_eq!(cert.serial.as_slice_less_safe(), &[255, 33, 82, 65, 17]);
410
411
        let ee = include_bytes!("../tests/misc/serial_large_positive.der");
412
        let cert = Cert::from_der(untrusted::Input::from(ee)).expect("failed to parse certificate");
413
        assert_eq!(
414
            cert.serial.as_slice_less_safe(),
415
            &[
416
                0, 230, 9, 254, 122, 234, 0, 104, 140, 224, 36, 180, 237, 32, 27, 31, 239, 82, 180,
417
                68, 209
418
            ]
419
        )
420
    }
421
422
    #[cfg(feature = "alloc")]
423
    #[test]
424
    fn test_spki_read() {
425
        let ee = include_bytes!("../tests/ed25519/ee.der");
426
        let cert = Cert::from_der(untrusted::Input::from(ee)).expect("failed to parse certificate");
427
        // How did I get this lovely string of hex bytes?
428
        // openssl x509 -in tests/ed25519/ee.der -pubkey -noout > pubkey.pem
429
        // openssl ec -pubin -in pubkey.pem -outform DER -out pubkey.der
430
        // xxd -plain -cols 1 pubkey.der
431
        let expected_spki = [
432
            0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00, 0xfe, 0x5a,
433
            0x1e, 0x36, 0x6c, 0x17, 0x27, 0x5b, 0xf1, 0x58, 0x1e, 0x3a, 0x0e, 0xe6, 0x56, 0x29,
434
            0x8d, 0x9e, 0x1b, 0x3f, 0xd3, 0x3f, 0x96, 0x46, 0xef, 0xbf, 0x04, 0x6b, 0xc7, 0x3d,
435
            0x47, 0x5c,
436
        ];
437
        assert_eq!(expected_spki, *cert.subject_public_key_info())
438
    }
439
440
    #[test]
441
    #[cfg(feature = "alloc")]
442
    fn test_crl_distribution_point_netflix() {
443
        let ee = include_bytes!("../tests/netflix/ee.der");
444
        let inter = include_bytes!("../tests/netflix/inter.der");
445
        let ee_cert = Cert::from_der(untrusted::Input::from(ee)).expect("failed to parse EE cert");
446
        let cert =
447
            Cert::from_der(untrusted::Input::from(inter)).expect("failed to parse certificate");
448
449
        // The end entity certificate shouldn't have a distribution point.
450
        assert!(ee_cert.crl_distribution_points.is_none());
451
452
        // We expect to be able to parse the intermediate certificate's CRL distribution points.
453
        let crl_distribution_points = cert
454
            .crl_distribution_points()
455
            .expect("missing distribution points extension")
456
            .collect::<Result<Vec<_>, Error>>()
457
            .expect("failed to parse distribution points");
458
459
        // There should be one distribution point present.
460
        assert_eq!(crl_distribution_points.len(), 1);
461
        let crl_distribution_point = crl_distribution_points
462
            .first()
463
            .expect("missing distribution point");
464
465
        // The distribution point shouldn't have revocation reasons listed.
466
        assert!(crl_distribution_point.reasons.is_none());
467
468
        // The distribution point shouldn't have a CRL issuer listed.
469
        assert!(crl_distribution_point.crl_issuer.is_none());
470
471
        // We should be able to parse the distribution point name.
472
        let distribution_point_name = crl_distribution_point
473
            .names()
474
            .expect("failed to parse distribution point names")
475
            .expect("missing distribution point name");
476
477
        // We expect the distribution point name to be a sequence of GeneralNames, not a name
478
        // relative to the CRL issuer.
479
        let names = match distribution_point_name {
480
            DistributionPointName::NameRelativeToCrlIssuer => {
481
                panic!("unexpected name relative to crl issuer")
482
            }
483
            DistributionPointName::FullName(names) => names,
484
        };
485
486
        // The general names should parse.
487
        let names = names
488
            .collect::<Result<Vec<_>, Error>>()
489
            .expect("failed to parse general names");
490
491
        // There should be one general name.
492
        assert_eq!(names.len(), 1);
493
        let name = names.first().expect("missing general name");
494
495
        // The general name should be a URI matching the expected value.
496
        match name {
497
            GeneralName::UniformResourceIdentifier(uri) => {
498
                assert_eq!(
499
                    uri.as_slice_less_safe(),
500
                    "http://s.symcb.com/pca3-g3.crl".as_bytes()
501
                );
502
            }
503
            _ => panic!("unexpected general name type"),
504
        }
505
    }
506
507
    #[test]
508
    #[cfg(feature = "alloc")]
509
    fn test_crl_distribution_point_with_reasons() {
510
        let der = include_bytes!("../tests/crl_distrib_point/with_reasons.der");
511
        let cert =
512
            Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
513
514
        // We expect to be able to parse the intermediate certificate's CRL distribution points.
515
        let crl_distribution_points = cert
516
            .crl_distribution_points()
517
            .expect("missing distribution points extension")
518
            .collect::<Result<Vec<_>, Error>>()
519
            .expect("failed to parse distribution points");
520
521
        // There should be one distribution point present.
522
        assert_eq!(crl_distribution_points.len(), 1);
523
        let crl_distribution_point = crl_distribution_points
524
            .first()
525
            .expect("missing distribution point");
526
527
        // The distribution point should include the expected revocation reasons, and no others.
528
        let reasons = crl_distribution_point
529
            .reasons
530
            .as_ref()
531
            .expect("missing revocation reasons");
532
        let expected = &[
533
            RevocationReason::KeyCompromise,
534
            RevocationReason::AffiliationChanged,
535
        ];
536
        for reason in RevocationReason::iter() {
537
            #[allow(clippy::as_conversions)]
538
            // revocation reason is u8, infallible to convert to usize.
539
            match expected.contains(&reason) {
540
                true => assert!(reasons.bit_set(reason as usize)),
541
                false => assert!(!reasons.bit_set(reason as usize)),
542
            }
543
        }
544
    }
545
546
    #[test]
547
    #[cfg(feature = "alloc")]
548
    fn test_crl_distribution_point_with_crl_issuer() {
549
        let der = include_bytes!("../tests/crl_distrib_point/with_crl_issuer.der");
550
        let cert =
551
            Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
552
553
        // We expect to be able to parse the intermediate certificate's CRL distribution points.
554
        let crl_distribution_points = cert
555
            .crl_distribution_points()
556
            .expect("missing distribution points extension")
557
            .collect::<Result<Vec<_>, Error>>()
558
            .expect("failed to parse distribution points");
559
560
        // There should be one distribution point present.
561
        assert_eq!(crl_distribution_points.len(), 1);
562
        let crl_distribution_point = crl_distribution_points
563
            .first()
564
            .expect("missing distribution point");
565
566
        // The CRL issuer should be present, but not anything else.
567
        assert!(crl_distribution_point.crl_issuer.is_some());
568
        assert!(crl_distribution_point.distribution_point.is_none());
569
        assert!(crl_distribution_point.reasons.is_none());
570
    }
571
572
    #[test]
573
    #[cfg(feature = "alloc")]
574
    fn test_crl_distribution_point_bad_der() {
575
        // Created w/
576
        //   ascii2der -i tests/crl_distrib_point/unknown_tag.der.txt -o tests/crl_distrib_point/unknown_tag.der
577
        let der = include_bytes!("../tests/crl_distrib_point/unknown_tag.der");
578
        let cert =
579
            Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
580
581
        // We expect there to be a distribution point extension, but parsing it should fail
582
        // due to the unknown tag in the SEQUENCE.
583
        let result = cert
584
            .crl_distribution_points()
585
            .expect("missing distribution points extension")
586
            .collect::<Result<Vec<_>, Error>>();
587
        assert!(matches!(result, Err(Error::BadDer)));
588
    }
589
590
    #[test]
591
    #[cfg(feature = "alloc")]
592
    fn test_crl_distribution_point_only_reasons() {
593
        // Created w/
594
        //   ascii2der -i tests/crl_distrib_point/only_reasons.der.txt -o tests/crl_distrib_point/only_reasons.der
595
        let der = include_bytes!("../tests/crl_distrib_point/only_reasons.der");
596
        let cert =
597
            Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
598
599
        // We expect there to be a distribution point extension, but parsing it should fail
600
        // because no distribution points or cRLIssuer are set in the SEQUENCE, just reason codes.
601
        let result = cert
602
            .crl_distribution_points()
603
            .expect("missing distribution points extension")
604
            .collect::<Result<Vec<_>, Error>>();
605
        assert!(matches!(result, Err(Error::MalformedExtensions)));
606
    }
607
608
    #[test]
609
    #[cfg(feature = "alloc")]
610
    fn test_crl_distribution_point_name_relative_to_issuer() {
611
        let der = include_bytes!("../tests/crl_distrib_point/dp_name_relative_to_issuer.der");
612
        let cert =
613
            Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
614
615
        // We expect to be able to parse the intermediate certificate's CRL distribution points.
616
        let crl_distribution_points = cert
617
            .crl_distribution_points()
618
            .expect("missing distribution points extension")
619
            .collect::<Result<Vec<_>, Error>>()
620
            .expect("failed to parse distribution points");
621
622
        // There should be one distribution point present.
623
        assert_eq!(crl_distribution_points.len(), 1);
624
        let crl_distribution_point = crl_distribution_points
625
            .first()
626
            .expect("missing distribution point");
627
628
        assert!(crl_distribution_point.crl_issuer.is_none());
629
        assert!(crl_distribution_point.reasons.is_none());
630
631
        // We should be able to parse the distribution point name.
632
        let distribution_point_name = crl_distribution_point
633
            .names()
634
            .expect("failed to parse distribution point names")
635
            .expect("missing distribution point name");
636
637
        // We expect the distribution point name to be a name relative to the CRL issuer.
638
        assert!(matches!(
639
            distribution_point_name,
640
            DistributionPointName::NameRelativeToCrlIssuer
641
        ));
642
    }
643
644
    #[test]
645
    #[cfg(feature = "alloc")]
646
    fn test_crl_distribution_point_unknown_name_tag() {
647
        // Created w/
648
        //   ascii2der -i tests/crl_distrib_point/unknown_dp_name_tag.der.txt > tests/crl_distrib_point/unknown_dp_name_tag.der
649
        let der = include_bytes!("../tests/crl_distrib_point/unknown_dp_name_tag.der");
650
        let cert =
651
            Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
652
653
        // We expect to be able to parse the intermediate certificate's CRL distribution points.
654
        let crl_distribution_points = cert
655
            .crl_distribution_points()
656
            .expect("missing distribution points extension")
657
            .collect::<Result<Vec<_>, Error>>()
658
            .expect("failed to parse distribution points");
659
660
        // There should be one distribution point present.
661
        assert_eq!(crl_distribution_points.len(), 1);
662
        let crl_distribution_point = crl_distribution_points
663
            .first()
664
            .expect("missing distribution point");
665
666
        // Parsing the distrubition point names should fail due to the unknown name tag.
667
        let result = crl_distribution_point.names();
668
        assert!(matches!(result, Err(Error::BadDer)))
669
    }
670
671
    #[test]
672
    #[cfg(feature = "alloc")]
673
    fn test_crl_distribution_point_multiple() {
674
        let der = include_bytes!("../tests/crl_distrib_point/multiple_distribution_points.der");
675
        let cert =
676
            Cert::from_der(untrusted::Input::from(der)).expect("failed to parse certificate");
677
678
        // We expect to be able to parse the intermediate certificate's CRL distribution points.
679
        let crl_distribution_points = cert
680
            .crl_distribution_points()
681
            .expect("missing distribution points extension")
682
            .collect::<Result<Vec<_>, Error>>()
683
            .expect("failed to parse distribution points");
684
685
        // There should be two distribution points present.
686
        let (point_a, point_b) = (
687
            crl_distribution_points
688
                .first()
689
                .expect("missing first distribution point"),
690
            crl_distribution_points
691
                .get(1)
692
                .expect("missing second distribution point"),
693
        );
694
695
        fn get_names<'a>(
696
            point: &'a CrlDistributionPoint<'a>,
697
        ) -> impl Iterator<Item = Result<GeneralName<'a>, Error>> {
698
            match point
699
                .names()
700
                .expect("failed to parse distribution point names")
701
                .expect("missing distribution point name")
702
            {
703
                DistributionPointName::NameRelativeToCrlIssuer => {
704
                    panic!("unexpected relative name")
705
                }
706
                DistributionPointName::FullName(names) => names,
707
            }
708
        }
709
710
        fn uri_bytes<'a>(name: &'a GeneralName<'a>) -> &'a [u8] {
711
            match name {
712
                GeneralName::UniformResourceIdentifier(uri) => uri.as_slice_less_safe(),
713
                _ => panic!("unexpected name type"),
714
            }
715
        }
716
717
        // We expect to find three URIs across the two distribution points.
718
        let expected_names = [
719
            "http://example.com/crl.1.der".as_bytes(),
720
            "http://example.com/crl.2.der".as_bytes(),
721
            "http://example.com/crl.3.der".as_bytes(),
722
        ];
723
        let all_names = get_names(point_a)
724
            .chain(get_names(point_b))
725
            .collect::<Result<Vec<_>, Error>>()
726
            .expect("failed to parse names");
727
728
        assert_eq!(
729
            all_names.iter().map(uri_bytes).collect::<Vec<_>>(),
730
            expected_names
731
        );
732
    }
733
}