1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chromeos/cryptohome/cryptohome_library.h"
10 #include "base/memory/weak_ptr.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/sys_info.h"
14 #include "chromeos/dbus/cryptohome_client.h"
15 #include "chromeos/dbus/dbus_method_call_status.h"
16 #include "chromeos/dbus/dbus_thread_manager.h"
17 #include "crypto/encryptor.h"
18 #include "crypto/nss_util.h"
19 #include "crypto/sha2.h"
20 #include "crypto/symmetric_key.h"
26 const char kStubSystemSalt
[] = "stub_system_salt";
27 const size_t kNonceSize
= 16;
31 // This class handles the interaction with the ChromeOS cryptohome library APIs.
32 class CryptohomeLibraryImpl
: public CryptohomeLibrary
{
34 CryptohomeLibraryImpl() {
37 virtual ~CryptohomeLibraryImpl() {
40 virtual std::string
GetSystemSalt() OVERRIDE
{
41 LoadSystemSalt(); // no-op if it's already loaded.
45 virtual std::string
EncryptWithSystemSalt(const std::string
& token
) OVERRIDE
{
46 // Don't care about token encryption while debugging.
47 if (!base::SysInfo::IsRunningOnChromeOS())
50 if (!LoadSystemSaltKey()) {
51 LOG(WARNING
) << "System salt key is not available for encrypt.";
54 return EncryptTokenWithKey(system_salt_key_
.get(),
59 virtual std::string
DecryptWithSystemSalt(
60 const std::string
& encrypted_token_hex
) OVERRIDE
{
61 // Don't care about token encryption while debugging.
62 if (!base::SysInfo::IsRunningOnChromeOS())
63 return encrypted_token_hex
;
65 if (!LoadSystemSaltKey()) {
66 LOG(WARNING
) << "System salt key is not available for decrypt.";
69 return DecryptTokenWithKey(system_salt_key_
.get(),
75 void LoadSystemSalt() {
76 if (!system_salt_
.empty())
78 std::vector
<uint8
> salt
;
79 DBusThreadManager::Get()->GetCryptohomeClient()->GetSystemSalt(&salt
);
80 if (salt
.empty() || salt
.size() % 2 != 0U) {
81 LOG(WARNING
) << "System salt not available";
84 system_salt_
= StringToLowerASCII(base::HexEncode(
85 reinterpret_cast<const void*>(salt
.data()), salt
.size()));
88 // TODO: should this use the system salt for both the password and the salt
89 // value, or should this use a separate salt value?
90 bool LoadSystemSaltKey() {
91 if (system_salt_
.empty())
93 if (!system_salt_key_
.get())
94 system_salt_key_
.reset(PassphraseToKey(system_salt_
, system_salt_
));
95 return system_salt_key_
.get();
98 crypto::SymmetricKey
* PassphraseToKey(const std::string
& passphrase
,
99 const std::string
& salt
) {
100 return crypto::SymmetricKey::DeriveKeyFromPassword(
101 crypto::SymmetricKey::AES
, passphrase
, salt
, 1000, 256);
105 // Encrypts (AES) the token given |key| and |salt|.
106 std::string
EncryptTokenWithKey(crypto::SymmetricKey
* key
,
107 const std::string
& salt
,
108 const std::string
& token
) {
109 crypto::Encryptor encryptor
;
110 if (!encryptor
.Init(key
, crypto::Encryptor::CTR
, std::string())) {
111 LOG(WARNING
) << "Failed to initialize Encryptor.";
112 return std::string();
114 std::string nonce
= salt
.substr(0, kNonceSize
);
115 std::string encoded_token
;
116 CHECK(encryptor
.SetCounter(nonce
));
117 if (!encryptor
.Encrypt(token
, &encoded_token
)) {
118 LOG(WARNING
) << "Failed to encrypt token.";
119 return std::string();
122 return StringToLowerASCII(base::HexEncode(
123 reinterpret_cast<const void*>(encoded_token
.data()),
124 encoded_token
.size()));
127 // Decrypts (AES) hex encoded encrypted token given |key| and |salt|.
128 std::string
DecryptTokenWithKey(crypto::SymmetricKey
* key
,
129 const std::string
& salt
,
130 const std::string
& encrypted_token_hex
) {
131 std::vector
<uint8
> encrypted_token_bytes
;
132 if (!base::HexStringToBytes(encrypted_token_hex
, &encrypted_token_bytes
)) {
133 LOG(WARNING
) << "Corrupt encrypted token found.";
134 return std::string();
137 std::string
encrypted_token(
138 reinterpret_cast<char*>(encrypted_token_bytes
.data()),
139 encrypted_token_bytes
.size());
140 crypto::Encryptor encryptor
;
141 if (!encryptor
.Init(key
, crypto::Encryptor::CTR
, std::string())) {
142 LOG(WARNING
) << "Failed to initialize Encryptor.";
143 return std::string();
146 std::string nonce
= salt
.substr(0, kNonceSize
);
148 CHECK(encryptor
.SetCounter(nonce
));
149 if (!encryptor
.Decrypt(encrypted_token
, &token
)) {
150 LOG(WARNING
) << "Failed to decrypt token.";
151 return std::string();
156 std::string system_salt_
;
157 // A key based on the system salt. Useful for encrypting device-level
158 // data for which we have no additional credentials.
159 scoped_ptr
<crypto::SymmetricKey
> system_salt_key_
;
161 DISALLOW_COPY_AND_ASSIGN(CryptohomeLibraryImpl
);
164 class CryptohomeLibraryStubImpl
: public CryptohomeLibrary
{
166 CryptohomeLibraryStubImpl()
168 virtual ~CryptohomeLibraryStubImpl() {}
170 virtual std::string
GetSystemSalt() OVERRIDE
{
171 return kStubSystemSalt
;
174 virtual std::string
EncryptWithSystemSalt(const std::string
& token
) OVERRIDE
{
178 virtual std::string
DecryptWithSystemSalt(
179 const std::string
& encrypted_token_hex
) OVERRIDE
{
180 return encrypted_token_hex
;
184 std::map
<std::string
, std::string
> install_attrs_
;
186 DISALLOW_COPY_AND_ASSIGN(CryptohomeLibraryStubImpl
);
189 CryptohomeLibrary::CryptohomeLibrary() {}
190 CryptohomeLibrary::~CryptohomeLibrary() {}
192 static CryptohomeLibrary
* g_cryptohome_library
= NULL
;
193 static CryptohomeLibrary
* g_test_cryptohome_library
= NULL
;
196 void CryptohomeLibrary::Initialize() {
197 CHECK(!g_cryptohome_library
);
198 if (base::SysInfo::IsRunningOnChromeOS())
199 g_cryptohome_library
= new CryptohomeLibraryImpl();
201 g_cryptohome_library
= new CryptohomeLibraryStubImpl();
205 bool CryptohomeLibrary::IsInitialized() {
206 return g_cryptohome_library
;
210 void CryptohomeLibrary::Shutdown() {
211 CHECK(g_cryptohome_library
);
212 delete g_cryptohome_library
;
213 g_cryptohome_library
= NULL
;
217 CryptohomeLibrary
* CryptohomeLibrary::Get() {
218 CHECK(g_cryptohome_library
|| g_test_cryptohome_library
)
219 << "CryptohomeLibrary::Get() called before Initialize()";
220 if (g_test_cryptohome_library
)
221 return g_test_cryptohome_library
;
222 return g_cryptohome_library
;
226 void CryptohomeLibrary::SetForTest(CryptohomeLibrary
* impl
) {
227 CHECK(!g_test_cryptohome_library
|| !impl
);
228 g_test_cryptohome_library
= impl
;
232 CryptohomeLibrary
* CryptohomeLibrary::GetTestImpl() {
233 return new CryptohomeLibraryStubImpl();
236 } // namespace chromeos