webpki/
verify_cert.rs

1// Copyright 2015 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
10// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15#[cfg(feature = "alloc")]
16use alloc::vec::Vec;
17use core::fmt;
18use core::ops::ControlFlow;
19
20use pki_types::{CertificateDer, SignatureVerificationAlgorithm, TrustAnchor, UnixTime};
21
22use crate::cert::Cert;
23use crate::crl::RevocationOptions;
24use crate::der::{self, FromDer};
25use crate::end_entity::EndEntityCert;
26use crate::error::Error;
27use crate::{public_values_eq, signed_data, subject_name};
28
29// Use `'a` for lifetimes that we don't care about, `'p` for lifetimes that become a part of
30// the `VerifiedPath`.
31pub(crate) struct ChainOptions<'a, 'p> {
32    pub(crate) eku: KeyUsage,
33    pub(crate) supported_sig_algs: &'a [&'a dyn SignatureVerificationAlgorithm],
34    pub(crate) trust_anchors: &'p [TrustAnchor<'p>],
35    pub(crate) intermediate_certs: &'p [CertificateDer<'p>],
36    pub(crate) revocation: Option<RevocationOptions<'a>>,
37}
38
39impl<'a, 'p: 'a> ChainOptions<'a, 'p> {
40    pub(crate) fn build_chain(
41        &self,
42        end_entity: &'p EndEntityCert<'p>,
43        time: UnixTime,
44        verify_path: Option<&dyn Fn(&VerifiedPath<'_>) -> Result<(), Error>>,
45    ) -> Result<VerifiedPath<'p>, Error> {
46        let mut path = PartialPath::new(end_entity);
47        match self.build_chain_inner(&mut path, time, verify_path, 0, &mut Budget::default()) {
48            Ok(anchor) => Ok(VerifiedPath::new(end_entity, anchor, path)),
49            Err(ControlFlow::Break(err)) | Err(ControlFlow::Continue(err)) => Err(err),
50        }
51    }
52
53    fn build_chain_inner(
54        &self,
55        path: &mut PartialPath<'p>,
56        time: UnixTime,
57        verify_path: Option<&dyn Fn(&VerifiedPath<'_>) -> Result<(), Error>>,
58        sub_ca_count: usize,
59        budget: &mut Budget,
60    ) -> Result<&'p TrustAnchor<'p>, ControlFlow<Error, Error>> {
61        let role = path.node().role();
62
63        check_issuer_independent_properties(path.head(), time, role, sub_ca_count, self.eku.inner)?;
64
65        // TODO: HPKP checks.
66
67        let result =
68            loop_while_non_fatal_error(Error::UnknownIssuer, self.trust_anchors, |trust_anchor| {
69                let trust_anchor_subject = untrusted::Input::from(trust_anchor.subject.as_ref());
70                if !public_values_eq(path.head().issuer, trust_anchor_subject) {
71                    return Err(Error::UnknownIssuer.into());
72                }
73
74                // TODO: check_distrust(trust_anchor_subject, trust_anchor_spki)?;
75
76                let node = path.node();
77                self.check_signed_chain(&node, time, trust_anchor, budget)?;
78                check_signed_chain_name_constraints(&node, trust_anchor, budget)?;
79
80                let verify = match verify_path {
81                    Some(verify) => verify,
82                    None => return Ok(trust_anchor),
83                };
84
85                let candidate = VerifiedPath {
86                    end_entity: path.end_entity,
87                    intermediates: Intermediates::Borrowed(&path.intermediates[..path.used]),
88                    anchor: trust_anchor,
89                };
90
91                match verify(&candidate) {
92                    Ok(()) => Ok(trust_anchor),
93                    Err(err) => Err(ControlFlow::Continue(err)),
94                }
95            });
96
97        let err = match result {
98            Ok(anchor) => return Ok(anchor),
99            // Fatal errors should halt further path building.
100            res @ Err(ControlFlow::Break(_)) => return res,
101            // Non-fatal errors should be carried forward as the default_error for subsequent
102            // loop_while_non_fatal_error processing and only returned once all other path-building
103            // options have been exhausted.
104            Err(ControlFlow::Continue(err)) => err,
105        };
106
107        loop_while_non_fatal_error(err, self.intermediate_certs, |cert_der| {
108            let potential_issuer = Cert::from_der(untrusted::Input::from(cert_der))?;
109            if !public_values_eq(potential_issuer.subject, path.head().issuer) {
110                return Err(Error::UnknownIssuer.into());
111            }
112
113            // Prevent loops; see RFC 4158 section 5.2.
114            if path.node().iter().any(|prev| {
115                public_values_eq(potential_issuer.spki, prev.cert.spki)
116                    && public_values_eq(potential_issuer.subject, prev.cert.subject)
117            }) {
118                return Err(Error::UnknownIssuer.into());
119            }
120
121            let next_sub_ca_count = match role {
122                Role::EndEntity => sub_ca_count,
123                Role::Issuer => sub_ca_count + 1,
124            };
125
126            budget.consume_build_chain_call()?;
127            path.push(potential_issuer)?;
128            let result = self.build_chain_inner(path, time, verify_path, next_sub_ca_count, budget);
129            if result.is_err() {
130                path.pop();
131            }
132
133            result
134        })
135    }
136
137    fn check_signed_chain(
138        &self,
139        path: &PathNode<'_>,
140        time: UnixTime,
141        trust_anchor: &TrustAnchor<'_>,
142        budget: &mut Budget,
143    ) -> Result<(), ControlFlow<Error, Error>> {
144        let mut spki_value = untrusted::Input::from(trust_anchor.subject_public_key_info.as_ref());
145        let mut issuer_subject = untrusted::Input::from(trust_anchor.subject.as_ref());
146        let mut issuer_key_usage = None; // TODO(XXX): Consider whether to track TrustAnchor KU.
147        for path in path.iter() {
148            signed_data::verify_signed_data(
149                self.supported_sig_algs,
150                spki_value,
151                &path.cert.signed_data,
152                budget,
153            )?;
154
155            if let Some(revocation_opts) = &self.revocation {
156                revocation_opts.check(
157                    &path,
158                    issuer_subject,
159                    spki_value,
160                    issuer_key_usage,
161                    self.supported_sig_algs,
162                    budget,
163                    time,
164                )?;
165            }
166
167            spki_value = path.cert.spki;
168            issuer_subject = path.cert.subject;
169            issuer_key_usage = path.cert.key_usage;
170        }
171
172        Ok(())
173    }
174}
175
176/// Path from end-entity certificate to trust anchor that's been verified.
177///
178/// See [`EndEntityCert::verify_for_usage()`] for more details on what verification entails.
179pub struct VerifiedPath<'p> {
180    end_entity: &'p EndEntityCert<'p>,
181    intermediates: Intermediates<'p>,
182    anchor: &'p TrustAnchor<'p>,
183}
184
185impl<'p> VerifiedPath<'p> {
186    fn new(
187        end_entity: &'p EndEntityCert<'p>,
188        anchor: &'p TrustAnchor<'p>,
189        partial: PartialPath<'p>,
190    ) -> Self {
191        Self {
192            end_entity,
193            intermediates: Intermediates::Owned {
194                certs: partial.intermediates,
195                used: partial.used,
196            },
197            anchor,
198        }
199    }
200
201    /// Yields a (double-ended) iterator over the intermediate certificates in this path.
202    pub fn intermediate_certificates(&'p self) -> IntermediateIterator<'p> {
203        IntermediateIterator {
204            intermediates: self.intermediates.as_ref(),
205        }
206    }
207
208    /// Yields the end-entity certificate for this path.
209    pub fn end_entity(&self) -> &'p EndEntityCert<'p> {
210        self.end_entity
211    }
212
213    /// Yields the trust anchor for this path.
214    pub fn anchor(&self) -> &'p TrustAnchor<'p> {
215        self.anchor
216    }
217}
218
219/// Iterator over a path's intermediate certificates.
220///
221/// Implements [`DoubleEndedIterator`] so it can be traversed in both directions.
222pub struct IntermediateIterator<'a> {
223    /// Invariant: all of these `Option`s are `Some`.
224    intermediates: &'a [Option<Cert<'a>>],
225}
226
227impl<'a> Iterator for IntermediateIterator<'a> {
228    type Item = &'a Cert<'a>;
229
230    fn next(&mut self) -> Option<Self::Item> {
231        match self.intermediates.split_first() {
232            Some((head, tail)) => {
233                self.intermediates = tail;
234                Some(head.as_ref().unwrap())
235            }
236            None => None,
237        }
238    }
239}
240
241impl DoubleEndedIterator for IntermediateIterator<'_> {
242    fn next_back(&mut self) -> Option<Self::Item> {
243        match self.intermediates.split_last() {
244            Some((head, tail)) => {
245                self.intermediates = tail;
246                Some(head.as_ref().unwrap())
247            }
248            None => None,
249        }
250    }
251}
252
253#[allow(clippy::large_enum_variant)]
254enum Intermediates<'a> {
255    Owned {
256        certs: [Option<Cert<'a>>; MAX_SUB_CA_COUNT],
257        used: usize,
258    },
259    Borrowed(&'a [Option<Cert<'a>>]),
260}
261
262impl<'a> AsRef<[Option<Cert<'a>>]> for Intermediates<'a> {
263    fn as_ref(&self) -> &[Option<Cert<'a>>] {
264        match self {
265            Intermediates::Owned { certs, used } => &certs[..*used],
266            Intermediates::Borrowed(certs) => certs,
267        }
268    }
269}
270
271fn check_signed_chain_name_constraints(
272    path: &PathNode<'_>,
273    trust_anchor: &TrustAnchor<'_>,
274    budget: &mut Budget,
275) -> Result<(), ControlFlow<Error, Error>> {
276    let mut name_constraints = trust_anchor
277        .name_constraints
278        .as_ref()
279        .map(|der| untrusted::Input::from(der.as_ref()));
280
281    for path in path.iter() {
282        untrusted::read_all_optional(name_constraints, Error::BadDer, |value| {
283            subject_name::check_name_constraints(value, &path, budget)
284        })?;
285
286        name_constraints = path.cert.name_constraints;
287    }
288
289    Ok(())
290}
291
292pub(crate) struct Budget {
293    signatures: usize,
294    build_chain_calls: usize,
295    name_constraint_comparisons: usize,
296}
297
298impl Budget {
299    #[inline]
300    pub(crate) fn consume_signature(&mut self) -> Result<(), Error> {
301        self.signatures = self
302            .signatures
303            .checked_sub(1)
304            .ok_or(Error::MaximumSignatureChecksExceeded)?;
305        Ok(())
306    }
307
308    #[inline]
309    fn consume_build_chain_call(&mut self) -> Result<(), Error> {
310        self.build_chain_calls = self
311            .build_chain_calls
312            .checked_sub(1)
313            .ok_or(Error::MaximumPathBuildCallsExceeded)?;
314        Ok(())
315    }
316
317    #[inline]
318    pub(crate) fn consume_name_constraint_comparison(&mut self) -> Result<(), Error> {
319        self.name_constraint_comparisons = self
320            .name_constraint_comparisons
321            .checked_sub(1)
322            .ok_or(Error::MaximumNameConstraintComparisonsExceeded)?;
323        Ok(())
324    }
325}
326
327impl Default for Budget {
328    fn default() -> Self {
329        Self {
330            // This limit is taken from the remediation for golang CVE-2018-16875.  However,
331            // note that golang subsequently implemented AKID matching due to this limit
332            // being hit in real applications (see <https://github.com/spiffe/spire/issues/1004>).
333            // So this may actually be too aggressive.
334            signatures: 100,
335
336            // This limit is taken from mozilla::pkix, see:
337            // <https://github.com/nss-dev/nss/blob/bb4a1d38dd9e92923525ac6b5ed0288479f3f3fc/lib/mozpkix/lib/pkixbuild.cpp#L381-L393>
338            build_chain_calls: 200_000,
339
340            // This limit is taken from golang crypto/x509's default, see:
341            // <https://github.com/golang/go/blob/ac17bb6f13979f2ab9fcd45f0758b43ed72d0973/src/crypto/x509/verify.go#L588-L592>
342            name_constraint_comparisons: 250_000,
343        }
344    }
345}
346
347fn check_issuer_independent_properties(
348    cert: &Cert<'_>,
349    time: UnixTime,
350    role: Role,
351    sub_ca_count: usize,
352    eku: ExtendedKeyUsage,
353) -> Result<(), Error> {
354    // TODO: check_distrust(trust_anchor_subject, trust_anchor_spki)?;
355    // TODO: Check signature algorithm like mozilla::pkix.
356    // TODO: Check SPKI like mozilla::pkix.
357    // TODO: check for active distrust like mozilla::pkix.
358
359    // For cert validation, we ignore the KeyUsage extension. For CA
360    // certificates, BasicConstraints.cA makes KeyUsage redundant. Firefox
361    // and other common browsers do not check KeyUsage for end-entities,
362    // though it would be kind of nice to ensure that a KeyUsage without
363    // the keyEncipherment bit could not be used for RSA key exchange.
364
365    cert.validity
366        .read_all(Error::BadDer, |value| check_validity(value, time))?;
367    untrusted::read_all_optional(cert.basic_constraints, Error::BadDer, |value| {
368        check_basic_constraints(value, role, sub_ca_count)
369    })?;
370    untrusted::read_all_optional(cert.eku, Error::BadDer, |value| eku.check(value))?;
371
372    Ok(())
373}
374
375// https://tools.ietf.org/html/rfc5280#section-4.1.2.5
376fn check_validity(input: &mut untrusted::Reader<'_>, time: UnixTime) -> Result<(), Error> {
377    let not_before = UnixTime::from_der(input)?;
378    let not_after = UnixTime::from_der(input)?;
379
380    if not_before > not_after {
381        return Err(Error::InvalidCertValidity);
382    }
383    if time < not_before {
384        return Err(Error::CertNotValidYet { time, not_before });
385    }
386    if time > not_after {
387        return Err(Error::CertExpired { time, not_after });
388    }
389
390    // TODO: mozilla::pkix allows the TrustDomain to check not_before and
391    // not_after, to enforce things like a maximum validity period. We should
392    // do something similar.
393
394    Ok(())
395}
396
397// https://tools.ietf.org/html/rfc5280#section-4.2.1.9
398fn check_basic_constraints(
399    input: Option<&mut untrusted::Reader<'_>>,
400    role: Role,
401    sub_ca_count: usize,
402) -> Result<(), Error> {
403    let (is_ca, path_len_constraint) = match input {
404        Some(input) => {
405            let is_ca = bool::from_der(input)?;
406
407            // https://bugzilla.mozilla.org/show_bug.cgi?id=985025: RFC 5280
408            // says that a certificate must not have pathLenConstraint unless
409            // it is a CA certificate, but some real-world end-entity
410            // certificates have pathLenConstraint.
411            let path_len_constraint = if !input.at_end() {
412                Some(usize::from(u8::from_der(input)?))
413            } else {
414                None
415            };
416
417            (is_ca, path_len_constraint)
418        }
419        None => (false, None),
420    };
421
422    match (role, is_ca, path_len_constraint) {
423        (Role::EndEntity, true, _) => Err(Error::CaUsedAsEndEntity),
424        (Role::Issuer, false, _) => Err(Error::EndEntityUsedAsCa),
425        (Role::Issuer, true, Some(len)) if sub_ca_count > len => {
426            Err(Error::PathLenConstraintViolated)
427        }
428        _ => Ok(()),
429    }
430}
431
432/// Additional context for the `RequiredEkuNotFoundContext` error variant.
433///
434/// The contents of this type depend on whether the `alloc` feature is enabled.
435#[derive(Clone, PartialEq, Eq)]
436pub struct RequiredEkuNotFoundContext {
437    /// The required ExtendedKeyUsage.
438    #[cfg(feature = "alloc")]
439    pub required: KeyUsage,
440    /// The ExtendedKeyUsage OIDs present in the certificate.
441    #[cfg(feature = "alloc")]
442    pub present: Vec<Vec<usize>>,
443}
444
445impl fmt::Debug for RequiredEkuNotFoundContext {
446    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447        let mut builder = f.debug_struct("RequiredEkuNotFoundContext");
448        #[cfg(feature = "alloc")]
449        builder.field(
450            "required",
451            match &self.required.inner {
452                ExtendedKeyUsage::Required(inner) => inner,
453                ExtendedKeyUsage::RequiredIfPresent(inner) => inner,
454            },
455        );
456        #[cfg(feature = "alloc")]
457        builder.field("present", &EkuListDebug(&self.present));
458        builder.finish()
459    }
460}
461
462#[cfg(feature = "alloc")]
463struct EkuListDebug<'a>(&'a [Vec<usize>]);
464
465#[cfg(feature = "alloc")]
466impl fmt::Debug for EkuListDebug<'_> {
467    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468        write!(f, "[")?;
469        for (i, part) in self.0.iter().enumerate() {
470            if i > 0 {
471                write!(f, ", ")?;
472            }
473
474            write!(f, "KeyPurposeId(")?;
475            for (j, part) in part.iter().enumerate() {
476                if j > 0 {
477                    write!(f, ".")?;
478                }
479                write!(f, "{part}")?;
480            }
481            write!(f, ")")?;
482        }
483        write!(f, "]")
484    }
485}
486
487/// The expected key usage of a certificate.
488///
489/// This type represents the expected key usage of an end entity certificate. Although for most
490/// kinds of certificates the extended key usage extension is optional (and so certificates
491/// not carrying a particular value in the EKU extension are acceptable). If the extension
492/// is present, the certificate MUST only be used for one of the purposes indicated.
493///
494/// <https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.12>
495#[derive(Clone, Copy, Debug, Eq, PartialEq)]
496pub struct KeyUsage {
497    inner: ExtendedKeyUsage,
498}
499
500impl KeyUsage {
501    /// Construct a new [`KeyUsage`] as appropriate for server certificate authentication.
502    ///
503    /// As specified in <https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.12>, this does not require the certificate to specify the eKU extension.
504    pub const fn server_auth() -> Self {
505        Self::required_if_present(EKU_SERVER_AUTH)
506    }
507
508    /// Construct a new [`KeyUsage`] as appropriate for client certificate authentication.
509    ///
510    /// As specified in <>, this does not require the certificate to specify the eKU extension.
511    pub const fn client_auth() -> Self {
512        Self::required_if_present(EKU_CLIENT_AUTH)
513    }
514
515    /// Construct a new [`KeyUsage`] requiring a certificate to support the specified OID.
516    pub const fn required(oid: &'static [u8]) -> Self {
517        Self {
518            inner: ExtendedKeyUsage::Required(KeyPurposeId::new(oid)),
519        }
520    }
521
522    /// Construct a new [`KeyUsage`] requiring a certificate to support the specified OID, if the certificate has EKUs.
523    pub const fn required_if_present(oid: &'static [u8]) -> Self {
524        Self {
525            inner: ExtendedKeyUsage::RequiredIfPresent(KeyPurposeId::new(oid)),
526        }
527    }
528
529    /// Yield the OID values of the required extended key usage.
530    pub fn oid_values(&self) -> impl Iterator<Item = usize> + '_ {
531        OidDecoder::new(
532            match &self.inner {
533                ExtendedKeyUsage::Required(eku) => eku,
534                ExtendedKeyUsage::RequiredIfPresent(eku) => eku,
535            }
536            .oid_value
537            .as_slice_less_safe(),
538        )
539    }
540
541    /// Human-readable representation of the server authentication OID.
542    pub const SERVER_AUTH_REPR: &[usize] = &[1, 3, 6, 1, 5, 5, 7, 3, 1];
543    /// Human-readable representation of the client authentication OID.
544    pub const CLIENT_AUTH_REPR: &[usize] = &[1, 3, 6, 1, 5, 5, 7, 3, 2];
545}
546
547/// Extended Key Usage (EKU) of a certificate.
548#[derive(Clone, Copy, Debug, Eq, PartialEq)]
549enum ExtendedKeyUsage {
550    /// The certificate must contain the specified [`KeyPurposeId`] as EKU.
551    Required(KeyPurposeId<'static>),
552
553    /// If the certificate has EKUs, then the specified [`KeyPurposeId`] must be included.
554    RequiredIfPresent(KeyPurposeId<'static>),
555}
556
557impl ExtendedKeyUsage {
558    // https://tools.ietf.org/html/rfc5280#section-4.2.1.12
559    fn check(&self, input: Option<&mut untrusted::Reader<'_>>) -> Result<(), Error> {
560        let input = match (input, self) {
561            (Some(input), _) => input,
562            (None, Self::RequiredIfPresent(_)) => return Ok(()),
563            (None, Self::Required(_)) => {
564                return Err(Error::RequiredEkuNotFoundContext(
565                    RequiredEkuNotFoundContext {
566                        #[cfg(feature = "alloc")]
567                        required: KeyUsage { inner: *self },
568                        #[cfg(feature = "alloc")]
569                        present: Vec::new(),
570                    },
571                ));
572            }
573        };
574
575        #[cfg(feature = "alloc")]
576        let mut present = Vec::new();
577        loop {
578            let value = der::expect_tag(input, der::Tag::OID)?;
579            if self.key_purpose_id_equals(value) {
580                input.skip_to_end();
581                break;
582            }
583
584            #[cfg(feature = "alloc")]
585            present.push(OidDecoder::new(value.as_slice_less_safe()).collect());
586            if input.at_end() {
587                return Err(Error::RequiredEkuNotFoundContext(
588                    RequiredEkuNotFoundContext {
589                        #[cfg(feature = "alloc")]
590                        required: KeyUsage { inner: *self },
591                        #[cfg(feature = "alloc")]
592                        present,
593                    },
594                ));
595            }
596        }
597
598        Ok(())
599    }
600
601    fn key_purpose_id_equals(&self, value: untrusted::Input<'_>) -> bool {
602        public_values_eq(
603            match self {
604                Self::Required(eku) => *eku,
605                Self::RequiredIfPresent(eku) => *eku,
606            }
607            .oid_value,
608            value,
609        )
610    }
611}
612
613/// An OID value indicating an Extended Key Usage (EKU) key purpose.
614#[derive(Clone, Copy)]
615struct KeyPurposeId<'a> {
616    oid_value: untrusted::Input<'a>,
617}
618
619impl<'a> KeyPurposeId<'a> {
620    /// Construct a new [`KeyPurposeId`].
621    ///
622    /// `oid` is the OBJECT IDENTIFIER in bytes.
623    const fn new(oid: &'a [u8]) -> Self {
624        Self {
625            oid_value: untrusted::Input::from(oid),
626        }
627    }
628}
629
630impl fmt::Debug for KeyPurposeId<'_> {
631    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
632        write!(f, "KeyPurposeId(")?;
633        let decoder = OidDecoder::new(self.oid_value.as_slice_less_safe());
634        for (i, part) in decoder.enumerate() {
635            if i > 0 {
636                write!(f, ".")?;
637            }
638            write!(f, "{part}")?;
639        }
640        write!(f, ")")
641    }
642}
643
644impl PartialEq<Self> for KeyPurposeId<'_> {
645    fn eq(&self, other: &Self) -> bool {
646        public_values_eq(self.oid_value, other.oid_value)
647    }
648}
649
650impl Eq for KeyPurposeId<'_> {}
651
652// id-pkix            OBJECT IDENTIFIER ::= { 1 3 6 1 5 5 7 }
653// id-kp              OBJECT IDENTIFIER ::= { id-pkix 3 }
654
655// id-kp-serverAuth   OBJECT IDENTIFIER ::= { id-kp 1 }
656const EKU_SERVER_AUTH: &[u8] = &oid!(1, 3, 6, 1, 5, 5, 7, 3, 1);
657
658// id-kp-clientAuth   OBJECT IDENTIFIER ::= { id-kp 2 }
659const EKU_CLIENT_AUTH: &[u8] = &oid!(1, 3, 6, 1, 5, 5, 7, 3, 2);
660
661struct OidDecoder<'a> {
662    encoded: &'a [u8],
663    left: Option<usize>,
664    first: bool,
665}
666
667impl<'a> OidDecoder<'a> {
668    fn new(encoded: &'a [u8]) -> Self {
669        Self {
670            encoded,
671            left: None,
672            first: true,
673        }
674    }
675}
676
677impl Iterator for OidDecoder<'_> {
678    type Item = usize;
679
680    fn next(&mut self) -> Option<Self::Item> {
681        if let Some(next) = self.left.take() {
682            return Some(next);
683        }
684
685        let mut cur = 0;
686        for (i, &byte) in self.encoded.iter().enumerate() {
687            cur = (cur << 8) + usize::from(byte & 0x7f);
688            if byte & 0x80 > 0 {
689                continue;
690            }
691
692            if !self.first {
693                self.encoded = &self.encoded[i + 1..];
694                return Some(cur);
695            }
696
697            let (cur, next) = match cur {
698                ..=39 => (0, cur),
699                40..=79 => (1, cur - 40),
700                _ => (2, cur - 80),
701            };
702
703            self.encoded = &self.encoded[i + 1..];
704            self.first = false;
705            self.left = Some(next);
706            return Some(cur);
707        }
708
709        None
710    }
711}
712
713fn loop_while_non_fatal_error<'a, V: IntoIterator + 'a>(
714    default_error: Error,
715    values: V,
716    mut f: impl FnMut(V::Item) -> Result<&'a TrustAnchor<'a>, ControlFlow<Error, Error>>,
717) -> Result<&'a TrustAnchor<'a>, ControlFlow<Error, Error>> {
718    let mut error = default_error;
719    for v in values {
720        match f(v) {
721            Ok(anchor) => return Ok(anchor),
722            // Fatal errors should halt further looping.
723            res @ Err(ControlFlow::Break(_)) => return res,
724            // Non-fatal errors should be ranked by specificity and only returned
725            // once all other path-building options have been exhausted.
726            Err(ControlFlow::Continue(new_error)) => error = error.most_specific(new_error),
727        }
728    }
729    Err(error.into())
730}
731
732/// A path for consideration in path building.
733///
734/// This represents a partial path because it does not yet contain the trust anchor. It stores
735/// the end-entity certificates, and an array of intermediate certificates.
736pub(crate) struct PartialPath<'a> {
737    end_entity: &'a EndEntityCert<'a>,
738    /// Intermediate certificates, in order from end-entity to trust anchor.
739    ///
740    /// Invariant: all values below `used` are `Some`.
741    intermediates: [Option<Cert<'a>>; MAX_SUB_CA_COUNT],
742    /// The number of `Some` values in `intermediates`.
743    ///
744    /// The next `Cert` passed to `push()` will be placed at `intermediates[used]`.
745    /// If this value is 0, the path contains only the end-entity certificate.
746    used: usize,
747}
748
749impl<'a> PartialPath<'a> {
750    pub(crate) fn new(end_entity: &'a EndEntityCert<'a>) -> Self {
751        Self {
752            end_entity,
753            intermediates: Default::default(),
754            used: 0,
755        }
756    }
757
758    pub(crate) fn push(&mut self, cert: Cert<'a>) -> Result<(), ControlFlow<Error, Error>> {
759        if self.used >= MAX_SUB_CA_COUNT {
760            return Err(Error::MaximumPathDepthExceeded.into());
761        }
762
763        self.intermediates[self.used] = Some(cert);
764        self.used += 1;
765        Ok(())
766    }
767
768    fn pop(&mut self) {
769        debug_assert!(self.used > 0);
770        if self.used == 0 {
771            return;
772        }
773
774        self.used -= 1;
775        self.intermediates[self.used] = None;
776    }
777
778    pub(crate) fn node(&self) -> PathNode<'_> {
779        PathNode {
780            path: self,
781            index: self.used,
782            cert: self.head(),
783        }
784    }
785
786    /// Current head of the path.
787    pub(crate) fn head(&self) -> &Cert<'a> {
788        self.get(self.used)
789    }
790
791    /// Get the certificate at index `idx` in the path.
792    ///
793    // `idx` must be in the range `0..=self.used`; `idx` 0 thus yields the `end_entity`,
794    // while subsequent indexes yield the intermediate at `self.intermediates[idx - 1]`.
795    fn get(&self, idx: usize) -> &Cert<'a> {
796        match idx {
797            0 => self.end_entity,
798            _ => self.intermediates[idx - 1].as_ref().unwrap(),
799        }
800    }
801}
802
803const MAX_SUB_CA_COUNT: usize = 6;
804
805pub(crate) struct PathNode<'a> {
806    /// The path we're iterating.
807    path: &'a PartialPath<'a>,
808    /// The index of the current node in the path (input for `path.get()`).
809    index: usize,
810    /// The [`Cert`] at `index`.
811    pub(crate) cert: &'a Cert<'a>,
812}
813
814impl<'a> PathNode<'a> {
815    pub(crate) fn iter(&self) -> PathIter<'a> {
816        PathIter {
817            path: self.path,
818            next: Some(self.index),
819        }
820    }
821
822    pub(crate) fn role(&self) -> Role {
823        match self.index {
824            0 => Role::EndEntity,
825            _ => Role::Issuer,
826        }
827    }
828}
829
830pub(crate) struct PathIter<'a> {
831    path: &'a PartialPath<'a>,
832    next: Option<usize>,
833}
834
835impl<'a> Iterator for PathIter<'a> {
836    type Item = PathNode<'a>;
837
838    fn next(&mut self) -> Option<Self::Item> {
839        let next = self.next?;
840        self.next = match next {
841            0 => None,
842            _ => Some(next - 1),
843        };
844
845        Some(PathNode {
846            path: self.path,
847            index: next,
848            cert: self.path.get(next),
849        })
850    }
851}
852
853#[derive(Clone, Copy, PartialEq)]
854pub(crate) enum Role {
855    Issuer,
856    EndEntity,
857}
858
859#[cfg(all(test, feature = "alloc", any(feature = "ring", feature = "aws-lc-rs")))]
860mod tests {
861    use super::*;
862    use crate::test_utils;
863    use crate::test_utils::{issuer_params, make_end_entity, make_issuer};
864    use crate::trust_anchor::anchor_from_trusted_cert;
865    use rcgen::{CertifiedKey, KeyPair};
866    use std::dbg;
867    use std::prelude::v1::*;
868
869    #[test]
870    fn roundtrip() {
871        // 2.999.3 -> 1079.3 -> [0x84, 0x37, 0x3]
872        const ENCODED: &[u8] = &[0x84, 0x37, 0x3];
873        let decoded = OidDecoder::new(ENCODED);
874        assert_eq!(decoded.collect::<Vec<_>>(), [2, 999, 3]);
875    }
876
877    #[test]
878    fn oid_decoding() {
879        assert_eq!(
880            KeyUsage::server_auth().oid_values().collect::<Vec<_>>(),
881            KeyUsage::SERVER_AUTH_REPR
882        );
883        assert_eq!(
884            KeyUsage::client_auth().oid_values().collect::<Vec<_>>(),
885            KeyUsage::CLIENT_AUTH_REPR
886        );
887    }
888
889    #[test]
890    fn eku_fail_empty() {
891        let err = ExtendedKeyUsage::Required(KeyPurposeId::new(EKU_SERVER_AUTH))
892            .check(None)
893            .unwrap_err();
894        assert_eq!(
895            err,
896            Error::RequiredEkuNotFoundContext(RequiredEkuNotFoundContext {
897                #[cfg(feature = "alloc")]
898                required: dbg!(KeyUsage::required(EKU_SERVER_AUTH)), // Cover Debug impl
899                #[cfg(feature = "alloc")]
900                present: Vec::new(),
901            })
902        );
903    }
904
905    #[test]
906    fn eku_key_purpose_id() {
907        assert!(
908            ExtendedKeyUsage::RequiredIfPresent(KeyPurposeId::new(EKU_SERVER_AUTH))
909                .key_purpose_id_equals(KeyPurposeId::new(EKU_SERVER_AUTH).oid_value)
910        )
911    }
912
913    #[test]
914    fn test_too_many_signatures() {
915        assert!(matches!(
916            build_and_verify_degenerate_chain(5, ChainTrustAnchor::NotInChain),
917            ControlFlow::Break(Error::MaximumSignatureChecksExceeded)
918        ));
919    }
920
921    #[test]
922    fn test_too_many_path_calls() {
923        assert!(matches!(
924            dbg!(build_and_verify_degenerate_chain(
925                10,
926                ChainTrustAnchor::InChain
927            )),
928            ControlFlow::Break(Error::MaximumPathBuildCallsExceeded)
929        ));
930    }
931
932    #[test]
933    fn longest_allowed_path() {
934        assert!(build_and_verify_linear_chain(1).is_ok());
935        assert!(build_and_verify_linear_chain(2).is_ok());
936        assert!(build_and_verify_linear_chain(3).is_ok());
937        assert!(build_and_verify_linear_chain(4).is_ok());
938        assert!(build_and_verify_linear_chain(5).is_ok());
939        assert!(build_and_verify_linear_chain(6).is_ok());
940    }
941
942    #[test]
943    fn path_too_long() {
944        assert!(matches!(
945            build_and_verify_linear_chain(7),
946            Err(ControlFlow::Continue(Error::MaximumPathDepthExceeded))
947        ));
948    }
949
950    #[test]
951    fn name_constraint_budget() {
952        // Issue a trust anchor that imposes name constraints. The constraint should match
953        // the end entity certificate SAN.
954        let mut ca_cert_params = issuer_params("Constrained Root");
955        ca_cert_params.name_constraints = Some(rcgen::NameConstraints {
956            permitted_subtrees: vec![rcgen::GeneralSubtree::DnsName(".com".into())],
957            excluded_subtrees: vec![],
958        });
959        let ca_key_pair = KeyPair::generate_for(test_utils::RCGEN_SIGNATURE_ALG).unwrap();
960        let ca_cert = ca_cert_params.self_signed(&ca_key_pair).unwrap();
961
962        // Create a series of intermediate issuers. We'll only use one in the actual built path,
963        // helping demonstrate that the name constraint budget is not expended checking certificates
964        // that are not part of the path we compute.
965        let mut intermediates = Vec::with_capacity(5);
966        for i in 0..5 {
967            let intermediate = issuer_params(format!("Intermediate {i}"));
968            let intermediate_key_pair =
969                KeyPair::generate_for(test_utils::RCGEN_SIGNATURE_ALG).unwrap();
970            // Each intermediate should be issued by the trust anchor.
971            let intermediate = intermediate
972                .signed_by(&intermediate_key_pair, &ca_cert, &ca_key_pair)
973                .unwrap();
974            intermediates.push((intermediate, intermediate_key_pair));
975        }
976
977        // Create an end-entity cert that is issued by the last of the intermediates.
978        let last_issuer = intermediates.last().unwrap();
979        let ee_cert = make_end_entity(&last_issuer.0, &last_issuer.1);
980        let ee_cert = EndEntityCert::try_from(ee_cert.cert.der()).unwrap();
981
982        // We use a custom budget to make it easier to write a test, otherwise it is tricky to
983        // stuff enough names/constraints into the potential chains while staying within the path
984        // depth limit and the build chain call limit.
985        let passing_budget = Budget {
986            // One comparison against the intermediate's distinguished name.
987            // One comparison against the EE's distinguished name.
988            // One comparison against the EE's SAN.
989            //  = 3 total comparisons.
990            name_constraint_comparisons: 3,
991            ..Budget::default()
992        };
993
994        let ca_cert_der = ca_cert.into();
995        let anchors = &[anchor_from_trusted_cert(&ca_cert_der).unwrap()];
996        let intermediates_der = intermediates
997            .iter()
998            .map(|(cert, _)| cert.der().clone())
999            .collect::<Vec<_>>();
1000
1001        // Validation should succeed with the name constraint comparison budget allocated above.
1002        // This shows that we're not consuming budget on unused intermediates: we didn't budget
1003        // enough comparisons for that to pass the overall chain building.
1004        let path = verify_chain(
1005            anchors,
1006            &intermediates_der,
1007            &ee_cert,
1008            None,
1009            Some(passing_budget),
1010        )
1011        .unwrap();
1012        assert_eq!(path.anchor().subject, anchors.first().unwrap().subject);
1013
1014        let failing_budget = Budget {
1015            // See passing_budget: 2 comparisons is not sufficient.
1016            name_constraint_comparisons: 2,
1017            ..Budget::default()
1018        };
1019        // Validation should fail when the budget is smaller than the number of comparisons performed
1020        // on the validated path. This demonstrates we properly fail path building when too many
1021        // name constraint comparisons occur.
1022        let result = verify_chain(
1023            anchors,
1024            &intermediates_der,
1025            &ee_cert,
1026            None,
1027            Some(failing_budget),
1028        );
1029
1030        assert!(matches!(
1031            result,
1032            Err(ControlFlow::Break(
1033                Error::MaximumNameConstraintComparisonsExceeded
1034            ))
1035        ));
1036    }
1037
1038    #[test]
1039    fn test_reject_candidate_path() {
1040        /*
1041         This test builds a PKI like the following diagram depicts. We first verify
1042         that we can build a path EE -> B -> A -> TA. Next we supply a custom path verification
1043         function that rejects the B->A path, and verify that we build a path EE -> B -> C -> TA.
1044
1045               ┌───────────┐
1046               │           │
1047               │     TA    │
1048               │           │
1049               └───┬───┬───┘
1050                   │   │
1051                   │   │
1052        ┌────────┐◄┘   └──►┌────────┐
1053        │        │         │        │
1054        │   A    │         │   C    │
1055        │        │         │        │
1056        └────┬───┘         └───┬────┘
1057             │                 │
1058             │                 │
1059             │   ┌─────────┐   │
1060             └──►│         │◄──┘
1061                 │    B    │
1062                 │         │
1063                 └────┬────┘
1064106510661067                 ┌────▼────┐
1068                 │         │
1069                 │    EE   │
1070                 │         │
1071                 └─────────┘
1072          */
1073
1074        // Create a trust anchor, and use it to issue two distinct intermediate certificates, each
1075        // with a unique subject and keypair.
1076        let trust_anchor = make_issuer("Trust Anchor");
1077        let trust_anchor_cert =
1078            Cert::from_der(untrusted::Input::from(trust_anchor.cert.der())).unwrap();
1079        let trust_anchors = &[anchor_from_trusted_cert(trust_anchor.cert.der()).unwrap()];
1080
1081        let intermediate_a = issuer_params("Intermediate A");
1082        let intermediate_a_kp = KeyPair::generate_for(test_utils::RCGEN_SIGNATURE_ALG).unwrap();
1083        let intermediate_a = intermediate_a
1084            .signed_by(
1085                &intermediate_a_kp,
1086                &trust_anchor.cert,
1087                &trust_anchor.key_pair,
1088            )
1089            .unwrap();
1090        let intermediate_a_cert =
1091            Cert::from_der(untrusted::Input::from(intermediate_a.der())).unwrap();
1092
1093        let intermediate_c = issuer_params("Intermediate C");
1094        let intermediate_c_kp = KeyPair::generate_for(test_utils::RCGEN_SIGNATURE_ALG).unwrap();
1095        let intermediate_c = intermediate_c
1096            .signed_by(
1097                &intermediate_c_kp,
1098                &trust_anchor.cert,
1099                &trust_anchor.key_pair,
1100            )
1101            .unwrap();
1102        let intermediate_c_cert =
1103            Cert::from_der(untrusted::Input::from(intermediate_c.der())).unwrap();
1104
1105        // Next, create an intermediate that is issued by both of the intermediates above.
1106        // Both should share the same subject, and key pair, but will differ in the issuer.
1107        let intermediate_b_key = KeyPair::generate_for(test_utils::RCGEN_SIGNATURE_ALG).unwrap();
1108        let intermediate_b_params = issuer_params("Intermediate");
1109        let intermediate_b_a = intermediate_b_params
1110            .clone()
1111            .signed_by(&intermediate_b_key, &intermediate_a, &intermediate_a_kp)
1112            .unwrap();
1113        let intermediate_b_c = intermediate_b_params
1114            .signed_by(&intermediate_b_key, &intermediate_c, &intermediate_c_kp)
1115            .unwrap();
1116
1117        let intermediates = &[
1118            intermediate_a.der().clone(),
1119            intermediate_c.der().clone(),
1120            intermediate_b_a.der().clone(),
1121            intermediate_b_c.der().clone(),
1122        ];
1123
1124        // Create an end entity certificate signed by the keypair of the intermediates created above.
1125        let ee = make_end_entity(&intermediate_b_a, &intermediate_b_key);
1126        let ee_cert = &EndEntityCert::try_from(ee.cert.der()).unwrap();
1127
1128        // We should be able to create a valid path from EE to trust anchor.
1129        let path = verify_chain(trust_anchors, intermediates, ee_cert, None, None).unwrap();
1130        let path_intermediates = path.intermediate_certificates().collect::<Vec<_>>();
1131
1132        // We expect that without applying any additional constraints, that the path will be
1133        // EE -> intermediate_b_a -> intermediate_a -> trust_anchor.
1134        assert_eq!(path_intermediates.len(), 2);
1135        assert_eq!(
1136            path_intermediates[0].issuer(),
1137            intermediate_a_cert.subject()
1138        );
1139        assert_eq!(path_intermediates[1].issuer(), trust_anchor_cert.subject());
1140
1141        // Now, we'll create a function that will reject the intermediate_b_a path.
1142        let expected_chain = |path: &VerifiedPath<'_>| {
1143            for intermediate in path.intermediate_certificates() {
1144                // Reject any intermediates issued by intermediate A.
1145                if intermediate.issuer() == intermediate_a_cert.subject() {
1146                    return Err(Error::UnknownIssuer);
1147                }
1148            }
1149
1150            Ok(())
1151        };
1152
1153        // We should still be able to build a valid path.
1154        let path = verify_chain(
1155            trust_anchors,
1156            intermediates,
1157            ee_cert,
1158            Some(&expected_chain),
1159            None,
1160        )
1161        .unwrap();
1162        let path_intermediates = path.intermediate_certificates().collect::<Vec<_>>();
1163
1164        // We expect that the path will now be
1165        // EE -> intermediate_b_c -> intermediate_c -> trust_anchor.
1166        assert_eq!(path_intermediates.len(), 2);
1167        assert_eq!(
1168            path_intermediates[0].issuer(),
1169            intermediate_c_cert.subject()
1170        );
1171        assert_eq!(path_intermediates[1].issuer(), trust_anchor_cert.subject());
1172    }
1173
1174    fn build_and_verify_degenerate_chain(
1175        intermediate_count: usize,
1176        trust_anchor: ChainTrustAnchor,
1177    ) -> ControlFlow<Error, Error> {
1178        let ca_cert = make_issuer("Bogus Subject");
1179        let mut intermediate_chain = build_linear_chain(&ca_cert, intermediate_count, true);
1180
1181        let verify_trust_anchor = match trust_anchor {
1182            ChainTrustAnchor::InChain => make_issuer("Bogus Trust Anchor"),
1183            ChainTrustAnchor::NotInChain => ca_cert,
1184        };
1185
1186        let ee_cert = make_end_entity(
1187            &intermediate_chain.last_issuer.cert,
1188            &intermediate_chain.last_issuer.key_pair,
1189        );
1190        let ee_cert = EndEntityCert::try_from(ee_cert.cert.der()).unwrap();
1191        let trust_anchor_der: CertificateDer<'_> = verify_trust_anchor.cert.into();
1192        let webpki_ta = anchor_from_trusted_cert(&trust_anchor_der).unwrap();
1193        if matches!(trust_anchor, ChainTrustAnchor::InChain) {
1194            // Note: we clone the trust anchor DER here because we can't move it into the chain
1195            // as it's loaned to webpki_ta above.
1196            intermediate_chain.chain.insert(0, trust_anchor_der.clone())
1197        }
1198
1199        verify_chain(
1200            &[webpki_ta],
1201            &intermediate_chain.chain,
1202            &ee_cert,
1203            None,
1204            None,
1205        )
1206        .map(|_| ())
1207        .unwrap_err()
1208    }
1209
1210    #[cfg(feature = "alloc")]
1211    enum ChainTrustAnchor {
1212        NotInChain,
1213        InChain,
1214    }
1215
1216    fn build_and_verify_linear_chain(chain_length: usize) -> Result<(), ControlFlow<Error, Error>> {
1217        let ca_cert = make_issuer(format!("Bogus Subject {chain_length}"));
1218        let intermediate_chain = build_linear_chain(&ca_cert, chain_length, false);
1219
1220        let ca_cert_der: CertificateDer<'_> = ca_cert.cert.into();
1221        let anchor = anchor_from_trusted_cert(&ca_cert_der).unwrap();
1222        let anchors = &[anchor.clone()];
1223
1224        let ee_cert = make_end_entity(
1225            &intermediate_chain.last_issuer.cert,
1226            &intermediate_chain.last_issuer.key_pair,
1227        );
1228        let ee_cert = EndEntityCert::try_from(ee_cert.cert.der()).unwrap();
1229
1230        let expected_chain = |path: &VerifiedPath<'_>| {
1231            assert_eq!(path.anchor().subject, anchor.subject);
1232            assert!(public_values_eq(path.end_entity().subject, ee_cert.subject));
1233            assert_eq!(path.intermediate_certificates().count(), chain_length);
1234
1235            let intermediate_certs = intermediate_chain
1236                .chain
1237                .iter()
1238                .map(|der| Cert::from_der(untrusted::Input::from(der)).unwrap())
1239                .collect::<Vec<_>>();
1240
1241            for (cert, expected) in path
1242                .intermediate_certificates()
1243                .rev()
1244                .zip(intermediate_certs.iter())
1245            {
1246                assert!(public_values_eq(cert.subject, expected.subject));
1247                assert_eq!(cert.der(), expected.der());
1248            }
1249
1250            for (cert, expected) in path
1251                .intermediate_certificates()
1252                .zip(intermediate_certs.iter().rev())
1253            {
1254                assert!(public_values_eq(cert.subject, expected.subject));
1255                assert_eq!(cert.der(), expected.der());
1256            }
1257
1258            Ok(())
1259        };
1260
1261        verify_chain(
1262            anchors,
1263            &intermediate_chain.chain,
1264            &ee_cert,
1265            Some(&expected_chain),
1266            None,
1267        )
1268        .map(|_| ())
1269    }
1270
1271    fn build_linear_chain(
1272        ca_cert: &CertifiedKey,
1273        chain_length: usize,
1274        all_same_subject: bool,
1275    ) -> IntermediateChain {
1276        let mut chain = Vec::with_capacity(chain_length);
1277
1278        let mut prev = None;
1279        for i in 0..chain_length {
1280            let issuer = match &prev {
1281                Some(prev) => prev,
1282                None => ca_cert,
1283            };
1284
1285            let intermediate = issuer_params(match all_same_subject {
1286                true => "Bogus Subject".to_string(),
1287                false => format!("Bogus Subject {i}"),
1288            });
1289
1290            let key_pair = KeyPair::generate_for(test_utils::RCGEN_SIGNATURE_ALG).unwrap();
1291            let cert = intermediate
1292                .signed_by(&key_pair, &issuer.cert, &issuer.key_pair)
1293                .unwrap();
1294
1295            chain.push(cert.der().clone());
1296            prev = Some(CertifiedKey { cert, key_pair });
1297        }
1298
1299        IntermediateChain {
1300            last_issuer: prev.unwrap(),
1301            chain,
1302        }
1303    }
1304
1305    struct IntermediateChain {
1306        last_issuer: CertifiedKey,
1307        chain: Vec<CertificateDer<'static>>,
1308    }
1309
1310    fn verify_chain<'a>(
1311        trust_anchors: &'a [TrustAnchor<'a>],
1312        intermediate_certs: &'a [CertificateDer<'a>],
1313        ee_cert: &'a EndEntityCert<'a>,
1314        verify_path: Option<&dyn Fn(&VerifiedPath<'_>) -> Result<(), Error>>,
1315        budget: Option<Budget>,
1316    ) -> Result<VerifiedPath<'a>, ControlFlow<Error, Error>> {
1317        use core::time::Duration;
1318
1319        let time = UnixTime::since_unix_epoch(Duration::from_secs(0x1fed_f00d));
1320        let mut path = PartialPath::new(ee_cert);
1321        let opts = ChainOptions {
1322            eku: KeyUsage::server_auth(),
1323            supported_sig_algs: crate::ALL_VERIFICATION_ALGS,
1324            trust_anchors,
1325            intermediate_certs,
1326            revocation: None,
1327        };
1328
1329        match opts.build_chain_inner(
1330            &mut path,
1331            time,
1332            verify_path,
1333            0,
1334            &mut budget.unwrap_or_default(),
1335        ) {
1336            Ok(anchor) => Ok(VerifiedPath::new(ee_cert, anchor, path)),
1337            Err(err) => Err(err),
1338        }
1339    }
1340}