OLD | NEW |
---|---|
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 Loading... | |
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 |
OLD | NEW |