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
15use core::fmt;
16use core::ops::ControlFlow;
17
18/// An error that occurs during certificate validation or name validation.
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20#[non_exhaustive]
21pub enum Error {
22 /// The encoding of some ASN.1 DER-encoded item is invalid.
23 BadDer,
24
25 /// The encoding of an ASN.1 DER-encoded time is invalid.
26 BadDerTime,
27
28 /// A CA certificate is being used as an end-entity certificate.
29 CaUsedAsEndEntity,
30
31 /// The certificate is expired; i.e. the time it is being validated for is
32 /// later than the certificate's notAfter time.
33 CertExpired,
34
35 /// The certificate is not valid for the name it is being validated for.
36 CertNotValidForName,
37
38 /// The certificate is not valid yet; i.e. the time it is being validated
39 /// for is earlier than the certificate's notBefore time.
40 CertNotValidYet,
41
42 /// The certificate, or one of its issuers, has been revoked.
43 CertRevoked,
44
45 /// An end-entity certificate is being used as a CA certificate.
46 EndEntityUsedAsCa,
47
48 /// An X.509 extension is invalid.
49 ExtensionValueInvalid,
50
51 /// The certificate validity period (notBefore, notAfter) is invalid; e.g.
52 /// the notAfter time is earlier than the notBefore time.
53 InvalidCertValidity,
54
55 /// A CRL number extension was invalid:
56 /// - it was mis-encoded
57 /// - it was negative
58 /// - it was too long
59 InvalidCrlNumber,
60
61 /// A iPAddress name constraint was invalid:
62 /// - it had a sparse network mask (ie, cannot be written in CIDR form).
63 /// - it was too long or short
64 InvalidNetworkMaskConstraint,
65
66 /// A serial number was invalid:
67 /// - it was misencoded
68 /// - it was negative
69 /// - it was too long
70 InvalidSerialNumber,
71
72 /// The CRL signature is invalid for the issuer's public key.
73 InvalidCrlSignatureForPublicKey,
74
75 /// The signature is invalid for the given public key.
76 InvalidSignatureForPublicKey,
77
78 /// A CRL was signed by an issuer that has a KeyUsage bitstring that does not include
79 /// the cRLSign key usage bit.
80 IssuerNotCrlSigner,
81
82 /// A presented or reference DNS identifier was malformed, potentially
83 /// containing invalid characters or invalid labels.
84 MalformedDnsIdentifier,
85
86 /// The certificate extensions are malformed.
87 ///
88 /// In particular, webpki requires the DNS name(s) be in the subjectAltName
89 /// extension as required by the CA/Browser Forum Baseline Requirements
90 /// and as recommended by RFC6125.
91 MalformedExtensions,
92
93 /// A name constraint was malformed, potentially containing invalid characters or
94 /// invalid labels.
95 MalformedNameConstraint,
96
97 /// The maximum number of name constraint comparisons has been reached.
98 MaximumNameConstraintComparisonsExceeded,
99
100 /// The maximum number of internal path building calls has been reached. Path complexity is too great.
101 MaximumPathBuildCallsExceeded,
102
103 /// The path search was terminated because it became too deep.
104 MaximumPathDepthExceeded,
105
106 /// The maximum number of signature checks has been reached. Path complexity is too great.
107 MaximumSignatureChecksExceeded,
108
109 /// The certificate violates one or more name constraints.
110 NameConstraintViolation,
111
112 /// The certificate violates one or more path length constraints.
113 PathLenConstraintViolated,
114
115 /// The certificate is not valid for the Extended Key Usage for which it is
116 /// being validated.
117 RequiredEkuNotFound,
118
119 /// The algorithm in the TBSCertificate "signature" field of a certificate
120 /// does not match the algorithm in the signature of the certificate.
121 SignatureAlgorithmMismatch,
122
123 /// A valid issuer for the certificate could not be found.
124 UnknownIssuer,
125
126 /// The certificate is not a v3 X.509 certificate.
127 ///
128 /// This error may be also reported if the certificate version field
129 /// is malformed.
130 UnsupportedCertVersion,
131
132 /// The certificate contains an unsupported critical extension.
133 UnsupportedCriticalExtension,
134
135 /// The CRL is not a v2 X.509 CRL.
136 ///
137 /// The RFC 5280 web PKI profile mandates only version 2 be used. See section
138 /// 5.1.2.1 for more information.
139 ///
140 /// This error may also be reported if the CRL version field is malformed.
141 UnsupportedCrlVersion,
142
143 /// The CRL is an unsupported "delta" CRL.
144 UnsupportedDeltaCrl,
145
146 /// The CRL contains unsupported "indirect" entries.
147 UnsupportedIndirectCrl,
148
149 /// The revocation reason is not in the set of supported revocation reasons.
150 UnsupportedRevocationReason,
151
152 /// The signature algorithm for a signature over a CRL is not in the set of supported
153 /// signature algorithms given.
154 UnsupportedCrlSignatureAlgorithm,
155
156 /// The signature algorithm for a signature is not in the set of supported
157 /// signature algorithms given.
158 UnsupportedSignatureAlgorithm,
159
160 /// The CRL signature's algorithm does not match the algorithm of the issuer
161 /// public key it is being validated for. This may be because the public key
162 /// algorithm's OID isn't recognized (e.g. DSA), or the public key
163 /// algorithm's parameters don't match the supported parameters for that
164 /// algorithm (e.g. ECC keys for unsupported curves), or the public key
165 /// algorithm and the signature algorithm simply don't match (e.g.
166 /// verifying an RSA signature with an ECC public key).
167 UnsupportedCrlSignatureAlgorithmForPublicKey,
168
169 /// The signature's algorithm does not match the algorithm of the public
170 /// key it is being validated for. This may be because the public key
171 /// algorithm's OID isn't recognized (e.g. DSA), or the public key
172 /// algorithm's parameters don't match the supported parameters for that
173 /// algorithm (e.g. ECC keys for unsupported curves), or the public key
174 /// algorithm and the signature algorithm simply don't match (e.g.
175 /// verifying an RSA signature with an ECC public key).
176 UnsupportedSignatureAlgorithmForPublicKey,
177}
178
179impl Error {
180 // Compare the Error with the new error by rank, returning the higher rank of the two as
181 // the most specific error.
182 pub(crate) fn most_specific(self, new: Error) -> Error {
183 // Assign an error a numeric value ranking it by specificity.
184 if self.rank() >= new.rank() {
185 self
186 } else {
187 new
188 }
189 }
190
191 // Return a numeric indication of how specific the error is, where an error with a higher rank
192 // is considered more useful to an end user than an error with a lower rank. This is used by
193 // Error::most_specific to compare two errors in order to return which is more specific.
194 #[allow(clippy::as_conversions)] // We won't exceed u32 errors.
195 pub(crate) fn rank(&self) -> u32 {
196 match &self {
197 // Errors related to certificate validity
198 Error::CertNotValidYet | Error::CertExpired => 290,
199 Error::CertNotValidForName => 280,
200 Error::CertRevoked => 270,
201 Error::InvalidCrlSignatureForPublicKey | Error::InvalidSignatureForPublicKey => 260,
202 Error::SignatureAlgorithmMismatch => 250,
203 Error::RequiredEkuNotFound => 240,
204 Error::NameConstraintViolation => 230,
205 Error::PathLenConstraintViolated => 220,
206 Error::CaUsedAsEndEntity | Error::EndEntityUsedAsCa => 210,
207 Error::IssuerNotCrlSigner => 200,
208
209 // Errors related to supported features used in an invalid way.
210 Error::InvalidCertValidity => 190,
211 Error::InvalidNetworkMaskConstraint => 180,
212 Error::InvalidSerialNumber => 170,
213 Error::InvalidCrlNumber => 160,
214
215 // Errors related to unsupported features.
216 Error::UnsupportedCrlSignatureAlgorithmForPublicKey
217 | Error::UnsupportedSignatureAlgorithmForPublicKey => 150,
218 Error::UnsupportedCrlSignatureAlgorithm | Error::UnsupportedSignatureAlgorithm => 140,
219 Error::UnsupportedCriticalExtension => 130,
220 Error::UnsupportedCertVersion => 130,
221 Error::UnsupportedCrlVersion => 120,
222 Error::UnsupportedDeltaCrl => 110,
223 Error::UnsupportedIndirectCrl => 100,
224 Error::UnsupportedRevocationReason => 90,
225 // Reserved for webpki 0.102.0+ usages:
226 // Error::UnsupportedRevocationReasonsPartitioning => 80,
227 // Error::UnsupportedCrlIssuingDistributionPoint => 70,
228 Error::MaximumPathDepthExceeded => 61,
229
230 // Errors related to malformed data.
231 Error::MalformedDnsIdentifier => 60,
232 Error::MalformedNameConstraint => 50,
233 Error::MalformedExtensions => 40,
234 Error::ExtensionValueInvalid => 30,
235
236 // Generic DER errors.
237 Error::BadDerTime => 20,
238 Error::BadDer => 10,
239
240 // Special case errors - not subject to ranking.
241 Error::MaximumSignatureChecksExceeded => 0,
242 Error::MaximumPathBuildCallsExceeded => 0,
243 Error::MaximumNameConstraintComparisonsExceeded => 0,
244
245 // Default catch all error - should be renamed in the future.
246 Error::UnknownIssuer => 0,
247 }
248 }
249
250 /// Returns true for errors that should be considered fatal during path building. Errors of
251 /// this class should halt any further path building and be returned immediately.
252 #[inline]
253 pub(crate) fn is_fatal(&self) -> bool {
254 matches!(
255 self,
256 Error::MaximumSignatureChecksExceeded
257 | Error::MaximumPathBuildCallsExceeded
258 | Error::MaximumNameConstraintComparisonsExceeded
259 )
260 }
261}
262
263impl From<Error> for ControlFlow<Error, Error> {
264 fn from(value: Error) -> Self {
265 match value {
266 // If an error is fatal, we've exhausted the potential for continued search.
267 err if err.is_fatal() => Self::Break(err),
268 // Otherwise we've rejected one candidate chain, but may continue to search for others.
269 err => Self::Continue(err),
270 }
271 }
272}
273
274impl fmt::Display for Error {
275 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
276 write!(f, "{:?}", self)
277 }
278}
279
280#[cfg(feature = "std")]
281#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
282impl ::std::error::Error for Error {}
283
284impl From<untrusted::EndOfInput> for Error {
285 fn from(_: untrusted::EndOfInput) -> Self {
286 Error::BadDer
287 }
288}