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