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 |