1 // Copyright 2016 The Chromium Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // ----------------------------
16 // Overview of error design
17 // ----------------------------
18 //
19 // Certificate path building/validation/parsing may emit a sequence of errors
20 // and warnings.
21 //
22 // Each individual error/warning entry (CertError) is comprised of:
23 //
24 //   * A unique identifier.
25 //
26 //     This serves similarly to an error code, and is used to query if a
27 //     particular error/warning occurred.
28 //
29 //   * [optional] A parameters object.
30 //
31 //     Nodes may attach a heap-allocated subclass of CertErrorParams to carry
32 //     extra information that is used when reporting the error. For instance
33 //     a parsing error may describe where in the DER the failure happened, or
34 //     what the unexpected value was.
35 //
36 // A collection of errors is represented by the CertErrors object. This may be
37 // used to group errors that have a common context, such as all the
38 // errors/warnings that apply to a specific certificate.
39 //
40 // Lastly, CertPathErrors composes multiple CertErrors -- one for each
41 // certificate in the verified chain.
42 //
43 // ----------------------------
44 // Defining new errors
45 // ----------------------------
46 //
47 // The error IDs are extensible and do not need to be centrally defined.
48 //
49 // To define a new error use the macro DEFINE_CERT_ERROR_ID() in a .cc file.
50 // If consumers are to be able to query for this error then the symbol should
51 // also be exposed in a header file.
52 //
53 // Error IDs are in truth string literals, whose pointer value will be unique
54 // per process.
55 
56 #ifndef BSSL_PKI_CERT_ERRORS_H_
57 #define BSSL_PKI_CERT_ERRORS_H_
58 
59 #include <memory>
60 #include <vector>
61 
62 #include <openssl/base.h>
63 
64 #include "cert_error_id.h"
65 #include "parsed_certificate.h"
66 
67 BSSL_NAMESPACE_BEGIN
68 
69 class CertErrorParams;
70 class CertPathErrors;
71 
72 // CertError represents either an error or a warning.
73 struct OPENSSL_EXPORT CertError {
74   enum Severity {
75     SEVERITY_HIGH,
76     SEVERITY_WARNING,
77   };
78 
79   CertError();
80   CertError(Severity severity, CertErrorId id,
81             std::unique_ptr<CertErrorParams> params);
82   CertError(CertError &&other);
83   CertError &operator=(CertError &&);
84   ~CertError();
85 
86   // Pretty-prints the error and its parameters.
87   std::string ToDebugString() const;
88 
89   Severity severity;
90   CertErrorId id;
91   std::unique_ptr<CertErrorParams> params;
92 };
93 
94 // CertErrors is a collection of CertError, along with convenience methods to
95 // add and inspect errors.
96 class OPENSSL_EXPORT CertErrors {
97  public:
98   CertErrors();
99   CertErrors(CertErrors &&other);
100   CertErrors &operator=(CertErrors &&);
101   ~CertErrors();
102 
103   // Adds an error/warning. |params| may be null.
104   void Add(CertError::Severity severity, CertErrorId id,
105            std::unique_ptr<CertErrorParams> params);
106 
107   // Adds a high severity error.
108   void AddError(CertErrorId id, std::unique_ptr<CertErrorParams> params);
109   void AddError(CertErrorId id);
110 
111   // Adds a low severity error.
112   void AddWarning(CertErrorId id, std::unique_ptr<CertErrorParams> params);
113   void AddWarning(CertErrorId id);
114 
115   // Dumps a textual representation of the errors for debugging purposes.
116   std::string ToDebugString() const;
117 
118   // Returns true if the error |id| was added to this CertErrors at
119   // severity |severity|
120   bool ContainsErrorWithSeverity(CertErrorId id,
121                                  CertError::Severity severity) const;
122 
123   // Returns true if the error |id| was added to this CertErrors at
124   // high serverity.
125   bool ContainsError(CertErrorId id) const;
126 
127   // Returns true if this contains any errors of the given severity level.
128   bool ContainsAnyErrorWithSeverity(CertError::Severity severity) const;
129 
130  private:
131  friend CertPathErrors;
132   std::vector<CertError> nodes_;
133 };
134 
135 // CertPathErrors is a collection of CertErrors, to group errors into different
136 // buckets for different certificates. The "index" should correspond with that
137 // of the certificate relative to its chain.
138 class OPENSSL_EXPORT CertPathErrors {
139  public:
140   CertPathErrors();
141   CertPathErrors(CertPathErrors &&other);
142   CertPathErrors &operator=(CertPathErrors &&);
143   ~CertPathErrors();
144 
145   // Gets a bucket to put errors in for |cert_index|. This will lookup and
146   // return the existing error bucket if one exists, or create a new one for the
147   // specified index. It is expected that |cert_index| is the corresponding
148   // index in a certificate chain (with 0 being the target).
149   CertErrors *GetErrorsForCert(size_t cert_index);
150 
151   // Const version of the above, with the difference that if there is no
152   // existing bucket for |cert_index| returns nullptr rather than lazyily
153   // creating one.
154   const CertErrors *GetErrorsForCert(size_t cert_index) const;
155 
156   // Returns a bucket to put errors that are not associated with a particular
157   // certificate.
158   CertErrors *GetOtherErrors();
159   const CertErrors *GetOtherErrors() const;
160 
161   // Returns true if CertPathErrors contains the specified error (of any
162   // severity).
163   bool ContainsError(CertErrorId id) const;
164 
165   // Returns true if this contains any errors of the given severity level.
166   bool ContainsAnyErrorWithSeverity(CertError::Severity severity) const;
167 
168   // If the path contains only one unique high severity error, return the
169   // error id and sets |out_depth| to the depth at which the error was
170   // first seen. A depth of -1 means the error is not associated with
171   // a single certificate of the path.
172   std::optional<CertErrorId> FindSingleHighSeverityError(
173       ptrdiff_t &out_depth) const;
174 
175   // Shortcut for ContainsAnyErrorWithSeverity(CertError::SEVERITY_HIGH).
ContainsHighSeverityErrors()176   bool ContainsHighSeverityErrors() const {
177     return ContainsAnyErrorWithSeverity(CertError::SEVERITY_HIGH);
178   }
179 
180   // Pretty-prints all the errors in the CertPathErrors. If there were no
181   // errors/warnings, returns an empty string.
182   std::string ToDebugString(const ParsedCertificateList &certs) const;
183 
184  private:
185   std::vector<CertErrors> cert_errors_;
186   CertErrors other_errors_;
187 };
188 
189 BSSL_NAMESPACE_END
190 
191 #endif  // BSSL_PKI_CERT_ERRORS_H_
192