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.
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/run_loop.h"
11 #include "base/stl_util.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "crypto/ec_private_key.h"
14 #include "net/base/test_data_directory.h"
15 #include "net/cert/asn1_util.h"
16 #include "net/extras/sqlite/sqlite_channel_id_store.h"
17 #include "net/ssl/channel_id_service.h"
18 #include "net/ssl/ssl_client_cert_type.h"
19 #include "net/test/cert_test_util.h"
20 #include "net/test/channel_id_test_util.h"
21 #include "sql/statement.h"
22 #include "testing/gtest/include/gtest/gtest.h"
26 const base::FilePath::CharType kTestChannelIDFilename
[] =
27 FILE_PATH_LITERAL("ChannelID");
29 class SQLiteChannelIDStoreTest
: public testing::Test
{
31 void Load(ScopedVector
<DefaultChannelIDStore::ChannelID
>* channel_ids
) {
32 base::RunLoop run_loop
;
33 store_
->Load(base::Bind(&SQLiteChannelIDStoreTest::OnLoaded
,
34 base::Unretained(this),
37 channel_ids
->swap(channel_ids_
);
42 base::RunLoop
* run_loop
,
43 scoped_ptr
<ScopedVector
<DefaultChannelIDStore::ChannelID
> > channel_ids
) {
44 channel_ids_
.swap(*channel_ids
);
49 static void ReadTestKeyAndCert(std::string
* key_data
,
50 std::string
* cert_data
,
51 scoped_ptr
<crypto::ECPrivateKey
>* key
) {
52 base::FilePath key_path
=
53 GetTestCertsDirectory().AppendASCII("unittest.originbound.key.der");
54 base::FilePath cert_path
=
55 GetTestCertsDirectory().AppendASCII("unittest.originbound.der");
56 ASSERT_TRUE(base::ReadFileToString(key_path
, key_data
));
57 ASSERT_TRUE(base::ReadFileToString(cert_path
, cert_data
));
58 std::vector
<uint8
> private_key(key_data
->size());
59 memcpy(vector_as_array(&private_key
), key_data
->data(), key_data
->size());
60 base::StringPiece spki
;
61 ASSERT_TRUE(asn1::ExtractSPKIFromDERCert(*cert_data
, &spki
));
62 std::vector
<uint8
> public_key(spki
.size());
63 memcpy(vector_as_array(&public_key
), spki
.data(), spki
.size());
64 key
->reset(crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
65 ChannelIDService::kEPKIPassword
, private_key
, public_key
));
68 static base::Time
GetTestCertExpirationTime() {
69 // Cert expiration time from 'openssl asn1parse -inform der -in
70 // unittest.originbound.der':
71 // UTCTIME :160507022239Z
72 // base::Time::FromUTCExploded can't generate values past 2038 on 32-bit
73 // linux, so we use the raw value here.
74 base::Time::Exploded exploded_time
;
75 exploded_time
.year
= 2016;
76 exploded_time
.month
= 5;
77 exploded_time
.day_of_week
= 0; // Unused.
78 exploded_time
.day_of_month
= 7;
79 exploded_time
.hour
= 2;
80 exploded_time
.minute
= 22;
81 exploded_time
.second
= 39;
82 exploded_time
.millisecond
= 0;
83 return base::Time::FromUTCExploded(exploded_time
);
86 static base::Time
GetTestCertCreationTime() {
87 // UTCTIME :150508022239Z
88 base::Time::Exploded exploded_time
;
89 exploded_time
.year
= 2015;
90 exploded_time
.month
= 5;
91 exploded_time
.day_of_week
= 0; // Unused.
92 exploded_time
.day_of_month
= 8;
93 exploded_time
.hour
= 2;
94 exploded_time
.minute
= 22;
95 exploded_time
.second
= 39;
96 exploded_time
.millisecond
= 0;
97 return base::Time::FromUTCExploded(exploded_time
);
100 void SetUp() override
{
101 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
102 store_
= new SQLiteChannelIDStore(
103 temp_dir_
.path().Append(kTestChannelIDFilename
),
104 base::ThreadTaskRunnerHandle::Get());
105 ScopedVector
<DefaultChannelIDStore::ChannelID
> channel_ids
;
107 ASSERT_EQ(0u, channel_ids
.size());
108 // Make sure the store gets written at least once.
109 google_key_
.reset(crypto::ECPrivateKey::Create());
110 store_
->AddChannelID(DefaultChannelIDStore::ChannelID(
111 "google.com", base::Time::FromInternalValue(1),
112 make_scoped_ptr(google_key_
->Copy())));
115 base::ScopedTempDir temp_dir_
;
116 scoped_refptr
<SQLiteChannelIDStore
> store_
;
117 ScopedVector
<DefaultChannelIDStore::ChannelID
> channel_ids_
;
118 scoped_ptr
<crypto::ECPrivateKey
> google_key_
;
121 // Test if data is stored as expected in the SQLite database.
122 TEST_F(SQLiteChannelIDStoreTest
, TestPersistence
) {
123 scoped_ptr
<crypto::ECPrivateKey
> foo_key(crypto::ECPrivateKey::Create());
124 store_
->AddChannelID(DefaultChannelIDStore::ChannelID(
125 "foo.com", base::Time::FromInternalValue(3),
126 make_scoped_ptr(foo_key
->Copy())));
128 ScopedVector
<DefaultChannelIDStore::ChannelID
> channel_ids
;
129 // Replace the store effectively destroying the current one and forcing it
130 // to write its data to disk. Then we can see if after loading it again it
133 // Make sure we wait until the destructor has run.
134 base::RunLoop().RunUntilIdle();
136 new SQLiteChannelIDStore(temp_dir_
.path().Append(kTestChannelIDFilename
),
137 base::ThreadTaskRunnerHandle::Get());
139 // Reload and test for persistence
141 ASSERT_EQ(2U, channel_ids
.size());
142 DefaultChannelIDStore::ChannelID
* goog_channel_id
;
143 DefaultChannelIDStore::ChannelID
* foo_channel_id
;
144 if (channel_ids
[0]->server_identifier() == "google.com") {
145 goog_channel_id
= channel_ids
[0];
146 foo_channel_id
= channel_ids
[1];
148 goog_channel_id
= channel_ids
[1];
149 foo_channel_id
= channel_ids
[0];
151 ASSERT_EQ("google.com", goog_channel_id
->server_identifier());
152 EXPECT_TRUE(KeysEqual(google_key_
.get(), goog_channel_id
->key()));
153 ASSERT_EQ(1, goog_channel_id
->creation_time().ToInternalValue());
154 ASSERT_EQ("foo.com", foo_channel_id
->server_identifier());
155 EXPECT_TRUE(KeysEqual(foo_key
.get(), foo_channel_id
->key()));
156 ASSERT_EQ(3, foo_channel_id
->creation_time().ToInternalValue());
158 // Now delete the keypair and check persistence again.
159 store_
->DeleteChannelID(*channel_ids
[0]);
160 store_
->DeleteChannelID(*channel_ids
[1]);
162 // Make sure we wait until the destructor has run.
163 base::RunLoop().RunUntilIdle();
166 new SQLiteChannelIDStore(temp_dir_
.path().Append(kTestChannelIDFilename
),
167 base::ThreadTaskRunnerHandle::Get());
169 // Reload and check if the keypair has been removed.
171 ASSERT_EQ(0U, channel_ids
.size());
174 // Make sure we wait until the destructor has run.
175 base::RunLoop().RunUntilIdle();
178 // Test if data is stored as expected in the SQLite database.
179 TEST_F(SQLiteChannelIDStoreTest
, TestDeleteAll
) {
180 store_
->AddChannelID(DefaultChannelIDStore::ChannelID(
181 "foo.com", base::Time::FromInternalValue(3),
182 make_scoped_ptr(crypto::ECPrivateKey::Create())));
184 ScopedVector
<DefaultChannelIDStore::ChannelID
> channel_ids
;
185 // Replace the store effectively destroying the current one and forcing it
186 // to write its data to disk. Then we can see if after loading it again it
189 // Make sure we wait until the destructor has run.
190 base::RunLoop().RunUntilIdle();
192 new SQLiteChannelIDStore(temp_dir_
.path().Append(kTestChannelIDFilename
),
193 base::ThreadTaskRunnerHandle::Get());
195 // Reload and test for persistence
197 ASSERT_EQ(2U, channel_ids
.size());
198 // DeleteAll except foo.com (shouldn't fail if one is missing either).
199 std::list
<std::string
> delete_server_identifiers
;
200 delete_server_identifiers
.push_back("google.com");
201 delete_server_identifiers
.push_back("missing.com");
202 store_
->DeleteAllInList(delete_server_identifiers
);
204 // Now check persistence again.
206 // Make sure we wait until the destructor has run.
207 base::RunLoop().RunUntilIdle();
210 new SQLiteChannelIDStore(temp_dir_
.path().Append(kTestChannelIDFilename
),
211 base::ThreadTaskRunnerHandle::Get());
213 // Reload and check that only foo.com persisted in store.
215 ASSERT_EQ(1U, channel_ids
.size());
216 ASSERT_EQ("foo.com", channel_ids
[0]->server_identifier());
219 // Make sure we wait until the destructor has run.
220 base::RunLoop().RunUntilIdle();
223 TEST_F(SQLiteChannelIDStoreTest
, TestUpgradeV1
) {
224 // Reset the store. We'll be using a different database for this test.
227 base::FilePath
v1_db_path(temp_dir_
.path().AppendASCII("v1db"));
229 std::string key_data
;
230 std::string cert_data
;
231 scoped_ptr
<crypto::ECPrivateKey
> key
;
232 ASSERT_NO_FATAL_FAILURE(ReadTestKeyAndCert(&key_data
, &cert_data
, &key
));
234 // Create a version 1 database.
237 ASSERT_TRUE(db
.Open(v1_db_path
));
238 ASSERT_TRUE(db
.Execute(
239 "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY,"
240 "value LONGVARCHAR);"
241 "INSERT INTO \"meta\" VALUES('version','1');"
242 "INSERT INTO \"meta\" VALUES('last_compatible_version','1');"
243 "CREATE TABLE origin_bound_certs ("
244 "origin TEXT NOT NULL UNIQUE PRIMARY KEY,"
245 "private_key BLOB NOT NULL,cert BLOB NOT NULL);"));
247 sql::Statement
add_smt(db
.GetUniqueStatement(
248 "INSERT INTO origin_bound_certs (origin, private_key, cert) "
250 add_smt
.BindString(0, "google.com");
251 add_smt
.BindBlob(1, key_data
.data(), key_data
.size());
252 add_smt
.BindBlob(2, cert_data
.data(), cert_data
.size());
253 ASSERT_TRUE(add_smt
.Run());
255 ASSERT_TRUE(db
.Execute(
256 "INSERT INTO \"origin_bound_certs\" VALUES("
257 "'foo.com',X'AA',X'BB');"));
260 // Load and test the DB contents twice. First time ensures that we can use
261 // the updated values immediately. Second time ensures that the updated
262 // values are stored and read correctly on next load.
263 for (int i
= 0; i
< 2; ++i
) {
266 ScopedVector
<DefaultChannelIDStore::ChannelID
> channel_ids
;
267 store_
= new SQLiteChannelIDStore(v1_db_path
,
268 base::ThreadTaskRunnerHandle::Get());
270 // Load the database. Because the existing v1 certs are implicitly of type
271 // RSA, which is unsupported, they're discarded.
273 ASSERT_EQ(0U, channel_ids
.size());
276 base::RunLoop().RunUntilIdle();
278 // Verify the database version is updated.
281 ASSERT_TRUE(db
.Open(v1_db_path
));
282 sql::Statement
smt(db
.GetUniqueStatement(
283 "SELECT value FROM meta WHERE key = \"version\""));
284 ASSERT_TRUE(smt
.Step());
285 EXPECT_EQ(5, smt
.ColumnInt(0));
286 EXPECT_FALSE(smt
.Step());
291 TEST_F(SQLiteChannelIDStoreTest
, TestUpgradeV2
) {
292 // Reset the store. We'll be using a different database for this test.
295 base::FilePath
v2_db_path(temp_dir_
.path().AppendASCII("v2db"));
297 std::string key_data
;
298 std::string cert_data
;
299 scoped_ptr
<crypto::ECPrivateKey
> key
;
300 ASSERT_NO_FATAL_FAILURE(ReadTestKeyAndCert(&key_data
, &cert_data
, &key
));
302 // Create a version 2 database.
305 ASSERT_TRUE(db
.Open(v2_db_path
));
306 ASSERT_TRUE(db
.Execute(
307 "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY,"
308 "value LONGVARCHAR);"
309 "INSERT INTO \"meta\" VALUES('version','2');"
310 "INSERT INTO \"meta\" VALUES('last_compatible_version','1');"
311 "CREATE TABLE origin_bound_certs ("
312 "origin TEXT NOT NULL UNIQUE PRIMARY KEY,"
313 "private_key BLOB NOT NULL,"
314 "cert BLOB NOT NULL,"
315 "cert_type INTEGER);"));
317 sql::Statement
add_smt(db
.GetUniqueStatement(
318 "INSERT INTO origin_bound_certs (origin, private_key, cert, cert_type) "
319 "VALUES (?,?,?,?)"));
320 add_smt
.BindString(0, "google.com");
321 add_smt
.BindBlob(1, key_data
.data(), key_data
.size());
322 add_smt
.BindBlob(2, cert_data
.data(), cert_data
.size());
323 add_smt
.BindInt64(3, 64);
324 ASSERT_TRUE(add_smt
.Run());
326 // Malformed certs will be ignored and not migrated.
327 ASSERT_TRUE(db
.Execute(
328 "INSERT INTO \"origin_bound_certs\" VALUES("
329 "'foo.com',X'AA',X'BB',64);"));
332 // Load and test the DB contents twice. First time ensures that we can use
333 // the updated values immediately. Second time ensures that the updated
334 // values are saved and read correctly on next load.
335 for (int i
= 0; i
< 2; ++i
) {
338 ScopedVector
<DefaultChannelIDStore::ChannelID
> channel_ids
;
339 store_
= new SQLiteChannelIDStore(v2_db_path
,
340 base::ThreadTaskRunnerHandle::Get());
342 // Load the database and ensure the certs can be read.
344 ASSERT_EQ(1U, channel_ids
.size());
346 ASSERT_EQ("google.com", channel_ids
[0]->server_identifier());
347 ASSERT_EQ(GetTestCertCreationTime(), channel_ids
[0]->creation_time());
348 EXPECT_TRUE(KeysEqual(key
.get(), channel_ids
[0]->key()));
351 // Make sure we wait until the destructor has run.
352 base::RunLoop().RunUntilIdle();
354 // Verify the database version is updated.
357 ASSERT_TRUE(db
.Open(v2_db_path
));
358 sql::Statement
smt(db
.GetUniqueStatement(
359 "SELECT value FROM meta WHERE key = \"version\""));
360 ASSERT_TRUE(smt
.Step());
361 EXPECT_EQ(5, smt
.ColumnInt(0));
362 EXPECT_FALSE(smt
.Step());
367 TEST_F(SQLiteChannelIDStoreTest
, TestUpgradeV3
) {
368 // Reset the store. We'll be using a different database for this test.
371 base::FilePath
v3_db_path(temp_dir_
.path().AppendASCII("v3db"));
373 std::string key_data
;
374 std::string cert_data
;
375 scoped_ptr
<crypto::ECPrivateKey
> key
;
376 ASSERT_NO_FATAL_FAILURE(ReadTestKeyAndCert(&key_data
, &cert_data
, &key
));
378 // Create a version 3 database.
381 ASSERT_TRUE(db
.Open(v3_db_path
));
382 ASSERT_TRUE(db
.Execute(
383 "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY,"
384 "value LONGVARCHAR);"
385 "INSERT INTO \"meta\" VALUES('version','3');"
386 "INSERT INTO \"meta\" VALUES('last_compatible_version','1');"
387 "CREATE TABLE origin_bound_certs ("
388 "origin TEXT NOT NULL UNIQUE PRIMARY KEY,"
389 "private_key BLOB NOT NULL,"
390 "cert BLOB NOT NULL,"
392 "expiration_time INTEGER);"));
394 sql::Statement
add_smt(db
.GetUniqueStatement(
395 "INSERT INTO origin_bound_certs (origin, private_key, cert, cert_type, "
396 "expiration_time) VALUES (?,?,?,?,?)"));
397 add_smt
.BindString(0, "google.com");
398 add_smt
.BindBlob(1, key_data
.data(), key_data
.size());
399 add_smt
.BindBlob(2, cert_data
.data(), cert_data
.size());
400 add_smt
.BindInt64(3, 64);
401 add_smt
.BindInt64(4, 1000);
402 ASSERT_TRUE(add_smt
.Run());
404 // Malformed certs will be ignored and not migrated.
405 ASSERT_TRUE(db
.Execute(
406 "INSERT INTO \"origin_bound_certs\" VALUES("
407 "'foo.com',X'AA',X'BB',64,2000);"));
410 // Load and test the DB contents twice. First time ensures that we can use
411 // the updated values immediately. Second time ensures that the updated
412 // values are saved and read correctly on next load.
413 for (int i
= 0; i
< 2; ++i
) {
416 ScopedVector
<DefaultChannelIDStore::ChannelID
> channel_ids
;
417 store_
= new SQLiteChannelIDStore(v3_db_path
,
418 base::ThreadTaskRunnerHandle::Get());
420 // Load the database and ensure the certs can be read.
422 ASSERT_EQ(1U, channel_ids
.size());
424 ASSERT_EQ("google.com", channel_ids
[0]->server_identifier());
425 ASSERT_EQ(GetTestCertCreationTime(), channel_ids
[0]->creation_time());
426 EXPECT_TRUE(KeysEqual(key
.get(), channel_ids
[0]->key()));
429 // Make sure we wait until the destructor has run.
430 base::RunLoop().RunUntilIdle();
432 // Verify the database version is updated.
435 ASSERT_TRUE(db
.Open(v3_db_path
));
436 sql::Statement
smt(db
.GetUniqueStatement(
437 "SELECT value FROM meta WHERE key = \"version\""));
438 ASSERT_TRUE(smt
.Step());
439 EXPECT_EQ(5, smt
.ColumnInt(0));
440 EXPECT_FALSE(smt
.Step());
445 TEST_F(SQLiteChannelIDStoreTest
, TestUpgradeV4
) {
446 // Reset the store. We'll be using a different database for this test.
449 base::FilePath
v4_db_path(temp_dir_
.path().AppendASCII("v4db"));
451 std::string key_data
;
452 std::string cert_data
;
453 scoped_ptr
<crypto::ECPrivateKey
> key
;
454 ASSERT_NO_FATAL_FAILURE(ReadTestKeyAndCert(&key_data
, &cert_data
, &key
));
456 // Create a version 4 database.
459 ASSERT_TRUE(db
.Open(v4_db_path
));
460 ASSERT_TRUE(db
.Execute(
461 "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY,"
462 "value LONGVARCHAR);"
463 "INSERT INTO \"meta\" VALUES('version','4');"
464 "INSERT INTO \"meta\" VALUES('last_compatible_version','1');"
465 "CREATE TABLE origin_bound_certs ("
466 "origin TEXT NOT NULL UNIQUE PRIMARY KEY,"
467 "private_key BLOB NOT NULL,"
468 "cert BLOB NOT NULL,"
470 "expiration_time INTEGER,"
471 "creation_time INTEGER);"));
473 sql::Statement
add_smt(db
.GetUniqueStatement(
474 "INSERT INTO origin_bound_certs (origin, private_key, cert, cert_type, "
475 "expiration_time, creation_time) VALUES (?,?,?,?,?,?)"));
476 add_smt
.BindString(0, "google.com");
477 add_smt
.BindBlob(1, key_data
.data(), key_data
.size());
478 add_smt
.BindBlob(2, cert_data
.data(), cert_data
.size());
479 add_smt
.BindInt64(3, 64);
480 add_smt
.BindInt64(4, 1000);
481 add_smt
.BindInt64(5, GetTestCertCreationTime().ToInternalValue());
482 ASSERT_TRUE(add_smt
.Run());
484 // Add an RSA cert to the db. This cert should be ignored in the migration.
486 add_smt
.Assign(db
.GetUniqueStatement(
487 "INSERT INTO origin_bound_certs "
488 "(origin, private_key, cert, cert_type, expiration_time, creation_time)"
489 " VALUES (?,?,?,?,?,?)"));
490 add_smt
.BindString(0, "foo.com");
491 add_smt
.BindBlob(1, key_data
.data(), key_data
.size());
492 add_smt
.BindBlob(2, cert_data
.data(), cert_data
.size());
493 add_smt
.BindInt64(3, 1);
494 add_smt
.BindInt64(4, GetTestCertExpirationTime().ToInternalValue());
495 add_smt
.BindInt64(5, base::Time::Now().ToInternalValue());
496 ASSERT_TRUE(add_smt
.Run());
498 // Malformed certs will be ignored and not migrated.
499 ASSERT_TRUE(db
.Execute(
500 "INSERT INTO \"origin_bound_certs\" VALUES("
501 "'bar.com',X'AA',X'BB',64,2000,3000);"));
504 // Load and test the DB contents twice. First time ensures that we can use
505 // the updated values immediately. Second time ensures that the updated
506 // values are saved and read correctly on next load.
507 for (int i
= 0; i
< 2; ++i
) {
510 ScopedVector
<DefaultChannelIDStore::ChannelID
> channel_ids
;
511 store_
= new SQLiteChannelIDStore(v4_db_path
,
512 base::ThreadTaskRunnerHandle::Get());
514 // Load the database and ensure the certs can be read.
516 ASSERT_EQ(1U, channel_ids
.size());
518 ASSERT_EQ("google.com", channel_ids
[0]->server_identifier());
519 ASSERT_EQ(GetTestCertCreationTime(), channel_ids
[0]->creation_time());
520 EXPECT_TRUE(KeysEqual(key
.get(), channel_ids
[0]->key()));
523 // Make sure we wait until the destructor has run.
524 base::RunLoop().RunUntilIdle();
526 // Verify the database version is updated.
529 ASSERT_TRUE(db
.Open(v4_db_path
));
530 sql::Statement
smt(db
.GetUniqueStatement(
531 "SELECT value FROM meta WHERE key = \"version\""));
532 ASSERT_TRUE(smt
.Step());
533 EXPECT_EQ(5, smt
.ColumnInt(0));
534 EXPECT_FALSE(smt
.Step());