|  | // 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 |