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