blob: 6545ffdb56a016a6d30df8149ac11696e2e423a3 [file] [log] [blame]
// Copyright 2010-2014, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "base/encryptor.h"
#if defined(OS_WIN)
#include <windows.h>
#include <wincrypt.h>
#elif defined(OS_MACOSX)
#include <unistd.h>
#include <sys/types.h>
#else
#include <string.h>
#endif // platforms (OS_WIN, OS_MACOSX, ...)
#include "base/logging.h"
#include "base/password_manager.h"
#include "base/unverified_aes256.h"
#include "base/unverified_sha1.h"
#include "base/util.h"
#ifdef OS_MACOSX
#include "base/mac_util.h"
#endif // OS_MACOSX
using ::mozc::internal::UnverifiedSHA1;
namespace mozc {
namespace {
// By using SHA1, emulate Microsoft's CryptDerivePassword API
// http://msdn.microsoft.com/en-us/library/aa379916.aspx
// says:
// Let n be the required derived key length, in bytes.
// The derived key is the first n bytes of the hash value after
// the hash computation has been completed by CryptDeriveKey.
// If the hash is not a member of the SHA-2 family and the required key
// is for either 3DES or AES, the key is derived as follows:
//
// 1. Form a 64-byte buffer by repeating the constant 0x36 64 times.
// Let k be the length of the hash value that is represented by
// the input parameter hBaseData. Set the first k bytes of the
// buffer to the result of an XOR operation of the first k bytes
// of the buffer with the hash value that is represented by the
// input parameter hBaseData.
// 2. Form a 64-byte buffer by repeating the constant 0x5C 64 times.
// Set the first k bytes of the buffer to the result of an XOR
// operation of the first k bytes of the buffer with the hash
// value that is represented by the input parameter hBaseData.
// 3. Hash the result of step 1 by using the same hash algorithm
// as that used to compute the hash value that is represented
// by the hBaseData parameter.
// 4. Hash the result of step 2 by using the same hash algorithm as
// that used to compute the hash value that is represented by the
// hBaseData parameter.
// 5. Concatenate the result of step 3 with the result of step 4.
// 6. Use the first n bytes of the result of step 5 as the derived key.
string GetMSCryptDeriveKeyWithSHA1(const string &password,
const string &salt) {
uint8 buf1[64];
uint8 buf2[64];
// Step1.
memset(buf1, 0x36, sizeof(buf1));
// Step2.
memset(buf2, 0x5c, sizeof(buf2));
// Step 3 & 4
const string hash = UnverifiedSHA1::MakeDigest(password + salt);
for (size_t i = 0; i < hash.size(); ++i) {
buf1[i] ^= static_cast<uint8>(hash[i]);
buf2[i] ^= static_cast<uint8>(hash[i]);
}
// Step 5 & 6
const string result1(reinterpret_cast<const char *>(buf1), sizeof(buf1));
const string result2(reinterpret_cast<const char *>(buf2), sizeof(buf2));
return (UnverifiedSHA1::MakeDigest(result1) +
UnverifiedSHA1::MakeDigest(result2));
}
const size_t kBlockSize = 16; // 128 bit
const size_t kKeySize = 32; // 256 bit key length
} // namespace
// TODO(yukawa): Consider to maintain these data directly in Encryptor::Key or
// completely rewrite Encryptor::Key once encryptor_legacy.cc is deprecated.
struct Encryptor::Key::InternalData {
uint8 key[kKeySize];
uint8 iv[kBlockSize];
bool is_available;
InternalData()
: is_available(false) {
memset(key, '\0', arraysize(key));
memset(iv, '\0', arraysize(iv));
}
};
size_t Encryptor::Key::block_size() const {
return kBlockSize;
}
const uint8 *Encryptor::Key::iv() const {
return data_->iv;
}
size_t Encryptor::Key::iv_size() const {
return kBlockSize; // the same as block size
}
size_t Encryptor::Key::key_size() const {
return kKeySize * 8;
}
bool Encryptor::Key::IsAvailable() const {
return data_->is_available;
}
size_t Encryptor::Key::GetEncryptedSize(size_t size) const {
// Even when given size is already multples of 16, we add
// an extra block_size as a padding.
// The extra padding can allow us to know that the message decrypted
// is invalid or broken.
return (size / block_size() + 1) * block_size();
}
bool Encryptor::Key::DeriveFromPassword(const string &password,
const string &salt,
const uint8 *iv) {
if (IsAvailable()) {
LOG(WARNING) << "key is already set";
return false;
}
if (password.empty()) {
LOG(WARNING) << "password is empty";
return false;
}
if (iv != NULL) {
memcpy(data_->iv, iv, iv_size());
} else {
memset(data_->iv, '\0', iv_size());
}
const string key = GetMSCryptDeriveKeyWithSHA1(password, salt);
DCHECK_EQ(40, key.size()); // SHA1 is 160bit hash, so 160*2/8 = 40byte
// Store the session key.
// NOTE: key_size() returns size in bit for historical reasons.
memcpy(data_->key, key.data(), key_size() / 8);
data_->is_available = true;
return true;
}
Encryptor::Key::Key()
: data_(new Encryptor::Key::InternalData) {
}
Encryptor::Key::~Key() {
}
bool Encryptor::EncryptString(const Encryptor::Key &key, string *data) {
if (data == NULL || data->empty()) {
LOG(ERROR) << "data is NULL or empty";
return false;
}
size_t size = data->size();
scoped_ptr<char[]> buf(new char[key.GetEncryptedSize(data->size())]);
memcpy(buf.get(), data->data(), data->size());
if (!Encryptor::EncryptArray(key, buf.get(), &size)) {
LOG(ERROR) << "EncryptArray() failed";
return false;
}
data->assign(buf.get(), size);
return true;
}
bool Encryptor::DecryptString(const Encryptor::Key &key, string *data) {
if (data == NULL || data->empty()) {
LOG(ERROR) << "data is NULL or empty";
return false;
}
size_t size = data->size();
scoped_ptr<char[]> buf(new char[data->size()]);
memcpy(buf.get(), data->data(), data->size());
if (!Encryptor::DecryptArray(key, buf.get(), &size)) {
LOG(ERROR) << "DecryptArray() failed";
return false;
}
data->assign(buf.get(), size);
return true;
}
bool Encryptor::EncryptArray(const Encryptor::Key &key,
char *buf, size_t *buf_size) {
if (!key.IsAvailable()) {
LOG(ERROR) << "key is not available";
return false;
}
if (buf == NULL || buf_size == NULL || *buf_size == 0) {
LOG(ERROR) << "invalid buffer given";
return false;
}
const size_t enc_size = key.GetEncryptedSize(*buf_size);
// perform PKCS#5 padding
const size_t padding_size = enc_size - *buf_size;
const uint8 padding_value = static_cast<uint8>(padding_size);
for (size_t i = *buf_size; i < enc_size; ++i) {
buf[i] = static_cast<char>(padding_value);
}
// For historical reasons, we are using AES256/CBC for obfuscation.
internal::UnverifiedAES256::TransformCBC(key.data_->key,
key.data_->iv,
reinterpret_cast<uint8 *>(buf),
enc_size / kBlockSize);
*buf_size = enc_size;
return true;
}
bool Encryptor::DecryptArray(const Encryptor::Key &key,
char *buf, size_t *buf_size) {
if (!key.IsAvailable()) {
LOG(ERROR) << "key is not available";
return false;
}
if (buf == NULL || buf_size == NULL || *buf_size == 0) {
LOG(ERROR) << "invalid buffer given";
return false;
}
if (*buf_size < key.block_size() || *buf_size % key.block_size() != 0) {
LOG(ERROR) << "buf size is not multiples of " << key.block_size();
return false;
}
size_t size = *buf_size;
// For historical reasons, we are using AES256/CBC for obfuscation.
internal::UnverifiedAES256::InverseTransformCBC(
key.data_->key,
key.data_->iv,
reinterpret_cast<uint8 *>(buf),
size / kBlockSize);
// perform PKCS#5 un-padding
// see. http://www.chilkatsoft.com/faq/PKCS5_Padding.html
const uint8 padding_value = static_cast<uint8>(buf[size - 1]);
const size_t padding_size = static_cast<size_t>(padding_value);
if (padding_value == 0x00 || padding_value > kBlockSize) {
LOG(ERROR) << "Cannot find PKCS#5 padding values: ";
return false;
}
if (size <= padding_size) {
LOG(ERROR) << "padding size is no smaller than original message";
return false;
}
for (size_t i = size - padding_size; i < size; ++i) {
if (static_cast<uint8>(buf[i]) != padding_value) {
LOG(ERROR) << "invalid padding value. message is broken";
return false;
}
}
*buf_size -= padding_size; // remove padding part
return true;
}
// Protect|Unprotect Data
#ifdef OS_WIN
// See. http://msdn.microsoft.com/en-us/library/aa380261.aspx
bool Encryptor::ProtectData(const string &plain_text,
string *cipher_text) {
DCHECK(cipher_text);
DATA_BLOB input;
input.pbData = const_cast<BYTE *>(
reinterpret_cast<const BYTE *>(plain_text.data()));
input.cbData = static_cast<DWORD>(plain_text.size());
DATA_BLOB output;
const BOOL result = ::CryptProtectData(&input, L"",
NULL, NULL, NULL,
CRYPTPROTECT_UI_FORBIDDEN,
&output);
if (!result) {
LOG(ERROR) << "CryptProtectData failed: " << ::GetLastError();
return false;
}
cipher_text->assign(reinterpret_cast<char *>(output.pbData),
output.cbData);
::LocalFree(output.pbData);
return true;
}
// See. http://msdn.microsoft.com/en-us/library/aa380882(VS.85).aspx
bool Encryptor::UnprotectData(const string &cipher_text,
string *plain_text) {
DCHECK(plain_text);
DATA_BLOB input;
input.pbData = const_cast<BYTE *>(
reinterpret_cast<const BYTE*>(cipher_text.data()));
input.cbData = static_cast<DWORD>(cipher_text.length());
DATA_BLOB output;
const BOOL result = ::CryptUnprotectData(&input,
NULL, NULL, NULL, NULL,
CRYPTPROTECT_UI_FORBIDDEN,
&output);
if (!result) {
LOG(ERROR) << "CryptUnprotectData failed: " << ::GetLastError();
return false;
}
plain_text->assign(reinterpret_cast<char *>(output.pbData), output.cbData);
::LocalFree(output.pbData);
return true;
}
#elif defined(OS_MACOSX)
// ProtectData for Mac uses the serial number and the current pid as the key.
bool Encryptor::ProtectData(const string &plain_text,
string *cipher_text) {
DCHECK(cipher_text);
Encryptor::Key key;
const string serial_number = MacUtil::GetSerialNumber();
const string salt = Util::StringPrintf("%x", ::getuid());
if (serial_number.empty()) {
LOG(ERROR) << "Cannot get the serial number";
return false;
}
if (!key.DeriveFromPassword(serial_number, salt)) {
LOG(ERROR) << "Cannot prepare the internal key";
return false;
}
cipher_text->assign(plain_text);
if (!EncryptString(key, cipher_text)) {
cipher_text->clear();
LOG(ERROR) << "Cannot encrypt the text";
return false;
}
return true;
}
// Same as ProtectData.
bool Encryptor::UnprotectData(const string &cipher_text,
string *plain_text) {
DCHECK(plain_text);
Encryptor::Key key;
const string serial_number = MacUtil::GetSerialNumber();
const string salt = Util::StringPrintf("%x", ::getuid());
if (serial_number.empty()) {
LOG(ERROR) << "Cannot get the serial number";
return false;
}
if (!key.DeriveFromPassword(serial_number, salt)) {
LOG(ERROR) << "Cannot prepare the internal key";
return false;
}
plain_text->assign(cipher_text);
if (!DecryptString(key, plain_text)) {
plain_text->clear();
LOG(ERROR) << "Cannot encrypt the text";
return false;
}
return true;
}
#else // OS_WIN | OS_MACOSX
namespace {
const size_t kSaltSize = 32;
} // namespace
// Use AES to emulate ProtectData
bool Encryptor::ProtectData(const string &plain_text, string *cipher_text) {
string password;
if (!PasswordManager::GetPassword(&password)) {
LOG(ERROR) << "Cannot get password";
return false;
}
char salt_buf[kSaltSize];
Util::GetRandomSequence(salt_buf, sizeof(salt_buf));
string salt(salt_buf, kSaltSize);
Encryptor::Key key;
if (!key.DeriveFromPassword(password, salt)) {
LOG(ERROR) << "DeriveFromPassword failed";
return false;
}
string buf = plain_text;
if (!Encryptor::EncryptString(key, &buf)) {
LOG(ERROR) << "Encryptor::EncryptString failed";
return false;
}
cipher_text->clear();
*cipher_text += salt;
*cipher_text += buf;
return true;
}
// Use AES to emulate UnprotectData
bool Encryptor::UnprotectData(const string &cipher_text, string *plain_text) {
if (cipher_text.size() < kSaltSize) {
LOG(ERROR) << "encrypted message is too short";
return false;
}
string password;
if (!PasswordManager::GetPassword(&password)) {
LOG(ERROR) << "Cannot get password";
return false;
}
const string salt(cipher_text.data(), kSaltSize);
Encryptor::Key key;
if (!key.DeriveFromPassword(password, salt)) {
LOG(ERROR) << "DeriveFromPassword failed";
return false;
}
string buf(cipher_text.data() + kSaltSize,
cipher_text.size() - kSaltSize);
if (!Encryptor::DecryptString(key, &buf)) {
LOG(ERROR) << "Encryptor::DecryptString failed";
return false;
}
plain_text->clear();
*plain_text += buf;
return true;
}
#endif // OS_WIN
} // namespace mozc