Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(166)

Unified Diff: net/cert/ct_objects_extractor_nss.cc

Issue 27026002: CT: Adding preliminary Certificate Transparency support to Chromium. Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Distinguish between SCTs from unknown logs and unverified ones Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/cert/ct_objects_extractor.h ('k') | net/cert/ct_objects_extractor_openssl.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
new file mode 100644
index 0000000000000000000000000000000000000000..e5f1fab6531d726f4fab6345dcc80c3b468ddf5e
--- /dev/null
+++ b/net/cert/ct_objects_extractor_nss.cc
@@ -0,0 +1,276 @@
+// Copyright (c) 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 "crypto/scoped_nss_types.h"
+#include "crypto/sha2.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/signed_certificate_timestamp.h"
+
+namespace net {
+
+namespace ct {
+
+namespace {
+
+struct FreeCERTCertificate {
+ public:
+ inline void operator()(CERTCertificate* x) const {
+ CERT_DestroyCertificate(x);
+ }
+};
+
+typedef scoped_ptr_malloc<CERTCertificate, FreeCERTCertificate>
+ ScopedCERTCertificate;
+
+// Wrapper class to convert a X509Certificate::OSCertHandle directly
+// into a CERTCertificate* usable with other NSS functions. This is used for
+// systems 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)
+ 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, 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";
+
+// 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,
+// updating |ext_data| to be the extension data after removing the DER
+// encoding.
+bool GetOctetStringExtension(CERTCertificate* cert,
+ SECOidTag oid,
+ std::string* extension_data) {
+ SECItem extension;
+ SECStatus rv = CERT_FindCertExtension(cert, oid, &extension);
+ if (rv != SECSuccess)
+ return false;
+
+ base::StringPiece raw_data(reinterpret_cast<char*>(extension.data),
+ extension.len);
+ base::StringPiece parsed_data;
+ if (!asn1::GetElement(&raw_data, asn1::kOCTETSTRING, &parsed_data)) {
+ rv = SECFailure;
+ } else {
+ parsed_data.CopyToString(extension_data);
+ }
+
+ SECITEM_FreeItem(&extension, PR_FALSE);
+ return rv == SECSuccess;
+}
+
+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.
+ CERTCertificateStr 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);
+ // XXX(eranm): Should probabry convert to a proper array here.
+ 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 || tbs_data.len == 0)
+ return false;
+
+ to_be_signed->assign(reinterpret_cast<char*>(tbs_data.data), tbs_data.len);
+ return true;
+}
+
+} // namespace
+
+bool ExtractEmbeddedSCTs(X509Certificate::OSCertHandle cert,
+ std::string* sct_list) {
+ DCHECK(cert);
+
+ NSSCertWrapper leaf_cert(cert);
+ if (!leaf_cert.cert)
+ return false;
+
+ return GetOctetStringExtension(
+ 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);
+
+ // 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;
+
+ result->Reset();
+ result->type = ct::LogEntry::LOG_ENTRY_TYPE_PRECERT;
+ result->tbs_certificate.swap(to_be_signed);
+ if (!issuer_cert.cert) {
+ // This can happen when the issuer and leaf certs share the same serial
+ // number, which should never be the case (but happened with bad test
+ // certs).
+ VLOG(1) << "Issuer cert is null, failing SCT verification.";
+ return false;
+ }
+
+ // Hash the entire SPKI, not just the public key.
+ // See https://codereview.appspot.com/8269046/
+ SECKEYPublicKey *issuer_pub_key =
+ SECKEY_ExtractPublicKey(&(issuer_cert.cert->subjectPublicKeyInfo));
+ SECItem *encoded_issuer_pubKey =
+ SECKEY_EncodeDERSubjectPublicKeyInfo(issuer_pub_key);
+
+ 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);
+ encoded_issuer_pubKey = NULL;
+ SECKEY_DestroyPublicKey(issuer_pub_key);
+ issuer_pub_key = NULL;
+
+ return true;
+}
+
+bool GetAsn1CertLogEntry(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;
+}
+
+} // namespace ct
+
+} // namespace net
« no previous file with comments | « net/cert/ct_objects_extractor.h ('k') | net/cert/ct_objects_extractor_openssl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698