blob: d545ea63f35f344e7836e32caa7de19bb1901d4d [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 <memory>
#include <string>
#include <base/files/file_path.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::SecureBlob;
namespace {
const int 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 FilePath::CharType kKeyFilePath[] = "/var/lib/boot-lockbox/key";
const mode_t kKeyFilePermissions = 0600;
// So we can use std::unique_ptr with openssl types.
struct RSADeleter {
void operator()(void* ptr) const {
if (ptr)
RSA_free(reinterpret_cast<RSA*>(ptr));
}
};
} // 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), 0) {
}
BootLockbox::~BootLockbox() {}
bool BootLockbox::Sign(const brillo::SecureBlob& 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, data, kPCRIndex, signature);
}
bool BootLockbox::Verify(const brillo::SecureBlob& data,
const brillo::SecureBlob& signature) {
brillo::SecureBlob 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(kPCRIndex, 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(
brillo::SecureBlob(std::begin(kPCRExtension),
std::end(kPCRExtension))));
}
bool BootLockbox::IsFinalized() {
brillo::SecureBlob actual_pcr_value;
return tpm_->ReadPCR(kPCRIndex, &actual_pcr_value) &&
actual_pcr_value != initial_pcr_value_;
}
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::SecureBlob* 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_.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(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(kPCRIndex, initial_pcr_value_, &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::SecureBlob& public_key,
const brillo::SecureBlob& signed_data,
const brillo::SecureBlob& signature) {
const unsigned char* asn1_ptr = public_key.data();
std::unique_ptr<RSA, RSADeleter> rsa(
d2i_RSAPublicKey(NULL, &asn1_ptr, public_key.size()));
if (!rsa.get()) {
LOG(ERROR) << "Failed to decode public key.";
return false;
}
brillo::SecureBlob digest = CryptoLib::Sha256(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