blob: cd07a0e5669216de0c309602d25e8ee7754e2aac [file] [log] [blame]
// Copyright (c) 2012 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 "chromeos/cryptohome/cryptohome_library.h"
#include <map>
#include "base/bind.h"
#include "base/chromeos/chromeos_version.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/dbus/dbus_method_call_status.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "crypto/encryptor.h"
#include "crypto/nss_util.h"
#include "crypto/sha2.h"
#include "crypto/symmetric_key.h"
namespace chromeos {
namespace {
const char kStubSystemSalt[] = "stub_system_salt";
const size_t kNonceSize = 16;
} // namespace
// This class handles the interaction with the ChromeOS cryptohome library APIs.
class CryptohomeLibraryImpl : public CryptohomeLibrary {
public:
CryptohomeLibraryImpl() {
}
virtual ~CryptohomeLibraryImpl() {
}
virtual bool TpmIsEnabled() OVERRIDE {
bool result = false;
DBusThreadManager::Get()->GetCryptohomeClient()->CallTpmIsEnabledAndBlock(
&result);
return result;
}
virtual bool TpmIsOwned() OVERRIDE {
bool result = false;
DBusThreadManager::Get()->GetCryptohomeClient()->CallTpmIsOwnedAndBlock(
&result);
return result;
}
virtual bool TpmIsBeingOwned() OVERRIDE {
bool result = false;
DBusThreadManager::Get()->GetCryptohomeClient()->
CallTpmIsBeingOwnedAndBlock(&result);
return result;
}
virtual void TpmCanAttemptOwnership() OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->TpmCanAttemptOwnership(
EmptyVoidDBusMethodCallback());
}
virtual void TpmClearStoredPassword() OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
CallTpmClearStoredPasswordAndBlock();
}
virtual bool InstallAttributesGet(
const std::string& name, std::string* value) OVERRIDE {
std::vector<uint8> buf;
bool success = false;
DBusThreadManager::Get()->GetCryptohomeClient()->
InstallAttributesGet(name, &buf, &success);
if (success) {
// Cryptohome returns 'buf' with a terminating '\0' character.
DCHECK(!buf.empty());
DCHECK_EQ(buf.back(), 0);
value->assign(reinterpret_cast<char*>(buf.data()), buf.size() - 1);
}
return success;
}
virtual bool InstallAttributesSet(
const std::string& name, const std::string& value) OVERRIDE {
std::vector<uint8> buf(value.c_str(), value.c_str() + value.size() + 1);
bool success = false;
DBusThreadManager::Get()->GetCryptohomeClient()->
InstallAttributesSet(name, buf, &success);
return success;
}
virtual bool InstallAttributesFinalize() OVERRIDE {
bool success = false;
DBusThreadManager::Get()->GetCryptohomeClient()->
InstallAttributesFinalize(&success);
return success;
}
virtual bool InstallAttributesIsInvalid() OVERRIDE {
bool result = false;
DBusThreadManager::Get()->GetCryptohomeClient()->
InstallAttributesIsInvalid(&result);
return result;
}
virtual bool InstallAttributesIsFirstInstall() OVERRIDE {
bool result = false;
DBusThreadManager::Get()->GetCryptohomeClient()->
InstallAttributesIsFirstInstall(&result);
return result;
}
virtual std::string GetSystemSalt() OVERRIDE {
LoadSystemSalt(); // no-op if it's already loaded.
return system_salt_;
}
virtual std::string EncryptWithSystemSalt(const std::string& token) OVERRIDE {
// Don't care about token encryption while debugging.
if (!base::chromeos::IsRunningOnChromeOS())
return token;
if (!LoadSystemSaltKey()) {
LOG(WARNING) << "System salt key is not available for encrypt.";
return std::string();
}
return EncryptTokenWithKey(system_salt_key_.get(),
system_salt_,
token);
}
virtual std::string DecryptWithSystemSalt(
const std::string& encrypted_token_hex) OVERRIDE {
// Don't care about token encryption while debugging.
if (!base::chromeos::IsRunningOnChromeOS())
return encrypted_token_hex;
if (!LoadSystemSaltKey()) {
LOG(WARNING) << "System salt key is not available for decrypt.";
return std::string();
}
return DecryptTokenWithKey(system_salt_key_.get(),
system_salt_,
encrypted_token_hex);
}
private:
void LoadSystemSalt() {
if (!system_salt_.empty())
return;
std::vector<uint8> salt;
DBusThreadManager::Get()->GetCryptohomeClient()->GetSystemSalt(&salt);
if (salt.empty() || salt.size() % 2 != 0U) {
LOG(WARNING) << "System salt not available";
return;
}
system_salt_ = StringToLowerASCII(base::HexEncode(
reinterpret_cast<const void*>(salt.data()), salt.size()));
}
// TODO: should this use the system salt for both the password and the salt
// value, or should this use a separate salt value?
bool LoadSystemSaltKey() {
if (system_salt_.empty())
return false;
if (!system_salt_key_.get())
system_salt_key_.reset(PassphraseToKey(system_salt_, system_salt_));
return system_salt_key_.get();
}
crypto::SymmetricKey* PassphraseToKey(const std::string& passphrase,
const std::string& salt) {
return crypto::SymmetricKey::DeriveKeyFromPassword(
crypto::SymmetricKey::AES, passphrase, salt, 1000, 256);
}
// Encrypts (AES) the token given |key| and |salt|.
std::string EncryptTokenWithKey(crypto::SymmetricKey* key,
const std::string& salt,
const std::string& token) {
crypto::Encryptor encryptor;
if (!encryptor.Init(key, crypto::Encryptor::CTR, std::string())) {
LOG(WARNING) << "Failed to initialize Encryptor.";
return std::string();
}
std::string nonce = salt.substr(0, kNonceSize);
std::string encoded_token;
CHECK(encryptor.SetCounter(nonce));
if (!encryptor.Encrypt(token, &encoded_token)) {
LOG(WARNING) << "Failed to encrypt token.";
return std::string();
}
return StringToLowerASCII(base::HexEncode(
reinterpret_cast<const void*>(encoded_token.data()),
encoded_token.size()));
}
// Decrypts (AES) hex encoded encrypted token given |key| and |salt|.
std::string DecryptTokenWithKey(crypto::SymmetricKey* key,
const std::string& salt,
const std::string& encrypted_token_hex) {
std::vector<uint8> encrypted_token_bytes;
if (!base::HexStringToBytes(encrypted_token_hex, &encrypted_token_bytes)) {
LOG(WARNING) << "Corrupt encrypted token found.";
return std::string();
}
std::string encrypted_token(
reinterpret_cast<char*>(encrypted_token_bytes.data()),
encrypted_token_bytes.size());
crypto::Encryptor encryptor;
if (!encryptor.Init(key, crypto::Encryptor::CTR, std::string())) {
LOG(WARNING) << "Failed to initialize Encryptor.";
return std::string();
}
std::string nonce = salt.substr(0, kNonceSize);
std::string token;
CHECK(encryptor.SetCounter(nonce));
if (!encryptor.Decrypt(encrypted_token, &token)) {
LOG(WARNING) << "Failed to decrypt token.";
return std::string();
}
return token;
}
std::string system_salt_;
// A key based on the system salt. Useful for encrypting device-level
// data for which we have no additional credentials.
scoped_ptr<crypto::SymmetricKey> system_salt_key_;
DISALLOW_COPY_AND_ASSIGN(CryptohomeLibraryImpl);
};
class CryptohomeLibraryStubImpl : public CryptohomeLibrary {
public:
CryptohomeLibraryStubImpl()
: locked_(false) {}
virtual ~CryptohomeLibraryStubImpl() {}
virtual bool TpmIsEnabled() OVERRIDE {
return true;
}
virtual bool TpmIsOwned() OVERRIDE {
return true;
}
virtual bool TpmIsBeingOwned() OVERRIDE {
return true;
}
virtual void TpmCanAttemptOwnership() OVERRIDE {}
virtual void TpmClearStoredPassword() OVERRIDE {}
virtual bool InstallAttributesGet(
const std::string& name, std::string* value) OVERRIDE {
if (install_attrs_.find(name) != install_attrs_.end()) {
*value = install_attrs_[name];
return true;
}
return false;
}
virtual bool InstallAttributesSet(
const std::string& name, const std::string& value) OVERRIDE {
install_attrs_[name] = value;
return true;
}
virtual bool InstallAttributesFinalize() OVERRIDE {
locked_ = true;
return true;
}
virtual bool InstallAttributesIsInvalid() OVERRIDE {
return false;
}
virtual bool InstallAttributesIsFirstInstall() OVERRIDE {
return !locked_;
}
virtual std::string GetSystemSalt() OVERRIDE {
return kStubSystemSalt;
}
virtual std::string EncryptWithSystemSalt(const std::string& token) OVERRIDE {
return token;
}
virtual std::string DecryptWithSystemSalt(
const std::string& encrypted_token_hex) OVERRIDE {
return encrypted_token_hex;
}
private:
std::map<std::string, std::string> install_attrs_;
bool locked_;
DISALLOW_COPY_AND_ASSIGN(CryptohomeLibraryStubImpl);
};
CryptohomeLibrary::CryptohomeLibrary() {}
CryptohomeLibrary::~CryptohomeLibrary() {}
static CryptohomeLibrary* g_cryptohome_library = NULL;
static CryptohomeLibrary* g_test_cryptohome_library = NULL;
// static
void CryptohomeLibrary::Initialize() {
CHECK(!g_cryptohome_library);
if (base::chromeos::IsRunningOnChromeOS())
g_cryptohome_library = new CryptohomeLibraryImpl();
else
g_cryptohome_library = new CryptohomeLibraryStubImpl();
}
// static
bool CryptohomeLibrary::IsInitialized() {
return g_cryptohome_library;
}
// static
void CryptohomeLibrary::Shutdown() {
CHECK(g_cryptohome_library);
delete g_cryptohome_library;
g_cryptohome_library = NULL;
}
// static
CryptohomeLibrary* CryptohomeLibrary::Get() {
CHECK(g_cryptohome_library || g_test_cryptohome_library)
<< "CryptohomeLibrary::Get() called before Initialize()";
if (g_test_cryptohome_library)
return g_test_cryptohome_library;
return g_cryptohome_library;
}
// static
void CryptohomeLibrary::SetForTest(CryptohomeLibrary* impl) {
CHECK(!g_test_cryptohome_library || !impl);
g_test_cryptohome_library = impl;
}
// static
CryptohomeLibrary* CryptohomeLibrary::GetTestImpl() {
return new CryptohomeLibraryStubImpl();
}
} // namespace chromeos