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