mirror of
https://github.com/LongSoft/UEFITool.git
synced 2024-11-27 10:28:22 +08:00
1190 lines
56 KiB
C++
1190 lines
56 KiB
C++
/* fitparser.cpp
|
|
|
|
Copyright (c) 2022, Nikolaj Schlej. All rights reserved.
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
*/
|
|
#include "fitparser.h"
|
|
|
|
#ifdef U_ENABLE_FIT_PARSING_SUPPORT
|
|
|
|
#include "intel_fit.h"
|
|
#include "ffs.h"
|
|
#include "parsingdata.h"
|
|
#include "types.h"
|
|
#include "utility.h"
|
|
#include "digest/sha2.h"
|
|
|
|
#include "umemstream.h"
|
|
#include "kaitai/kaitaistream.h"
|
|
#include "generated/intel_acbp_v1.h"
|
|
#include "generated/intel_acbp_v2.h"
|
|
#include "generated/intel_keym_v1.h"
|
|
#include "generated/intel_keym_v2.h"
|
|
#include "generated/intel_acm.h"
|
|
|
|
USTATUS FitParser::parseFit(const UModelIndex & index)
|
|
{
|
|
// Reset parser state
|
|
fitTable.clear();
|
|
securityInfo = "";
|
|
bgAcmFound = false;
|
|
bgKeyManifestFound = false;
|
|
bgBootPolicyFound = false;
|
|
bgKmHash = UByteArray();
|
|
bgBpHashSha256 = UByteArray();
|
|
bgBpHashSha384 = UByteArray();
|
|
|
|
// Check sanity
|
|
if (!index.isValid()) {
|
|
return U_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Search for FIT
|
|
UModelIndex fitIndex;
|
|
UINT32 fitOffset;
|
|
findFitRecursive(index, fitIndex, fitOffset);
|
|
|
|
// FIT not found
|
|
if (!fitIndex.isValid()) {
|
|
// Nothing to parse further
|
|
return U_SUCCESS;
|
|
}
|
|
// Explicitly set the item containing FIT as fixed
|
|
model->setFixed(fitIndex, true);
|
|
|
|
// Special case of FIT header
|
|
UByteArray fitBody = model->body(fitIndex);
|
|
// This is safe, as we checked the size in findFitRecursive already
|
|
const INTEL_FIT_ENTRY* fitHeader = (const INTEL_FIT_ENTRY*)(fitBody.constData() + fitOffset);
|
|
|
|
// Sanity check
|
|
UINT32 fitSize = fitHeader->Size * sizeof(INTEL_FIT_ENTRY);
|
|
if ((UINT32)fitBody.size() - fitOffset < fitSize) {
|
|
msg(usprintf("%s: not enough space to contain the whole FIT table", __FUNCTION__), fitIndex);
|
|
return U_INVALID_FIT;
|
|
}
|
|
|
|
// Check FIT checksum, if present
|
|
if (fitHeader->ChecksumValid) {
|
|
// Calculate FIT entry checksum
|
|
UByteArray tempFIT = model->body(fitIndex).mid(fitOffset, fitSize);
|
|
INTEL_FIT_ENTRY* tempFitHeader = (INTEL_FIT_ENTRY*)tempFIT.data();
|
|
tempFitHeader->Checksum = 0;
|
|
UINT8 calculated = calculateChecksum8((const UINT8*)tempFitHeader, fitSize);
|
|
if (calculated != fitHeader->Checksum) {
|
|
msg(usprintf("%s: invalid FIT table checksum %02Xh, should be %02Xh", __FUNCTION__, fitHeader->Checksum, calculated), fitIndex);
|
|
}
|
|
}
|
|
|
|
// Check fit header type
|
|
if (fitHeader->Type != INTEL_FIT_TYPE_HEADER) {
|
|
msg(usprintf("%s: invalid FIT header type", __FUNCTION__), fitIndex);
|
|
return U_INVALID_FIT;
|
|
}
|
|
|
|
// Add FIT header
|
|
std::vector<UString> currentStrings;
|
|
currentStrings.push_back(UString("_FIT_ "));
|
|
currentStrings.push_back(usprintf("%08Xh", fitSize));
|
|
currentStrings.push_back(usprintf("%04Xh", fitHeader->Version));
|
|
currentStrings.push_back(usprintf("%02Xh", fitHeader->Checksum));
|
|
currentStrings.push_back(fitEntryTypeToUString(fitHeader->Type));
|
|
currentStrings.push_back(UString()); // Empty info for FIT header
|
|
fitTable.push_back(std::pair<std::vector<UString>, UModelIndex>(currentStrings, fitIndex));
|
|
|
|
// Process all other entries
|
|
UModelIndex acmIndex;
|
|
UModelIndex kmIndex;
|
|
UModelIndex bpIndex;
|
|
for (UINT32 i = 1; i < fitHeader->Size; i++) {
|
|
currentStrings.clear();
|
|
UString info;
|
|
UModelIndex itemIndex;
|
|
const INTEL_FIT_ENTRY* currentEntry = fitHeader + i;
|
|
UINT32 currentEntrySize = currentEntry->Size;
|
|
|
|
// Check sanity
|
|
if (currentEntry->Type == INTEL_FIT_TYPE_HEADER) {
|
|
msg(usprintf("%s: second FIT header found, the table is damaged", __FUNCTION__), fitIndex);
|
|
return U_INVALID_FIT;
|
|
}
|
|
|
|
// Special case of version 0 entries for TXT and TPM policies
|
|
if ((currentEntry->Type == INTEL_FIT_TYPE_TXT_POLICY || currentEntry->Type == INTEL_FIT_TYPE_TPM_POLICY)
|
|
&& currentEntry->Version == 0) {
|
|
const INTEL_FIT_INDEX_IO_ADDRESS* policy = (const INTEL_FIT_INDEX_IO_ADDRESS*)currentEntry;
|
|
info += usprintf("Index: %04Xh, BitPosition: %02Xh, AccessWidth: %02Xh, DataRegAddr: %04Xh, IndexRegAddr: %04Xh",
|
|
policy->Index,
|
|
policy->BitPosition,
|
|
policy->AccessWidthInBytes,
|
|
policy->DataRegisterAddress,
|
|
policy->IndexRegisterAddress);
|
|
}
|
|
else if (currentEntry->Address > ffsParser->addressDiff && currentEntry->Address < 0xFFFFFFFFUL) { // Only elements in the image need to be parsed
|
|
UINT32 currentEntryBase = (UINT32)(currentEntry->Address - ffsParser->addressDiff);
|
|
itemIndex = model->findByBase(currentEntryBase);
|
|
if (itemIndex.isValid()) {
|
|
UByteArray item = model->header(itemIndex) + model->body(itemIndex) + model->tail(itemIndex);
|
|
UINT32 localOffset = currentEntryBase - model->base(itemIndex);
|
|
|
|
switch (currentEntry->Type) {
|
|
case INTEL_FIT_TYPE_MICROCODE:
|
|
(void)parseFitEntryMicrocode(item, localOffset, itemIndex, info, currentEntrySize);
|
|
break;
|
|
|
|
case INTEL_FIT_TYPE_STARTUP_AC_MODULE:
|
|
(void)parseFitEntryAcm(item, localOffset, itemIndex, info, currentEntrySize);
|
|
acmIndex = itemIndex;
|
|
break;
|
|
|
|
case INTEL_FIT_TYPE_BOOT_GUARD_KEY_MANIFEST:
|
|
(void)parseFitEntryBootGuardKeyManifest(item, localOffset, itemIndex, info, currentEntrySize);
|
|
kmIndex = itemIndex;
|
|
break;
|
|
|
|
case INTEL_FIT_TYPE_BOOT_GUARD_BOOT_POLICY:
|
|
(void)parseFitEntryBootGuardBootPolicy(item, localOffset, itemIndex, info, currentEntrySize);
|
|
bpIndex = itemIndex;
|
|
break;
|
|
|
|
default:
|
|
// Do nothing
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
msg(usprintf("%s: FIT entry #%u not found in the image", __FUNCTION__, i), fitIndex);
|
|
}
|
|
}
|
|
|
|
// Explicitly set the item referenced by FIT as fixed
|
|
if (itemIndex.isValid()) {
|
|
model->setFixed(itemIndex, true);
|
|
}
|
|
|
|
// Add entry to fitTable
|
|
currentStrings.push_back(usprintf("%016" PRIX64 "h", currentEntry->Address));
|
|
currentStrings.push_back(usprintf("%08Xh", currentEntrySize));
|
|
currentStrings.push_back(usprintf("%04Xh", currentEntry->Version));
|
|
currentStrings.push_back(usprintf("%02Xh", currentEntry->Checksum));
|
|
currentStrings.push_back(fitEntryTypeToUString(currentEntry->Type));
|
|
currentStrings.push_back(info);
|
|
fitTable.push_back(std::pair<std::vector<UString>, UModelIndex>(currentStrings, itemIndex));
|
|
}
|
|
|
|
// Perform validation of BootGuard components
|
|
if (bgAcmFound) {
|
|
if (!bgKeyManifestFound) {
|
|
msg(usprintf("%s: startup ACM found, but KeyManifest is not", __FUNCTION__), acmIndex);
|
|
}
|
|
else if (!bgBootPolicyFound) {
|
|
msg(usprintf("%s: startup ACM and Key Manifest found, Boot Policy is not", __FUNCTION__), kmIndex);
|
|
}
|
|
else {
|
|
// Check key hashes
|
|
if (!bgKmHash.isEmpty()
|
|
&& !(bgKmHash == bgBpHashSha256 || bgKmHash == bgBpHashSha384)) {
|
|
msg(usprintf("%s: Boot Policy key hash stored in Key Manifest differs from the hash of the public key stored in Boot Policy", __FUNCTION__), bpIndex);
|
|
return U_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
void FitParser::findFitRecursive(const UModelIndex & index, UModelIndex & found, UINT32 & fitOffset)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid()) {
|
|
return;
|
|
}
|
|
|
|
// Process child items
|
|
for (int i = 0; i < model->rowCount(index); i++) {
|
|
findFitRecursive(index.model()->index(i, 0, index), found, fitOffset);
|
|
|
|
if (found.isValid()) {
|
|
// Found it, no need to process further
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check for all FIT signatures in item body
|
|
UByteArray lastVtfBody = model->body(ffsParser->lastVtf);
|
|
UINT64 fitSignatureValue = INTEL_FIT_SIGNATURE;
|
|
UByteArray fitSignature((const char*)&fitSignatureValue, sizeof(fitSignatureValue));
|
|
UINT32 storedFitAddress = *(const UINT32*)(lastVtfBody.constData() + lastVtfBody.size() - INTEL_FIT_POINTER_OFFSET);
|
|
for (INT32 offset = (INT32)model->body(index).indexOf(fitSignature);
|
|
offset >= 0;
|
|
offset = (INT32)model->body(index).indexOf(fitSignature, offset + 1)) {
|
|
// FIT candidate found, calculate its physical address
|
|
UINT32 fitAddress = (UINT32)(model->base(index) + (UINT32)ffsParser->addressDiff + model->header(index).size() + (UINT32)offset);
|
|
|
|
// Check FIT address to be stored in the last VTF
|
|
if (fitAddress == storedFitAddress) {
|
|
// Valid FIT table must have at least two entries
|
|
if ((UINT32)model->body(index).size() < offset + 2*sizeof(INTEL_FIT_ENTRY)) {
|
|
msg(usprintf("%s: FIT table candidate found, too small to contain real FIT", __FUNCTION__), index);
|
|
}
|
|
else {
|
|
// Real FIT found
|
|
found = index;
|
|
fitOffset = offset;
|
|
msg(usprintf("%s: real FIT table found at physical address %08Xh", __FUNCTION__, fitAddress), found);
|
|
break;
|
|
}
|
|
}
|
|
else if (model->rowCount(index) == 0) { // Show messages only to leaf items
|
|
msg(usprintf("%s: FIT table candidate found, but not referenced from the last VTF", __FUNCTION__), index);
|
|
}
|
|
}
|
|
}
|
|
|
|
USTATUS FitParser::parseFitEntryMicrocode(const UByteArray & microcode, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize)
|
|
{
|
|
U_UNUSED_PARAMETER(parent);
|
|
if ((UINT32)microcode.size() - localOffset < sizeof(INTEL_MICROCODE_HEADER)) {
|
|
return U_INVALID_MICROCODE;
|
|
}
|
|
|
|
const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)(microcode.constData() + localOffset);
|
|
if (!ffsParser->microcodeHeaderValid(ucodeHeader)) {
|
|
return U_INVALID_MICROCODE;
|
|
}
|
|
|
|
if ((UINT32)microcode.size() - localOffset < ucodeHeader->TotalSize) {
|
|
return U_INVALID_MICROCODE;
|
|
}
|
|
|
|
// Valid microcode found
|
|
info = usprintf("CpuSignature: %08Xh, Revision: %08Xh, Date: %02X.%02X.%04X",
|
|
ucodeHeader->ProcessorSignature,
|
|
ucodeHeader->UpdateRevision,
|
|
ucodeHeader->DateDay,
|
|
ucodeHeader->DateMonth,
|
|
ucodeHeader->DateYear);
|
|
realSize = ucodeHeader->TotalSize;
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS FitParser::parseFitEntryAcm(const UByteArray & acm, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize)
|
|
{
|
|
try {
|
|
umemstream is(acm.constData(), acm.size());
|
|
is.seekg(localOffset, is.beg);
|
|
kaitai::kstream ks(&is);
|
|
intel_acm_t parsed(&ks);
|
|
intel_acm_t::header_t* header = parsed.header();
|
|
|
|
realSize = header->module_size();
|
|
|
|
// Check header version to be of a known value
|
|
if (header->header_version() != intel_acm_t::KNOWN_HEADER_VERSION_V0_0
|
|
&& header->header_version() != intel_acm_t::KNOWN_HEADER_VERSION_V3_0) {
|
|
msg(usprintf("%s: Intel ACM with unknown header version %08Xh found", __FUNCTION__, header->header_version()), parent);
|
|
}
|
|
|
|
// Valid ACM found
|
|
info = usprintf("LocalOffset: %08Xh, EntryPoint: %08Xh, ACM SVN: %04Xh, Date: %02X.%02X.%04X",
|
|
localOffset,
|
|
header->entry_point(),
|
|
header->acm_svn(),
|
|
header->date_day(),
|
|
header->date_month(),
|
|
header->date_year());
|
|
|
|
// Populate ACM info
|
|
UString acmInfo;
|
|
if (header->module_subtype() == intel_acm_t::MODULE_SUBTYPE_TXT) {
|
|
acmInfo = "TXT ACM ";
|
|
}
|
|
else if(header->module_subtype() == intel_acm_t::MODULE_SUBTYPE_STARTUP) {
|
|
acmInfo = "Startup ACM ";
|
|
}
|
|
else if (header->module_subtype() == intel_acm_t::MODULE_SUBTYPE_BOOT_GUARD) {
|
|
acmInfo = "BootGuard ACM ";
|
|
}
|
|
else {
|
|
acmInfo = usprintf("Unknown ACM (%04Xh)", header->module_subtype());
|
|
msg(usprintf("%s: Intel ACM with unknown subtype %04Xh found", __FUNCTION__, header->module_subtype()), parent);
|
|
}
|
|
|
|
acmInfo += usprintf("found at base %Xh\n"
|
|
"ModuleType: %04Xh\n"
|
|
"ModuleSubtype: %04Xh\n"
|
|
"HeaderSize: %08Xh\n"
|
|
"HeaderVersion: %08Xh\n"
|
|
"ChipsetId: %04Xh\n"
|
|
"Flags: %04Xh\n"
|
|
"ModuleVendor: %04Xh\n"
|
|
"Date: %02X.%02X.%04X\n"
|
|
"ModuleSize: %08Xh\n"
|
|
"AcmSvn: %04Xh\n"
|
|
"SeSvn: %04Xh\n"
|
|
"CodeControlFlags: %08Xh\n"
|
|
"ErrorEntryPoint: %08Xh\n"
|
|
"GdtMax: %08Xh\n"
|
|
"GdtBase: %08Xh\n"
|
|
"SegmentSel: %08Xh\n"
|
|
"EntryPoint: %08Xh\n"
|
|
"KeySize: %08Xh\n"
|
|
"ScratchSpaceSize: %08Xh\n",
|
|
model->base(parent) + localOffset,
|
|
header->module_type(),
|
|
header->module_subtype(),
|
|
header->header_size() * (UINT32)sizeof(UINT32),
|
|
header->header_version(),
|
|
header->chipset_id(),
|
|
header->flags(),
|
|
header->module_vendor(),
|
|
header->date_day(), header->date_month(), header->date_year(),
|
|
header->module_size() * (UINT32)sizeof(UINT32),
|
|
header->acm_svn(),
|
|
header->se_svn(),
|
|
header->code_control_flags(),
|
|
header->error_entry_point(),
|
|
header->gdt_max(),
|
|
header->gdt_base(),
|
|
header->segment_sel(),
|
|
header->entry_point(),
|
|
header->key_size() * (UINT32)sizeof(UINT32),
|
|
header->scratch_space_size() * (UINT32)sizeof(UINT32));
|
|
|
|
// Add RsaPublicKey
|
|
if (header->_is_null_rsa_exponent() == false) {
|
|
acmInfo += usprintf("ACM RSA Public Key Exponent: %Xh\n", header->rsa_exponent());
|
|
}
|
|
else {
|
|
acmInfo += usprintf("ACM RSA Public Key Exponent: %Xh\n", INTEL_ACM_HARDCODED_RSA_EXPONENT);
|
|
}
|
|
acmInfo += usprintf("ACM RSA Public Key:");
|
|
for (UINT32 i = 0; i < header->rsa_public_key().size(); i++) {
|
|
if (i % 32 == 0) acmInfo += "\n";
|
|
acmInfo += usprintf("%02X", (UINT8)header->rsa_public_key().at(i));
|
|
}
|
|
acmInfo += "\n";
|
|
|
|
// Add RsaSignature
|
|
acmInfo += UString("ACM RSA Signature:");
|
|
for (UINT32 i = 0; i < header->rsa_signature().size(); i++) {
|
|
if (i % 32 == 0) acmInfo +="\n";
|
|
acmInfo += usprintf("%02X", (UINT8)header->rsa_signature().at(i));
|
|
}
|
|
acmInfo += "\n";
|
|
|
|
securityInfo += acmInfo + "\n";
|
|
bgAcmFound = true;
|
|
return U_SUCCESS;
|
|
}
|
|
catch (...) {
|
|
msg(usprintf("%s: unable to parse ACM", __FUNCTION__), parent);
|
|
return U_INVALID_ACM;
|
|
}
|
|
}
|
|
|
|
USTATUS FitParser::parseFitEntryBootGuardKeyManifest(const UByteArray & keyManifest, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize)
|
|
{
|
|
U_UNUSED_PARAMETER(realSize);
|
|
|
|
// v1
|
|
try {
|
|
umemstream is(keyManifest.constData(), keyManifest.size());
|
|
is.seekg(localOffset, is.beg);
|
|
kaitai::kstream ks(&is);
|
|
intel_keym_v1_t parsed(&ks);
|
|
|
|
// Valid KM found
|
|
info = usprintf("LocalOffset: %08Xh, Version: %02Xh, KM Version: %02Xh, KM SVN: %02Xh",
|
|
localOffset,
|
|
parsed.version(),
|
|
parsed.km_version(),
|
|
parsed.km_svn());
|
|
|
|
// Populate KM info
|
|
UString kmInfo
|
|
= usprintf("Intel BootGuard Key manifest found at base %Xh\n"
|
|
"Tag: '__KEYM__'\n"
|
|
"Version: %02Xh\n"
|
|
"KmVersion: %02Xh\n"
|
|
"KmSvn: %02Xh\n"
|
|
"KmId: %02Xh\n",
|
|
model->base(parent) + localOffset,
|
|
parsed.version(),
|
|
parsed.km_version(),
|
|
parsed.km_svn(),
|
|
parsed.km_id());
|
|
|
|
// Add KM hash
|
|
kmInfo += UString("KM Hash (") + hashTypeToUString(parsed.km_hash()->hash_algorithm_id()) + "): ";
|
|
for (UINT16 j = 0; j < parsed.km_hash()->len_hash(); j++) {
|
|
kmInfo += usprintf("%02X", (UINT8) parsed.km_hash()->hash().data()[j]);
|
|
}
|
|
kmInfo += "\n";
|
|
|
|
// Add Key Signature
|
|
const intel_keym_v1_t::key_signature_t* key_signature = parsed.key_signature();
|
|
kmInfo += usprintf("Key Manifest Key Signature:\n"
|
|
"Version: %02Xh\n"
|
|
"KeyId: %04Xh\n"
|
|
"SigScheme: %04Xh\n",
|
|
key_signature->version(),
|
|
key_signature->key_id(),
|
|
key_signature->sig_scheme());
|
|
|
|
// Add PubKey
|
|
kmInfo += usprintf("Key Manifest Public Key Exponent: %Xh\n", key_signature->public_key()->exponent());
|
|
kmInfo += usprintf("Key Manifest Public Key:");
|
|
for (UINT16 i = 0; i < (UINT16)key_signature->public_key()->modulus().length(); i++) {
|
|
if (i % 32 == 0) kmInfo += UString("\n");
|
|
kmInfo += usprintf("%02X", (UINT8)key_signature->public_key()->modulus().at(i));
|
|
}
|
|
kmInfo += "\n";
|
|
|
|
// One of those hashes is what's getting written into Field Programmable Fuses
|
|
// Calculate the hashes of public key modulus only
|
|
UINT8 hash[SHA384_HASH_SIZE] = {};
|
|
sha256(key_signature->public_key()->modulus().data(), key_signature->public_key()->modulus().length(), hash);
|
|
kmInfo += usprintf("Key Manifest Public Key Hash (Modulus Only, SHA256): ");
|
|
for (UINT8 i = 0; i < SHA256_HASH_SIZE; i++) {
|
|
kmInfo += usprintf("%02X", hash[i]);
|
|
}
|
|
kmInfo += "\n";
|
|
sha384(key_signature->public_key()->modulus().data(), key_signature->public_key()->modulus().length(), hash);
|
|
kmInfo += usprintf("Key Manifest Public Key Hash (Modulus Only, SHA384): ");
|
|
for (UINT8 i = 0; i < SHA384_HASH_SIZE; i++) {
|
|
kmInfo += usprintf("%02X", hash[i]);
|
|
}
|
|
kmInfo += "\n";
|
|
// Calculate the hashes of public key modulus + exponent
|
|
UByteArray dataToHash;
|
|
dataToHash += UByteArray(key_signature->public_key()->modulus().data(), key_signature->public_key()->modulus().length());
|
|
UINT32 exponent = key_signature->public_key()->exponent();
|
|
dataToHash += UByteArray((const char*)&exponent, sizeof(exponent));
|
|
sha256(dataToHash.constData(), dataToHash.size(), hash);
|
|
kmInfo += usprintf("Key Manifest Public Key Hash (Modulus+Exponent, SHA256): ");
|
|
for (UINT8 i = 0; i < SHA256_HASH_SIZE; i++) {
|
|
kmInfo += usprintf("%02X", hash[i]);
|
|
}
|
|
kmInfo += "\n";
|
|
sha384(dataToHash.constData(), dataToHash.size(), hash);
|
|
kmInfo += usprintf("Key Manifest Public Key Hash (Modulus+Exponent, SHA384): ");
|
|
for (UINT8 i = 0; i < SHA384_HASH_SIZE; i++) {
|
|
kmInfo += usprintf("%02X", hash[i]);
|
|
}
|
|
kmInfo += "\n";
|
|
|
|
// Add Signature
|
|
kmInfo += UString("Key Manifest Signature: ");
|
|
for (UINT16 i = 0; i < (UINT16)key_signature->signature()->signature().length(); i++) {
|
|
if (i % 32 == 0) kmInfo += UString("\n");
|
|
kmInfo += usprintf("%02X", (UINT8)key_signature->signature()->signature().at(i));
|
|
}
|
|
kmInfo += "\n";
|
|
|
|
securityInfo += kmInfo + "\n";
|
|
bgKeyManifestFound = true;
|
|
return U_SUCCESS;
|
|
}
|
|
catch (...) {
|
|
// Do nothing here, will try parsing as v2 next
|
|
}
|
|
|
|
// v2
|
|
try {
|
|
umemstream is(keyManifest.constData(), keyManifest.size());
|
|
is.seekg(localOffset, is.beg);
|
|
kaitai::kstream ks(&is);
|
|
intel_keym_v2_t parsed(&ks);
|
|
intel_keym_v2_t::header_t* header = parsed.header();
|
|
|
|
// Valid KM found
|
|
info = usprintf("LocalOffset: %08Xh, Version: %02Xh, KM Version: %02Xh, KM SVN: %02Xh",
|
|
localOffset,
|
|
header->version(),
|
|
parsed.km_version(),
|
|
parsed.km_svn());
|
|
|
|
// Populate KM info
|
|
UString kmInfo
|
|
= usprintf("Intel BootGuard Key manifest found at base %Xh\n"
|
|
"Tag: '__KEYM__'\n"
|
|
"Version: %02Xh\n"
|
|
"KmVersion: %02Xh\n"
|
|
"KmSvn: %02Xh\n"
|
|
"KmId: %02Xh\n"
|
|
"KeySignatureOffset: %04Xh\n"
|
|
"FPFHashAlgorithmId: %04Xh\n"
|
|
"HashCount: %04Xh\n",
|
|
model->base(parent) + localOffset,
|
|
header->version(),
|
|
parsed.km_version(),
|
|
parsed.km_svn(),
|
|
parsed.km_id(),
|
|
parsed.key_signature_offset(),
|
|
parsed.fpf_hash_algorithm_id(),
|
|
parsed.num_km_hashes());
|
|
|
|
// Add KM hashes
|
|
if (parsed.num_km_hashes() == 0) {
|
|
kmInfo += UString("KM Hashes: N/A\n");
|
|
msg(usprintf("%s: Key Manifest without KM hashes", __FUNCTION__), parent);
|
|
}
|
|
else {
|
|
kmInfo += UString("KM Hashes:\n");
|
|
for (UINT16 i = 0; i < parsed.num_km_hashes(); i++) {
|
|
const auto & current_km_hash = parsed.km_hashes()->at(i);
|
|
|
|
// Add KM hash
|
|
kmInfo += usprintf("UsageFlags: %016" PRIX64 "h, ", current_km_hash->usage_flags()) + hashTypeToUString(current_km_hash->hash_algorithm_id()) + ": ";
|
|
for (UINT16 j = 0; j < current_km_hash->len_hash(); j++) {
|
|
kmInfo += usprintf("%02X", (UINT8)current_km_hash->hash().data()[j]);
|
|
}
|
|
kmInfo += "\n";
|
|
|
|
if (current_km_hash->usage_flags() == intel_keym_v2_t::KM_USAGE_FLAGS_BOOT_POLICY_MANIFEST) {
|
|
bgKmHash = UByteArray((const char*)current_km_hash->hash().data(), current_km_hash->hash().size());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add Key Signature
|
|
const intel_keym_v2_t::key_signature_t* key_signature = parsed.key_signature();
|
|
kmInfo += usprintf("Key Manifest Key Signature:\n"
|
|
"Version: %02Xh\n"
|
|
"KeyId: %04Xh\n"
|
|
"SigScheme: %04Xh\n",
|
|
key_signature->version(),
|
|
key_signature->key_id(),
|
|
key_signature->sig_scheme());
|
|
|
|
// Add PubKey
|
|
kmInfo += usprintf("Key Manifest Public Key Exponent: %Xh\n", key_signature->public_key()->exponent());
|
|
kmInfo += usprintf("Key Manifest Public Key:");
|
|
for (UINT16 i = 0; i < (UINT16)key_signature->public_key()->modulus().length(); i++) {
|
|
if (i % 32 == 0) kmInfo += UString("\n");
|
|
kmInfo += usprintf("%02X", (UINT8)key_signature->public_key()->modulus().at(i));
|
|
}
|
|
kmInfo += "\n";
|
|
|
|
// One of those hashes is what's getting written into Field Programmable Fuses
|
|
// Calculate the hashes of public key modulus only
|
|
UINT8 hash[SHA384_HASH_SIZE] = {};
|
|
sha256(key_signature->public_key()->modulus().data(), key_signature->public_key()->modulus().length(), hash);
|
|
kmInfo += usprintf("Key Manifest Public Key Hash (Modulus Only, SHA256): ");
|
|
for (UINT8 i = 0; i < SHA256_HASH_SIZE; i++) {
|
|
kmInfo += usprintf("%02X", hash[i]);
|
|
}
|
|
kmInfo += "\n";
|
|
sha384(key_signature->public_key()->modulus().data(), key_signature->public_key()->modulus().length(), hash);
|
|
kmInfo += usprintf("Key Manifest Public Key Hash (Modulus Only, SHA384): ");
|
|
for (UINT8 i = 0; i < SHA384_HASH_SIZE; i++) {
|
|
kmInfo += usprintf("%02X", hash[i]);
|
|
}
|
|
kmInfo += "\n";
|
|
// Calculate the hashes of public key modulus + exponent
|
|
UByteArray dataToHash;
|
|
dataToHash += UByteArray(key_signature->public_key()->modulus().data(), key_signature->public_key()->modulus().length());
|
|
UINT32 exponent = key_signature->public_key()->exponent();
|
|
dataToHash += UByteArray((const char*)&exponent, sizeof(exponent));
|
|
sha256(dataToHash.constData(), dataToHash.size(), hash);
|
|
kmInfo += usprintf("Key Manifest Public Key Hash (Modulus+Exponent, SHA256): ");
|
|
for (UINT8 i = 0; i < SHA256_HASH_SIZE; i++) {
|
|
kmInfo += usprintf("%02X", hash[i]);
|
|
}
|
|
kmInfo += "\n";
|
|
sha384(dataToHash.constData(), dataToHash.size(), hash);
|
|
kmInfo += usprintf("Key Manifest Public Key Hash (Modulus+Exponent, SHA384): ");
|
|
for (UINT8 i = 0; i < SHA384_HASH_SIZE; i++) {
|
|
kmInfo += usprintf("%02X", hash[i]);
|
|
}
|
|
kmInfo += "\n";
|
|
|
|
// Add Signature
|
|
kmInfo += UString("Key Manifest Signature: ");
|
|
for (UINT16 i = 0; i < (UINT16)key_signature->signature()->signature().length(); i++) {
|
|
if (i % 32 == 0) kmInfo += UString("\n");
|
|
kmInfo += usprintf("%02X", (UINT8)key_signature->signature()->signature().at(i));
|
|
}
|
|
kmInfo += "\n";
|
|
|
|
securityInfo += kmInfo + "\n";
|
|
bgKeyManifestFound = true;
|
|
return U_SUCCESS;
|
|
}
|
|
catch (...) {
|
|
msg(usprintf("%s: unable to parse Key Manifest", __FUNCTION__), parent);
|
|
return U_INVALID_BOOT_GUARD_KEY_MANIFEST;
|
|
}
|
|
}
|
|
|
|
USTATUS FitParser::parseFitEntryBootGuardBootPolicy(const UByteArray & bootPolicy, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize)
|
|
{
|
|
U_UNUSED_PARAMETER(realSize);
|
|
|
|
// v1
|
|
try {
|
|
umemstream is(bootPolicy.constData(), bootPolicy.size());
|
|
is.seekg(localOffset, is.beg);
|
|
kaitai::kstream ks(&is);
|
|
intel_acbp_v1_t parsed(&ks);
|
|
|
|
// Valid BPM found
|
|
info = usprintf("LocalOffset: %08Xh, Version: %02Xh, BP SVN: %02Xh, ACM SVN: %02Xh",
|
|
localOffset,
|
|
parsed.version(),
|
|
parsed.bp_svn(),
|
|
parsed.acm_svn());
|
|
|
|
UString bpInfo
|
|
= usprintf("Intel BootGuard Boot Policy Manifest found at base %Xh\n"
|
|
"StructureId: '__ACBP__'\n"
|
|
"Version: %02Xh\n"
|
|
"BPMRevision: %02Xh\n"
|
|
"BPSVN: %02Xh\n"
|
|
"ACMSVN: %02Xh\n"
|
|
"NEMDataSize: %04Xh\n",
|
|
model->base(parent) + localOffset,
|
|
parsed.version(),
|
|
parsed.bpm_revision(),
|
|
parsed.bp_svn(),
|
|
parsed.acm_svn(),
|
|
parsed.nem_data_size());
|
|
|
|
bpInfo += UString("Boot Policy Elements:\n");
|
|
for (const auto & element : *parsed.elements()) {
|
|
const auto & element_header = element->header();
|
|
|
|
UINT64 structure_id = (UINT64) element_header->structure_id();
|
|
const char* structure_id_bytes = (const char*)&structure_id;
|
|
|
|
bpInfo += usprintf("StructureId: '%c%c%c%c%c%c%c%c'\n"
|
|
"Version: %02Xh\n",
|
|
structure_id_bytes[0],
|
|
structure_id_bytes[1],
|
|
structure_id_bytes[2],
|
|
structure_id_bytes[3],
|
|
structure_id_bytes[4],
|
|
structure_id_bytes[5],
|
|
structure_id_bytes[6],
|
|
structure_id_bytes[7],
|
|
element_header->version());
|
|
|
|
// IBBS
|
|
if (element->_is_null_ibbs_body() == false) {
|
|
const intel_acbp_v1_t::ibbs_body_t* ibbs_body = element->ibbs_body();
|
|
|
|
// Valid IBBS element found
|
|
bpInfo += usprintf("Flags: %08Xh\n"
|
|
"MchBar: %016" PRIX64 "h\n"
|
|
"VtdBar: %016" PRIX64 "h\n"
|
|
"DmaProtectionBase0: %08Xh\n"
|
|
"DmaProtectionLimit0: %08Xh\n"
|
|
"DmaProtectionBase1: %016" PRIX64 "h\n"
|
|
"DmaProtectionLimit1: %016" PRIX64 "h\n"
|
|
"IbbEntryPoint: %08Xh\n"
|
|
"IbbSegmentsCount: %02Xh\n",
|
|
ibbs_body->flags(),
|
|
ibbs_body->mch_bar(),
|
|
ibbs_body->vtd_bar(),
|
|
ibbs_body->dma_protection_base0(),
|
|
ibbs_body->dma_protection_limit0(),
|
|
ibbs_body->dma_protection_base1(),
|
|
ibbs_body->dma_protection_limit1(),
|
|
ibbs_body->ibb_entry_point(),
|
|
ibbs_body->num_ibb_segments());
|
|
|
|
// Check for non-empty PostIbbHash
|
|
if (ibbs_body->post_ibb_hash()->len_hash() == 0) {
|
|
bpInfo += UString("PostIBB Hash: N/A\n");
|
|
}
|
|
else {
|
|
// Add postIbbHash protected range
|
|
UByteArray postIbbHash(ibbs_body->post_ibb_hash()->hash().data(), ibbs_body->post_ibb_hash()->len_hash());
|
|
if (postIbbHash.count('\x00') != postIbbHash.size()
|
|
&& postIbbHash.count('\xFF') != postIbbHash.size()) {
|
|
PROTECTED_RANGE range = {};
|
|
range.Type = PROTECTED_RANGE_INTEL_BOOT_GUARD_POST_IBB;
|
|
range.AlgorithmId = ibbs_body->post_ibb_hash()->hash_algorithm_id();
|
|
range.Hash = postIbbHash;
|
|
ffsParser->protectedRanges.push_back(range);
|
|
}
|
|
|
|
// Add PostIbbHash
|
|
bpInfo += UString("PostIBB Hash (") + hashTypeToUString(ibbs_body->post_ibb_hash()->hash_algorithm_id()) + "): ";
|
|
for (UINT16 i = 0; i < ibbs_body->post_ibb_hash()->len_hash(); i++) {
|
|
bpInfo += usprintf("%02X", (UINT8)ibbs_body->post_ibb_hash()->hash().data()[i]);
|
|
}
|
|
bpInfo += "\n";
|
|
}
|
|
|
|
// Add IbbHash
|
|
bpInfo += UString("IBB Hash (") + hashTypeToUString(ibbs_body->ibb_hash()->hash_algorithm_id()) + "): ";
|
|
for (UINT16 j = 0; j < ibbs_body->ibb_hash()->len_hash(); j++) {
|
|
bpInfo += usprintf("%02X", (UINT8)ibbs_body->ibb_hash()->hash().data()[j]);
|
|
}
|
|
bpInfo += "\n";
|
|
|
|
// Check for non-empty IbbSegments
|
|
if (ibbs_body->num_ibb_segments() == 0) {
|
|
bpInfo += UString("IBB Segments: N/A\n");
|
|
msg(usprintf("%s: Boot Policy without IBB segments", __FUNCTION__), parent);
|
|
}
|
|
else {
|
|
bpInfo += UString("IBB Segments:\n");
|
|
for (UINT8 i = 0; i < ibbs_body->num_ibb_segments(); i++) {
|
|
const auto & current_segment = ibbs_body->ibb_segments()->at(i);
|
|
|
|
bpInfo += usprintf("Flags: %04Xh, Address: %08Xh, Size: %08Xh\n",
|
|
current_segment->flags(),
|
|
current_segment->base(),
|
|
current_segment->size());
|
|
|
|
if (current_segment->flags() == intel_acbp_v1_t::IBB_SEGMENT_TYPE_IBB
|
|
&& current_segment->base() != 0xFFFFFFFF && current_segment->size() != 0 && current_segment->size() != 0xFFFFFFFF) {
|
|
PROTECTED_RANGE range = {};
|
|
range.Offset = current_segment->base();
|
|
range.Size = current_segment->size();
|
|
range.AlgorithmId = TCG_HASH_ALGORITHM_ID_SHA256;
|
|
range.Type = PROTECTED_RANGE_INTEL_BOOT_GUARD_IBB;
|
|
ffsParser->protectedRanges.push_back(range);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// PMDA
|
|
else if (element->_is_null_pmda_body() == false) {
|
|
intel_acbp_v1_t::pmda_body_t* pmda_body = element->pmda_body();
|
|
|
|
// Valid Microsoft PMDA element found
|
|
bpInfo += usprintf("TotalSize: %04Xh\n"
|
|
"Version: %08Xh\n"
|
|
"NumEntries: %08Xh\n",
|
|
pmda_body->total_size(),
|
|
pmda_body->version(),
|
|
pmda_body->num_entries());
|
|
if (pmda_body->num_entries() == 0) {
|
|
bpInfo += UString("PMDA Entries: N/A\n");
|
|
}
|
|
else {
|
|
bpInfo += UString("PMDA Entries:\n");
|
|
// v1 entries
|
|
if (pmda_body->_is_null_entries_v1() == false) {
|
|
for (UINT32 i = 0; i < pmda_body->num_entries(); i++) {
|
|
const auto & current_element = pmda_body->entries_v1()->at(i);
|
|
|
|
// Add element
|
|
bpInfo += usprintf("Address: %08Xh, Size: %08Xh\n",
|
|
current_element->base(),
|
|
current_element->size());
|
|
|
|
// Add hash
|
|
bpInfo += "SHA256: ";
|
|
for (UINT16 j = 0; j < (UINT16)current_element->hash().size(); j++) {
|
|
bpInfo += usprintf("%02X", (UINT8)current_element->hash().data()[j]);
|
|
}
|
|
bpInfo += "\n";
|
|
|
|
// Add protected range
|
|
if (current_element->base() != 0xFFFFFFFF && current_element->size() != 0 && current_element->size() != 0xFFFFFFFF) {
|
|
PROTECTED_RANGE range = {};
|
|
range.Offset = current_element->base();
|
|
range.Size = current_element->size();
|
|
range.Type = PROTECTED_RANGE_VENDOR_HASH_MICROSOFT_PMDA;
|
|
range.AlgorithmId = TCG_HASH_ALGORITHM_ID_SHA256;
|
|
range.Hash = UByteArray(current_element->hash().data(), current_element->hash().size());
|
|
ffsParser->protectedRanges.push_back(range);
|
|
}
|
|
}
|
|
}
|
|
// v2 entries
|
|
else if (pmda_body->_is_null_entries_v2() == false) {
|
|
for (UINT32 i = 0; i < pmda_body->num_entries(); i++) {
|
|
const auto & current_element = pmda_body->entries_v2()->at(i);
|
|
|
|
// Add element
|
|
bpInfo += usprintf("Address: %08Xh, Size: %08Xh\n",
|
|
current_element->base(),
|
|
current_element->size());
|
|
|
|
// Add hash
|
|
bpInfo += hashTypeToUString(current_element->hash()->hash_algorithm_id()) + ": ";
|
|
for (UINT16 j = 0; j < (UINT16)current_element->hash()->hash().size(); j++) {
|
|
bpInfo += usprintf("%02X", (UINT8)current_element->hash()->hash().data()[j]);
|
|
}
|
|
bpInfo += "\n";
|
|
|
|
// Add protected range
|
|
if (current_element->base() != 0xFFFFFFFF && current_element->size() != 0 && current_element->size() != 0xFFFFFFFF) {
|
|
PROTECTED_RANGE range = {};
|
|
range.Offset = current_element->base();
|
|
range.Size = current_element->size();
|
|
range.Type = PROTECTED_RANGE_VENDOR_HASH_MICROSOFT_PMDA;
|
|
range.AlgorithmId = current_element->hash()->hash_algorithm_id();
|
|
range.Hash = UByteArray(current_element->hash()->hash().data(), current_element->hash()->hash().size());
|
|
ffsParser->protectedRanges.push_back(range);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// PMSG
|
|
else if (element->_is_null_pmsg_body() == false) {
|
|
const intel_acbp_v1_t::pmsg_body_t* key_signature = element->pmsg_body();
|
|
bpInfo += usprintf("Boot Policy Key Signature:\n"
|
|
"Version: %02Xh\n"
|
|
"KeyId: %04Xh\n"
|
|
"SigScheme: %04Xh\n",
|
|
key_signature->version(),
|
|
key_signature->key_id(),
|
|
key_signature->sig_scheme());
|
|
|
|
// Add PubKey
|
|
bpInfo += usprintf("Boot Policy Public Key Exponent: %Xh\n", key_signature->public_key()->exponent());
|
|
bpInfo += usprintf("Boot Policy Public Key:");
|
|
for (UINT16 i = 0; i < (UINT16)key_signature->public_key()->modulus().length(); i++) {
|
|
if (i % 32 == 0) bpInfo += UString("\n");
|
|
bpInfo += usprintf("%02X", (UINT8)key_signature->public_key()->modulus().at(i));
|
|
}
|
|
bpInfo += "\n";
|
|
|
|
// Calculate and add PubKey hashes
|
|
UINT8 hash[SHA384_HASH_SIZE];
|
|
// SHA256
|
|
sha256(key_signature->public_key()->modulus().data(), key_signature->public_key()->modulus().length() , hash);
|
|
bpInfo += UString("Boot Policy Public Key Hash (SHA256): ");
|
|
for (UINT8 i = 0; i < SHA256_HASH_SIZE; i++) {
|
|
bpInfo += usprintf("%02X", hash[i]);
|
|
}
|
|
bpInfo += "\n";
|
|
bgBpHashSha256 = UByteArray((const char*)hash, SHA256_HASH_SIZE);
|
|
// SHA384
|
|
sha384(key_signature->public_key()->modulus().data(), key_signature->public_key()->modulus().length() , hash);
|
|
bpInfo += UString("Boot Policy Public Key Hash (SHA384): ");
|
|
for (UINT8 i = 0; i < SHA384_HASH_SIZE; i++) {
|
|
bpInfo += usprintf("%02X", hash[i]);
|
|
}
|
|
bpInfo += "\n";
|
|
bgBpHashSha384 = UByteArray((const char*)hash, SHA384_HASH_SIZE);
|
|
|
|
// Add Signature
|
|
bpInfo += UString("Boot Policy Signature: ");
|
|
for (UINT16 i = 0; i < (UINT16)key_signature->signature()->signature().length(); i++) {
|
|
if (i % 32 == 0) bpInfo += UString("\n");
|
|
bpInfo += usprintf("%02X", (UINT8)key_signature->signature()->signature().at(i));
|
|
}
|
|
bpInfo += "\n";
|
|
}
|
|
}
|
|
|
|
securityInfo += bpInfo + "\n";
|
|
bgBootPolicyFound = true;
|
|
return U_SUCCESS;
|
|
}
|
|
catch (...) {
|
|
// Do nothing here, will try parsing as v2 next
|
|
}
|
|
|
|
// v2
|
|
try {
|
|
umemstream is(bootPolicy.constData(), bootPolicy.size());
|
|
is.seekg(localOffset, is.beg);
|
|
kaitai::kstream ks(&is);
|
|
intel_acbp_v2_t parsed(&ks); // This already verified the version to be >= 0x20
|
|
// Valid BPM found
|
|
info = usprintf("LocalOffset: %08Xh, Version: %02Xh, BP SVN: %02Xh, ACM SVN: %02Xh",
|
|
localOffset,
|
|
parsed.version(),
|
|
parsed.bp_svn(),
|
|
parsed.acm_svn());
|
|
|
|
// Add BP header and body info
|
|
UString bpInfo
|
|
= usprintf("Intel BootGuard Boot Policy Manifest found at base %Xh\n"
|
|
"StructureId: '__ACBP__'\n"
|
|
"Version: %02Xh\n"
|
|
"HeaderSpecific: %02Xh\n"
|
|
"TotalSize: %04Xh\n"
|
|
"KeySignatureOffset: %04Xh\n"
|
|
"BPMRevision: %02Xh\n"
|
|
"BPSVN: %02Xh\n"
|
|
"ACMSVN: %02Xh\n"
|
|
"NEMDataSize: %04Xh\n",
|
|
model->base(parent) + localOffset,
|
|
parsed.version(),
|
|
parsed.header_specific(),
|
|
parsed.total_size(),
|
|
parsed.key_signature_offset(),
|
|
parsed.bpm_revision(),
|
|
parsed.bp_svn(),
|
|
parsed.acm_svn(),
|
|
parsed.nem_data_size());
|
|
|
|
bpInfo += UString("Boot Policy Elements:\n");
|
|
for (const auto & element : *parsed.elements()) {
|
|
const intel_acbp_v2_t::header_t* element_header = element->header();
|
|
|
|
UINT64 structure_id = element_header->structure_id();
|
|
const char* structure_id_bytes = (const char*)&structure_id;
|
|
|
|
bpInfo += usprintf("StructureId: '%c%c%c%c%c%c%c%c'\n"
|
|
"Version: %02Xh\n"
|
|
"HeaderSpecific: %02Xh\n"
|
|
"TotalSize: %04Xh\n",
|
|
structure_id_bytes[0],
|
|
structure_id_bytes[1],
|
|
structure_id_bytes[2],
|
|
structure_id_bytes[3],
|
|
structure_id_bytes[4],
|
|
structure_id_bytes[5],
|
|
structure_id_bytes[6],
|
|
structure_id_bytes[7],
|
|
element_header->version(),
|
|
element_header->header_specific(),
|
|
element_header->total_size());
|
|
|
|
// IBBS
|
|
if (element->_is_null_ibbs_body() == false) {
|
|
const intel_acbp_v2_t::ibbs_body_t* ibbs_body = element->ibbs_body();
|
|
|
|
// Valid IBBS element found
|
|
bpInfo += usprintf("SetNumber: %02Xh\n"
|
|
"PBETValue: %02Xh\n"
|
|
"Flags: %08Xh\n"
|
|
"MchBar: %016" PRIX64 "h\n"
|
|
"VtdBar: %016" PRIX64 "h\n"
|
|
"DmaProtectionBase0: %08Xh\n"
|
|
"DmaProtectionLimit0: %08Xh\n"
|
|
"DmaProtectionBase1: %016" PRIX64 "h\n"
|
|
"DmaProtectionLimit1: %016" PRIX64 "h\n"
|
|
"IbbEntryPoint: %08Xh\n"
|
|
"IbbDigestsSize: %02Xh\n"
|
|
"IbbDigestsCount: %02Xh\n"
|
|
"IbbSegmentsCount: %02Xh\n",
|
|
ibbs_body->set_number(),
|
|
ibbs_body->pbet_value(),
|
|
ibbs_body->flags(),
|
|
ibbs_body->mch_bar(),
|
|
ibbs_body->vtd_bar(),
|
|
ibbs_body->dma_protection_base0(),
|
|
ibbs_body->dma_protection_limit0(),
|
|
ibbs_body->dma_protection_base1(),
|
|
ibbs_body->dma_protection_limit1(),
|
|
ibbs_body->ibb_entry_point(),
|
|
ibbs_body->ibb_digests_size(),
|
|
ibbs_body->num_ibb_digests(),
|
|
ibbs_body->num_ibb_segments());
|
|
|
|
// Check for non-empty PostIbbHash
|
|
if (ibbs_body->post_ibb_digest()->len_hash() == 0) {
|
|
bpInfo += UString("PostIBB Hash: N/A\n");
|
|
}
|
|
else {
|
|
// Add postIbbHash protected range
|
|
UByteArray postIbbHash(ibbs_body->post_ibb_digest()->hash().data(), ibbs_body->post_ibb_digest()->len_hash());
|
|
if (postIbbHash.count('\x00') != postIbbHash.size()
|
|
&& postIbbHash.count('\xFF') != postIbbHash.size()) {
|
|
PROTECTED_RANGE range = {};
|
|
range.Type = PROTECTED_RANGE_INTEL_BOOT_GUARD_POST_IBB;
|
|
range.AlgorithmId = ibbs_body->post_ibb_digest()->hash_algorithm_id();
|
|
range.Hash = postIbbHash;
|
|
ffsParser->protectedRanges.push_back(range);
|
|
}
|
|
|
|
// Add PostIbbDigest
|
|
bpInfo += UString("PostIBB Hash (") + hashTypeToUString(ibbs_body->post_ibb_digest()->hash_algorithm_id()) + "): ";
|
|
for (UINT16 i = 0; i < ibbs_body->post_ibb_digest()->len_hash(); i++) {
|
|
bpInfo += usprintf("%02X", (UINT8)ibbs_body->post_ibb_digest()->hash().data()[i]);
|
|
}
|
|
bpInfo += "\n";
|
|
}
|
|
|
|
// Check for non-empty ObbHash
|
|
if (ibbs_body->obb_digest() == 0) {
|
|
bpInfo += UString("OBB Hash: N/A\n");
|
|
}
|
|
else {
|
|
// Add ObbHash
|
|
bpInfo += UString("OBB Hash (") + hashTypeToUString(ibbs_body->obb_digest()->hash_algorithm_id()) + "): ";
|
|
for (UINT16 i = 0; i < ibbs_body->obb_digest()->len_hash(); i++) {
|
|
bpInfo += usprintf("%02X", (UINT8)ibbs_body->obb_digest()->hash().data()[i]);
|
|
}
|
|
bpInfo += "\n";
|
|
|
|
// Add ObbHash protected range
|
|
UByteArray obbHash(ibbs_body->obb_digest()->hash().data(), ibbs_body->obb_digest()->len_hash());
|
|
if (obbHash.count('\x00') != obbHash.size()
|
|
&& obbHash.count('\xFF') != obbHash.size()) {
|
|
PROTECTED_RANGE range = {};
|
|
range.Type = PROTECTED_RANGE_INTEL_BOOT_GUARD_OBB;
|
|
range.AlgorithmId = ibbs_body->obb_digest()->hash_algorithm_id();
|
|
range.Hash = obbHash;
|
|
ffsParser->protectedRanges.push_back(range);
|
|
}
|
|
}
|
|
|
|
// Check for non-empty IbbDigests
|
|
if (ibbs_body->num_ibb_digests() == 0) {
|
|
bpInfo += UString("IBB Hashes: N/A\n");
|
|
msg(usprintf("%s: Boot Policy without IBB digests", __FUNCTION__), parent);
|
|
}
|
|
else {
|
|
bpInfo += UString("IBB Hashes:\n");
|
|
for (UINT16 i = 0; i < ibbs_body->num_ibb_digests(); i++) {
|
|
const auto & current_hash = ibbs_body->ibb_digests()->at(i);
|
|
bpInfo += hashTypeToUString(current_hash->hash_algorithm_id()) + ": ";
|
|
for (UINT16 j = 0; j < current_hash->len_hash(); j++) {
|
|
bpInfo += usprintf("%02X", (UINT8)current_hash->hash().data()[j]);
|
|
}
|
|
bpInfo += "\n";
|
|
}
|
|
}
|
|
|
|
// Check for non-empty IbbSegments
|
|
if (ibbs_body->num_ibb_segments() == 0) {
|
|
bpInfo += UString("IBB Segments: N/A\n");
|
|
msg(usprintf("%s: Boot Policy without IBB segments", __FUNCTION__), parent);
|
|
}
|
|
else {
|
|
bpInfo += UString("IBB Segments:\n");
|
|
for (UINT8 i = 0; i < ibbs_body->num_ibb_segments(); i++) {
|
|
const auto & current_segment = ibbs_body->ibb_segments()->at(i);
|
|
|
|
bpInfo += usprintf("Flags: %04Xh, Address: %08Xh, Size: %08Xh\n",
|
|
current_segment->flags(),
|
|
current_segment->base(),
|
|
current_segment->size());
|
|
|
|
if (current_segment->flags() == intel_acbp_v2_t::IBB_SEGMENT_TYPE_IBB
|
|
&& current_segment->base() != 0xFFFFFFFF && current_segment->size() != 0 && current_segment->size() != 0xFFFFFFFF) {
|
|
PROTECTED_RANGE range = {};
|
|
range.Offset = current_segment->base();
|
|
range.Size =current_segment->size();
|
|
range.Type = PROTECTED_RANGE_INTEL_BOOT_GUARD_IBB;
|
|
range.AlgorithmId = TCG_HASH_ALGORITHM_ID_SHA256;
|
|
ffsParser->protectedRanges.push_back(range);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// PMDA
|
|
else if (element->_is_null_pmda_body() == false) {
|
|
const intel_acbp_v2_t::pmda_body_t* pmda_body = element->pmda_body();
|
|
|
|
// Valid Microsoft PMDA element found
|
|
bpInfo += usprintf("TotalSize: %04Xh\n"
|
|
"Version: %08Xh\n"
|
|
"NumEntries: %08Xh\n",
|
|
pmda_body->total_size(),
|
|
pmda_body->version(),
|
|
pmda_body->num_entries());
|
|
|
|
if (pmda_body->num_entries() == 0) {
|
|
bpInfo += UString("PMDA Entries: N/A\n");
|
|
}
|
|
else {
|
|
bpInfo += UString("PMDA Entries:\n");
|
|
for (UINT32 i = 0; i < pmda_body->num_entries(); i++) {
|
|
const auto & current_entry = pmda_body->entries()->at(i);
|
|
|
|
UINT64 entry_id = current_entry->entry_id();
|
|
const char* entry_id_bytes = (const char*)&entry_id;
|
|
|
|
// Add element
|
|
bpInfo += usprintf("EntryId: '%c%c%c%c', Version: %04Xh, Address: %08Xh, Size: %08Xh\n",
|
|
entry_id_bytes[0],
|
|
entry_id_bytes[1],
|
|
entry_id_bytes[2],
|
|
entry_id_bytes[3],
|
|
current_entry->version(),
|
|
current_entry->base(),
|
|
current_entry->size());
|
|
|
|
// Add hash
|
|
bpInfo += hashTypeToUString(current_entry->hash()->hash_algorithm_id()) + ": ";
|
|
for (UINT16 j = 0; j < current_entry->hash()->len_hash(); j++) {
|
|
bpInfo += usprintf("%02X", (UINT8)current_entry->hash()->hash().data()[j]);
|
|
}
|
|
bpInfo += "\n";
|
|
|
|
// Add protected range
|
|
if (current_entry->base() != 0xFFFFFFFF && current_entry->size() != 0 && current_entry->size() != 0xFFFFFFFF) {
|
|
PROTECTED_RANGE range = {};
|
|
range.Offset = current_entry->base();
|
|
range.Size = current_entry->size();
|
|
range.Type = PROTECTED_RANGE_VENDOR_HASH_MICROSOFT_PMDA;
|
|
range.AlgorithmId = current_entry->hash()->hash_algorithm_id();
|
|
range.Hash = UByteArray(current_entry->hash()->hash().data(), current_entry->hash()->hash().size());
|
|
ffsParser->protectedRanges.push_back(range);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
bpInfo += "\n";
|
|
}
|
|
|
|
// Add Key Signature
|
|
const intel_acbp_v2_t::key_signature_t* key_signature = parsed.key_signature();
|
|
bpInfo += usprintf("Boot Policy Key Signature:\n"
|
|
"Version: %02Xh\n"
|
|
"KeyId: %04Xh\n"
|
|
"SigScheme: %04Xh\n",
|
|
key_signature->version(),
|
|
key_signature->key_id(),
|
|
key_signature->sig_scheme());
|
|
|
|
// Add PubKey
|
|
bpInfo += usprintf("Boot Policy Public Key Exponent: %Xh\n", key_signature->public_key()->exponent());
|
|
bpInfo += usprintf("Boot Policy Public Key:");
|
|
for (UINT16 i = 0; i < (UINT16)key_signature->public_key()->modulus().length(); i++) {
|
|
if (i % 32 == 0) bpInfo += UString("\n");
|
|
bpInfo += usprintf("%02X", (UINT8)key_signature->public_key()->modulus().at(i));
|
|
}
|
|
bpInfo += "\n";
|
|
|
|
// Calculate and add PubKey hashes
|
|
UINT8 hash[SHA384_HASH_SIZE];
|
|
// SHA256
|
|
sha256(key_signature->public_key()->modulus().data(), key_signature->public_key()->modulus().length() , hash);
|
|
bpInfo += UString("Boot Policy Public Key Hash (SHA256): ");
|
|
for (UINT8 i = 0; i < SHA256_HASH_SIZE; i++) {
|
|
bpInfo += usprintf("%02X", hash[i]);
|
|
}
|
|
bpInfo += "\n";
|
|
bgBpHashSha256 = UByteArray((const char*)hash, SHA256_HASH_SIZE);
|
|
// SHA384
|
|
sha384(key_signature->public_key()->modulus().data(), key_signature->public_key()->modulus().length() , hash);
|
|
bpInfo += UString("Boot Policy Public Key Hash (SHA384): ");
|
|
for (UINT8 i = 0; i < SHA384_HASH_SIZE; i++) {
|
|
bpInfo += usprintf("%02X", hash[i]);
|
|
}
|
|
bpInfo += "\n";
|
|
bgBpHashSha384 = UByteArray((const char*)hash, SHA384_HASH_SIZE);
|
|
|
|
// Add Signature
|
|
bpInfo += UString("Boot Policy Signature: ");
|
|
for (UINT16 i = 0; i < (UINT16)key_signature->signature()->signature().length(); i++) {
|
|
if (i % 32 == 0) bpInfo += UString("\n");
|
|
bpInfo += usprintf("%02X", (UINT8)key_signature->signature()->signature().at(i));
|
|
}
|
|
bpInfo += "\n";
|
|
|
|
securityInfo += bpInfo + "\n";
|
|
bgBootPolicyFound = true;
|
|
return U_SUCCESS;
|
|
}
|
|
catch (...) {
|
|
msg(usprintf("%s: unable to parse Boot Policy", __FUNCTION__), parent);
|
|
return U_INVALID_BOOT_GUARD_BOOT_POLICY;
|
|
}
|
|
}
|
|
#endif // U_ENABLE_ME_PARSING_SUPPORT
|