diff options
author | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-06 04:21:09 +0000 |
---|---|---|
committer | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-06 04:21:09 +0000 |
commit | 550cee900473ff9d964a9c4d18e1b3670391967b (patch) | |
tree | 20a03e6d39a2274c831b0fe121ad0c7f14b1c688 /net/base | |
parent | 115bee94449b0594f797973aa05992bbbd6c61d3 (diff) | |
download | chromium_src-550cee900473ff9d964a9c4d18e1b3670391967b.zip chromium_src-550cee900473ff9d964a9c4d18e1b3670391967b.tar.gz chromium_src-550cee900473ff9d964a9c4d18e1b3670391967b.tar.bz2 |
Support EC certs in OriginBoundCertService and OriginBoundCertStore.
OriginBoundCertService::GetOriginBoundCert takes a vector of requested cert
types in order of preferrence and will return or generate one of an acceptable
type.
BUG=88782
TEST=net_unittests, unit_tests
Review URL: http://codereview.chromium.org/8662036
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113108 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base')
-rw-r--r-- | net/base/default_origin_bound_cert_store.cc | 29 | ||||
-rw-r--r-- | net/base/default_origin_bound_cert_store.h | 40 | ||||
-rw-r--r-- | net/base/default_origin_bound_cert_store_unittest.cc | 81 | ||||
-rw-r--r-- | net/base/net_error_list.h | 7 | ||||
-rw-r--r-- | net/base/origin_bound_cert_service.cc | 180 | ||||
-rw-r--r-- | net/base/origin_bound_cert_service.h | 35 | ||||
-rw-r--r-- | net/base/origin_bound_cert_service_unittest.cc | 279 | ||||
-rw-r--r-- | net/base/origin_bound_cert_store.cc | 23 | ||||
-rw-r--r-- | net/base/origin_bound_cert_store.h | 52 | ||||
-rw-r--r-- | net/base/ssl_client_cert_type.h | 22 |
10 files changed, 589 insertions, 159 deletions
diff --git a/net/base/default_origin_bound_cert_store.cc b/net/base/default_origin_bound_cert_store.cc index 82aec7d..8104658 100644 --- a/net/base/default_origin_bound_cert_store.cc +++ b/net/base/default_origin_bound_cert_store.cc @@ -29,6 +29,7 @@ void DefaultOriginBoundCertStore::FlushStore( bool DefaultOriginBoundCertStore::GetOriginBoundCert( const std::string& origin, + SSLClientCertType* type, std::string* private_key_result, std::string* cert_result) { base::AutoLock autolock(lock_); @@ -40,6 +41,7 @@ bool DefaultOriginBoundCertStore::GetOriginBoundCert( return false; OriginBoundCert* cert = it->second; + *type = cert->type(); *private_key_result = cert->private_key(); *cert_result = cert->cert(); @@ -48,14 +50,15 @@ bool DefaultOriginBoundCertStore::GetOriginBoundCert( void DefaultOriginBoundCertStore::SetOriginBoundCert( const std::string& origin, + SSLClientCertType type, const std::string& private_key, const std::string& cert) { base::AutoLock autolock(lock_); InitIfNecessary(); InternalDeleteOriginBoundCert(origin); - InternalInsertOriginBoundCert(origin, - new OriginBoundCert(origin, private_key, cert)); + InternalInsertOriginBoundCert( + origin, new OriginBoundCert(origin, type, private_key, cert)); } void DefaultOriginBoundCertStore::DeleteOriginBoundCert( @@ -79,20 +82,12 @@ void DefaultOriginBoundCertStore::DeleteAll() { } void DefaultOriginBoundCertStore::GetAllOriginBoundCerts( - std::vector<OriginBoundCertInfo>* origin_bound_certs) { + std::vector<OriginBoundCert>* origin_bound_certs) { base::AutoLock autolock(lock_); InitIfNecessary(); for (OriginBoundCertMap::iterator it = origin_bound_certs_.begin(); it != origin_bound_certs_.end(); ++it) { - OriginBoundCertInfo cert_info = { - it->second->origin(), - it->second->private_key(), - it->second->cert() - }; - // TODO(rkn): Make changes so we can simply write - // origin_bound_certs->push_back(*it->second). This is probably best done - // by unnesting the OriginBoundCert class. - origin_bound_certs->push_back(cert_info); + origin_bound_certs->push_back(*it->second); } } @@ -160,16 +155,6 @@ void DefaultOriginBoundCertStore::InternalInsertOriginBoundCert( origin_bound_certs_[origin] = cert; } -DefaultOriginBoundCertStore::OriginBoundCert::OriginBoundCert() {} - -DefaultOriginBoundCertStore::OriginBoundCert::OriginBoundCert( - const std::string& origin, - const std::string& private_key, - const std::string& cert) - : origin_(origin), - private_key_(private_key), - cert_(cert) {} - DefaultOriginBoundCertStore::PersistentStore::PersistentStore() {} } // namespace net diff --git a/net/base/default_origin_bound_cert_store.h b/net/base/default_origin_bound_cert_store.h index 05dd70b..f5e0394 100644 --- a/net/base/default_origin_bound_cert_store.h +++ b/net/base/default_origin_bound_cert_store.h @@ -31,7 +31,6 @@ namespace net { // by IO and origin bound cert management UI. class NET_EXPORT DefaultOriginBoundCertStore : public OriginBoundCertStore { public: - class OriginBoundCert; class PersistentStore; // The key for each OriginBoundCert* in OriginBoundCertMap is the @@ -55,16 +54,20 @@ class NET_EXPORT DefaultOriginBoundCertStore : public OriginBoundCertStore { void FlushStore(const base::Closure& completion_task); // OriginBoundCertStore implementation. - virtual bool GetOriginBoundCert(const std::string& origin, - std::string* private_key_result, - std::string* cert_result) OVERRIDE; - virtual void SetOriginBoundCert(const std::string& origin, - const std::string& private_key, - const std::string& cert) OVERRIDE; + virtual bool GetOriginBoundCert( + const std::string& origin, + SSLClientCertType* type, + std::string* private_key_result, + std::string* cert_result) OVERRIDE; + virtual void SetOriginBoundCert( + const std::string& origin, + SSLClientCertType type, + const std::string& private_key, + const std::string& cert) OVERRIDE; virtual void DeleteOriginBoundCert(const std::string& origin) OVERRIDE; virtual void DeleteAll() OVERRIDE; virtual void GetAllOriginBoundCerts( - std::vector<OriginBoundCertInfo>* origin_bound_certs) OVERRIDE; + std::vector<OriginBoundCert>* origin_bound_certs) OVERRIDE; virtual int GetCertCount() OVERRIDE; private: @@ -113,25 +116,6 @@ class NET_EXPORT DefaultOriginBoundCertStore : public OriginBoundCertStore { DISALLOW_COPY_AND_ASSIGN(DefaultOriginBoundCertStore); }; -// The OriginBoundCert class contains a private key in addition to the origin -// and the cert. -class NET_EXPORT DefaultOriginBoundCertStore::OriginBoundCert { - public: - OriginBoundCert(); - OriginBoundCert(const std::string& origin, - const std::string& privatekey, - const std::string& cert); - - const std::string& origin() const { return origin_; } - const std::string& private_key() const { return private_key_; } - const std::string& cert() const { return cert_; } - - private: - std::string origin_; - std::string private_key_; - std::string cert_; -}; - typedef base::RefCountedThreadSafe<DefaultOriginBoundCertStore::PersistentStore> RefcountedPersistentStore; @@ -144,7 +128,7 @@ class NET_EXPORT DefaultOriginBoundCertStore::PersistentStore // called only once at startup. Note that the certs are individually allocated // and that ownership is transferred to the caller upon return. virtual bool Load( - std::vector<DefaultOriginBoundCertStore::OriginBoundCert*>* certs) = 0; + std::vector<OriginBoundCert*>* certs) = 0; virtual void AddOriginBoundCert(const OriginBoundCert& cert) = 0; diff --git a/net/base/default_origin_bound_cert_store_unittest.cc b/net/base/default_origin_bound_cert_store_unittest.cc index 45356e8..6b888e0 100644 --- a/net/base/default_origin_bound_cert_store_unittest.cc +++ b/net/base/default_origin_bound_cert_store_unittest.cc @@ -77,33 +77,40 @@ TEST(DefaultOriginBoundCertStoreTest, TestLoading) { persistent_store->AddOriginBoundCert( DefaultOriginBoundCertStore::OriginBoundCert( - "https://encrypted.google.com/", "a", "b")); + "https://encrypted.google.com/", CLIENT_CERT_RSA_SIGN, "a", "b")); persistent_store->AddOriginBoundCert( DefaultOriginBoundCertStore::OriginBoundCert( - "https://www.verisign.com/", "c", "d")); + "https://www.verisign.com/", CLIENT_CERT_ECDSA_SIGN, "c", "d")); // Make sure certs load properly. DefaultOriginBoundCertStore store(persistent_store.get()); EXPECT_EQ(2, store.GetCertCount()); - store.SetOriginBoundCert("https://www.verisign.com/", "e", "f"); + store.SetOriginBoundCert( + "https://www.verisign.com/", CLIENT_CERT_RSA_SIGN, "e", "f"); EXPECT_EQ(2, store.GetCertCount()); - store.SetOriginBoundCert("https://www.twitter.com/", "g", "h"); + store.SetOriginBoundCert( + "https://www.twitter.com/", CLIENT_CERT_RSA_SIGN, "g", "h"); EXPECT_EQ(3, store.GetCertCount()); } TEST(DefaultOriginBoundCertStoreTest, TestSettingAndGetting) { DefaultOriginBoundCertStore store(NULL); + SSLClientCertType type; std::string private_key, cert; EXPECT_EQ(0, store.GetCertCount()); EXPECT_FALSE(store.GetOriginBoundCert("https://www.verisign.com/", - &private_key, - &cert)); + &type, + &private_key, + &cert)); EXPECT_TRUE(private_key.empty()); EXPECT_TRUE(cert.empty()); - store.SetOriginBoundCert("https://www.verisign.com/", "i", "j"); + store.SetOriginBoundCert( + "https://www.verisign.com/", CLIENT_CERT_RSA_SIGN, "i", "j"); EXPECT_TRUE(store.GetOriginBoundCert("https://www.verisign.com/", - &private_key, - &cert)); + &type, + &private_key, + &cert)); + EXPECT_EQ(CLIENT_CERT_RSA_SIGN, type); EXPECT_EQ("i", private_key); EXPECT_EQ("j", cert); } @@ -112,15 +119,20 @@ TEST(DefaultOriginBoundCertStoreTest, TestDuplicateCerts) { scoped_refptr<MockPersistentStore> persistent_store(new MockPersistentStore); DefaultOriginBoundCertStore store(persistent_store.get()); + SSLClientCertType type; std::string private_key, cert; EXPECT_EQ(0, store.GetCertCount()); - store.SetOriginBoundCert("https://www.verisign.com/", "a", "b"); - store.SetOriginBoundCert("https://www.verisign.com/", "c", "d"); + store.SetOriginBoundCert( + "https://www.verisign.com/", CLIENT_CERT_RSA_SIGN, "a", "b"); + store.SetOriginBoundCert( + "https://www.verisign.com/", CLIENT_CERT_ECDSA_SIGN, "c", "d"); EXPECT_EQ(1, store.GetCertCount()); EXPECT_TRUE(store.GetOriginBoundCert("https://www.verisign.com/", - &private_key, - &cert)); + &type, + &private_key, + &cert)); + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type); EXPECT_EQ("c", private_key); EXPECT_EQ("d", cert); } @@ -130,9 +142,12 @@ TEST(DefaultOriginBoundCertStoreTest, TestDeleteAll) { DefaultOriginBoundCertStore store(persistent_store.get()); EXPECT_EQ(0, store.GetCertCount()); - store.SetOriginBoundCert("https://www.verisign.com/", "a", "b"); - store.SetOriginBoundCert("https://www.google.com/", "c", "d"); - store.SetOriginBoundCert("https://www.harvard.com/", "e", "f"); + store.SetOriginBoundCert( + "https://www.verisign.com/", CLIENT_CERT_RSA_SIGN, "a", "b"); + store.SetOriginBoundCert( + "https://www.google.com/", CLIENT_CERT_RSA_SIGN, "c", "d"); + store.SetOriginBoundCert( + "https://www.harvard.com/", CLIENT_CERT_RSA_SIGN, "e", "f"); EXPECT_EQ(3, store.GetCertCount()); store.DeleteAll(); @@ -143,25 +158,31 @@ TEST(DefaultOriginBoundCertStoreTest, TestDelete) { scoped_refptr<MockPersistentStore> persistent_store(new MockPersistentStore); DefaultOriginBoundCertStore store(persistent_store.get()); + SSLClientCertType type; std::string private_key, cert; EXPECT_EQ(0, store.GetCertCount()); - store.SetOriginBoundCert("https://www.verisign.com/", "a", "b"); - store.SetOriginBoundCert("https://www.google.com/", "c", "d"); + store.SetOriginBoundCert( + "https://www.verisign.com/", CLIENT_CERT_RSA_SIGN, "a", "b"); + store.SetOriginBoundCert( + "https://www.google.com/", CLIENT_CERT_ECDSA_SIGN, "c", "d"); EXPECT_EQ(2, store.GetCertCount()); store.DeleteOriginBoundCert("https://www.verisign.com/"); EXPECT_EQ(1, store.GetCertCount()); EXPECT_FALSE(store.GetOriginBoundCert("https://www.verisign.com/", - &private_key, - &cert)); - EXPECT_TRUE(store.GetOriginBoundCert("https://www.google.com/", + &type, &private_key, &cert)); + EXPECT_TRUE(store.GetOriginBoundCert("https://www.google.com/", + &type, + &private_key, + &cert)); store.DeleteOriginBoundCert("https://www.google.com/"); EXPECT_EQ(0, store.GetCertCount()); EXPECT_FALSE(store.GetOriginBoundCert("https://www.google.com/", - &private_key, - &cert)); + &type, + &private_key, + &cert)); } TEST(DefaultOriginBoundCertStoreTest, TestGetAll) { @@ -169,13 +190,17 @@ TEST(DefaultOriginBoundCertStoreTest, TestGetAll) { DefaultOriginBoundCertStore store(persistent_store.get()); EXPECT_EQ(0, store.GetCertCount()); - store.SetOriginBoundCert("https://www.verisign.com/", "a", "b"); - store.SetOriginBoundCert("https://www.google.com/", "c", "d"); - store.SetOriginBoundCert("https://www.harvard.com/", "e", "f"); - store.SetOriginBoundCert("https://www.mit.com/", "g", "h"); + store.SetOriginBoundCert( + "https://www.verisign.com/", CLIENT_CERT_RSA_SIGN, "a", "b"); + store.SetOriginBoundCert( + "https://www.google.com/", CLIENT_CERT_ECDSA_SIGN, "c", "d"); + store.SetOriginBoundCert( + "https://www.harvard.com/", CLIENT_CERT_RSA_SIGN, "e", "f"); + store.SetOriginBoundCert( + "https://www.mit.com/", CLIENT_CERT_RSA_SIGN, "g", "h"); EXPECT_EQ(4, store.GetCertCount()); - std::vector<OriginBoundCertStore::OriginBoundCertInfo> certs; + std::vector<OriginBoundCertStore::OriginBoundCert> certs; store.GetAllOriginBoundCerts(&certs); EXPECT_EQ(4u, certs.size()); } diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h index 430d7ae..f9734e0 100644 --- a/net/base/net_error_list.h +++ b/net/base/net_error_list.h @@ -278,6 +278,13 @@ NET_ERROR(SSL_BAD_PEER_PUBLIC_KEY, -149) // one of a set of public keys exist on the path from the leaf to the root. NET_ERROR(SSL_PINNED_KEY_NOT_IN_CERT_CHAIN, -150) +// Server request for client certificate did not contain any types we support. +NET_ERROR(CLIENT_AUTH_CERT_TYPE_UNSUPPORTED, -151) + +// Server requested one type of cert, then requested a different type while the +// first was still being generated. +NET_ERROR(ORIGIN_BOUND_CERT_GENERATION_TYPE_MISMATCH, -152) + // Certificate error codes // // The values of certificate error codes must be consecutive. diff --git a/net/base/origin_bound_cert_service.cc b/net/base/origin_bound_cert_service.cc index 4d1af28..f86d82c 100644 --- a/net/base/origin_bound_cert_service.cc +++ b/net/base/origin_bound_cert_service.cc @@ -4,6 +4,7 @@ #include "net/base/origin_bound_cert_service.h" +#include <algorithm> #include <limits> #include "base/compiler_specific.h" @@ -15,6 +16,7 @@ #include "base/rand_util.h" #include "base/stl_util.h" #include "base/threading/worker_pool.h" +#include "crypto/ec_private_key.h" #include "crypto/rsa_private_key.h" #include "net/base/net_errors.h" #include "net/base/origin_bound_cert_store.h" @@ -32,15 +34,27 @@ namespace { const int kKeySizeInBits = 1024; const int kValidityPeriodInDays = 365; +bool IsSupportedCertType(uint8 type) { + switch(type) { + case CLIENT_CERT_RSA_SIGN: + case CLIENT_CERT_ECDSA_SIGN: + return true; + default: + return false; + } +} + } // namespace // Represents the output and result callback of a request. class OriginBoundCertServiceRequest { public: OriginBoundCertServiceRequest(const CompletionCallback& callback, + SSLClientCertType* type, std::string* private_key, std::string* cert) : callback_(callback), + type_(type), private_key_(private_key), cert_(cert) { } @@ -48,6 +62,7 @@ class OriginBoundCertServiceRequest { // Ensures that the result callback will never be made. void Cancel() { callback_.Reset(); + type_ = NULL; private_key_ = NULL; cert_ = NULL; } @@ -55,9 +70,11 @@ class OriginBoundCertServiceRequest { // 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); @@ -69,6 +86,7 @@ class OriginBoundCertServiceRequest { private: CompletionCallback callback_; + SSLClientCertType* type_; std::string* private_key_; std::string* cert_; }; @@ -80,8 +98,10 @@ class OriginBoundCertServiceWorker { public: OriginBoundCertServiceWorker( const std::string& origin, + SSLClientCertType type, OriginBoundCertService* origin_bound_cert_service) : origin_(origin), + type_(type), serial_number_(base::RandInt(0, std::numeric_limits<int>::max())), origin_loop_(MessageLoop::current()), origin_bound_cert_service_(origin_bound_cert_service), @@ -110,6 +130,7 @@ class OriginBoundCertServiceWorker { void Run() { // Runs on a worker thread. error_ = OriginBoundCertService::GenerateCert(origin_, + type_, serial_number_, &private_key_, &cert_); @@ -136,7 +157,7 @@ class OriginBoundCertServiceWorker { // memory leaks or worse errors. base::AutoLock locked(lock_); if (!canceled_) { - origin_bound_cert_service_->HandleResult(origin_, error_, + origin_bound_cert_service_->HandleResult(origin_, error_, type_, private_key_, cert_); } } @@ -169,6 +190,7 @@ class OriginBoundCertServiceWorker { } const std::string origin_; + const SSLClientCertType type_; // Note that serial_number_ must be initialized on a non-worker thread // (see documentation for OriginBoundCertService::GenerateCert). uint32 serial_number_; @@ -195,8 +217,9 @@ class OriginBoundCertServiceWorker { // origin message loop. class OriginBoundCertServiceJob { public: - explicit OriginBoundCertServiceJob(OriginBoundCertServiceWorker* worker) - : worker_(worker) { + OriginBoundCertServiceJob(OriginBoundCertServiceWorker* worker, + SSLClientCertType type) + : worker_(worker), type_(type) { } ~OriginBoundCertServiceJob() { @@ -206,19 +229,23 @@ class OriginBoundCertServiceJob { } } + SSLClientCertType type() const { return type_; } + void AddRequest(OriginBoundCertServiceRequest* request) { requests_.push_back(request); } void HandleResult(int error, + SSLClientCertType type, const std::string& private_key, const std::string& cert) { worker_ = NULL; - PostAll(error, private_key, cert); + PostAll(error, type, private_key, cert); } private: void PostAll(int error, + SSLClientCertType type, const std::string& private_key, const std::string& cert) { std::vector<OriginBoundCertServiceRequest*> requests; @@ -226,7 +253,7 @@ class OriginBoundCertServiceJob { for (std::vector<OriginBoundCertServiceRequest*>::iterator i = requests.begin(); i != requests.end(); i++) { - (*i)->Post(error, private_key, cert); + (*i)->Post(error, type, private_key, cert); // Post() causes the OriginBoundCertServiceRequest to delete itself. } } @@ -244,8 +271,12 @@ class OriginBoundCertServiceJob { std::vector<OriginBoundCertServiceRequest*> requests_; OriginBoundCertServiceWorker* worker_; + SSLClientCertType type_; }; +// static +const char OriginBoundCertService::kEPKIPassword[] = ""; + OriginBoundCertService::OriginBoundCertService( OriginBoundCertStore* origin_bound_cert_store) : origin_bound_cert_store_(origin_bound_cert_store), @@ -259,43 +290,80 @@ OriginBoundCertService::~OriginBoundCertService() { int OriginBoundCertService::GetOriginBoundCert( const std::string& origin, + const std::vector<uint8>& requested_types, + SSLClientCertType* type, std::string* private_key, std::string* cert, const CompletionCallback& callback, RequestHandle* out_req) { DCHECK(CalledOnValidThread()); - if (callback.is_null() || !private_key || !cert || origin.empty()) { + if (callback.is_null() || !private_key || !cert || origin.empty() || + requested_types.empty()) { *out_req = NULL; 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<SSLClientCertType>(requested_types[i]); + break; + } + } + if (preferred_type == CLIENT_CERT_INVALID_TYPE) { + // None of the requested types are supported. + *out_req = NULL; + return ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED; + } + requests_++; - // Check if an origin bound cert already exists for this origin. + // Check if an origin bound cert of an acceptable type already exists for this + // origin. if (origin_bound_cert_store_->GetOriginBoundCert(origin, + type, private_key, cert)) { - cert_store_hits_++; - *out_req = NULL; - return OK; + if (IsSupportedCertType(*type) && + std::find(requested_types.begin(), requested_types.end(), *type) != + requested_types.end()) { + cert_store_hits_++; + *out_req = NULL; + return OK; + } + DVLOG(1) << "Cert store had cert of wrong type " << *type << " for " + << origin; } // |origin_bound_cert_store_| has no cert for this origin. See if an // identical request is currently in flight. - OriginBoundCertServiceJob* job; + OriginBoundCertServiceJob* job = NULL; std::map<std::string, OriginBoundCertServiceJob*>::const_iterator j; j = inflight_.find(origin); if (j != inflight_.end()) { // An identical request is in flight already. We'll just attach our // callback. - inflight_joins_++; 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 " << origin; + *out_req = NULL; + // 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 origin, 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. OriginBoundCertServiceWorker* worker = - new OriginBoundCertServiceWorker(origin, this); - job = new OriginBoundCertServiceJob(worker); + new OriginBoundCertServiceWorker(origin, preferred_type, this); + job = new OriginBoundCertServiceJob(worker, preferred_type); if (!worker->Start()) { delete job; delete worker; @@ -308,7 +376,7 @@ int OriginBoundCertService::GetOriginBoundCert( } OriginBoundCertServiceRequest* request = - new OriginBoundCertServiceRequest(callback, private_key, cert); + new OriginBoundCertServiceRequest(callback, type, private_key, cert); job->AddRequest(request); *out_req = request; return ERR_IO_PENDING; @@ -316,31 +384,64 @@ int OriginBoundCertService::GetOriginBoundCert( // static int OriginBoundCertService::GenerateCert(const std::string& origin, + SSLClientCertType type, uint32 serial_number, std::string* private_key, std::string* cert) { - scoped_ptr<crypto::RSAPrivateKey> key( - crypto::RSAPrivateKey::Create(kKeySizeInBits)); - if (!key.get()) { - LOG(WARNING) << "Unable to create key pair for client"; - return ERR_KEY_GENERATION_FAILED; - } std::string der_cert; - if (!x509_util::CreateOriginBoundCertRSA( - key.get(), - origin, - serial_number, - base::TimeDelta::FromDays(kValidityPeriodInDays), - &der_cert)) { - LOG(WARNING) << "Unable to create x509 cert for client"; - return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED; - } - std::vector<uint8> private_key_info; - if (!key->ExportPrivateKey(&private_key_info)) { - LOG(WARNING) << "Unable to export private key"; - return ERR_PRIVATE_KEY_EXPORT_FAILED; + switch (type) { + case CLIENT_CERT_RSA_SIGN: { + scoped_ptr<crypto::RSAPrivateKey> key( + crypto::RSAPrivateKey::Create(kKeySizeInBits)); + if (!key.get()) { + DLOG(ERROR) << "Unable to create key pair for client"; + return ERR_KEY_GENERATION_FAILED; + } + if (!x509_util::CreateOriginBoundCertRSA( + key.get(), + origin, + serial_number, + base::TimeDelta::FromDays(kValidityPeriodInDays), + &der_cert)) { + DLOG(ERROR) << "Unable to create x509 cert for client"; + return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED; + } + + if (!key->ExportPrivateKey(&private_key_info)) { + DLOG(ERROR) << "Unable to export private key"; + return ERR_PRIVATE_KEY_EXPORT_FAILED; + } + break; + } + case CLIENT_CERT_ECDSA_SIGN: { + scoped_ptr<crypto::ECPrivateKey> key(crypto::ECPrivateKey::Create()); + if (!key.get()) { + DLOG(ERROR) << "Unable to create key pair for client"; + return ERR_KEY_GENERATION_FAILED; + } + if (!x509_util::CreateOriginBoundCertEC( + key.get(), + origin, + serial_number, + base::TimeDelta::FromDays(kValidityPeriodInDays), + &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()); @@ -360,12 +461,13 @@ void OriginBoundCertService::CancelRequest(RequestHandle req) { // HandleResult is called by OriginBoundCertServiceWorker on the origin message // loop. It deletes OriginBoundCertServiceJob. void OriginBoundCertService::HandleResult(const std::string& origin, - int error, - const std::string& private_key, - const std::string& cert) { + int error, + SSLClientCertType type, + const std::string& private_key, + const std::string& cert) { DCHECK(CalledOnValidThread()); - origin_bound_cert_store_->SetOriginBoundCert(origin, private_key, cert); + origin_bound_cert_store_->SetOriginBoundCert(origin, type, private_key, cert); std::map<std::string, OriginBoundCertServiceJob*>::iterator j; j = inflight_.find(origin); @@ -376,7 +478,7 @@ void OriginBoundCertService::HandleResult(const std::string& origin, OriginBoundCertServiceJob* job = j->second; inflight_.erase(j); - job->HandleResult(error, private_key, cert); + job->HandleResult(error, type, private_key, cert); delete job; } diff --git a/net/base/origin_bound_cert_service.h b/net/base/origin_bound_cert_service.h index 861602f..c3861e6 100644 --- a/net/base/origin_bound_cert_service.h +++ b/net/base/origin_bound_cert_service.h @@ -8,12 +8,14 @@ #include <map> #include <string> +#include <vector> #include "base/basictypes.h" #include "base/memory/scoped_ptr.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 { @@ -30,20 +32,28 @@ class NET_EXPORT OriginBoundCertService // 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 origin_bound_cert_store. explicit OriginBoundCertService( OriginBoundCertStore* origin_bound_cert_store); ~OriginBoundCertService(); - // TODO(rkn): Specify certificate type (RSA or DSA). + // Fetches the origin 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. // - // Fetches the origin bound cert for the specified origin 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. + // 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 @@ -52,11 +62,14 @@ class NET_EXPORT OriginBoundCertService // If |out_req| is non-NULL, then |*out_req| will be filled with a handle to // the async request. This handle is not valid after the request has // completed. - int GetOriginBoundCert(const std::string& origin, - std::string* private_key, - std::string* cert, - const CompletionCallback& callback, - RequestHandle* out_req); + int GetOriginBoundCert( + const std::string& origin, + const std::vector<uint8>& 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 // GetOriginBoundCert(). After a request is canceled, its completion @@ -79,12 +92,14 @@ class NET_EXPORT OriginBoundCertService // 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& origin, + SSLClientCertType type, uint32 serial_number, std::string* private_key, std::string* cert); void HandleResult(const std::string& origin, int error, + SSLClientCertType type, const std::string& private_key, const std::string& cert); diff --git a/net/base/origin_bound_cert_service_unittest.cc b/net/base/origin_bound_cert_service_unittest.cc index 3fdb443..1adedfa 100644 --- a/net/base/origin_bound_cert_service_unittest.cc +++ b/net/base/origin_bound_cert_service_unittest.cc @@ -9,7 +9,9 @@ #include "base/bind.h" #include "base/memory/scoped_ptr.h" +#include "crypto/ec_private_key.h" #include "crypto/rsa_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" @@ -33,53 +35,178 @@ TEST(OriginBoundCertServiceTest, CacheHit) { std::string origin("https://encrypted.google.com:443"); int error; + std::vector<uint8> types; + types.push_back(CLIENT_CERT_RSA_SIGN); TestCompletionCallback callback; OriginBoundCertService::RequestHandle request_handle; // Asynchronous completion. + SSLClientCertType type1; std::string private_key_info1, der_cert1; EXPECT_EQ(0, service->cert_count()); error = service->GetOriginBoundCert( - origin, &private_key_info1, &der_cert1, callback.callback(), - &request_handle); + 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_RSA_SIGN, type1); EXPECT_FALSE(private_key_info1.empty()); EXPECT_FALSE(der_cert1.empty()); // Synchronous completion. + SSLClientCertType type2; + // If we request EC and RSA, should still retrieve the RSA cert. + types.insert(types.begin(), CLIENT_CERT_ECDSA_SIGN); std::string private_key_info2, der_cert2; error = service->GetOriginBoundCert( - origin, &private_key_info2, &der_cert2, callback.callback(), - &request_handle); + 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_RSA_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()); + // Request only EC. Should generate a new EC cert and discard the old RSA + // cert. + SSLClientCertType type3; + types.pop_back(); // Remove CLIENT_CERT_RSA_SIGN from requested types. + std::string private_key_info3, der_cert3; + EXPECT_EQ(1, service->cert_count()); + error = service->GetOriginBoundCert( + origin, 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(1, service->cert_count()); + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type3); + EXPECT_FALSE(private_key_info1.empty()); + EXPECT_FALSE(der_cert1.empty()); + EXPECT_NE(private_key_info1, private_key_info3); + EXPECT_NE(der_cert1, der_cert3); + + // Synchronous completion. + // If we request RSA and EC, should now retrieve the EC cert. + SSLClientCertType type4; + types.insert(types.begin(), CLIENT_CERT_RSA_SIGN); + std::string private_key_info4, der_cert4; + error = service->GetOriginBoundCert( + origin, types, &type4, &private_key_info4, &der_cert4, + 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, type4); + EXPECT_EQ(private_key_info3, private_key_info4); + EXPECT_EQ(der_cert3, der_cert4); + + EXPECT_EQ(4u, service->requests()); + EXPECT_EQ(2u, service->cert_store_hits()); EXPECT_EQ(0u, service->inflight_joins()); } +TEST(OriginBoundCertServiceTest, UnsupportedTypes) { + scoped_ptr<OriginBoundCertService> service( + new OriginBoundCertService(new DefaultOriginBoundCertStore(NULL))); + std::string origin("https://encrypted.google.com:443"); + + int error; + std::vector<uint8> types; + TestCompletionCallback callback; + OriginBoundCertService::RequestHandle request_handle; + + // Empty requested_types. + SSLClientCertType type1; + std::string private_key_info1, der_cert1; + error = service->GetOriginBoundCert( + 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(2); + types.push_back(3); + error = service->GetOriginBoundCert( + 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); + types.push_back(CLIENT_CERT_RSA_SIGN); + // Asynchronous completion. + EXPECT_EQ(0, service->cert_count()); + error = service->GetOriginBoundCert( + 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->GetOriginBoundCert( + 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(2); + types.push_back(3); + error = service->GetOriginBoundCert( + 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_RSA_SIGN); + types.push_back(CLIENT_CERT_ECDSA_SIGN); + error = service->GetOriginBoundCert( + 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(OriginBoundCertServiceTest, StoreCerts) { scoped_ptr<OriginBoundCertService> service( new OriginBoundCertService(new DefaultOriginBoundCertStore(NULL))); int error; + std::vector<uint8> types; + types.push_back(CLIENT_CERT_RSA_SIGN); TestCompletionCallback callback; OriginBoundCertService::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->GetOriginBoundCert( - origin1, &private_key_info1, &der_cert1, callback.callback(), - &request_handle); + 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(); @@ -87,10 +214,11 @@ TEST(OriginBoundCertServiceTest, StoreCerts) { 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->GetOriginBoundCert( - origin2, &private_key_info2, &der_cert2, callback.callback(), - &request_handle); + 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(); @@ -98,10 +226,12 @@ TEST(OriginBoundCertServiceTest, StoreCerts) { EXPECT_EQ(2, service->cert_count()); std::string origin3("https://www.twitter.com:443"); + SSLClientCertType type3; std::string private_key_info3, der_cert3; + types[0] = CLIENT_CERT_ECDSA_SIGN; error = service->GetOriginBoundCert( - origin3, &private_key_info3, &der_cert3, callback.callback(), - &request_handle); + 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(); @@ -114,6 +244,9 @@ TEST(OriginBoundCertServiceTest, StoreCerts) { EXPECT_NE(der_cert1, der_cert3); EXPECT_NE(private_key_info2, private_key_info3); EXPECT_NE(der_cert2, der_cert3); + EXPECT_EQ(CLIENT_CERT_RSA_SIGN, type1); + EXPECT_EQ(CLIENT_CERT_RSA_SIGN, type2); + EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type3); } // Tests an inflight join. @@ -122,23 +255,30 @@ TEST(OriginBoundCertServiceTest, InflightJoin) { new OriginBoundCertService(new DefaultOriginBoundCertStore(NULL))); std::string origin("https://encrypted.google.com:443"); int error; + std::vector<uint8> types; + types.push_back(CLIENT_CERT_RSA_SIGN); + SSLClientCertType type1; std::string private_key_info1, der_cert1; TestCompletionCallback callback1; OriginBoundCertService::RequestHandle request_handle1; + SSLClientCertType type2; std::string private_key_info2, der_cert2; TestCompletionCallback callback2; OriginBoundCertService::RequestHandle request_handle2; error = service->GetOriginBoundCert( - origin, &private_key_info1, &der_cert1, callback1.callback(), - &request_handle1); + 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 EC and RSA in the 2nd request, should still join with the + // original request. + types.insert(types.begin(), CLIENT_CERT_ECDSA_SIGN); error = service->GetOriginBoundCert( - origin, &private_key_info2, &der_cert2, callback2.callback(), - &request_handle2); + origin, types, &type2, &private_key_info2, &der_cert2, + callback2.callback(), &request_handle2); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle2 != NULL); @@ -147,22 +287,73 @@ TEST(OriginBoundCertServiceTest, InflightJoin) { error = callback2.WaitForResult(); EXPECT_EQ(OK, error); + EXPECT_EQ(CLIENT_CERT_RSA_SIGN, type1); + EXPECT_EQ(CLIENT_CERT_RSA_SIGN, type2); EXPECT_EQ(2u, service->requests()); EXPECT_EQ(0u, service->cert_store_hits()); EXPECT_EQ(1u, service->inflight_joins()); } -TEST(OriginBoundCertServiceTest, ExtractValuesFromBytes) { +// Tests an inflight join with mismatching request types. +TEST(OriginBoundCertServiceTest, InflightJoinTypeMismatch) { scoped_ptr<OriginBoundCertService> service( new OriginBoundCertService(new DefaultOriginBoundCertStore(NULL))); std::string origin("https://encrypted.google.com:443"); + int error; + std::vector<uint8> types1; + types1.push_back(CLIENT_CERT_RSA_SIGN); + std::vector<uint8> types2; + types2.push_back(CLIENT_CERT_ECDSA_SIGN); + + SSLClientCertType type1; + std::string private_key_info1, der_cert1; + TestCompletionCallback callback1; + OriginBoundCertService::RequestHandle request_handle1; + + SSLClientCertType type2; + std::string private_key_info2, der_cert2; + TestCompletionCallback callback2; + OriginBoundCertService::RequestHandle request_handle2; + + error = service->GetOriginBoundCert( + origin, types1, &type1, &private_key_info1, &der_cert1, + callback1.callback(), &request_handle1); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle1 != NULL); + // If we request only EC in the 2nd request, it should return an error. + error = service->GetOriginBoundCert( + origin, types2, &type2, &private_key_info2, &der_cert2, + callback2.callback(), &request_handle2); + EXPECT_EQ(ERR_ORIGIN_BOUND_CERT_GENERATION_TYPE_MISMATCH, error); + EXPECT_TRUE(request_handle2 == NULL); + + error = callback1.WaitForResult(); + EXPECT_EQ(OK, error); + + EXPECT_FALSE(private_key_info1.empty()); + EXPECT_FALSE(der_cert1.empty()); + EXPECT_TRUE(private_key_info2.empty()); + EXPECT_TRUE(der_cert2.empty()); + EXPECT_EQ(CLIENT_CERT_RSA_SIGN, type1); + EXPECT_EQ(2u, service->requests()); + EXPECT_EQ(0u, service->cert_store_hits()); + EXPECT_EQ(0u, service->inflight_joins()); +} + +TEST(OriginBoundCertServiceTest, ExtractValuesFromBytesRSA) { + scoped_ptr<OriginBoundCertService> service( + new OriginBoundCertService(new DefaultOriginBoundCertStore(NULL))); + std::string origin("https://encrypted.google.com:443"); + SSLClientCertType type; std::string private_key_info, der_cert; int error; + std::vector<uint8> types; + types.push_back(CLIENT_CERT_RSA_SIGN); TestCompletionCallback callback; OriginBoundCertService::RequestHandle request_handle; error = service->GetOriginBoundCert( - origin, &private_key_info, &der_cert, callback.callback(), + origin, types, &type, &private_key_info, &der_cert, callback.callback(), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle != NULL); @@ -181,16 +372,60 @@ TEST(OriginBoundCertServiceTest, ExtractValuesFromBytes) { EXPECT_TRUE(x509cert != NULL); } +TEST(OriginBoundCertServiceTest, ExtractValuesFromBytesEC) { + scoped_ptr<OriginBoundCertService> service( + new OriginBoundCertService(new DefaultOriginBoundCertStore(NULL))); + std::string origin("https://encrypted.google.com:443"); + SSLClientCertType type; + std::string private_key_info, der_cert; + int error; + std::vector<uint8> types; + types.push_back(CLIENT_CERT_ECDSA_SIGN); + TestCompletionCallback callback; + OriginBoundCertService::RequestHandle request_handle; + + error = service->GetOriginBoundCert( + 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<uint8> spki( + spki_piece.data(), + spki_piece.data() + spki_piece.size()); + + // Check that we can retrieve the key from the bytes. + std::vector<uint8> key_vec(private_key_info.begin(), private_key_info.end()); + scoped_ptr<crypto::ECPrivateKey> private_key( + crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( + OriginBoundCertService::kEPKIPassword, key_vec, spki)); + EXPECT_TRUE(private_key != NULL); + + // Check that we can retrieve the cert from the bytes. + scoped_refptr<X509Certificate> x509cert( + X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size())); + EXPECT_TRUE(x509cert != NULL); +} + // Tests that the callback of a canceled request is never made. TEST(OriginBoundCertServiceTest, CancelRequest) { scoped_ptr<OriginBoundCertService> service( new OriginBoundCertService(new DefaultOriginBoundCertStore(NULL))); std::string origin("https://encrypted.google.com:443"); + SSLClientCertType type; std::string private_key_info, der_cert; int error; + std::vector<uint8> types; + types.push_back(CLIENT_CERT_RSA_SIGN); OriginBoundCertService::RequestHandle request_handle; error = service->GetOriginBoundCert(origin, + types, + &type, &private_key_info, &der_cert, base::Bind(&FailTest), @@ -206,6 +441,8 @@ TEST(OriginBoundCertServiceTest, CancelRequest) { for (int i = 0; i < 5; ++i) { error = service->GetOriginBoundCert( "https://encrypted.google.com:" + std::string(1, (char) ('1' + i)), + types, + &type, &private_key_info, &der_cert, callback.callback(), @@ -214,6 +451,10 @@ TEST(OriginBoundCertServiceTest, CancelRequest) { 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()); } #endif // !defined(USE_OPENSSL) diff --git a/net/base/origin_bound_cert_store.cc b/net/base/origin_bound_cert_store.cc new file mode 100644 index 0000000..16d054a --- /dev/null +++ b/net/base/origin_bound_cert_store.cc @@ -0,0 +1,23 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/origin_bound_cert_store.h" + +namespace net { + +OriginBoundCertStore::OriginBoundCert::OriginBoundCert() {} + +OriginBoundCertStore::OriginBoundCert::OriginBoundCert( + const std::string& origin, + SSLClientCertType type, + const std::string& private_key, + const std::string& cert) + : origin_(origin), + type_(type), + private_key_(private_key), + cert_(cert) {} + +OriginBoundCertStore::OriginBoundCert::~OriginBoundCert() {} + +} // namespace net diff --git a/net/base/origin_bound_cert_store.h b/net/base/origin_bound_cert_store.h index 4cb1132..094839b 100644 --- a/net/base/origin_bound_cert_store.h +++ b/net/base/origin_bound_cert_store.h @@ -10,6 +10,7 @@ #include <vector> #include "net/base/net_export.h" +#include "net/base/ssl_client_cert_type.h" namespace net { @@ -22,30 +23,55 @@ namespace net { class NET_EXPORT OriginBoundCertStore { public: - // Used by GetAllOriginBoundCerts. - struct OriginBoundCertInfo { - std::string origin; // Origin, for instance "https://www.verisign.com:443". - std::string private_key; // DER-encoded PrivateKeyInfo struct. - std::string cert; // DER-encoded certificate. + // The OriginBoundCert class contains a private key in addition to the origin + // cert, and cert type. + class NET_EXPORT OriginBoundCert { + public: + OriginBoundCert(); + OriginBoundCert(const std::string& origin, + SSLClientCertType type, + const std::string& private_key, + const std::string& cert); + ~OriginBoundCert(); + + // Origin, for instance "https://www.verisign.com:443" + const std::string& origin() const { return origin_; } + // TLS ClientCertificateType. + SSLClientCertType type() const { return type_; } + // 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 origin_; + SSLClientCertType type_; + std::string private_key_; + std::string cert_; }; virtual ~OriginBoundCertStore() {} - // TODO(rkn): Specify certificate type (RSA or DSA). // TODO(rkn): File I/O may be required, so this should have an asynchronous // interface. // Returns true on success. |private_key_result| stores a DER-encoded // PrivateKeyInfo struct and |cert_result| stores a DER-encoded // certificate. Returns false if no origin bound cert exists for the // specified origin. - virtual bool GetOriginBoundCert(const std::string& origin, - std::string* private_key_result, - std::string* cert_result) = 0; + virtual bool GetOriginBoundCert( + const std::string& origin, + SSLClientCertType* type, + std::string* private_key_result, + std::string* cert_result) = 0; // Adds an origin bound cert and the corresponding private key to the store. - virtual void SetOriginBoundCert(const std::string& origin, - const std::string& private_key, - const std::string& cert) = 0; + virtual void SetOriginBoundCert( + const std::string& origin, + SSLClientCertType type, + const std::string& private_key, + const std::string& cert) = 0; // Removes an origin bound cert and the corresponding private key from the // store. @@ -57,7 +83,7 @@ class NET_EXPORT OriginBoundCertStore { // Returns all origin bound certs and the corresponding private keys. virtual void GetAllOriginBoundCerts( - std::vector<OriginBoundCertInfo>* origin_bound_certs) = 0; + std::vector<OriginBoundCert>* origin_bound_certs) = 0; // Returns the number of certs in the store. // Public only for unit testing. diff --git a/net/base/ssl_client_cert_type.h b/net/base/ssl_client_cert_type.h new file mode 100644 index 0000000..921a6f4 --- /dev/null +++ b/net/base/ssl_client_cert_type.h @@ -0,0 +1,22 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_BASE_SSL_CLIENT_CERT_TYPE_H_ +#define NET_BASE_SSL_CLIENT_CERT_TYPE_H_ +#pragma once + +namespace net { + +// TLS ClientCertificateType Identifiers +// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-1 +enum SSLClientCertType { + CLIENT_CERT_RSA_SIGN = 1, + CLIENT_CERT_ECDSA_SIGN = 64, + // 224-255 are Reserved for Private Use, we pick one to use as "invalid". + CLIENT_CERT_INVALID_TYPE = 255, +}; + +} // namespace net + +#endif // NET_BASE_SSL_CLIENT_CERT_TYPE_H_ |