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::Cryptographer() : default_nigori_(NULL) { 20 } 21 22 Cryptographer::~Cryptographer() {} 23 24 void Cryptographer::Bootstrap(const std::string& restored_bootstrap_token) { 25 if (is_ready()) { 26 NOTREACHED(); 27 return; 28 } 29 30 scoped_ptr<Nigori> nigori(UnpackBootstrapToken(restored_bootstrap_token)); 31 if (nigori.get()) 32 AddKeyImpl(nigori.release()); 33 } 34 35 bool Cryptographer::CanDecrypt(const sync_pb::EncryptedData& data) const { 36 return nigoris_.end() != nigoris_.find(data.key_name()); 37 } 38 39 bool Cryptographer::CanDecryptUsingDefaultKey( 40 const sync_pb::EncryptedData& data) const { 41 return default_nigori_ && (data.key_name() == default_nigori_->first); 42 } 43 44 bool Cryptographer::Encrypt(const ::google::protobuf::MessageLite& message, 45 sync_pb::EncryptedData* encrypted) const { 46 DCHECK(encrypted); 47 DCHECK(default_nigori_); 48 49 std::string serialized; 50 if (!message.SerializeToString(&serialized)) { 51 NOTREACHED(); // |message| is invalid/missing a required field. 52 return false; 53 } 54 55 encrypted->set_key_name(default_nigori_->first); 56 if (!default_nigori_->second->Encrypt(serialized, 57 encrypted->mutable_blob())) { 58 NOTREACHED(); // Encrypt should not fail. 59 return false; 60 } 61 return true; 62 } 63 64 bool Cryptographer::Decrypt(const sync_pb::EncryptedData& encrypted, 65 ::google::protobuf::MessageLite* message) const { 66 DCHECK(message); 67 std::string plaintext = DecryptToString(encrypted); 68 return message->ParseFromString(plaintext); 69 } 70 71 std::string Cryptographer::DecryptToString( 72 const sync_pb::EncryptedData& encrypted) const { 73 NigoriMap::const_iterator it = nigoris_.find(encrypted.key_name()); 74 if (nigoris_.end() == it) { 75 NOTREACHED() << "Cannot decrypt message"; 76 return std::string(""); // Caller should have called CanDecrypt(encrypt). 77 } 78 79 std::string plaintext; 80 if (!it->second->Decrypt(encrypted.blob(), &plaintext)) { 81 return std::string(""); 82 } 83 84 return plaintext; 85 } 86 87 bool Cryptographer::GetKeys(sync_pb::EncryptedData* encrypted) const { 88 DCHECK(encrypted); 89 DCHECK(!nigoris_.empty()); 90 91 // Create a bag of all the Nigori parameters we know about. 92 sync_pb::NigoriKeyBag bag; 93 for (NigoriMap::const_iterator it = nigoris_.begin(); it != nigoris_.end(); 94 ++it) { 95 const Nigori& nigori = *it->second; 96 sync_pb::NigoriKey* key = bag.add_key(); 97 key->set_name(it->first); 98 nigori.ExportKeys(key->mutable_user_key(), 99 key->mutable_encryption_key(), 100 key->mutable_mac_key()); 101 } 102 103 // Encrypt the bag with the default Nigori. 104 return Encrypt(bag, encrypted); 105 } 106 107 bool Cryptographer::AddKey(const KeyParams& params) { 108 DCHECK(NULL == pending_keys_.get()); 109 110 // Create the new Nigori and make it the default encryptor. 111 scoped_ptr<Nigori> nigori(new Nigori); 112 if (!nigori->InitByDerivation(params.hostname, 113 params.username, 114 params.password)) { 115 NOTREACHED(); // Invalid username or password. 116 return false; 117 } 118 return AddKeyImpl(nigori.release()); 119 } 120 121 bool Cryptographer::AddKeyImpl(Nigori* initialized_nigori) { 122 scoped_ptr<Nigori> nigori(initialized_nigori); 123 std::string name; 124 if (!nigori->Permute(Nigori::Password, kNigoriKeyName, &name)) { 125 NOTREACHED(); 126 return false; 127 } 128 nigoris_[name] = make_linked_ptr(nigori.release()); 129 default_nigori_ = &*nigoris_.find(name); 130 return true; 131 } 132 133 bool Cryptographer::SetKeys(const sync_pb::EncryptedData& encrypted) { 134 DCHECK(CanDecrypt(encrypted)); 135 136 sync_pb::NigoriKeyBag bag; 137 if (!Decrypt(encrypted, &bag)) { 138 return false; 139 } 140 InstallKeys(encrypted.key_name(), bag); 141 return true; 142 } 143 144 void Cryptographer::SetPendingKeys(const sync_pb::EncryptedData& encrypted) { 145 DCHECK(!CanDecrypt(encrypted)); 146 pending_keys_.reset(new sync_pb::EncryptedData(encrypted)); 147 } 148 149 bool Cryptographer::DecryptPendingKeys(const KeyParams& params) { 150 Nigori nigori; 151 if (!nigori.InitByDerivation(params.hostname, 152 params.username, 153 params.password)) { 154 NOTREACHED(); 155 return false; 156 } 157 158 std::string plaintext; 159 if (!nigori.Decrypt(pending_keys_->blob(), &plaintext)) 160 return false; 161 162 sync_pb::NigoriKeyBag bag; 163 if (!bag.ParseFromString(plaintext)) { 164 NOTREACHED(); 165 return false; 166 } 167 InstallKeys(pending_keys_->key_name(), bag); 168 pending_keys_.reset(); 169 return true; 170 } 171 172 bool Cryptographer::GetBootstrapToken(std::string* token) const { 173 DCHECK(token); 174 if (!is_ready()) 175 return false; 176 177 return PackBootstrapToken(default_nigori_->second.get(), token); 178 } 179 180 bool Cryptographer::PackBootstrapToken(const Nigori* nigori, 181 std::string* pack_into) const { 182 DCHECK(pack_into); 183 DCHECK(nigori); 184 185 sync_pb::NigoriKey key; 186 if (!nigori->ExportKeys(key.mutable_user_key(), 187 key.mutable_encryption_key(), 188 key.mutable_mac_key())) { 189 NOTREACHED(); 190 return false; 191 } 192 193 std::string unencrypted_token; 194 if (!key.SerializeToString(&unencrypted_token)) { 195 NOTREACHED(); 196 return false; 197 } 198 199 std::string encrypted_token; 200 if (!Encryptor::EncryptString(unencrypted_token, &encrypted_token)) { 201 NOTREACHED(); 202 return false; 203 } 204 205 if (!base::Base64Encode(encrypted_token, pack_into)) { 206 NOTREACHED(); 207 return false; 208 } 209 return true; 210 } 211 212 Nigori* Cryptographer::UnpackBootstrapToken(const std::string& token) const { 213 if (token.empty()) 214 return NULL; 215 216 std::string encrypted_data; 217 if (!base::Base64Decode(token, &encrypted_data)) { 218 DLOG(WARNING) << "Could not decode token."; 219 return NULL; 220 } 221 222 std::string unencrypted_token; 223 if (!Encryptor::DecryptString(encrypted_data, &unencrypted_token)) { 224 DLOG(WARNING) << "Decryption of bootstrap token failed."; 225 return NULL; 226 } 227 228 sync_pb::NigoriKey key; 229 if (!key.ParseFromString(unencrypted_token)) { 230 DLOG(WARNING) << "Parsing of bootstrap token failed."; 231 return NULL; 232 } 233 234 scoped_ptr<Nigori> nigori(new Nigori); 235 if (!nigori->InitByImport(key.user_key(), key.encryption_key(), 236 key.mac_key())) { 237 NOTREACHED(); 238 return NULL; 239 } 240 241 return nigori.release(); 242 } 243 244 void Cryptographer::InstallKeys(const std::string& default_key_name, 245 const sync_pb::NigoriKeyBag& bag) { 246 int key_size = bag.key_size(); 247 for (int i = 0; i < key_size; ++i) { 248 const sync_pb::NigoriKey key = bag.key(i); 249 // Only use this key if we don't already know about it. 250 if (nigoris_.end() == nigoris_.find(key.name())) { 251 scoped_ptr<Nigori> new_nigori(new Nigori); 252 if (!new_nigori->InitByImport(key.user_key(), 253 key.encryption_key(), 254 key.mac_key())) { 255 NOTREACHED(); 256 continue; 257 } 258 nigoris_[key.name()] = make_linked_ptr(new_nigori.release()); 259 } 260 } 261 DCHECK(nigoris_.end() != nigoris_.find(default_key_name)); 262 default_nigori_ = &*nigoris_.find(default_key_name); 263 } 264 265 } // namespace browser_sync 266