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 "chrome/browser/sync/engine/nigori_util.h" | 5 #include "sync/engine/nigori_util.h" |
6 | 6 |
7 #include <queue> | 7 #include <queue> |
8 #include <string> | 8 #include <string> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "chrome/browser/sync/engine/syncer_util.h" | 11 #include "base/json/json_writer.h" |
12 #include "chrome/browser/sync/internal_api/write_node.h" | 12 #include "sync/engine/syncer_util.h" |
13 #include "chrome/browser/sync/syncable/syncable.h" | 13 #include "sync/syncable/syncable.h" |
14 #include "chrome/browser/sync/util/cryptographer.h" | 14 #include "sync/util/cryptographer.h" |
15 | 15 |
16 namespace syncable { | 16 namespace syncable { |
17 | 17 |
18 bool ProcessUnsyncedChangesForEncryption( | 18 bool ProcessUnsyncedChangesForEncryption( |
19 WriteTransaction* const trans, | 19 WriteTransaction* const trans, |
20 browser_sync::Cryptographer* cryptographer) { | 20 browser_sync::Cryptographer* cryptographer) { |
21 DCHECK(cryptographer->is_ready()); | 21 DCHECK(cryptographer->is_ready()); |
22 // Get list of all datatypes with unsynced changes. It's possible that our | 22 // Get list of all datatypes with unsynced changes. It's possible that our |
23 // local changes need to be encrypted if encryption for that datatype was | 23 // local changes need to be encrypted if encryption for that datatype was |
24 // just turned on (and vice versa). | 24 // just turned on (and vice versa). |
25 // Note: we do not attempt to re-encrypt data with a new key here as key | 25 // Note: we do not attempt to re-encrypt data with a new key here as key |
26 // changes in this code path are likely due to consistency issues (we have | 26 // changes in this code path are likely due to consistency issues (we have |
27 // to be updated to a key we already have, e.g. an old key). | 27 // to be updated to a key we already have, e.g. an old key). |
28 std::vector<int64> handles; | 28 std::vector<int64> handles; |
29 browser_sync::SyncerUtil::GetUnsyncedEntries(trans, &handles); | 29 browser_sync::SyncerUtil::GetUnsyncedEntries(trans, &handles); |
30 for (size_t i = 0; i < handles.size(); ++i) { | 30 for (size_t i = 0; i < handles.size(); ++i) { |
31 MutableEntry entry(trans, GET_BY_HANDLE, handles[i]); | 31 MutableEntry entry(trans, GET_BY_HANDLE, handles[i]); |
32 const sync_pb::EntitySpecifics& specifics = entry.Get(SPECIFICS); | 32 const sync_pb::EntitySpecifics& specifics = entry.Get(SPECIFICS); |
33 // Ignore types that don't need encryption or entries that are already | 33 // Ignore types that don't need encryption or entries that are already |
34 // encrypted. | 34 // encrypted. |
35 if (!SpecificsNeedsEncryption(cryptographer->GetEncryptedTypes(), | 35 if (!SpecificsNeedsEncryption(cryptographer->GetEncryptedTypes(), |
36 specifics)) { | 36 specifics)) { |
37 continue; | 37 continue; |
38 } | 38 } |
39 if (!sync_api::WriteNode::UpdateEntryWithEncryption(cryptographer, | 39 if (!UpdateEntryWithEncryption(cryptographer, specifics, &entry)) { |
40 specifics, | |
41 &entry)) { | |
42 NOTREACHED(); | 40 NOTREACHED(); |
43 return false; | 41 return false; |
44 } | 42 } |
45 } | 43 } |
46 return true; | 44 return true; |
47 } | 45 } |
48 | 46 |
49 bool VerifyUnsyncedChangesAreEncrypted( | 47 bool VerifyUnsyncedChangesAreEncrypted( |
50 BaseTransaction* const trans, | 48 BaseTransaction* const trans, |
51 ModelTypeSet encrypted_types) { | 49 ModelTypeSet encrypted_types) { |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
147 if (!cryptographer->CanDecryptUsingDefaultKey(specifics.encrypted())) | 145 if (!cryptographer->CanDecryptUsingDefaultKey(specifics.encrypted())) |
148 return false; | 146 return false; |
149 } | 147 } |
150 } | 148 } |
151 // Push the successor. | 149 // Push the successor. |
152 to_visit.push(child.Get(NEXT_ID)); | 150 to_visit.push(child.Get(NEXT_ID)); |
153 } | 151 } |
154 return true; | 152 return true; |
155 } | 153 } |
156 | 154 |
| 155 bool UpdateEntryWithEncryption( |
| 156 browser_sync::Cryptographer* cryptographer, |
| 157 const sync_pb::EntitySpecifics& new_specifics, |
| 158 syncable::MutableEntry* entry) { |
| 159 syncable::ModelType type = syncable::GetModelTypeFromSpecifics(new_specifics); |
| 160 DCHECK_GE(type, syncable::FIRST_REAL_MODEL_TYPE); |
| 161 const sync_pb::EntitySpecifics& old_specifics = entry->Get(SPECIFICS); |
| 162 const syncable::ModelTypeSet encrypted_types = |
| 163 cryptographer->GetEncryptedTypes(); |
| 164 // It's possible the nigori lost the set of encrypted types. If the current |
| 165 // specifics are already encrypted, we want to ensure we continue encrypting. |
| 166 bool was_encrypted = old_specifics.has_encrypted(); |
| 167 sync_pb::EntitySpecifics generated_specifics; |
| 168 if (new_specifics.has_encrypted()) { |
| 169 NOTREACHED() << "New specifics already has an encrypted blob."; |
| 170 return false; |
| 171 } |
| 172 if ((!SpecificsNeedsEncryption(encrypted_types, new_specifics) && |
| 173 !was_encrypted) || |
| 174 !cryptographer->is_initialized()) { |
| 175 // No encryption required or we are unable to encrypt. |
| 176 generated_specifics.CopyFrom(new_specifics); |
| 177 } else { |
| 178 // Encrypt new_specifics into generated_specifics. |
| 179 if (VLOG_IS_ON(2)) { |
| 180 scoped_ptr<DictionaryValue> value(entry->ToValue()); |
| 181 std::string info; |
| 182 base::JSONWriter::Write(value.get(), true, &info); |
| 183 DVLOG(2) << "Encrypting specifics of type " |
| 184 << syncable::ModelTypeToString(type) |
| 185 << " with content: " |
| 186 << info; |
| 187 } |
| 188 // Only copy over the old specifics if it is of the right type and already |
| 189 // encrypted. The first time we encrypt a node we start from scratch, hence |
| 190 // removing all the unencrypted data, but from then on we only want to |
| 191 // update the node if the data changes or the encryption key changes. |
| 192 if (syncable::GetModelTypeFromSpecifics(old_specifics) == type && |
| 193 was_encrypted) { |
| 194 generated_specifics.CopyFrom(old_specifics); |
| 195 } else { |
| 196 syncable::AddDefaultFieldValue(type, &generated_specifics); |
| 197 } |
| 198 // Does not change anything if underlying encrypted blob was already up |
| 199 // to date and encrypted with the default key. |
| 200 if (!cryptographer->Encrypt(new_specifics, |
| 201 generated_specifics.mutable_encrypted())) { |
| 202 NOTREACHED() << "Could not encrypt data for node of type " |
| 203 << syncable::ModelTypeToString(type); |
| 204 return false; |
| 205 } |
| 206 } |
| 207 |
| 208 // It's possible this entry was encrypted but didn't properly overwrite the |
| 209 // non_unique_name (see crbug.com/96314). |
| 210 bool encrypted_without_overwriting_name = (was_encrypted && |
| 211 entry->Get(syncable::NON_UNIQUE_NAME) != kEncryptedString); |
| 212 |
| 213 // If we're encrypted but the name wasn't overwritten properly we still want |
| 214 // to rewrite the entry, irrespective of whether the specifics match. |
| 215 if (!encrypted_without_overwriting_name && |
| 216 old_specifics.SerializeAsString() == |
| 217 generated_specifics.SerializeAsString()) { |
| 218 DVLOG(2) << "Specifics of type " << syncable::ModelTypeToString(type) |
| 219 << " already match, dropping change."; |
| 220 return true; |
| 221 } |
| 222 |
| 223 if (generated_specifics.has_encrypted()) { |
| 224 // Overwrite the possibly sensitive non-specifics data. |
| 225 entry->Put(syncable::NON_UNIQUE_NAME, kEncryptedString); |
| 226 // For bookmarks we actually put bogus data into the unencrypted specifics, |
| 227 // else the server will try to do it for us. |
| 228 if (type == syncable::BOOKMARKS) { |
| 229 sync_pb::BookmarkSpecifics* bookmark_specifics = |
| 230 generated_specifics.mutable_bookmark(); |
| 231 if (!entry->Get(syncable::IS_DIR)) |
| 232 bookmark_specifics->set_url(kEncryptedString); |
| 233 bookmark_specifics->set_title(kEncryptedString); |
| 234 } |
| 235 } |
| 236 entry->Put(syncable::SPECIFICS, generated_specifics); |
| 237 DVLOG(1) << "Overwriting specifics of type " |
| 238 << syncable::ModelTypeToString(type) |
| 239 << " and marking for syncing."; |
| 240 syncable::MarkForSyncing(entry); |
| 241 return true; |
| 242 } |
| 243 |
157 } // namespace syncable | 244 } // namespace syncable |
OLD | NEW |