1 // Copyright 2014 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 "base/bind.h" 6 #include "base/files/file_util.h" 7 #include "base/files/scoped_temp_dir.h" 8 #include "base/memory/ref_counted.h" 9 #include "base/memory/scoped_vector.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/run_loop.h" 12 #include "base/stl_util.h" 13 #include "net/base/test_data_directory.h" 14 #include "net/extras/sqlite/sqlite_channel_id_store.h" 15 #include "net/ssl/ssl_client_cert_type.h" 16 #include "net/test/cert_test_util.h" 17 #include "sql/statement.h" 18 #include "testing/gtest/include/gtest/gtest.h" 19 20 namespace net { 21 22 const base::FilePath::CharType kTestChannelIDFilename[] = 23 FILE_PATH_LITERAL("ChannelID"); 24 25 class SQLiteChannelIDStoreTest : public testing::Test { 26 public: 27 void Load(ScopedVector<DefaultChannelIDStore::ChannelID>* channel_ids) { 28 base::RunLoop run_loop; 29 store_->Load(base::Bind(&SQLiteChannelIDStoreTest::OnLoaded, 30 base::Unretained(this), 31 &run_loop)); 32 run_loop.Run(); 33 channel_ids->swap(channel_ids_); 34 channel_ids_.clear(); 35 } 36 37 void OnLoaded( 38 base::RunLoop* run_loop, 39 scoped_ptr<ScopedVector<DefaultChannelIDStore::ChannelID> > channel_ids) { 40 channel_ids_.swap(*channel_ids); 41 run_loop->Quit(); 42 } 43 44 protected: 45 static void ReadTestKeyAndCert(std::string* key, std::string* cert) { 46 base::FilePath key_path = 47 GetTestCertsDirectory().AppendASCII("unittest.originbound.key.der"); 48 base::FilePath cert_path = 49 GetTestCertsDirectory().AppendASCII("unittest.originbound.der"); 50 ASSERT_TRUE(base::ReadFileToString(key_path, key)); 51 ASSERT_TRUE(base::ReadFileToString(cert_path, cert)); 52 } 53 54 static base::Time GetTestCertExpirationTime() { 55 // Cert expiration time from 'dumpasn1 unittest.originbound.der': 56 // GeneralizedTime 19/11/2111 02:23:45 GMT 57 // base::Time::FromUTCExploded can't generate values past 2038 on 32-bit 58 // linux, so we use the raw value here. 59 return base::Time::FromInternalValue(GG_INT64_C(16121816625000000)); 60 } 61 62 static base::Time GetTestCertCreationTime() { 63 // UTCTime 13/12/2011 02:23:45 GMT 64 base::Time::Exploded exploded_time; 65 exploded_time.year = 2011; 66 exploded_time.month = 12; 67 exploded_time.day_of_week = 0; // Unused. 68 exploded_time.day_of_month = 13; 69 exploded_time.hour = 2; 70 exploded_time.minute = 23; 71 exploded_time.second = 45; 72 exploded_time.millisecond = 0; 73 return base::Time::FromUTCExploded(exploded_time); 74 } 75 76 virtual void SetUp() { 77 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 78 store_ = new SQLiteChannelIDStore( 79 temp_dir_.path().Append(kTestChannelIDFilename), 80 base::MessageLoopProxy::current()); 81 ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; 82 Load(&channel_ids); 83 ASSERT_EQ(0u, channel_ids.size()); 84 // Make sure the store gets written at least once. 85 store_->AddChannelID( 86 DefaultChannelIDStore::ChannelID("google.com", 87 base::Time::FromInternalValue(1), 88 base::Time::FromInternalValue(2), 89 "a", 90 "b")); 91 } 92 93 base::ScopedTempDir temp_dir_; 94 scoped_refptr<SQLiteChannelIDStore> store_; 95 ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids_; 96 }; 97 98 // Test if data is stored as expected in the SQLite database. 99 TEST_F(SQLiteChannelIDStoreTest, TestPersistence) { 100 store_->AddChannelID( 101 DefaultChannelIDStore::ChannelID("foo.com", 102 base::Time::FromInternalValue(3), 103 base::Time::FromInternalValue(4), 104 "c", 105 "d")); 106 107 ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; 108 // Replace the store effectively destroying the current one and forcing it 109 // to write its data to disk. Then we can see if after loading it again it 110 // is still there. 111 store_ = NULL; 112 // Make sure we wait until the destructor has run. 113 base::RunLoop().RunUntilIdle(); 114 store_ = 115 new SQLiteChannelIDStore(temp_dir_.path().Append(kTestChannelIDFilename), 116 base::MessageLoopProxy::current()); 117 118 // Reload and test for persistence 119 Load(&channel_ids); 120 ASSERT_EQ(2U, channel_ids.size()); 121 DefaultChannelIDStore::ChannelID* goog_channel_id; 122 DefaultChannelIDStore::ChannelID* foo_channel_id; 123 if (channel_ids[0]->server_identifier() == "google.com") { 124 goog_channel_id = channel_ids[0]; 125 foo_channel_id = channel_ids[1]; 126 } else { 127 goog_channel_id = channel_ids[1]; 128 foo_channel_id = channel_ids[0]; 129 } 130 ASSERT_EQ("google.com", goog_channel_id->server_identifier()); 131 ASSERT_STREQ("a", goog_channel_id->private_key().c_str()); 132 ASSERT_STREQ("b", goog_channel_id->cert().c_str()); 133 ASSERT_EQ(1, goog_channel_id->creation_time().ToInternalValue()); 134 ASSERT_EQ(2, goog_channel_id->expiration_time().ToInternalValue()); 135 ASSERT_EQ("foo.com", foo_channel_id->server_identifier()); 136 ASSERT_STREQ("c", foo_channel_id->private_key().c_str()); 137 ASSERT_STREQ("d", foo_channel_id->cert().c_str()); 138 ASSERT_EQ(3, foo_channel_id->creation_time().ToInternalValue()); 139 ASSERT_EQ(4, foo_channel_id->expiration_time().ToInternalValue()); 140 141 // Now delete the cert and check persistence again. 142 store_->DeleteChannelID(*channel_ids[0]); 143 store_->DeleteChannelID(*channel_ids[1]); 144 store_ = NULL; 145 // Make sure we wait until the destructor has run. 146 base::RunLoop().RunUntilIdle(); 147 channel_ids.clear(); 148 store_ = 149 new SQLiteChannelIDStore(temp_dir_.path().Append(kTestChannelIDFilename), 150 base::MessageLoopProxy::current()); 151 152 // Reload and check if the cert has been removed. 153 Load(&channel_ids); 154 ASSERT_EQ(0U, channel_ids.size()); 155 // Close the store. 156 store_ = NULL; 157 // Make sure we wait until the destructor has run. 158 base::RunLoop().RunUntilIdle(); 159 } 160 161 // Test if data is stored as expected in the SQLite database. 162 TEST_F(SQLiteChannelIDStoreTest, TestDeleteAll) { 163 store_->AddChannelID( 164 DefaultChannelIDStore::ChannelID("foo.com", 165 base::Time::FromInternalValue(3), 166 base::Time::FromInternalValue(4), 167 "c", 168 "d")); 169 170 ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; 171 // Replace the store effectively destroying the current one and forcing it 172 // to write its data to disk. Then we can see if after loading it again it 173 // is still there. 174 store_ = NULL; 175 // Make sure we wait until the destructor has run. 176 base::RunLoop().RunUntilIdle(); 177 store_ = 178 new SQLiteChannelIDStore(temp_dir_.path().Append(kTestChannelIDFilename), 179 base::MessageLoopProxy::current()); 180 181 // Reload and test for persistence 182 Load(&channel_ids); 183 ASSERT_EQ(2U, channel_ids.size()); 184 // DeleteAll except foo.com (shouldn't fail if one is missing either). 185 std::list<std::string> delete_server_identifiers; 186 delete_server_identifiers.push_back("google.com"); 187 delete_server_identifiers.push_back("missing.com"); 188 store_->DeleteAllInList(delete_server_identifiers); 189 190 // Now check persistence again. 191 store_ = NULL; 192 // Make sure we wait until the destructor has run. 193 base::RunLoop().RunUntilIdle(); 194 channel_ids.clear(); 195 store_ = 196 new SQLiteChannelIDStore(temp_dir_.path().Append(kTestChannelIDFilename), 197 base::MessageLoopProxy::current()); 198 199 // Reload and check that only foo.com persisted in store. 200 Load(&channel_ids); 201 ASSERT_EQ(1U, channel_ids.size()); 202 ASSERT_EQ("foo.com", channel_ids[0]->server_identifier()); 203 // Close the store. 204 store_ = NULL; 205 // Make sure we wait until the destructor has run. 206 base::RunLoop().RunUntilIdle(); 207 } 208 209 TEST_F(SQLiteChannelIDStoreTest, TestUpgradeV1) { 210 // Reset the store. We'll be using a different database for this test. 211 store_ = NULL; 212 213 base::FilePath v1_db_path(temp_dir_.path().AppendASCII("v1db")); 214 215 std::string key_data; 216 std::string cert_data; 217 ReadTestKeyAndCert(&key_data, &cert_data); 218 219 // Create a version 1 database. 220 { 221 sql::Connection db; 222 ASSERT_TRUE(db.Open(v1_db_path)); 223 ASSERT_TRUE(db.Execute( 224 "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY," 225 "value LONGVARCHAR);" 226 "INSERT INTO \"meta\" VALUES('version','1');" 227 "INSERT INTO \"meta\" VALUES('last_compatible_version','1');" 228 "CREATE TABLE origin_bound_certs (" 229 "origin TEXT NOT NULL UNIQUE PRIMARY KEY," 230 "private_key BLOB NOT NULL,cert BLOB NOT NULL);")); 231 232 sql::Statement add_smt(db.GetUniqueStatement( 233 "INSERT INTO origin_bound_certs (origin, private_key, cert) " 234 "VALUES (?,?,?)")); 235 add_smt.BindString(0, "google.com"); 236 add_smt.BindBlob(1, key_data.data(), key_data.size()); 237 add_smt.BindBlob(2, cert_data.data(), cert_data.size()); 238 ASSERT_TRUE(add_smt.Run()); 239 240 ASSERT_TRUE(db.Execute( 241 "INSERT INTO \"origin_bound_certs\" VALUES(" 242 "'foo.com',X'AA',X'BB');")); 243 } 244 245 // Load and test the DB contents twice. First time ensures that we can use 246 // the updated values immediately. Second time ensures that the updated 247 // values are stored and read correctly on next load. 248 for (int i = 0; i < 2; ++i) { 249 SCOPED_TRACE(i); 250 251 ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; 252 store_ = 253 new SQLiteChannelIDStore(v1_db_path, base::MessageLoopProxy::current()); 254 255 // Load the database. Because the existing v1 certs are implicitly of type 256 // RSA, which is unsupported, they're discarded. 257 Load(&channel_ids); 258 ASSERT_EQ(0U, channel_ids.size()); 259 260 store_ = NULL; 261 base::RunLoop().RunUntilIdle(); 262 263 // Verify the database version is updated. 264 { 265 sql::Connection db; 266 ASSERT_TRUE(db.Open(v1_db_path)); 267 sql::Statement smt(db.GetUniqueStatement( 268 "SELECT value FROM meta WHERE key = \"version\"")); 269 ASSERT_TRUE(smt.Step()); 270 EXPECT_EQ(4, smt.ColumnInt(0)); 271 EXPECT_FALSE(smt.Step()); 272 } 273 } 274 } 275 276 TEST_F(SQLiteChannelIDStoreTest, TestUpgradeV2) { 277 // Reset the store. We'll be using a different database for this test. 278 store_ = NULL; 279 280 base::FilePath v2_db_path(temp_dir_.path().AppendASCII("v2db")); 281 282 std::string key_data; 283 std::string cert_data; 284 ReadTestKeyAndCert(&key_data, &cert_data); 285 286 // Create a version 2 database. 287 { 288 sql::Connection db; 289 ASSERT_TRUE(db.Open(v2_db_path)); 290 ASSERT_TRUE(db.Execute( 291 "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY," 292 "value LONGVARCHAR);" 293 "INSERT INTO \"meta\" VALUES('version','2');" 294 "INSERT INTO \"meta\" VALUES('last_compatible_version','1');" 295 "CREATE TABLE origin_bound_certs (" 296 "origin TEXT NOT NULL UNIQUE PRIMARY KEY," 297 "private_key BLOB NOT NULL," 298 "cert BLOB NOT NULL," 299 "cert_type INTEGER);")); 300 301 sql::Statement add_smt(db.GetUniqueStatement( 302 "INSERT INTO origin_bound_certs (origin, private_key, cert, cert_type) " 303 "VALUES (?,?,?,?)")); 304 add_smt.BindString(0, "google.com"); 305 add_smt.BindBlob(1, key_data.data(), key_data.size()); 306 add_smt.BindBlob(2, cert_data.data(), cert_data.size()); 307 add_smt.BindInt64(3, 64); 308 ASSERT_TRUE(add_smt.Run()); 309 310 ASSERT_TRUE(db.Execute( 311 "INSERT INTO \"origin_bound_certs\" VALUES(" 312 "'foo.com',X'AA',X'BB',64);")); 313 } 314 315 // Load and test the DB contents twice. First time ensures that we can use 316 // the updated values immediately. Second time ensures that the updated 317 // values are saved and read correctly on next load. 318 for (int i = 0; i < 2; ++i) { 319 SCOPED_TRACE(i); 320 321 ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; 322 store_ = 323 new SQLiteChannelIDStore(v2_db_path, base::MessageLoopProxy::current()); 324 325 // Load the database and ensure the certs can be read. 326 Load(&channel_ids); 327 ASSERT_EQ(2U, channel_ids.size()); 328 329 ASSERT_EQ("google.com", channel_ids[0]->server_identifier()); 330 ASSERT_EQ(GetTestCertExpirationTime(), channel_ids[0]->expiration_time()); 331 ASSERT_EQ(key_data, channel_ids[0]->private_key()); 332 ASSERT_EQ(cert_data, channel_ids[0]->cert()); 333 334 ASSERT_EQ("foo.com", channel_ids[1]->server_identifier()); 335 // Undecodable cert, expiration time will be uninitialized. 336 ASSERT_EQ(base::Time(), channel_ids[1]->expiration_time()); 337 ASSERT_STREQ("\xaa", channel_ids[1]->private_key().c_str()); 338 ASSERT_STREQ("\xbb", channel_ids[1]->cert().c_str()); 339 340 store_ = NULL; 341 // Make sure we wait until the destructor has run. 342 base::RunLoop().RunUntilIdle(); 343 344 // Verify the database version is updated. 345 { 346 sql::Connection db; 347 ASSERT_TRUE(db.Open(v2_db_path)); 348 sql::Statement smt(db.GetUniqueStatement( 349 "SELECT value FROM meta WHERE key = \"version\"")); 350 ASSERT_TRUE(smt.Step()); 351 EXPECT_EQ(4, smt.ColumnInt(0)); 352 EXPECT_FALSE(smt.Step()); 353 } 354 } 355 } 356 357 TEST_F(SQLiteChannelIDStoreTest, TestUpgradeV3) { 358 // Reset the store. We'll be using a different database for this test. 359 store_ = NULL; 360 361 base::FilePath v3_db_path(temp_dir_.path().AppendASCII("v3db")); 362 363 std::string key_data; 364 std::string cert_data; 365 ReadTestKeyAndCert(&key_data, &cert_data); 366 367 // Create a version 3 database. 368 { 369 sql::Connection db; 370 ASSERT_TRUE(db.Open(v3_db_path)); 371 ASSERT_TRUE(db.Execute( 372 "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY," 373 "value LONGVARCHAR);" 374 "INSERT INTO \"meta\" VALUES('version','3');" 375 "INSERT INTO \"meta\" VALUES('last_compatible_version','1');" 376 "CREATE TABLE origin_bound_certs (" 377 "origin TEXT NOT NULL UNIQUE PRIMARY KEY," 378 "private_key BLOB NOT NULL," 379 "cert BLOB NOT NULL," 380 "cert_type INTEGER," 381 "expiration_time INTEGER);")); 382 383 sql::Statement add_smt(db.GetUniqueStatement( 384 "INSERT INTO origin_bound_certs (origin, private_key, cert, cert_type, " 385 "expiration_time) VALUES (?,?,?,?,?)")); 386 add_smt.BindString(0, "google.com"); 387 add_smt.BindBlob(1, key_data.data(), key_data.size()); 388 add_smt.BindBlob(2, cert_data.data(), cert_data.size()); 389 add_smt.BindInt64(3, 64); 390 add_smt.BindInt64(4, 1000); 391 ASSERT_TRUE(add_smt.Run()); 392 393 ASSERT_TRUE(db.Execute( 394 "INSERT INTO \"origin_bound_certs\" VALUES(" 395 "'foo.com',X'AA',X'BB',64,2000);")); 396 } 397 398 // Load and test the DB contents twice. First time ensures that we can use 399 // the updated values immediately. Second time ensures that the updated 400 // values are saved and read correctly on next load. 401 for (int i = 0; i < 2; ++i) { 402 SCOPED_TRACE(i); 403 404 ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; 405 store_ = 406 new SQLiteChannelIDStore(v3_db_path, base::MessageLoopProxy::current()); 407 408 // Load the database and ensure the certs can be read. 409 Load(&channel_ids); 410 ASSERT_EQ(2U, channel_ids.size()); 411 412 ASSERT_EQ("google.com", channel_ids[0]->server_identifier()); 413 ASSERT_EQ(1000, channel_ids[0]->expiration_time().ToInternalValue()); 414 ASSERT_EQ(GetTestCertCreationTime(), channel_ids[0]->creation_time()); 415 ASSERT_EQ(key_data, channel_ids[0]->private_key()); 416 ASSERT_EQ(cert_data, channel_ids[0]->cert()); 417 418 ASSERT_EQ("foo.com", channel_ids[1]->server_identifier()); 419 ASSERT_EQ(2000, channel_ids[1]->expiration_time().ToInternalValue()); 420 // Undecodable cert, creation time will be uninitialized. 421 ASSERT_EQ(base::Time(), channel_ids[1]->creation_time()); 422 ASSERT_STREQ("\xaa", channel_ids[1]->private_key().c_str()); 423 ASSERT_STREQ("\xbb", channel_ids[1]->cert().c_str()); 424 425 store_ = NULL; 426 // Make sure we wait until the destructor has run. 427 base::RunLoop().RunUntilIdle(); 428 429 // Verify the database version is updated. 430 { 431 sql::Connection db; 432 ASSERT_TRUE(db.Open(v3_db_path)); 433 sql::Statement smt(db.GetUniqueStatement( 434 "SELECT value FROM meta WHERE key = \"version\"")); 435 ASSERT_TRUE(smt.Step()); 436 EXPECT_EQ(4, smt.ColumnInt(0)); 437 EXPECT_FALSE(smt.Step()); 438 } 439 } 440 } 441 442 TEST_F(SQLiteChannelIDStoreTest, TestRSADiscarded) { 443 // Reset the store. We'll be using a different database for this test. 444 store_ = NULL; 445 446 base::FilePath v4_db_path(temp_dir_.path().AppendASCII("v4dbrsa")); 447 448 std::string key_data; 449 std::string cert_data; 450 ReadTestKeyAndCert(&key_data, &cert_data); 451 452 // Create a version 4 database with a mix of RSA and ECDSA certs. 453 { 454 sql::Connection db; 455 ASSERT_TRUE(db.Open(v4_db_path)); 456 ASSERT_TRUE(db.Execute( 457 "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY," 458 "value LONGVARCHAR);" 459 "INSERT INTO \"meta\" VALUES('version','4');" 460 "INSERT INTO \"meta\" VALUES('last_compatible_version','1');" 461 "CREATE TABLE origin_bound_certs (" 462 "origin TEXT NOT NULL UNIQUE PRIMARY KEY," 463 "private_key BLOB NOT NULL," 464 "cert BLOB NOT NULL," 465 "cert_type INTEGER," 466 "expiration_time INTEGER," 467 "creation_time INTEGER);")); 468 469 sql::Statement add_smt(db.GetUniqueStatement( 470 "INSERT INTO origin_bound_certs " 471 "(origin, private_key, cert, cert_type, expiration_time, creation_time)" 472 " VALUES (?,?,?,?,?,?)")); 473 add_smt.BindString(0, "google.com"); 474 add_smt.BindBlob(1, key_data.data(), key_data.size()); 475 add_smt.BindBlob(2, cert_data.data(), cert_data.size()); 476 add_smt.BindInt64(3, 64); 477 add_smt.BindInt64(4, GetTestCertExpirationTime().ToInternalValue()); 478 add_smt.BindInt64(5, base::Time::Now().ToInternalValue()); 479 ASSERT_TRUE(add_smt.Run()); 480 481 add_smt.Clear(); 482 add_smt.Assign(db.GetUniqueStatement( 483 "INSERT INTO origin_bound_certs " 484 "(origin, private_key, cert, cert_type, expiration_time, creation_time)" 485 " VALUES (?,?,?,?,?,?)")); 486 add_smt.BindString(0, "foo.com"); 487 add_smt.BindBlob(1, key_data.data(), key_data.size()); 488 add_smt.BindBlob(2, cert_data.data(), cert_data.size()); 489 add_smt.BindInt64(3, 1); 490 add_smt.BindInt64(4, GetTestCertExpirationTime().ToInternalValue()); 491 add_smt.BindInt64(5, base::Time::Now().ToInternalValue()); 492 ASSERT_TRUE(add_smt.Run()); 493 } 494 495 ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; 496 store_ = 497 new SQLiteChannelIDStore(v4_db_path, base::MessageLoopProxy::current()); 498 499 // Load the database and ensure the certs can be read. 500 Load(&channel_ids); 501 // Only the ECDSA cert (for google.com) is read, the RSA one is discarded. 502 ASSERT_EQ(1U, channel_ids.size()); 503 504 ASSERT_EQ("google.com", channel_ids[0]->server_identifier()); 505 ASSERT_EQ(GetTestCertExpirationTime(), channel_ids[0]->expiration_time()); 506 ASSERT_EQ(key_data, channel_ids[0]->private_key()); 507 ASSERT_EQ(cert_data, channel_ids[0]->cert()); 508 509 store_ = NULL; 510 // Make sure we wait until the destructor has run. 511 base::RunLoop().RunUntilIdle(); 512 } 513 514 } // namespace net 515