Index: net/base/origin_bound_cert_service.temp.cc |
=================================================================== |
--- net/base/origin_bound_cert_service.temp.cc (revision 94628) |
+++ net/base/origin_bound_cert_service.temp.cc (working copy) |
@@ -2,14 +2,21 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#include "net/base/cert_verifier.h" |
+#include "net/base/origin_bound_cert_service.h" |
+#include <limits> |
+ |
#include "base/compiler_specific.h" |
+#include "base/logging.h" |
+#include "base/memory/ref_counted.h" |
+#include "base/memory/scoped_ptr.h" |
#include "base/message_loop.h" |
+#include "base/rand_util.h" |
#include "base/stl_util.h" |
-#include "base/synchronization/lock.h" |
#include "base/threading/worker_pool.h" |
+#include "crypto/rsa_private_key.h" |
#include "net/base/net_errors.h" |
+#include "net/base/origin_bound_cert_store.h" |
#include "net/base/x509_certificate.h" |
#if defined(USE_NSS) |
@@ -18,93 +25,40 @@ |
namespace net { |
-//////////////////////////////////////////////////////////////////////////// |
- |
-// Life of a request: |
-// |
-// CertVerifier CertVerifierJob CertVerifierWorker Request |
-// | (origin loop) (worker loop) |
-// | |
-// Verify() |
-// |---->-------------------<creates> |
-// | |
-// |---->----<creates> |
-// | |
-// |---->---------------------------------------------------<creates> |
-// | |
-// |---->--------------------Start |
-// | | |
-// | PostTask |
-// | |
-// | <starts verifying> |
-// |---->-----AddRequest | |
-// | |
-// | |
-// | |
-// Finish |
-// | |
-// PostTask |
-// |
-// | |
-// DoReply |
-// |----<-----------------------| |
-// HandleResult |
-// | |
-// |---->-----HandleResult |
-// | |
-// |------>-----------------------------------Post |
-// |
-// |
-// |
-// On a cache hit, CertVerifier::Verify() returns synchronously without |
-// posting a task to a worker thread. |
- |
-// The number of CachedCertVerifyResult objects that we'll cache. |
-static const unsigned kMaxCacheEntries = 256; |
- |
-// The number of seconds for which we'll cache a cache entry. |
-static const unsigned kTTLSecs = 1800; // 30 minutes. |
- |
namespace { |
-class DefaultTimeService : public CertVerifier::TimeService { |
- public: |
- // CertVerifier::TimeService methods: |
- virtual base::Time Now() { return base::Time::Now(); } |
-}; |
+const int kKeySizeInBits = 1024; |
+const int kValidityPeriodInDays = 365; |
} // namespace |
-CachedCertVerifyResult::CachedCertVerifyResult() : error(ERR_FAILED) { |
-} |
- |
-CachedCertVerifyResult::~CachedCertVerifyResult() {} |
- |
-bool CachedCertVerifyResult::HasExpired(const base::Time current_time) const { |
- return current_time >= expiry; |
-} |
- |
// Represents the output and result callback of a request. |
-class CertVerifierRequest { |
+class OriginBoundCertServiceRequest { |
public: |
- CertVerifierRequest(CompletionCallback* callback, |
- CertVerifyResult* verify_result) |
+ OriginBoundCertServiceRequest(CompletionCallback* callback, |
+ std::string* private_key, |
+ std::string* cert) |
: callback_(callback), |
- verify_result_(verify_result) { |
+ private_key_(private_key), |
+ cert_(cert) { |
} |
// Ensures that the result callback will never be made. |
void Cancel() { |
callback_ = NULL; |
- verify_result_ = NULL; |
+ private_key_ = NULL; |
+ cert_ = NULL; |
} |
- // Copies the contents of |verify_result| to the caller's |
- // CertVerifyResult and calls the callback. |
- void Post(const CachedCertVerifyResult& verify_result) { |
+ // Copies the contents of |private_key| and |cert| to the caller's fields |
+ // and calls the callback. |
+ void Post(int error, |
+ const std::string& private_key, |
+ const std::string& cert) { |
if (callback_) { |
- *verify_result_ = verify_result.result; |
- callback_->Run(verify_result.error); |
+ *private_key_ = private_key; |
+ *cert_ = cert; |
+ callback_->Run(error); |
} |
delete this; |
} |
@@ -113,24 +67,21 @@ |
private: |
CompletionCallback* callback_; |
- CertVerifyResult* verify_result_; |
+ std::string* private_key_; |
+ std::string* cert_; |
}; |
- |
-// CertVerifierWorker runs on a worker thread and takes care of the blocking |
-// process of performing the certificate verification. Deletes itself |
-// eventually if Start() succeeds. |
-class CertVerifierWorker { |
+// OriginBoundCertServiceWorker runs on a worker thread and takes care of the |
+// blocking process of performing key generation. Deletes itself eventually |
+// if Start() succeeds. |
+class OriginBoundCertServiceWorker { |
public: |
- CertVerifierWorker(X509Certificate* cert, |
- const std::string& hostname, |
- int flags, |
- CertVerifier* cert_verifier) |
- : cert_(cert), |
- hostname_(hostname), |
- flags_(flags), |
+ OriginBoundCertServiceWorker( |
+ const std::string& origin, |
+ OriginBoundCertService* origin_bound_cert_service) |
+ : origin_(origin), |
origin_loop_(MessageLoop::current()), |
- cert_verifier_(cert_verifier), |
+ origin_bound_cert_service_(origin_bound_cert_service), |
canceled_(false), |
error_(ERR_FAILED) { |
} |
@@ -139,12 +90,13 @@ |
DCHECK_EQ(MessageLoop::current(), origin_loop_); |
return base::WorkerPool::PostTask( |
- FROM_HERE, NewRunnableMethod(this, &CertVerifierWorker::Run), |
+ FROM_HERE, |
+ NewRunnableMethod(this, &OriginBoundCertServiceWorker::Run), |
true /* task is slow */); |
} |
- // Cancel is called from the origin loop when the CertVerifier is getting |
- // deleted. |
+ // Cancel is called from the origin loop when the OriginBoundCertService is |
+ // getting deleted. |
void Cancel() { |
DCHECK_EQ(MessageLoop::current(), origin_loop_); |
base::AutoLock locked(lock_); |
@@ -153,8 +105,12 @@ |
private: |
void Run() { |
+ uint32 serial_number = base::RandInt(0, std::numeric_limits<int>::max()); |
// Runs on a worker thread. |
- error_ = cert_->Verify(hostname_, flags_, &verify_result_); |
+ error_ = origin_bound_cert_service_->GenerateCert(origin_, |
+ serial_number, |
+ &private_key_, |
+ &cert_); |
#if defined(USE_NSS) |
// Detach the thread from NSPR. |
// Calling NSS functions attaches the thread to NSPR, which stores |
@@ -178,8 +134,8 @@ |
// memory leaks or worse errors. |
base::AutoLock locked(lock_); |
if (!canceled_) { |
- cert_verifier_->HandleResult(cert_, hostname_, flags_, |
- error_, verify_result_); |
+ origin_bound_cert_service_->HandleResult( |
+ origin_, error_, private_key_, cert_); |
} |
} |
delete this; |
@@ -187,14 +143,14 @@ |
void Finish() { |
// Runs on the worker thread. |
- // We assume that the origin loop outlives the CertVerifier. If the |
- // CertVerifier is deleted, it will call Cancel on us. If it does so |
- // before the Acquire, we'll delete ourselves and return. If it's trying to |
- // do so concurrently, then it'll block on the lock and we'll call PostTask |
- // while the CertVerifier (and therefore the MessageLoop) is still alive. |
- // If it does so after this function, we assume that the MessageLoop will |
- // process pending tasks. In which case we'll notice the |canceled_| flag |
- // in DoReply. |
+ // We assume that the origin loop outlives the OriginBoundCertService. If |
+ // the OriginBoundCertService is deleted, it will call Cancel on us. If it |
+ // does so before the Acquire, we'll delete ourselves and return. If it's |
+ // trying to do so concurrently, then it'll block on the lock and we'll |
+ // call PostTask while the OriginBoundCertService (and therefore the |
+ // MessageLoop) is still alive. If it does so after this function, we |
+ // assume that the MessageLoop will process pending tasks. In which case |
+ // we'll notice the |canceled_| flag in DoReply. |
bool canceled; |
{ |
@@ -202,141 +158,128 @@ |
canceled = canceled_; |
if (!canceled) { |
origin_loop_->PostTask( |
- FROM_HERE, NewRunnableMethod(this, &CertVerifierWorker::DoReply)); |
+ FROM_HERE, |
+ NewRunnableMethod(this, &OriginBoundCertServiceWorker::DoReply)); |
} |
} |
- |
if (canceled) |
delete this; |
} |
- scoped_refptr<X509Certificate> cert_; |
- const std::string hostname_; |
- const int flags_; |
+ const std::string origin_; |
MessageLoop* const origin_loop_; |
- CertVerifier* const cert_verifier_; |
+ OriginBoundCertService* const origin_bound_cert_service_; |
// lock_ protects canceled_. |
base::Lock lock_; |
// If canceled_ is true, |
// * origin_loop_ cannot be accessed by the worker thread, |
- // * cert_verifier_ cannot be accessed by any thread. |
+ // * origin_bound_cert_service_ cannot be accessed by any thread. |
bool canceled_; |
int error_; |
- CertVerifyResult verify_result_; |
+ std::string private_key_; |
+ std::string cert_; |
- DISALLOW_COPY_AND_ASSIGN(CertVerifierWorker); |
+ DISALLOW_COPY_AND_ASSIGN(OriginBoundCertServiceWorker); |
}; |
-// A CertVerifierJob is a one-to-one counterpart of a CertVerifierWorker. It |
-// lives only on the CertVerifier's origin message loop. |
-class CertVerifierJob { |
+// An OriginBoundCertServiceJob is a one-to-one counterpart of an |
+// OriginBoundCertServiceWorker. It lives only on the OriginBoundCertService's |
+// origin message loop. |
+class OriginBoundCertServiceJob { |
public: |
- explicit CertVerifierJob(CertVerifierWorker* worker) : worker_(worker) { |
+ explicit OriginBoundCertServiceJob(OriginBoundCertServiceWorker* worker) |
+ : worker_(worker) { |
} |
- ~CertVerifierJob() { |
+ ~OriginBoundCertServiceJob() { |
if (worker_) { |
worker_->Cancel(); |
DeleteAllCanceled(); |
} |
} |
- void AddRequest(CertVerifierRequest* request) { |
+ void AddRequest(OriginBoundCertServiceRequest* request) { |
requests_.push_back(request); |
} |
- void HandleResult(const CachedCertVerifyResult& verify_result) { |
+ void HandleResult(int error, |
+ const std::string& private_key, |
+ const std::string& cert) { |
worker_ = NULL; |
- PostAll(verify_result); |
+ PostAll(error, private_key, cert); |
} |
private: |
- void PostAll(const CachedCertVerifyResult& verify_result) { |
- std::vector<CertVerifierRequest*> requests; |
+ void PostAll(int error, |
+ const std::string& private_key, |
+ const std::string& cert) { |
+ std::vector<OriginBoundCertServiceRequest*> requests; |
requests_.swap(requests); |
- for (std::vector<CertVerifierRequest*>::iterator |
+ for (std::vector<OriginBoundCertServiceRequest*>::iterator |
i = requests.begin(); i != requests.end(); i++) { |
- (*i)->Post(verify_result); |
- // Post() causes the CertVerifierRequest to delete itself. |
+ (*i)->Post(error, private_key, cert); |
+ // Post() causes the OriginBoundCertServiceRequest to delete itself. |
} |
} |
void DeleteAllCanceled() { |
- for (std::vector<CertVerifierRequest*>::iterator |
+ for (std::vector<OriginBoundCertServiceRequest*>::iterator |
i = requests_.begin(); i != requests_.end(); i++) { |
if ((*i)->canceled()) { |
delete *i; |
} else { |
- LOG(DFATAL) << "CertVerifierRequest leaked!"; |
+ LOG(DFATAL) << "OriginBoundCertServiceRequest leaked!"; |
} |
} |
} |
- std::vector<CertVerifierRequest*> requests_; |
- CertVerifierWorker* worker_; |
+ std::vector<OriginBoundCertServiceRequest*> requests_; |
+ OriginBoundCertServiceWorker* worker_; |
}; |
- |
-CertVerifier::CertVerifier() |
- : time_service_(new DefaultTimeService), |
+OriginBoundCertService::OriginBoundCertService( |
+ OriginBoundCertStore* origin_bound_cert_store) |
+ : origin_bound_cert_store_(origin_bound_cert_store), |
requests_(0), |
cache_hits_(0), |
- inflight_joins_(0) { |
- CertDatabase::AddObserver(this); |
-} |
+ inflight_joins_(0) {} |
-CertVerifier::CertVerifier(TimeService* time_service) |
- : time_service_(time_service), |
- requests_(0), |
- cache_hits_(0), |
- inflight_joins_(0) { |
- CertDatabase::AddObserver(this); |
-} |
- |
-CertVerifier::~CertVerifier() { |
+OriginBoundCertService::~OriginBoundCertService() { |
STLDeleteValues(&inflight_); |
- |
- CertDatabase::RemoveObserver(this); |
} |
-int CertVerifier::Verify(X509Certificate* cert, |
- const std::string& hostname, |
- int flags, |
- CertVerifyResult* verify_result, |
- CompletionCallback* callback, |
- RequestHandle* out_req) { |
+int OriginBoundCertService::GetOriginBoundCert(const std::string& origin, |
+ std::string* private_key, |
+ std::string* cert, |
+ CompletionCallback* callback, |
+ RequestHandle* out_req) { |
+ |
DCHECK(CalledOnValidThread()); |
- if (!callback || !verify_result || hostname.empty()) { |
+ if (!callback || !private_key || !cert || origin.empty()) { |
*out_req = NULL; |
return ERR_INVALID_ARGUMENT; |
} |
requests_++; |
- const RequestParams key = {cert->fingerprint(), hostname, flags}; |
- // First check the cache. |
- std::map<RequestParams, CachedCertVerifyResult>::iterator i; |
- i = cache_.find(key); |
- if (i != cache_.end()) { |
- if (!i->second.HasExpired(time_service_->Now())) { |
- cache_hits_++; |
- *out_req = NULL; |
- *verify_result = i->second.result; |
- return i->second.error; |
- } |
- // Cache entry has expired. |
- cache_.erase(i); |
+ // Check if an origin bound cert already exists for this origin. |
+ if (origin_bound_cert_store_->GetOriginBoundCert(origin, |
+ private_key, |
+ cert)) { |
+ cache_hits_++; |
+ *out_req = NULL; |
+ return OK; |
} |
// No cache hit. See if an identical request is currently in flight. |
- CertVerifierJob* job; |
- std::map<RequestParams, CertVerifierJob*>::const_iterator j; |
- j = inflight_.find(key); |
+ OriginBoundCertServiceJob* job; |
+ 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. |
@@ -344,163 +287,103 @@ |
job = j->second; |
} else { |
// Need to make a new request. |
- CertVerifierWorker* worker = new CertVerifierWorker(cert, hostname, flags, |
- this); |
- job = new CertVerifierJob(worker); |
+ OriginBoundCertServiceWorker* worker = |
+ new OriginBoundCertServiceWorker(origin, this); |
+ job = new OriginBoundCertServiceJob(worker); |
if (!worker->Start()) { |
delete job; |
delete worker; |
*out_req = NULL; |
- // TODO(wtc): log to the NetLog. |
- LOG(ERROR) << "CertVerifierWorker couldn't be started."; |
+ // TODO(rkn): Log to the NetLog. |
+ LOG(ERROR) << "OriginBoundCertServiceWorker couldn't be started."; |
return ERR_INSUFFICIENT_RESOURCES; // Just a guess. |
} |
- inflight_.insert(std::make_pair(key, job)); |
+ inflight_[origin] = job; |
} |
- CertVerifierRequest* request = |
- new CertVerifierRequest(callback, verify_result); |
+ OriginBoundCertServiceRequest* request = |
+ new OriginBoundCertServiceRequest(callback, private_key, cert); |
job->AddRequest(request); |
*out_req = request; |
return ERR_IO_PENDING; |
} |
-void CertVerifier::CancelRequest(RequestHandle req) { |
- DCHECK(CalledOnValidThread()); |
- CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req); |
- request->Cancel(); |
-} |
+int OriginBoundCertService::GenerateCert(const std::string& origin, |
+ uint32 serial_number, |
+ std::string* private_key, |
+ std::string* cert) { |
+ std::string subject = "CN=OBC"; |
+ scoped_ptr<crypto::RSAPrivateKey> key( |
+ crypto::RSAPrivateKey::Create(kKeySizeInBits)); |
+ if (!key.get()) { |
+ LOG(WARNING) << "Unable to create key pair for client"; |
+ return ERR_FAILED; |
+ } |
-void CertVerifier::ClearCache() { |
- DCHECK(CalledOnValidThread()); |
+ scoped_refptr<X509Certificate> x509_cert = X509Certificate::CreateSelfSigned( |
+ key.get(), |
+ subject, |
+ serial_number, |
+ base::TimeDelta::FromDays(kValidityPeriodInDays)); |
+ if (!x509_cert) { |
+ LOG(WARNING) << "Unable to create x509 cert for client"; |
+ return ERR_FAILED; |
+ } |
- cache_.clear(); |
- // Leaves inflight_ alone. |
+ std::vector<uint8> private_key_info; |
+ if (!key->ExportPrivateKey(&private_key_info)) { |
+ LOG(WARNING) << "Unable to export private key"; |
+ return ERR_FAILED; |
+ } |
+ // 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()); |
+ |
+ std::string der_cert; |
+ if (!x509_cert->GetDEREncoded(&der_cert)) { |
+ LOG(WARNING) << "Unable to get DER-enconded cert"; |
+ return ERR_FAILED; |
+ } |
+ |
+ private_key->swap(key_out); |
+ cert->swap(der_cert); |
+ return OK; |
} |
-size_t CertVerifier::GetCacheSize() const { |
+void OriginBoundCertService::CancelRequest(RequestHandle req) { |
DCHECK(CalledOnValidThread()); |
- |
- return cache_.size(); |
+ OriginBoundCertServiceRequest* request = |
+ reinterpret_cast<OriginBoundCertServiceRequest*>(req); |
+ request->Cancel(); |
} |
-// HandleResult is called by CertVerifierWorker on the origin message loop. |
-// It deletes CertVerifierJob. |
-void CertVerifier::HandleResult(X509Certificate* cert, |
- const std::string& hostname, |
- int flags, |
- int error, |
- const CertVerifyResult& verify_result) { |
+// 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) { |
DCHECK(CalledOnValidThread()); |
- const base::Time current_time(time_service_->Now()); |
+ origin_bound_cert_store_->SetOriginBoundCert(origin, private_key, cert); |
- CachedCertVerifyResult cached_result; |
- cached_result.error = error; |
- cached_result.result = verify_result; |
- uint32 ttl = kTTLSecs; |
- cached_result.expiry = current_time + base::TimeDelta::FromSeconds(ttl); |
- |
- const RequestParams key = {cert->fingerprint(), hostname, flags}; |
- |
- DCHECK_GE(kMaxCacheEntries, 1u); |
- DCHECK_LE(cache_.size(), kMaxCacheEntries); |
- if (cache_.size() == kMaxCacheEntries) { |
- // Need to remove an element of the cache. |
- std::map<RequestParams, CachedCertVerifyResult>::iterator i, cur; |
- for (i = cache_.begin(); i != cache_.end(); ) { |
- cur = i++; |
- if (cur->second.HasExpired(current_time)) |
- cache_.erase(cur); |
- } |
- } |
- if (cache_.size() == kMaxCacheEntries) { |
- // If we didn't clear out any expired entries, we just remove the first |
- // element. Crummy but simple. |
- cache_.erase(cache_.begin()); |
- } |
- |
- cache_.insert(std::make_pair(key, cached_result)); |
- |
- std::map<RequestParams, CertVerifierJob*>::iterator j; |
- j = inflight_.find(key); |
+ std::map<std::string, OriginBoundCertServiceJob*>::iterator j; |
+ j = inflight_.find(origin); |
if (j == inflight_.end()) { |
NOTREACHED(); |
return; |
} |
- CertVerifierJob* job = j->second; |
+ OriginBoundCertServiceJob* job = j->second; |
inflight_.erase(j); |
- job->HandleResult(cached_result); |
+ job->HandleResult(error, private_key, cert); |
delete job; |
} |
-void CertVerifier::OnCertTrustChanged(const X509Certificate* cert) { |
- DCHECK(CalledOnValidThread()); |
- |
- ClearCache(); |
+int OriginBoundCertService::GetCertCount() { |
+ return origin_bound_cert_store_->GetCertCount(); |
} |
-///////////////////////////////////////////////////////////////////// |
- |
-SingleRequestCertVerifier::SingleRequestCertVerifier( |
- CertVerifier* cert_verifier) |
- : cert_verifier_(cert_verifier), |
- cur_request_(NULL), |
- cur_request_callback_(NULL), |
- ALLOW_THIS_IN_INITIALIZER_LIST( |
- callback_(this, &SingleRequestCertVerifier::OnVerifyCompletion)) { |
- DCHECK(cert_verifier_ != NULL); |
-} |
- |
-SingleRequestCertVerifier::~SingleRequestCertVerifier() { |
- if (cur_request_) { |
- cert_verifier_->CancelRequest(cur_request_); |
- cur_request_ = NULL; |
- } |
-} |
- |
-int SingleRequestCertVerifier::Verify(X509Certificate* cert, |
- const std::string& hostname, |
- int flags, |
- CertVerifyResult* verify_result, |
- CompletionCallback* callback) { |
- // Should not be already in use. |
- DCHECK(!cur_request_ && !cur_request_callback_); |
- |
- // Do a synchronous verification. |
- if (!callback) |
- return cert->Verify(hostname, flags, verify_result); |
- |
- CertVerifier::RequestHandle request = NULL; |
- |
- // We need to be notified of completion before |callback| is called, so that |
- // we can clear out |cur_request_*|. |
- int rv = cert_verifier_->Verify( |
- cert, hostname, flags, verify_result, &callback_, &request); |
- |
- if (rv == ERR_IO_PENDING) { |
- // Cleared in OnVerifyCompletion(). |
- cur_request_ = request; |
- cur_request_callback_ = callback; |
- } |
- |
- return rv; |
-} |
- |
-void SingleRequestCertVerifier::OnVerifyCompletion(int result) { |
- DCHECK(cur_request_ && cur_request_callback_); |
- |
- CompletionCallback* callback = cur_request_callback_; |
- |
- // Clear the outstanding request information. |
- cur_request_ = NULL; |
- cur_request_callback_ = NULL; |
- |
- // Call the user's original callback. |
- callback->Run(result); |
-} |
- |
} // namespace net |
-DISABLE_RUNNABLE_METHOD_REFCOUNT(net::CertVerifierWorker); |
+DISABLE_RUNNABLE_METHOD_REFCOUNT(net::OriginBoundCertServiceWorker); |