| 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 "base/base64.h" | |
| 6 #include "chrome/browser/sync/util/cryptographer.h" | |
| 7 #include "chrome/browser/password_manager/encryptor.h" | |
| 8 | |
| 9 namespace browser_sync { | |
| 10 | |
| 11 const char kNigoriTag[] = "google_chrome_nigori"; | |
| 12 | |
| 13 // We name a particular Nigori instance (ie. a triplet consisting of a hostname, | |
| 14 // a username, and a password) by calling Permute on this string. Since the | |
| 15 // output of Permute is always the same for a given triplet, clients will always | |
| 16 // assign the same name to a particular triplet. | |
| 17 const char kNigoriKeyName[] = "nigori-key"; | |
| 18 | |
| 19 Cryptographer::Observer::~Observer() {} | |
| 20 | |
| 21 Cryptographer::Cryptographer() | |
| 22 : default_nigori_(NULL), | |
| 23 encrypt_everything_(false) { | |
| 24 syncable::ModelTypeSet sensitive_types = SensitiveTypes(); | |
| 25 encrypted_types_.insert(sensitive_types.begin(), sensitive_types.end()); | |
| 26 } | |
| 27 | |
| 28 Cryptographer::~Cryptographer() {} | |
| 29 | |
| 30 void Cryptographer::AddObserver(Observer* observer) { | |
| 31 observers_.AddObserver(observer); | |
| 32 } | |
| 33 | |
| 34 void Cryptographer::RemoveObserver(Observer* observer) { | |
| 35 observers_.RemoveObserver(observer); | |
| 36 } | |
| 37 | |
| 38 void Cryptographer::Bootstrap(const std::string& restored_bootstrap_token) { | |
| 39 if (is_initialized()) { | |
| 40 NOTREACHED(); | |
| 41 return; | |
| 42 } | |
| 43 | |
| 44 scoped_ptr<Nigori> nigori(UnpackBootstrapToken(restored_bootstrap_token)); | |
| 45 if (nigori.get()) | |
| 46 AddKeyImpl(nigori.release()); | |
| 47 } | |
| 48 | |
| 49 bool Cryptographer::CanDecrypt(const sync_pb::EncryptedData& data) const { | |
| 50 return nigoris_.end() != nigoris_.find(data.key_name()); | |
| 51 } | |
| 52 | |
| 53 bool Cryptographer::CanDecryptUsingDefaultKey( | |
| 54 const sync_pb::EncryptedData& data) const { | |
| 55 return default_nigori_ && (data.key_name() == default_nigori_->first); | |
| 56 } | |
| 57 | |
| 58 bool Cryptographer::Encrypt(const ::google::protobuf::MessageLite& message, | |
| 59 sync_pb::EncryptedData* encrypted) const { | |
| 60 if (!encrypted || !default_nigori_) { | |
| 61 LOG(ERROR) << "Cryptographer not ready, failed to encrypt."; | |
| 62 return false; | |
| 63 } | |
| 64 | |
| 65 std::string serialized; | |
| 66 if (!message.SerializeToString(&serialized)) { | |
| 67 LOG(ERROR) << "Message is invalid/missing a required field."; | |
| 68 return false; | |
| 69 } | |
| 70 | |
| 71 encrypted->set_key_name(default_nigori_->first); | |
| 72 if (!default_nigori_->second->Encrypt(serialized, | |
| 73 encrypted->mutable_blob())) { | |
| 74 LOG(ERROR) << "Failed to encrypt data."; | |
| 75 return false; | |
| 76 } | |
| 77 return true; | |
| 78 } | |
| 79 | |
| 80 bool Cryptographer::Decrypt(const sync_pb::EncryptedData& encrypted, | |
| 81 ::google::protobuf::MessageLite* message) const { | |
| 82 DCHECK(message); | |
| 83 std::string plaintext = DecryptToString(encrypted); | |
| 84 return message->ParseFromString(plaintext); | |
| 85 } | |
| 86 | |
| 87 std::string Cryptographer::DecryptToString( | |
| 88 const sync_pb::EncryptedData& encrypted) const { | |
| 89 NigoriMap::const_iterator it = nigoris_.find(encrypted.key_name()); | |
| 90 if (nigoris_.end() == it) { | |
| 91 NOTREACHED() << "Cannot decrypt message"; | |
| 92 return std::string(""); // Caller should have called CanDecrypt(encrypt). | |
| 93 } | |
| 94 | |
| 95 std::string plaintext; | |
| 96 if (!it->second->Decrypt(encrypted.blob(), &plaintext)) { | |
| 97 return std::string(""); | |
| 98 } | |
| 99 | |
| 100 return plaintext; | |
| 101 } | |
| 102 | |
| 103 bool Cryptographer::GetKeys(sync_pb::EncryptedData* encrypted) const { | |
| 104 DCHECK(encrypted); | |
| 105 DCHECK(!nigoris_.empty()); | |
| 106 | |
| 107 // Create a bag of all the Nigori parameters we know about. | |
| 108 sync_pb::NigoriKeyBag bag; | |
| 109 for (NigoriMap::const_iterator it = nigoris_.begin(); it != nigoris_.end(); | |
| 110 ++it) { | |
| 111 const Nigori& nigori = *it->second; | |
| 112 sync_pb::NigoriKey* key = bag.add_key(); | |
| 113 key->set_name(it->first); | |
| 114 nigori.ExportKeys(key->mutable_user_key(), | |
| 115 key->mutable_encryption_key(), | |
| 116 key->mutable_mac_key()); | |
| 117 } | |
| 118 | |
| 119 // Encrypt the bag with the default Nigori. | |
| 120 return Encrypt(bag, encrypted); | |
| 121 } | |
| 122 | |
| 123 bool Cryptographer::AddKey(const KeyParams& params) { | |
| 124 DCHECK(NULL == pending_keys_.get()); | |
| 125 | |
| 126 // Create the new Nigori and make it the default encryptor. | |
| 127 scoped_ptr<Nigori> nigori(new Nigori); | |
| 128 if (!nigori->InitByDerivation(params.hostname, | |
| 129 params.username, | |
| 130 params.password)) { | |
| 131 NOTREACHED(); // Invalid username or password. | |
| 132 return false; | |
| 133 } | |
| 134 return AddKeyImpl(nigori.release()); | |
| 135 } | |
| 136 | |
| 137 bool Cryptographer::AddKeyImpl(Nigori* initialized_nigori) { | |
| 138 scoped_ptr<Nigori> nigori(initialized_nigori); | |
| 139 std::string name; | |
| 140 if (!nigori->Permute(Nigori::Password, kNigoriKeyName, &name)) { | |
| 141 NOTREACHED(); | |
| 142 return false; | |
| 143 } | |
| 144 nigoris_[name] = make_linked_ptr(nigori.release()); | |
| 145 default_nigori_ = &*nigoris_.find(name); | |
| 146 return true; | |
| 147 } | |
| 148 | |
| 149 bool Cryptographer::SetKeys(const sync_pb::EncryptedData& encrypted) { | |
| 150 DCHECK(CanDecrypt(encrypted)); | |
| 151 | |
| 152 sync_pb::NigoriKeyBag bag; | |
| 153 if (!Decrypt(encrypted, &bag)) { | |
| 154 return false; | |
| 155 } | |
| 156 InstallKeys(encrypted.key_name(), bag); | |
| 157 return true; | |
| 158 } | |
| 159 | |
| 160 void Cryptographer::SetPendingKeys(const sync_pb::EncryptedData& encrypted) { | |
| 161 DCHECK(!CanDecrypt(encrypted)); | |
| 162 pending_keys_.reset(new sync_pb::EncryptedData(encrypted)); | |
| 163 } | |
| 164 | |
| 165 bool Cryptographer::DecryptPendingKeys(const KeyParams& params) { | |
| 166 Nigori nigori; | |
| 167 if (!nigori.InitByDerivation(params.hostname, | |
| 168 params.username, | |
| 169 params.password)) { | |
| 170 NOTREACHED(); | |
| 171 return false; | |
| 172 } | |
| 173 | |
| 174 std::string plaintext; | |
| 175 if (!nigori.Decrypt(pending_keys_->blob(), &plaintext)) | |
| 176 return false; | |
| 177 | |
| 178 sync_pb::NigoriKeyBag bag; | |
| 179 if (!bag.ParseFromString(plaintext)) { | |
| 180 NOTREACHED(); | |
| 181 return false; | |
| 182 } | |
| 183 InstallKeys(pending_keys_->key_name(), bag); | |
| 184 pending_keys_.reset(); | |
| 185 return true; | |
| 186 } | |
| 187 | |
| 188 bool Cryptographer::GetBootstrapToken(std::string* token) const { | |
| 189 DCHECK(token); | |
| 190 if (!is_initialized()) | |
| 191 return false; | |
| 192 | |
| 193 return PackBootstrapToken(default_nigori_->second.get(), token); | |
| 194 } | |
| 195 | |
| 196 bool Cryptographer::PackBootstrapToken(const Nigori* nigori, | |
| 197 std::string* pack_into) const { | |
| 198 DCHECK(pack_into); | |
| 199 DCHECK(nigori); | |
| 200 | |
| 201 sync_pb::NigoriKey key; | |
| 202 if (!nigori->ExportKeys(key.mutable_user_key(), | |
| 203 key.mutable_encryption_key(), | |
| 204 key.mutable_mac_key())) { | |
| 205 NOTREACHED(); | |
| 206 return false; | |
| 207 } | |
| 208 | |
| 209 std::string unencrypted_token; | |
| 210 if (!key.SerializeToString(&unencrypted_token)) { | |
| 211 NOTREACHED(); | |
| 212 return false; | |
| 213 } | |
| 214 | |
| 215 std::string encrypted_token; | |
| 216 if (!Encryptor::EncryptString(unencrypted_token, &encrypted_token)) { | |
| 217 NOTREACHED(); | |
| 218 return false; | |
| 219 } | |
| 220 | |
| 221 if (!base::Base64Encode(encrypted_token, pack_into)) { | |
| 222 NOTREACHED(); | |
| 223 return false; | |
| 224 } | |
| 225 return true; | |
| 226 } | |
| 227 | |
| 228 Nigori* Cryptographer::UnpackBootstrapToken(const std::string& token) const { | |
| 229 if (token.empty()) | |
| 230 return NULL; | |
| 231 | |
| 232 std::string encrypted_data; | |
| 233 if (!base::Base64Decode(token, &encrypted_data)) { | |
| 234 DLOG(WARNING) << "Could not decode token."; | |
| 235 return NULL; | |
| 236 } | |
| 237 | |
| 238 std::string unencrypted_token; | |
| 239 if (!Encryptor::DecryptString(encrypted_data, &unencrypted_token)) { | |
| 240 DLOG(WARNING) << "Decryption of bootstrap token failed."; | |
| 241 return NULL; | |
| 242 } | |
| 243 | |
| 244 sync_pb::NigoriKey key; | |
| 245 if (!key.ParseFromString(unencrypted_token)) { | |
| 246 DLOG(WARNING) << "Parsing of bootstrap token failed."; | |
| 247 return NULL; | |
| 248 } | |
| 249 | |
| 250 scoped_ptr<Nigori> nigori(new Nigori); | |
| 251 if (!nigori->InitByImport(key.user_key(), key.encryption_key(), | |
| 252 key.mac_key())) { | |
| 253 NOTREACHED(); | |
| 254 return NULL; | |
| 255 } | |
| 256 | |
| 257 return nigori.release(); | |
| 258 } | |
| 259 | |
| 260 Cryptographer::UpdateResult Cryptographer::Update( | |
| 261 const sync_pb::NigoriSpecifics& nigori) { | |
| 262 UpdateEncryptedTypesFromNigori(nigori); | |
| 263 if (!nigori.encrypted().blob().empty()) { | |
| 264 if (CanDecrypt(nigori.encrypted())) { | |
| 265 SetKeys(nigori.encrypted()); | |
| 266 return Cryptographer::SUCCESS; | |
| 267 } else { | |
| 268 SetPendingKeys(nigori.encrypted()); | |
| 269 return Cryptographer::NEEDS_PASSPHRASE; | |
| 270 } | |
| 271 } | |
| 272 return Cryptographer::SUCCESS; | |
| 273 } | |
| 274 | |
| 275 // Static | |
| 276 syncable::ModelTypeSet Cryptographer::SensitiveTypes() { | |
| 277 syncable::ModelTypeSet types; | |
| 278 // Both of these have their own encryption schemes, but we include them | |
| 279 // anyways. | |
| 280 types.insert(syncable::PASSWORDS); | |
| 281 types.insert(syncable::NIGORI); | |
| 282 return types; | |
| 283 } | |
| 284 | |
| 285 void Cryptographer::UpdateEncryptedTypesFromNigori( | |
| 286 const sync_pb::NigoriSpecifics& nigori) { | |
| 287 if (nigori.encrypt_everything()) { | |
| 288 set_encrypt_everything(); | |
| 289 return; | |
| 290 } | |
| 291 | |
| 292 syncable::ModelTypeSet encrypted_types(SensitiveTypes()); | |
| 293 if (nigori.encrypt_bookmarks()) | |
| 294 encrypted_types.insert(syncable::BOOKMARKS); | |
| 295 if (nigori.encrypt_preferences()) | |
| 296 encrypted_types.insert(syncable::PREFERENCES); | |
| 297 if (nigori.encrypt_autofill_profile()) | |
| 298 encrypted_types.insert(syncable::AUTOFILL_PROFILE); | |
| 299 if (nigori.encrypt_autofill()) | |
| 300 encrypted_types.insert(syncable::AUTOFILL); | |
| 301 if (nigori.encrypt_themes()) | |
| 302 encrypted_types.insert(syncable::THEMES); | |
| 303 if (nigori.encrypt_typed_urls()) | |
| 304 encrypted_types.insert(syncable::TYPED_URLS); | |
| 305 if (nigori.encrypt_extension_settings()) | |
| 306 encrypted_types.insert(syncable::EXTENSION_SETTINGS); | |
| 307 if (nigori.encrypt_extensions()) | |
| 308 encrypted_types.insert(syncable::EXTENSIONS); | |
| 309 if (nigori.encrypt_search_engines()) | |
| 310 encrypted_types.insert(syncable::SEARCH_ENGINES); | |
| 311 if (nigori.encrypt_sessions()) | |
| 312 encrypted_types.insert(syncable::SESSIONS); | |
| 313 if (nigori.encrypt_app_settings()) | |
| 314 encrypted_types.insert(syncable::APP_SETTINGS); | |
| 315 if (nigori.encrypt_apps()) | |
| 316 encrypted_types.insert(syncable::APPS); | |
| 317 if (nigori.encrypt_app_notifications()) | |
| 318 encrypted_types.insert(syncable::APP_NOTIFICATIONS); | |
| 319 | |
| 320 // Note: the initial version with encryption did not support the | |
| 321 // encrypt_everything field. If anything more than the sensitive types were | |
| 322 // encrypted, it meant we were encrypting everything. | |
| 323 if (!nigori.has_encrypt_everything() && | |
| 324 encrypted_types.size() > SensitiveTypes().size()) { | |
| 325 set_encrypt_everything(); | |
| 326 return; | |
| 327 } | |
| 328 | |
| 329 SetEncryptedTypes(encrypted_types); | |
| 330 } | |
| 331 | |
| 332 void Cryptographer::UpdateNigoriFromEncryptedTypes( | |
| 333 sync_pb::NigoriSpecifics* nigori) const { | |
| 334 nigori->set_encrypt_everything(encrypt_everything_); | |
| 335 nigori->set_encrypt_bookmarks( | |
| 336 encrypted_types_.count(syncable::BOOKMARKS) > 0); | |
| 337 nigori->set_encrypt_preferences( | |
| 338 encrypted_types_.count(syncable::PREFERENCES) > 0); | |
| 339 nigori->set_encrypt_autofill_profile( | |
| 340 encrypted_types_.count(syncable::AUTOFILL_PROFILE) > 0); | |
| 341 nigori->set_encrypt_autofill(encrypted_types_.count(syncable::AUTOFILL) > 0); | |
| 342 nigori->set_encrypt_themes(encrypted_types_.count(syncable::THEMES) > 0); | |
| 343 nigori->set_encrypt_typed_urls( | |
| 344 encrypted_types_.count(syncable::TYPED_URLS) > 0); | |
| 345 nigori->set_encrypt_extension_settings( | |
| 346 encrypted_types_.count(syncable::EXTENSION_SETTINGS) > 0); | |
| 347 nigori->set_encrypt_extensions( | |
| 348 encrypted_types_.count(syncable::EXTENSIONS) > 0); | |
| 349 nigori->set_encrypt_search_engines( | |
| 350 encrypted_types_.count(syncable::SEARCH_ENGINES) > 0); | |
| 351 nigori->set_encrypt_sessions(encrypted_types_.count(syncable::SESSIONS) > 0); | |
| 352 nigori->set_encrypt_app_settings( | |
| 353 encrypted_types_.count(syncable::APP_SETTINGS) > 0); | |
| 354 nigori->set_encrypt_apps(encrypted_types_.count(syncable::APPS) > 0); | |
| 355 nigori->set_encrypt_app_notifications( | |
| 356 encrypted_types_.count(syncable::APP_NOTIFICATIONS) > 0); | |
| 357 } | |
| 358 | |
| 359 void Cryptographer::set_encrypt_everything() { | |
| 360 if (encrypt_everything_) { | |
| 361 DCHECK(encrypted_types_ == syncable::GetAllRealModelTypes()); | |
| 362 return; | |
| 363 } | |
| 364 encrypt_everything_ = true; | |
| 365 // Change |encrypted_types_| directly to avoid sending more than one | |
| 366 // notification. | |
| 367 encrypted_types_ = syncable::GetAllRealModelTypes(); | |
| 368 EmitEncryptedTypesChangedNotification(); | |
| 369 } | |
| 370 | |
| 371 bool Cryptographer::encrypt_everything() const { | |
| 372 return encrypt_everything_; | |
| 373 } | |
| 374 | |
| 375 syncable::ModelTypeSet Cryptographer::GetEncryptedTypes() const { | |
| 376 return encrypted_types_; | |
| 377 } | |
| 378 | |
| 379 void Cryptographer::SetEncryptedTypesForTest( | |
| 380 const syncable::ModelTypeSet& encrypted_types) { | |
| 381 SetEncryptedTypes(encrypted_types); | |
| 382 } | |
| 383 | |
| 384 void Cryptographer::SetEncryptedTypes( | |
| 385 const syncable::ModelTypeSet& encrypted_types) { | |
| 386 if (encrypted_types_ == encrypted_types) { | |
| 387 return; | |
| 388 } | |
| 389 encrypted_types_ = encrypted_types; | |
| 390 EmitEncryptedTypesChangedNotification(); | |
| 391 } | |
| 392 | |
| 393 void Cryptographer::EmitEncryptedTypesChangedNotification() { | |
| 394 FOR_EACH_OBSERVER( | |
| 395 Observer, observers_, | |
| 396 OnEncryptedTypesChanged(encrypted_types_, encrypt_everything_)); | |
| 397 } | |
| 398 | |
| 399 void Cryptographer::InstallKeys(const std::string& default_key_name, | |
| 400 const sync_pb::NigoriKeyBag& bag) { | |
| 401 int key_size = bag.key_size(); | |
| 402 for (int i = 0; i < key_size; ++i) { | |
| 403 const sync_pb::NigoriKey key = bag.key(i); | |
| 404 // Only use this key if we don't already know about it. | |
| 405 if (nigoris_.end() == nigoris_.find(key.name())) { | |
| 406 scoped_ptr<Nigori> new_nigori(new Nigori); | |
| 407 if (!new_nigori->InitByImport(key.user_key(), | |
| 408 key.encryption_key(), | |
| 409 key.mac_key())) { | |
| 410 NOTREACHED(); | |
| 411 continue; | |
| 412 } | |
| 413 nigoris_[key.name()] = make_linked_ptr(new_nigori.release()); | |
| 414 } | |
| 415 } | |
| 416 DCHECK(nigoris_.end() != nigoris_.find(default_key_name)); | |
| 417 default_nigori_ = &*nigoris_.find(default_key_name); | |
| 418 } | |
| 419 | |
| 420 } // namespace browser_sync | |
| OLD | NEW |