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