nigori.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
1dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project// Use of this source code is governed by a BSD-style license that can be 3dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project// found in the LICENSE file. 4dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 5dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "chrome/browser/sync/util/nigori.h" 6dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 7dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#if defined(OS_WIN) 8dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <winsock2.h> // for htonl 9dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#else 10dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <arpa/inet.h> 11dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#endif 12dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 13dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <sstream> 14dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <vector> 15dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 16dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "base/base64.h" 17dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "base/logging.h" 18dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "base/rand_util.h" 19dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "base/string_util.h" 20dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "crypto/encryptor.h" 21dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "crypto/hmac.h" 22dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 23dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectusing base::Base64Encode; 24dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectusing base::Base64Decode; 25dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectusing base::RandInt; 26dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectusing crypto::Encryptor; 27dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectusing crypto::HMAC; 28dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectusing crypto::SymmetricKey; 29dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 30dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectnamespace browser_sync { 31dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 32dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project// NigoriStream simplifies the concatenation operation of the Nigori protocol. 33dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectclass NigoriStream { 34dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project public: 35dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Append the big-endian representation of the length of |value| with 32 bits, 36dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // followed by |value| itself to the stream. 37dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project NigoriStream& operator<<(const std::string& value) { 38dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project uint32 size = htonl(value.size()); 39dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project stream_.write((char *) &size, sizeof(uint32)); 40dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project stream_ << value; 41dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return *this; 42dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 43dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 44dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Append the big-endian representation of the length of |type| with 32 bits, 45dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // followed by the big-endian representation of the value of |type|, with 32 46dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // bits, to the stream. 47dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project NigoriStream& operator<<(const Nigori::Type type) { 48dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project uint32 size = htonl(sizeof(uint32)); 49dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project stream_.write((char *) &size, sizeof(uint32)); 50dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project uint32 value = htonl(type); 51dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project stream_.write((char *) &value, sizeof(uint32)); 52dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return *this; 53dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 54dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 55dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project std::string str() { 56dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return stream_.str(); 57dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 58dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 59dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private: 60dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project std::ostringstream stream_; 61dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project}; 62dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 63dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project// static 64dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectconst char Nigori::kSaltSalt[] = "saltsalt"; 65dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 66dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source ProjectNigori::Nigori() { 67dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project} 68dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 69dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source ProjectNigori::~Nigori() { 70dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project} 71dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 72dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectbool Nigori::InitByDerivation(const std::string& hostname, 73dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project const std::string& username, 74dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project const std::string& password) { 75dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project NigoriStream salt_password; 76dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project salt_password << username << hostname; 77dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 78dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Suser = PBKDF2(Username || Servername, "saltsalt", Nsalt, 8) 79dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project scoped_ptr<SymmetricKey> user_salt(SymmetricKey::DeriveKeyFromPassword( 80dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project SymmetricKey::HMAC_SHA1, salt_password.str(), 81dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project kSaltSalt, 82dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project kSaltIterations, 83dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project kSaltKeySizeInBits)); 84dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project DCHECK(user_salt.get()); 85dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 86dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project std::string raw_user_salt; 87dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (!user_salt->GetRawKey(&raw_user_salt)) 88dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return false; 89dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 90dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Kuser = PBKDF2(P, Suser, Nuser, 16) 91dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project user_key_.reset(SymmetricKey::DeriveKeyFromPassword(SymmetricKey::AES, 92dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project password, raw_user_salt, kUserIterations, kDerivedKeySizeInBits)); 93dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project DCHECK(user_key_.get()); 94dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 95dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Kenc = PBKDF2(P, Suser, Nenc, 16) 96dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project encryption_key_.reset(SymmetricKey::DeriveKeyFromPassword(SymmetricKey::AES, 97dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project password, raw_user_salt, kEncryptionIterations, kDerivedKeySizeInBits)); 98dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project DCHECK(encryption_key_.get()); 99dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 100dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Kmac = PBKDF2(P, Suser, Nmac, 16) 101dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mac_key_.reset(SymmetricKey::DeriveKeyFromPassword( 102dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project SymmetricKey::HMAC_SHA1, password, raw_user_salt, kSigningIterations, 103dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project kDerivedKeySizeInBits)); 104dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project DCHECK(mac_key_.get()); 105dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 106dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return true; 107dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project} 108dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 109dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectbool Nigori::InitByImport(const std::string& user_key, 110dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project const std::string& encryption_key, 111dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project const std::string& mac_key) { 112dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project user_key_.reset(SymmetricKey::Import(SymmetricKey::AES, user_key)); 113dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project DCHECK(user_key_.get()); 114dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 115dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project encryption_key_.reset(SymmetricKey::Import(SymmetricKey::AES, 116dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project encryption_key)); 117dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project DCHECK(encryption_key_.get()); 118dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 119dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mac_key_.reset(SymmetricKey::Import(SymmetricKey::HMAC_SHA1, mac_key)); 120dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project DCHECK(mac_key_.get()); 121dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 122dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return user_key_.get() && encryption_key_.get() && mac_key_.get(); 123dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project} 124dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 125dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project// Permute[Kenc,Kmac](type || name) 126dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectbool Nigori::Permute(Type type, const std::string& name, 127dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project std::string* permuted) const { 128dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project DCHECK_LT(0U, name.size()); 129dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 130dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project NigoriStream plaintext; 131dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 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 161std::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) 170bool 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 203bool 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 248bool 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