summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authoreranm@google.com <eranm@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-24 22:33:00 +0000
committereranm@google.com <eranm@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-24 22:33:00 +0000
commit1f11d6fce0146543320116e0daa4d27d847c1c49 (patch)
tree140df1e8077a8e707befbc9b65a67503908d52de /net
parentff4d672e9e3aa4ebe831eb52f05b10f5ab145699 (diff)
downloadchromium_src-1f11d6fce0146543320116e0daa4d27d847c1c49.zip
chromium_src-1f11d6fce0146543320116e0daa4d27d847c1c49.tar.gz
chromium_src-1f11d6fce0146543320116e0daa4d27d847c1c49.tar.bz2
Add the high-level interface for verifying SCTs over multiple logs
This interface (and the default implementation) verify SCT lists obtained during the TLS handshake or from OCSP stapling, as well as embedded ones. The result will be used to modify the ssl_info with indicatior of CT status. The next, and final, patch will wire the CTVerifier to the SSL client socket. BUG=309578 Review URL: https://codereview.chromium.org/67513008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@237008 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/cert/ct_serialization.cc7
-rw-r--r--net/cert/ct_serialization.h3
-rw-r--r--net/cert/ct_verifier.h36
-rw-r--r--net/cert/ct_verify_result.cc17
-rw-r--r--net/cert/ct_verify_result.h37
-rw-r--r--net/cert/multi_log_ct_verifier.cc149
-rw-r--r--net/cert/multi_log_ct_verifier.h70
-rw-r--r--net/cert/multi_log_ct_verifier_unittest.cc138
-rw-r--r--net/cert/signed_certificate_timestamp.h2
-rw-r--r--net/data/ssl/certificates/README10
-rw-r--r--net/data/ssl/certificates/ct-test-embedded-with-intermediate-chain.pem188
-rw-r--r--net/data/ssl/certificates/ct-test-embedded-with-intermediate-preca-chain.pem188
-rw-r--r--net/data/ssl/certificates/ct-test-embedded-with-preca-chain.pem38
-rw-r--r--net/net.gyp7
-rw-r--r--net/test/cert_test_util.cc18
-rw-r--r--net/test/cert_test_util.h14
16 files changed, 917 insertions, 5 deletions
diff --git a/net/cert/ct_serialization.cc b/net/cert/ct_serialization.cc
index c139ec9e..2aeb71a 100644
--- a/net/cert/ct_serialization.cc
+++ b/net/cert/ct_serialization.cc
@@ -355,6 +355,13 @@ bool DecodeSignedCertificateTimestamp(base::StringPiece* input,
return true;
}
+bool EncodeSCTListForTesting(const base::StringPiece& sct,
+ std::string* output) {
+ std::string encoded_sct;
+ return WriteVariableBytes(kSerializedSCTLengthBytes, sct, &encoded_sct) &&
+ WriteVariableBytes(kSCTListLengthBytes, encoded_sct, output);
+}
+
} // namespace ct
} // namespace net
diff --git a/net/cert/ct_serialization.h b/net/cert/ct_serialization.h
index 0b7c9f0..19dc087 100644
--- a/net/cert/ct_serialization.h
+++ b/net/cert/ct_serialization.h
@@ -65,6 +65,9 @@ NET_EXPORT_PRIVATE bool DecodeSignedCertificateTimestamp(
base::StringPiece* input,
scoped_refptr<ct::SignedCertificateTimestamp>* output);
+// Writes an SCTList into |output|, containing a single |sct|.
+NET_EXPORT_PRIVATE bool EncodeSCTListForTesting(const base::StringPiece& sct,
+ std::string* output);
} // namespace ct
} // namespace net
diff --git a/net/cert/ct_verifier.h b/net/cert/ct_verifier.h
new file mode 100644
index 0000000..56685b8
--- /dev/null
+++ b/net/cert/ct_verifier.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef NET_CERT_CT_VERIFIER_H_
+#define NET_CERT_CT_VERIFIER_H_
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+namespace ct {
+struct CTVerifyResult;
+} // namespace ct
+
+class X509Certificate;
+
+// Interface for verifying Signed Certificate Timestamps over a certificate.
+class NET_EXPORT CTVerifier {
+ public:
+ virtual ~CTVerifier() {}
+
+ // Verifies either embedded SCTs or SCTs obtained via the
+ // signed_certificate_timestamp TLS extension or OCSP on the given |cert|
+ // |result| will be filled with these SCTs, divided into categories based on
+ // the verification result.
+ virtual int Verify(X509Certificate* cert,
+ const std::string& sct_list_from_ocsp,
+ const std::string& sct_list_from_tls_extension,
+ ct::CTVerifyResult* result) = 0;
+
+};
+
+} // namespace net
+
+#endif // NET_CERT_CT_VERIFIER_H_
diff --git a/net/cert/ct_verify_result.cc b/net/cert/ct_verify_result.cc
new file mode 100644
index 0000000..c62a18a
--- /dev/null
+++ b/net/cert/ct_verify_result.cc
@@ -0,0 +1,17 @@
+// 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_verify_result.h"
+
+namespace net {
+
+namespace ct {
+
+CTVerifyResult::CTVerifyResult() {}
+
+CTVerifyResult::~CTVerifyResult() {}
+
+} // namespace ct
+
+} // namespace net
diff --git a/net/cert/ct_verify_result.h b/net/cert/ct_verify_result.h
new file mode 100644
index 0000000..ac0a74b
--- /dev/null
+++ b/net/cert/ct_verify_result.h
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef NET_CERT_CT_VERIFY_RESULT_H_
+#define NET_CERT_CT_VERIFY_RESULT_H_
+
+#include <vector>
+
+#include "net/cert/signed_certificate_timestamp.h"
+
+namespace net {
+
+namespace ct {
+
+typedef std::vector<scoped_refptr<SignedCertificateTimestamp> > SCTList;
+
+// Holds Signed Certificate Timestamps, depending on their verification results.
+// More information could be tracked here about SCTs, but for the current UI
+// this categorization is enough.
+struct NET_EXPORT CTVerifyResult {
+ CTVerifyResult();
+ ~CTVerifyResult();
+
+ // SCTs from known logs where the signature verified correctly.
+ SCTList verified_scts;
+ // SCTs from known logs where the signature failed to verify.
+ SCTList unverified_scts;
+ // SCTs from unknown logs.
+ SCTList unknown_logs_scts;
+};
+
+} // namespace ct
+
+} // namespace net
+
+#endif // NET_CERT_CT_VERIFY_RESULT_H_
diff --git a/net/cert/multi_log_ct_verifier.cc b/net/cert/multi_log_ct_verifier.cc
new file mode 100644
index 0000000..5a19546
--- /dev/null
+++ b/net/cert/multi_log_ct_verifier.cc
@@ -0,0 +1,149 @@
+// 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/multi_log_ct_verifier.h"
+
+#include "net/base/net_errors.h"
+#include "net/cert/ct_log_verifier.h"
+#include "net/cert/ct_objects_extractor.h"
+#include "net/cert/ct_serialization.h"
+#include "net/cert/ct_verify_result.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+MultiLogCTVerifier::MultiLogCTVerifier() { }
+
+MultiLogCTVerifier::~MultiLogCTVerifier() { }
+
+void MultiLogCTVerifier::AddLog(scoped_ptr<CTLogVerifier> log_verifier) {
+ DCHECK(log_verifier);
+ if (!log_verifier)
+ return;
+
+ linked_ptr<CTLogVerifier> log(log_verifier.release());
+ logs_[log->key_id()] = log;
+}
+
+int MultiLogCTVerifier::Verify(
+ X509Certificate* cert,
+ const std::string& sct_list_from_ocsp,
+ const std::string& sct_list_from_tls_extension,
+ ct::CTVerifyResult* result) {
+ DCHECK(cert);
+ DCHECK(result);
+
+ result->verified_scts.clear();
+ result->unverified_scts.clear();
+ result->unknown_logs_scts.clear();
+
+ bool has_verified_scts = false;
+
+ std::string embedded_scts;
+ if (!cert->GetIntermediateCertificates().empty() &&
+ ct::ExtractEmbeddedSCTList(
+ cert->os_cert_handle(),
+ &embedded_scts)) {
+ ct::LogEntry precert_entry;
+
+ has_verified_scts =
+ ct::GetPrecertLogEntry(
+ cert->os_cert_handle(),
+ cert->GetIntermediateCertificates().front(),
+ &precert_entry) &&
+ VerifySCTs(
+ embedded_scts,
+ precert_entry,
+ ct::SignedCertificateTimestamp::SCT_EMBEDDED,
+ result);
+ }
+
+ ct::LogEntry x509_entry;
+ if (!ct::GetX509LogEntry(cert->os_cert_handle(), &x509_entry))
+ return has_verified_scts ? OK : ERR_FAILED;
+
+ has_verified_scts |= VerifySCTs(
+ sct_list_from_ocsp,
+ x509_entry,
+ ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
+ result);
+
+ has_verified_scts |= VerifySCTs(
+ sct_list_from_tls_extension,
+ x509_entry,
+ ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+ result);
+
+ if (has_verified_scts)
+ return OK;
+
+ return ERR_FAILED;
+}
+
+bool MultiLogCTVerifier::VerifySCTs(
+ const std::string& encoded_sct_list,
+ const ct::LogEntry& expected_entry,
+ ct::SignedCertificateTimestamp::Origin origin,
+ ct::CTVerifyResult* result) {
+ if (logs_.empty())
+ return false;
+
+ base::StringPiece temp(encoded_sct_list);
+ std::vector<base::StringPiece> sct_list;
+
+ if (!ct::DecodeSCTList(&temp, &sct_list))
+ return false;
+
+ bool verified = false;
+ for (std::vector<base::StringPiece>::const_iterator it = sct_list.begin();
+ it != sct_list.end(); ++it) {
+ base::StringPiece encoded_sct(*it);
+
+ scoped_refptr<ct::SignedCertificateTimestamp> decoded_sct;
+ if (!DecodeSignedCertificateTimestamp(&encoded_sct, &decoded_sct)) {
+ // XXX(rsleevi): Should we really just skip over bad SCTs?
+ continue;
+ }
+ decoded_sct->origin = origin;
+
+ verified |= VerifySingleSCT(decoded_sct, expected_entry, result);
+ }
+
+ return verified;
+}
+
+bool MultiLogCTVerifier::VerifySingleSCT(
+ scoped_refptr<ct::SignedCertificateTimestamp> sct,
+ const ct::LogEntry& expected_entry,
+ ct::CTVerifyResult* result) {
+
+ // Assume this SCT is untrusted until proven otherwise.
+
+ IDToLogMap::iterator it = logs_.find(sct->log_id);
+ if (it == logs_.end()) {
+ DVLOG(1) << "SCT does not match any known log.";
+ result->unknown_logs_scts.push_back(sct);
+ return false;
+ }
+
+ if (!it->second->Verify(expected_entry, *sct)) {
+ DVLOG(1) << "Unable to verify SCT signature.";
+ result->unverified_scts.push_back(sct);
+ return false;
+ }
+
+ // SCT verified ok, just make sure the timestamp is legitimate.
+ // Add 1 second to allow some slack for accepting SCTs which have *Just*
+ // been issued.
+ if (sct->timestamp > base::Time::Now()) {
+ DVLOG(1) << "SCT is from the future!";
+ result->unverified_scts.push_back(sct);
+ return false;
+ }
+
+ result->verified_scts.push_back(sct);
+ return true;
+}
+
+} // namespace net
diff --git a/net/cert/multi_log_ct_verifier.h b/net/cert/multi_log_ct_verifier.h
new file mode 100644
index 0000000..7ceace0
--- /dev/null
+++ b/net/cert/multi_log_ct_verifier.h
@@ -0,0 +1,70 @@
+// 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.
+
+#ifndef NET_CERT_MULTI_LOG_CT_VERIFIER_H_
+#define NET_CERT_MULTI_LOG_CT_VERIFIER_H_
+
+#include <map>
+#include <string>
+
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+#include "net/cert/ct_verifier.h"
+#include "net/cert/signed_certificate_timestamp.h"
+
+namespace net {
+
+namespace ct {
+struct LogEntry;
+} // namespace ct
+
+class CTLogVerifier;
+
+// A Certificate Transparency verifier that can verify Signed Certificate
+// Timestamps from multiple logs.
+// There should be a global instance of this class and for all known logs,
+// AddLog should be called with a CTLogVerifier (which is created from the
+// log's public key).
+class NET_EXPORT MultiLogCTVerifier : public CTVerifier {
+ public:
+ MultiLogCTVerifier();
+ virtual ~MultiLogCTVerifier();
+
+ void AddLog(scoped_ptr<CTLogVerifier> log_verifier);
+
+ // CTVerifier implementation:
+ virtual int Verify(X509Certificate* cert,
+ const std::string& sct_list_from_ocsp,
+ const std::string& sct_list_from_tls_extension,
+ ct::CTVerifyResult* result) OVERRIDE;
+
+ private:
+ // Mapping from a log's ID to the verifier for this log.
+ // A log's ID is the SHA-256 of the log's key, as defined in section 3.2.
+ // of RFC6962.
+ typedef std::map<std::string, linked_ptr<CTLogVerifier> > IDToLogMap;
+
+ // Verify a list of SCTs from |encoded_sct_list| over |expected_entry|,
+ // placing the verification results in |result|. The SCTs in the list
+ // come from |origin| (as will be indicated in the origin field of each SCT).
+ bool VerifySCTs(const std::string& encoded_sct_list,
+ const ct::LogEntry& expected_entry,
+ ct::SignedCertificateTimestamp::Origin origin,
+ ct::CTVerifyResult* result);
+
+ // Verifies a single, parsed SCT against all logs.
+ bool VerifySingleSCT(
+ scoped_refptr<ct::SignedCertificateTimestamp> sct,
+ const ct::LogEntry& expected_entry,
+ ct::CTVerifyResult* result);
+
+ IDToLogMap logs_;
+
+ DISALLOW_COPY_AND_ASSIGN(MultiLogCTVerifier);
+};
+
+} // namespace net
+
+#endif // NET_CERT_MULTI_LOG_CT_VERIFIER_H_
diff --git a/net/cert/multi_log_ct_verifier_unittest.cc b/net/cert/multi_log_ct_verifier_unittest.cc
new file mode 100644
index 0000000..287b150
--- /dev/null
+++ b/net/cert/multi_log_ct_verifier_unittest.cc
@@ -0,0 +1,138 @@
+// 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/multi_log_ct_verifier.h"
+
+#include <string>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/ct_log_verifier.h"
+#include "net/cert/ct_serialization.h"
+#include "net/cert/ct_verify_result.h"
+#include "net/cert/pem_tokenizer.h"
+#include "net/cert/signed_certificate_timestamp.h"
+#include "net/cert/x509_certificate.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/ct_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+class MultiLogCTVerifierTest : public ::testing::Test {
+ public:
+ virtual void SetUp() OVERRIDE {
+ scoped_ptr<CTLogVerifier> log(
+ CTLogVerifier::Create(ct::GetTestPublicKey(), ""));
+ ASSERT_TRUE(log);
+
+ verifier_.reset(new MultiLogCTVerifier());
+ verifier_->AddLog(log.Pass());
+ std::string der_test_cert(ct::GetDerEncodedX509Cert());
+ chain_ = X509Certificate::CreateFromBytes(
+ der_test_cert.data(),
+ der_test_cert.length());
+ ASSERT_TRUE(chain_);
+ }
+
+ bool CheckForSingleVerifiedSCTInResult(const ct::CTVerifyResult& result) {
+ return (result.verified_scts.size() == 1U) &&
+ result.unverified_scts.empty() &&
+ result.unknown_logs_scts.empty();
+ }
+
+ bool CheckForSCTOrigin(
+ const ct::CTVerifyResult& result,
+ ct::SignedCertificateTimestamp::Origin origin) {
+ return (result.verified_scts.size() > 0) &&
+ (result.verified_scts[0]->origin == origin);
+ }
+
+ bool CheckPrecertificateVerification(scoped_refptr<X509Certificate> chain) {
+ ct::CTVerifyResult result;
+ return (verifier_->Verify(chain, "", "", &result) == OK) &&
+ CheckForSingleVerifiedSCTInResult(result) &&
+ CheckForSCTOrigin(
+ result, ct::SignedCertificateTimestamp::SCT_EMBEDDED);
+ }
+
+ protected:
+ scoped_ptr<MultiLogCTVerifier> verifier_;
+ scoped_refptr<X509Certificate> chain_;
+};
+
+TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCT) {
+ scoped_refptr<X509Certificate> chain(
+ CreateCertificateChainFromFile(GetTestCertsDirectory(),
+ "ct-test-embedded-cert.pem",
+ X509Certificate::FORMAT_AUTO));
+ ASSERT_TRUE(chain);
+ ASSERT_TRUE(CheckPrecertificateVerification(chain));
+}
+
+TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCTWithPreCA) {
+ scoped_refptr<X509Certificate> chain(
+ CreateCertificateChainFromFile(GetTestCertsDirectory(),
+ "ct-test-embedded-with-preca-chain.pem",
+ X509Certificate::FORMAT_AUTO));
+ ASSERT_TRUE(chain);
+ ASSERT_TRUE(CheckPrecertificateVerification(chain));
+}
+
+TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCTWithIntermediate) {
+ scoped_refptr<X509Certificate> chain(CreateCertificateChainFromFile(
+ GetTestCertsDirectory(),
+ "ct-test-embedded-with-intermediate-chain.pem",
+ X509Certificate::FORMAT_AUTO));
+ ASSERT_TRUE(chain);
+ ASSERT_TRUE(CheckPrecertificateVerification(chain));
+}
+
+TEST_F(MultiLogCTVerifierTest,
+ VerifiesEmbeddedSCTWithIntermediateAndPreCA) {
+ scoped_refptr<X509Certificate> chain(CreateCertificateChainFromFile(
+ GetTestCertsDirectory(),
+ "ct-test-embedded-with-intermediate-preca-chain.pem",
+ X509Certificate::FORMAT_AUTO));
+ ASSERT_TRUE(chain);
+ ASSERT_TRUE(CheckPrecertificateVerification(chain));
+}
+
+TEST_F(MultiLogCTVerifierTest,
+ VerifiesSCTOverX509Cert) {
+ std::string sct(ct::GetTestSignedCertificateTimestamp());
+
+ std::string sct_list;
+ ASSERT_TRUE(ct::EncodeSCTListForTesting(sct, &sct_list));
+
+ ct::CTVerifyResult result;
+ EXPECT_EQ(OK, verifier_->Verify(chain_, "", sct_list, &result));
+ ASSERT_TRUE(CheckForSingleVerifiedSCTInResult(result));
+ ASSERT_TRUE(CheckForSCTOrigin(
+ result, ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION));
+}
+
+TEST_F(MultiLogCTVerifierTest,
+ IdentifiesSCTFromUnknownLog) {
+ std::string sct(ct::GetTestSignedCertificateTimestamp());
+
+ // Change a byte inside the Log ID part of the SCT so it does
+ // not match the log used in the tests
+ sct[15] = 't';
+
+ std::string sct_list;
+ ASSERT_TRUE(ct::EncodeSCTListForTesting(sct, &sct_list));
+
+ ct::CTVerifyResult result;
+ EXPECT_NE(OK, verifier_->Verify(chain_, sct_list, "", &result));
+ EXPECT_EQ(1U, result.unknown_logs_scts.size());
+}
+
+} // namespace
+
+} // namespace net
diff --git a/net/cert/signed_certificate_timestamp.h b/net/cert/signed_certificate_timestamp.h
index 2ff14ec..9c73dee 100644
--- a/net/cert/signed_certificate_timestamp.h
+++ b/net/cert/signed_certificate_timestamp.h
@@ -87,7 +87,7 @@ struct NET_EXPORT SignedCertificateTimestamp
// Source of the SCT - supplementary, not defined in CT RFC.
enum Origin {
SCT_EMBEDDED = 0,
- SCT_FROM_TLS_HANDSHAKE = 1,
+ SCT_FROM_TLS_EXTENSION = 1,
SCT_FROM_OCSP_RESPONSE = 2,
};
diff --git a/net/data/ssl/certificates/README b/net/data/ssl/certificates/README
index 86f10a6..0ef59fc 100644
--- a/net/data/ssl/certificates/README
+++ b/net/data/ssl/certificates/README
@@ -227,5 +227,11 @@ unit tests.
net/data/ssl/scripts/generate-policy-certs.sh
- ct-test-embedded-cert.pem
- Test certificate chain for Certificate Transparency: The leaf certificate
- in this file contains embedded SCTs, followed by the issuer certificate.
+- ct-test-embedded-with-intermediate-chain.pem
+- ct-test-embedded-with-intermediate-preca-chain.pem
+- ct-test-embedded-with-preca-chain.pem
+ Test certificate chains for Certificate Transparency: Each of these
+ files contains a leaf certificate as the first certificate, which has
+ embedded SCTs, followed by the issuer certificates chain.
+ All files are from the src/test/testdada directory in
+ https://code.google.com/p/certificate-transparency/
diff --git a/net/data/ssl/certificates/ct-test-embedded-with-intermediate-chain.pem b/net/data/ssl/certificates/ct-test-embedded-with-intermediate-chain.pem
new file mode 100644
index 0000000..2224461
--- /dev/null
+++ b/net/data/ssl/certificates/ct-test-embedded-with-intermediate-chain.pem
@@ -0,0 +1,188 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GB, O=Certificate Transparency Intermediate CA, ST=Wales, L=Erw Wen
+ Validity
+ Not Before: Jun 1 00:00:00 2012 GMT
+ Not After : Jun 1 00:00:00 2022 GMT
+ Subject: C=GB, O=Certificate Transparency, ST=Wales, L=Erw Wen
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:bb:27:2b:26:e5:de:b5:45:9d:4a:cc:a0:27:e8:
+ f1:2a:4d:83:9a:c3:73:0a:6a:10:9f:f7:e2:54:98:
+ dd:bd:3f:18:95:d0:8b:a4:1f:8d:e3:49:67:a3:a0:
+ 86:ce:13:a9:0d:d5:ad:bb:54:18:4b:dc:08:e1:ac:
+ 78:26:ad:b8:dc:9c:71:7b:fd:7d:a5:b4:1b:4d:b1:
+ 73:6e:00:f1:da:c3:ce:c9:81:9c:cb:1a:28:ba:12:
+ 0b:02:0a:82:0e:94:0d:d6:1f:95:b5:43:2a:4b:c0:
+ 5d:08:18:f1:8c:e2:15:4e:b3:8d:2f:a7:d2:2d:72:
+ b9:76:e5:60:db:0c:7f:c7:7f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ B1:B1:48:E6:58:E7:03:F5:F7:F3:10:5F:20:B3:C3:84:D7:EF:F1:BF
+ X509v3 Authority Key Identifier:
+ keyid:96:55:08:05:02:78:47:9E:87:73:76:41:31:BC:14:3A:47:E2:29:AB
+ DirName:/C=GB/O=Certificate Transparency CA/ST=Wales/L=Erw Wen
+ serial:09
+
+ X509v3 Basic Constraints:
+ CA:FALSE
+ 1.3.6.1.4.1.11129.2.4.2:
+ .z.x.v........RG.ah2].\yY..........?t.d...=.'.......G0E.!...E..9-...W.....E.L..8V.......u.. ^&...."...)....4...O.......f...?
+ Signature Algorithm: sha1WithRSAEncryption
+ 0f:95:a5:b4:e1:28:a9:14:b1:e8:8b:e8:b3:29:64:22:1b:58:
+ f4:55:84:33:d0:20:a8:e2:46:cc:a6:5a:40:bc:bf:5f:2d:48:
+ 93:3e:bc:99:be:69:27:ca:75:64:72:fb:0b:dc:7f:50:5f:41:
+ f4:62:f2:bc:19:d0:b2:99:c9:90:91:8d:f8:82:0f:3d:31:db:
+ 37:97:9e:8b:ad:56:3b:17:f0:0a:e6:7b:0f:87:31:c1:06:c9:
+ 43:a7:3b:f5:36:af:16:8a:fe:21:ef:4a:df:ca:e1:9a:3c:c0:
+ 74:89:99:92:bf:50:6b:c5:ce:1d:ec:aa:f0:7f:fe:eb:c8:05:
+ c0:39
+-----BEGIN CERTIFICATE-----
+MIIDZjCCAs+gAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJHQjEx
+MC8GA1UEChMoQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IEludGVybWVkaWF0ZSBD
+QTEOMAwGA1UECBMFV2FsZXMxEDAOBgNVBAcTB0VydyBXZW4wHhcNMTIwNjAxMDAw
+MDAwWhcNMjIwNjAxMDAwMDAwWjBSMQswCQYDVQQGEwJHQjEhMB8GA1UEChMYQ2Vy
+dGlmaWNhdGUgVHJhbnNwYXJlbmN5MQ4wDAYDVQQIEwVXYWxlczEQMA4GA1UEBxMH
+RXJ3IFdlbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuycrJuXetUWdSsyg
+J+jxKk2DmsNzCmoQn/fiVJjdvT8YldCLpB+N40lno6CGzhOpDdWtu1QYS9wI4ax4
+Jq243Jxxe/19pbQbTbFzbgDx2sPOyYGcyxoouhILAgqCDpQN1h+VtUMqS8BdCBjx
+jOIVTrONL6fSLXK5duVg2wx/x38CAwEAAaOCATowggE2MB0GA1UdDgQWBBSxsUjm
+WOcD9ffzEF8gs8OE1+/xvzB9BgNVHSMEdjB0gBSWVQgFAnhHnodzdkExvBQ6R+Ip
+q6FZpFcwVTELMAkGA1UEBhMCR0IxJDAiBgNVBAoTG0NlcnRpZmljYXRlIFRyYW5z
+cGFyZW5jeSBDQTEOMAwGA1UECBMFV2FsZXMxEDAOBgNVBAcTB0VydyBXZW6CAQkw
+CQYDVR0TBAIwADCBigYKKwYBBAHWeQIEAgR8BHoAeAB2AN8cLsEVAJRSR6lhaDJd
+3Fx5Wej3xtOI/AAuC70/dNdkAAABPdsn4qQAAAQDAEcwRQIhAKbTRRfzOS2exdJX
+rfHFl9xFvUzTtzhWxhap+5nlrnWoAiBeJsjRx+Ii/ozaKbrrBKg07pfTT9gXGPGq
+4M1m9LipPzANBgkqhkiG9w0BAQUFAAOBgQAPlaW04SipFLHoi+izKWQiG1j0VYQz
+0CCo4kbMplpAvL9fLUiTPryZvmknynVkcvsL3H9QX0H0YvK8GdCymcmQkY34gg89
+Mds3l56LrVY7F/AK5nsPhzHBBslDpzv1Nq8Wiv4h70rfyuGaPMB0iZmSv1Brxc4d
+7Krwf/7ryAXAOQ==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 9 (0x9)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GB, O=Certificate Transparency CA, ST=Wales, L=Erw Wen
+ Validity
+ Not Before: Jun 1 00:00:00 2012 GMT
+ Not After : Jun 1 00:00:00 2022 GMT
+ Subject: C=GB, O=Certificate Transparency Intermediate CA, ST=Wales, L=Erw Wen
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:d7:6a:67:8d:11:6f:52:2e:55:ff:82:1c:90:64:
+ 25:08:b7:07:4b:14:d7:71:15:90:64:f7:92:7e:fd:
+ ed:b8:71:35:a1:36:5e:e7:de:18:cb:d5:ce:86:5f:
+ 86:0c:78:f4:33:b4:d0:d3:d3:40:77:02:e7:a3:ef:
+ 54:2b:1d:fe:9b:ba:a7:cd:f9:4d:c5:97:5f:c7:29:
+ f8:6f:10:5f:38:1b:24:35:35:cf:9c:80:0f:5c:a7:
+ 80:c1:d3:c8:44:00:ee:65:d1:6e:e9:cf:52:db:8a:
+ df:fe:50:f5:c4:93:35:0b:21:90:bf:50:d5:bc:36:
+ f3:ca:c5:a8:da:ae:92:cd:8b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 96:55:08:05:02:78:47:9E:87:73:76:41:31:BC:14:3A:47:E2:29:AB
+ X509v3 Authority Key Identifier:
+ keyid:5F:9D:88:0D:C8:73:E6:54:D4:F8:0D:D8:E6:B0:C1:24:B4:47:C3:55
+ DirName:/C=GB/O=Certificate Transparency CA/ST=Wales/L=Erw Wen
+ serial:00
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha1WithRSAEncryption
+ 22:06:da:b1:c6:6b:71:dc:e0:95:c3:f6:aa:2e:f7:2c:f7:76:
+ 1b:e7:ab:d7:fc:39:c3:1a:4c:fe:1b:d9:6d:67:34:ca:82:f2:
+ 2d:de:5a:0c:8b:bb:dd:82:5d:7b:6f:3e:76:12:ad:8d:b3:00:
+ a7:e2:11:69:88:60:23:26:22:84:c3:aa:5d:21:91:ef:da:10:
+ bf:92:35:d3:7b:3a:2a:34:0d:59:41:9b:94:a4:85:66:f3:fa:
+ c3:cd:8b:53:d5:a4:e9:82:70:ea:d2:97:b0:72:10:f9:ce:4a:
+ 21:38:b1:88:11:14:3b:93:fa:4e:7a:87:dd:37:e1:38:5f:2c:
+ 29:08
+-----BEGIN CERTIFICATE-----
+MIIC3TCCAkagAwIBAgIBCTANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJHQjEk
+MCIGA1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4wDAYDVQQIEwVX
+YWxlczEQMA4GA1UEBxMHRXJ3IFdlbjAeFw0xMjA2MDEwMDAwMDBaFw0yMjA2MDEw
+MDAwMDBaMGIxCzAJBgNVBAYTAkdCMTEwLwYDVQQKEyhDZXJ0aWZpY2F0ZSBUcmFu
+c3BhcmVuY3kgSW50ZXJtZWRpYXRlIENBMQ4wDAYDVQQIEwVXYWxlczEQMA4GA1UE
+BxMHRXJ3IFdlbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA12pnjRFvUi5V
+/4IckGQlCLcHSxTXcRWQZPeSfv3tuHE1oTZe594Yy9XOhl+GDHj0M7TQ09NAdwLn
+o+9UKx3+m7qnzflNxZdfxyn4bxBfOBskNTXPnIAPXKeAwdPIRADuZdFu6c9S24rf
+/lD1xJM1CyGQv1DVvDbzysWo2q6SzYsCAwEAAaOBrzCBrDAdBgNVHQ4EFgQUllUI
+BQJ4R56Hc3ZBMbwUOkfiKaswfQYDVR0jBHYwdIAUX52IDchz5lTU+A3Y5rDBJLRH
+w1WhWaRXMFUxCzAJBgNVBAYTAkdCMSQwIgYDVQQKExtDZXJ0aWZpY2F0ZSBUcmFu
+c3BhcmVuY3kgQ0ExDjAMBgNVBAgTBVdhbGVzMRAwDgYDVQQHEwdFcncgV2VuggEA
+MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAIgbascZrcdzglcP2qi73
+LPd2G+er1/w5wxpM/hvZbWc0yoLyLd5aDIu73YJde28+dhKtjbMAp+IRaYhgIyYi
+hMOqXSGR79oQv5I103s6KjQNWUGblKSFZvP6w82LU9Wk6YJw6tKXsHIQ+c5KITix
+iBEUO5P6TnqH3TfhOF8sKQg=
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 0 (0x0)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GB, O=Certificate Transparency CA, ST=Wales, L=Erw Wen
+ Validity
+ Not Before: Jun 1 00:00:00 2012 GMT
+ Not After : Jun 1 00:00:00 2022 GMT
+ Subject: C=GB, O=Certificate Transparency CA, ST=Wales, L=Erw Wen
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:d5:8a:68:53:62:10:a2:71:19:93:6e:77:83:21:
+ 18:1c:2a:40:13:c6:d0:7b:8c:76:eb:91:57:d3:d0:
+ fb:4b:3b:51:6e:ce:cb:d1:c9:8d:91:c5:2f:74:3f:
+ ab:63:5d:55:09:9c:d1:3a:ba:f3:1a:e5:41:44:24:
+ 51:a7:4c:78:16:f2:24:3c:f8:48:cf:28:31:cc:e6:
+ 7b:a0:4a:5a:23:81:9f:3c:ba:37:e6:24:d9:c3:bd:
+ b2:99:b8:39:dd:fe:26:31:d2:cb:3a:84:fc:7b:b2:
+ b5:c5:2f:cf:c1:4f:ff:40:6f:5c:d4:46:69:cb:b2:
+ f7:cf:df:86:fb:6a:b9:d1:b1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 5F:9D:88:0D:C8:73:E6:54:D4:F8:0D:D8:E6:B0:C1:24:B4:47:C3:55
+ X509v3 Authority Key Identifier:
+ keyid:5F:9D:88:0D:C8:73:E6:54:D4:F8:0D:D8:E6:B0:C1:24:B4:47:C3:55
+ DirName:/C=GB/O=Certificate Transparency CA/ST=Wales/L=Erw Wen
+ serial:00
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha1WithRSAEncryption
+ 06:08:cc:4a:6d:64:f2:20:5e:14:6c:04:b2:76:f9:2b:0e:fa:
+ 94:a5:da:f2:3a:fc:38:06:60:6d:39:90:d0:a1:ea:23:3d:40:
+ 29:57:69:46:3b:04:66:61:e7:fa:1d:17:99:15:20:9a:ea:2e:
+ 0a:77:51:76:41:12:27:d7:c0:03:07:c7:47:0e:61:58:4f:d7:
+ 33:42:24:72:7f:51:d6:90:bc:47:a9:df:35:4d:b0:f6:eb:25:
+ 95:5d:e1:89:3c:4d:d5:20:2b:24:a2:f3:e4:40:d2:74:b5:4e:
+ 1b:d3:76:26:9c:a9:62:89:b7:6e:ca:a4:10:90:e1:4f:3b:0a:
+ 94:2e
+-----BEGIN CERTIFICATE-----
+MIIC0DCCAjmgAwIBAgIBADANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJHQjEk
+MCIGA1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4wDAYDVQQIEwVX
+YWxlczEQMA4GA1UEBxMHRXJ3IFdlbjAeFw0xMjA2MDEwMDAwMDBaFw0yMjA2MDEw
+MDAwMDBaMFUxCzAJBgNVBAYTAkdCMSQwIgYDVQQKExtDZXJ0aWZpY2F0ZSBUcmFu
+c3BhcmVuY3kgQ0ExDjAMBgNVBAgTBVdhbGVzMRAwDgYDVQQHEwdFcncgV2VuMIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVimhTYhCicRmTbneDIRgcKkATxtB7
+jHbrkVfT0PtLO1FuzsvRyY2RxS90P6tjXVUJnNE6uvMa5UFEJFGnTHgW8iQ8+EjP
+KDHM5nugSlojgZ88ujfmJNnDvbKZuDnd/iYx0ss6hPx7srXFL8/BT/9Ab1zURmnL
+svfP34b7arnRsQIDAQABo4GvMIGsMB0GA1UdDgQWBBRfnYgNyHPmVNT4DdjmsMEk
+tEfDVTB9BgNVHSMEdjB0gBRfnYgNyHPmVNT4DdjmsMEktEfDVaFZpFcwVTELMAkG
+A1UEBhMCR0IxJDAiBgNVBAoTG0NlcnRpZmljYXRlIFRyYW5zcGFyZW5jeSBDQTEO
+MAwGA1UECBMFV2FsZXMxEDAOBgNVBAcTB0VydyBXZW6CAQAwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQUFAAOBgQAGCMxKbWTyIF4UbASydvkrDvqUpdryOvw4BmBt
+OZDQoeojPUApV2lGOwRmYef6HReZFSCa6i4Kd1F2QRIn18ADB8dHDmFYT9czQiRy
+f1HWkLxHqd81TbD26yWVXeGJPE3VICskovPkQNJ0tU4b03YmnKliibduyqQQkOFP
+OwqULg==
+-----END CERTIFICATE-----
diff --git a/net/data/ssl/certificates/ct-test-embedded-with-intermediate-preca-chain.pem b/net/data/ssl/certificates/ct-test-embedded-with-intermediate-preca-chain.pem
new file mode 100644
index 0000000..597cebc
--- /dev/null
+++ b/net/data/ssl/certificates/ct-test-embedded-with-intermediate-preca-chain.pem
@@ -0,0 +1,188 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 3 (0x3)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GB, O=Certificate Transparency Intermediate CA, ST=Wales, L=Erw Wen
+ Validity
+ Not Before: Jun 1 00:00:00 2012 GMT
+ Not After : Jun 1 00:00:00 2022 GMT
+ Subject: C=GB, O=Certificate Transparency, ST=Wales, L=Erw Wen
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:d4:49:70:56:cd:fc:65:e1:34:2c:c3:df:6e:65:
+ 4b:8a:f0:10:47:02:ac:d2:27:5c:7d:3f:b1:fc:43:
+ 8a:89:b2:12:11:0d:64:19:bc:c1:3a:e4:7d:64:bb:
+ a2:41:e6:70:6b:9e:d6:27:f8:b3:4a:0d:7d:ff:1c:
+ 44:b9:62:87:c5:4b:ea:9d:10:dc:01:7b:ce:b6:4f:
+ 7b:6a:ff:3c:35:a4:74:af:ec:40:38:ab:36:40:b0:
+ cd:1f:b0:58:2e:c0:3b:17:9a:27:76:c8:c4:35:d1:
+ 4a:b4:88:2d:59:d7:b7:24:fa:37:7c:a6:db:08:39:
+ 21:73:f9:c6:05:6b:3a:ba:df
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 32:DA:55:18:D8:7F:1D:26:EA:27:67:97:3C:0B:EF:28:6E:78:6A:4A
+ X509v3 Authority Key Identifier:
+ keyid:96:55:08:05:02:78:47:9E:87:73:76:41:31:BC:14:3A:47:E2:29:AB
+ DirName:/C=GB/O=Certificate Transparency CA/ST=Wales/L=Erw Wen
+ serial:09
+
+ X509v3 Basic Constraints:
+ CA:FALSE
+ 1.3.6.1.4.1.11129.2.4.2:
+ .z.x.v........RG.ah2].\yY..........?t.d...=.'.......G0E.!.......!...<..p.3..7K ...e.^..C.0. .v.GQ8..v.810M..C....n..O..|....
+ Signature Algorithm: sha1WithRSAEncryption
+ 88:ee:4e:9e:5e:ed:6b:11:2c:c7:64:b1:51:ed:92:94:00:e9:
+ 40:67:89:c1:5f:bb:cf:cd:ab:2f:10:b4:00:23:41:39:e6:ce:
+ 65:c1:e5:1b:47:bf:7c:89:50:f8:0b:cc:d5:71:68:56:79:54:
+ ed:35:b0:ce:93:46:06:5a:5e:ae:5b:f9:5d:41:da:8e:27:ce:
+ e9:ee:ac:68:8f:4b:d3:43:f9:c2:88:83:27:ab:d8:b9:f6:8d:
+ cb:1e:30:50:04:1d:31:bd:a8:e2:dd:6d:39:b3:66:4d:e5:ce:
+ 08:70:f5:fc:7e:6a:00:d6:ed:00:52:84:58:d9:53:d2:37:58:
+ 6d:73
+-----BEGIN CERTIFICATE-----
+MIIDZjCCAs+gAwIBAgIBAzANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJHQjEx
+MC8GA1UEChMoQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IEludGVybWVkaWF0ZSBD
+QTEOMAwGA1UECBMFV2FsZXMxEDAOBgNVBAcTB0VydyBXZW4wHhcNMTIwNjAxMDAw
+MDAwWhcNMjIwNjAxMDAwMDAwWjBSMQswCQYDVQQGEwJHQjEhMB8GA1UEChMYQ2Vy
+dGlmaWNhdGUgVHJhbnNwYXJlbmN5MQ4wDAYDVQQIEwVXYWxlczEQMA4GA1UEBxMH
+RXJ3IFdlbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1ElwVs38ZeE0LMPf
+bmVLivAQRwKs0idcfT+x/EOKibISEQ1kGbzBOuR9ZLuiQeZwa57WJ/izSg19/xxE
+uWKHxUvqnRDcAXvOtk97av88NaR0r+xAOKs2QLDNH7BYLsA7F5ondsjENdFKtIgt
+Wde3JPo3fKbbCDkhc/nGBWs6ut8CAwEAAaOCATowggE2MB0GA1UdDgQWBBQy2lUY
+2H8dJuonZ5c8C+8obnhqSjB9BgNVHSMEdjB0gBSWVQgFAnhHnodzdkExvBQ6R+Ip
+q6FZpFcwVTELMAkGA1UEBhMCR0IxJDAiBgNVBAoTG0NlcnRpZmljYXRlIFRyYW5z
+cGFyZW5jeSBDQTEOMAwGA1UECBMFV2FsZXMxEDAOBgNVBAcTB0VydyBXZW6CAQkw
+CQYDVR0TBAIwADCBigYKKwYBBAHWeQIEAgR8BHoAeAB2AN8cLsEVAJRSR6lhaDJd
+3Fx5Wej3xtOI/AAuC70/dNdkAAABPdsn474AAAQDAEcwRQIhANn2Ggf+4CHjFZ88
+ovVw2DP/ATdLIJbLpWWMXhb7Q+swAiALdv5HUTjYz3aDODEwTavwQ+sSE8luE/9P
+o39808jcHzANBgkqhkiG9w0BAQUFAAOBgQCI7k6eXu1rESzHZLFR7ZKUAOlAZ4nB
+X7vPzasvELQAI0E55s5lweUbR798iVD4C8zVcWhWeVTtNbDOk0YGWl6uW/ldQdqO
+J87p7qxoj0vTQ/nCiIMnq9i59o3LHjBQBB0xvaji3W05s2ZN5c4IcPX8fmoA1u0A
+UoRY2VPSN1htcw==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 9 (0x9)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GB, O=Certificate Transparency CA, ST=Wales, L=Erw Wen
+ Validity
+ Not Before: Jun 1 00:00:00 2012 GMT
+ Not After : Jun 1 00:00:00 2022 GMT
+ Subject: C=GB, O=Certificate Transparency Intermediate CA, ST=Wales, L=Erw Wen
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:d7:6a:67:8d:11:6f:52:2e:55:ff:82:1c:90:64:
+ 25:08:b7:07:4b:14:d7:71:15:90:64:f7:92:7e:fd:
+ ed:b8:71:35:a1:36:5e:e7:de:18:cb:d5:ce:86:5f:
+ 86:0c:78:f4:33:b4:d0:d3:d3:40:77:02:e7:a3:ef:
+ 54:2b:1d:fe:9b:ba:a7:cd:f9:4d:c5:97:5f:c7:29:
+ f8:6f:10:5f:38:1b:24:35:35:cf:9c:80:0f:5c:a7:
+ 80:c1:d3:c8:44:00:ee:65:d1:6e:e9:cf:52:db:8a:
+ df:fe:50:f5:c4:93:35:0b:21:90:bf:50:d5:bc:36:
+ f3:ca:c5:a8:da:ae:92:cd:8b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 96:55:08:05:02:78:47:9E:87:73:76:41:31:BC:14:3A:47:E2:29:AB
+ X509v3 Authority Key Identifier:
+ keyid:5F:9D:88:0D:C8:73:E6:54:D4:F8:0D:D8:E6:B0:C1:24:B4:47:C3:55
+ DirName:/C=GB/O=Certificate Transparency CA/ST=Wales/L=Erw Wen
+ serial:00
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha1WithRSAEncryption
+ 22:06:da:b1:c6:6b:71:dc:e0:95:c3:f6:aa:2e:f7:2c:f7:76:
+ 1b:e7:ab:d7:fc:39:c3:1a:4c:fe:1b:d9:6d:67:34:ca:82:f2:
+ 2d:de:5a:0c:8b:bb:dd:82:5d:7b:6f:3e:76:12:ad:8d:b3:00:
+ a7:e2:11:69:88:60:23:26:22:84:c3:aa:5d:21:91:ef:da:10:
+ bf:92:35:d3:7b:3a:2a:34:0d:59:41:9b:94:a4:85:66:f3:fa:
+ c3:cd:8b:53:d5:a4:e9:82:70:ea:d2:97:b0:72:10:f9:ce:4a:
+ 21:38:b1:88:11:14:3b:93:fa:4e:7a:87:dd:37:e1:38:5f:2c:
+ 29:08
+-----BEGIN CERTIFICATE-----
+MIIC3TCCAkagAwIBAgIBCTANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJHQjEk
+MCIGA1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4wDAYDVQQIEwVX
+YWxlczEQMA4GA1UEBxMHRXJ3IFdlbjAeFw0xMjA2MDEwMDAwMDBaFw0yMjA2MDEw
+MDAwMDBaMGIxCzAJBgNVBAYTAkdCMTEwLwYDVQQKEyhDZXJ0aWZpY2F0ZSBUcmFu
+c3BhcmVuY3kgSW50ZXJtZWRpYXRlIENBMQ4wDAYDVQQIEwVXYWxlczEQMA4GA1UE
+BxMHRXJ3IFdlbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA12pnjRFvUi5V
+/4IckGQlCLcHSxTXcRWQZPeSfv3tuHE1oTZe594Yy9XOhl+GDHj0M7TQ09NAdwLn
+o+9UKx3+m7qnzflNxZdfxyn4bxBfOBskNTXPnIAPXKeAwdPIRADuZdFu6c9S24rf
+/lD1xJM1CyGQv1DVvDbzysWo2q6SzYsCAwEAAaOBrzCBrDAdBgNVHQ4EFgQUllUI
+BQJ4R56Hc3ZBMbwUOkfiKaswfQYDVR0jBHYwdIAUX52IDchz5lTU+A3Y5rDBJLRH
+w1WhWaRXMFUxCzAJBgNVBAYTAkdCMSQwIgYDVQQKExtDZXJ0aWZpY2F0ZSBUcmFu
+c3BhcmVuY3kgQ0ExDjAMBgNVBAgTBVdhbGVzMRAwDgYDVQQHEwdFcncgV2VuggEA
+MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAIgbascZrcdzglcP2qi73
+LPd2G+er1/w5wxpM/hvZbWc0yoLyLd5aDIu73YJde28+dhKtjbMAp+IRaYhgIyYi
+hMOqXSGR79oQv5I103s6KjQNWUGblKSFZvP6w82LU9Wk6YJw6tKXsHIQ+c5KITix
+iBEUO5P6TnqH3TfhOF8sKQg=
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 0 (0x0)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GB, O=Certificate Transparency CA, ST=Wales, L=Erw Wen
+ Validity
+ Not Before: Jun 1 00:00:00 2012 GMT
+ Not After : Jun 1 00:00:00 2022 GMT
+ Subject: C=GB, O=Certificate Transparency CA, ST=Wales, L=Erw Wen
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:d5:8a:68:53:62:10:a2:71:19:93:6e:77:83:21:
+ 18:1c:2a:40:13:c6:d0:7b:8c:76:eb:91:57:d3:d0:
+ fb:4b:3b:51:6e:ce:cb:d1:c9:8d:91:c5:2f:74:3f:
+ ab:63:5d:55:09:9c:d1:3a:ba:f3:1a:e5:41:44:24:
+ 51:a7:4c:78:16:f2:24:3c:f8:48:cf:28:31:cc:e6:
+ 7b:a0:4a:5a:23:81:9f:3c:ba:37:e6:24:d9:c3:bd:
+ b2:99:b8:39:dd:fe:26:31:d2:cb:3a:84:fc:7b:b2:
+ b5:c5:2f:cf:c1:4f:ff:40:6f:5c:d4:46:69:cb:b2:
+ f7:cf:df:86:fb:6a:b9:d1:b1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 5F:9D:88:0D:C8:73:E6:54:D4:F8:0D:D8:E6:B0:C1:24:B4:47:C3:55
+ X509v3 Authority Key Identifier:
+ keyid:5F:9D:88:0D:C8:73:E6:54:D4:F8:0D:D8:E6:B0:C1:24:B4:47:C3:55
+ DirName:/C=GB/O=Certificate Transparency CA/ST=Wales/L=Erw Wen
+ serial:00
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha1WithRSAEncryption
+ 06:08:cc:4a:6d:64:f2:20:5e:14:6c:04:b2:76:f9:2b:0e:fa:
+ 94:a5:da:f2:3a:fc:38:06:60:6d:39:90:d0:a1:ea:23:3d:40:
+ 29:57:69:46:3b:04:66:61:e7:fa:1d:17:99:15:20:9a:ea:2e:
+ 0a:77:51:76:41:12:27:d7:c0:03:07:c7:47:0e:61:58:4f:d7:
+ 33:42:24:72:7f:51:d6:90:bc:47:a9:df:35:4d:b0:f6:eb:25:
+ 95:5d:e1:89:3c:4d:d5:20:2b:24:a2:f3:e4:40:d2:74:b5:4e:
+ 1b:d3:76:26:9c:a9:62:89:b7:6e:ca:a4:10:90:e1:4f:3b:0a:
+ 94:2e
+-----BEGIN CERTIFICATE-----
+MIIC0DCCAjmgAwIBAgIBADANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJHQjEk
+MCIGA1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4wDAYDVQQIEwVX
+YWxlczEQMA4GA1UEBxMHRXJ3IFdlbjAeFw0xMjA2MDEwMDAwMDBaFw0yMjA2MDEw
+MDAwMDBaMFUxCzAJBgNVBAYTAkdCMSQwIgYDVQQKExtDZXJ0aWZpY2F0ZSBUcmFu
+c3BhcmVuY3kgQ0ExDjAMBgNVBAgTBVdhbGVzMRAwDgYDVQQHEwdFcncgV2VuMIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVimhTYhCicRmTbneDIRgcKkATxtB7
+jHbrkVfT0PtLO1FuzsvRyY2RxS90P6tjXVUJnNE6uvMa5UFEJFGnTHgW8iQ8+EjP
+KDHM5nugSlojgZ88ujfmJNnDvbKZuDnd/iYx0ss6hPx7srXFL8/BT/9Ab1zURmnL
+svfP34b7arnRsQIDAQABo4GvMIGsMB0GA1UdDgQWBBRfnYgNyHPmVNT4DdjmsMEk
+tEfDVTB9BgNVHSMEdjB0gBRfnYgNyHPmVNT4DdjmsMEktEfDVaFZpFcwVTELMAkG
+A1UEBhMCR0IxJDAiBgNVBAoTG0NlcnRpZmljYXRlIFRyYW5zcGFyZW5jeSBDQTEO
+MAwGA1UECBMFV2FsZXMxEDAOBgNVBAcTB0VydyBXZW6CAQAwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQUFAAOBgQAGCMxKbWTyIF4UbASydvkrDvqUpdryOvw4BmBt
+OZDQoeojPUApV2lGOwRmYef6HReZFSCa6i4Kd1F2QRIn18ADB8dHDmFYT9czQiRy
+f1HWkLxHqd81TbD26yWVXeGJPE3VICskovPkQNJ0tU4b03YmnKliibduyqQQkOFP
+OwqULg==
+-----END CERTIFICATE-----
diff --git a/net/data/ssl/certificates/ct-test-embedded-with-preca-chain.pem b/net/data/ssl/certificates/ct-test-embedded-with-preca-chain.pem
new file mode 100644
index 0000000..8e206a7
--- /dev/null
+++ b/net/data/ssl/certificates/ct-test-embedded-with-preca-chain.pem
@@ -0,0 +1,38 @@
+-----BEGIN CERTIFICATE-----
+MIIDWTCCAsKgAwIBAgIBCDANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJHQjEk
+MCIGA1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4wDAYDVQQIEwVX
+YWxlczEQMA4GA1UEBxMHRXJ3IFdlbjAeFw0xMjA2MDEwMDAwMDBaFw0yMjA2MDEw
+MDAwMDBaMFIxCzAJBgNVBAYTAkdCMSEwHwYDVQQKExhDZXJ0aWZpY2F0ZSBUcmFu
+c3BhcmVuY3kxDjAMBgNVBAgTBVdhbGVzMRAwDgYDVQQHEwdFcncgV2VuMIGfMA0G
+CSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvrurKxRq3zr356srn3RdSleGTlVoXmJrv
+jZerfN/3dhCTwLgj0qTjpRoXuG8oFitmolOJNevs3BA2Iz2i3WUxsMY7zGh2Hr3I
+VAN7dzmSRrhwp7crFMmxZn3gmpZA7Z8/PHJdlQtNJlWYaf5/HpGaZut201wBF8a8
+0NjP0hAosQIDAQABo4IBOjCCATYwHQYDVR0OBBYEFGEsZO+sebcoOXydk+bfhkZf
+p2qIMH0GA1UdIwR2MHSAFF+diA3Ic+ZU1PgN2OawwSS0R8NVoVmkVzBVMQswCQYD
+VQQGEwJHQjEkMCIGA1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4w
+DAYDVQQIEwVXYWxlczEQMA4GA1UEBxMHRXJ3IFdlboIBADAJBgNVHRMEAjAAMIGK
+BgorBgEEAdZ5AgQCBHwEegB4AHYA3xwuwRUAlFJHqWFoMl3cXHlZ6PfG04j8AC4L
+vT9012QAAAE92yfgWwAABAMARzBFAiB6p5YExHSA83J7CE+Qs5ifeQkYheAEhEMa
+Kil8vzo1XAIhALSf2BILDWRM1+dSabTaYxepNWy5UCJPwRzClrLjmyOGMA0GCSqG
+SIb3DQEBBQUAA4GBAKOobEGtAIiiWu3E57Upot2/nhh/+zYhV+kwLZYbc7Q8ugrh
+4jDZ5FBJt+jJJHkuu+fRdbqoexcN+tjueImEWZ0FJXmUCE4uDnlvylg2iBw+BTVT
+4GqyMPkZCJuRTkqOLaRfiofyyBolph8E/hys5gFVZTgn1B+tnwZY8ofQWBks
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC0DCCAjmgAwIBAgIBADANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJHQjEk
+MCIGA1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4wDAYDVQQIEwVX
+YWxlczEQMA4GA1UEBxMHRXJ3IFdlbjAeFw0xMjA2MDEwMDAwMDBaFw0yMjA2MDEw
+MDAwMDBaMFUxCzAJBgNVBAYTAkdCMSQwIgYDVQQKExtDZXJ0aWZpY2F0ZSBUcmFu
+c3BhcmVuY3kgQ0ExDjAMBgNVBAgTBVdhbGVzMRAwDgYDVQQHEwdFcncgV2VuMIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVimhTYhCicRmTbneDIRgcKkATxtB7
+jHbrkVfT0PtLO1FuzsvRyY2RxS90P6tjXVUJnNE6uvMa5UFEJFGnTHgW8iQ8+EjP
+KDHM5nugSlojgZ88ujfmJNnDvbKZuDnd/iYx0ss6hPx7srXFL8/BT/9Ab1zURmnL
+svfP34b7arnRsQIDAQABo4GvMIGsMB0GA1UdDgQWBBRfnYgNyHPmVNT4DdjmsMEk
+tEfDVTB9BgNVHSMEdjB0gBRfnYgNyHPmVNT4DdjmsMEktEfDVaFZpFcwVTELMAkG
+A1UEBhMCR0IxJDAiBgNVBAoTG0NlcnRpZmljYXRlIFRyYW5zcGFyZW5jeSBDQTEO
+MAwGA1UECBMFV2FsZXMxEDAOBgNVBAcTB0VydyBXZW6CAQAwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQUFAAOBgQAGCMxKbWTyIF4UbASydvkrDvqUpdryOvw4BmBt
+OZDQoeojPUApV2lGOwRmYef6HReZFSCa6i4Kd1F2QRIn18ADB8dHDmFYT9czQiRy
+f1HWkLxHqd81TbD26yWVXeGJPE3VICskovPkQNJ0tU4b03YmnKliibduyqQQkOFP
+OwqULg==
+-----END CERTIFICATE-----
diff --git a/net/net.gyp b/net/net.gyp
index 9b80bd4..b391ce2 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -279,11 +279,16 @@
'cert/ct_objects_extractor_openssl.cc',
'cert/ct_serialization.cc',
'cert/ct_serialization.h',
+ 'cert/ct_verifier.h',
+ 'cert/ct_verify_result.cc',
+ 'cert/ct_verify_result.h',
'cert/ev_root_ca_metadata.cc',
'cert/ev_root_ca_metadata.h',
'cert/jwk_serializer_nss.cc',
'cert/jwk_serializer_openssl.cc',
'cert/jwk_serializer.h',
+ 'cert/multi_log_ct_verifier.cc',
+ 'cert/multi_log_ct_verifier.h',
'cert/multi_threaded_cert_verifier.cc',
'cert/multi_threaded_cert_verifier.h',
'cert/nss_cert_database.cc',
@@ -1606,6 +1611,7 @@
'cert/ct_serialization_unittest.cc',
'cert/ev_root_ca_metadata_unittest.cc',
'cert/jwk_serializer_unittest.cc',
+ 'cert/multi_log_ct_verifier_unittest.cc',
'cert/multi_threaded_cert_verifier_unittest.cc',
'cert/nss_cert_database_unittest.cc',
'cert/pem_tokenizer_unittest.cc',
@@ -2073,6 +2079,7 @@
# functionality is ported to OpenSSL.
'sources!': [
'cert/ct_objects_extractor_unittest.cc',
+ 'cert/multi_log_ct_verifier_unittest.cc',
'cert/nss_cert_database_unittest.cc',
'cert/x509_util_nss_unittest.cc',
'quic/test_tools/crypto_test_utils_nss.cc',
diff --git a/net/test/cert_test_util.cc b/net/test/cert_test_util.cc
index 5ec0774..3ccfa65 100644
--- a/net/test/cert_test_util.cc
+++ b/net/test/cert_test_util.cc
@@ -26,6 +26,24 @@ CertificateList CreateCertificateListFromFile(
format);
}
+scoped_refptr<X509Certificate> CreateCertificateChainFromFile(
+ const base::FilePath& certs_dir,
+ const std::string& cert_file,
+ int format) {
+ CertificateList certs = CreateCertificateListFromFile(
+ certs_dir, cert_file, format);
+ if (certs.empty())
+ return NULL;
+
+ X509Certificate::OSCertHandles intermediates;
+ for (size_t i = 1; i < certs.size(); ++i)
+ intermediates.push_back(certs[i]->os_cert_handle());
+
+ scoped_refptr<X509Certificate> result(X509Certificate::CreateFromHandle(
+ certs[0]->os_cert_handle(), intermediates));
+ return result;
+}
+
scoped_refptr<X509Certificate> ImportCertFromFile(
const base::FilePath& certs_dir,
const std::string& cert_file) {
diff --git a/net/test/cert_test_util.h b/net/test/cert_test_util.h
index d4aa4d7..31b768a 100644
--- a/net/test/cert_test_util.h
+++ b/net/test/cert_test_util.h
@@ -19,12 +19,22 @@ namespace net {
class EVRootCAMetadata;
+// Imports all of the certificates in |cert_file|, a file in |certs_dir|,
+// // into a CertificateList.
CertificateList CreateCertificateListFromFile(const base::FilePath& certs_dir,
const std::string& cert_file,
int format);
-// Imports a certificate file in the directory net::GetTestCertsDirectory()
-// returns.
+// Imports all of the certificates in |cert_file|, a file in |certs_dir|, into
+// a new X509Certificate. The first certificate in the chain will be used for
+// the returned cert, with any additional certificates configured as
+// intermediate certificates.
+scoped_refptr<X509Certificate> CreateCertificateChainFromFile(
+ const base::FilePath& certs_dir,
+ const std::string& cert_file,
+ int format);
+
+// Imports a single certificate from |cert_file|.
// |certs_dir| represents the test certificates directory. |cert_file| is the
// name of the certificate file. If cert_file contains multiple certificates,
// the first certificate found will be returned.