Skip to main content

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