Chromium Code Reviews| Index: chrome/browser/chromeos/platform_keys/platform_keys_service.cc |
| diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service.cc b/chrome/browser/chromeos/platform_keys/platform_keys_service.cc |
| index 4318043187267268698dff8eec92c9c01696d0d8..ad3d15e6737c38ecb0e2f1c2f2dfca6db94ed579 100644 |
| --- a/chrome/browser/chromeos/platform_keys/platform_keys_service.cc |
| +++ b/chrome/browser/chromeos/platform_keys/platform_keys_service.cc |
| @@ -6,6 +6,7 @@ |
| #include "base/base64.h" |
| #include "base/callback.h" |
| +#include "base/callback_helpers.h" |
| #include "base/values.h" |
| #include "chrome/browser/chromeos/platform_keys/platform_keys.h" |
| #include "content/public/browser/browser_thread.h" |
| @@ -30,32 +31,218 @@ scoped_ptr<base::StringValue> GetPublicKeyValue( |
| return make_scoped_ptr(new base::StringValue(public_key_spki_der_b64)); |
| } |
| -void RunGenerateKeyCallback( |
| - const PlatformKeysService::GenerateKeyCallback& callback, |
| - const std::string& public_key_spki_der) { |
| - callback.Run(public_key_spki_der, std::string() /* no error */); |
| -} |
| +} // namespace |
| -// Callback used by |PlatformKeysService::Sign|. |
| -// Is called with the old validity of |public_key_spki_der| (or false if an |
| -// error occurred during reading the StateStore). If allowed, starts the actual |
| -// signing operation which will call back |callback|. If not allowed, calls |
| -// |callback| with an error. |
| -void CheckValidityAndSign(const std::string& token_id, |
| - scoped_ptr<platform_keys::SignRSAParams> params, |
| - const PlatformKeysService::SignCallback& callback, |
| - content::BrowserContext* browser_context, |
| - bool key_is_valid) { |
| - if (!key_is_valid) { |
| - callback.Run(std::string() /* no signature */, |
| - kErrorKeyNotAllowedForSigning); |
| - return; |
| +class PlatformKeysService::Task { |
| + public: |
| + Task() {} |
| + virtual ~Task() {} |
| + virtual void Start() = 0; |
| + virtual bool IsDone() = 0; |
| + |
| + private: |
| + DISALLOW_ASSIGN(Task); |
| +}; |
| + |
| +class PlatformKeysService::PermissionUpdateTask : public Task { |
| + public: |
| + enum class Step { |
| + READ_PLATFORM_KEYS, |
| + WRITE_UPDATE_AND_CALLBACK, |
| + DONE, |
| + }; |
| + |
| + PermissionUpdateTask(const bool new_permission_value, |
|
kaliamoorthi
2015/02/09 15:13:15
Document
pneubeck (no reviews)
2015/02/09 15:36:11
Done.
|
| + const std::string& public_key_spki_der, |
| + const std::string& extension_id, |
| + base::Callback<void(Task*)> callback, |
| + PlatformKeysService* service) |
| + : new_permission_value_(new_permission_value), |
| + public_key_spki_der_(public_key_spki_der), |
| + extension_id_(extension_id), |
| + callback_(callback), |
| + service_(service), |
| + weak_factory_(this) {} |
| + |
| + ~PermissionUpdateTask() override {} |
| + |
| + void Start() override { |
| + CHECK(next_step_ == Step::READ_PLATFORM_KEYS); |
| + DoStep(); |
| } |
| - platform_keys::subtle::SignRSA(token_id, params.Pass(), callback, |
| - browser_context); |
| -} |
| -} // namespace |
| + bool IsDone() override { return next_step_ == Step::DONE; } |
| + |
| + bool old_permission_value() { return old_permission_value_; } |
| + |
| + private: |
| + void DoStep() { |
| + switch (next_step_) { |
| + case Step::READ_PLATFORM_KEYS: |
| + next_step_ = Step::WRITE_UPDATE_AND_CALLBACK; |
| + ReadPlatformKeys(); |
| + return; |
| + case Step::WRITE_UPDATE_AND_CALLBACK: |
| + next_step_ = Step::DONE; |
| + WriteUpdate(); |
| + if (!callback_.is_null()) { |
| + // Make a local copy of the callback to run as it might be deleted |
| + // during the Run(). |
| + base::ResetAndReturn(&callback_).Run(this); |
| + // |this| might be invalid now. |
| + } |
| + return; |
| + case Step::DONE: |
| + NOTREACHED(); |
| + return; |
| + } |
| + } |
| + |
| + // Reads the PlatformKeys value from the extension's state store and calls |
| + // back to GotPlatformKeys(). |
| + void ReadPlatformKeys() { |
| + service_->GetPlatformKeysOfExtension( |
| + extension_id_, base::Bind(&PermissionUpdateTask::GotPlatformKeys, |
| + weak_factory_.GetWeakPtr())); |
| + } |
| + |
| + void GotPlatformKeys(scoped_ptr<base::ListValue> platform_keys) { |
| + platform_keys_ = platform_keys.Pass(); |
| + DoStep(); |
| + } |
| + |
| + // Returns whether the extension has permission to use the key for signing |
| + // according to the PlatformKeys value read from the extensions state store. |
| + // Invalidates the key if it was found to be valid. |
| + void WriteUpdate() { |
| + scoped_ptr<base::StringValue> key_value( |
| + GetPublicKeyValue(public_key_spki_der_)); |
| + |
| + base::ListValue::const_iterator it = platform_keys_->Find(*key_value); |
| + old_permission_value_ = it != platform_keys_->end(); |
| + if (old_permission_value_ == new_permission_value_) |
| + return; |
| + |
| + if (new_permission_value_) |
| + platform_keys_->Append(key_value.release()); |
| + else |
| + platform_keys_->Remove(*key_value, nullptr); |
| + |
| + service_->SetPlatformKeysOfExtension(extension_id_, platform_keys_.Pass()); |
| + } |
| + |
| + Step next_step_ = Step::READ_PLATFORM_KEYS; |
| + scoped_ptr<base::ListValue> platform_keys_; |
| + bool old_permission_value_ = false; |
| + |
| + const bool new_permission_value_; |
| + const std::string public_key_spki_der_; |
| + const std::string extension_id_; |
| + base::Callback<void(Task*)> callback_; |
| + PlatformKeysService* const service_; |
| + base::WeakPtrFactory<PermissionUpdateTask> weak_factory_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(PermissionUpdateTask); |
| +}; |
| + |
| +class PlatformKeysService::SignTask : public Task { |
| + public: |
| + enum class Step { |
| + UPDATE_PERMISSION, |
| + SIGN_OR_ABORT, |
| + DONE, |
| + }; |
| + |
| + // This Task will check the permissions of the extension with |extension_id| |
| + // for the key identified by |public_key_spki_der|, then updates the |
| + // permission to prevent any future signing operation of that extension using |
| + // that same key. If the permission check was positive, it will actually sign |
| + // |data| with the key and pass the signature to |callback|. |
| + // If an error occurs, an error message is passed to |callback| instead. |
| + SignTask(const std::string& token_id, |
| + scoped_ptr<platform_keys::SignRSAParams> params, |
| + const std::string& extension_id, |
| + const SignCallback& callback, |
| + PlatformKeysService* service) |
| + : token_id_(token_id), |
| + params_(params.Pass()), |
| + extension_id_(extension_id), |
| + callback_(callback), |
| + service_(service), |
| + weak_factory_(this) {} |
| + ~SignTask() override {} |
| + |
| + void Start() override { |
| + CHECK(next_step_ == Step::UPDATE_PERMISSION); |
| + DoStep(); |
| + } |
| + bool IsDone() override { return next_step_ == Step::DONE; } |
| + |
| + private: |
| + void DoStep() { |
| + switch (next_step_) { |
| + case Step::UPDATE_PERMISSION: |
| + next_step_ = Step::SIGN_OR_ABORT; |
| + UpdatePermission(); |
| + return; |
| + case Step::SIGN_OR_ABORT: |
| + next_step_ = Step::DONE; |
| + if (!service_->permission_check_enabled_ || |
| + permission_update_->old_permission_value()) { |
| + Sign(); |
| + } else { |
| + callback_.Run(std::string() /* no signature */, |
| + kErrorKeyNotAllowedForSigning); |
| + DoStep(); |
| + } |
| + return; |
| + case Step::DONE: |
| + service_->TaskFinished(this); |
| + // |this| might be invalid now. |
| + return; |
| + } |
| + } |
| + |
| + // Reads the current permission of the extension with |extension_id_| for key |
| + // |params_->public_key| and updates the permission to disable further |
| + // signing operations with that key. |
| + void UpdatePermission() { |
| + permission_update_.reset(new PermissionUpdateTask( |
| + false /* new permission value */, params_->public_key(), extension_id_, |
| + base::Bind(&SignTask::DidUpdatePermission, weak_factory_.GetWeakPtr()), |
| + service_)); |
| + permission_update_->Start(); |
| + } |
| + |
| + void DidUpdatePermission(Task* /* task */) { DoStep(); } |
| + |
| + // Starts the actual signing operation and afterwards passes the signature (or |
| + // error) to |callback_|. |
| + void Sign() { |
| + platform_keys::subtle::SignRSA( |
| + token_id_, params_.Pass(), |
| + base::Bind(&SignTask::DidSign, weak_factory_.GetWeakPtr()), |
| + service_->browser_context_); |
| + } |
| + |
| + void DidSign(const std::string& signature, const std::string& error_message) { |
| + callback_.Run(signature, error_message); |
| + DoStep(); |
| + } |
| + |
| + Step next_step_ = Step::UPDATE_PERMISSION; |
| + scoped_ptr<base::ListValue> platform_keys_; |
| + scoped_ptr<PermissionUpdateTask> permission_update_; |
| + |
| + const std::string token_id_; |
| + scoped_ptr<platform_keys::SignRSAParams> params_; |
| + const std::string extension_id_; |
| + const SignCallback callback_; |
| + PlatformKeysService* const service_; |
| + base::WeakPtrFactory<SignTask> weak_factory_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(SignTask); |
| +}; |
| PlatformKeysService::PlatformKeysService( |
| content::BrowserContext* browser_context, |
| @@ -80,12 +267,9 @@ void PlatformKeysService::GenerateRSAKey(const std::string& token_id, |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| platform_keys::subtle::GenerateRSAKey( |
| - token_id, |
| - modulus_length, |
| - base::Bind(&PlatformKeysService::GenerateRSAKeyCallback, |
| - weak_factory_.GetWeakPtr(), |
| - extension_id, |
| - callback), |
| + token_id, modulus_length, |
| + base::Bind(&PlatformKeysService::GeneratedKey, weak_factory_.GetWeakPtr(), |
| + extension_id, callback), |
| browser_context_); |
| } |
| @@ -95,10 +279,8 @@ void PlatformKeysService::SignRSA( |
| const std::string& extension_id, |
| const SignCallback& callback) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - ReadValidityAndInvalidateKey( |
| - extension_id, params->public_key(), |
| - base::Bind(&CheckValidityAndSign, token_id, base::Passed(¶ms), |
| - callback, browser_context_)); |
| + StartOrQueueTask(make_scoped_ptr( |
| + new SignTask(token_id, params.Pass(), extension_id, callback, this))); |
| } |
| void PlatformKeysService::SelectClientCertificates( |
| @@ -114,29 +296,20 @@ void PlatformKeysService::SelectClientCertificates( |
| browser_context_); |
| } |
| -void PlatformKeysService::RegisterPublicKey( |
| - const std::string& extension_id, |
| - const std::string& public_key_spki_der, |
| - const base::Closure& callback) { |
| - GetPlatformKeysOfExtension( |
| - extension_id, |
| - base::Bind(&PlatformKeysService::RegisterPublicKeyGotPlatformKeys, |
| - weak_factory_.GetWeakPtr(), |
| - extension_id, |
| - public_key_spki_der, |
| - callback)); |
| +void PlatformKeysService::StartOrQueueTask(scoped_ptr<Task> task) { |
| + tasks_.push(make_linked_ptr(task.release())); |
|
kaliamoorthi
2015/02/09 15:13:15
Is there are reason why linked_ptr is used here ra
pneubeck (no reviews)
2015/02/09 15:36:11
scoped_ptrs don't work in containers as it interna
|
| + if (tasks_.size() == 1) |
| + tasks_.front()->Start(); |
| } |
| -void PlatformKeysService::ReadValidityAndInvalidateKey( |
| - const std::string& extension_id, |
| - const std::string& public_key_spki_der, |
| - const base::Callback<void(bool)>& callback) { |
| - GetPlatformKeysOfExtension(extension_id, |
| - base::Bind(&PlatformKeysService::InvalidateKey, |
| - weak_factory_.GetWeakPtr(), |
| - extension_id, |
| - public_key_spki_der, |
| - callback)); |
| +void PlatformKeysService::TaskFinished(Task* task) { |
| + DCHECK(!tasks_.empty()); |
| + DCHECK(task == tasks_.front().get()); |
| + while (!tasks_.empty() && tasks_.front()->IsDone()) |
|
kaliamoorthi
2015/02/09 15:13:15
Can multiple tasks execute at the same time? Eithe
pneubeck (no reviews)
2015/02/09 15:36:11
At most one task is running at a time.
Do you sugg
|
| + tasks_.pop(); |
| + |
| + if (!tasks_.empty()) |
| + tasks_.front()->Start(); |
| } |
| void PlatformKeysService::GetPlatformKeysOfExtension( |
| @@ -155,18 +328,28 @@ void PlatformKeysService::SetPlatformKeysOfExtension( |
| platform_keys.Pass()); |
| } |
| -void PlatformKeysService::GenerateRSAKeyCallback( |
| - const std::string& extension_id, |
| - const GenerateKeyCallback& callback, |
| - const std::string& public_key_spki_der, |
| - const std::string& error_message) { |
| +void PlatformKeysService::GeneratedKey(const std::string& extension_id, |
| + const GenerateKeyCallback& callback, |
| + const std::string& public_key_spki_der, |
| + const std::string& error_message) { |
| if (!error_message.empty()) { |
| callback.Run(std::string() /* no public key */, error_message); |
| return; |
| } |
| - base::Closure wrapped_callback( |
| - base::Bind(&RunGenerateKeyCallback, callback, public_key_spki_der)); |
| - RegisterPublicKey(extension_id, public_key_spki_der, wrapped_callback); |
| + |
| + StartOrQueueTask(make_scoped_ptr(new PermissionUpdateTask( |
| + true /* new permission value */, public_key_spki_der, extension_id, |
| + base::Bind(&PlatformKeysService::DidRegisterGeneratedKey, |
| + base::Unretained(this), callback, public_key_spki_der), |
| + this))); |
| +} |
| + |
| +void PlatformKeysService::DidRegisterGeneratedKey( |
| + const GenerateKeyCallback& callback, |
| + const std::string& public_key_spki_der, |
| + Task* task) { |
| + callback.Run(public_key_spki_der, std::string() /* no error */); |
|
kaliamoorthi
2015/02/09 15:13:15
Why is this parameter needed if it is empty? If it
pneubeck (no reviews)
2015/02/09 15:36:11
which parameter? the error parameter?
See Generate
|
| + TaskFinished(task); |
| } |
| void PlatformKeysService::SelectClientCertificatesCallback( |
| @@ -181,51 +364,6 @@ void PlatformKeysService::SelectClientCertificatesCallback( |
| callback.Run(matches.Pass(), error_message); |
| } |
| -void PlatformKeysService::RegisterPublicKeyGotPlatformKeys( |
| - const std::string& extension_id, |
| - const std::string& public_key_spki_der, |
| - const base::Closure& callback, |
| - scoped_ptr<base::ListValue> platform_keys) { |
| - scoped_ptr<base::StringValue> key_value( |
| - GetPublicKeyValue(public_key_spki_der)); |
| - |
| - DCHECK(platform_keys->end() == platform_keys->Find(*key_value)) |
| - << "Keys are assumed to be generated and not to be registered multiple " |
| - "times."; |
| - platform_keys->Append(key_value.release()); |
| - SetPlatformKeysOfExtension(extension_id, platform_keys.Pass()); |
| - callback.Run(); |
| -} |
| - |
| -void PlatformKeysService::InvalidateKey( |
| - const std::string& extension_id, |
| - const std::string& public_key_spki_der, |
| - const base::Callback<void(bool)>& callback, |
| - scoped_ptr<base::ListValue> platform_keys) { |
| - scoped_ptr<base::StringValue> key_value( |
| - GetPublicKeyValue(public_key_spki_der)); |
| - |
| - size_t index = 0; |
| - // If the key is found in |platform_keys|, it's valid for the extension to use |
| - // it for signing. |
| - bool key_was_valid = platform_keys->Remove(*key_value, &index); |
| - |
| - if (key_was_valid) { |
| - // Persist that the key is now invalid. |
| - SetPlatformKeysOfExtension(extension_id, platform_keys.Pass()); |
| - } |
| - |
| - if (permission_check_enabled_) { |
| - // If permission checks are enabled, pass back the key permission (before |
| - // it was removed above). |
| - callback.Run(key_was_valid); |
| - } else { |
| - // Otherwise just allow signing with the key (which is enabled for testing |
| - // only). |
| - callback.Run(true); |
| - } |
| -} |
| - |
| void PlatformKeysService::GotPlatformKeysOfExtension( |
| const std::string& extension_id, |
| const GetPlatformKeysCallback& callback, |