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