• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Cryptographer(Encryptor * encryptor)25 Cryptographer::Cryptographer(Encryptor* encryptor)
26     : encryptor_(encryptor) {
27   DCHECK(encryptor);
28 }
29 
Cryptographer(const Cryptographer & other)30 Cryptographer::Cryptographer(const Cryptographer& other)
31     : encryptor_(other.encryptor_),
32       default_nigori_name_(other.default_nigori_name_) {
33   for (NigoriMap::const_iterator it = other.nigoris_.begin();
34        it != other.nigoris_.end();
35        ++it) {
36     std::string user_key, encryption_key, mac_key;
37     it->second->ExportKeys(&user_key, &encryption_key, &mac_key);
38     linked_ptr<Nigori> nigori_copy(new Nigori());
39     nigori_copy->InitByImport(user_key, encryption_key, mac_key);
40     nigoris_.insert(std::make_pair(it->first, nigori_copy));
41   }
42 
43   if (other.pending_keys_) {
44     pending_keys_.reset(new sync_pb::EncryptedData(*(other.pending_keys_)));
45   }
46 }
47 
~Cryptographer()48 Cryptographer::~Cryptographer() {}
49 
50 
Bootstrap(const std::string & restored_bootstrap_token)51 void Cryptographer::Bootstrap(const std::string& restored_bootstrap_token) {
52   if (is_initialized()) {
53     NOTREACHED();
54     return;
55   }
56 
57   std::string serialized_nigori_key =
58       UnpackBootstrapToken(restored_bootstrap_token);
59   if (serialized_nigori_key.empty())
60     return;
61   ImportNigoriKey(serialized_nigori_key);
62 }
63 
CanDecrypt(const sync_pb::EncryptedData & data) const64 bool Cryptographer::CanDecrypt(const sync_pb::EncryptedData& data) const {
65   return nigoris_.end() != nigoris_.find(data.key_name());
66 }
67 
CanDecryptUsingDefaultKey(const sync_pb::EncryptedData & data) const68 bool Cryptographer::CanDecryptUsingDefaultKey(
69     const sync_pb::EncryptedData& data) const {
70   return !default_nigori_name_.empty() &&
71          data.key_name() == default_nigori_name_;
72 }
73 
Encrypt(const::google::protobuf::MessageLite & message,sync_pb::EncryptedData * encrypted) const74 bool Cryptographer::Encrypt(
75     const ::google::protobuf::MessageLite& message,
76     sync_pb::EncryptedData* encrypted) const {
77   DCHECK(encrypted);
78   if (default_nigori_name_.empty()) {
79     LOG(ERROR) << "Cryptographer not ready, failed to encrypt.";
80     return false;
81   }
82 
83   std::string serialized;
84   if (!message.SerializeToString(&serialized)) {
85     LOG(ERROR) << "Message is invalid/missing a required field.";
86     return false;
87   }
88 
89   return EncryptString(serialized, encrypted);
90 }
91 
EncryptString(const std::string & serialized,sync_pb::EncryptedData * encrypted) const92 bool Cryptographer::EncryptString(
93     const std::string& serialized,
94     sync_pb::EncryptedData* encrypted) const {
95   if (CanDecryptUsingDefaultKey(*encrypted)) {
96     const std::string& original_serialized = DecryptToString(*encrypted);
97     if (original_serialized == serialized) {
98       DVLOG(2) << "Re-encryption unnecessary, encrypted data already matches.";
99       return true;
100     }
101   }
102 
103   NigoriMap::const_iterator default_nigori =
104       nigoris_.find(default_nigori_name_);
105   if (default_nigori == nigoris_.end()) {
106     LOG(ERROR) << "Corrupt default key.";
107     return false;
108   }
109 
110   encrypted->set_key_name(default_nigori_name_);
111   if (!default_nigori->second->Encrypt(serialized,
112                                        encrypted->mutable_blob())) {
113     LOG(ERROR) << "Failed to encrypt data.";
114     return false;
115   }
116   return true;
117 }
118 
Decrypt(const sync_pb::EncryptedData & encrypted,::google::protobuf::MessageLite * message) const119 bool Cryptographer::Decrypt(const sync_pb::EncryptedData& encrypted,
120                             ::google::protobuf::MessageLite* message) const {
121   DCHECK(message);
122   std::string plaintext = DecryptToString(encrypted);
123   return message->ParseFromString(plaintext);
124 }
125 
DecryptToString(const sync_pb::EncryptedData & encrypted) const126 std::string Cryptographer::DecryptToString(
127     const sync_pb::EncryptedData& encrypted) const {
128   NigoriMap::const_iterator it = nigoris_.find(encrypted.key_name());
129   if (nigoris_.end() == it) {
130     NOTREACHED() << "Cannot decrypt message";
131     return std::string();  // Caller should have called CanDecrypt(encrypt).
132   }
133 
134   std::string plaintext;
135   if (!it->second->Decrypt(encrypted.blob(), &plaintext)) {
136     return std::string();
137   }
138 
139   return plaintext;
140 }
141 
GetKeys(sync_pb::EncryptedData * encrypted) const142 bool Cryptographer::GetKeys(sync_pb::EncryptedData* encrypted) const {
143   DCHECK(encrypted);
144   DCHECK(!nigoris_.empty());
145 
146   // Create a bag of all the Nigori parameters we know about.
147   sync_pb::NigoriKeyBag bag;
148   for (NigoriMap::const_iterator it = nigoris_.begin(); it != nigoris_.end();
149        ++it) {
150     const Nigori& nigori = *it->second;
151     sync_pb::NigoriKey* key = bag.add_key();
152     key->set_name(it->first);
153     nigori.ExportKeys(key->mutable_user_key(),
154                       key->mutable_encryption_key(),
155                       key->mutable_mac_key());
156   }
157 
158   // Encrypt the bag with the default Nigori.
159   return Encrypt(bag, encrypted);
160 }
161 
AddKey(const KeyParams & params)162 bool Cryptographer::AddKey(const KeyParams& params) {
163   // Create the new Nigori and make it the default encryptor.
164   scoped_ptr<Nigori> nigori(new Nigori);
165   if (!nigori->InitByDerivation(params.hostname,
166                                 params.username,
167                                 params.password)) {
168     NOTREACHED();  // Invalid username or password.
169     return false;
170   }
171   return AddKeyImpl(nigori.Pass(), true);
172 }
173 
AddNonDefaultKey(const KeyParams & params)174 bool Cryptographer::AddNonDefaultKey(const KeyParams& params) {
175   DCHECK(is_initialized());
176   // Create the new Nigori and add it to the keybag.
177   scoped_ptr<Nigori> nigori(new Nigori);
178   if (!nigori->InitByDerivation(params.hostname,
179                                 params.username,
180                                 params.password)) {
181     NOTREACHED();  // Invalid username or password.
182     return false;
183   }
184   return AddKeyImpl(nigori.Pass(), false);
185 }
186 
AddKeyFromBootstrapToken(const std::string restored_bootstrap_token)187 bool Cryptographer::AddKeyFromBootstrapToken(
188     const std::string restored_bootstrap_token) {
189   // Create the new Nigori and make it the default encryptor.
190   std::string serialized_nigori_key = UnpackBootstrapToken(
191       restored_bootstrap_token);
192   return ImportNigoriKey(serialized_nigori_key);
193 }
194 
AddKeyImpl(scoped_ptr<Nigori> initialized_nigori,bool set_as_default)195 bool Cryptographer::AddKeyImpl(scoped_ptr<Nigori> initialized_nigori,
196                                bool set_as_default) {
197   std::string name;
198   if (!initialized_nigori->Permute(Nigori::Password, kNigoriKeyName, &name)) {
199     NOTREACHED();
200     return false;
201   }
202 
203   nigoris_[name] = make_linked_ptr(initialized_nigori.release());
204 
205   // Check if the key we just added can decrypt the pending keys and add them
206   // too if so.
207   if (pending_keys_.get() && CanDecrypt(*pending_keys_)) {
208     sync_pb::NigoriKeyBag pending_bag;
209     Decrypt(*pending_keys_, &pending_bag);
210     InstallKeyBag(pending_bag);
211     SetDefaultKey(pending_keys_->key_name());
212     pending_keys_.reset();
213   }
214 
215   // The just-added key takes priority over the pending keys as default.
216   if (set_as_default) SetDefaultKey(name);
217   return true;
218 }
219 
InstallKeys(const sync_pb::EncryptedData & encrypted)220 void Cryptographer::InstallKeys(const sync_pb::EncryptedData& encrypted) {
221   DCHECK(CanDecrypt(encrypted));
222 
223   sync_pb::NigoriKeyBag bag;
224   if (!Decrypt(encrypted, &bag))
225     return;
226   InstallKeyBag(bag);
227 }
228 
SetDefaultKey(const std::string & key_name)229 void Cryptographer::SetDefaultKey(const std::string& key_name) {
230   DCHECK(nigoris_.end() != nigoris_.find(key_name));
231   default_nigori_name_ = key_name;
232 }
233 
SetPendingKeys(const sync_pb::EncryptedData & encrypted)234 void Cryptographer::SetPendingKeys(const sync_pb::EncryptedData& encrypted) {
235   DCHECK(!CanDecrypt(encrypted));
236   DCHECK(!encrypted.blob().empty());
237   pending_keys_.reset(new sync_pb::EncryptedData(encrypted));
238 }
239 
GetPendingKeys() const240 const sync_pb::EncryptedData& Cryptographer::GetPendingKeys() const {
241   DCHECK(has_pending_keys());
242   return *(pending_keys_.get());
243 }
244 
DecryptPendingKeys(const KeyParams & params)245 bool Cryptographer::DecryptPendingKeys(const KeyParams& params) {
246   Nigori nigori;
247   if (!nigori.InitByDerivation(params.hostname,
248                                params.username,
249                                params.password)) {
250     NOTREACHED();
251     return false;
252   }
253 
254   std::string plaintext;
255   if (!nigori.Decrypt(pending_keys_->blob(), &plaintext))
256     return false;
257 
258   sync_pb::NigoriKeyBag bag;
259   if (!bag.ParseFromString(plaintext)) {
260     NOTREACHED();
261     return false;
262   }
263   InstallKeyBag(bag);
264   const std::string& new_default_key_name = pending_keys_->key_name();
265   SetDefaultKey(new_default_key_name);
266   pending_keys_.reset();
267   return true;
268 }
269 
GetBootstrapToken(std::string * token) const270 bool Cryptographer::GetBootstrapToken(std::string* token) const {
271   DCHECK(token);
272   std::string unencrypted_token = GetDefaultNigoriKeyData();
273   if (unencrypted_token.empty())
274     return false;
275 
276   std::string encrypted_token;
277   if (!encryptor_->EncryptString(unencrypted_token, &encrypted_token)) {
278     NOTREACHED();
279     return false;
280   }
281 
282   base::Base64Encode(encrypted_token, token);
283 
284   return true;
285 }
286 
UnpackBootstrapToken(const std::string & token) const287 std::string Cryptographer::UnpackBootstrapToken(
288     const std::string& token) const {
289   if (token.empty())
290     return std::string();
291 
292   std::string encrypted_data;
293   if (!base::Base64Decode(token, &encrypted_data)) {
294     DLOG(WARNING) << "Could not decode token.";
295     return std::string();
296   }
297 
298   std::string unencrypted_token;
299   if (!encryptor_->DecryptString(encrypted_data, &unencrypted_token)) {
300     DLOG(WARNING) << "Decryption of bootstrap token failed.";
301     return std::string();
302   }
303   return unencrypted_token;
304 }
305 
InstallKeyBag(const sync_pb::NigoriKeyBag & bag)306 void Cryptographer::InstallKeyBag(const sync_pb::NigoriKeyBag& bag) {
307   int key_size = bag.key_size();
308   for (int i = 0; i < key_size; ++i) {
309     const sync_pb::NigoriKey key = bag.key(i);
310     // Only use this key if we don't already know about it.
311     if (nigoris_.end() == nigoris_.find(key.name())) {
312       scoped_ptr<Nigori> new_nigori(new Nigori);
313       if (!new_nigori->InitByImport(key.user_key(),
314                                     key.encryption_key(),
315                                     key.mac_key())) {
316         NOTREACHED();
317         continue;
318       }
319       nigoris_[key.name()] = make_linked_ptr(new_nigori.release());
320     }
321   }
322 }
323 
KeybagIsStale(const sync_pb::EncryptedData & encrypted_bag) const324 bool Cryptographer::KeybagIsStale(
325     const sync_pb::EncryptedData& encrypted_bag) const {
326   if (!is_ready())
327     return false;
328   if (encrypted_bag.blob().empty())
329     return true;
330   if (!CanDecrypt(encrypted_bag))
331     return false;
332   if (!CanDecryptUsingDefaultKey(encrypted_bag))
333     return true;
334   sync_pb::NigoriKeyBag bag;
335   if (!Decrypt(encrypted_bag, &bag)) {
336     LOG(ERROR) << "Failed to decrypt keybag for stale check. "
337                << "Assuming keybag is corrupted.";
338     return true;
339   }
340   if (static_cast<size_t>(bag.key_size()) < nigoris_.size())
341     return true;
342   return false;
343 }
344 
GetDefaultNigoriKeyName() const345 std::string Cryptographer::GetDefaultNigoriKeyName() const {
346   return default_nigori_name_;
347 }
348 
GetDefaultNigoriKeyData() const349 std::string Cryptographer::GetDefaultNigoriKeyData() const {
350   if (!is_initialized())
351     return std::string();
352   NigoriMap::const_iterator iter = nigoris_.find(default_nigori_name_);
353   if (iter == nigoris_.end())
354     return std::string();
355   sync_pb::NigoriKey key;
356   if (!iter->second->ExportKeys(key.mutable_user_key(),
357                                 key.mutable_encryption_key(),
358                                 key.mutable_mac_key()))
359     return std::string();
360   return key.SerializeAsString();
361 }
362 
ImportNigoriKey(const std::string serialized_nigori_key)363 bool Cryptographer::ImportNigoriKey(const std::string serialized_nigori_key) {
364   if (serialized_nigori_key.empty())
365     return false;
366 
367   sync_pb::NigoriKey key;
368   if (!key.ParseFromString(serialized_nigori_key))
369     return false;
370 
371   scoped_ptr<Nigori> nigori(new Nigori);
372   if (!nigori->InitByImport(key.user_key(), key.encryption_key(),
373                             key.mac_key())) {
374     NOTREACHED();
375     return false;
376   }
377 
378   if (!AddKeyImpl(nigori.Pass(), true))
379     return false;
380   return true;
381 }
382 
383 }  // namespace syncer
384