blob: c7ee431a0ff98a1966fcb0c4eb11c59e5abe3df3 [file] [log] [blame]
// Copyright (c) 2014 The Chromium OS 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 "cryptohome/boot_lockbox.h"
#include <sys/types.h>
#include <string>
#include <base/stl_util.h>
#include <openssl/objects.h>
#include <openssl/rsa.h>
#include "cryptohome/crypto.h"
#include "cryptohome/cryptolib.h"
#include "cryptohome/platform.h"
#include "cryptohome/tpm.h"
using chromeos::SecureBlob;
namespace {
// The DER encoding of SHA-256 DigestInfo as defined in PKCS #1.
const unsigned char kSha256DigestInfo[] = {
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
};
const int kPCRIndex = 15;
const unsigned char kPCRValue[20] = {0};
// This is an arbitrary value, our only goal is for the PCR to be non-zero.
const char kPCRExtension[] = "CROS_PCR15_845A4A757B94";
const char kKeyFilePath[] = "/var/lib/boot-lockbox/key";
const mode_t kKeyFilePermissions = 0600;
// So we can use scoped_ptr with openssl types.
struct RSADeleter {
void operator()(void* ptr) const {
if (ptr)
RSA_free(reinterpret_cast<RSA*>(ptr));
}
};
} // namespace
namespace cryptohome {
BootLockbox::BootLockbox(Tpm* tpm, Platform* platform, Crypto* crypto)
: tpm_(tpm), platform_(platform), crypto_(crypto) {
}
BootLockbox::~BootLockbox() {}
bool BootLockbox::Sign(const chromeos::SecureBlob& data,
chromeos::SecureBlob* signature) {
CHECK(signature);
chromeos::SecureBlob key_blob;
if (!GetKeyBlob(&key_blob)) {
return false;
}
chromeos::SecureBlob der_header(kSha256DigestInfo,
arraysize(kSha256DigestInfo));
chromeos::SecureBlob der_encoded_input = SecureBlob::Combine(
der_header,
CryptoLib::Sha256(data));
return tpm_->Sign(key_blob, der_encoded_input, signature);
}
bool BootLockbox::Verify(const chromeos::SecureBlob& data,
const chromeos::SecureBlob& signature) {
chromeos::SecureBlob public_key;
if (!GetPublicKey(&public_key)) {
return false;
}
if (!VerifySignature(public_key, data, signature)) {
return false;
}
chromeos::SecureBlob key_blob;
if (!GetKeyBlob(&key_blob)) {
return false;
}
chromeos::SecureBlob pcr_value(kPCRValue, arraysize(kPCRValue));
if (!tpm_->VerifyPCRBoundKey(kPCRIndex, pcr_value, key_blob)) {
return false;
}
return true;
}
bool BootLockbox::FinalizeBoot() {
if (IsFinalized()) {
// The PCR is already not at the initial value, no need to extend again.
return true;
}
return tpm_->ExtendPCR(kPCRIndex, CryptoLib::Sha1(
chromeos::SecureBlob(kPCRExtension, arraysize(kPCRExtension))));
}
bool BootLockbox::IsFinalized() {
chromeos::SecureBlob actual_pcr_value;
return tpm_->ReadPCR(kPCRIndex, &actual_pcr_value) &&
actual_pcr_value.size() == arraysize(kPCRValue) &&
memcmp(actual_pcr_value.data(), kPCRValue, arraysize(kPCRValue));
}
bool BootLockbox::GetKeyBlob(chromeos::SecureBlob* key_blob) {
if (!key_.has_key_blob() && !LoadKey(&key_) && !CreateKey(&key_)) {
return false;
}
CHECK(key_.has_key_blob());
if (key_blob) {
chromeos::SecureBlob tmp = SecureBlob(key_.key_blob());
key_blob->swap(tmp);
}
return true;
}
bool BootLockbox::GetPublicKey(chromeos::SecureBlob* public_key) {
if (!key_.has_public_key_der() && !LoadKey(&key_)) {
return false;
}
CHECK(key_.has_public_key_der());
if (public_key) {
chromeos::SecureBlob tmp = SecureBlob(key_.public_key_der());
public_key->swap(tmp);
}
return true;
}
bool BootLockbox::LoadKey(BootLockboxKey* key) {
std::string file_contents;
if (!platform_->ReadFileToString(kKeyFilePath, &file_contents)) {
return false;
}
chromeos::SecureBlob protobuf;
if (!crypto_->DecryptWithTpm(file_contents, &protobuf)) {
LOG(WARNING) << "Failed to decrypt boot-lockbox key.";
return false;
}
if (!key->ParseFromArray(protobuf.const_data(), protobuf.size())) {
LOG(ERROR) << "Invalid boot-lockbox key.";
return false;
}
return true;
}
bool BootLockbox::SaveKey(const BootLockboxKey& key) {
chromeos::SecureBlob protobuf(key.ByteSize());
if (!key.SerializeToArray(protobuf.data(), protobuf.size())) {
LOG(ERROR) << "Failed to serialize boot-lockbox key.";
return false;
}
std::string encrypted_protobuf;
if (!crypto_->EncryptWithTpm(protobuf, &encrypted_protobuf)) {
LOG(ERROR) << "Failed to encrypt boot-lockbox key.";
return false;
}
if (!platform_->WriteStringToFileAtomicDurable(kKeyFilePath,
encrypted_protobuf,
kKeyFilePermissions)) {
LOG(ERROR) << "Failed to write boot-lockbox key.";
return false;
}
return true;
}
bool BootLockbox::CreateKey(BootLockboxKey* key) {
LOG(INFO) << "Creating new boot-lockbox key.";
chromeos::SecureBlob key_blob;
chromeos::SecureBlob public_key;
chromeos::SecureBlob pcr_value(kPCRValue, arraysize(kPCRValue));
if (!tpm_->CreatePCRBoundKey(kPCRIndex, pcr_value, &key_blob, &public_key)) {
LOG(ERROR) << "Failed to create boot-lockbox key.";
return false;
}
key->set_key_blob(key_blob.to_string());
key->set_public_key_der(public_key.to_string());
return SaveKey(*key);
}
bool BootLockbox::VerifySignature(const chromeos::SecureBlob& public_key,
const chromeos::SecureBlob& signed_data,
const chromeos::SecureBlob& signature) {
const unsigned char* asn1_ptr = &public_key.front();
scoped_ptr<RSA, RSADeleter> rsa(
d2i_RSAPublicKey(NULL, &asn1_ptr, public_key.size()));
if (!rsa.get()) {
LOG(ERROR) << "Failed to decode public key.";
return false;
}
chromeos::SecureBlob digest = CryptoLib::Sha256(signed_data);
if (!RSA_verify(NID_sha256, &digest.front(), digest.size(),
const_cast<unsigned char*>(&signature.front()),
signature.size(), rsa.get())) {
LOG(ERROR) << "Failed to verify signature.";
return false;
}
return true;
}
} // namespace cryptohome