Home | History | Annotate | Download | only in sqlite
      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