• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <algorithm>
6 
7 #include "net/cert/pki/cert_errors.h"
8 #include "net/cert/pki/crl.h"
9 #include "net/cert/pki/revocation_util.h"
10 #include "net/cert/pki/signature_algorithm.h"
11 #include "net/cert/pki/verify_name_match.h"
12 #include "net/cert/pki/verify_signed_data.h"
13 #include "net/der/input.h"
14 #include "net/der/parse_values.h"
15 #include "net/der/parser.h"
16 #include "net/der/tag.h"
17 
18 namespace net {
19 
20 namespace {
21 
22 // id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 }
23 // In dotted notation: 2.5.29.28
24 inline constexpr uint8_t kIssuingDistributionPointOid[] = {0x55, 0x1d, 0x1c};
25 
NormalizeNameTLV(const der::Input & name_tlv,std::string * out_normalized_name)26 [[nodiscard]] bool NormalizeNameTLV(const der::Input& name_tlv,
27                                     std::string* out_normalized_name) {
28   der::Parser parser(name_tlv);
29   der::Input name_rdn;
30   net::CertErrors unused_errors;
31   return parser.ReadTag(der::kSequence, &name_rdn) &&
32          NormalizeName(name_rdn, out_normalized_name, &unused_errors) &&
33          !parser.HasMore();
34 }
35 
ContainsExactMatchingName(std::vector<std::string_view> a,std::vector<std::string_view> b)36 bool ContainsExactMatchingName(std::vector<std::string_view> a,
37                                std::vector<std::string_view> b) {
38   std::sort(a.begin(), a.end());
39   std::sort(b.begin(), b.end());
40   std::vector<std::string_view> names_in_common;
41   std::set_intersection(a.begin(), a.end(), b.begin(), b.end(),
42                         std::back_inserter(names_in_common));
43   return !names_in_common.empty();
44 }
45 
46 }  // namespace
47 
ParseCrlCertificateList(const der::Input & crl_tlv,der::Input * out_tbs_cert_list_tlv,der::Input * out_signature_algorithm_tlv,der::BitString * out_signature_value)48 bool ParseCrlCertificateList(const der::Input& crl_tlv,
49                              der::Input* out_tbs_cert_list_tlv,
50                              der::Input* out_signature_algorithm_tlv,
51                              der::BitString* out_signature_value) {
52   der::Parser parser(crl_tlv);
53 
54   //   CertificateList  ::=  SEQUENCE  {
55   der::Parser certificate_list_parser;
56   if (!parser.ReadSequence(&certificate_list_parser))
57     return false;
58 
59   //        tbsCertList          TBSCertList
60   if (!certificate_list_parser.ReadRawTLV(out_tbs_cert_list_tlv))
61     return false;
62 
63   //        signatureAlgorithm   AlgorithmIdentifier,
64   if (!certificate_list_parser.ReadRawTLV(out_signature_algorithm_tlv))
65     return false;
66 
67   //        signatureValue       BIT STRING  }
68   absl::optional<der::BitString> signature_value =
69       certificate_list_parser.ReadBitString();
70   if (!signature_value)
71     return false;
72   *out_signature_value = signature_value.value();
73 
74   // There isn't an extension point at the end of CertificateList.
75   if (certificate_list_parser.HasMore())
76     return false;
77 
78   // By definition the input was a single CertificateList, so there shouldn't be
79   // unconsumed data.
80   if (parser.HasMore())
81     return false;
82 
83   return true;
84 }
85 
ParseCrlTbsCertList(const der::Input & tbs_tlv,ParsedCrlTbsCertList * out)86 bool ParseCrlTbsCertList(const der::Input& tbs_tlv, ParsedCrlTbsCertList* out) {
87   der::Parser parser(tbs_tlv);
88 
89   //   TBSCertList  ::=  SEQUENCE  {
90   der::Parser tbs_parser;
91   if (!parser.ReadSequence(&tbs_parser))
92     return false;
93 
94   //         version                 Version OPTIONAL,
95   //                                      -- if present, MUST be v2
96   absl::optional<der::Input> version_der;
97   if (!tbs_parser.ReadOptionalTag(der::kInteger, &version_der))
98     return false;
99   if (version_der.has_value()) {
100     uint64_t version64;
101     if (!der::ParseUint64(*version_der, &version64))
102       return false;
103     // If version is present, it MUST be v2(1).
104     if (version64 != 1)
105       return false;
106     out->version = CrlVersion::V2;
107   } else {
108     // Uh, RFC 5280 doesn't actually say it anywhere, but presumably if version
109     // is not specified, it is V1.
110     out->version = CrlVersion::V1;
111   }
112 
113   //         signature               AlgorithmIdentifier,
114   if (!tbs_parser.ReadRawTLV(&out->signature_algorithm_tlv))
115     return false;
116 
117   //         issuer                  Name,
118   if (!tbs_parser.ReadRawTLV(&out->issuer_tlv))
119     return false;
120 
121   //         thisUpdate              Time,
122   if (!ReadUTCOrGeneralizedTime(&tbs_parser, &out->this_update))
123     return false;
124 
125   //         nextUpdate              Time OPTIONAL,
126   der::Tag maybe_next_update_tag;
127   der::Input unused_next_update_input;
128   if (tbs_parser.PeekTagAndValue(&maybe_next_update_tag,
129                                  &unused_next_update_input) &&
130       (maybe_next_update_tag == der::kUtcTime ||
131        maybe_next_update_tag == der::kGeneralizedTime)) {
132     der::GeneralizedTime next_update_time;
133     if (!ReadUTCOrGeneralizedTime(&tbs_parser, &next_update_time))
134       return false;
135     out->next_update = next_update_time;
136   } else {
137     out->next_update = absl::nullopt;
138   }
139 
140   //         revokedCertificates     SEQUENCE OF SEQUENCE  { ... } OPTIONAL,
141   der::Input unused_revoked_certificates;
142   der::Tag maybe_revoked_certifigates_tag;
143   if (tbs_parser.PeekTagAndValue(&maybe_revoked_certifigates_tag,
144                                  &unused_revoked_certificates) &&
145       maybe_revoked_certifigates_tag == der::kSequence) {
146     der::Input revoked_certificates_tlv;
147     if (!tbs_parser.ReadRawTLV(&revoked_certificates_tlv))
148       return false;
149     out->revoked_certificates_tlv = revoked_certificates_tlv;
150   } else {
151     out->revoked_certificates_tlv = absl::nullopt;
152   }
153 
154   //         crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
155   //                                       -- if present, version MUST be v2
156   if (!tbs_parser.ReadOptionalTag(der::ContextSpecificConstructed(0),
157                                   &out->crl_extensions_tlv)) {
158     return false;
159   }
160   if (out->crl_extensions_tlv.has_value()) {
161     if (out->version != CrlVersion::V2)
162       return false;
163   }
164 
165   if (tbs_parser.HasMore()) {
166     // Invalid or extraneous elements.
167     return false;
168   }
169 
170   // By definition the input was a single sequence, so there shouldn't be
171   // unconsumed data.
172   if (parser.HasMore())
173     return false;
174 
175   return true;
176 }
177 
ParseIssuingDistributionPoint(const der::Input & extension_value,std::unique_ptr<GeneralNames> * out_distribution_point_names,ContainedCertsType * out_only_contains_cert_type)178 bool ParseIssuingDistributionPoint(
179     const der::Input& extension_value,
180     std::unique_ptr<GeneralNames>* out_distribution_point_names,
181     ContainedCertsType* out_only_contains_cert_type) {
182   der::Parser idp_extension_value_parser(extension_value);
183   // IssuingDistributionPoint ::= SEQUENCE {
184   der::Parser idp_parser;
185   if (!idp_extension_value_parser.ReadSequence(&idp_parser))
186     return false;
187 
188   // 5.2.5.  Conforming CRLs issuers MUST NOT issue CRLs where the DER
189   //    encoding of the issuing distribution point extension is an empty
190   //    sequence.
191   if (!idp_parser.HasMore())
192     return false;
193 
194   //  distributionPoint          [0] DistributionPointName OPTIONAL,
195   absl::optional<der::Input> distribution_point;
196   if (!idp_parser.ReadOptionalTag(
197           der::kTagContextSpecific | der::kTagConstructed | 0,
198           &distribution_point)) {
199     return false;
200   }
201 
202   if (distribution_point.has_value()) {
203     //   DistributionPointName ::= CHOICE {
204     der::Parser dp_name_parser(*distribution_point);
205     //        fullName                [0]     GeneralNames,
206     //        nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
207     absl::optional<der::Input> der_full_name;
208     if (!dp_name_parser.ReadOptionalTag(
209             der::kTagContextSpecific | der::kTagConstructed | 0,
210             &der_full_name)) {
211       return false;
212     }
213     if (!der_full_name) {
214       // Only fullName is supported.
215       return false;
216     }
217     CertErrors errors;
218     *out_distribution_point_names =
219         GeneralNames::CreateFromValue(*der_full_name, &errors);
220     if (!*out_distribution_point_names)
221       return false;
222 
223     if (dp_name_parser.HasMore()) {
224       // CHOICE represents a single value.
225       return false;
226     }
227   }
228 
229   *out_only_contains_cert_type = ContainedCertsType::ANY_CERTS;
230 
231   //  onlyContainsUserCerts      [1] BOOLEAN DEFAULT FALSE,
232   absl::optional<der::Input> only_contains_user_certs;
233   if (!idp_parser.ReadOptionalTag(der::kTagContextSpecific | 1,
234                                   &only_contains_user_certs)) {
235     return false;
236   }
237   if (only_contains_user_certs.has_value()) {
238     bool bool_value;
239     if (!der::ParseBool(*only_contains_user_certs, &bool_value))
240       return false;
241     if (!bool_value)
242       return false;  // DER-encoding requires DEFAULT values be omitted.
243     *out_only_contains_cert_type = ContainedCertsType::USER_CERTS;
244   }
245 
246   //  onlyContainsCACerts        [2] BOOLEAN DEFAULT FALSE,
247   absl::optional<der::Input> only_contains_ca_certs;
248   if (!idp_parser.ReadOptionalTag(der::kTagContextSpecific | 2,
249                                   &only_contains_ca_certs)) {
250     return false;
251   }
252   if (only_contains_ca_certs.has_value()) {
253     bool bool_value;
254     if (!der::ParseBool(*only_contains_ca_certs, &bool_value))
255       return false;
256     if (!bool_value)
257       return false;  // DER-encoding requires DEFAULT values be omitted.
258     if (*out_only_contains_cert_type != ContainedCertsType::ANY_CERTS) {
259       // 5.2.5.  at most one of onlyContainsUserCerts, onlyContainsCACerts,
260       //         and onlyContainsAttributeCerts may be set to TRUE.
261       return false;
262     }
263     *out_only_contains_cert_type = ContainedCertsType::CA_CERTS;
264   }
265 
266   //  onlySomeReasons            [3] ReasonFlags OPTIONAL,
267   //  indirectCRL                [4] BOOLEAN DEFAULT FALSE,
268   //  onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }
269   // onlySomeReasons, indirectCRL, and onlyContainsAttributeCerts are not
270   // supported, fail parsing if they are present.
271   if (idp_parser.HasMore())
272     return false;
273 
274   return true;
275 }
276 
GetCRLStatusForCert(const der::Input & cert_serial,CrlVersion crl_version,const absl::optional<der::Input> & revoked_certificates_tlv)277 CRLRevocationStatus GetCRLStatusForCert(
278     const der::Input& cert_serial,
279     CrlVersion crl_version,
280     const absl::optional<der::Input>& revoked_certificates_tlv) {
281   if (!revoked_certificates_tlv.has_value()) {
282     // RFC 5280 Section 5.1.2.6: "When there are no revoked certificates, the
283     // revoked certificates list MUST be absent."
284     // No covered certificates are revoked, therefore the cert is good.
285     return CRLRevocationStatus::GOOD;
286   }
287 
288   der::Parser parser(*revoked_certificates_tlv);
289 
290   //         revokedCertificates     SEQUENCE OF SEQUENCE  {
291   der::Parser revoked_certificates_parser;
292   if (!parser.ReadSequence(&revoked_certificates_parser))
293     return CRLRevocationStatus::UNKNOWN;
294 
295   // RFC 5280 Section 5.1.2.6: "When there are no revoked certificates, the
296   // revoked certificates list MUST be absent."
297   if (!revoked_certificates_parser.HasMore())
298     return CRLRevocationStatus::UNKNOWN;
299 
300   // By definition the input was a single Extensions sequence, so there
301   // shouldn't be unconsumed data.
302   if (parser.HasMore())
303     return CRLRevocationStatus::UNKNOWN;
304 
305   bool found_matching_serial = false;
306 
307   while (revoked_certificates_parser.HasMore()) {
308     //         revokedCertificates     SEQUENCE OF SEQUENCE  {
309     der::Parser crl_entry_parser;
310     if (!revoked_certificates_parser.ReadSequence(&crl_entry_parser))
311       return CRLRevocationStatus::UNKNOWN;
312 
313     der::Input revoked_cert_serial_number;
314     //              userCertificate         CertificateSerialNumber,
315     if (!crl_entry_parser.ReadTag(der::kInteger, &revoked_cert_serial_number))
316       return CRLRevocationStatus::UNKNOWN;
317 
318     //              revocationDate          Time,
319     der::GeneralizedTime unused_revocation_date;
320     if (!ReadUTCOrGeneralizedTime(&crl_entry_parser, &unused_revocation_date))
321       return CRLRevocationStatus::UNKNOWN;
322 
323     //              crlEntryExtensions      Extensions OPTIONAL
324     if (crl_entry_parser.HasMore()) {
325       //                                       -- if present, version MUST be v2
326       if (crl_version != CrlVersion::V2)
327         return CRLRevocationStatus::UNKNOWN;
328 
329       der::Input crl_entry_extensions_tlv;
330       if (!crl_entry_parser.ReadRawTLV(&crl_entry_extensions_tlv))
331         return CRLRevocationStatus::UNKNOWN;
332 
333       std::map<der::Input, ParsedExtension> extensions;
334       if (!ParseExtensions(crl_entry_extensions_tlv, &extensions))
335         return CRLRevocationStatus::UNKNOWN;
336 
337       // RFC 5280 Section 5.3: "If a CRL contains a critical CRL entry
338       // extension that the application cannot process, then the application
339       // MUST NOT use that CRL to determine the status of any certificates."
340       for (const auto& ext : extensions) {
341         if (ext.second.critical)
342           return CRLRevocationStatus::UNKNOWN;
343       }
344     }
345 
346     if (crl_entry_parser.HasMore())
347       return CRLRevocationStatus::UNKNOWN;
348 
349     if (revoked_cert_serial_number == cert_serial) {
350       // Cert is revoked, but can't return yet since there might be critical
351       // extensions on later entries that would prevent use of this CRL.
352       found_matching_serial = true;
353     }
354   }
355 
356   if (found_matching_serial)
357     return CRLRevocationStatus::REVOKED;
358 
359   // |cert| is not present in the revokedCertificates list.
360   return CRLRevocationStatus::GOOD;
361 }
362 
363 ParsedCrlTbsCertList::ParsedCrlTbsCertList() = default;
364 ParsedCrlTbsCertList::~ParsedCrlTbsCertList() = default;
365 
CheckCRL(std::string_view raw_crl,const ParsedCertificateList & valid_chain,size_t target_cert_index,const ParsedDistributionPoint & cert_dp,int64_t verify_time_epoch_seconds,absl::optional<int64_t> max_age_seconds)366 CRLRevocationStatus CheckCRL(std::string_view raw_crl,
367                              const ParsedCertificateList& valid_chain,
368                              size_t target_cert_index,
369                              const ParsedDistributionPoint& cert_dp,
370                              int64_t verify_time_epoch_seconds,
371                              absl::optional<int64_t> max_age_seconds) {
372   DCHECK_LT(target_cert_index, valid_chain.size());
373 
374   if (cert_dp.reasons) {
375     // Reason codes are not supported. If the distribution point contains a
376     // subset of reasons then skip it. We aren't interested in subsets of CRLs
377     // and the RFC states that there MUST be a CRL that covers all reasons.
378     return CRLRevocationStatus::UNKNOWN;
379   }
380   if (cert_dp.crl_issuer) {
381     // Indirect CRLs are not supported.
382     return CRLRevocationStatus::UNKNOWN;
383   }
384 
385   const ParsedCertificate* target_cert = valid_chain[target_cert_index].get();
386 
387   // 6.3.3 (a) Update the local CRL cache by obtaining a complete CRL, a
388   //           delta CRL, or both, as required.
389   //
390   // This implementation only supports complete CRLs and takes the CRL as
391   // input, it is up to the caller to provide an up to date CRL.
392 
393   der::Input tbs_cert_list_tlv;
394   der::Input signature_algorithm_tlv;
395   der::BitString signature_value;
396   if (!ParseCrlCertificateList(der::Input(raw_crl), &tbs_cert_list_tlv,
397                                &signature_algorithm_tlv, &signature_value)) {
398     return CRLRevocationStatus::UNKNOWN;
399   }
400 
401   ParsedCrlTbsCertList tbs_cert_list;
402   if (!ParseCrlTbsCertList(tbs_cert_list_tlv, &tbs_cert_list))
403     return CRLRevocationStatus::UNKNOWN;
404 
405   // 5.1.1.2  signatureAlgorithm
406   //
407   // TODO(https://crbug.com/749276): Check the signature algorithm against
408   // policy.
409   absl::optional<SignatureAlgorithm> signature_algorithm =
410       ParseSignatureAlgorithm(signature_algorithm_tlv);
411   if (!signature_algorithm) {
412     return CRLRevocationStatus::UNKNOWN;
413   }
414 
415   //    This field MUST contain the same algorithm identifier as the
416   //    signature field in the sequence tbsCertList (Section 5.1.2.2).
417   absl::optional<SignatureAlgorithm> tbs_alg =
418       ParseSignatureAlgorithm(tbs_cert_list.signature_algorithm_tlv);
419   if (!tbs_alg || *signature_algorithm != *tbs_alg) {
420     return CRLRevocationStatus::UNKNOWN;
421   }
422 
423   // Check CRL dates. Roughly corresponds to 6.3.3 (a) (1) but does not attempt
424   // to update the CRL if it is out of date.
425   if (!CheckRevocationDateValid(tbs_cert_list.this_update,
426                                 tbs_cert_list.next_update.has_value()
427                                     ? &tbs_cert_list.next_update.value()
428                                     : nullptr,
429                                 verify_time_epoch_seconds, max_age_seconds)) {
430     return CRLRevocationStatus::UNKNOWN;
431   }
432 
433   // 6.3.3 (a) (2) is skipped: This implementation does not support delta CRLs.
434 
435   // 6.3.3 (b) Verify the issuer and scope of the complete CRL as follows:
436   // 6.3.3 (b) (1) If the DP includes cRLIssuer, then verify that the issuer
437   //               field in the complete CRL matches cRLIssuer in the DP and
438   //               that the complete CRL contains an issuing distribution
439   //               point extension with the indirectCRL boolean asserted.
440   //
441   // Nothing is done here since distribution points with crlIssuer were skipped
442   // above.
443 
444   // 6.3.3 (b) (1) Otherwise, verify that the CRL issuer matches the
445   //               certificate issuer.
446   //
447   // Normalization for the name comparison is used although the RFC is not
448   // clear on this. There are several places that explicitly are called out as
449   // requiring identical encodings:
450   //
451   // 4.2.1.13.  CRL Distribution Points (cert extension) says the DP cRLIssuer
452   //    field MUST be exactly the same as the encoding in issuer field of the
453   //    CRL.
454   //
455   // 5.2.5.  Issuing Distribution Point (crl extension)
456   //    The identical encoding MUST be used in the distributionPoint fields
457   //    of the certificate and the CRL.
458   //
459   // 5.3.3.  Certificate Issuer (crl entry extension) also says "The encoding of
460   //    the DN MUST be identical to the encoding used in the certificate"
461   //
462   // But 6.3.3 (b) (1) just says "matches". Also NIST PKITS includes at least
463   // one test that requires normalization here.
464   // TODO(https://crbug.com/749276): could do exact comparison first and only
465   // fall back to normalizing if that fails.
466   std::string normalized_crl_issuer;
467   if (!NormalizeNameTLV(tbs_cert_list.issuer_tlv, &normalized_crl_issuer))
468     return CRLRevocationStatus::UNKNOWN;
469   if (der::Input(&normalized_crl_issuer) != target_cert->normalized_issuer())
470     return CRLRevocationStatus::UNKNOWN;
471 
472   if (tbs_cert_list.crl_extensions_tlv.has_value()) {
473     std::map<der::Input, ParsedExtension> extensions;
474     if (!ParseExtensions(*tbs_cert_list.crl_extensions_tlv, &extensions))
475       return CRLRevocationStatus::UNKNOWN;
476 
477     // 6.3.3 (b) (2) If the complete CRL includes an issuing distribution point
478     //               (IDP) CRL extension, check the following:
479     ParsedExtension idp_extension;
480     if (ConsumeExtension(der::Input(kIssuingDistributionPointOid), &extensions,
481                          &idp_extension)) {
482       std::unique_ptr<GeneralNames> distribution_point_names;
483       ContainedCertsType only_contains_cert_type;
484       if (!ParseIssuingDistributionPoint(idp_extension.value,
485                                          &distribution_point_names,
486                                          &only_contains_cert_type)) {
487         return CRLRevocationStatus::UNKNOWN;
488       }
489 
490       if (distribution_point_names) {
491         // 6.3.3. (b) (2) (i) If the distribution point name is present in the
492         //                    IDP CRL extension and the distribution field is
493         //                    present in the DP, then verify that one of the
494         //                    names in the IDP matches one of the names in the
495         //                    DP.
496         // 5.2.5.  The identical encoding MUST be used in the distributionPoint
497         //         fields of the certificate and the CRL.
498         // TODO(https://crbug.com/749276): Check other name types?
499         if (!cert_dp.distribution_point_fullname ||
500             !ContainsExactMatchingName(
501                 cert_dp.distribution_point_fullname
502                     ->uniform_resource_identifiers,
503                 distribution_point_names->uniform_resource_identifiers)) {
504           return CRLRevocationStatus::UNKNOWN;
505         }
506 
507         // 6.3.3. (b) (2) (i) If the distribution point name is present in the
508         //                    IDP CRL extension and the distribution field is
509         //                    omitted from the DP, then verify that one of the
510         //                    names in the IDP matches one of the names in the
511         //                    cRLIssuer field of the DP.
512         // Indirect CRLs are not supported, if indirectCRL was specified,
513         // ParseIssuingDistributionPoint would already have failed.
514       }
515 
516       switch (only_contains_cert_type) {
517         case ContainedCertsType::USER_CERTS:
518           // 6.3.3. (b) (2) (ii)  If the onlyContainsUserCerts boolean is
519           //                      asserted in the IDP CRL extension, verify
520           //                      that the certificate does not include the
521           //                      basic constraints extension with the cA
522           //                      boolean asserted.
523           // 5.2.5.  If either onlyContainsUserCerts or onlyContainsCACerts is
524           //         set to TRUE, then the scope of the CRL MUST NOT include any
525           //         version 1 or version 2 certificates.
526           if ((target_cert->has_basic_constraints() &&
527                target_cert->basic_constraints().is_ca) ||
528               target_cert->tbs().version == CertificateVersion::V1 ||
529               target_cert->tbs().version == CertificateVersion::V2) {
530             return CRLRevocationStatus::UNKNOWN;
531           }
532           break;
533 
534         case ContainedCertsType::CA_CERTS:
535           // 6.3.3. (b) (2) (iii) If the onlyContainsCACerts boolean is asserted
536           //                      in the IDP CRL extension, verify that the
537           //                      certificate includes the basic constraints
538           //                      extension with the cA boolean asserted.
539           // The version check is not done here, as the basicConstraints
540           // extension is required, and could not be present unless it is a V3
541           // certificate.
542           if (!target_cert->has_basic_constraints() ||
543               !target_cert->basic_constraints().is_ca) {
544             return CRLRevocationStatus::UNKNOWN;
545           }
546           break;
547 
548         case ContainedCertsType::ANY_CERTS:
549           //                (iv)  Verify that the onlyContainsAttributeCerts
550           //                      boolean is not asserted.
551           // If onlyContainsAttributeCerts was present,
552           // ParseIssuingDistributionPoint would already have failed.
553           break;
554       }
555     }
556 
557     for (const auto& ext : extensions) {
558       // Fail if any unhandled critical CRL extensions are present.
559       if (ext.second.critical)
560         return CRLRevocationStatus::UNKNOWN;
561     }
562   }
563 
564   // 6.3.3 (c-e) skipped: delta CRLs and reason codes are not supported.
565 
566   // This implementation only supports direct CRLs where the CRL was signed by
567   // one of the certs in its validated issuer chain. This allows handling some
568   // cases of key rollover without requiring additional CRL issuer cert
569   // discovery & path building.
570   // TODO(https://crbug.com/749276): should this loop start at
571   // |target_cert_index|? There doesn't seem to be anything in the specs that
572   // precludes a CRL signed by a self-issued cert from covering itself. On the
573   // other hand it seems like a pretty weird thing to allow and causes NIST
574   // PKITS 4.5.3 to pass when it seems like it would not be intended to (since
575   // issuingDistributionPoint CRL extension is not handled).
576   for (size_t i = target_cert_index + 1; i < valid_chain.size(); ++i) {
577     const ParsedCertificate* issuer_cert = valid_chain[i].get();
578 
579     // 6.3.3 (f) Obtain and validate the certification path for the issuer of
580     //           the complete CRL.  The trust anchor for the certification
581     //           path MUST be the same as the trust anchor used to validate
582     //           the target certificate.
583     //
584     // As the |issuer_cert| is from the already validated chain, it is already
585     // known to chain to the same trust anchor as the target certificate.
586     if (der::Input(&normalized_crl_issuer) != issuer_cert->normalized_subject())
587       continue;
588 
589     // 6.3.3 (f) If a key usage extension is present in the CRL issuer's
590     //           certificate, verify that the cRLSign bit is set.
591     if (issuer_cert->has_key_usage() &&
592         !issuer_cert->key_usage().AssertsBit(KEY_USAGE_BIT_CRL_SIGN)) {
593       continue;
594     }
595 
596     // 6.3.3 (g) Validate the signature on the complete CRL using the public
597     //           key validated in step (f).
598     if (!VerifySignedData(*signature_algorithm, tbs_cert_list_tlv,
599                           signature_value, issuer_cert->tbs().spki_tlv,
600                           /*cache=*/nullptr)) {
601       continue;
602     }
603 
604     // 6.3.3 (h,i) skipped. This implementation does not support delta CRLs.
605 
606     // 6.3.3 (j) If (cert_status is UNREVOKED), then search for the
607     //           certificate on the complete CRL.  If an entry is found that
608     //           matches the certificate issuer and serial number as described
609     //           in Section 5.3.3, then set the cert_status variable to the
610     //           indicated reason as described in step (i).
611     //
612     // CRL is valid and covers |target_cert|, check if |target_cert| is present
613     // in the revokedCertificates sequence.
614     return GetCRLStatusForCert(target_cert->tbs().serial_number,
615                                tbs_cert_list.version,
616                                tbs_cert_list.revoked_certificates_tlv);
617 
618     // 6.3.3 (k,l) skipped. This implementation does not support reason codes.
619   }
620 
621   // Did not find the issuer & signer of |raw_crl| in |valid_chain|.
622   return CRLRevocationStatus::UNKNOWN;
623 }
624 
625 }  // namespace net
626