| Index: components/certificate_transparency/single_tree_tracker_unittest.cc
|
| diff --git a/components/certificate_transparency/single_tree_tracker_unittest.cc b/components/certificate_transparency/single_tree_tracker_unittest.cc
|
| index 43ea4fd446a7c7c450a8f7b0e0159b95a29bbb22..3da78d1cfd6bc6d858153a4f772df9a0221a303d 100644
|
| --- a/components/certificate_transparency/single_tree_tracker_unittest.cc
|
| +++ b/components/certificate_transparency/single_tree_tracker_unittest.cc
|
| @@ -7,16 +7,28 @@
|
| #include <string>
|
| #include <utility>
|
|
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/run_loop.h"
|
| #include "base/strings/string_piece.h"
|
| #include "base/test/histogram_tester.h"
|
| +#include "components/certificate_transparency/log_dns_client.h"
|
| +#include "components/certificate_transparency/mock_log_dns_traffic.h"
|
| +#include "crypto/sha2.h"
|
| +#include "net/base/network_change_notifier.h"
|
| #include "net/cert/ct_log_verifier.h"
|
| #include "net/cert/ct_serialization.h"
|
| #include "net/cert/signed_certificate_timestamp.h"
|
| #include "net/cert/signed_tree_head.h"
|
| #include "net/cert/x509_certificate.h"
|
| +#include "net/dns/dns_client.h"
|
| +#include "net/log/net_log.h"
|
| #include "net/test/ct_test_util.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| +using net::ct::SignedCertificateTimestamp;
|
| +using net::ct::SignedTreeHead;
|
| +
|
| namespace certificate_transparency {
|
|
|
| namespace {
|
| @@ -24,8 +36,8 @@ namespace {
|
| const char kCanCheckForInclusionHistogramName[] =
|
| "Net.CertificateTransparency.CanInclusionCheckSCT";
|
|
|
| -bool GetOldSignedTreeHead(net::ct::SignedTreeHead* sth) {
|
| - sth->version = net::ct::SignedTreeHead::V1;
|
| +bool GetOldSignedTreeHead(SignedTreeHead* sth) {
|
| + sth->version = SignedTreeHead::V1;
|
| sth->timestamp = base::Time::UnixEpoch() +
|
| base::TimeDelta::FromMilliseconds(INT64_C(1348589665525));
|
| sth->tree_size = 12u;
|
| @@ -51,6 +63,23 @@ bool GetOldSignedTreeHead(net::ct::SignedTreeHead* sth) {
|
| return DecodeDigitallySigned(&sp, &(sth->signature)) && sp.empty();
|
| }
|
|
|
| +// TODO(eranm): Extract common method to net/test/ct_test_util.h
|
| +std::vector<std::string> GetSampleAuditProof(size_t length) {
|
| + std::vector<std::string> audit_proof(length);
|
| + // Makes each node of the audit proof different, so that tests are able to
|
| + // confirm that the audit proof is reconstructed in the correct order.
|
| + for (size_t i = 0; i < length; ++i) {
|
| + std::string node(crypto::kSHA256Length, '\0');
|
| + // Each node is 32 bytes, with each byte having a different value.
|
| + for (size_t j = 0; j < crypto::kSHA256Length; ++j) {
|
| + node[j] = static_cast<char>((-127 + i + j) % 128);
|
| + }
|
| + audit_proof[i].assign(std::move(node));
|
| + }
|
| +
|
| + return audit_proof;
|
| +}
|
| +
|
| } // namespace
|
|
|
| class SingleTreeTrackerTest : public ::testing::Test {
|
| @@ -62,19 +91,33 @@ class SingleTreeTrackerTest : public ::testing::Test {
|
| ASSERT_TRUE(log_);
|
| ASSERT_EQ(log_->key_id(), net::ct::GetTestPublicKeyId());
|
|
|
| - tree_tracker_.reset(new SingleTreeTracker(log_));
|
| + net_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
|
| +
|
| + std::unique_ptr<net::DnsClient> dns_client =
|
| + net::DnsClient::CreateClient(net_log_.net_log());
|
| +
|
| + log_dns_client_ =
|
| + base::MakeUnique<LogDnsClient>(std::move(dns_client), net_log_);
|
| +
|
| + tree_tracker_.reset(new SingleTreeTracker(log_, log_dns_client_.get()));
|
| +
|
| const std::string der_test_cert(net::ct::GetDerEncodedX509Cert());
|
| chain_ = net::X509Certificate::CreateFromBytes(der_test_cert.data(),
|
| der_test_cert.length());
|
| ASSERT_TRUE(chain_.get());
|
| net::ct::GetX509CertSCT(&cert_sct_);
|
| + cert_sct_->origin = SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE;
|
| }
|
|
|
| protected:
|
| + base::MessageLoopForIO message_loop_;
|
| scoped_refptr<const net::CTLogVerifier> log_;
|
| + std::unique_ptr<net::NetworkChangeNotifier> net_change_notifier_;
|
| + std::unique_ptr<LogDnsClient> log_dns_client_;
|
| std::unique_ptr<SingleTreeTracker> tree_tracker_;
|
| scoped_refptr<net::X509Certificate> chain_;
|
| - scoped_refptr<net::ct::SignedCertificateTimestamp> cert_sct_;
|
| + scoped_refptr<SignedCertificateTimestamp> cert_sct_;
|
| + net::BoundNetLog net_log_;
|
| };
|
|
|
| // Test that an SCT is classified as pending for a newer STH if the
|
| @@ -104,7 +147,7 @@ TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTNoSTH) {
|
| TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTWithRecentSTH) {
|
| base::HistogramTester histograms;
|
| // Provide an STH to the tree_tracker_.
|
| - net::ct::SignedTreeHead sth;
|
| + SignedTreeHead sth;
|
| net::ct::GetSampleSignedTreeHead(&sth);
|
| tree_tracker_->NewSTHObserved(sth);
|
|
|
| @@ -143,7 +186,7 @@ TEST_F(SingleTreeTrackerTest, CorrectlyUpdatesSCTStatusOnNewSTH) {
|
| histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
|
|
|
| // Provide with a fresh STH
|
| - net::ct::SignedTreeHead sth;
|
| + SignedTreeHead sth;
|
| net::ct::GetSampleSignedTreeHead(&sth);
|
| tree_tracker_->NewSTHObserved(sth);
|
|
|
| @@ -168,7 +211,7 @@ TEST_F(SingleTreeTrackerTest, DoesNotUpdatesSCTStatusOnOldSTH) {
|
| tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
|
|
|
| // Provide an old STH for the same log.
|
| - net::ct::SignedTreeHead sth;
|
| + SignedTreeHead sth;
|
| GetOldSignedTreeHead(&sth);
|
| tree_tracker_->NewSTHObserved(sth);
|
|
|
| @@ -184,7 +227,7 @@ TEST_F(SingleTreeTrackerTest, DoesNotUpdatesSCTStatusOnOldSTH) {
|
| TEST_F(SingleTreeTrackerTest, LogsUMAForNewSCTAndOldSTH) {
|
| base::HistogramTester histograms;
|
| // Provide an old STH for the same log.
|
| - net::ct::SignedTreeHead sth;
|
| + SignedTreeHead sth;
|
| GetOldSignedTreeHead(&sth);
|
| tree_tracker_->NewSTHObserved(sth);
|
|
|
| @@ -199,4 +242,118 @@ TEST_F(SingleTreeTrackerTest, LogsUMAForNewSCTAndOldSTH) {
|
| histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 1, 1);
|
| }
|
|
|
| +// Test that an entry transitions to the "not found" state if the LogDnsClient
|
| +// fails to get a leaf index.
|
| +TEST_F(SingleTreeTrackerTest, TestEntryNotPendingAfterLeafIndexFetchFailure) {
|
| + MockLogDnsTraffic mock_dns;
|
| + mock_dns.InitializeDnsConfig();
|
| + mock_dns.ExpectRequestAndSocketError(
|
| + "ASTEWZDDDMBHBVWSAQLIZUSNBMSLMIQMDZNH56TBNXWRMW5XALTA.hash.dns.example."
|
| + "com",
|
| + net::Error::ERR_FAILED);
|
| +
|
| + log_dns_client_ =
|
| + base::MakeUnique<LogDnsClient>(mock_dns.CreateDnsClient(), net_log_);
|
| + tree_tracker_.reset(new SingleTreeTracker(log_, log_dns_client_.get()));
|
| +
|
| + tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
|
| + EXPECT_EQ(
|
| + SingleTreeTracker::SCT_PENDING_NEWER_STH,
|
| + tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
|
| +
|
| + // Provide with a fresh STH
|
| + SignedTreeHead sth;
|
| + net::ct::GetSampleSignedTreeHead(&sth);
|
| + tree_tracker_->NewSTHObserved(sth);
|
| + base::RunLoop().RunUntilIdle();
|
| +
|
| + EXPECT_EQ(
|
| + SingleTreeTracker::SCT_NOT_OBSERVED,
|
| + tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
|
| +}
|
| +
|
| +// Test that an entry transitions to the "not found" state if the LogDnsClient
|
| +// succeeds to get a leaf index but fails to get an inclusion proof.
|
| +TEST_F(SingleTreeTrackerTest, TestEntryNotPendingAfterInclusionCheckFailure) {
|
| + MockLogDnsTraffic mock_dns;
|
| + mock_dns.InitializeDnsConfig();
|
| + mock_dns.ExpectLeafIndexRequestAndResponse(
|
| + "ASTEWZDDDMBHBVWSAQLIZUSNBMSLMIQMDZNH56TBNXWRMW5XALTA.hash.dns.example."
|
| + "com",
|
| + "12");
|
| + mock_dns.ExpectRequestAndSocketError("0.12.21.tree.dns.example.com",
|
| + net::Error::ERR_FAILED);
|
| +
|
| + log_dns_client_ =
|
| + base::MakeUnique<LogDnsClient>(mock_dns.CreateDnsClient(), net_log_);
|
| + tree_tracker_.reset(new SingleTreeTracker(log_, log_dns_client_.get()));
|
| +
|
| + tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
|
| + EXPECT_EQ(
|
| + SingleTreeTracker::SCT_PENDING_NEWER_STH,
|
| + tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
|
| +
|
| + // Provide with a fresh STH
|
| + SignedTreeHead sth;
|
| + net::ct::GetSampleSignedTreeHead(&sth);
|
| + tree_tracker_->NewSTHObserved(sth);
|
| + base::RunLoop().RunUntilIdle();
|
| +
|
| + EXPECT_EQ(
|
| + SingleTreeTracker::SCT_NOT_OBSERVED,
|
| + tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
|
| +}
|
| +
|
| +// Test that an entry transitions to the "included" state if the LogDnsClient
|
| +// succeeds to get a leaf index and an inclusion proof.
|
| +TEST_F(SingleTreeTrackerTest, TestEntryIncludedAfterInclusionCheckSuccess) {
|
| + const std::vector<std::string> audit_proof = GetSampleAuditProof(3);
|
| +
|
| + MockLogDnsTraffic mock_dns;
|
| + mock_dns.InitializeDnsConfig();
|
| + mock_dns.ExpectLeafIndexRequestAndResponse(
|
| + "ASTEWZDDDMBHBVWSAQLIZUSNBMSLMIQMDZNH56TBNXWRMW5XALTA.hash.dns.example."
|
| + "com",
|
| + "12");
|
| + mock_dns.ExpectAuditProofRequestAndResponse("0.12.21.tree.dns.example.com",
|
| + audit_proof.begin(),
|
| + audit_proof.begin() + 2);
|
| +
|
| + log_dns_client_ =
|
| + base::MakeUnique<LogDnsClient>(mock_dns.CreateDnsClient(), net_log_);
|
| + tree_tracker_.reset(new SingleTreeTracker(log_, log_dns_client_.get()));
|
| +
|
| + tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
|
| + EXPECT_EQ(
|
| + SingleTreeTracker::SCT_PENDING_NEWER_STH,
|
| + tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
|
| +
|
| + // Provide with a fresh STH
|
| + SignedTreeHead sth;
|
| + net::ct::GetSampleSignedTreeHead(&sth);
|
| + tree_tracker_->NewSTHObserved(sth);
|
| + base::RunLoop().RunUntilIdle();
|
| +
|
| + EXPECT_EQ(
|
| + SingleTreeTracker::SCT_INCLUDED_IN_LOG,
|
| + tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
|
| +}
|
| +
|
| +// Test that pending entries transition states correctly according to the
|
| +// STHs provided:
|
| +// * Start without an STH.
|
| +// * Add a collection of entries with mixed timestamps (i.e. SCTs not added
|
| +// in the order of their timestamps).
|
| +// * Provide an STH that covers some of the entries, test these are audited.
|
| +// * Provide another STH that covers more of the entries, test that the entries
|
| +// already audited are not audited again and that those that need to be
|
| +// audited are audited, while those that are not covered by that STH are
|
| +// not audited.
|
| +
|
| +// Test that if a request for an entry is throttled, it remains in a
|
| +// pending state.
|
| +
|
| +// Test that if one entry is throttled and the next one audited, the first,
|
| +// throttled, entry is also audited as a follow-up.
|
| +
|
| } // namespace certificate_transparency
|
|
|