| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/certificate_transparency/single_tree_tracker.h" | 5 #include "components/certificate_transparency/single_tree_tracker.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/strings/string_piece.h" | 10 #include "base/strings/string_piece.h" |
| 11 #include "base/test/histogram_tester.h" |
| 11 #include "net/cert/ct_log_verifier.h" | 12 #include "net/cert/ct_log_verifier.h" |
| 12 #include "net/cert/ct_serialization.h" | 13 #include "net/cert/ct_serialization.h" |
| 13 #include "net/cert/signed_certificate_timestamp.h" | 14 #include "net/cert/signed_certificate_timestamp.h" |
| 14 #include "net/cert/signed_tree_head.h" | 15 #include "net/cert/signed_tree_head.h" |
| 15 #include "net/cert/x509_certificate.h" | 16 #include "net/cert/x509_certificate.h" |
| 16 #include "net/test/ct_test_util.h" | 17 #include "net/test/ct_test_util.h" |
| 17 #include "testing/gtest/include/gtest/gtest.h" | 18 #include "testing/gtest/include/gtest/gtest.h" |
| 18 | 19 |
| 19 namespace certificate_transparency { | 20 namespace certificate_transparency { |
| 20 | 21 |
| 21 namespace { | 22 namespace { |
| 22 | 23 |
| 24 const char kCanCheckForInclusionHistogramName[] = |
| 25 "Net.CertificateTransparency.CanInclusionCheckSCT"; |
| 26 |
| 23 bool GetOldSignedTreeHead(net::ct::SignedTreeHead* sth) { | 27 bool GetOldSignedTreeHead(net::ct::SignedTreeHead* sth) { |
| 24 sth->version = net::ct::SignedTreeHead::V1; | 28 sth->version = net::ct::SignedTreeHead::V1; |
| 25 sth->timestamp = base::Time::UnixEpoch() + | 29 sth->timestamp = base::Time::UnixEpoch() + |
| 26 base::TimeDelta::FromMilliseconds(INT64_C(1348589665525)); | 30 base::TimeDelta::FromMilliseconds(INT64_C(1348589665525)); |
| 27 sth->tree_size = 12u; | 31 sth->tree_size = 12u; |
| 28 | 32 |
| 29 const uint8_t kOldSTHRootHash[] = { | 33 const uint8_t kOldSTHRootHash[] = { |
| 30 0x18, 0x04, 0x1b, 0xd4, 0x66, 0x50, 0x83, 0x00, 0x1f, 0xba, 0x8c, | 34 0x18, 0x04, 0x1b, 0xd4, 0x66, 0x50, 0x83, 0x00, 0x1f, 0xba, 0x8c, |
| 31 0x54, 0x11, 0xd2, 0xd7, 0x48, 0xe8, 0xab, 0xbf, 0xdc, 0xdf, 0xd9, | 35 0x54, 0x11, 0xd2, 0xd7, 0x48, 0xe8, 0xab, 0xbf, 0xdc, 0xdf, 0xd9, |
| 32 0x21, 0x8c, 0xb0, 0x2b, 0x68, 0xa7, 0x8e, 0x7d, 0x4c, 0x23}; | 36 0x21, 0x8c, 0xb0, 0x2b, 0x68, 0xa7, 0x8e, 0x7d, 0x4c, 0x23}; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 68 protected: | 72 protected: |
| 69 scoped_refptr<const net::CTLogVerifier> log_; | 73 scoped_refptr<const net::CTLogVerifier> log_; |
| 70 std::unique_ptr<SingleTreeTracker> tree_tracker_; | 74 std::unique_ptr<SingleTreeTracker> tree_tracker_; |
| 71 scoped_refptr<net::X509Certificate> chain_; | 75 scoped_refptr<net::X509Certificate> chain_; |
| 72 scoped_refptr<net::ct::SignedCertificateTimestamp> cert_sct_; | 76 scoped_refptr<net::ct::SignedCertificateTimestamp> cert_sct_; |
| 73 }; | 77 }; |
| 74 | 78 |
| 75 // Test that an SCT is classified as pending for a newer STH if the | 79 // Test that an SCT is classified as pending for a newer STH if the |
| 76 // SingleTreeTracker has not seen any STHs so far. | 80 // SingleTreeTracker has not seen any STHs so far. |
| 77 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTNoSTH) { | 81 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTNoSTH) { |
| 82 base::HistogramTester histograms; |
| 78 // First make sure the SCT has not been observed at all. | 83 // First make sure the SCT has not been observed at all. |
| 79 EXPECT_EQ( | 84 EXPECT_EQ( |
| 80 SingleTreeTracker::SCT_NOT_OBSERVED, | 85 SingleTreeTracker::SCT_NOT_OBSERVED, |
| 81 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); | 86 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 82 | 87 |
| 83 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); | 88 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); |
| 84 | 89 |
| 85 // Since no STH was provided to the tree_tracker_ the status should be that | 90 // Since no STH was provided to the tree_tracker_ the status should be that |
| 86 // the SCT is pending a newer STH. | 91 // the SCT is pending a newer STH. |
| 87 EXPECT_EQ( | 92 EXPECT_EQ( |
| 88 SingleTreeTracker::SCT_PENDING_NEWER_STH, | 93 SingleTreeTracker::SCT_PENDING_NEWER_STH, |
| 89 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); | 94 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 95 |
| 96 // Expect logging of a value indicating a valid STH is required. |
| 97 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); |
| 98 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 0, 1); |
| 90 } | 99 } |
| 91 | 100 |
| 92 // Test that an SCT is classified as pending an inclusion check if the | 101 // Test that an SCT is classified as pending an inclusion check if the |
| 93 // SingleTreeTracker has a fresh-enough STH to check inclusion against. | 102 // SingleTreeTracker has a fresh-enough STH to check inclusion against. |
| 94 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTWithRecentSTH) { | 103 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTWithRecentSTH) { |
| 104 base::HistogramTester histograms; |
| 95 // Provide an STH to the tree_tracker_. | 105 // Provide an STH to the tree_tracker_. |
| 96 net::ct::SignedTreeHead sth; | 106 net::ct::SignedTreeHead sth; |
| 97 net::ct::GetSampleSignedTreeHead(&sth); | 107 net::ct::GetSampleSignedTreeHead(&sth); |
| 98 tree_tracker_->NewSTHObserved(sth); | 108 tree_tracker_->NewSTHObserved(sth); |
| 99 | 109 |
| 100 // Make sure the SCT status is the same as if there's no STH for | 110 // Make sure the SCT status is the same as if there's no STH for |
| 101 // this log. | 111 // this log. |
| 102 EXPECT_EQ( | 112 EXPECT_EQ( |
| 103 SingleTreeTracker::SCT_NOT_OBSERVED, | 113 SingleTreeTracker::SCT_NOT_OBSERVED, |
| 104 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); | 114 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 105 | 115 |
| 106 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); | 116 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); |
| 107 | 117 |
| 108 // The status for this SCT should be 'pending inclusion check' since the STH | 118 // The status for this SCT should be 'pending inclusion check' since the STH |
| 109 // provided at the beginning of the test is newer than the SCT. | 119 // provided at the beginning of the test is newer than the SCT. |
| 110 EXPECT_EQ( | 120 EXPECT_EQ( |
| 111 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, | 121 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, |
| 112 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); | 122 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 123 |
| 124 // Exactly one value should be logged, indicating the SCT can be checked for |
| 125 // inclusion, as |tree_tracker_| did have a valid STH when it was notified |
| 126 // of a new SCT. |
| 127 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); |
| 128 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 2, 1); |
| 113 } | 129 } |
| 114 | 130 |
| 115 // Test that the SingleTreeTracker correctly queues verified SCTs for inclusion | 131 // Test that the SingleTreeTracker correctly queues verified SCTs for inclusion |
| 116 // checking such that, upon receiving a fresh STH, it changes the SCT's status | 132 // checking such that, upon receiving a fresh STH, it changes the SCT's status |
| 117 // from pending newer STH to pending inclusion check. | 133 // from pending newer STH to pending inclusion check. |
| 118 TEST_F(SingleTreeTrackerTest, CorrectlyUpdatesSCTStatusOnNewSTH) { | 134 TEST_F(SingleTreeTrackerTest, CorrectlyUpdatesSCTStatusOnNewSTH) { |
| 135 base::HistogramTester histograms; |
| 119 // Report an observed SCT and make sure it's in the pending newer STH | 136 // Report an observed SCT and make sure it's in the pending newer STH |
| 120 // state. | 137 // state. |
| 121 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); | 138 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); |
| 122 EXPECT_EQ( | 139 EXPECT_EQ( |
| 123 SingleTreeTracker::SCT_PENDING_NEWER_STH, | 140 SingleTreeTracker::SCT_PENDING_NEWER_STH, |
| 124 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); | 141 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 142 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); |
| 125 | 143 |
| 126 // Provide with a fresh STH | 144 // Provide with a fresh STH |
| 127 net::ct::SignedTreeHead sth; | 145 net::ct::SignedTreeHead sth; |
| 128 net::ct::GetSampleSignedTreeHead(&sth); | 146 net::ct::GetSampleSignedTreeHead(&sth); |
| 129 tree_tracker_->NewSTHObserved(sth); | 147 tree_tracker_->NewSTHObserved(sth); |
| 130 | 148 |
| 131 // Test that its status has changed. | 149 // Test that its status has changed. |
| 132 EXPECT_EQ( | 150 EXPECT_EQ( |
| 133 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, | 151 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, |
| 134 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); | 152 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 153 // Check that no additional UMA was logged for this case as the histogram is |
| 154 // only supposed to measure the state of newly-observed SCTs, not pending |
| 155 // ones. |
| 156 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); |
| 135 } | 157 } |
| 136 | 158 |
| 137 // Test that the SingleTreeTracker does not change an SCT's status if an STH | 159 // Test that the SingleTreeTracker does not change an SCT's status if an STH |
| 138 // from the log it was issued by is observed, but that STH is too old to check | 160 // from the log it was issued by is observed, but that STH is too old to check |
| 139 // inclusion against. | 161 // inclusion against. |
| 140 TEST_F(SingleTreeTrackerTest, DoesNotUpdatesSCTStatusOnOldSTH) { | 162 TEST_F(SingleTreeTrackerTest, DoesNotUpdatesSCTStatusOnOldSTH) { |
| 141 // Notify of an SCT and make sure it's in the 'pending newer STH' state. | 163 // Notify of an SCT and make sure it's in the 'pending newer STH' state. |
| 142 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); | 164 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); |
| 143 EXPECT_EQ( | 165 EXPECT_EQ( |
| 144 SingleTreeTracker::SCT_PENDING_NEWER_STH, | 166 SingleTreeTracker::SCT_PENDING_NEWER_STH, |
| 145 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); | 167 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 146 | 168 |
| 147 // Provide an old STH for the same log. | 169 // Provide an old STH for the same log. |
| 148 net::ct::SignedTreeHead sth; | 170 net::ct::SignedTreeHead sth; |
| 149 GetOldSignedTreeHead(&sth); | 171 GetOldSignedTreeHead(&sth); |
| 150 tree_tracker_->NewSTHObserved(sth); | 172 tree_tracker_->NewSTHObserved(sth); |
| 151 | 173 |
| 152 // Make sure the SCT's state hasn't changed. | 174 // Make sure the SCT's state hasn't changed. |
| 153 EXPECT_EQ( | 175 EXPECT_EQ( |
| 154 SingleTreeTracker::SCT_PENDING_NEWER_STH, | 176 SingleTreeTracker::SCT_PENDING_NEWER_STH, |
| 155 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); | 177 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 156 } | 178 } |
| 157 | 179 |
| 180 // Test that the SingleTreeTracker correctly logs that an SCT is pending a new |
| 181 // STH, when it has a valid STH, but the observed SCT is not covered by the |
| 182 // STH. |
| 183 TEST_F(SingleTreeTrackerTest, LogsUMAForNewSCTAndOldSTH) { |
| 184 base::HistogramTester histograms; |
| 185 // Provide an old STH for the same log. |
| 186 net::ct::SignedTreeHead sth; |
| 187 GetOldSignedTreeHead(&sth); |
| 188 tree_tracker_->NewSTHObserved(sth); |
| 189 |
| 190 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 0); |
| 191 |
| 192 // Notify of an SCT and make sure it's in the 'pending newer STH' state. |
| 193 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); |
| 194 |
| 195 // Exactly one value should be logged, indicating the SCT cannot be checked |
| 196 // for inclusion as the STH is too old. |
| 197 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); |
| 198 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 1, 1); |
| 199 } |
| 200 |
| 158 } // namespace certificate_transparency | 201 } // namespace certificate_transparency |
| OLD | NEW |