Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h" | 5 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h" |
| 6 | 6 |
| 7 #include "base/base64.h" | 7 #include "base/base64.h" |
| 8 #include "base/callback.h" | 8 #include "base/callback.h" |
| 9 #include "base/callback_helpers.h" | 9 #include "base/callback_helpers.h" |
| 10 #include "base/values.h" | 10 #include "base/values.h" |
| 11 #include "chrome/browser/chromeos/platform_keys/platform_keys.h" | 11 #include "chrome/browser/chromeos/platform_keys/platform_keys.h" |
| 12 #include "content/public/browser/browser_thread.h" | 12 #include "content/public/browser/browser_thread.h" |
| 13 #include "extensions/browser/state_store.h" | 13 #include "extensions/browser/state_store.h" |
| 14 #include "net/cert/x509_certificate.h" | 14 #include "net/cert/x509_certificate.h" |
| 15 | 15 |
| 16 using content::BrowserThread; | 16 using content::BrowserThread; |
| 17 | 17 |
| 18 namespace chromeos { | 18 namespace chromeos { |
| 19 | 19 |
| 20 struct PlatformKeysService::KeyEntry { | |
| 21 // The base64 encoded DER of a X.509 Subject Public Key Info. | |
|
Andrew T Wilson (Slow)
2015/02/18 19:53:35
Nit: "the..." -> "a base64-encoded DER"
pneubeck (no reviews)
2015/02/19 11:08:40
Done.
| |
| 22 std::string spki_b64; | |
| 23 | |
| 24 // True if the key can be used once for singing. | |
| 25 bool sign_once = false; | |
|
kaliamoorthi
2015/02/18 17:04:09
Why do we have two bools for sign_once and sign_un
pneubeck (no reviews)
2015/02/19 11:08:40
These two permissions origin from the two differen
| |
| 26 | |
| 27 // True if the key can be used for signing unlimited often. | |
|
Andrew T Wilson (Slow)
2015/02/18 19:53:35
nit: "signing an unlimited number of times" - "unl
pneubeck (no reviews)
2015/02/19 11:08:39
Done.
| |
| 28 bool sign_unlimited = false; | |
| 29 }; | |
| 30 | |
| 20 namespace { | 31 namespace { |
| 21 | 32 |
| 22 const char kErrorKeyNotAllowedForSigning[] = | 33 const char kErrorKeyNotAllowedForSigning[] = |
| 23 "This key is not allowed for signing. Either it was used for signing " | 34 "This key is not allowed for signing. Either it was used for signing " |
| 24 "before or it was not correctly generated."; | 35 "before or it was not correctly generated."; |
| 36 | |
| 37 // The key at which platform key specific data is stored in each extension's | |
| 38 // state store. | |
| 39 // From older versions of ChromeOS, this key can hold a list of base64 encoded | |
|
Andrew T Wilson (Slow)
2015/02/18 19:53:34
nit: base64-encoded
pneubeck (no reviews)
2015/02/19 11:08:40
Done.
| |
| 40 // SPKIs. A key can be used for signing at most once if it is part of that list | |
| 41 // and removed from that list afterwards. | |
| 42 // | |
| 43 // The current format of data that is written to the PlatformKeys field is a | |
| 44 // list of serialized KeyEntry objects: | |
| 45 // { 'SPKI': string , | |
| 46 // 'signOnce': bool , | |
| 47 // 'signUnlimited': bool | |
| 48 // } | |
| 49 // | |
| 50 // Do not change this constant as clients will lose their existing state. | |
| 25 const char kStateStorePlatformKeys[] = "PlatformKeys"; | 51 const char kStateStorePlatformKeys[] = "PlatformKeys"; |
| 52 const char kStateStoreSPKI[] = "SPKI"; | |
| 53 const char kStateStoreSignOnce[] = "signOnce"; | |
| 54 const char kStateStoreSignUnlimited[] = "signUnlimited"; | |
| 26 | 55 |
| 27 scoped_ptr<base::StringValue> GetPublicKeyValue( | 56 scoped_ptr<PlatformKeysService::KeyEntries> KeyEntriesFromState( |
| 28 const std::string& public_key_spki_der) { | 57 const base::Value& state) { |
| 29 std::string public_key_spki_der_b64; | 58 scoped_ptr<PlatformKeysService::KeyEntries> new_entries( |
| 30 base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64); | 59 new PlatformKeysService::KeyEntries); |
| 31 return make_scoped_ptr(new base::StringValue(public_key_spki_der_b64)); | 60 |
| 61 const base::ListValue* entries = nullptr; | |
| 62 if (!state.GetAsList(&entries)) { | |
| 63 LOG(ERROR) << "Found a state store of wrong type."; | |
| 64 return new_entries.Pass(); | |
| 65 } | |
| 66 for (const base::Value* entry : *entries) { | |
| 67 if (!entry) { | |
| 68 LOG(ERROR) << "Found invalid NULL entry in PlatformKeys state store."; | |
| 69 continue; | |
| 70 } | |
| 71 | |
| 72 PlatformKeysService::KeyEntry new_entry; | |
| 73 const base::DictionaryValue* dict_entry = nullptr; | |
| 74 if (entry->GetAsString(&new_entry.spki_b64)) { | |
|
Andrew T Wilson (Slow)
2015/02/18 19:53:35
You talk about this above, but not bad to explain
pneubeck (no reviews)
2015/02/19 11:08:39
Done.
| |
| 75 new_entry.sign_once = true; | |
| 76 } else if (entry->GetAsDictionary(&dict_entry)) { | |
| 77 dict_entry->GetStringWithoutPathExpansion(kStateStoreSPKI, | |
| 78 &new_entry.spki_b64); | |
| 79 dict_entry->GetBooleanWithoutPathExpansion(kStateStoreSignOnce, | |
| 80 &new_entry.sign_once); | |
| 81 dict_entry->GetBooleanWithoutPathExpansion(kStateStoreSignUnlimited, | |
| 82 &new_entry.sign_unlimited); | |
| 83 } else { | |
| 84 LOG(ERROR) << "Found invalid entry of type " << entry->GetType() | |
| 85 << " in PlatformKeys state store."; | |
| 86 continue; | |
| 87 } | |
| 88 new_entries->push_back(new_entry); | |
| 89 } | |
| 90 return new_entries.Pass(); | |
| 91 } | |
| 92 | |
| 93 scoped_ptr<base::ListValue> KeyEntriesToState( | |
| 94 const PlatformKeysService::KeyEntries& entries) { | |
| 95 scoped_ptr<base::ListValue> new_state(new base::ListValue); | |
| 96 for (const PlatformKeysService::KeyEntry& entry : entries) { | |
| 97 scoped_ptr<base::DictionaryValue> new_entry(new base::DictionaryValue); | |
| 98 | |
|
Andrew T Wilson (Slow)
2015/02/18 19:53:35
nit:permissions, not premissions.
pneubeck (no reviews)
2015/02/19 11:08:40
Done.
| |
| 99 // Drop entries that the extension doesn't have any premissions for anymore. | |
| 100 if (!entry.sign_once && !entry.sign_unlimited) | |
|
kaliamoorthi
2015/02/18 17:04:09
Move this above the new.
pneubeck (no reviews)
2015/02/19 11:08:39
Done.
| |
| 101 continue; | |
| 102 new_entry->SetStringWithoutPathExpansion(kStateStoreSPKI, entry.spki_b64); | |
| 103 if (entry.sign_once) { | |
|
kaliamoorthi
2015/02/18 17:04:09
Is there a reason why the bool is written only whe
pneubeck (no reviews)
2015/02/19 11:08:40
added a comment that defaults values are not store
| |
| 104 new_entry->SetBooleanWithoutPathExpansion(kStateStoreSignOnce, | |
| 105 entry.sign_once); | |
| 106 } | |
| 107 if (entry.sign_unlimited) { | |
| 108 new_entry->SetBooleanWithoutPathExpansion(kStateStoreSignUnlimited, | |
| 109 entry.sign_unlimited); | |
| 110 } | |
| 111 new_state->Append(new_entry.release()); | |
| 112 } | |
| 113 return new_state.Pass(); | |
| 114 } | |
| 115 | |
| 116 // Searches |platform_keys| for an entry for |public_key_spki_der_b64|. If found | |
| 117 // returns a pointer to it, otherwise returns null. | |
| 118 PlatformKeysService::KeyEntry* GetMatchingEntry( | |
| 119 const std::string& public_key_spki_der_b64, | |
| 120 PlatformKeysService::KeyEntries* platform_keys) { | |
| 121 for (PlatformKeysService::KeyEntry& entry : *platform_keys) { | |
| 122 if (entry.spki_b64 == public_key_spki_der_b64) | |
|
Andrew T Wilson (Slow)
2015/02/18 19:53:35
Thing to remember here is that DER encodings are n
pneubeck (no reviews)
2015/02/19 11:08:40
Good point. I added a clarification that DER is ac
| |
| 123 return &entry; | |
| 124 } | |
| 125 return nullptr; | |
| 32 } | 126 } |
| 33 | 127 |
| 34 } // namespace | 128 } // namespace |
| 35 | 129 |
| 36 class PlatformKeysService::Task { | 130 class PlatformKeysService::Task { |
| 37 public: | 131 public: |
| 38 Task() {} | 132 Task() {} |
| 39 virtual ~Task() {} | 133 virtual ~Task() {} |
| 40 virtual void Start() = 0; | 134 virtual void Start() = 0; |
| 41 virtual bool IsDone() = 0; | 135 virtual bool IsDone() = 0; |
| 42 | 136 |
| 43 private: | 137 private: |
| 44 DISALLOW_ASSIGN(Task); | 138 DISALLOW_ASSIGN(Task); |
| 45 }; | 139 }; |
| 46 | 140 |
| 47 class PlatformKeysService::PermissionUpdateTask : public Task { | 141 class PlatformKeysService::PermissionUpdateTask : public Task { |
| 48 public: | 142 public: |
| 49 enum class Step { | 143 enum class Step { |
| 50 READ_PLATFORM_KEYS, | 144 READ_PLATFORM_KEYS, |
| 51 WRITE_UPDATE_AND_CALLBACK, | 145 WRITE_UPDATE_AND_CALLBACK, |
| 52 DONE, | 146 DONE, |
| 53 }; | 147 }; |
| 54 | 148 |
| 55 // Creates a task that reads the current permission for an extension to access | 149 // Creates a task that reads the current permission for an extension to access |
| 56 // a certain key. Afterwards it updates and persists the permission to the new | 150 // a certain key. Afterwards it updates and persists the permission to the new |
| 57 // value |new_permission_value|. |callback| will be run after the permission | 151 // value |new_permission_value|. |callback| will be run after the permission |
| 58 // was persisted. The old permission value is then accessible through | 152 // was persisted. The old permission values are then available through |
| 59 // old_permission_value(). | 153 // old_key_entry(). |
| 60 PermissionUpdateTask(const bool new_permission_value, | 154 PermissionUpdateTask(const SignPermission permission, |
| 155 const bool new_permission_value, | |
| 61 const std::string& public_key_spki_der, | 156 const std::string& public_key_spki_der, |
| 62 const std::string& extension_id, | 157 const std::string& extension_id, |
| 63 base::Callback<void(Task*)> callback, | 158 base::Callback<void(Task*)> callback, |
| 64 PlatformKeysService* service) | 159 PlatformKeysService* service) |
| 65 : new_permission_value_(new_permission_value), | 160 : permission_(permission), |
| 161 new_permission_value_(new_permission_value), | |
| 66 public_key_spki_der_(public_key_spki_der), | 162 public_key_spki_der_(public_key_spki_der), |
| 67 extension_id_(extension_id), | 163 extension_id_(extension_id), |
| 68 callback_(callback), | 164 callback_(callback), |
| 69 service_(service), | 165 service_(service), |
| 70 weak_factory_(this) {} | 166 weak_factory_(this) {} |
| 71 | 167 |
| 72 ~PermissionUpdateTask() override {} | 168 ~PermissionUpdateTask() override {} |
| 73 | 169 |
| 74 void Start() override { | 170 void Start() override { |
| 75 CHECK(next_step_ == Step::READ_PLATFORM_KEYS); | 171 CHECK(next_step_ == Step::READ_PLATFORM_KEYS); |
| 76 DoStep(); | 172 DoStep(); |
| 77 } | 173 } |
| 78 | 174 |
| 79 bool IsDone() override { return next_step_ == Step::DONE; } | 175 bool IsDone() override { return next_step_ == Step::DONE; } |
| 80 | 176 |
| 81 // The original permission value before setting the new value | 177 // The original key entry before setting the new value |new_permission_value|. |
|
kaliamoorthi
2015/02/18 17:04:09
This is confusing, original key entry and new perm
pneubeck (no reviews)
2015/02/19 11:08:40
Done.
| |
| 82 // |new_permission_value|. | 178 const KeyEntry& old_key_entry() { return old_key_entry_; } |
| 83 bool old_permission_value() { return old_permission_value_; } | |
| 84 | 179 |
| 85 private: | 180 private: |
| 86 void DoStep() { | 181 void DoStep() { |
| 87 switch (next_step_) { | 182 switch (next_step_) { |
| 88 case Step::READ_PLATFORM_KEYS: | 183 case Step::READ_PLATFORM_KEYS: |
| 89 next_step_ = Step::WRITE_UPDATE_AND_CALLBACK; | 184 next_step_ = Step::WRITE_UPDATE_AND_CALLBACK; |
| 90 ReadPlatformKeys(); | 185 ReadPlatformKeys(); |
| 91 return; | 186 return; |
| 92 case Step::WRITE_UPDATE_AND_CALLBACK: | 187 case Step::WRITE_UPDATE_AND_CALLBACK: |
| 93 next_step_ = Step::DONE; | 188 next_step_ = Step::DONE; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 106 } | 201 } |
| 107 | 202 |
| 108 // Reads the PlatformKeys value from the extension's state store and calls | 203 // Reads the PlatformKeys value from the extension's state store and calls |
| 109 // back to GotPlatformKeys(). | 204 // back to GotPlatformKeys(). |
| 110 void ReadPlatformKeys() { | 205 void ReadPlatformKeys() { |
| 111 service_->GetPlatformKeysOfExtension( | 206 service_->GetPlatformKeysOfExtension( |
| 112 extension_id_, base::Bind(&PermissionUpdateTask::GotPlatformKeys, | 207 extension_id_, base::Bind(&PermissionUpdateTask::GotPlatformKeys, |
| 113 weak_factory_.GetWeakPtr())); | 208 weak_factory_.GetWeakPtr())); |
| 114 } | 209 } |
| 115 | 210 |
| 116 void GotPlatformKeys(scoped_ptr<base::ListValue> platform_keys) { | 211 void GotPlatformKeys(scoped_ptr<KeyEntries> platform_keys) { |
| 117 platform_keys_ = platform_keys.Pass(); | 212 platform_keys_ = platform_keys.Pass(); |
| 118 DoStep(); | 213 DoStep(); |
| 119 } | 214 } |
| 120 | 215 |
| 121 // Returns whether the extension has permission to use the key for signing | 216 // Persists the existing KeyEntry in |old_key_entry_|, updates the entry with |
| 122 // according to the PlatformKeys value read from the extensions state store. | 217 // the new permission and persists it to the extension's state store if it was |
| 123 // Invalidates the key if it was found to be valid. | 218 // changed. |
| 124 void WriteUpdate() { | 219 void WriteUpdate() { |
| 125 scoped_ptr<base::StringValue> key_value( | 220 DCHECK(platform_keys_); |
| 126 GetPublicKeyValue(public_key_spki_der_)); | |
| 127 | 221 |
| 128 base::ListValue::const_iterator it = platform_keys_->Find(*key_value); | 222 std::string public_key_spki_der_b64; |
| 129 old_permission_value_ = it != platform_keys_->end(); | 223 base::Base64Encode(public_key_spki_der_, &public_key_spki_der_b64); |
| 130 if (old_permission_value_ == new_permission_value_) | |
| 131 return; | |
| 132 | 224 |
| 133 if (new_permission_value_) | 225 KeyEntry* matching_entry = |
| 134 platform_keys_->Append(key_value.release()); | 226 GetMatchingEntry(public_key_spki_der_b64, platform_keys_.get()); |
| 135 else | |
| 136 platform_keys_->Remove(*key_value, nullptr); | |
| 137 | 227 |
| 138 service_->SetPlatformKeysOfExtension(extension_id_, platform_keys_.Pass()); | 228 if (!matching_entry) { |
| 229 platform_keys_->push_back(KeyEntry()); | |
| 230 matching_entry = &platform_keys_->back(); | |
| 231 matching_entry->spki_b64 = public_key_spki_der_b64; | |
| 232 } else if (permission_ == SignPermission::ONCE && new_permission_value_) { | |
| 233 // The one-time sign permission is supposed to be granted once per key | |
| 234 // during generation. Generated keys should be unique and thus this case | |
| 235 // should never occur. | |
| 236 LOG(ERROR) << "Requested one-time sign permission on existing key."; | |
|
Andrew T Wilson (Slow)
2015/02/18 19:53:34
Should this be a CHECK()? Or at least a DCHECK()?
pneubeck (no reviews)
2015/02/19 11:08:40
'should never occur'... so DCHECK (according to th
| |
| 237 } | |
| 238 old_key_entry_ = *matching_entry; | |
| 239 | |
| 240 bool* permission_value = nullptr; | |
| 241 switch (permission_) { | |
| 242 case SignPermission::ONCE: | |
| 243 permission_value = &matching_entry->sign_once; | |
| 244 break; | |
| 245 case SignPermission::UNLIMITED: | |
| 246 permission_value = &matching_entry->sign_unlimited; | |
| 247 break; | |
| 248 } | |
|
kaliamoorthi
2015/02/18 17:04:09
Should there be a default unreached(); return here
pneubeck (no reviews)
2015/02/19 11:08:40
The defensive thing is to not use default.
As it i
| |
| 249 | |
| 250 if (*permission_value != new_permission_value_) { | |
| 251 *permission_value = new_permission_value_; | |
| 252 service_->SetPlatformKeysOfExtension(extension_id_, *platform_keys_); | |
| 253 } | |
| 139 } | 254 } |
| 140 | 255 |
| 141 Step next_step_ = Step::READ_PLATFORM_KEYS; | 256 Step next_step_ = Step::READ_PLATFORM_KEYS; |
| 142 scoped_ptr<base::ListValue> platform_keys_; | 257 KeyEntry old_key_entry_; |
| 143 bool old_permission_value_ = false; | |
| 144 | 258 |
| 259 const SignPermission permission_; | |
| 145 const bool new_permission_value_; | 260 const bool new_permission_value_; |
| 146 const std::string public_key_spki_der_; | 261 const std::string public_key_spki_der_; |
| 147 const std::string extension_id_; | 262 const std::string extension_id_; |
| 263 scoped_ptr<KeyEntries> platform_keys_; | |
| 148 base::Callback<void(Task*)> callback_; | 264 base::Callback<void(Task*)> callback_; |
| 149 PlatformKeysService* const service_; | 265 PlatformKeysService* const service_; |
| 150 base::WeakPtrFactory<PermissionUpdateTask> weak_factory_; | 266 base::WeakPtrFactory<PermissionUpdateTask> weak_factory_; |
| 151 | 267 |
| 152 DISALLOW_COPY_AND_ASSIGN(PermissionUpdateTask); | 268 DISALLOW_COPY_AND_ASSIGN(PermissionUpdateTask); |
| 153 }; | 269 }; |
| 154 | 270 |
| 155 class PlatformKeysService::SignTask : public Task { | 271 class PlatformKeysService::SignTask : public Task { |
| 156 public: | 272 public: |
| 157 enum class Step { | 273 enum class Step { |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 191 } | 307 } |
| 192 bool IsDone() override { return next_step_ == Step::DONE; } | 308 bool IsDone() override { return next_step_ == Step::DONE; } |
| 193 | 309 |
| 194 private: | 310 private: |
| 195 void DoStep() { | 311 void DoStep() { |
| 196 switch (next_step_) { | 312 switch (next_step_) { |
| 197 case Step::UPDATE_PERMISSION: | 313 case Step::UPDATE_PERMISSION: |
| 198 next_step_ = Step::SIGN_OR_ABORT; | 314 next_step_ = Step::SIGN_OR_ABORT; |
| 199 UpdatePermission(); | 315 UpdatePermission(); |
| 200 return; | 316 return; |
| 201 case Step::SIGN_OR_ABORT: | 317 case Step::SIGN_OR_ABORT: { |
| 202 next_step_ = Step::DONE; | 318 next_step_ = Step::DONE; |
| 203 if (!service_->permission_check_enabled_ || | 319 bool sign_granted = permission_update_->old_key_entry().sign_once || |
| 204 permission_update_->old_permission_value()) { | 320 permission_update_->old_key_entry().sign_unlimited; |
| 321 if (sign_granted) { | |
| 205 Sign(); | 322 Sign(); |
| 206 } else { | 323 } else { |
| 207 callback_.Run(std::string() /* no signature */, | 324 if (!callback_.is_null()) { |
|
Andrew T Wilson (Slow)
2015/02/18 19:53:35
Is this necessary? I thought calling Run() on a nu
pneubeck (no reviews)
2015/02/19 11:08:40
no it's not:
CallbackBase::CallbackBase(BindState
| |
| 208 kErrorKeyNotAllowedForSigning); | 325 callback_.Run(std::string() /* no signature */, |
| 326 kErrorKeyNotAllowedForSigning); | |
| 327 } | |
| 209 DoStep(); | 328 DoStep(); |
| 210 } | 329 } |
| 211 return; | 330 return; |
| 331 } | |
| 212 case Step::DONE: | 332 case Step::DONE: |
| 213 service_->TaskFinished(this); | 333 service_->TaskFinished(this); |
| 214 // |this| might be invalid now. | 334 // |this| might be invalid now. |
| 215 return; | 335 return; |
| 216 } | 336 } |
| 217 } | 337 } |
| 218 | 338 |
| 219 // Reads the current permission of the extension with |extension_id_| for key | 339 // Reads the current permission of the extension with |extension_id_| for key |
| 220 // |params_->public_key| and updates the permission to disable further | 340 // |params_->public_key| and updates the permission to disable further |
| 221 // signing operations with that key. | 341 // signing operations with that key. |
| 222 void UpdatePermission() { | 342 void UpdatePermission() { |
| 223 permission_update_.reset(new PermissionUpdateTask( | 343 permission_update_.reset(new PermissionUpdateTask( |
| 224 false /* new permission value */, public_key_, extension_id_, | 344 SignPermission::ONCE, false /* new permission value */, public_key_, |
| 345 extension_id_, | |
| 225 base::Bind(&SignTask::DidUpdatePermission, weak_factory_.GetWeakPtr()), | 346 base::Bind(&SignTask::DidUpdatePermission, weak_factory_.GetWeakPtr()), |
| 226 service_)); | 347 service_)); |
| 227 permission_update_->Start(); | 348 permission_update_->Start(); |
| 228 } | 349 } |
| 229 | 350 |
| 230 void DidUpdatePermission(Task* /* task */) { DoStep(); } | 351 void DidUpdatePermission(Task* /* task */) { DoStep(); } |
| 231 | 352 |
| 232 // Starts the actual signing operation and afterwards passes the signature (or | 353 // Starts the actual signing operation and afterwards passes the signature (or |
| 233 // error) to |callback_|. | 354 // error) to |callback_|. |
| 234 void Sign() { | 355 void Sign() { |
| 235 if (sign_direct_pkcs_padded_) { | 356 if (sign_direct_pkcs_padded_) { |
| 236 platform_keys::subtle::SignRSAPKCS1Raw( | 357 platform_keys::subtle::SignRSAPKCS1Raw( |
| 237 token_id_, data_, public_key_, | 358 token_id_, data_, public_key_, |
| 238 base::Bind(&SignTask::DidSign, weak_factory_.GetWeakPtr()), | 359 base::Bind(&SignTask::DidSign, weak_factory_.GetWeakPtr()), |
| 239 service_->browser_context_); | 360 service_->browser_context_); |
| 240 } else { | 361 } else { |
| 241 platform_keys::subtle::SignRSAPKCS1Digest( | 362 platform_keys::subtle::SignRSAPKCS1Digest( |
| 242 token_id_, data_, public_key_, hash_algorithm_, | 363 token_id_, data_, public_key_, hash_algorithm_, |
| 243 base::Bind(&SignTask::DidSign, weak_factory_.GetWeakPtr()), | 364 base::Bind(&SignTask::DidSign, weak_factory_.GetWeakPtr()), |
| 244 service_->browser_context_); | 365 service_->browser_context_); |
| 245 } | 366 } |
| 246 } | 367 } |
| 247 | 368 |
| 248 void DidSign(const std::string& signature, const std::string& error_message) { | 369 void DidSign(const std::string& signature, const std::string& error_message) { |
| 249 callback_.Run(signature, error_message); | 370 callback_.Run(signature, error_message); |
| 250 DoStep(); | 371 DoStep(); |
| 251 } | 372 } |
| 252 | 373 |
| 253 Step next_step_ = Step::UPDATE_PERMISSION; | 374 Step next_step_ = Step::UPDATE_PERMISSION; |
| 254 scoped_ptr<base::ListValue> platform_keys_; | 375 scoped_ptr<KeyEntries> platform_keys_; |
| 255 scoped_ptr<PermissionUpdateTask> permission_update_; | 376 scoped_ptr<PermissionUpdateTask> permission_update_; |
| 256 | 377 |
| 257 const std::string token_id_; | 378 const std::string token_id_; |
| 258 const std::string data_; | 379 const std::string data_; |
| 259 const std::string public_key_; | 380 const std::string public_key_; |
| 260 | 381 |
| 261 // If true, |data_| will not be hashed before signing. Only PKCS#1 v1.5 | 382 // If true, |data_| will not be hashed before signing. Only PKCS#1 v1.5 |
| 262 // padding will be applied before signing. | 383 // padding will be applied before signing. |
| 263 // If false, |hash_algorithm_| is set to a value != NONE. | 384 // If false, |hash_algorithm_| is set to a value != NONE. |
| 264 const bool sign_direct_pkcs_padded_; | 385 const bool sign_direct_pkcs_padded_; |
| 265 const platform_keys::HashAlgorithm hash_algorithm_; | 386 const platform_keys::HashAlgorithm hash_algorithm_; |
| 266 const std::string extension_id_; | 387 const std::string extension_id_; |
| 267 const SignCallback callback_; | 388 const SignCallback callback_; |
| 268 PlatformKeysService* const service_; | 389 PlatformKeysService* const service_; |
| 269 base::WeakPtrFactory<SignTask> weak_factory_; | 390 base::WeakPtrFactory<SignTask> weak_factory_; |
| 270 | 391 |
| 271 DISALLOW_COPY_AND_ASSIGN(SignTask); | 392 DISALLOW_COPY_AND_ASSIGN(SignTask); |
| 272 }; | 393 }; |
| 273 | 394 |
| 395 class PlatformKeysService::SelectTask : public Task { | |
| 396 public: | |
| 397 enum class Step { | |
| 398 GET_MATCHING_CERTS, | |
| 399 SELECT_CERTS, | |
| 400 READ_PLATFORM_KEYS, | |
| 401 UPDATE_PERMISSION, | |
| 402 FILTER_BY_PERMISSIONS, | |
| 403 DONE, | |
| 404 }; | |
| 405 | |
| 406 // This task determines all known client certs matching |request|. If | |
| 407 // |interactive| is true, calls |service->select_delegate_->Select()| to | |
| 408 // select a cert from all matches. The extension with |extension_id| will be | |
| 409 // granted unlimited sign permission for the selected cert. | |
| 410 // Finally, either the selection or, if |interactive| is false, matching certs | |
| 411 // that the extension has permission for are passed to |callback|. | |
| 412 SelectTask(const platform_keys::ClientCertificateRequest& request, | |
| 413 bool interactive, | |
| 414 const std::string& extension_id, | |
| 415 const SelectCertificatesCallback& callback, | |
| 416 PlatformKeysService* service) | |
| 417 : request_(request), | |
| 418 interactive_(interactive), | |
| 419 extension_id_(extension_id), | |
| 420 callback_(callback), | |
| 421 service_(service), | |
| 422 weak_factory_(this) {} | |
| 423 ~SelectTask() override {} | |
| 424 | |
| 425 void Start() override { | |
| 426 CHECK(next_step_ == Step::GET_MATCHING_CERTS); | |
| 427 DoStep(); | |
| 428 } | |
| 429 bool IsDone() override { return next_step_ == Step::DONE; } | |
| 430 | |
| 431 private: | |
| 432 void DoStep() { | |
| 433 switch (next_step_) { | |
| 434 case Step::GET_MATCHING_CERTS: | |
| 435 next_step_ = Step::SELECT_CERTS; | |
| 436 GetMatchingCerts(); | |
| 437 return; | |
| 438 case Step::SELECT_CERTS: | |
| 439 next_step_ = Step::UPDATE_PERMISSION; | |
| 440 SelectCerts(); | |
| 441 return; | |
| 442 case Step::UPDATE_PERMISSION: | |
| 443 next_step_ = Step::READ_PLATFORM_KEYS; | |
| 444 UpdatePermission(); | |
| 445 return; | |
| 446 case Step::READ_PLATFORM_KEYS: | |
| 447 next_step_ = Step::FILTER_BY_PERMISSIONS; | |
| 448 ReadPlatformKeys(); | |
| 449 return; | |
| 450 case Step::FILTER_BY_PERMISSIONS: | |
| 451 next_step_ = Step::DONE; | |
| 452 FilterSelectionByPermission(); | |
| 453 return; | |
| 454 case Step::DONE: | |
| 455 service_->TaskFinished(this); | |
| 456 // |this| might be invalid now. | |
| 457 return; | |
| 458 } | |
| 459 } | |
| 460 | |
| 461 // Retrieves all certificates matching |request_|. Will call back to | |
| 462 // |GotMatchingCerts()|. | |
| 463 void GetMatchingCerts() { | |
| 464 platform_keys::subtle::SelectClientCertificates( | |
| 465 request_, | |
| 466 base::Bind(&SelectTask::GotMatchingCerts, weak_factory_.GetWeakPtr()), | |
| 467 service_->browser_context_); | |
| 468 } | |
| 469 | |
| 470 // If the certificate request could be processed successfully, |matches| will | |
| 471 // contain the list of matching certificates (maybe empty) and |error_message| | |
| 472 // will be empty. If an error occurred, |matches| will be null and | |
| 473 // |error_message| contain an error message. | |
| 474 void GotMatchingCerts(scoped_ptr<net::CertificateList> matches, | |
| 475 const std::string& error_message) { | |
| 476 if (!error_message.empty()) { | |
| 477 next_step_ = Step::DONE; | |
| 478 callback_.Run(nullptr /* no certificates */, error_message); | |
| 479 DoStep(); | |
| 480 return; | |
| 481 } | |
| 482 matches_.swap(*matches); | |
| 483 DoStep(); | |
| 484 } | |
| 485 | |
| 486 // If |interactive_|, calls |service_->select_delegate_->Select()| to select a | |
| 487 // cert from |matches_|, which will be stored in |selected_cert_|. | |
| 488 // Will call back to |GotSelection()|. | |
| 489 void SelectCerts() { | |
| 490 if (!interactive_ || matches_.empty()) { | |
|
kaliamoorthi
2015/02/18 17:04:09
This seem to unnecessarily continue through the re
pneubeck (no reviews)
2015/02/19 11:08:40
I did this to make the transitions as constant as
| |
| 491 // Don't show a select dialog in non-interactive calls or if no | |
| 492 // certificate is matching. | |
| 493 DoStep(); | |
| 494 return; | |
| 495 } | |
| 496 service_->select_delegate_->Select( | |
| 497 extension_id_, matches_, | |
| 498 base::Bind(&SelectTask::GotSelection, base::Unretained(this))); | |
| 499 } | |
| 500 | |
| 501 // Will be called by |SelectCerts()| with the selected cert or null if no cert | |
| 502 // was selected. | |
| 503 void GotSelection(scoped_refptr<net::X509Certificate> selected_cert) { | |
| 504 selected_cert_ = selected_cert; | |
| 505 DoStep(); | |
| 506 } | |
| 507 | |
| 508 // Updates the extension's state store about unlimited sign permission for the | |
| 509 // selected cert. Does nothing if no cert was selected or if |interactive_| is | |
| 510 // false. | |
| 511 // Will call back to |DidUpdatePermission()|. | |
| 512 void UpdatePermission() { | |
| 513 if (!interactive_ || !selected_cert_) { | |
| 514 DoStep(); | |
| 515 return; | |
| 516 } | |
| 517 const std::string public_key_spki_der( | |
| 518 platform_keys::GetSubjectPublicKeyInfo(selected_cert_)); | |
| 519 permission_update_.reset(new PermissionUpdateTask( | |
| 520 SignPermission::UNLIMITED, true /* new permission value */, | |
| 521 public_key_spki_der, extension_id_, | |
| 522 base::Bind(&SelectTask::DidUpdatePermission, base::Unretained(this)), | |
| 523 service_)); | |
| 524 permission_update_->Start(); | |
| 525 } | |
| 526 | |
| 527 void DidUpdatePermission(Task* /* task */) { DoStep(); } | |
| 528 | |
| 529 // Reads the PlatformKeys value from the extension's state store and calls | |
| 530 // back to GotPlatformKeys(). | |
| 531 void ReadPlatformKeys() { | |
| 532 service_->GetPlatformKeysOfExtension( | |
| 533 extension_id_, | |
| 534 base::Bind(&SelectTask::GotPlatformKeys, weak_factory_.GetWeakPtr())); | |
| 535 } | |
| 536 | |
| 537 void GotPlatformKeys(scoped_ptr<KeyEntries> platform_keys) { | |
| 538 platform_keys_ = platform_keys.Pass(); | |
| 539 DoStep(); | |
| 540 } | |
| 541 | |
| 542 // Filters from all matches (if not interactive) or from the selection (if | |
| 543 // interactive), the certificates that the extension has unlimited sign | |
| 544 // permission for. Passes the filtered certs to |callback_|. | |
| 545 // Note: In the interactive case this should filter exactly the selected cert | |
| 546 // and checking the permissions again is not striclty necessary. However, by | |
| 547 // running the filtering additionally, ensures that the permissions were | |
| 548 // updated correctly. | |
|
Andrew T Wilson (Slow)
2015/02/18 19:53:35
What does "ensures that the permissions were updat
pneubeck (no reviews)
2015/02/19 11:08:40
Done.
| |
| 549 void FilterSelectionByPermission() { | |
| 550 scoped_ptr<net::CertificateList> selection(new net::CertificateList); | |
| 551 if (interactive_) { | |
| 552 if (selected_cert_) | |
| 553 selection->push_back(selected_cert_); | |
| 554 } else { | |
| 555 selection->assign(matches_.begin(), matches_.end()); | |
| 556 } | |
| 557 | |
| 558 scoped_ptr<net::CertificateList> filtered_certs(new net::CertificateList); | |
| 559 for (scoped_refptr<net::X509Certificate> selected_cert : *selection) { | |
| 560 const std::string public_key_spki_der( | |
| 561 platform_keys::GetSubjectPublicKeyInfo(selected_cert)); | |
| 562 std::string public_key_spki_der_b64; | |
| 563 base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64); | |
| 564 | |
| 565 KeyEntry* matching_entry = | |
| 566 GetMatchingEntry(public_key_spki_der_b64, platform_keys_.get()); | |
| 567 if (!matching_entry || !matching_entry->sign_unlimited) | |
| 568 continue; | |
| 569 | |
| 570 filtered_certs->push_back(selected_cert); | |
| 571 } | |
| 572 callback_.Run(filtered_certs.Pass(), std::string() /* no error */); | |
| 573 DoStep(); | |
| 574 } | |
| 575 | |
| 576 Step next_step_ = Step::GET_MATCHING_CERTS; | |
| 577 scoped_ptr<KeyEntries> platform_keys_; | |
| 578 scoped_ptr<PermissionUpdateTask> permission_update_; | |
| 579 | |
| 580 net::CertificateList matches_; | |
| 581 scoped_refptr<net::X509Certificate> selected_cert_; | |
| 582 platform_keys::ClientCertificateRequest request_; | |
| 583 const bool interactive_; | |
| 584 const std::string extension_id_; | |
| 585 const SelectCertificatesCallback callback_; | |
| 586 PlatformKeysService* const service_; | |
| 587 base::WeakPtrFactory<SelectTask> weak_factory_; | |
| 588 | |
| 589 DISALLOW_COPY_AND_ASSIGN(SelectTask); | |
| 590 }; | |
| 591 | |
| 592 PlatformKeysService::SelectDelegate::SelectDelegate() { | |
| 593 } | |
| 594 | |
| 595 PlatformKeysService::SelectDelegate::~SelectDelegate() { | |
| 596 } | |
| 597 | |
| 274 PlatformKeysService::PlatformKeysService( | 598 PlatformKeysService::PlatformKeysService( |
| 275 content::BrowserContext* browser_context, | 599 content::BrowserContext* browser_context, |
| 276 extensions::StateStore* state_store) | 600 extensions::StateStore* state_store) |
| 277 : browser_context_(browser_context), | 601 : browser_context_(browser_context), |
| 278 state_store_(state_store), | 602 state_store_(state_store), |
| 279 weak_factory_(this) { | 603 weak_factory_(this) { |
| 280 DCHECK(state_store); | 604 DCHECK(state_store); |
| 281 } | 605 } |
| 282 | 606 |
| 283 PlatformKeysService::~PlatformKeysService() { | 607 PlatformKeysService::~PlatformKeysService() { |
| 284 } | 608 } |
| 285 | 609 |
| 286 void PlatformKeysService::DisablePermissionCheckForTesting() { | 610 void PlatformKeysService::SetSelectDelegate( |
| 287 permission_check_enabled_ = false; | 611 scoped_ptr<SelectDelegate> delegate) { |
| 612 select_delegate_ = delegate.Pass(); | |
|
Andrew T Wilson (Slow)
2015/02/18 19:53:35
DCHECK(interactive_) might be appropriate here? Or
pneubeck (no reviews)
2015/02/19 11:08:39
There's some confusion about the scoped of interac
| |
| 613 } | |
| 614 | |
| 615 void PlatformKeysService::GrantUnlimitedSignPermission( | |
| 616 const std::string& extension_id, | |
| 617 scoped_refptr<net::X509Certificate> cert) { | |
| 618 const std::string public_key_spki_der( | |
| 619 platform_keys::GetSubjectPublicKeyInfo(cert)); | |
| 620 | |
| 621 StartOrQueueTask(make_scoped_ptr(new PermissionUpdateTask( | |
| 622 SignPermission::UNLIMITED, true /* new permission value */, | |
| 623 public_key_spki_der, extension_id, | |
| 624 base::Bind(&PlatformKeysService::TaskFinished, base::Unretained(this)), | |
| 625 this))); | |
| 288 } | 626 } |
| 289 | 627 |
| 290 void PlatformKeysService::GenerateRSAKey(const std::string& token_id, | 628 void PlatformKeysService::GenerateRSAKey(const std::string& token_id, |
| 291 unsigned int modulus_length, | 629 unsigned int modulus_length, |
| 292 const std::string& extension_id, | 630 const std::string& extension_id, |
| 293 const GenerateKeyCallback& callback) { | 631 const GenerateKeyCallback& callback) { |
| 294 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 632 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 295 | 633 |
| 296 platform_keys::subtle::GenerateRSAKey( | 634 platform_keys::subtle::GenerateRSAKey( |
| 297 token_id, modulus_length, | 635 token_id, modulus_length, |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 319 const std::string& extension_id, | 657 const std::string& extension_id, |
| 320 const SignCallback& callback) { | 658 const SignCallback& callback) { |
| 321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 659 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 322 StartOrQueueTask(make_scoped_ptr(new SignTask( | 660 StartOrQueueTask(make_scoped_ptr(new SignTask( |
| 323 token_id, data, public_key, true /* sign directly without hashing */, | 661 token_id, data, public_key, true /* sign directly without hashing */, |
| 324 platform_keys::HASH_ALGORITHM_NONE, extension_id, callback, this))); | 662 platform_keys::HASH_ALGORITHM_NONE, extension_id, callback, this))); |
| 325 } | 663 } |
| 326 | 664 |
| 327 void PlatformKeysService::SelectClientCertificates( | 665 void PlatformKeysService::SelectClientCertificates( |
| 328 const platform_keys::ClientCertificateRequest& request, | 666 const platform_keys::ClientCertificateRequest& request, |
| 667 bool interactive, | |
|
kaliamoorthi
2015/02/18 17:04:09
Can't you deduce this by the fact that SelectDeleg
pneubeck (no reviews)
2015/02/19 11:08:40
see reply to Drew's comment above.
| |
| 329 const std::string& extension_id, | 668 const std::string& extension_id, |
| 330 const SelectCertificatesCallback& callback) { | 669 const SelectCertificatesCallback& callback) { |
| 331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 670 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 332 | 671 StartOrQueueTask(make_scoped_ptr( |
| 333 platform_keys::subtle::SelectClientCertificates( | 672 new SelectTask(request, interactive, extension_id, callback, this))); |
| 334 request, | |
| 335 base::Bind(&PlatformKeysService::SelectClientCertificatesCallback, | |
| 336 weak_factory_.GetWeakPtr(), extension_id, callback), | |
| 337 browser_context_); | |
| 338 } | 673 } |
| 339 | 674 |
| 340 void PlatformKeysService::StartOrQueueTask(scoped_ptr<Task> task) { | 675 void PlatformKeysService::StartOrQueueTask(scoped_ptr<Task> task) { |
| 341 tasks_.push(make_linked_ptr(task.release())); | 676 tasks_.push(make_linked_ptr(task.release())); |
| 342 if (tasks_.size() == 1) | 677 if (tasks_.size() == 1) |
| 343 tasks_.front()->Start(); | 678 tasks_.front()->Start(); |
| 344 } | 679 } |
| 345 | 680 |
| 346 void PlatformKeysService::TaskFinished(Task* task) { | 681 void PlatformKeysService::TaskFinished(Task* task) { |
| 347 DCHECK(!tasks_.empty()); | 682 DCHECK(!tasks_.empty()); |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 360 const std::string& extension_id, | 695 const std::string& extension_id, |
| 361 const GetPlatformKeysCallback& callback) { | 696 const GetPlatformKeysCallback& callback) { |
| 362 state_store_->GetExtensionValue( | 697 state_store_->GetExtensionValue( |
| 363 extension_id, kStateStorePlatformKeys, | 698 extension_id, kStateStorePlatformKeys, |
| 364 base::Bind(&PlatformKeysService::GotPlatformKeysOfExtension, | 699 base::Bind(&PlatformKeysService::GotPlatformKeysOfExtension, |
| 365 weak_factory_.GetWeakPtr(), extension_id, callback)); | 700 weak_factory_.GetWeakPtr(), extension_id, callback)); |
| 366 } | 701 } |
| 367 | 702 |
| 368 void PlatformKeysService::SetPlatformKeysOfExtension( | 703 void PlatformKeysService::SetPlatformKeysOfExtension( |
| 369 const std::string& extension_id, | 704 const std::string& extension_id, |
| 370 scoped_ptr<base::ListValue> platform_keys) { | 705 const KeyEntries& platform_keys) { |
| 371 state_store_->SetExtensionValue(extension_id, kStateStorePlatformKeys, | 706 state_store_->SetExtensionValue(extension_id, kStateStorePlatformKeys, |
| 372 platform_keys.Pass()); | 707 KeyEntriesToState(platform_keys)); |
| 373 } | 708 } |
| 374 | 709 |
| 375 void PlatformKeysService::GeneratedKey(const std::string& extension_id, | 710 void PlatformKeysService::GeneratedKey(const std::string& extension_id, |
| 376 const GenerateKeyCallback& callback, | 711 const GenerateKeyCallback& callback, |
| 377 const std::string& public_key_spki_der, | 712 const std::string& public_key_spki_der, |
| 378 const std::string& error_message) { | 713 const std::string& error_message) { |
| 379 if (!error_message.empty()) { | 714 if (!error_message.empty()) { |
| 380 callback.Run(std::string() /* no public key */, error_message); | 715 callback.Run(std::string() /* no public key */, error_message); |
| 381 return; | 716 return; |
| 382 } | 717 } |
| 383 | 718 |
| 384 StartOrQueueTask(make_scoped_ptr(new PermissionUpdateTask( | 719 StartOrQueueTask(make_scoped_ptr(new PermissionUpdateTask( |
| 385 true /* new permission value */, public_key_spki_der, extension_id, | 720 SignPermission::ONCE, true /* new permission value */, |
| 721 public_key_spki_der, extension_id, | |
| 386 base::Bind(&PlatformKeysService::RegisteredGeneratedKey, | 722 base::Bind(&PlatformKeysService::RegisteredGeneratedKey, |
| 387 base::Unretained(this), callback, public_key_spki_der), | 723 base::Unretained(this), callback, public_key_spki_der), |
| 388 this))); | 724 this))); |
| 389 } | 725 } |
| 390 | 726 |
| 391 void PlatformKeysService::RegisteredGeneratedKey( | 727 void PlatformKeysService::RegisteredGeneratedKey( |
| 392 const GenerateKeyCallback& callback, | 728 const GenerateKeyCallback& callback, |
| 393 const std::string& public_key_spki_der, | 729 const std::string& public_key_spki_der, |
| 394 Task* task) { | 730 Task* task) { |
| 395 callback.Run(public_key_spki_der, std::string() /* no error */); | 731 callback.Run(public_key_spki_der, std::string() /* no error */); |
| 396 TaskFinished(task); | 732 TaskFinished(task); |
| 397 } | 733 } |
| 398 | 734 |
| 399 void PlatformKeysService::SelectClientCertificatesCallback( | |
| 400 const std::string& extension_id, | |
| 401 const SelectCertificatesCallback& callback, | |
| 402 scoped_ptr<net::CertificateList> matches, | |
| 403 const std::string& error_message) { | |
| 404 if (permission_check_enabled_) | |
| 405 matches->clear(); | |
| 406 | |
| 407 // TODO(pneubeck): Remove all certs that the extension doesn't have access to. | |
| 408 callback.Run(matches.Pass(), error_message); | |
| 409 } | |
| 410 | 735 |
| 411 void PlatformKeysService::GotPlatformKeysOfExtension( | 736 void PlatformKeysService::GotPlatformKeysOfExtension( |
| 412 const std::string& extension_id, | 737 const std::string& extension_id, |
| 413 const GetPlatformKeysCallback& callback, | 738 const GetPlatformKeysCallback& callback, |
| 414 scoped_ptr<base::Value> value) { | 739 scoped_ptr<base::Value> value) { |
| 415 if (!value) | 740 scoped_ptr<KeyEntries> key_entries(new KeyEntries); |
| 416 value.reset(new base::ListValue); | 741 if (value) |
| 742 key_entries = KeyEntriesFromState(*value); | |
| 417 | 743 |
| 418 base::ListValue* keys = NULL; | 744 callback.Run(key_entries.Pass()); |
| 419 if (!value->GetAsList(&keys)) { | |
| 420 LOG(ERROR) << "Found a value of wrong type."; | |
| 421 | |
| 422 keys = new base::ListValue; | |
| 423 value.reset(keys); | |
| 424 } | |
| 425 | |
| 426 ignore_result(value.release()); | |
| 427 callback.Run(make_scoped_ptr(keys)); | |
| 428 } | 745 } |
| 429 | 746 |
| 430 } // namespace chromeos | 747 } // namespace chromeos |
| OLD | NEW |