| 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 ca333c9b64e51957a1e5e8bd772feee5092bce43..4f9eb1761d888a5fcf95cfc08cecab7a476c01ba 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,40 +31,245 @@ 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,
|
| - const std::string& data,
|
| - const std::string& public_key,
|
| - bool sign_direct_pkcs_padded,
|
| - platform_keys::HashAlgorithm hash_algorithm,
|
| - 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,
|
| + };
|
| +
|
| + // Creates a task that reads the current permission for an extension to access
|
| + // a certain key. Afterwards it updates and persists the permission to the new
|
| + // value |new_permission_value|. |callback| will be run after the permission
|
| + // was persisted. The old permission value is then accessible through
|
| + // old_permission_value().
|
| + PermissionUpdateTask(const bool new_permission_value,
|
| + 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();
|
| }
|
| - if (sign_direct_pkcs_padded) {
|
| - platform_keys::subtle::SignRSAPKCS1Raw(token_id, data, public_key, callback,
|
| - browser_context);
|
| - } else {
|
| - platform_keys::subtle::SignRSAPKCS1Digest(
|
| - token_id, data, public_key, hash_algorithm, callback, browser_context);
|
| +
|
| + bool IsDone() override { return next_step_ == Step::DONE; }
|
| +
|
| + // The original permission value before setting the new value
|
| + // |new_permission_value|.
|
| + 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;
|
| + }
|
| }
|
| -}
|
|
|
| -} // namespace
|
| + // 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,
|
| + const std::string& data,
|
| + const std::string& public_key,
|
| + bool sign_direct_pkcs_padded,
|
| + platform_keys::HashAlgorithm hash_algorithm,
|
| + const std::string& extension_id,
|
| + const SignCallback& callback,
|
| + PlatformKeysService* service)
|
| + : token_id_(token_id),
|
| + data_(data),
|
| + public_key_(public_key),
|
| + sign_direct_pkcs_padded_(sign_direct_pkcs_padded),
|
| + hash_algorithm_(hash_algorithm),
|
| + 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 */, 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() {
|
| + if (sign_direct_pkcs_padded_) {
|
| + platform_keys::subtle::SignRSAPKCS1Raw(
|
| + token_id_, data_, public_key_,
|
| + base::Bind(&SignTask::DidSign, weak_factory_.GetWeakPtr()),
|
| + service_->browser_context_);
|
| + } else {
|
| + platform_keys::subtle::SignRSAPKCS1Digest(
|
| + token_id_, data_, public_key_, hash_algorithm_,
|
| + 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_;
|
| + const std::string data_;
|
| + const std::string public_key_;
|
| +
|
| + // If true, |data_| will not be hashed before signing. Only PKCS#1 v1.5
|
| + // padding will be applied before signing.
|
| + // If false, |hash_algorithm_| is set to a value != NONE.
|
| + const bool sign_direct_pkcs_padded_;
|
| + const platform_keys::HashAlgorithm hash_algorithm_;
|
| + 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,
|
| @@ -88,12 +294,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_);
|
| }
|
|
|
| @@ -105,11 +308,9 @@ void PlatformKeysService::SignRSAPKCS1Digest(
|
| const std::string& extension_id,
|
| const SignCallback& callback) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - ReadValidityAndInvalidateKey(
|
| - extension_id, public_key,
|
| - base::Bind(&CheckValidityAndSign, token_id, data, public_key,
|
| - false /* digest before signing */, hash_algorithm, callback,
|
| - browser_context_));
|
| + StartOrQueueTask(make_scoped_ptr(new SignTask(
|
| + token_id, data, public_key, false /* digest before signing */,
|
| + hash_algorithm, extension_id, callback, this)));
|
| }
|
|
|
| void PlatformKeysService::SignRSAPKCS1Raw(const std::string& token_id,
|
| @@ -118,12 +319,9 @@ void PlatformKeysService::SignRSAPKCS1Raw(const std::string& token_id,
|
| const std::string& extension_id,
|
| const SignCallback& callback) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - ReadValidityAndInvalidateKey(
|
| - extension_id, public_key,
|
| - base::Bind(&CheckValidityAndSign, token_id, data, public_key,
|
| - true /* sign directly without hashing */,
|
| - platform_keys::HASH_ALGORITHM_NONE, callback,
|
| - browser_context_));
|
| + StartOrQueueTask(make_scoped_ptr(new SignTask(
|
| + token_id, data, public_key, true /* sign directly without hashing */,
|
| + platform_keys::HASH_ALGORITHM_NONE, extension_id, callback, this)));
|
| }
|
|
|
| void PlatformKeysService::SelectClientCertificates(
|
| @@ -139,29 +337,23 @@ 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()));
|
| + 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());
|
| + // Remove all finished tasks from the queue (should be at most one).
|
| + while (!tasks_.empty() && tasks_.front()->IsDone())
|
| + tasks_.pop();
|
| +
|
| + // Now either the queue is empty or the next task is not finished yet and it
|
| + // can be started.
|
| + if (!tasks_.empty())
|
| + tasks_.front()->Start();
|
| }
|
|
|
| void PlatformKeysService::GetPlatformKeysOfExtension(
|
| @@ -180,18 +372,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::RegisteredGeneratedKey,
|
| + base::Unretained(this), callback, public_key_spki_der),
|
| + this)));
|
| +}
|
| +
|
| +void PlatformKeysService::RegisteredGeneratedKey(
|
| + const GenerateKeyCallback& callback,
|
| + const std::string& public_key_spki_der,
|
| + Task* task) {
|
| + callback.Run(public_key_spki_der, std::string() /* no error */);
|
| + TaskFinished(task);
|
| }
|
|
|
| void PlatformKeysService::SelectClientCertificatesCallback(
|
| @@ -206,51 +408,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,
|
|
|