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

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: review comments 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 // ExtractSingleResponses extracts the ASN.1 'responses' element
330 // (SEQUENCE OF SingleResponse contents, excluding the SEQUENCE tag and length)
331 // from the 'tbsResponseData' of a BasicOCSPResponse. On successful return,
332 // |single_responses| points into |ocsp_response|.
333 static bool ExtractSingleResponses(base::StringPiece ocsp_response,
334 base::StringPiece* single_responses) {
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;
354 }
355 if (!ocsp_response_sequence.empty())
356 return false;
357
358 // ResponseBytes ::= SEQUENCE {
359 // responseType OBJECT IDENTIFIER,
360 // response OCTET STRING } -- DER encoding of BasicOCSPResponse
361 base::StringPiece response_bytes;
362 if (!GetElement(&response_bytes_outer, kSEQUENCE, &response_bytes))
363 return false;
364 if (!response_bytes_outer.empty())
365 return false;
366
367 // responseType
368 base::StringPiece response_type;
369 if (!GetElement(&response_bytes, kOID, &response_type))
370 return false;
371
372 // id-ad-ocsp: 1.3.6.1.5.5.7.48.1.1
373 static const uint8 kBasicOCSPResponseOID[] = { 0x2b, 0x06, 0x01, 0x05, 0x05,
374 0x07, 0x30, 0x01, 0x01 };
375
376 if (response_type.size() != sizeof(kBasicOCSPResponseOID) ||
377 memcmp(response_type.data(), kBasicOCSPResponseOID,
378 response_type.size()) != 0) {
379 return false;
380 }
381
382 base::StringPiece wrapped_response;
383 if (!GetElement(&response_bytes, kOCTETSTRING, &wrapped_response))
384 return false;
385
386 if (!response_bytes.empty())
387 return false;
388
389 // BasicOCSPResponse ::= SEQUENCE {
390 // tbsResponseData ResponseData,
391 // signatureAlgorithm AlgorithmIdentifier,
392 // signature BIT STRING,
393 // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
394 base::StringPiece basic_ocsp_response;
395 if (!GetElement(&wrapped_response, kSEQUENCE, &basic_ocsp_response))
396 return false;
397 if (!wrapped_response.empty())
398 return false;
399
400 // ResponseData ::= SEQUENCE {
401 // version [0] EXPLICIT Version DEFAULT v1,
402 // responderID ResponderID,
403 // producedAt GeneralizedTime,
404 // responses SEQUENCE OF SingleResponse,
405 // responseExtensions [1] EXPLICIT Extensions OPTIONAL }
406 base::StringPiece tbs_response_data;
407 if (!GetElement(&basic_ocsp_response, kSEQUENCE, &tbs_response_data))
408 return false;
409
410 // version
411 if (!GetElement(&tbs_response_data,
412 kOptional | kConstructed | kContextSpecific | 0, NULL)) {
413 return false;
414 }
415
416 // ResponderID ::= CHOICE {
417 // byName [1] Name,
418 // byKey [2] KeyHash }
419 if (!GetElement(&tbs_response_data,
420 kConstructed | kContextSpecific | 1, NULL) &&
421 !GetElement(&tbs_response_data,
422 kConstructed | kContextSpecific | 2, NULL)) {
423 return false;
424 }
425
426 // producedAt
427 if (!GetElement(&tbs_response_data, kGENERALIZEDTIME, NULL))
428 return false;
429
430 // responses: SEQUENCE OF and SEQUENCE share the same tag.
431 base::StringPiece responses;
432 if (!GetElement(&tbs_response_data, kSEQUENCE, &responses))
433 return false;
wtc 2013/12/03 21:04:25 Nit: you are meticulous in checking there is no ju
434
435 *single_responses = responses;
436 return true;
437 }
438
439 // ExtractExtensionsWithMatchingCertID extracts the 'singleExtensions' inner
wtc 2013/12/03 21:04:25 Nit: this comment says "inner contents", but the c
440 // contents (Extensions) of a SingleResponse matching the given certificate
441 // identifiers. Returns False if ASN.1 parsing failed, or if the OCSP response
wtc 2013/12/03 21:04:25 Nit: "identifiers" => "identifier" or "serial numb
442 // did not contain a matching SingleResponse. On successful return,
443 // |single_extensions| is either empty (no extensions found in the matching
444 // response) or points into |single_responses|.
445 static bool ExtractExtensionsWithMatchingCertID(
446 base::StringPiece single_responses,
447 base::StringPiece cert_serial_number,
448 base::StringPiece* single_extensions) {
449 while (single_responses.size() > 0) {
wtc 2013/12/03 21:04:25 Nit: use !single_responses.empty(), to be consiste
450 // SingleResponse ::= SEQUENCE {
451 // certID CertID,
452 // certStatus CertStatus,
453 // thisUpdate GeneralizedTime,
454 // nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
455 // singleExtensions [1] EXPLICIT Extensions OPTIONAL }
456 base::StringPiece single_response;
457 if (!GetElement(&single_responses, kSEQUENCE, &single_response))
458 return false;
459
460 // CertID ::= SEQUENCE {
461 // hashAlgorithm AlgorithmIdentifier,
462 // issuerNameHash OCTET STRING, -- Hash of Issuer's DN
463 // issuerKeyHash OCTET STRING, -- Hash of Issuers public key
464 // serialNumber CertificateSerialNumber }
465 base::StringPiece cert_id;
466 if (!GetElement(&single_response, kSEQUENCE, &cert_id))
467 return false;
468
469 // hashAlgorithm
470 if (!GetElement(&cert_id, kSEQUENCE, NULL))
471 return false;
472 // issuerNameHash
473 if (!GetElement(&cert_id, kOCTETSTRING, NULL))
474 return false;
475 // issuerKeyHash
476 if (!GetElement(&cert_id, kOCTETSTRING, NULL))
477 return false;
478
479 base::StringPiece serial_number;
480 if (!GetElement(&cert_id, kINTEGER, &serial_number))
481 return false;
482
483 if (serial_number != cert_serial_number) {
484 // Serial number mismatch - continue.
485 continue;
486 }
487 // Serial number match: continue to see if the CT extension is present.
488 // CertStatus ::= CHOICE {
489 // good [0] IMPLICIT NULL,
490 // revoked [1] IMPLICIT RevokedInfo,
491 // unknown [2] IMPLICIT UnknownInfo }
492 if (!GetElement(&single_response, kContextSpecific | 0, NULL) &&
493 !GetElement(&single_response,
494 kContextSpecific | kConstructed | 1, NULL) &&
495 !GetElement(&single_response, kContextSpecific | 2, NULL)) {
496 return false;
497 }
498
499 // thisUpdate
500 if (!GetElement(&single_response, kGENERALIZEDTIME, NULL))
501 return false;
502 // nextUpdate
503 if (!GetElement(&single_response,
504 kConstructed | kOptional | kContextSpecific | 0, NULL)) {
505 return false;
506 }
507
508 base::StringPiece single_extensions_outer;
509 if (!GetElement(&single_response,
510 kConstructed | kOptional | kContextSpecific | 1,
511 &single_extensions_outer)) {
512 return false;
513 }
514 if (!single_response.empty())
515 return false;
516
517 *single_extensions = single_extensions_outer;
518 return true;
519 }
520
521 // Match not found.
522 return false;
523 }
524
525 bool ExtractSCTExtensionFromOCSPResponse(
wtc 2013/12/03 21:04:25 I found that NSS has a function for decoding an OC
526 base::StringPiece ocsp_response,
527 base::StringPiece cert_serial_number,
528 base::StringPiece* sct_list_out) {
529 sct_list_out->clear();
530
531 base::StringPiece single_responses;
532 if (!ExtractSingleResponses(ocsp_response, &single_responses))
533 return false;
534
535 base::StringPiece single_extensions;
536 if (!ExtractExtensionsWithMatchingCertID(single_responses,
537 cert_serial_number,
538 &single_extensions)) {
539 return false;
540 }
541
542 if (single_extensions.empty()) {
543 // Extensions are optional.
544 return true;
545 }
546
547 // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
548 base::StringPiece single_extensions_inner;
549 if (!GetElement(&single_extensions, kSEQUENCE, &single_extensions_inner))
550 return false;
wtc 2013/12/03 21:04:25 Nit: you didn't check for extra junk at the end of
551
552 if (single_extensions_inner.empty()) {
553 // Extensions, if present, should contain at least one element.
554 return false;
555 }
556
557 // Extension ::= SEQUENCE {
558 // extnID OBJECT IDENTIFIER,
559 // critical BOOLEAN DEFAULT FALSE,
560 // extnValue OCTET STRING
561 // -- contains the DER encoding of an ASN.1 value
562 // -- corresponding to the extension type identified
563 // -- by extnID
564 // }
565 while (!single_extensions_inner.empty()) {
566 base::StringPiece extension;
567 if (!GetElement(&single_extensions_inner, kSEQUENCE, &extension))
568 return false;
569
570 base::StringPiece extn_id;
571 if (!GetElement(&extension, kOID, &extn_id))
572 return false;
573
574 static const uint8 kSignedCertTimestampListOID[] = {
575 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x05
576 };
577 if (extn_id.size() != sizeof(kSignedCertTimestampListOID) ||
578 memcmp(extn_id.data(), kSignedCertTimestampListOID,
579 extn_id.size() != 0)) {
wtc 2013/12/03 21:04:25 BUG: extn_id.size() != 0)) => extn_id.size()) != 0
ekasper 2013/12/04 19:25:15 This has gone away but... YIKES. Good catch.
580 continue;
581 }
582
583 // critical
584 if (!GetElement(&extension, kBOOLEAN, NULL))
wtc 2013/12/03 21:04:25 IMPORTANT: Should we allow |critical| to be option
ekasper 2013/12/04 19:25:15 No, this was a bug that tests didn't catch because
585 return false;
586
587 // extnValue
588 base::StringPiece extn_value;
589 if (!GetElement(&extension, kOCTETSTRING, &extn_value))
590 return false;
591 if (!extension.empty())
592 return false;
593
594 // The value of this extension is an OCTET STRING:
595 // SignedCertificateTimestampList ::= OCTET STRING
596 base::StringPiece sct_list;
597 if (!GetElement(&extn_value, kOCTETSTRING, &sct_list))
598 return false;
599 *sct_list_out = sct_list;
600 break;
601 }
602
603 // Either we found the right extension or we looped through all of them
604 // without success. The latter is not an error because the presence of an
605 // SCT extension is optional.
606 return true;
607 }
608
329 } // namespace asn1 609 } // namespace asn1
330 610
331 } // namespace net 611 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698