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 15namespace syncer { 16 17const 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. 23const char kNigoriKeyName[] = "nigori-key"; 24 25Cryptographer::Cryptographer(Encryptor* encryptor) 26 : encryptor_(encryptor) { 27 DCHECK(encryptor); 28} 29 30Cryptographer::~Cryptographer() {} 31 32 33void 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 46bool Cryptographer::CanDecrypt(const sync_pb::EncryptedData& data) const { 47 return nigoris_.end() != nigoris_.find(data.key_name()); 48} 49 50bool Cryptographer::CanDecryptUsingDefaultKey( 51 const sync_pb::EncryptedData& data) const { 52 return !default_nigori_name_.empty() && 53 data.key_name() == default_nigori_name_; 54} 55 56bool 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 74bool 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 101bool 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 108std::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 124bool 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 144bool 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 156bool 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 169bool 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 177bool 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 202void 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 211void Cryptographer::SetDefaultKey(const std::string& key_name) { 212 DCHECK(nigoris_.end() != nigoris_.find(key_name)); 213 default_nigori_name_ = key_name; 214} 215 216void 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 222const sync_pb::EncryptedData& Cryptographer::GetPendingKeys() const { 223 DCHECK(has_pending_keys()); 224 return *(pending_keys_.get()); 225} 226 227bool 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 252bool 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 base::Base64Encode(encrypted_token, token); 265 266 return true; 267} 268 269std::string Cryptographer::UnpackBootstrapToken( 270 const std::string& token) const { 271 if (token.empty()) 272 return std::string(); 273 274 std::string encrypted_data; 275 if (!base::Base64Decode(token, &encrypted_data)) { 276 DLOG(WARNING) << "Could not decode token."; 277 return std::string(); 278 } 279 280 std::string unencrypted_token; 281 if (!encryptor_->DecryptString(encrypted_data, &unencrypted_token)) { 282 DLOG(WARNING) << "Decryption of bootstrap token failed."; 283 return std::string(); 284 } 285 return unencrypted_token; 286} 287 288void Cryptographer::InstallKeyBag(const sync_pb::NigoriKeyBag& bag) { 289 int key_size = bag.key_size(); 290 for (int i = 0; i < key_size; ++i) { 291 const sync_pb::NigoriKey key = bag.key(i); 292 // Only use this key if we don't already know about it. 293 if (nigoris_.end() == nigoris_.find(key.name())) { 294 scoped_ptr<Nigori> new_nigori(new Nigori); 295 if (!new_nigori->InitByImport(key.user_key(), 296 key.encryption_key(), 297 key.mac_key())) { 298 NOTREACHED(); 299 continue; 300 } 301 nigoris_[key.name()] = make_linked_ptr(new_nigori.release()); 302 } 303 } 304} 305 306bool Cryptographer::KeybagIsStale( 307 const sync_pb::EncryptedData& encrypted_bag) const { 308 if (!is_ready()) 309 return false; 310 if (encrypted_bag.blob().empty()) 311 return true; 312 if (!CanDecrypt(encrypted_bag)) 313 return false; 314 if (!CanDecryptUsingDefaultKey(encrypted_bag)) 315 return true; 316 sync_pb::NigoriKeyBag bag; 317 if (!Decrypt(encrypted_bag, &bag)) { 318 LOG(ERROR) << "Failed to decrypt keybag for stale check. " 319 << "Assuming keybag is corrupted."; 320 return true; 321 } 322 if (static_cast<size_t>(bag.key_size()) < nigoris_.size()) 323 return true; 324 return false; 325} 326 327std::string Cryptographer::GetDefaultNigoriKey() const { 328 if (!is_initialized()) 329 return std::string(); 330 NigoriMap::const_iterator iter = nigoris_.find(default_nigori_name_); 331 if (iter == nigoris_.end()) 332 return std::string(); 333 sync_pb::NigoriKey key; 334 if (!iter->second->ExportKeys(key.mutable_user_key(), 335 key.mutable_encryption_key(), 336 key.mutable_mac_key())) 337 return std::string(); 338 return key.SerializeAsString(); 339} 340 341bool Cryptographer::ImportNigoriKey(const std::string serialized_nigori_key) { 342 if (serialized_nigori_key.empty()) 343 return false; 344 345 sync_pb::NigoriKey key; 346 if (!key.ParseFromString(serialized_nigori_key)) 347 return false; 348 349 scoped_ptr<Nigori> nigori(new Nigori); 350 if (!nigori->InitByImport(key.user_key(), key.encryption_key(), 351 key.mac_key())) { 352 NOTREACHED(); 353 return false; 354 } 355 356 if (!AddKeyImpl(nigori.Pass(), true)) 357 return false; 358 return true; 359} 360 361} // namespace syncer 362