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