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 /// Trailing data was found while parsing DER-encoded input for the named type.
124 TrailingData(DerTypeId),
125
126 /// A valid issuer for the certificate could not be found.
127 UnknownIssuer,
128
129 /// The certificate's revocation status could not be determined.
130 UnknownRevocationStatus,
131
132 /// The certificate is not a v3 X.509 certificate.
133 ///
134 /// This error may be also reported if the certificate version field
135 /// is malformed.
136 UnsupportedCertVersion,
137
138 /// The certificate contains an unsupported critical extension.
139 UnsupportedCriticalExtension,
140
141 /// The CRL contains an issuing distribution point with no distribution point name,
142 /// or a distribution point name relative to an issuer.
143 UnsupportedCrlIssuingDistributionPoint,
144
145 /// The CRL is not a v2 X.509 CRL.
146 ///
147 /// The RFC 5280 web PKI profile mandates only version 2 be used. See section
148 /// 5.1.2.1 for more information.
149 ///
150 /// This error may also be reported if the CRL version field is malformed.
151 UnsupportedCrlVersion,
152
153 /// The CRL is an unsupported "delta" CRL.
154 UnsupportedDeltaCrl,
155
156 /// The CRL contains unsupported "indirect" entries.
157 UnsupportedIndirectCrl,
158
159 /// The `ServerName` contained an unsupported type of value.
160 UnsupportedNameType,
161
162 /// The revocation reason is not in the set of supported revocation reasons.
163 UnsupportedRevocationReason,
164
165 /// The CRL is partitioned by revocation reasons.
166 UnsupportedRevocationReasonsPartitioning,
167
168 /// The signature algorithm for a signature over a CRL is not in the set of supported
169 /// signature algorithms given.
170 UnsupportedCrlSignatureAlgorithm,
171
172 /// The signature algorithm for a signature is not in the set of supported
173 /// signature algorithms given.
174 UnsupportedSignatureAlgorithm,
175
176 /// The CRL signature's algorithm does not match the algorithm of the issuer
177 /// public key it is being validated for. This may be because the public key
178 /// algorithm's OID isn't recognized (e.g. DSA), or the public key
179 /// algorithm's parameters don't match the supported parameters for that
180 /// algorithm (e.g. ECC keys for unsupported curves), or the public key
181 /// algorithm and the signature algorithm simply don't match (e.g.
182 /// verifying an RSA signature with an ECC public key).
183 UnsupportedCrlSignatureAlgorithmForPublicKey,
184
185 /// The signature's algorithm does not match the algorithm of the public
186 /// key it is being validated for. This may be because the public key
187 /// algorithm's OID isn't recognized (e.g. DSA), or the public key
188 /// algorithm's parameters don't match the supported parameters for that
189 /// algorithm (e.g. ECC keys for unsupported curves), or the public key
190 /// algorithm and the signature algorithm simply don't match (e.g.
191 /// verifying an RSA signature with an ECC public key).
192 UnsupportedSignatureAlgorithmForPublicKey,
193}
194
195impl Error {
196 // Compare the Error with the new error by rank, returning the higher rank of the two as
197 // the most specific error.
198 pub(crate) fn most_specific(self, new: Error) -> Error {
199 // Assign an error a numeric value ranking it by specificity.
200 if self.rank() >= new.rank() {
201 self
202 } else {
203 new
204 }
205 }
206
207 // Return a numeric indication of how specific the error is, where an error with a higher rank
208 // is considered more useful to an end user than an error with a lower rank. This is used by
209 // Error::most_specific to compare two errors in order to return which is more specific.
210 #[allow(clippy::as_conversions)] // We won't exceed u32 errors.
211 pub(crate) fn rank(&self) -> u32 {
212 match &self {
213 // Errors related to certificate validity
214 Error::CertNotValidYet | Error::CertExpired => 290,
215 Error::CertNotValidForName => 280,
216 Error::CertRevoked | Error::UnknownRevocationStatus => 270,
217 Error::InvalidCrlSignatureForPublicKey | Error::InvalidSignatureForPublicKey => 260,
218 Error::SignatureAlgorithmMismatch => 250,
219 Error::RequiredEkuNotFound => 240,
220 Error::NameConstraintViolation => 230,
221 Error::PathLenConstraintViolated => 220,
222 Error::CaUsedAsEndEntity | Error::EndEntityUsedAsCa => 210,
223 Error::IssuerNotCrlSigner => 200,
224
225 // Errors related to supported features used in an invalid way.
226 Error::InvalidCertValidity => 190,
227 Error::InvalidNetworkMaskConstraint => 180,
228 Error::InvalidSerialNumber => 170,
229 Error::InvalidCrlNumber => 160,
230
231 // Errors related to unsupported features.
232 Error::UnsupportedCrlSignatureAlgorithmForPublicKey
233 | Error::UnsupportedSignatureAlgorithmForPublicKey => 150,
234 Error::UnsupportedCrlSignatureAlgorithm | Error::UnsupportedSignatureAlgorithm => 140,
235 Error::UnsupportedCriticalExtension => 130,
236 Error::UnsupportedCertVersion => 130,
237 Error::UnsupportedCrlVersion => 120,
238 Error::UnsupportedDeltaCrl => 110,
239 Error::UnsupportedIndirectCrl => 100,
240 Error::UnsupportedNameType => 95,
241 Error::UnsupportedRevocationReason => 90,
242 Error::UnsupportedRevocationReasonsPartitioning => 80,
243 Error::UnsupportedCrlIssuingDistributionPoint => 70,
244 Error::MaximumPathDepthExceeded => 61,
245
246 // Errors related to malformed data.
247 Error::MalformedDnsIdentifier => 60,
248 Error::MalformedNameConstraint => 50,
249 Error::MalformedExtensions | Error::TrailingData(_) => 40,
250 Error::ExtensionValueInvalid => 30,
251
252 // Generic DER errors.
253 Error::BadDerTime => 20,
254 Error::BadDer => 10,
255
256 // Special case errors - not subject to ranking.
257 Error::MaximumSignatureChecksExceeded => 0,
258 Error::MaximumPathBuildCallsExceeded => 0,
259 Error::MaximumNameConstraintComparisonsExceeded => 0,
260
261 // Default catch all error - should be renamed in the future.
262 Error::UnknownIssuer => 0,
263 }
264 }
265
266 /// Returns true for errors that should be considered fatal during path building. Errors of
267 /// this class should halt any further path building and be returned immediately.
268 #[inline]
269 pub(crate) fn is_fatal(&self) -> bool {
270 matches!(
271 self,
272 Error::MaximumSignatureChecksExceeded
273 | Error::MaximumPathBuildCallsExceeded
274 | Error::MaximumNameConstraintComparisonsExceeded
275 )
276 }
277}
278
279impl From<Error> for ControlFlow<Error, Error> {
280 fn from(value: Error) -> Self {
281 match value {
282 // If an error is fatal, we've exhausted the potential for continued search.
283 err: Error if err.is_fatal() => Self::Break(err),
284 // Otherwise we've rejected one candidate chain, but may continue to search for others.
285 err: Error => Self::Continue(err),
286 }
287 }
288}
289
290impl fmt::Display for Error {
291 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
292 write!(f, "{:?}", self)
293 }
294}
295
296#[cfg(feature = "std")]
297impl ::std::error::Error for Error {}
298
299/// Trailing data was found while parsing DER-encoded input for the named type.
300#[allow(missing_docs)]
301#[non_exhaustive]
302#[derive(Clone, Copy, Debug, Eq, PartialEq)]
303pub enum DerTypeId {
304 BitString,
305 Bool,
306 Certificate,
307 CertificateExtensions,
308 CertificateTbsCertificate,
309 CertRevocationList,
310 CertRevocationListExtension,
311 CrlDistributionPoint,
312 CommonNameInner,
313 CommonNameOuter,
314 DistributionPointName,
315 Extension,
316 GeneralName,
317 RevocationReason,
318 Signature,
319 SignatureAlgorithm,
320 SignedData,
321 SubjectPublicKeyInfo,
322 Time,
323 TrustAnchorV1,
324 TrustAnchorV1TbsCertificate,
325 U8,
326 RevokedCertificate,
327 RevokedCertificateExtension,
328 RevokedCertEntry,
329 IssuingDistributionPoint,
330}
331