webpki/
error.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::string::String;
17#[cfg(feature = "alloc")]
18use alloc::vec::Vec;
19use core::fmt;
20use core::ops::ControlFlow;
21
22#[cfg(feature = "alloc")]
23use pki_types::ServerName;
24use pki_types::UnixTime;
25
26/// An error that occurs during certificate validation or name validation.
27#[derive(Clone, Debug, PartialEq, Eq)]
28#[non_exhaustive]
29pub enum Error {
30    /// The encoding of some ASN.1 DER-encoded item is invalid.
31    BadDer,
32
33    /// The encoding of an ASN.1 DER-encoded time is invalid.
34    BadDerTime,
35
36    /// A CA certificate is being used as an end-entity certificate.
37    CaUsedAsEndEntity,
38
39    /// The certificate is expired; i.e. the time it is being validated for is
40    /// later than the certificate's notAfter time.
41    CertExpired {
42        /// The validation time.
43        time: UnixTime,
44        /// The notAfter time of the certificate.
45        not_after: UnixTime,
46    },
47
48    /// The certificate is not valid for the name it is being validated for.
49    CertNotValidForName(InvalidNameContext),
50
51    /// The certificate is not valid yet; i.e. the time it is being validated
52    /// for is earlier than the certificate's notBefore time.
53    CertNotValidYet {
54        /// The validation time.
55        time: UnixTime,
56        /// The notBefore time of the certificate.
57        not_before: UnixTime,
58    },
59
60    /// The certificate, or one of its issuers, has been revoked.
61    CertRevoked,
62
63    /// The CRL is expired; i.e. the verification time is not before the time
64    /// in the CRL nextUpdate field.
65    CrlExpired {
66        /// The validation time.
67        time: UnixTime,
68        /// The nextUpdate time of the CRL.
69        next_update: UnixTime,
70    },
71
72    /// An end-entity certificate is being used as a CA certificate.
73    EndEntityUsedAsCa,
74
75    /// An X.509 extension is invalid.
76    ExtensionValueInvalid,
77
78    /// The certificate validity period (notBefore, notAfter) is invalid; e.g.
79    /// the notAfter time is earlier than the notBefore time.
80    InvalidCertValidity,
81
82    /// A CRL number extension was invalid:
83    ///  - it was mis-encoded
84    ///  - it was negative
85    ///  - it was too long
86    InvalidCrlNumber,
87
88    /// A iPAddress name constraint was invalid:
89    /// - it had a sparse network mask (ie, cannot be written in CIDR form).
90    /// - it was too long or short
91    InvalidNetworkMaskConstraint,
92
93    /// A serial number was invalid:
94    ///  - it was misencoded
95    ///  - it was negative
96    ///  - it was too long
97    InvalidSerialNumber,
98
99    /// The CRL signature is invalid for the issuer's public key.
100    InvalidCrlSignatureForPublicKey,
101
102    /// The signature is invalid for the given public key.
103    InvalidSignatureForPublicKey,
104
105    /// A CRL was signed by an issuer that has a KeyUsage bitstring that does not include
106    /// the cRLSign key usage bit.
107    IssuerNotCrlSigner,
108
109    /// A presented or reference DNS identifier was malformed, potentially
110    /// containing invalid characters or invalid labels.
111    MalformedDnsIdentifier,
112
113    /// The certificate extensions are malformed.
114    ///
115    /// In particular, webpki requires the DNS name(s) be in the subjectAltName
116    /// extension as required by the CA/Browser Forum Baseline Requirements
117    /// and as recommended by RFC6125.
118    MalformedExtensions,
119
120    /// A name constraint was malformed, potentially containing invalid characters or
121    /// invalid labels.
122    MalformedNameConstraint,
123
124    /// The maximum number of name constraint comparisons has been reached.
125    MaximumNameConstraintComparisonsExceeded,
126
127    /// The maximum number of internal path building calls has been reached. Path complexity is too great.
128    MaximumPathBuildCallsExceeded,
129
130    /// The path search was terminated because it became too deep.
131    MaximumPathDepthExceeded,
132
133    /// The maximum number of signature checks has been reached. Path complexity is too great.
134    MaximumSignatureChecksExceeded,
135
136    /// The certificate violates one or more name constraints.
137    NameConstraintViolation,
138
139    /// The certificate violates one or more path length constraints.
140    PathLenConstraintViolated,
141
142    /// The certificate is not valid for the Extended Key Usage for which it is
143    /// being validated.
144    RequiredEkuNotFound,
145
146    /// The algorithm in the TBSCertificate "signature" field of a certificate
147    /// does not match the algorithm in the signature of the certificate.
148    SignatureAlgorithmMismatch,
149
150    /// Trailing data was found while parsing DER-encoded input for the named type.
151    TrailingData(DerTypeId),
152
153    /// A valid issuer for the certificate could not be found.
154    UnknownIssuer,
155
156    /// The certificate's revocation status could not be determined.
157    UnknownRevocationStatus,
158
159    /// The certificate is not a v3 X.509 certificate.
160    ///
161    /// This error may be also reported if the certificate version field
162    /// is malformed.
163    UnsupportedCertVersion,
164
165    /// The certificate contains an unsupported critical extension.
166    UnsupportedCriticalExtension,
167
168    /// The CRL contains an issuing distribution point with no distribution point name,
169    /// or a distribution point name relative to an issuer.
170    UnsupportedCrlIssuingDistributionPoint,
171
172    /// The CRL is not a v2 X.509 CRL.
173    ///
174    /// The RFC 5280 web PKI profile mandates only version 2 be used. See section
175    /// 5.1.2.1 for more information.
176    ///
177    /// This error may also be reported if the CRL version field is malformed.
178    UnsupportedCrlVersion,
179
180    /// The CRL is an unsupported "delta" CRL.
181    UnsupportedDeltaCrl,
182
183    /// The CRL contains unsupported "indirect" entries.
184    UnsupportedIndirectCrl,
185
186    /// The `ServerName` contained an unsupported type of value.
187    UnsupportedNameType,
188
189    /// The revocation reason is not in the set of supported revocation reasons.
190    UnsupportedRevocationReason,
191
192    /// The CRL is partitioned by revocation reasons.
193    UnsupportedRevocationReasonsPartitioning,
194
195    /// The signature algorithm for a signature over a CRL is not in the set of supported
196    /// signature algorithms given.
197    UnsupportedCrlSignatureAlgorithm,
198
199    /// The signature algorithm for a signature is not in the set of supported
200    /// signature algorithms given.
201    UnsupportedSignatureAlgorithm,
202
203    /// The CRL signature's algorithm does not match the algorithm of the issuer
204    /// public key it is being validated for. This may be because the public key
205    /// algorithm's OID isn't recognized (e.g. DSA), or the public key
206    /// algorithm's parameters don't match the supported parameters for that
207    /// algorithm (e.g. ECC keys for unsupported curves), or the public key
208    /// algorithm and the signature algorithm simply don't match (e.g.
209    /// verifying an RSA signature with an ECC public key).
210    UnsupportedCrlSignatureAlgorithmForPublicKey,
211
212    /// The signature's algorithm does not match the algorithm of the public
213    /// key it is being validated for. This may be because the public key
214    /// algorithm's OID isn't recognized (e.g. DSA), or the public key
215    /// algorithm's parameters don't match the supported parameters for that
216    /// algorithm (e.g. ECC keys for unsupported curves), or the public key
217    /// algorithm and the signature algorithm simply don't match (e.g.
218    /// verifying an RSA signature with an ECC public key).
219    UnsupportedSignatureAlgorithmForPublicKey,
220}
221
222impl Error {
223    // Compare the Error with the new error by rank, returning the higher rank of the two as
224    // the most specific error.
225    pub(crate) fn most_specific(self, new: Self) -> Self {
226        // Assign an error a numeric value ranking it by specificity.
227        if self.rank() >= new.rank() { self } else { new }
228    }
229
230    // Return a numeric indication of how specific the error is, where an error with a higher rank
231    // is considered more useful to an end user than an error with a lower rank. This is used by
232    // Error::most_specific to compare two errors in order to return which is more specific.
233    #[allow(clippy::as_conversions)] // We won't exceed u32 errors.
234    pub(crate) fn rank(&self) -> u32 {
235        match &self {
236            // Errors related to certificate validity
237            Self::CertNotValidYet { .. } | Self::CertExpired { .. } => 290,
238            Self::CertNotValidForName(_) => 280,
239            Self::CertRevoked | Self::UnknownRevocationStatus | Self::CrlExpired { .. } => 270,
240            Self::InvalidCrlSignatureForPublicKey | Self::InvalidSignatureForPublicKey => 260,
241            Self::SignatureAlgorithmMismatch => 250,
242            Self::RequiredEkuNotFound => 240,
243            Self::NameConstraintViolation => 230,
244            Self::PathLenConstraintViolated => 220,
245            Self::CaUsedAsEndEntity | Self::EndEntityUsedAsCa => 210,
246            Self::IssuerNotCrlSigner => 200,
247
248            // Errors related to supported features used in an invalid way.
249            Self::InvalidCertValidity => 190,
250            Self::InvalidNetworkMaskConstraint => 180,
251            Self::InvalidSerialNumber => 170,
252            Self::InvalidCrlNumber => 160,
253
254            // Errors related to unsupported features.
255            Self::UnsupportedCrlSignatureAlgorithmForPublicKey
256            | Self::UnsupportedSignatureAlgorithmForPublicKey => 150,
257            Self::UnsupportedCrlSignatureAlgorithm | Self::UnsupportedSignatureAlgorithm => 140,
258            Self::UnsupportedCriticalExtension => 130,
259            Self::UnsupportedCertVersion => 130,
260            Self::UnsupportedCrlVersion => 120,
261            Self::UnsupportedDeltaCrl => 110,
262            Self::UnsupportedIndirectCrl => 100,
263            Self::UnsupportedNameType => 95,
264            Self::UnsupportedRevocationReason => 90,
265            Self::UnsupportedRevocationReasonsPartitioning => 80,
266            Self::UnsupportedCrlIssuingDistributionPoint => 70,
267            Self::MaximumPathDepthExceeded => 61,
268
269            // Errors related to malformed data.
270            Self::MalformedDnsIdentifier => 60,
271            Self::MalformedNameConstraint => 50,
272            Self::MalformedExtensions | Self::TrailingData(_) => 40,
273            Self::ExtensionValueInvalid => 30,
274
275            // Generic DER errors.
276            Self::BadDerTime => 20,
277            Self::BadDer => 10,
278
279            // Special case errors - not subject to ranking.
280            Self::MaximumSignatureChecksExceeded => 0,
281            Self::MaximumPathBuildCallsExceeded => 0,
282            Self::MaximumNameConstraintComparisonsExceeded => 0,
283
284            // Default catch all error - should be renamed in the future.
285            Self::UnknownIssuer => 0,
286        }
287    }
288
289    /// Returns true for errors that should be considered fatal during path building. Errors of
290    /// this class should halt any further path building and be returned immediately.
291    #[inline]
292    pub(crate) fn is_fatal(&self) -> bool {
293        matches!(
294            self,
295            Self::MaximumSignatureChecksExceeded
296                | Self::MaximumPathBuildCallsExceeded
297                | Self::MaximumNameConstraintComparisonsExceeded
298        )
299    }
300}
301
302impl From<Error> for ControlFlow<Error, Error> {
303    fn from(value: Error) -> Self {
304        match value {
305            // If an error is fatal, we've exhausted the potential for continued search.
306            err if err.is_fatal() => Self::Break(err),
307            // Otherwise we've rejected one candidate chain, but may continue to search for others.
308            err => Self::Continue(err),
309        }
310    }
311}
312
313impl fmt::Display for Error {
314    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315        write!(f, "{:?}", self)
316    }
317}
318
319#[cfg(feature = "std")]
320impl ::std::error::Error for Error {}
321
322/// Additional context for the `CertNotValidForName` error variant.
323///
324/// The contents of this type depend on whether the `alloc` feature is enabled.
325#[derive(Clone, Debug, PartialEq, Eq)]
326pub struct InvalidNameContext {
327    /// Expected server name.
328    #[cfg(feature = "alloc")]
329    pub expected: ServerName<'static>,
330    /// The names presented in the end entity certificate.
331    ///
332    /// These are the subject names as present in the leaf certificate and may contain DNS names
333    /// with or without a wildcard label as well as IP address names.
334    #[cfg(feature = "alloc")]
335    pub presented: Vec<String>,
336}
337
338/// Trailing data was found while parsing DER-encoded input for the named type.
339#[allow(missing_docs)]
340#[non_exhaustive]
341#[derive(Clone, Copy, Debug, Eq, PartialEq)]
342pub enum DerTypeId {
343    BitString,
344    Bool,
345    Certificate,
346    CertificateExtensions,
347    CertificateTbsCertificate,
348    CertRevocationList,
349    CertRevocationListExtension,
350    CrlDistributionPoint,
351    CommonNameInner,
352    CommonNameOuter,
353    DistributionPointName,
354    Extension,
355    GeneralName,
356    RevocationReason,
357    Signature,
358    SignatureAlgorithm,
359    SignedData,
360    SubjectPublicKeyInfo,
361    Time,
362    TrustAnchorV1,
363    TrustAnchorV1TbsCertificate,
364    U8,
365    RevokedCertificate,
366    RevokedCertificateExtension,
367    RevokedCertEntry,
368    IssuingDistributionPoint,
369}