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