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