| 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 |