diff options
-rw-r--r-- | chrome/browser/net/sqlite_origin_bound_cert_store.cc | 404 | ||||
-rw-r--r-- | chrome/browser/net/sqlite_origin_bound_cert_store.h | 44 | ||||
-rw-r--r-- | chrome/browser/net/sqlite_origin_bound_cert_store_unittest.cc | 175 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | chrome/common/chrome_constants.cc | 1 | ||||
-rw-r--r-- | chrome/common/chrome_constants.h | 1 | ||||
-rw-r--r-- | net/base/default_origin_bound_cert_store.cc | 137 | ||||
-rw-r--r-- | net/base/default_origin_bound_cert_store.h | 163 | ||||
-rw-r--r-- | net/base/default_origin_bound_cert_store_unittest.cc | 112 | ||||
-rw-r--r-- | net/base/origin_bound_cert_service.cc | 29 | ||||
-rw-r--r-- | net/base/origin_bound_cert_service.h | 23 | ||||
-rw-r--r-- | net/base/origin_bound_cert_service_unittest.cc | 68 | ||||
-rw-r--r-- | net/base/origin_bound_cert_store.h | 25 | ||||
-rw-r--r-- | net/net.gyp | 4 |
15 files changed, 1164 insertions, 25 deletions
diff --git a/chrome/browser/net/sqlite_origin_bound_cert_store.cc b/chrome/browser/net/sqlite_origin_bound_cert_store.cc new file mode 100644 index 0000000..9e77277 --- /dev/null +++ b/chrome/browser/net/sqlite_origin_bound_cert_store.cc @@ -0,0 +1,404 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/net/sqlite_origin_bound_cert_store.h" + +#include <list> + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/string_util.h" +#include "base/threading/thread.h" +#include "base/threading/thread_restrictions.h" +#include "chrome/browser/diagnostics/sqlite_diagnostics.h" +#include "content/browser/browser_thread.h" +#include "sql/meta_table.h" +#include "sql/statement.h" +#include "sql/transaction.h" + +// This class is designed to be shared between any calling threads and the +// database thread. It batches operations and commits them on a timer. +class SQLiteOriginBoundCertStore::Backend + : public base::RefCountedThreadSafe<SQLiteOriginBoundCertStore::Backend> { + public: + explicit Backend(const FilePath& path) + : path_(path), + db_(NULL), + num_pending_(0), + clear_local_state_on_exit_(false) { + } + + // Creates or load the SQLite database. + bool Load( + std::vector<net::DefaultOriginBoundCertStore::OriginBoundCert*>* certs); + + // Batch an origin bound cert addition. + void AddOriginBoundCert( + const net::DefaultOriginBoundCertStore::OriginBoundCert& cert); + + // Batch an origin bound cert deletion. + void DeleteOriginBoundCert( + const net::DefaultOriginBoundCertStore::OriginBoundCert& cert); + + // Commit pending operations as soon as possible. + void Flush(Task* completion_task); + + // Commit any pending operations and close the database. This must be called + // before the object is destructed. + void Close(); + + void SetClearLocalStateOnExit(bool clear_local_state); + + private: + friend class base::RefCountedThreadSafe<SQLiteOriginBoundCertStore::Backend>; + + // You should call Close() before destructing this object. + ~Backend() { + DCHECK(!db_.get()) << "Close should have already been called."; + DCHECK(num_pending_ == 0 && pending_.empty()); + } + + // Database upgrade statements. + bool EnsureDatabaseVersion(); + + class PendingOperation { + public: + typedef enum { + CERT_ADD, + CERT_DELETE + } OperationType; + + PendingOperation( + OperationType op, + const net::DefaultOriginBoundCertStore::OriginBoundCert& cert) + : op_(op), cert_(cert) {} + + OperationType op() const { return op_; } + const net::DefaultOriginBoundCertStore::OriginBoundCert& cert() const { + return cert_; + } + + private: + OperationType op_; + net::DefaultOriginBoundCertStore::OriginBoundCert cert_; + }; + + private: + // Batch an origin bound cert operation (add or delete) + void BatchOperation( + PendingOperation::OperationType op, + const net::DefaultOriginBoundCertStore::OriginBoundCert& cert); + // Commit our pending operations to the database. + void Commit(); + // Close() executed on the background thread. + void InternalBackgroundClose(); + + FilePath path_; + scoped_ptr<sql::Connection> db_; + sql::MetaTable meta_table_; + + typedef std::list<PendingOperation*> PendingOperationsList; + PendingOperationsList pending_; + PendingOperationsList::size_type num_pending_; + // True if the persistent store should be deleted upon destruction. + bool clear_local_state_on_exit_; + // Guard |pending_|, |num_pending_| and |clear_local_state_on_exit_|. + base::Lock lock_; + + DISALLOW_COPY_AND_ASSIGN(Backend); +}; + +// Version number of the database. +static const int kCurrentVersionNumber = 1; +static const int kCompatibleVersionNumber = 1; + +namespace { + +// Initializes the certs table, returning true on success. +bool InitTable(sql::Connection* db) { + if (!db->DoesTableExist("origin_bound_certs")) { + if (!db->Execute("CREATE TABLE origin_bound_certs (" + "origin TEXT NOT NULL UNIQUE PRIMARY KEY," + "private_key BLOB NOT NULL," + "cert BLOB NOT NULL)")) + return false; + } + + return true; +} + +} // namespace + +bool SQLiteOriginBoundCertStore::Backend::Load( + std::vector<net::DefaultOriginBoundCertStore::OriginBoundCert*>* certs) { + // This function should be called only once per instance. + DCHECK(!db_.get()); + + // Ensure the parent directory for storing certs is created before reading + // from it. We make an exception to allow IO on the UI thread here because + // we are going to disk anyway in db_->Open. (This code will be moved to the + // DB thread as part of http://crbug.com/52909.) + { + base::ThreadRestrictions::ScopedAllowIO allow_io; + const FilePath dir = path_.DirName(); + if (!file_util::PathExists(dir) && !file_util::CreateDirectory(dir)) + return false; + } + + db_.reset(new sql::Connection); + if (!db_->Open(path_)) { + NOTREACHED() << "Unable to open cert DB."; + db_.reset(); + return false; + } + + if (!EnsureDatabaseVersion() || !InitTable(db_.get())) { + NOTREACHED() << "Unable to open cert DB."; + db_.reset(); + return false; + } + + db_->Preload(); + + // Slurp all the certs into the out-vector. + sql::Statement smt(db_->GetUniqueStatement( + "SELECT origin, private_key, cert FROM origin_bound_certs")); + if (!smt) { + NOTREACHED() << "select statement prep failed"; + db_.reset(); + return false; + } + + while (smt.Step()) { + std::string private_key_from_db, cert_from_db; + smt.ColumnBlobAsString(1, &private_key_from_db); + smt.ColumnBlobAsString(2, &cert_from_db); + scoped_ptr<net::DefaultOriginBoundCertStore::OriginBoundCert> cert( + new net::DefaultOriginBoundCertStore::OriginBoundCert( + smt.ColumnString(0), // origin + private_key_from_db, + cert_from_db)); + certs->push_back(cert.release()); + } + + return true; +} + +bool SQLiteOriginBoundCertStore::Backend::EnsureDatabaseVersion() { + // Version check. + if (!meta_table_.Init( + db_.get(), kCurrentVersionNumber, kCompatibleVersionNumber)) { + return false; + } + + if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { + LOG(WARNING) << "Origin bound cert database is too new."; + return false; + } + + int cur_version = meta_table_.GetVersionNumber(); + + // Put future migration cases here. + + // When the version is too old, we just try to continue anyway, there should + // not be a released product that makes a database too old for us to handle. + LOG_IF(WARNING, cur_version < kCurrentVersionNumber) << + "Origin bound cert database version " << cur_version << + " is too old to handle."; + + return true; +} + +void SQLiteOriginBoundCertStore::Backend::AddOriginBoundCert( + const net::DefaultOriginBoundCertStore::OriginBoundCert& cert) { + BatchOperation(PendingOperation::CERT_ADD, cert); +} + +void SQLiteOriginBoundCertStore::Backend::DeleteOriginBoundCert( + const net::DefaultOriginBoundCertStore::OriginBoundCert& cert) { + BatchOperation(PendingOperation::CERT_DELETE, cert); +} + +void SQLiteOriginBoundCertStore::Backend::BatchOperation( + PendingOperation::OperationType op, + const net::DefaultOriginBoundCertStore::OriginBoundCert& cert) { + // Commit every 30 seconds. + static const int kCommitIntervalMs = 30 * 1000; + // Commit right away if we have more than 512 outstanding operations. + static const size_t kCommitAfterBatchSize = 512; + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::DB)); + + // We do a full copy of the cert here, and hopefully just here. + scoped_ptr<PendingOperation> po(new PendingOperation(op, cert)); + + PendingOperationsList::size_type num_pending; + { + base::AutoLock locked(lock_); + pending_.push_back(po.release()); + num_pending = ++num_pending_; + } + + if (num_pending == 1) { + // We've gotten our first entry for this batch, fire off the timer. + BrowserThread::PostDelayedTask( + BrowserThread::DB, FROM_HERE, + NewRunnableMethod(this, &Backend::Commit), kCommitIntervalMs); + } else if (num_pending == kCommitAfterBatchSize) { + // We've reached a big enough batch, fire off a commit now. + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, + NewRunnableMethod(this, &Backend::Commit)); + } +} + +void SQLiteOriginBoundCertStore::Backend::Commit() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + + PendingOperationsList ops; + { + base::AutoLock locked(lock_); + pending_.swap(ops); + num_pending_ = 0; + } + + // Maybe an old timer fired or we are already Close()'ed. + if (!db_.get() || ops.empty()) + return; + + sql::Statement add_smt(db_->GetCachedStatement(SQL_FROM_HERE, + "INSERT INTO origin_bound_certs (origin, private_key, cert) " + "VALUES (?,?,?)")); + if (!add_smt) { + NOTREACHED(); + return; + } + + sql::Statement del_smt(db_->GetCachedStatement(SQL_FROM_HERE, + "DELETE FROM origin_bound_certs WHERE origin=?")); + if (!del_smt) { + NOTREACHED(); + return; + } + + sql::Transaction transaction(db_.get()); + if (!transaction.Begin()) { + NOTREACHED(); + return; + } + for (PendingOperationsList::iterator it = ops.begin(); + it != ops.end(); ++it) { + // Free the certs as we commit them to the database. + scoped_ptr<PendingOperation> po(*it); + switch (po->op()) { + case PendingOperation::CERT_ADD: { + add_smt.Reset(); + add_smt.BindString(0, po->cert().origin()); + const std::string& private_key = po->cert().private_key(); + add_smt.BindBlob(1, private_key.data(), private_key.size()); + const std::string& cert = po->cert().cert(); + add_smt.BindBlob(2, cert.data(), cert.size()); + if (!add_smt.Run()) + NOTREACHED() << "Could not add an origin bound cert to the DB."; + break; + } + case PendingOperation::CERT_DELETE: + del_smt.Reset(); + del_smt.BindString(0, po->cert().origin()); + if (!del_smt.Run()) + NOTREACHED() << "Could not delete an origin bound cert from the DB."; + break; + + default: + NOTREACHED(); + break; + } + } + transaction.Commit(); +} + +void SQLiteOriginBoundCertStore::Backend::Flush(Task* completion_task) { + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::DB)); + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, NewRunnableMethod(this, &Backend::Commit)); + if (completion_task) { + // We want the completion task to run immediately after Commit() returns. + // Posting it from here means there is less chance of another task getting + // onto the message queue first, than if we posted it from Commit() itself. + BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, completion_task); + } +} + +// Fire off a close message to the background thread. We could still have a +// pending commit timer that will be holding a reference on us, but if/when +// this fires we will already have been cleaned up and it will be ignored. +void SQLiteOriginBoundCertStore::Backend::Close() { + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::DB)); + // Must close the backend on the background thread. + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, + NewRunnableMethod(this, &Backend::InternalBackgroundClose)); +} + +void SQLiteOriginBoundCertStore::Backend::InternalBackgroundClose() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + // Commit any pending operations + Commit(); + + db_.reset(); + + if (clear_local_state_on_exit_) + file_util::Delete(path_, false); +} + +void SQLiteOriginBoundCertStore::Backend::SetClearLocalStateOnExit( + bool clear_local_state) { + base::AutoLock locked(lock_); + clear_local_state_on_exit_ = clear_local_state; +} + +SQLiteOriginBoundCertStore::SQLiteOriginBoundCertStore(const FilePath& path) + : backend_(new Backend(path)) { +} + +SQLiteOriginBoundCertStore::~SQLiteOriginBoundCertStore() { + if (backend_.get()) { + backend_->Close(); + // Release our reference, it will probably still have a reference if the + // background thread has not run Close() yet. + backend_ = NULL; + } +} + +bool SQLiteOriginBoundCertStore::Load( + std::vector<net::DefaultOriginBoundCertStore::OriginBoundCert*>* certs) { + return backend_->Load(certs); +} + +void SQLiteOriginBoundCertStore::AddOriginBoundCert( + const net::DefaultOriginBoundCertStore::OriginBoundCert& cert) { + if (backend_.get()) + backend_->AddOriginBoundCert(cert); +} + +void SQLiteOriginBoundCertStore::DeleteOriginBoundCert( + const net::DefaultOriginBoundCertStore::OriginBoundCert& cert) { + if (backend_.get()) + backend_->DeleteOriginBoundCert(cert); +} + +void SQLiteOriginBoundCertStore::SetClearLocalStateOnExit( + bool clear_local_state) { + if (backend_.get()) + backend_->SetClearLocalStateOnExit(clear_local_state); +} + +void SQLiteOriginBoundCertStore::Flush(Task* completion_task) { + if (backend_.get()) + backend_->Flush(completion_task); + else if (completion_task) + MessageLoop::current()->PostTask(FROM_HERE, completion_task); +} diff --git a/chrome/browser/net/sqlite_origin_bound_cert_store.h b/chrome/browser/net/sqlite_origin_bound_cert_store.h new file mode 100644 index 0000000..f112ad8 --- /dev/null +++ b/chrome/browser/net/sqlite_origin_bound_cert_store.h @@ -0,0 +1,44 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_NET_SQLITE_ORIGIN_BOUND_CERT_STORE_H_ +#define CHROME_BROWSER_NET_SQLITE_ORIGIN_BOUND_CERT_STORE_H_ +#pragma once + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "net/base/default_origin_bound_cert_store.h" + +class FilePath; + +// Implements the net::DefaultOriginBoundCertStore::PersistentStore interface +// in terms of a SQLite database. For documentation about the actual member +// functions consult the documentation of the parent class +// |net::DefaultOriginBoundCertStore::PersistentCertStore|. +class SQLiteOriginBoundCertStore + : public net::DefaultOriginBoundCertStore::PersistentStore { + public: + explicit SQLiteOriginBoundCertStore(const FilePath& path); + virtual ~SQLiteOriginBoundCertStore(); + + // net::DefaultOriginBoundCertStore::PersistentStore implementation. + virtual bool Load( + std::vector<net::DefaultOriginBoundCertStore::OriginBoundCert*>* certs) + OVERRIDE; + virtual void AddOriginBoundCert( + const net::DefaultOriginBoundCertStore::OriginBoundCert& cert) OVERRIDE; + virtual void DeleteOriginBoundCert( + const net::DefaultOriginBoundCertStore::OriginBoundCert& cert) OVERRIDE; + virtual void SetClearLocalStateOnExit(bool clear_local_state) OVERRIDE; + virtual void Flush(Task* completion_task) OVERRIDE; + + private: + class Backend; + + scoped_refptr<Backend> backend_; + + DISALLOW_COPY_AND_ASSIGN(SQLiteOriginBoundCertStore); +}; + +#endif // CHROME_BROWSER_NET_SQLITE_ORIGIN_BOUND_CERT_STORE_H_ diff --git a/chrome/browser/net/sqlite_origin_bound_cert_store_unittest.cc b/chrome/browser/net/sqlite_origin_bound_cert_store_unittest.cc new file mode 100644 index 0000000..aae87a9 --- /dev/null +++ b/chrome/browser/net/sqlite_origin_bound_cert_store_unittest.cc @@ -0,0 +1,175 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop.h" +#include "base/scoped_temp_dir.h" +#include "base/stl_util.h" +#include "base/test/thread_test_helper.h" +#include "chrome/browser/net/sqlite_origin_bound_cert_store.h" +#include "chrome/common/chrome_constants.h" +#include "content/browser/browser_thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +class SQLiteOriginBoundCertStoreTest : public testing::Test { + public: + SQLiteOriginBoundCertStoreTest() + : db_thread_(BrowserThread::DB) { + } + + protected: + virtual void SetUp() { + db_thread_.Start(); + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + store_ = new SQLiteOriginBoundCertStore( + temp_dir_.path().Append(chrome::kOBCertFilename)); + std::vector<net::DefaultOriginBoundCertStore::OriginBoundCert*> certs; + ASSERT_TRUE(store_->Load(&certs)); + ASSERT_EQ(0u, certs.size()); + // Make sure the store gets written at least once. + store_->AddOriginBoundCert( + net::DefaultOriginBoundCertStore::OriginBoundCert( + "https://encrypted.google.com:8443", "a", "b")); + } + + BrowserThread db_thread_; + ScopedTempDir temp_dir_; + scoped_refptr<SQLiteOriginBoundCertStore> store_; +}; + +TEST_F(SQLiteOriginBoundCertStoreTest, KeepOnDestruction) { + store_->SetClearLocalStateOnExit(false); + store_ = NULL; + // Make sure we wait until the destructor has run. + scoped_refptr<base::ThreadTestHelper> helper( + new base::ThreadTestHelper( + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB))); + ASSERT_TRUE(helper->Run()); + + ASSERT_TRUE(file_util::PathExists( + temp_dir_.path().Append(chrome::kOBCertFilename))); + ASSERT_TRUE(file_util::Delete( + temp_dir_.path().Append(chrome::kOBCertFilename), false)); +} + +TEST_F(SQLiteOriginBoundCertStoreTest, RemoveOnDestruction) { + store_->SetClearLocalStateOnExit(true); + // Replace the store effectively destroying the current one and forcing it + // to write it's data to disk. Then we can see if after loading it again it + // is still there. + store_ = NULL; + // Make sure we wait until the destructor has run. + scoped_refptr<base::ThreadTestHelper> helper( + new base::ThreadTestHelper( + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB))); + ASSERT_TRUE(helper->Run()); + + ASSERT_FALSE(file_util::PathExists( + temp_dir_.path().Append(chrome::kOBCertFilename))); +} + +// Test if data is stored as expected in the SQLite database. +TEST_F(SQLiteOriginBoundCertStoreTest, TestPersistence) { + std::vector<net::DefaultOriginBoundCertStore::OriginBoundCert*> certs; + // Replace the store effectively destroying the current one and forcing it + // to write it's data to disk. Then we can see if after loading it again it + // is still there. + store_ = NULL; + scoped_refptr<base::ThreadTestHelper> helper( + new base::ThreadTestHelper( + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB))); + // Make sure we wait until the destructor has run. + ASSERT_TRUE(helper->Run()); + store_ = new SQLiteOriginBoundCertStore( + temp_dir_.path().Append(chrome::kOBCertFilename)); + + // Reload and test for persistence + ASSERT_TRUE(store_->Load(&certs)); + ASSERT_EQ(1U, certs.size()); + ASSERT_STREQ("https://encrypted.google.com:8443", certs[0]->origin().c_str()); + ASSERT_STREQ("a", certs[0]->private_key().c_str()); + ASSERT_STREQ("b", certs[0]->cert().c_str()); + + // Now delete the cert and check persistence again. + store_->DeleteOriginBoundCert(*certs[0]); + store_ = NULL; + // Make sure we wait until the destructor has run. + ASSERT_TRUE(helper->Run()); + STLDeleteContainerPointers(certs.begin(), certs.end()); + certs.clear(); + store_ = new SQLiteOriginBoundCertStore( + temp_dir_.path().Append(chrome::kOBCertFilename)); + + // Reload and check if the cert has been removed. + ASSERT_TRUE(store_->Load(&certs)); + ASSERT_EQ(0U, certs.size()); +} + +// Test that we can force the database to be written by calling Flush(). +TEST_F(SQLiteOriginBoundCertStoreTest, TestFlush) { + // File timestamps don't work well on all platforms, so we'll determine + // whether the DB file has been modified by checking its size. + FilePath path = temp_dir_.path().Append(chrome::kOBCertFilename); + base::PlatformFileInfo info; + ASSERT_TRUE(file_util::GetFileInfo(path, &info)); + int64 base_size = info.size; + + // Write some certs, so the DB will have to expand by several KB. + for (char c = 'a'; c < 'z'; ++c) { + std::string origin(1, c); + std::string private_key(1000, c); + std::string cert(1000, c); + store_->AddOriginBoundCert( + net::DefaultOriginBoundCertStore::OriginBoundCert(origin, + private_key, + cert)); + } + + // Call Flush() and wait until the DB thread is idle. + store_->Flush(NULL); + scoped_refptr<base::ThreadTestHelper> helper( + new base::ThreadTestHelper( + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB))); + ASSERT_TRUE(helper->Run()); + + // We forced a write, so now the file will be bigger. + ASSERT_TRUE(file_util::GetFileInfo(path, &info)); + ASSERT_GT(info.size, base_size); +} + +// Counts the number of times Callback() has been run. +class CallbackCounter : public base::RefCountedThreadSafe<CallbackCounter> { + public: + CallbackCounter() : callback_count_(0) {} + + void Callback() { + ++callback_count_; + } + + int callback_count() { + return callback_count_; + } + + private: + friend class base::RefCountedThreadSafe<CallbackCounter>; + volatile int callback_count_; +}; + +// Test that we can get a completion callback after a Flush(). +TEST_F(SQLiteOriginBoundCertStoreTest, TestFlushCompletionCallback) { + scoped_refptr<CallbackCounter> counter(new CallbackCounter()); + + // Callback shouldn't be invoked until we call Flush(). + ASSERT_EQ(0, counter->callback_count()); + + store_->Flush(NewRunnableMethod(counter.get(), &CallbackCounter::Callback)); + + scoped_refptr<base::ThreadTestHelper> helper( + new base::ThreadTestHelper( + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB))); + ASSERT_TRUE(helper->Run()); + + ASSERT_EQ(1, counter->callback_count()); +} diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 08f6128..540017b 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1472,6 +1472,8 @@ 'browser/net/sdch_dictionary_fetcher.h', 'browser/net/service_providers_win.cc', 'browser/net/service_providers_win.h', + 'browser/net/sqlite_origin_bound_cert_store.cc', + 'browser/net/sqlite_origin_bound_cert_store.h', 'browser/net/sqlite_persistent_cookie_store.cc', 'browser/net/sqlite_persistent_cookie_store.h', 'browser/net/ssl_config_service_manager.h', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index cbf6f88..f872890 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1501,6 +1501,7 @@ 'browser/net/predictor_unittest.cc', 'browser/net/pref_proxy_config_service_unittest.cc', 'browser/net/quoted_printable_unittest.cc', + 'browser/net/sqlite_origin_bound_cert_store_unittest.cc', 'browser/net/sqlite_persistent_cookie_store_unittest.cc', 'browser/net/ssl_config_service_manager_pref_unittest.cc', 'browser/net/url_fixer_upper_unittest.cc', diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc index f1af5b6..2d241ad 100644 --- a/chrome/common/chrome_constants.cc +++ b/chrome/common/chrome_constants.cc @@ -126,6 +126,7 @@ const FilePath::CharType kOffTheRecordMediaCacheDirname[] = const FilePath::CharType kAppCacheDirname[] = FPL("Application Cache"); const FilePath::CharType kThemePackFilename[] = FPL("Cached Theme.pak"); const FilePath::CharType kCookieFilename[] = FPL("Cookies"); +const FilePath::CharType kOBCertFilename[] = FPL("Origin Bound Certs"); const FilePath::CharType kExtensionsCookieFilename[] = FPL("Extension Cookies"); const FilePath::CharType kIsolatedAppStateDirname[] = FPL("Isolated Apps"); const FilePath::CharType kFaviconsFilename[] = FPL("Favicons"); diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h index ac5129d..6cd6e45 100644 --- a/chrome/common/chrome_constants.h +++ b/chrome/common/chrome_constants.h @@ -50,6 +50,7 @@ extern const FilePath::CharType kOffTheRecordMediaCacheDirname[]; extern const FilePath::CharType kAppCacheDirname[]; extern const FilePath::CharType kThemePackFilename[]; extern const FilePath::CharType kCookieFilename[]; +extern const FilePath::CharType kOBCertFilename[]; extern const FilePath::CharType kExtensionsCookieFilename[]; extern const FilePath::CharType kIsolatedAppStateDirname[]; extern const FilePath::CharType kFaviconsFilename[]; diff --git a/net/base/default_origin_bound_cert_store.cc b/net/base/default_origin_bound_cert_store.cc new file mode 100644 index 0000000..e047af8 --- /dev/null +++ b/net/base/default_origin_bound_cert_store.cc @@ -0,0 +1,137 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/default_origin_bound_cert_store.h" + +#include "base/message_loop.h" + +namespace net { + +// static +const size_t DefaultOriginBoundCertStore::kMaxCerts = 3300; + +DefaultOriginBoundCertStore::DefaultOriginBoundCertStore( + PersistentStore* store) + : initialized_(false), + store_(store) {} + +void DefaultOriginBoundCertStore::FlushStore(Task* completion_task) { + base::AutoLock autolock(lock_); + + if (initialized_ && store_) + store_->Flush(completion_task); + else if (completion_task) + MessageLoop::current()->PostTask(FROM_HERE, completion_task); +} + +bool DefaultOriginBoundCertStore::GetOriginBoundCert( + const std::string& origin, + std::string* private_key_result, + std::string* cert_result) { + base::AutoLock autolock(lock_); + InitIfNecessary(); + + OriginBoundCertMap::iterator it = origin_bound_certs_.find(origin); + + if (it == origin_bound_certs_.end()) + return false; + + OriginBoundCert* cert = it->second; + *private_key_result = cert->private_key(); + *cert_result = cert->cert(); + + return true; +} + +bool DefaultOriginBoundCertStore::SetOriginBoundCert( + const std::string& origin, + const std::string& private_key, + const std::string& cert) { + base::AutoLock autolock(lock_); + InitIfNecessary(); + + InternalDeleteOriginBoundCert(origin); + InternalInsertOriginBoundCert(origin, + new OriginBoundCert(origin, private_key, cert)); + + return true; +} + +int DefaultOriginBoundCertStore::GetCertCount() { + base::AutoLock autolock(lock_); + InitIfNecessary(); + + return origin_bound_certs_.size(); +} + +DefaultOriginBoundCertStore::~DefaultOriginBoundCertStore() { + DeleteAll(); +} + +void DefaultOriginBoundCertStore::DeleteAll() { + base::AutoLock autolock(lock_); + + for (OriginBoundCertMap::iterator it = origin_bound_certs_.begin(); + it != origin_bound_certs_.end(); it++) { + delete it->second; + } + origin_bound_certs_.clear(); +} + +void DefaultOriginBoundCertStore::InitStore() { + lock_.AssertAcquired(); + + DCHECK(store_) << "Store must exist to initialize"; + + // Initialize the store and sync in any saved persistent certs. + std::vector<OriginBoundCert*> certs; + // Reserve space for the maximum amount of certs a database should have. + // This prevents multiple vector growth / copies as we append certs. + certs.reserve(kMaxCerts); + store_->Load(&certs); + + for (std::vector<OriginBoundCert*>::const_iterator it = certs.begin(); + it != certs.end(); ++it) { + origin_bound_certs_[(*it)->origin()] = *it; + } +} + +void DefaultOriginBoundCertStore::InternalDeleteOriginBoundCert( + const std::string& origin) { + lock_.AssertAcquired(); + + OriginBoundCertMap::iterator it = origin_bound_certs_.find(origin); + if (it == origin_bound_certs_.end()) + return; // There is nothing to delete. + + OriginBoundCert* cert = it->second; + if (store_) + store_->DeleteOriginBoundCert(*cert); + origin_bound_certs_.erase(it); + delete cert; +} + +void DefaultOriginBoundCertStore::InternalInsertOriginBoundCert( + const std::string& origin, + OriginBoundCert* cert) { + lock_.AssertAcquired(); + + if (store_) + store_->AddOriginBoundCert(*cert); + origin_bound_certs_[origin] = cert; +} + +DefaultOriginBoundCertStore::OriginBoundCert::OriginBoundCert() {} + +DefaultOriginBoundCertStore::OriginBoundCert::OriginBoundCert( + const std::string& origin, + const std::string& private_key, + const std::string& cert) + : origin_(origin), + private_key_(private_key), + cert_(cert) {} + +DefaultOriginBoundCertStore::PersistentStore::PersistentStore() {} + +} // namespace net diff --git a/net/base/default_origin_bound_cert_store.h b/net/base/default_origin_bound_cert_store.h new file mode 100644 index 0000000..4b8b8a9 --- /dev/null +++ b/net/base/default_origin_bound_cert_store.h @@ -0,0 +1,163 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_BASE_DEFAULT_ORIGIN_BOUND_CERT_STORE_H_ +#define NET_BASE_DEFAULT_ORIGIN_BOUND_CERT_STORE_H_ +#pragma once + +#include <map> +#include <string> +#include <vector> + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "net/base/origin_bound_cert_store.h" + +class Task; + +namespace net { + +// This class is the system for storing and retrieving origin bound certs. +// Modelled after the CookieMonster class, it has an in-memory cert store, +// and synchronizes origin bound certs to an optional permanent storage that +// implements the PersistentStore interface. The use case is described in +// http://balfanz.github.com/tls-obc-spec/draft-balfanz-tls-obc-00.html +// +// This class can be accessed by multiple threads. For example, it can be used +// by IO and origin bound cert management UI. +class DefaultOriginBoundCertStore : public OriginBoundCertStore { + public: + class OriginBoundCert; + class PersistentStore; + + // The key for each OriginBoundCert* in OriginBoundCertMap is the + // corresponding origin. + typedef std::map<std::string, OriginBoundCert*> OriginBoundCertMap; + + // The store passed in should not have had Init() called on it yet. This + // class will take care of initializing it. The backing store is NOT owned by + // this class, but it must remain valid for the duration of the + // DefaultOriginBoundCertStore's existence. If |store| is NULL, then no + // backing store will be updated. + explicit DefaultOriginBoundCertStore(PersistentStore* store); + + virtual ~DefaultOriginBoundCertStore(); + + // Flush the backing store (if any) to disk and post the given task when done. + // WARNING: THE CALLBACK WILL RUN ON A RANDOM THREAD. IT MUST BE THREAD SAFE. + // It may be posted to the current thread, or it may run on the thread that + // actually does the flushing. Your Task should generally post a notification + // to the thread you actually want to be notified on. + void FlushStore(Task* completion_task); + + // OriginBoundCertStore implementation. + virtual bool GetOriginBoundCert(const std::string& origin, + std::string* private_key_result, + std::string* cert_result) OVERRIDE; + virtual bool SetOriginBoundCert(const std::string& origin, + const std::string& private_key, + const std::string& cert) OVERRIDE; + virtual int GetCertCount() OVERRIDE; + + private: + static const size_t kMaxCerts; + + // Deletes all of the certs. Does not delete them from |store_|. + void DeleteAll(); + + // Called by all non-static functions to ensure that the cert store has + // been initialized. This is not done during creating so it doesn't block + // the window showing. + // Note: this method should always be called with lock_ held. + void InitIfNecessary() { + if (!initialized_) { + if (store_) + InitStore(); + initialized_ = true; + } + } + + // Initializes the backing store and reads existing certs from it. + // Should only be called by InitIfNecessary(). + void InitStore(); + + // Deletes the cert for the specified origin, if such a cert exists, from the + // in-memory store. Deletes it from |store_| if |store_| is not NULL. + void InternalDeleteOriginBoundCert(const std::string& origin); + + // Takes ownership of *cert. + // Adds the cert for the specified origin to the in-memory store. Deletes it + // from |store_| if |store_| is not NULL. + void InternalInsertOriginBoundCert(const std::string& origin, + OriginBoundCert* cert); + + // Indicates whether the cert store has been initialized. This happens + // Lazily in InitStoreIfNecessary(). + bool initialized_; + + scoped_refptr<PersistentStore> store_; + + OriginBoundCertMap origin_bound_certs_; + + // Lock for thread-safety + base::Lock lock_; + + DISALLOW_COPY_AND_ASSIGN(DefaultOriginBoundCertStore); +}; + +// The OriginBoundCert class contains a private key in addition to the origin +// and the cert. +class DefaultOriginBoundCertStore::OriginBoundCert { + public: + OriginBoundCert(); + OriginBoundCert(const std::string& origin, + const std::string& privatekey, + const std::string& cert); + + const std::string& origin() const { return origin_; } + const std::string& private_key() const { return private_key_; } + const std::string& cert() const { return cert_; } + + private: + std::string origin_; + std::string private_key_; + std::string cert_; +}; + +typedef base::RefCountedThreadSafe<DefaultOriginBoundCertStore::PersistentStore> + RefcountedPersistentStore; + +class DefaultOriginBoundCertStore::PersistentStore + : public RefcountedPersistentStore { + public: + virtual ~PersistentStore() {} + + // Initializes the store and retrieves the existing certs. This will be + // called only once at startup. Note that the certs are individually allocated + // and that ownership is transferred to the caller upon return. + virtual bool Load( + std::vector<DefaultOriginBoundCertStore::OriginBoundCert*>* certs) = 0; + + virtual void AddOriginBoundCert(const OriginBoundCert& cert) = 0; + + virtual void DeleteOriginBoundCert(const OriginBoundCert& cert) = 0; + + // Sets the value of the user preference whether the persistent storage + // must be deleted upon destruction. + virtual void SetClearLocalStateOnExit(bool clear_local_state) = 0; + + // Flush the store and post the given Task when complete. + virtual void Flush(Task* completion_task) = 0; + + protected: + PersistentStore(); + + private: + DISALLOW_COPY_AND_ASSIGN(PersistentStore); +}; + +} // namespace net + +#endif // NET_DEFAULT_ORIGIN_BOUND_CERT_STORE_H_ diff --git a/net/base/default_origin_bound_cert_store_unittest.cc b/net/base/default_origin_bound_cert_store_unittest.cc new file mode 100644 index 0000000..5fe77ef --- /dev/null +++ b/net/base/default_origin_bound_cert_store_unittest.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/default_origin_bound_cert_store.h" + +#include <map> +#include <string> +#include <vector> + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +class MockPersistentStore + : public DefaultOriginBoundCertStore::PersistentStore { + public: + MockPersistentStore(); + virtual ~MockPersistentStore(); + + // DefaultOriginBoundCertStore::PersistentStore implementation. + virtual bool Load( + std::vector<DefaultOriginBoundCertStore::OriginBoundCert*>* certs) + OVERRIDE; + virtual void AddOriginBoundCert( + const DefaultOriginBoundCertStore::OriginBoundCert& cert) OVERRIDE; + virtual void DeleteOriginBoundCert( + const DefaultOriginBoundCertStore::OriginBoundCert& cert) OVERRIDE; + virtual void SetClearLocalStateOnExit(bool clear_local_state) OVERRIDE; + virtual void Flush(Task* completion_task) OVERRIDE; + + private: + typedef std::map<std::string, DefaultOriginBoundCertStore::OriginBoundCert> + OriginBoundCertMap; + + OriginBoundCertMap origin_certs_; +}; + +MockPersistentStore::MockPersistentStore() {} + +MockPersistentStore::~MockPersistentStore() {} + +bool MockPersistentStore::Load( + std::vector<DefaultOriginBoundCertStore::OriginBoundCert*>* certs) { + OriginBoundCertMap::iterator it; + + for (it = origin_certs_.begin(); it != origin_certs_.end(); ++it) { + certs->push_back( + new DefaultOriginBoundCertStore::OriginBoundCert(it->second)); + } + + return true; +} + +void MockPersistentStore::AddOriginBoundCert( + const DefaultOriginBoundCertStore::OriginBoundCert& cert) { + origin_certs_[cert.origin()] = cert; +} + +void MockPersistentStore::DeleteOriginBoundCert( + const DefaultOriginBoundCertStore::OriginBoundCert& cert) { + origin_certs_.erase(cert.origin()); +} + +void MockPersistentStore::SetClearLocalStateOnExit(bool clear_local_state) {} + +void MockPersistentStore::Flush(Task* completion_task) { + NOTREACHED(); +} + +TEST(DefaultOriginBoundCertStoreTest, TestLoading) { + scoped_refptr<MockPersistentStore> persistent_store(new MockPersistentStore); + + persistent_store->AddOriginBoundCert( + DefaultOriginBoundCertStore::OriginBoundCert( + "https://encrypted.google.com/", "a", "b")); + persistent_store->AddOriginBoundCert( + DefaultOriginBoundCertStore::OriginBoundCert( + "https://www.verisign.com/", "c", "d")); + + // Make sure certs load properly. + scoped_ptr<DefaultOriginBoundCertStore> store( + new DefaultOriginBoundCertStore(persistent_store)); + EXPECT_EQ(2, store->GetCertCount()); + EXPECT_TRUE(store->SetOriginBoundCert("https://www.verisign.com/", "e", "f")); + EXPECT_EQ(2, store->GetCertCount()); + EXPECT_TRUE(store->SetOriginBoundCert("https://www.twitter.com/", "g", "h")); + EXPECT_EQ(3, store->GetCertCount()); +} + +TEST(DefaultOriginBoundCertStoreTest, TestSettingAndGetting) { + scoped_ptr<DefaultOriginBoundCertStore> store( + new DefaultOriginBoundCertStore(NULL)); + std::string private_key, cert; + EXPECT_EQ(0, store->GetCertCount()); + EXPECT_FALSE(store->GetOriginBoundCert("https://www.verisign.com/", + &private_key, + &cert)); + EXPECT_TRUE(private_key.empty()); + EXPECT_TRUE(cert.empty()); + EXPECT_TRUE(store->SetOriginBoundCert("https://www.verisign.com/", "i", "j")); + EXPECT_TRUE(store->GetOriginBoundCert("https://www.verisign.com/", + &private_key, + &cert)); + EXPECT_EQ("i", private_key); + EXPECT_EQ("j", cert); +} + +} // namespace net diff --git a/net/base/origin_bound_cert_service.cc b/net/base/origin_bound_cert_service.cc index 0d706bc..61a80eb 100644 --- a/net/base/origin_bound_cert_service.cc +++ b/net/base/origin_bound_cert_service.cc @@ -11,7 +11,6 @@ #include "base/memory/scoped_ptr.h" #include "base/rand_util.h" #include "crypto/rsa_private_key.h" -#include "googleurl/src/gurl.h" #include "net/base/origin_bound_cert_store.h" #include "net/base/x509_certificate.h" @@ -24,19 +23,23 @@ const int kValidityPeriodInDays = 365; } // namespace -bool OriginBoundCertService::GetOriginBoundCert(const GURL& url, +OriginBoundCertService::OriginBoundCertService( + OriginBoundCertStore* origin_bound_cert_store) + : origin_bound_cert_store_(origin_bound_cert_store) {} + +OriginBoundCertService::~OriginBoundCertService() {} + +bool OriginBoundCertService::GetOriginBoundCert(const std::string& origin, std::string* private_key_result, std::string* cert_result) { // Check if origin bound cert already exists for this origin. - if (origin_bound_cert_store_->HasOriginBoundCert(url)) { - return origin_bound_cert_store_->GetOriginBoundCert(url, - private_key_result, - cert_result); - } + if (origin_bound_cert_store_->GetOriginBoundCert(origin, + private_key_result, + cert_result)) + return true; // No origin bound cert exists, we have to create one. - std::string origin = url.GetOrigin().spec(); - std::string subject = "CN=origin-bound certificate for " + origin; + std::string subject = "CN=OBC"; scoped_ptr<crypto::RSAPrivateKey> key( crypto::RSAPrivateKey::Create(kKeySizeInBits)); if (!key.get()) { @@ -68,7 +71,9 @@ bool OriginBoundCertService::GetOriginBoundCert(const GURL& url, return false; } - if (!origin_bound_cert_store_->SetOriginBoundCert(url, key_out, der_cert)) { + if (!origin_bound_cert_store_->SetOriginBoundCert(origin, + key_out, + der_cert)) { LOG(WARNING) << "Unable to set origin bound certificate"; return false; } @@ -78,4 +83,8 @@ bool OriginBoundCertService::GetOriginBoundCert(const GURL& url, return true; } +int OriginBoundCertService::GetCertCount() { + return origin_bound_cert_store_->GetCertCount(); +} + } // namespace net diff --git a/net/base/origin_bound_cert_service.h b/net/base/origin_bound_cert_service.h index c1d65b9..9f700b9 100644 --- a/net/base/origin_bound_cert_service.h +++ b/net/base/origin_bound_cert_service.h @@ -8,30 +8,39 @@ #include <string> -class GURL; +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" namespace net { class OriginBoundCertStore; // A class for creating and fetching origin bound certs. -class OriginBoundCertService { +class OriginBoundCertService + : public base::RefCountedThreadSafe<OriginBoundCertService> { public: - explicit OriginBoundCertService(OriginBoundCertStore* origin_bound_cert_store) - : origin_bound_cert_store_(origin_bound_cert_store) {} + // This object owns origin_bound_cert_store. + explicit OriginBoundCertService( + OriginBoundCertStore* origin_bound_cert_store); + + ~OriginBoundCertService(); // TODO(rkn): Specify certificate type (RSA or DSA). // TODO(rkn): Key generation can be time consuming, so this should have an // asynchronous interface. // Fetches the origin bound cert for the specified origin if one exists // and creates one otherwise. On success, |private_key_result| stores a - // PrivateKeyInfo struct, and |cert_result| stores a DER-encoded certificate. - bool GetOriginBoundCert(const GURL& url, + // DER-encoded PrivateKeyInfo struct, and |cert_result| stores a DER-encoded + // certificate. + bool GetOriginBoundCert(const std::string& origin, std::string* private_key_result, std::string* cert_result); + // Public only for unit testing. + int GetCertCount(); + private: - OriginBoundCertStore* origin_bound_cert_store_; + scoped_ptr<OriginBoundCertStore> origin_bound_cert_store_; }; } // namespace net diff --git a/net/base/origin_bound_cert_service_unittest.cc b/net/base/origin_bound_cert_service_unittest.cc new file mode 100644 index 0000000..59be848 --- /dev/null +++ b/net/base/origin_bound_cert_service_unittest.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/origin_bound_cert_service.h" + +#include <string> +#include <vector> + +#include "base/memory/ref_counted.h" +#include "crypto/rsa_private_key.h" +#include "net/base/default_origin_bound_cert_store.h" +#include "net/base/x509_certificate.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +TEST(OriginBoundCertServiceTest, DuplicateCertTest) { + scoped_refptr<OriginBoundCertService> service( + new OriginBoundCertService(new DefaultOriginBoundCertStore(NULL))); + std::string origin1("https://encrypted.google.com/"); + std::string origin2("https://www.verisign.com/"); + + // The store should start out empty and should increment appropriately. + std::string private_key_info_1a, der_cert_1a; + EXPECT_EQ(0, service->GetCertCount()); + EXPECT_TRUE(service->GetOriginBoundCert( + origin1, &private_key_info_1a, &der_cert_1a)); + EXPECT_EQ(1, service->GetCertCount()); + + // We should get the same cert and key for the same origin. + std::string private_key_info_1b, der_cert_1b; + EXPECT_TRUE(service->GetOriginBoundCert( + origin1, &private_key_info_1b, &der_cert_1b)); + EXPECT_EQ(1, service->GetCertCount()); + EXPECT_EQ(private_key_info_1a, private_key_info_1b); + EXPECT_EQ(der_cert_1a, der_cert_1b); + + // We should get a different cert and key for different origins. + std::string private_key_info_2, der_cert_2; + EXPECT_TRUE(service->GetOriginBoundCert( + origin2, &private_key_info_2, &der_cert_2)); + EXPECT_EQ(2, service->GetCertCount()); + EXPECT_NE(private_key_info_1a, private_key_info_2); + EXPECT_NE(der_cert_1a, der_cert_2); +} + +TEST(OriginBoundCertServiceTest, ExtractValuesFromBytes) { + scoped_refptr<OriginBoundCertService> service( + new OriginBoundCertService(new DefaultOriginBoundCertStore(NULL))); + std::string origin("https://encrypted.google.com/"); + std::string private_key_info, der_cert; + EXPECT_TRUE(service->GetOriginBoundCert( + origin, &private_key_info, &der_cert)); + std::vector<uint8> key_vec(private_key_info.begin(), private_key_info.end()); + + // Check that we can retrieve the key pair from the bytes. + scoped_ptr<crypto::RSAPrivateKey> private_key( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_vec)); + EXPECT_TRUE(private_key != NULL); + + // Check that we can retrieve the cert from the bytes. + scoped_refptr<X509Certificate> x509cert( + X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size())); + EXPECT_TRUE(x509cert != NULL); +} + +} // namespace net diff --git a/net/base/origin_bound_cert_store.h b/net/base/origin_bound_cert_store.h index 8529fbd..63989f5 100644 --- a/net/base/origin_bound_cert_store.h +++ b/net/base/origin_bound_cert_store.h @@ -8,29 +8,38 @@ #include <string> -class GURL; - namespace net { // An interface for storing and retrieving origin bound certs. Origin bound // certificates are specified in // http://balfanz.github.com/tls-obc-spec/draft-balfanz-tls-obc-00.html. +// Owned only by a single OriginBoundCertService object, which is responsible +// for deleting it. + class OriginBoundCertStore { public: - virtual bool HasOriginBoundCert(const GURL& url) = 0; + virtual ~OriginBoundCertStore() {} // TODO(rkn): Specify certificate type (RSA or DSA). - // TODO(rkn): Key generation can be time consuming, so this should have an - // asynchronous interface. - // The output is stored in |private_key_result| and |cert_result|. - virtual bool GetOriginBoundCert(const GURL& url, + // TODO(rkn): File I/O may be required, so this should have an asynchronous + // interface. + // Returns true on success. |private_key_result| stores a DER-encoded + // PrivateKeyInfo struct and |cert_result| stores a DER-encoded + // certificate. Returns false if no origin bound cert exists for the + // specified origin. + virtual bool GetOriginBoundCert(const std::string& origin, std::string* private_key_result, std::string* cert_result) = 0; - virtual bool SetOriginBoundCert(const GURL& url, + // Adds an origin bound cert to the store. + virtual bool SetOriginBoundCert(const std::string& origin, const std::string& private_key, const std::string& cert) = 0; + + // Returns the number of certs in the store. + // Public only for unit testing. + virtual int GetCertCount() = 0; }; } // namespace net diff --git a/net/net.gyp b/net/net.gyp index 4ae27c0..de2b4df 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -67,6 +67,8 @@ 'base/crypto_module_openssl.cc', 'base/data_url.cc', 'base/data_url.h', + 'base/default_origin_bound_cert_store.cc', + 'base/default_origin_bound_cert_store.h', 'base/directory_lister.cc', 'base/directory_lister.h', 'base/dns_reload_timer.cc', @@ -877,6 +879,7 @@ 'base/cookie_monster_unittest.cc', 'base/crl_filter_unittest.cc', 'base/data_url_unittest.cc', + 'base/default_origin_bound_cert_store_unittest.cc', 'base/directory_lister_unittest.cc', 'base/dnssec_unittest.cc', 'base/dns_util_unittest.cc', @@ -900,6 +903,7 @@ 'base/net_log_unittest.cc', 'base/net_log_unittest.h', 'base/net_util_unittest.cc', + 'base/origin_bound_cert_service_unittest.cc', 'base/pem_tokenizer_unittest.cc', 'base/registry_controlled_domain_unittest.cc', 'base/run_all_unittests.cc', |