Home | History | Annotate | Download | only in util
      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 
     19 Cryptographer::Cryptographer() : default_nigori_(NULL) {
     20 }
     21 
     22 Cryptographer::~Cryptographer() {}
     23 
     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 
     35 bool Cryptographer::CanDecrypt(const sync_pb::EncryptedData& data) const {
     36   return nigoris_.end() != nigoris_.find(data.key_name());
     37 }
     38 
     39 bool Cryptographer::CanDecryptUsingDefaultKey(
     40     const sync_pb::EncryptedData& data) const {
     41   return default_nigori_ && (data.key_name() == default_nigori_->first);
     42 }
     43 
     44 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 
     64 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 
     71 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 
     87 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 
    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 
    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 
    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 
    144 void Cryptographer::SetPendingKeys(const sync_pb::EncryptedData& encrypted) {
    145   DCHECK(!CanDecrypt(encrypted));
    146   pending_keys_.reset(new sync_pb::EncryptedData(encrypted));
    147 }
    148 
    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 
    172 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 
    180 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 
    212 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 
    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