diff options
Diffstat (limited to 'chrome/browser/sync/util/nigori.cc')
-rw-r--r-- | chrome/browser/sync/util/nigori.cc | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/chrome/browser/sync/util/nigori.cc b/chrome/browser/sync/util/nigori.cc new file mode 100644 index 0000000..01de18d --- /dev/null +++ b/chrome/browser/sync/util/nigori.cc @@ -0,0 +1,231 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/util/nigori.h" + +#if defined(OS_WIN) +#include <winsock2.h> // for htonl +#endif + +#include <sstream> +#include <vector> + +#include "base/base64.h" +#include "base/crypto/encryptor.h" +#include "base/hmac.h" +#include "base/logging.h" +#include "base/rand_util.h" +#include "base/string_util.h" + +using base::Base64Encode; +using base::Base64Decode; +using base::Encryptor; +using base::HMAC; +using base::RandInt; +using base::SymmetricKey; + +namespace browser_sync { + +// NigoriStream simplifies the concatenation operation of the Nigori protocol. +class NigoriStream { + public: + // Append the big-endian representation of the length of |value| with 32 bits, + // followed by |value| itself to the stream. + NigoriStream& operator<<(const std::string& value) { + uint32 size = htonl(value.size()); + stream_.write((char *) &size, sizeof(uint32)); + stream_ << value; + return *this; + } + + // Append the big-endian representation of the length of |type| with 32 bits, + // followed by the big-endian representation of the value of |type|, with 32 + // bits, to the stream. + NigoriStream& operator<<(const Nigori::Type type) { + uint32 size = htonl(sizeof(uint32)); + stream_.write((char *) &size, sizeof(uint32)); + uint32 value = htonl(type); + stream_.write((char *) &value, sizeof(uint32)); + return *this; + } + + std::string str() { + return stream_.str(); + } + + private: + std::ostringstream stream_; +}; + +// static +const char Nigori::kSaltSalt[] = "saltsalt"; + +Nigori::Nigori(const std::string& hostname) + : hostname_(hostname) { +} + +Nigori::~Nigori() { +} + +bool Nigori::Init(const std::string& username, const std::string& password) { + username_ = username; + password_ = password; + + NigoriStream salt_password; + salt_password << username << hostname_; + + // Suser = PBKDF2(Username || Servername, "saltsalt", Nsalt, 8) + scoped_ptr<SymmetricKey> user_salt(SymmetricKey::DeriveKeyFromPassword( + SymmetricKey::HMAC_SHA1, salt_password.str(), + kSaltSalt, + kSaltIterations, + kSaltKeySizeInBits)); + DCHECK(user_salt.get()); + + std::string raw_user_salt; + user_salt->GetRawKey(&raw_user_salt); + + // Kuser = PBKDF2(P, Suser, Nuser, 16) + user_key_.reset(SymmetricKey::DeriveKeyFromPassword(SymmetricKey::AES, + password, raw_user_salt, kUserIterations, kDerivedKeySizeInBits)); + DCHECK(user_key_.get()); + + // Kenc = PBKDF2(P, Suser, Nenc, 16) + encryption_key_.reset(SymmetricKey::DeriveKeyFromPassword(SymmetricKey::AES, + password, raw_user_salt, kEncryptionIterations, kDerivedKeySizeInBits)); + DCHECK(encryption_key_.get()); + + // Kmac = PBKDF2(P, Suser, Nmac, 16) + mac_key_.reset(SymmetricKey::DeriveKeyFromPassword( + SymmetricKey::HMAC_SHA1, password, raw_user_salt, kSigningIterations, + kDerivedKeySizeInBits)); + DCHECK(mac_key_.get()); + + return true; +} + +// Permute[Kenc,Kmac](type || name) +bool Nigori::Permute(Type type, const std::string& name, + std::string* permuted) const { + DCHECK_LT(0U, name.size()); + + NigoriStream plaintext; + plaintext << type << name; + + Encryptor encryptor; + if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, + std::string(kIvSize, 0))) + return false; + + std::string ciphertext; + if (!encryptor.Encrypt(plaintext.str(), &ciphertext)) + return false; + + std::string raw_mac_key; + if (!mac_key_->GetRawKey(&raw_mac_key)) + return false; + + HMAC hmac(HMAC::SHA256); + if (!hmac.Init(raw_mac_key)) + return false; + + std::vector<unsigned char> hash(kHashSize); + if (!hmac.Sign(ciphertext, &hash[0], hash.size())) + return false; + + std::string output; + output.assign(ciphertext); + output.append(hash.begin(), hash.end()); + + return Base64Encode(output, permuted); +} + +std::string GenerateRandomString(size_t size) { + // TODO(albertb): Use a secure random function. + std::string random(size, 0); + for (size_t i = 0; i < size; ++i) + random[i] = RandInt(0, 0xff); + return random; +} + +// Enc[Kenc,Kmac](value) +bool Nigori::Encrypt(const std::string& value, std::string* encrypted) const { + DCHECK_LT(0U, value.size()); + + std::string iv = GenerateRandomString(kIvSize); + + Encryptor encryptor; + if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, iv)) + return false; + + std::string ciphertext; + if (!encryptor.Encrypt(value, &ciphertext)) + return false; + + std::string raw_mac_key; + if (!mac_key_->GetRawKey(&raw_mac_key)) + return false; + + HMAC hmac(HMAC::SHA256); + if (!hmac.Init(raw_mac_key)) + return false; + + std::vector<unsigned char> hash(kHashSize); + if (!hmac.Sign(ciphertext, &hash[0], hash.size())) + return false; + + std::string output; + output.assign(iv); + output.append(ciphertext); + output.append(hash.begin(), hash.end()); + + return Base64Encode(output, encrypted); +} + +bool Nigori::Decrypt(const std::string& encrypted, std::string* value) const { + std::string input; + if (!Base64Decode(encrypted, &input)) + return false; + + if (input.size() < kIvSize * 2 + kHashSize) + return false; + + // The input is: + // * iv (16 bytes) + // * ciphertext (multiple of 16 bytes) + // * hash (32 bytes) + std::string iv(input.substr(0, kIvSize)); + std::string ciphertext(input.substr(kIvSize, + input.size() - (kIvSize + kHashSize))); + std::string hash(input.substr(input.size() - kHashSize, kHashSize)); + + std::string raw_mac_key; + if (!mac_key_->GetRawKey(&raw_mac_key)) + return false; + + HMAC hmac(HMAC::SHA256); + if (!hmac.Init(raw_mac_key)) + return false; + + std::vector<unsigned char> expected(kHashSize); + if (!hmac.Sign(ciphertext, &expected[0], expected.size())) + return false; + + if (hash.compare(0, hash.size(), + reinterpret_cast<char *>(&expected[0]), + expected.size())) + return false; + + Encryptor encryptor; + if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, iv)) + return false; + + std::string plaintext; + if (!encryptor.Decrypt(ciphertext, value)) + return false; + + return true; +} + +} // namespace browser_sync |