OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 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 "chrome/browser/transport_security_persister.h" |
| 6 |
| 7 #include <string> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/file_path.h" |
| 11 #include "base/file_util.h" |
| 12 #include "base/message_loop.h" |
| 13 #include "base/scoped_temp_dir.h" |
| 14 #include "content/test/test_browser_thread.h" |
| 15 #include "content/public/browser/browser_thread.h" |
| 16 #include "net/base/transport_security_state.h" |
| 17 #include "net/base/x509_cert_types.h" |
| 18 #include "testing/gtest/include/gtest/gtest.h" |
| 19 |
| 20 using net::TransportSecurityState; |
| 21 using content::BrowserThread; |
| 22 |
| 23 class TransportSecurityPersisterTest : public testing::Test { |
| 24 public: |
| 25 TransportSecurityPersisterTest() |
| 26 : message_loop(MessageLoop::TYPE_IO), |
| 27 test_io_thread(BrowserThread::IO, &message_loop), |
| 28 persister(&state, temp_dir.path(), false) { |
| 29 } |
| 30 |
| 31 virtual void SetUp() { } |
| 32 |
| 33 MessageLoop message_loop; |
| 34 content::TestBrowserThread test_io_thread; |
| 35 ScopedTempDir temp_dir; |
| 36 TransportSecurityPersister persister; |
| 37 TransportSecurityState state; |
| 38 }; |
| 39 |
| 40 TEST_F(TransportSecurityPersisterTest, SerializeData1) { |
| 41 std::string output; |
| 42 bool dirty; |
| 43 |
| 44 EXPECT_TRUE(persister.SerializeData(&output)); |
| 45 EXPECT_TRUE(persister.LoadEntries(output, &dirty)); |
| 46 EXPECT_FALSE(dirty); |
| 47 } |
| 48 |
| 49 TEST_F(TransportSecurityPersisterTest, SerializeData2) { |
| 50 TransportSecurityState::DomainState domain_state; |
| 51 const base::Time current_time(base::Time::Now()); |
| 52 const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000); |
| 53 |
| 54 EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state)); |
| 55 domain_state.upgrade_mode = |
| 56 TransportSecurityState::DomainState::MODE_FORCE_HTTPS; |
| 57 domain_state.upgrade_expiry = expiry; |
| 58 domain_state.include_subdomains = true; |
| 59 state.EnableHost("yahoo.com", domain_state); |
| 60 |
| 61 std::string output; |
| 62 bool dirty; |
| 63 EXPECT_TRUE(persister.SerializeData(&output)); |
| 64 EXPECT_TRUE(persister.LoadEntries(output, &dirty)); |
| 65 |
| 66 EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state)); |
| 67 EXPECT_EQ(domain_state.upgrade_mode, |
| 68 TransportSecurityState::DomainState::MODE_FORCE_HTTPS); |
| 69 EXPECT_TRUE(state.GetDomainState("foo.yahoo.com", true, &domain_state)); |
| 70 EXPECT_EQ(domain_state.upgrade_mode, |
| 71 TransportSecurityState::DomainState::MODE_FORCE_HTTPS); |
| 72 EXPECT_TRUE(state.GetDomainState("foo.bar.yahoo.com", true, &domain_state)); |
| 73 EXPECT_EQ(domain_state.upgrade_mode, |
| 74 TransportSecurityState::DomainState::MODE_FORCE_HTTPS); |
| 75 EXPECT_TRUE(state.GetDomainState("foo.bar.baz.yahoo.com", true, |
| 76 &domain_state)); |
| 77 EXPECT_EQ(domain_state.upgrade_mode, |
| 78 TransportSecurityState::DomainState::MODE_FORCE_HTTPS); |
| 79 EXPECT_FALSE(state.GetDomainState("com", true, &domain_state)); |
| 80 } |
| 81 |
| 82 TEST_F(TransportSecurityPersisterTest, SerializeData3) { |
| 83 using std::string; |
| 84 using net::SHA1Fingerprint; |
| 85 |
| 86 // Add an entry. |
| 87 SHA1Fingerprint fp1; |
| 88 memset(fp1.data, 0, sizeof(fp1.data)); |
| 89 SHA1Fingerprint fp2; |
| 90 memset(fp2.data, 1, sizeof(fp2.data)); |
| 91 TransportSecurityState::DomainState example_state; |
| 92 example_state.upgrade_expiry = |
| 93 base::Time::Now() + base::TimeDelta::FromSeconds(1000); |
| 94 example_state.upgrade_mode = |
| 95 TransportSecurityState::DomainState::MODE_FORCE_HTTPS; |
| 96 example_state.dynamic_spki_hashes_expiry = example_state.upgrade_expiry; |
| 97 example_state.dynamic_spki_hashes.push_back(fp1); |
| 98 example_state.dynamic_spki_hashes.push_back(fp2); |
| 99 state.EnableHost("www.example.com", example_state); |
| 100 |
| 101 // Add another entry. |
| 102 memset(fp1.data, 2, sizeof(fp1.data)); |
| 103 memset(fp2.data, 3, sizeof(fp2.data)); |
| 104 example_state.upgrade_expiry = |
| 105 base::Time::Now() + base::TimeDelta::FromSeconds(3000); |
| 106 example_state.upgrade_mode = |
| 107 TransportSecurityState::DomainState::MODE_DEFAULT; |
| 108 example_state.dynamic_spki_hashes_expiry = example_state.upgrade_expiry; |
| 109 example_state.dynamic_spki_hashes.push_back(fp1); |
| 110 example_state.dynamic_spki_hashes.push_back(fp2); |
| 111 state.EnableHost("www.example.net", example_state); |
| 112 |
| 113 // Save a copy of everything. |
| 114 std::map<string, TransportSecurityState::DomainState> saved; |
| 115 TransportSecurityState::Iterator i(state); |
| 116 while (i.HasNext()) { |
| 117 saved[i.hostname()] = i.domain_state(); |
| 118 i.Advance(); |
| 119 } |
| 120 |
| 121 std::string serialized; |
| 122 EXPECT_TRUE(persister.SerializeData(&serialized)); |
| 123 |
| 124 // Persist the data to the file. For the test to be fast and not flaky, we |
| 125 // just do it directly rather than call persister.StateIsDirty. (That uses |
| 126 // ImportantFileWriter, which has an asynchronous commit interval rather |
| 127 // than block.) Use a different basename just for cleanliness. |
| 128 FilePath path = temp_dir.path().AppendASCII("TransportSecurityPersisterTest"); |
| 129 EXPECT_TRUE(file_util::WriteFile(path, serialized.c_str(), |
| 130 serialized.size())); |
| 131 |
| 132 // Read the data back. |
| 133 std::string persisted; |
| 134 EXPECT_TRUE(file_util::ReadFileToString(path, &persisted)); |
| 135 EXPECT_EQ(persisted, serialized); |
| 136 bool dirty; |
| 137 EXPECT_TRUE(persister.LoadEntries(persisted, &dirty)); |
| 138 EXPECT_FALSE(dirty); |
| 139 |
| 140 // Check that states are the same as saved. |
| 141 size_t count = 0; |
| 142 TransportSecurityState::Iterator j(state); |
| 143 while (j.HasNext()) { |
| 144 EXPECT_TRUE(saved[j.hostname()].Equals(j.domain_state())); |
| 145 count++; |
| 146 j.Advance(); |
| 147 } |
| 148 EXPECT_EQ(count, saved.size()); |
| 149 } |
| 150 |
| 151 TEST_F(TransportSecurityPersisterTest, SerializeDataOld) { |
| 152 // This is an old-style piece of transport state JSON, which has no creation |
| 153 // date. |
| 154 std::string output = |
| 155 "{ " |
| 156 "\"NiyD+3J1r6z1wjl2n1ALBu94Zj9OsEAMo0kCN8js0Uk=\": {" |
| 157 "\"expiry\": 1266815027.983453, " |
| 158 "\"include_subdomains\": false, " |
| 159 "\"mode\": \"strict\" " |
| 160 "}" |
| 161 "}"; |
| 162 bool dirty; |
| 163 EXPECT_TRUE(persister.LoadEntries(output, &dirty)); |
| 164 EXPECT_TRUE(dirty); |
| 165 } |
| 166 |
| 167 TEST_F(TransportSecurityPersisterTest, PublicKeyHashes) { |
| 168 TransportSecurityState::DomainState domain_state; |
| 169 EXPECT_FALSE(state.GetDomainState("example.com", false, &domain_state)); |
| 170 net::FingerprintVector hashes; |
| 171 EXPECT_TRUE(domain_state.IsChainOfPublicKeysPermitted(hashes)); |
| 172 |
| 173 net::SHA1Fingerprint hash; |
| 174 memset(hash.data, '1', sizeof(hash.data)); |
| 175 domain_state.static_spki_hashes.push_back(hash); |
| 176 |
| 177 EXPECT_FALSE(domain_state.IsChainOfPublicKeysPermitted(hashes)); |
| 178 hashes.push_back(hash); |
| 179 EXPECT_TRUE(domain_state.IsChainOfPublicKeysPermitted(hashes)); |
| 180 hashes[0].data[0] = '2'; |
| 181 EXPECT_FALSE(domain_state.IsChainOfPublicKeysPermitted(hashes)); |
| 182 |
| 183 const base::Time current_time(base::Time::Now()); |
| 184 const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000); |
| 185 domain_state.upgrade_expiry = expiry; |
| 186 state.EnableHost("example.com", domain_state); |
| 187 std::string ser; |
| 188 EXPECT_TRUE(persister.SerializeData(&ser)); |
| 189 bool dirty; |
| 190 EXPECT_TRUE(persister.LoadEntries(ser, &dirty)); |
| 191 EXPECT_TRUE(state.GetDomainState("example.com", false, &domain_state)); |
| 192 EXPECT_EQ(1u, domain_state.static_spki_hashes.size()); |
| 193 EXPECT_EQ(0, memcmp(domain_state.static_spki_hashes[0].data, hash.data, |
| 194 sizeof(hash.data))); |
| 195 } |
| 196 |
| 197 TEST_F(TransportSecurityPersisterTest, ForcePreloads) { |
| 198 // The static state for docs.google.com, defined in |
| 199 // net/base/transport_security_state_static.h, has pins and mode strict. |
| 200 // This new policy overrides that with no pins and a weaker mode. We apply |
| 201 // this new policy with |DeserializeFromCommandLine| and expect that the |
| 202 // new policy is in effect, overriding the static policy. |
| 203 std::string preload("{" |
| 204 "\"4AGT3lHihuMSd5rUj7B4u6At0jlSH3HFePovjPR+oLE=\": {" |
| 205 "\"created\": 0.0," |
| 206 "\"expiry\": 2000000000.0," |
| 207 "\"include_subdomains\": false," |
| 208 "\"mode\": \"pinning-only\"" |
| 209 "}}"); |
| 210 |
| 211 EXPECT_TRUE(persister.DeserializeFromCommandLine(preload)); |
| 212 |
| 213 TransportSecurityState::DomainState domain_state; |
| 214 EXPECT_TRUE(state.GetDomainState("docs.google.com", true, &domain_state)); |
| 215 EXPECT_FALSE(domain_state.HasPins()); |
| 216 EXPECT_FALSE(domain_state.ShouldRedirectHTTPToHTTPS()); |
| 217 } |
| 218 |
OLD | NEW |