| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "components/certificate_transparency/single_tree_tracker.h" |
| 6 |
| 7 #include <string> |
| 8 #include <utility> |
| 9 |
| 10 #include "base/strings/string_piece.h" |
| 11 #include "net/cert/ct_log_verifier.h" |
| 12 #include "net/cert/ct_serialization.h" |
| 13 #include "net/cert/signed_certificate_timestamp.h" |
| 14 #include "net/cert/signed_tree_head.h" |
| 15 #include "net/cert/x509_certificate.h" |
| 16 #include "net/test/ct_test_util.h" |
| 17 #include "testing/gtest/include/gtest/gtest.h" |
| 18 |
| 19 namespace certificate_transparency { |
| 20 |
| 21 namespace { |
| 22 |
| 23 bool GetOldSignedTreeHead(net::ct::SignedTreeHead* sth) { |
| 24 sth->version = net::ct::SignedTreeHead::V1; |
| 25 sth->timestamp = base::Time::UnixEpoch() + |
| 26 base::TimeDelta::FromMilliseconds(INT64_C(1348589665525)); |
| 27 sth->tree_size = 12u; |
| 28 |
| 29 const uint8_t kOldSTHRootHash[] = { |
| 30 0x18, 0x04, 0x1b, 0xd4, 0x66, 0x50, 0x83, 0x00, 0x1f, 0xba, 0x8c, |
| 31 0x54, 0x11, 0xd2, 0xd7, 0x48, 0xe8, 0xab, 0xbf, 0xdc, 0xdf, 0xd9, |
| 32 0x21, 0x8c, 0xb0, 0x2b, 0x68, 0xa7, 0x8e, 0x7d, 0x4c, 0x23}; |
| 33 memcpy(sth->sha256_root_hash, kOldSTHRootHash, net::ct::kSthRootHashLength); |
| 34 |
| 35 sth->log_id = net::ct::GetTestPublicKeyId(); |
| 36 |
| 37 const uint8_t kOldSTHSignatureData[] = { |
| 38 0x04, 0x03, 0x00, 0x47, 0x30, 0x45, 0x02, 0x20, 0x15, 0x7b, 0x23, |
| 39 0x42, 0xa2, 0x5f, 0x88, 0xc9, 0x0b, 0x30, 0xa6, 0xb4, 0x49, 0x50, |
| 40 0xb3, 0xab, 0xf5, 0x25, 0xfe, 0x27, 0xf0, 0x3f, 0x9a, 0xbf, 0xc1, |
| 41 0x16, 0x5a, 0x7a, 0xc0, 0x62, 0x2b, 0xbb, 0x02, 0x21, 0x00, 0xe6, |
| 42 0x57, 0xa3, 0xfe, 0xfc, 0x5a, 0x82, 0x9b, 0x29, 0x46, 0x15, 0x1d, |
| 43 0xbc, 0xfd, 0x9e, 0x87, 0x7f, 0xd0, 0x00, 0x5d, 0x62, 0x4f, 0x9a, |
| 44 0x1a, 0x9f, 0x20, 0x79, 0xd0, 0xc1, 0x34, 0x2e, 0x08}; |
| 45 base::StringPiece sp(reinterpret_cast<const char*>(kOldSTHSignatureData), |
| 46 sizeof(kOldSTHSignatureData)); |
| 47 return DecodeDigitallySigned(&sp, &(sth->signature)) && sp.empty(); |
| 48 } |
| 49 |
| 50 } // namespace |
| 51 |
| 52 class SingleTreeTrackerTest : public ::testing::Test { |
| 53 void SetUp() override { |
| 54 log_ = net::CTLogVerifier::Create(net::ct::GetTestPublicKey(), "testlog", |
| 55 "https://ct.example.com"); |
| 56 |
| 57 ASSERT_TRUE(log_); |
| 58 ASSERT_EQ(log_->key_id(), net::ct::GetTestPublicKeyId()); |
| 59 |
| 60 tree_tracker_.reset(new SingleTreeTracker(log_)); |
| 61 const std::string der_test_cert(net::ct::GetDerEncodedX509Cert()); |
| 62 chain_ = net::X509Certificate::CreateFromBytes(der_test_cert.data(), |
| 63 der_test_cert.length()); |
| 64 ASSERT_TRUE(chain_.get()); |
| 65 net::ct::GetX509CertSCT(&cert_sct_); |
| 66 } |
| 67 |
| 68 protected: |
| 69 scoped_refptr<const net::CTLogVerifier> log_; |
| 70 std::unique_ptr<SingleTreeTracker> tree_tracker_; |
| 71 scoped_refptr<net::X509Certificate> chain_; |
| 72 scoped_refptr<net::ct::SignedCertificateTimestamp> cert_sct_; |
| 73 }; |
| 74 |
| 75 // Test that an SCT is classified as pending for a newer STH if the |
| 76 // SingleTreeTracker has not seen any STHs so far. |
| 77 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTNoSTH) { |
| 78 // First make sure the SCT has not been observed at all. |
| 79 EXPECT_EQ( |
| 80 SingleTreeTracker::SCT_NOT_OBSERVED, |
| 81 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 82 |
| 83 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); |
| 84 |
| 85 // Since no STH was provided to the tree_tracker_ the status should be that |
| 86 // the SCT is pending a newer STH. |
| 87 EXPECT_EQ( |
| 88 SingleTreeTracker::SCT_PENDING_NEWER_STH, |
| 89 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 90 } |
| 91 |
| 92 // Test that an SCT is classified as pending an inclusion check if the |
| 93 // SingleTreeTracker has a fresh-enough STH to check inclusion against. |
| 94 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTWithRecentSTH) { |
| 95 // Provide an STH to the tree_tracker_. |
| 96 net::ct::SignedTreeHead sth; |
| 97 net::ct::GetSampleSignedTreeHead(&sth); |
| 98 tree_tracker_->NewSTHObserved(sth); |
| 99 |
| 100 // Make sure the SCT status is the same as if there's no STH for |
| 101 // this log. |
| 102 EXPECT_EQ( |
| 103 SingleTreeTracker::SCT_NOT_OBSERVED, |
| 104 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 105 |
| 106 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); |
| 107 |
| 108 // 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. |
| 110 EXPECT_EQ( |
| 111 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, |
| 112 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 113 } |
| 114 |
| 115 // 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 |
| 117 // from pending newer STH to pending inclusion check. |
| 118 TEST_F(SingleTreeTrackerTest, CorrectlyUpdatesSCTStatusOnNewSTH) { |
| 119 // Report an observed SCT and make sure it's in the pending newer STH |
| 120 // state. |
| 121 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); |
| 122 EXPECT_EQ( |
| 123 SingleTreeTracker::SCT_PENDING_NEWER_STH, |
| 124 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 125 |
| 126 // Provide with a fresh STH |
| 127 net::ct::SignedTreeHead sth; |
| 128 net::ct::GetSampleSignedTreeHead(&sth); |
| 129 tree_tracker_->NewSTHObserved(sth); |
| 130 |
| 131 // Test that its status has changed. |
| 132 EXPECT_EQ( |
| 133 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, |
| 134 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 135 } |
| 136 |
| 137 // 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 |
| 139 // inclusion against. |
| 140 TEST_F(SingleTreeTrackerTest, DoesNotUpdatesSCTStatusOnOldSTH) { |
| 141 // Notify of an SCT and make sure it's in the 'pending newer STH' state. |
| 142 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); |
| 143 EXPECT_EQ( |
| 144 SingleTreeTracker::SCT_PENDING_NEWER_STH, |
| 145 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 146 |
| 147 // Provide an old STH for the same log. |
| 148 net::ct::SignedTreeHead sth; |
| 149 GetOldSignedTreeHead(&sth); |
| 150 tree_tracker_->NewSTHObserved(sth); |
| 151 |
| 152 // Make sure the SCT's state hasn't changed. |
| 153 EXPECT_EQ( |
| 154 SingleTreeTracker::SCT_PENDING_NEWER_STH, |
| 155 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); |
| 156 } |
| 157 |
| 158 } // namespace certificate_transparency |
| OLD | NEW |