| // Copyright 2010-2015, 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 |