| // Copyright 2016 The Chromium Authors | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     https://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | #include "ocsp.h" | 
 |  | 
 | #include <openssl/bytestring.h> | 
 | #include <openssl/digest.h> | 
 | #include <openssl/mem.h> | 
 | #include <openssl/pool.h> | 
 | #include <openssl/sha.h> | 
 | #include <openssl/span.h> | 
 |  | 
 | #include "cert_errors.h" | 
 | #include "extended_key_usage.h" | 
 | #include "parsed_certificate.h" | 
 | #include "revocation_util.h" | 
 | #include "string_util.h" | 
 | #include "verify_name_match.h" | 
 | #include "verify_signed_data.h" | 
 |  | 
 | BSSL_NAMESPACE_BEGIN | 
 |  | 
 | OCSPCertID::OCSPCertID() = default; | 
 | OCSPCertID::~OCSPCertID() = default; | 
 |  | 
 | OCSPSingleResponse::OCSPSingleResponse() = default; | 
 | OCSPSingleResponse::~OCSPSingleResponse() = default; | 
 |  | 
 | OCSPResponseData::OCSPResponseData() = default; | 
 | OCSPResponseData::~OCSPResponseData() = default; | 
 |  | 
 | OCSPResponse::OCSPResponse() = default; | 
 | OCSPResponse::~OCSPResponse() = default; | 
 |  | 
 | // 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(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(CBS_ASN1_OCTETSTRING, &out->issuer_name_hash)) { | 
 |     return false; | 
 |   } | 
 |   if (!parser.ReadTag(CBS_ASN1_OCTETSTRING, &out->issuer_key_hash)) { | 
 |     return false; | 
 |   } | 
 |   if (!parser.ReadTag(CBS_ASN1_INTEGER, &out->serial_number)) { | 
 |     return false; | 
 |   } | 
 |   CertErrors errors; | 
 |   if (!VerifySerialNumber(out->serial_number, false /*warnings_only*/, | 
 |                           &errors)) { | 
 |     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(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( | 
 |           CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 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(CBS_ASN1_ENUMERATED, &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(der::Input raw_tlv, OCSPCertStatus *out) { | 
 |   der::Parser parser(raw_tlv); | 
 |   CBS_ASN1_TAG status_tag; | 
 |   der::Input status; | 
 |   if (!parser.ReadTagAndValue(&status_tag, &status)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   out->has_reason = false; | 
 |   if (status_tag == (CBS_ASN1_CONTEXT_SPECIFIC | 0)) { | 
 |     out->status = OCSPRevocationStatus::GOOD; | 
 |   } else if (status_tag == | 
 |              (CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1)) { | 
 |     out->status = OCSPRevocationStatus::REVOKED; | 
 |     if (!ParseRevokedInfo(status, out)) { | 
 |       return false; | 
 |     } | 
 |   } else if (status_tag == (CBS_ASN1_CONTEXT_SPECIFIC | 2)) { | 
 |     out->status = OCSPRevocationStatus::UNKNOWN; | 
 |   } else { | 
 |     return false; | 
 |   } | 
 |  | 
 |   return !parser.HasMore(); | 
 | } | 
 |  | 
 | // Writes the hash of |value| as an OCTET STRING to |cbb|, using |hash_type| as | 
 | // the algorithm. Returns true on success. | 
 | bool AppendHashAsOctetString(const EVP_MD *hash_type, CBB *cbb, | 
 |                              der::Input value) { | 
 |   CBB octet_string; | 
 |   unsigned hash_len; | 
 |   uint8_t hash_buffer[EVP_MAX_MD_SIZE]; | 
 |  | 
 |   return CBB_add_asn1(cbb, &octet_string, CBS_ASN1_OCTETSTRING) && | 
 |          EVP_Digest(value.data(), value.size(), hash_buffer, &hash_len, | 
 |                     hash_type, nullptr) && | 
 |          CBB_add_bytes(&octet_string, hash_buffer, hash_len) && CBB_flush(cbb); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // SingleResponse ::= SEQUENCE { | 
 | //      certID                       CertID, | 
 | //      certStatus                   CertStatus, | 
 | //      thisUpdate                   GeneralizedTime, | 
 | //      nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL, | 
 | //      singleExtensions   [1]       EXPLICIT Extensions OPTIONAL | 
 | // } | 
 | bool ParseOCSPSingleResponse(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( | 
 |           CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 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( | 
 |           CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 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(der::Input raw_tlv, OCSPResponseData::ResponderID *out) { | 
 |   der::Parser parser(raw_tlv); | 
 |   CBS_ASN1_TAG id_tag; | 
 |   der::Input id_input; | 
 |   if (!parser.ReadTagAndValue(&id_tag, &id_input)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (id_tag == (CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1)) { | 
 |     out->type = OCSPResponseData::ResponderType::NAME; | 
 |     out->name = id_input; | 
 |   } else if (id_tag == (CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 2)) { | 
 |     der::Parser key_parser(id_input); | 
 |     der::Input key_hash; | 
 |     if (!key_parser.ReadTag(CBS_ASN1_OCTETSTRING, &key_hash)) { | 
 |       return false; | 
 |     } | 
 |     if (key_parser.HasMore()) { | 
 |       return false; | 
 |     } | 
 |     if (key_hash.size() != SHA_DIGEST_LENGTH) { | 
 |       return false; | 
 |     } | 
 |  | 
 |     out->type = OCSPResponseData::ResponderType::KEY_HASH; | 
 |     out->key_hash = 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(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( | 
 |           CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, &version_input, | 
 |           &version_present)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   // For compatibility, 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( | 
 |           CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 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(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; | 
 |   } | 
 |   // TODO(crbug.com/634443): Propagate the errors. | 
 |   std::optional<SignatureAlgorithm> sigalg = | 
 |       ParseSignatureAlgorithm(sigalg_tlv); | 
 |   if (!sigalg) { | 
 |     return false; | 
 |   } | 
 |   out->signature_algorithm = sigalg.value(); | 
 |   std::optional<der::BitString> signature = parser.ReadBitString(); | 
 |   if (!signature) { | 
 |     return false; | 
 |   } | 
 |   out->signature = signature.value(); | 
 |   der::Input certs_input; | 
 |   if (!parser.ReadOptionalTag( | 
 |           CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 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(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(CBS_ASN1_ENUMERATED, &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( | 
 |             CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 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(CBS_ASN1_OBJECT, &type_oid)) { | 
 |       return false; | 
 |     } | 
 |     if (type_oid != der::Input(kBasicOCSPResponseOid)) { | 
 |       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(CBS_ASN1_OCTETSTRING, &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(const EVP_MD *type, der::Input hash, der::Input value) { | 
 |   unsigned value_hash_len; | 
 |   uint8_t value_hash[EVP_MAX_MD_SIZE]; | 
 |   if (!EVP_Digest(value.data(), value.size(), value_hash, &value_hash_len, type, | 
 |                   nullptr)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   return hash == der::Input(value_hash, value_hash_len); | 
 | } | 
 |  | 
 | // Extracts the bytes of the SubjectPublicKey bit string given an SPKI. That is | 
 | // to say, the value of subjectPublicKey without the leading unused bit | 
 | // count octet. | 
 | // | 
 | // Returns true on success and fills |*spk_tlv| with the result. | 
 | // | 
 | // From RFC 5280, Section 4.1 | 
 | //   SubjectPublicKeyInfo  ::=  SEQUENCE  { | 
 | //     algorithm            AlgorithmIdentifier, | 
 | //     subjectPublicKey     BIT STRING  } | 
 | // | 
 | //   AlgorithmIdentifier  ::=  SEQUENCE  { | 
 | //     algorithm               OBJECT IDENTIFIER, | 
 | //     parameters              ANY DEFINED BY algorithm OPTIONAL  } | 
 | // | 
 | bool GetSubjectPublicKeyBytes(der::Input spki_tlv, der::Input *spk_tlv) { | 
 |   CBS outer, inner, alg, spk; | 
 |   uint8_t unused_bit_count; | 
 |   CBS_init(&outer, spki_tlv.data(), spki_tlv.size()); | 
 |   //   The subjectPublicKey field includes the unused bit count. For this | 
 |   //   application, the unused bit count must be zero, and is not included in | 
 |   //   the result. We extract the subjectPubicKey bit string, verify the first | 
 |   //   byte is 0, and if so set |spk_tlv| to the remaining bytes. | 
 |   if (!CBS_get_asn1(&outer, &inner, CBS_ASN1_SEQUENCE) || | 
 |       !CBS_get_asn1(&inner, &alg, CBS_ASN1_SEQUENCE) || | 
 |       !CBS_get_asn1(&inner, &spk, CBS_ASN1_BITSTRING) || | 
 |       !CBS_get_u8(&spk, &unused_bit_count) || unused_bit_count != 0) { | 
 |     return false; | 
 |   } | 
 |   *spk_tlv = der::Input(CBS_data(&spk), CBS_len(&spk)); | 
 |   return true; | 
 | } | 
 |  | 
 | // Checks the OCSPCertID |id| identifies |certificate|. | 
 | bool CheckCertIDMatchesCertificate( | 
 |     const OCSPCertID &id, const ParsedCertificate *certificate, | 
 |     const ParsedCertificate *issuer_certificate) { | 
 |   const EVP_MD *type = nullptr; | 
 |   switch (id.hash_algorithm) { | 
 |     case DigestAlgorithm::Md2: | 
 |     case DigestAlgorithm::Md4: | 
 |     case DigestAlgorithm::Md5: | 
 |       // Unsupported. | 
 |       return false; | 
 |     case DigestAlgorithm::Sha1: | 
 |       type = EVP_sha1(); | 
 |       break; | 
 |     case DigestAlgorithm::Sha256: | 
 |       type = EVP_sha256(); | 
 |       break; | 
 |     case DigestAlgorithm::Sha384: | 
 |       type = EVP_sha384(); | 
 |       break; | 
 |     case DigestAlgorithm::Sha512: | 
 |       type = EVP_sha512(); | 
 |       break; | 
 |   } | 
 |  | 
 |   if (!VerifyHash(type, id.issuer_name_hash, certificate->tbs().issuer_tlv)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   der::Input key_tlv; | 
 |   if (!GetSubjectPublicKeyBytes(issuer_certificate->tbs().spki_tlv, &key_tlv)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (!VerifyHash(type, id.issuer_key_hash, key_tlv)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   return id.serial_number == certificate->tbs().serial_number; | 
 | } | 
 |  | 
 | // TODO(eroman): Revisit how certificate parsing is used by this file. Ideally | 
 | // would either pass in the parsed bits, or have a better abstraction for lazily | 
 | // parsing. | 
 | std::shared_ptr<const ParsedCertificate> OCSPParseCertificate( | 
 |     std::string_view der) { | 
 |   ParseCertificateOptions parse_options; | 
 |   parse_options.allow_invalid_serial_numbers = true; | 
 |  | 
 |   // The objects returned by this function only last for the duration of a | 
 |   // single certificate verification, so there is no need to pool them to save | 
 |   // memory. | 
 |   // | 
 |   // TODO(eroman): Swallows the parsing errors. However uses a permissive | 
 |   // parsing model. | 
 |   CertErrors errors; | 
 |   return ParsedCertificate::Create( | 
 |       bssl::UniquePtr<CRYPTO_BUFFER>(CRYPTO_BUFFER_new( | 
 |           reinterpret_cast<const uint8_t *>(der.data()), der.size(), nullptr)), | 
 |       {}, &errors); | 
 | } | 
 |  | 
 | // 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). | 
 | [[nodiscard]] bool CheckResponderIDMatchesCertificate( | 
 |     const OCSPResponseData::ResponderID &id, const ParsedCertificate *cert) { | 
 |   switch (id.type) { | 
 |     case OCSPResponseData::ResponderType::NAME: { | 
 |       der::Input name_rdn; | 
 |       der::Input cert_rdn; | 
 |       if (!der::Parser(id.name).ReadTag(CBS_ASN1_SEQUENCE, &name_rdn) || | 
 |           !der::Parser(cert->tbs().subject_tlv) | 
 |                .ReadTag(CBS_ASN1_SEQUENCE, &cert_rdn)) { | 
 |         return false; | 
 |       } | 
 |       return VerifyNameMatch(name_rdn, cert_rdn); | 
 |     } | 
 |     case OCSPResponseData::ResponderType::KEY_HASH: { | 
 |       der::Input key; | 
 |       if (!GetSubjectPublicKeyBytes(cert->tbs().spki_tlv, &key)) { | 
 |         return false; | 
 |       } | 
 |       return VerifyHash(EVP_sha1(), id.key_hash, key); | 
 |     } | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | // Verifies that |responder_certificate| has been authority for OCSP signing, | 
 | // delegated to it by |issuer_certificate|. | 
 | // | 
 | // TODO(eroman): No revocation checks are done (see id-pkix-ocsp-nocheck in the | 
 | //     spec). extension). | 
 | // | 
 | // TODO(eroman): Not all properties of the certificate are verified, only the | 
 | //     signature and EKU. Can full RFC 5280 validation be used, or are there | 
 | //     compatibility concerns? | 
 | [[nodiscard]] bool VerifyAuthorizedResponderCert( | 
 |     const ParsedCertificate *responder_certificate, | 
 |     const ParsedCertificate *issuer_certificate) { | 
 |   // The Authorized Responder must be directly signed by the issuer of the | 
 |   // certificate being checked. | 
 |   // TODO(eroman): Must check the signature algorithm against policy. | 
 |   if (!responder_certificate->signature_algorithm().has_value() || | 
 |       !VerifySignedData(*responder_certificate->signature_algorithm(), | 
 |                         responder_certificate->tbs_certificate_tlv(), | 
 |                         responder_certificate->signature_value(), | 
 |                         issuer_certificate->tbs().spki_tlv, | 
 |                         /*cache=*/nullptr)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   // The Authorized Responder must include the value id-kp-OCSPSigning as | 
 |   // part of the extended key usage extension. | 
 |   if (!responder_certificate->has_extended_key_usage()) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   for (const auto &key_purpose_oid : | 
 |        responder_certificate->extended_key_usage()) { | 
 |     if (key_purpose_oid == der::Input(kOCSPSigning)) { | 
 |       return true; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | [[nodiscard]] bool VerifyOCSPResponseSignatureGivenCert( | 
 |     const OCSPResponse &response, const ParsedCertificate *cert) { | 
 |   // TODO(eroman): Must check the signature algorithm against policy. | 
 |   return VerifySignedData(response.signature_algorithm, response.data, | 
 |                           response.signature, cert->tbs().spki_tlv, | 
 |                           /*cache=*/nullptr); | 
 | } | 
 |  | 
 | // Verifies that the OCSP response has a valid signature using | 
 | // |issuer_certificate|, or an authorized responder issued by | 
 | // |issuer_certificate| for OCSP signing. | 
 | [[nodiscard]] bool VerifyOCSPResponseSignature( | 
 |     const OCSPResponse &response, const OCSPResponseData &response_data, | 
 |     const ParsedCertificate *issuer_certificate) { | 
 |   // 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 (CheckResponderIDMatchesCertificate(response_data.responder_id, | 
 |                                          issuer_certificate) && | 
 |       VerifyOCSPResponseSignatureGivenCert(response, issuer_certificate)) { | 
 |     return true; | 
 |   } | 
 |  | 
 |   // Otherwise search through the provided certificates for the Authorized | 
 |   // Responder. Want a certificate that: | 
 |   //  (1) Matches the OCSP Responder ID. | 
 |   //  (2) Has been given authority for OCSP signing by |issuer_certificate|. | 
 |   //  (3) Has signed the OCSP response using its public key. | 
 |   for (const auto &responder_cert_tlv : response.certs) { | 
 |     std::shared_ptr<const ParsedCertificate> cur_responder_certificate = | 
 |         OCSPParseCertificate(BytesAsStringView(responder_cert_tlv)); | 
 |  | 
 |     // If failed parsing the certificate, keep looking. | 
 |     if (!cur_responder_certificate) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     // If the certificate doesn't match the OCSP's responder ID, keep looking. | 
 |     if (!CheckResponderIDMatchesCertificate(response_data.responder_id, | 
 |                                             cur_responder_certificate.get())) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     // If the certificate isn't a valid Authorized Responder certificate, keep | 
 |     // looking. | 
 |     if (!VerifyAuthorizedResponderCert(cur_responder_certificate.get(), | 
 |                                        issuer_certificate)) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     // If the certificate signed this OCSP response, have found a match. | 
 |     // Otherwise keep looking. | 
 |     if (VerifyOCSPResponseSignatureGivenCert(response, | 
 |                                              cur_responder_certificate.get())) { | 
 |       return true; | 
 |     } | 
 |   } | 
 |  | 
 |   // Failed to confirm the validity of the OCSP signature using any of the | 
 |   // candidate certificates. | 
 |   return false; | 
 | } | 
 |  | 
 | // Parse ResponseData and return false if any unhandled critical extensions are | 
 | // found. No known critical ResponseData extensions exist. | 
 | bool ParseOCSPResponseDataExtensions( | 
 |     der::Input response_extensions, | 
 |     OCSPVerifyResult::ResponseStatus *response_details) { | 
 |   std::map<der::Input, ParsedExtension> extensions; | 
 |   if (!ParseExtensions(response_extensions, &extensions)) { | 
 |     *response_details = OCSPVerifyResult::PARSE_RESPONSE_DATA_ERROR; | 
 |     return false; | 
 |   } | 
 |  | 
 |   for (const auto &ext : extensions) { | 
 |     // TODO: handle ResponseData extensions | 
 |  | 
 |     if (ext.second.critical) { | 
 |       *response_details = OCSPVerifyResult::UNHANDLED_CRITICAL_EXTENSION; | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | // Parse SingleResponse and return false if any unhandled critical extensions | 
 | // (other than the CT extension) are found. The CT-SCT extension is not required | 
 | // to be marked critical, but since it is handled by Chrome, we will overlook | 
 | // the flag setting. | 
 | bool ParseOCSPSingleResponseExtensions( | 
 |     der::Input single_extensions, | 
 |     OCSPVerifyResult::ResponseStatus *response_details) { | 
 |   std::map<der::Input, ParsedExtension> extensions; | 
 |   if (!ParseExtensions(single_extensions, &extensions)) { | 
 |     *response_details = OCSPVerifyResult::PARSE_RESPONSE_DATA_ERROR; | 
 |     return false; | 
 |   } | 
 |  | 
 |   // The wire form of the OID 1.3.6.1.4.1.11129.2.4.5 - OCSP SingleExtension for | 
 |   // X.509v3 Certificate Transparency Signed Certificate Timestamp List, see | 
 |   // Section 3.3 of RFC6962. | 
 |   const uint8_t ct_ocsp_ext_oid[] = {0x2B, 0x06, 0x01, 0x04, 0x01, | 
 |                                      0xD6, 0x79, 0x02, 0x04, 0x05}; | 
 |   der::Input ct_ext_oid(ct_ocsp_ext_oid); | 
 |  | 
 |   for (const auto &ext : extensions) { | 
 |     // The CT OCSP extension is handled in ct::ExtractSCTListFromOCSPResponse | 
 |     if (ext.second.oid == ct_ext_oid) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     // TODO: handle SingleResponse extensions | 
 |  | 
 |     if (ext.second.critical) { | 
 |       *response_details = OCSPVerifyResult::UNHANDLED_CRITICAL_EXTENSION; | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | // Loops through the OCSPSingleResponses to find the best match for |cert|. | 
 | OCSPRevocationStatus GetRevocationStatusForCert( | 
 |     const OCSPResponseData &response_data, const ParsedCertificate *cert, | 
 |     const ParsedCertificate *issuer_certificate, | 
 |     int64_t verify_time_epoch_seconds, std::optional<int64_t> max_age_seconds, | 
 |     OCSPVerifyResult::ResponseStatus *response_details) { | 
 |   OCSPRevocationStatus result = OCSPRevocationStatus::UNKNOWN; | 
 |   *response_details = OCSPVerifyResult::NO_MATCHING_RESPONSE; | 
 |  | 
 |   for (const auto &single_response_der : response_data.responses) { | 
 |     // In the common case, there should only be one SingleResponse in the | 
 |     // ResponseData (matching the certificate requested and used on this | 
 |     // connection). However, it is possible for the OCSP responder to provide | 
 |     // multiple responses for multiple certificates. Look through all the | 
 |     // provided SingleResponses, and check to see if any match the | 
 |     // certificate. A SingleResponse matches a certificate if it has the same | 
 |     // serial number, issuer name (hash), and issuer public key (hash). | 
 |     OCSPSingleResponse single_response; | 
 |     if (!ParseOCSPSingleResponse(single_response_der, &single_response)) { | 
 |       return OCSPRevocationStatus::UNKNOWN; | 
 |     } | 
 |  | 
 |     // Reject unhandled critical extensions in SingleResponse | 
 |     if (single_response.has_extensions && | 
 |         !ParseOCSPSingleResponseExtensions(single_response.extensions, | 
 |                                            response_details)) { | 
 |       return OCSPRevocationStatus::UNKNOWN; | 
 |     } | 
 |  | 
 |     OCSPCertID cert_id; | 
 |     if (!ParseOCSPCertID(single_response.cert_id_tlv, &cert_id)) { | 
 |       return OCSPRevocationStatus::UNKNOWN; | 
 |     } | 
 |     if (!CheckCertIDMatchesCertificate(cert_id, cert, issuer_certificate)) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     // The SingleResponse matches the certificate, but may be out of date. Out | 
 |     // of date responses are noted separate from responses with mismatched | 
 |     // serial numbers. If an OCSP responder provides both an up to date | 
 |     // response and an expired response, the up to date response takes | 
 |     // precedence (PROVIDED > INVALID_DATE). | 
 |     if (!CheckRevocationDateValid(single_response.this_update, | 
 |                                   single_response.has_next_update | 
 |                                       ? &single_response.next_update | 
 |                                       : nullptr, | 
 |                                   verify_time_epoch_seconds, max_age_seconds)) { | 
 |       if (*response_details != OCSPVerifyResult::PROVIDED) { | 
 |         *response_details = OCSPVerifyResult::INVALID_DATE; | 
 |       } | 
 |       continue; | 
 |     } | 
 |  | 
 |     // In the case with multiple matching and up to date responses, keep only | 
 |     // the strictest status (REVOKED > UNKNOWN > GOOD). | 
 |     if (*response_details != OCSPVerifyResult::PROVIDED || | 
 |         result == OCSPRevocationStatus::GOOD || | 
 |         single_response.cert_status.status == OCSPRevocationStatus::REVOKED) { | 
 |       result = single_response.cert_status.status; | 
 |     } | 
 |     *response_details = OCSPVerifyResult::PROVIDED; | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | OCSPRevocationStatus CheckOCSP( | 
 |     std::string_view raw_response, std::string_view certificate_der, | 
 |     const ParsedCertificate *certificate, | 
 |     std::string_view issuer_certificate_der, | 
 |     const ParsedCertificate *issuer_certificate, | 
 |     int64_t verify_time_epoch_seconds, std::optional<int64_t> max_age_seconds, | 
 |     OCSPVerifyResult::ResponseStatus *response_details) { | 
 |   *response_details = OCSPVerifyResult::NOT_CHECKED; | 
 |  | 
 |   if (raw_response.empty()) { | 
 |     *response_details = OCSPVerifyResult::MISSING; | 
 |     return OCSPRevocationStatus::UNKNOWN; | 
 |   } | 
 |  | 
 |   OCSPResponse response; | 
 |   if (!ParseOCSPResponse(StringAsBytes(raw_response), &response)) { | 
 |     *response_details = OCSPVerifyResult::PARSE_RESPONSE_ERROR; | 
 |     return OCSPRevocationStatus::UNKNOWN; | 
 |   } | 
 |  | 
 |   // RFC 6960 defines all responses |response_status| != SUCCESSFUL as error | 
 |   // responses. No revocation information is provided on error responses, and | 
 |   // the OCSPResponseData structure is not set. | 
 |   if (response.status != OCSPResponse::ResponseStatus::SUCCESSFUL) { | 
 |     *response_details = OCSPVerifyResult::ERROR_RESPONSE; | 
 |     return OCSPRevocationStatus::UNKNOWN; | 
 |   } | 
 |  | 
 |   // Actual revocation information is contained within the BasicOCSPResponse as | 
 |   // a ResponseData structure. The BasicOCSPResponse was parsed above, and | 
 |   // contains an unparsed ResponseData. From RFC 6960: | 
 |   // | 
 |   // BasicOCSPResponse       ::= SEQUENCE { | 
 |   //    tbsResponseData      ResponseData, | 
 |   //    signatureAlgorithm   AlgorithmIdentifier, | 
 |   //    signature            BIT STRING, | 
 |   //    certs            [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } | 
 |   // | 
 |   // ResponseData ::= SEQUENCE { | 
 |   //     version              [0] EXPLICIT Version DEFAULT v1, | 
 |   //     responderID              ResponderID, | 
 |   //     producedAt               GeneralizedTime, | 
 |   //     responses                SEQUENCE OF SingleResponse, | 
 |   //     responseExtensions   [1] EXPLICIT Extensions OPTIONAL } | 
 |   OCSPResponseData response_data; | 
 |   if (!ParseOCSPResponseData(response.data, &response_data)) { | 
 |     *response_details = OCSPVerifyResult::PARSE_RESPONSE_DATA_ERROR; | 
 |     return OCSPRevocationStatus::UNKNOWN; | 
 |   } | 
 |  | 
 |   // Process the OCSP ResponseData extensions. In particular, must reject if | 
 |   // there are any critical extensions that are not understood. | 
 |   if (response_data.has_extensions && | 
 |       !ParseOCSPResponseDataExtensions(response_data.extensions, | 
 |                                        response_details)) { | 
 |     return OCSPRevocationStatus::UNKNOWN; | 
 |   } | 
 |  | 
 |   std::shared_ptr<const ParsedCertificate> parsed_certificate; | 
 |   std::shared_ptr<const ParsedCertificate> parsed_issuer_certificate; | 
 |   if (!certificate) { | 
 |     parsed_certificate = OCSPParseCertificate(certificate_der); | 
 |     certificate = parsed_certificate.get(); | 
 |   } | 
 |   if (!issuer_certificate) { | 
 |     parsed_issuer_certificate = OCSPParseCertificate(issuer_certificate_der); | 
 |     issuer_certificate = parsed_issuer_certificate.get(); | 
 |   } | 
 |  | 
 |   if (!certificate || !issuer_certificate) { | 
 |     *response_details = OCSPVerifyResult::NOT_CHECKED; | 
 |     return OCSPRevocationStatus::UNKNOWN; | 
 |   } | 
 |  | 
 |   // If producedAt is outside of the certificate validity period, reject the | 
 |   // response. | 
 |   if (response_data.produced_at < certificate->tbs().validity_not_before || | 
 |       response_data.produced_at > certificate->tbs().validity_not_after) { | 
 |     *response_details = OCSPVerifyResult::BAD_PRODUCED_AT; | 
 |     return OCSPRevocationStatus::UNKNOWN; | 
 |   } | 
 |  | 
 |   // Look through all of the OCSPSingleResponses for a match (based on CertID | 
 |   // and time). | 
 |   OCSPRevocationStatus status = GetRevocationStatusForCert( | 
 |       response_data, certificate, issuer_certificate, verify_time_epoch_seconds, | 
 |       max_age_seconds, response_details); | 
 |  | 
 |   // Check that the OCSP response has a valid signature. It must either be | 
 |   // signed directly by the issuing certificate, or a valid authorized | 
 |   // responder. | 
 |   if (!VerifyOCSPResponseSignature(response, response_data, | 
 |                                    issuer_certificate)) { | 
 |     return OCSPRevocationStatus::UNKNOWN; | 
 |   } | 
 |  | 
 |   return status; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | OCSPRevocationStatus CheckOCSP( | 
 |     std::string_view raw_response, std::string_view certificate_der, | 
 |     std::string_view issuer_certificate_der, int64_t verify_time_epoch_seconds, | 
 |     std::optional<int64_t> max_age_seconds, | 
 |     OCSPVerifyResult::ResponseStatus *response_details) { | 
 |   return CheckOCSP(raw_response, certificate_der, nullptr, | 
 |                    issuer_certificate_der, nullptr, verify_time_epoch_seconds, | 
 |                    max_age_seconds, response_details); | 
 | } | 
 |  | 
 | OCSPRevocationStatus CheckOCSP( | 
 |     std::string_view raw_response, const ParsedCertificate *certificate, | 
 |     const ParsedCertificate *issuer_certificate, | 
 |     int64_t verify_time_epoch_seconds, std::optional<int64_t> max_age_seconds, | 
 |     OCSPVerifyResult::ResponseStatus *response_details) { | 
 |   return CheckOCSP(raw_response, std::string_view(), certificate, | 
 |                    std::string_view(), issuer_certificate, | 
 |                    verify_time_epoch_seconds, max_age_seconds, | 
 |                    response_details); | 
 | } | 
 |  | 
 | bool CreateOCSPRequest(const ParsedCertificate *cert, | 
 |                        const ParsedCertificate *issuer, | 
 |                        std::vector<uint8_t> *request_der) { | 
 |   request_der->clear(); | 
 |  | 
 |   bssl::ScopedCBB cbb; | 
 |  | 
 |   // This initial buffer size is big enough for 20 octet long serial numbers | 
 |   // (upper bound from RFC 5280) and then a handful of extra bytes. This | 
 |   // number doesn't matter for correctness. | 
 |   const size_t kInitialBufferSize = 100; | 
 |  | 
 |   if (!CBB_init(cbb.get(), kInitialBufferSize)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   //   OCSPRequest     ::=     SEQUENCE { | 
 |   //       tbsRequest                  TBSRequest, | 
 |   //       optionalSignature   [0]     EXPLICIT Signature OPTIONAL } | 
 |   // | 
 |   //   TBSRequest      ::=     SEQUENCE { | 
 |   //       version             [0]     EXPLICIT Version DEFAULT v1, | 
 |   //       requestorName       [1]     EXPLICIT GeneralName OPTIONAL, | 
 |   //       requestList                 SEQUENCE OF Request, | 
 |   //       requestExtensions   [2]     EXPLICIT Extensions OPTIONAL } | 
 |   CBB ocsp_request; | 
 |   if (!CBB_add_asn1(cbb.get(), &ocsp_request, CBS_ASN1_SEQUENCE)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   CBB tbs_request; | 
 |   if (!CBB_add_asn1(&ocsp_request, &tbs_request, CBS_ASN1_SEQUENCE)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   // "version", "requestorName", and "requestExtensions" are omitted. | 
 |  | 
 |   CBB request_list; | 
 |   if (!CBB_add_asn1(&tbs_request, &request_list, CBS_ASN1_SEQUENCE)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   CBB request; | 
 |   if (!CBB_add_asn1(&request_list, &request, CBS_ASN1_SEQUENCE)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   //   Request         ::=     SEQUENCE { | 
 |   //       reqCert                     CertID, | 
 |   //       singleRequestExtensions     [0] EXPLICIT Extensions OPTIONAL } | 
 |   CBB req_cert; | 
 |   if (!CBB_add_asn1(&request, &req_cert, CBS_ASN1_SEQUENCE)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   //   CertID          ::=     SEQUENCE { | 
 |   //       hashAlgorithm       AlgorithmIdentifier, | 
 |   //       issuerNameHash      OCTET STRING, -- Hash of issuer's DN | 
 |   //       issuerKeyHash       OCTET STRING, -- Hash of issuer's public key | 
 |   //       serialNumber        CertificateSerialNumber } | 
 |   // | 
 |   // It is unclear whether the parameters for hashAlgorithm should be omitted or | 
 |   // NULL. Section 2.1 of RFC 4055 would suggest omitting it is the right | 
 |   // default behavior. However, both OpenSSL and Go include it, so we match them | 
 |   // for now. | 
 |  | 
 |   // TODO(eroman): Don't use SHA1. | 
 |   const EVP_MD *md = EVP_sha1(); | 
 |   if (!EVP_marshal_digest_algorithm(&req_cert, md)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   AppendHashAsOctetString(md, &req_cert, issuer->tbs().subject_tlv); | 
 |  | 
 |   der::Input key_tlv; | 
 |   if (!GetSubjectPublicKeyBytes(issuer->tbs().spki_tlv, &key_tlv)) { | 
 |     return false; | 
 |   } | 
 |   AppendHashAsOctetString(md, &req_cert, key_tlv); | 
 |  | 
 |   CBB serial_number; | 
 |   if (!CBB_add_asn1(&req_cert, &serial_number, CBS_ASN1_INTEGER)) { | 
 |     return false; | 
 |   } | 
 |   if (!CBB_add_bytes(&serial_number, cert->tbs().serial_number.data(), | 
 |                      cert->tbs().serial_number.size())) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   uint8_t *result_bytes; | 
 |   size_t result_bytes_length; | 
 |   if (!CBB_finish(cbb.get(), &result_bytes, &result_bytes_length)) { | 
 |     return false; | 
 |   } | 
 |   bssl::UniquePtr<uint8_t> delete_tbs_cert_bytes(result_bytes); | 
 |  | 
 |   request_der->assign(result_bytes, result_bytes + result_bytes_length); | 
 |   return true; | 
 | } | 
 |  | 
 | // From RFC 2560 section A.1.1: | 
 | // | 
 | //    An OCSP request using the GET method is constructed as follows: | 
 | // | 
 | //    GET {url}/{url-encoding of base-64 encoding of the DER encoding of | 
 | //    the OCSPRequest} | 
 | std::optional<std::string> CreateOCSPGetURL( | 
 |     const ParsedCertificate *cert, const ParsedCertificate *issuer, | 
 |     std::string_view ocsp_responder_url) { | 
 |   std::vector<uint8_t> ocsp_request_der; | 
 |   if (!CreateOCSPRequest(cert, issuer, &ocsp_request_der)) { | 
 |     // Unexpected (means BoringSSL failed an operation). | 
 |     return std::nullopt; | 
 |   } | 
 |  | 
 |   // Base64 encode the request data. | 
 |   size_t len; | 
 |   if (!EVP_EncodedLength(&len, ocsp_request_der.size())) { | 
 |     return std::nullopt; | 
 |   } | 
 |   std::vector<uint8_t> encoded(len); | 
 |   len = EVP_EncodeBlock(encoded.data(), ocsp_request_der.data(), | 
 |                         ocsp_request_der.size()); | 
 |  | 
 |   std::string b64_encoded(encoded.begin(), encoded.begin() + len); | 
 |  | 
 |   // In theory +, /, and = are valid in paths and don't need to be escaped. | 
 |   // However from the example in RFC 5019 section 5 it is clear that the intent | 
 |   // is to escape non-alphanumeric characters (the example conclusively escapes | 
 |   // '/' and '=', but doesn't clarify '+'). | 
 |   b64_encoded = bssl::string_util::FindAndReplace(b64_encoded, "+", "%2B"); | 
 |   b64_encoded = bssl::string_util::FindAndReplace(b64_encoded, "/", "%2F"); | 
 |   b64_encoded = bssl::string_util::FindAndReplace(b64_encoded, "=", "%3D"); | 
 |  | 
 |   // No attempt is made to collapse double slashes for URLs that end in slash, | 
 |   // since the spec doesn't do that. | 
 |   return std::string(ocsp_responder_url) + "/" + b64_encoded; | 
 | } | 
 |  | 
 | BSSL_NAMESPACE_END |