diff mbox series

[2/2,V2] tpmevlog: add tests for snatic check of the Tpm event log

Message ID 20201021100956.25470-1-ivan.hu@canonical.com
State Superseded
Headers show
Series None | expand

Commit Message

Ivan Hu Oct. 21, 2020, 10:09 a.m. UTC
Signed-off-by: Ivan Hu <ivan.hu@canonical.com>
---
 src/Makefile.am             |   1 +
 src/tpm/tpmevlog/tpmevlog.c | 446 ++++++++++++++++++++++++++++++++++++
 2 files changed, 447 insertions(+)
 create mode 100644 src/tpm/tpmevlog/tpmevlog.c

Comments

Colin Ian King Oct. 21, 2020, 10:16 a.m. UTC | #1
On 21/10/2020 11:09, Ivan Hu wrote:
> Signed-off-by: Ivan Hu <ivan.hu@canonical.com>
> ---
>  src/Makefile.am             |   1 +
>  src/tpm/tpmevlog/tpmevlog.c | 446 ++++++++++++++++++++++++++++++++++++
>  2 files changed, 447 insertions(+)
>  create mode 100644 src/tpm/tpmevlog/tpmevlog.c
> 
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 5d78411b..b7f3f7db 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -184,6 +184,7 @@ fwts_SOURCES = main.c 				\
>  	pci/aspm/aspm.c 			\
>  	pci/crs/crs.c 				\
>  	pci/maxreadreq/maxreadreq.c 		\
> +	tpm/tpmevlog/tpmevlog.c			\
>  	tpm/tpmevlogdump/tpmevlogdump.c		\
>  	uefi/csm/csm.c 				\
>  	uefi/uefidump/uefidump.c 		\
> diff --git a/src/tpm/tpmevlog/tpmevlog.c b/src/tpm/tpmevlog/tpmevlog.c
> new file mode 100644
> index 00000000..70810bb6
> --- /dev/null
> +++ b/src/tpm/tpmevlog/tpmevlog.c
> @@ -0,0 +1,446 @@
> +/*
> + * Copyright (C) 2020 Canonical
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + */
> +#include "fwts.h"
> +
> +#include <sys/types.h>
> +#include <dirent.h>
> +#include <errno.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +
> +#include "fwts_tpm.h"
> +
> +#define FWTS_TPM_LOG_DIR_PATH	"/sys/kernel/security"
> +
> +static int tpmevlog_pcrindex_value_check(fwts_framework *fw, const uint32_t pcr)
> +{
> +	/*
> +	 * Current PCRs defined from 0 to 16, and 23 if for application support,
> +	 * define from TCG PC Client Platform Firmware Profile Specification
> +	 * https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/
> +	 * 2.3.4 PCR Usage
> +	 */
> +	if ((pcr > 16) && (pcr != 23)) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "PCRIndexValue",
> +			"The PCR Index value is undefined, 0x%8.8" PRIx32 ".",
> +			pcr);
> +		return FWTS_ERROR;
> +	}
> +
> +	return FWTS_OK;
> +}
> +
> +static int tpmevlog_eventtype_check(fwts_framework *fw, const fwts_tpmlog_event_type event_type)
> +{
> +
> +	switch (event_type) {
> +	case EV_PREBOOT_CERT:
> +	case EV_POST_CODE:
> +	case EV_UNUSED:
> +	case EV_NO_ACTION:
> +	case EV_SEPARATOR:
> +	case EV_ACTION:
> +	case EV_EVENT_TAG:
> +	case EV_S_CRTM_CONTENTS:
> +	case EV_S_CRTM_VERSION:
> +	case EV_CPU_MICROCODE:
> +	case EV_PLATFORM_CONFIG_FLAGS:
> +	case EV_TABLE_OF_DEVICES:
> +	case EV_IPL:
> +	case EV_IPL_PARTITION_DATA:
> +	case EV_NONHOST_CODE:
> +	case EV_NONHOST_CONFIG:
> +	case EV_NONHOST_INFO:
> +	case EV_OMIT_BOOT_DEVICE_EVENTS:
> +	case EV_EFI_EVENT_BASE:
> +	case EV_EFI_VARIABLE_DRIVER_CONFIG:
> +	case EV_EFI_VARIABLE_BOOT:
> +	case EV_EFI_BOOT_SERVICES_APPLICATION:
> +	case EV_EFI_BOOT_SERVICES_DRIVER:
> +	case EV_EFI_RUNTIME_SERVICES_DRIVER:
> +	case EV_EFI_GPT_EVENT:
> +	case EV_EFI_ACTION:
> +	case EV_EFI_PLATFORM_FIRMWARE_BLOB:
> +	case EV_EFI_HANDOFF_TABLES:
> +	case EV_EFI_HCRTM_EVENT:
> +	case EV_EFI_VARIABLE_AUTHORITY:
> +		return FWTS_OK;
> +	default:
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "PCREventType",
> +			"The Event Type is undefined, 0x%8.8" PRIx32 ".",
> +			event_type);
> +		return FWTS_ERROR;
> +	}
> +
> +	return FWTS_OK;
> +}
> +
> +static int tpmevlog_algid_check(fwts_framework *fw, const TPM2_ALG_ID hash)
> +{
> +
> +	switch (hash) {
> +	case TPM2_ALG_RSA:
> +	case TPM2_ALG_TDES:
> +	case TPM2_ALG_SHA1:
> +	case TPM2_ALG_HMAC:
> +	case TPM2_ALG_AES:
> +	case TPM2_ALG_MGF1:
> +	case TPM2_ALG_KEYEDHASH:
> +	case TPM2_ALG_XOR:
> +	case TPM2_ALG_SHA256:
> +	case TPM2_ALG_SHA384:
> +	case TPM2_ALG_SHA512:
> +	case TPM2_ALG_NULL:
> +	case TPM2_ALG_SM3_256:
> +	case TPM2_ALG_SM4:
> +	case TPM2_ALG_RSASSA:
> +	case TPM2_ALG_RSAES:
> +	case TPM2_ALG_RSAPSS:
> +	case TPM2_ALG_OAEP:
> +	case TPM2_ALG_ECDSA:
> +	case TPM2_ALG_ECDH:
> +	case TPM2_ALG_ECDAA:
> +	case TPM2_ALG_SM2:
> +	case TPM2_ALG_ECSCHNORR:
> +	case TPM2_ALG_ECMQV:
> +	case TPM2_ALG_KDF1_SP800_56A:
> +	case TPM2_ALG_KDF2:
> +	case TPM2_ALG_KDF1_SP800_108:
> +	case TPM2_ALG_ECC:
> +	case TPM2_ALG_SYMCIPHER:
> +	case TPM2_ALG_CAMELLIA:
> +	case TPM2_ALG_CMAC:
> +	case TPM2_ALG_CTR:
> +	case TPM2_ALG_SHA3_256:
> +	case TPM2_ALG_SHA3_384:
> +	case TPM2_ALG_SHA3_512:
> +	case TPM2_ALG_OFB:
> +	case TPM2_ALG_CBC:
> +	case TPM2_ALG_CFB:
> +	case TPM2_ALG_ECB:
> +		return FWTS_OK;
> +	default:
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "AlgorithmID",
> +			"The AlgorithmID is undefined, 0x%4.4" PRIx16 ".",
> +			hash);
> +		return FWTS_ERROR;
> +	}
> +
> +	return FWTS_OK;
> +}
> +
> +static int tpmevlog_v2_check(fwts_framework *fw, uint8_t *data, size_t len)
> +{
> +	int ret = FWTS_OK;
> +	size_t len_remain = len;
> +	uint8_t *pdata = data;
> +	int i = 0;
> +	uint8_t vendor_info_size = 0;
> +	uint8_t hash_size = 0;
> +	uint32_t event_size = 0;
> +
> +	/* specid_event_check */
> +	if (len < sizeof(fwts_pc_client_pcr_event)) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength",
> +				"The length of the Specid event is %zd bytes "
> +				"is smaller than the PCClientPCREvent %zd bytes.",
> +				len_remain,
> +				sizeof(fwts_pc_client_pcr_event));
> +		return FWTS_ERROR;
> +	}
> +
> +	fwts_pc_client_pcr_event *pc_event = (fwts_pc_client_pcr_event *)pdata;
> +	ret = tpmevlog_pcrindex_value_check(fw, pc_event->pcr_index);
> +	if (ret != FWTS_OK)
> +		return ret;
> +	ret = tpmevlog_eventtype_check(fw, pc_event->event_type);
> +	if (ret != FWTS_OK)
> +		return ret;
> +	for (i = 0; i < TPM2_SHA1_DIGEST_SIZE; i++) {
> +		if (pc_event->digest[i] != 0) {
> +			fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvDigest",
> +				"The digest filed of SpecId event should be all zero.");
> +			fwts_tpm_data_hexdump(fw, pc_event->digest, sizeof(pc_event->digest), "Digest");
> +		}
> +	}
> +
> +	pdata += sizeof(fwts_pc_client_pcr_event);
> +	len_remain -= sizeof(fwts_pc_client_pcr_event);
> +
> +	/* check the data length specid event */
> +	if (len_remain < sizeof(fwts_efi_spec_id_event)) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength",
> +				"The length of the Specid event is %zd bytes "
> +				"is smaller than the SpecId event %zd bytes.",
> +				len_remain,
> +				sizeof(fwts_efi_spec_id_event));
> +		return FWTS_ERROR;
> +	}
> +
> +	fwts_efi_spec_id_event *specid_evcent = (fwts_efi_spec_id_event *)pdata;
> +	if (strcmp((char *)specid_evcent->signature, FWTS_TPM_EVENTLOG_V2_SIGNATURE) != 0) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvSignature",
> +			"The signature of SpecId event is not the same as expected "
> +			"Spec ID Event03, got %s.",
> +			(char *)specid_evcent->signature);
> +		return FWTS_ERROR;
> +	}
> +
> +	/*
> +	 * Check the platform class value which defined in TCG ACPI Specification,
> +	 * 0 for client platforms, 1 for server platforms.
> +	 */
> +	if (specid_evcent->platform_class > 1) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvPlatformClass",
> +			"The PlatformClass value of SpecId event is unexpected "
> +			"0 for client platforms, 1 for server platforms, "
> +			"got 0x%8.8" PRIx32 ".", specid_evcent->platform_class);
> +		return FWTS_ERROR;
> +	}
> +
> +	if (specid_evcent->uintn_size < 1 || specid_evcent->uintn_size > 2) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvUINTNFields",
> +			"The size of the UINTN fieldsof SpecId event is unexpected "
> +			"0x01 indicates UINT32 and 0x02 indicates UINT64, "
> +			"got 0x%" PRIx8 ".", specid_evcent->uintn_size);
> +		return FWTS_ERROR;
> +	}
> +
> +	if (specid_evcent->number_of_alg < 1) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvAlgNumber",
> +			"The number of Hash algorithms of SpecId event must "
> +			"be set to a value of 0x01 or greater "
> +			"got 0x%" PRIx8 ".", specid_evcent->number_of_alg);
> +		return FWTS_ERROR;
> +	}
> +
> +	pdata += sizeof(fwts_efi_spec_id_event);
> +	len_remain -= sizeof(fwts_efi_spec_id_event);
> +	fwts_spec_id_event_alg_sz *alg_sz = (fwts_spec_id_event_alg_sz *)pdata;
> +	for (i = 0; i < specid_evcent->number_of_alg; i++) {
> +		if (len_remain < sizeof(fwts_spec_id_event_alg_sz)) {
> +			fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength",
> +					"The length of the Specid event is %zd bytes "
> +					"is smaller than AlgorithmSize %zd bytes.",
> +					len_remain,
> +					sizeof(fwts_spec_id_event_alg_sz));
> +			return FWTS_ERROR;
> +		}
> +
> +		ret = tpmevlog_algid_check(fw, alg_sz->algorithm_id);
> +		if (ret != FWTS_OK)
> +			return ret;
> +
> +		pdata += sizeof(fwts_spec_id_event_alg_sz);
> +		len_remain -= sizeof(fwts_spec_id_event_alg_sz);
> +		alg_sz = (fwts_spec_id_event_alg_sz *)pdata;
> +	}
> +
> +	vendor_info_size = *(uint8_t *)pdata;
> +	pdata += sizeof(vendor_info_size);
> +	len_remain -= sizeof(vendor_info_size);
> +	if (vendor_info_size > 0) {
> +		if (len_remain < vendor_info_size) {
> +			fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength",
> +					"The remain length of the Specid event is "
> +					"is too small (%zd bytes) for "
> +					"vendor info size.",
> +					len_remain);
> +			return FWTS_ERROR;
> +		}
> +		len_remain -= vendor_info_size;
> +		pdata += vendor_info_size;
> +	}
> +
> +	/* Check the Crypto agile log format event */
> +	while (len_remain > 0) {
> +		if (len_remain < sizeof(fwts_tcg_pcr_event2)) {
> +			fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2Length",
> +					"The length of the event2 is %zd bytes "
> +					"is smaller than the tcg pcr event2 %zd bytes.",
> +					len_remain,
> +					sizeof(fwts_tcg_pcr_event2));
> +			return FWTS_ERROR;
> +		}
> +
> +		fwts_tcg_pcr_event2 *pcr_event2 = (fwts_tcg_pcr_event2 *)pdata;
> +		ret = tpmevlog_pcrindex_value_check(fw, pcr_event2->pcr_index);
> +		if (ret != FWTS_OK)
> +			return ret;
> +		ret = tpmevlog_eventtype_check(fw, pcr_event2->event_type);
> +		if (ret != FWTS_OK)
> +			return ret;
> +
> +		pdata += sizeof(fwts_tcg_pcr_event2);
> +		len_remain -= sizeof(fwts_tcg_pcr_event2);
> +		for (i = 0; i < pcr_event2->digests_count; i++) {
> +
> +			hash_size = 0;
> +			TPM2_ALG_ID alg_id = *(TPM2_ALG_ID *)pdata;
> +
> +			ret = tpmevlog_algid_check(fw, alg_id);
> +			if (ret != FWTS_OK)
> +				return ret;
> +
> +			pdata += sizeof(TPM2_ALG_ID);
> +			len_remain -= sizeof(TPM2_ALG_ID);
> +
> +			hash_size = fwts_tpm_get_hash_size(alg_id);
> +			if (!hash_size) {
> +				fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2HashSize",
> +						"The hash sie of the event2 is %zd bytes "
> +						"is smaller than the tcg pcr event2 %zd bytes.",
> +						len_remain,
> +						sizeof(fwts_tcg_pcr_event2));
> +				return FWTS_ERROR;
> +			}
> +
> +			pdata += hash_size;
> +			len_remain -= hash_size;
> +		}
> +
> +		event_size = *(uint32_t *)pdata;
> +
> +		if (len_remain < event_size) {
> +			fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2Length",
> +					"The remain length of the event2 is %zd bytes "
> +					"is smaller than required event2 length %zd bytes.",
> +					len_remain,
> +					sizeof(event_size));
> +			return FWTS_ERROR;
> +		}
> +		pdata += (event_size + sizeof(event_size));
> +		len_remain -= (event_size + sizeof(event_size));
> +
> +	}
> +	fwts_passed(fw, "Check TPM crypto agile event log test passed.");
> +	return FWTS_OK;
> +}
> +
> +static uint8_t *tpmevlog_load_file(const int fd, size_t *length)
> +{
> +	uint8_t *ptr = NULL, *tmp;
> +	size_t size = 0;
> +	char buffer[4096];
> +
> +	*length = 0;
> +
> +	for (;;) {
> +		ssize_t n = read(fd, buffer, sizeof(buffer));
> +
> +		if (n == 0)
> +			break;
> +		if (n < 0) {
> +			if (errno != EINTR && errno != EAGAIN) {
> +				free(ptr);
> +				return NULL;
> +			}
> +			continue;
> +		}
> +		if (n > (ssize_t)sizeof(buffer))
> +			goto err;
> +		if (size + n > 0xffffffff)
> +			goto err;
> +
> +		if ((tmp = (uint8_t*)realloc(ptr, size + n + 1)) == NULL) {
> +			free(ptr);
> +			return NULL;
> +		}
> +		ptr = tmp;
> +		memcpy(ptr + size, buffer, n);
> +		size += n;
> +	}
> +
> +	if (!ptr || !size)
> +		goto err_no_data;
> +
> +	*length = size;
> +	return ptr;
> +
> +err:
> +	free(ptr);
> +err_no_data:
> +	*length = 0;
> +	return NULL;
> +}
> +
> +static int tpmevlog_test1(fwts_framework *fw)
> +{
> +	DIR *dir;
> +	struct dirent *tpmdir;
> +	bool tpm_logfile_found = false;
> +
> +	if (!(dir = opendir(FWTS_TPM_LOG_DIR_PATH))) {
> +		fwts_log_info(fw, "Cannot find the TPM event log. Aborted.");
> +		return FWTS_ABORTED;
> +	}
> +
> +	do {
> +		tpmdir = readdir(dir);
> +		if (tpmdir && strstr(tpmdir->d_name, "tpm")) {
> +			char path[PATH_MAX];
> +			uint8_t *data;
> +			int fd;
> +			size_t length;
> +
> +			fwts_log_nl(fw);
> +			fwts_log_info_verbatim(fw, "%s", tpmdir->d_name);
> +
> +			snprintf(path, sizeof(path), FWTS_TPM_LOG_DIR_PATH "/%s/binary_bios_measurements", tpmdir->d_name);
> +
> +			if ((fd = open(path, O_RDONLY)) >= 0) {
> +				data = tpmevlog_load_file(fd, &length);
> +				tpm_logfile_found = true;
> +				if (data == NULL) {
> +					fwts_log_info(fw, "Cannot load the TPM event logs. Aborted.");
> +					(void)closedir(dir);
> +					(void)close(fd);
> +					return FWTS_ABORTED;
> +				} else {
> +					/* check if the TPM2 eventlog */
> +					if (strstr((char *)(data + sizeof(fwts_pc_client_pcr_event)), FWTS_TPM_EVENTLOG_V2_SIGNATURE))
> +						tpmevlog_v2_check(fw, data, length);
> +
> +					free(data);
> +				}
> +				(void)close(fd);
> +			}
> +		}
> +	} while (tpmdir);
> +
> +	(void)closedir(dir);
> +
> +	if (!tpm_logfile_found) {
> +		fwts_log_info(fw, "Cannot find the TPM event log. Aborted.");
> +		return FWTS_ABORTED;
> +	}
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test tpmevlog_tests[] = {
> +	{ tpmevlog_test1, "Sanity check TPM event log." },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops tpmevlog_ops = {
> +	.description = "Sanity check TPM event log.",
> +	.minor_tests = tpmevlog_tests
> +};
> +
> +FWTS_REGISTER("tpmevlog", &tpmevlog_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_ROOT_PRIV)
> 

Thanks for the V2.

Acked-by: Colin Ian King <colin.king@canonical.com>
Ivan Hu Oct. 21, 2020, 10:17 a.m. UTC | #2
some typos

Ivan

On 10/21/20 6:09 PM, Ivan Hu wrote:
> Signed-off-by: Ivan Hu <ivan.hu@canonical.com>
> ---
>  src/Makefile.am             |   1 +
>  src/tpm/tpmevlog/tpmevlog.c | 446 ++++++++++++++++++++++++++++++++++++
>  2 files changed, 447 insertions(+)
>  create mode 100644 src/tpm/tpmevlog/tpmevlog.c
> 
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 5d78411b..b7f3f7db 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -184,6 +184,7 @@ fwts_SOURCES = main.c 				\
>  	pci/aspm/aspm.c 			\
>  	pci/crs/crs.c 				\
>  	pci/maxreadreq/maxreadreq.c 		\
> +	tpm/tpmevlog/tpmevlog.c			\
>  	tpm/tpmevlogdump/tpmevlogdump.c		\
>  	uefi/csm/csm.c 				\
>  	uefi/uefidump/uefidump.c 		\
> diff --git a/src/tpm/tpmevlog/tpmevlog.c b/src/tpm/tpmevlog/tpmevlog.c
> new file mode 100644
> index 00000000..70810bb6
> --- /dev/null
> +++ b/src/tpm/tpmevlog/tpmevlog.c
> @@ -0,0 +1,446 @@
> +/*
> + * Copyright (C) 2020 Canonical
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + */
> +#include "fwts.h"
> +
> +#include <sys/types.h>
> +#include <dirent.h>
> +#include <errno.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +
> +#include "fwts_tpm.h"
> +
> +#define FWTS_TPM_LOG_DIR_PATH	"/sys/kernel/security"
> +
> +static int tpmevlog_pcrindex_value_check(fwts_framework *fw, const uint32_t pcr)
> +{
> +	/*
> +	 * Current PCRs defined from 0 to 16, and 23 if for application support,
> +	 * define from TCG PC Client Platform Firmware Profile Specification
> +	 * https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/
> +	 * 2.3.4 PCR Usage
> +	 */
> +	if ((pcr > 16) && (pcr != 23)) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "PCRIndexValue",
> +			"The PCR Index value is undefined, 0x%8.8" PRIx32 ".",
> +			pcr);
> +		return FWTS_ERROR;
> +	}
> +
> +	return FWTS_OK;
> +}
> +
> +static int tpmevlog_eventtype_check(fwts_framework *fw, const fwts_tpmlog_event_type event_type)
> +{
> +
> +	switch (event_type) {
> +	case EV_PREBOOT_CERT:
> +	case EV_POST_CODE:
> +	case EV_UNUSED:
> +	case EV_NO_ACTION:
> +	case EV_SEPARATOR:
> +	case EV_ACTION:
> +	case EV_EVENT_TAG:
> +	case EV_S_CRTM_CONTENTS:
> +	case EV_S_CRTM_VERSION:
> +	case EV_CPU_MICROCODE:
> +	case EV_PLATFORM_CONFIG_FLAGS:
> +	case EV_TABLE_OF_DEVICES:
> +	case EV_IPL:
> +	case EV_IPL_PARTITION_DATA:
> +	case EV_NONHOST_CODE:
> +	case EV_NONHOST_CONFIG:
> +	case EV_NONHOST_INFO:
> +	case EV_OMIT_BOOT_DEVICE_EVENTS:
> +	case EV_EFI_EVENT_BASE:
> +	case EV_EFI_VARIABLE_DRIVER_CONFIG:
> +	case EV_EFI_VARIABLE_BOOT:
> +	case EV_EFI_BOOT_SERVICES_APPLICATION:
> +	case EV_EFI_BOOT_SERVICES_DRIVER:
> +	case EV_EFI_RUNTIME_SERVICES_DRIVER:
> +	case EV_EFI_GPT_EVENT:
> +	case EV_EFI_ACTION:
> +	case EV_EFI_PLATFORM_FIRMWARE_BLOB:
> +	case EV_EFI_HANDOFF_TABLES:
> +	case EV_EFI_HCRTM_EVENT:
> +	case EV_EFI_VARIABLE_AUTHORITY:
> +		return FWTS_OK;
> +	default:
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "PCREventType",
> +			"The Event Type is undefined, 0x%8.8" PRIx32 ".",
> +			event_type);
> +		return FWTS_ERROR;
> +	}
> +
> +	return FWTS_OK;
> +}
> +
> +static int tpmevlog_algid_check(fwts_framework *fw, const TPM2_ALG_ID hash)
> +{
> +
> +	switch (hash) {
> +	case TPM2_ALG_RSA:
> +	case TPM2_ALG_TDES:
> +	case TPM2_ALG_SHA1:
> +	case TPM2_ALG_HMAC:
> +	case TPM2_ALG_AES:
> +	case TPM2_ALG_MGF1:
> +	case TPM2_ALG_KEYEDHASH:
> +	case TPM2_ALG_XOR:
> +	case TPM2_ALG_SHA256:
> +	case TPM2_ALG_SHA384:
> +	case TPM2_ALG_SHA512:
> +	case TPM2_ALG_NULL:
> +	case TPM2_ALG_SM3_256:
> +	case TPM2_ALG_SM4:
> +	case TPM2_ALG_RSASSA:
> +	case TPM2_ALG_RSAES:
> +	case TPM2_ALG_RSAPSS:
> +	case TPM2_ALG_OAEP:
> +	case TPM2_ALG_ECDSA:
> +	case TPM2_ALG_ECDH:
> +	case TPM2_ALG_ECDAA:
> +	case TPM2_ALG_SM2:
> +	case TPM2_ALG_ECSCHNORR:
> +	case TPM2_ALG_ECMQV:
> +	case TPM2_ALG_KDF1_SP800_56A:
> +	case TPM2_ALG_KDF2:
> +	case TPM2_ALG_KDF1_SP800_108:
> +	case TPM2_ALG_ECC:
> +	case TPM2_ALG_SYMCIPHER:
> +	case TPM2_ALG_CAMELLIA:
> +	case TPM2_ALG_CMAC:
> +	case TPM2_ALG_CTR:
> +	case TPM2_ALG_SHA3_256:
> +	case TPM2_ALG_SHA3_384:
> +	case TPM2_ALG_SHA3_512:
> +	case TPM2_ALG_OFB:
> +	case TPM2_ALG_CBC:
> +	case TPM2_ALG_CFB:
> +	case TPM2_ALG_ECB:
> +		return FWTS_OK;
> +	default:
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "AlgorithmID",
> +			"The AlgorithmID is undefined, 0x%4.4" PRIx16 ".",
> +			hash);
> +		return FWTS_ERROR;
> +	}
> +
> +	return FWTS_OK;
> +}
> +
> +static int tpmevlog_v2_check(fwts_framework *fw, uint8_t *data, size_t len)
> +{
> +	int ret = FWTS_OK;
> +	size_t len_remain = len;
> +	uint8_t *pdata = data;
> +	int i = 0;
> +	uint8_t vendor_info_size = 0;
> +	uint8_t hash_size = 0;
> +	uint32_t event_size = 0;
> +
> +	/* specid_event_check */
> +	if (len < sizeof(fwts_pc_client_pcr_event)) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength",
> +				"The length of the Specid event is %zd bytes "
> +				"is smaller than the PCClientPCREvent %zd bytes.",
> +				len_remain,
> +				sizeof(fwts_pc_client_pcr_event));
> +		return FWTS_ERROR;
> +	}
> +
> +	fwts_pc_client_pcr_event *pc_event = (fwts_pc_client_pcr_event *)pdata;
> +	ret = tpmevlog_pcrindex_value_check(fw, pc_event->pcr_index);
> +	if (ret != FWTS_OK)
> +		return ret;
> +	ret = tpmevlog_eventtype_check(fw, pc_event->event_type);
> +	if (ret != FWTS_OK)
> +		return ret;
> +	for (i = 0; i < TPM2_SHA1_DIGEST_SIZE; i++) {
> +		if (pc_event->digest[i] != 0) {
> +			fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvDigest",
> +				"The digest filed of SpecId event should be all zero.");
> +			fwts_tpm_data_hexdump(fw, pc_event->digest, sizeof(pc_event->digest), "Digest");
> +		}
> +	}
> +
> +	pdata += sizeof(fwts_pc_client_pcr_event);
> +	len_remain -= sizeof(fwts_pc_client_pcr_event);
> +
> +	/* check the data length specid event */
> +	if (len_remain < sizeof(fwts_efi_spec_id_event)) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength",
> +				"The length of the Specid event is %zd bytes "
> +				"is smaller than the SpecId event %zd bytes.",
> +				len_remain,
> +				sizeof(fwts_efi_spec_id_event));
> +		return FWTS_ERROR;
> +	}
> +
> +	fwts_efi_spec_id_event *specid_evcent = (fwts_efi_spec_id_event *)pdata;
> +	if (strcmp((char *)specid_evcent->signature, FWTS_TPM_EVENTLOG_V2_SIGNATURE) != 0) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvSignature",
> +			"The signature of SpecId event is not the same as expected "
> +			"Spec ID Event03, got %s.",
> +			(char *)specid_evcent->signature);
> +		return FWTS_ERROR;
> +	}
> +
> +	/*
> +	 * Check the platform class value which defined in TCG ACPI Specification,
> +	 * 0 for client platforms, 1 for server platforms.
> +	 */
> +	if (specid_evcent->platform_class > 1) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvPlatformClass",
> +			"The PlatformClass value of SpecId event is unexpected "
> +			"0 for client platforms, 1 for server platforms, "
> +			"got 0x%8.8" PRIx32 ".", specid_evcent->platform_class);
> +		return FWTS_ERROR;
> +	}
> +
> +	if (specid_evcent->uintn_size < 1 || specid_evcent->uintn_size > 2) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvUINTNFields",
> +			"The size of the UINTN fieldsof SpecId event is unexpected "
> +			"0x01 indicates UINT32 and 0x02 indicates UINT64, "
> +			"got 0x%" PRIx8 ".", specid_evcent->uintn_size);
> +		return FWTS_ERROR;
> +	}
> +
> +	if (specid_evcent->number_of_alg < 1) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvAlgNumber",
> +			"The number of Hash algorithms of SpecId event must "
> +			"be set to a value of 0x01 or greater "
> +			"got 0x%" PRIx8 ".", specid_evcent->number_of_alg);
> +		return FWTS_ERROR;
> +	}
> +
> +	pdata += sizeof(fwts_efi_spec_id_event);
> +	len_remain -= sizeof(fwts_efi_spec_id_event);
> +	fwts_spec_id_event_alg_sz *alg_sz = (fwts_spec_id_event_alg_sz *)pdata;
> +	for (i = 0; i < specid_evcent->number_of_alg; i++) {
> +		if (len_remain < sizeof(fwts_spec_id_event_alg_sz)) {
> +			fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength",
> +					"The length of the Specid event is %zd bytes "
> +					"is smaller than AlgorithmSize %zd bytes.",
> +					len_remain,
> +					sizeof(fwts_spec_id_event_alg_sz));
> +			return FWTS_ERROR;
> +		}
> +
> +		ret = tpmevlog_algid_check(fw, alg_sz->algorithm_id);
> +		if (ret != FWTS_OK)
> +			return ret;
> +
> +		pdata += sizeof(fwts_spec_id_event_alg_sz);
> +		len_remain -= sizeof(fwts_spec_id_event_alg_sz);
> +		alg_sz = (fwts_spec_id_event_alg_sz *)pdata;
> +	}
> +
> +	vendor_info_size = *(uint8_t *)pdata;
> +	pdata += sizeof(vendor_info_size);
> +	len_remain -= sizeof(vendor_info_size);
> +	if (vendor_info_size > 0) {
> +		if (len_remain < vendor_info_size) {
> +			fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength",
> +					"The remain length of the Specid event is "
> +					"is too small (%zd bytes) for "
> +					"vendor info size.",
> +					len_remain);
> +			return FWTS_ERROR;
> +		}
> +		len_remain -= vendor_info_size;
> +		pdata += vendor_info_size;
> +	}
> +
> +	/* Check the Crypto agile log format event */
> +	while (len_remain > 0) {
> +		if (len_remain < sizeof(fwts_tcg_pcr_event2)) {
> +			fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2Length",
> +					"The length of the event2 is %zd bytes "
> +					"is smaller than the tcg pcr event2 %zd bytes.",
> +					len_remain,
> +					sizeof(fwts_tcg_pcr_event2));
> +			return FWTS_ERROR;
> +		}
> +
> +		fwts_tcg_pcr_event2 *pcr_event2 = (fwts_tcg_pcr_event2 *)pdata;
> +		ret = tpmevlog_pcrindex_value_check(fw, pcr_event2->pcr_index);
> +		if (ret != FWTS_OK)
> +			return ret;
> +		ret = tpmevlog_eventtype_check(fw, pcr_event2->event_type);
> +		if (ret != FWTS_OK)
> +			return ret;
> +
> +		pdata += sizeof(fwts_tcg_pcr_event2);
> +		len_remain -= sizeof(fwts_tcg_pcr_event2);
> +		for (i = 0; i < pcr_event2->digests_count; i++) {
> +
> +			hash_size = 0;
> +			TPM2_ALG_ID alg_id = *(TPM2_ALG_ID *)pdata;
> +
> +			ret = tpmevlog_algid_check(fw, alg_id);
> +			if (ret != FWTS_OK)
> +				return ret;
> +
> +			pdata += sizeof(TPM2_ALG_ID);
> +			len_remain -= sizeof(TPM2_ALG_ID);
> +
> +			hash_size = fwts_tpm_get_hash_size(alg_id);
> +			if (!hash_size) {
> +				fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2HashSize",
> +						"The hash sie of the event2 is %zd bytes "
> +						"is smaller than the tcg pcr event2 %zd bytes.",
> +						len_remain,
> +						sizeof(fwts_tcg_pcr_event2));
> +				return FWTS_ERROR;
> +			}
> +
> +			pdata += hash_size;
> +			len_remain -= hash_size;
> +		}
> +
> +		event_size = *(uint32_t *)pdata;
> +
> +		if (len_remain < event_size) {
> +			fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2Length",
> +					"The remain length of the event2 is %zd bytes "
> +					"is smaller than required event2 length %zd bytes.",
> +					len_remain,
> +					sizeof(event_size));
> +			return FWTS_ERROR;
> +		}
> +		pdata += (event_size + sizeof(event_size));
> +		len_remain -= (event_size + sizeof(event_size));
> +
> +	}
> +	fwts_passed(fw, "Check TPM crypto agile event log test passed.");
> +	return FWTS_OK;
> +}
> +
> +static uint8_t *tpmevlog_load_file(const int fd, size_t *length)
> +{
> +	uint8_t *ptr = NULL, *tmp;
> +	size_t size = 0;
> +	char buffer[4096];
> +
> +	*length = 0;
> +
> +	for (;;) {
> +		ssize_t n = read(fd, buffer, sizeof(buffer));
> +
> +		if (n == 0)
> +			break;
> +		if (n < 0) {
> +			if (errno != EINTR && errno != EAGAIN) {
> +				free(ptr);
> +				return NULL;
> +			}
> +			continue;
> +		}
> +		if (n > (ssize_t)sizeof(buffer))
> +			goto err;
> +		if (size + n > 0xffffffff)
> +			goto err;
> +
> +		if ((tmp = (uint8_t*)realloc(ptr, size + n + 1)) == NULL) {
> +			free(ptr);
> +			return NULL;
> +		}
> +		ptr = tmp;
> +		memcpy(ptr + size, buffer, n);
> +		size += n;
> +	}
> +
> +	if (!ptr || !size)
> +		goto err_no_data;
> +
> +	*length = size;
> +	return ptr;
> +
> +err:
> +	free(ptr);
> +err_no_data:
> +	*length = 0;
> +	return NULL;
> +}
> +
> +static int tpmevlog_test1(fwts_framework *fw)
> +{
> +	DIR *dir;
> +	struct dirent *tpmdir;
> +	bool tpm_logfile_found = false;
> +
> +	if (!(dir = opendir(FWTS_TPM_LOG_DIR_PATH))) {
> +		fwts_log_info(fw, "Cannot find the TPM event log. Aborted.");
> +		return FWTS_ABORTED;
> +	}
> +
> +	do {
> +		tpmdir = readdir(dir);
> +		if (tpmdir && strstr(tpmdir->d_name, "tpm")) {
> +			char path[PATH_MAX];
> +			uint8_t *data;
> +			int fd;
> +			size_t length;
> +
> +			fwts_log_nl(fw);
> +			fwts_log_info_verbatim(fw, "%s", tpmdir->d_name);
> +
> +			snprintf(path, sizeof(path), FWTS_TPM_LOG_DIR_PATH "/%s/binary_bios_measurements", tpmdir->d_name);
> +
> +			if ((fd = open(path, O_RDONLY)) >= 0) {
> +				data = tpmevlog_load_file(fd, &length);
> +				tpm_logfile_found = true;
> +				if (data == NULL) {
> +					fwts_log_info(fw, "Cannot load the TPM event logs. Aborted.");
> +					(void)closedir(dir);
> +					(void)close(fd);
> +					return FWTS_ABORTED;
> +				} else {
> +					/* check if the TPM2 eventlog */
> +					if (strstr((char *)(data + sizeof(fwts_pc_client_pcr_event)), FWTS_TPM_EVENTLOG_V2_SIGNATURE))
> +						tpmevlog_v2_check(fw, data, length);
> +
> +					free(data);
> +				}
> +				(void)close(fd);
> +			}
> +		}
> +	} while (tpmdir);
> +
> +	(void)closedir(dir);
> +
> +	if (!tpm_logfile_found) {
> +		fwts_log_info(fw, "Cannot find the TPM event log. Aborted.");
> +		return FWTS_ABORTED;
> +	}
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test tpmevlog_tests[] = {
> +	{ tpmevlog_test1, "Sanity check TPM event log." },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops tpmevlog_ops = {
> +	.description = "Sanity check TPM event log.",
> +	.minor_tests = tpmevlog_tests
> +};
> +
> +FWTS_REGISTER("tpmevlog", &tpmevlog_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_ROOT_PRIV)
>
diff mbox series

Patch

diff --git a/src/Makefile.am b/src/Makefile.am
index 5d78411b..b7f3f7db 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -184,6 +184,7 @@  fwts_SOURCES = main.c 				\
 	pci/aspm/aspm.c 			\
 	pci/crs/crs.c 				\
 	pci/maxreadreq/maxreadreq.c 		\
+	tpm/tpmevlog/tpmevlog.c			\
 	tpm/tpmevlogdump/tpmevlogdump.c		\
 	uefi/csm/csm.c 				\
 	uefi/uefidump/uefidump.c 		\
diff --git a/src/tpm/tpmevlog/tpmevlog.c b/src/tpm/tpmevlog/tpmevlog.c
new file mode 100644
index 00000000..70810bb6
--- /dev/null
+++ b/src/tpm/tpmevlog/tpmevlog.c
@@ -0,0 +1,446 @@ 
+/*
+ * Copyright (C) 2020 Canonical
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "fwts.h"
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "fwts_tpm.h"
+
+#define FWTS_TPM_LOG_DIR_PATH	"/sys/kernel/security"
+
+static int tpmevlog_pcrindex_value_check(fwts_framework *fw, const uint32_t pcr)
+{
+	/*
+	 * Current PCRs defined from 0 to 16, and 23 if for application support,
+	 * define from TCG PC Client Platform Firmware Profile Specification
+	 * https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/
+	 * 2.3.4 PCR Usage
+	 */
+	if ((pcr > 16) && (pcr != 23)) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "PCRIndexValue",
+			"The PCR Index value is undefined, 0x%8.8" PRIx32 ".",
+			pcr);
+		return FWTS_ERROR;
+	}
+
+	return FWTS_OK;
+}
+
+static int tpmevlog_eventtype_check(fwts_framework *fw, const fwts_tpmlog_event_type event_type)
+{
+
+	switch (event_type) {
+	case EV_PREBOOT_CERT:
+	case EV_POST_CODE:
+	case EV_UNUSED:
+	case EV_NO_ACTION:
+	case EV_SEPARATOR:
+	case EV_ACTION:
+	case EV_EVENT_TAG:
+	case EV_S_CRTM_CONTENTS:
+	case EV_S_CRTM_VERSION:
+	case EV_CPU_MICROCODE:
+	case EV_PLATFORM_CONFIG_FLAGS:
+	case EV_TABLE_OF_DEVICES:
+	case EV_IPL:
+	case EV_IPL_PARTITION_DATA:
+	case EV_NONHOST_CODE:
+	case EV_NONHOST_CONFIG:
+	case EV_NONHOST_INFO:
+	case EV_OMIT_BOOT_DEVICE_EVENTS:
+	case EV_EFI_EVENT_BASE:
+	case EV_EFI_VARIABLE_DRIVER_CONFIG:
+	case EV_EFI_VARIABLE_BOOT:
+	case EV_EFI_BOOT_SERVICES_APPLICATION:
+	case EV_EFI_BOOT_SERVICES_DRIVER:
+	case EV_EFI_RUNTIME_SERVICES_DRIVER:
+	case EV_EFI_GPT_EVENT:
+	case EV_EFI_ACTION:
+	case EV_EFI_PLATFORM_FIRMWARE_BLOB:
+	case EV_EFI_HANDOFF_TABLES:
+	case EV_EFI_HCRTM_EVENT:
+	case EV_EFI_VARIABLE_AUTHORITY:
+		return FWTS_OK;
+	default:
+		fwts_failed(fw, LOG_LEVEL_HIGH, "PCREventType",
+			"The Event Type is undefined, 0x%8.8" PRIx32 ".",
+			event_type);
+		return FWTS_ERROR;
+	}
+
+	return FWTS_OK;
+}
+
+static int tpmevlog_algid_check(fwts_framework *fw, const TPM2_ALG_ID hash)
+{
+
+	switch (hash) {
+	case TPM2_ALG_RSA:
+	case TPM2_ALG_TDES:
+	case TPM2_ALG_SHA1:
+	case TPM2_ALG_HMAC:
+	case TPM2_ALG_AES:
+	case TPM2_ALG_MGF1:
+	case TPM2_ALG_KEYEDHASH:
+	case TPM2_ALG_XOR:
+	case TPM2_ALG_SHA256:
+	case TPM2_ALG_SHA384:
+	case TPM2_ALG_SHA512:
+	case TPM2_ALG_NULL:
+	case TPM2_ALG_SM3_256:
+	case TPM2_ALG_SM4:
+	case TPM2_ALG_RSASSA:
+	case TPM2_ALG_RSAES:
+	case TPM2_ALG_RSAPSS:
+	case TPM2_ALG_OAEP:
+	case TPM2_ALG_ECDSA:
+	case TPM2_ALG_ECDH:
+	case TPM2_ALG_ECDAA:
+	case TPM2_ALG_SM2:
+	case TPM2_ALG_ECSCHNORR:
+	case TPM2_ALG_ECMQV:
+	case TPM2_ALG_KDF1_SP800_56A:
+	case TPM2_ALG_KDF2:
+	case TPM2_ALG_KDF1_SP800_108:
+	case TPM2_ALG_ECC:
+	case TPM2_ALG_SYMCIPHER:
+	case TPM2_ALG_CAMELLIA:
+	case TPM2_ALG_CMAC:
+	case TPM2_ALG_CTR:
+	case TPM2_ALG_SHA3_256:
+	case TPM2_ALG_SHA3_384:
+	case TPM2_ALG_SHA3_512:
+	case TPM2_ALG_OFB:
+	case TPM2_ALG_CBC:
+	case TPM2_ALG_CFB:
+	case TPM2_ALG_ECB:
+		return FWTS_OK;
+	default:
+		fwts_failed(fw, LOG_LEVEL_HIGH, "AlgorithmID",
+			"The AlgorithmID is undefined, 0x%4.4" PRIx16 ".",
+			hash);
+		return FWTS_ERROR;
+	}
+
+	return FWTS_OK;
+}
+
+static int tpmevlog_v2_check(fwts_framework *fw, uint8_t *data, size_t len)
+{
+	int ret = FWTS_OK;
+	size_t len_remain = len;
+	uint8_t *pdata = data;
+	int i = 0;
+	uint8_t vendor_info_size = 0;
+	uint8_t hash_size = 0;
+	uint32_t event_size = 0;
+
+	/* specid_event_check */
+	if (len < sizeof(fwts_pc_client_pcr_event)) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength",
+				"The length of the Specid event is %zd bytes "
+				"is smaller than the PCClientPCREvent %zd bytes.",
+				len_remain,
+				sizeof(fwts_pc_client_pcr_event));
+		return FWTS_ERROR;
+	}
+
+	fwts_pc_client_pcr_event *pc_event = (fwts_pc_client_pcr_event *)pdata;
+	ret = tpmevlog_pcrindex_value_check(fw, pc_event->pcr_index);
+	if (ret != FWTS_OK)
+		return ret;
+	ret = tpmevlog_eventtype_check(fw, pc_event->event_type);
+	if (ret != FWTS_OK)
+		return ret;
+	for (i = 0; i < TPM2_SHA1_DIGEST_SIZE; i++) {
+		if (pc_event->digest[i] != 0) {
+			fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvDigest",
+				"The digest filed of SpecId event should be all zero.");
+			fwts_tpm_data_hexdump(fw, pc_event->digest, sizeof(pc_event->digest), "Digest");
+		}
+	}
+
+	pdata += sizeof(fwts_pc_client_pcr_event);
+	len_remain -= sizeof(fwts_pc_client_pcr_event);
+
+	/* check the data length specid event */
+	if (len_remain < sizeof(fwts_efi_spec_id_event)) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength",
+				"The length of the Specid event is %zd bytes "
+				"is smaller than the SpecId event %zd bytes.",
+				len_remain,
+				sizeof(fwts_efi_spec_id_event));
+		return FWTS_ERROR;
+	}
+
+	fwts_efi_spec_id_event *specid_evcent = (fwts_efi_spec_id_event *)pdata;
+	if (strcmp((char *)specid_evcent->signature, FWTS_TPM_EVENTLOG_V2_SIGNATURE) != 0) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvSignature",
+			"The signature of SpecId event is not the same as expected "
+			"Spec ID Event03, got %s.",
+			(char *)specid_evcent->signature);
+		return FWTS_ERROR;
+	}
+
+	/*
+	 * Check the platform class value which defined in TCG ACPI Specification,
+	 * 0 for client platforms, 1 for server platforms.
+	 */
+	if (specid_evcent->platform_class > 1) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvPlatformClass",
+			"The PlatformClass value of SpecId event is unexpected "
+			"0 for client platforms, 1 for server platforms, "
+			"got 0x%8.8" PRIx32 ".", specid_evcent->platform_class);
+		return FWTS_ERROR;
+	}
+
+	if (specid_evcent->uintn_size < 1 || specid_evcent->uintn_size > 2) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvUINTNFields",
+			"The size of the UINTN fieldsof SpecId event is unexpected "
+			"0x01 indicates UINT32 and 0x02 indicates UINT64, "
+			"got 0x%" PRIx8 ".", specid_evcent->uintn_size);
+		return FWTS_ERROR;
+	}
+
+	if (specid_evcent->number_of_alg < 1) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvAlgNumber",
+			"The number of Hash algorithms of SpecId event must "
+			"be set to a value of 0x01 or greater "
+			"got 0x%" PRIx8 ".", specid_evcent->number_of_alg);
+		return FWTS_ERROR;
+	}
+
+	pdata += sizeof(fwts_efi_spec_id_event);
+	len_remain -= sizeof(fwts_efi_spec_id_event);
+	fwts_spec_id_event_alg_sz *alg_sz = (fwts_spec_id_event_alg_sz *)pdata;
+	for (i = 0; i < specid_evcent->number_of_alg; i++) {
+		if (len_remain < sizeof(fwts_spec_id_event_alg_sz)) {
+			fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength",
+					"The length of the Specid event is %zd bytes "
+					"is smaller than AlgorithmSize %zd bytes.",
+					len_remain,
+					sizeof(fwts_spec_id_event_alg_sz));
+			return FWTS_ERROR;
+		}
+
+		ret = tpmevlog_algid_check(fw, alg_sz->algorithm_id);
+		if (ret != FWTS_OK)
+			return ret;
+
+		pdata += sizeof(fwts_spec_id_event_alg_sz);
+		len_remain -= sizeof(fwts_spec_id_event_alg_sz);
+		alg_sz = (fwts_spec_id_event_alg_sz *)pdata;
+	}
+
+	vendor_info_size = *(uint8_t *)pdata;
+	pdata += sizeof(vendor_info_size);
+	len_remain -= sizeof(vendor_info_size);
+	if (vendor_info_size > 0) {
+		if (len_remain < vendor_info_size) {
+			fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength",
+					"The remain length of the Specid event is "
+					"is too small (%zd bytes) for "
+					"vendor info size.",
+					len_remain);
+			return FWTS_ERROR;
+		}
+		len_remain -= vendor_info_size;
+		pdata += vendor_info_size;
+	}
+
+	/* Check the Crypto agile log format event */
+	while (len_remain > 0) {
+		if (len_remain < sizeof(fwts_tcg_pcr_event2)) {
+			fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2Length",
+					"The length of the event2 is %zd bytes "
+					"is smaller than the tcg pcr event2 %zd bytes.",
+					len_remain,
+					sizeof(fwts_tcg_pcr_event2));
+			return FWTS_ERROR;
+		}
+
+		fwts_tcg_pcr_event2 *pcr_event2 = (fwts_tcg_pcr_event2 *)pdata;
+		ret = tpmevlog_pcrindex_value_check(fw, pcr_event2->pcr_index);
+		if (ret != FWTS_OK)
+			return ret;
+		ret = tpmevlog_eventtype_check(fw, pcr_event2->event_type);
+		if (ret != FWTS_OK)
+			return ret;
+
+		pdata += sizeof(fwts_tcg_pcr_event2);
+		len_remain -= sizeof(fwts_tcg_pcr_event2);
+		for (i = 0; i < pcr_event2->digests_count; i++) {
+
+			hash_size = 0;
+			TPM2_ALG_ID alg_id = *(TPM2_ALG_ID *)pdata;
+
+			ret = tpmevlog_algid_check(fw, alg_id);
+			if (ret != FWTS_OK)
+				return ret;
+
+			pdata += sizeof(TPM2_ALG_ID);
+			len_remain -= sizeof(TPM2_ALG_ID);
+
+			hash_size = fwts_tpm_get_hash_size(alg_id);
+			if (!hash_size) {
+				fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2HashSize",
+						"The hash sie of the event2 is %zd bytes "
+						"is smaller than the tcg pcr event2 %zd bytes.",
+						len_remain,
+						sizeof(fwts_tcg_pcr_event2));
+				return FWTS_ERROR;
+			}
+
+			pdata += hash_size;
+			len_remain -= hash_size;
+		}
+
+		event_size = *(uint32_t *)pdata;
+
+		if (len_remain < event_size) {
+			fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2Length",
+					"The remain length of the event2 is %zd bytes "
+					"is smaller than required event2 length %zd bytes.",
+					len_remain,
+					sizeof(event_size));
+			return FWTS_ERROR;
+		}
+		pdata += (event_size + sizeof(event_size));
+		len_remain -= (event_size + sizeof(event_size));
+
+	}
+	fwts_passed(fw, "Check TPM crypto agile event log test passed.");
+	return FWTS_OK;
+}
+
+static uint8_t *tpmevlog_load_file(const int fd, size_t *length)
+{
+	uint8_t *ptr = NULL, *tmp;
+	size_t size = 0;
+	char buffer[4096];
+
+	*length = 0;
+
+	for (;;) {
+		ssize_t n = read(fd, buffer, sizeof(buffer));
+
+		if (n == 0)
+			break;
+		if (n < 0) {
+			if (errno != EINTR && errno != EAGAIN) {
+				free(ptr);
+				return NULL;
+			}
+			continue;
+		}
+		if (n > (ssize_t)sizeof(buffer))
+			goto err;
+		if (size + n > 0xffffffff)
+			goto err;
+
+		if ((tmp = (uint8_t*)realloc(ptr, size + n + 1)) == NULL) {
+			free(ptr);
+			return NULL;
+		}
+		ptr = tmp;
+		memcpy(ptr + size, buffer, n);
+		size += n;
+	}
+
+	if (!ptr || !size)
+		goto err_no_data;
+
+	*length = size;
+	return ptr;
+
+err:
+	free(ptr);
+err_no_data:
+	*length = 0;
+	return NULL;
+}
+
+static int tpmevlog_test1(fwts_framework *fw)
+{
+	DIR *dir;
+	struct dirent *tpmdir;
+	bool tpm_logfile_found = false;
+
+	if (!(dir = opendir(FWTS_TPM_LOG_DIR_PATH))) {
+		fwts_log_info(fw, "Cannot find the TPM event log. Aborted.");
+		return FWTS_ABORTED;
+	}
+
+	do {
+		tpmdir = readdir(dir);
+		if (tpmdir && strstr(tpmdir->d_name, "tpm")) {
+			char path[PATH_MAX];
+			uint8_t *data;
+			int fd;
+			size_t length;
+
+			fwts_log_nl(fw);
+			fwts_log_info_verbatim(fw, "%s", tpmdir->d_name);
+
+			snprintf(path, sizeof(path), FWTS_TPM_LOG_DIR_PATH "/%s/binary_bios_measurements", tpmdir->d_name);
+
+			if ((fd = open(path, O_RDONLY)) >= 0) {
+				data = tpmevlog_load_file(fd, &length);
+				tpm_logfile_found = true;
+				if (data == NULL) {
+					fwts_log_info(fw, "Cannot load the TPM event logs. Aborted.");
+					(void)closedir(dir);
+					(void)close(fd);
+					return FWTS_ABORTED;
+				} else {
+					/* check if the TPM2 eventlog */
+					if (strstr((char *)(data + sizeof(fwts_pc_client_pcr_event)), FWTS_TPM_EVENTLOG_V2_SIGNATURE))
+						tpmevlog_v2_check(fw, data, length);
+
+					free(data);
+				}
+				(void)close(fd);
+			}
+		}
+	} while (tpmdir);
+
+	(void)closedir(dir);
+
+	if (!tpm_logfile_found) {
+		fwts_log_info(fw, "Cannot find the TPM event log. Aborted.");
+		return FWTS_ABORTED;
+	}
+	return FWTS_OK;
+}
+
+static fwts_framework_minor_test tpmevlog_tests[] = {
+	{ tpmevlog_test1, "Sanity check TPM event log." },
+	{ NULL, NULL }
+};
+
+static fwts_framework_ops tpmevlog_ops = {
+	.description = "Sanity check TPM event log.",
+	.minor_tests = tpmevlog_tests
+};
+
+FWTS_REGISTER("tpmevlog", &tpmevlog_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_ROOT_PRIV)