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