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

Side by Side Diff: net/cert/asn1_util.cc

Issue 92443002: Extract Certificate Transparency SCTs from stapled OCSP responses (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@extract_scts
Patch Set: rebase and wire extracted SCTs to the CT verifier Created 7 years 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/cert/asn1_util.h" 5 #include "net/cert/asn1_util.h"
6 6
7 namespace net { 7 namespace net {
8 8
9 namespace asn1 { 9 namespace asn1 {
10 10
(...skipping 308 matching lines...) Expand 10 before | Expand all | Expand 10 after
319 return false; 319 return false;
320 } 320 }
321 } 321 }
322 } 322 }
323 } 323 }
324 324
325 urls_out->swap(tmp_urls_out); 325 urls_out->swap(tmp_urls_out);
326 return true; 326 return true;
327 } 327 }
328 328
329 bool ExtractSCTExtensionFromOCSPResponse(
330 base::StringPiece ocsp_response,
331 const base::StringPiece& cert_serial_number,
332 base::StringPiece* sct_list_out) {
333 sct_list_out->clear();
334
335 // OCSPResponse ::= SEQUENCE {
336 // responseStatus OCSPResponseStatus,
337 // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
338 base::StringPiece ocsp_response_sequence;
339 if (!GetElement(&ocsp_response, kSEQUENCE, &ocsp_response_sequence))
340 return false;
341 if (!ocsp_response.empty())
342 return false;
343
344 // responseStatus
345 if (!GetElement(&ocsp_response_sequence, kENUMERATED, NULL))
346 return false;
347
348 // responseBytes
349 base::StringPiece response_bytes_outer;
350 if (!GetElement(&ocsp_response_sequence,
351 kOptional | kConstructed | kContextSpecific | 0,
352 &response_bytes_outer))
353 return false;
wtc 2013/12/03 01:18:06 Nit: add curly braces {} because the conditional e
ekasper 2013/12/03 13:50:51 Done.
354 if (!ocsp_response_sequence.empty())
355 return false;
356
357 // ResponseBytes ::= SEQUENCE {
358 // responseType OBJECT IDENTIFIER,
359 // response OCTET STRING } -- DER encoding of BasicOCSPResponse
360 base::StringPiece response_bytes;
361 if (!GetElement(&response_bytes_outer, kSEQUENCE, &response_bytes))
362 return false;
363 if (!response_bytes_outer.empty())
364 return false;
365
366 // responseType
367 base::StringPiece response_type;
368 if (!GetElement(&response_bytes, kOID, &response_type))
369 return false;
370
371 // id-ad-ocsp: 1.3.6.1.5.5.7.48.1.1
372 static const uint8 kBasicOCSPResponseOID[] = {0x2b, 0x06, 0x01, 0x05, 0x05,
373 0x07, 0x30, 0x01, 0x01};
wtc 2013/12/03 01:18:06 Nit: add a space after '{' and before '}'.
ekasper 2013/12/03 13:50:51 Done.
374
375 if (response_type.size() != sizeof(kBasicOCSPResponseOID) ||
376 memcmp(response_type.data(), kBasicOCSPResponseOID,
377 response_type.size()) != 0)
378 return false;
379
380 base::StringPiece wrapped_response;
381 if (!GetElement(&response_bytes, kOCTETSTRING, &wrapped_response))
382 return false;
383
384 if (!response_bytes.empty())
385 return false;
386
387 // BasicOCSPResponse ::= SEQUENCE {
388 // tbsResponseData ResponseData,
389 // signatureAlgorithm AlgorithmIdentifier,
390 // signature BIT STRING,
391 // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
392 base::StringPiece basic_ocsp_response;
393 if (!GetElement(&wrapped_response, kSEQUENCE, &basic_ocsp_response))
394 return false;
395 if (!wrapped_response.empty())
396 return false;
397
398 // ResponseData ::= SEQUENCE {
399 // version [0] EXPLICIT Version DEFAULT v1,
400 // responderID ResponderID,
401 // producedAt GeneralizedTime,
402 // responses SEQUENCE OF SingleResponse,
403 // responseExtensions [1] EXPLICIT Extensions OPTIONAL }
404 base::StringPiece tbs_response_data;
405 if (!GetElement(&basic_ocsp_response, kSEQUENCE, &tbs_response_data))
406 return false;
407
408 // version
409 if (!GetElement(&tbs_response_data,
410 kOptional | kConstructed | kContextSpecific | 0, NULL))
411 return false;
412
413 // ResponderID ::= CHOICE {
414 // byName [1] Name,
415 // byKey [2] KeyHash }
416 if (!GetElement(&tbs_response_data,
417 kConstructed | kContextSpecific | 1, NULL) &&
418 !GetElement(&tbs_response_data,
419 kConstructed | kContextSpecific | 2, NULL))
420 return false;
421
422 // producedAt
423 if (!GetElement(&tbs_response_data, kGENERALIZEDTIME, NULL))
424 return false;
425
426 // responses: SEQUENCE OF and SEQUENCE share the same tag.
427 base::StringPiece responses;
428 if (!GetElement(&tbs_response_data, kSEQUENCE, &responses))
429 return false;
430
431 while (responses.size() > 0) {
wtc 2013/12/03 01:18:06 Nit: this function is very long. If we want to bre
ekasper 2013/12/03 13:50:51 I've broken it up so that the seeking to the right
432 // SingleResponse ::= SEQUENCE {
433 // certID CertID,
434 // certStatus CertStatus,
435 // thisUpdate GeneralizedTime,
436 // nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
437 // singleExtensions [1] EXPLICIT Extensions OPTIONAL }
438 base::StringPiece single_response;
439 if (!GetElement(&responses, kSEQUENCE, &single_response))
440 return false;
441
442 // CertID ::= SEQUENCE {
443 // hashAlgorithm AlgorithmIdentifier,
444 // issuerNameHash OCTET STRING, -- Hash of Issuer's DN
445 // issuerKeyHash OCTET STRING, -- Hash of Issuers public key
446 // serialNumber CertificateSerialNumber }
447 base::StringPiece cert_id;
448 if (!GetElement(&single_response, kSEQUENCE, &cert_id))
449 return false;
450
451 // hashAlgorithm
452 if (!GetElement(&cert_id, kSEQUENCE, NULL))
453 return false;
454 // issuerNameHash
455 if (!GetElement(&cert_id, kOCTETSTRING, NULL))
456 return false;
457 // issuerKeyHash
458 if (!GetElement(&cert_id, kOCTETSTRING, NULL))
459 return false;
460
461 base::StringPiece serial_number;
462 if (!GetElement(&cert_id, kINTEGER, &serial_number))
463 return false;
464
465 if (serial_number != cert_serial_number) {
466 // Serial number mismatch - continue.
467 continue;
468 }
469
470 // Serial number match: continue to see if the CT extension is present.
471 // CertStatus ::= CHOICE {
472 // good [0] IMPLICIT NULL,
473 // revoked [1] IMPLICIT RevokedInfo,
474 // unknown [2] IMPLICIT UnknownInfo }
475 if (!GetElement(&single_response, kContextSpecific | 0, NULL) &&
476 !GetElement(&single_response,
477 kContextSpecific | kConstructed | 1, NULL) &&
478 !GetElement(&single_response, kContextSpecific | 2, NULL))
479 return false;
480
481 // thisUpdate
482 if (!GetElement(&single_response, kGENERALIZEDTIME, NULL))
483 return false;
484 // nextUpdate
485 if (!GetElement(&single_response,
486 kConstructed | kOptional | kContextSpecific | 0, NULL))
487 return false;
488
489 base::StringPiece single_extensions_outer;
490 if (!GetElement(&single_response,
491 kConstructed | kOptional | kContextSpecific | 1,
492 &single_extensions_outer))
493 return false;
494 if (!single_response.empty())
495 return false;
496
497 if (single_extensions_outer.empty()) {
498 // No extensions; and since we should only have one singleResponse per
499 // certificate, we stop here.
500 return true;
501 }
502
503 // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
504 base::StringPiece single_extensions;
505 if (!GetElement(&single_extensions_outer, kSEQUENCE, &single_extensions))
506 return false;
507
508 // Extension ::= SEQUENCE {
509 // extnID OBJECT IDENTIFIER,
510 // critical BOOLEAN DEFAULT FALSE,
511 // extnValue OCTET STRING
512 // -- contains the DER encoding of an ASN.1 value
513 // -- corresponding to the extension type identified
514 // -- by extnID
515 // }
516
517 while (!single_extensions.empty()) {
518 base::StringPiece extension;
519 if (!GetElement(&single_extensions, kSEQUENCE, &extension))
520 return false;
521
522 base::StringPiece extn_id;
523 if (!GetElement(&extension, kOID, &extn_id))
524 return false;
525
526 static const uint8 kSignedCertTimestampListOID[] = {
527 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x05
528 };
529 if (extn_id.size() != sizeof(kSignedCertTimestampListOID) ||
530 memcmp(extn_id.data(), kSignedCertTimestampListOID,
531 extn_id.size() != 0))
532 continue;
533
534 // critical
535 if (!GetElement(&extension, kBOOLEAN, NULL))
536 return false;
537
538 // extnValue
539 base::StringPiece extn_value;
540 if (!GetElement(&extension, kOCTETSTRING, &extn_value))
541 return false;
542 if (!extension.empty())
543 return false;
544
545 // The value of this extension is an OCTET STRING:
546 // SignedCertificateTimestampList ::= OCTET STRING
547 base::StringPiece sct_list;
548 if (!GetElement(&extn_value, kOCTETSTRING, &sct_list))
549 return false;
550 *sct_list_out = sct_list;
551 return true;
552 }
wtc 2013/12/03 01:18:06 We should also return true at the end of this (inn
ekasper 2013/12/03 13:50:51 Good catch, done as you suggested, break; followed
553 }
554
555 // No extension found.
556 return true;
wtc 2013/12/03 01:18:06 With the change I proposed above, the only reason
ekasper 2013/12/03 13:50:51 Yes, a false is probably better in this case. This
557 }
558
329 } // namespace asn1 559 } // namespace asn1
330 560
331 } // namespace net 561 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698