Chromium Code Reviews| Index: net/cert/internal/parse_ocsp.cc |
| diff --git a/net/cert/internal/parse_ocsp.cc b/net/cert/internal/parse_ocsp.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..92ce081360000f23f051f602ee9cf08d7d17e498 |
| --- /dev/null |
| +++ b/net/cert/internal/parse_ocsp.cc |
| @@ -0,0 +1,614 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include <algorithm> |
| + |
| +#include "base/sha1.h" |
| +#include "crypto/sha2.h" |
| +#include "net/cert/internal/extended_key_usage.h" |
| +#include "net/cert/internal/parse_ocsp.h" |
| +#include "net/cert/internal/signature_policy.h" |
| +#include "net/cert/internal/verify_name_match.h" |
| +#include "net/cert/internal/verify_signed_data.h" |
| + |
| +namespace net { |
| + |
| +OCSPCertID::OCSPCertID() {} |
| +OCSPCertID::~OCSPCertID() {} |
| + |
| +OCSPSingleResponse::OCSPSingleResponse() {} |
| +OCSPSingleResponse::~OCSPSingleResponse() {} |
| + |
| +OCSPResponseData::OCSPResponseData() {} |
| +OCSPResponseData::~OCSPResponseData() {} |
| + |
| +OCSPResponse::OCSPResponse() {} |
| +OCSPResponse::~OCSPResponse() {} |
| + |
| +der::Input BasicOCSPResponseOid() { |
| + // From RFC 6960: |
| + // |
| + // id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp } |
| + // id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 } |
| + // |
| + // In dotted notation: 1.3.6.1.5.5.7.48.1.1 |
| + static const uint8_t oid[] = {0x2b, 0x06, 0x01, 0x05, 0x05, |
| + 0x07, 0x30, 0x01, 0x01}; |
| + return der::Input(oid); |
| +} |
| + |
| +// CertID ::= SEQUENCE { |
| +// hashAlgorithm AlgorithmIdentifier, |
| +// issuerNameHash OCTET STRING, -- Hash of issuer's DN |
| +// issuerKeyHash OCTET STRING, -- Hash of issuer's public key |
| +// serialNumber CertificateSerialNumber |
| +// } |
| +bool ParseOCSPCertID(const der::Input& raw_tlv, OCSPCertID* out) { |
| + der::Parser outer_parser(raw_tlv); |
| + der::Parser parser; |
| + if (!outer_parser.ReadSequence(&parser)) |
| + return false; |
| + if (outer_parser.HasMore()) |
| + return false; |
| + |
| + der::Input sigalg_tlv; |
| + if (!parser.ReadRawTLV(&sigalg_tlv)) |
| + return false; |
| + if (!ParseHashAlgorithm(sigalg_tlv, &(out->hash_algorithm))) |
| + return false; |
| + if (!parser.ReadTag(der::kOctetString, &(out->issuer_name_hash))) |
| + return false; |
| + if (!parser.ReadTag(der::kOctetString, &(out->issuer_key_hash))) |
| + return false; |
| + if (!parser.ReadTag(der::kInteger, &(out->serial_number))) |
| + return false; |
| + if (!VerifySerialNumber(out->serial_number)) |
| + return false; |
| + |
| + return !parser.HasMore(); |
| +} |
| + |
| +namespace { |
| + |
| +// Parses |raw_tlv| to extract an OCSP RevokedInfo (RFC 6960) and stores the |
| +// result in the OCSPCertStatus |out|. Returns whether the parsing was |
| +// successful. |
| +// |
| +// RevokedInfo ::= SEQUENCE { |
| +// revocationTime GeneralizedTime, |
| +// revocationReason [0] EXPLICIT CRLReason OPTIONAL |
| +// } |
| +bool ParseRevokedInfo(const der::Input& raw_tlv, OCSPCertStatus* out) { |
| + der::Parser parser(raw_tlv); |
| + if (!parser.ReadGeneralizedTime(&(out->revocation_time))) |
| + return false; |
| + |
| + der::Input reason_input; |
| + if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(0), &reason_input, |
| + &(out->has_reason))) { |
| + return false; |
| + } |
| + if (out->has_reason) { |
| + der::Parser reason_parser(reason_input); |
| + der::Input reason_value_input; |
| + uint8_t reason_value; |
| + if (!reason_parser.ReadTag(der::kEnumerated, &reason_value_input)) |
| + return false; |
| + if (!der::ParseUint8(reason_value_input, &reason_value)) |
| + return false; |
| + if (reason_value > |
| + static_cast<uint8_t>(OCSPCertStatus::RevocationReason::LAST)) { |
| + return false; |
| + } |
| + out->revocation_reason = |
| + static_cast<OCSPCertStatus::RevocationReason>(reason_value); |
| + if (out->revocation_reason == OCSPCertStatus::RevocationReason::UNUSED) |
| + return false; |
| + if (reason_parser.HasMore()) |
| + return false; |
| + } |
| + return !parser.HasMore(); |
| +} |
| + |
| +// Parses |raw_tlv| to extract an OCSP CertStatus (RFC 6960) and stores the |
| +// result in the OCSPCertStatus |out|. Returns whether the parsing was |
| +// successful. |
| +// |
| +// CertStatus ::= CHOICE { |
| +// good [0] IMPLICIT NULL, |
| +// revoked [1] IMPLICIT RevokedInfo, |
| +// unknown [2] IMPLICIT UnknownInfo |
| +// } |
| +// |
| +// UnknownInfo ::= NULL |
| +bool ParseCertStatus(const der::Input& raw_tlv, OCSPCertStatus* out) { |
| + der::Parser parser(raw_tlv); |
| + der::Tag status_tag; |
| + der::Input status; |
| + if (!parser.ReadTagAndValue(&status_tag, &status)) |
| + return false; |
| + |
| + out->has_reason = false; |
| + if (status_tag == der::ContextSpecificPrimitive(0)) { |
| + out->status = OCSPCertStatus::Status::GOOD; |
| + } else if (status_tag == der::ContextSpecificConstructed(1)) { |
| + out->status = OCSPCertStatus::Status::REVOKED; |
| + if (!ParseRevokedInfo(status, out)) |
| + return false; |
| + } else if (status_tag == der::ContextSpecificPrimitive(2)) { |
| + out->status = OCSPCertStatus::Status::UNKNOWN; |
| + } else { |
| + return false; |
| + } |
| + |
| + return !parser.HasMore(); |
| +} |
| + |
| +} // namespace |
| + |
| +// SingleResponse ::= SEQUENCE { |
| +// certID CertID, |
| +// certStatus CertStatus, |
| +// thisUpdate GeneralizedTime, |
| +// nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, |
| +// singleExtensions [1] EXPLICIT Extensions OPTIONAL |
| +// } |
| +bool ParseOCSPSingleResponse(const der::Input& raw_tlv, |
| + OCSPSingleResponse* out) { |
| + der::Parser outer_parser(raw_tlv); |
| + der::Parser parser; |
| + if (!outer_parser.ReadSequence(&parser)) |
| + return false; |
| + if (outer_parser.HasMore()) |
| + return false; |
| + |
| + if (!parser.ReadRawTLV(&(out->cert_id_tlv))) |
| + return false; |
| + der::Input status_tlv; |
| + if (!parser.ReadRawTLV(&status_tlv)) |
| + return false; |
| + if (!ParseCertStatus(status_tlv, &(out->cert_status))) |
| + return false; |
| + if (!parser.ReadGeneralizedTime(&(out->this_update))) |
| + return false; |
| + |
| + der::Input next_update_input; |
| + if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(0), |
| + &next_update_input, &(out->has_next_update))) { |
| + return false; |
| + } |
| + if (out->has_next_update) { |
| + der::Parser next_update_parser(next_update_input); |
| + if (!next_update_parser.ReadGeneralizedTime(&(out->next_update))) |
| + return false; |
| + if (next_update_parser.HasMore()) |
| + return false; |
| + } |
| + |
| + if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(1), |
| + &(out->extensions), &(out->has_extensions))) { |
| + return false; |
| + } |
| + |
| + return !parser.HasMore(); |
| +} |
| + |
| +namespace { |
| + |
| +// Parses |raw_tlv| to extract a ResponderID (RFC 6960) and stores the |
| +// result in the ResponderID |out|. Returns whether the parsing was successful. |
| +// |
| +// ResponderID ::= CHOICE { |
| +// byName [1] Name, |
| +// byKey [2] KeyHash |
| +// } |
| +bool ParseResponderID(const der::Input& raw_tlv, |
| + OCSPResponseData::ResponderID* out) { |
| + der::Parser parser(raw_tlv); |
| + der::Tag id_tag; |
| + der::Input id_input; |
| + if (!parser.ReadTagAndValue(&id_tag, &id_input)) |
| + return false; |
| + |
| + if (id_tag == der::ContextSpecificConstructed(1)) { |
| + out->type = OCSPResponseData::ResponderType::NAME; |
| + out->name = id_input; |
| + } else if (id_tag == der::ContextSpecificConstructed(2)) { |
| + der::Parser key_parser(id_input); |
| + der::Input responder_key; |
| + if (!key_parser.ReadTag(der::kOctetString, &responder_key)) |
| + return false; |
| + if (key_parser.HasMore()) |
| + return false; |
| + |
| + SHA1HashValue key_hash; |
| + if (responder_key.Length() != sizeof(key_hash.data)) |
| + return false; |
| + memcpy(key_hash.data, responder_key.UnsafeData(), sizeof(key_hash.data)); |
| + out->type = OCSPResponseData::ResponderType::KEY_HASH; |
| + out->key_hash = HashValue(key_hash); |
| + } else { |
| + return false; |
| + } |
| + return !parser.HasMore(); |
| +} |
| + |
| +} // namespace |
| + |
| +// ResponseData ::= SEQUENCE { |
| +// version [0] EXPLICIT Version DEFAULT v1, |
| +// responderID ResponderID, |
| +// producedAt GeneralizedTime, |
| +// responses SEQUENCE OF SingleResponse, |
| +// responseExtensions [1] EXPLICIT Extensions OPTIONAL |
| +// } |
| +bool ParseOCSPResponseData(const der::Input& raw_tlv, OCSPResponseData* out) { |
| + der::Parser outer_parser(raw_tlv); |
| + der::Parser parser; |
| + if (!outer_parser.ReadSequence(&parser)) |
| + return false; |
| + if (outer_parser.HasMore()) |
| + return false; |
| + |
| + der::Input version_input; |
| + bool version_present; |
| + if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(0), |
| + &version_input, &version_present)) { |
| + return false; |
| + } |
| + |
| + // For compatibilty, we ignore the restriction from X.690 Section 11.5 that |
| + // DEFAULT values should be omitted for values equal to the default value. |
| + // TODO: Add warning about non-strict parsing. |
| + if (version_present) { |
| + der::Parser version_parser(version_input); |
| + if (!version_parser.ReadUint8(&(out->version))) |
|
eroman
2016/02/23 22:29:03
Where is the version number being checked?
RFC 696
svaldez
2016/02/24 16:44:37
Adding a check. It looks like we don't have any ca
|
| + return false; |
| + if (version_parser.HasMore()) |
| + return false; |
| + } else { |
| + out->version = 0; |
| + } |
| + |
| + der::Input responder_input; |
| + if (!parser.ReadRawTLV(&responder_input)) |
| + return false; |
| + if (!ParseResponderID(responder_input, &(out->responder_id))) |
| + return false; |
| + if (!parser.ReadGeneralizedTime(&(out->produced_at))) |
| + return false; |
| + |
| + der::Parser responses_parser; |
| + if (!parser.ReadSequence(&responses_parser)) |
| + return false; |
| + out->responses.clear(); |
| + while (responses_parser.HasMore()) { |
| + der::Input single_response; |
| + if (!responses_parser.ReadRawTLV(&single_response)) |
| + return false; |
| + out->responses.push_back(single_response); |
| + } |
| + |
| + if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(1), |
| + &(out->extensions), &(out->has_extensions))) { |
| + return false; |
| + } |
| + |
| + return !parser.HasMore(); |
| +} |
| + |
| +namespace { |
| + |
| +// Parses |raw_tlv| to extract a BasicOCSPResponse (RFC 6960) and stores the |
| +// result in the OCSPResponse |out|. Returns whether the parsing was |
| +// successful. |
| +// |
| +// BasicOCSPResponse ::= SEQUENCE { |
| +// tbsResponseData ResponseData, |
| +// signatureAlgorithm AlgorithmIdentifier, |
| +// signature BIT STRING, |
| +// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL |
| +// } |
| +bool ParseBasicOCSPResponse(const der::Input& raw_tlv, OCSPResponse* out) { |
| + der::Parser outer_parser(raw_tlv); |
| + der::Parser parser; |
| + if (!outer_parser.ReadSequence(&parser)) |
| + return false; |
| + if (outer_parser.HasMore()) |
| + return false; |
| + |
| + if (!parser.ReadRawTLV(&(out->data))) |
| + return false; |
| + der::Input sigalg_tlv; |
| + if (!parser.ReadRawTLV(&sigalg_tlv)) |
| + return false; |
| + out->signature_algorithm = SignatureAlgorithm::CreateFromDer(sigalg_tlv); |
| + if (!out->signature_algorithm) |
| + return false; |
| + if (!parser.ReadBitString(&(out->signature))) |
| + return false; |
| + der::Input certs_input; |
| + if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(0), &certs_input, |
| + &(out->has_certs))) { |
| + return false; |
| + } |
| + |
| + out->certs.clear(); |
| + if (out->has_certs) { |
| + der::Parser certs_seq_parser(certs_input); |
| + der::Parser certs_parser; |
| + if (!certs_seq_parser.ReadSequence(&certs_parser)) |
| + return false; |
| + if (certs_seq_parser.HasMore()) |
| + return false; |
| + while (certs_parser.HasMore()) { |
| + der::Input cert_tlv; |
| + if (!certs_parser.ReadRawTLV(&cert_tlv)) |
| + return false; |
| + out->certs.push_back(cert_tlv); |
| + } |
| + } |
| + |
| + return !parser.HasMore(); |
| +} |
| + |
| +} // namespace |
| + |
| +// OCSPResponse ::= SEQUENCE { |
| +// responseStatus OCSPResponseStatus, |
| +// responseBytes [0] EXPLICIT ResponseBytes OPTIONAL |
| +// } |
| +// |
| +// ResponseBytes ::= SEQUENCE { |
| +// responseType OBJECT IDENTIFIER, |
| +// response OCTET STRING |
| +// } |
| +bool ParseOCSPResponse(const der::Input& raw_tlv, OCSPResponse* out) { |
| + der::Parser outer_parser(raw_tlv); |
| + der::Parser parser; |
| + if (!outer_parser.ReadSequence(&parser)) |
| + return false; |
| + if (outer_parser.HasMore()) |
| + return false; |
| + |
| + der::Input response_status_input; |
| + uint8_t response_status; |
| + if (!parser.ReadTag(der::kEnumerated, &response_status_input)) |
| + return false; |
| + if (!der::ParseUint8(response_status_input, &response_status)) |
| + return false; |
| + if (response_status > |
| + static_cast<uint8_t>(OCSPResponse::ResponseStatus::LAST)) { |
| + return false; |
| + } |
| + out->status = static_cast<OCSPResponse::ResponseStatus>(response_status); |
| + if (out->status == OCSPResponse::ResponseStatus::UNUSED) |
| + return false; |
| + |
| + if (out->status == OCSPResponse::ResponseStatus::SUCCESSFUL) { |
| + der::Parser outer_bytes_parser; |
| + der::Parser bytes_parser; |
| + if (!parser.ReadConstructed(der::ContextSpecificConstructed(0), |
| + &outer_bytes_parser)) { |
| + return false; |
| + } |
| + if (!outer_bytes_parser.ReadSequence(&bytes_parser)) |
| + return false; |
| + if (outer_bytes_parser.HasMore()) |
| + return false; |
| + |
| + der::Input type_oid; |
| + if (!bytes_parser.ReadTag(der::kOid, &type_oid)) |
| + return false; |
| + if (type_oid != BasicOCSPResponseOid()) |
| + return false; |
| + |
| + // As per RFC 6960 Section 4.2.1, the value of |response| SHALL be the DER |
| + // encoding of BasicOCSPResponse. |
| + der::Input response; |
| + if (!bytes_parser.ReadTag(der::kOctetString, &response)) |
| + return false; |
| + if (!ParseBasicOCSPResponse(response, out)) |
| + return false; |
| + if (bytes_parser.HasMore()) |
| + return false; |
| + } |
| + |
| + return !parser.HasMore(); |
| +} |
| + |
| +namespace { |
| + |
| +// Checks that the |type| hash of |value| is equal to |hash| |
| +bool VerifyHash(HashValueTag type, |
| + const der::Input& hash, |
| + const der::Input& value) { |
| + HashValue target(type); |
| + if (target.size() != hash.Length()) |
| + return false; |
| + memcpy(target.data(), hash.UnsafeData(), target.size()); |
| + |
| + HashValue value_hash(type); |
| + if (type == HASH_VALUE_SHA1) { |
| + base::SHA1HashBytes(value.UnsafeData(), value.Length(), value_hash.data()); |
| + } else if (type == HASH_VALUE_SHA256) { |
| + std::string hash_string = crypto::SHA256HashString(value.AsString()); |
| + memcpy(value_hash.data(), hash_string.data(), value_hash.size()); |
| + } else { |
| + return false; |
| + } |
| + |
| + return target.Equals(value_hash); |
| +} |
| + |
| +// Checks that the input |id_tlv| parses to a valid CertID and matches the |
| +// issuer |issuer| and serial number |serial_number|. |
| +bool CheckCertID(const der::Input& id_tlv, |
| + const ParsedTbsCertificate& issuer, |
| + const der::Input& serial_number) { |
| + OCSPCertID id; |
| + if (!ParseOCSPCertID(id_tlv, &id)) |
| + return false; |
| + |
| + HashValueTag type; |
| + switch (id.hash_algorithm) { |
| + case DigestAlgorithm::Sha1: |
| + type = HASH_VALUE_SHA1; |
| + break; |
| + case DigestAlgorithm::Sha256: |
| + type = HASH_VALUE_SHA256; |
| + break; |
| + case DigestAlgorithm::Sha384: |
| + case DigestAlgorithm::Sha512: |
| + NOTIMPLEMENTED(); |
| + return false; |
| + } |
| + |
| + if (!VerifyHash(type, id.issuer_name_hash, issuer.subject_tlv)) |
| + return false; |
| + |
| + der::Parser outer_parser(issuer.spki_tlv); |
| + der::Parser spki_parser; |
| + der::BitString key_bits; |
| + if (!outer_parser.ReadSequence(&spki_parser)) |
| + return false; |
| + if (outer_parser.HasMore()) |
| + return false; |
| + if (!spki_parser.SkipTag(der::kSequence)) |
| + return false; |
| + if (!spki_parser.ReadBitString(&key_bits)) |
| + return false; |
| + der::Input key_tlv = key_bits.bytes(); |
| + if (!VerifyHash(type, id.issuer_key_hash, key_tlv)) |
| + return false; |
| + |
| + return id.serial_number == serial_number; |
| +} |
| + |
| +} // namespace |
| + |
| +bool GetOCSPCertStatus(const OCSPResponseData& response_data, |
| + const ParsedCertificate& issuer, |
| + const ParsedCertificate& cert, |
| + OCSPCertStatus* out) { |
| + out->status = OCSPCertStatus::Status::UNKNOWN; |
| + |
| + ParsedTbsCertificate tbs_cert; |
| + if (!ParseTbsCertificate(cert.tbs_certificate_tlv, &tbs_cert)) |
| + return false; |
| + ParsedTbsCertificate issuer_tbs_cert; |
| + if (!ParseTbsCertificate(issuer.tbs_certificate_tlv, &issuer_tbs_cert)) |
| + return false; |
| + |
| + for (const auto& response : response_data.responses) { |
| + OCSPSingleResponse single_response; |
| + if (!ParseOCSPSingleResponse(response, &single_response)) |
| + return false; |
| + if (CheckCertID(single_response.cert_id_tlv, issuer_tbs_cert, |
| + tbs_cert.serial_number)) { |
| + *out = single_response.cert_status; |
| + if (single_response.cert_status.status != OCSPCertStatus::Status::GOOD) |
| + return true; |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +// Verify Code Below (MOVING TO SEPARATE CL) |
|
eroman
2016/02/23 22:29:03
What is the resolution to this comment?
svaldez
2016/02/24 16:44:37
Will end up removing right before committing, sinc
eroman
2016/02/25 00:15:59
This is easily managed through git.
Create a new b
|
| + |
| +namespace { |
| + |
| +// Checks that the ResponderID |id| matches the certificate |cert|. |
| +bool CheckResponder(const OCSPResponseData::ResponderID& id, |
| + 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) || |
| + !der::Parser(cert.subject_tlv).ReadTag(der::kSequence, &cert_rdn)) |
| + return false; |
| + return VerifyNameMatch(name_rdn, cert_rdn); |
| + } else { |
| + 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 |
| + |
| +bool VerifyOCSPResponse(const OCSPResponse& response, |
| + const ParsedCertificate& issuer_cert) { |
| + SimpleSignaturePolicy signature_policy(1024); |
| + |
| + 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; |
| + |
| + if (CheckResponder(response_data.responder_id, issuer)) { |
| + responder = issuer; |
| + } else { |
| + bool found = false; |
| + 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; |
| + |
| + if (CheckResponder(response_data.responder_id, tbs_cert)) { |
| + found = true; |
| + responder = tbs_cert; |
| + |
| + scoped_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; |
| + } |
| + |
| + 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; |
| + break; |
| + } |
| + } |
| + if (!found) |
| + return false; |
| + } |
| + return VerifySignedData(*(response.signature_algorithm), response.data, |
| + response.signature, responder.spki_tlv, |
| + &signature_policy); |
| +} |
| + |
| +} // namespace net |