From 32cb7fb9cd0dbeec93af0be818f1bdcb04a323b2 Mon Sep 17 00:00:00 2001 From: "mattm@chromium.org" Date: Thu, 22 Mar 2012 22:41:11 +0000 Subject: Rename origin_bound_cert files to server_bound_cert. BUG=115348 TEST=try bots Review URL: https://chromiumcodereview.appspot.com/9814028 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@128323 0039d316-1c4b-4281-b951-d872f2087c98 --- net/base/default_origin_bound_cert_store.cc | 180 -------- net/base/default_origin_bound_cert_store.h | 160 ------- .../default_origin_bound_cert_store_unittest.cc | 294 ------------ net/base/default_server_bound_cert_store.cc | 180 ++++++++ net/base/default_server_bound_cert_store.h | 160 +++++++ .../default_server_bound_cert_store_unittest.cc | 294 ++++++++++++ net/base/origin_bound_cert_service.cc | 509 --------------------- net/base/origin_bound_cert_service.h | 135 ------ net/base/origin_bound_cert_service_unittest.cc | 418 ----------------- net/base/origin_bound_cert_store.cc | 29 -- net/base/origin_bound_cert_store.h | 117 ----- net/base/server_bound_cert_service.cc | 509 +++++++++++++++++++++ net/base/server_bound_cert_service.h | 135 ++++++ net/base/server_bound_cert_service_unittest.cc | 418 +++++++++++++++++ net/base/server_bound_cert_store.cc | 29 ++ net/base/server_bound_cert_store.h | 117 +++++ net/net.gyp | 16 +- net/socket/ssl_client_socket_nss.h | 2 +- net/spdy/spdy_http_stream_spdy2_unittest.cc | 2 +- net/spdy/spdy_http_stream_spdy3_unittest.cc | 2 +- net/spdy/spdy_session.cc | 2 +- net/spdy/spdy_stream.h | 2 +- net/url_request/url_request_context_storage.cc | 2 +- net/url_request/url_request_test_util.cc | 4 +- 24 files changed, 1858 insertions(+), 1858 deletions(-) delete mode 100644 net/base/default_origin_bound_cert_store.cc delete mode 100644 net/base/default_origin_bound_cert_store.h delete mode 100644 net/base/default_origin_bound_cert_store_unittest.cc create mode 100644 net/base/default_server_bound_cert_store.cc create mode 100644 net/base/default_server_bound_cert_store.h create mode 100644 net/base/default_server_bound_cert_store_unittest.cc delete mode 100644 net/base/origin_bound_cert_service.cc delete mode 100644 net/base/origin_bound_cert_service.h delete mode 100644 net/base/origin_bound_cert_service_unittest.cc delete mode 100644 net/base/origin_bound_cert_store.cc delete mode 100644 net/base/origin_bound_cert_store.h create mode 100644 net/base/server_bound_cert_service.cc create mode 100644 net/base/server_bound_cert_service.h create mode 100644 net/base/server_bound_cert_service_unittest.cc create mode 100644 net/base/server_bound_cert_store.cc create mode 100644 net/base/server_bound_cert_store.h (limited to 'net') diff --git a/net/base/default_origin_bound_cert_store.cc b/net/base/default_origin_bound_cert_store.cc deleted file mode 100644 index 3fd8c34..0000000 --- a/net/base/default_origin_bound_cert_store.cc +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) 2012 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/bind.h" -#include "base/message_loop.h" - -namespace net { - -// static -const size_t DefaultServerBoundCertStore::kMaxCerts = 3300; - -DefaultServerBoundCertStore::DefaultServerBoundCertStore( - PersistentStore* store) - : initialized_(false), - store_(store) {} - -void DefaultServerBoundCertStore::FlushStore( - const base::Closure& completion_task) { - base::AutoLock autolock(lock_); - - if (initialized_ && store_) - store_->Flush(completion_task); - else if (!completion_task.is_null()) - MessageLoop::current()->PostTask(FROM_HERE, completion_task); -} - -bool DefaultServerBoundCertStore::GetServerBoundCert( - const std::string& server_identifier, - SSLClientCertType* type, - base::Time* creation_time, - base::Time* expiration_time, - std::string* private_key_result, - std::string* cert_result) { - base::AutoLock autolock(lock_); - InitIfNecessary(); - - ServerBoundCertMap::iterator it = server_bound_certs_.find(server_identifier); - - if (it == server_bound_certs_.end()) - return false; - - ServerBoundCert* cert = it->second; - *type = cert->type(); - *creation_time = cert->creation_time(); - *expiration_time = cert->expiration_time(); - *private_key_result = cert->private_key(); - *cert_result = cert->cert(); - - return true; -} - -void DefaultServerBoundCertStore::SetServerBoundCert( - const std::string& server_identifier, - SSLClientCertType type, - base::Time creation_time, - base::Time expiration_time, - const std::string& private_key, - const std::string& cert) { - base::AutoLock autolock(lock_); - InitIfNecessary(); - - InternalDeleteServerBoundCert(server_identifier); - InternalInsertServerBoundCert( - server_identifier, - new ServerBoundCert( - server_identifier, type, creation_time, expiration_time, private_key, - cert)); -} - -void DefaultServerBoundCertStore::DeleteServerBoundCert( - const std::string& server_identifier) { - base::AutoLock autolock(lock_); - InitIfNecessary(); - InternalDeleteServerBoundCert(server_identifier); -} - -void DefaultServerBoundCertStore::DeleteAllCreatedBetween( - base::Time delete_begin, - base::Time delete_end) { - base::AutoLock autolock(lock_); - InitIfNecessary(); - for (ServerBoundCertMap::iterator it = server_bound_certs_.begin(); - it != server_bound_certs_.end();) { - ServerBoundCertMap::iterator cur = it; - ++it; - ServerBoundCert* cert = cur->second; - if ((delete_begin.is_null() || cert->creation_time() >= delete_begin) && - (delete_end.is_null() || cert->creation_time() < delete_end)) { - if (store_) - store_->DeleteServerBoundCert(*cert); - delete cert; - server_bound_certs_.erase(cur); - } - } -} - -void DefaultServerBoundCertStore::DeleteAll() { - DeleteAllCreatedBetween(base::Time(), base::Time()); -} - -void DefaultServerBoundCertStore::GetAllServerBoundCerts( - std::vector* server_bound_certs) { - base::AutoLock autolock(lock_); - InitIfNecessary(); - for (ServerBoundCertMap::iterator it = server_bound_certs_.begin(); - it != server_bound_certs_.end(); ++it) { - server_bound_certs->push_back(*it->second); - } -} - -int DefaultServerBoundCertStore::GetCertCount() { - base::AutoLock autolock(lock_); - InitIfNecessary(); - - return server_bound_certs_.size(); -} - -DefaultServerBoundCertStore::~DefaultServerBoundCertStore() { - DeleteAllInMemory(); -} - -void DefaultServerBoundCertStore::DeleteAllInMemory() { - base::AutoLock autolock(lock_); - - for (ServerBoundCertMap::iterator it = server_bound_certs_.begin(); - it != server_bound_certs_.end(); ++it) { - delete it->second; - } - server_bound_certs_.clear(); -} - -void DefaultServerBoundCertStore::InitStore() { - lock_.AssertAcquired(); - - DCHECK(store_) << "Store must exist to initialize"; - - // Initialize the store and sync in any saved persistent certs. - std::vector 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::const_iterator it = certs.begin(); - it != certs.end(); ++it) { - server_bound_certs_[(*it)->server_identifier()] = *it; - } -} - -void DefaultServerBoundCertStore::InternalDeleteServerBoundCert( - const std::string& server_identifier) { - lock_.AssertAcquired(); - - ServerBoundCertMap::iterator it = server_bound_certs_.find(server_identifier); - if (it == server_bound_certs_.end()) - return; // There is nothing to delete. - - ServerBoundCert* cert = it->second; - if (store_) - store_->DeleteServerBoundCert(*cert); - server_bound_certs_.erase(it); - delete cert; -} - -void DefaultServerBoundCertStore::InternalInsertServerBoundCert( - const std::string& server_identifier, - ServerBoundCert* cert) { - lock_.AssertAcquired(); - - if (store_) - store_->AddServerBoundCert(*cert); - server_bound_certs_[server_identifier] = cert; -} - -DefaultServerBoundCertStore::PersistentStore::PersistentStore() {} - -} // namespace net diff --git a/net/base/default_origin_bound_cert_store.h b/net/base/default_origin_bound_cert_store.h deleted file mode 100644 index 81a6f05..0000000 --- a/net/base/default_origin_bound_cert_store.h +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2012 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 -#include -#include - -#include "base/callback_forward.h" -#include "base/compiler_specific.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "net/base/net_export.h" -#include "net/base/origin_bound_cert_store.h" - -class Task; - -namespace net { - -// This class is the system for storing and retrieving server bound certs. -// Modeled after the CookieMonster class, it has an in-memory cert store, -// and synchronizes server 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 server bound cert management UI. -class NET_EXPORT DefaultServerBoundCertStore : public ServerBoundCertStore { - public: - class PersistentStore; - - // The key for each ServerBoundCert* in ServerBoundCertMap is the - // corresponding server. - typedef std::map ServerBoundCertMap; - - // 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 - // DefaultServerBoundCertStore's existence. If |store| is NULL, then no - // backing store will be updated. - explicit DefaultServerBoundCertStore(PersistentStore* store); - - virtual ~DefaultServerBoundCertStore(); - - // 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(const base::Closure& completion_task); - - // ServerBoundCertStore implementation. - virtual bool GetServerBoundCert( - const std::string& server_identifier, - SSLClientCertType* type, - base::Time* creation_time, - base::Time* expiration_time, - std::string* private_key_result, - std::string* cert_result) OVERRIDE; - virtual void SetServerBoundCert( - const std::string& server_identifier, - SSLClientCertType type, - base::Time creation_time, - base::Time expiration_time, - const std::string& private_key, - const std::string& cert) OVERRIDE; - virtual void DeleteServerBoundCert(const std::string& server_identifier) - OVERRIDE; - virtual void DeleteAllCreatedBetween(base::Time delete_begin, - base::Time delete_end) OVERRIDE; - virtual void DeleteAll() OVERRIDE; - virtual void GetAllServerBoundCerts( - std::vector* server_bound_certs) OVERRIDE; - virtual int GetCertCount() OVERRIDE; - - private: - static const size_t kMaxCerts; - - // Deletes all of the certs. Does not delete them from |store_|. - void DeleteAllInMemory(); - - // 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 server, if such a cert exists, from the - // in-memory store. Deletes it from |store_| if |store_| is not NULL. - void InternalDeleteServerBoundCert(const std::string& server); - - // Takes ownership of *cert. - // Adds the cert for the specified server to the in-memory store. Deletes it - // from |store_| if |store_| is not NULL. - void InternalInsertServerBoundCert(const std::string& server_identifier, - ServerBoundCert* cert); - - // Indicates whether the cert store has been initialized. This happens - // Lazily in InitStoreIfNecessary(). - bool initialized_; - - scoped_refptr store_; - - ServerBoundCertMap server_bound_certs_; - - // Lock for thread-safety - base::Lock lock_; - - DISALLOW_COPY_AND_ASSIGN(DefaultServerBoundCertStore); -}; - -typedef base::RefCountedThreadSafe - RefcountedPersistentStore; - -class NET_EXPORT DefaultServerBoundCertStore::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* certs) = 0; - - virtual void AddServerBoundCert(const ServerBoundCert& cert) = 0; - - virtual void DeleteServerBoundCert(const ServerBoundCert& 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(const base::Closure& 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 deleted file mode 100644 index 4e8628f..0000000 --- a/net/base/default_origin_bound_cert_store_unittest.cc +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright (c) 2012 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 -#include -#include - -#include "base/bind.h" -#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 DefaultServerBoundCertStore::PersistentStore { - public: - MockPersistentStore(); - virtual ~MockPersistentStore(); - - // DefaultServerBoundCertStore::PersistentStore implementation. - virtual bool Load( - std::vector* certs) - OVERRIDE; - virtual void AddServerBoundCert( - const DefaultServerBoundCertStore::ServerBoundCert& cert) OVERRIDE; - virtual void DeleteServerBoundCert( - const DefaultServerBoundCertStore::ServerBoundCert& cert) OVERRIDE; - virtual void SetClearLocalStateOnExit(bool clear_local_state) OVERRIDE; - virtual void Flush(const base::Closure& completion_task) OVERRIDE; - - private: - typedef std::map - ServerBoundCertMap; - - ServerBoundCertMap origin_certs_; -}; - -MockPersistentStore::MockPersistentStore() {} - -MockPersistentStore::~MockPersistentStore() {} - -bool MockPersistentStore::Load( - std::vector* certs) { - ServerBoundCertMap::iterator it; - - for (it = origin_certs_.begin(); it != origin_certs_.end(); ++it) { - certs->push_back( - new DefaultServerBoundCertStore::ServerBoundCert(it->second)); - } - - return true; -} - -void MockPersistentStore::AddServerBoundCert( - const DefaultServerBoundCertStore::ServerBoundCert& cert) { - origin_certs_[cert.server_identifier()] = cert; -} - -void MockPersistentStore::DeleteServerBoundCert( - const DefaultServerBoundCertStore::ServerBoundCert& cert) { - origin_certs_.erase(cert.server_identifier()); -} - -void MockPersistentStore::SetClearLocalStateOnExit(bool clear_local_state) {} - -void MockPersistentStore::Flush(const base::Closure& completion_task) { - NOTREACHED(); -} - -TEST(DefaultServerBoundCertStoreTest, TestLoading) { - scoped_refptr persistent_store(new MockPersistentStore); - - persistent_store->AddServerBoundCert( - DefaultServerBoundCertStore::ServerBoundCert( - "google.com", - CLIENT_CERT_RSA_SIGN, - base::Time(), - base::Time(), - "a", "b")); - persistent_store->AddServerBoundCert( - DefaultServerBoundCertStore::ServerBoundCert( - "verisign.com", - CLIENT_CERT_ECDSA_SIGN, - base::Time(), - base::Time(), - "c", "d")); - - // Make sure certs load properly. - DefaultServerBoundCertStore store(persistent_store.get()); - EXPECT_EQ(2, store.GetCertCount()); - store.SetServerBoundCert( - "verisign.com", - CLIENT_CERT_RSA_SIGN, - base::Time(), - base::Time(), - "e", "f"); - EXPECT_EQ(2, store.GetCertCount()); - store.SetServerBoundCert( - "twitter.com", - CLIENT_CERT_RSA_SIGN, - base::Time(), - base::Time(), - "g", "h"); - EXPECT_EQ(3, store.GetCertCount()); -} - -TEST(DefaultServerBoundCertStoreTest, TestSettingAndGetting) { - DefaultServerBoundCertStore store(NULL); - SSLClientCertType type; - base::Time creation_time; - base::Time expiration_time; - std::string private_key, cert; - EXPECT_EQ(0, store.GetCertCount()); - EXPECT_FALSE(store.GetServerBoundCert("verisign.com", - &type, - &creation_time, - &expiration_time, - &private_key, - &cert)); - EXPECT_TRUE(private_key.empty()); - EXPECT_TRUE(cert.empty()); - store.SetServerBoundCert( - "verisign.com", - CLIENT_CERT_RSA_SIGN, - base::Time::FromInternalValue(123), - base::Time::FromInternalValue(456), - "i", "j"); - EXPECT_TRUE(store.GetServerBoundCert("verisign.com", - &type, - &creation_time, - &expiration_time, - &private_key, - &cert)); - EXPECT_EQ(CLIENT_CERT_RSA_SIGN, type); - EXPECT_EQ(123, creation_time.ToInternalValue()); - EXPECT_EQ(456, expiration_time.ToInternalValue()); - EXPECT_EQ("i", private_key); - EXPECT_EQ("j", cert); -} - -TEST(DefaultServerBoundCertStoreTest, TestDuplicateCerts) { - scoped_refptr persistent_store(new MockPersistentStore); - DefaultServerBoundCertStore store(persistent_store.get()); - - SSLClientCertType type; - base::Time creation_time; - base::Time expiration_time; - std::string private_key, cert; - EXPECT_EQ(0, store.GetCertCount()); - store.SetServerBoundCert( - "verisign.com", - CLIENT_CERT_RSA_SIGN, - base::Time::FromInternalValue(123), - base::Time::FromInternalValue(1234), - "a", "b"); - store.SetServerBoundCert( - "verisign.com", - CLIENT_CERT_ECDSA_SIGN, - base::Time::FromInternalValue(456), - base::Time::FromInternalValue(4567), - "c", "d"); - - EXPECT_EQ(1, store.GetCertCount()); - EXPECT_TRUE(store.GetServerBoundCert("verisign.com", - &type, - &creation_time, - &expiration_time, - &private_key, - &cert)); - EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type); - EXPECT_EQ(456, creation_time.ToInternalValue()); - EXPECT_EQ(4567, expiration_time.ToInternalValue()); - EXPECT_EQ("c", private_key); - EXPECT_EQ("d", cert); -} - -TEST(DefaultServerBoundCertStoreTest, TestDeleteAll) { - scoped_refptr persistent_store(new MockPersistentStore); - DefaultServerBoundCertStore store(persistent_store.get()); - - EXPECT_EQ(0, store.GetCertCount()); - store.SetServerBoundCert( - "verisign.com", - CLIENT_CERT_RSA_SIGN, - base::Time(), - base::Time(), - "a", "b"); - store.SetServerBoundCert( - "google.com", - CLIENT_CERT_RSA_SIGN, - base::Time(), - base::Time(), - "c", "d"); - store.SetServerBoundCert( - "harvard.com", - CLIENT_CERT_RSA_SIGN, - base::Time(), - base::Time(), - "e", "f"); - - EXPECT_EQ(3, store.GetCertCount()); - store.DeleteAll(); - EXPECT_EQ(0, store.GetCertCount()); -} - -TEST(DefaultServerBoundCertStoreTest, TestDelete) { - scoped_refptr persistent_store(new MockPersistentStore); - DefaultServerBoundCertStore store(persistent_store.get()); - - SSLClientCertType type; - base::Time creation_time; - base::Time expiration_time; - std::string private_key, cert; - EXPECT_EQ(0, store.GetCertCount()); - store.SetServerBoundCert( - "verisign.com", - CLIENT_CERT_RSA_SIGN, - base::Time(), - base::Time(), - "a", "b"); - store.SetServerBoundCert( - "google.com", - CLIENT_CERT_ECDSA_SIGN, - base::Time(), - base::Time(), - "c", "d"); - - EXPECT_EQ(2, store.GetCertCount()); - store.DeleteServerBoundCert("verisign.com"); - EXPECT_EQ(1, store.GetCertCount()); - EXPECT_FALSE(store.GetServerBoundCert("verisign.com", - &type, - &creation_time, - &expiration_time, - &private_key, - &cert)); - EXPECT_TRUE(store.GetServerBoundCert("google.com", - &type, - &creation_time, - &expiration_time, - &private_key, - &cert)); - store.DeleteServerBoundCert("google.com"); - EXPECT_EQ(0, store.GetCertCount()); - EXPECT_FALSE(store.GetServerBoundCert("google.com", - &type, - &creation_time, - &expiration_time, - &private_key, - &cert)); -} - -TEST(DefaultServerBoundCertStoreTest, TestGetAll) { - scoped_refptr persistent_store(new MockPersistentStore); - DefaultServerBoundCertStore store(persistent_store.get()); - - EXPECT_EQ(0, store.GetCertCount()); - store.SetServerBoundCert( - "verisign.com", - CLIENT_CERT_RSA_SIGN, - base::Time(), - base::Time(), - "a", "b"); - store.SetServerBoundCert( - "google.com", - CLIENT_CERT_ECDSA_SIGN, - base::Time(), - base::Time(), - "c", "d"); - store.SetServerBoundCert( - "harvard.com", - CLIENT_CERT_RSA_SIGN, - base::Time(), - base::Time(), - "e", "f"); - store.SetServerBoundCert( - "mit.com", - CLIENT_CERT_RSA_SIGN, - base::Time(), - base::Time(), - "g", "h"); - - EXPECT_EQ(4, store.GetCertCount()); - std::vector certs; - store.GetAllServerBoundCerts(&certs); - EXPECT_EQ(4u, certs.size()); -} - -} // namespace net diff --git a/net/base/default_server_bound_cert_store.cc b/net/base/default_server_bound_cert_store.cc new file mode 100644 index 0000000..180792a --- /dev/null +++ b/net/base/default_server_bound_cert_store.cc @@ -0,0 +1,180 @@ +// Copyright (c) 2012 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_server_bound_cert_store.h" + +#include "base/bind.h" +#include "base/message_loop.h" + +namespace net { + +// static +const size_t DefaultServerBoundCertStore::kMaxCerts = 3300; + +DefaultServerBoundCertStore::DefaultServerBoundCertStore( + PersistentStore* store) + : initialized_(false), + store_(store) {} + +void DefaultServerBoundCertStore::FlushStore( + const base::Closure& completion_task) { + base::AutoLock autolock(lock_); + + if (initialized_ && store_) + store_->Flush(completion_task); + else if (!completion_task.is_null()) + MessageLoop::current()->PostTask(FROM_HERE, completion_task); +} + +bool DefaultServerBoundCertStore::GetServerBoundCert( + const std::string& server_identifier, + SSLClientCertType* type, + base::Time* creation_time, + base::Time* expiration_time, + std::string* private_key_result, + std::string* cert_result) { + base::AutoLock autolock(lock_); + InitIfNecessary(); + + ServerBoundCertMap::iterator it = server_bound_certs_.find(server_identifier); + + if (it == server_bound_certs_.end()) + return false; + + ServerBoundCert* cert = it->second; + *type = cert->type(); + *creation_time = cert->creation_time(); + *expiration_time = cert->expiration_time(); + *private_key_result = cert->private_key(); + *cert_result = cert->cert(); + + return true; +} + +void DefaultServerBoundCertStore::SetServerBoundCert( + const std::string& server_identifier, + SSLClientCertType type, + base::Time creation_time, + base::Time expiration_time, + const std::string& private_key, + const std::string& cert) { + base::AutoLock autolock(lock_); + InitIfNecessary(); + + InternalDeleteServerBoundCert(server_identifier); + InternalInsertServerBoundCert( + server_identifier, + new ServerBoundCert( + server_identifier, type, creation_time, expiration_time, private_key, + cert)); +} + +void DefaultServerBoundCertStore::DeleteServerBoundCert( + const std::string& server_identifier) { + base::AutoLock autolock(lock_); + InitIfNecessary(); + InternalDeleteServerBoundCert(server_identifier); +} + +void DefaultServerBoundCertStore::DeleteAllCreatedBetween( + base::Time delete_begin, + base::Time delete_end) { + base::AutoLock autolock(lock_); + InitIfNecessary(); + for (ServerBoundCertMap::iterator it = server_bound_certs_.begin(); + it != server_bound_certs_.end();) { + ServerBoundCertMap::iterator cur = it; + ++it; + ServerBoundCert* cert = cur->second; + if ((delete_begin.is_null() || cert->creation_time() >= delete_begin) && + (delete_end.is_null() || cert->creation_time() < delete_end)) { + if (store_) + store_->DeleteServerBoundCert(*cert); + delete cert; + server_bound_certs_.erase(cur); + } + } +} + +void DefaultServerBoundCertStore::DeleteAll() { + DeleteAllCreatedBetween(base::Time(), base::Time()); +} + +void DefaultServerBoundCertStore::GetAllServerBoundCerts( + std::vector* server_bound_certs) { + base::AutoLock autolock(lock_); + InitIfNecessary(); + for (ServerBoundCertMap::iterator it = server_bound_certs_.begin(); + it != server_bound_certs_.end(); ++it) { + server_bound_certs->push_back(*it->second); + } +} + +int DefaultServerBoundCertStore::GetCertCount() { + base::AutoLock autolock(lock_); + InitIfNecessary(); + + return server_bound_certs_.size(); +} + +DefaultServerBoundCertStore::~DefaultServerBoundCertStore() { + DeleteAllInMemory(); +} + +void DefaultServerBoundCertStore::DeleteAllInMemory() { + base::AutoLock autolock(lock_); + + for (ServerBoundCertMap::iterator it = server_bound_certs_.begin(); + it != server_bound_certs_.end(); ++it) { + delete it->second; + } + server_bound_certs_.clear(); +} + +void DefaultServerBoundCertStore::InitStore() { + lock_.AssertAcquired(); + + DCHECK(store_) << "Store must exist to initialize"; + + // Initialize the store and sync in any saved persistent certs. + std::vector 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::const_iterator it = certs.begin(); + it != certs.end(); ++it) { + server_bound_certs_[(*it)->server_identifier()] = *it; + } +} + +void DefaultServerBoundCertStore::InternalDeleteServerBoundCert( + const std::string& server_identifier) { + lock_.AssertAcquired(); + + ServerBoundCertMap::iterator it = server_bound_certs_.find(server_identifier); + if (it == server_bound_certs_.end()) + return; // There is nothing to delete. + + ServerBoundCert* cert = it->second; + if (store_) + store_->DeleteServerBoundCert(*cert); + server_bound_certs_.erase(it); + delete cert; +} + +void DefaultServerBoundCertStore::InternalInsertServerBoundCert( + const std::string& server_identifier, + ServerBoundCert* cert) { + lock_.AssertAcquired(); + + if (store_) + store_->AddServerBoundCert(*cert); + server_bound_certs_[server_identifier] = cert; +} + +DefaultServerBoundCertStore::PersistentStore::PersistentStore() {} + +} // namespace net diff --git a/net/base/default_server_bound_cert_store.h b/net/base/default_server_bound_cert_store.h new file mode 100644 index 0000000..1ba7090d --- /dev/null +++ b/net/base/default_server_bound_cert_store.h @@ -0,0 +1,160 @@ +// Copyright (c) 2012 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_SERVER_BOUND_CERT_STORE_H_ +#define NET_BASE_DEFAULT_SERVER_BOUND_CERT_STORE_H_ +#pragma once + +#include +#include +#include + +#include "base/callback_forward.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "net/base/net_export.h" +#include "net/base/server_bound_cert_store.h" + +class Task; + +namespace net { + +// This class is the system for storing and retrieving server bound certs. +// Modeled after the CookieMonster class, it has an in-memory cert store, +// and synchronizes server 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 server bound cert management UI. +class NET_EXPORT DefaultServerBoundCertStore : public ServerBoundCertStore { + public: + class PersistentStore; + + // The key for each ServerBoundCert* in ServerBoundCertMap is the + // corresponding server. + typedef std::map ServerBoundCertMap; + + // 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 + // DefaultServerBoundCertStore's existence. If |store| is NULL, then no + // backing store will be updated. + explicit DefaultServerBoundCertStore(PersistentStore* store); + + virtual ~DefaultServerBoundCertStore(); + + // 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(const base::Closure& completion_task); + + // ServerBoundCertStore implementation. + virtual bool GetServerBoundCert( + const std::string& server_identifier, + SSLClientCertType* type, + base::Time* creation_time, + base::Time* expiration_time, + std::string* private_key_result, + std::string* cert_result) OVERRIDE; + virtual void SetServerBoundCert( + const std::string& server_identifier, + SSLClientCertType type, + base::Time creation_time, + base::Time expiration_time, + const std::string& private_key, + const std::string& cert) OVERRIDE; + virtual void DeleteServerBoundCert(const std::string& server_identifier) + OVERRIDE; + virtual void DeleteAllCreatedBetween(base::Time delete_begin, + base::Time delete_end) OVERRIDE; + virtual void DeleteAll() OVERRIDE; + virtual void GetAllServerBoundCerts( + std::vector* server_bound_certs) OVERRIDE; + virtual int GetCertCount() OVERRIDE; + + private: + static const size_t kMaxCerts; + + // Deletes all of the certs. Does not delete them from |store_|. + void DeleteAllInMemory(); + + // 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 server, if such a cert exists, from the + // in-memory store. Deletes it from |store_| if |store_| is not NULL. + void InternalDeleteServerBoundCert(const std::string& server); + + // Takes ownership of *cert. + // Adds the cert for the specified server to the in-memory store. Deletes it + // from |store_| if |store_| is not NULL. + void InternalInsertServerBoundCert(const std::string& server_identifier, + ServerBoundCert* cert); + + // Indicates whether the cert store has been initialized. This happens + // Lazily in InitStoreIfNecessary(). + bool initialized_; + + scoped_refptr store_; + + ServerBoundCertMap server_bound_certs_; + + // Lock for thread-safety + base::Lock lock_; + + DISALLOW_COPY_AND_ASSIGN(DefaultServerBoundCertStore); +}; + +typedef base::RefCountedThreadSafe + RefcountedPersistentStore; + +class NET_EXPORT DefaultServerBoundCertStore::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* certs) = 0; + + virtual void AddServerBoundCert(const ServerBoundCert& cert) = 0; + + virtual void DeleteServerBoundCert(const ServerBoundCert& 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(const base::Closure& 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_server_bound_cert_store_unittest.cc b/net/base/default_server_bound_cert_store_unittest.cc new file mode 100644 index 0000000..5fbe4e2 --- /dev/null +++ b/net/base/default_server_bound_cert_store_unittest.cc @@ -0,0 +1,294 @@ +// Copyright (c) 2012 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_server_bound_cert_store.h" + +#include +#include +#include + +#include "base/bind.h" +#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 DefaultServerBoundCertStore::PersistentStore { + public: + MockPersistentStore(); + virtual ~MockPersistentStore(); + + // DefaultServerBoundCertStore::PersistentStore implementation. + virtual bool Load( + std::vector* certs) + OVERRIDE; + virtual void AddServerBoundCert( + const DefaultServerBoundCertStore::ServerBoundCert& cert) OVERRIDE; + virtual void DeleteServerBoundCert( + const DefaultServerBoundCertStore::ServerBoundCert& cert) OVERRIDE; + virtual void SetClearLocalStateOnExit(bool clear_local_state) OVERRIDE; + virtual void Flush(const base::Closure& completion_task) OVERRIDE; + + private: + typedef std::map + ServerBoundCertMap; + + ServerBoundCertMap origin_certs_; +}; + +MockPersistentStore::MockPersistentStore() {} + +MockPersistentStore::~MockPersistentStore() {} + +bool MockPersistentStore::Load( + std::vector* certs) { + ServerBoundCertMap::iterator it; + + for (it = origin_certs_.begin(); it != origin_certs_.end(); ++it) { + certs->push_back( + new DefaultServerBoundCertStore::ServerBoundCert(it->second)); + } + + return true; +} + +void MockPersistentStore::AddServerBoundCert( + const DefaultServerBoundCertStore::ServerBoundCert& cert) { + origin_certs_[cert.server_identifier()] = cert; +} + +void MockPersistentStore::DeleteServerBoundCert( + const DefaultServerBoundCertStore::ServerBoundCert& cert) { + origin_certs_.erase(cert.server_identifier()); +} + +void MockPersistentStore::SetClearLocalStateOnExit(bool clear_local_state) {} + +void MockPersistentStore::Flush(const base::Closure& completion_task) { + NOTREACHED(); +} + +TEST(DefaultServerBoundCertStoreTest, TestLoading) { + scoped_refptr persistent_store(new MockPersistentStore); + + persistent_store->AddServerBoundCert( + DefaultServerBoundCertStore::ServerBoundCert( + "google.com", + CLIENT_CERT_RSA_SIGN, + base::Time(), + base::Time(), + "a", "b")); + persistent_store->AddServerBoundCert( + DefaultServerBoundCertStore::ServerBoundCert( + "verisign.com", + CLIENT_CERT_ECDSA_SIGN, + base::Time(), + base::Time(), + "c", "d")); + + // Make sure certs load properly. + DefaultServerBoundCertStore store(persistent_store.get()); + EXPECT_EQ(2, store.GetCertCount()); + store.SetServerBoundCert( + "verisign.com", + CLIENT_CERT_RSA_SIGN, + base::Time(), + base::Time(), + "e", "f"); + EXPECT_EQ(2, store.GetCertCount()); + store.SetServerBoundCert( + "twitter.com", + CLIENT_CERT_RSA_SIGN, + base::Time(), + base::Time(), + "g", "h"); + EXPECT_EQ(3, store.GetCertCount()); +} + +TEST(DefaultServerBoundCertStoreTest, TestSettingAndGetting) { + DefaultServerBoundCertStore store(NULL); + SSLClientCertType type; + base::Time creation_time; + base::Time expiration_time; + std::string private_key, cert; + EXPECT_EQ(0, store.GetCertCount()); + EXPECT_FALSE(store.GetServerBoundCert("verisign.com", + &type, + &creation_time, + &expiration_time, + &private_key, + &cert)); + EXPECT_TRUE(private_key.empty()); + EXPECT_TRUE(cert.empty()); + store.SetServerBoundCert( + "verisign.com", + CLIENT_CERT_RSA_SIGN, + base::Time::FromInternalValue(123), + base::Time::FromInternalValue(456), + "i", "j"); + EXPECT_TRUE(store.GetServerBoundCert("verisign.com", + &type, + &creation_time, + &expiration_time, + &private_key, + &cert)); + EXPECT_EQ(CLIENT_CERT_RSA_SIGN, type); + EXPECT_EQ(123, creation_time.ToInternalValue()); + EXPECT_EQ(456, expiration_time.ToInternalValue()); + EXPECT_EQ("i", private_key); + EXPECT_EQ("j", cert); +} + +TEST(DefaultServerBoundCertStoreTest, TestDuplicateCerts) { + scoped_refptr persistent_store(new MockPersistentStore); + DefaultServerBoundCertStore store(persistent_store.get()); + + SSLClientCertType type; + base::Time creation_time; + base::Time expiration_time; + std::string private_key, cert; + EXPECT_EQ(0, store.GetCertCount()); + store.SetServerBoundCert( + "verisign.com", + CLIENT_CERT_RSA_SIGN, + base::Time::FromInternalValue(123), + base::Time::FromInternalValue(1234), + "a", "b"); + store.SetServerBoundCert( + "verisign.com", + CLIENT_CERT_ECDSA_SIGN, + base::Time::FromInternalValue(456), + base::Time::FromInternalValue(4567), + "c", "d"); + + EXPECT_EQ(1, store.GetCertCount()); + EXPECT_TRUE(store.GetServerBoundCert("verisign.com", + &type, + &creation_time, + &expiration_time, + &private_key, + &cert)); + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type); + EXPECT_EQ(456, creation_time.ToInternalValue()); + EXPECT_EQ(4567, expiration_time.ToInternalValue()); + EXPECT_EQ("c", private_key); + EXPECT_EQ("d", cert); +} + +TEST(DefaultServerBoundCertStoreTest, TestDeleteAll) { + scoped_refptr persistent_store(new MockPersistentStore); + DefaultServerBoundCertStore store(persistent_store.get()); + + EXPECT_EQ(0, store.GetCertCount()); + store.SetServerBoundCert( + "verisign.com", + CLIENT_CERT_RSA_SIGN, + base::Time(), + base::Time(), + "a", "b"); + store.SetServerBoundCert( + "google.com", + CLIENT_CERT_RSA_SIGN, + base::Time(), + base::Time(), + "c", "d"); + store.SetServerBoundCert( + "harvard.com", + CLIENT_CERT_RSA_SIGN, + base::Time(), + base::Time(), + "e", "f"); + + EXPECT_EQ(3, store.GetCertCount()); + store.DeleteAll(); + EXPECT_EQ(0, store.GetCertCount()); +} + +TEST(DefaultServerBoundCertStoreTest, TestDelete) { + scoped_refptr persistent_store(new MockPersistentStore); + DefaultServerBoundCertStore store(persistent_store.get()); + + SSLClientCertType type; + base::Time creation_time; + base::Time expiration_time; + std::string private_key, cert; + EXPECT_EQ(0, store.GetCertCount()); + store.SetServerBoundCert( + "verisign.com", + CLIENT_CERT_RSA_SIGN, + base::Time(), + base::Time(), + "a", "b"); + store.SetServerBoundCert( + "google.com", + CLIENT_CERT_ECDSA_SIGN, + base::Time(), + base::Time(), + "c", "d"); + + EXPECT_EQ(2, store.GetCertCount()); + store.DeleteServerBoundCert("verisign.com"); + EXPECT_EQ(1, store.GetCertCount()); + EXPECT_FALSE(store.GetServerBoundCert("verisign.com", + &type, + &creation_time, + &expiration_time, + &private_key, + &cert)); + EXPECT_TRUE(store.GetServerBoundCert("google.com", + &type, + &creation_time, + &expiration_time, + &private_key, + &cert)); + store.DeleteServerBoundCert("google.com"); + EXPECT_EQ(0, store.GetCertCount()); + EXPECT_FALSE(store.GetServerBoundCert("google.com", + &type, + &creation_time, + &expiration_time, + &private_key, + &cert)); +} + +TEST(DefaultServerBoundCertStoreTest, TestGetAll) { + scoped_refptr persistent_store(new MockPersistentStore); + DefaultServerBoundCertStore store(persistent_store.get()); + + EXPECT_EQ(0, store.GetCertCount()); + store.SetServerBoundCert( + "verisign.com", + CLIENT_CERT_RSA_SIGN, + base::Time(), + base::Time(), + "a", "b"); + store.SetServerBoundCert( + "google.com", + CLIENT_CERT_ECDSA_SIGN, + base::Time(), + base::Time(), + "c", "d"); + store.SetServerBoundCert( + "harvard.com", + CLIENT_CERT_RSA_SIGN, + base::Time(), + base::Time(), + "e", "f"); + store.SetServerBoundCert( + "mit.com", + CLIENT_CERT_RSA_SIGN, + base::Time(), + base::Time(), + "g", "h"); + + EXPECT_EQ(4, store.GetCertCount()); + std::vector certs; + store.GetAllServerBoundCerts(&certs); + EXPECT_EQ(4u, certs.size()); +} + +} // namespace net diff --git a/net/base/origin_bound_cert_service.cc b/net/base/origin_bound_cert_service.cc deleted file mode 100644 index b380645..0000000 --- a/net/base/origin_bound_cert_service.cc +++ /dev/null @@ -1,509 +0,0 @@ -// Copyright (c) 2012 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 -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/compiler_specific.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop.h" -#include "base/rand_util.h" -#include "base/stl_util.h" -#include "base/threading/worker_pool.h" -#include "crypto/ec_private_key.h" -#include "googleurl/src/gurl.h" -#include "net/base/net_errors.h" -#include "net/base/origin_bound_cert_store.h" -#include "net/base/registry_controlled_domain.h" -#include "net/base/x509_certificate.h" -#include "net/base/x509_util.h" - -#if defined(USE_NSS) -#include // PR_DetachThread -#endif - -namespace net { - -namespace { - -const int kKeySizeInBits = 1024; -const int kValidityPeriodInDays = 365; - -bool IsSupportedCertType(uint8 type) { - switch(type) { - case CLIENT_CERT_ECDSA_SIGN: - return true; - default: - return false; - } -} - -} // namespace - -// Represents the output and result callback of a request. -class ServerBoundCertServiceRequest { - public: - ServerBoundCertServiceRequest(const CompletionCallback& callback, - SSLClientCertType* type, - std::string* private_key, - std::string* cert) - : callback_(callback), - type_(type), - private_key_(private_key), - cert_(cert) { - } - - // Ensures that the result callback will never be made. - void Cancel() { - callback_.Reset(); - type_ = NULL; - private_key_ = NULL; - cert_ = NULL; - } - - // Copies the contents of |private_key| and |cert| to the caller's output - // arguments and calls the callback. - void Post(int error, - SSLClientCertType type, - const std::string& private_key, - const std::string& cert) { - if (!callback_.is_null()) { - *type_ = type; - *private_key_ = private_key; - *cert_ = cert; - callback_.Run(error); - } - delete this; - } - - bool canceled() const { return callback_.is_null(); } - - private: - CompletionCallback callback_; - SSLClientCertType* type_; - std::string* private_key_; - std::string* cert_; -}; - -// ServerBoundCertServiceWorker runs on a worker thread and takes care of the -// blocking process of performing key generation. Deletes itself eventually -// if Start() succeeds. -class ServerBoundCertServiceWorker { - public: - ServerBoundCertServiceWorker( - const std::string& server_identifier, - SSLClientCertType type, - ServerBoundCertService* server_bound_cert_service) - : server_identifier_(server_identifier), - type_(type), - serial_number_(base::RandInt(0, std::numeric_limits::max())), - origin_loop_(MessageLoop::current()), - server_bound_cert_service_(server_bound_cert_service), - canceled_(false), - error_(ERR_FAILED) { - } - - bool Start() { - DCHECK_EQ(MessageLoop::current(), origin_loop_); - - return base::WorkerPool::PostTask( - FROM_HERE, - base::Bind(&ServerBoundCertServiceWorker::Run, base::Unretained(this)), - true /* task is slow */); - } - - // Cancel is called from the origin loop when the ServerBoundCertService is - // getting deleted. - void Cancel() { - DCHECK_EQ(MessageLoop::current(), origin_loop_); - base::AutoLock locked(lock_); - canceled_ = true; - } - - private: - void Run() { - // Runs on a worker thread. - error_ = ServerBoundCertService::GenerateCert(server_identifier_, - type_, - serial_number_, - &creation_time_, - &expiration_time_, - &private_key_, - &cert_); -#if defined(USE_NSS) - // Detach the thread from NSPR. - // Calling NSS functions attaches the thread to NSPR, which stores - // the NSPR thread ID in thread-specific data. - // The threads in our thread pool terminate after we have called - // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets - // segfaults on shutdown when the threads' thread-specific data - // destructors run. - PR_DetachThread(); -#endif - Finish(); - } - - // DoReply runs on the origin thread. - void DoReply() { - DCHECK_EQ(MessageLoop::current(), origin_loop_); - { - // We lock here because the worker thread could still be in Finished, - // after the PostTask, but before unlocking |lock_|. If we do not lock in - // this case, we will end up deleting a locked Lock, which can lead to - // memory leaks or worse errors. - base::AutoLock locked(lock_); - if (!canceled_) { - server_bound_cert_service_->HandleResult( - server_identifier_, error_, type_, creation_time_, expiration_time_, - private_key_, cert_); - } - } - delete this; - } - - void Finish() { - // Runs on the worker thread. - // We assume that the origin loop outlives the ServerBoundCertService. If - // the ServerBoundCertService is deleted, it will call Cancel on us. If it - // does so before the Acquire, we'll delete ourselves and return. If it's - // trying to do so concurrently, then it'll block on the lock and we'll - // call PostTask while the ServerBoundCertService (and therefore the - // MessageLoop) is still alive. If it does so after this function, we - // assume that the MessageLoop will process pending tasks. In which case - // we'll notice the |canceled_| flag in DoReply. - - bool canceled; - { - base::AutoLock locked(lock_); - canceled = canceled_; - if (!canceled) { - origin_loop_->PostTask( - FROM_HERE, base::Bind(&ServerBoundCertServiceWorker::DoReply, - base::Unretained(this))); - } - } - if (canceled) - delete this; - } - - const std::string server_identifier_; - const SSLClientCertType type_; - // Note that serial_number_ must be initialized on a non-worker thread - // (see documentation for ServerBoundCertService::GenerateCert). - uint32 serial_number_; - MessageLoop* const origin_loop_; - ServerBoundCertService* const server_bound_cert_service_; - - // lock_ protects canceled_. - base::Lock lock_; - - // If canceled_ is true, - // * origin_loop_ cannot be accessed by the worker thread, - // * server_bound_cert_service_ cannot be accessed by any thread. - bool canceled_; - - int error_; - base::Time creation_time_; - base::Time expiration_time_; - std::string private_key_; - std::string cert_; - - DISALLOW_COPY_AND_ASSIGN(ServerBoundCertServiceWorker); -}; - -// A ServerBoundCertServiceJob is a one-to-one counterpart of an -// ServerBoundCertServiceWorker. It lives only on the ServerBoundCertService's -// origin message loop. -class ServerBoundCertServiceJob { - public: - ServerBoundCertServiceJob(ServerBoundCertServiceWorker* worker, - SSLClientCertType type) - : worker_(worker), type_(type) { - } - - ~ServerBoundCertServiceJob() { - if (worker_) { - worker_->Cancel(); - DeleteAllCanceled(); - } - } - - SSLClientCertType type() const { return type_; } - - void AddRequest(ServerBoundCertServiceRequest* request) { - requests_.push_back(request); - } - - void HandleResult(int error, - SSLClientCertType type, - const std::string& private_key, - const std::string& cert) { - worker_ = NULL; - PostAll(error, type, private_key, cert); - } - - private: - void PostAll(int error, - SSLClientCertType type, - const std::string& private_key, - const std::string& cert) { - std::vector requests; - requests_.swap(requests); - - for (std::vector::iterator - i = requests.begin(); i != requests.end(); i++) { - (*i)->Post(error, type, private_key, cert); - // Post() causes the ServerBoundCertServiceRequest to delete itself. - } - } - - void DeleteAllCanceled() { - for (std::vector::iterator - i = requests_.begin(); i != requests_.end(); i++) { - if ((*i)->canceled()) { - delete *i; - } else { - LOG(DFATAL) << "ServerBoundCertServiceRequest leaked!"; - } - } - } - - std::vector requests_; - ServerBoundCertServiceWorker* worker_; - SSLClientCertType type_; -}; - -// static -const char ServerBoundCertService::kEPKIPassword[] = ""; - -ServerBoundCertService::ServerBoundCertService( - ServerBoundCertStore* server_bound_cert_store) - : server_bound_cert_store_(server_bound_cert_store), - requests_(0), - cert_store_hits_(0), - inflight_joins_(0) {} - -ServerBoundCertService::~ServerBoundCertService() { - STLDeleteValues(&inflight_); -} - -//static -std::string ServerBoundCertService::GetDomainForHost(const std::string& host) { - std::string domain = - RegistryControlledDomainService::GetDomainAndRegistry(host); - if (domain.empty()) - return host; - return domain; -} - -int ServerBoundCertService::GetDomainBoundCert( - const std::string& origin, - const std::vector& requested_types, - SSLClientCertType* type, - std::string* private_key, - std::string* cert, - const CompletionCallback& callback, - RequestHandle* out_req) { - DCHECK(CalledOnValidThread()); - - *out_req = NULL; - - if (callback.is_null() || !private_key || !cert || origin.empty() || - requested_types.empty()) { - return ERR_INVALID_ARGUMENT; - } - - std::string domain = GetDomainForHost(GURL(origin).host()); - if (domain.empty()) - return ERR_INVALID_ARGUMENT; - - SSLClientCertType preferred_type = CLIENT_CERT_INVALID_TYPE; - for (size_t i = 0; i < requested_types.size(); ++i) { - if (IsSupportedCertType(requested_types[i])) { - preferred_type = static_cast(requested_types[i]); - break; - } - } - if (preferred_type == CLIENT_CERT_INVALID_TYPE) { - // None of the requested types are supported. - return ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED; - } - - requests_++; - - // Check if a domain bound cert of an acceptable type already exists for this - // domain, and that it has not expired. - base::Time now = base::Time::Now(); - base::Time creation_time; - base::Time expiration_time; - if (server_bound_cert_store_->GetServerBoundCert(domain, - type, - &creation_time, - &expiration_time, - private_key, - cert)) { - if (expiration_time < now) { - DVLOG(1) << "Cert store had expired cert for " << domain; - } else if (!IsSupportedCertType(*type) || - std::find(requested_types.begin(), requested_types.end(), - *type) == requested_types.end()) { - DVLOG(1) << "Cert store had cert of wrong type " << *type << " for " - << domain; - } else { - cert_store_hits_++; - return OK; - } - } - - // |server_bound_cert_store_| has no cert for this domain. See if an - // identical request is currently in flight. - ServerBoundCertServiceJob* job = NULL; - std::map::const_iterator j; - j = inflight_.find(domain); - if (j != inflight_.end()) { - // An identical request is in flight already. We'll just attach our - // callback. - job = j->second; - // Check that the job is for an acceptable type of cert. - if (std::find(requested_types.begin(), requested_types.end(), job->type()) - == requested_types.end()) { - DVLOG(1) << "Found inflight job of wrong type " << job->type() - << " for " << domain; - // If we get here, the server is asking for different types of certs in - // short succession. This probably means the server is broken or - // misconfigured. Since we only store one type of cert per domain, we - // are unable to handle this well. Just return an error and let the first - // job finish. - return ERR_ORIGIN_BOUND_CERT_GENERATION_TYPE_MISMATCH; - } - inflight_joins_++; - } else { - // Need to make a new request. - ServerBoundCertServiceWorker* worker = new ServerBoundCertServiceWorker( - domain, - preferred_type, - this); - job = new ServerBoundCertServiceJob(worker, preferred_type); - if (!worker->Start()) { - delete job; - delete worker; - // TODO(rkn): Log to the NetLog. - LOG(ERROR) << "ServerBoundCertServiceWorker couldn't be started."; - return ERR_INSUFFICIENT_RESOURCES; // Just a guess. - } - inflight_[domain] = job; - } - - ServerBoundCertServiceRequest* request = - new ServerBoundCertServiceRequest(callback, type, private_key, cert); - job->AddRequest(request); - *out_req = request; - return ERR_IO_PENDING; -} - -ServerBoundCertStore* ServerBoundCertService::GetCertStore() { - return server_bound_cert_store_.get(); -} - -// static -int ServerBoundCertService::GenerateCert(const std::string& server_identifier, - SSLClientCertType type, - uint32 serial_number, - base::Time* creation_time, - base::Time* expiration_time, - std::string* private_key, - std::string* cert) { - base::Time now = base::Time::Now(); - base::Time not_valid_after = - now + base::TimeDelta::FromDays(kValidityPeriodInDays); - std::string der_cert; - std::vector private_key_info; - switch (type) { - case CLIENT_CERT_ECDSA_SIGN: { - scoped_ptr key(crypto::ECPrivateKey::Create()); - if (!key.get()) { - DLOG(ERROR) << "Unable to create key pair for client"; - return ERR_KEY_GENERATION_FAILED; - } - if (!x509_util::CreateDomainBoundCertEC( - key.get(), - server_identifier, - serial_number, - now, - not_valid_after, - &der_cert)) { - DLOG(ERROR) << "Unable to create x509 cert for client"; - return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED; - } - - if (!key->ExportEncryptedPrivateKey( - kEPKIPassword, 1, &private_key_info)) { - DLOG(ERROR) << "Unable to export private key"; - return ERR_PRIVATE_KEY_EXPORT_FAILED; - } - break; - } - default: - NOTREACHED(); - return ERR_INVALID_ARGUMENT; - } - - // TODO(rkn): Perhaps ExportPrivateKey should be changed to output a - // std::string* to prevent this copying. - std::string key_out(private_key_info.begin(), private_key_info.end()); - - private_key->swap(key_out); - cert->swap(der_cert); - *creation_time = now; - *expiration_time = not_valid_after; - return OK; -} - -void ServerBoundCertService::CancelRequest(RequestHandle req) { - DCHECK(CalledOnValidThread()); - ServerBoundCertServiceRequest* request = - reinterpret_cast(req); - request->Cancel(); -} - -// HandleResult is called by ServerBoundCertServiceWorker on the origin message -// loop. It deletes ServerBoundCertServiceJob. -void ServerBoundCertService::HandleResult(const std::string& server_identifier, - int error, - SSLClientCertType type, - base::Time creation_time, - base::Time expiration_time, - const std::string& private_key, - const std::string& cert) { - DCHECK(CalledOnValidThread()); - - server_bound_cert_store_->SetServerBoundCert( - server_identifier, type, creation_time, expiration_time, private_key, - cert); - - std::map::iterator j; - j = inflight_.find(server_identifier); - if (j == inflight_.end()) { - NOTREACHED(); - return; - } - ServerBoundCertServiceJob* job = j->second; - inflight_.erase(j); - - job->HandleResult(error, type, private_key, cert); - delete job; -} - -int ServerBoundCertService::cert_count() { - return server_bound_cert_store_->GetCertCount(); -} - -} // namespace net diff --git a/net/base/origin_bound_cert_service.h b/net/base/origin_bound_cert_service.h deleted file mode 100644 index 355379f..0000000 --- a/net/base/origin_bound_cert_service.h +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) 2012 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_ORIGIN_BOUND_CERT_SERVICE_H_ -#define NET_BASE_ORIGIN_BOUND_CERT_SERVICE_H_ -#pragma once - -#include -#include -#include - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "base/time.h" -#include "base/threading/non_thread_safe.h" -#include "net/base/completion_callback.h" -#include "net/base/net_export.h" -#include "net/base/ssl_client_cert_type.h" - -namespace net { - -class ServerBoundCertServiceJob; -class ServerBoundCertServiceWorker; -class ServerBoundCertStore; - -// A class for creating and fetching server bound certs. -// Inherits from NonThreadSafe in order to use the function -// |CalledOnValidThread|. -class NET_EXPORT ServerBoundCertService - : NON_EXPORTED_BASE(public base::NonThreadSafe) { - public: - // Opaque type used to cancel a request. - typedef void* RequestHandle; - - // Password used on EncryptedPrivateKeyInfo data stored in EC private_key - // values. (This is not used to provide any security, but to workaround NSS - // being unable to import unencrypted PrivateKeyInfo for EC keys.) - static const char kEPKIPassword[]; - - // This object owns server_bound_cert_store. - explicit ServerBoundCertService( - ServerBoundCertStore* server_bound_cert_store); - - ~ServerBoundCertService(); - - // Returns the domain to be used for |host|. The domain is the - // "registry controlled domain", or the "ETLD + 1" where one exists, or - // the origin otherwise. - static std::string GetDomainForHost(const std::string& host); - - // Fetches the domain bound cert for the specified origin of the specified - // type if one exists and creates one otherwise. Returns OK if successful or - // an error code upon failure. - // - // |requested_types| is a list of the TLS ClientCertificateTypes the site will - // accept, ordered from most preferred to least preferred. Types we don't - // support will be ignored. See ssl_client_cert_type.h. - // - // On successful completion, |private_key| stores a DER-encoded - // PrivateKeyInfo struct, and |cert| stores a DER-encoded certificate, and - // |type| specifies the type of certificate that was returned. - // - // |callback| must not be null. ERR_IO_PENDING is returned if the operation - // could not be completed immediately, in which case the result code will - // be passed to the callback when available. - // - // |*out_req| will be filled with a handle to the async request. This handle - // is not valid after the request has completed. - int GetDomainBoundCert( - const std::string& origin, - const std::vector& requested_types, - SSLClientCertType* type, - std::string* private_key, - std::string* cert, - const CompletionCallback& callback, - RequestHandle* out_req); - - // Cancels the specified request. |req| is the handle returned by - // GetDomainBoundCert(). After a request is canceled, its completion - // callback will not be called. - void CancelRequest(RequestHandle req); - - // Returns the backing ServerBoundCertStore. - ServerBoundCertStore* GetCertStore(); - - // Public only for unit testing. - int cert_count(); - uint64 requests() const { return requests_; } - uint64 cert_store_hits() const { return cert_store_hits_; } - uint64 inflight_joins() const { return inflight_joins_; } - - private: - friend class ServerBoundCertServiceWorker; // Calls HandleResult. - - // On success, |private_key| stores a DER-encoded PrivateKeyInfo - // struct, |cert| stores a DER-encoded certificate, |creation_time| stores the - // start of the validity period of the certificate and |expiration_time| - // stores the expiration time of the certificate. Returns OK if successful and - // an error code otherwise. - // |serial_number| is passed in because it is created with the function - // base::RandInt, which opens the file /dev/urandom. /dev/urandom is opened - // with a LazyInstance, which is not allowed on a worker thread. - static int GenerateCert(const std::string& server_identifier, - SSLClientCertType type, - uint32 serial_number, - base::Time* creation_time, - base::Time* expiration_time, - std::string* private_key, - std::string* cert); - - void HandleResult(const std::string& server_identifier, - int error, - SSLClientCertType type, - base::Time creation_time, - base::Time expiration_time, - const std::string& private_key, - const std::string& cert); - - scoped_ptr server_bound_cert_store_; - - // inflight_ maps from a server to an active generation which is taking - // place. - std::map inflight_; - - uint64 requests_; - uint64 cert_store_hits_; - uint64 inflight_joins_; - - DISALLOW_COPY_AND_ASSIGN(ServerBoundCertService); -}; - -} // namespace net - -#endif // NET_BASE_ORIGIN_BOUND_CERT_SERVICE_H_ diff --git a/net/base/origin_bound_cert_service_unittest.cc b/net/base/origin_bound_cert_service_unittest.cc deleted file mode 100644 index 64dd010..0000000 --- a/net/base/origin_bound_cert_service_unittest.cc +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright (c) 2012 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 -#include - -#include "base/bind.h" -#include "base/memory/scoped_ptr.h" -#include "crypto/ec_private_key.h" -#include "net/base/asn1_util.h" -#include "net/base/default_origin_bound_cert_store.h" -#include "net/base/net_errors.h" -#include "net/base/test_completion_callback.h" -#include "net/base/x509_certificate.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { - -namespace { - -void FailTest(int /* result */) { - FAIL(); -} - -TEST(ServerBoundCertServiceTest, GetDomainForHost) { - EXPECT_EQ("google.com", - ServerBoundCertService::GetDomainForHost("google.com")); - EXPECT_EQ("google.com", - ServerBoundCertService::GetDomainForHost("www.google.com")); - // NOTE(rch): we would like to segregate cookies and certificates for - // *.appspot.com, but currently we can not do that becaues we want to - // allow direct navigation to appspot.com. - EXPECT_EQ("appspot.com", - ServerBoundCertService::GetDomainForHost("foo.appspot.com")); - EXPECT_EQ("google.com", - ServerBoundCertService::GetDomainForHost("www.mail.google.com")); - EXPECT_EQ("goto", - ServerBoundCertService::GetDomainForHost("goto")); - EXPECT_EQ("127.0.0.1", - ServerBoundCertService::GetDomainForHost("127.0.0.1")); -} - -// See http://crbug.com/91512 - implement OpenSSL version of CreateSelfSigned. -#if !defined(USE_OPENSSL) - -TEST(ServerBoundCertServiceTest, CacheHit) { - scoped_ptr service( - new ServerBoundCertService(new DefaultServerBoundCertStore(NULL))); - std::string origin("https://encrypted.google.com:443"); - - int error; - std::vector types; - types.push_back(CLIENT_CERT_ECDSA_SIGN); - TestCompletionCallback callback; - ServerBoundCertService::RequestHandle request_handle; - - // Asynchronous completion. - SSLClientCertType type1; - std::string private_key_info1, der_cert1; - EXPECT_EQ(0, service->cert_count()); - error = service->GetDomainBoundCert( - origin, types, &type1, &private_key_info1, &der_cert1, - callback.callback(), &request_handle); - EXPECT_EQ(ERR_IO_PENDING, error); - EXPECT_TRUE(request_handle != NULL); - error = callback.WaitForResult(); - EXPECT_EQ(OK, error); - EXPECT_EQ(1, service->cert_count()); - EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); - EXPECT_FALSE(private_key_info1.empty()); - EXPECT_FALSE(der_cert1.empty()); - - // Synchronous completion. - SSLClientCertType type2; - std::string private_key_info2, der_cert2; - error = service->GetDomainBoundCert( - origin, types, &type2, &private_key_info2, &der_cert2, - callback.callback(), &request_handle); - EXPECT_TRUE(request_handle == NULL); - EXPECT_EQ(OK, error); - EXPECT_EQ(1, service->cert_count()); - EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); - EXPECT_EQ(private_key_info1, private_key_info2); - EXPECT_EQ(der_cert1, der_cert2); - - EXPECT_EQ(2u, service->requests()); - EXPECT_EQ(1u, service->cert_store_hits()); - EXPECT_EQ(0u, service->inflight_joins()); -} - -TEST(ServerBoundCertServiceTest, UnsupportedTypes) { - scoped_ptr service( - new ServerBoundCertService(new DefaultServerBoundCertStore(NULL))); - std::string origin("https://encrypted.google.com:443"); - - int error; - std::vector types; - TestCompletionCallback callback; - ServerBoundCertService::RequestHandle request_handle; - - // Empty requested_types. - SSLClientCertType type1; - std::string private_key_info1, der_cert1; - error = service->GetDomainBoundCert( - origin, types, &type1, &private_key_info1, &der_cert1, - callback.callback(), &request_handle); - EXPECT_EQ(ERR_INVALID_ARGUMENT, error); - EXPECT_TRUE(request_handle == NULL); - - // No supported types in requested_types. - types.push_back(CLIENT_CERT_RSA_SIGN); - types.push_back(2); - types.push_back(3); - error = service->GetDomainBoundCert( - origin, types, &type1, &private_key_info1, &der_cert1, - callback.callback(), &request_handle); - EXPECT_EQ(ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED, error); - EXPECT_TRUE(request_handle == NULL); - - // Supported types after unsupported ones in requested_types. - types.push_back(CLIENT_CERT_ECDSA_SIGN); - // Asynchronous completion. - EXPECT_EQ(0, service->cert_count()); - error = service->GetDomainBoundCert( - origin, types, &type1, &private_key_info1, &der_cert1, - callback.callback(), &request_handle); - EXPECT_EQ(ERR_IO_PENDING, error); - EXPECT_TRUE(request_handle != NULL); - error = callback.WaitForResult(); - EXPECT_EQ(OK, error); - EXPECT_EQ(1, service->cert_count()); - EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); - EXPECT_FALSE(private_key_info1.empty()); - EXPECT_FALSE(der_cert1.empty()); - - // Now that the cert is created, doing requests for unsupported types - // shouldn't affect the created cert. - // Empty requested_types. - types.clear(); - SSLClientCertType type2; - std::string private_key_info2, der_cert2; - error = service->GetDomainBoundCert( - origin, types, &type2, &private_key_info2, &der_cert2, - callback.callback(), &request_handle); - EXPECT_EQ(ERR_INVALID_ARGUMENT, error); - EXPECT_TRUE(request_handle == NULL); - - // No supported types in requested_types. - types.push_back(CLIENT_CERT_RSA_SIGN); - types.push_back(2); - types.push_back(3); - error = service->GetDomainBoundCert( - origin, types, &type2, &private_key_info2, &der_cert2, - callback.callback(), &request_handle); - EXPECT_EQ(ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED, error); - EXPECT_TRUE(request_handle == NULL); - - // If we request EC, the cert we created before should still be there. - types.push_back(CLIENT_CERT_ECDSA_SIGN); - error = service->GetDomainBoundCert( - origin, types, &type2, &private_key_info2, &der_cert2, - callback.callback(), &request_handle); - EXPECT_TRUE(request_handle == NULL); - EXPECT_EQ(OK, error); - EXPECT_EQ(1, service->cert_count()); - EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); - EXPECT_EQ(private_key_info1, private_key_info2); - EXPECT_EQ(der_cert1, der_cert2); -} - -TEST(ServerBoundCertServiceTest, StoreCerts) { - scoped_ptr service( - new ServerBoundCertService(new DefaultServerBoundCertStore(NULL))); - int error; - std::vector types; - types.push_back(CLIENT_CERT_ECDSA_SIGN); - TestCompletionCallback callback; - ServerBoundCertService::RequestHandle request_handle; - - std::string origin1("https://encrypted.google.com:443"); - SSLClientCertType type1; - std::string private_key_info1, der_cert1; - EXPECT_EQ(0, service->cert_count()); - error = service->GetDomainBoundCert( - origin1, types, &type1, &private_key_info1, &der_cert1, - callback.callback(), &request_handle); - EXPECT_EQ(ERR_IO_PENDING, error); - EXPECT_TRUE(request_handle != NULL); - error = callback.WaitForResult(); - EXPECT_EQ(OK, error); - EXPECT_EQ(1, service->cert_count()); - - std::string origin2("https://www.verisign.com:443"); - SSLClientCertType type2; - std::string private_key_info2, der_cert2; - error = service->GetDomainBoundCert( - origin2, types, &type2, &private_key_info2, &der_cert2, - callback.callback(), &request_handle); - EXPECT_EQ(ERR_IO_PENDING, error); - EXPECT_TRUE(request_handle != NULL); - error = callback.WaitForResult(); - EXPECT_EQ(OK, error); - EXPECT_EQ(2, service->cert_count()); - - std::string origin3("https://www.twitter.com:443"); - SSLClientCertType type3; - std::string private_key_info3, der_cert3; - error = service->GetDomainBoundCert( - origin3, types, &type3, &private_key_info3, &der_cert3, - callback.callback(), &request_handle); - EXPECT_EQ(ERR_IO_PENDING, error); - EXPECT_TRUE(request_handle != NULL); - error = callback.WaitForResult(); - EXPECT_EQ(OK, error); - EXPECT_EQ(3, service->cert_count()); - - EXPECT_NE(private_key_info1, private_key_info2); - EXPECT_NE(der_cert1, der_cert2); - EXPECT_NE(private_key_info1, private_key_info3); - EXPECT_NE(der_cert1, der_cert3); - EXPECT_NE(private_key_info2, private_key_info3); - EXPECT_NE(der_cert2, der_cert3); - EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); - EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); - EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type3); -} - -// Tests an inflight join. -TEST(ServerBoundCertServiceTest, InflightJoin) { - scoped_ptr service( - new ServerBoundCertService(new DefaultServerBoundCertStore(NULL))); - std::string origin("https://encrypted.google.com:443"); - int error; - std::vector types; - types.push_back(CLIENT_CERT_ECDSA_SIGN); - - SSLClientCertType type1; - std::string private_key_info1, der_cert1; - TestCompletionCallback callback1; - ServerBoundCertService::RequestHandle request_handle1; - - SSLClientCertType type2; - std::string private_key_info2, der_cert2; - TestCompletionCallback callback2; - ServerBoundCertService::RequestHandle request_handle2; - - error = service->GetDomainBoundCert( - origin, types, &type1, &private_key_info1, &der_cert1, - callback1.callback(), &request_handle1); - EXPECT_EQ(ERR_IO_PENDING, error); - EXPECT_TRUE(request_handle1 != NULL); - // If we request RSA and EC in the 2nd request, should still join with the - // original request. - types.insert(types.begin(), CLIENT_CERT_RSA_SIGN); - error = service->GetDomainBoundCert( - origin, types, &type2, &private_key_info2, &der_cert2, - callback2.callback(), &request_handle2); - EXPECT_EQ(ERR_IO_PENDING, error); - EXPECT_TRUE(request_handle2 != NULL); - - error = callback1.WaitForResult(); - EXPECT_EQ(OK, error); - error = callback2.WaitForResult(); - EXPECT_EQ(OK, error); - - EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); - EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); - EXPECT_EQ(2u, service->requests()); - EXPECT_EQ(0u, service->cert_store_hits()); - EXPECT_EQ(1u, service->inflight_joins()); -} - -TEST(ServerBoundCertServiceTest, ExtractValuesFromBytesEC) { - scoped_ptr service( - new ServerBoundCertService(new DefaultServerBoundCertStore(NULL))); - std::string origin("https://encrypted.google.com:443"); - SSLClientCertType type; - std::string private_key_info, der_cert; - int error; - std::vector types; - types.push_back(CLIENT_CERT_ECDSA_SIGN); - TestCompletionCallback callback; - ServerBoundCertService::RequestHandle request_handle; - - error = service->GetDomainBoundCert( - origin, types, &type, &private_key_info, &der_cert, callback.callback(), - &request_handle); - EXPECT_EQ(ERR_IO_PENDING, error); - EXPECT_TRUE(request_handle != NULL); - error = callback.WaitForResult(); - EXPECT_EQ(OK, error); - - base::StringPiece spki_piece; - ASSERT_TRUE(asn1::ExtractSPKIFromDERCert(der_cert, &spki_piece)); - std::vector spki( - spki_piece.data(), - spki_piece.data() + spki_piece.size()); - - // Check that we can retrieve the key from the bytes. - std::vector key_vec(private_key_info.begin(), private_key_info.end()); - scoped_ptr private_key( - crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( - ServerBoundCertService::kEPKIPassword, key_vec, spki)); - EXPECT_TRUE(private_key != NULL); - - // Check that we can retrieve the cert from the bytes. - scoped_refptr x509cert( - X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size())); - EXPECT_TRUE(x509cert != NULL); -} - -// Tests that the callback of a canceled request is never made. -TEST(ServerBoundCertServiceTest, CancelRequest) { - scoped_ptr service( - new ServerBoundCertService(new DefaultServerBoundCertStore(NULL))); - std::string origin("https://encrypted.google.com:443"); - SSLClientCertType type; - std::string private_key_info, der_cert; - int error; - std::vector types; - types.push_back(CLIENT_CERT_ECDSA_SIGN); - ServerBoundCertService::RequestHandle request_handle; - - error = service->GetDomainBoundCert(origin, - types, - &type, - &private_key_info, - &der_cert, - base::Bind(&FailTest), - &request_handle); - EXPECT_EQ(ERR_IO_PENDING, error); - EXPECT_TRUE(request_handle != NULL); - service->CancelRequest(request_handle); - - // Issue a few more requests to the worker pool and wait for their - // completion, so that the task of the canceled request (which runs on a - // worker thread) is likely to complete by the end of this test. - TestCompletionCallback callback; - for (int i = 0; i < 5; ++i) { - error = service->GetDomainBoundCert( - "https://foo" + std::string(1, (char) ('1' + i)), - types, - &type, - &private_key_info, - &der_cert, - callback.callback(), - &request_handle); - EXPECT_EQ(ERR_IO_PENDING, error); - EXPECT_TRUE(request_handle != NULL); - error = callback.WaitForResult(); - } - - // Even though the original request was cancelled, the service will still - // store the result, it just doesn't call the callback. - EXPECT_EQ(6, service->cert_count()); -} - -TEST(ServerBoundCertServiceTest, Expiration) { - ServerBoundCertStore* store = new DefaultServerBoundCertStore(NULL); - base::Time now = base::Time::Now(); - store->SetServerBoundCert("good", - CLIENT_CERT_ECDSA_SIGN, - now, - now + base::TimeDelta::FromDays(1), - "a", - "b"); - store->SetServerBoundCert("expired", - CLIENT_CERT_ECDSA_SIGN, - now - base::TimeDelta::FromDays(2), - now - base::TimeDelta::FromDays(1), - "c", - "d"); - ServerBoundCertService service(store); - EXPECT_EQ(2, service.cert_count()); - - int error; - std::vector types; - types.push_back(CLIENT_CERT_ECDSA_SIGN); - TestCompletionCallback callback; - ServerBoundCertService::RequestHandle request_handle; - - // Cert still valid - synchronous completion. - SSLClientCertType type1; - std::string private_key_info1, der_cert1; - error = service.GetDomainBoundCert( - "https://good", types, &type1, &private_key_info1, &der_cert1, - callback.callback(), &request_handle); - EXPECT_EQ(OK, error); - EXPECT_TRUE(request_handle == NULL); - EXPECT_EQ(2, service.cert_count()); - EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); - EXPECT_STREQ("a", private_key_info1.c_str()); - EXPECT_STREQ("b", der_cert1.c_str()); - - // Cert expired - New cert will be generated, asynchronous completion. - SSLClientCertType type2; - std::string private_key_info2, der_cert2; - error = service.GetDomainBoundCert( - "https://expired", types, &type2, &private_key_info2, &der_cert2, - callback.callback(), &request_handle); - EXPECT_EQ(ERR_IO_PENDING, error); - EXPECT_TRUE(request_handle != NULL); - error = callback.WaitForResult(); - EXPECT_EQ(OK, error); - EXPECT_EQ(2, service.cert_count()); - EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); - EXPECT_LT(1U, private_key_info2.size()); - EXPECT_LT(1U, der_cert2.size()); -} - -#endif // !defined(USE_OPENSSL) - -} // namespace - -} // namespace net diff --git a/net/base/origin_bound_cert_store.cc b/net/base/origin_bound_cert_store.cc deleted file mode 100644 index cd4264a..0000000 --- a/net/base/origin_bound_cert_store.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2012 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_store.h" - -namespace net { - -ServerBoundCertStore::ServerBoundCert::ServerBoundCert() - : type_(CLIENT_CERT_INVALID_TYPE) { -} - -ServerBoundCertStore::ServerBoundCert::ServerBoundCert( - const std::string& server_identifier, - SSLClientCertType type, - base::Time creation_time, - base::Time expiration_time, - const std::string& private_key, - const std::string& cert) - : server_identifier_(server_identifier), - type_(type), - creation_time_(creation_time), - expiration_time_(expiration_time), - private_key_(private_key), - cert_(cert) {} - -ServerBoundCertStore::ServerBoundCert::~ServerBoundCert() {} - -} // namespace net diff --git a/net/base/origin_bound_cert_store.h b/net/base/origin_bound_cert_store.h deleted file mode 100644 index 2ae22c5..0000000 --- a/net/base/origin_bound_cert_store.h +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) 2012 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_ORIGIN_BOUND_CERT_STORE_H_ -#define NET_BASE_ORIGIN_BOUND_CERT_STORE_H_ -#pragma once - -#include -#include - -#include "base/time.h" -#include "net/base/net_export.h" -#include "net/base/ssl_client_cert_type.h" - -namespace net { - -// An interface for storing and retrieving server bound certs. -// There isn't a domain bound certs spec yet, but the old origin bound -// certificates are specified in -// http://balfanz.github.com/tls-obc-spec/draft-balfanz-tls-obc-01.html. - -// Owned only by a single ServerBoundCertService object, which is responsible -// for deleting it. -class NET_EXPORT ServerBoundCertStore { - public: - // The ServerBoundCert class contains a private key in addition to the server - // cert, and cert type. - class NET_EXPORT ServerBoundCert { - public: - ServerBoundCert(); - ServerBoundCert(const std::string& server_identifier, - SSLClientCertType type, - base::Time creation_time, - base::Time expiration_time, - const std::string& private_key, - const std::string& cert); - ~ServerBoundCert(); - - // Server identifier. For domain bound certs, for instance "verisign.com". - const std::string& server_identifier() const { return server_identifier_; } - // TLS ClientCertificateType. - SSLClientCertType type() const { return type_; } - // The time the certificate was created, also the start of the certificate - // validity period. - base::Time creation_time() const { return creation_time_; } - // The time after which this certificate is no longer valid. - base::Time expiration_time() const { return expiration_time_; } - // The encoding of the private key depends on the type. - // rsa_sign: DER-encoded PrivateKeyInfo struct. - // ecdsa_sign: DER-encoded EncryptedPrivateKeyInfo struct. - const std::string& private_key() const { return private_key_; } - // DER-encoded certificate. - const std::string& cert() const { return cert_; } - - private: - std::string server_identifier_; - SSLClientCertType type_; - base::Time creation_time_; - base::Time expiration_time_; - std::string private_key_; - std::string cert_; - }; - - virtual ~ServerBoundCertStore() {} - - // 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, |cert_result| stores a DER-encoded certificate, - // |type| is the ClientCertificateType of the returned certificate, - // |creation_time| stores the start of the validity period of the certificate - // and |expiration_time| is the expiration time of the certificate. - // Returns false if no server bound cert exists for the specified server. - virtual bool GetServerBoundCert( - const std::string& server_identifier, - SSLClientCertType* type, - base::Time* creation_time, - base::Time* expiration_time, - std::string* private_key_result, - std::string* cert_result) = 0; - - // Adds a server bound cert and the corresponding private key to the store. - virtual void SetServerBoundCert( - const std::string& server_identifier, - SSLClientCertType type, - base::Time creation_time, - base::Time expiration_time, - const std::string& private_key, - const std::string& cert) = 0; - - // Removes a server bound cert and the corresponding private key from the - // store. - virtual void DeleteServerBoundCert(const std::string& server_identifier) = 0; - - // Deletes all of the server bound certs that have a creation_date greater - // than or equal to |delete_begin| and less than |delete_end|. If a - // base::Time value is_null, that side of the comparison is unbounded. - virtual void DeleteAllCreatedBetween(base::Time delete_begin, - base::Time delete_end) = 0; - - // Removes all server bound certs and the corresponding private keys from - // the store. - virtual void DeleteAll() = 0; - - // Returns all server bound certs and the corresponding private keys. - virtual void GetAllServerBoundCerts( - std::vector* server_bound_certs) = 0; - - // Returns the number of certs in the store. - // Public only for unit testing. - virtual int GetCertCount() = 0; -}; - -} // namespace net - -#endif // NET_BASE_ORIGIN_BOUND_CERT_STORE_H_ diff --git a/net/base/server_bound_cert_service.cc b/net/base/server_bound_cert_service.cc new file mode 100644 index 0000000..cd158c7 --- /dev/null +++ b/net/base/server_bound_cert_service.cc @@ -0,0 +1,509 @@ +// Copyright (c) 2012 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/server_bound_cert_service.h" + +#include +#include + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/compiler_specific.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/rand_util.h" +#include "base/stl_util.h" +#include "base/threading/worker_pool.h" +#include "crypto/ec_private_key.h" +#include "googleurl/src/gurl.h" +#include "net/base/net_errors.h" +#include "net/base/registry_controlled_domain.h" +#include "net/base/server_bound_cert_store.h" +#include "net/base/x509_certificate.h" +#include "net/base/x509_util.h" + +#if defined(USE_NSS) +#include // PR_DetachThread +#endif + +namespace net { + +namespace { + +const int kKeySizeInBits = 1024; +const int kValidityPeriodInDays = 365; + +bool IsSupportedCertType(uint8 type) { + switch(type) { + case CLIENT_CERT_ECDSA_SIGN: + return true; + default: + return false; + } +} + +} // namespace + +// Represents the output and result callback of a request. +class ServerBoundCertServiceRequest { + public: + ServerBoundCertServiceRequest(const CompletionCallback& callback, + SSLClientCertType* type, + std::string* private_key, + std::string* cert) + : callback_(callback), + type_(type), + private_key_(private_key), + cert_(cert) { + } + + // Ensures that the result callback will never be made. + void Cancel() { + callback_.Reset(); + type_ = NULL; + private_key_ = NULL; + cert_ = NULL; + } + + // Copies the contents of |private_key| and |cert| to the caller's output + // arguments and calls the callback. + void Post(int error, + SSLClientCertType type, + const std::string& private_key, + const std::string& cert) { + if (!callback_.is_null()) { + *type_ = type; + *private_key_ = private_key; + *cert_ = cert; + callback_.Run(error); + } + delete this; + } + + bool canceled() const { return callback_.is_null(); } + + private: + CompletionCallback callback_; + SSLClientCertType* type_; + std::string* private_key_; + std::string* cert_; +}; + +// ServerBoundCertServiceWorker runs on a worker thread and takes care of the +// blocking process of performing key generation. Deletes itself eventually +// if Start() succeeds. +class ServerBoundCertServiceWorker { + public: + ServerBoundCertServiceWorker( + const std::string& server_identifier, + SSLClientCertType type, + ServerBoundCertService* server_bound_cert_service) + : server_identifier_(server_identifier), + type_(type), + serial_number_(base::RandInt(0, std::numeric_limits::max())), + origin_loop_(MessageLoop::current()), + server_bound_cert_service_(server_bound_cert_service), + canceled_(false), + error_(ERR_FAILED) { + } + + bool Start() { + DCHECK_EQ(MessageLoop::current(), origin_loop_); + + return base::WorkerPool::PostTask( + FROM_HERE, + base::Bind(&ServerBoundCertServiceWorker::Run, base::Unretained(this)), + true /* task is slow */); + } + + // Cancel is called from the origin loop when the ServerBoundCertService is + // getting deleted. + void Cancel() { + DCHECK_EQ(MessageLoop::current(), origin_loop_); + base::AutoLock locked(lock_); + canceled_ = true; + } + + private: + void Run() { + // Runs on a worker thread. + error_ = ServerBoundCertService::GenerateCert(server_identifier_, + type_, + serial_number_, + &creation_time_, + &expiration_time_, + &private_key_, + &cert_); +#if defined(USE_NSS) + // Detach the thread from NSPR. + // Calling NSS functions attaches the thread to NSPR, which stores + // the NSPR thread ID in thread-specific data. + // The threads in our thread pool terminate after we have called + // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets + // segfaults on shutdown when the threads' thread-specific data + // destructors run. + PR_DetachThread(); +#endif + Finish(); + } + + // DoReply runs on the origin thread. + void DoReply() { + DCHECK_EQ(MessageLoop::current(), origin_loop_); + { + // We lock here because the worker thread could still be in Finished, + // after the PostTask, but before unlocking |lock_|. If we do not lock in + // this case, we will end up deleting a locked Lock, which can lead to + // memory leaks or worse errors. + base::AutoLock locked(lock_); + if (!canceled_) { + server_bound_cert_service_->HandleResult( + server_identifier_, error_, type_, creation_time_, expiration_time_, + private_key_, cert_); + } + } + delete this; + } + + void Finish() { + // Runs on the worker thread. + // We assume that the origin loop outlives the ServerBoundCertService. If + // the ServerBoundCertService is deleted, it will call Cancel on us. If it + // does so before the Acquire, we'll delete ourselves and return. If it's + // trying to do so concurrently, then it'll block on the lock and we'll + // call PostTask while the ServerBoundCertService (and therefore the + // MessageLoop) is still alive. If it does so after this function, we + // assume that the MessageLoop will process pending tasks. In which case + // we'll notice the |canceled_| flag in DoReply. + + bool canceled; + { + base::AutoLock locked(lock_); + canceled = canceled_; + if (!canceled) { + origin_loop_->PostTask( + FROM_HERE, base::Bind(&ServerBoundCertServiceWorker::DoReply, + base::Unretained(this))); + } + } + if (canceled) + delete this; + } + + const std::string server_identifier_; + const SSLClientCertType type_; + // Note that serial_number_ must be initialized on a non-worker thread + // (see documentation for ServerBoundCertService::GenerateCert). + uint32 serial_number_; + MessageLoop* const origin_loop_; + ServerBoundCertService* const server_bound_cert_service_; + + // lock_ protects canceled_. + base::Lock lock_; + + // If canceled_ is true, + // * origin_loop_ cannot be accessed by the worker thread, + // * server_bound_cert_service_ cannot be accessed by any thread. + bool canceled_; + + int error_; + base::Time creation_time_; + base::Time expiration_time_; + std::string private_key_; + std::string cert_; + + DISALLOW_COPY_AND_ASSIGN(ServerBoundCertServiceWorker); +}; + +// A ServerBoundCertServiceJob is a one-to-one counterpart of an +// ServerBoundCertServiceWorker. It lives only on the ServerBoundCertService's +// origin message loop. +class ServerBoundCertServiceJob { + public: + ServerBoundCertServiceJob(ServerBoundCertServiceWorker* worker, + SSLClientCertType type) + : worker_(worker), type_(type) { + } + + ~ServerBoundCertServiceJob() { + if (worker_) { + worker_->Cancel(); + DeleteAllCanceled(); + } + } + + SSLClientCertType type() const { return type_; } + + void AddRequest(ServerBoundCertServiceRequest* request) { + requests_.push_back(request); + } + + void HandleResult(int error, + SSLClientCertType type, + const std::string& private_key, + const std::string& cert) { + worker_ = NULL; + PostAll(error, type, private_key, cert); + } + + private: + void PostAll(int error, + SSLClientCertType type, + const std::string& private_key, + const std::string& cert) { + std::vector requests; + requests_.swap(requests); + + for (std::vector::iterator + i = requests.begin(); i != requests.end(); i++) { + (*i)->Post(error, type, private_key, cert); + // Post() causes the ServerBoundCertServiceRequest to delete itself. + } + } + + void DeleteAllCanceled() { + for (std::vector::iterator + i = requests_.begin(); i != requests_.end(); i++) { + if ((*i)->canceled()) { + delete *i; + } else { + LOG(DFATAL) << "ServerBoundCertServiceRequest leaked!"; + } + } + } + + std::vector requests_; + ServerBoundCertServiceWorker* worker_; + SSLClientCertType type_; +}; + +// static +const char ServerBoundCertService::kEPKIPassword[] = ""; + +ServerBoundCertService::ServerBoundCertService( + ServerBoundCertStore* server_bound_cert_store) + : server_bound_cert_store_(server_bound_cert_store), + requests_(0), + cert_store_hits_(0), + inflight_joins_(0) {} + +ServerBoundCertService::~ServerBoundCertService() { + STLDeleteValues(&inflight_); +} + +//static +std::string ServerBoundCertService::GetDomainForHost(const std::string& host) { + std::string domain = + RegistryControlledDomainService::GetDomainAndRegistry(host); + if (domain.empty()) + return host; + return domain; +} + +int ServerBoundCertService::GetDomainBoundCert( + const std::string& origin, + const std::vector& requested_types, + SSLClientCertType* type, + std::string* private_key, + std::string* cert, + const CompletionCallback& callback, + RequestHandle* out_req) { + DCHECK(CalledOnValidThread()); + + *out_req = NULL; + + if (callback.is_null() || !private_key || !cert || origin.empty() || + requested_types.empty()) { + return ERR_INVALID_ARGUMENT; + } + + std::string domain = GetDomainForHost(GURL(origin).host()); + if (domain.empty()) + return ERR_INVALID_ARGUMENT; + + SSLClientCertType preferred_type = CLIENT_CERT_INVALID_TYPE; + for (size_t i = 0; i < requested_types.size(); ++i) { + if (IsSupportedCertType(requested_types[i])) { + preferred_type = static_cast(requested_types[i]); + break; + } + } + if (preferred_type == CLIENT_CERT_INVALID_TYPE) { + // None of the requested types are supported. + return ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED; + } + + requests_++; + + // Check if a domain bound cert of an acceptable type already exists for this + // domain, and that it has not expired. + base::Time now = base::Time::Now(); + base::Time creation_time; + base::Time expiration_time; + if (server_bound_cert_store_->GetServerBoundCert(domain, + type, + &creation_time, + &expiration_time, + private_key, + cert)) { + if (expiration_time < now) { + DVLOG(1) << "Cert store had expired cert for " << domain; + } else if (!IsSupportedCertType(*type) || + std::find(requested_types.begin(), requested_types.end(), + *type) == requested_types.end()) { + DVLOG(1) << "Cert store had cert of wrong type " << *type << " for " + << domain; + } else { + cert_store_hits_++; + return OK; + } + } + + // |server_bound_cert_store_| has no cert for this domain. See if an + // identical request is currently in flight. + ServerBoundCertServiceJob* job = NULL; + std::map::const_iterator j; + j = inflight_.find(domain); + if (j != inflight_.end()) { + // An identical request is in flight already. We'll just attach our + // callback. + job = j->second; + // Check that the job is for an acceptable type of cert. + if (std::find(requested_types.begin(), requested_types.end(), job->type()) + == requested_types.end()) { + DVLOG(1) << "Found inflight job of wrong type " << job->type() + << " for " << domain; + // If we get here, the server is asking for different types of certs in + // short succession. This probably means the server is broken or + // misconfigured. Since we only store one type of cert per domain, we + // are unable to handle this well. Just return an error and let the first + // job finish. + return ERR_ORIGIN_BOUND_CERT_GENERATION_TYPE_MISMATCH; + } + inflight_joins_++; + } else { + // Need to make a new request. + ServerBoundCertServiceWorker* worker = new ServerBoundCertServiceWorker( + domain, + preferred_type, + this); + job = new ServerBoundCertServiceJob(worker, preferred_type); + if (!worker->Start()) { + delete job; + delete worker; + // TODO(rkn): Log to the NetLog. + LOG(ERROR) << "ServerBoundCertServiceWorker couldn't be started."; + return ERR_INSUFFICIENT_RESOURCES; // Just a guess. + } + inflight_[domain] = job; + } + + ServerBoundCertServiceRequest* request = + new ServerBoundCertServiceRequest(callback, type, private_key, cert); + job->AddRequest(request); + *out_req = request; + return ERR_IO_PENDING; +} + +ServerBoundCertStore* ServerBoundCertService::GetCertStore() { + return server_bound_cert_store_.get(); +} + +// static +int ServerBoundCertService::GenerateCert(const std::string& server_identifier, + SSLClientCertType type, + uint32 serial_number, + base::Time* creation_time, + base::Time* expiration_time, + std::string* private_key, + std::string* cert) { + base::Time now = base::Time::Now(); + base::Time not_valid_after = + now + base::TimeDelta::FromDays(kValidityPeriodInDays); + std::string der_cert; + std::vector private_key_info; + switch (type) { + case CLIENT_CERT_ECDSA_SIGN: { + scoped_ptr key(crypto::ECPrivateKey::Create()); + if (!key.get()) { + DLOG(ERROR) << "Unable to create key pair for client"; + return ERR_KEY_GENERATION_FAILED; + } + if (!x509_util::CreateDomainBoundCertEC( + key.get(), + server_identifier, + serial_number, + now, + not_valid_after, + &der_cert)) { + DLOG(ERROR) << "Unable to create x509 cert for client"; + return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED; + } + + if (!key->ExportEncryptedPrivateKey( + kEPKIPassword, 1, &private_key_info)) { + DLOG(ERROR) << "Unable to export private key"; + return ERR_PRIVATE_KEY_EXPORT_FAILED; + } + break; + } + default: + NOTREACHED(); + return ERR_INVALID_ARGUMENT; + } + + // TODO(rkn): Perhaps ExportPrivateKey should be changed to output a + // std::string* to prevent this copying. + std::string key_out(private_key_info.begin(), private_key_info.end()); + + private_key->swap(key_out); + cert->swap(der_cert); + *creation_time = now; + *expiration_time = not_valid_after; + return OK; +} + +void ServerBoundCertService::CancelRequest(RequestHandle req) { + DCHECK(CalledOnValidThread()); + ServerBoundCertServiceRequest* request = + reinterpret_cast(req); + request->Cancel(); +} + +// HandleResult is called by ServerBoundCertServiceWorker on the origin message +// loop. It deletes ServerBoundCertServiceJob. +void ServerBoundCertService::HandleResult(const std::string& server_identifier, + int error, + SSLClientCertType type, + base::Time creation_time, + base::Time expiration_time, + const std::string& private_key, + const std::string& cert) { + DCHECK(CalledOnValidThread()); + + server_bound_cert_store_->SetServerBoundCert( + server_identifier, type, creation_time, expiration_time, private_key, + cert); + + std::map::iterator j; + j = inflight_.find(server_identifier); + if (j == inflight_.end()) { + NOTREACHED(); + return; + } + ServerBoundCertServiceJob* job = j->second; + inflight_.erase(j); + + job->HandleResult(error, type, private_key, cert); + delete job; +} + +int ServerBoundCertService::cert_count() { + return server_bound_cert_store_->GetCertCount(); +} + +} // namespace net diff --git a/net/base/server_bound_cert_service.h b/net/base/server_bound_cert_service.h new file mode 100644 index 0000000..33b94fe --- /dev/null +++ b/net/base/server_bound_cert_service.h @@ -0,0 +1,135 @@ +// Copyright (c) 2012 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_SERVER_BOUND_CERT_SERVICE_H_ +#define NET_BASE_SERVER_BOUND_CERT_SERVICE_H_ +#pragma once + +#include +#include +#include + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/non_thread_safe.h" +#include "base/time.h" +#include "net/base/completion_callback.h" +#include "net/base/net_export.h" +#include "net/base/ssl_client_cert_type.h" + +namespace net { + +class ServerBoundCertServiceJob; +class ServerBoundCertServiceWorker; +class ServerBoundCertStore; + +// A class for creating and fetching server bound certs. +// Inherits from NonThreadSafe in order to use the function +// |CalledOnValidThread|. +class NET_EXPORT ServerBoundCertService + : NON_EXPORTED_BASE(public base::NonThreadSafe) { + public: + // Opaque type used to cancel a request. + typedef void* RequestHandle; + + // Password used on EncryptedPrivateKeyInfo data stored in EC private_key + // values. (This is not used to provide any security, but to workaround NSS + // being unable to import unencrypted PrivateKeyInfo for EC keys.) + static const char kEPKIPassword[]; + + // This object owns server_bound_cert_store. + explicit ServerBoundCertService( + ServerBoundCertStore* server_bound_cert_store); + + ~ServerBoundCertService(); + + // Returns the domain to be used for |host|. The domain is the + // "registry controlled domain", or the "ETLD + 1" where one exists, or + // the origin otherwise. + static std::string GetDomainForHost(const std::string& host); + + // Fetches the domain bound cert for the specified origin of the specified + // type if one exists and creates one otherwise. Returns OK if successful or + // an error code upon failure. + // + // |requested_types| is a list of the TLS ClientCertificateTypes the site will + // accept, ordered from most preferred to least preferred. Types we don't + // support will be ignored. See ssl_client_cert_type.h. + // + // On successful completion, |private_key| stores a DER-encoded + // PrivateKeyInfo struct, and |cert| stores a DER-encoded certificate, and + // |type| specifies the type of certificate that was returned. + // + // |callback| must not be null. ERR_IO_PENDING is returned if the operation + // could not be completed immediately, in which case the result code will + // be passed to the callback when available. + // + // |*out_req| will be filled with a handle to the async request. This handle + // is not valid after the request has completed. + int GetDomainBoundCert( + const std::string& origin, + const std::vector& requested_types, + SSLClientCertType* type, + std::string* private_key, + std::string* cert, + const CompletionCallback& callback, + RequestHandle* out_req); + + // Cancels the specified request. |req| is the handle returned by + // GetDomainBoundCert(). After a request is canceled, its completion + // callback will not be called. + void CancelRequest(RequestHandle req); + + // Returns the backing ServerBoundCertStore. + ServerBoundCertStore* GetCertStore(); + + // Public only for unit testing. + int cert_count(); + uint64 requests() const { return requests_; } + uint64 cert_store_hits() const { return cert_store_hits_; } + uint64 inflight_joins() const { return inflight_joins_; } + + private: + friend class ServerBoundCertServiceWorker; // Calls HandleResult. + + // On success, |private_key| stores a DER-encoded PrivateKeyInfo + // struct, |cert| stores a DER-encoded certificate, |creation_time| stores the + // start of the validity period of the certificate and |expiration_time| + // stores the expiration time of the certificate. Returns OK if successful and + // an error code otherwise. + // |serial_number| is passed in because it is created with the function + // base::RandInt, which opens the file /dev/urandom. /dev/urandom is opened + // with a LazyInstance, which is not allowed on a worker thread. + static int GenerateCert(const std::string& server_identifier, + SSLClientCertType type, + uint32 serial_number, + base::Time* creation_time, + base::Time* expiration_time, + std::string* private_key, + std::string* cert); + + void HandleResult(const std::string& server_identifier, + int error, + SSLClientCertType type, + base::Time creation_time, + base::Time expiration_time, + const std::string& private_key, + const std::string& cert); + + scoped_ptr server_bound_cert_store_; + + // inflight_ maps from a server to an active generation which is taking + // place. + std::map inflight_; + + uint64 requests_; + uint64 cert_store_hits_; + uint64 inflight_joins_; + + DISALLOW_COPY_AND_ASSIGN(ServerBoundCertService); +}; + +} // namespace net + +#endif // NET_BASE_SERVER_BOUND_CERT_SERVICE_H_ diff --git a/net/base/server_bound_cert_service_unittest.cc b/net/base/server_bound_cert_service_unittest.cc new file mode 100644 index 0000000..4804b70 --- /dev/null +++ b/net/base/server_bound_cert_service_unittest.cc @@ -0,0 +1,418 @@ +// Copyright (c) 2012 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/server_bound_cert_service.h" + +#include +#include + +#include "base/bind.h" +#include "base/memory/scoped_ptr.h" +#include "crypto/ec_private_key.h" +#include "net/base/asn1_util.h" +#include "net/base/default_server_bound_cert_store.h" +#include "net/base/net_errors.h" +#include "net/base/test_completion_callback.h" +#include "net/base/x509_certificate.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +namespace { + +void FailTest(int /* result */) { + FAIL(); +} + +TEST(ServerBoundCertServiceTest, GetDomainForHost) { + EXPECT_EQ("google.com", + ServerBoundCertService::GetDomainForHost("google.com")); + EXPECT_EQ("google.com", + ServerBoundCertService::GetDomainForHost("www.google.com")); + // NOTE(rch): we would like to segregate cookies and certificates for + // *.appspot.com, but currently we can not do that becaues we want to + // allow direct navigation to appspot.com. + EXPECT_EQ("appspot.com", + ServerBoundCertService::GetDomainForHost("foo.appspot.com")); + EXPECT_EQ("google.com", + ServerBoundCertService::GetDomainForHost("www.mail.google.com")); + EXPECT_EQ("goto", + ServerBoundCertService::GetDomainForHost("goto")); + EXPECT_EQ("127.0.0.1", + ServerBoundCertService::GetDomainForHost("127.0.0.1")); +} + +// See http://crbug.com/91512 - implement OpenSSL version of CreateSelfSigned. +#if !defined(USE_OPENSSL) + +TEST(ServerBoundCertServiceTest, CacheHit) { + scoped_ptr service( + new ServerBoundCertService(new DefaultServerBoundCertStore(NULL))); + std::string origin("https://encrypted.google.com:443"); + + int error; + std::vector types; + types.push_back(CLIENT_CERT_ECDSA_SIGN); + TestCompletionCallback callback; + ServerBoundCertService::RequestHandle request_handle; + + // Asynchronous completion. + SSLClientCertType type1; + std::string private_key_info1, der_cert1; + EXPECT_EQ(0, service->cert_count()); + error = service->GetDomainBoundCert( + origin, types, &type1, &private_key_info1, &der_cert1, + callback.callback(), &request_handle); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle != NULL); + error = callback.WaitForResult(); + EXPECT_EQ(OK, error); + EXPECT_EQ(1, service->cert_count()); + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); + EXPECT_FALSE(private_key_info1.empty()); + EXPECT_FALSE(der_cert1.empty()); + + // Synchronous completion. + SSLClientCertType type2; + std::string private_key_info2, der_cert2; + error = service->GetDomainBoundCert( + origin, types, &type2, &private_key_info2, &der_cert2, + callback.callback(), &request_handle); + EXPECT_TRUE(request_handle == NULL); + EXPECT_EQ(OK, error); + EXPECT_EQ(1, service->cert_count()); + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); + EXPECT_EQ(private_key_info1, private_key_info2); + EXPECT_EQ(der_cert1, der_cert2); + + EXPECT_EQ(2u, service->requests()); + EXPECT_EQ(1u, service->cert_store_hits()); + EXPECT_EQ(0u, service->inflight_joins()); +} + +TEST(ServerBoundCertServiceTest, UnsupportedTypes) { + scoped_ptr service( + new ServerBoundCertService(new DefaultServerBoundCertStore(NULL))); + std::string origin("https://encrypted.google.com:443"); + + int error; + std::vector types; + TestCompletionCallback callback; + ServerBoundCertService::RequestHandle request_handle; + + // Empty requested_types. + SSLClientCertType type1; + std::string private_key_info1, der_cert1; + error = service->GetDomainBoundCert( + origin, types, &type1, &private_key_info1, &der_cert1, + callback.callback(), &request_handle); + EXPECT_EQ(ERR_INVALID_ARGUMENT, error); + EXPECT_TRUE(request_handle == NULL); + + // No supported types in requested_types. + types.push_back(CLIENT_CERT_RSA_SIGN); + types.push_back(2); + types.push_back(3); + error = service->GetDomainBoundCert( + origin, types, &type1, &private_key_info1, &der_cert1, + callback.callback(), &request_handle); + EXPECT_EQ(ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED, error); + EXPECT_TRUE(request_handle == NULL); + + // Supported types after unsupported ones in requested_types. + types.push_back(CLIENT_CERT_ECDSA_SIGN); + // Asynchronous completion. + EXPECT_EQ(0, service->cert_count()); + error = service->GetDomainBoundCert( + origin, types, &type1, &private_key_info1, &der_cert1, + callback.callback(), &request_handle); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle != NULL); + error = callback.WaitForResult(); + EXPECT_EQ(OK, error); + EXPECT_EQ(1, service->cert_count()); + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); + EXPECT_FALSE(private_key_info1.empty()); + EXPECT_FALSE(der_cert1.empty()); + + // Now that the cert is created, doing requests for unsupported types + // shouldn't affect the created cert. + // Empty requested_types. + types.clear(); + SSLClientCertType type2; + std::string private_key_info2, der_cert2; + error = service->GetDomainBoundCert( + origin, types, &type2, &private_key_info2, &der_cert2, + callback.callback(), &request_handle); + EXPECT_EQ(ERR_INVALID_ARGUMENT, error); + EXPECT_TRUE(request_handle == NULL); + + // No supported types in requested_types. + types.push_back(CLIENT_CERT_RSA_SIGN); + types.push_back(2); + types.push_back(3); + error = service->GetDomainBoundCert( + origin, types, &type2, &private_key_info2, &der_cert2, + callback.callback(), &request_handle); + EXPECT_EQ(ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED, error); + EXPECT_TRUE(request_handle == NULL); + + // If we request EC, the cert we created before should still be there. + types.push_back(CLIENT_CERT_ECDSA_SIGN); + error = service->GetDomainBoundCert( + origin, types, &type2, &private_key_info2, &der_cert2, + callback.callback(), &request_handle); + EXPECT_TRUE(request_handle == NULL); + EXPECT_EQ(OK, error); + EXPECT_EQ(1, service->cert_count()); + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); + EXPECT_EQ(private_key_info1, private_key_info2); + EXPECT_EQ(der_cert1, der_cert2); +} + +TEST(ServerBoundCertServiceTest, StoreCerts) { + scoped_ptr service( + new ServerBoundCertService(new DefaultServerBoundCertStore(NULL))); + int error; + std::vector types; + types.push_back(CLIENT_CERT_ECDSA_SIGN); + TestCompletionCallback callback; + ServerBoundCertService::RequestHandle request_handle; + + std::string origin1("https://encrypted.google.com:443"); + SSLClientCertType type1; + std::string private_key_info1, der_cert1; + EXPECT_EQ(0, service->cert_count()); + error = service->GetDomainBoundCert( + origin1, types, &type1, &private_key_info1, &der_cert1, + callback.callback(), &request_handle); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle != NULL); + error = callback.WaitForResult(); + EXPECT_EQ(OK, error); + EXPECT_EQ(1, service->cert_count()); + + std::string origin2("https://www.verisign.com:443"); + SSLClientCertType type2; + std::string private_key_info2, der_cert2; + error = service->GetDomainBoundCert( + origin2, types, &type2, &private_key_info2, &der_cert2, + callback.callback(), &request_handle); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle != NULL); + error = callback.WaitForResult(); + EXPECT_EQ(OK, error); + EXPECT_EQ(2, service->cert_count()); + + std::string origin3("https://www.twitter.com:443"); + SSLClientCertType type3; + std::string private_key_info3, der_cert3; + error = service->GetDomainBoundCert( + origin3, types, &type3, &private_key_info3, &der_cert3, + callback.callback(), &request_handle); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle != NULL); + error = callback.WaitForResult(); + EXPECT_EQ(OK, error); + EXPECT_EQ(3, service->cert_count()); + + EXPECT_NE(private_key_info1, private_key_info2); + EXPECT_NE(der_cert1, der_cert2); + EXPECT_NE(private_key_info1, private_key_info3); + EXPECT_NE(der_cert1, der_cert3); + EXPECT_NE(private_key_info2, private_key_info3); + EXPECT_NE(der_cert2, der_cert3); + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type3); +} + +// Tests an inflight join. +TEST(ServerBoundCertServiceTest, InflightJoin) { + scoped_ptr service( + new ServerBoundCertService(new DefaultServerBoundCertStore(NULL))); + std::string origin("https://encrypted.google.com:443"); + int error; + std::vector types; + types.push_back(CLIENT_CERT_ECDSA_SIGN); + + SSLClientCertType type1; + std::string private_key_info1, der_cert1; + TestCompletionCallback callback1; + ServerBoundCertService::RequestHandle request_handle1; + + SSLClientCertType type2; + std::string private_key_info2, der_cert2; + TestCompletionCallback callback2; + ServerBoundCertService::RequestHandle request_handle2; + + error = service->GetDomainBoundCert( + origin, types, &type1, &private_key_info1, &der_cert1, + callback1.callback(), &request_handle1); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle1 != NULL); + // If we request RSA and EC in the 2nd request, should still join with the + // original request. + types.insert(types.begin(), CLIENT_CERT_RSA_SIGN); + error = service->GetDomainBoundCert( + origin, types, &type2, &private_key_info2, &der_cert2, + callback2.callback(), &request_handle2); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle2 != NULL); + + error = callback1.WaitForResult(); + EXPECT_EQ(OK, error); + error = callback2.WaitForResult(); + EXPECT_EQ(OK, error); + + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); + EXPECT_EQ(2u, service->requests()); + EXPECT_EQ(0u, service->cert_store_hits()); + EXPECT_EQ(1u, service->inflight_joins()); +} + +TEST(ServerBoundCertServiceTest, ExtractValuesFromBytesEC) { + scoped_ptr service( + new ServerBoundCertService(new DefaultServerBoundCertStore(NULL))); + std::string origin("https://encrypted.google.com:443"); + SSLClientCertType type; + std::string private_key_info, der_cert; + int error; + std::vector types; + types.push_back(CLIENT_CERT_ECDSA_SIGN); + TestCompletionCallback callback; + ServerBoundCertService::RequestHandle request_handle; + + error = service->GetDomainBoundCert( + origin, types, &type, &private_key_info, &der_cert, callback.callback(), + &request_handle); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle != NULL); + error = callback.WaitForResult(); + EXPECT_EQ(OK, error); + + base::StringPiece spki_piece; + ASSERT_TRUE(asn1::ExtractSPKIFromDERCert(der_cert, &spki_piece)); + std::vector spki( + spki_piece.data(), + spki_piece.data() + spki_piece.size()); + + // Check that we can retrieve the key from the bytes. + std::vector key_vec(private_key_info.begin(), private_key_info.end()); + scoped_ptr private_key( + crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( + ServerBoundCertService::kEPKIPassword, key_vec, spki)); + EXPECT_TRUE(private_key != NULL); + + // Check that we can retrieve the cert from the bytes. + scoped_refptr x509cert( + X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size())); + EXPECT_TRUE(x509cert != NULL); +} + +// Tests that the callback of a canceled request is never made. +TEST(ServerBoundCertServiceTest, CancelRequest) { + scoped_ptr service( + new ServerBoundCertService(new DefaultServerBoundCertStore(NULL))); + std::string origin("https://encrypted.google.com:443"); + SSLClientCertType type; + std::string private_key_info, der_cert; + int error; + std::vector types; + types.push_back(CLIENT_CERT_ECDSA_SIGN); + ServerBoundCertService::RequestHandle request_handle; + + error = service->GetDomainBoundCert(origin, + types, + &type, + &private_key_info, + &der_cert, + base::Bind(&FailTest), + &request_handle); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle != NULL); + service->CancelRequest(request_handle); + + // Issue a few more requests to the worker pool and wait for their + // completion, so that the task of the canceled request (which runs on a + // worker thread) is likely to complete by the end of this test. + TestCompletionCallback callback; + for (int i = 0; i < 5; ++i) { + error = service->GetDomainBoundCert( + "https://foo" + std::string(1, (char) ('1' + i)), + types, + &type, + &private_key_info, + &der_cert, + callback.callback(), + &request_handle); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle != NULL); + error = callback.WaitForResult(); + } + + // Even though the original request was cancelled, the service will still + // store the result, it just doesn't call the callback. + EXPECT_EQ(6, service->cert_count()); +} + +TEST(ServerBoundCertServiceTest, Expiration) { + ServerBoundCertStore* store = new DefaultServerBoundCertStore(NULL); + base::Time now = base::Time::Now(); + store->SetServerBoundCert("good", + CLIENT_CERT_ECDSA_SIGN, + now, + now + base::TimeDelta::FromDays(1), + "a", + "b"); + store->SetServerBoundCert("expired", + CLIENT_CERT_ECDSA_SIGN, + now - base::TimeDelta::FromDays(2), + now - base::TimeDelta::FromDays(1), + "c", + "d"); + ServerBoundCertService service(store); + EXPECT_EQ(2, service.cert_count()); + + int error; + std::vector types; + types.push_back(CLIENT_CERT_ECDSA_SIGN); + TestCompletionCallback callback; + ServerBoundCertService::RequestHandle request_handle; + + // Cert still valid - synchronous completion. + SSLClientCertType type1; + std::string private_key_info1, der_cert1; + error = service.GetDomainBoundCert( + "https://good", types, &type1, &private_key_info1, &der_cert1, + callback.callback(), &request_handle); + EXPECT_EQ(OK, error); + EXPECT_TRUE(request_handle == NULL); + EXPECT_EQ(2, service.cert_count()); + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); + EXPECT_STREQ("a", private_key_info1.c_str()); + EXPECT_STREQ("b", der_cert1.c_str()); + + // Cert expired - New cert will be generated, asynchronous completion. + SSLClientCertType type2; + std::string private_key_info2, der_cert2; + error = service.GetDomainBoundCert( + "https://expired", types, &type2, &private_key_info2, &der_cert2, + callback.callback(), &request_handle); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle != NULL); + error = callback.WaitForResult(); + EXPECT_EQ(OK, error); + EXPECT_EQ(2, service.cert_count()); + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); + EXPECT_LT(1U, private_key_info2.size()); + EXPECT_LT(1U, der_cert2.size()); +} + +#endif // !defined(USE_OPENSSL) + +} // namespace + +} // namespace net diff --git a/net/base/server_bound_cert_store.cc b/net/base/server_bound_cert_store.cc new file mode 100644 index 0000000..faed54b --- /dev/null +++ b/net/base/server_bound_cert_store.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2012 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/server_bound_cert_store.h" + +namespace net { + +ServerBoundCertStore::ServerBoundCert::ServerBoundCert() + : type_(CLIENT_CERT_INVALID_TYPE) { +} + +ServerBoundCertStore::ServerBoundCert::ServerBoundCert( + const std::string& server_identifier, + SSLClientCertType type, + base::Time creation_time, + base::Time expiration_time, + const std::string& private_key, + const std::string& cert) + : server_identifier_(server_identifier), + type_(type), + creation_time_(creation_time), + expiration_time_(expiration_time), + private_key_(private_key), + cert_(cert) {} + +ServerBoundCertStore::ServerBoundCert::~ServerBoundCert() {} + +} // namespace net diff --git a/net/base/server_bound_cert_store.h b/net/base/server_bound_cert_store.h new file mode 100644 index 0000000..02dfe52 --- /dev/null +++ b/net/base/server_bound_cert_store.h @@ -0,0 +1,117 @@ +// Copyright (c) 2012 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_SERVER_BOUND_CERT_STORE_H_ +#define NET_BASE_SERVER_BOUND_CERT_STORE_H_ +#pragma once + +#include +#include + +#include "base/time.h" +#include "net/base/net_export.h" +#include "net/base/ssl_client_cert_type.h" + +namespace net { + +// An interface for storing and retrieving server bound certs. +// There isn't a domain bound certs spec yet, but the old origin bound +// certificates are specified in +// http://balfanz.github.com/tls-obc-spec/draft-balfanz-tls-obc-01.html. + +// Owned only by a single ServerBoundCertService object, which is responsible +// for deleting it. +class NET_EXPORT ServerBoundCertStore { + public: + // The ServerBoundCert class contains a private key in addition to the server + // cert, and cert type. + class NET_EXPORT ServerBoundCert { + public: + ServerBoundCert(); + ServerBoundCert(const std::string& server_identifier, + SSLClientCertType type, + base::Time creation_time, + base::Time expiration_time, + const std::string& private_key, + const std::string& cert); + ~ServerBoundCert(); + + // Server identifier. For domain bound certs, for instance "verisign.com". + const std::string& server_identifier() const { return server_identifier_; } + // TLS ClientCertificateType. + SSLClientCertType type() const { return type_; } + // The time the certificate was created, also the start of the certificate + // validity period. + base::Time creation_time() const { return creation_time_; } + // The time after which this certificate is no longer valid. + base::Time expiration_time() const { return expiration_time_; } + // The encoding of the private key depends on the type. + // rsa_sign: DER-encoded PrivateKeyInfo struct. + // ecdsa_sign: DER-encoded EncryptedPrivateKeyInfo struct. + const std::string& private_key() const { return private_key_; } + // DER-encoded certificate. + const std::string& cert() const { return cert_; } + + private: + std::string server_identifier_; + SSLClientCertType type_; + base::Time creation_time_; + base::Time expiration_time_; + std::string private_key_; + std::string cert_; + }; + + virtual ~ServerBoundCertStore() {} + + // 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, |cert_result| stores a DER-encoded certificate, + // |type| is the ClientCertificateType of the returned certificate, + // |creation_time| stores the start of the validity period of the certificate + // and |expiration_time| is the expiration time of the certificate. + // Returns false if no server bound cert exists for the specified server. + virtual bool GetServerBoundCert( + const std::string& server_identifier, + SSLClientCertType* type, + base::Time* creation_time, + base::Time* expiration_time, + std::string* private_key_result, + std::string* cert_result) = 0; + + // Adds a server bound cert and the corresponding private key to the store. + virtual void SetServerBoundCert( + const std::string& server_identifier, + SSLClientCertType type, + base::Time creation_time, + base::Time expiration_time, + const std::string& private_key, + const std::string& cert) = 0; + + // Removes a server bound cert and the corresponding private key from the + // store. + virtual void DeleteServerBoundCert(const std::string& server_identifier) = 0; + + // Deletes all of the server bound certs that have a creation_date greater + // than or equal to |delete_begin| and less than |delete_end|. If a + // base::Time value is_null, that side of the comparison is unbounded. + virtual void DeleteAllCreatedBetween(base::Time delete_begin, + base::Time delete_end) = 0; + + // Removes all server bound certs and the corresponding private keys from + // the store. + virtual void DeleteAll() = 0; + + // Returns all server bound certs and the corresponding private keys. + virtual void GetAllServerBoundCerts( + std::vector* server_bound_certs) = 0; + + // Returns the number of certs in the store. + // Public only for unit testing. + virtual int GetCertCount() = 0; +}; + +} // namespace net + +#endif // NET_BASE_SERVER_BOUND_CERT_STORE_H_ diff --git a/net/net.gyp b/net/net.gyp index 4fe5200..f9844a1 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -92,8 +92,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/default_server_bound_cert_store.cc', + 'base/default_server_bound_cert_store.h', 'base/directory_lister.cc', 'base/directory_lister.h', 'base/dns_reloader.cc', @@ -202,10 +202,6 @@ 'base/openssl_memory_private_key_store.cc', 'base/openssl_private_key_store.h', 'base/openssl_private_key_store_android.cc', - 'base/origin_bound_cert_service.cc', - 'base/origin_bound_cert_service.h', - 'base/origin_bound_cert_store.cc', - 'base/origin_bound_cert_store.h', 'base/pem_tokenizer.cc', 'base/pem_tokenizer.h', 'base/platform_mime_util.h', @@ -224,6 +220,10 @@ 'base/sdch_filter.h', 'base/sdch_manager.cc', 'base/sdch_manager.h', + 'base/server_bound_cert_service.cc', + 'base/server_bound_cert_service.h', + 'base/server_bound_cert_store.cc', + 'base/server_bound_cert_store.h', 'base/single_request_cert_verifier.cc', 'base/single_request_cert_verifier.h', 'base/single_request_host_resolver.cc', @@ -1047,7 +1047,7 @@ 'base/cert_database_nss_unittest.cc', 'base/crl_set_unittest.cc', 'base/data_url_unittest.cc', - 'base/default_origin_bound_cert_store_unittest.cc', + 'base/default_server_bound_cert_store_unittest.cc', 'base/directory_lister_unittest.cc', 'base/dnssec_unittest.cc', 'base/dns_util_unittest.cc', @@ -1077,13 +1077,13 @@ 'base/net_util_unittest.cc', 'base/network_change_notifier_linux_unittest.cc', 'base/network_change_notifier_win_unittest.cc', - 'base/origin_bound_cert_service_unittest.cc', 'base/pem_tokenizer_unittest.cc', 'base/prioritized_dispatcher_unittest.cc', 'base/priority_queue_unittest.cc', 'base/registry_controlled_domain_unittest.cc', 'base/run_all_unittests.cc', 'base/sdch_filter_unittest.cc', + 'base/server_bound_cert_service_unittest.cc', 'base/single_request_host_resolver_unittest.cc', 'base/ssl_cipher_suite_names_unittest.cc', 'base/ssl_client_auth_cache_unittest.cc', diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h index 49343d1..0860276 100644 --- a/net/socket/ssl_client_socket_nss.h +++ b/net/socket/ssl_client_socket_nss.h @@ -25,7 +25,7 @@ #include "net/base/net_export.h" #include "net/base/net_log.h" #include "net/base/nss_memio.h" -#include "net/base/origin_bound_cert_service.h" +#include "net/base/server_bound_cert_service.h" #include "net/base/ssl_config_service.h" #include "net/base/x509_certificate.h" #include "net/socket/ssl_client_socket.h" diff --git a/net/spdy/spdy_http_stream_spdy2_unittest.cc b/net/spdy/spdy_http_stream_spdy2_unittest.cc index b45de55..c002e33 100644 --- a/net/spdy/spdy_http_stream_spdy2_unittest.cc +++ b/net/spdy/spdy_http_stream_spdy2_unittest.cc @@ -8,7 +8,7 @@ #include "crypto/ec_signature_creator.h" #include "crypto/signature_creator.h" #include "net/base/asn1_util.h" -#include "net/base/default_origin_bound_cert_store.h" +#include "net/base/default_server_bound_cert_store.h" #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" #include "net/spdy/spdy_session.h" diff --git a/net/spdy/spdy_http_stream_spdy3_unittest.cc b/net/spdy/spdy_http_stream_spdy3_unittest.cc index 92ebf4a..c130ad5 100644 --- a/net/spdy/spdy_http_stream_spdy3_unittest.cc +++ b/net/spdy/spdy_http_stream_spdy3_unittest.cc @@ -8,7 +8,7 @@ #include "crypto/ec_signature_creator.h" #include "crypto/signature_creator.h" #include "net/base/asn1_util.h" -#include "net/base/default_origin_bound_cert_store.h" +#include "net/base/default_server_bound_cert_store.h" #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" #include "net/spdy/spdy_session.h" diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index 0fdc5ef..7767e4f 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -26,7 +26,7 @@ #include "net/base/connection_type_histograms.h" #include "net/base/net_log.h" #include "net/base/net_util.h" -#include "net/base/origin_bound_cert_service.h" +#include "net/base/server_bound_cert_service.h" #include "net/http/http_network_session.h" #include "net/http/http_server_properties.h" #include "net/spdy/spdy_frame_builder.h" diff --git a/net/spdy/spdy_stream.h b/net/spdy/spdy_stream.h index 4741ad4..84e9fbd 100644 --- a/net/spdy/spdy_stream.h +++ b/net/spdy/spdy_stream.h @@ -18,7 +18,7 @@ #include "net/base/io_buffer.h" #include "net/base/net_export.h" #include "net/base/net_log.h" -#include "net/base/origin_bound_cert_service.h" +#include "net/base/server_bound_cert_service.h" #include "net/base/ssl_client_cert_type.h" #include "net/base/upload_data.h" #include "net/socket/ssl_client_socket.h" diff --git a/net/url_request/url_request_context_storage.cc b/net/url_request/url_request_context_storage.cc index 2c9f816..ed7950e 100644 --- a/net/url_request/url_request_context_storage.cc +++ b/net/url_request/url_request_context_storage.cc @@ -9,7 +9,7 @@ #include "net/base/host_resolver.h" #include "net/base/net_log.h" #include "net/base/network_delegate.h" -#include "net/base/origin_bound_cert_service.h" +#include "net/base/server_bound_cert_service.h" #include "net/cookies/cookie_store.h" #include "net/ftp/ftp_transaction_factory.h" #include "net/http/http_auth_handler_factory.h" diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc index 2ed65bd..5fc4f72 100644 --- a/net/url_request/url_request_test_util.cc +++ b/net/url_request/url_request_test_util.cc @@ -9,9 +9,9 @@ #include "base/message_loop.h" #include "base/threading/thread.h" #include "net/base/cert_verifier.h" -#include "net/base/default_origin_bound_cert_store.h" +#include "net/base/default_server_bound_cert_store.h" #include "net/base/host_port_pair.h" -#include "net/base/origin_bound_cert_service.h" +#include "net/base/server_bound_cert_service.h" #include "net/http/http_network_session.h" #include "net/http/http_server_properties_impl.h" #include "net/url_request/url_request_job_factory.h" -- cgit v1.1