OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 "chrome/browser/sync/engine/nigori_util.h" | |
6 | |
7 #include <queue> | |
8 #include <string> | |
9 #include <vector> | |
10 | |
11 #include "chrome/browser/sync/engine/syncer_util.h" | |
12 #include "chrome/browser/sync/internal_api/write_node.h" | |
13 #include "chrome/browser/sync/syncable/syncable.h" | |
14 #include "chrome/browser/sync/util/cryptographer.h" | |
15 | |
16 namespace syncable { | |
17 | |
18 bool ProcessUnsyncedChangesForEncryption( | |
19 WriteTransaction* const trans, | |
20 browser_sync::Cryptographer* cryptographer) { | |
21 DCHECK(cryptographer->is_ready()); | |
22 syncable::ModelTypeSet encrypted_types = cryptographer->GetEncryptedTypes(); | |
23 | |
24 // Get list of all datatypes with unsynced changes. It's possible that our | |
25 // local changes need to be encrypted if encryption for that datatype was | |
26 // just turned on (and vice versa). This should never affect passwords. | |
27 std::vector<int64> handles; | |
28 browser_sync::SyncerUtil::GetUnsyncedEntries(trans, &handles); | |
29 for (size_t i = 0; i < handles.size(); ++i) { | |
30 MutableEntry entry(trans, GET_BY_HANDLE, handles[i]); | |
31 if (!sync_api::WriteNode::UpdateEntryWithEncryption(cryptographer, | |
32 entry.Get(SPECIFICS), | |
33 &entry)) { | |
34 NOTREACHED(); | |
35 return false; | |
36 } | |
37 } | |
38 return true; | |
39 } | |
40 | |
41 bool VerifyUnsyncedChangesAreEncrypted( | |
42 BaseTransaction* const trans, | |
43 const ModelTypeSet& encrypted_types) { | |
44 std::vector<int64> handles; | |
45 browser_sync::SyncerUtil::GetUnsyncedEntries(trans, &handles); | |
46 for (size_t i = 0; i < handles.size(); ++i) { | |
47 Entry entry(trans, GET_BY_HANDLE, handles[i]); | |
48 if (!entry.good()) { | |
49 NOTREACHED(); | |
50 return false; | |
51 } | |
52 if (EntryNeedsEncryption(encrypted_types, entry)) | |
53 return false; | |
54 } | |
55 return true; | |
56 } | |
57 | |
58 bool EntryNeedsEncryption(const ModelTypeSet& encrypted_types, | |
59 const Entry& entry) { | |
60 if (!entry.Get(UNIQUE_SERVER_TAG).empty()) | |
61 return false; // We don't encrypt unique server nodes. | |
62 syncable::ModelType type = entry.GetModelType(); | |
63 if (type == PASSWORDS || type == NIGORI) | |
64 return false; | |
65 // Checking NON_UNIQUE_NAME is not necessary for the correctness of encrypting | |
66 // the data, nor for determining if data is encrypted. We simply ensure it has | |
67 // been overwritten to avoid any possible leaks of sensitive data. | |
68 return SpecificsNeedsEncryption(encrypted_types, entry.Get(SPECIFICS)) || | |
69 (encrypted_types.count(type) > 0 && | |
70 entry.Get(NON_UNIQUE_NAME) != kEncryptedString); | |
71 } | |
72 | |
73 bool SpecificsNeedsEncryption(const ModelTypeSet& encrypted_types, | |
74 const sync_pb::EntitySpecifics& specifics) { | |
75 ModelType type = GetModelTypeFromSpecifics(specifics); | |
76 if (type == PASSWORDS || type == NIGORI) | |
77 return false; // These types have their own encryption schemes. | |
78 if (encrypted_types.count(type) == 0) | |
79 return false; // This type does not require encryption | |
80 return !specifics.has_encrypted(); | |
81 } | |
82 | |
83 // Mainly for testing. | |
84 bool VerifyDataTypeEncryptionForTest( | |
85 BaseTransaction* const trans, | |
86 browser_sync::Cryptographer* cryptographer, | |
87 ModelType type, | |
88 bool is_encrypted) { | |
89 if (type == PASSWORDS || type == NIGORI) { | |
90 NOTREACHED(); | |
91 return true; | |
92 } | |
93 std::string type_tag = ModelTypeToRootTag(type); | |
94 Entry type_root(trans, GET_BY_SERVER_TAG, type_tag); | |
95 if (!type_root.good()) { | |
96 NOTREACHED(); | |
97 return false; | |
98 } | |
99 | |
100 std::queue<Id> to_visit; | |
101 Id id_string; | |
102 if (!trans->directory()->GetFirstChildId( | |
103 trans, type_root.Get(ID), &id_string)) { | |
104 NOTREACHED(); | |
105 return false; | |
106 } | |
107 to_visit.push(id_string); | |
108 while (!to_visit.empty()) { | |
109 id_string = to_visit.front(); | |
110 to_visit.pop(); | |
111 if (id_string.IsRoot()) | |
112 continue; | |
113 | |
114 Entry child(trans, GET_BY_ID, id_string); | |
115 if (!child.good()) { | |
116 NOTREACHED(); | |
117 return false; | |
118 } | |
119 if (child.Get(IS_DIR)) { | |
120 Id child_id_string; | |
121 if (!trans->directory()->GetFirstChildId( | |
122 trans, child.Get(ID), &child_id_string)) { | |
123 NOTREACHED(); | |
124 return false; | |
125 } | |
126 // Traverse the children. | |
127 to_visit.push(child_id_string); | |
128 } | |
129 const sync_pb::EntitySpecifics& specifics = child.Get(SPECIFICS); | |
130 DCHECK_EQ(type, child.GetModelType()); | |
131 DCHECK_EQ(type, GetModelTypeFromSpecifics(specifics)); | |
132 // We don't encrypt the server's permanent items. | |
133 if (child.Get(UNIQUE_SERVER_TAG).empty()) { | |
134 if (specifics.has_encrypted() != is_encrypted) | |
135 return false; | |
136 if (specifics.has_encrypted()) { | |
137 if (child.Get(NON_UNIQUE_NAME) != kEncryptedString) | |
138 return false; | |
139 if (!cryptographer->CanDecryptUsingDefaultKey(specifics.encrypted())) | |
140 return false; | |
141 } | |
142 } | |
143 // Push the successor. | |
144 to_visit.push(child.Get(NEXT_ID)); | |
145 } | |
146 return true; | |
147 } | |
148 | |
149 } // namespace syncable | |
OLD | NEW |