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

Side by Side Diff: sync/util/cryptographer.cc

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

Powered by Google App Engine
This is Rietveld 408576698