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/chromeos/chromeos_version.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "chromeos/dbus/cryptohome_client.h"
15 #include "chromeos/dbus/dbus_thread_manager.h"
16 #include "crypto/encryptor.h"
17 #include "crypto/nss_util.h"
18 #include "crypto/sha2.h"
19 #include "crypto/symmetric_key.h"
25 const char kStubSystemSalt
[] = "stub_system_salt";
26 const size_t kNonceSize
= 16;
28 // Does nothing. Used as a Cryptohome::VoidMethodCallback.
29 void DoNothing(DBusMethodCallStatus call_status
) {}
33 // This class handles the interaction with the ChromeOS cryptohome library APIs.
34 class CryptohomeLibraryImpl
: public CryptohomeLibrary
{
36 CryptohomeLibraryImpl() : weak_ptr_factory_(this) {
39 virtual ~CryptohomeLibraryImpl() {
42 virtual bool TpmIsEnabled() OVERRIDE
{
44 DBusThreadManager::Get()->GetCryptohomeClient()->CallTpmIsEnabledAndBlock(
49 virtual bool TpmIsOwned() OVERRIDE
{
51 DBusThreadManager::Get()->GetCryptohomeClient()->CallTpmIsOwnedAndBlock(
56 virtual bool TpmIsBeingOwned() OVERRIDE
{
58 DBusThreadManager::Get()->GetCryptohomeClient()->
59 CallTpmIsBeingOwnedAndBlock(&result
);
63 virtual void TpmCanAttemptOwnership() OVERRIDE
{
64 DBusThreadManager::Get()->GetCryptohomeClient()->TpmCanAttemptOwnership(
65 base::Bind(&DoNothing
));
68 virtual void TpmClearStoredPassword() OVERRIDE
{
69 DBusThreadManager::Get()->GetCryptohomeClient()->
70 CallTpmClearStoredPasswordAndBlock();
73 virtual bool InstallAttributesGet(
74 const std::string
& name
, std::string
* value
) OVERRIDE
{
75 std::vector
<uint8
> buf
;
77 DBusThreadManager::Get()->GetCryptohomeClient()->
78 InstallAttributesGet(name
, &buf
, &success
);
80 // Cryptohome returns 'buf' with a terminating '\0' character.
82 DCHECK_EQ(buf
.back(), 0);
83 value
->assign(reinterpret_cast<char*>(buf
.data()), buf
.size() - 1);
88 virtual bool InstallAttributesSet(
89 const std::string
& name
, const std::string
& value
) OVERRIDE
{
90 std::vector
<uint8
> buf(value
.c_str(), value
.c_str() + value
.size() + 1);
92 DBusThreadManager::Get()->GetCryptohomeClient()->
93 InstallAttributesSet(name
, buf
, &success
);
97 virtual bool InstallAttributesFinalize() OVERRIDE
{
99 DBusThreadManager::Get()->GetCryptohomeClient()->
100 InstallAttributesFinalize(&success
);
104 virtual bool InstallAttributesIsInvalid() OVERRIDE
{
106 DBusThreadManager::Get()->GetCryptohomeClient()->
107 InstallAttributesIsInvalid(&result
);
111 virtual bool InstallAttributesIsFirstInstall() OVERRIDE
{
113 DBusThreadManager::Get()->GetCryptohomeClient()->
114 InstallAttributesIsFirstInstall(&result
);
118 virtual std::string
GetSystemSalt() OVERRIDE
{
119 LoadSystemSalt(); // no-op if it's already loaded.
120 return StringToLowerASCII(base::HexEncode(
121 reinterpret_cast<const void*>(system_salt_
.data()),
122 system_salt_
.size()));
125 virtual std::string
EncryptWithSystemSalt(const std::string
& token
) OVERRIDE
{
126 // Don't care about token encryption while debugging.
127 if (!base::chromeos::IsRunningOnChromeOS())
130 if (!LoadSystemSaltKey()) {
131 LOG(WARNING
) << "System salt key is not available for encrypt.";
132 return std::string();
134 return EncryptTokenWithKey(system_salt_key_
.get(),
139 virtual std::string
DecryptWithSystemSalt(
140 const std::string
& encrypted_token_hex
) OVERRIDE
{
141 // Don't care about token encryption while debugging.
142 if (!base::chromeos::IsRunningOnChromeOS())
143 return encrypted_token_hex
;
145 if (!LoadSystemSaltKey()) {
146 LOG(WARNING
) << "System salt key is not available for decrypt.";
147 return std::string();
149 return DecryptTokenWithKey(system_salt_key_
.get(),
151 encrypted_token_hex
);
155 void LoadSystemSalt() {
156 if (!system_salt_
.empty())
158 DBusThreadManager::Get()->GetCryptohomeClient()->
159 GetSystemSalt(&system_salt_
);
160 CHECK(!system_salt_
.empty());
161 CHECK_EQ(system_salt_
.size() % 2, 0U);
164 // TODO: should this use the system salt for both the password and the salt
165 // value, or should this use a separate salt value?
166 bool LoadSystemSaltKey() {
167 if (!system_salt_key_
.get())
168 system_salt_key_
.reset(PassphraseToKey(GetSystemSalt(), GetSystemSalt()));
169 return system_salt_key_
.get();
172 crypto::SymmetricKey
* PassphraseToKey(const std::string
& passphrase
,
173 const std::string
& salt
) {
174 return crypto::SymmetricKey::DeriveKeyFromPassword(
175 crypto::SymmetricKey::AES
, passphrase
, salt
, 1000, 256);
179 // Encrypts (AES) the token given |key| and |salt|.
180 std::string
EncryptTokenWithKey(crypto::SymmetricKey
* key
,
181 const std::string
& salt
,
182 const std::string
& token
) {
183 crypto::Encryptor encryptor
;
184 if (!encryptor
.Init(key
, crypto::Encryptor::CTR
, std::string())) {
185 LOG(WARNING
) << "Failed to initialize Encryptor.";
186 return std::string();
188 std::string nonce
= salt
.substr(0, kNonceSize
);
189 std::string encoded_token
;
190 CHECK(encryptor
.SetCounter(nonce
));
191 if (!encryptor
.Encrypt(token
, &encoded_token
)) {
192 LOG(WARNING
) << "Failed to encrypt token.";
193 return std::string();
196 return StringToLowerASCII(base::HexEncode(
197 reinterpret_cast<const void*>(encoded_token
.data()),
198 encoded_token
.size()));
201 // Decrypts (AES) hex encoded encrypted token given |key| and |salt|.
202 std::string
DecryptTokenWithKey(crypto::SymmetricKey
* key
,
203 const std::string
& salt
,
204 const std::string
& encrypted_token_hex
) {
205 std::vector
<uint8
> encrypted_token_bytes
;
206 if (!base::HexStringToBytes(encrypted_token_hex
, &encrypted_token_bytes
)) {
207 LOG(WARNING
) << "Corrupt encrypted token found.";
208 return std::string();
211 std::string
encrypted_token(
212 reinterpret_cast<char*>(encrypted_token_bytes
.data()),
213 encrypted_token_bytes
.size());
214 crypto::Encryptor encryptor
;
215 if (!encryptor
.Init(key
, crypto::Encryptor::CTR
, std::string())) {
216 LOG(WARNING
) << "Failed to initialize Encryptor.";
217 return std::string();
220 std::string nonce
= salt
.substr(0, kNonceSize
);
222 CHECK(encryptor
.SetCounter(nonce
));
223 if (!encryptor
.Decrypt(encrypted_token
, &token
)) {
224 LOG(WARNING
) << "Failed to decrypt token.";
225 return std::string();
230 base::WeakPtrFactory
<CryptohomeLibraryImpl
> weak_ptr_factory_
;
231 std::vector
<uint8
> system_salt_
;
232 // A key based on the system salt. Useful for encrypting device-level
233 // data for which we have no additional credentials.
234 scoped_ptr
<crypto::SymmetricKey
> system_salt_key_
;
236 DISALLOW_COPY_AND_ASSIGN(CryptohomeLibraryImpl
);
239 class CryptohomeLibraryStubImpl
: public CryptohomeLibrary
{
241 CryptohomeLibraryStubImpl()
243 virtual ~CryptohomeLibraryStubImpl() {}
245 virtual bool TpmIsEnabled() OVERRIDE
{
249 virtual bool TpmIsOwned() OVERRIDE
{
253 virtual bool TpmIsBeingOwned() OVERRIDE
{
257 virtual void TpmCanAttemptOwnership() OVERRIDE
{}
259 virtual void TpmClearStoredPassword() OVERRIDE
{}
261 virtual bool InstallAttributesGet(
262 const std::string
& name
, std::string
* value
) OVERRIDE
{
263 if (install_attrs_
.find(name
) != install_attrs_
.end()) {
264 *value
= install_attrs_
[name
];
270 virtual bool InstallAttributesSet(
271 const std::string
& name
, const std::string
& value
) OVERRIDE
{
272 install_attrs_
[name
] = value
;
276 virtual bool InstallAttributesFinalize() OVERRIDE
{
281 virtual bool InstallAttributesIsInvalid() OVERRIDE
{
285 virtual bool InstallAttributesIsFirstInstall() OVERRIDE
{
289 virtual std::string
GetSystemSalt() OVERRIDE
{
290 return kStubSystemSalt
;
293 virtual std::string
EncryptWithSystemSalt(const std::string
& token
) OVERRIDE
{
297 virtual std::string
DecryptWithSystemSalt(
298 const std::string
& encrypted_token_hex
) OVERRIDE
{
299 return encrypted_token_hex
;
303 std::map
<std::string
, std::string
> install_attrs_
;
305 DISALLOW_COPY_AND_ASSIGN(CryptohomeLibraryStubImpl
);
308 CryptohomeLibrary::CryptohomeLibrary() {}
309 CryptohomeLibrary::~CryptohomeLibrary() {}
311 static CryptohomeLibrary
* g_cryptohome_library
= NULL
;
312 static CryptohomeLibrary
* g_test_cryptohome_library
= NULL
;
315 void CryptohomeLibrary::Initialize() {
316 CHECK(!g_cryptohome_library
);
317 if (base::chromeos::IsRunningOnChromeOS())
318 g_cryptohome_library
= new CryptohomeLibraryImpl();
320 g_cryptohome_library
= new CryptohomeLibraryStubImpl();
324 bool CryptohomeLibrary::IsInitialized() {
325 return g_cryptohome_library
;
329 void CryptohomeLibrary::Shutdown() {
330 CHECK(g_cryptohome_library
);
331 delete g_cryptohome_library
;
332 g_cryptohome_library
= NULL
;
336 CryptohomeLibrary
* CryptohomeLibrary::Get() {
337 CHECK(g_cryptohome_library
|| g_test_cryptohome_library
)
338 << "CryptohomeLibrary::Get() called before Initialize()";
339 if (g_test_cryptohome_library
)
340 return g_test_cryptohome_library
;
341 return g_cryptohome_library
;
345 void CryptohomeLibrary::SetForTest(CryptohomeLibrary
* impl
) {
346 CHECK(!g_test_cryptohome_library
|| !impl
);
347 g_test_cryptohome_library
= impl
;
351 CryptohomeLibrary
* CryptohomeLibrary::GetTestImpl() {
352 return new CryptohomeLibraryStubImpl();
355 } // namespace chromeos