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

Unified Diff: net/cert/internal/ocsp.cc

Issue 1849773002: Adding OCSP Verification Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix scoped_ptr. Created 4 years, 7 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 side-by-side diff with in-line comments
Download patch
Index: net/cert/internal/ocsp.cc
diff --git a/net/cert/internal/parse_ocsp.cc b/net/cert/internal/ocsp.cc
similarity index 70%
rename from net/cert/internal/parse_ocsp.cc
rename to net/cert/internal/ocsp.cc
index e06b29abe616346278fa917ea2e43073448ed192..24464e023311a6a2adad609233bc8823e09af3d4 100644
--- a/net/cert/internal/parse_ocsp.cc
+++ b/net/cert/internal/ocsp.cc
@@ -2,11 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "net/cert/internal/ocsp.h"
+
#include <algorithm>
#include "base/sha1.h"
#include "crypto/sha2.h"
-#include "net/cert/internal/parse_ocsp.h"
+#include "net/cert/internal/extended_key_usage.h"
+#include "net/cert/internal/verify_name_match.h"
+#include "net/cert/internal/verify_signed_data.h"
namespace net {
@@ -22,7 +26,7 @@ OCSPResponseData::~OCSPResponseData() {}
OCSPResponse::OCSPResponse() {}
OCSPResponse::~OCSPResponse() {}
-der::Input BasicOCSPResponseOid() {
+WARN_UNUSED_RESULT der::Input BasicOCSPResponseOid() {
// From RFC 6960:
//
// id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp }
@@ -40,7 +44,8 @@ der::Input BasicOCSPResponseOid() {
// issuerKeyHash OCTET STRING, -- Hash of issuer's public key
// serialNumber CertificateSerialNumber
// }
-bool ParseOCSPCertID(const der::Input& raw_tlv, OCSPCertID* out) {
+WARN_UNUSED_RESULT bool ParseOCSPCertID(const der::Input& raw_tlv,
+ OCSPCertID* out) {
der::Parser outer_parser(raw_tlv);
der::Parser parser;
if (!outer_parser.ReadSequence(&parser))
@@ -75,7 +80,8 @@ namespace {
// revocationTime GeneralizedTime,
// revocationReason [0] EXPLICIT CRLReason OPTIONAL
// }
-bool ParseRevokedInfo(const der::Input& raw_tlv, OCSPCertStatus* out) {
+WARN_UNUSED_RESULT bool ParseRevokedInfo(const der::Input& raw_tlv,
+ OCSPCertStatus* out) {
der::Parser parser(raw_tlv);
if (!parser.ReadGeneralizedTime(&(out->revocation_time)))
return false;
@@ -118,7 +124,8 @@ bool ParseRevokedInfo(const der::Input& raw_tlv, OCSPCertStatus* out) {
// }
//
// UnknownInfo ::= NULL
-bool ParseCertStatus(const der::Input& raw_tlv, OCSPCertStatus* out) {
+WARN_UNUSED_RESULT bool ParseCertStatus(const der::Input& raw_tlv,
+ OCSPCertStatus* out) {
der::Parser parser(raw_tlv);
der::Tag status_tag;
der::Input status;
@@ -150,8 +157,8 @@ bool ParseCertStatus(const der::Input& raw_tlv, OCSPCertStatus* out) {
// nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
// singleExtensions [1] EXPLICIT Extensions OPTIONAL
// }
-bool ParseOCSPSingleResponse(const der::Input& raw_tlv,
- OCSPSingleResponse* out) {
+WARN_UNUSED_RESULT bool ParseOCSPSingleResponse(const der::Input& raw_tlv,
+ OCSPSingleResponse* out) {
der::Parser outer_parser(raw_tlv);
der::Parser parser;
if (!outer_parser.ReadSequence(&parser))
@@ -199,8 +206,8 @@ namespace {
// byName [1] Name,
// byKey [2] KeyHash
// }
-bool ParseResponderID(const der::Input& raw_tlv,
- OCSPResponseData::ResponderID* out) {
+WARN_UNUSED_RESULT bool ParseResponderID(const der::Input& raw_tlv,
+ OCSPResponseData::ResponderID* out) {
der::Parser parser(raw_tlv);
der::Tag id_tag;
der::Input id_input;
@@ -239,7 +246,8 @@ bool ParseResponderID(const der::Input& raw_tlv,
// responses SEQUENCE OF SingleResponse,
// responseExtensions [1] EXPLICIT Extensions OPTIONAL
// }
-bool ParseOCSPResponseData(const der::Input& raw_tlv, OCSPResponseData* out) {
+WARN_UNUSED_RESULT bool ParseOCSPResponseData(const der::Input& raw_tlv,
+ OCSPResponseData* out) {
der::Parser outer_parser(raw_tlv);
der::Parser parser;
if (!outer_parser.ReadSequence(&parser))
@@ -309,7 +317,8 @@ namespace {
// signature BIT STRING,
// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL
// }
-bool ParseBasicOCSPResponse(const der::Input& raw_tlv, OCSPResponse* out) {
+WARN_UNUSED_RESULT bool ParseBasicOCSPResponse(const der::Input& raw_tlv,
+ OCSPResponse* out) {
der::Parser outer_parser(raw_tlv);
der::Parser parser;
if (!outer_parser.ReadSequence(&parser))
@@ -363,7 +372,8 @@ bool ParseBasicOCSPResponse(const der::Input& raw_tlv, OCSPResponse* out) {
// responseType OBJECT IDENTIFIER,
// response OCTET STRING
// }
-bool ParseOCSPResponse(const der::Input& raw_tlv, OCSPResponse* out) {
+WARN_UNUSED_RESULT bool ParseOCSPResponse(const der::Input& raw_tlv,
+ OCSPResponse* out) {
der::Parser outer_parser(raw_tlv);
der::Parser parser;
if (!outer_parser.ReadSequence(&parser))
@@ -420,9 +430,9 @@ bool ParseOCSPResponse(const der::Input& raw_tlv, OCSPResponse* out) {
namespace {
// Checks that the |type| hash of |value| is equal to |hash|
-bool VerifyHash(HashValueTag type,
- const der::Input& hash,
- const der::Input& value) {
+WARN_UNUSED_RESULT bool VerifyHash(HashValueTag type,
+ const der::Input& hash,
+ const der::Input& value) {
HashValue target(type);
if (target.size() != hash.Length())
return false;
@@ -443,10 +453,10 @@ bool VerifyHash(HashValueTag type,
// Checks that the input |id_tlv| parses to a valid CertID and matches the
// issuer |issuer| name and key, as well as the serial number |serial_number|.
-bool CheckCertID(const der::Input& id_tlv,
- const ParsedTbsCertificate& certificate,
- const ParsedTbsCertificate& issuer,
- const der::Input& serial_number) {
+WARN_UNUSED_RESULT bool CheckCertID(const der::Input& id_tlv,
+ const ParsedTbsCertificate& certificate,
+ const ParsedTbsCertificate& issuer,
+ const der::Input& serial_number) {
OCSPCertID id;
if (!ParseOCSPCertID(id_tlv, &id))
return false;
@@ -492,10 +502,10 @@ bool CheckCertID(const der::Input& id_tlv,
} // namespace
-bool GetOCSPCertStatus(const OCSPResponseData& response_data,
- const ParsedCertificate& issuer,
- const ParsedCertificate& cert,
- OCSPCertStatus* out) {
+WARN_UNUSED_RESULT bool GetOCSPCertStatus(const OCSPResponseData& response_data,
+ const ParsedCertificate& issuer,
+ const ParsedCertificate& cert,
+ OCSPCertStatus* out) {
out->status = OCSPCertStatus::Status::GOOD;
ParsedTbsCertificate tbs_cert;
@@ -529,4 +539,117 @@ bool GetOCSPCertStatus(const OCSPResponseData& response_data,
return found;
}
+namespace {
+
+// Checks that the ResponderID |id| matches the certificate |cert| either
+// by verifying the name matches that of the certificate or that the hash
+// matches the certificate's public key hash (RFC 6960, 4.2.2.3).
+WARN_UNUSED_RESULT bool CheckResponder(const OCSPResponseData::ResponderID& id,
eroman 2016/05/31 19:12:46 Can you use a more specific name -- how about Resp
+ const ParsedTbsCertificate& cert) {
+ if (id.type == OCSPResponseData::ResponderType::NAME) {
+ der::Input name_rdn;
+ der::Input cert_rdn;
+ if (!der::Parser(id.name).ReadTag(der::kSequence, &name_rdn) ||
eroman 2016/05/31 19:12:46 Can you add documentation for ResponderID indicati
+ !der::Parser(cert.subject_tlv).ReadTag(der::kSequence, &cert_rdn))
+ return false;
+ return VerifyNameMatch(name_rdn, cert_rdn);
+ }
eroman 2016/05/31 19:12:46 For future proofing my preference is a switch(). B
+
+ // Otherwise we extract the SPKI and compare the public key hash to key
eroman 2016/05/31 19:12:46 avoid "we" in comments.
+ // in the ResponderID.
+ der::Parser parser(cert.spki_tlv);
+ der::Parser spki_parser;
+ der::BitString key_bits;
+ if (!parser.ReadSequence(&spki_parser))
+ return false;
+ if (!spki_parser.SkipTag(der::kSequence))
+ return false;
+ if (!spki_parser.ReadBitString(&key_bits))
+ return false;
+
+ der::Input key = key_bits.bytes();
+ HashValue key_hash(HASH_VALUE_SHA1);
+ base::SHA1HashBytes(key.UnsafeData(), key.Length(), key_hash.data());
+ return key_hash.Equals(id.key_hash);
+}
+
+} // namespace
+
+WARN_UNUSED_RESULT bool VerifyOCSPResponse(
+ const OCSPResponse& response,
+ const ParsedCertificate& issuer_cert,
+ const SignaturePolicy& signature_policy) {
+ OCSPResponseData response_data;
+ if (!ParseOCSPResponseData(response.data, &response_data))
+ return false;
+
+ ParsedTbsCertificate issuer;
+ ParsedTbsCertificate responder;
+ if (!ParseTbsCertificate(issuer_cert.tbs_certificate_tlv, &issuer))
+ return false;
+
+ // In order to verify the OCSP signature, a valid responder matching the OCSP
+ // Responder ID must be located (RFC 6960, 4.2.2.2). The responder is allowed
+ // to be either the certificate issuer or a delegated authority directly
+ // signed by the issuer.
+ if (CheckResponder(response_data.responder_id, issuer)) {
+ responder = issuer;
+ } else {
+ bool found = false;
+ // If the authority to sign OCSP responses has been delegated to another
+ // authority, the OCSP Response will contain the responder certificate.
eroman 2016/05/31 19:12:46 Is there any normative reference for this logic?
+ for (const auto& responder_cert_tlv : response.certs) {
+ ParsedCertificate responder_cert;
+ ParsedTbsCertificate tbs_cert;
+ if (!ParseCertificate(responder_cert_tlv, &responder_cert))
+ return false;
+ if (!ParseTbsCertificate(responder_cert.tbs_certificate_tlv, &tbs_cert))
+ return false;
+
+ // The OCSP Responder ID is checked against each provided certificate to
+ // determine whether it is the correct Authorized Responder.
+ if (CheckResponder(response_data.responder_id, tbs_cert)) {
+ found = true;
+ responder = tbs_cert;
+
+ // The Authorized Responder must be directly signed by the issuer of the
+ // certificate being checked.
+ std::unique_ptr<SignatureAlgorithm> signature_algorithm =
+ SignatureAlgorithm::CreateFromDer(
+ responder_cert.signature_algorithm_tlv);
+ if (!signature_algorithm)
+ return false;
+ der::Input issuer_spki = issuer.spki_tlv;
+ if (!VerifySignedData(*signature_algorithm,
+ responder_cert.tbs_certificate_tlv,
+ responder_cert.signature_value, issuer_spki,
+ &signature_policy)) {
+ return false;
+ }
+
+ // The Authorized Responder must include the value id-kp-OCSPSigning as
+ // part of the extended key usage extension.
+ std::map<der::Input, ParsedExtension> extensions;
+ std::vector<der::Input> eku;
+ if (!ParseExtensions(responder.extensions_tlv, &extensions))
+ return false;
+ if (!ParseEKUExtension(extensions[ExtKeyUsageOid()].value, &eku))
+ return false;
+ if (std::find(eku.begin(), eku.end(), OCSPSigning()) == eku.end())
+ return false;
+
+ // TODO(svaldez): Add Revocation Checking of Authorized Responder.
+ break;
eroman 2016/05/31 19:12:46 Can you pull this out to a separate function inste
+ }
+ }
+ if (!found)
+ return false;
+ }
+ // Once a valid responder has been found, the signature of the OCSP
+ // response is verified against the public key of the responder.
+ return VerifySignedData(*(response.signature_algorithm), response.data,
+ response.signature, responder.spki_tlv,
+ &signature_policy);
+}
+
} // namespace net

Powered by Google App Engine
This is Rietveld 408576698