// 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 #include "base/file_util.h" #include "base/files/file_path.h" #include "net/base/capturing_net_log.h" #include "net/base/net_errors.h" #include "net/base/net_log.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 { const char kLogDescription[] = "somelog"; class MultiLogCTVerifierTest : public ::testing::Test { public: virtual void SetUp() OVERRIDE { scoped_ptr log( CTLogVerifier::Create(ct::GetTestPublicKey(), kLogDescription)); 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.invalid_scts.empty() && result.unknown_logs_scts.empty() && result.verified_scts[0]->log_description == kLogDescription; } bool CheckForSCTOrigin( const ct::CTVerifyResult& result, ct::SignedCertificateTimestamp::Origin origin) { return (result.verified_scts.size() > 0) && (result.verified_scts[0]->origin == origin); } bool CheckForEmbeddedSCTInNetLog(CapturingNetLog& net_log) { CapturingNetLog::CapturedEntryList entries; net_log.GetEntries(&entries); if (entries.size() != 2) return false; const CapturingNetLog::CapturedEntry& received(entries[0]); std::string embedded_scts; if (!received.GetStringValue("embedded_scts", &embedded_scts)) return false; if (embedded_scts.empty()) return false; //XXX(eranm): entries[1] is the NetLog message with the checked SCTs. //When CapturedEntry has methods to get a dictionary, rather than just //a string, add more checks here. return true; } bool CheckPrecertificateVerification(scoped_refptr chain) { ct::CTVerifyResult result; CapturingNetLog net_log; BoundNetLog bound_net_log = BoundNetLog::Make(&net_log, NetLog::SOURCE_CONNECT_JOB); return (verifier_->Verify(chain, std::string(), std::string(), &result, bound_net_log) == OK) && CheckForSingleVerifiedSCTInResult(result) && CheckForSCTOrigin( result, ct::SignedCertificateTimestamp::SCT_EMBEDDED) && CheckForEmbeddedSCTInNetLog(net_log); } protected: scoped_ptr verifier_; scoped_refptr chain_; }; TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCT) { scoped_refptr chain( CreateCertificateChainFromFile(GetTestCertsDirectory(), "ct-test-embedded-cert.pem", X509Certificate::FORMAT_AUTO)); ASSERT_TRUE(chain); ASSERT_TRUE(CheckPrecertificateVerification(chain)); } TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCTWithPreCA) { scoped_refptr 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 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 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_, std::string(), sct_list, &result, BoundNetLog())); 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_, std::string(), sct_list, &result, BoundNetLog())); EXPECT_EQ(1U, result.unknown_logs_scts.size()); EXPECT_EQ("", result.unknown_logs_scts[0]->log_description); } } // namespace } // namespace net