| Index: net/ssl/channel_id_service.cc
|
| diff --git a/net/ssl/channel_id_service.cc b/net/ssl/channel_id_service.cc
|
| index 7fcbc539b32187d0e1abcacc140a62eb88672876..811ebc9cef7f2beba06396d69e9021aa1de45296 100644
|
| --- a/net/ssl/channel_id_service.cc
|
| +++ b/net/ssl/channel_id_service.cc
|
| @@ -35,12 +35,6 @@ namespace net {
|
|
|
| namespace {
|
|
|
| -const int kValidityPeriodInDays = 365;
|
| -// When we check the system time, we add this many days to the end of the check
|
| -// so the result will still hold even after chrome has been running for a
|
| -// while.
|
| -const int kSystemTimeValidityBufferInDays = 90;
|
| -
|
| // Used by the GetDomainBoundCertResult histogram to record the final
|
| // outcome of each GetChannelID or GetOrCreateChannelID call.
|
| // Do not re-use values.
|
| @@ -88,45 +82,27 @@ void RecordGetChannelIDTime(base::TimeDelta request_time) {
|
| // unjoined thread, due to relying on a non-leaked LazyInstance
|
| scoped_ptr<ChannelIDStore::ChannelID> GenerateChannelID(
|
| const std::string& server_identifier,
|
| - uint32 serial_number,
|
| int* error) {
|
| scoped_ptr<ChannelIDStore::ChannelID> result;
|
|
|
| base::TimeTicks start = base::TimeTicks::Now();
|
| base::Time not_valid_before = base::Time::Now();
|
| - base::Time not_valid_after =
|
| - not_valid_before + base::TimeDelta::FromDays(kValidityPeriodInDays);
|
| - std::string der_cert;
|
| - std::vector<uint8> private_key_info;
|
| - scoped_ptr<crypto::ECPrivateKey> key;
|
| - if (!x509_util::CreateKeyAndChannelIDEC(server_identifier,
|
| - serial_number,
|
| - not_valid_before,
|
| - not_valid_after,
|
| - &key,
|
| - &der_cert)) {
|
| - DLOG(ERROR) << "Unable to create x509 cert for client";
|
| - *error = ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED;
|
| - return result.Pass();
|
| - }
|
| + scoped_ptr<crypto::ECPrivateKey> key(crypto::ECPrivateKey::Create());
|
|
|
| - if (!key->ExportEncryptedPrivateKey(ChannelIDService::kEPKIPassword,
|
| - 1, &private_key_info)) {
|
| - DLOG(ERROR) << "Unable to export private key";
|
| - *error = ERR_PRIVATE_KEY_EXPORT_FAILED;
|
| + if (!key) {
|
| + DLOG(ERROR) << "Unable to create channel ID key pair";
|
| + *error = ERR_KEY_GENERATION_FAILED;
|
| return result.Pass();
|
| }
|
|
|
| - // 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 private_key_out;
|
| + std::string public_key_out;
|
| + *error = ExportKeyPair(key, &public_key_out, &private_key_out);
|
| + if (*error != OK)
|
| + return result.Pass();
|
|
|
| result.reset(new ChannelIDStore::ChannelID(
|
| - server_identifier,
|
| - not_valid_before,
|
| - not_valid_after,
|
| - key_out,
|
| - der_cert));
|
| + server_identifier, not_valid_before, private_key_out, public_key_out));
|
| UMA_HISTOGRAM_CUSTOM_TIMES("DomainBoundCerts.GenerateCertTime",
|
| base::TimeTicks::Now() - start,
|
| base::TimeDelta::FromMilliseconds(1),
|
| @@ -138,32 +114,71 @@ scoped_ptr<ChannelIDStore::ChannelID> GenerateChannelID(
|
|
|
| } // namespace
|
|
|
| +int CreateECPrivateKeyFromSerializedKey(
|
| + const std::string& public_key,
|
| + const std::string& private_key,
|
| + scoped_ptr<crypto::ECPrivateKey>* key_out) {
|
| + if (public_key.empty() || private_key.empty())
|
| + return ERR_UNEXPECTED;
|
| +
|
| + std::vector<uint8> public_key_vector(public_key.size());
|
| + memcpy(&public_key_vector[0], public_key.data(), public_key.size());
|
| + std::vector<uint8> private_key_vector(private_key.size());
|
| + memcpy(&private_key_vector[0], private_key.data(), private_key.size());
|
| + crypto::ECPrivateKey* key =
|
| + crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
|
| + ChannelIDService::kEPKIPassword, private_key_vector,
|
| + public_key_vector);
|
| + if (key == nullptr)
|
| + return ERR_UNEXPECTED;
|
| + key_out->reset(key);
|
| + return OK;
|
| +}
|
| +
|
| +int ExportKeyPair(const scoped_ptr<crypto::ECPrivateKey>& key,
|
| + std::string* public_key,
|
| + std::string* private_key) {
|
| + std::vector<uint8> public_key_vector;
|
| + std::vector<uint8> private_key_vector;
|
| +
|
| + if (!key)
|
| + return ERR_UNEXPECTED;
|
| +
|
| + if (!key->ExportEncryptedPrivateKey(ChannelIDService::kEPKIPassword, 1,
|
| + &private_key_vector) ||
|
| + !key->ExportPublicKey(&public_key_vector)) {
|
| + DLOG(ERROR) << "Unable to export key";
|
| + return ERR_PRIVATE_KEY_EXPORT_FAILED;
|
| + }
|
| +
|
| + // TODO(rkn): Perhaps ExportPrivateKey should be changed to output a
|
| + // std::string* to prevent this copying.
|
| + *public_key = std::string(public_key_vector.begin(), public_key_vector.end());
|
| + *private_key =
|
| + std::string(private_key_vector.begin(), private_key_vector.end());
|
| + return OK;
|
| +}
|
| +
|
| // Represents the output and result callback of a request.
|
| class ChannelIDServiceRequest {
|
| public:
|
| ChannelIDServiceRequest(base::TimeTicks request_start,
|
| const CompletionCallback& callback,
|
| - std::string* private_key,
|
| - std::string* cert)
|
| - : request_start_(request_start),
|
| - callback_(callback),
|
| - private_key_(private_key),
|
| - cert_(cert) {
|
| - }
|
| + scoped_ptr<crypto::ECPrivateKey>* key)
|
| + : request_start_(request_start), callback_(callback), key_(key) {}
|
|
|
| // Ensures that the result callback will never be made.
|
| void Cancel() {
|
| RecordGetChannelIDResult(ASYNC_CANCELLED);
|
| callback_.Reset();
|
| - private_key_ = NULL;
|
| - cert_ = NULL;
|
| + key_->reset();
|
| }
|
|
|
| // Copies the contents of |private_key| and |cert| to the caller's output
|
| // arguments and calls the callback.
|
| void Post(int error,
|
| const std::string& private_key,
|
| - const std::string& cert) {
|
| + const std::string& public_key) {
|
| switch (error) {
|
| case OK: {
|
| base::TimeDelta request_time = base::TimeTicks::Now() - request_start_;
|
| @@ -193,9 +208,12 @@ class ChannelIDServiceRequest {
|
| break;
|
| }
|
| if (!callback_.is_null()) {
|
| - *private_key_ = private_key;
|
| - *cert_ = cert;
|
| - callback_.Run(error);
|
| + if (error == OK) {
|
| + callback_.Run(
|
| + CreateECPrivateKeyFromSerializedKey(public_key, private_key, key_));
|
| + } else {
|
| + callback_.Run(error);
|
| + }
|
| }
|
| delete this;
|
| }
|
| @@ -205,8 +223,7 @@ class ChannelIDServiceRequest {
|
| private:
|
| base::TimeTicks request_start_;
|
| CompletionCallback callback_;
|
| - std::string* private_key_;
|
| - std::string* cert_;
|
| + scoped_ptr<crypto::ECPrivateKey>* key_;
|
| };
|
|
|
| // ChannelIDServiceWorker runs on a worker thread and takes care of the
|
| @@ -223,7 +240,6 @@ class ChannelIDServiceWorker {
|
| const std::string& server_identifier,
|
| const WorkerDoneCallback& callback)
|
| : server_identifier_(server_identifier),
|
| - serial_number_(base::RandInt(0, std::numeric_limits<int>::max())),
|
| origin_loop_(base::MessageLoopProxy::current()),
|
| callback_(callback) {
|
| }
|
| @@ -244,7 +260,7 @@ class ChannelIDServiceWorker {
|
| // Runs on a worker thread.
|
| int error = ERR_FAILED;
|
| scoped_ptr<ChannelIDStore::ChannelID> cert =
|
| - GenerateChannelID(server_identifier_, serial_number_, &error);
|
| + GenerateChannelID(server_identifier_, &error);
|
| DVLOG(1) << "GenerateCert " << server_identifier_ << " returned " << error;
|
| #if !defined(USE_OPENSSL)
|
| // Detach the thread from NSPR.
|
| @@ -262,9 +278,6 @@ class ChannelIDServiceWorker {
|
| }
|
|
|
| const std::string server_identifier_;
|
| - // Note that serial_number_ must be initialized on a non-worker thread
|
| - // (see documentation for GenerateCert).
|
| - uint32 serial_number_;
|
| scoped_refptr<base::SequencedTaskRunner> origin_loop_;
|
| WorkerDoneCallback callback_;
|
|
|
| @@ -293,8 +306,8 @@ class ChannelIDServiceJob {
|
|
|
| void HandleResult(int error,
|
| const std::string& private_key,
|
| - const std::string& cert) {
|
| - PostAll(error, private_key, cert);
|
| + const std::string& public_key) {
|
| + PostAll(error, private_key, public_key);
|
| }
|
|
|
| bool CreateIfMissing() const { return create_if_missing_; }
|
| @@ -302,13 +315,13 @@ class ChannelIDServiceJob {
|
| private:
|
| void PostAll(int error,
|
| const std::string& private_key,
|
| - const std::string& cert) {
|
| + const std::string& public_key) {
|
| std::vector<ChannelIDServiceRequest*> requests;
|
| requests_.swap(requests);
|
|
|
| for (std::vector<ChannelIDServiceRequest*>::iterator
|
| i = requests.begin(); i != requests.end(); i++) {
|
| - (*i)->Post(error, private_key, cert);
|
| + (*i)->Post(error, private_key, public_key);
|
| // Post() causes the ChannelIDServiceRequest to delete itself.
|
| }
|
| }
|
| @@ -370,14 +383,10 @@ ChannelIDService::ChannelIDService(
|
| : channel_id_store_(channel_id_store),
|
| task_runner_(task_runner),
|
| requests_(0),
|
| - cert_store_hits_(0),
|
| + key_store_hits_(0),
|
| inflight_joins_(0),
|
| workers_created_(0),
|
| weak_ptr_factory_(this) {
|
| - base::Time start = base::Time::Now();
|
| - base::Time end = start + base::TimeDelta::FromDays(
|
| - kValidityPeriodInDays + kSystemTimeValidityBufferInDays);
|
| - is_system_time_valid_ = x509_util::IsSupportedValidityRange(start, end);
|
| }
|
|
|
| ChannelIDService::~ChannelIDService() {
|
| @@ -396,15 +405,14 @@ std::string ChannelIDService::GetDomainForHost(const std::string& host) {
|
|
|
| int ChannelIDService::GetOrCreateChannelID(
|
| const std::string& host,
|
| - std::string* private_key,
|
| - std::string* cert,
|
| + scoped_ptr<crypto::ECPrivateKey>* key,
|
| const CompletionCallback& callback,
|
| RequestHandle* out_req) {
|
| DVLOG(1) << __FUNCTION__ << " " << host;
|
| DCHECK(CalledOnValidThread());
|
| base::TimeTicks request_start = base::TimeTicks::Now();
|
|
|
| - if (callback.is_null() || !private_key || !cert || host.empty()) {
|
| + if (callback.is_null() || !key || host.empty()) {
|
| RecordGetChannelIDResult(INVALID_ARGUMENT);
|
| return ERR_INVALID_ARGUMENT;
|
| }
|
| @@ -419,13 +427,13 @@ int ChannelIDService::GetOrCreateChannelID(
|
|
|
| // See if a request for the same domain is currently in flight.
|
| bool create_if_missing = true;
|
| - if (JoinToInFlightRequest(request_start, domain, private_key, cert,
|
| - create_if_missing, callback, out_req)) {
|
| + if (JoinToInFlightRequest(request_start, domain, key, create_if_missing,
|
| + callback, out_req)) {
|
| return ERR_IO_PENDING;
|
| }
|
|
|
| - int err = LookupChannelID(request_start, domain, private_key, cert,
|
| - create_if_missing, callback, out_req);
|
| + int err = LookupChannelID(request_start, domain, key, create_if_missing,
|
| + callback, out_req);
|
| if (err == ERR_FILE_NOT_FOUND) {
|
| // Sync lookup did not find a valid cert. Start generating a new one.
|
| workers_created_++;
|
| @@ -444,11 +452,9 @@ int ChannelIDService::GetOrCreateChannelID(
|
| inflight_[domain] = job;
|
|
|
| ChannelIDServiceRequest* request = new ChannelIDServiceRequest(
|
| - request_start,
|
| - base::Bind(&RequestHandle::OnRequestComplete,
|
| - base::Unretained(out_req)),
|
| - private_key,
|
| - cert);
|
| + request_start, base::Bind(&RequestHandle::OnRequestComplete,
|
| + base::Unretained(out_req)),
|
| + key);
|
| job->AddRequest(request);
|
| out_req->RequestStarted(this, request, callback);
|
| return ERR_IO_PENDING;
|
| @@ -457,17 +463,15 @@ int ChannelIDService::GetOrCreateChannelID(
|
| return err;
|
| }
|
|
|
| -int ChannelIDService::GetChannelID(
|
| - const std::string& host,
|
| - std::string* private_key,
|
| - std::string* cert,
|
| - const CompletionCallback& callback,
|
| - RequestHandle* out_req) {
|
| +int ChannelIDService::GetChannelID(const std::string& host,
|
| + scoped_ptr<crypto::ECPrivateKey>* key,
|
| + const CompletionCallback& callback,
|
| + RequestHandle* out_req) {
|
| DVLOG(1) << __FUNCTION__ << " " << host;
|
| DCHECK(CalledOnValidThread());
|
| base::TimeTicks request_start = base::TimeTicks::Now();
|
|
|
| - if (callback.is_null() || !private_key || !cert || host.empty()) {
|
| + if (callback.is_null() || !key || host.empty()) {
|
| RecordGetChannelIDResult(INVALID_ARGUMENT);
|
| return ERR_INVALID_ARGUMENT;
|
| }
|
| @@ -482,22 +486,20 @@ int ChannelIDService::GetChannelID(
|
|
|
| // See if a request for the same domain currently in flight.
|
| bool create_if_missing = false;
|
| - if (JoinToInFlightRequest(request_start, domain, private_key, cert,
|
| - create_if_missing, callback, out_req)) {
|
| + if (JoinToInFlightRequest(request_start, domain, key, create_if_missing,
|
| + callback, out_req)) {
|
| return ERR_IO_PENDING;
|
| }
|
|
|
| - int err = LookupChannelID(request_start, domain, private_key, cert,
|
| - create_if_missing, callback, out_req);
|
| + int err = LookupChannelID(request_start, domain, key, create_if_missing,
|
| + callback, out_req);
|
| return err;
|
| }
|
|
|
| -void ChannelIDService::GotChannelID(
|
| - int err,
|
| - const std::string& server_identifier,
|
| - base::Time expiration_time,
|
| - const std::string& key,
|
| - const std::string& cert) {
|
| +void ChannelIDService::GotChannelID(int err,
|
| + const std::string& server_identifier,
|
| + const std::string& private_key,
|
| + const std::string& public_key) {
|
| DCHECK(CalledOnValidThread());
|
|
|
| std::map<std::string, ChannelIDServiceJob*>::iterator j;
|
| @@ -509,17 +511,17 @@ void ChannelIDService::GotChannelID(
|
|
|
| if (err == OK) {
|
| // Async DB lookup found a valid cert.
|
| - DVLOG(1) << "Cert store had valid cert for " << server_identifier;
|
| - cert_store_hits_++;
|
| + DVLOG(1) << "Cert store had valid key for " << server_identifier;
|
| + key_store_hits_++;
|
| // ChannelIDServiceRequest::Post will do the histograms and stuff.
|
| - HandleResult(OK, server_identifier, key, cert);
|
| + HandleResult(OK, server_identifier, private_key, public_key);
|
| return;
|
| }
|
| // Async lookup failed or the certificate was missing. Return the error
|
| // directly, unless the certificate was missing and a request asked to create
|
| // one.
|
| if (err != ERR_FILE_NOT_FOUND || !j->second->CreateIfMissing()) {
|
| - HandleResult(err, server_identifier, key, cert);
|
| + HandleResult(err, server_identifier, private_key, public_key);
|
| return;
|
| }
|
| // At least one request asked to create a cert => start generating a new one.
|
| @@ -556,24 +558,21 @@ void ChannelIDService::GeneratedChannelID(
|
| if (error == OK) {
|
| // TODO(mattm): we should just Pass() the cert object to
|
| // SetChannelID().
|
| - channel_id_store_->SetChannelID(
|
| - cert->server_identifier(),
|
| - cert->creation_time(),
|
| - cert->expiration_time(),
|
| - cert->private_key(),
|
| - cert->cert());
|
| -
|
| - HandleResult(error, server_identifier, cert->private_key(), cert->cert());
|
| + channel_id_store_->SetChannelID(cert->server_identifier(),
|
| + cert->creation_time(), cert->private_key(),
|
| + cert->public_key());
|
| +
|
| + HandleResult(error, server_identifier, cert->private_key(),
|
| + cert->public_key());
|
| } else {
|
| HandleResult(error, server_identifier, std::string(), std::string());
|
| }
|
| }
|
|
|
| -void ChannelIDService::HandleResult(
|
| - int error,
|
| - const std::string& server_identifier,
|
| - const std::string& private_key,
|
| - const std::string& cert) {
|
| +void ChannelIDService::HandleResult(int error,
|
| + const std::string& server_identifier,
|
| + const std::string& private_key,
|
| + const std::string& public_key) {
|
| DCHECK(CalledOnValidThread());
|
|
|
| std::map<std::string, ChannelIDServiceJob*>::iterator j;
|
| @@ -585,15 +584,14 @@ void ChannelIDService::HandleResult(
|
| ChannelIDServiceJob* job = j->second;
|
| inflight_.erase(j);
|
|
|
| - job->HandleResult(error, private_key, cert);
|
| + job->HandleResult(error, private_key, public_key);
|
| delete job;
|
| }
|
|
|
| bool ChannelIDService::JoinToInFlightRequest(
|
| const base::TimeTicks& request_start,
|
| const std::string& domain,
|
| - std::string* private_key,
|
| - std::string* cert,
|
| + scoped_ptr<crypto::ECPrivateKey>* key,
|
| bool create_if_missing,
|
| const CompletionCallback& callback,
|
| RequestHandle* out_req) {
|
| @@ -607,11 +605,9 @@ bool ChannelIDService::JoinToInFlightRequest(
|
| inflight_joins_++;
|
|
|
| ChannelIDServiceRequest* request = new ChannelIDServiceRequest(
|
| - request_start,
|
| - base::Bind(&RequestHandle::OnRequestComplete,
|
| - base::Unretained(out_req)),
|
| - private_key,
|
| - cert);
|
| + request_start, base::Bind(&RequestHandle::OnRequestComplete,
|
| + base::Unretained(out_req)),
|
| + key);
|
| job->AddRequest(request, create_if_missing);
|
| out_req->RequestStarted(this, request, callback);
|
| return true;
|
| @@ -619,29 +615,29 @@ bool ChannelIDService::JoinToInFlightRequest(
|
| return false;
|
| }
|
|
|
| -int ChannelIDService::LookupChannelID(
|
| - const base::TimeTicks& request_start,
|
| - const std::string& domain,
|
| - std::string* private_key,
|
| - std::string* cert,
|
| - bool create_if_missing,
|
| - const CompletionCallback& callback,
|
| - RequestHandle* out_req) {
|
| +int ChannelIDService::LookupChannelID(const base::TimeTicks& request_start,
|
| + const std::string& domain,
|
| + scoped_ptr<crypto::ECPrivateKey>* key,
|
| + bool create_if_missing,
|
| + const CompletionCallback& callback,
|
| + RequestHandle* out_req) {
|
| // Check if a domain bound cert already exists for this domain. Note that
|
| // |expiration_time| is ignored, and expired certs are considered valid.
|
| - base::Time expiration_time;
|
| + std::string public_key;
|
| + std::string private_key;
|
| int err = channel_id_store_->GetChannelID(
|
| - domain,
|
| - &expiration_time /* ignored */,
|
| - private_key,
|
| - cert,
|
| + domain, &private_key, &public_key,
|
| base::Bind(&ChannelIDService::GotChannelID,
|
| weak_ptr_factory_.GetWeakPtr()));
|
|
|
| if (err == OK) {
|
| + err = CreateECPrivateKeyFromSerializedKey(public_key, private_key, key);
|
| + if (err != OK) {
|
| + return err;
|
| + }
|
| // Sync lookup found a valid cert.
|
| - DVLOG(1) << "Cert store had valid cert for " << domain;
|
| - cert_store_hits_++;
|
| + DVLOG(1) << "Cert store had valid key for " << domain;
|
| + key_store_hits_++;
|
| RecordGetChannelIDResult(SYNC_SUCCESS);
|
| base::TimeDelta request_time = base::TimeTicks::Now() - request_start;
|
| UMA_HISTOGRAM_TIMES("DomainBoundCerts.GetCertTimeSync", request_time);
|
| @@ -655,11 +651,9 @@ int ChannelIDService::LookupChannelID(
|
| inflight_[domain] = job;
|
|
|
| ChannelIDServiceRequest* request = new ChannelIDServiceRequest(
|
| - request_start,
|
| - base::Bind(&RequestHandle::OnRequestComplete,
|
| - base::Unretained(out_req)),
|
| - private_key,
|
| - cert);
|
| + request_start, base::Bind(&RequestHandle::OnRequestComplete,
|
| + base::Unretained(out_req)),
|
| + key);
|
| job->AddRequest(request);
|
| out_req->RequestStarted(this, request, callback);
|
| return ERR_IO_PENDING;
|
|
|