Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 "sync/engine/apply_control_data_updates.h" | 5 #include "sync/engine/apply_control_data_updates.h" |
| 6 | 6 |
| 7 #include "base/metrics/histogram.h" | 7 #include "base/metrics/histogram.h" |
| 8 #include "sync/engine/conflict_resolver.h" | 8 #include "sync/engine/conflict_resolver.h" |
| 9 #include "sync/engine/conflict_util.h" | 9 #include "sync/engine/conflict_util.h" |
| 10 #include "sync/engine/syncer_util.h" | 10 #include "sync/engine/syncer_util.h" |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 50 | 50 |
| 51 // Mainly for unit tests. We should have a Nigori node by this point. | 51 // Mainly for unit tests. We should have a Nigori node by this point. |
| 52 if (!nigori_node.good()) { | 52 if (!nigori_node.good()) { |
| 53 return false; | 53 return false; |
| 54 } | 54 } |
| 55 | 55 |
| 56 if (!nigori_node.Get(IS_UNAPPLIED_UPDATE)) { | 56 if (!nigori_node.Get(IS_UNAPPLIED_UPDATE)) { |
| 57 return true; | 57 return true; |
| 58 } | 58 } |
| 59 | 59 |
| 60 // We apply the nigori update regardless of whether there's a conflict or | |
| 61 // not in order to preserve any new encrypted types or encryption keys. | |
| 60 const sync_pb::NigoriSpecifics& nigori = | 62 const sync_pb::NigoriSpecifics& nigori = |
| 61 nigori_node.Get(SERVER_SPECIFICS).nigori(); | 63 nigori_node.Get(SERVER_SPECIFICS).nigori(); |
| 62 trans->directory()->GetNigoriHandler()->ApplyNigoriUpdate(nigori, trans); | 64 trans->directory()->GetNigoriHandler()->ApplyNigoriUpdate(nigori, trans); |
| 63 | 65 |
| 64 // Make sure any unsynced changes are properly encrypted as necessary. | 66 // Make sure any unsynced changes are properly encrypted as necessary. |
| 65 // We only perform this if the cryptographer is ready. If not, these are | 67 // We only perform this if the cryptographer is ready. If not, these are |
| 66 // re-encrypted at SetDecryptionPassphrase time (via ReEncryptEverything). | 68 // re-encrypted at SetDecryptionPassphrase time (via ReEncryptEverything). |
| 67 // This logic covers the case where the nigori update marked new datatypes | 69 // This logic covers the case where the nigori update marked new datatypes |
| 68 // for encryption, but didn't change the passphrase. | 70 // for encryption, but didn't change the passphrase. |
| 69 if (cryptographer->is_ready()) { | 71 if (cryptographer->is_ready()) { |
| 70 // Note that we don't bother to encrypt any data for which IS_UNSYNCED | 72 // Note that we don't bother to encrypt any data for which IS_UNSYNCED |
| 71 // == false here. The machine that turned on encryption should know about | 73 // == false here. The machine that turned on encryption should know about |
| 72 // and re-encrypt all synced data. It's possible it could get interrupted | 74 // and re-encrypt all synced data. It's possible it could get interrupted |
| 73 // during this process, but we currently reencrypt everything at startup | 75 // during this process, but we currently reencrypt everything at startup |
| 74 // as well, so as soon as a client is restarted with this datatype marked | 76 // as well, so as soon as a client is restarted with this datatype marked |
| 75 // for encryption, all the data should be updated as necessary. | 77 // for encryption, all the data should be updated as necessary. |
| 76 | 78 |
| 77 // If this fails, something is wrong with the cryptographer, but there's | 79 // If this fails, something is wrong with the cryptographer, but there's |
| 78 // nothing we can do about it here. | 80 // nothing we can do about it here. |
| 79 DVLOG(1) << "Received new nigori, encrypting unsynced changes."; | 81 DVLOG(1) << "Received new nigori, encrypting unsynced changes."; |
| 80 syncable::ProcessUnsyncedChangesForEncryption(trans); | 82 syncable::ProcessUnsyncedChangesForEncryption(trans); |
| 81 } | 83 } |
| 82 | 84 |
| 83 if (!nigori_node.Get(IS_UNSYNCED)) { // Update only. | 85 if (!nigori_node.Get(IS_UNSYNCED)) { // Update only. |
| 84 UpdateLocalDataFromServerData(trans, &nigori_node); | 86 UpdateLocalDataFromServerData(trans, &nigori_node); |
| 85 } else { // Conflict. | 87 } else { // Conflict. |
| 86 // Create a new set of specifics based on the server specifics (which | 88 const sync_pb::EntitySpecifics& server_specifics = |
| 87 // preserves their encryption keys). | 89 nigori_node.Get(SERVER_SPECIFICS); |
| 88 sync_pb::EntitySpecifics specifics = nigori_node.Get(SERVER_SPECIFICS); | 90 const sync_pb::NigoriSpecifics& server_nigori = server_specifics.nigori(); |
| 89 sync_pb::NigoriSpecifics* server_nigori = specifics.mutable_nigori(); | 91 const sync_pb::EntitySpecifics& local_specifics = |
| 90 // Store the merged set of encrypted types. | 92 nigori_node.Get(SPECIFICS); |
| 91 // (NigoriHandler::ApplyNigoriUpdate(..) will have merged the local types | 93 const sync_pb::NigoriSpecifics& local_nigori = local_specifics.nigori(); |
| 92 // already). | 94 |
| 95 // We initialize the new nigori with the server state, and will override | |
| 96 // it as necessary below. | |
| 97 sync_pb::EntitySpecifics new_specifics = nigori_node.Get(SERVER_SPECIFICS); | |
| 98 sync_pb::NigoriSpecifics* new_nigori = new_specifics.mutable_nigori(); | |
| 99 | |
| 100 // Always update to the safest set of encrypted types. | |
| 93 trans->directory()->GetNigoriHandler()->UpdateNigoriFromEncryptedTypes( | 101 trans->directory()->GetNigoriHandler()->UpdateNigoriFromEncryptedTypes( |
| 94 server_nigori, | 102 new_nigori, |
| 95 trans); | 103 trans); |
| 96 // The cryptographer has the both the local and remote encryption keys | 104 |
| 97 // (added at NigoriHandler::ApplyNigoriUpdate(..) time). | 105 // If the cryptographer is not ready, another client set a new encryption |
| 98 // If the cryptographer is ready, then it already merged both sets of keys | 106 // passphrase. If we had migrated locally, we will re-migrate when the |
| 99 // and we can store them back in. In that case, the remote key was already | 107 // pending keys are provided. If we had set a new custom passphrase locally |
| 100 // part of the local keybag, so we preserve the local key as the default | 108 // the user will have another chance to set a custom passphrase later |
| 101 // (including whether it's an explicit key). | 109 // (assuming they hadn't set a custom passphrase on the other client). |
| 102 // If the cryptographer is not ready, then the user will have to provide | 110 // Therefore, we only attempt to merge the nigori nodes if the cryptographer |
| 103 // the passphrase to decrypt the pending keys. When they do so, the | 111 // is ready. |
| 104 // SetDecryptionPassphrase code will act based on whether the server | 112 // Note: we only update the encryption keybag if we're sure that we aren't |
| 105 // update has an explicit passphrase or not. | 113 // invalidating the keystore_decryptor_token (i.e. we're either |
| 106 // - If the server had an explicit passphrase, that explicit passphrase | 114 // not migrated or we copying over all local state). |
| 107 // will be preserved as the default encryption key. | |
| 108 // - If the server did not have an explicit passphrase, we assume the | |
| 109 // local passphrase is the most up to date and preserve the local | |
| 110 // default encryption key marked as an implicit passphrase. | |
| 111 // This works fine except for the case where we had locally set an | |
| 112 // explicit passphrase. In that case the nigori node will have the default | |
| 113 // key based on the local explicit passphassphrase, but will not have it | |
| 114 // marked as explicit. To fix this we'd have to track whether we have a | |
| 115 // explicit passphrase or not separate from the nigori, which would | |
| 116 // introduce even more complexity, so we leave it up to the user to reset | |
| 117 // that passphrase as an explicit one via settings. The goal here is to | |
| 118 // ensure both sets of encryption keys are preserved. | |
| 119 if (cryptographer->is_ready()) { | 115 if (cryptographer->is_ready()) { |
| 120 cryptographer->GetKeys(server_nigori->mutable_encryption_keybag()); | 116 if (local_nigori.has_passphrase_type() && |
| 121 server_nigori->set_keybag_is_frozen( | 117 server_nigori.has_passphrase_type()) { |
| 122 nigori_node.Get(SPECIFICS).nigori().keybag_is_frozen()); | 118 // They're both migrated, preserve the local passphrase type if it |
| 119 // is more conservative. We don't have to worry about copying over the | |
| 120 // keystore decryptor since this only happens if local has an explicit | |
|
rlarocque
2012/09/13 22:10:51
DCHECK that assumption?
Nicolas Zea
2012/09/13 22:50:14
Done.
| |
| 121 // passphrase type. | |
| 122 if (server_nigori.passphrase_type() == | |
| 123 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE && | |
| 124 local_nigori.passphrase_type() != | |
| 125 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE) { | |
| 126 new_nigori->set_passphrase_type(local_nigori.passphrase_type()); | |
| 127 new_nigori->clear_keystore_decryptor_token(); | |
| 128 cryptographer->GetKeys(new_nigori->mutable_encryption_keybag()); | |
|
rlarocque
2012/09/13 22:10:51
It seems you do this almost everywhere. Why not m
Nicolas Zea
2012/09/13 22:50:14
Yes, see the "Note: we only update the encryption
| |
| 129 } | |
| 130 } else if (!local_nigori.has_passphrase_type() && | |
| 131 !server_nigori.has_passphrase_type()) { | |
| 132 // Set the explicit passphrase based on the local state. If the server | |
| 133 // had set an explict passphrase, we should have pending keys, so | |
| 134 // should not reach this code. | |
| 135 // Because neither side is migrated, we don't have to worry about the | |
| 136 // keystore decryptor token. | |
| 137 new_nigori->set_keybag_is_frozen(local_nigori.keybag_is_frozen()); | |
| 138 cryptographer->GetKeys(new_nigori->mutable_encryption_keybag()); | |
| 139 } else { | |
| 140 // One side is migrated, the other is not. Because we don't have | |
| 141 // pending keys, we can just preserve whichever side is migrated. | |
| 142 if (local_nigori.has_passphrase_type()) { | |
|
rlarocque
2012/09/13 22:10:51
Let's make the division of cases more explicit. I
Nicolas Zea
2012/09/13 22:50:14
Done.
| |
| 143 new_nigori->set_keybag_is_frozen(true); | |
| 144 new_nigori->set_passphrase_type(local_nigori.passphrase_type()); | |
| 145 new_nigori->set_keystore_migration_time( | |
| 146 local_nigori.keystore_migration_time()); | |
| 147 new_nigori->mutable_keystore_decryptor_token()->CopyFrom( | |
| 148 local_nigori.keystore_decryptor_token()); | |
| 149 cryptographer->GetKeys(new_nigori->mutable_encryption_keybag()); | |
| 150 } // else leave the new nigori with the server state. | |
| 151 } | |
| 123 } | 152 } |
| 124 nigori_node.Put(SPECIFICS, specifics); | 153 |
| 154 nigori_node.Put(SPECIFICS, new_specifics); | |
|
rlarocque
2012/09/13 22:10:51
SetNigoriSpecifics(), which seems to be used for n
Nicolas Zea
2012/09/13 22:50:14
We don't have access to SetNigoriSpecifics here ac
| |
| 125 DVLOG(1) << "Resolving simple conflict, merging nigori nodes: " | 155 DVLOG(1) << "Resolving simple conflict, merging nigori nodes: " |
| 126 << nigori_node; | 156 << nigori_node; |
| 127 | 157 |
| 128 OverwriteServerChanges(&nigori_node); | 158 OverwriteServerChanges(&nigori_node); |
| 129 | 159 |
| 130 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 160 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 131 ConflictResolver::NIGORI_MERGE, | 161 ConflictResolver::NIGORI_MERGE, |
| 132 ConflictResolver::CONFLICT_RESOLUTION_SIZE); | 162 ConflictResolver::CONFLICT_RESOLUTION_SIZE); |
| 133 } | 163 } |
| 134 | 164 |
| 135 return true; | 165 return true; |
| 136 } | 166 } |
| 137 | 167 |
| 138 } // namespace syncer | 168 } // namespace syncer |
| OLD | NEW |