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