OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "sync/internal_api/sync_encryption_handler_impl.h" | |
6 | |
7 #include <queue> | |
8 #include <string> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/message_loop.h" | |
12 #include "base/tracked_objects.h" | |
13 #include "base/metrics/histogram.h" | |
14 #include "sync/internal_api/public/read_node.h" | |
15 #include "sync/internal_api/public/read_transaction.h" | |
16 #include "sync/internal_api/public/user_share.h" | |
17 #include "sync/internal_api/public/util/experiments.h" | |
18 #include "sync/internal_api/public/write_node.h" | |
19 #include "sync/internal_api/public/write_transaction.h" | |
20 #include "sync/protocol/encryption.pb.h" | |
21 #include "sync/protocol/nigori_specifics.pb.h" | |
22 #include "sync/syncable/base_transaction.h" | |
23 #include "sync/syncable/directory.h" | |
24 #include "sync/syncable/entry.h" | |
25 #include "sync/syncable/nigori_util.h" | |
26 #include "sync/util/cryptographer.h" | |
27 | |
28 namespace syncer { | |
29 | |
30 namespace { | |
31 // The maximum number of times we will automatically overwrite the nigori node | |
32 // because the encryption keys don't match (per chrome instantiation). | |
33 // We protect ourselves against nigori rollbacks, but it's possible two | |
34 // different clients might have contrasting view of what the nigori node state | |
35 // should be, in which case they might ping pong (see crbug.com/119207). | |
36 static const int kNigoriOverwriteLimit = 10; | |
37 } | |
38 | |
39 SyncEncryptionHandlerImpl::SyncEncryptionHandlerImpl( | |
40 UserShare* user_share, | |
41 Cryptographer* cryptographer) | |
42 : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), | |
43 user_share_(user_share), | |
44 cryptographer_(cryptographer), | |
45 encrypted_types_(SensitiveTypes()), | |
46 encrypt_everything_(false), | |
47 explicit_passphrase_(false), | |
48 nigori_overwrite_count_(0) { | |
49 } | |
50 | |
51 SyncEncryptionHandlerImpl::~SyncEncryptionHandlerImpl() {} | |
52 | |
53 void SyncEncryptionHandlerImpl::AddObserver(Observer* observer) { | |
54 DCHECK(!observers_.HasObserver(observer)); | |
55 observers_.AddObserver(observer); | |
56 } | |
57 | |
58 void SyncEncryptionHandlerImpl::RemoveObserver(Observer* observer) { | |
59 DCHECK(observers_.HasObserver(observer)); | |
60 observers_.RemoveObserver(observer); | |
61 } | |
62 | |
63 void SyncEncryptionHandlerImpl::Init() { | |
64 WriteTransaction trans(FROM_HERE, user_share_); | |
65 WriteNode node(&trans); | |
66 Cryptographer* cryptographer = trans.GetCryptographer(); | |
67 cryptographer_ = cryptographer; | |
68 | |
69 if (node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) | |
70 return; | |
71 if (!ApplyNigoriUpdateImpl(node.GetNigoriSpecifics(), | |
72 trans.GetWrappedTrans())) | |
73 WriteEncryptionStateToNigori(&trans); | |
tim (not reviewing)
2012/08/15 00:23:30
nit - { } around multi line ifs
Nicolas Zea
2012/08/15 01:08:29
Done.
| |
74 | |
75 FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_, | |
76 OnCryptographerStateChanged(cryptographer)); | |
77 | |
78 if (cryptographer->is_ready()) | |
tim (not reviewing)
2012/08/15 00:23:30
Comment about what happens if !is_ready
Nicolas Zea
2012/08/15 01:08:29
Done.
| |
79 ReEncryptEverything(&trans); | |
80 } | |
81 | |
82 // Note: this is called from within a syncable transaction, so we need to post | |
83 // tasks if we want to do any work that creates a new sync_api transaction. | |
84 void SyncEncryptionHandlerImpl::ApplyNigoriUpdate( | |
85 const sync_pb::NigoriSpecifics& nigori, | |
86 syncable::BaseTransaction* const trans) { | |
87 DCHECK(trans); | |
88 if (!ApplyNigoriUpdateImpl(nigori, trans)) { | |
89 MessageLoop::current()->PostTask( | |
90 FROM_HERE, | |
91 base::Bind(&SyncEncryptionHandlerImpl::RewriteNigori, | |
92 weak_ptr_factory_.GetWeakPtr())); | |
93 } | |
94 | |
95 FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_, | |
96 OnCryptographerStateChanged(cryptographer_)); | |
97 } | |
98 | |
99 // Note: this is always called via the Cryptographer interface right now, | |
100 // so a transaction is already held. Once we remove that interface, we'll | |
101 // need to enforce holding a transaction when calling this method. | |
102 ModelTypeSet SyncEncryptionHandlerImpl::GetEncryptedTypes() const { | |
103 return encrypted_types_; | |
104 } | |
105 | |
106 void SyncEncryptionHandlerImpl::SetEncryptionPassphrase( | |
107 const std::string& passphrase, | |
108 bool is_explicit) { | |
109 // We do not accept empty passphrases. | |
110 if (passphrase.empty()) { | |
111 NOTREACHED() << "Cannot encrypt with an empty passphrase."; | |
112 return; | |
113 } | |
114 | |
115 // All accesses to the cryptographer are protected by a transaction. | |
116 WriteTransaction trans(FROM_HERE, user_share_); | |
117 Cryptographer* cryptographer = trans.GetCryptographer(); | |
118 KeyParams key_params = {"localhost", "dummy", passphrase}; | |
119 WriteNode node(&trans); | |
120 if (node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) { | |
121 NOTREACHED(); | |
122 return; | |
123 } | |
124 | |
125 bool nigori_has_explicit_passphrase = | |
126 node.GetNigoriSpecifics().using_explicit_passphrase(); | |
127 std::string bootstrap_token; | |
128 sync_pb::EncryptedData pending_keys; | |
129 if (cryptographer->has_pending_keys()) | |
130 pending_keys = cryptographer->GetPendingKeys(); | |
131 bool success = false; | |
132 | |
133 | |
134 // There are six cases to handle here: | |
135 // 1. The user has no pending keys and is setting their current GAIA password | |
136 // as the encryption passphrase. This happens either during first time sync | |
137 // with a clean profile, or after re-authenticating on a profile that was | |
138 // already signed in with the cryptographer ready. | |
139 // 2. The user has no pending keys, and is overwriting an (already provided) | |
140 // implicit passphrase with an explicit (custom) passphrase. | |
141 // 3. The user has pending keys for an explicit passphrase that is somehow set | |
142 // to their current GAIA passphrase. | |
143 // 4. The user has pending keys encrypted with their current GAIA passphrase | |
144 // and the caller passes in the current GAIA passphrase. | |
145 // 5. The user has pending keys encrypted with an older GAIA passphrase | |
146 // and the caller passes in the current GAIA passphrase. | |
147 // 6. The user has previously done encryption with an explicit passphrase. | |
148 // Furthermore, we enforce the fact that the bootstrap encryption token will | |
149 // always be derived from the newest GAIA password if the account is using | |
150 // an implicit passphrase (even if the data is encrypted with an old GAIA | |
151 // password). If the account is using an explicit (custom) passphrase, the | |
152 // bootstrap token will be derived from the most recently provided explicit | |
153 // passphrase (that was able to decrypt the data). | |
154 if (!nigori_has_explicit_passphrase) { | |
155 if (!cryptographer->has_pending_keys()) { | |
156 if (cryptographer->AddKey(key_params)) { | |
157 // Case 1 and 2. We set a new GAIA passphrase when there are no pending | |
158 // keys (1), or overwriting an implicit passphrase with a new explicit | |
159 // one (2) when there are no pending keys. | |
160 DVLOG(1) << "Setting " << (is_explicit ? "explicit" : "implicit" ) | |
161 << " passphrase for encryption."; | |
162 cryptographer->GetBootstrapToken(&bootstrap_token); | |
163 success = true; | |
164 } else { | |
165 NOTREACHED() << "Failed to add key to cryptographer."; | |
166 success = false; | |
167 } | |
168 } else { // cryptographer->has_pending_keys() == true | |
169 if (is_explicit) { | |
170 // This can only happen if the nigori node is updated with a new | |
171 // implicit passphrase while a client is attempting to set a new custom | |
172 // passphrase (race condition). | |
173 DVLOG(1) << "Failing because an implicit passphrase is already set."; | |
174 success = false; | |
175 } else { // is_explicit == false | |
176 if (cryptographer->DecryptPendingKeys(key_params)) { | |
177 // Case 4. We successfully decrypted with the implicit GAIA passphrase | |
178 // passed in. | |
179 DVLOG(1) << "Implicit internal passphrase accepted for decryption."; | |
180 cryptographer->GetBootstrapToken(&bootstrap_token); | |
181 success = true; | |
182 } else { | |
183 // Case 5. Encryption was done with an old GAIA password, but we were | |
184 // provided with the current GAIA password. We need to generate a new | |
185 // bootstrap token to preserve it. We build a temporary cryptographer | |
186 // to allow us to extract these params without polluting our current | |
187 // cryptographer. | |
188 DVLOG(1) << "Implicit internal passphrase failed to decrypt, adding " | |
189 << "anyways as default passphrase and persisting via " | |
190 << "bootstrap token."; | |
191 Cryptographer temp_cryptographer(cryptographer->encryptor()); | |
192 temp_cryptographer.AddKey(key_params); | |
193 temp_cryptographer.GetBootstrapToken(&bootstrap_token); | |
194 // We then set the new passphrase as the default passphrase of the | |
195 // real cryptographer, even though we have pending keys. This is safe, | |
196 // as although Cryptographer::is_initialized() will now be true, | |
197 // is_ready() will remain false due to having pending keys. | |
198 cryptographer->AddKey(key_params); | |
199 success = false; | |
200 } | |
201 } // is_explicit | |
202 } // cryptographer->has_pending_keys() | |
203 } else { // nigori_has_explicit_passphrase == true | |
204 // Case 6. We do not want to override a previously set explicit passphrase, | |
205 // so we return a failure. | |
206 DVLOG(1) << "Failing because an explicit passphrase is already set."; | |
207 success = false; | |
208 } | |
209 | |
210 DVLOG_IF(1, !success) | |
211 << "Failure in SetEncryptionPassphrase; notifying and returning."; | |
212 DVLOG_IF(1, success) | |
213 << "Successfully set encryption passphrase; updating nigori and " | |
214 "reencrypting."; | |
215 | |
216 FinishSetPassphrase( | |
217 success, bootstrap_token, is_explicit, &trans, &node); | |
218 } | |
219 | |
220 void SyncEncryptionHandlerImpl::SetDecryptionPassphrase( | |
221 const std::string& passphrase) { | |
222 // We do not accept empty passphrases. | |
223 if (passphrase.empty()) { | |
224 NOTREACHED() << "Cannot decrypt with an empty passphrase."; | |
225 return; | |
226 } | |
227 | |
228 // All accesses to the cryptographer are protected by a transaction. | |
229 WriteTransaction trans(FROM_HERE, user_share_); | |
230 Cryptographer* cryptographer = trans.GetCryptographer(); | |
231 KeyParams key_params = {"localhost", "dummy", passphrase}; | |
232 WriteNode node(&trans); | |
233 if (node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) { | |
234 NOTREACHED(); | |
235 return; | |
236 } | |
237 | |
238 if (!cryptographer->has_pending_keys()) { | |
239 // Note that this *can* happen in a rare situation where data is | |
240 // re-encrypted on another client while a SetDecryptionPassphrase() call is | |
241 // in-flight on this client. It is rare enough that we choose to do nothing. | |
242 NOTREACHED() << "Attempt to set decryption passphrase failed because there " | |
243 << "were no pending keys."; | |
244 return; | |
245 } | |
246 | |
247 bool nigori_has_explicit_passphrase = | |
248 node.GetNigoriSpecifics().using_explicit_passphrase(); | |
249 std::string bootstrap_token; | |
250 sync_pb::EncryptedData pending_keys; | |
251 pending_keys = cryptographer->GetPendingKeys(); | |
252 bool success = false; | |
253 | |
254 // There are three cases to handle here: | |
255 // 7. We're using the current GAIA password to decrypt the pending keys. This | |
256 // happens when signing in to an account with a previously set implicit | |
257 // passphrase, where the data is already encrypted with the newest GAIA | |
258 // password. | |
259 // 8. The user is providing an old GAIA password to decrypt the pending keys. | |
260 // In this case, the user is using an implicit passphrase, but has changed | |
261 // their password since they last encrypted their data, and therefore | |
262 // their current GAIA password was unable to decrypt the data. This will | |
263 // happen when the user is setting up a new profile with a previously | |
264 // encrypted account (after changing passwords). | |
265 // 9. The user is providing a previously set explicit passphrase to decrypt | |
266 // the pending keys. | |
267 if (!nigori_has_explicit_passphrase) { | |
268 if (cryptographer->is_initialized()) { | |
269 // We only want to change the default encryption key to the pending | |
270 // one if the pending keybag already contains the current default. | |
271 // This covers the case where a different client re-encrypted | |
272 // everything with a newer gaia passphrase (and hence the keybag | |
273 // contains keys from all previously used gaia passphrases). | |
274 // Otherwise, we're in a situation where the pending keys are | |
275 // encrypted with an old gaia passphrase, while the default is the | |
276 // current gaia passphrase. In that case, we preserve the default. | |
277 Cryptographer temp_cryptographer(cryptographer->encryptor()); | |
278 temp_cryptographer.SetPendingKeys(cryptographer->GetPendingKeys()); | |
279 if (temp_cryptographer.DecryptPendingKeys(key_params)) { | |
280 // Check to see if the pending bag of keys contains the current | |
281 // default key. | |
282 sync_pb::EncryptedData encrypted; | |
283 cryptographer->GetKeys(&encrypted); | |
284 if (temp_cryptographer.CanDecrypt(encrypted)) { | |
285 DVLOG(1) << "Implicit user provided passphrase accepted for " | |
286 << "decryption, overwriting default."; | |
287 // Case 7. The pending keybag contains the current default. Go ahead | |
288 // and update the cryptographer, letting the default change. | |
289 cryptographer->DecryptPendingKeys(key_params); | |
290 cryptographer->GetBootstrapToken(&bootstrap_token); | |
291 success = true; | |
292 } else { | |
293 // Case 8. The pending keybag does not contain the current default | |
294 // encryption key. We decrypt the pending keys here, and in | |
295 // FinishSetPassphrase, re-encrypt everything with the current GAIA | |
296 // passphrase instead of the passphrase just provided by the user. | |
297 DVLOG(1) << "Implicit user provided passphrase accepted for " | |
298 << "decryption, restoring implicit internal passphrase " | |
299 << "as default."; | |
300 std::string bootstrap_token_from_current_key; | |
301 cryptographer->GetBootstrapToken( | |
302 &bootstrap_token_from_current_key); | |
303 cryptographer->DecryptPendingKeys(key_params); | |
304 // Overwrite the default from the pending keys. | |
305 cryptographer->AddKeyFromBootstrapToken( | |
306 bootstrap_token_from_current_key); | |
307 success = true; | |
308 } | |
309 } else { // !temp_cryptographer.DecryptPendingKeys(..) | |
310 DVLOG(1) << "Implicit user provided passphrase failed to decrypt."; | |
311 success = false; | |
312 } // temp_cryptographer.DecryptPendingKeys(...) | |
313 } else { // cryptographer->is_initialized() == false | |
314 if (cryptographer->DecryptPendingKeys(key_params)) { | |
315 // This can happpen in two cases: | |
316 // - First time sync on android, where we'll never have a | |
317 // !user_provided passphrase. | |
318 // - This is a restart for a client that lost their bootstrap token. | |
319 // In both cases, we should go ahead and initialize the cryptographer | |
320 // and persist the new bootstrap token. | |
321 // | |
322 // Note: at this point, we cannot distinguish between cases 7 and 8 | |
323 // above. This user provided passphrase could be the current or the | |
324 // old. But, as long as we persist the token, there's nothing more | |
325 // we can do. | |
326 cryptographer->GetBootstrapToken(&bootstrap_token); | |
327 DVLOG(1) << "Implicit user provided passphrase accepted, initializing" | |
328 << " cryptographer."; | |
329 success = true; | |
330 } else { | |
331 DVLOG(1) << "Implicit user provided passphrase failed to decrypt."; | |
332 success = false; | |
333 } | |
334 } // cryptographer->is_initialized() | |
335 } else { // nigori_has_explicit_passphrase == true | |
336 // Case 9. Encryption was done with an explicit passphrase, and we decrypt | |
337 // with the passphrase provided by the user. | |
338 if (cryptographer->DecryptPendingKeys(key_params)) { | |
339 DVLOG(1) << "Explicit passphrase accepted for decryption."; | |
340 cryptographer->GetBootstrapToken(&bootstrap_token); | |
341 success = true; | |
342 } else { | |
343 DVLOG(1) << "Explicit passphrase failed to decrypt."; | |
344 success = false; | |
345 } | |
346 } // nigori_has_explicit_passphrase | |
347 | |
348 DVLOG_IF(1, !success) | |
349 << "Failure in SetDecryptionPassphrase; notifying and returning."; | |
350 DVLOG_IF(1, success) | |
351 << "Successfully set decryption passphrase; updating nigori and " | |
352 "reencrypting."; | |
353 | |
354 FinishSetPassphrase(success, | |
355 bootstrap_token, | |
356 nigori_has_explicit_passphrase, | |
357 &trans, | |
358 &node); | |
359 } | |
360 | |
361 void SyncEncryptionHandlerImpl::EnableEncryptEverything() { | |
362 if (encrypt_everything_) { | |
363 DCHECK(encrypted_types_.Equals(ModelTypeSet::All())); | |
364 return; | |
365 } | |
366 WriteTransaction trans(FROM_HERE, user_share_); | |
367 encrypt_everything_ = true; | |
368 // Change |encrypted_types_| directly to avoid sending more than one | |
369 // notification. | |
370 encrypted_types_ = ModelTypeSet::All(); | |
371 FOR_EACH_OBSERVER( | |
372 Observer, observers_, | |
373 OnEncryptedTypesChanged(encrypted_types_, encrypt_everything_)); | |
374 WriteEncryptionStateToNigori(&trans); | |
375 ReEncryptEverything(&trans); | |
376 } | |
377 | |
378 bool SyncEncryptionHandlerImpl::EncryptEverythingEnabled() const { | |
379 ReadTransaction trans(FROM_HERE, user_share_); | |
380 return encrypt_everything_; | |
381 } | |
382 | |
383 bool SyncEncryptionHandlerImpl::IsUsingExplicitPassphrase() const { | |
384 ReadTransaction trans(FROM_HERE, user_share_); | |
385 return explicit_passphrase_; | |
386 } | |
387 | |
388 // This function iterates over all encrypted types. There are many scenarios in | |
389 // which data for some or all types is not currently available. In that case, | |
390 // the lookup of the root node will fail and we will skip encryption for that | |
391 // type. | |
392 void SyncEncryptionHandlerImpl::ReEncryptEverything( | |
393 WriteTransaction* trans) { | |
394 Cryptographer* cryptographer = trans->GetCryptographer(); | |
395 if (!cryptographer->is_ready()) | |
396 return; | |
397 ModelTypeSet encrypted_types = GetEncryptedTypes(); | |
398 for (ModelTypeSet::Iterator iter = encrypted_types.First(); | |
399 iter.Good(); iter.Inc()) { | |
400 if (iter.Get() == PASSWORDS || iter.Get() == NIGORI) | |
401 continue; // These types handle encryption differently. | |
402 | |
403 ReadNode type_root(trans); | |
404 std::string tag = ModelTypeToRootTag(iter.Get()); | |
405 if (type_root.InitByTagLookup(tag) != BaseNode::INIT_OK) | |
406 continue; // Don't try to reencrypt if the type's data is unavailable. | |
407 | |
408 // Iterate through all children of this datatype. | |
409 std::queue<int64> to_visit; | |
410 int64 child_id = type_root.GetFirstChildId(); | |
411 to_visit.push(child_id); | |
412 while (!to_visit.empty()) { | |
413 child_id = to_visit.front(); | |
414 to_visit.pop(); | |
415 if (child_id == kInvalidId) | |
416 continue; | |
417 | |
418 WriteNode child(trans); | |
419 if (child.InitByIdLookup(child_id) != BaseNode::INIT_OK) { | |
420 NOTREACHED(); | |
421 continue; | |
422 } | |
423 if (child.GetIsFolder()) { | |
424 to_visit.push(child.GetFirstChildId()); | |
425 } | |
426 if (child.GetEntry()->Get(syncable::UNIQUE_SERVER_TAG).empty()) { | |
427 // Rewrite the specifics of the node with encrypted data if necessary | |
428 // (only rewrite the non-unique folders). | |
429 child.ResetFromSpecifics(); | |
430 } | |
431 to_visit.push(child.GetSuccessorId()); | |
432 } | |
433 } | |
434 | |
435 // Passwords are encrypted with their own legacy scheme. Passwords are always | |
436 // encrypted so we don't need to check GetEncryptedTypes() here. | |
437 ReadNode passwords_root(trans); | |
438 std::string passwords_tag = ModelTypeToRootTag(PASSWORDS); | |
439 if (passwords_root.InitByTagLookup(passwords_tag) == | |
440 BaseNode::INIT_OK) { | |
441 int64 child_id = passwords_root.GetFirstChildId(); | |
442 while (child_id != kInvalidId) { | |
443 WriteNode child(trans); | |
444 if (child.InitByIdLookup(child_id) != BaseNode::INIT_OK) { | |
445 NOTREACHED(); | |
446 return; | |
447 } | |
448 child.SetPasswordSpecifics(child.GetPasswordSpecifics()); | |
449 child_id = child.GetSuccessorId(); | |
450 } | |
451 } | |
452 | |
453 // NOTE: We notify from within a transaction. | |
454 FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_, | |
455 OnEncryptionComplete()); | |
456 } | |
457 | |
458 bool SyncEncryptionHandlerImpl::ApplyNigoriUpdateImpl( | |
459 const sync_pb::NigoriSpecifics& nigori, | |
460 syncable::BaseTransaction* const trans) { | |
461 Cryptographer* cryptographer = trans->directory()->GetCryptographer(trans); | |
462 bool nigori_types_need_update = !UpdateEncryptedTypesFromNigori(nigori); | |
463 if (nigori.using_explicit_passphrase()) | |
464 explicit_passphrase_ = true; | |
465 | |
466 bool nigori_needs_new_keys = false; | |
467 if (!nigori.encrypted().blob().empty()) { | |
468 if (cryptographer->CanDecrypt(nigori.encrypted())) { | |
469 cryptographer->InstallKeys(nigori.encrypted()); | |
470 // We only update the default passphrase if this was a new explicit | |
471 // passphrase. Else, since it was decryptable, it must not have been a new | |
472 // key. | |
473 if (nigori.using_explicit_passphrase()) | |
474 cryptographer->SetDefaultKey(nigori.encrypted().key_name()); | |
475 | |
476 // Check if the cryptographer's keybag is newer than the nigori's | |
477 // keybag. If so, we need to overwrite the nigori node. | |
478 sync_pb::EncryptedData new_keys = nigori.encrypted(); | |
479 if (!cryptographer->GetKeys(&new_keys)) | |
480 NOTREACHED(); | |
481 if (nigori.encrypted().SerializeAsString() != | |
482 new_keys.SerializeAsString()) | |
483 nigori_needs_new_keys = true; | |
484 } else { | |
485 cryptographer->SetPendingKeys(nigori.encrypted()); | |
486 } | |
487 } else { | |
488 nigori_needs_new_keys = true; | |
489 } | |
490 | |
491 // If we've completed a sync cycle and the cryptographer isn't ready | |
492 // yet or has pending keys, prompt the user for a passphrase. | |
493 if (cryptographer->has_pending_keys()) { | |
494 DVLOG(1) << "OnPassphraseRequired Sent"; | |
495 sync_pb::EncryptedData pending_keys = cryptographer->GetPendingKeys(); | |
496 FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_, | |
497 OnPassphraseRequired(REASON_DECRYPTION, | |
498 pending_keys)); | |
499 } else if (!cryptographer->is_ready()) { | |
500 DVLOG(1) << "OnPassphraseRequired sent because cryptographer is not " | |
501 << "ready"; | |
502 FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_, | |
503 OnPassphraseRequired(REASON_ENCRYPTION, | |
504 sync_pb::EncryptedData())); | |
505 } | |
506 | |
507 // Check if the current local encryption state is stricter/newer than the | |
508 // nigori state. If so, we need to overwrite the nigori node with the local | |
509 // state. | |
510 if (nigori.using_explicit_passphrase() != explicit_passphrase_ || | |
511 nigori.encrypt_everything() != encrypt_everything_ || | |
512 nigori_types_need_update || | |
513 nigori_needs_new_keys) { | |
514 return false; | |
515 } | |
516 return true; | |
517 } | |
518 | |
519 void SyncEncryptionHandlerImpl::RewriteNigori() { | |
520 WriteTransaction trans(FROM_HERE, user_share_); | |
521 WriteEncryptionStateToNigori(&trans); | |
522 } | |
523 | |
524 void SyncEncryptionHandlerImpl::WriteEncryptionStateToNigori( | |
525 WriteTransaction* trans) { | |
526 WriteNode nigori_node(trans); | |
527 // This can happen in tests that don't have nigori nodes. | |
528 if (!nigori_node.InitByTagLookup(kNigoriTag) == BaseNode::INIT_OK) | |
529 return; | |
530 sync_pb::NigoriSpecifics nigori = nigori_node.GetNigoriSpecifics(); | |
531 Cryptographer* cryptographer = trans->GetCryptographer(); | |
532 if (cryptographer->is_ready() && | |
533 nigori_overwrite_count_ < kNigoriOverwriteLimit) { | |
534 // Does not modify the encrypted blob if the unencrypted data already | |
535 // matches what is about to be written. | |
536 sync_pb::EncryptedData original_keys = nigori.encrypted(); | |
537 if (!cryptographer->GetKeys(nigori.mutable_encrypted())) | |
538 NOTREACHED(); | |
539 | |
540 if (nigori.encrypted().SerializeAsString() != | |
541 original_keys.SerializeAsString()) { | |
542 // We've updated the nigori node's encryption keys. In order to prevent | |
543 // a possible looping of two clients constantly overwriting each other, | |
544 // we limit the absolute number of overwrites per client instantiation. | |
545 nigori_overwrite_count_++; | |
546 UMA_HISTOGRAM_COUNTS("Sync.AutoNigoriOverwrites", | |
547 nigori_overwrite_count_); | |
548 } | |
549 | |
550 // Note: we don't try to set using_explicit_passphrase here since if that | |
551 // is lost the user can always set it again. The main point is to preserve | |
552 // the encryption keys so all data remains decryptable. | |
553 } | |
554 syncable::UpdateNigoriFromEncryptedTypes(encrypted_types_, | |
555 encrypt_everything_, | |
556 &nigori); | |
557 | |
558 // If nothing has changed, this is a no-op. | |
559 nigori_node.SetNigoriSpecifics(nigori); | |
560 } | |
561 | |
562 bool SyncEncryptionHandlerImpl::UpdateEncryptedTypesFromNigori( | |
563 const sync_pb::NigoriSpecifics& nigori) { | |
564 if (nigori.encrypt_everything()) { | |
565 if (!encrypt_everything_) { | |
566 encrypt_everything_ = true; | |
567 encrypted_types_ = ModelTypeSet::All(); | |
568 FOR_EACH_OBSERVER( | |
569 Observer, observers_, | |
570 OnEncryptedTypesChanged(encrypted_types_, encrypt_everything_)); | |
571 } | |
572 DCHECK(encrypted_types_.Equals(ModelTypeSet::All())); | |
573 return true; | |
574 } | |
575 | |
576 ModelTypeSet encrypted_types; | |
577 encrypted_types = syncable::GetEncryptedTypesFromNigori(nigori); | |
578 encrypted_types.PutAll(SensitiveTypes()); | |
579 | |
580 // If anything more than the sensitive types were encrypted, and | |
581 // encrypt_everything is not explicitly set to false, we assume it means | |
582 // a client intended to enable encrypt everything. | |
583 if (!nigori.has_encrypt_everything() && | |
584 !Difference(encrypted_types, SensitiveTypes()).Empty()) { | |
585 if (!encrypt_everything_) { | |
586 encrypt_everything_ = true; | |
587 encrypted_types_ = ModelTypeSet::All(); | |
588 FOR_EACH_OBSERVER( | |
589 Observer, observers_, | |
590 OnEncryptedTypesChanged(encrypted_types_, encrypt_everything_)); | |
591 } | |
592 DCHECK(encrypted_types_.Equals(ModelTypeSet::All())); | |
593 return false; | |
594 } | |
595 | |
596 MergeEncryptedTypes(encrypted_types); | |
597 return encrypted_types_.Equals(encrypted_types); | |
598 } | |
599 | |
600 void SyncEncryptionHandlerImpl::UpdateNigoriFromEncryptedTypes( | |
tim (not reviewing)
2012/08/15 00:23:30
If we keep the nigori_util version, do we still ne
Nicolas Zea
2012/08/15 01:08:29
This one accesses the local state (i.e. encrypted_
| |
601 sync_pb::NigoriSpecifics* nigori, | |
602 syncable::BaseTransaction* const trans) const { | |
603 syncable::UpdateNigoriFromEncryptedTypes(encrypted_types_, | |
604 encrypt_everything_, | |
605 nigori); | |
606 } | |
607 | |
608 void SyncEncryptionHandlerImpl::FinishSetPassphrase( | |
609 bool success, | |
610 const std::string& bootstrap_token, | |
611 bool is_explicit, | |
612 WriteTransaction* trans, | |
613 WriteNode* nigori_node) { | |
614 Cryptographer* cryptographer = trans->GetCryptographer(); | |
615 FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_, | |
616 OnCryptographerStateChanged(cryptographer)); | |
617 | |
618 // It's possible we need to change the bootstrap token even if we failed to | |
619 // set the passphrase (for example if we need to preserve the new GAIA | |
620 // passphrase). | |
621 if (!bootstrap_token.empty()) { | |
622 DVLOG(1) << "Bootstrap token updated."; | |
623 FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_, | |
624 OnBootstrapTokenUpdated(bootstrap_token)); | |
625 } | |
626 | |
627 if (!success) { | |
628 if (cryptographer->is_ready()) { | |
629 LOG(ERROR) << "Attempt to change passphrase failed while cryptographer " | |
630 << "was ready."; | |
631 } else if (cryptographer->has_pending_keys()) { | |
632 FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_, | |
633 OnPassphraseRequired(REASON_DECRYPTION, | |
634 cryptographer->GetPendingKeys())); | |
635 } else { | |
636 FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_, | |
637 OnPassphraseRequired(REASON_ENCRYPTION, | |
638 sync_pb::EncryptedData())); | |
639 } | |
640 return; | |
641 } | |
642 | |
643 FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_, | |
644 OnPassphraseAccepted()); | |
645 DCHECK(cryptographer->is_ready()); | |
646 | |
647 sync_pb::NigoriSpecifics specifics(nigori_node->GetNigoriSpecifics()); | |
648 // Does not modify specifics.encrypted() if the original decrypted data was | |
649 // the same. | |
650 if (!cryptographer->GetKeys(specifics.mutable_encrypted())) { | |
651 NOTREACHED(); | |
652 return; | |
653 } | |
654 explicit_passphrase_ = is_explicit; | |
655 specifics.set_using_explicit_passphrase(is_explicit); | |
656 nigori_node->SetNigoriSpecifics(specifics); | |
657 | |
658 // Does nothing if everything is already encrypted or the cryptographer has | |
659 // pending keys. | |
660 ReEncryptEverything(trans); | |
661 } | |
662 | |
663 void SyncEncryptionHandlerImpl::MergeEncryptedTypes( | |
664 ModelTypeSet encrypted_types) { | |
665 if (!encrypted_types_.HasAll(encrypted_types)) { | |
666 encrypted_types_ = encrypted_types; | |
667 FOR_EACH_OBSERVER( | |
668 Observer, observers_, | |
669 OnEncryptedTypesChanged(encrypted_types_, encrypt_everything_)); | |
670 } | |
671 } | |
672 | |
673 } // namespace browser_sync | |
OLD | NEW |