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/util/cryptographer.h" | |
6 | |
7 #include <stddef.h> | |
8 #include <algorithm> | |
9 #include <utility> | |
10 | |
11 #include "base/base64.h" | |
12 #include "base/logging.h" | |
13 #include "sync/protocol/nigori_specifics.pb.h" | |
14 #include "sync/util/encryptor.h" | |
15 | |
16 namespace syncer { | |
17 | |
18 const char kNigoriTag[] = "google_chrome_nigori"; | |
19 | |
20 // We name a particular Nigori instance (ie. a triplet consisting of a hostname, | |
21 // a username, and a password) by calling Permute on this string. Since the | |
22 // output of Permute is always the same for a given triplet, clients will always | |
23 // assign the same name to a particular triplet. | |
24 const char kNigoriKeyName[] = "nigori-key"; | |
25 | |
26 Cryptographer::Cryptographer(Encryptor* encryptor) | |
27 : encryptor_(encryptor) { | |
28 DCHECK(encryptor); | |
29 } | |
30 | |
31 Cryptographer::Cryptographer(const Cryptographer& other) | |
32 : encryptor_(other.encryptor_), | |
33 default_nigori_name_(other.default_nigori_name_) { | |
34 for (NigoriMap::const_iterator it = other.nigoris_.begin(); | |
35 it != other.nigoris_.end(); | |
36 ++it) { | |
37 std::string user_key, encryption_key, mac_key; | |
38 it->second->ExportKeys(&user_key, &encryption_key, &mac_key); | |
39 linked_ptr<Nigori> nigori_copy(new Nigori()); | |
40 nigori_copy->InitByImport(user_key, encryption_key, mac_key); | |
41 nigoris_.insert(std::make_pair(it->first, nigori_copy)); | |
42 } | |
43 | |
44 if (other.pending_keys_) { | |
45 pending_keys_.reset(new sync_pb::EncryptedData(*(other.pending_keys_))); | |
46 } | |
47 } | |
48 | |
49 Cryptographer::~Cryptographer() {} | |
50 | |
51 | |
52 void Cryptographer::Bootstrap(const std::string& restored_bootstrap_token) { | |
53 if (is_initialized()) { | |
54 NOTREACHED(); | |
55 return; | |
56 } | |
57 | |
58 std::string serialized_nigori_key = | |
59 UnpackBootstrapToken(restored_bootstrap_token); | |
60 if (serialized_nigori_key.empty()) | |
61 return; | |
62 ImportNigoriKey(serialized_nigori_key); | |
63 } | |
64 | |
65 bool Cryptographer::CanDecrypt(const sync_pb::EncryptedData& data) const { | |
66 return nigoris_.end() != nigoris_.find(data.key_name()); | |
67 } | |
68 | |
69 bool Cryptographer::CanDecryptUsingDefaultKey( | |
70 const sync_pb::EncryptedData& data) const { | |
71 return !default_nigori_name_.empty() && | |
72 data.key_name() == default_nigori_name_; | |
73 } | |
74 | |
75 bool Cryptographer::Encrypt( | |
76 const ::google::protobuf::MessageLite& message, | |
77 sync_pb::EncryptedData* encrypted) const { | |
78 DCHECK(encrypted); | |
79 if (default_nigori_name_.empty()) { | |
80 LOG(ERROR) << "Cryptographer not ready, failed to encrypt."; | |
81 return false; | |
82 } | |
83 | |
84 std::string serialized; | |
85 if (!message.SerializeToString(&serialized)) { | |
86 LOG(ERROR) << "Message is invalid/missing a required field."; | |
87 return false; | |
88 } | |
89 | |
90 return EncryptString(serialized, encrypted); | |
91 } | |
92 | |
93 bool Cryptographer::EncryptString( | |
94 const std::string& serialized, | |
95 sync_pb::EncryptedData* encrypted) const { | |
96 if (CanDecryptUsingDefaultKey(*encrypted)) { | |
97 const std::string& original_serialized = DecryptToString(*encrypted); | |
98 if (original_serialized == serialized) { | |
99 DVLOG(2) << "Re-encryption unnecessary, encrypted data already matches."; | |
100 return true; | |
101 } | |
102 } | |
103 | |
104 NigoriMap::const_iterator default_nigori = | |
105 nigoris_.find(default_nigori_name_); | |
106 if (default_nigori == nigoris_.end()) { | |
107 LOG(ERROR) << "Corrupt default key."; | |
108 return false; | |
109 } | |
110 | |
111 encrypted->set_key_name(default_nigori_name_); | |
112 if (!default_nigori->second->Encrypt(serialized, | |
113 encrypted->mutable_blob())) { | |
114 LOG(ERROR) << "Failed to encrypt data."; | |
115 return false; | |
116 } | |
117 return true; | |
118 } | |
119 | |
120 bool Cryptographer::Decrypt(const sync_pb::EncryptedData& encrypted, | |
121 ::google::protobuf::MessageLite* message) const { | |
122 DCHECK(message); | |
123 std::string plaintext = DecryptToString(encrypted); | |
124 return message->ParseFromString(plaintext); | |
125 } | |
126 | |
127 std::string Cryptographer::DecryptToString( | |
128 const sync_pb::EncryptedData& encrypted) const { | |
129 NigoriMap::const_iterator it = nigoris_.find(encrypted.key_name()); | |
130 if (nigoris_.end() == it) { | |
131 // The key used to encrypt the blob is not part of the set of installed | |
132 // nigoris. | |
133 LOG(ERROR) << "Cannot decrypt message"; | |
134 return std::string(); | |
135 } | |
136 | |
137 std::string plaintext; | |
138 if (!it->second->Decrypt(encrypted.blob(), &plaintext)) { | |
139 return std::string(); | |
140 } | |
141 | |
142 return plaintext; | |
143 } | |
144 | |
145 bool Cryptographer::GetKeys(sync_pb::EncryptedData* encrypted) const { | |
146 DCHECK(encrypted); | |
147 DCHECK(!nigoris_.empty()); | |
148 | |
149 // Create a bag of all the Nigori parameters we know about. | |
150 sync_pb::NigoriKeyBag bag; | |
151 for (NigoriMap::const_iterator it = nigoris_.begin(); it != nigoris_.end(); | |
152 ++it) { | |
153 const Nigori& nigori = *it->second; | |
154 sync_pb::NigoriKey* key = bag.add_key(); | |
155 key->set_name(it->first); | |
156 nigori.ExportKeys(key->mutable_user_key(), | |
157 key->mutable_encryption_key(), | |
158 key->mutable_mac_key()); | |
159 } | |
160 | |
161 // Encrypt the bag with the default Nigori. | |
162 return Encrypt(bag, encrypted); | |
163 } | |
164 | |
165 bool Cryptographer::AddKey(const KeyParams& params) { | |
166 // Create the new Nigori and make it the default encryptor. | |
167 std::unique_ptr<Nigori> nigori(new Nigori); | |
168 if (!nigori->InitByDerivation(params.hostname, | |
169 params.username, | |
170 params.password)) { | |
171 NOTREACHED(); // Invalid username or password. | |
172 return false; | |
173 } | |
174 return AddKeyImpl(std::move(nigori), true); | |
175 } | |
176 | |
177 bool Cryptographer::AddNonDefaultKey(const KeyParams& params) { | |
178 DCHECK(is_initialized()); | |
179 // Create the new Nigori and add it to the keybag. | |
180 std::unique_ptr<Nigori> nigori(new Nigori); | |
181 if (!nigori->InitByDerivation(params.hostname, | |
182 params.username, | |
183 params.password)) { | |
184 NOTREACHED(); // Invalid username or password. | |
185 return false; | |
186 } | |
187 return AddKeyImpl(std::move(nigori), false); | |
188 } | |
189 | |
190 bool Cryptographer::AddKeyFromBootstrapToken( | |
191 const std::string& restored_bootstrap_token) { | |
192 // Create the new Nigori and make it the default encryptor. | |
193 std::string serialized_nigori_key = UnpackBootstrapToken( | |
194 restored_bootstrap_token); | |
195 return ImportNigoriKey(serialized_nigori_key); | |
196 } | |
197 | |
198 bool Cryptographer::AddKeyImpl(std::unique_ptr<Nigori> initialized_nigori, | |
199 bool set_as_default) { | |
200 std::string name; | |
201 if (!initialized_nigori->Permute(Nigori::Password, kNigoriKeyName, &name)) { | |
202 NOTREACHED(); | |
203 return false; | |
204 } | |
205 | |
206 nigoris_[name] = make_linked_ptr(initialized_nigori.release()); | |
207 | |
208 // Check if the key we just added can decrypt the pending keys and add them | |
209 // too if so. | |
210 if (pending_keys_.get() && CanDecrypt(*pending_keys_)) { | |
211 sync_pb::NigoriKeyBag pending_bag; | |
212 Decrypt(*pending_keys_, &pending_bag); | |
213 InstallKeyBag(pending_bag); | |
214 SetDefaultKey(pending_keys_->key_name()); | |
215 pending_keys_.reset(); | |
216 } | |
217 | |
218 // The just-added key takes priority over the pending keys as default. | |
219 if (set_as_default) SetDefaultKey(name); | |
220 return true; | |
221 } | |
222 | |
223 void Cryptographer::InstallKeys(const sync_pb::EncryptedData& encrypted) { | |
224 DCHECK(CanDecrypt(encrypted)); | |
225 | |
226 sync_pb::NigoriKeyBag bag; | |
227 if (!Decrypt(encrypted, &bag)) | |
228 return; | |
229 InstallKeyBag(bag); | |
230 } | |
231 | |
232 void Cryptographer::SetDefaultKey(const std::string& key_name) { | |
233 DCHECK(nigoris_.end() != nigoris_.find(key_name)); | |
234 default_nigori_name_ = key_name; | |
235 } | |
236 | |
237 void Cryptographer::SetPendingKeys(const sync_pb::EncryptedData& encrypted) { | |
238 DCHECK(!CanDecrypt(encrypted)); | |
239 DCHECK(!encrypted.blob().empty()); | |
240 pending_keys_.reset(new sync_pb::EncryptedData(encrypted)); | |
241 } | |
242 | |
243 const sync_pb::EncryptedData& Cryptographer::GetPendingKeys() const { | |
244 DCHECK(has_pending_keys()); | |
245 return *(pending_keys_.get()); | |
246 } | |
247 | |
248 bool Cryptographer::DecryptPendingKeys(const KeyParams& params) { | |
249 Nigori nigori; | |
250 if (!nigori.InitByDerivation(params.hostname, | |
251 params.username, | |
252 params.password)) { | |
253 NOTREACHED(); | |
254 return false; | |
255 } | |
256 | |
257 std::string plaintext; | |
258 if (!nigori.Decrypt(pending_keys_->blob(), &plaintext)) | |
259 return false; | |
260 | |
261 sync_pb::NigoriKeyBag bag; | |
262 if (!bag.ParseFromString(plaintext)) { | |
263 NOTREACHED(); | |
264 return false; | |
265 } | |
266 InstallKeyBag(bag); | |
267 const std::string& new_default_key_name = pending_keys_->key_name(); | |
268 SetDefaultKey(new_default_key_name); | |
269 pending_keys_.reset(); | |
270 return true; | |
271 } | |
272 | |
273 bool Cryptographer::GetBootstrapToken(std::string* token) const { | |
274 DCHECK(token); | |
275 std::string unencrypted_token = GetDefaultNigoriKeyData(); | |
276 if (unencrypted_token.empty()) | |
277 return false; | |
278 | |
279 std::string encrypted_token; | |
280 if (!encryptor_->EncryptString(unencrypted_token, &encrypted_token)) { | |
281 return false; | |
282 } | |
283 | |
284 base::Base64Encode(encrypted_token, token); | |
285 | |
286 return true; | |
287 } | |
288 | |
289 std::string Cryptographer::UnpackBootstrapToken( | |
290 const std::string& token) const { | |
291 if (token.empty()) | |
292 return std::string(); | |
293 | |
294 std::string encrypted_data; | |
295 if (!base::Base64Decode(token, &encrypted_data)) { | |
296 DLOG(WARNING) << "Could not decode token."; | |
297 return std::string(); | |
298 } | |
299 | |
300 std::string unencrypted_token; | |
301 if (!encryptor_->DecryptString(encrypted_data, &unencrypted_token)) { | |
302 DLOG(WARNING) << "Decryption of bootstrap token failed."; | |
303 return std::string(); | |
304 } | |
305 return unencrypted_token; | |
306 } | |
307 | |
308 void Cryptographer::InstallKeyBag(const sync_pb::NigoriKeyBag& bag) { | |
309 int key_size = bag.key_size(); | |
310 for (int i = 0; i < key_size; ++i) { | |
311 const sync_pb::NigoriKey key = bag.key(i); | |
312 // Only use this key if we don't already know about it. | |
313 if (nigoris_.end() == nigoris_.find(key.name())) { | |
314 std::unique_ptr<Nigori> new_nigori(new Nigori); | |
315 if (!new_nigori->InitByImport(key.user_key(), | |
316 key.encryption_key(), | |
317 key.mac_key())) { | |
318 NOTREACHED(); | |
319 continue; | |
320 } | |
321 nigoris_[key.name()] = make_linked_ptr(new_nigori.release()); | |
322 } | |
323 } | |
324 } | |
325 | |
326 bool Cryptographer::KeybagIsStale( | |
327 const sync_pb::EncryptedData& encrypted_bag) const { | |
328 if (!is_ready()) | |
329 return false; | |
330 if (encrypted_bag.blob().empty()) | |
331 return true; | |
332 if (!CanDecrypt(encrypted_bag)) | |
333 return false; | |
334 if (!CanDecryptUsingDefaultKey(encrypted_bag)) | |
335 return true; | |
336 sync_pb::NigoriKeyBag bag; | |
337 if (!Decrypt(encrypted_bag, &bag)) { | |
338 LOG(ERROR) << "Failed to decrypt keybag for stale check. " | |
339 << "Assuming keybag is corrupted."; | |
340 return true; | |
341 } | |
342 if (static_cast<size_t>(bag.key_size()) < nigoris_.size()) | |
343 return true; | |
344 return false; | |
345 } | |
346 | |
347 std::string Cryptographer::GetDefaultNigoriKeyName() const { | |
348 return default_nigori_name_; | |
349 } | |
350 | |
351 std::string Cryptographer::GetDefaultNigoriKeyData() const { | |
352 if (!is_initialized()) | |
353 return std::string(); | |
354 NigoriMap::const_iterator iter = nigoris_.find(default_nigori_name_); | |
355 if (iter == nigoris_.end()) | |
356 return std::string(); | |
357 sync_pb::NigoriKey key; | |
358 if (!iter->second->ExportKeys(key.mutable_user_key(), | |
359 key.mutable_encryption_key(), | |
360 key.mutable_mac_key())) | |
361 return std::string(); | |
362 return key.SerializeAsString(); | |
363 } | |
364 | |
365 bool Cryptographer::ImportNigoriKey(const std::string& serialized_nigori_key) { | |
366 if (serialized_nigori_key.empty()) | |
367 return false; | |
368 | |
369 sync_pb::NigoriKey key; | |
370 if (!key.ParseFromString(serialized_nigori_key)) | |
371 return false; | |
372 | |
373 std::unique_ptr<Nigori> nigori(new Nigori); | |
374 if (!nigori->InitByImport(key.user_key(), key.encryption_key(), | |
375 key.mac_key())) { | |
376 NOTREACHED(); | |
377 return false; | |
378 } | |
379 | |
380 if (!AddKeyImpl(std::move(nigori), true)) | |
381 return false; | |
382 return true; | |
383 } | |
384 | |
385 } // namespace syncer | |
OLD | NEW |