| 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 "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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |