Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(831)

Side by Side Diff: net/cert/internal/parse_ocsp.cc

Issue 1541213002: Adding OCSP Parser (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix serial number parsing. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <algorithm>
6
7 #include "base/sha1.h"
8 #include "crypto/sha2.h"
9 #include "net/cert/internal/extended_key_usage.h"
10 #include "net/cert/internal/parse_ocsp.h"
11 #include "net/cert/internal/signature_policy.h"
12 #include "net/cert/internal/verify_name_match.h"
13 #include "net/cert/internal/verify_signed_data.h"
14
15 namespace net {
16
17 namespace {
18
19 // Continues reading from |parser| to extract an OCSP CertStatus (RFC 6960)
20 // and stores the result in the OCSPSingleResponse |out|. Returns whether
21 // the parsing was successful.
22 bool ParseOCSPStatus(der::Parser* parser, OCSPCertStatus* out) {
eroman 2016/02/13 00:56:50 I would find it useful when reading this to duplic
svaldez 2016/02/16 17:25:11 Acknowledged.
23 der::Tag status_tag;
24 der::Input status;
25
26 if (!parser->ReadTagAndValue(&status_tag, &status))
27 return false;
28
29 if (status_tag == der::ContextSpecificPrimitive(0)) {
30 out->status = OCSPCertStatus::Status::GOOD;
31 } else if (status_tag == der::ContextSpecificConstructed(1)) {
32 out->status = OCSPCertStatus::Status::REVOKED;
33 der::Parser revoked_info_parser(status);
34 der::Input revocation_time;
35 der::Input revocation_reason_input;
36 if (!revoked_info_parser.ReadGeneralizedTime(&(out->revocation_time)))
37 return false;
38 if (!revoked_info_parser.ReadOptionalTag(der::ContextSpecificConstructed(0),
39 &revocation_reason_input,
40 &(out->has_reason))) {
eroman 2016/02/13 00:56:49 How about initializing has_reason to false at the
svaldez 2016/02/16 17:25:11 Done.
41 return false;
42 }
43 if (revoked_info_parser.HasMore())
44 return false;
45 if (!out->has_reason)
46 return true;
eroman 2016/02/13 00:56:49 From a future-proofing perspective, earlier return
svaldez 2016/02/16 17:25:11 Done.
47 der::Parser reason_parser(revocation_reason_input);
48 der::Input revocation_reason;
49 if (!reason_parser.ReadTag(der::kEnumerated, &revocation_reason))
eroman 2016/02/13 00:56:49 Why is this der::kEnumerated? I assumed the defin
svaldez 2016/02/16 17:25:10 CRLReason is defined as an ENUMERATED in RFC5912 (
50 return false;
51 uint8_t revocation_reason_value;
52 if (!der::ParseUint8(revocation_reason, &revocation_reason_value))
53 return false;
54 if (revocation_reason_value >=
55 (uint8_t)OCSPCertStatus::RevocationReason::REVOCATION_REASON_MAX) {
eroman 2016/02/13 00:56:49 static_cast<>
svaldez 2016/02/16 17:25:10 Done.
56 return false;
57 }
58 out->revocation_reason =
59 static_cast<OCSPCertStatus::RevocationReason>(revocation_reason_value);
eroman 2016/02/13 00:56:49 I don't see that reason_parser.HasMore() is checke
svaldez 2016/02/16 17:25:11 Done.
60 } else if (status_tag == der::ContextSpecificPrimitive(2)) {
61 out->status = OCSPCertStatus::Status::UNKNOWN;
eroman 2016/02/13 00:56:49 For sanity it would be a good idea to reset all th
svaldez 2016/02/16 17:25:10 Done.
62 } else {
63 return false;
64 }
65
66 return true;
67 }
68
69 // Continues reading from |parser| to extract the ResponderID (RFC 6960) and
70 // stores the result in |out|. Returns whether the parsing was successful.
71 bool ParseOCSPResponder(der::Parser* parser,
72 OCSPResponseData::ResponderID* out) {
73 der::Tag id_tag;
74 der::Input responder_id_input;
75 if (!parser->ReadTagAndValue(&id_tag, &responder_id_input))
76 return false;
77
78 OCSPResponseData::ResponderID responder_id;
eroman 2016/02/13 00:56:50 Why this temporary rather than writing directly to
svaldez 2016/02/16 17:25:11 Done.
79 if (id_tag == der::ContextSpecificConstructed(1)) {
80 responder_id.type = OCSPResponseData::ResponderType::NAME;
81 responder_id.name = responder_id_input;
82 } else if (id_tag == der::ContextSpecificConstructed(2)) {
83 der::Parser key_parser(responder_id_input);
84 der::Input responder_key;
85 if (!key_parser.ReadTag(der::kOctetString, &responder_key))
86 return false;
87 if (key_parser.HasMore())
88 return false;
89
90 SHA1HashValue key_hash;
91 if (responder_key.Length() != sizeof(key_hash.data))
92 return false;
93 memcpy(key_hash.data, responder_key.UnsafeData(), base::kSHA1Length);
eroman 2016/02/13 00:56:49 can you use sizeof(key_hash.data) instead of base:
svaldez 2016/02/16 17:25:11 Done.
94 responder_id.type = OCSPResponseData::ResponderType::KEY_HASH;
95 responder_id.key_hash = HashValue(key_hash);
96 } else {
97 return false;
98 }
99 *out = responder_id;
100 return true;
101 }
102
103 // Continues reading from |parser| to extract a BasicOCSPResponse (RFC 6960)
104 // and stores the result in OCSPResponse |out|. Returns whether the parsing
105 // was successful.
106 bool ParseBasicOCSPResponse(der::Parser* parser, OCSPResponse* out) {
107 der::Input sigalg_tlv;
108 if (!parser->ReadRawTLV(&(out->data)))
109 return false;
110 if (!parser->ReadRawTLV(&sigalg_tlv))
111 return false;
112 out->signature_algorithm = SignatureAlgorithm::CreateFromDer(sigalg_tlv);
113 if (!out->signature_algorithm)
114 return false;
115
116 if (!parser->ReadBitString(&(out->signature)))
117 return false;
118 der::Input certs_input;
119 bool certs_present;
120 if (!parser->ReadOptionalTag(der::ContextSpecificConstructed(0), &certs_input,
121 &certs_present)) {
122 return false;
123 }
124
125 if (parser->HasMore())
eroman 2016/02/13 00:56:50 I think this would be more appropriate in the call
svaldez 2016/02/16 17:25:10 Done.
126 return false;
127
128 if (!certs_present)
129 return true;
eroman 2016/02/13 00:56:49 Same comment as earlier - "return true" short cuic
svaldez 2016/02/16 17:25:10 Done.
130 der::Parser certs_input_parser(certs_input);
131 der::Parser certs_parser;
132 if (!certs_input_parser.ReadSequence(&certs_parser))
133 return false;
134 if (certs_input_parser.HasMore())
135 return false;
136 while (certs_parser.HasMore()) {
137 der::Input cert_tlv;
138 if (!certs_parser.ReadRawTLV(&cert_tlv))
139 return false;
140 out->certs.push_back(cert_tlv);
eroman 2016/02/13 00:56:50 Where is the array first initialized? In other wo
svaldez 2016/02/16 17:25:10 Done.
141 }
142
143 return true;
144 }
145
146 } // namespace
147
148 OCSPCertID::OCSPCertID() {}
149 OCSPCertID::~OCSPCertID() {}
150
151 OCSPSingleResponse::OCSPSingleResponse() {}
152 OCSPSingleResponse::~OCSPSingleResponse() {}
153
154 OCSPResponseData::OCSPResponseData() {}
155 OCSPResponseData::~OCSPResponseData() {}
156
157 OCSPResponse::OCSPResponse() {}
158 OCSPResponse::~OCSPResponse() {}
159
160 der::Input BasicOCSPResponseOid() {
161 // From RFC 6960:
162 //
163 // id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp }
164 // id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 }
165 //
166 // In dotted notation: 1.3.6.1.5.5.7.48.1.1
167 static const uint8_t oid[] = {0x2b, 0x06, 0x01, 0x05, 0x05,
168 0x07, 0x30, 0x01, 0x01};
169 return der::Input(oid);
170 }
171
172 bool ParseOCSPCertID(der::Input raw_tlv, OCSPCertID* out) {
173 der::Parser outer_parser(raw_tlv);
174 der::Parser parser;
175 if (!outer_parser.ReadSequence(&parser))
176 return false;
177 der::Input sigalg_tlv;
178 if (!parser.ReadRawTLV(&sigalg_tlv))
179 return false;
180 if (!ParseHashAlgorithm(sigalg_tlv, &(out->hash_algorithm)))
181 return false;
182 if (!parser.ReadTag(der::kOctetString, &(out->issuer_name_hash)))
183 return false;
184 if (!parser.ReadTag(der::kOctetString, &(out->issuer_key_hash)))
185 return false;
186 if (!parser.ReadTag(der::kInteger, &(out->serial_number)))
eroman 2016/02/13 00:56:50 This is more lenient then the certificate serial n
svaldez 2016/02/16 17:25:11 Done.
187 return false;
188 return !parser.HasMore();
189 }
190
191 bool ParseOCSPSingleResponse(der::Input raw_tlv, OCSPSingleResponse* out) {
192 der::Parser response_parser(raw_tlv);
193 der::Parser parser;
194 if (!response_parser.ReadSequence(&parser))
195 return false;
196 if (response_parser.HasMore())
197 return false;
198 if (!parser.ReadRawTLV(&(out->cert_id_tlv)))
199 return false;
200 if (!ParseOCSPStatus(&parser, &(out->cert_status)))
201 return false;
202 der::Input this_update;
203 if (!parser.ReadGeneralizedTime(&(out->this_update)))
204 return false;
205 der::Input next_update_input;
206 if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(0),
207 &next_update_input, &(out->has_next_update))) {
208 return false;
209 }
210
211 if (out->has_next_update) {
212 der::Parser next_update_parser(next_update_input);
213 der::Input next_update;
214 if (!next_update_parser.ReadGeneralizedTime(&(out->next_update)))
215 return false;
216 if (next_update_parser.HasMore())
217 return false;
218 }
219
220 if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(1),
221 &(out->extensions), &(out->has_extensions))) {
222 return false;
223 }
224
225 return !parser.HasMore();
226 }
227
228 bool ParseOCSPResponseData(der::Input raw_tlv, OCSPResponseData* out) {
229 der::Parser outer_parser(raw_tlv);
230 der::Parser parser;
231 if (!outer_parser.ReadSequence(&parser))
232 return false;
233 if (outer_parser.HasMore())
234 return false;
235
236 der::Input version_input;
237 bool version_present;
238 if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(0),
239 &version_input, &version_present)) {
240 return false;
241 }
242
243 // We ignore the restriction that the DEFAULT Version v1 shouldn't be
eroman 2016/02/13 00:56:49 Per previous comment, try to avoid "We" when possi
svaldez 2016/02/16 17:25:10 Done.
244 // specified due to many implementations including it regardless of the DER
245 // spec.
246 if (version_present) {
247 der::Parser version_parser(version_input);
248 der::Input version;
249 if (!version_parser.ReadTag(der::kInteger, &version))
250 return false;
251 if (version_parser.HasMore())
252 return false;
253 if (!der::ParseUint8(version, &(out->version)))
254 return false;
255 } else {
256 out->version = 0;
257 }
258
259 if (!ParseOCSPResponder(&parser, &(out->responder_id)))
260 return false;
261
262 der::Input produced_at;
263 if (!parser.ReadGeneralizedTime(&(out->produced_at)))
264 return false;
265
266 der::Parser responses_parser;
267 if (!parser.ReadSequence(&responses_parser))
268 return false;
269 out->responses.clear();
270 while (responses_parser.HasMore()) {
271 der::Input single_response;
272 if (!responses_parser.ReadRawTLV(&single_response))
273 return false;
274 out->responses.push_back(single_response);
275 }
276
277 if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(1),
278 &(out->extensions), &(out->has_extensions))) {
279 return false;
280 }
281
282 return !parser.HasMore();
283 }
284
285 bool ParseOCSPResponse(der::Input ocsp_response, OCSPResponse* out) {
eroman 2016/02/13 00:56:49 Would be useful to see the ASN.1 structure duplica
svaldez 2016/02/16 17:25:11 Done.
286 der::Parser parser(ocsp_response);
287 der::Input response_status;
288 der::Parser ocsp_response_parser;
289 if (!parser.ReadSequence(&ocsp_response_parser))
290 return false;
291 if (parser.HasMore())
292 return false;
293 if (!ocsp_response_parser.ReadTag(der::kEnumerated, &response_status))
294 return false;
295 uint8_t response_status_value;
296 if (!der::ParseUint8(response_status, &response_status_value))
297 return false;
298 if (response_status_value >=
299 (uint8_t)OCSPResponse::ResponseStatus::RESPONSE_STATUS_MAX) {
eroman 2016/02/13 00:56:49 static_cast
svaldez 2016/02/16 17:25:10 Done.
300 return false;
301 }
302 out->status =
303 static_cast<OCSPResponse::ResponseStatus>(response_status_value);
304 if (out->status != OCSPResponse::ResponseStatus::SUCCESSFUL)
305 return true;
eroman 2016/02/13 00:56:50 Same comment as earlier regarding "return true;" s
svaldez 2016/02/16 17:25:11 Done.
306
307 der::Input response_type_oid;
308 der::Input response_string;
309 der::Parser response_bytes_input_parser;
310 der::Parser response_bytes_parser;
311 if (!ocsp_response_parser.ReadConstructed(der::ContextSpecificConstructed(0),
eroman 2016/02/13 00:56:50 Is this allowed to be omitted in the case of succe
svaldez 2016/02/16 17:25:10 responseBytes must be set if the status is SUCCESS
312 &response_bytes_input_parser)) {
313 return false;
314 }
315 if (ocsp_response_parser.HasMore())
316 return false;
317 if (!response_bytes_input_parser.ReadSequence(&response_bytes_parser))
eroman 2016/02/13 00:56:49 I would find it easier to read if each sequence is
svaldez 2016/02/16 17:25:10 Moved the sequence parsing into BasicResponse pars
318 return false;
319 if (response_bytes_input_parser.HasMore())
320 return false;
321 if (!response_bytes_parser.ReadTag(der::kOid, &response_type_oid))
322 return false;
323 if (response_type_oid != BasicOCSPResponseOid())
eroman 2016/02/13 00:56:50 (Note: Not familiar enough if this is the only OID
svaldez 2016/02/16 17:25:10 Its the only defined OID currently.
324 return false;
325 if (!response_bytes_parser.ReadTag(der::kOctetString, &response_string))
eroman 2016/02/13 00:56:50 Please provide documentation here that the octet s
svaldez 2016/02/16 17:25:11 Done.
326 return false;
327 if (response_bytes_parser.HasMore())
328 return false;
329 der::Parser response_parser(response_string);
330 der::Parser basic_response_parser;
331 if (!response_parser.ReadSequence(&basic_response_parser))
332 return false;
333 if (response_parser.HasMore())
334 return false;
335 return ParseBasicOCSPResponse(&basic_response_parser, out);
336 }
337
338 namespace {
339
340 bool CheckResponder(OCSPResponseData::ResponderID id,
eroman 2016/02/13 00:56:50 The non-parsing functions feel like they belong in
svaldez 2016/02/16 17:25:11 I'll split out the non-parsing bits after finishin
341 ParsedTbsCertificate cert) {
342 if (id.type == OCSPResponseData::ResponderType::NAME) {
343 der::Input name_rdn;
344 der::Input cert_rdn;
345 if (!der::Parser(id.name).ReadTag(der::kSequence, &name_rdn) ||
346 !der::Parser(cert.subject_tlv).ReadTag(der::kSequence, &cert_rdn))
347 return false;
348 return VerifyNameMatch(name_rdn, cert_rdn);
349 } else {
350 der::Parser parser(cert.spki_tlv);
351 der::Parser spki_parser;
352 der::BitString key_bits;
353 if (!parser.ReadSequence(&spki_parser))
354 return false;
355 if (!spki_parser.SkipTag(der::kSequence))
356 return false;
357 if (!spki_parser.ReadBitString(&key_bits))
358 return false;
359
360 der::Input key = key_bits.bytes();
361 HashValue key_hash(HASH_VALUE_SHA1);
362 base::SHA1HashBytes(key.UnsafeData(), key.Length(), key_hash.data());
363 return key_hash.Equals(id.key_hash);
364 }
365 }
366
367 bool CheckCertID(der::Input id_tlv,
368 ParsedTbsCertificate issuer,
369 der::Input serial_number) {
370 OCSPCertID id;
371 if (!ParseOCSPCertID(id_tlv, &id))
372 return false;
373
374 HashValueTag type;
375 switch (id.hash_algorithm) {
376 case DigestAlgorithm::Sha1:
377 type = HASH_VALUE_SHA1;
378 break;
379 case DigestAlgorithm::Sha256:
380 type = HASH_VALUE_SHA256;
381 break;
382 default:
383 NOTIMPLEMENTED();
384 return false;
385 }
386
387 HashValue id_name_hash(type);
388 if (id.issuer_name_hash.Length() != id_name_hash.size())
389 return false;
390 memcpy(id_name_hash.data(), id.issuer_name_hash.UnsafeData(),
391 id_name_hash.size());
392 HashValue issuer_name_hash(type);
393 der::Input name = issuer.subject_tlv;
394 if (type == HASH_VALUE_SHA1)
395 base::SHA1HashBytes(name.UnsafeData(), name.Length(),
396 issuer_name_hash.data());
397 else {
398 std::string hash = crypto::SHA256HashString(name.AsString());
399 memcpy(issuer_name_hash.data(), hash.data(), issuer_name_hash.size());
400 }
401 if (!id_name_hash.Equals(issuer_name_hash))
402 return false;
403
404 HashValue id_key_hash(type);
405 if (id.issuer_key_hash.Length() != id_key_hash.size())
406 return false;
407 memcpy(id_key_hash.data(), id.issuer_key_hash.UnsafeData(),
408 id_key_hash.size());
409 HashValue issuer_key_hash(type);
410 der::Parser parser(issuer.spki_tlv);
411 der::Parser spki_parser;
412 der::BitString key_bits;
413 if (!parser.ReadSequence(&spki_parser))
414 return false;
415 if (!spki_parser.SkipTag(der::kSequence))
416 return false;
417 if (!spki_parser.ReadBitString(&key_bits))
418 return false;
419 der::Input key = key_bits.bytes();
420 if (type == HASH_VALUE_SHA1)
421 base::SHA1HashBytes(key.UnsafeData(), key.Length(), issuer_key_hash.data());
422 else {
423 std::string hash = crypto::SHA256HashString(key.AsString());
424 memcpy(issuer_key_hash.data(), hash.data(), issuer_key_hash.size());
425 }
426
427 if (!id_key_hash.Equals(issuer_key_hash))
428 return false;
429
430 return id.serial_number == serial_number;
431 }
432
433 } // namespace
434
435 bool VerifyOCSPResponse(const OCSPResponse* response,
436 const ParsedCertificate* issuer_cert) {
437 SimpleSignaturePolicy signature_policy(1024);
438
439 OCSPResponseData response_data;
440 if (!ParseOCSPResponseData(response->data, &response_data))
441 return false;
442
443 ParsedTbsCertificate issuer;
444 ParsedTbsCertificate responder;
445 if (!issuer_cert ||
446 !ParseTbsCertificate(issuer_cert->tbs_certificate_tlv, &issuer))
447 return false;
448
449 if (CheckResponder(response_data.responder_id, issuer)) {
450 responder = issuer;
451 } else {
452 bool found = false;
453 for (const auto& responder_cert_tlv : response->certs) {
454 ParsedCertificate responder_cert;
455 ParsedTbsCertificate tbs_cert;
456 if (!ParseCertificate(responder_cert_tlv, &responder_cert))
457 return false;
458 if (!ParseTbsCertificate(responder_cert.tbs_certificate_tlv, &tbs_cert))
459 return false;
460
461 if (CheckResponder(response_data.responder_id, tbs_cert)) {
462 found = true;
463 responder = tbs_cert;
464
465 scoped_ptr<SignatureAlgorithm> signature_algorithm =
466 SignatureAlgorithm::CreateFromDer(
467 responder_cert.signature_algorithm_tlv);
468 der::Input issuer_spki = issuer.spki_tlv;
469 if (!VerifySignedData(*signature_algorithm,
470 responder_cert.tbs_certificate_tlv,
471 responder_cert.signature_value, issuer_spki,
472 &signature_policy)) {
473 return false;
474 }
475
476 std::map<der::Input, ParsedExtension> extensions;
477 std::vector<der::Input> eku;
478 if (!ParseExtensions(responder.extensions_tlv, &extensions))
479 return false;
480 if (!ParseEKUExtension(extensions[ExtKeyUsageOid()].value, &eku))
481 return false;
482 if (std::find(eku.begin(), eku.end(), OCSPSigning()) == eku.end())
483 return false;
484 break;
485 }
486 }
487 if (!found)
488 return false;
489 }
490 return VerifySignedData(*(response->signature_algorithm), response->data,
491 response->signature, responder.spki_tlv,
492 &signature_policy);
493 }
494
495 bool GetOCSPCertStatus(const OCSPResponseData* response_data,
496 const ParsedCertificate* issuer,
497 const ParsedCertificate* cert,
498 OCSPCertStatus* out) {
499 out->status = OCSPCertStatus::Status::UNKNOWN;
500
501 ParsedTbsCertificate tbs_cert;
502 if (!ParseTbsCertificate(cert->tbs_certificate_tlv, &tbs_cert))
503 return false;
504 ParsedTbsCertificate issuer_tbs_cert;
505 if (!ParseTbsCertificate(issuer->tbs_certificate_tlv, &issuer_tbs_cert))
506 return false;
507
508 for (const auto& response : response_data->responses) {
509 OCSPSingleResponse single_response;
510 if (!ParseOCSPSingleResponse(response, &single_response))
511 return false;
512 if (CheckCertID(single_response.cert_id_tlv, issuer_tbs_cert,
513 tbs_cert.serial_number)) {
514 *out = single_response.cert_status;
515 if (single_response.cert_status.status != OCSPCertStatus::Status::GOOD)
516 return true;
517 }
518 }
519
520 return true;
521 }
522
523 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698