diff mbox series

tpmevlogdump: add function for dumping tpm event log

Message ID 1593676355-2850-1-git-send-email-ivan.hu@canonical.com
State Superseded
Headers show
Series tpmevlogdump: add function for dumping tpm event log | expand

Commit Message

Ivan Hu July 2, 2020, 7:52 a.m. UTC
The tpm event log define in "TCG PC Client Specific Platform Firmware Profile
Specification" chapter 9.
https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/

Signed-off-by: Ivan Hu <ivan.hu@canonical.com>
---
 src/Makefile.am                     |   1 +
 src/lib/include/fwts_tpm.h          | 166 +++++++++++++++
 src/tpm/tpmevlogdump/tpmevlogdump.c | 408 ++++++++++++++++++++++++++++++++++++
 3 files changed, 575 insertions(+)
 create mode 100644 src/lib/include/fwts_tpm.h
 create mode 100644 src/tpm/tpmevlogdump/tpmevlogdump.c

Comments

Colin Ian King July 2, 2020, 8:18 a.m. UTC | #1
Thanks, just some corrections and style fixups required.

Colin

On 02/07/2020 08:52, Ivan Hu wrote:
> The tpm event log define in "TCG PC Client Specific Platform Firmware Profile
> Specification" chapter 9.
> https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/
> 
> Signed-off-by: Ivan Hu <ivan.hu@canonical.com>
> ---
>  src/Makefile.am                     |   1 +
>  src/lib/include/fwts_tpm.h          | 166 +++++++++++++++
>  src/tpm/tpmevlogdump/tpmevlogdump.c | 408 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 575 insertions(+)
>  create mode 100644 src/lib/include/fwts_tpm.h
>  create mode 100644 src/tpm/tpmevlogdump/tpmevlogdump.c
> 
> diff --git a/src/Makefile.am b/src/Makefile.am
> index b0e51dd..19ae7a0 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -183,6 +183,7 @@ fwts_SOURCES = main.c 				\
>  	pci/aspm/aspm.c 			\
>  	pci/crs/crs.c 				\
>  	pci/maxreadreq/maxreadreq.c 		\
> +	tpm/tpmevlogdump/tpmevlogdump.c		\
>  	uefi/csm/csm.c 				\
>  	uefi/uefidump/uefidump.c 		\
>  	uefi/uefirttime/uefirttime.c		\
> diff --git a/src/lib/include/fwts_tpm.h b/src/lib/include/fwts_tpm.h
> new file mode 100644
> index 0000000..baeb5cc
> --- /dev/null
> +++ b/src/lib/include/fwts_tpm.h
> @@ -0,0 +1,166 @@
> +/*
> + * 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.
> + *
> + */
> +
> +#ifndef __FWTS_TPM_H__
> +#define __FWTS_TPM_H__
> +
> +PRAGMA_PUSH
> +PRAGMA_PACK_WARN_OFF
> +
> +#define FWTS_TPM_EVENTLOG_V2_SIGNATURE	"Spec ID Event03"
> +
> +/* define from tpm2-tss */
> +#define TPM2_SHA_DIGEST_SIZE     20
> +#define TPM2_SHA1_DIGEST_SIZE    20
> +#define TPM2_SHA256_DIGEST_SIZE  32
> +#define TPM2_SHA384_DIGEST_SIZE  48
> +#define TPM2_SHA512_DIGEST_SIZE  64
> +
> +typedef uint16_t TPM2_ALG_ID;
> +#define TPM2_ALG_ERROR               ((TPM2_ALG_ID) 0x0000)
> +#define TPM2_ALG_RSA                 ((TPM2_ALG_ID) 0x0001)
> +#define TPM2_ALG_TDES                ((TPM2_ALG_ID) 0x0003)
> +#define TPM2_ALG_SHA                 ((TPM2_ALG_ID) 0x0004)
> +#define TPM2_ALG_SHA1                ((TPM2_ALG_ID) 0x0004)
> +#define TPM2_ALG_HMAC                ((TPM2_ALG_ID) 0x0005)
> +#define TPM2_ALG_AES                 ((TPM2_ALG_ID) 0x0006)
> +#define TPM2_ALG_MGF1                ((TPM2_ALG_ID) 0x0007)
> +#define TPM2_ALG_KEYEDHASH           ((TPM2_ALG_ID) 0x0008)
> +#define TPM2_ALG_XOR                 ((TPM2_ALG_ID) 0x000A)
> +#define TPM2_ALG_SHA256              ((TPM2_ALG_ID) 0x000B)
> +#define TPM2_ALG_SHA384              ((TPM2_ALG_ID) 0x000C)
> +#define TPM2_ALG_SHA512              ((TPM2_ALG_ID) 0x000D)
> +#define TPM2_ALG_NULL                ((TPM2_ALG_ID) 0x0010)
> +#define TPM2_ALG_SM3_256             ((TPM2_ALG_ID) 0x0012)
> +#define TPM2_ALG_SM4                 ((TPM2_ALG_ID) 0x0013)
> +#define TPM2_ALG_RSASSA              ((TPM2_ALG_ID) 0x0014)
> +#define TPM2_ALG_RSAES               ((TPM2_ALG_ID) 0x0015)
> +#define TPM2_ALG_RSAPSS              ((TPM2_ALG_ID) 0x0016)
> +#define TPM2_ALG_OAEP                ((TPM2_ALG_ID) 0x0017)
> +#define TPM2_ALG_ECDSA               ((TPM2_ALG_ID) 0x0018)
> +#define TPM2_ALG_ECDH                ((TPM2_ALG_ID) 0x0019)
> +#define TPM2_ALG_ECDAA               ((TPM2_ALG_ID) 0x001A)
> +#define TPM2_ALG_SM2                 ((TPM2_ALG_ID) 0x001B)
> +#define TPM2_ALG_ECSCHNORR           ((TPM2_ALG_ID) 0x001C)
> +#define TPM2_ALG_ECMQV               ((TPM2_ALG_ID) 0x001D)
> +#define TPM2_ALG_KDF1_SP800_56A      ((TPM2_ALG_ID) 0x0020)
> +#define TPM2_ALG_KDF2                ((TPM2_ALG_ID) 0x0021)
> +#define TPM2_ALG_KDF1_SP800_108      ((TPM2_ALG_ID) 0x0022)
> +#define TPM2_ALG_ECC                 ((TPM2_ALG_ID) 0x0023)
> +#define TPM2_ALG_SYMCIPHER           ((TPM2_ALG_ID) 0x0025)
> +#define TPM2_ALG_CAMELLIA            ((TPM2_ALG_ID) 0x0026)
> +#define TPM2_ALG_CMAC                ((TPM2_ALG_ID) 0x003F)
> +#define TPM2_ALG_CTR                 ((TPM2_ALG_ID) 0x0040)
> +#define TPM2_ALG_SHA3_256            ((TPM2_ALG_ID) 0x0027)
> +#define TPM2_ALG_SHA3_384            ((TPM2_ALG_ID) 0x0028)
> +#define TPM2_ALG_SHA3_512            ((TPM2_ALG_ID) 0x0029)
> +#define TPM2_ALG_OFB                 ((TPM2_ALG_ID) 0x0041)
> +#define TPM2_ALG_CBC                 ((TPM2_ALG_ID) 0x0042)
> +#define TPM2_ALG_CFB                 ((TPM2_ALG_ID) 0x0043)
> +#define TPM2_ALG_ECB                 ((TPM2_ALG_ID) 0x0044)
> +
> +/*
> + * define from TCG PC Client Platform Firmware Profile Specification
> + * https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/
> + */
> +
> +typedef enum {
> +	EV_PREBOOT_CERT				= 0x00000000,
> +	EV_POST_CODE				= 0x00000001,
> +	EV_UNUSED				= 0x00000002,
> +	EV_NO_ACTION				= 0x00000003,
> +	EV_SEPARATOR				= 0x00000004,
> +	EV_ACTION				= 0x00000005,
> +	EV_EVENT_TAG				= 0x00000006,
> +	EV_S_CRTM_CONTENTS			= 0x00000007,
> +	EV_S_CRTM_VERSION			= 0x00000008,
> +	EV_CPU_MICROCODE			= 0x00000009,
> +	EV_PLATFORM_CONFIG_FLAGS		= 0x0000000a,
> +	EV_TABLE_OF_DEVICES			= 0x0000000b,
> +	EV_COMPACT_HASH				= 0x0000000c,
> +	EV_IPL					= 0x0000000d,
> +	EV_IPL_PARTITION_DATA			= 0x0000000e,
> +	EV_NONHOST_CODE				= 0x0000000f,
> +	EV_NONHOST_CONFIG			= 0x00000010,
> +	EV_NONHOST_INFO				= 0x00000011,
> +	EV_OMIT_BOOT_DEVICE_EVENTS		= 0x00000012,
> +	EV_EFI_EVENT_BASE			= 0x80000000,
> +	EV_EFI_VARIABLE_DRIVER_CONFIG		= 0x80000001,
> +	EV_EFI_VARIABLE_BOOT			= 0x80000002,
> +	EV_EFI_BOOT_SERVICES_APPLICATION	= 0x80000003,
> +	EV_EFI_BOOT_SERVICES_DRIVER		= 0x80000004,
> +	EV_EFI_RUNTIME_SERVICES_DRIVER		= 0x80000005,
> +	EV_EFI_GPT_EVENT			= 0x80000006,
> +	EV_EFI_ACTION				= 0x80000007,
> +	EV_EFI_PLATFORM_FIRMWARE_BLOB		= 0x80000008,
> +	EV_EFI_HANDOFF_TABLES			= 0x80000009,
> +	EV_EFI_HCRTM_EVENT			= 0x80000010,
> +	EV_EFI_VARIABLE_AUTHORITY		= 0x800000e0
> +} fwts_tpmlog_event_type;
> +
> +
> +typedef struct {
> +	uint32_t pcr_index;
> +	uint32_t event_type;
> +	uint8_t digest[20];
> +	uint32_t event_data_size;
> +	uint8_t event[0];
> +} __attribute__ ((packed)) fwts_pc_client_pcr_event;
> +
> +typedef struct {
> +uint16_t algorithm_id;
> +uint16_t digest_size;
> +} __attribute__ ((packed)) fwts_spec_id_event_alg_sz;
> +
> +typedef struct {
> +	uint8_t				signature[16];
> +	uint32_t			platform_class;
> +	uint8_t				spec_version_minor;
> +	uint8_t				spec_version_major;
> +	uint8_t				spec_errata;
> +	uint8_t				uintn_size;
> +	uint32_t			number_of_alg;
> +/*
> + * Below items are not fix sizes, skip to define them
> + * but it should be calculated when evaluate the
> + * struct size.
> + */
> +//	fwts_spec_id_event_alg_sz	digest_sizes[0];
> +//	uint8_t				vendor_info_size;
> +//	uint8_t				vendor_info[0];
> +} __attribute__ ((packed)) fwts_efi_spec_id_event;
> +

I'd rather not have // comments in fwts if that's OK

> +
> +typedef struct {
> +	uint32_t	pcr_index;
> +	uint32_t	event_type;
> +	uint32_t	digests_count;
> +/*
> + * Below items are not fix size, skip to define them
> + * but it should be calculated when evaluate the
> + * struct size.
> + */
> +//	uint8_t		digests[0];
> +//	uint32_t	event_size;
> +//	uint8_t		event[eventSize]

missing ; after event[eventSize], and no // comments please

> +} __attribute__ ((packed)) fwts_tcg_pcr_event2;
> +
> +PRAGMA_POP
> +
> +#endif
> diff --git a/src/tpm/tpmevlogdump/tpmevlogdump.c b/src/tpm/tpmevlogdump/tpmevlogdump.c
> new file mode 100644
> index 0000000..cd515fd
> --- /dev/null
> +++ b/src/tpm/tpmevlogdump/tpmevlogdump.c
> @@ -0,0 +1,408 @@
> +/*
> + * 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 void tpmevlogdump_data_hexdump(fwts_framework *fw, uint8_t *data, size_t size, char *str)
> +{
> +	size_t i;
> +
> +	fwts_log_info_verbatim(fw, "%s: ", str);
> +	for (i = 0; i < size; i += 16) {
> +		char buffer[128];
> +		size_t left = size - i;
> +
> +		fwts_dump_raw_data(buffer, sizeof(buffer), data + i, i, left > 16 ? 16 : left);
> +		fwts_log_info_verbatim(fw, "%s", buffer+2);

spaces between better and + and 2, buffer + 2 is preferred

> +	}
> +}
> +
> +static char *tpmevlogdump_evtype_to_string (fwts_tpmlog_event_type event_type)
> +{

A switch() statement is preferred

> +	if (event_type == EV_PREBOOT_CERT)
> +		return "EV_PREBOOT_CERT";
> +	if (event_type == EV_POST_CODE)
> +		return "EV_POST_CODE";
> +	if (event_type == EV_UNUSED)
> +		return "EV_UNUSED";
> +	if (event_type == EV_NO_ACTION)
> +		return "EV_NO_ACTION";
> +	if (event_type == EV_SEPARATOR)
> +		return "EV_SEPARATOR";
> +	if (event_type == EV_ACTION)
> +		return "EV_ACTION";
> +	if (event_type == EV_EVENT_TAG)
> +		return "EV_EVENT_TAG";
> +	if (event_type == EV_S_CRTM_CONTENTS)
> +		return "EV_S_CRTM_CONTENTS";
> +	if (event_type == EV_S_CRTM_VERSION)
> +		return "EV_S_CRTM_VERSION";
> +	if (event_type == EV_CPU_MICROCODE)
> +		return "EV_CPU_MICROCODE";
> +	if (event_type == EV_PLATFORM_CONFIG_FLAGS)
> +		return "EV_PLATFORM_CONFIG_FLAGS";
> +	if (event_type == EV_TABLE_OF_DEVICES)
> +		return "EV_TABLE_OF_DEVICES";
> +	if (event_type == EV_COMPACT_HASH)
> +		return "EV_COMPACT_HASH";
> +	if (event_type == EV_IPL)
> +		return "EV_IPL";
> +	if (event_type == EV_IPL_PARTITION_DATA)
> +		return "EV_IPL_PARTITION_DATA";
> +	if (event_type == EV_NONHOST_CODE)
> +		return "EV_NONHOST_CODE";
> +	if (event_type == EV_NONHOST_CONFIG)
> +		return "EV_NONHOST_CONFIG";
> +	if (event_type == EV_NONHOST_INFO)
> +		return "EV_NONHOST_INFO";
> +	if (event_type == EV_OMIT_BOOT_DEVICE_EVENTS)
> +		return "EV_OMIT_BOOT_DEVICE_EVENTS";
> +	if (event_type == EV_EFI_EVENT_BASE)
> +		return "EV_EFI_EVENT_BASE";
> +	if (event_type == EV_EFI_VARIABLE_DRIVER_CONFIG)
> +		return "EV_EFI_VARIABLE_DRIVER_CONFIG";
> +	if (event_type == EV_EFI_VARIABLE_BOOT)
> +		return "EV_EFI_VARIABLE_BOOT";
> +	if (event_type == EV_EFI_BOOT_SERVICES_APPLICATION)
> +		return "EV_BOOT_SERVICES_APPLICATION";
> +	if (event_type == EV_EFI_BOOT_SERVICES_DRIVER)
> +		return "EV_EFI_BOOT_SERVICES_DRIVER";
> +	if (event_type == EV_EFI_RUNTIME_SERVICES_DRIVER)
> +		return "EV_EFI_RUNTIME_SERVICES_DRIVER";
> +	if (event_type == EV_EFI_GPT_EVENT)
> +		return "EV_EFI_GPT_EVENT";
> +	if (event_type == EV_EFI_ACTION)
> +		return "EV_EFI_ACTION";
> +	if (event_type == EV_EFI_PLATFORM_FIRMWARE_BLOB)
> +		return "EV_EFI_PLATFORM_FIRMWARE_BLOB";
> +	if (event_type == EV_EFI_HANDOFF_TABLES)
> +		return "EV_EFI_HANDOFF_TABLES";
> +	if (event_type == EV_EFI_HCRTM_EVENT)
> +		return "EV_EFI_HCRTM_EVENT";
> +	if (event_type == EV_EFI_VARIABLE_AUTHORITY)
> +		return "EV_EFI_EFI_VARIABLE_AUTHORITY";
> +	return "Unknown";
> +}
> +
> +static char *tpmevlogdump_hash_to_string (TPM2_ALG_ID hash)
> +{

switch statement here

> +	if (hash == TPM2_ALG_SHA1)
> +		return "SHA1";
> +	if (hash == TPM2_ALG_SHA256)
> +		return "SHA256";
> +	if (hash == TPM2_ALG_SHA384)
> +		return "SHA384";
> +	if (hash == TPM2_ALG_SHA512)
> +		return "SHA512";
> +	return "Unknown";
> +}
> +
> +static uint8_t tpmevlogdump_get_hash_size (TPM2_ALG_ID hash)
> +{

switch statement here too

> +	if (hash == TPM2_ALG_SHA1)
> +		return TPM2_SHA1_DIGEST_SIZE;
> +	if (hash == TPM2_ALG_SHA256)
> +		return TPM2_SHA256_DIGEST_SIZE;
> +	if (hash == TPM2_ALG_SHA384)
> +		return TPM2_SHA384_DIGEST_SIZE;
> +	if (hash == TPM2_ALG_SHA512)
> +		return TPM2_SHA512_DIGEST_SIZE;
> +	return 0;
> +}
> +
> +static size_t tpmevlogdump_specid_event_dump(fwts_framework *fw, uint8_t *data, size_t len)
> +{
> +
> +	uint32_t i;
> +	size_t len_remain = len;
> +	uint8_t *pdata = data;
> +	char *str_info;
> +
> +	/* check the data length for dumping */
> +	if (len_remain < sizeof(fwts_pc_client_pcr_event)) {
> +		fwts_log_info(fw, "Cannot get enough length for dumping data.");
> +		return 0;
> +	}
> +	fwts_pc_client_pcr_event *pc_event = (fwts_pc_client_pcr_event *)pdata;
> +	fwts_log_info_verbatim(fw, "PCRIndex: 0x%8.8" PRIx32, pc_event->pcr_index);
> +	str_info = tpmevlogdump_evtype_to_string(pc_event->event_type);
> +	fwts_log_info_verbatim(fw, "EventType: 0x%8.8" PRIx32 "(%s)", pc_event->event_type, str_info);
> +	tpmevlogdump_data_hexdump(fw, pc_event->digest, sizeof(pc_event->digest), "Digest");
> +	fwts_log_info_verbatim(fw, "EventSize: 0x%8.8" PRIx32, pc_event->event_data_size);
> +
> +	pdata += sizeof(fwts_pc_client_pcr_event);
> +	len_remain -= sizeof(fwts_pc_client_pcr_event);
> +
> +	/* check the data length for dumping */
> +	if (len_remain < sizeof(fwts_efi_spec_id_event)) {
> +		fwts_log_info(fw, "Cannot get enough length for dumping data.");
> +		return 0;
> +	}
> +	fwts_efi_spec_id_event *specid_evcent = (fwts_efi_spec_id_event *)pdata;
> +	fwts_log_info_verbatim(fw, "EfiSpecIdEvent:");
> +	fwts_log_info_verbatim(fw, "  Signature: %s", (char *)specid_evcent->signature);
> +	fwts_log_info_verbatim(fw, "  platformClass: 0x%8.8" PRIx32, specid_evcent->platform_class);
> +	fwts_log_info_verbatim(fw, "  specVersionMinor: 0x%" PRIx8, specid_evcent->spec_version_minor);
> +	fwts_log_info_verbatim(fw, "  specVersionMajor: 0x%" PRIx8, specid_evcent->platform_class);
> +	fwts_log_info_verbatim(fw, "  specErrata: 0x%" PRIx8, specid_evcent->spec_errata);
> +	fwts_log_info_verbatim(fw, "  uintnSize: 0x%" PRIx8, specid_evcent->uintn_size);
> +	fwts_log_info_verbatim(fw, "  NumberOfAlgorithms: 0x%8.8" PRIx32, specid_evcent->number_of_alg);

Minor output formatting nitpick, is it possible to have all the : lined
up so the output something like:

Signature:           sometext
platformClass:       0x01234567
specVersionMajor:    0x23
...
NumberOfAlgorithmns: 0x01

> +
> +	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++)
> +	{

	^^

for (i = 0; i < specid_evcent->number_of_alg; i++) {

> +		/* check the data length for dumping */
> +		if (len_remain < sizeof(fwts_spec_id_event_alg_sz)) {
> +			fwts_log_info(fw, "Cannot get enough length for dumping data.");
> +			return 0;
> +		}
> +		str_info = tpmevlogdump_hash_to_string(alg_sz->algorithm_id);
> +		fwts_log_info_verbatim(fw, "  digestSizes[%d].AlgId: 0x%4.4" PRIx16 "(%s)", i, alg_sz->algorithm_id, str_info);
> +		fwts_log_info_verbatim(fw, "  digestSizes[%d].DigestSize: %" PRIu16, i, alg_sz->digest_size);
> +		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;
> +	}
> +
> +	uint8_t	vendor_info_size = *(uint8_t *)pdata;
> +	fwts_log_info_verbatim(fw, "  vendorInfoSize: 0x%" PRIx8, vendor_info_size);
> +	pdata += sizeof(vendor_info_size);
> +	len_remain -= sizeof(vendor_info_size);
> +	if (vendor_info_size > 0) {
> +		/* check the data length for dumping */
> +		if (len_remain < vendor_info_size) {
> +			fwts_log_info(fw, "Cannot get enough length for dumping data.");
> +			return 0;
> +		}
> +		tpmevlogdump_data_hexdump(fw, pdata, vendor_info_size, "  vendorInfo");
> +		pdata += vendor_info_size;
> +		len_remain -= vendor_info_size;

I understand the idea of updating pdata here, but it is effectively dead
code as pdata is not referenced any more, so can the pdata += event_size
be removed?


> +	}
> +
> +	return len_remain;
> +}
> +
> +static size_t tpmevlogdump_event_v2_dump(fwts_framework *fw, uint8_t *data, size_t len)
> +{
> +	uint32_t i;
> +	uint8_t *pdata = data;
> +	size_t len_remain = len;
> +	char *str_info;
> +
> +	/* check the data length for dumping */
> +	if (len_remain < sizeof(fwts_tcg_pcr_event2)) {
> +		fwts_log_info(fw, "Cannot get enough length for dumping data.");
> +		return 0;
> +	}
> +	fwts_tcg_pcr_event2 *pcr_event2 = (fwts_tcg_pcr_event2 *)pdata;
> +	fwts_log_info_verbatim(fw, "PCRIndex: 0x%8.8" PRIx32, pcr_event2->pcr_index);
> +	str_info = tpmevlogdump_evtype_to_string(pcr_event2->event_type);
> +	fwts_log_info_verbatim(fw, "EventType: 0x%8.8" PRIx32 "(%s)", pcr_event2->event_type, str_info);
> +	fwts_log_info_verbatim(fw, "Digests Count : 0x%8.8" PRIx32, pcr_event2->digests_count);
> +	pdata += sizeof(fwts_tcg_pcr_event2);
> +	len_remain -= sizeof(fwts_tcg_pcr_event2);
> +	for (i = 0; i < pcr_event2->digests_count; i++) {
> +		uint8_t hash_size = 0;

add a blank line after the declaration

> +		TPM2_ALG_ID alg_id = *(TPM2_ALG_ID *)pdata;
> +		/* check the data length for dumping */
> +		if (len_remain < sizeof(TPM2_ALG_ID)) {
> +			fwts_log_info(fw, "Cannot get enough length for dumping data.");
> +			return 0;
> +		}
> +		str_info = tpmevlogdump_hash_to_string(alg_id);
> +		fwts_log_info_verbatim(fw, "  Digests[%d].AlgId: 0x%4.4" PRIx16 "(%s)", i, alg_id, str_info);
> +		hash_size = tpmevlogdump_get_hash_size(alg_id);
> +		if (!hash_size) {
> +			fwts_log_info(fw, "Unknown hash algorithm. Aborted.");
> +			return 0;
> +		}
> +		pdata += sizeof(TPM2_ALG_ID);
> +		len_remain -= sizeof(TPM2_ALG_ID);
> +		/* check the data length for dumping */
> +		if (len_remain < sizeof(TPM2_ALG_ID)) {
> +			fwts_log_info(fw, "Cannot get enough length for dumping data.");
> +			return 0;
> +		}
> +		tpmevlogdump_data_hexdump(fw, pdata, hash_size, "  Digest");
> +		pdata += hash_size;
> +		len_remain -= hash_size;
> +	}
> +
> +	uint32_t event_size = *(uint32_t *)pdata;
> +	/* check the data length for dumping */
> +	if (len_remain < event_size + sizeof(event_size)) {
> +		fwts_log_info(fw, "Cannot get enough length for dumping data.");
> +		return 0;
> +	}
> +	pdata += sizeof(event_size);
> +	len_remain -= sizeof(event_size);
> +
> +	fwts_log_info_verbatim(fw, "  EventSize: %" PRIu32, event_size);
> +	if (event_size > 0) {
> +		tpmevlogdump_data_hexdump(fw, pdata, event_size, "  Event");
> +		pdata += event_size;

I understand the idea of updating pdata here, but it is effectively dead
code as pdata is not referenced any more, so can the pdata += event_size
be removed?

> +		len_remain -= event_size;
> +	}
> +
> +	return len_remain;
> +}
> +
> +static void tpmevlogdump_parser(fwts_framework *fw, uint8_t *data, size_t len)
> +{
> +	size_t t_len = len;
> +	size_t len_remain = 0;
> +	uint8_t *pdata = data;

add a blank line here

> +	len_remain = tpmevlogdump_specid_event_dump(fw, pdata, t_len);
> +	fwts_log_nl(fw);
> +	pdata += (t_len - len_remain);
> +	while (len_remain > 0) {
> +		t_len = len_remain;
> +		len_remain = tpmevlogdump_event_v2_dump(fw, pdata, t_len);
> +		pdata += (t_len - len_remain);
> +		fwts_log_nl(fw);
> +	}
> +	return;
> +}
> +
> +static uint8_t *tpmevlogdump_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) {
> +				fwts_low_free(ptr);

should be 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 tpmevlogdump_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 = tpmevlogdump_load_file(fd, &length);
> +				tpm_logfile_found = true;
> +				if (data == NULL) {
> +					fwts_log_info(fw, "Cannot load the tpm event logs. Aborted.");
> +					closedir(dir);
> +					return FWTS_ABORTED;
> +				} else {
> +					/* check if the TPM2 eventlog */
> +					if (strstr((char *)(data + sizeof(fwts_pc_client_pcr_event)), FWTS_TPM_EVENTLOG_V2_SIGNATURE))
> +						tpmevlogdump_parser(fw, data, length);
> +					else {
> +						fwts_log_info(fw, "Cannot find the tpm2 event log. Aborted.");
> +						free(data);
> +						closedir(dir);
> +						return FWTS_ABORTED;
> +					}
> +					free(data);
> +				}
> +			}

we need a close(fd) otherwise we leak an open file descriptor

> +		}
> +	} while (tpmdir);
> +
> +	if (!tpm_logfile_found) {
> +		fwts_log_info(fw, "Cannot find the tpm event log. Aborted.");
> +		closedir(dir);
> +		return FWTS_ABORTED;
> +	}
> +
> +	closedir(dir);
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test tpmevlogdump_tests[] = {
> +	{ tpmevlogdump_test1, "Dump Tpm2 Event Log." },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops tpmevlogdump_ops = {
> +	.description = "Dump Tpm2 Event Log.",
> +	.minor_tests = tpmevlogdump_tests
> +};
> +
> +FWTS_REGISTER("tpmevlogdump", &tpmevlogdump_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_UTILS | FWTS_FLAG_ROOT_PRIV)
>
diff mbox series

Patch

diff --git a/src/Makefile.am b/src/Makefile.am
index b0e51dd..19ae7a0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -183,6 +183,7 @@  fwts_SOURCES = main.c 				\
 	pci/aspm/aspm.c 			\
 	pci/crs/crs.c 				\
 	pci/maxreadreq/maxreadreq.c 		\
+	tpm/tpmevlogdump/tpmevlogdump.c		\
 	uefi/csm/csm.c 				\
 	uefi/uefidump/uefidump.c 		\
 	uefi/uefirttime/uefirttime.c		\
diff --git a/src/lib/include/fwts_tpm.h b/src/lib/include/fwts_tpm.h
new file mode 100644
index 0000000..baeb5cc
--- /dev/null
+++ b/src/lib/include/fwts_tpm.h
@@ -0,0 +1,166 @@ 
+/*
+ * 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.
+ *
+ */
+
+#ifndef __FWTS_TPM_H__
+#define __FWTS_TPM_H__
+
+PRAGMA_PUSH
+PRAGMA_PACK_WARN_OFF
+
+#define FWTS_TPM_EVENTLOG_V2_SIGNATURE	"Spec ID Event03"
+
+/* define from tpm2-tss */
+#define TPM2_SHA_DIGEST_SIZE     20
+#define TPM2_SHA1_DIGEST_SIZE    20
+#define TPM2_SHA256_DIGEST_SIZE  32
+#define TPM2_SHA384_DIGEST_SIZE  48
+#define TPM2_SHA512_DIGEST_SIZE  64
+
+typedef uint16_t TPM2_ALG_ID;
+#define TPM2_ALG_ERROR               ((TPM2_ALG_ID) 0x0000)
+#define TPM2_ALG_RSA                 ((TPM2_ALG_ID) 0x0001)
+#define TPM2_ALG_TDES                ((TPM2_ALG_ID) 0x0003)
+#define TPM2_ALG_SHA                 ((TPM2_ALG_ID) 0x0004)
+#define TPM2_ALG_SHA1                ((TPM2_ALG_ID) 0x0004)
+#define TPM2_ALG_HMAC                ((TPM2_ALG_ID) 0x0005)
+#define TPM2_ALG_AES                 ((TPM2_ALG_ID) 0x0006)
+#define TPM2_ALG_MGF1                ((TPM2_ALG_ID) 0x0007)
+#define TPM2_ALG_KEYEDHASH           ((TPM2_ALG_ID) 0x0008)
+#define TPM2_ALG_XOR                 ((TPM2_ALG_ID) 0x000A)
+#define TPM2_ALG_SHA256              ((TPM2_ALG_ID) 0x000B)
+#define TPM2_ALG_SHA384              ((TPM2_ALG_ID) 0x000C)
+#define TPM2_ALG_SHA512              ((TPM2_ALG_ID) 0x000D)
+#define TPM2_ALG_NULL                ((TPM2_ALG_ID) 0x0010)
+#define TPM2_ALG_SM3_256             ((TPM2_ALG_ID) 0x0012)
+#define TPM2_ALG_SM4                 ((TPM2_ALG_ID) 0x0013)
+#define TPM2_ALG_RSASSA              ((TPM2_ALG_ID) 0x0014)
+#define TPM2_ALG_RSAES               ((TPM2_ALG_ID) 0x0015)
+#define TPM2_ALG_RSAPSS              ((TPM2_ALG_ID) 0x0016)
+#define TPM2_ALG_OAEP                ((TPM2_ALG_ID) 0x0017)
+#define TPM2_ALG_ECDSA               ((TPM2_ALG_ID) 0x0018)
+#define TPM2_ALG_ECDH                ((TPM2_ALG_ID) 0x0019)
+#define TPM2_ALG_ECDAA               ((TPM2_ALG_ID) 0x001A)
+#define TPM2_ALG_SM2                 ((TPM2_ALG_ID) 0x001B)
+#define TPM2_ALG_ECSCHNORR           ((TPM2_ALG_ID) 0x001C)
+#define TPM2_ALG_ECMQV               ((TPM2_ALG_ID) 0x001D)
+#define TPM2_ALG_KDF1_SP800_56A      ((TPM2_ALG_ID) 0x0020)
+#define TPM2_ALG_KDF2                ((TPM2_ALG_ID) 0x0021)
+#define TPM2_ALG_KDF1_SP800_108      ((TPM2_ALG_ID) 0x0022)
+#define TPM2_ALG_ECC                 ((TPM2_ALG_ID) 0x0023)
+#define TPM2_ALG_SYMCIPHER           ((TPM2_ALG_ID) 0x0025)
+#define TPM2_ALG_CAMELLIA            ((TPM2_ALG_ID) 0x0026)
+#define TPM2_ALG_CMAC                ((TPM2_ALG_ID) 0x003F)
+#define TPM2_ALG_CTR                 ((TPM2_ALG_ID) 0x0040)
+#define TPM2_ALG_SHA3_256            ((TPM2_ALG_ID) 0x0027)
+#define TPM2_ALG_SHA3_384            ((TPM2_ALG_ID) 0x0028)
+#define TPM2_ALG_SHA3_512            ((TPM2_ALG_ID) 0x0029)
+#define TPM2_ALG_OFB                 ((TPM2_ALG_ID) 0x0041)
+#define TPM2_ALG_CBC                 ((TPM2_ALG_ID) 0x0042)
+#define TPM2_ALG_CFB                 ((TPM2_ALG_ID) 0x0043)
+#define TPM2_ALG_ECB                 ((TPM2_ALG_ID) 0x0044)
+
+/*
+ * define from TCG PC Client Platform Firmware Profile Specification
+ * https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/
+ */
+
+typedef enum {
+	EV_PREBOOT_CERT				= 0x00000000,
+	EV_POST_CODE				= 0x00000001,
+	EV_UNUSED				= 0x00000002,
+	EV_NO_ACTION				= 0x00000003,
+	EV_SEPARATOR				= 0x00000004,
+	EV_ACTION				= 0x00000005,
+	EV_EVENT_TAG				= 0x00000006,
+	EV_S_CRTM_CONTENTS			= 0x00000007,
+	EV_S_CRTM_VERSION			= 0x00000008,
+	EV_CPU_MICROCODE			= 0x00000009,
+	EV_PLATFORM_CONFIG_FLAGS		= 0x0000000a,
+	EV_TABLE_OF_DEVICES			= 0x0000000b,
+	EV_COMPACT_HASH				= 0x0000000c,
+	EV_IPL					= 0x0000000d,
+	EV_IPL_PARTITION_DATA			= 0x0000000e,
+	EV_NONHOST_CODE				= 0x0000000f,
+	EV_NONHOST_CONFIG			= 0x00000010,
+	EV_NONHOST_INFO				= 0x00000011,
+	EV_OMIT_BOOT_DEVICE_EVENTS		= 0x00000012,
+	EV_EFI_EVENT_BASE			= 0x80000000,
+	EV_EFI_VARIABLE_DRIVER_CONFIG		= 0x80000001,
+	EV_EFI_VARIABLE_BOOT			= 0x80000002,
+	EV_EFI_BOOT_SERVICES_APPLICATION	= 0x80000003,
+	EV_EFI_BOOT_SERVICES_DRIVER		= 0x80000004,
+	EV_EFI_RUNTIME_SERVICES_DRIVER		= 0x80000005,
+	EV_EFI_GPT_EVENT			= 0x80000006,
+	EV_EFI_ACTION				= 0x80000007,
+	EV_EFI_PLATFORM_FIRMWARE_BLOB		= 0x80000008,
+	EV_EFI_HANDOFF_TABLES			= 0x80000009,
+	EV_EFI_HCRTM_EVENT			= 0x80000010,
+	EV_EFI_VARIABLE_AUTHORITY		= 0x800000e0
+} fwts_tpmlog_event_type;
+
+
+typedef struct {
+	uint32_t pcr_index;
+	uint32_t event_type;
+	uint8_t digest[20];
+	uint32_t event_data_size;
+	uint8_t event[0];
+} __attribute__ ((packed)) fwts_pc_client_pcr_event;
+
+typedef struct {
+uint16_t algorithm_id;
+uint16_t digest_size;
+} __attribute__ ((packed)) fwts_spec_id_event_alg_sz;
+
+typedef struct {
+	uint8_t				signature[16];
+	uint32_t			platform_class;
+	uint8_t				spec_version_minor;
+	uint8_t				spec_version_major;
+	uint8_t				spec_errata;
+	uint8_t				uintn_size;
+	uint32_t			number_of_alg;
+/*
+ * Below items are not fix sizes, skip to define them
+ * but it should be calculated when evaluate the
+ * struct size.
+ */
+//	fwts_spec_id_event_alg_sz	digest_sizes[0];
+//	uint8_t				vendor_info_size;
+//	uint8_t				vendor_info[0];
+} __attribute__ ((packed)) fwts_efi_spec_id_event;
+
+
+typedef struct {
+	uint32_t	pcr_index;
+	uint32_t	event_type;
+	uint32_t	digests_count;
+/*
+ * Below items are not fix size, skip to define them
+ * but it should be calculated when evaluate the
+ * struct size.
+ */
+//	uint8_t		digests[0];
+//	uint32_t	event_size;
+//	uint8_t		event[eventSize]
+} __attribute__ ((packed)) fwts_tcg_pcr_event2;
+
+PRAGMA_POP
+
+#endif
diff --git a/src/tpm/tpmevlogdump/tpmevlogdump.c b/src/tpm/tpmevlogdump/tpmevlogdump.c
new file mode 100644
index 0000000..cd515fd
--- /dev/null
+++ b/src/tpm/tpmevlogdump/tpmevlogdump.c
@@ -0,0 +1,408 @@ 
+/*
+ * 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 void tpmevlogdump_data_hexdump(fwts_framework *fw, uint8_t *data, size_t size, char *str)
+{
+	size_t i;
+
+	fwts_log_info_verbatim(fw, "%s: ", str);
+	for (i = 0; i < size; i += 16) {
+		char buffer[128];
+		size_t left = size - i;
+
+		fwts_dump_raw_data(buffer, sizeof(buffer), data + i, i, left > 16 ? 16 : left);
+		fwts_log_info_verbatim(fw, "%s", buffer+2);
+	}
+}
+
+static char *tpmevlogdump_evtype_to_string (fwts_tpmlog_event_type event_type)
+{
+	if (event_type == EV_PREBOOT_CERT)
+		return "EV_PREBOOT_CERT";
+	if (event_type == EV_POST_CODE)
+		return "EV_POST_CODE";
+	if (event_type == EV_UNUSED)
+		return "EV_UNUSED";
+	if (event_type == EV_NO_ACTION)
+		return "EV_NO_ACTION";
+	if (event_type == EV_SEPARATOR)
+		return "EV_SEPARATOR";
+	if (event_type == EV_ACTION)
+		return "EV_ACTION";
+	if (event_type == EV_EVENT_TAG)
+		return "EV_EVENT_TAG";
+	if (event_type == EV_S_CRTM_CONTENTS)
+		return "EV_S_CRTM_CONTENTS";
+	if (event_type == EV_S_CRTM_VERSION)
+		return "EV_S_CRTM_VERSION";
+	if (event_type == EV_CPU_MICROCODE)
+		return "EV_CPU_MICROCODE";
+	if (event_type == EV_PLATFORM_CONFIG_FLAGS)
+		return "EV_PLATFORM_CONFIG_FLAGS";
+	if (event_type == EV_TABLE_OF_DEVICES)
+		return "EV_TABLE_OF_DEVICES";
+	if (event_type == EV_COMPACT_HASH)
+		return "EV_COMPACT_HASH";
+	if (event_type == EV_IPL)
+		return "EV_IPL";
+	if (event_type == EV_IPL_PARTITION_DATA)
+		return "EV_IPL_PARTITION_DATA";
+	if (event_type == EV_NONHOST_CODE)
+		return "EV_NONHOST_CODE";
+	if (event_type == EV_NONHOST_CONFIG)
+		return "EV_NONHOST_CONFIG";
+	if (event_type == EV_NONHOST_INFO)
+		return "EV_NONHOST_INFO";
+	if (event_type == EV_OMIT_BOOT_DEVICE_EVENTS)
+		return "EV_OMIT_BOOT_DEVICE_EVENTS";
+	if (event_type == EV_EFI_EVENT_BASE)
+		return "EV_EFI_EVENT_BASE";
+	if (event_type == EV_EFI_VARIABLE_DRIVER_CONFIG)
+		return "EV_EFI_VARIABLE_DRIVER_CONFIG";
+	if (event_type == EV_EFI_VARIABLE_BOOT)
+		return "EV_EFI_VARIABLE_BOOT";
+	if (event_type == EV_EFI_BOOT_SERVICES_APPLICATION)
+		return "EV_BOOT_SERVICES_APPLICATION";
+	if (event_type == EV_EFI_BOOT_SERVICES_DRIVER)
+		return "EV_EFI_BOOT_SERVICES_DRIVER";
+	if (event_type == EV_EFI_RUNTIME_SERVICES_DRIVER)
+		return "EV_EFI_RUNTIME_SERVICES_DRIVER";
+	if (event_type == EV_EFI_GPT_EVENT)
+		return "EV_EFI_GPT_EVENT";
+	if (event_type == EV_EFI_ACTION)
+		return "EV_EFI_ACTION";
+	if (event_type == EV_EFI_PLATFORM_FIRMWARE_BLOB)
+		return "EV_EFI_PLATFORM_FIRMWARE_BLOB";
+	if (event_type == EV_EFI_HANDOFF_TABLES)
+		return "EV_EFI_HANDOFF_TABLES";
+	if (event_type == EV_EFI_HCRTM_EVENT)
+		return "EV_EFI_HCRTM_EVENT";
+	if (event_type == EV_EFI_VARIABLE_AUTHORITY)
+		return "EV_EFI_EFI_VARIABLE_AUTHORITY";
+	return "Unknown";
+}
+
+static char *tpmevlogdump_hash_to_string (TPM2_ALG_ID hash)
+{
+	if (hash == TPM2_ALG_SHA1)
+		return "SHA1";
+	if (hash == TPM2_ALG_SHA256)
+		return "SHA256";
+	if (hash == TPM2_ALG_SHA384)
+		return "SHA384";
+	if (hash == TPM2_ALG_SHA512)
+		return "SHA512";
+	return "Unknown";
+}
+
+static uint8_t tpmevlogdump_get_hash_size (TPM2_ALG_ID hash)
+{
+	if (hash == TPM2_ALG_SHA1)
+		return TPM2_SHA1_DIGEST_SIZE;
+	if (hash == TPM2_ALG_SHA256)
+		return TPM2_SHA256_DIGEST_SIZE;
+	if (hash == TPM2_ALG_SHA384)
+		return TPM2_SHA384_DIGEST_SIZE;
+	if (hash == TPM2_ALG_SHA512)
+		return TPM2_SHA512_DIGEST_SIZE;
+	return 0;
+}
+
+static size_t tpmevlogdump_specid_event_dump(fwts_framework *fw, uint8_t *data, size_t len)
+{
+
+	uint32_t i;
+	size_t len_remain = len;
+	uint8_t *pdata = data;
+	char *str_info;
+
+	/* check the data length for dumping */
+	if (len_remain < sizeof(fwts_pc_client_pcr_event)) {
+		fwts_log_info(fw, "Cannot get enough length for dumping data.");
+		return 0;
+	}
+	fwts_pc_client_pcr_event *pc_event = (fwts_pc_client_pcr_event *)pdata;
+	fwts_log_info_verbatim(fw, "PCRIndex: 0x%8.8" PRIx32, pc_event->pcr_index);
+	str_info = tpmevlogdump_evtype_to_string(pc_event->event_type);
+	fwts_log_info_verbatim(fw, "EventType: 0x%8.8" PRIx32 "(%s)", pc_event->event_type, str_info);
+	tpmevlogdump_data_hexdump(fw, pc_event->digest, sizeof(pc_event->digest), "Digest");
+	fwts_log_info_verbatim(fw, "EventSize: 0x%8.8" PRIx32, pc_event->event_data_size);
+
+	pdata += sizeof(fwts_pc_client_pcr_event);
+	len_remain -= sizeof(fwts_pc_client_pcr_event);
+
+	/* check the data length for dumping */
+	if (len_remain < sizeof(fwts_efi_spec_id_event)) {
+		fwts_log_info(fw, "Cannot get enough length for dumping data.");
+		return 0;
+	}
+	fwts_efi_spec_id_event *specid_evcent = (fwts_efi_spec_id_event *)pdata;
+	fwts_log_info_verbatim(fw, "EfiSpecIdEvent:");
+	fwts_log_info_verbatim(fw, "  Signature: %s", (char *)specid_evcent->signature);
+	fwts_log_info_verbatim(fw, "  platformClass: 0x%8.8" PRIx32, specid_evcent->platform_class);
+	fwts_log_info_verbatim(fw, "  specVersionMinor: 0x%" PRIx8, specid_evcent->spec_version_minor);
+	fwts_log_info_verbatim(fw, "  specVersionMajor: 0x%" PRIx8, specid_evcent->platform_class);
+	fwts_log_info_verbatim(fw, "  specErrata: 0x%" PRIx8, specid_evcent->spec_errata);
+	fwts_log_info_verbatim(fw, "  uintnSize: 0x%" PRIx8, specid_evcent->uintn_size);
+	fwts_log_info_verbatim(fw, "  NumberOfAlgorithms: 0x%8.8" PRIx32, specid_evcent->number_of_alg);
+
+	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++)
+	{
+		/* check the data length for dumping */
+		if (len_remain < sizeof(fwts_spec_id_event_alg_sz)) {
+			fwts_log_info(fw, "Cannot get enough length for dumping data.");
+			return 0;
+		}
+		str_info = tpmevlogdump_hash_to_string(alg_sz->algorithm_id);
+		fwts_log_info_verbatim(fw, "  digestSizes[%d].AlgId: 0x%4.4" PRIx16 "(%s)", i, alg_sz->algorithm_id, str_info);
+		fwts_log_info_verbatim(fw, "  digestSizes[%d].DigestSize: %" PRIu16, i, alg_sz->digest_size);
+		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;
+	}
+
+	uint8_t	vendor_info_size = *(uint8_t *)pdata;
+	fwts_log_info_verbatim(fw, "  vendorInfoSize: 0x%" PRIx8, vendor_info_size);
+	pdata += sizeof(vendor_info_size);
+	len_remain -= sizeof(vendor_info_size);
+	if (vendor_info_size > 0) {
+		/* check the data length for dumping */
+		if (len_remain < vendor_info_size) {
+			fwts_log_info(fw, "Cannot get enough length for dumping data.");
+			return 0;
+		}
+		tpmevlogdump_data_hexdump(fw, pdata, vendor_info_size, "  vendorInfo");
+		pdata += vendor_info_size;
+		len_remain -= vendor_info_size;
+	}
+
+	return len_remain;
+}
+
+static size_t tpmevlogdump_event_v2_dump(fwts_framework *fw, uint8_t *data, size_t len)
+{
+	uint32_t i;
+	uint8_t *pdata = data;
+	size_t len_remain = len;
+	char *str_info;
+
+	/* check the data length for dumping */
+	if (len_remain < sizeof(fwts_tcg_pcr_event2)) {
+		fwts_log_info(fw, "Cannot get enough length for dumping data.");
+		return 0;
+	}
+	fwts_tcg_pcr_event2 *pcr_event2 = (fwts_tcg_pcr_event2 *)pdata;
+	fwts_log_info_verbatim(fw, "PCRIndex: 0x%8.8" PRIx32, pcr_event2->pcr_index);
+	str_info = tpmevlogdump_evtype_to_string(pcr_event2->event_type);
+	fwts_log_info_verbatim(fw, "EventType: 0x%8.8" PRIx32 "(%s)", pcr_event2->event_type, str_info);
+	fwts_log_info_verbatim(fw, "Digests Count : 0x%8.8" PRIx32, pcr_event2->digests_count);
+	pdata += sizeof(fwts_tcg_pcr_event2);
+	len_remain -= sizeof(fwts_tcg_pcr_event2);
+	for (i = 0; i < pcr_event2->digests_count; i++) {
+		uint8_t hash_size = 0;
+		TPM2_ALG_ID alg_id = *(TPM2_ALG_ID *)pdata;
+		/* check the data length for dumping */
+		if (len_remain < sizeof(TPM2_ALG_ID)) {
+			fwts_log_info(fw, "Cannot get enough length for dumping data.");
+			return 0;
+		}
+		str_info = tpmevlogdump_hash_to_string(alg_id);
+		fwts_log_info_verbatim(fw, "  Digests[%d].AlgId: 0x%4.4" PRIx16 "(%s)", i, alg_id, str_info);
+		hash_size = tpmevlogdump_get_hash_size(alg_id);
+		if (!hash_size) {
+			fwts_log_info(fw, "Unknown hash algorithm. Aborted.");
+			return 0;
+		}
+		pdata += sizeof(TPM2_ALG_ID);
+		len_remain -= sizeof(TPM2_ALG_ID);
+		/* check the data length for dumping */
+		if (len_remain < sizeof(TPM2_ALG_ID)) {
+			fwts_log_info(fw, "Cannot get enough length for dumping data.");
+			return 0;
+		}
+		tpmevlogdump_data_hexdump(fw, pdata, hash_size, "  Digest");
+		pdata += hash_size;
+		len_remain -= hash_size;
+	}
+
+	uint32_t event_size = *(uint32_t *)pdata;
+	/* check the data length for dumping */
+	if (len_remain < event_size + sizeof(event_size)) {
+		fwts_log_info(fw, "Cannot get enough length for dumping data.");
+		return 0;
+	}
+	pdata += sizeof(event_size);
+	len_remain -= sizeof(event_size);
+
+	fwts_log_info_verbatim(fw, "  EventSize: %" PRIu32, event_size);
+	if (event_size > 0) {
+		tpmevlogdump_data_hexdump(fw, pdata, event_size, "  Event");
+		pdata += event_size;
+		len_remain -= event_size;
+	}
+
+	return len_remain;
+}
+
+static void tpmevlogdump_parser(fwts_framework *fw, uint8_t *data, size_t len)
+{
+	size_t t_len = len;
+	size_t len_remain = 0;
+	uint8_t *pdata = data;
+	len_remain = tpmevlogdump_specid_event_dump(fw, pdata, t_len);
+	fwts_log_nl(fw);
+	pdata += (t_len - len_remain);
+	while (len_remain > 0) {
+		t_len = len_remain;
+		len_remain = tpmevlogdump_event_v2_dump(fw, pdata, t_len);
+		pdata += (t_len - len_remain);
+		fwts_log_nl(fw);
+	}
+	return;
+}
+
+static uint8_t *tpmevlogdump_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) {
+				fwts_low_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 tpmevlogdump_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 = tpmevlogdump_load_file(fd, &length);
+				tpm_logfile_found = true;
+				if (data == NULL) {
+					fwts_log_info(fw, "Cannot load the tpm event logs. Aborted.");
+					closedir(dir);
+					return FWTS_ABORTED;
+				} else {
+					/* check if the TPM2 eventlog */
+					if (strstr((char *)(data + sizeof(fwts_pc_client_pcr_event)), FWTS_TPM_EVENTLOG_V2_SIGNATURE))
+						tpmevlogdump_parser(fw, data, length);
+					else {
+						fwts_log_info(fw, "Cannot find the tpm2 event log. Aborted.");
+						free(data);
+						closedir(dir);
+						return FWTS_ABORTED;
+					}
+					free(data);
+				}
+			}
+		}
+	} while (tpmdir);
+
+	if (!tpm_logfile_found) {
+		fwts_log_info(fw, "Cannot find the tpm event log. Aborted.");
+		closedir(dir);
+		return FWTS_ABORTED;
+	}
+
+	closedir(dir);
+	return FWTS_OK;
+}
+
+static fwts_framework_minor_test tpmevlogdump_tests[] = {
+	{ tpmevlogdump_test1, "Dump Tpm2 Event Log." },
+	{ NULL, NULL }
+};
+
+static fwts_framework_ops tpmevlogdump_ops = {
+	.description = "Dump Tpm2 Event Log.",
+	.minor_tests = tpmevlogdump_tests
+};
+
+FWTS_REGISTER("tpmevlogdump", &tpmevlogdump_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_UTILS | FWTS_FLAG_ROOT_PRIV)