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 | use core::fmt; |
16 | use 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 ] |
21 | pub 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 | |
195 | impl 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 | |
279 | impl 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 | |
290 | impl 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" )] |
297 | impl ::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)] |
303 | pub 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 | |