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