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, |