Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(271)

Side by Side Diff: sync/syncable/nigori_util.cc

Issue 2130453004: [Sync] Move //sync to //components/sync. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sync/syncable/nigori_util.h ('k') | sync/syncable/nigori_util_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 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/syncable/nigori_util.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <queue>
11 #include <string>
12 #include <vector>
13
14 #include "base/json/json_writer.h"
15 #include "sync/syncable/directory.h"
16 #include "sync/syncable/entry.h"
17 #include "sync/syncable/mutable_entry.h"
18 #include "sync/syncable/nigori_handler.h"
19 #include "sync/syncable/syncable_util.h"
20 #include "sync/syncable/syncable_write_transaction.h"
21 #include "sync/util/cryptographer.h"
22
23 namespace syncer {
24 namespace syncable {
25
26 bool ProcessUnsyncedChangesForEncryption(
27 WriteTransaction* const trans) {
28 NigoriHandler* nigori_handler = trans->directory()->GetNigoriHandler();
29 ModelTypeSet encrypted_types = nigori_handler->GetEncryptedTypes(trans);
30 Cryptographer* cryptographer = trans->directory()->GetCryptographer(trans);
31 DCHECK(cryptographer->is_ready());
32
33 // Get list of all datatypes with unsynced changes. It's possible that our
34 // local changes need to be encrypted if encryption for that datatype was
35 // just turned on (and vice versa).
36 // Note: we do not attempt to re-encrypt data with a new key here as key
37 // changes in this code path are likely due to consistency issues (we have
38 // to be updated to a key we already have, e.g. an old key).
39 std::vector<int64_t> handles;
40 GetUnsyncedEntries(trans, &handles);
41 for (size_t i = 0; i < handles.size(); ++i) {
42 MutableEntry entry(trans, GET_BY_HANDLE, handles[i]);
43 const sync_pb::EntitySpecifics& specifics = entry.GetSpecifics();
44 // Ignore types that don't need encryption or entries that are already
45 // encrypted.
46 if (!SpecificsNeedsEncryption(encrypted_types, specifics))
47 continue;
48 if (!UpdateEntryWithEncryption(trans, specifics, &entry))
49 return false;
50 }
51 return true;
52 }
53
54 bool VerifyUnsyncedChangesAreEncrypted(
55 BaseTransaction* const trans,
56 ModelTypeSet encrypted_types) {
57 std::vector<int64_t> handles;
58 GetUnsyncedEntries(trans, &handles);
59 for (size_t i = 0; i < handles.size(); ++i) {
60 Entry entry(trans, GET_BY_HANDLE, handles[i]);
61 if (!entry.good()) {
62 NOTREACHED();
63 return false;
64 }
65 if (EntryNeedsEncryption(encrypted_types, entry))
66 return false;
67 }
68 return true;
69 }
70
71 bool EntryNeedsEncryption(ModelTypeSet encrypted_types,
72 const Entry& entry) {
73 if (!entry.GetUniqueServerTag().empty())
74 return false; // We don't encrypt unique server nodes.
75 ModelType type = entry.GetModelType();
76 if (type == PASSWORDS || IsControlType(type))
77 return false;
78 // Checking NON_UNIQUE_NAME is not necessary for the correctness of encrypting
79 // the data, nor for determining if data is encrypted. We simply ensure it has
80 // been overwritten to avoid any possible leaks of sensitive data.
81 return SpecificsNeedsEncryption(encrypted_types, entry.GetSpecifics()) ||
82 (encrypted_types.Has(type) &&
83 entry.GetNonUniqueName() != kEncryptedString);
84 }
85
86 bool SpecificsNeedsEncryption(ModelTypeSet encrypted_types,
87 const sync_pb::EntitySpecifics& specifics) {
88 const ModelType type = GetModelTypeFromSpecifics(specifics);
89 if (type == PASSWORDS || IsControlType(type))
90 return false; // These types have their own encryption schemes.
91 if (!encrypted_types.Has(type))
92 return false; // This type does not require encryption
93 return !specifics.has_encrypted();
94 }
95
96 // Mainly for testing.
97 bool VerifyDataTypeEncryptionForTest(
98 BaseTransaction* const trans,
99 ModelType type,
100 bool is_encrypted) {
101 Cryptographer* cryptographer = trans->directory()->GetCryptographer(trans);
102 if (type == PASSWORDS || IsControlType(type)) {
103 NOTREACHED();
104 return true;
105 }
106 Entry type_root(trans, GET_TYPE_ROOT, type);
107 if (!type_root.good()) {
108 NOTREACHED();
109 return false;
110 }
111
112 std::queue<Id> to_visit;
113 Id id_string = type_root.GetFirstChildId();
114 to_visit.push(id_string);
115 while (!to_visit.empty()) {
116 id_string = to_visit.front();
117 to_visit.pop();
118 if (id_string.IsNull())
119 continue;
120
121 Entry child(trans, GET_BY_ID, id_string);
122 if (!child.good()) {
123 NOTREACHED();
124 return false;
125 }
126 if (child.GetIsDir()) {
127 Id child_id_string = child.GetFirstChildId();
128 // Traverse the children.
129 to_visit.push(child_id_string);
130 }
131 const sync_pb::EntitySpecifics& specifics = child.GetSpecifics();
132 DCHECK_EQ(type, child.GetModelType());
133 DCHECK_EQ(type, GetModelTypeFromSpecifics(specifics));
134 // We don't encrypt the server's permanent items.
135 if (child.GetUniqueServerTag().empty()) {
136 if (specifics.has_encrypted() != is_encrypted)
137 return false;
138 if (specifics.has_encrypted()) {
139 if (child.GetNonUniqueName() != kEncryptedString)
140 return false;
141 if (!cryptographer->CanDecryptUsingDefaultKey(specifics.encrypted()))
142 return false;
143 }
144 }
145 // Push the successor.
146 to_visit.push(child.GetSuccessorId());
147 }
148 return true;
149 }
150
151 bool UpdateEntryWithEncryption(
152 BaseTransaction* const trans,
153 const sync_pb::EntitySpecifics& new_specifics,
154 syncable::MutableEntry* entry) {
155 NigoriHandler* nigori_handler = trans->directory()->GetNigoriHandler();
156 Cryptographer* cryptographer = trans->directory()->GetCryptographer(trans);
157 ModelType type = GetModelTypeFromSpecifics(new_specifics);
158 DCHECK_GE(type, FIRST_REAL_MODEL_TYPE);
159 const sync_pb::EntitySpecifics& old_specifics = entry->GetSpecifics();
160 const ModelTypeSet encrypted_types =
161 nigori_handler?
162 nigori_handler->GetEncryptedTypes(trans) : ModelTypeSet();
163 // It's possible the nigori lost the set of encrypted types. If the current
164 // specifics are already encrypted, we want to ensure we continue encrypting.
165 bool was_encrypted = old_specifics.has_encrypted();
166 sync_pb::EntitySpecifics generated_specifics;
167 if (new_specifics.has_encrypted()) {
168 NOTREACHED() << "New specifics already has an encrypted blob.";
169 return false;
170 }
171 if ((!SpecificsNeedsEncryption(encrypted_types, new_specifics) &&
172 !was_encrypted) ||
173 !cryptographer || !cryptographer->is_initialized()) {
174 // No encryption required or we are unable to encrypt.
175 generated_specifics.CopyFrom(new_specifics);
176 } else {
177 // Encrypt new_specifics into generated_specifics.
178 if (VLOG_IS_ON(2)) {
179 std::unique_ptr<base::DictionaryValue> value(entry->ToValue(NULL));
180 std::string info;
181 base::JSONWriter::WriteWithOptions(
182 *value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &info);
183 DVLOG(2) << "Encrypting specifics of type "
184 << 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 (GetModelTypeFromSpecifics(old_specifics) == type &&
193 was_encrypted) {
194 generated_specifics.CopyFrom(old_specifics);
195 } else {
196 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 << 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->GetNonUniqueName() != 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 " << 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->PutNonUniqueName(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 == BOOKMARKS) {
229 sync_pb::BookmarkSpecifics* bookmark_specifics =
230 generated_specifics.mutable_bookmark();
231 if (!entry->GetIsDir())
232 bookmark_specifics->set_url(kEncryptedString);
233 bookmark_specifics->set_title(kEncryptedString);
234 }
235 }
236 entry->PutSpecifics(generated_specifics);
237 DVLOG(1) << "Overwriting specifics of type "
238 << ModelTypeToString(type)
239 << " and marking for syncing.";
240 syncable::MarkForSyncing(entry);
241 return true;
242 }
243
244 void UpdateNigoriFromEncryptedTypes(ModelTypeSet encrypted_types,
245 bool encrypt_everything,
246 sync_pb::NigoriSpecifics* nigori) {
247 nigori->set_encrypt_everything(encrypt_everything);
248 static_assert(37 == MODEL_TYPE_COUNT, "update encrypted types");
249 nigori->set_encrypt_bookmarks(
250 encrypted_types.Has(BOOKMARKS));
251 nigori->set_encrypt_preferences(
252 encrypted_types.Has(PREFERENCES));
253 nigori->set_encrypt_autofill_profile(
254 encrypted_types.Has(AUTOFILL_PROFILE));
255 nigori->set_encrypt_autofill(encrypted_types.Has(AUTOFILL));
256 nigori->set_encrypt_autofill_wallet_metadata(
257 encrypted_types.Has(AUTOFILL_WALLET_METADATA));
258 nigori->set_encrypt_themes(encrypted_types.Has(THEMES));
259 nigori->set_encrypt_typed_urls(
260 encrypted_types.Has(TYPED_URLS));
261 nigori->set_encrypt_extension_settings(
262 encrypted_types.Has(EXTENSION_SETTINGS));
263 nigori->set_encrypt_extensions(
264 encrypted_types.Has(EXTENSIONS));
265 nigori->set_encrypt_search_engines(
266 encrypted_types.Has(SEARCH_ENGINES));
267 nigori->set_encrypt_sessions(encrypted_types.Has(SESSIONS));
268 nigori->set_encrypt_app_settings(
269 encrypted_types.Has(APP_SETTINGS));
270 nigori->set_encrypt_apps(encrypted_types.Has(APPS));
271 nigori->set_encrypt_app_notifications(
272 encrypted_types.Has(APP_NOTIFICATIONS));
273 nigori->set_encrypt_dictionary(encrypted_types.Has(DICTIONARY));
274 nigori->set_encrypt_favicon_images(encrypted_types.Has(FAVICON_IMAGES));
275 nigori->set_encrypt_favicon_tracking(encrypted_types.Has(FAVICON_TRACKING));
276 nigori->set_encrypt_articles(encrypted_types.Has(ARTICLES));
277 nigori->set_encrypt_app_list(encrypted_types.Has(APP_LIST));
278 nigori->set_encrypt_arc_package(encrypted_types.Has(ARC_PACKAGE));
279 }
280
281 ModelTypeSet GetEncryptedTypesFromNigori(
282 const sync_pb::NigoriSpecifics& nigori) {
283 if (nigori.encrypt_everything())
284 return ModelTypeSet::All();
285
286 ModelTypeSet encrypted_types;
287 static_assert(37 == MODEL_TYPE_COUNT, "update encrypted types");
288 if (nigori.encrypt_bookmarks())
289 encrypted_types.Put(BOOKMARKS);
290 if (nigori.encrypt_preferences())
291 encrypted_types.Put(PREFERENCES);
292 if (nigori.encrypt_autofill_profile())
293 encrypted_types.Put(AUTOFILL_PROFILE);
294 if (nigori.encrypt_autofill())
295 encrypted_types.Put(AUTOFILL);
296 if (nigori.encrypt_autofill_wallet_metadata())
297 encrypted_types.Put(AUTOFILL_WALLET_METADATA);
298 if (nigori.encrypt_themes())
299 encrypted_types.Put(THEMES);
300 if (nigori.encrypt_typed_urls())
301 encrypted_types.Put(TYPED_URLS);
302 if (nigori.encrypt_extension_settings())
303 encrypted_types.Put(EXTENSION_SETTINGS);
304 if (nigori.encrypt_extensions())
305 encrypted_types.Put(EXTENSIONS);
306 if (nigori.encrypt_search_engines())
307 encrypted_types.Put(SEARCH_ENGINES);
308 if (nigori.encrypt_sessions())
309 encrypted_types.Put(SESSIONS);
310 if (nigori.encrypt_app_settings())
311 encrypted_types.Put(APP_SETTINGS);
312 if (nigori.encrypt_apps())
313 encrypted_types.Put(APPS);
314 if (nigori.encrypt_app_notifications())
315 encrypted_types.Put(APP_NOTIFICATIONS);
316 if (nigori.encrypt_dictionary())
317 encrypted_types.Put(DICTIONARY);
318 if (nigori.encrypt_favicon_images())
319 encrypted_types.Put(FAVICON_IMAGES);
320 if (nigori.encrypt_favicon_tracking())
321 encrypted_types.Put(FAVICON_TRACKING);
322 if (nigori.encrypt_articles())
323 encrypted_types.Put(ARTICLES);
324 if (nigori.encrypt_app_list())
325 encrypted_types.Put(APP_LIST);
326 if (nigori.encrypt_arc_package())
327 encrypted_types.Put(ARC_PACKAGE);
328 return encrypted_types;
329 }
330
331 } // namespace syncable
332 } // namespace syncer
OLDNEW
« no previous file with comments | « sync/syncable/nigori_util.h ('k') | sync/syncable/nigori_util_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698