Coverage Report

Created: 2026-05-16 07:06

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