summaryrefslogtreecommitdiffstats
path: root/net/base
diff options
context:
space:
mode:
authormattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-06 04:21:09 +0000
committermattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-06 04:21:09 +0000
commit550cee900473ff9d964a9c4d18e1b3670391967b (patch)
tree20a03e6d39a2274c831b0fe121ad0c7f14b1c688 /net/base
parent115bee94449b0594f797973aa05992bbbd6c61d3 (diff)
downloadchromium_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.cc29
-rw-r--r--net/base/default_origin_bound_cert_store.h40
-rw-r--r--net/base/default_origin_bound_cert_store_unittest.cc81
-rw-r--r--net/base/net_error_list.h7
-rw-r--r--net/base/origin_bound_cert_service.cc180
-rw-r--r--net/base/origin_bound_cert_service.h35
-rw-r--r--net/base/origin_bound_cert_service_unittest.cc279
-rw-r--r--net/base/origin_bound_cert_store.cc23
-rw-r--r--net/base/origin_bound_cert_store.h52
-rw-r--r--net/base/ssl_client_cert_type.h22
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_