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() {} 31 32 33 void Cryptographer::Bootstrap(const std::string& restored_bootstrap_token) { 34 if (is_initialized()) { 35 NOTREACHED(); 36 return; 37 } 38 39 std::string serialized_nigori_key = 40 UnpackBootstrapToken(restored_bootstrap_token); 41 if (serialized_nigori_key.empty()) 42 return; 43 ImportNigoriKey(serialized_nigori_key); 44 } 45 46 bool Cryptographer::CanDecrypt(const sync_pb::EncryptedData& data) const { 47 return nigoris_.end() != nigoris_.find(data.key_name()); 48 } 49 50 bool Cryptographer::CanDecryptUsingDefaultKey( 51 const sync_pb::EncryptedData& data) const { 52 return !default_nigori_name_.empty() && 53 data.key_name() == default_nigori_name_; 54 } 55 56 bool Cryptographer::Encrypt( 57 const ::google::protobuf::MessageLite& message, 58 sync_pb::EncryptedData* encrypted) const { 59 DCHECK(encrypted); 60 if (default_nigori_name_.empty()) { 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 return EncryptString(serialized, encrypted); 72 } 73 74 bool Cryptographer::EncryptString( 75 const std::string& serialized, 76 sync_pb::EncryptedData* encrypted) const { 77 if (CanDecryptUsingDefaultKey(*encrypted)) { 78 const std::string& original_serialized = DecryptToString(*encrypted); 79 if (original_serialized == serialized) { 80 DVLOG(2) << "Re-encryption unnecessary, encrypted data already matches."; 81 return true; 82 } 83 } 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 92 encrypted->set_key_name(default_nigori_name_); 93 if (!default_nigori->second->Encrypt(serialized, 94 encrypted->mutable_blob())) { 95 LOG(ERROR) << "Failed to encrypt data."; 96 return false; 97 } 98 return true; 99 } 100 101 bool Cryptographer::Decrypt(const sync_pb::EncryptedData& encrypted, 102 ::google::protobuf::MessageLite* message) const { 103 DCHECK(message); 104 std::string plaintext = DecryptToString(encrypted); 105 return message->ParseFromString(plaintext); 106 } 107 108 std::string Cryptographer::DecryptToString( 109 const sync_pb::EncryptedData& encrypted) const { 110 NigoriMap::const_iterator it = nigoris_.find(encrypted.key_name()); 111 if (nigoris_.end() == it) { 112 NOTREACHED() << "Cannot decrypt message"; 113 return std::string(); // Caller should have called CanDecrypt(encrypt). 114 } 115 116 std::string plaintext; 117 if (!it->second->Decrypt(encrypted.blob(), &plaintext)) { 118 return std::string(); 119 } 120 121 return plaintext; 122 } 123 124 bool Cryptographer::GetKeys(sync_pb::EncryptedData* encrypted) const { 125 DCHECK(encrypted); 126 DCHECK(!nigoris_.empty()); 127 128 // Create a bag of all the Nigori parameters we know about. 129 sync_pb::NigoriKeyBag bag; 130 for (NigoriMap::const_iterator it = nigoris_.begin(); it != nigoris_.end(); 131 ++it) { 132 const Nigori& nigori = *it->second; 133 sync_pb::NigoriKey* key = bag.add_key(); 134 key->set_name(it->first); 135 nigori.ExportKeys(key->mutable_user_key(), 136 key->mutable_encryption_key(), 137 key->mutable_mac_key()); 138 } 139 140 // Encrypt the bag with the default Nigori. 141 return Encrypt(bag, encrypted); 142 } 143 144 bool Cryptographer::AddKey(const KeyParams& params) { 145 // Create the new Nigori and make it the default encryptor. 146 scoped_ptr<Nigori> nigori(new Nigori); 147 if (!nigori->InitByDerivation(params.hostname, 148 params.username, 149 params.password)) { 150 NOTREACHED(); // Invalid username or password. 151 return false; 152 } 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); 167 } 168 169 bool Cryptographer::AddKeyFromBootstrapToken( 170 const std::string restored_bootstrap_token) { 171 // Create the new Nigori and make it the default encryptor. 172 std::string serialized_nigori_key = UnpackBootstrapToken( 173 restored_bootstrap_token); 174 return ImportNigoriKey(serialized_nigori_key); 175 } 176 177 bool Cryptographer::AddKeyImpl(scoped_ptr<Nigori> initialized_nigori, 178 bool set_as_default) { 179 std::string name; 180 if (!initialized_nigori->Permute(Nigori::Password, kNigoriKeyName, &name)) { 181 NOTREACHED(); 182 return false; 183 } 184 185 nigoris_[name] = make_linked_ptr(initialized_nigori.release()); 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); 199 return true; 200 } 201 202 void Cryptographer::InstallKeys(const sync_pb::EncryptedData& encrypted) { 203 DCHECK(CanDecrypt(encrypted)); 204 205 sync_pb::NigoriKeyBag bag; 206 if (!Decrypt(encrypted, &bag)) 207 return; 208 InstallKeyBag(bag); 209 } 210 211 void Cryptographer::SetDefaultKey(const std::string& key_name) { 212 DCHECK(nigoris_.end() != nigoris_.find(key_name)); 213 default_nigori_name_ = key_name; 214 } 215 216 void Cryptographer::SetPendingKeys(const sync_pb::EncryptedData& encrypted) { 217 DCHECK(!CanDecrypt(encrypted)); 218 DCHECK(!encrypted.blob().empty()); 219 pending_keys_.reset(new sync_pb::EncryptedData(encrypted)); 220 } 221 222 const sync_pb::EncryptedData& Cryptographer::GetPendingKeys() const { 223 DCHECK(has_pending_keys()); 224 return *(pending_keys_.get()); 225 } 226 227 bool Cryptographer::DecryptPendingKeys(const KeyParams& params) { 228 Nigori nigori; 229 if (!nigori.InitByDerivation(params.hostname, 230 params.username, 231 params.password)) { 232 NOTREACHED(); 233 return false; 234 } 235 236 std::string plaintext; 237 if (!nigori.Decrypt(pending_keys_->blob(), &plaintext)) 238 return false; 239 240 sync_pb::NigoriKeyBag bag; 241 if (!bag.ParseFromString(plaintext)) { 242 NOTREACHED(); 243 return false; 244 } 245 InstallKeyBag(bag); 246 const std::string& new_default_key_name = pending_keys_->key_name(); 247 SetDefaultKey(new_default_key_name); 248 pending_keys_.reset(); 249 return true; 250 } 251 252 bool Cryptographer::GetBootstrapToken(std::string* token) const { 253 DCHECK(token); 254 std::string unencrypted_token = GetDefaultNigoriKey(); 255 if (unencrypted_token.empty()) 256 return false; 257 258 std::string encrypted_token; 259 if (!encryptor_->EncryptString(unencrypted_token, &encrypted_token)) { 260 NOTREACHED(); 261 return false; 262 } 263 264 if (!base::Base64Encode(encrypted_token, token)) { 265 NOTREACHED(); 266 return false; 267 } 268 return true; 269 } 270 271 std::string Cryptographer::UnpackBootstrapToken( 272 const std::string& token) const { 273 if (token.empty()) 274 return std::string(); 275 276 std::string encrypted_data; 277 if (!base::Base64Decode(token, &encrypted_data)) { 278 DLOG(WARNING) << "Could not decode token."; 279 return std::string(); 280 } 281 282 std::string unencrypted_token; 283 if (!encryptor_->DecryptString(encrypted_data, &unencrypted_token)) { 284 DLOG(WARNING) << "Decryption of bootstrap token failed."; 285 return std::string(); 286 } 287 return unencrypted_token; 288 } 289 290 void Cryptographer::InstallKeyBag(const sync_pb::NigoriKeyBag& bag) { 291 int key_size = bag.key_size(); 292 for (int i = 0; i < key_size; ++i) { 293 const sync_pb::NigoriKey key = bag.key(i); 294 // Only use this key if we don't already know about it. 295 if (nigoris_.end() == nigoris_.find(key.name())) { 296 scoped_ptr<Nigori> new_nigori(new Nigori); 297 if (!new_nigori->InitByImport(key.user_key(), 298 key.encryption_key(), 299 key.mac_key())) { 300 NOTREACHED(); 301 continue; 302 } 303 nigoris_[key.name()] = make_linked_ptr(new_nigori.release()); 304 } 305 } 306 } 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 std::string(); 332 NigoriMap::const_iterator iter = nigoris_.find(default_nigori_name_); 333 if (iter == nigoris_.end()) 334 return std::string(); 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 std::string(); 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 363 } // namespace syncer 364