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