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
Cryptographer()19 Cryptographer::Cryptographer() : default_nigori_(NULL) {
20 }
21
~Cryptographer()22 Cryptographer::~Cryptographer() {}
23
Bootstrap(const std::string & restored_bootstrap_token)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
CanDecrypt(const sync_pb::EncryptedData & data) const35 bool Cryptographer::CanDecrypt(const sync_pb::EncryptedData& data) const {
36 return nigoris_.end() != nigoris_.find(data.key_name());
37 }
38
CanDecryptUsingDefaultKey(const sync_pb::EncryptedData & data) const39 bool Cryptographer::CanDecryptUsingDefaultKey(
40 const sync_pb::EncryptedData& data) const {
41 return default_nigori_ && (data.key_name() == default_nigori_->first);
42 }
43
Encrypt(const::google::protobuf::MessageLite & message,sync_pb::EncryptedData * encrypted) const44 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
Decrypt(const sync_pb::EncryptedData & encrypted,::google::protobuf::MessageLite * message) const64 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
DecryptToString(const sync_pb::EncryptedData & encrypted) const71 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
GetKeys(sync_pb::EncryptedData * encrypted) const87 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
AddKey(const KeyParams & params)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
AddKeyImpl(Nigori * initialized_nigori)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
SetKeys(const sync_pb::EncryptedData & encrypted)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
SetPendingKeys(const sync_pb::EncryptedData & encrypted)144 void Cryptographer::SetPendingKeys(const sync_pb::EncryptedData& encrypted) {
145 DCHECK(!CanDecrypt(encrypted));
146 pending_keys_.reset(new sync_pb::EncryptedData(encrypted));
147 }
148
DecryptPendingKeys(const KeyParams & params)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
GetBootstrapToken(std::string * token) const172 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
PackBootstrapToken(const Nigori * nigori,std::string * pack_into) const180 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
UnpackBootstrapToken(const std::string & token) const212 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
InstallKeys(const std::string & default_key_name,const sync_pb::NigoriKeyBag & bag)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