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/nigori.h"
      6 
      7 #include <sstream>
      8 #include <vector>
      9 
     10 #include "base/base64.h"
     11 #include "base/logging.h"
     12 #include "base/strings/string_util.h"
     13 #include "base/sys_byteorder.h"
     14 #include "crypto/encryptor.h"
     15 #include "crypto/hmac.h"
     16 #include "crypto/random.h"
     17 #include "crypto/symmetric_key.h"
     18 
     19 using base::Base64Encode;
     20 using base::Base64Decode;
     21 using crypto::Encryptor;
     22 using crypto::HMAC;
     23 using crypto::SymmetricKey;
     24 
     25 namespace syncer {
     26 
     27 // NigoriStream simplifies the concatenation operation of the Nigori protocol.
     28 class NigoriStream {
     29  public:
     30   // Append the big-endian representation of the length of |value| with 32 bits,
     31   // followed by |value| itself to the stream.
     32   NigoriStream& operator<<(const std::string& value) {
     33     uint32 size = base::HostToNet32(value.size());
     34     stream_.write((char *) &size, sizeof(uint32));
     35     stream_ << value;
     36     return *this;
     37   }
     38 
     39   // Append the big-endian representation of the length of |type| with 32 bits,
     40   // followed by the big-endian representation of the value of |type|, with 32
     41   // bits, to the stream.
     42   NigoriStream& operator<<(const Nigori::Type type) {
     43     uint32 size = base::HostToNet32(sizeof(uint32));
     44     stream_.write((char *) &size, sizeof(uint32));
     45     uint32 value = base::HostToNet32(type);
     46     stream_.write((char *) &value, sizeof(uint32));
     47     return *this;
     48   }
     49 
     50   std::string str() {
     51     return stream_.str();
     52   }
     53 
     54  private:
     55   std::ostringstream stream_;
     56 };
     57 
     58 // static
     59 const char Nigori::kSaltSalt[] = "saltsalt";
     60 
     61 Nigori::Nigori() {
     62 }
     63 
     64 Nigori::~Nigori() {
     65 }
     66 
     67 bool Nigori::InitByDerivation(const std::string& hostname,
     68                               const std::string& username,
     69                               const std::string& password) {
     70   NigoriStream salt_password;
     71   salt_password << username << hostname;
     72 
     73   // Suser = PBKDF2(Username || Servername, "saltsalt", Nsalt, 8)
     74   scoped_ptr<SymmetricKey> user_salt(SymmetricKey::DeriveKeyFromPassword(
     75       SymmetricKey::HMAC_SHA1, salt_password.str(),
     76       kSaltSalt,
     77       kSaltIterations,
     78       kSaltKeySizeInBits));
     79   DCHECK(user_salt.get());
     80 
     81   std::string raw_user_salt;
     82   if (!user_salt->GetRawKey(&raw_user_salt))
     83     return false;
     84 
     85   // Kuser = PBKDF2(P, Suser, Nuser, 16)
     86   user_key_.reset(SymmetricKey::DeriveKeyFromPassword(SymmetricKey::AES,
     87       password, raw_user_salt, kUserIterations, kDerivedKeySizeInBits));
     88   DCHECK(user_key_.get());
     89 
     90   // Kenc = PBKDF2(P, Suser, Nenc, 16)
     91   encryption_key_.reset(SymmetricKey::DeriveKeyFromPassword(SymmetricKey::AES,
     92       password, raw_user_salt, kEncryptionIterations, kDerivedKeySizeInBits));
     93   DCHECK(encryption_key_.get());
     94 
     95   // Kmac = PBKDF2(P, Suser, Nmac, 16)
     96   mac_key_.reset(SymmetricKey::DeriveKeyFromPassword(
     97       SymmetricKey::HMAC_SHA1, password, raw_user_salt, kSigningIterations,
     98       kDerivedKeySizeInBits));
     99   DCHECK(mac_key_.get());
    100 
    101   return user_key_.get() && encryption_key_.get() && mac_key_.get();
    102 }
    103 
    104 bool Nigori::InitByImport(const std::string& user_key,
    105                           const std::string& encryption_key,
    106                           const std::string& mac_key) {
    107   user_key_.reset(SymmetricKey::Import(SymmetricKey::AES, user_key));
    108   DCHECK(user_key_.get());
    109 
    110   encryption_key_.reset(SymmetricKey::Import(SymmetricKey::AES,
    111                                              encryption_key));
    112   DCHECK(encryption_key_.get());
    113 
    114   mac_key_.reset(SymmetricKey::Import(SymmetricKey::HMAC_SHA1, mac_key));
    115   DCHECK(mac_key_.get());
    116 
    117   return user_key_.get() && encryption_key_.get() && mac_key_.get();
    118 }
    119 
    120 // Permute[Kenc,Kmac](type || name)
    121 bool Nigori::Permute(Type type, const std::string& name,
    122                      std::string* permuted) const {
    123   DCHECK_LT(0U, name.size());
    124 
    125   NigoriStream plaintext;
    126   plaintext << type << name;
    127 
    128   Encryptor encryptor;
    129   if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC,
    130                       std::string(kIvSize, 0)))
    131     return false;
    132 
    133   std::string ciphertext;
    134   if (!encryptor.Encrypt(plaintext.str(), &ciphertext))
    135     return false;
    136 
    137   std::string raw_mac_key;
    138   if (!mac_key_->GetRawKey(&raw_mac_key))
    139     return false;
    140 
    141   HMAC hmac(HMAC::SHA256);
    142   if (!hmac.Init(raw_mac_key))
    143     return false;
    144 
    145   std::vector<unsigned char> hash(kHashSize);
    146   if (!hmac.Sign(ciphertext, &hash[0], hash.size()))
    147     return false;
    148 
    149   std::string output;
    150   output.assign(ciphertext);
    151   output.append(hash.begin(), hash.end());
    152 
    153   Base64Encode(output, permuted);
    154   return true;
    155 }
    156 
    157 // Enc[Kenc,Kmac](value)
    158 bool Nigori::Encrypt(const std::string& value, std::string* encrypted) const {
    159   if (0U >= value.size())
    160     return false;
    161 
    162   std::string iv;
    163   crypto::RandBytes(WriteInto(&iv, kIvSize + 1), kIvSize);
    164 
    165   Encryptor encryptor;
    166   if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, iv))
    167     return false;
    168 
    169   std::string ciphertext;
    170   if (!encryptor.Encrypt(value, &ciphertext))
    171     return false;
    172 
    173   std::string raw_mac_key;
    174   if (!mac_key_->GetRawKey(&raw_mac_key))
    175     return false;
    176 
    177   HMAC hmac(HMAC::SHA256);
    178   if (!hmac.Init(raw_mac_key))
    179     return false;
    180 
    181   std::vector<unsigned char> hash(kHashSize);
    182   if (!hmac.Sign(ciphertext, &hash[0], hash.size()))
    183     return false;
    184 
    185   std::string output;
    186   output.assign(iv);
    187   output.append(ciphertext);
    188   output.append(hash.begin(), hash.end());
    189 
    190   Base64Encode(output, encrypted);
    191   return true;
    192 }
    193 
    194 bool Nigori::Decrypt(const std::string& encrypted, std::string* value) const {
    195   std::string input;
    196   if (!Base64Decode(encrypted, &input))
    197     return false;
    198 
    199   if (input.size() < kIvSize * 2 + kHashSize)
    200     return false;
    201 
    202   // The input is:
    203   // * iv (16 bytes)
    204   // * ciphertext (multiple of 16 bytes)
    205   // * hash (32 bytes)
    206   std::string iv(input.substr(0, kIvSize));
    207   std::string ciphertext(input.substr(kIvSize,
    208                                       input.size() - (kIvSize + kHashSize)));
    209   std::string hash(input.substr(input.size() - kHashSize, kHashSize));
    210 
    211   std::string raw_mac_key;
    212   if (!mac_key_->GetRawKey(&raw_mac_key))
    213     return false;
    214 
    215   HMAC hmac(HMAC::SHA256);
    216   if (!hmac.Init(raw_mac_key))
    217     return false;
    218 
    219   std::vector<unsigned char> expected(kHashSize);
    220   if (!hmac.Sign(ciphertext, &expected[0], expected.size()))
    221     return false;
    222 
    223   if (hash.compare(0, hash.size(),
    224                    reinterpret_cast<char *>(&expected[0]),
    225                    expected.size()))
    226     return false;
    227 
    228   Encryptor encryptor;
    229   if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, iv))
    230     return false;
    231 
    232   if (!encryptor.Decrypt(ciphertext, value))
    233     return false;
    234 
    235   return true;
    236 }
    237 
    238 bool Nigori::ExportKeys(std::string* user_key,
    239                         std::string* encryption_key,
    240                         std::string* mac_key) const {
    241   DCHECK(user_key);
    242   DCHECK(encryption_key);
    243   DCHECK(mac_key);
    244 
    245   return user_key_->GetRawKey(user_key) &&
    246       encryption_key_->GetRawKey(encryption_key) &&
    247       mac_key_->GetRawKey(mac_key);
    248 }
    249 
    250 }  // namespace syncer
    251