summaryrefslogtreecommitdiffstats
path: root/components/os_crypt
diff options
context:
space:
mode:
authortfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-18 01:34:54 +0000
committertfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-18 01:34:54 +0000
commitc523d20e1db8fb7fc003686f336b958605e8a58f (patch)
tree6995a795f122aa0abc6d71befb21149ed9232877 /components/os_crypt
parent77d2fa7ea388573238e8503ecbfa50974a5372f2 (diff)
downloadchromium_src-c523d20e1db8fb7fc003686f336b958605e8a58f.zip
chromium_src-c523d20e1db8fb7fc003686f336b958605e8a58f.tar.gz
chromium_src-c523d20e1db8fb7fc003686f336b958605e8a58f.tar.bz2
components: Rename encryptor directory to os_crypt.
Joi suggested doing this rename in a follow up CL ( https://codereview.chromium.org/183953005/diff/20001/components/encryptor/encryptor_password_mac.h#newcode16). So here it is. BUG=341293 TEST=None, no functional changes. R=joi@chromium.org,thestig@chromium.org,bcwhite@chromium.org Review URL: https://codereview.chromium.org/200713005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@257573 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/os_crypt')
-rw-r--r--components/os_crypt/DEPS4
-rw-r--r--components/os_crypt/OWNERS1
-rw-r--r--components/os_crypt/README4
-rw-r--r--components/os_crypt/ie7_password_win.cc148
-rw-r--r--components/os_crypt/ie7_password_win.h54
-rw-r--r--components/os_crypt/ie7_password_win_unittest.cc90
-rw-r--r--components/os_crypt/keychain_password_mac.h36
-rw-r--r--components/os_crypt/keychain_password_mac.mm79
-rw-r--r--components/os_crypt/keychain_password_mac_unittest.mm80
-rw-r--r--components/os_crypt/os_crypt.h48
-rw-r--r--components/os_crypt/os_crypt_mac.mm155
-rw-r--r--components/os_crypt/os_crypt_posix.cc139
-rw-r--r--components/os_crypt/os_crypt_switches.cc17
-rw-r--r--components/os_crypt/os_crypt_switches.h26
-rw-r--r--components/os_crypt/os_crypt_unittest.cc144
-rw-r--r--components/os_crypt/os_crypt_win.cc66
16 files changed, 1091 insertions, 0 deletions
diff --git a/components/os_crypt/DEPS b/components/os_crypt/DEPS
new file mode 100644
index 0000000..d175bda
--- /dev/null
+++ b/components/os_crypt/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "-content",
+ "+crypto"
+]
diff --git a/components/os_crypt/OWNERS b/components/os_crypt/OWNERS
new file mode 100644
index 0000000..1967bf5
--- /dev/null
+++ b/components/os_crypt/OWNERS
@@ -0,0 +1 @@
+thestig@chromium.org
diff --git a/components/os_crypt/README b/components/os_crypt/README
new file mode 100644
index 0000000..14bcf15
--- /dev/null
+++ b/components/os_crypt/README
@@ -0,0 +1,4 @@
+OSCrypt gives access to simple encryption and decryption of strings.
+
+On systems where available (currently Linux and Mac), this uses system
+services to perform the encryption/decryption.
diff --git a/components/os_crypt/ie7_password_win.cc b/components/os_crypt/ie7_password_win.cc
new file mode 100644
index 0000000..01569b1
--- /dev/null
+++ b/components/os_crypt/ie7_password_win.cc
@@ -0,0 +1,148 @@
+// Copyright 2014 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 "components/os_crypt/ie7_password_win.h"
+
+#include <wincrypt.h>
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/sha1.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+
+namespace {
+
+// Structures that IE7/IE8 use to store a username/password.
+// Some of the fields might have been incorrectly reverse engineered.
+struct PreHeader {
+ DWORD pre_header_size; // Size of this header structure. Always 12.
+ DWORD header_size; // Size of the real Header: sizeof(Header) +
+ // item_count * sizeof(Entry);
+ DWORD data_size; // Size of the data referenced by the entries.
+};
+
+struct Header {
+ char wick[4]; // The string "WICK". I don't know what it means.
+ DWORD fixed_header_size; // The size of this structure without the entries:
+ // sizeof(Header).
+ DWORD item_count; // Number of entries. Should be even.
+ wchar_t two_letters[2]; // Two unknown bytes.
+ DWORD unknown[2]; // Two unknown DWORDs.
+};
+
+struct Entry {
+ DWORD offset; // Offset where the data referenced by this entry is
+ // located.
+ FILETIME time_stamp; // Timestamp when the password got added.
+ DWORD string_length; // The length of the data string.
+};
+
+// Main data structure.
+struct PasswordEntry {
+ PreHeader pre_header; // Contains the size of the different sections.
+ Header header; // Contains the number of items.
+ Entry entry[1]; // List of entries containing a string. Even-indexed
+ // are usernames, odd are passwords. There may be
+ // several sets saved for a single url hash.
+};
+} // namespace
+
+namespace ie7_password {
+
+bool GetUserPassFromData(const std::vector<unsigned char>& data,
+ std::vector<DecryptedCredentials>* credentials) {
+ const PasswordEntry* information =
+ reinterpret_cast<const PasswordEntry*>(&data.front());
+
+ // Some expected values. If it's not what we expect we don't even try to
+ // understand the data.
+ if (information->pre_header.pre_header_size != sizeof(PreHeader))
+ return false;
+
+ const int entry_count = information->header.item_count;
+ if (entry_count % 2) // Usernames and Passwords
+ return false;
+
+ if (information->header.fixed_header_size != sizeof(Header))
+ return false;
+
+ const uint8* offset_to_data = &data[0] +
+ information->pre_header.header_size +
+ information->pre_header.pre_header_size;
+
+ for (int i = 0; i < entry_count / 2; ++i) {
+
+ const Entry* user_entry = &information->entry[2*i];
+ const Entry* pass_entry = user_entry+1;
+
+ DecryptedCredentials c;
+ c.username = reinterpret_cast<const wchar_t*>(offset_to_data +
+ user_entry->offset);
+ c.password = reinterpret_cast<const wchar_t*>(offset_to_data +
+ pass_entry->offset);
+ credentials->push_back(c);
+ }
+ return true;
+}
+
+std::wstring GetUrlHash(const std::wstring& url) {
+ std::wstring lower_case_url = StringToLowerASCII(url);
+ // Get a data buffer out of our std::wstring to pass to SHA1HashString.
+ std::string url_buffer(
+ reinterpret_cast<const char*>(lower_case_url.c_str()),
+ (lower_case_url.size() + 1) * sizeof(wchar_t));
+ std::string hash_bin = base::SHA1HashString(url_buffer);
+
+ std::wstring url_hash;
+
+ // Transform the buffer to an hexadecimal string.
+ unsigned char checksum = 0;
+ for (size_t i = 0; i < hash_bin.size(); ++i) {
+ // std::string gives signed chars, which mess with StringPrintf and
+ // check_sum.
+ unsigned char hash_byte = static_cast<unsigned char>(hash_bin[i]);
+ checksum += hash_byte;
+ url_hash += base::StringPrintf(L"%2.2X", static_cast<unsigned>(hash_byte));
+ }
+ url_hash += base::StringPrintf(L"%2.2X", checksum);
+
+ return url_hash;
+}
+
+bool DecryptPasswords(const std::wstring& url,
+ const std::vector<unsigned char>& data,
+ std::vector<DecryptedCredentials>* credentials) {
+ std::wstring lower_case_url = StringToLowerASCII(url);
+ DATA_BLOB input = {0};
+ DATA_BLOB output = {0};
+ DATA_BLOB url_key = {0};
+
+ input.pbData = const_cast<unsigned char*>(&data.front());
+ input.cbData = static_cast<DWORD>((data.size()) *
+ sizeof(std::string::value_type));
+
+ url_key.pbData = reinterpret_cast<unsigned char*>(
+ const_cast<wchar_t*>(lower_case_url.data()));
+ url_key.cbData = static_cast<DWORD>((lower_case_url.size() + 1) *
+ sizeof(std::wstring::value_type));
+
+ if (CryptUnprotectData(&input, NULL, &url_key, NULL, NULL,
+ CRYPTPROTECT_UI_FORBIDDEN, &output)) {
+ // Now that we have the decrypted information, we need to understand it.
+ std::vector<unsigned char> decrypted_data;
+ decrypted_data.resize(output.cbData);
+ memcpy(&decrypted_data.front(), output.pbData, output.cbData);
+
+ GetUserPassFromData(decrypted_data, credentials);
+
+ LocalFree(output.pbData);
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace ie7_password
diff --git a/components/os_crypt/ie7_password_win.h b/components/os_crypt/ie7_password_win.h
new file mode 100644
index 0000000..459fd92
--- /dev/null
+++ b/components/os_crypt/ie7_password_win.h
@@ -0,0 +1,54 @@
+// Copyright 2014 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.
+
+#ifndef COMPONENTS_OS_CRYPT_IE7_PASSWORD_WIN_H_
+#define COMPONENTS_OS_CRYPT_IE7_PASSWORD_WIN_H_
+
+#include <windows.h>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/time/time.h"
+
+// Contains the information read from the IE7/IE8 Storage2 key in the registry.
+struct IE7PasswordInfo {
+ // Hash of the url.
+ std::wstring url_hash;
+
+ // Encrypted data containing the username, password and some more undocumented
+ // fields.
+ std::vector<unsigned char> encrypted_data;
+
+ // When the login was imported.
+ base::Time date_created;
+};
+
+namespace ie7_password {
+
+struct DecryptedCredentials {
+ std::wstring username;
+ std::wstring password;
+};
+
+// Parses a data structure to find passwords and usernames.
+// The collection of bytes in |data| is interpreted as a special PasswordEntry
+// structure. IE saves the login information as a binary dump of this structure.
+// Credentials extracted from |data| end up in |credentials|.
+bool GetUserPassFromData(const std::vector<unsigned char>& data,
+ std::vector<DecryptedCredentials>* credentials);
+
+// Decrypts usernames and passwords for a given data vector using the url as
+// the key.
+// Output ends up in |credentials|.
+bool DecryptPasswords(const std::wstring& url,
+ const std::vector<unsigned char>& data,
+ std::vector<DecryptedCredentials>* credentials);
+
+// Returns the hash of a url.
+std::wstring GetUrlHash(const std::wstring& url);
+
+} // namespace ie7_password
+
+#endif // COMPONENTS_OS_CRYPT_IE7_PASSWORD_WIN_H_
diff --git a/components/os_crypt/ie7_password_win_unittest.cc b/components/os_crypt/ie7_password_win_unittest.cc
new file mode 100644
index 0000000..a17787c
--- /dev/null
+++ b/components/os_crypt/ie7_password_win_unittest.cc
@@ -0,0 +1,90 @@
+// Copyright 2014 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 "components/os_crypt/ie7_password_win.h"
+
+#include <windows.h>
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(IE7PasswordTest, GetUserPassword) {
+ // This is the unencrypted values of my keys under Storage2.
+ // The passwords have been manually changed to abcdef... but the size remains
+ // the same.
+ const unsigned char kData1[] =
+ "\x0c\x00\x00\x00\x38\x00\x00\x00\x2c\x00\x00\x00"
+ "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00"
+ "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x4e\xfa\x67\x76\x22\x94\xc8\x01"
+ "\x08\x00\x00\x00\x12\x00\x00\x00\x4e\xfa\x67\x76"
+ "\x22\x94\xc8\x01\x0c\x00\x00\x00\x61\x00\x62\x00"
+ "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00"
+ "\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00"
+ "\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00"
+ "\x6c\x00\x00\x00";
+
+ const unsigned char kData2[] =
+ "\x0c\x00\x00\x00\x38\x00\x00\x00\x24\x00\x00\x00"
+ "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00"
+ "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\xa8\xea\xf4\xe5\x9f\x9a\xc8\x01"
+ "\x09\x00\x00\x00\x14\x00\x00\x00\xa8\xea\xf4\xe5"
+ "\x9f\x9a\xc8\x01\x07\x00\x00\x00\x61\x00\x62\x00"
+ "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00"
+ "\x69\x00\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00"
+ "\x65\x00\x66\x00\x67\x00\x00\x00";
+
+ std::vector<unsigned char> decrypted_data1;
+ decrypted_data1.resize(arraysize(kData1));
+ memcpy(&decrypted_data1.front(), kData1, sizeof(kData1));
+
+ std::vector<unsigned char> decrypted_data2;
+ decrypted_data2.resize(arraysize(kData2));
+ memcpy(&decrypted_data2.front(), kData2, sizeof(kData2));
+
+ std::vector<ie7_password::DecryptedCredentials> credentials;
+ ASSERT_TRUE(ie7_password::GetUserPassFromData(decrypted_data1, &credentials));
+ ASSERT_EQ(1u, credentials.size());
+ EXPECT_EQ(L"abcdefgh", credentials[0].username);
+ EXPECT_EQ(L"abcdefghijkl", credentials[0].password);
+
+ credentials.clear();
+ ASSERT_TRUE(ie7_password::GetUserPassFromData(decrypted_data2, &credentials));
+ ASSERT_EQ(1u, credentials.size());
+ EXPECT_EQ(L"abcdefghi", credentials[0].username);
+ EXPECT_EQ(L"abcdefg", credentials[0].password);
+}
+
+TEST(IE7PasswordTest, GetThreeUserPasswords) {
+ // Unencrypted binary data holding 3 sets of credentials
+ const unsigned char kData[] =
+ "\x0c\x00\x00\x00\x78\x00\x00\x00\x48\x00\x00\x00\x57\x49\x43\x4b\x18"
+ "\x00\x00\x00\x06\x00\x00\x00\x5c\x00\x55\x00\x01\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\xc3\xf2\x4b\xda\x1d\xc4\xce\x01\x04\x00\x00"
+ "\x00\x0a\x00\x00\x00\xc3\xf2\x4b\xda\x1d\xc4\xce\x01\x06\x00\x00\x00"
+ "\x18\x00\x00\x00\xbe\x5e\xe9\xe1\x1d\xc4\xce\x01\x04\x00\x00\x00\x22"
+ "\x00\x00\x00\xbe\x5e\xe9\xe1\x1d\xc4\xce\x01\x06\x00\x00\x00\x30\x00"
+ "\x00\x00\x07\x50\x1f\xe6\x1d\xc4\xce\x01\x04\x00\x00\x00\x3a\x00\x00"
+ "\x00\x07\x50\x1f\xe6\x1d\xc4\xce\x01\x06\x00\x00\x00\x71\x00\x77\x00"
+ "\x65\x00\x72\x00\x00\x00\x71\x00\x77\x00\x65\x00\x72\x00\x74\x00\x79"
+ "\x00\x00\x00\x61\x00\x73\x00\x64\x00\x66\x00\x00\x00\x61\x00\x73\x00"
+ "\x64\x00\x66\x00\x67\x00\x68\x00\x00\x00\x7a\x00\x78\x00\x63\x00\x76"
+ "\x00\x00\x00\x7a\x00\x78\x00\x63\x00\x76\x00\x62\x00\x6e\x00\x00\x00";
+
+ std::vector<unsigned char> decrypted_data;
+ decrypted_data.resize(arraysize(kData));
+ memcpy(&decrypted_data.front(), kData, sizeof(kData));
+
+ std::vector<ie7_password::DecryptedCredentials> credentials;
+ ASSERT_TRUE(ie7_password::GetUserPassFromData(decrypted_data, &credentials));
+ ASSERT_EQ(3u, credentials.size());
+ EXPECT_EQ(L"qwer", credentials[0].username);
+ EXPECT_EQ(L"qwerty", credentials[0].password);
+ EXPECT_EQ(L"asdf", credentials[1].username);
+ EXPECT_EQ(L"asdfgh", credentials[1].password);
+ EXPECT_EQ(L"zxcv", credentials[2].username);
+ EXPECT_EQ(L"zxcvbn", credentials[2].password);
+}
diff --git a/components/os_crypt/keychain_password_mac.h b/components/os_crypt/keychain_password_mac.h
new file mode 100644
index 0000000..f044f04
--- /dev/null
+++ b/components/os_crypt/keychain_password_mac.h
@@ -0,0 +1,36 @@
+// Copyright 2014 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.
+
+#ifndef COMPONENTS_OS_CRYPT_KEYCHAIN_PASSWORD_MAC_H_
+#define COMPONENTS_OS_CRYPT_KEYCHAIN_PASSWORD_MAC_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace crypto {
+class AppleKeychain;
+}
+
+class KeychainPassword {
+ public:
+ explicit KeychainPassword(const crypto::AppleKeychain& keychain)
+ : keychain_(keychain) {
+ }
+
+ // Get the OSCrypt password for this system. If no password exists
+ // in the Keychain then one is generated, stored in the Mac keychain, and
+ // returned.
+ // If one exists then it is fetched from the Keychain and returned.
+ // If the user disallows access to the Keychain (or an error occurs) then an
+ // empty string is returned.
+ std::string GetPassword() const;
+
+ private:
+ const crypto::AppleKeychain& keychain_;
+
+ DISALLOW_COPY_AND_ASSIGN(KeychainPassword);
+};
+
+#endif // COMPONENTS_OS_CRYPT_KEYCHAIN_PASSWORD_MAC_H_
diff --git a/components/os_crypt/keychain_password_mac.mm b/components/os_crypt/keychain_password_mac.mm
new file mode 100644
index 0000000..d731cff
--- /dev/null
+++ b/components/os_crypt/keychain_password_mac.mm
@@ -0,0 +1,79 @@
+// Copyright 2014 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 "components/os_crypt/keychain_password_mac.h"
+
+#import <Security/Security.h>
+
+#include "base/base64.h"
+#include "base/mac/mac_logging.h"
+#include "base/rand_util.h"
+#include "crypto/apple_keychain.h"
+
+using crypto::AppleKeychain;
+
+namespace {
+
+// Generates a random password and adds it to the Keychain. The added password
+// is returned from the function. If an error occurs, an empty password is
+// returned.
+std::string AddRandomPasswordToKeychain(const AppleKeychain& keychain,
+ const std::string& service_name,
+ const std::string& account_name) {
+ // Generate a password with 128 bits of randomness.
+ const int kBytes = 128 / 8;
+ std::string password;
+ base::Base64Encode(base::RandBytesAsString(kBytes), &password);
+ void* password_data =
+ const_cast<void*>(static_cast<const void*>(password.data()));
+
+ OSStatus error = keychain.AddGenericPassword(NULL,
+ service_name.size(),
+ service_name.data(),
+ account_name.size(),
+ account_name.data(),
+ password.size(),
+ password_data,
+ NULL);
+
+ if (error != noErr) {
+ OSSTATUS_DLOG(ERROR, error) << "Keychain add failed";
+ return std::string();
+ }
+
+ return password;
+}
+
+} // namespace
+
+std::string KeychainPassword::GetPassword() const {
+ // These two strings ARE indeed user facing. But they are used to access
+ // the encryption keyword. So as to not lose encrypted data when system
+ // locale changes we DO NOT LOCALIZE.
+ const std::string service_name = "Chrome Safe Storage";
+ const std::string account_name = "Chrome";
+
+ UInt32 password_length = 0;
+ void* password_data = NULL;
+ OSStatus error = keychain_.FindGenericPassword(NULL,
+ service_name.size(),
+ service_name.data(),
+ account_name.size(),
+ account_name.data(),
+ &password_length,
+ &password_data,
+ NULL);
+
+ if (error == noErr) {
+ std::string password =
+ std::string(static_cast<char*>(password_data), password_length);
+ keychain_.ItemFreeContent(NULL, password_data);
+ return password;
+ } else if (error == errSecItemNotFound) {
+ return AddRandomPasswordToKeychain(keychain_, service_name, account_name);
+ } else {
+ OSSTATUS_DLOG(ERROR, error) << "Keychain lookup failed";
+ return std::string();
+ }
+}
diff --git a/components/os_crypt/keychain_password_mac_unittest.mm b/components/os_crypt/keychain_password_mac_unittest.mm
new file mode 100644
index 0000000..4e879df
--- /dev/null
+++ b/components/os_crypt/keychain_password_mac_unittest.mm
@@ -0,0 +1,80 @@
+// Copyright 2014 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 "components/os_crypt/keychain_password_mac.h"
+
+#include "crypto/mock_apple_keychain.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using crypto::MockAppleKeychain;
+
+// Test that if we have an existing password in the Keychain and we are
+// authorized by the user to read it then we get it back correctly.
+TEST(KeychainPasswordTest, FindPasswordSuccess) {
+ MockAppleKeychain keychain;
+ keychain.set_find_generic_result(noErr);
+ KeychainPassword password(keychain);
+ EXPECT_FALSE(password.GetPassword().empty());
+ EXPECT_FALSE(keychain.called_add_generic());
+ EXPECT_EQ(0, keychain.password_data_count());
+}
+
+// Test that if we do not have an existing password in the Keychain then it
+// gets added successfully and returned.
+TEST(KeychainPasswordTest, FindPasswordNotFound) {
+ MockAppleKeychain keychain;
+ keychain.set_find_generic_result(errSecItemNotFound);
+ KeychainPassword password(keychain);
+ EXPECT_EQ(24U, password.GetPassword().length());
+ EXPECT_TRUE(keychain.called_add_generic());
+ EXPECT_EQ(0, keychain.password_data_count());
+}
+
+// Test that if get denied access by the user then we return an empty password.
+// And we should not try to add one.
+TEST(KeychainPasswordTest, FindPasswordNotAuthorized) {
+ MockAppleKeychain keychain;
+ keychain.set_find_generic_result(errSecAuthFailed);
+ KeychainPassword password(keychain);
+ EXPECT_TRUE(password.GetPassword().empty());
+ EXPECT_FALSE(keychain.called_add_generic());
+ EXPECT_EQ(0, keychain.password_data_count());
+}
+
+// Test that if some random other error happens then we return an empty
+// password, and we should not try to add one.
+TEST(KeychainPasswordTest, FindPasswordOtherError) {
+ MockAppleKeychain keychain;
+ keychain.set_find_generic_result(errSecNotAvailable);
+ KeychainPassword password(keychain);
+ EXPECT_TRUE(password.GetPassword().empty());
+ EXPECT_FALSE(keychain.called_add_generic());
+ EXPECT_EQ(0, keychain.password_data_count());
+}
+
+// Test that subsequent additions to the keychain give different passwords.
+TEST(KeychainPasswordTest, PasswordsDiffer) {
+ MockAppleKeychain keychain1;
+ keychain1.set_find_generic_result(errSecItemNotFound);
+ KeychainPassword encryptor_password1(keychain1);
+ std::string password1 = encryptor_password1.GetPassword();
+ EXPECT_FALSE(password1.empty());
+ EXPECT_TRUE(keychain1.called_add_generic());
+ EXPECT_EQ(0, keychain1.password_data_count());
+
+ MockAppleKeychain keychain2;
+ keychain2.set_find_generic_result(errSecItemNotFound);
+ KeychainPassword encryptor_password2(keychain2);
+ std::string password2 = encryptor_password2.GetPassword();
+ EXPECT_FALSE(password2.empty());
+ EXPECT_TRUE(keychain2.called_add_generic());
+ EXPECT_EQ(0, keychain2.password_data_count());
+
+ // And finally check that the passwords are different.
+ EXPECT_NE(password1, password2);
+}
+
+} // namespace
diff --git a/components/os_crypt/os_crypt.h b/components/os_crypt/os_crypt.h
new file mode 100644
index 0000000..a75ccf9
--- /dev/null
+++ b/components/os_crypt/os_crypt.h
@@ -0,0 +1,48 @@
+// Copyright 2014 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.
+
+#ifndef COMPONENTS_OS_CRYPT_OS_CRYPT_H_
+#define COMPONENTS_OS_CRYPT_OS_CRYPT_H_
+
+#include <string>
+
+#include "base/strings/string16.h"
+
+// The OSCrypt class gives access to simple encryption and decryption of
+// strings. Note that on Mac, access to the system Keychain is required and
+// these calls can block the current thread to collect user input.
+class OSCrypt {
+ public:
+ // Encrypt a string16. The output (second argument) is really an array of
+ // bytes, but we're passing it back as a std::string.
+ static bool EncryptString16(const base::string16& plaintext,
+ std::string* ciphertext);
+
+ // Decrypt an array of bytes obtained with EncryptString16 back into a
+ // string16. Note that the input (first argument) is a std::string, so you
+ // need to first get your (binary) data into a string.
+ static bool DecryptString16(const std::string& ciphertext,
+ base::string16* plaintext);
+
+ // Encrypt a string.
+ static bool EncryptString(const std::string& plaintext,
+ std::string* ciphertext);
+
+ // Decrypt an array of bytes obtained with EnctryptString back into a string.
+ // Note that the input (first argument) is a std::string, so you need to first
+ // get your (binary) data into a string.
+ static bool DecryptString(const std::string& ciphertext,
+ std::string* plaintext);
+
+#if defined(OS_MACOSX)
+ // For unit testing purposes we instruct the Encryptor to use a mock Keychain
+ // on the Mac. The default is to use the real Keychain.
+ static void UseMockKeychain(bool use_mock);
+#endif
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(OSCrypt);
+};
+
+#endif // COMPONENTS_OS_CRYPT_OS_CRYPT_H_
diff --git a/components/os_crypt/os_crypt_mac.mm b/components/os_crypt/os_crypt_mac.mm
new file mode 100644
index 0000000..09448d7
--- /dev/null
+++ b/components/os_crypt/os_crypt_mac.mm
@@ -0,0 +1,155 @@
+// Copyright 2014 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 "components/os_crypt/os_crypt.h"
+
+#include <CommonCrypto/CommonCryptor.h> // for kCCBlockSizeAES128
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/os_crypt/keychain_password_mac.h"
+#include "components/os_crypt/os_crypt_switches.h"
+#include "crypto/apple_keychain.h"
+#include "crypto/encryptor.h"
+#include "crypto/symmetric_key.h"
+
+using crypto::AppleKeychain;
+
+namespace {
+
+// Salt for Symmetric key derivation.
+const char kSalt[] = "saltysalt";
+
+// Key size required for 128 bit AES.
+const size_t kDerivedKeySizeInBits = 128;
+
+// Constant for Symmetic key derivation.
+const size_t kEncryptionIterations = 1003;
+
+// TODO(dhollowa): Refactor to allow dependency injection of Keychain.
+static bool use_mock_keychain = false;
+
+// Prefix for cypher text returned by current encryption version. We prefix
+// the cypher text with this string so that future data migration can detect
+// this and migrate to different encryption without data loss.
+const char kEncryptionVersionPrefix[] = "v10";
+
+// Generates a newly allocated SymmetricKey object based on the password found
+// in the Keychain. The generated key is for AES encryption. Ownership of the
+// key is passed to the caller. Returns NULL key in the case password access
+// is denied or key generation error occurs.
+crypto::SymmetricKey* GetEncryptionKey() {
+ static bool mock_keychain_command_line_flag =
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ os_crypt::switches::kUseMockKeychain);
+
+ std::string password;
+ if (use_mock_keychain || mock_keychain_command_line_flag) {
+ password = "mock_password";
+ } else {
+ AppleKeychain keychain;
+ KeychainPassword encryptor_password(keychain);
+ password = encryptor_password.GetPassword();
+ }
+
+ if (password.empty())
+ return NULL;
+
+ std::string salt(kSalt);
+
+ // Create an encryption key from our password and salt.
+ scoped_ptr<crypto::SymmetricKey> encryption_key(
+ crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES,
+ password,
+ salt,
+ kEncryptionIterations,
+ kDerivedKeySizeInBits));
+ DCHECK(encryption_key.get());
+
+ return encryption_key.release();
+}
+
+} // namespace
+
+bool OSCrypt::EncryptString16(const base::string16& plaintext,
+ std::string* ciphertext) {
+ return EncryptString(base::UTF16ToUTF8(plaintext), ciphertext);
+}
+
+bool OSCrypt::DecryptString16(const std::string& ciphertext,
+ base::string16* plaintext) {
+ std::string utf8;
+ if (!DecryptString(ciphertext, &utf8))
+ return false;
+
+ *plaintext = base::UTF8ToUTF16(utf8);
+ return true;
+}
+
+bool OSCrypt::EncryptString(const std::string& plaintext,
+ std::string* ciphertext) {
+ if (plaintext.empty()) {
+ *ciphertext = std::string();
+ return true;
+ }
+
+ scoped_ptr<crypto::SymmetricKey> encryption_key(GetEncryptionKey());
+ if (!encryption_key.get())
+ return false;
+
+ std::string iv(kCCBlockSizeAES128, ' ');
+ crypto::Encryptor encryptor;
+ if (!encryptor.Init(encryption_key.get(), crypto::Encryptor::CBC, iv))
+ return false;
+
+ if (!encryptor.Encrypt(plaintext, ciphertext))
+ return false;
+
+ // Prefix the cypher text with version information.
+ ciphertext->insert(0, kEncryptionVersionPrefix);
+ return true;
+}
+
+bool OSCrypt::DecryptString(const std::string& ciphertext,
+ std::string* plaintext) {
+ if (ciphertext.empty()) {
+ *plaintext = std::string();
+ return true;
+ }
+
+ // Check that the incoming cyphertext was indeed encrypted with the expected
+ // version. If the prefix is not found then we'll assume we're dealing with
+ // old data saved as clear text and we'll return it directly.
+ // Credit card numbers are current legacy data, so false match with prefix
+ // won't happen.
+ if (ciphertext.find(kEncryptionVersionPrefix) != 0) {
+ *plaintext = ciphertext;
+ return true;
+ }
+
+ // Strip off the versioning prefix before decrypting.
+ std::string raw_ciphertext =
+ ciphertext.substr(strlen(kEncryptionVersionPrefix));
+
+ scoped_ptr<crypto::SymmetricKey> encryption_key(GetEncryptionKey());
+ if (!encryption_key.get())
+ return false;
+
+ std::string iv(kCCBlockSizeAES128, ' ');
+ crypto::Encryptor encryptor;
+ if (!encryptor.Init(encryption_key.get(), crypto::Encryptor::CBC, iv))
+ return false;
+
+ if (!encryptor.Decrypt(raw_ciphertext, plaintext))
+ return false;
+
+ return true;
+}
+
+void OSCrypt::UseMockKeychain(bool use_mock) {
+ use_mock_keychain = use_mock;
+}
+
diff --git a/components/os_crypt/os_crypt_posix.cc b/components/os_crypt/os_crypt_posix.cc
new file mode 100644
index 0000000..74e8db3
--- /dev/null
+++ b/components/os_crypt/os_crypt_posix.cc
@@ -0,0 +1,139 @@
+// Copyright 2014 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 "components/os_crypt/os_crypt.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "crypto/encryptor.h"
+#include "crypto/symmetric_key.h"
+
+namespace {
+
+// Salt for Symmetric key derivation.
+const char kSalt[] = "saltysalt";
+
+// Key size required for 128 bit AES.
+const size_t kDerivedKeySizeInBits = 128;
+
+// Constant for Symmetic key derivation.
+const size_t kEncryptionIterations = 1;
+
+// Size of initialization vector for AES 128-bit.
+const size_t kIVBlockSizeAES128 = 16;
+
+// Prefix for cypher text returned by obfuscation version. We prefix the
+// cyphertext with this string so that future data migration can detect
+// this and migrate to full encryption without data loss.
+const char kObfuscationPrefix[] = "v10";
+
+// Generates a newly allocated SymmetricKey object based a hard-coded password.
+// Ownership of the key is passed to the caller. Returns NULL key if a key
+// generation error occurs.
+crypto::SymmetricKey* GetEncryptionKey() {
+ // We currently "obfuscate" by encrypting and decrypting with hard-coded
+ // password. We need to improve this password situation by moving a secure
+ // password into a system-level key store.
+ // http://crbug.com/25404 and http://crbug.com/49115
+ std::string password = "peanuts";
+ std::string salt(kSalt);
+
+ // Create an encryption key from our password and salt.
+ scoped_ptr<crypto::SymmetricKey> encryption_key(
+ crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES,
+ password,
+ salt,
+ kEncryptionIterations,
+ kDerivedKeySizeInBits));
+ DCHECK(encryption_key.get());
+
+ return encryption_key.release();
+}
+
+} // namespace
+
+bool OSCrypt::EncryptString16(const base::string16& plaintext,
+ std::string* ciphertext) {
+ return EncryptString(base::UTF16ToUTF8(plaintext), ciphertext);
+}
+
+bool OSCrypt::DecryptString16(const std::string& ciphertext,
+ base::string16* plaintext) {
+ std::string utf8;
+ if (!DecryptString(ciphertext, &utf8))
+ return false;
+
+ *plaintext = base::UTF8ToUTF16(utf8);
+ return true;
+}
+
+bool OSCrypt::EncryptString(const std::string& plaintext,
+ std::string* ciphertext) {
+ // This currently "obfuscates" by encrypting with hard-coded password.
+ // We need to improve this password situation by moving a secure password
+ // into a system-level key store.
+ // http://crbug.com/25404 and http://crbug.com/49115
+
+ if (plaintext.empty()) {
+ *ciphertext = std::string();
+ return true;
+ }
+
+ scoped_ptr<crypto::SymmetricKey> encryption_key(GetEncryptionKey());
+ if (!encryption_key.get())
+ return false;
+
+ std::string iv(kIVBlockSizeAES128, ' ');
+ crypto::Encryptor encryptor;
+ if (!encryptor.Init(encryption_key.get(), crypto::Encryptor::CBC, iv))
+ return false;
+
+ if (!encryptor.Encrypt(plaintext, ciphertext))
+ return false;
+
+ // Prefix the cypher text with version information.
+ ciphertext->insert(0, kObfuscationPrefix);
+ return true;
+}
+
+bool OSCrypt::DecryptString(const std::string& ciphertext,
+ std::string* plaintext) {
+ // This currently "obfuscates" by encrypting with hard-coded password.
+ // We need to improve this password situation by moving a secure password
+ // into a system-level key store.
+ // http://crbug.com/25404 and http://crbug.com/49115
+
+ if (ciphertext.empty()) {
+ *plaintext = std::string();
+ return true;
+ }
+
+ // Check that the incoming cyphertext was indeed encrypted with the expected
+ // version. If the prefix is not found then we'll assume we're dealing with
+ // old data saved as clear text and we'll return it directly.
+ // Credit card numbers are current legacy data, so false match with prefix
+ // won't happen.
+ if (ciphertext.find(kObfuscationPrefix) != 0) {
+ *plaintext = ciphertext;
+ return true;
+ }
+
+ // Strip off the versioning prefix before decrypting.
+ std::string raw_ciphertext = ciphertext.substr(strlen(kObfuscationPrefix));
+
+ scoped_ptr<crypto::SymmetricKey> encryption_key(GetEncryptionKey());
+ if (!encryption_key.get())
+ return false;
+
+ std::string iv(kIVBlockSizeAES128, ' ');
+ crypto::Encryptor encryptor;
+ if (!encryptor.Init(encryption_key.get(), crypto::Encryptor::CBC, iv))
+ return false;
+
+ if (!encryptor.Decrypt(raw_ciphertext, plaintext))
+ return false;
+
+ return true;
+}
diff --git a/components/os_crypt/os_crypt_switches.cc b/components/os_crypt/os_crypt_switches.cc
new file mode 100644
index 0000000..6dffb39
--- /dev/null
+++ b/components/os_crypt/os_crypt_switches.cc
@@ -0,0 +1,17 @@
+// Copyright 2014 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 "components/os_crypt/os_crypt_switches.h"
+
+namespace os_crypt {
+namespace switches {
+
+#if defined(OS_MACOSX)
+
+const char kUseMockKeychain[] = "use-mock-keychain";
+
+#endif // OS_MACOSX
+
+} // namespace switches
+} // namespace os_crypt
diff --git a/components/os_crypt/os_crypt_switches.h b/components/os_crypt/os_crypt_switches.h
new file mode 100644
index 0000000..5529446
--- /dev/null
+++ b/components/os_crypt/os_crypt_switches.h
@@ -0,0 +1,26 @@
+// Copyright 2014 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.
+//
+#ifndef COMPONENTS_OS_CRYPT_OS_CRYPT_SWITCHES_H_
+#define COMPONENTS_OS_CRYPT_OS_CRYPT_SWITCHES_H_
+
+// Defines all the command-line switches used by the encryptor component.
+
+#include "build/build_config.h"
+
+namespace os_crypt {
+namespace switches {
+
+#if defined(OS_MACOSX)
+
+// Uses mock keychain for testing purposes, which prevents blocking dialogs
+// from causing timeouts.
+extern const char kUseMockKeychain[];
+
+#endif // OS_MACOSX
+
+} // namespace switches
+} // namespace os_crypt
+
+#endif // COMPONENTS_OS_CRYPT_OS_CRYPT_SWITCHES_H_
diff --git a/components/os_crypt/os_crypt_unittest.cc b/components/os_crypt/os_crypt_unittest.cc
new file mode 100644
index 0000000..ef9fde0
--- /dev/null
+++ b/components/os_crypt/os_crypt_unittest.cc
@@ -0,0 +1,144 @@
+// Copyright 2014 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 "components/os_crypt/os_crypt.h"
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class OSCryptTest : public testing::Test {
+ public:
+ OSCryptTest() {}
+
+ virtual void SetUp() OVERRIDE {
+#if defined(OS_MACOSX)
+ OSCrypt::UseMockKeychain(true);
+#endif
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OSCryptTest);
+};
+
+TEST_F(OSCryptTest, String16EncryptionDecryption) {
+ base::string16 plaintext;
+ base::string16 result;
+ std::string utf8_plaintext;
+ std::string utf8_result;
+ std::string ciphertext;
+
+ // Test borderline cases (empty strings).
+ EXPECT_TRUE(OSCrypt::EncryptString16(plaintext, &ciphertext));
+ EXPECT_TRUE(OSCrypt::DecryptString16(ciphertext, &result));
+ EXPECT_EQ(plaintext, result);
+
+ // Test a simple string.
+ plaintext = base::ASCIIToUTF16("hello");
+ EXPECT_TRUE(OSCrypt::EncryptString16(plaintext, &ciphertext));
+ EXPECT_TRUE(OSCrypt::DecryptString16(ciphertext, &result));
+ EXPECT_EQ(plaintext, result);
+
+ // Test a 16-byte aligned string. This previously hit a boundary error in
+ // base::OSCrypt::Crypt() on Mac.
+ plaintext = base::ASCIIToUTF16("1234567890123456");
+ EXPECT_TRUE(OSCrypt::EncryptString16(plaintext, &ciphertext));
+ EXPECT_TRUE(OSCrypt::DecryptString16(ciphertext, &result));
+ EXPECT_EQ(plaintext, result);
+
+ // Test Unicode.
+ base::char16 wchars[] = { 0xdbeb, 0xdf1b, 0x4e03, 0x6708, 0x8849,
+ 0x661f, 0x671f, 0x56db, 0x597c, 0x4e03,
+ 0x6708, 0x56db, 0x6708, 0xe407, 0xdbaf,
+ 0xdeb5, 0x4ec5, 0x544b, 0x661f, 0x671f,
+ 0x65e5, 0x661f, 0x671f, 0x4e94, 0xd8b1,
+ 0xdce1, 0x7052, 0x5095, 0x7c0b, 0xe586, 0};
+ plaintext = wchars;
+ utf8_plaintext = base::UTF16ToUTF8(plaintext);
+ EXPECT_EQ(plaintext, base::UTF8ToUTF16(utf8_plaintext));
+ EXPECT_TRUE(OSCrypt::EncryptString16(plaintext, &ciphertext));
+ EXPECT_TRUE(OSCrypt::DecryptString16(ciphertext, &result));
+ EXPECT_EQ(plaintext, result);
+ EXPECT_TRUE(OSCrypt::DecryptString(ciphertext, &utf8_result));
+ EXPECT_EQ(utf8_plaintext, base::UTF16ToUTF8(result));
+
+ EXPECT_TRUE(OSCrypt::EncryptString(utf8_plaintext, &ciphertext));
+ EXPECT_TRUE(OSCrypt::DecryptString16(ciphertext, &result));
+ EXPECT_EQ(plaintext, result);
+ EXPECT_TRUE(OSCrypt::DecryptString(ciphertext, &utf8_result));
+ EXPECT_EQ(utf8_plaintext, base::UTF16ToUTF8(result));
+}
+
+TEST_F(OSCryptTest, EncryptionDecryption) {
+ std::string plaintext;
+ std::string result;
+ std::string ciphertext;
+
+ // Test borderline cases (empty strings).
+ ASSERT_TRUE(OSCrypt::EncryptString(plaintext, &ciphertext));
+ ASSERT_TRUE(OSCrypt::DecryptString(ciphertext, &result));
+ EXPECT_EQ(plaintext, result);
+
+ // Test a simple string.
+ plaintext = "hello";
+ ASSERT_TRUE(OSCrypt::EncryptString(plaintext, &ciphertext));
+ ASSERT_TRUE(OSCrypt::DecryptString(ciphertext, &result));
+ EXPECT_EQ(plaintext, result);
+
+ // Make sure it null terminates.
+ plaintext.assign("hello", 3);
+ ASSERT_TRUE(OSCrypt::EncryptString(plaintext, &ciphertext));
+ ASSERT_TRUE(OSCrypt::DecryptString(ciphertext, &result));
+ EXPECT_EQ(plaintext, "hel");
+}
+
+TEST_F(OSCryptTest, CypherTextDiffers) {
+ std::string plaintext;
+ std::string result;
+ std::string ciphertext;
+
+ // Test borderline cases (empty strings).
+ ASSERT_TRUE(OSCrypt::EncryptString(plaintext, &ciphertext));
+ ASSERT_TRUE(OSCrypt::DecryptString(ciphertext, &result));
+ // |cyphertext| is empty on the Mac, different on Windows.
+ EXPECT_TRUE(ciphertext.empty() || plaintext != ciphertext);
+ EXPECT_EQ(plaintext, result);
+
+ // Test a simple string.
+ plaintext = "hello";
+ ASSERT_TRUE(OSCrypt::EncryptString(plaintext, &ciphertext));
+ ASSERT_TRUE(OSCrypt::DecryptString(ciphertext, &result));
+ EXPECT_NE(plaintext, ciphertext);
+ EXPECT_EQ(plaintext, result);
+
+ // Make sure it null terminates.
+ plaintext.assign("hello", 3);
+ ASSERT_TRUE(OSCrypt::EncryptString(plaintext, &ciphertext));
+ ASSERT_TRUE(OSCrypt::DecryptString(ciphertext, &result));
+ EXPECT_NE(plaintext, ciphertext);
+ EXPECT_EQ(result, "hel");
+}
+
+TEST_F(OSCryptTest, DecryptError) {
+ std::string plaintext;
+ std::string result;
+ std::string ciphertext;
+
+ // Test a simple string, messing with ciphertext prior to decrypting.
+ plaintext = "hello";
+ ASSERT_TRUE(OSCrypt::EncryptString(plaintext, &ciphertext));
+ EXPECT_NE(plaintext, ciphertext);
+ ASSERT_LT(4UL, ciphertext.size());
+ ciphertext[3] = ciphertext[3] + 1;
+ EXPECT_FALSE(OSCrypt::DecryptString(ciphertext, &result));
+ EXPECT_NE(plaintext, result);
+ EXPECT_TRUE(result.empty());
+}
+
+} // namespace
diff --git a/components/os_crypt/os_crypt_win.cc b/components/os_crypt/os_crypt_win.cc
new file mode 100644
index 0000000..1f4e749
--- /dev/null
+++ b/components/os_crypt/os_crypt_win.cc
@@ -0,0 +1,66 @@
+// Copyright 2014 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 "components/os_crypt/os_crypt.h"
+
+#include <windows.h>
+#include <wincrypt.h>
+
+#include "base/strings/utf_string_conversions.h"
+
+#pragma comment(lib, "crypt32.lib")
+
+bool OSCrypt::EncryptString16(const base::string16& plaintext,
+ std::string* ciphertext) {
+ return EncryptString(base::UTF16ToUTF8(plaintext), ciphertext);
+}
+
+bool OSCrypt::DecryptString16(const std::string& ciphertext,
+ base::string16* plaintext) {
+ std::string utf8;
+ if (!DecryptString(ciphertext, &utf8))
+ return false;
+
+ *plaintext = base::UTF8ToUTF16(utf8);
+ return true;
+}
+
+bool OSCrypt::EncryptString(const std::string& plaintext,
+ std::string* ciphertext) {
+ DATA_BLOB input;
+ input.pbData = const_cast<BYTE*>(
+ reinterpret_cast<const BYTE*>(plaintext.data()));
+ input.cbData = static_cast<DWORD>(plaintext.length());
+
+ DATA_BLOB output;
+ BOOL result = CryptProtectData(&input, L"", NULL, NULL, NULL,
+ 0, &output);
+ if (!result)
+ return false;
+
+ // this does a copy
+ ciphertext->assign(reinterpret_cast<std::string::value_type*>(output.pbData),
+ output.cbData);
+
+ LocalFree(output.pbData);
+ return true;
+}
+
+bool OSCrypt::DecryptString(const std::string& ciphertext,
+ std::string* plaintext) {
+ DATA_BLOB input;
+ input.pbData = const_cast<BYTE*>(
+ reinterpret_cast<const BYTE*>(ciphertext.data()));
+ input.cbData = static_cast<DWORD>(ciphertext.length());
+
+ DATA_BLOB output;
+ BOOL result = CryptUnprotectData(&input, NULL, NULL, NULL, NULL,
+ 0, &output);
+ if (!result)
+ return false;
+
+ plaintext->assign(reinterpret_cast<char*>(output.pbData), output.cbData);
+ LocalFree(output.pbData);
+ return true;
+}