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