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(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 
     48 Cryptographer::~Cryptographer() {}
     49 
     50 
     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 
     64 bool Cryptographer::CanDecrypt(const sync_pb::EncryptedData& data) const {
     65   return nigoris_.end() != nigoris_.find(data.key_name());
     66 }
     67 
     68 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 
     74 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 
     92 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 
    119 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 
    126 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 
    142 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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    240 const sync_pb::EncryptedData& Cryptographer::GetPendingKeys() const {
    241   DCHECK(has_pending_keys());
    242   return *(pending_keys_.get());
    243 }
    244 
    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 
    270 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 
    287 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 
    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 
    324 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 
    345 std::string Cryptographer::GetDefaultNigoriKeyName() const {
    346   return default_nigori_name_;
    347 }
    348 
    349 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 
    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