xref: /aosp_15_r20/external/cronet/net/cert/internal/revocation_checker.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2017 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker #include "net/cert/internal/revocation_checker.h"
6*6777b538SAndroid Build Coastguard Worker 
7*6777b538SAndroid Build Coastguard Worker #include <optional>
8*6777b538SAndroid Build Coastguard Worker #include <string>
9*6777b538SAndroid Build Coastguard Worker #include <string_view>
10*6777b538SAndroid Build Coastguard Worker 
11*6777b538SAndroid Build Coastguard Worker #include "base/logging.h"
12*6777b538SAndroid Build Coastguard Worker #include "crypto/sha2.h"
13*6777b538SAndroid Build Coastguard Worker #include "net/cert/cert_net_fetcher.h"
14*6777b538SAndroid Build Coastguard Worker #include "third_party/boringssl/src/pki/common_cert_errors.h"
15*6777b538SAndroid Build Coastguard Worker #include "third_party/boringssl/src/pki/crl.h"
16*6777b538SAndroid Build Coastguard Worker #include "third_party/boringssl/src/pki/ocsp.h"
17*6777b538SAndroid Build Coastguard Worker #include "third_party/boringssl/src/pki/ocsp_verify_result.h"
18*6777b538SAndroid Build Coastguard Worker #include "third_party/boringssl/src/pki/parsed_certificate.h"
19*6777b538SAndroid Build Coastguard Worker #include "third_party/boringssl/src/pki/trust_store.h"
20*6777b538SAndroid Build Coastguard Worker #include "url/gurl.h"
21*6777b538SAndroid Build Coastguard Worker 
22*6777b538SAndroid Build Coastguard Worker namespace net {
23*6777b538SAndroid Build Coastguard Worker 
24*6777b538SAndroid Build Coastguard Worker namespace {
25*6777b538SAndroid Build Coastguard Worker 
MarkCertificateRevoked(bssl::CertErrors * errors)26*6777b538SAndroid Build Coastguard Worker void MarkCertificateRevoked(bssl::CertErrors* errors) {
27*6777b538SAndroid Build Coastguard Worker   // TODO(eroman): Add a parameter to the error indicating which mechanism
28*6777b538SAndroid Build Coastguard Worker   // caused the revocation (i.e. CRLSet, OCSP, stapled OCSP, etc).
29*6777b538SAndroid Build Coastguard Worker   errors->AddError(bssl::cert_errors::kCertificateRevoked);
30*6777b538SAndroid Build Coastguard Worker }
31*6777b538SAndroid Build Coastguard Worker 
32*6777b538SAndroid Build Coastguard Worker // Checks the revocation status of |certs[target_cert_index]| according to
33*6777b538SAndroid Build Coastguard Worker // |policy|. If the checks failed, returns false and adds errors to
34*6777b538SAndroid Build Coastguard Worker // |cert_errors|.
35*6777b538SAndroid Build Coastguard Worker //
36*6777b538SAndroid Build Coastguard Worker // TODO(eroman): Make the verification time an input.
CheckCertRevocation(const bssl::ParsedCertificateList & certs,size_t target_cert_index,const RevocationPolicy & policy,base::TimeTicks deadline,std::string_view stapled_ocsp_response,std::optional<int64_t> max_age_seconds,CertNetFetcher * net_fetcher,bssl::CertErrors * cert_errors,bssl::OCSPVerifyResult * stapled_ocsp_verify_result)37*6777b538SAndroid Build Coastguard Worker bool CheckCertRevocation(const bssl::ParsedCertificateList& certs,
38*6777b538SAndroid Build Coastguard Worker                          size_t target_cert_index,
39*6777b538SAndroid Build Coastguard Worker                          const RevocationPolicy& policy,
40*6777b538SAndroid Build Coastguard Worker                          base::TimeTicks deadline,
41*6777b538SAndroid Build Coastguard Worker                          std::string_view stapled_ocsp_response,
42*6777b538SAndroid Build Coastguard Worker                          std::optional<int64_t> max_age_seconds,
43*6777b538SAndroid Build Coastguard Worker                          CertNetFetcher* net_fetcher,
44*6777b538SAndroid Build Coastguard Worker                          bssl::CertErrors* cert_errors,
45*6777b538SAndroid Build Coastguard Worker                          bssl::OCSPVerifyResult* stapled_ocsp_verify_result) {
46*6777b538SAndroid Build Coastguard Worker   DCHECK_LT(target_cert_index, certs.size());
47*6777b538SAndroid Build Coastguard Worker   const bssl::ParsedCertificate* cert = certs[target_cert_index].get();
48*6777b538SAndroid Build Coastguard Worker   const bssl::ParsedCertificate* issuer_cert =
49*6777b538SAndroid Build Coastguard Worker       target_cert_index + 1 < certs.size() ? certs[target_cert_index + 1].get()
50*6777b538SAndroid Build Coastguard Worker                                            : nullptr;
51*6777b538SAndroid Build Coastguard Worker 
52*6777b538SAndroid Build Coastguard Worker   // Check using stapled OCSP, if available.
53*6777b538SAndroid Build Coastguard Worker   if (!stapled_ocsp_response.empty() && issuer_cert) {
54*6777b538SAndroid Build Coastguard Worker     bssl::OCSPVerifyResult::ResponseStatus response_details;
55*6777b538SAndroid Build Coastguard Worker     bssl::OCSPRevocationStatus ocsp_status = bssl::CheckOCSP(
56*6777b538SAndroid Build Coastguard Worker         stapled_ocsp_response, cert, issuer_cert, base::Time::Now().ToTimeT(),
57*6777b538SAndroid Build Coastguard Worker         max_age_seconds, &response_details);
58*6777b538SAndroid Build Coastguard Worker     if (stapled_ocsp_verify_result) {
59*6777b538SAndroid Build Coastguard Worker       stapled_ocsp_verify_result->response_status = response_details;
60*6777b538SAndroid Build Coastguard Worker       stapled_ocsp_verify_result->revocation_status = ocsp_status;
61*6777b538SAndroid Build Coastguard Worker     }
62*6777b538SAndroid Build Coastguard Worker 
63*6777b538SAndroid Build Coastguard Worker     // TODO(eroman): Save the stapled OCSP response to cache.
64*6777b538SAndroid Build Coastguard Worker     switch (ocsp_status) {
65*6777b538SAndroid Build Coastguard Worker       case bssl::OCSPRevocationStatus::REVOKED:
66*6777b538SAndroid Build Coastguard Worker         MarkCertificateRevoked(cert_errors);
67*6777b538SAndroid Build Coastguard Worker         return false;
68*6777b538SAndroid Build Coastguard Worker       case bssl::OCSPRevocationStatus::GOOD:
69*6777b538SAndroid Build Coastguard Worker         return true;
70*6777b538SAndroid Build Coastguard Worker       case bssl::OCSPRevocationStatus::UNKNOWN:
71*6777b538SAndroid Build Coastguard Worker         // TODO(eroman): If the OCSP response was invalid, should we keep
72*6777b538SAndroid Build Coastguard Worker         //               looking or fail?
73*6777b538SAndroid Build Coastguard Worker         break;
74*6777b538SAndroid Build Coastguard Worker     }
75*6777b538SAndroid Build Coastguard Worker   }
76*6777b538SAndroid Build Coastguard Worker 
77*6777b538SAndroid Build Coastguard Worker   if (!policy.check_revocation) {
78*6777b538SAndroid Build Coastguard Worker     // TODO(eroman): Should still check CRL/OCSP caches.
79*6777b538SAndroid Build Coastguard Worker     return true;
80*6777b538SAndroid Build Coastguard Worker   }
81*6777b538SAndroid Build Coastguard Worker 
82*6777b538SAndroid Build Coastguard Worker   bool found_revocation_info = false;
83*6777b538SAndroid Build Coastguard Worker 
84*6777b538SAndroid Build Coastguard Worker   // Check OCSP.
85*6777b538SAndroid Build Coastguard Worker   if (cert->has_authority_info_access()) {
86*6777b538SAndroid Build Coastguard Worker     // Try each of the OCSP URIs
87*6777b538SAndroid Build Coastguard Worker     for (const auto& ocsp_uri : cert->ocsp_uris()) {
88*6777b538SAndroid Build Coastguard Worker       // Only consider http:// URLs (https:// could create a circular
89*6777b538SAndroid Build Coastguard Worker       // dependency).
90*6777b538SAndroid Build Coastguard Worker       GURL parsed_ocsp_url(ocsp_uri);
91*6777b538SAndroid Build Coastguard Worker       if (!parsed_ocsp_url.is_valid() ||
92*6777b538SAndroid Build Coastguard Worker           !parsed_ocsp_url.SchemeIs(url::kHttpScheme)) {
93*6777b538SAndroid Build Coastguard Worker         continue;
94*6777b538SAndroid Build Coastguard Worker       }
95*6777b538SAndroid Build Coastguard Worker 
96*6777b538SAndroid Build Coastguard Worker       found_revocation_info = true;
97*6777b538SAndroid Build Coastguard Worker 
98*6777b538SAndroid Build Coastguard Worker       // Check the deadline after setting found_revocation_info, to not give a
99*6777b538SAndroid Build Coastguard Worker       // misleading kNoRevocationMechanism failure.
100*6777b538SAndroid Build Coastguard Worker       if (!deadline.is_null() && base::TimeTicks::Now() > deadline)
101*6777b538SAndroid Build Coastguard Worker         break;
102*6777b538SAndroid Build Coastguard Worker 
103*6777b538SAndroid Build Coastguard Worker       if (!policy.networking_allowed)
104*6777b538SAndroid Build Coastguard Worker         continue;
105*6777b538SAndroid Build Coastguard Worker 
106*6777b538SAndroid Build Coastguard Worker       if (!net_fetcher) {
107*6777b538SAndroid Build Coastguard Worker         LOG(ERROR) << "Cannot fetch OCSP as didn't specify a |net_fetcher|";
108*6777b538SAndroid Build Coastguard Worker         continue;
109*6777b538SAndroid Build Coastguard Worker       }
110*6777b538SAndroid Build Coastguard Worker 
111*6777b538SAndroid Build Coastguard Worker       // TODO(eroman): Duplication of work if there are multiple URLs to try.
112*6777b538SAndroid Build Coastguard Worker       // TODO(eroman): Are there cases where we would need to POST instead?
113*6777b538SAndroid Build Coastguard Worker       std::optional<std::string> get_url_str =
114*6777b538SAndroid Build Coastguard Worker           CreateOCSPGetURL(cert, issuer_cert, ocsp_uri);
115*6777b538SAndroid Build Coastguard Worker       if (!get_url_str.has_value()) {
116*6777b538SAndroid Build Coastguard Worker         // An unexpected failure from BoringSSL, or the input was too large to
117*6777b538SAndroid Build Coastguard Worker         // base64-encode.
118*6777b538SAndroid Build Coastguard Worker         continue;
119*6777b538SAndroid Build Coastguard Worker       }
120*6777b538SAndroid Build Coastguard Worker       GURL get_url(get_url_str.value());
121*6777b538SAndroid Build Coastguard Worker       if (!get_url.is_valid()) {
122*6777b538SAndroid Build Coastguard Worker         // Invalid URL.
123*6777b538SAndroid Build Coastguard Worker         continue;
124*6777b538SAndroid Build Coastguard Worker       }
125*6777b538SAndroid Build Coastguard Worker 
126*6777b538SAndroid Build Coastguard Worker       // Fetch it over network.
127*6777b538SAndroid Build Coastguard Worker       //
128*6777b538SAndroid Build Coastguard Worker       // TODO(eroman): Issue POST instead of GET if request is larger than 255
129*6777b538SAndroid Build Coastguard Worker       //               bytes?
130*6777b538SAndroid Build Coastguard Worker       // TODO(eroman): Improve interplay with HTTP cache.
131*6777b538SAndroid Build Coastguard Worker       std::unique_ptr<CertNetFetcher::Request> net_ocsp_request =
132*6777b538SAndroid Build Coastguard Worker           net_fetcher->FetchOcsp(get_url, CertNetFetcher::DEFAULT,
133*6777b538SAndroid Build Coastguard Worker                                  CertNetFetcher::DEFAULT);
134*6777b538SAndroid Build Coastguard Worker 
135*6777b538SAndroid Build Coastguard Worker       Error net_error;
136*6777b538SAndroid Build Coastguard Worker       std::vector<uint8_t> ocsp_response_bytes;
137*6777b538SAndroid Build Coastguard Worker       net_ocsp_request->WaitForResult(&net_error, &ocsp_response_bytes);
138*6777b538SAndroid Build Coastguard Worker 
139*6777b538SAndroid Build Coastguard Worker       if (net_error != OK)
140*6777b538SAndroid Build Coastguard Worker         continue;
141*6777b538SAndroid Build Coastguard Worker 
142*6777b538SAndroid Build Coastguard Worker       bssl::OCSPVerifyResult::ResponseStatus response_details;
143*6777b538SAndroid Build Coastguard Worker 
144*6777b538SAndroid Build Coastguard Worker       bssl::OCSPRevocationStatus ocsp_status = bssl::CheckOCSP(
145*6777b538SAndroid Build Coastguard Worker           std::string_view(
146*6777b538SAndroid Build Coastguard Worker               reinterpret_cast<const char*>(ocsp_response_bytes.data()),
147*6777b538SAndroid Build Coastguard Worker               ocsp_response_bytes.size()),
148*6777b538SAndroid Build Coastguard Worker           cert, issuer_cert, base::Time::Now().ToTimeT(), max_age_seconds,
149*6777b538SAndroid Build Coastguard Worker           &response_details);
150*6777b538SAndroid Build Coastguard Worker 
151*6777b538SAndroid Build Coastguard Worker       switch (ocsp_status) {
152*6777b538SAndroid Build Coastguard Worker         case bssl::OCSPRevocationStatus::REVOKED:
153*6777b538SAndroid Build Coastguard Worker           MarkCertificateRevoked(cert_errors);
154*6777b538SAndroid Build Coastguard Worker           return false;
155*6777b538SAndroid Build Coastguard Worker         case bssl::OCSPRevocationStatus::GOOD:
156*6777b538SAndroid Build Coastguard Worker           return true;
157*6777b538SAndroid Build Coastguard Worker         case bssl::OCSPRevocationStatus::UNKNOWN:
158*6777b538SAndroid Build Coastguard Worker           break;
159*6777b538SAndroid Build Coastguard Worker       }
160*6777b538SAndroid Build Coastguard Worker     }
161*6777b538SAndroid Build Coastguard Worker   }
162*6777b538SAndroid Build Coastguard Worker 
163*6777b538SAndroid Build Coastguard Worker   // Check CRLs.
164*6777b538SAndroid Build Coastguard Worker   bssl::ParsedExtension crl_dp_extension;
165*6777b538SAndroid Build Coastguard Worker   if (policy.crl_allowed &&
166*6777b538SAndroid Build Coastguard Worker       cert->GetExtension(bssl::der::Input(bssl::kCrlDistributionPointsOid),
167*6777b538SAndroid Build Coastguard Worker                          &crl_dp_extension)) {
168*6777b538SAndroid Build Coastguard Worker     std::vector<bssl::ParsedDistributionPoint> distribution_points;
169*6777b538SAndroid Build Coastguard Worker     if (ParseCrlDistributionPoints(crl_dp_extension.value,
170*6777b538SAndroid Build Coastguard Worker                                    &distribution_points)) {
171*6777b538SAndroid Build Coastguard Worker       for (const auto& distribution_point : distribution_points) {
172*6777b538SAndroid Build Coastguard Worker         if (distribution_point.crl_issuer) {
173*6777b538SAndroid Build Coastguard Worker           // Ignore indirect CRLs (CRL where CRLissuer != cert issuer), which
174*6777b538SAndroid Build Coastguard Worker           // are optional according to RFC 5280's profile.
175*6777b538SAndroid Build Coastguard Worker           continue;
176*6777b538SAndroid Build Coastguard Worker         }
177*6777b538SAndroid Build Coastguard Worker 
178*6777b538SAndroid Build Coastguard Worker         if (distribution_point.reasons) {
179*6777b538SAndroid Build Coastguard Worker           // Ignore CRLs that only contain some reasons. RFC 5280's profile
180*6777b538SAndroid Build Coastguard Worker           // requires that conforming CAs "MUST include at least one
181*6777b538SAndroid Build Coastguard Worker           // DistributionPoint that points to a CRL that covers the certificate
182*6777b538SAndroid Build Coastguard Worker           // for all reasons".
183*6777b538SAndroid Build Coastguard Worker           continue;
184*6777b538SAndroid Build Coastguard Worker         }
185*6777b538SAndroid Build Coastguard Worker 
186*6777b538SAndroid Build Coastguard Worker         if (!distribution_point.distribution_point_fullname) {
187*6777b538SAndroid Build Coastguard Worker           // Only distributionPoints with a fullName containing URIs are
188*6777b538SAndroid Build Coastguard Worker           // supported.
189*6777b538SAndroid Build Coastguard Worker           continue;
190*6777b538SAndroid Build Coastguard Worker         }
191*6777b538SAndroid Build Coastguard Worker 
192*6777b538SAndroid Build Coastguard Worker         for (const auto& crl_uri :
193*6777b538SAndroid Build Coastguard Worker              distribution_point.distribution_point_fullname
194*6777b538SAndroid Build Coastguard Worker                  ->uniform_resource_identifiers) {
195*6777b538SAndroid Build Coastguard Worker           // Only consider http:// URLs (https:// could create a circular
196*6777b538SAndroid Build Coastguard Worker           // dependency).
197*6777b538SAndroid Build Coastguard Worker           GURL parsed_crl_url(crl_uri);
198*6777b538SAndroid Build Coastguard Worker           if (!parsed_crl_url.is_valid() ||
199*6777b538SAndroid Build Coastguard Worker               !parsed_crl_url.SchemeIs(url::kHttpScheme)) {
200*6777b538SAndroid Build Coastguard Worker             continue;
201*6777b538SAndroid Build Coastguard Worker           }
202*6777b538SAndroid Build Coastguard Worker 
203*6777b538SAndroid Build Coastguard Worker           found_revocation_info = true;
204*6777b538SAndroid Build Coastguard Worker 
205*6777b538SAndroid Build Coastguard Worker           // Check the deadline after setting found_revocation_info, to not give
206*6777b538SAndroid Build Coastguard Worker           // a misleading kNoRevocationMechanism failure.
207*6777b538SAndroid Build Coastguard Worker           if (!deadline.is_null() && base::TimeTicks::Now() > deadline)
208*6777b538SAndroid Build Coastguard Worker             break;
209*6777b538SAndroid Build Coastguard Worker 
210*6777b538SAndroid Build Coastguard Worker           if (!policy.networking_allowed)
211*6777b538SAndroid Build Coastguard Worker             continue;
212*6777b538SAndroid Build Coastguard Worker 
213*6777b538SAndroid Build Coastguard Worker           if (!net_fetcher) {
214*6777b538SAndroid Build Coastguard Worker             LOG(ERROR) << "Cannot fetch CRL as didn't specify a |net_fetcher|";
215*6777b538SAndroid Build Coastguard Worker             continue;
216*6777b538SAndroid Build Coastguard Worker           }
217*6777b538SAndroid Build Coastguard Worker 
218*6777b538SAndroid Build Coastguard Worker           // Fetch it over network.
219*6777b538SAndroid Build Coastguard Worker           //
220*6777b538SAndroid Build Coastguard Worker           // Note that no attempt is made to refetch without cache if a cached
221*6777b538SAndroid Build Coastguard Worker           // CRL is too old, nor is there a separate CRL cache. It is assumed
222*6777b538SAndroid Build Coastguard Worker           // the CRL server will send reasonable HTTP caching headers.
223*6777b538SAndroid Build Coastguard Worker           std::unique_ptr<CertNetFetcher::Request> net_crl_request =
224*6777b538SAndroid Build Coastguard Worker               net_fetcher->FetchCrl(parsed_crl_url, CertNetFetcher::DEFAULT,
225*6777b538SAndroid Build Coastguard Worker                                     CertNetFetcher::DEFAULT);
226*6777b538SAndroid Build Coastguard Worker 
227*6777b538SAndroid Build Coastguard Worker           Error net_error;
228*6777b538SAndroid Build Coastguard Worker           std::vector<uint8_t> crl_response_bytes;
229*6777b538SAndroid Build Coastguard Worker           net_crl_request->WaitForResult(&net_error, &crl_response_bytes);
230*6777b538SAndroid Build Coastguard Worker 
231*6777b538SAndroid Build Coastguard Worker           if (net_error != OK)
232*6777b538SAndroid Build Coastguard Worker             continue;
233*6777b538SAndroid Build Coastguard Worker 
234*6777b538SAndroid Build Coastguard Worker           bssl::CRLRevocationStatus crl_status = CheckCRL(
235*6777b538SAndroid Build Coastguard Worker               std::string_view(
236*6777b538SAndroid Build Coastguard Worker                   reinterpret_cast<const char*>(crl_response_bytes.data()),
237*6777b538SAndroid Build Coastguard Worker                   crl_response_bytes.size()),
238*6777b538SAndroid Build Coastguard Worker               certs, target_cert_index, distribution_point,
239*6777b538SAndroid Build Coastguard Worker               base::Time::Now().ToTimeT(), max_age_seconds);
240*6777b538SAndroid Build Coastguard Worker 
241*6777b538SAndroid Build Coastguard Worker           switch (crl_status) {
242*6777b538SAndroid Build Coastguard Worker             case bssl::CRLRevocationStatus::REVOKED:
243*6777b538SAndroid Build Coastguard Worker               MarkCertificateRevoked(cert_errors);
244*6777b538SAndroid Build Coastguard Worker               return false;
245*6777b538SAndroid Build Coastguard Worker             case bssl::CRLRevocationStatus::GOOD:
246*6777b538SAndroid Build Coastguard Worker               return true;
247*6777b538SAndroid Build Coastguard Worker             case bssl::CRLRevocationStatus::UNKNOWN:
248*6777b538SAndroid Build Coastguard Worker               break;
249*6777b538SAndroid Build Coastguard Worker           }
250*6777b538SAndroid Build Coastguard Worker         }
251*6777b538SAndroid Build Coastguard Worker       }
252*6777b538SAndroid Build Coastguard Worker     }
253*6777b538SAndroid Build Coastguard Worker   }
254*6777b538SAndroid Build Coastguard Worker 
255*6777b538SAndroid Build Coastguard Worker   // Reaching here means that revocation checking was inconclusive. Determine
256*6777b538SAndroid Build Coastguard Worker   // whether failure to complete revocation checking constitutes an error.
257*6777b538SAndroid Build Coastguard Worker 
258*6777b538SAndroid Build Coastguard Worker   if (!found_revocation_info) {
259*6777b538SAndroid Build Coastguard Worker     if (policy.allow_missing_info) {
260*6777b538SAndroid Build Coastguard Worker       // If the certificate lacked any (recognized) revocation mechanisms, and
261*6777b538SAndroid Build Coastguard Worker       // the policy permits it, consider revocation checking a success.
262*6777b538SAndroid Build Coastguard Worker       return true;
263*6777b538SAndroid Build Coastguard Worker     } else {
264*6777b538SAndroid Build Coastguard Worker       // If the certificate lacked any (recognized) revocation mechanisms, and
265*6777b538SAndroid Build Coastguard Worker       // the policy forbids it, fail revocation checking.
266*6777b538SAndroid Build Coastguard Worker       cert_errors->AddError(bssl::cert_errors::kNoRevocationMechanism);
267*6777b538SAndroid Build Coastguard Worker       return false;
268*6777b538SAndroid Build Coastguard Worker     }
269*6777b538SAndroid Build Coastguard Worker   }
270*6777b538SAndroid Build Coastguard Worker 
271*6777b538SAndroid Build Coastguard Worker   // In soft-fail mode permit other failures.
272*6777b538SAndroid Build Coastguard Worker   // TODO(eroman): Add a warning to |cert_errors| indicating the failure.
273*6777b538SAndroid Build Coastguard Worker   if (policy.allow_unable_to_check)
274*6777b538SAndroid Build Coastguard Worker     return true;
275*6777b538SAndroid Build Coastguard Worker 
276*6777b538SAndroid Build Coastguard Worker   // Otherwise the policy doesn't allow revocation checking to fail.
277*6777b538SAndroid Build Coastguard Worker   cert_errors->AddError(bssl::cert_errors::kUnableToCheckRevocation);
278*6777b538SAndroid Build Coastguard Worker   return false;
279*6777b538SAndroid Build Coastguard Worker }
280*6777b538SAndroid Build Coastguard Worker 
281*6777b538SAndroid Build Coastguard Worker }  // namespace
282*6777b538SAndroid Build Coastguard Worker 
CheckValidatedChainRevocation(const bssl::ParsedCertificateList & certs,const RevocationPolicy & policy,base::TimeTicks deadline,std::string_view stapled_leaf_ocsp_response,CertNetFetcher * net_fetcher,bssl::CertPathErrors * errors,bssl::OCSPVerifyResult * stapled_ocsp_verify_result)283*6777b538SAndroid Build Coastguard Worker void CheckValidatedChainRevocation(
284*6777b538SAndroid Build Coastguard Worker     const bssl::ParsedCertificateList& certs,
285*6777b538SAndroid Build Coastguard Worker     const RevocationPolicy& policy,
286*6777b538SAndroid Build Coastguard Worker     base::TimeTicks deadline,
287*6777b538SAndroid Build Coastguard Worker     std::string_view stapled_leaf_ocsp_response,
288*6777b538SAndroid Build Coastguard Worker     CertNetFetcher* net_fetcher,
289*6777b538SAndroid Build Coastguard Worker     bssl::CertPathErrors* errors,
290*6777b538SAndroid Build Coastguard Worker     bssl::OCSPVerifyResult* stapled_ocsp_verify_result) {
291*6777b538SAndroid Build Coastguard Worker   if (stapled_ocsp_verify_result)
292*6777b538SAndroid Build Coastguard Worker     *stapled_ocsp_verify_result = bssl::OCSPVerifyResult();
293*6777b538SAndroid Build Coastguard Worker 
294*6777b538SAndroid Build Coastguard Worker   // Check each certificate for revocation using OCSP/CRL. Checks proceed
295*6777b538SAndroid Build Coastguard Worker   // from the root certificate towards the leaf certificate. Revocation errors
296*6777b538SAndroid Build Coastguard Worker   // are added to |errors|.
297*6777b538SAndroid Build Coastguard Worker   for (size_t reverse_i = 0; reverse_i < certs.size(); ++reverse_i) {
298*6777b538SAndroid Build Coastguard Worker     size_t i = certs.size() - reverse_i - 1;
299*6777b538SAndroid Build Coastguard Worker 
300*6777b538SAndroid Build Coastguard Worker     // Trust anchors bypass OCSP/CRL revocation checks. (The only way to revoke
301*6777b538SAndroid Build Coastguard Worker     // trust anchors is via CRLSet or the built-in SPKI block list). Since
302*6777b538SAndroid Build Coastguard Worker     // |certs| must be a validated chain, the final cert must be a trust
303*6777b538SAndroid Build Coastguard Worker     // anchor.
304*6777b538SAndroid Build Coastguard Worker     if (reverse_i == 0)
305*6777b538SAndroid Build Coastguard Worker       continue;
306*6777b538SAndroid Build Coastguard Worker 
307*6777b538SAndroid Build Coastguard Worker     // TODO(eroman): Plumb stapled OCSP for non-leaf certificates from TLS?
308*6777b538SAndroid Build Coastguard Worker     std::string_view stapled_ocsp =
309*6777b538SAndroid Build Coastguard Worker         (i == 0) ? stapled_leaf_ocsp_response : std::string_view();
310*6777b538SAndroid Build Coastguard Worker 
311*6777b538SAndroid Build Coastguard Worker     std::optional<int64_t> max_age_seconds;
312*6777b538SAndroid Build Coastguard Worker     if (policy.enforce_baseline_requirements) {
313*6777b538SAndroid Build Coastguard Worker       max_age_seconds = ((i == 0) ? kMaxRevocationLeafUpdateAge
314*6777b538SAndroid Build Coastguard Worker                                   : kMaxRevocationIntermediateUpdateAge)
315*6777b538SAndroid Build Coastguard Worker                             .InSeconds();
316*6777b538SAndroid Build Coastguard Worker     }
317*6777b538SAndroid Build Coastguard Worker 
318*6777b538SAndroid Build Coastguard Worker     // Check whether this certificate's revocation status complies with the
319*6777b538SAndroid Build Coastguard Worker     // policy.
320*6777b538SAndroid Build Coastguard Worker     bool cert_ok = CheckCertRevocation(
321*6777b538SAndroid Build Coastguard Worker         certs, i, policy, deadline, stapled_ocsp, max_age_seconds, net_fetcher,
322*6777b538SAndroid Build Coastguard Worker         errors->GetErrorsForCert(i),
323*6777b538SAndroid Build Coastguard Worker         (i == 0) ? stapled_ocsp_verify_result : nullptr);
324*6777b538SAndroid Build Coastguard Worker 
325*6777b538SAndroid Build Coastguard Worker     if (!cert_ok) {
326*6777b538SAndroid Build Coastguard Worker       // If any certificate in the chain fails revocation checks, the chain is
327*6777b538SAndroid Build Coastguard Worker       // revoked and no need to check revocation status for the remaining
328*6777b538SAndroid Build Coastguard Worker       // certificates.
329*6777b538SAndroid Build Coastguard Worker       DCHECK(errors->GetErrorsForCert(i)->ContainsAnyErrorWithSeverity(
330*6777b538SAndroid Build Coastguard Worker           bssl::CertError::SEVERITY_HIGH));
331*6777b538SAndroid Build Coastguard Worker       break;
332*6777b538SAndroid Build Coastguard Worker     }
333*6777b538SAndroid Build Coastguard Worker   }
334*6777b538SAndroid Build Coastguard Worker }
335*6777b538SAndroid Build Coastguard Worker 
CheckChainRevocationUsingCRLSet(const CRLSet * crl_set,const bssl::ParsedCertificateList & certs,bssl::CertPathErrors * errors)336*6777b538SAndroid Build Coastguard Worker CRLSet::Result CheckChainRevocationUsingCRLSet(
337*6777b538SAndroid Build Coastguard Worker     const CRLSet* crl_set,
338*6777b538SAndroid Build Coastguard Worker     const bssl::ParsedCertificateList& certs,
339*6777b538SAndroid Build Coastguard Worker     bssl::CertPathErrors* errors) {
340*6777b538SAndroid Build Coastguard Worker   // Iterate from the root certificate towards the leaf (the root certificate is
341*6777b538SAndroid Build Coastguard Worker   // also checked for revocation by CRLSet).
342*6777b538SAndroid Build Coastguard Worker   std::string issuer_spki_hash;
343*6777b538SAndroid Build Coastguard Worker   for (size_t reverse_i = 0; reverse_i < certs.size(); ++reverse_i) {
344*6777b538SAndroid Build Coastguard Worker     size_t i = certs.size() - reverse_i - 1;
345*6777b538SAndroid Build Coastguard Worker     const bssl::ParsedCertificate* cert = certs[i].get();
346*6777b538SAndroid Build Coastguard Worker 
347*6777b538SAndroid Build Coastguard Worker     // True if |cert| is the root of the chain.
348*6777b538SAndroid Build Coastguard Worker     const bool is_root = reverse_i == 0;
349*6777b538SAndroid Build Coastguard Worker     // True if |cert| is the leaf certificate of the chain.
350*6777b538SAndroid Build Coastguard Worker     const bool is_target = i == 0;
351*6777b538SAndroid Build Coastguard Worker 
352*6777b538SAndroid Build Coastguard Worker     // Check for revocation using the certificate's SPKI.
353*6777b538SAndroid Build Coastguard Worker     std::string spki_hash =
354*6777b538SAndroid Build Coastguard Worker         crypto::SHA256HashString(cert->tbs().spki_tlv.AsStringView());
355*6777b538SAndroid Build Coastguard Worker     CRLSet::Result result = crl_set->CheckSPKI(spki_hash);
356*6777b538SAndroid Build Coastguard Worker 
357*6777b538SAndroid Build Coastguard Worker     // Check for revocation using the certificate's Subject.
358*6777b538SAndroid Build Coastguard Worker     if (result != CRLSet::REVOKED) {
359*6777b538SAndroid Build Coastguard Worker       result = crl_set->CheckSubject(cert->tbs().subject_tlv.AsStringView(),
360*6777b538SAndroid Build Coastguard Worker                                      spki_hash);
361*6777b538SAndroid Build Coastguard Worker     }
362*6777b538SAndroid Build Coastguard Worker 
363*6777b538SAndroid Build Coastguard Worker     // Check for revocation using the certificate's serial number and issuer's
364*6777b538SAndroid Build Coastguard Worker     // SPKI.
365*6777b538SAndroid Build Coastguard Worker     if (result != CRLSet::REVOKED && !is_root) {
366*6777b538SAndroid Build Coastguard Worker       result = crl_set->CheckSerial(cert->tbs().serial_number.AsStringView(),
367*6777b538SAndroid Build Coastguard Worker                                     issuer_spki_hash);
368*6777b538SAndroid Build Coastguard Worker     }
369*6777b538SAndroid Build Coastguard Worker 
370*6777b538SAndroid Build Coastguard Worker     // Prepare for the next iteration.
371*6777b538SAndroid Build Coastguard Worker     issuer_spki_hash = std::move(spki_hash);
372*6777b538SAndroid Build Coastguard Worker 
373*6777b538SAndroid Build Coastguard Worker     switch (result) {
374*6777b538SAndroid Build Coastguard Worker       case CRLSet::REVOKED:
375*6777b538SAndroid Build Coastguard Worker         MarkCertificateRevoked(errors->GetErrorsForCert(i));
376*6777b538SAndroid Build Coastguard Worker         return CRLSet::Result::REVOKED;
377*6777b538SAndroid Build Coastguard Worker       case CRLSet::UNKNOWN:
378*6777b538SAndroid Build Coastguard Worker         // If the status is unknown, advance to the subordinate certificate.
379*6777b538SAndroid Build Coastguard Worker         break;
380*6777b538SAndroid Build Coastguard Worker       case CRLSet::GOOD:
381*6777b538SAndroid Build Coastguard Worker         if (is_target && !crl_set->IsExpired()) {
382*6777b538SAndroid Build Coastguard Worker           // If the target is covered by the CRLSet and known good, consider
383*6777b538SAndroid Build Coastguard Worker           // the entire chain to be valid (even though the revocation status
384*6777b538SAndroid Build Coastguard Worker           // of the intermediates may have been UNKNOWN).
385*6777b538SAndroid Build Coastguard Worker           //
386*6777b538SAndroid Build Coastguard Worker           // Only the leaf certificate is considered for coverage because some
387*6777b538SAndroid Build Coastguard Worker           // intermediates have CRLs with no revocations (after filtering) and
388*6777b538SAndroid Build Coastguard Worker           // those CRLs are pruned from the CRLSet at generation time.
389*6777b538SAndroid Build Coastguard Worker           return CRLSet::Result::GOOD;
390*6777b538SAndroid Build Coastguard Worker         }
391*6777b538SAndroid Build Coastguard Worker         break;
392*6777b538SAndroid Build Coastguard Worker     }
393*6777b538SAndroid Build Coastguard Worker   }
394*6777b538SAndroid Build Coastguard Worker 
395*6777b538SAndroid Build Coastguard Worker   // If no certificate was revoked, and the target was not known good, then
396*6777b538SAndroid Build Coastguard Worker   // the revocation status is still unknown.
397*6777b538SAndroid Build Coastguard Worker   return CRLSet::Result::UNKNOWN;
398*6777b538SAndroid Build Coastguard Worker }
399*6777b538SAndroid Build Coastguard Worker 
400*6777b538SAndroid Build Coastguard Worker }  // namespace net
401