/src/boringssl/pki/ocsp.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2016 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 "ocsp.h" |
16 | | |
17 | | #include <openssl/bytestring.h> |
18 | | #include <openssl/digest.h> |
19 | | #include <openssl/mem.h> |
20 | | #include <openssl/pool.h> |
21 | | #include <openssl/sha.h> |
22 | | #include <openssl/span.h> |
23 | | |
24 | | #include "cert_errors.h" |
25 | | #include "extended_key_usage.h" |
26 | | #include "parsed_certificate.h" |
27 | | #include "revocation_util.h" |
28 | | #include "string_util.h" |
29 | | #include "verify_name_match.h" |
30 | | #include "verify_signed_data.h" |
31 | | |
32 | | BSSL_NAMESPACE_BEGIN |
33 | | |
34 | 872 | OCSPCertID::OCSPCertID() = default; |
35 | 872 | OCSPCertID::~OCSPCertID() = default; |
36 | | |
37 | 1.16k | OCSPSingleResponse::OCSPSingleResponse() = default; |
38 | 1.16k | OCSPSingleResponse::~OCSPSingleResponse() = default; |
39 | | |
40 | 1.13k | OCSPResponseData::OCSPResponseData() = default; |
41 | 1.13k | OCSPResponseData::~OCSPResponseData() = default; |
42 | | |
43 | 1.22k | OCSPResponse::OCSPResponse() = default; |
44 | 1.22k | OCSPResponse::~OCSPResponse() = default; |
45 | | |
46 | | // CertID ::= SEQUENCE { |
47 | | // hashAlgorithm AlgorithmIdentifier, |
48 | | // issuerNameHash OCTET STRING, -- Hash of issuer's DN |
49 | | // issuerKeyHash OCTET STRING, -- Hash of issuer's public key |
50 | | // serialNumber CertificateSerialNumber |
51 | | // } |
52 | 872 | bool ParseOCSPCertID(der::Input raw_tlv, OCSPCertID *out) { |
53 | 872 | der::Parser outer_parser(raw_tlv); |
54 | 872 | der::Parser parser; |
55 | 872 | if (!outer_parser.ReadSequence(&parser)) { |
56 | 492 | return false; |
57 | 492 | } |
58 | 380 | if (outer_parser.HasMore()) { |
59 | 64 | return false; |
60 | 64 | } |
61 | | |
62 | 316 | der::Input sigalg_tlv; |
63 | 316 | if (!parser.ReadRawTLV(&sigalg_tlv)) { |
64 | 5 | return false; |
65 | 5 | } |
66 | 311 | if (!ParseHashAlgorithm(sigalg_tlv, &out->hash_algorithm)) { |
67 | 242 | return false; |
68 | 242 | } |
69 | 69 | if (!parser.ReadTag(CBS_ASN1_OCTETSTRING, &out->issuer_name_hash)) { |
70 | 6 | return false; |
71 | 6 | } |
72 | 63 | if (!parser.ReadTag(CBS_ASN1_OCTETSTRING, &out->issuer_key_hash)) { |
73 | 1 | return false; |
74 | 1 | } |
75 | 62 | if (!parser.ReadTag(CBS_ASN1_INTEGER, &out->serial_number)) { |
76 | 6 | return false; |
77 | 6 | } |
78 | 56 | CertErrors errors; |
79 | 56 | if (!VerifySerialNumber(out->serial_number, false /*warnings_only*/, |
80 | 56 | &errors)) { |
81 | 21 | return false; |
82 | 21 | } |
83 | | |
84 | 35 | return !parser.HasMore(); |
85 | 56 | } |
86 | | |
87 | | namespace { |
88 | | |
89 | | // Parses |raw_tlv| to extract an OCSP RevokedInfo (RFC 6960) and stores the |
90 | | // result in the OCSPCertStatus |out|. Returns whether the parsing was |
91 | | // successful. |
92 | | // |
93 | | // RevokedInfo ::= SEQUENCE { |
94 | | // revocationTime GeneralizedTime, |
95 | | // revocationReason [0] EXPLICIT CRLReason OPTIONAL |
96 | | // } |
97 | 312 | bool ParseRevokedInfo(der::Input raw_tlv, OCSPCertStatus *out) { |
98 | 312 | der::Parser parser(raw_tlv); |
99 | 312 | if (!parser.ReadGeneralizedTime(&(out->revocation_time))) { |
100 | 54 | return false; |
101 | 54 | } |
102 | | |
103 | 258 | der::Input reason_input; |
104 | 258 | if (!parser.ReadOptionalTag( |
105 | 258 | CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, &reason_input, |
106 | 258 | &(out->has_reason))) { |
107 | 1 | return false; |
108 | 1 | } |
109 | 257 | if (out->has_reason) { |
110 | 172 | der::Parser reason_parser(reason_input); |
111 | 172 | der::Input reason_value_input; |
112 | 172 | uint8_t reason_value; |
113 | 172 | if (!reason_parser.ReadTag(CBS_ASN1_ENUMERATED, &reason_value_input)) { |
114 | 3 | return false; |
115 | 3 | } |
116 | 169 | if (!der::ParseUint8(reason_value_input, &reason_value)) { |
117 | 131 | return false; |
118 | 131 | } |
119 | 38 | if (reason_value > |
120 | 38 | static_cast<uint8_t>(OCSPCertStatus::RevocationReason::LAST)) { |
121 | 13 | return false; |
122 | 13 | } |
123 | 25 | out->revocation_reason = |
124 | 25 | static_cast<OCSPCertStatus::RevocationReason>(reason_value); |
125 | 25 | if (out->revocation_reason == OCSPCertStatus::RevocationReason::UNUSED) { |
126 | 1 | return false; |
127 | 1 | } |
128 | 24 | if (reason_parser.HasMore()) { |
129 | 1 | return false; |
130 | 1 | } |
131 | 24 | } |
132 | 108 | return !parser.HasMore(); |
133 | 257 | } |
134 | | |
135 | | // Parses |raw_tlv| to extract an OCSP CertStatus (RFC 6960) and stores the |
136 | | // result in the OCSPCertStatus |out|. Returns whether the parsing was |
137 | | // successful. |
138 | | // |
139 | | // CertStatus ::= CHOICE { |
140 | | // good [0] IMPLICIT NULL, |
141 | | // revoked [1] IMPLICIT RevokedInfo, |
142 | | // unknown [2] IMPLICIT UnknownInfo |
143 | | // } |
144 | | // |
145 | | // UnknownInfo ::= NULL |
146 | 585 | bool ParseCertStatus(der::Input raw_tlv, OCSPCertStatus *out) { |
147 | 585 | der::Parser parser(raw_tlv); |
148 | 585 | CBS_ASN1_TAG status_tag; |
149 | 585 | der::Input status; |
150 | 585 | if (!parser.ReadTagAndValue(&status_tag, &status)) { |
151 | 0 | return false; |
152 | 0 | } |
153 | | |
154 | 585 | out->has_reason = false; |
155 | 585 | if (status_tag == (CBS_ASN1_CONTEXT_SPECIFIC | 0)) { |
156 | 113 | out->status = OCSPRevocationStatus::GOOD; |
157 | 472 | } else if (status_tag == |
158 | 472 | (CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1)) { |
159 | 312 | out->status = OCSPRevocationStatus::REVOKED; |
160 | 312 | if (!ParseRevokedInfo(status, out)) { |
161 | 206 | return false; |
162 | 206 | } |
163 | 312 | } else if (status_tag == (CBS_ASN1_CONTEXT_SPECIFIC | 2)) { |
164 | 50 | out->status = OCSPRevocationStatus::UNKNOWN; |
165 | 110 | } else { |
166 | 110 | return false; |
167 | 110 | } |
168 | | |
169 | 269 | return !parser.HasMore(); |
170 | 585 | } |
171 | | |
172 | | // Writes the hash of |value| as an OCTET STRING to |cbb|, using |hash_type| as |
173 | | // the algorithm. Returns true on success. |
174 | | bool AppendHashAsOctetString(const EVP_MD *hash_type, CBB *cbb, |
175 | 0 | der::Input value) { |
176 | 0 | CBB octet_string; |
177 | 0 | unsigned hash_len; |
178 | 0 | uint8_t hash_buffer[EVP_MAX_MD_SIZE]; |
179 | |
|
180 | 0 | return CBB_add_asn1(cbb, &octet_string, CBS_ASN1_OCTETSTRING) && |
181 | 0 | EVP_Digest(value.data(), value.size(), hash_buffer, &hash_len, |
182 | 0 | hash_type, nullptr) && |
183 | 0 | CBB_add_bytes(&octet_string, hash_buffer, hash_len) && CBB_flush(cbb); |
184 | 0 | } |
185 | | |
186 | | } // namespace |
187 | | |
188 | | // SingleResponse ::= SEQUENCE { |
189 | | // certID CertID, |
190 | | // certStatus CertStatus, |
191 | | // thisUpdate GeneralizedTime, |
192 | | // nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, |
193 | | // singleExtensions [1] EXPLICIT Extensions OPTIONAL |
194 | | // } |
195 | 1.16k | bool ParseOCSPSingleResponse(der::Input raw_tlv, OCSPSingleResponse *out) { |
196 | 1.16k | der::Parser outer_parser(raw_tlv); |
197 | 1.16k | der::Parser parser; |
198 | 1.16k | if (!outer_parser.ReadSequence(&parser)) { |
199 | 503 | return false; |
200 | 503 | } |
201 | 664 | if (outer_parser.HasMore()) { |
202 | 62 | return false; |
203 | 62 | } |
204 | | |
205 | 602 | if (!parser.ReadRawTLV(&(out->cert_id_tlv))) { |
206 | 10 | return false; |
207 | 10 | } |
208 | 592 | der::Input status_tlv; |
209 | 592 | if (!parser.ReadRawTLV(&status_tlv)) { |
210 | 7 | return false; |
211 | 7 | } |
212 | 585 | if (!ParseCertStatus(status_tlv, &(out->cert_status))) { |
213 | 316 | return false; |
214 | 316 | } |
215 | 269 | if (!parser.ReadGeneralizedTime(&(out->this_update))) { |
216 | 160 | return false; |
217 | 160 | } |
218 | | |
219 | 109 | der::Input next_update_input; |
220 | 109 | if (!parser.ReadOptionalTag( |
221 | 109 | CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, |
222 | 109 | &next_update_input, &(out->has_next_update))) { |
223 | 4 | return false; |
224 | 4 | } |
225 | 105 | if (out->has_next_update) { |
226 | 40 | der::Parser next_update_parser(next_update_input); |
227 | 40 | if (!next_update_parser.ReadGeneralizedTime(&(out->next_update))) { |
228 | 19 | return false; |
229 | 19 | } |
230 | 21 | if (next_update_parser.HasMore()) { |
231 | 2 | return false; |
232 | 2 | } |
233 | 21 | } |
234 | | |
235 | 84 | if (!parser.ReadOptionalTag( |
236 | 84 | CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1, |
237 | 84 | &(out->extensions), &(out->has_extensions))) { |
238 | 1 | return false; |
239 | 1 | } |
240 | | |
241 | 83 | return !parser.HasMore(); |
242 | 84 | } |
243 | | |
244 | | namespace { |
245 | | |
246 | | // Parses |raw_tlv| to extract a ResponderID (RFC 6960) and stores the |
247 | | // result in the ResponderID |out|. Returns whether the parsing was successful. |
248 | | // |
249 | | // ResponderID ::= CHOICE { |
250 | | // byName [1] Name, |
251 | | // byKey [2] KeyHash |
252 | | // } |
253 | 387 | bool ParseResponderID(der::Input raw_tlv, OCSPResponseData::ResponderID *out) { |
254 | 387 | der::Parser parser(raw_tlv); |
255 | 387 | CBS_ASN1_TAG id_tag; |
256 | 387 | der::Input id_input; |
257 | 387 | if (!parser.ReadTagAndValue(&id_tag, &id_input)) { |
258 | 0 | return false; |
259 | 0 | } |
260 | | |
261 | 387 | if (id_tag == (CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1)) { |
262 | 246 | out->type = OCSPResponseData::ResponderType::NAME; |
263 | 246 | out->name = id_input; |
264 | 246 | } else if (id_tag == (CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 2)) { |
265 | 24 | der::Parser key_parser(id_input); |
266 | 24 | der::Input key_hash; |
267 | 24 | if (!key_parser.ReadTag(CBS_ASN1_OCTETSTRING, &key_hash)) { |
268 | 6 | return false; |
269 | 6 | } |
270 | 18 | if (key_parser.HasMore()) { |
271 | 1 | return false; |
272 | 1 | } |
273 | 17 | if (key_hash.size() != SHA_DIGEST_LENGTH) { |
274 | 12 | return false; |
275 | 12 | } |
276 | | |
277 | 5 | out->type = OCSPResponseData::ResponderType::KEY_HASH; |
278 | 5 | out->key_hash = key_hash; |
279 | 117 | } else { |
280 | 117 | return false; |
281 | 117 | } |
282 | 251 | return !parser.HasMore(); |
283 | 387 | } |
284 | | |
285 | | } // namespace |
286 | | |
287 | | // ResponseData ::= SEQUENCE { |
288 | | // version [0] EXPLICIT Version DEFAULT v1, |
289 | | // responderID ResponderID, |
290 | | // producedAt GeneralizedTime, |
291 | | // responses SEQUENCE OF SingleResponse, |
292 | | // responseExtensions [1] EXPLICIT Extensions OPTIONAL |
293 | | // } |
294 | 1.13k | bool ParseOCSPResponseData(der::Input raw_tlv, OCSPResponseData *out) { |
295 | 1.13k | der::Parser outer_parser(raw_tlv); |
296 | 1.13k | der::Parser parser; |
297 | 1.13k | if (!outer_parser.ReadSequence(&parser)) { |
298 | 491 | return false; |
299 | 491 | } |
300 | 640 | if (outer_parser.HasMore()) { |
301 | 62 | return false; |
302 | 62 | } |
303 | | |
304 | 578 | der::Input version_input; |
305 | 578 | bool version_present; |
306 | 578 | if (!parser.ReadOptionalTag( |
307 | 578 | CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, &version_input, |
308 | 578 | &version_present)) { |
309 | 11 | return false; |
310 | 11 | } |
311 | | |
312 | | // For compatibilty, we ignore the restriction from X.690 Section 11.5 that |
313 | | // DEFAULT values should be omitted for values equal to the default value. |
314 | | // TODO: Add warning about non-strict parsing. |
315 | 567 | if (version_present) { |
316 | 191 | der::Parser version_parser(version_input); |
317 | 191 | if (!version_parser.ReadUint8(&(out->version))) { |
318 | 164 | return false; |
319 | 164 | } |
320 | 27 | if (version_parser.HasMore()) { |
321 | 1 | return false; |
322 | 1 | } |
323 | 376 | } else { |
324 | 376 | out->version = 0; |
325 | 376 | } |
326 | | |
327 | 402 | if (out->version != 0) { |
328 | 12 | return false; |
329 | 12 | } |
330 | | |
331 | 390 | der::Input responder_input; |
332 | 390 | if (!parser.ReadRawTLV(&responder_input)) { |
333 | 3 | return false; |
334 | 3 | } |
335 | 387 | if (!ParseResponderID(responder_input, &(out->responder_id))) { |
336 | 136 | return false; |
337 | 136 | } |
338 | 251 | if (!parser.ReadGeneralizedTime(&(out->produced_at))) { |
339 | 159 | return false; |
340 | 159 | } |
341 | | |
342 | 92 | der::Parser responses_parser; |
343 | 92 | if (!parser.ReadSequence(&responses_parser)) { |
344 | 28 | return false; |
345 | 28 | } |
346 | 64 | out->responses.clear(); |
347 | 20.5k | while (responses_parser.HasMore()) { |
348 | 20.4k | der::Input single_response; |
349 | 20.4k | if (!responses_parser.ReadRawTLV(&single_response)) { |
350 | 27 | return false; |
351 | 27 | } |
352 | 20.4k | out->responses.push_back(single_response); |
353 | 20.4k | } |
354 | | |
355 | 37 | if (!parser.ReadOptionalTag( |
356 | 37 | CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1, |
357 | 37 | &(out->extensions), &(out->has_extensions))) { |
358 | 6 | return false; |
359 | 6 | } |
360 | | |
361 | 31 | return !parser.HasMore(); |
362 | 37 | } |
363 | | |
364 | | namespace { |
365 | | |
366 | | // Parses |raw_tlv| to extract a BasicOCSPResponse (RFC 6960) and stores the |
367 | | // result in the OCSPResponse |out|. Returns whether the parsing was |
368 | | // successful. |
369 | | // |
370 | | // BasicOCSPResponse ::= SEQUENCE { |
371 | | // tbsResponseData ResponseData, |
372 | | // signatureAlgorithm AlgorithmIdentifier, |
373 | | // signature BIT STRING, |
374 | | // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL |
375 | | // } |
376 | 327 | bool ParseBasicOCSPResponse(der::Input raw_tlv, OCSPResponse *out) { |
377 | 327 | der::Parser outer_parser(raw_tlv); |
378 | 327 | der::Parser parser; |
379 | 327 | if (!outer_parser.ReadSequence(&parser)) { |
380 | 1 | return false; |
381 | 1 | } |
382 | 326 | if (outer_parser.HasMore()) { |
383 | 1 | return false; |
384 | 1 | } |
385 | | |
386 | 325 | if (!parser.ReadRawTLV(&(out->data))) { |
387 | 3 | return false; |
388 | 3 | } |
389 | 322 | der::Input sigalg_tlv; |
390 | 322 | if (!parser.ReadRawTLV(&sigalg_tlv)) { |
391 | 3 | return false; |
392 | 3 | } |
393 | | // TODO(crbug.com/634443): Propagate the errors. |
394 | 319 | std::optional<SignatureAlgorithm> sigalg = |
395 | 319 | ParseSignatureAlgorithm(sigalg_tlv); |
396 | 319 | if (!sigalg) { |
397 | 190 | return false; |
398 | 190 | } |
399 | 129 | out->signature_algorithm = sigalg.value(); |
400 | 129 | std::optional<der::BitString> signature = parser.ReadBitString(); |
401 | 129 | if (!signature) { |
402 | 26 | return false; |
403 | 26 | } |
404 | 103 | out->signature = signature.value(); |
405 | 103 | der::Input certs_input; |
406 | 103 | if (!parser.ReadOptionalTag( |
407 | 103 | CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, &certs_input, |
408 | 103 | &(out->has_certs))) { |
409 | 6 | return false; |
410 | 6 | } |
411 | | |
412 | 97 | out->certs.clear(); |
413 | 97 | if (out->has_certs) { |
414 | 33 | der::Parser certs_seq_parser(certs_input); |
415 | 33 | der::Parser certs_parser; |
416 | 33 | if (!certs_seq_parser.ReadSequence(&certs_parser)) { |
417 | 7 | return false; |
418 | 7 | } |
419 | 26 | if (certs_seq_parser.HasMore()) { |
420 | 2 | return false; |
421 | 2 | } |
422 | 1.35k | while (certs_parser.HasMore()) { |
423 | 1.34k | der::Input cert_tlv; |
424 | 1.34k | if (!certs_parser.ReadRawTLV(&cert_tlv)) { |
425 | 15 | return false; |
426 | 15 | } |
427 | 1.32k | out->certs.push_back(cert_tlv); |
428 | 1.32k | } |
429 | 24 | } |
430 | | |
431 | 73 | return !parser.HasMore(); |
432 | 97 | } |
433 | | |
434 | | } // namespace |
435 | | |
436 | | // OCSPResponse ::= SEQUENCE { |
437 | | // responseStatus OCSPResponseStatus, |
438 | | // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL |
439 | | // } |
440 | | // |
441 | | // ResponseBytes ::= SEQUENCE { |
442 | | // responseType OBJECT IDENTIFIER, |
443 | | // response OCTET STRING |
444 | | // } |
445 | 1.22k | bool ParseOCSPResponse(der::Input raw_tlv, OCSPResponse *out) { |
446 | 1.22k | der::Parser outer_parser(raw_tlv); |
447 | 1.22k | der::Parser parser; |
448 | 1.22k | if (!outer_parser.ReadSequence(&parser)) { |
449 | 530 | return false; |
450 | 530 | } |
451 | 691 | if (outer_parser.HasMore()) { |
452 | 62 | return false; |
453 | 62 | } |
454 | | |
455 | 629 | der::Input response_status_input; |
456 | 629 | uint8_t response_status; |
457 | 629 | if (!parser.ReadTag(CBS_ASN1_ENUMERATED, &response_status_input)) { |
458 | 13 | return false; |
459 | 13 | } |
460 | 616 | if (!der::ParseUint8(response_status_input, &response_status)) { |
461 | 168 | return false; |
462 | 168 | } |
463 | 448 | if (response_status > |
464 | 448 | static_cast<uint8_t>(OCSPResponse::ResponseStatus::LAST)) { |
465 | 15 | return false; |
466 | 15 | } |
467 | 433 | out->status = static_cast<OCSPResponse::ResponseStatus>(response_status); |
468 | 433 | if (out->status == OCSPResponse::ResponseStatus::UNUSED) { |
469 | 1 | return false; |
470 | 1 | } |
471 | | |
472 | 432 | if (out->status == OCSPResponse::ResponseStatus::SUCCESSFUL) { |
473 | 427 | der::Parser outer_bytes_parser; |
474 | 427 | der::Parser bytes_parser; |
475 | 427 | if (!parser.ReadConstructed( |
476 | 427 | CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, |
477 | 427 | &outer_bytes_parser)) { |
478 | 2 | return false; |
479 | 2 | } |
480 | 425 | if (!outer_bytes_parser.ReadSequence(&bytes_parser)) { |
481 | 3 | return false; |
482 | 3 | } |
483 | 422 | if (outer_bytes_parser.HasMore()) { |
484 | 1 | return false; |
485 | 1 | } |
486 | | |
487 | 421 | der::Input type_oid; |
488 | 421 | if (!bytes_parser.ReadTag(CBS_ASN1_OBJECT, &type_oid)) { |
489 | 2 | return false; |
490 | 2 | } |
491 | 419 | if (type_oid != der::Input(kBasicOCSPResponseOid)) { |
492 | 91 | return false; |
493 | 91 | } |
494 | | |
495 | | // As per RFC 6960 Section 4.2.1, the value of |response| SHALL be the DER |
496 | | // encoding of BasicOCSPResponse. |
497 | 328 | der::Input response; |
498 | 328 | if (!bytes_parser.ReadTag(CBS_ASN1_OCTETSTRING, &response)) { |
499 | 1 | return false; |
500 | 1 | } |
501 | 327 | if (!ParseBasicOCSPResponse(response, out)) { |
502 | 318 | return false; |
503 | 318 | } |
504 | 9 | if (bytes_parser.HasMore()) { |
505 | 1 | return false; |
506 | 1 | } |
507 | 9 | } |
508 | | |
509 | 13 | return !parser.HasMore(); |
510 | 432 | } |
511 | | |
512 | | namespace { |
513 | | |
514 | | // Checks that the |type| hash of |value| is equal to |hash| |
515 | 0 | bool VerifyHash(const EVP_MD *type, der::Input hash, der::Input value) { |
516 | 0 | unsigned value_hash_len; |
517 | 0 | uint8_t value_hash[EVP_MAX_MD_SIZE]; |
518 | 0 | if (!EVP_Digest(value.data(), value.size(), value_hash, &value_hash_len, type, |
519 | 0 | nullptr)) { |
520 | 0 | return false; |
521 | 0 | } |
522 | | |
523 | 0 | return hash == der::Input(value_hash, value_hash_len); |
524 | 0 | } |
525 | | |
526 | | // Extracts the bytes of the SubjectPublicKey bit string given an SPKI. That is |
527 | | // to say, the value of subjectPublicKey without the leading unused bit |
528 | | // count octet. |
529 | | // |
530 | | // Returns true on success and fills |*spk_tlv| with the result. |
531 | | // |
532 | | // From RFC 5280, Section 4.1 |
533 | | // SubjectPublicKeyInfo ::= SEQUENCE { |
534 | | // algorithm AlgorithmIdentifier, |
535 | | // subjectPublicKey BIT STRING } |
536 | | // |
537 | | // AlgorithmIdentifier ::= SEQUENCE { |
538 | | // algorithm OBJECT IDENTIFIER, |
539 | | // parameters ANY DEFINED BY algorithm OPTIONAL } |
540 | | // |
541 | 0 | bool GetSubjectPublicKeyBytes(der::Input spki_tlv, der::Input *spk_tlv) { |
542 | 0 | CBS outer, inner, alg, spk; |
543 | 0 | uint8_t unused_bit_count; |
544 | 0 | CBS_init(&outer, spki_tlv.data(), spki_tlv.size()); |
545 | | // The subjectPublicKey field includes the unused bit count. For this |
546 | | // application, the unused bit count must be zero, and is not included in |
547 | | // the result. We extract the subjectPubicKey bit string, verify the first |
548 | | // byte is 0, and if so set |spk_tlv| to the remaining bytes. |
549 | 0 | if (!CBS_get_asn1(&outer, &inner, CBS_ASN1_SEQUENCE) || |
550 | 0 | !CBS_get_asn1(&inner, &alg, CBS_ASN1_SEQUENCE) || |
551 | 0 | !CBS_get_asn1(&inner, &spk, CBS_ASN1_BITSTRING) || |
552 | 0 | !CBS_get_u8(&spk, &unused_bit_count) || unused_bit_count != 0) { |
553 | 0 | return false; |
554 | 0 | } |
555 | 0 | *spk_tlv = der::Input(CBS_data(&spk), CBS_len(&spk)); |
556 | 0 | return true; |
557 | 0 | } |
558 | | |
559 | | // Checks the OCSPCertID |id| identifies |certificate|. |
560 | | bool CheckCertIDMatchesCertificate( |
561 | | const OCSPCertID &id, const ParsedCertificate *certificate, |
562 | 0 | const ParsedCertificate *issuer_certificate) { |
563 | 0 | const EVP_MD *type = nullptr; |
564 | 0 | switch (id.hash_algorithm) { |
565 | 0 | case DigestAlgorithm::Md2: |
566 | 0 | case DigestAlgorithm::Md4: |
567 | 0 | case DigestAlgorithm::Md5: |
568 | | // Unsupported. |
569 | 0 | return false; |
570 | 0 | case DigestAlgorithm::Sha1: |
571 | 0 | type = EVP_sha1(); |
572 | 0 | break; |
573 | 0 | case DigestAlgorithm::Sha256: |
574 | 0 | type = EVP_sha256(); |
575 | 0 | break; |
576 | 0 | case DigestAlgorithm::Sha384: |
577 | 0 | type = EVP_sha384(); |
578 | 0 | break; |
579 | 0 | case DigestAlgorithm::Sha512: |
580 | 0 | type = EVP_sha512(); |
581 | 0 | break; |
582 | 0 | } |
583 | | |
584 | 0 | if (!VerifyHash(type, id.issuer_name_hash, certificate->tbs().issuer_tlv)) { |
585 | 0 | return false; |
586 | 0 | } |
587 | | |
588 | 0 | der::Input key_tlv; |
589 | 0 | if (!GetSubjectPublicKeyBytes(issuer_certificate->tbs().spki_tlv, &key_tlv)) { |
590 | 0 | return false; |
591 | 0 | } |
592 | | |
593 | 0 | if (!VerifyHash(type, id.issuer_key_hash, key_tlv)) { |
594 | 0 | return false; |
595 | 0 | } |
596 | | |
597 | 0 | return id.serial_number == certificate->tbs().serial_number; |
598 | 0 | } |
599 | | |
600 | | // TODO(eroman): Revisit how certificate parsing is used by this file. Ideally |
601 | | // would either pass in the parsed bits, or have a better abstraction for lazily |
602 | | // parsing. |
603 | | std::shared_ptr<const ParsedCertificate> OCSPParseCertificate( |
604 | 0 | std::string_view der) { |
605 | 0 | ParseCertificateOptions parse_options; |
606 | 0 | parse_options.allow_invalid_serial_numbers = true; |
607 | | |
608 | | // The objects returned by this function only last for the duration of a |
609 | | // single certificate verification, so there is no need to pool them to save |
610 | | // memory. |
611 | | // |
612 | | // TODO(eroman): Swallows the parsing errors. However uses a permissive |
613 | | // parsing model. |
614 | 0 | CertErrors errors; |
615 | 0 | return ParsedCertificate::Create( |
616 | 0 | bssl::UniquePtr<CRYPTO_BUFFER>(CRYPTO_BUFFER_new( |
617 | 0 | reinterpret_cast<const uint8_t *>(der.data()), der.size(), nullptr)), |
618 | 0 | {}, &errors); |
619 | 0 | } |
620 | | |
621 | | // Checks that the ResponderID |id| matches the certificate |cert| either |
622 | | // by verifying the name matches that of the certificate or that the hash |
623 | | // matches the certificate's public key hash (RFC 6960, 4.2.2.3). |
624 | | [[nodiscard]] bool CheckResponderIDMatchesCertificate( |
625 | 0 | const OCSPResponseData::ResponderID &id, const ParsedCertificate *cert) { |
626 | 0 | switch (id.type) { |
627 | 0 | case OCSPResponseData::ResponderType::NAME: { |
628 | 0 | der::Input name_rdn; |
629 | 0 | der::Input cert_rdn; |
630 | 0 | if (!der::Parser(id.name).ReadTag(CBS_ASN1_SEQUENCE, &name_rdn) || |
631 | 0 | !der::Parser(cert->tbs().subject_tlv) |
632 | 0 | .ReadTag(CBS_ASN1_SEQUENCE, &cert_rdn)) { |
633 | 0 | return false; |
634 | 0 | } |
635 | 0 | return VerifyNameMatch(name_rdn, cert_rdn); |
636 | 0 | } |
637 | 0 | case OCSPResponseData::ResponderType::KEY_HASH: { |
638 | 0 | der::Input key; |
639 | 0 | if (!GetSubjectPublicKeyBytes(cert->tbs().spki_tlv, &key)) { |
640 | 0 | return false; |
641 | 0 | } |
642 | 0 | return VerifyHash(EVP_sha1(), id.key_hash, key); |
643 | 0 | } |
644 | 0 | } |
645 | | |
646 | 0 | return false; |
647 | 0 | } |
648 | | |
649 | | // Verifies that |responder_certificate| has been authority for OCSP signing, |
650 | | // delegated to it by |issuer_certificate|. |
651 | | // |
652 | | // TODO(eroman): No revocation checks are done (see id-pkix-ocsp-nocheck in the |
653 | | // spec). extension). |
654 | | // |
655 | | // TODO(eroman): Not all properties of the certificate are verified, only the |
656 | | // signature and EKU. Can full RFC 5280 validation be used, or are there |
657 | | // compatibility concerns? |
658 | | [[nodiscard]] bool VerifyAuthorizedResponderCert( |
659 | | const ParsedCertificate *responder_certificate, |
660 | 0 | const ParsedCertificate *issuer_certificate) { |
661 | | // The Authorized Responder must be directly signed by the issuer of the |
662 | | // certificate being checked. |
663 | | // TODO(eroman): Must check the signature algorithm against policy. |
664 | 0 | if (!responder_certificate->signature_algorithm().has_value() || |
665 | 0 | !VerifySignedData(*responder_certificate->signature_algorithm(), |
666 | 0 | responder_certificate->tbs_certificate_tlv(), |
667 | 0 | responder_certificate->signature_value(), |
668 | 0 | issuer_certificate->tbs().spki_tlv, |
669 | 0 | /*cache=*/nullptr)) { |
670 | 0 | return false; |
671 | 0 | } |
672 | | |
673 | | // The Authorized Responder must include the value id-kp-OCSPSigning as |
674 | | // part of the extended key usage extension. |
675 | 0 | if (!responder_certificate->has_extended_key_usage()) { |
676 | 0 | return false; |
677 | 0 | } |
678 | | |
679 | 0 | for (const auto &key_purpose_oid : |
680 | 0 | responder_certificate->extended_key_usage()) { |
681 | 0 | if (key_purpose_oid == der::Input(kOCSPSigning)) { |
682 | 0 | return true; |
683 | 0 | } |
684 | 0 | } |
685 | 0 | return false; |
686 | 0 | } |
687 | | |
688 | | [[nodiscard]] bool VerifyOCSPResponseSignatureGivenCert( |
689 | 0 | const OCSPResponse &response, const ParsedCertificate *cert) { |
690 | | // TODO(eroman): Must check the signature algorithm against policy. |
691 | 0 | return VerifySignedData(response.signature_algorithm, response.data, |
692 | 0 | response.signature, cert->tbs().spki_tlv, |
693 | 0 | /*cache=*/nullptr); |
694 | 0 | } |
695 | | |
696 | | // Verifies that the OCSP response has a valid signature using |
697 | | // |issuer_certificate|, or an authorized responder issued by |
698 | | // |issuer_certificate| for OCSP signing. |
699 | | [[nodiscard]] bool VerifyOCSPResponseSignature( |
700 | | const OCSPResponse &response, const OCSPResponseData &response_data, |
701 | 0 | const ParsedCertificate *issuer_certificate) { |
702 | | // In order to verify the OCSP signature, a valid responder matching the OCSP |
703 | | // Responder ID must be located (RFC 6960, 4.2.2.2). The responder is allowed |
704 | | // to be either the certificate issuer or a delegated authority directly |
705 | | // signed by the issuer. |
706 | 0 | if (CheckResponderIDMatchesCertificate(response_data.responder_id, |
707 | 0 | issuer_certificate) && |
708 | 0 | VerifyOCSPResponseSignatureGivenCert(response, issuer_certificate)) { |
709 | 0 | return true; |
710 | 0 | } |
711 | | |
712 | | // Otherwise search through the provided certificates for the Authorized |
713 | | // Responder. Want a certificate that: |
714 | | // (1) Matches the OCSP Responder ID. |
715 | | // (2) Has been given authority for OCSP signing by |issuer_certificate|. |
716 | | // (3) Has signed the OCSP response using its public key. |
717 | 0 | for (const auto &responder_cert_tlv : response.certs) { |
718 | 0 | std::shared_ptr<const ParsedCertificate> cur_responder_certificate = |
719 | 0 | OCSPParseCertificate(BytesAsStringView(responder_cert_tlv)); |
720 | | |
721 | | // If failed parsing the certificate, keep looking. |
722 | 0 | if (!cur_responder_certificate) { |
723 | 0 | continue; |
724 | 0 | } |
725 | | |
726 | | // If the certificate doesn't match the OCSP's responder ID, keep looking. |
727 | 0 | if (!CheckResponderIDMatchesCertificate(response_data.responder_id, |
728 | 0 | cur_responder_certificate.get())) { |
729 | 0 | continue; |
730 | 0 | } |
731 | | |
732 | | // If the certificate isn't a valid Authorized Responder certificate, keep |
733 | | // looking. |
734 | 0 | if (!VerifyAuthorizedResponderCert(cur_responder_certificate.get(), |
735 | 0 | issuer_certificate)) { |
736 | 0 | continue; |
737 | 0 | } |
738 | | |
739 | | // If the certificate signed this OCSP response, have found a match. |
740 | | // Otherwise keep looking. |
741 | 0 | if (VerifyOCSPResponseSignatureGivenCert(response, |
742 | 0 | cur_responder_certificate.get())) { |
743 | 0 | return true; |
744 | 0 | } |
745 | 0 | } |
746 | | |
747 | | // Failed to confirm the validity of the OCSP signature using any of the |
748 | | // candidate certificates. |
749 | 0 | return false; |
750 | 0 | } |
751 | | |
752 | | // Parse ResponseData and return false if any unhandled critical extensions are |
753 | | // found. No known critical ResponseData extensions exist. |
754 | | bool ParseOCSPResponseDataExtensions( |
755 | | der::Input response_extensions, |
756 | 0 | OCSPVerifyResult::ResponseStatus *response_details) { |
757 | 0 | std::map<der::Input, ParsedExtension> extensions; |
758 | 0 | if (!ParseExtensions(response_extensions, &extensions)) { |
759 | 0 | *response_details = OCSPVerifyResult::PARSE_RESPONSE_DATA_ERROR; |
760 | 0 | return false; |
761 | 0 | } |
762 | | |
763 | 0 | for (const auto &ext : extensions) { |
764 | | // TODO: handle ResponseData extensions |
765 | |
|
766 | 0 | if (ext.second.critical) { |
767 | 0 | *response_details = OCSPVerifyResult::UNHANDLED_CRITICAL_EXTENSION; |
768 | 0 | return false; |
769 | 0 | } |
770 | 0 | } |
771 | | |
772 | 0 | return true; |
773 | 0 | } |
774 | | |
775 | | // Parse SingleResponse and return false if any unhandled critical extensions |
776 | | // (other than the CT extension) are found. The CT-SCT extension is not required |
777 | | // to be marked critical, but since it is handled by Chrome, we will overlook |
778 | | // the flag setting. |
779 | | bool ParseOCSPSingleResponseExtensions( |
780 | | der::Input single_extensions, |
781 | 0 | OCSPVerifyResult::ResponseStatus *response_details) { |
782 | 0 | std::map<der::Input, ParsedExtension> extensions; |
783 | 0 | if (!ParseExtensions(single_extensions, &extensions)) { |
784 | 0 | *response_details = OCSPVerifyResult::PARSE_RESPONSE_DATA_ERROR; |
785 | 0 | return false; |
786 | 0 | } |
787 | | |
788 | | // The wire form of the OID 1.3.6.1.4.1.11129.2.4.5 - OCSP SingleExtension for |
789 | | // X.509v3 Certificate Transparency Signed Certificate Timestamp List, see |
790 | | // Section 3.3 of RFC6962. |
791 | 0 | const uint8_t ct_ocsp_ext_oid[] = {0x2B, 0x06, 0x01, 0x04, 0x01, |
792 | 0 | 0xD6, 0x79, 0x02, 0x04, 0x05}; |
793 | 0 | der::Input ct_ext_oid(ct_ocsp_ext_oid); |
794 | |
|
795 | 0 | for (const auto &ext : extensions) { |
796 | | // The CT OCSP extension is handled in ct::ExtractSCTListFromOCSPResponse |
797 | 0 | if (ext.second.oid == ct_ext_oid) { |
798 | 0 | continue; |
799 | 0 | } |
800 | | |
801 | | // TODO: handle SingleResponse extensions |
802 | | |
803 | 0 | if (ext.second.critical) { |
804 | 0 | *response_details = OCSPVerifyResult::UNHANDLED_CRITICAL_EXTENSION; |
805 | 0 | return false; |
806 | 0 | } |
807 | 0 | } |
808 | | |
809 | 0 | return true; |
810 | 0 | } |
811 | | |
812 | | // Loops through the OCSPSingleResponses to find the best match for |cert|. |
813 | | OCSPRevocationStatus GetRevocationStatusForCert( |
814 | | const OCSPResponseData &response_data, const ParsedCertificate *cert, |
815 | | const ParsedCertificate *issuer_certificate, |
816 | | int64_t verify_time_epoch_seconds, std::optional<int64_t> max_age_seconds, |
817 | 0 | OCSPVerifyResult::ResponseStatus *response_details) { |
818 | 0 | OCSPRevocationStatus result = OCSPRevocationStatus::UNKNOWN; |
819 | 0 | *response_details = OCSPVerifyResult::NO_MATCHING_RESPONSE; |
820 | |
|
821 | 0 | for (const auto &single_response_der : response_data.responses) { |
822 | | // In the common case, there should only be one SingleResponse in the |
823 | | // ResponseData (matching the certificate requested and used on this |
824 | | // connection). However, it is possible for the OCSP responder to provide |
825 | | // multiple responses for multiple certificates. Look through all the |
826 | | // provided SingleResponses, and check to see if any match the |
827 | | // certificate. A SingleResponse matches a certificate if it has the same |
828 | | // serial number, issuer name (hash), and issuer public key (hash). |
829 | 0 | OCSPSingleResponse single_response; |
830 | 0 | if (!ParseOCSPSingleResponse(single_response_der, &single_response)) { |
831 | 0 | return OCSPRevocationStatus::UNKNOWN; |
832 | 0 | } |
833 | | |
834 | | // Reject unhandled critical extensions in SingleResponse |
835 | 0 | if (single_response.has_extensions && |
836 | 0 | !ParseOCSPSingleResponseExtensions(single_response.extensions, |
837 | 0 | response_details)) { |
838 | 0 | return OCSPRevocationStatus::UNKNOWN; |
839 | 0 | } |
840 | | |
841 | 0 | OCSPCertID cert_id; |
842 | 0 | if (!ParseOCSPCertID(single_response.cert_id_tlv, &cert_id)) { |
843 | 0 | return OCSPRevocationStatus::UNKNOWN; |
844 | 0 | } |
845 | 0 | if (!CheckCertIDMatchesCertificate(cert_id, cert, issuer_certificate)) { |
846 | 0 | continue; |
847 | 0 | } |
848 | | |
849 | | // The SingleResponse matches the certificate, but may be out of date. Out |
850 | | // of date responses are noted seperate from responses with mismatched |
851 | | // serial numbers. If an OCSP responder provides both an up to date |
852 | | // response and an expired response, the up to date response takes |
853 | | // precedence (PROVIDED > INVALID_DATE). |
854 | 0 | if (!CheckRevocationDateValid(single_response.this_update, |
855 | 0 | single_response.has_next_update |
856 | 0 | ? &single_response.next_update |
857 | 0 | : nullptr, |
858 | 0 | verify_time_epoch_seconds, max_age_seconds)) { |
859 | 0 | if (*response_details != OCSPVerifyResult::PROVIDED) { |
860 | 0 | *response_details = OCSPVerifyResult::INVALID_DATE; |
861 | 0 | } |
862 | 0 | continue; |
863 | 0 | } |
864 | | |
865 | | // In the case with multiple matching and up to date responses, keep only |
866 | | // the strictest status (REVOKED > UNKNOWN > GOOD). |
867 | 0 | if (*response_details != OCSPVerifyResult::PROVIDED || |
868 | 0 | result == OCSPRevocationStatus::GOOD || |
869 | 0 | single_response.cert_status.status == OCSPRevocationStatus::REVOKED) { |
870 | 0 | result = single_response.cert_status.status; |
871 | 0 | } |
872 | 0 | *response_details = OCSPVerifyResult::PROVIDED; |
873 | 0 | } |
874 | | |
875 | 0 | return result; |
876 | 0 | } |
877 | | |
878 | | OCSPRevocationStatus CheckOCSP( |
879 | | std::string_view raw_response, std::string_view certificate_der, |
880 | | const ParsedCertificate *certificate, |
881 | | std::string_view issuer_certificate_der, |
882 | | const ParsedCertificate *issuer_certificate, |
883 | | int64_t verify_time_epoch_seconds, std::optional<int64_t> max_age_seconds, |
884 | 0 | OCSPVerifyResult::ResponseStatus *response_details) { |
885 | 0 | *response_details = OCSPVerifyResult::NOT_CHECKED; |
886 | |
|
887 | 0 | if (raw_response.empty()) { |
888 | 0 | *response_details = OCSPVerifyResult::MISSING; |
889 | 0 | return OCSPRevocationStatus::UNKNOWN; |
890 | 0 | } |
891 | | |
892 | 0 | OCSPResponse response; |
893 | 0 | if (!ParseOCSPResponse(StringAsBytes(raw_response), &response)) { |
894 | 0 | *response_details = OCSPVerifyResult::PARSE_RESPONSE_ERROR; |
895 | 0 | return OCSPRevocationStatus::UNKNOWN; |
896 | 0 | } |
897 | | |
898 | | // RFC 6960 defines all responses |response_status| != SUCCESSFUL as error |
899 | | // responses. No revocation information is provided on error responses, and |
900 | | // the OCSPResponseData structure is not set. |
901 | 0 | if (response.status != OCSPResponse::ResponseStatus::SUCCESSFUL) { |
902 | 0 | *response_details = OCSPVerifyResult::ERROR_RESPONSE; |
903 | 0 | return OCSPRevocationStatus::UNKNOWN; |
904 | 0 | } |
905 | | |
906 | | // Actual revocation information is contained within the BasicOCSPResponse as |
907 | | // a ResponseData structure. The BasicOCSPResponse was parsed above, and |
908 | | // contains an unparsed ResponseData. From RFC 6960: |
909 | | // |
910 | | // BasicOCSPResponse ::= SEQUENCE { |
911 | | // tbsResponseData ResponseData, |
912 | | // signatureAlgorithm AlgorithmIdentifier, |
913 | | // signature BIT STRING, |
914 | | // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } |
915 | | // |
916 | | // ResponseData ::= SEQUENCE { |
917 | | // version [0] EXPLICIT Version DEFAULT v1, |
918 | | // responderID ResponderID, |
919 | | // producedAt GeneralizedTime, |
920 | | // responses SEQUENCE OF SingleResponse, |
921 | | // responseExtensions [1] EXPLICIT Extensions OPTIONAL } |
922 | 0 | OCSPResponseData response_data; |
923 | 0 | if (!ParseOCSPResponseData(response.data, &response_data)) { |
924 | 0 | *response_details = OCSPVerifyResult::PARSE_RESPONSE_DATA_ERROR; |
925 | 0 | return OCSPRevocationStatus::UNKNOWN; |
926 | 0 | } |
927 | | |
928 | | // Process the OCSP ResponseData extensions. In particular, must reject if |
929 | | // there are any critical extensions that are not understood. |
930 | 0 | if (response_data.has_extensions && |
931 | 0 | !ParseOCSPResponseDataExtensions(response_data.extensions, |
932 | 0 | response_details)) { |
933 | 0 | return OCSPRevocationStatus::UNKNOWN; |
934 | 0 | } |
935 | | |
936 | 0 | std::shared_ptr<const ParsedCertificate> parsed_certificate; |
937 | 0 | std::shared_ptr<const ParsedCertificate> parsed_issuer_certificate; |
938 | 0 | if (!certificate) { |
939 | 0 | parsed_certificate = OCSPParseCertificate(certificate_der); |
940 | 0 | certificate = parsed_certificate.get(); |
941 | 0 | } |
942 | 0 | if (!issuer_certificate) { |
943 | 0 | parsed_issuer_certificate = OCSPParseCertificate(issuer_certificate_der); |
944 | 0 | issuer_certificate = parsed_issuer_certificate.get(); |
945 | 0 | } |
946 | |
|
947 | 0 | if (!certificate || !issuer_certificate) { |
948 | 0 | *response_details = OCSPVerifyResult::NOT_CHECKED; |
949 | 0 | return OCSPRevocationStatus::UNKNOWN; |
950 | 0 | } |
951 | | |
952 | | // If producedAt is outside of the certificate validity period, reject the |
953 | | // response. |
954 | 0 | if (response_data.produced_at < certificate->tbs().validity_not_before || |
955 | 0 | response_data.produced_at > certificate->tbs().validity_not_after) { |
956 | 0 | *response_details = OCSPVerifyResult::BAD_PRODUCED_AT; |
957 | 0 | return OCSPRevocationStatus::UNKNOWN; |
958 | 0 | } |
959 | | |
960 | | // Look through all of the OCSPSingleResponses for a match (based on CertID |
961 | | // and time). |
962 | 0 | OCSPRevocationStatus status = GetRevocationStatusForCert( |
963 | 0 | response_data, certificate, issuer_certificate, verify_time_epoch_seconds, |
964 | 0 | max_age_seconds, response_details); |
965 | | |
966 | | // Check that the OCSP response has a valid signature. It must either be |
967 | | // signed directly by the issuing certificate, or a valid authorized |
968 | | // responder. |
969 | 0 | if (!VerifyOCSPResponseSignature(response, response_data, |
970 | 0 | issuer_certificate)) { |
971 | 0 | return OCSPRevocationStatus::UNKNOWN; |
972 | 0 | } |
973 | | |
974 | 0 | return status; |
975 | 0 | } |
976 | | |
977 | | } // namespace |
978 | | |
979 | | OCSPRevocationStatus CheckOCSP( |
980 | | std::string_view raw_response, std::string_view certificate_der, |
981 | | std::string_view issuer_certificate_der, int64_t verify_time_epoch_seconds, |
982 | | std::optional<int64_t> max_age_seconds, |
983 | 0 | OCSPVerifyResult::ResponseStatus *response_details) { |
984 | 0 | return CheckOCSP(raw_response, certificate_der, nullptr, |
985 | 0 | issuer_certificate_der, nullptr, verify_time_epoch_seconds, |
986 | 0 | max_age_seconds, response_details); |
987 | 0 | } |
988 | | |
989 | | OCSPRevocationStatus CheckOCSP( |
990 | | std::string_view raw_response, const ParsedCertificate *certificate, |
991 | | const ParsedCertificate *issuer_certificate, |
992 | | int64_t verify_time_epoch_seconds, std::optional<int64_t> max_age_seconds, |
993 | 0 | OCSPVerifyResult::ResponseStatus *response_details) { |
994 | 0 | return CheckOCSP(raw_response, std::string_view(), certificate, |
995 | 0 | std::string_view(), issuer_certificate, |
996 | 0 | verify_time_epoch_seconds, max_age_seconds, |
997 | 0 | response_details); |
998 | 0 | } |
999 | | |
1000 | | bool CreateOCSPRequest(const ParsedCertificate *cert, |
1001 | | const ParsedCertificate *issuer, |
1002 | 0 | std::vector<uint8_t> *request_der) { |
1003 | 0 | request_der->clear(); |
1004 | |
|
1005 | 0 | bssl::ScopedCBB cbb; |
1006 | | |
1007 | | // This initial buffer size is big enough for 20 octet long serial numbers |
1008 | | // (upper bound from RFC 5280) and then a handful of extra bytes. This |
1009 | | // number doesn't matter for correctness. |
1010 | 0 | const size_t kInitialBufferSize = 100; |
1011 | |
|
1012 | 0 | if (!CBB_init(cbb.get(), kInitialBufferSize)) { |
1013 | 0 | return false; |
1014 | 0 | } |
1015 | | |
1016 | | // OCSPRequest ::= SEQUENCE { |
1017 | | // tbsRequest TBSRequest, |
1018 | | // optionalSignature [0] EXPLICIT Signature OPTIONAL } |
1019 | | // |
1020 | | // TBSRequest ::= SEQUENCE { |
1021 | | // version [0] EXPLICIT Version DEFAULT v1, |
1022 | | // requestorName [1] EXPLICIT GeneralName OPTIONAL, |
1023 | | // requestList SEQUENCE OF Request, |
1024 | | // requestExtensions [2] EXPLICIT Extensions OPTIONAL } |
1025 | 0 | CBB ocsp_request; |
1026 | 0 | if (!CBB_add_asn1(cbb.get(), &ocsp_request, CBS_ASN1_SEQUENCE)) { |
1027 | 0 | return false; |
1028 | 0 | } |
1029 | | |
1030 | 0 | CBB tbs_request; |
1031 | 0 | if (!CBB_add_asn1(&ocsp_request, &tbs_request, CBS_ASN1_SEQUENCE)) { |
1032 | 0 | return false; |
1033 | 0 | } |
1034 | | |
1035 | | // "version", "requestorName", and "requestExtensions" are omitted. |
1036 | | |
1037 | 0 | CBB request_list; |
1038 | 0 | if (!CBB_add_asn1(&tbs_request, &request_list, CBS_ASN1_SEQUENCE)) { |
1039 | 0 | return false; |
1040 | 0 | } |
1041 | | |
1042 | 0 | CBB request; |
1043 | 0 | if (!CBB_add_asn1(&request_list, &request, CBS_ASN1_SEQUENCE)) { |
1044 | 0 | return false; |
1045 | 0 | } |
1046 | | |
1047 | | // Request ::= SEQUENCE { |
1048 | | // reqCert CertID, |
1049 | | // singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL } |
1050 | 0 | CBB req_cert; |
1051 | 0 | if (!CBB_add_asn1(&request, &req_cert, CBS_ASN1_SEQUENCE)) { |
1052 | 0 | return false; |
1053 | 0 | } |
1054 | | |
1055 | | // CertID ::= SEQUENCE { |
1056 | | // hashAlgorithm AlgorithmIdentifier, |
1057 | | // issuerNameHash OCTET STRING, -- Hash of issuer's DN |
1058 | | // issuerKeyHash OCTET STRING, -- Hash of issuer's public key |
1059 | | // serialNumber CertificateSerialNumber } |
1060 | | // |
1061 | | // It is unclear whether the parameters for hashAlgorithm should be omitted or |
1062 | | // NULL. Section 2.1 of RFC 4055 would suggest omitting it is the right |
1063 | | // default behavior. However, both OpenSSL and Go include it, so we match them |
1064 | | // for now. |
1065 | | |
1066 | | // TODO(eroman): Don't use SHA1. |
1067 | 0 | const EVP_MD *md = EVP_sha1(); |
1068 | 0 | if (!EVP_marshal_digest_algorithm(&req_cert, md)) { |
1069 | 0 | return false; |
1070 | 0 | } |
1071 | | |
1072 | 0 | AppendHashAsOctetString(md, &req_cert, issuer->tbs().subject_tlv); |
1073 | |
|
1074 | 0 | der::Input key_tlv; |
1075 | 0 | if (!GetSubjectPublicKeyBytes(issuer->tbs().spki_tlv, &key_tlv)) { |
1076 | 0 | return false; |
1077 | 0 | } |
1078 | 0 | AppendHashAsOctetString(md, &req_cert, key_tlv); |
1079 | |
|
1080 | 0 | CBB serial_number; |
1081 | 0 | if (!CBB_add_asn1(&req_cert, &serial_number, CBS_ASN1_INTEGER)) { |
1082 | 0 | return false; |
1083 | 0 | } |
1084 | 0 | if (!CBB_add_bytes(&serial_number, cert->tbs().serial_number.data(), |
1085 | 0 | cert->tbs().serial_number.size())) { |
1086 | 0 | return false; |
1087 | 0 | } |
1088 | | |
1089 | 0 | uint8_t *result_bytes; |
1090 | 0 | size_t result_bytes_length; |
1091 | 0 | if (!CBB_finish(cbb.get(), &result_bytes, &result_bytes_length)) { |
1092 | 0 | return false; |
1093 | 0 | } |
1094 | 0 | bssl::UniquePtr<uint8_t> delete_tbs_cert_bytes(result_bytes); |
1095 | |
|
1096 | 0 | request_der->assign(result_bytes, result_bytes + result_bytes_length); |
1097 | 0 | return true; |
1098 | 0 | } |
1099 | | |
1100 | | // From RFC 2560 section A.1.1: |
1101 | | // |
1102 | | // An OCSP request using the GET method is constructed as follows: |
1103 | | // |
1104 | | // GET {url}/{url-encoding of base-64 encoding of the DER encoding of |
1105 | | // the OCSPRequest} |
1106 | | std::optional<std::string> CreateOCSPGetURL( |
1107 | | const ParsedCertificate *cert, const ParsedCertificate *issuer, |
1108 | 0 | std::string_view ocsp_responder_url) { |
1109 | 0 | std::vector<uint8_t> ocsp_request_der; |
1110 | 0 | if (!CreateOCSPRequest(cert, issuer, &ocsp_request_der)) { |
1111 | | // Unexpected (means BoringSSL failed an operation). |
1112 | 0 | return std::nullopt; |
1113 | 0 | } |
1114 | | |
1115 | | // Base64 encode the request data. |
1116 | 0 | size_t len; |
1117 | 0 | if (!EVP_EncodedLength(&len, ocsp_request_der.size())) { |
1118 | 0 | return std::nullopt; |
1119 | 0 | } |
1120 | 0 | std::vector<uint8_t> encoded(len); |
1121 | 0 | len = EVP_EncodeBlock(encoded.data(), ocsp_request_der.data(), |
1122 | 0 | ocsp_request_der.size()); |
1123 | |
|
1124 | 0 | std::string b64_encoded(encoded.begin(), encoded.begin() + len); |
1125 | | |
1126 | | // In theory +, /, and = are valid in paths and don't need to be escaped. |
1127 | | // However from the example in RFC 5019 section 5 it is clear that the intent |
1128 | | // is to escape non-alphanumeric characters (the example conclusively escapes |
1129 | | // '/' and '=', but doesn't clarify '+'). |
1130 | 0 | b64_encoded = bssl::string_util::FindAndReplace(b64_encoded, "+", "%2B"); |
1131 | 0 | b64_encoded = bssl::string_util::FindAndReplace(b64_encoded, "/", "%2F"); |
1132 | 0 | b64_encoded = bssl::string_util::FindAndReplace(b64_encoded, "=", "%3D"); |
1133 | | |
1134 | | // No attempt is made to collapse double slashes for URLs that end in slash, |
1135 | | // since the spec doesn't do that. |
1136 | 0 | return std::string(ocsp_responder_url) + "/" + b64_encoded; |
1137 | 0 | } |
1138 | | |
1139 | | BSSL_NAMESPACE_END |