| // Copyright 2016 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/firmware_management_parameters.h" |
| |
| #include <arpa/inet.h> |
| #include <limits.h> |
| #include <stdint.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include <base/logging.h> |
| #include <base/strings/string_split.h> |
| #include <brillo/secure_blob.h> |
| #include <openssl/sha.h> |
| |
| #include "cryptohome/cryptolib.h" |
| |
| extern "C" { |
| #include "cryptohome/crc8.h" |
| } |
| |
| using brillo::SecureBlob; |
| |
| namespace { |
| const uint32_t kNvramVersionV1_0 = 0x10; |
| } |
| |
| namespace cryptohome { |
| |
| // Defines the raw NVRAM contents. |
| struct FirmwareManagementParametersRawV1_0 { |
| uint8_t crc; |
| uint8_t struct_size; |
| // Data after this is covered by the crc |
| uint8_t struct_version; // Set to kNvramVersionV1_0 |
| uint8_t reserved0; |
| uint32_t flags; |
| uint8_t developer_key_hash[SHA256_DIGEST_LENGTH]; |
| } __attribute__((packed)); |
| |
| |
| // Index must match firmware; see README.firmware_management_parameters |
| const uint32_t FirmwareManagementParameters::kNvramIndex = 0x100a; |
| const uint32_t FirmwareManagementParameters::kNvramBytes = |
| sizeof(struct FirmwareManagementParametersRawV1_0); |
| const uint32_t FirmwareManagementParameters::kCrcDataOffset = 2; |
| |
| FirmwareManagementParameters::FirmwareManagementParameters(Tpm* tpm) |
| : tpm_(tpm), |
| raw_(new FirmwareManagementParametersRawV1_0()) { |
| } |
| |
| FirmwareManagementParameters::~FirmwareManagementParameters() { |
| } |
| |
| bool FirmwareManagementParameters::TpmIsReady() const { |
| if (!tpm_) { |
| LOG(ERROR) << "TpmIsReady: no tpm_ instance."; |
| return false; |
| } |
| if (!tpm_->IsEnabled()) { |
| LOG(ERROR) << "TpmIsReady: is not enabled."; |
| return false; |
| } |
| if (!tpm_->IsOwned()) { |
| LOG(ERROR) << "TpmIsReady: is not owned."; |
| return false; |
| } |
| return true; |
| } |
| |
| bool FirmwareManagementParameters::HasAuthorization() const { |
| if (!TpmIsReady()) { |
| LOG(ERROR) << "HasAuthorization: TPM not ready."; |
| return false; |
| } |
| // Need owner password to create or destroy NVRAM spaces |
| brillo::Blob owner_password; |
| if (tpm_->GetOwnerPassword(&owner_password) && owner_password.size() != 0) { |
| return true; |
| } |
| LOG(INFO) << "HasAuthorization: TPM Owner password not available."; |
| return false; |
| } |
| |
| bool FirmwareManagementParameters::Destroy(void) { |
| if (!HasAuthorization()) { |
| LOG(ERROR) << "Destroy() called with insufficient authorization."; |
| return false; |
| } |
| |
| // Only destroy the space if it exists |
| if (tpm_->IsNvramDefined(kNvramIndex) && !tpm_->DestroyNvram(kNvramIndex)) { |
| return false; |
| } |
| |
| loaded_ = false; |
| return true; |
| } |
| |
| bool FirmwareManagementParameters::Create(void) { |
| uint32_t nvram_bytes; |
| |
| // Make sure we have what we need now. |
| if (!HasAuthorization()) { |
| LOG(ERROR) << "Create() called with insufficient authorization."; |
| return false; |
| } |
| if (!Destroy()) { |
| LOG(ERROR) << "Failed to destroy Firmware Management Parameters data " |
| "before creation."; |
| return false; |
| } |
| |
| nvram_bytes = kNvramBytes; |
| |
| // Use a WriteDefine space with no PCR0 locking |
| if (!tpm_->DefineNvram(kNvramIndex, nvram_bytes, |
| Tpm::kTpmNvramWriteDefine)) { |
| LOG(ERROR) << "Create() failed to defined NVRAM space."; |
| return false; |
| } |
| |
| LOG(INFO) << "Firmware Management Parameters created."; |
| return true; |
| } |
| |
| bool FirmwareManagementParameters::Load(void) { |
| if (loaded_) { |
| return true; |
| } |
| |
| if (!tpm_->IsNvramDefined(kNvramIndex)) { |
| LOG(INFO) << "Load() called with no NVRAM space defined."; |
| return false; |
| } |
| |
| SecureBlob nvram_data(0); |
| if (!tpm_->ReadNvram(kNvramIndex, &nvram_data)) { |
| LOG(ERROR) << "Load() could not read from NVRAM space."; |
| return false; |
| } |
| |
| // Make sure we've read enough data for a 1.0 struct |
| unsigned int nvram_size = nvram_data.size(); |
| if (nvram_size < kNvramBytes) { |
| LOG(ERROR) << "Load() found unexpected NVRAM size: " << nvram_size; |
| return false; |
| } |
| |
| // Copy the raw data |
| memcpy(raw_.get(), nvram_data.data(), kNvramBytes); |
| |
| // Verify the size |
| if (raw_->struct_size != nvram_size) { |
| LOG(ERROR) << "Load() found unexpected NVRAM size: " << nvram_size; |
| return false; |
| } |
| |
| // Verify the CRC |
| uint8_t crc = crc8(nvram_data.data() + kCrcDataOffset, |
| nvram_size - kCrcDataOffset); |
| if (crc != raw_->crc) { |
| LOG(ERROR) << "Load() got bad CRC"; |
| return false; |
| } |
| |
| // We are a 1.0 reader, so we can read 1.x structs |
| if ((raw_->struct_version >> 4) != (kNvramVersionV1_0 >> 4)) { |
| LOG(ERROR) << "Load() got incompatible NVRAM version: " |
| << (unsigned int)raw_->struct_version; |
| return false; |
| } |
| // We don't need to check minor version, because all 1.x structs are |
| // compatible with us |
| |
| DLOG(INFO) << "Load() successfully loaded NVRAM data."; |
| loaded_ = true; |
| return true; |
| } |
| |
| bool FirmwareManagementParameters::Store(uint32_t flags, |
| const brillo::Blob* developer_key_hash) { |
| if (!TpmIsReady()) { |
| LOG(ERROR) << "Store() called when TPM was not ready!"; |
| return false; |
| } |
| |
| // Ensure we have the space ready. |
| if (!tpm_->IsNvramDefined(kNvramIndex)) { |
| LOG(ERROR) << "Store() called with no NVRAM space."; |
| return false; |
| } |
| if (tpm_->IsNvramLocked(kNvramIndex)) { |
| LOG(ERROR) << "Store() called with a locked NVRAM space."; |
| return false; |
| } |
| |
| // Check defined NVRAM size. |
| unsigned int nvram_size = tpm_->GetNvramSize(kNvramIndex); |
| if (nvram_size != kNvramBytes) { |
| LOG(ERROR) << "Store() found unexpected NVRAM size " << nvram_size << "."; |
| return false; |
| } |
| |
| // Reset the NVRAM contents |
| loaded_ = false; |
| memset(raw_.get(), 0, kNvramBytes); |
| raw_->struct_size = kNvramBytes; |
| raw_->struct_version = kNvramVersionV1_0; |
| raw_->flags = flags; |
| |
| // Store the hash, if any |
| if (developer_key_hash) { |
| // Make sure hash is the right size |
| if ((developer_key_hash->size() != sizeof(raw_->developer_key_hash))) { |
| LOG(ERROR) << "Store() called with bad hash size " |
| << developer_key_hash->size() << "."; |
| return false; |
| } |
| |
| memcpy(raw_->developer_key_hash, developer_key_hash->data(), |
| sizeof(raw_->developer_key_hash)); |
| } |
| |
| // Recalculate the CRC |
| const uint8_t *raw8 = reinterpret_cast<uint8_t*>(raw_.get()); |
| raw_->crc = crc8(raw8 + kCrcDataOffset, |
| raw_->struct_size - kCrcDataOffset); |
| |
| // Write the data to nvram |
| SecureBlob nvram_data(raw_->struct_size); |
| memcpy(nvram_data.data(), raw_.get(), raw_->struct_size); |
| if (!tpm_->WriteNvram(kNvramIndex, nvram_data)) { |
| LOG(ERROR) << "Store() failed to write to NVRAM"; |
| return false; |
| } |
| |
| // Write 0 to the nvram to lock it |
| SecureBlob lock(0); |
| if (!tpm_->WriteNvram(kNvramIndex, lock)) { |
| LOG(ERROR) << "Store() failed to lock the NVRAM space"; |
| return false; |
| } |
| |
| // Ensure the space is now locked. |
| if (!tpm_->IsNvramLocked(kNvramIndex)) { |
| LOG(ERROR) << "NVRAM space did not lock as expected."; |
| return false; |
| } |
| |
| loaded_ = true; |
| return true; |
| } |
| |
| bool FirmwareManagementParameters::GetFlags(uint32_t* flags) { |
| CHECK(flags); |
| |
| // Load if needed |
| if (!Load()) { |
| return false; |
| } |
| |
| *flags = raw_->flags; |
| return true; |
| } |
| |
| bool FirmwareManagementParameters::GetDeveloperKeyHash(brillo::Blob* hash) { |
| CHECK(hash); |
| |
| // Load if needed |
| if (!Load()) { |
| return false; |
| } |
| |
| hash->resize(sizeof(raw_->developer_key_hash)); |
| memcpy(hash->data(), raw_->developer_key_hash, hash->size()); |
| return true; |
| } |
| |
| } // namespace cryptohome |