blob: 3abd23b892bdb0bab7029aa93bbc814c114fb04c [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/bootlockbox/boot_lockbox.h"
#include <stdint.h>
#include <sys/types.h>
#include <map>
#include <memory>
#include <string>
#include <base/files/file_path.h>
#include <crypto/scoped_openssl_types.h>
#include <openssl/objects.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include "cryptohome/crypto.h"
#include "cryptohome/cryptolib.h"
#include "cryptohome/platform.h"
#include "cryptohome/tpm.h"
using base::FilePath;
using brillo::Blob;
using brillo::BlobFromString;
using brillo::BlobToString;
using brillo::SecureBlob;
namespace {
const uint32_t kPCRIndex = 15;
// 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;
} // namespace
namespace cryptohome {
size_t GetPcrValueSize(Tpm* tpm) {
return (tpm && tpm->GetVersion() == Tpm::TpmVersion::TPM_2_0)
? SHA256_DIGEST_LENGTH
: SHA_DIGEST_LENGTH;
}
BootLockbox::BootLockbox(Tpm* tpm, Platform* platform, Crypto* crypto)
: tpm_(tpm),
platform_(platform),
crypto_(crypto),
initial_pcr_value_(GetPcrValueSize(tpm)) {}
BootLockbox::~BootLockbox() {}
bool BootLockbox::Sign(const brillo::Blob& data,
brillo::SecureBlob* signature) {
CHECK(signature);
if (IsFinalized()) {
LOG(INFO) << "Can't sign: boot-lockbox is finalized.";
return false;
}
brillo::SecureBlob key_blob;
if (!GetKeyBlob(&key_blob)) {
return false;
}
return tpm_->Sign(key_blob, brillo::SecureBlob(data), kPCRIndex, signature);
}
bool BootLockbox::Verify(const brillo::Blob& data,
const brillo::SecureBlob& signature) {
brillo::Blob public_key;
if (!GetPublicKey(&public_key)) {
return false;
}
if (!VerifySignature(public_key, data, signature)) {
return false;
}
brillo::SecureBlob key_blob;
if (!GetKeyBlob(&key_blob)) {
return false;
}
brillo::SecureBlob creation_blob;
if (!GetCreationBlob(&creation_blob)) {
return false;
}
if (!tpm_->VerifyPCRBoundKey(
std::map<uint32_t, std::string>(
{{kPCRIndex, BlobToString(initial_pcr_value_)}}),
key_blob, creation_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(BlobFromString(kPCRExtension)));
}
bool BootLockbox::IsFinalized() {
Blob actual_pcr_value;
return tpm_->ReadPCR(kPCRIndex, &actual_pcr_value) &&
actual_pcr_value != initial_pcr_value_;
}
bool BootLockbox::PreLoadKey() {
if (!key_.has_key_blob() && !LoadKey() && !CreateKey()) {
return false;
}
return true;
}
bool BootLockbox::GetKeyBlob(brillo::SecureBlob* key_blob) {
if (!key_.has_key_blob() && !LoadKey() && !CreateKey()) {
return false;
}
CHECK(key_.has_key_blob());
if (key_blob) {
key_blob->assign(key_.key_blob().begin(), key_.key_blob().end());
}
return true;
}
bool BootLockbox::GetPublicKey(brillo::Blob* public_key) {
if (!key_.has_public_key_der() && !LoadKey()) {
return false;
}
CHECK(key_.has_public_key_der());
if (public_key) {
public_key->assign(key_.public_key_der().begin(),
key_.public_key_der().end());
}
return true;
}
bool BootLockbox::GetCreationBlob(brillo::SecureBlob* creation_blob) {
if (!key_.has_creation_blob() && !LoadKey()) {
return false;
}
if (creation_blob) {
creation_blob->assign(key_.creation_blob().begin(),
key_.creation_blob().end());
}
return true;
}
bool BootLockbox::LoadKey() {
std::string file_contents;
if (!platform_->ReadFileToString(FilePath(kKeyFilePath), &file_contents)) {
return false;
}
brillo::SecureBlob protobuf;
if (!crypto_->DecryptWithTpm(file_contents, &protobuf)) {
LOG(WARNING) << "Failed to decrypt boot-lockbox key.";
return false;
}
if (!key_.ParseFromArray(protobuf.data(), protobuf.size())) {
LOG(ERROR) << "Invalid boot-lockbox key.";
return false;
}
return true;
}
bool BootLockbox::SaveKey() {
brillo::SecureBlob protobuf(key_.ByteSizeLong());
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(
FilePath(kKeyFilePath), encrypted_protobuf, kKeyFilePermissions)) {
LOG(ERROR) << "Failed to write boot-lockbox key.";
return false;
}
return true;
}
bool BootLockbox::CreateKey() {
LOG(INFO) << "Creating new boot-lockbox key.";
brillo::SecureBlob key_blob;
brillo::SecureBlob public_key;
brillo::SecureBlob creation_blob;
if (!tpm_->CreatePCRBoundKey(
std::map<uint32_t, std::string>(
{{kPCRIndex, BlobToString(initial_pcr_value_)}}),
AsymmetricKeyUsage::kSignKey, &key_blob, &public_key,
&creation_blob)) {
LOG(ERROR) << "Failed to create boot-lockbox key.";
return false;
}
if (IsFinalized()) {
LOG(WARNING) << "Boot-lockbox finalized while creating key: aborting.";
return false;
}
key_.set_key_blob(key_blob.to_string());
key_.set_public_key_der(public_key.to_string());
key_.set_creation_blob(creation_blob.to_string());
return SaveKey();
}
bool BootLockbox::VerifySignature(const brillo::Blob& public_key,
const brillo::Blob& signed_data,
const brillo::SecureBlob& signature) {
const unsigned char* asn1_ptr = public_key.data();
crypto::ScopedRSA rsa(
d2i_RSAPublicKey(nullptr, &asn1_ptr, public_key.size()));
if (!rsa.get()) {
LOG(ERROR) << "Failed to decode public key.";
return false;
}
brillo::SecureBlob digest = CryptoLib::Sha256ToSecureBlob(signed_data);
if (!RSA_verify(NID_sha256, digest.data(), digest.size(), signature.data(),
signature.size(), rsa.get())) {
LOG(ERROR) << "Failed to verify signature.";
return false;
}
return true;
}
} // namespace cryptohome