• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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