| Index: net/cert/ct_objects_extractor_nss.cc
|
| diff --git a/net/cert/ct_objects_extractor_nss.cc b/net/cert/ct_objects_extractor_nss.cc
|
| deleted file mode 100644
|
| index 76a3be51d2ef4b5c467ec6521634b8f08ad89857..0000000000000000000000000000000000000000
|
| --- a/net/cert/ct_objects_extractor_nss.cc
|
| +++ /dev/null
|
| @@ -1,619 +0,0 @@
|
| -// Copyright 2013 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/cert/ct_objects_extractor.h"
|
| -
|
| -#include <cert.h>
|
| -#include <secasn1.h>
|
| -#include <secitem.h>
|
| -#include <secoid.h>
|
| -
|
| -#include "base/lazy_instance.h"
|
| -#include "base/macros.h"
|
| -#include "base/sha1.h"
|
| -#include "crypto/scoped_nss_types.h"
|
| -#include "crypto/sha2.h"
|
| -#include "net/cert/asn1_util.h"
|
| -#include "net/cert/scoped_nss_types.h"
|
| -#include "net/cert/signed_certificate_timestamp.h"
|
| -#include "net/der/input.h"
|
| -#include "net/der/parser.h"
|
| -
|
| -namespace net {
|
| -
|
| -namespace ct {
|
| -
|
| -namespace {
|
| -
|
| -// NSS black magic to get the address of externally defined template at runtime.
|
| -SEC_ASN1_MKSUB(SEC_IntegerTemplate)
|
| -SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
|
| -SEC_ASN1_MKSUB(SEC_GeneralizedTimeTemplate)
|
| -SEC_ASN1_MKSUB(CERT_SequenceOfCertExtensionTemplate)
|
| -
|
| -// Wrapper class to convert a X509Certificate::OSCertHandle directly
|
| -// into a CERTCertificate* usable with other NSS functions. This is used for
|
| -// platforms where X509Certificate::OSCertHandle refers to a different type
|
| -// than a CERTCertificate*.
|
| -struct NSSCertWrapper {
|
| - explicit NSSCertWrapper(X509Certificate::OSCertHandle cert_handle);
|
| - ~NSSCertWrapper() {}
|
| -
|
| - ScopedCERTCertificate cert;
|
| -};
|
| -
|
| -NSSCertWrapper::NSSCertWrapper(X509Certificate::OSCertHandle cert_handle) {
|
| -#if defined(USE_NSS_CERTS)
|
| - cert.reset(CERT_DupCertificate(cert_handle));
|
| -#else
|
| - SECItem der_cert;
|
| - std::string der_data;
|
| - if (!X509Certificate::GetDEREncoded(cert_handle, &der_data))
|
| - return;
|
| - der_cert.data =
|
| - reinterpret_cast<unsigned char*>(const_cast<char*>(der_data.data()));
|
| - der_cert.len = der_data.size();
|
| -
|
| - // Note: CERT_NewTempCertificate may return NULL if the certificate
|
| - // shares a serial number with another cert issued by the same CA,
|
| - // which is not supposed to happen.
|
| - cert.reset(CERT_NewTempCertificate(
|
| - CERT_GetDefaultCertDB(), &der_cert, NULL, PR_FALSE, PR_TRUE));
|
| -#endif
|
| - DCHECK(cert.get() != NULL);
|
| -}
|
| -
|
| -// The wire form of the OID 1.3.6.1.4.1.11129.2.4.2. See Section 3.3 of
|
| -// RFC6962.
|
| -const unsigned char kEmbeddedSCTOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01,
|
| - 0xD6, 0x79, 0x02, 0x04, 0x02};
|
| -const char kEmbeddedSCTDescription[] =
|
| - "X.509v3 Certificate Transparency Embedded Signed Certificate Timestamp "
|
| - "List";
|
| -
|
| -// 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 unsigned char kOCSPExtensionOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01,
|
| - 0xD6, 0x79, 0x02, 0x04, 0x05};
|
| -
|
| -const SECItem kOCSPExtensionOidItem = {
|
| - siBuffer, const_cast<unsigned char*>(kOCSPExtensionOid),
|
| - sizeof(kOCSPExtensionOid)
|
| -};
|
| -
|
| -// id-ad-ocsp: 1.3.6.1.5.5.7.48.1.1
|
| -const unsigned char kBasicOCSPResponseOid[] = {0x2B, 0x06, 0x01, 0x05, 0x05,
|
| - 0x07, 0x30, 0x01, 0x01};
|
| -
|
| -const SECItem kBasicOCSPResponseOidItem = {
|
| - siBuffer, const_cast<unsigned char*>(kBasicOCSPResponseOid),
|
| - sizeof(kBasicOCSPResponseOid)
|
| -};
|
| -
|
| -
|
| -// Initializes the necessary NSS internals for use with Certificate
|
| -// Transparency.
|
| -class CTInitSingleton {
|
| - public:
|
| - SECOidTag embedded_oid() const { return embedded_oid_; }
|
| -
|
| - private:
|
| - friend struct base::DefaultLazyInstanceTraits<CTInitSingleton>;
|
| -
|
| - CTInitSingleton() : embedded_oid_(SEC_OID_UNKNOWN) {
|
| - embedded_oid_ = RegisterOid(
|
| - kEmbeddedSCTOid, sizeof(kEmbeddedSCTOid), kEmbeddedSCTDescription);
|
| - }
|
| -
|
| - ~CTInitSingleton() {}
|
| -
|
| - SECOidTag RegisterOid(const unsigned char* oid,
|
| - unsigned int oid_len,
|
| - const char* description) {
|
| - SECOidData oid_data;
|
| - oid_data.oid.len = oid_len;
|
| - oid_data.oid.data = const_cast<unsigned char*>(oid);
|
| - oid_data.offset = SEC_OID_UNKNOWN;
|
| - oid_data.desc = description;
|
| - oid_data.mechanism = CKM_INVALID_MECHANISM;
|
| - // Setting this to SUPPORTED_CERT_EXTENSION ensures that if a certificate
|
| - // contains this extension with the critical bit set, NSS will not reject
|
| - // it. However, because verification of this extension happens after NSS,
|
| - // it is currently left as INVALID_CERT_EXTENSION.
|
| - oid_data.supportedExtension = INVALID_CERT_EXTENSION;
|
| -
|
| - SECOidTag result = SECOID_AddEntry(&oid_data);
|
| - CHECK_NE(SEC_OID_UNKNOWN, result);
|
| -
|
| - return result;
|
| - }
|
| -
|
| - SECOidTag embedded_oid_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(CTInitSingleton);
|
| -};
|
| -
|
| -base::LazyInstance<CTInitSingleton>::Leaky g_ct_singleton =
|
| - LAZY_INSTANCE_INITIALIZER;
|
| -
|
| -// Obtains the data for an X.509v3 certificate extension identified by |oid|
|
| -// and encoded as an OCTET STRING. Returns true if the extension was found in
|
| -// the certificate, updating |ext_data| to be the extension data after removing
|
| -// the DER encoding of OCTET STRING.
|
| -bool GetCertOctetStringExtension(CERTCertificate* cert,
|
| - SECOidTag oid,
|
| - std::string* extension_data) {
|
| - SECItem extension;
|
| - SECStatus rv = CERT_FindCertExtension(cert, oid, &extension);
|
| - if (rv != SECSuccess)
|
| - return false;
|
| -
|
| - der::Parser parser(der::Input(extension.data, extension.len));
|
| - der::Input parsed_extension;
|
| - if (!parser.ReadTag(der::kOctetString, &parsed_extension) ||
|
| - parser.HasMore()) { // Decoding failure or raw data left
|
| - rv = SECFailure;
|
| - } else {
|
| - *extension_data = parsed_extension.AsString();
|
| - }
|
| -
|
| - SECITEM_FreeItem(&extension, PR_FALSE);
|
| - return rv == SECSuccess;
|
| -}
|
| -
|
| -// NSS offers CERT_FindCertExtension for certificates, but that only accepts
|
| -// CERTCertificate* inputs, so the method below extracts the SCT extension
|
| -// directly from the CERTCertExtension** of an OCSP response.
|
| -//
|
| -// Obtains the data for an OCSP extension identified by kOCSPExtensionOidItem
|
| -// and encoded as an OCTET STRING. Returns true if the extension was found in
|
| -// |extensions|, updating |extension_data| to be the extension data after
|
| -// removing the DER encoding of OCTET STRING.
|
| -bool GetSCTListFromOCSPExtension(PLArenaPool* arena,
|
| - const CERTCertExtension* const* extensions,
|
| - std::string* extension_data) {
|
| - if (!extensions)
|
| - return false;
|
| -
|
| - const CERTCertExtension* match = NULL;
|
| -
|
| - for (const CERTCertExtension* const* exts = extensions; *exts; ++exts) {
|
| - const CERTCertExtension* ext = *exts;
|
| - if (SECITEM_ItemsAreEqual(&kOCSPExtensionOidItem, &ext->id)) {
|
| - match = ext;
|
| - break;
|
| - }
|
| - }
|
| -
|
| - if (!match)
|
| - return false;
|
| -
|
| - SECItem contents;
|
| - // SEC_QuickDERDecodeItem sets |contents| to point to |match|, so it is not
|
| - // necessary to free the contents of |contents|.
|
| - SECStatus rv = SEC_QuickDERDecodeItem(arena, &contents,
|
| - SEC_ASN1_GET(SEC_OctetStringTemplate),
|
| - &match->value);
|
| - if (rv != SECSuccess)
|
| - return false;
|
| -
|
| - base::StringPiece parsed_data(reinterpret_cast<char*>(contents.data),
|
| - contents.len);
|
| - parsed_data.CopyToString(extension_data);
|
| - return true;
|
| -}
|
| -
|
| -// Given a |cert|, extract the TBSCertificate from this certificate, also
|
| -// removing the X.509 extension with OID 1.3.6.1.4.1.11129.2.4.2 (that is,
|
| -// the embedded SCT)
|
| -bool ExtractTBSCertWithoutSCTs(CERTCertificate* cert,
|
| - std::string* to_be_signed) {
|
| - SECOidData* oid = SECOID_FindOIDByTag(g_ct_singleton.Get().embedded_oid());
|
| - if (!oid)
|
| - return false;
|
| -
|
| - // This is a giant hack, due to the fact that NSS does not expose a good API
|
| - // for simply removing certificate fields from existing certificates.
|
| - CERTCertificate temp_cert;
|
| - temp_cert = *cert;
|
| - temp_cert.extensions = NULL;
|
| -
|
| - // Strip out the embedded SCT OID from the new certificate by directly
|
| - // mutating the extensions in place.
|
| - std::vector<CERTCertExtension*> new_extensions;
|
| - if (cert->extensions) {
|
| - for (CERTCertExtension** exts = cert->extensions; *exts; ++exts) {
|
| - CERTCertExtension* ext = *exts;
|
| - SECComparison result = SECITEM_CompareItem(&oid->oid, &ext->id);
|
| - if (result != SECEqual)
|
| - new_extensions.push_back(ext);
|
| - }
|
| - }
|
| - if (!new_extensions.empty()) {
|
| - new_extensions.push_back(NULL);
|
| - temp_cert.extensions = &new_extensions[0];
|
| - }
|
| -
|
| - crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
|
| -
|
| - SECItem tbs_data;
|
| - tbs_data.len = 0;
|
| - tbs_data.data = NULL;
|
| - void* result = SEC_ASN1EncodeItem(arena.get(),
|
| - &tbs_data,
|
| - &temp_cert,
|
| - SEC_ASN1_GET(CERT_CertificateTemplate));
|
| - if (!result)
|
| - return false;
|
| -
|
| - to_be_signed->assign(reinterpret_cast<char*>(tbs_data.data), tbs_data.len);
|
| - return true;
|
| -}
|
| -
|
| -// The following code is adapted from the NSS OCSP module, in order to expose
|
| -// the internal structure of an OCSP response.
|
| -
|
| -// ResponseBytes ::= SEQUENCE {
|
| -// responseType OBJECT IDENTIFIER,
|
| -// response OCTET STRING }
|
| -struct ResponseBytes {
|
| - SECItem response_type;
|
| - SECItem der_response;
|
| -};
|
| -
|
| -const SEC_ASN1Template kResponseBytesTemplate[] = {
|
| - { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ResponseBytes) },
|
| - { SEC_ASN1_OBJECT_ID, offsetof(ResponseBytes, response_type) },
|
| - { SEC_ASN1_OCTET_STRING, offsetof(ResponseBytes, der_response) },
|
| - { 0 }
|
| -};
|
| -
|
| -// OCSPResponse ::= SEQUENCE {
|
| -// responseStatus OCSPResponseStatus,
|
| -// responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
|
| -struct OCSPResponse {
|
| - SECItem response_status;
|
| - // This indirection is needed because |response_bytes| is an optional
|
| - // component and we need a way to determine if it is missing.
|
| - ResponseBytes* response_bytes;
|
| -};
|
| -
|
| -const SEC_ASN1Template kPointerToResponseBytesTemplate[] = {
|
| - { SEC_ASN1_POINTER, 0, kResponseBytesTemplate }
|
| -};
|
| -
|
| -const SEC_ASN1Template kOCSPResponseTemplate[] = {
|
| - { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(OCSPResponse) },
|
| - { SEC_ASN1_ENUMERATED, offsetof(OCSPResponse, response_status) },
|
| - { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
|
| - SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(OCSPResponse, response_bytes),
|
| - kPointerToResponseBytesTemplate },
|
| - { 0 }
|
| -};
|
| -
|
| -// CertID ::= SEQUENCE {
|
| -// hashAlgorithm AlgorithmIdentifier,
|
| -// issuerNameHash OCTET STRING, -- Hash of Issuer's DN
|
| -// issuerKeyHash OCTET STRING, -- Hash of Issuers public key
|
| -// serialNumber CertificateSerialNumber }
|
| -struct CertID {
|
| - SECAlgorithmID hash_algorithm;
|
| - SECItem issuer_name_hash;
|
| - SECItem issuer_key_hash;
|
| - SECItem serial_number;
|
| -};
|
| -
|
| -const SEC_ASN1Template kCertIDTemplate[] = {
|
| - { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CertID) },
|
| - { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CertID, hash_algorithm),
|
| - SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
|
| - { SEC_ASN1_OCTET_STRING, offsetof(CertID, issuer_name_hash) },
|
| - { SEC_ASN1_OCTET_STRING, offsetof(CertID, issuer_key_hash) },
|
| - { SEC_ASN1_INTEGER, offsetof(CertID, serial_number) },
|
| - { 0 }
|
| -};
|
| -
|
| -// SingleResponse ::= SEQUENCE {
|
| -// certID CertID,
|
| -// certStatus CertStatus,
|
| -// thisUpdate GeneralizedTime,
|
| -// nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
|
| -// singleExtensions [1] EXPLICIT Extensions OPTIONAL }
|
| -struct SingleResponse {
|
| - CertID cert_id;
|
| - // The following three fields are not used.
|
| - SECItem der_cert_status;
|
| - SECItem this_update;
|
| - SECItem next_update;
|
| - CERTCertExtension** single_extensions;
|
| -};
|
| -
|
| -const SEC_ASN1Template kSingleResponseTemplate[] = {
|
| - { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SingleResponse) },
|
| - { SEC_ASN1_INLINE, offsetof(SingleResponse, cert_id), kCertIDTemplate },
|
| - // Really a CHOICE but we make it an ANY because we don't care about the
|
| - // contents of this field.
|
| - // TODO(ekasper): use SEC_ASN1_CHOICE.
|
| - { SEC_ASN1_ANY, offsetof(SingleResponse, der_cert_status) },
|
| - { SEC_ASN1_GENERALIZED_TIME, offsetof(SingleResponse, this_update) },
|
| - { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
|
| - SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
|
| - offsetof(SingleResponse, next_update),
|
| - SEC_ASN1_SUB(SEC_GeneralizedTimeTemplate) },
|
| - { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
|
| - SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1,
|
| - offsetof(SingleResponse, single_extensions),
|
| - SEC_ASN1_SUB(CERT_SequenceOfCertExtensionTemplate) },
|
| - { 0 }
|
| -};
|
| -
|
| -// ResponseData ::= SEQUENCE {
|
| -// version [0] EXPLICIT Version DEFAULT v1,
|
| -// responderID ResponderID,
|
| -// producedAt GeneralizedTime,
|
| -// responses SEQUENCE OF SingleResponse,
|
| -// responseExtensions [1] EXPLICIT Extensions OPTIONAL }
|
| -struct ResponseData {
|
| - // The first three fields are not used.
|
| - SECItem version;
|
| - SECItem der_responder_id;
|
| - SECItem produced_at;
|
| - SingleResponse** single_responses;
|
| - // Skip extensions.
|
| -};
|
| -
|
| -const SEC_ASN1Template kResponseDataTemplate[] = {
|
| - { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ResponseData) },
|
| - { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
|
| - SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
|
| - offsetof(ResponseData, version), SEC_ASN1_SUB(SEC_IntegerTemplate) },
|
| - // Really a CHOICE but we make it an ANY because we don't care about the
|
| - // contents of this field.
|
| - // TODO(ekasper): use SEC_ASN1_CHOICE.
|
| - { SEC_ASN1_ANY, offsetof(ResponseData, der_responder_id) },
|
| - { SEC_ASN1_GENERALIZED_TIME, offsetof(ResponseData, produced_at) },
|
| - { SEC_ASN1_SEQUENCE_OF, offsetof(ResponseData, single_responses),
|
| - kSingleResponseTemplate },
|
| - { SEC_ASN1_SKIP_REST },
|
| - { 0 }
|
| -};
|
| -
|
| -// BasicOCSPResponse ::= SEQUENCE {
|
| -// tbsResponseData ResponseData,
|
| -// signatureAlgorithm AlgorithmIdentifier,
|
| -// signature BIT STRING,
|
| -// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
|
| -struct BasicOCSPResponse {
|
| - ResponseData tbs_response_data;
|
| - // We do not care about the rest.
|
| -};
|
| -
|
| -const SEC_ASN1Template kBasicOCSPResponseTemplate[] = {
|
| - { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(BasicOCSPResponse) },
|
| - { SEC_ASN1_INLINE, offsetof(BasicOCSPResponse, tbs_response_data),
|
| - kResponseDataTemplate },
|
| - { SEC_ASN1_SKIP_REST },
|
| - { 0 }
|
| -};
|
| -
|
| -bool StringEqualToSECItem(const std::string& value1, const SECItem& value2) {
|
| - if (value1.size() != value2.len)
|
| - return false;
|
| - return memcmp(value1.data(), value2.data, value2.len) == 0;
|
| -}
|
| -
|
| -// TODO(ekasper): also use the issuer name hash in matching.
|
| -bool CertIDMatches(const CertID& cert_id,
|
| - const std::string& serial_number,
|
| - const std::string& issuer_key_sha1_hash,
|
| - const std::string& issuer_key_sha256_hash) {
|
| - if (!StringEqualToSECItem(serial_number, cert_id.serial_number))
|
| - return false;
|
| -
|
| - SECOidTag hash_alg = SECOID_FindOIDTag(&cert_id.hash_algorithm.algorithm);
|
| - switch (hash_alg) {
|
| - case SEC_OID_SHA1:
|
| - return StringEqualToSECItem(issuer_key_sha1_hash,
|
| - cert_id.issuer_key_hash);
|
| - case SEC_OID_SHA256:
|
| - return StringEqualToSECItem(issuer_key_sha256_hash,
|
| - cert_id.issuer_key_hash);
|
| - default:
|
| - return false;
|
| - }
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -bool ExtractEmbeddedSCTList(X509Certificate::OSCertHandle cert,
|
| - std::string* sct_list) {
|
| - DCHECK(cert);
|
| -
|
| - NSSCertWrapper leaf_cert(cert);
|
| - if (!leaf_cert.cert)
|
| - return false;
|
| -
|
| - return GetCertOctetStringExtension(leaf_cert.cert.get(),
|
| - g_ct_singleton.Get().embedded_oid(),
|
| - sct_list);
|
| -}
|
| -
|
| -bool GetPrecertLogEntry(X509Certificate::OSCertHandle leaf,
|
| - X509Certificate::OSCertHandle issuer,
|
| - LogEntry* result) {
|
| - DCHECK(leaf);
|
| - DCHECK(issuer);
|
| -
|
| - NSSCertWrapper leaf_cert(leaf);
|
| - NSSCertWrapper issuer_cert(issuer);
|
| -
|
| - result->Reset();
|
| - // XXX(rsleevi): This check may be overkill, since we should be able to
|
| - // generate precerts for certs without the extension. For now, just a sanity
|
| - // check to match the reference implementation.
|
| - SECItem extension;
|
| - SECStatus rv = CERT_FindCertExtension(
|
| - leaf_cert.cert.get(), g_ct_singleton.Get().embedded_oid(), &extension);
|
| - if (rv != SECSuccess)
|
| - return false;
|
| - SECITEM_FreeItem(&extension, PR_FALSE);
|
| -
|
| - std::string to_be_signed;
|
| - if (!ExtractTBSCertWithoutSCTs(leaf_cert.cert.get(), &to_be_signed))
|
| - return false;
|
| -
|
| - if (!issuer_cert.cert) {
|
| - // This can happen when the issuer and leaf certs share the same serial
|
| - // number and are from the same CA, which should never be the case
|
| - // (but happened with bad test certs).
|
| - VLOG(1) << "Issuer cert is null, cannot generate Precert entry.";
|
| - return false;
|
| - }
|
| -
|
| - SECKEYPublicKey* issuer_pub_key =
|
| - SECKEY_ExtractPublicKey(&(issuer_cert.cert->subjectPublicKeyInfo));
|
| - if (!issuer_pub_key) {
|
| - VLOG(1) << "Could not extract issuer public key, "
|
| - << "cannot generate Precert entry.";
|
| - return false;
|
| - }
|
| -
|
| - SECItem* encoded_issuer_pubKey =
|
| - SECKEY_EncodeDERSubjectPublicKeyInfo(issuer_pub_key);
|
| - if (!encoded_issuer_pubKey) {
|
| - SECKEY_DestroyPublicKey(issuer_pub_key);
|
| - return false;
|
| - }
|
| -
|
| - result->type = ct::LogEntry::LOG_ENTRY_TYPE_PRECERT;
|
| - result->tbs_certificate.swap(to_be_signed);
|
| -
|
| - crypto::SHA256HashString(
|
| - base::StringPiece(reinterpret_cast<char*>(encoded_issuer_pubKey->data),
|
| - encoded_issuer_pubKey->len),
|
| - result->issuer_key_hash.data,
|
| - sizeof(result->issuer_key_hash.data));
|
| -
|
| - SECITEM_FreeItem(encoded_issuer_pubKey, PR_TRUE);
|
| - SECKEY_DestroyPublicKey(issuer_pub_key);
|
| -
|
| - return true;
|
| -}
|
| -
|
| -bool GetX509LogEntry(X509Certificate::OSCertHandle leaf, LogEntry* result) {
|
| - DCHECK(leaf);
|
| -
|
| - std::string encoded;
|
| - if (!X509Certificate::GetDEREncoded(leaf, &encoded))
|
| - return false;
|
| -
|
| - result->Reset();
|
| - result->type = ct::LogEntry::LOG_ENTRY_TYPE_X509;
|
| - result->leaf_certificate.swap(encoded);
|
| - return true;
|
| -}
|
| -
|
| -bool ExtractSCTListFromOCSPResponse(X509Certificate::OSCertHandle issuer,
|
| - const std::string& cert_serial_number,
|
| - const std::string& ocsp_response,
|
| - std::string* sct_list) {
|
| - DCHECK(issuer);
|
| -
|
| - // Any OCSP response is unlikely to be even close to 2^24 bytes; further, CT
|
| - // only uses stapled OCSP responses which have this limit imposed by the TLS
|
| - // protocol.
|
| - if (ocsp_response.size() > 0xffffff)
|
| - return false;
|
| -
|
| - crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
|
| -
|
| - OCSPResponse response;
|
| - memset(&response, 0, sizeof(response));
|
| -
|
| - SECItem src = { siBuffer,
|
| - reinterpret_cast<unsigned char*>(const_cast<char*>(
|
| - ocsp_response.data())),
|
| - static_cast<unsigned int>(ocsp_response.size()) };
|
| -
|
| - // |response| will point directly into |src|, so it's not necessary to
|
| - // free the |response| contents, but they may only be used while |src|
|
| - // is valid (i.e., in this method).
|
| - SECStatus rv = SEC_QuickDERDecodeItem(arena.get(), &response,
|
| - kOCSPResponseTemplate, &src);
|
| - if (rv != SECSuccess)
|
| - return false;
|
| -
|
| - if (!response.response_bytes)
|
| - return false;
|
| -
|
| - if (!SECITEM_ItemsAreEqual(&kBasicOCSPResponseOidItem,
|
| - &response.response_bytes->response_type)) {
|
| - return false;
|
| - }
|
| -
|
| - BasicOCSPResponse basic_response;
|
| - memset(&basic_response, 0, sizeof(basic_response));
|
| -
|
| - rv = SEC_QuickDERDecodeItem(arena.get(), &basic_response,
|
| - kBasicOCSPResponseTemplate,
|
| - &response.response_bytes->der_response);
|
| - if (rv != SECSuccess)
|
| - return false;
|
| -
|
| - SingleResponse** responses =
|
| - basic_response.tbs_response_data.single_responses;
|
| - if (!responses)
|
| - return false;
|
| -
|
| - std::string issuer_der;
|
| - if (!X509Certificate::GetDEREncoded(issuer, &issuer_der))
|
| - return false;
|
| -
|
| - base::StringPiece issuer_spki;
|
| - if (!asn1::ExtractSPKIFromDERCert(issuer_der, &issuer_spki))
|
| - return false;
|
| -
|
| - // In OCSP, only the key itself is under hash.
|
| - base::StringPiece issuer_spk;
|
| - if (!asn1::ExtractSubjectPublicKeyFromSPKI(issuer_spki, &issuer_spk))
|
| - return false;
|
| -
|
| - // ExtractSubjectPublicKey... does not remove the initial octet encoding the
|
| - // number of unused bits in the ASN.1 BIT STRING so we do it here. For public
|
| - // keys, the bitstring is in practice always byte-aligned.
|
| - if (issuer_spk.empty() || issuer_spk[0] != 0)
|
| - return false;
|
| - issuer_spk.remove_prefix(1);
|
| -
|
| - // NSS OCSP lib recognizes SHA1, MD5 and MD2; MD5 and MD2 are dead but
|
| - // https://bugzilla.mozilla.org/show_bug.cgi?id=663315 will add SHA-256
|
| - // and SHA-384.
|
| - // TODO(ekasper): add SHA-384 to crypto/sha2.h and here if it proves
|
| - // necessary.
|
| - // TODO(ekasper): only compute the hashes on demand.
|
| - std::string issuer_key_sha256_hash = crypto::SHA256HashString(issuer_spk);
|
| - std::string issuer_key_sha1_hash = base::SHA1HashString(
|
| - issuer_spk.as_string());
|
| -
|
| - const SingleResponse* match = NULL;
|
| - for (const SingleResponse* const* resps = responses; *resps; ++resps) {
|
| - const SingleResponse* resp = *resps;
|
| - if (CertIDMatches(resp->cert_id, cert_serial_number,
|
| - issuer_key_sha1_hash, issuer_key_sha256_hash)) {
|
| - match = resp;
|
| - break;
|
| - }
|
| - }
|
| -
|
| - if (!match)
|
| - return false;
|
| -
|
| - return GetSCTListFromOCSPExtension(arena.get(), match->single_extensions,
|
| - sct_list);
|
| -}
|
| -
|
| -} // namespace ct
|
| -
|
| -} // namespace net
|
|
|