| Index: net/cert/internal/parse_ocsp.cc
|
| diff --git a/net/cert/internal/parse_ocsp.cc b/net/cert/internal/parse_ocsp.cc
|
| deleted file mode 100644
|
| index e06b29abe616346278fa917ea2e43073448ed192..0000000000000000000000000000000000000000
|
| --- a/net/cert/internal/parse_ocsp.cc
|
| +++ /dev/null
|
| @@ -1,532 +0,0 @@
|
| -// 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/parse_ocsp.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)))
|
| - return false;
|
| - if (version_parser.HasMore())
|
| - return false;
|
| - } else {
|
| - out->version = 0;
|
| - }
|
| -
|
| - if (out->version != 0)
|
| - return false;
|
| -
|
| - 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| 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) {
|
| - OCSPCertID id;
|
| - if (!ParseOCSPCertID(id_tlv, &id))
|
| - return false;
|
| -
|
| - HashValueTag type = HASH_VALUE_SHA1;
|
| - 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, certificate.issuer_tlv))
|
| - return false;
|
| -
|
| - // SubjectPublicKeyInfo ::= SEQUENCE {
|
| - // algorithm AlgorithmIdentifier,
|
| - // subjectPublicKey BIT STRING
|
| - // }
|
| - 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::GOOD;
|
| -
|
| - 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;
|
| -
|
| - bool found = 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, tbs_cert, issuer_tbs_cert,
|
| - tbs_cert.serial_number)) {
|
| - OCSPCertStatus new_status = single_response.cert_status;
|
| - found = true;
|
| - // In the case that we receive multiple responses, we keep only the
|
| - // strictest status (REVOKED > UNKNOWN > GOOD).
|
| - if (out->status == OCSPCertStatus::Status::GOOD ||
|
| - new_status.status == OCSPCertStatus::Status::REVOKED) {
|
| - *out = new_status;
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (!found)
|
| - out->status = OCSPCertStatus::Status::UNKNOWN;
|
| -
|
| - return found;
|
| -}
|
| -
|
| -} // namespace net
|
|
|