123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- // Copyright 2020 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 "net/test/revocation_builder.h"
- #include "base/hash/sha1.h"
- #include "base/strings/string_piece.h"
- #include "base/strings/string_util.h"
- #include "net/cert/asn1_util.h"
- #include "net/cert/x509_util.h"
- #include "net/der/encode_values.h"
- #include "net/der/input.h"
- #include "net/test/cert_builder.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "third_party/boringssl/src/include/openssl/bytestring.h"
- #include "third_party/boringssl/src/include/openssl/mem.h"
- namespace net {
- namespace {
- std::string Sha1() {
- // SEQUENCE { OBJECT_IDENTIFIER { 1.3.14.3.2.26 } }
- const uint8_t kSHA1[] = {0x30, 0x07, 0x06, 0x05, 0x2b,
- 0x0e, 0x03, 0x02, 0x1a};
- return std::string(std::begin(kSHA1), std::end(kSHA1));
- }
- // Adds bytes (specified as a StringPiece) to the given CBB.
- // The argument ordering follows the boringssl CBB_* api style.
- bool CBBAddBytes(CBB* cbb, base::StringPiece bytes) {
- return CBB_add_bytes(cbb, reinterpret_cast<const uint8_t*>(bytes.data()),
- bytes.size());
- }
- // Adds bytes (from fixed size array) to the given CBB.
- // The argument ordering follows the boringssl CBB_* api style.
- template <size_t N>
- bool CBBAddBytes(CBB* cbb, const uint8_t (&data)[N]) {
- return CBB_add_bytes(cbb, data, N);
- }
- // Adds a GeneralizedTime value to the given CBB.
- // The argument ordering follows the boringssl CBB_* api style.
- bool CBBAddGeneralizedTime(CBB* cbb, const base::Time& time) {
- der::GeneralizedTime generalized_time;
- if (!der::EncodeTimeAsGeneralizedTime(time, &generalized_time))
- return false;
- CBB time_cbb;
- uint8_t out[der::kGeneralizedTimeLength];
- if (!der::EncodeGeneralizedTime(generalized_time, out) ||
- !CBB_add_asn1(cbb, &time_cbb, CBS_ASN1_GENERALIZEDTIME) ||
- !CBBAddBytes(&time_cbb, out) || !CBB_flush(cbb))
- return false;
- return true;
- }
- // Finalizes the CBB to a std::string.
- std::string FinishCBB(CBB* cbb) {
- size_t cbb_len;
- uint8_t* cbb_bytes;
- if (!CBB_finish(cbb, &cbb_bytes, &cbb_len)) {
- ADD_FAILURE() << "CBB_finish() failed";
- return std::string();
- }
- bssl::UniquePtr<uint8_t> delete_bytes(cbb_bytes);
- return std::string(reinterpret_cast<char*>(cbb_bytes), cbb_len);
- }
- std::string PKeyToSPK(const EVP_PKEY* pkey) {
- bssl::ScopedCBB cbb;
- if (!CBB_init(cbb.get(), 64) || !EVP_marshal_public_key(cbb.get(), pkey)) {
- ADD_FAILURE();
- return std::string();
- }
- std::string spki = FinishCBB(cbb.get());
- base::StringPiece spk;
- if (!asn1::ExtractSubjectPublicKeyFromSPKI(spki, &spk)) {
- ADD_FAILURE();
- return std::string();
- }
- // ExtractSubjectPublicKeyFromSPKI() includes the unused bit count. For this
- // application, the unused bit count must be zero, and is not included in the
- // result.
- if (!base::StartsWith(spk, "\0")) {
- ADD_FAILURE();
- return std::string();
- }
- spk.remove_prefix(1);
- return std::string(spk);
- }
- // Returns a DER-encoded OCSPResponse with the given |response_status|.
- // |response_type| and |response| are optional and may be empty.
- std::string EncodeOCSPResponse(OCSPResponse::ResponseStatus response_status,
- der::Input response_type,
- std::string response) {
- // RFC 6960 section 4.2.1:
- //
- // OCSPResponse ::= SEQUENCE {
- // responseStatus OCSPResponseStatus,
- // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
- //
- // OCSPResponseStatus ::= ENUMERATED {
- // successful (0), -- Response has valid confirmations
- // malformedRequest (1), -- Illegal confirmation request
- // internalError (2), -- Internal error in issuer
- // tryLater (3), -- Try again later
- // -- (4) is not used
- // sigRequired (5), -- Must sign the request
- // unauthorized (6) -- Request unauthorized
- // }
- //
- // The value for responseBytes consists of an OBJECT IDENTIFIER and a
- // response syntax identified by that OID encoded as an OCTET STRING.
- //
- // ResponseBytes ::= SEQUENCE {
- // responseType OBJECT IDENTIFIER,
- // response OCTET STRING }
- bssl::ScopedCBB cbb;
- CBB ocsp_response, ocsp_response_status, ocsp_response_bytes,
- ocsp_response_bytes_sequence, ocsp_response_type,
- ocsp_response_octet_string;
- if (!CBB_init(cbb.get(), 64 + response_type.Length() + response.size()) ||
- !CBB_add_asn1(cbb.get(), &ocsp_response, CBS_ASN1_SEQUENCE) ||
- !CBB_add_asn1(&ocsp_response, &ocsp_response_status,
- CBS_ASN1_ENUMERATED) ||
- !CBB_add_u8(&ocsp_response_status,
- static_cast<uint8_t>(response_status))) {
- ADD_FAILURE();
- return std::string();
- }
- if (response_type.Length()) {
- if (!CBB_add_asn1(&ocsp_response, &ocsp_response_bytes,
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
- !CBB_add_asn1(&ocsp_response_bytes, &ocsp_response_bytes_sequence,
- CBS_ASN1_SEQUENCE) ||
- !CBB_add_asn1(&ocsp_response_bytes_sequence, &ocsp_response_type,
- CBS_ASN1_OBJECT) ||
- !CBBAddBytes(&ocsp_response_type, response_type.AsStringPiece()) ||
- !CBB_add_asn1(&ocsp_response_bytes_sequence,
- &ocsp_response_octet_string, CBS_ASN1_OCTETSTRING) ||
- !CBBAddBytes(&ocsp_response_octet_string, response)) {
- ADD_FAILURE();
- return std::string();
- }
- }
- return FinishCBB(cbb.get());
- }
- // Adds a DER-encoded OCSP SingleResponse to |responses_cbb|.
- // |issuer_name_hash| and |issuer_key_hash| should be binary SHA1 hashes.
- bool AddOCSPSingleResponse(CBB* responses_cbb,
- const OCSPBuilderSingleResponse& response,
- const std::string& issuer_name_hash,
- const std::string& issuer_key_hash) {
- // RFC 6960 section 4.2.1:
- //
- // SingleResponse ::= SEQUENCE {
- // certID CertID,
- // certStatus CertStatus,
- // thisUpdate GeneralizedTime,
- // nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
- // singleExtensions [1] EXPLICIT Extensions OPTIONAL }
- //
- // CertStatus ::= CHOICE {
- // good [0] IMPLICIT NULL,
- // revoked [1] IMPLICIT RevokedInfo,
- // unknown [2] IMPLICIT UnknownInfo }
- //
- // RevokedInfo ::= SEQUENCE {
- // revocationTime GeneralizedTime,
- // revocationReason [0] EXPLICIT CRLReason OPTIONAL }
- //
- // UnknownInfo ::= NULL
- //
- // RFC 6960 section 4.1.1:
- // CertID ::= SEQUENCE {
- // hashAlgorithm AlgorithmIdentifier,
- // issuerNameHash OCTET STRING, -- Hash of issuer's DN
- // issuerKeyHash OCTET STRING, -- Hash of issuer's public key
- // serialNumber CertificateSerialNumber }
- //
- // The contents of CertID include the following fields:
- //
- // o hashAlgorithm is the hash algorithm used to generate the
- // issuerNameHash and issuerKeyHash values.
- //
- // o issuerNameHash is the hash of the issuer's distinguished name
- // (DN). The hash shall be calculated over the DER encoding of the
- // issuer's name field in the certificate being checked.
- //
- // o issuerKeyHash is the hash of the issuer's public key. The hash
- // shall be calculated over the value (excluding tag and length) of
- // the subject public key field in the issuer's certificate.
- //
- // o serialNumber is the serial number of the certificate for which
- // status is being requested.
- CBB single_response, issuer_name_hash_cbb, issuer_key_hash_cbb, cert_id;
- if (!CBB_add_asn1(responses_cbb, &single_response, CBS_ASN1_SEQUENCE) ||
- !CBB_add_asn1(&single_response, &cert_id, CBS_ASN1_SEQUENCE) ||
- !CBBAddBytes(&cert_id, Sha1()) ||
- !CBB_add_asn1(&cert_id, &issuer_name_hash_cbb, CBS_ASN1_OCTETSTRING) ||
- !CBBAddBytes(&issuer_name_hash_cbb, issuer_name_hash) ||
- !CBB_add_asn1(&cert_id, &issuer_key_hash_cbb, CBS_ASN1_OCTETSTRING) ||
- !CBBAddBytes(&issuer_key_hash_cbb, issuer_key_hash) ||
- !CBB_add_asn1_uint64(&cert_id, response.serial)) {
- ADD_FAILURE();
- return false;
- }
- unsigned int cert_status_tag_number;
- switch (response.cert_status) {
- case OCSPRevocationStatus::GOOD:
- cert_status_tag_number = CBS_ASN1_CONTEXT_SPECIFIC | 0;
- break;
- case OCSPRevocationStatus::REVOKED:
- cert_status_tag_number =
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1;
- break;
- case OCSPRevocationStatus::UNKNOWN:
- cert_status_tag_number = CBS_ASN1_CONTEXT_SPECIFIC | 2;
- break;
- }
- CBB cert_status_cbb;
- if (!CBB_add_asn1(&single_response, &cert_status_cbb,
- cert_status_tag_number)) {
- ADD_FAILURE();
- return false;
- }
- if (response.cert_status == OCSPRevocationStatus::REVOKED &&
- !CBBAddGeneralizedTime(&cert_status_cbb, response.revocation_time)) {
- ADD_FAILURE();
- return false;
- }
- CBB next_update_cbb;
- if (!CBBAddGeneralizedTime(&single_response, response.this_update) ||
- !CBB_add_asn1(&single_response, &next_update_cbb,
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
- !CBBAddGeneralizedTime(&next_update_cbb, response.next_update)) {
- ADD_FAILURE();
- return false;
- }
- return CBB_flush(responses_cbb);
- }
- } // namespace
- std::string BuildOCSPResponseError(
- OCSPResponse::ResponseStatus response_status) {
- DCHECK_NE(response_status, OCSPResponse::ResponseStatus::SUCCESSFUL);
- return EncodeOCSPResponse(response_status, der::Input(), std::string());
- }
- std::string BuildOCSPResponse(
- const std::string& responder_subject,
- EVP_PKEY* responder_key,
- base::Time produced_at,
- const std::vector<OCSPBuilderSingleResponse>& responses) {
- std::string responder_name_hash = base::SHA1HashString(responder_subject);
- std::string responder_key_hash =
- base::SHA1HashString(PKeyToSPK(responder_key));
- // RFC 6960 section 4.2.1:
- //
- // ResponseData ::= SEQUENCE {
- // version [0] EXPLICIT Version DEFAULT v1,
- // responderID ResponderID,
- // producedAt GeneralizedTime,
- // responses SEQUENCE OF SingleResponse,
- // responseExtensions [1] EXPLICIT Extensions OPTIONAL }
- //
- // ResponderID ::= CHOICE {
- // byName [1] Name,
- // byKey [2] KeyHash }
- //
- // KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
- // (excluding the tag and length fields)
- bssl::ScopedCBB tbs_cbb;
- CBB response_data, responder_id, responder_id_by_key, responses_cbb;
- if (!CBB_init(tbs_cbb.get(), 64) ||
- !CBB_add_asn1(tbs_cbb.get(), &response_data, CBS_ASN1_SEQUENCE) ||
- // Version is the default v1, so it is not encoded.
- !CBB_add_asn1(&response_data, &responder_id,
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 2) ||
- !CBB_add_asn1(&responder_id, &responder_id_by_key,
- CBS_ASN1_OCTETSTRING) ||
- !CBBAddBytes(&responder_id_by_key, responder_key_hash) ||
- !CBBAddGeneralizedTime(&response_data, produced_at) ||
- !CBB_add_asn1(&response_data, &responses_cbb, CBS_ASN1_SEQUENCE)) {
- ADD_FAILURE();
- return std::string();
- }
- for (const auto& response : responses) {
- if (!AddOCSPSingleResponse(&responses_cbb, response, responder_name_hash,
- responder_key_hash)) {
- return std::string();
- }
- }
- // responseExtensions not currently supported.
- return BuildOCSPResponseWithResponseData(responder_key,
- FinishCBB(tbs_cbb.get()));
- }
- std::string BuildOCSPResponseWithResponseData(
- EVP_PKEY* responder_key,
- const std::string& tbs_response_data,
- absl::optional<SignatureAlgorithm> signature_algorithm) {
- // For a basic OCSP responder, responseType will be id-pkix-ocsp-basic.
- //
- // id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp }
- // id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 }
- //
- // The value for response SHALL be the DER encoding of
- // BasicOCSPResponse.
- //
- // BasicOCSPResponse ::= SEQUENCE {
- // tbsResponseData ResponseData,
- // signatureAlgorithm AlgorithmIdentifier,
- // signature BIT STRING,
- // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
- //
- // The value for signature SHALL be computed on the hash of the DER
- // encoding of ResponseData. The responder MAY include certificates in
- // the certs field of BasicOCSPResponse that help the OCSP client verify
- // the responder's signature. If no certificates are included, then
- // certs SHOULD be absent.
- //
- bssl::ScopedCBB basic_ocsp_response_cbb;
- CBB basic_ocsp_response, signature;
- if (!responder_key) {
- ADD_FAILURE();
- return std::string();
- }
- if (!signature_algorithm)
- signature_algorithm =
- CertBuilder::DefaultSignatureAlgorithmForKey(responder_key);
- if (!signature_algorithm) {
- ADD_FAILURE();
- return std::string();
- }
- std::string signature_algorithm_tlv =
- CertBuilder::SignatureAlgorithmToDer(*signature_algorithm);
- if (signature_algorithm_tlv.empty() ||
- !CBB_init(basic_ocsp_response_cbb.get(), 64 + tbs_response_data.size()) ||
- !CBB_add_asn1(basic_ocsp_response_cbb.get(), &basic_ocsp_response,
- CBS_ASN1_SEQUENCE) ||
- !CBBAddBytes(&basic_ocsp_response, tbs_response_data) ||
- !CBBAddBytes(&basic_ocsp_response, signature_algorithm_tlv) ||
- !CBB_add_asn1(&basic_ocsp_response, &signature, CBS_ASN1_BITSTRING) ||
- !CBB_add_u8(&signature, 0 /* no unused bits */) ||
- !CertBuilder::SignData(*signature_algorithm, tbs_response_data,
- responder_key, &signature)) {
- ADD_FAILURE();
- return std::string();
- }
- // certs field not currently supported.
- return EncodeOCSPResponse(OCSPResponse::ResponseStatus::SUCCESSFUL,
- der::Input(kBasicOCSPResponseOid),
- FinishCBB(basic_ocsp_response_cbb.get()));
- }
- std::string BuildCrl(const std::string& crl_issuer_subject,
- EVP_PKEY* crl_issuer_key,
- const std::vector<uint64_t>& revoked_serials,
- absl::optional<SignatureAlgorithm> signature_algorithm) {
- if (!crl_issuer_key) {
- ADD_FAILURE();
- return std::string();
- }
- if (!signature_algorithm)
- signature_algorithm =
- CertBuilder::DefaultSignatureAlgorithmForKey(crl_issuer_key);
- if (!signature_algorithm) {
- ADD_FAILURE();
- return std::string();
- }
- std::string signature_algorithm_tlv =
- CertBuilder::SignatureAlgorithmToDer(*signature_algorithm);
- if (signature_algorithm_tlv.empty()) {
- ADD_FAILURE();
- return std::string();
- }
- // TBSCertList ::= SEQUENCE {
- // version Version OPTIONAL,
- // -- if present, MUST be v2
- // signature AlgorithmIdentifier,
- // issuer Name,
- // thisUpdate Time,
- // nextUpdate Time OPTIONAL,
- // revokedCertificates SEQUENCE OF SEQUENCE {
- // userCertificate CertificateSerialNumber,
- // revocationDate Time,
- // crlEntryExtensions Extensions OPTIONAL
- // -- if present, version MUST be v2
- // } OPTIONAL,
- // crlExtensions [0] EXPLICIT Extensions OPTIONAL
- // -- if present, version MUST be v2
- // }
- bssl::ScopedCBB tbs_cbb;
- CBB tbs_cert_list, revoked_serials_cbb;
- if (!CBB_init(tbs_cbb.get(), 10) ||
- !CBB_add_asn1(tbs_cbb.get(), &tbs_cert_list, CBS_ASN1_SEQUENCE) ||
- !CBB_add_asn1_uint64(&tbs_cert_list, 1 /* V2 */) ||
- !CBBAddBytes(&tbs_cert_list, signature_algorithm_tlv) ||
- !CBBAddBytes(&tbs_cert_list, crl_issuer_subject) ||
- !x509_util::CBBAddTime(&tbs_cert_list,
- base::Time::Now() - base::Days(1)) ||
- !x509_util::CBBAddTime(&tbs_cert_list,
- base::Time::Now() + base::Days(6))) {
- ADD_FAILURE();
- return std::string();
- }
- if (!revoked_serials.empty()) {
- if (!CBB_add_asn1(&tbs_cert_list, &revoked_serials_cbb,
- CBS_ASN1_SEQUENCE)) {
- ADD_FAILURE();
- return std::string();
- }
- for (const int64_t revoked_serial : revoked_serials) {
- CBB revoked_serial_cbb;
- if (!CBB_add_asn1(&revoked_serials_cbb, &revoked_serial_cbb,
- CBS_ASN1_SEQUENCE) ||
- !CBB_add_asn1_uint64(&revoked_serial_cbb, revoked_serial) ||
- !x509_util::CBBAddTime(&revoked_serial_cbb,
- base::Time::Now() - base::Days(1)) ||
- !CBB_flush(&revoked_serials_cbb)) {
- ADD_FAILURE();
- return std::string();
- }
- }
- }
- std::string tbs_tlv = FinishCBB(tbs_cbb.get());
- // CertificateList ::= SEQUENCE {
- // tbsCertList TBSCertList,
- // signatureAlgorithm AlgorithmIdentifier,
- // signatureValue BIT STRING }
- bssl::ScopedCBB crl_cbb;
- CBB cert_list, signature;
- if (!CBB_init(crl_cbb.get(), 10) ||
- !CBB_add_asn1(crl_cbb.get(), &cert_list, CBS_ASN1_SEQUENCE) ||
- !CBBAddBytes(&cert_list, tbs_tlv) ||
- !CBBAddBytes(&cert_list, signature_algorithm_tlv) ||
- !CBB_add_asn1(&cert_list, &signature, CBS_ASN1_BITSTRING) ||
- !CBB_add_u8(&signature, 0 /* no unused bits */) ||
- !CertBuilder::SignData(*signature_algorithm, tbs_tlv, crl_issuer_key,
- &signature)) {
- ADD_FAILURE();
- return std::string();
- }
- return FinishCBB(crl_cbb.get());
- }
- } // namespace net
|