From 8b31466bc1f47ffe5b8a81b7f3659c028fe43e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Hor=C3=A1=C4=8Dek?= Date: Mon, 25 May 2026 20:52:48 +0200 Subject: [PATCH] Backport upstream fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves: RHEL-164796 Signed-off-by: Štěpán Horáček --- ...og-Fix-parsing-on-big-endian-systems.patch | 849 ++++++++++++++++++ ..._policysigned-add-parameter-raw-data.patch | 32 + ...xternal-Add-option-rsa_exponent_zero.patch | 87 ++ ...quote-Fix-usage-of-little-endian-ser.patch | 539 +++++++++++ ...edential-Fix-usage-of-name-parameter.patch | 93 ++ ...Fix-indentation-in-parse_marshaled_s.patch | 51 ++ ...heckquote-Fix-missing-error-checking.patch | 30 + ...Fix-memory-leaks-malloced-buffer-not.patch | 47 + ...redential-fix-wrong-tcg-ek-templates.patch | 178 ++++ ...-tools-Fix-several-clang-tidy-errors.patch | 383 ++++++++ ...-tools-Fix-wrong-type-usage-of-enums.patch | 166 ++++ ...ntlog-Extend-pcrs-using-event-EV_IPL.patch | 33 + ...et-size-if-the-size-parameter-is-not.patch | 49 + ...creation-of-ctx-file-if-TPM2_CreateL.patch | 265 ++++++ ...ix-off-by-one-in-PCR-digest-list-han.patch | 66 ++ ...Add-size-checks-for-PCR-desearializa.patch | 54 ++ ...lidate-command_size-before-computing.patch | 44 + ...oid-unintended-stdio-closing-in-clos.patch | 27 + ...x-error-message-in-tpm2_pem_encoded_.patch | 27 + ...dify-tpm2_safe_read_from_stdin-to-re.patch | 70 ++ ...-bounds-write-with-too-many-auxiliar.patch | 447 +++++++++ ...l-avoid-mutating-input-scheme-in-han.patch | 127 +++ ...add-missing-null-checks-after-malloc.patch | 115 +++ ...add-missing-null-checks-after-malloc.patch | 38 + ...x-buffer-overflow-in-string-validati.patch | 45 + ...ght-amount-of-memory-for-auth_policy.patch | 30 + ...me-checks-to-tpm2_safe_read_from_std.patch | 37 + 0028-tpm2_createak-add-attribute-option.patch | 150 ++++ ...ernal-check-return-value-of-RAND_byt.patch | 41 + ..._identity_util-add-buffer-size-check.patch | 154 ++++ tpm2-tools.spec | 39 +- 31 files changed, 4309 insertions(+), 4 deletions(-) create mode 100644 0001-tpm2_eventlog-Fix-parsing-on-big-endian-systems.patch create mode 100644 0002-man-tpm2_policysigned-add-parameter-raw-data.patch create mode 100644 0003-tpm2_loadexternal-Add-option-rsa_exponent_zero.patch create mode 100644 0004-tpm2_quote-checkquote-Fix-usage-of-little-endian-ser.patch create mode 100644 0005-tpm2_makecredential-Fix-usage-of-name-parameter.patch create mode 100644 0006-tpm2_checkquote-Fix-indentation-in-parse_marshaled_s.patch create mode 100644 0007-tpm2_checkquote-Fix-missing-error-checking.patch create mode 100644 0008-tpm2_checkquote-Fix-memory-leaks-malloced-buffer-not.patch create mode 100644 0009-tpm2_makecredential-fix-wrong-tcg-ek-templates.patch create mode 100644 0010-tools-Fix-several-clang-tidy-errors.patch create mode 100644 0011-tools-Fix-wrong-type-usage-of-enums.patch create mode 100644 0012-tpm2_eventlog-Extend-pcrs-using-event-EV_IPL.patch create mode 100644 0013-tpm2_nvcertify-Set-size-if-the-size-parameter-is-not.patch create mode 100644 0014-tpm2_create-Fix-creation-of-ctx-file-if-TPM2_CreateL.patch create mode 100644 0015-fix-checkquote-fix-off-by-one-in-PCR-digest-list-han.patch create mode 100644 0016-tpm2_checkquote-Add-size-checks-for-PCR-desearializa.patch create mode 100644 0017-fix-tpm2_send-validate-command_size-before-computing.patch create mode 100644 0018-fix-tpm2_send-avoid-unintended-stdio-closing-in-clos.patch create mode 100644 0019-fix-tpm2_util-fix-error-message-in-tpm2_pem_encoded_.patch create mode 100644 0020-fix-tpm2_util-modify-tpm2_safe_read_from_stdin-to-re.patch create mode 100644 0021-fix-tools-out-of-bounds-write-with-too-many-auxiliar.patch create mode 100644 0022-fix-tpm2_alg_util-avoid-mutating-input-scheme-in-han.patch create mode 100644 0023-fix-tools-add-missing-null-checks-after-malloc.patch create mode 100644 0024-fix-test-add-missing-null-checks-after-malloc.patch create mode 100644 0025-fix-tpm2_util-fix-buffer-overflow-in-string-validati.patch create mode 100644 0026-Allocate-the-right-amount-of-memory-for-auth_policy.patch create mode 100644 0027-tpm2_util-add-some-checks-to-tpm2_safe_read_from_std.patch create mode 100644 0028-tpm2_createak-add-attribute-option.patch create mode 100644 0029-fix-tpm2_loadexternal-check-return-value-of-RAND_byt.patch create mode 100644 0030-fix-tpm2_identity_util-add-buffer-size-check.patch diff --git a/0001-tpm2_eventlog-Fix-parsing-on-big-endian-systems.patch b/0001-tpm2_eventlog-Fix-parsing-on-big-endian-systems.patch new file mode 100644 index 0000000..5950ffe --- /dev/null +++ b/0001-tpm2_eventlog-Fix-parsing-on-big-endian-systems.patch @@ -0,0 +1,849 @@ +From b294a9eebae6be1563526c017cca1b3de71944dc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Hor=C3=A1=C4=8Dek?= + +Date: Fri, 8 Aug 2025 22:24:30 +0200 +Subject: [PATCH 01/30] tpm2_eventlog: Fix parsing on big-endian systems +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Štěpán Horáček +--- + lib/tpm2_eventlog.c | 73 ++++++------- + lib/tpm2_eventlog_yaml.c | 219 ++++++++++++++++++++------------------- + 2 files changed, 147 insertions(+), 145 deletions(-) + +diff --git a/lib/tpm2_eventlog.c b/lib/tpm2_eventlog.c +index 70ae35fe..40148f8b 100644 +--- a/lib/tpm2_eventlog.c ++++ b/lib/tpm2_eventlog.c +@@ -1,6 +1,7 @@ + #include + #include + #include ++#include + + #include + +@@ -54,7 +55,7 @@ bool foreach_digest2(tpm2_eventlog_context *ctx, UINT32 eventType, unsigned pcr_ + return false; + } + +- const TPMI_ALG_HASH alg = digest->AlgorithmId; ++ const TPMI_ALG_HASH alg = le16toh(digest->AlgorithmId); + const size_t alg_size = tpm2_alg_util_get_hash_size(alg); + if (size < sizeof(*digest) + alg_size) { + LOG_ERR("insufficient size for digest buffer"); +@@ -122,13 +123,13 @@ bool parse_event2body(TCG_EVENT2 const *event, UINT32 type) { + case EV_EFI_VARIABLE_AUTHORITY: + { + UEFI_VARIABLE_DATA *data = (UEFI_VARIABLE_DATA*)event->Event; +- if (event->EventSize < sizeof(*data)) { ++ if (le32toh(event->EventSize) < sizeof(*data)) { + LOG_ERR("size is insufficient for UEFI variable data"); + return false; + } + +- if (event->EventSize < sizeof(*data) + data->UnicodeNameLength * +- sizeof(UTF16_CHAR) + data->VariableDataLength) ++ if (le32toh(event->EventSize) < sizeof(*data) + le64toh(data->UnicodeNameLength) * ++ sizeof(UTF16_CHAR) + le64toh(data->VariableDataLength)) + { + LOG_ERR("size is insufficient for UEFI variable data"); + return false; +@@ -146,7 +147,7 @@ bool parse_event2body(TCG_EVENT2 const *event, UINT32 type) { + UEFI_PLATFORM_FIRMWARE_BLOB *data = + (UEFI_PLATFORM_FIRMWARE_BLOB*)event->Event; + UNUSED(data); +- if (event->EventSize < sizeof(*data)) { ++ if (le32toh(event->EventSize) < sizeof(*data)) { + LOG_ERR("size is insufficient for UEFI FW blob data"); + return false; + } +@@ -158,7 +159,7 @@ bool parse_event2body(TCG_EVENT2 const *event, UINT32 type) { + { + UEFI_IMAGE_LOAD_EVENT *data = (UEFI_IMAGE_LOAD_EVENT*)event->Event; + UNUSED(data); +- if (event->EventSize < sizeof(*data)) { ++ if (le32toh(event->EventSize) < sizeof(*data)) { + LOG_ERR("size is insufficient for UEFI image load event"); + return false; + } +@@ -171,7 +172,7 @@ bool parse_event2body(TCG_EVENT2 const *event, UINT32 type) { + const char hcrtm_data[] = "HCRTM"; + size_t len = strlen(hcrtm_data); + BYTE *data = (BYTE *)event->Event; +- if (event->EventSize != len || ++ if (le32toh(event->EventSize) != len || + strncmp((const char *)data, hcrtm_data, len)) { + LOG_ERR("HCRTM Event Data MUST be the string: \"%s\"", hcrtm_data); + return false; +@@ -201,9 +202,9 @@ bool parse_event2(TCG_EVENT_HEADER2 const *eventhdr, size_t buf_size, + .data = digests_size, + .digest2_cb = digest2_accumulator_callback, + }; +- ret = foreach_digest2(&ctx, eventhdr->EventType, +- eventhdr->PCRIndex, +- eventhdr->Digests, eventhdr->DigestCount, ++ ret = foreach_digest2(&ctx, le32toh(eventhdr->EventType), ++ le32toh(eventhdr->PCRIndex), ++ eventhdr->Digests, le32toh(eventhdr->DigestCount), + buf_size - sizeof(*eventhdr), 0); + if (ret != true) { + return false; +@@ -217,11 +218,11 @@ bool parse_event2(TCG_EVENT_HEADER2 const *eventhdr, size_t buf_size, + } + *event_size += sizeof(*event); + +- if (buf_size < *event_size + event->EventSize) { ++ if (buf_size < *event_size + le32toh(event->EventSize)) { + LOG_ERR("size insufficient for event data"); + return false; + } +- *event_size += event->EventSize; ++ *event_size += le32toh(event->EventSize); + + return true; + } +@@ -238,19 +239,19 @@ bool parse_sha1_log_event(tpm2_eventlog_context *ctx, TCG_EVENT const *event, si + } + *event_size = sizeof(*event); + +- pcr = ctx->sha1_pcrs[ event->pcrIndex]; +- if (event->eventType != EV_NO_ACTION && pcr) { ++ pcr = ctx->sha1_pcrs[le32toh(event->pcrIndex)]; ++ if (le32toh(event->eventType) != EV_NO_ACTION && pcr) { + tpm2_openssl_pcr_extend(TPM2_ALG_SHA1, pcr, &event->digest[0], 20); +- ctx->sha1_used |= (1 << event->pcrIndex); ++ ctx->sha1_used |= (1 << le32toh(event->pcrIndex)); + } + + /* buffer size must be sufficient to hold event and event data */ + if (size < sizeof(*event) + (sizeof(event->event[0]) * +- event->eventDataSize)) { ++ le32toh(event->eventDataSize))) { + LOG_ERR("insufficient size for SpecID event data"); + return false; + } +- *event_size += event->eventDataSize; ++ *event_size += le32toh(event->eventDataSize); + return true; + } + +@@ -289,14 +290,14 @@ bool foreach_sha1_log_event(tpm2_eventlog_context *ctx, TCG_EVENT const *eventhd + } + } + +- ret = parse_event2body(event, eventhdr->eventType); ++ ret = parse_event2body(event, le32toh(eventhdr->eventType)); + if (ret != true) { + return ret; + } + + /* event data callback */ + if (ctx->event2_cb != NULL) { +- ret = ctx->event2_cb(event, eventhdr->eventType, ctx->data, ++ ret = ctx->event2_cb(event, le32toh(eventhdr->eventType), ctx->data, + ctx->eventlog_version); + if (ret != true) { + return false; +@@ -467,12 +468,12 @@ bool foreach_event2(tpm2_eventlog_context *ctx, TCG_EVENT_HEADER2 const *eventhd + + TCG_EVENT2 *event = (TCG_EVENT2*)((uintptr_t)eventhdr->Digests + digests_size); + +- if (eventhdr->EventType == EV_EFI_HCRTM_EVENT && eventhdr->PCRIndex == 0) { ++ if (le32toh(eventhdr->EventType) == EV_EFI_HCRTM_EVENT && le32toh(eventhdr->PCRIndex) == 0) { + found_hcrtm = true; + } + + /* Handle StartupLocality in replay for PCR0 */ +- if (!found_hcrtm && eventhdr->EventType == EV_NO_ACTION && eventhdr->PCRIndex == 0) { ++ if (!found_hcrtm && le32toh(eventhdr->EventType) == EV_NO_ACTION && le32toh(eventhdr->PCRIndex) == 0) { + if (event_size < sizeof(EV_NO_ACTION_STRUCT)) { + LOG_ERR("EventSize is too small\n"); + return false; +@@ -496,13 +497,13 @@ bool foreach_event2(tpm2_eventlog_context *ctx, TCG_EVENT_HEADER2 const *eventhd + } + + /* digest callback foreach digest */ +- ret = foreach_digest2(ctx, eventhdr->EventType, eventhdr->PCRIndex, +- eventhdr->Digests, eventhdr->DigestCount, digests_size, locality); ++ ret = foreach_digest2(ctx, le32toh(eventhdr->EventType), le32toh(eventhdr->PCRIndex), ++ eventhdr->Digests, le32toh(eventhdr->DigestCount), digests_size, locality); + if (ret != true) { + return false; + } + +- ret = parse_event2body(event, eventhdr->EventType); ++ ret = parse_event2body(event, le32toh(eventhdr->EventType)); + if (ret != true) { + return ret; + } +@@ -514,7 +515,7 @@ bool foreach_event2(tpm2_eventlog_context *ctx, TCG_EVENT_HEADER2 const *eventhd + + /* event data callback */ + if (ctx->event2_cb != NULL) { +- ret = ctx->event2_cb(event, eventhdr->EventType, ctx->data, ctx->eventlog_version); ++ ret = ctx->event2_cb(event, le32toh(eventhdr->EventType), ctx->data, ctx->eventlog_version); + if (ret != true) { + return false; + } +@@ -533,12 +534,12 @@ bool specid_event(TCG_EVENT const *event, size_t size, + return false; + } + +- if (event->eventType != EV_NO_ACTION) { ++ if (le32toh(event->eventType) != EV_NO_ACTION) { + LOG_ERR("SpecID eventType must be EV_NO_ACTION"); + return false; + } + +- if (event->pcrIndex != 0) { ++ if (le32toh(event->pcrIndex) != 0) { + LOG_ERR("bad pcrIndex for EV_NO_ACTION event"); + return false; + } +@@ -552,29 +553,29 @@ bool specid_event(TCG_EVENT const *event, size_t size, + } + + /* eventDataSize must be sufficient to hold the specid event */ +- if (event->eventDataSize < sizeof(TCG_SPECID_EVENT)) { ++ if (le32toh(event->eventDataSize) < sizeof(TCG_SPECID_EVENT)) { + LOG_ERR("invalid eventDataSize in specid event"); + return false; + } + + /* buffer size must be sufficient to hold event and event data */ + if (size < sizeof(*event) + (sizeof(event->event[0]) * +- event->eventDataSize)) { ++ le32toh(event->eventDataSize))) { + LOG_ERR("insufficient size for SpecID event data"); + return false; + } + + /* specid event must have 1 or more algorithms */ + TCG_SPECID_EVENT *event_specid = (TCG_SPECID_EVENT*)event->event; +- if (event_specid->numberOfAlgorithms == 0) { ++ if (le32toh(event_specid->numberOfAlgorithms) == 0) { + LOG_ERR("numberOfAlgorithms is invalid, may not be 0"); + return false; + } + + /* buffer size must be sufficient to hold event, specid event & algs */ + if (size < sizeof(*event) + sizeof(*event_specid) + +- sizeof(event_specid->digestSizes[0]) * +- event_specid->numberOfAlgorithms) { ++ sizeof(event_specid->digestSizes[0]) * ++ le32toh(event_specid->numberOfAlgorithms)) { + LOG_ERR("insufficient size for SpecID algorithms"); + return false; + } +@@ -582,18 +583,18 @@ bool specid_event(TCG_EVENT const *event, size_t size, + /* size must be sufficient for event, specid, algs & vendor stuff */ + if (size < sizeof(*event) + sizeof(*event_specid) + + sizeof(event_specid->digestSizes[0]) * +- event_specid->numberOfAlgorithms + sizeof(TCG_VENDOR_INFO)) { ++ le32toh(event_specid->numberOfAlgorithms) + sizeof(TCG_VENDOR_INFO)) { + LOG_ERR("insufficient size for VendorStuff"); + return false; + } + + TCG_VENDOR_INFO *vendor = (TCG_VENDOR_INFO*)((uintptr_t)event_specid->digestSizes + + sizeof(*event_specid->digestSizes) * +- event_specid->numberOfAlgorithms); ++ le32toh(event_specid->numberOfAlgorithms)); + /* size must be sufficient for vendorInfo */ + if (size < sizeof(*event) + sizeof(*event_specid) + + sizeof(event_specid->digestSizes[0]) * +- event_specid->numberOfAlgorithms + sizeof(*vendor) + ++ le32toh(event_specid->numberOfAlgorithms) + sizeof(*vendor) + + vendor->vendorInfoSize) { + LOG_ERR("insufficient size for VendorStuff data"); + return false; +@@ -614,7 +615,7 @@ bool parse_eventlog(tpm2_eventlog_context *ctx, BYTE const *eventlog, size_t siz + } + + TCG_EVENT *event = (TCG_EVENT*)eventlog; +- if (event->eventType == EV_NO_ACTION) { ++ if (le32toh(event->eventType) == EV_NO_ACTION) { + TCG_EVENT_HEADER2 *next; + bool ret = specid_event(event, size, &next); + if (!ret) { +diff --git a/lib/tpm2_eventlog_yaml.c b/lib/tpm2_eventlog_yaml.c +index c9c87efb..0f86aca0 100644 +--- a/lib/tpm2_eventlog_yaml.c ++++ b/lib/tpm2_eventlog_yaml.c +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + + #include + +@@ -46,7 +47,7 @@ + static void guid_unparse_lower(EFI_GUID guid, char guid_buf[37]) { + + snprintf(guid_buf, 37, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", +- guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], ++ le32toh(guid.Data1), le16toh(guid.Data2), le16toh(guid.Data3), guid.Data4[0], guid.Data4[1], + guid.Data4[2], guid.Data4[3], guid.Data4[4], + guid.Data4[5], guid.Data4[6], guid.Data4[7]); + } +@@ -140,9 +141,9 @@ void yaml_event2hdr(TCG_EVENT_HEADER2 const *eventhdr, size_t size) { + tpm2_tool_output(" PCRIndex: %d\n" + " EventType: %s\n" + " DigestCount: %d\n", +- eventhdr->PCRIndex, +- eventtype_to_string(eventhdr->EventType), +- eventhdr->DigestCount); ++ le32toh(eventhdr->PCRIndex), ++ eventtype_to_string(le32toh(eventhdr->EventType)), ++ le32toh(eventhdr->DigestCount)); + + return; + } +@@ -152,8 +153,8 @@ void yaml_sha1_log_eventhdr(TCG_EVENT const *eventhdr, size_t size) { + + tpm2_tool_output(" PCRIndex: %d\n" + " EventType: %s\n", +- eventhdr->pcrIndex, +- eventtype_to_string(eventhdr->eventType)); ++ le32toh(eventhdr->pcrIndex), ++ eventtype_to_string(le32toh(eventhdr->eventType))); + + return; + } +@@ -167,7 +168,7 @@ bool yaml_digest2(TCG_DIGEST2 const *digest, size_t size) { + + tpm2_tool_output(" - AlgorithmId: %s\n" + " Digest: \"%s\"\n", +- tpm2_alg_util_algtostr(digest->AlgorithmId, tpm2_alg_util_flags_hash), ++ tpm2_alg_util_algtostr(le16toh(digest->AlgorithmId), tpm2_alg_util_flags_hash), + hexstr); + + return true; +@@ -187,7 +188,7 @@ static char *yaml_utf16_to_str(UTF16_CHAR *data, size_t len) { + } + + for(size_t i = 0; i < len; ++i, tmp += ret) { +- ret = c16rtomb(tmp, data[i].c, &st); ++ ret = c16rtomb(tmp, le16toh(data[i].c), &st); + if (ret < 0) { + LOG_ERR("c16rtomb failed: %s", strerror(errno)); + free(mbstr); +@@ -196,21 +197,21 @@ static char *yaml_utf16_to_str(UTF16_CHAR *data, size_t len) { + } + return mbstr; + } +-#define VAR_DATA_HEX_SIZE(data) BYTES_TO_HEX_STRING_SIZE(data->VariableDataLength) ++#define VAR_DATA_HEX_SIZE(data) BYTES_TO_HEX_STRING_SIZE(le64toh(data->VariableDataLength)) + static bool yaml_uefi_var_data(UEFI_VARIABLE_DATA *data) { + +- if (data->VariableDataLength == 0) { ++ if (le64toh(data->VariableDataLength) == 0) { + return true; + } + + char *var_data = calloc (1, VAR_DATA_HEX_SIZE(data)); + uint8_t *variable_data = (uint8_t*)&data->UnicodeName[ +- data->UnicodeNameLength]; ++ le64toh(data->UnicodeNameLength)]; + if (var_data == NULL) { + LOG_ERR("failled to allocate data: %s\n", strerror(errno)); + return false; + } +- bytes_to_str(variable_data, data->VariableDataLength, var_data, ++ bytes_to_str(variable_data, le64toh(data->VariableDataLength), var_data, + VAR_DATA_HEX_SIZE(data)); + + tpm2_tool_output(" VariableData: \"%s\"\n", var_data); +@@ -238,7 +239,7 @@ static bool yaml_uefi_var_data(UEFI_VARIABLE_DATA *data) { + */ + + static bool yaml_uefi_post_code(const TCG_EVENT2* const event) { +- const size_t len = event->EventSize; ++ const size_t len = le32toh(event->EventSize); + + /* if length is 16, we treat it as EV_EFI_PLATFORM_FIRMWARE_BLOB */ + if (len == 16) { +@@ -260,7 +261,7 @@ static bool yaml_uefi_post_code(const TCG_EVENT2* const event) { + + static bool yaml_uefi_hcrtm(const TCG_EVENT2* const event) { + +- const size_t len = event->EventSize; ++ const size_t len = le32toh(event->EventSize); + + const char* const data = (const char *) event->Event; + tpm2_tool_output(" Event: |-\n" +@@ -494,28 +495,28 @@ static bool yaml_uefi_var(UEFI_VARIABLE_DATA *data, size_t size, UINT32 type, + " VariableName: %s\n" + " UnicodeNameLength: %"PRIu64"\n" + " VariableDataLength: %" PRIu64 "\n", +- uuidstr, data->UnicodeNameLength, +- data->VariableDataLength); ++ uuidstr, le64toh(data->UnicodeNameLength), ++ le64toh(data->VariableDataLength)); + + start += sizeof(*data); +- if (start + data->UnicodeNameLength*2 > size) { ++ if (start + le64toh(data->UnicodeNameLength)*2 > size) { + LOG_ERR("EventSize is too small\n"); + return false; + } + +- char *ret = yaml_utf16_to_str(data->UnicodeName, data->UnicodeNameLength); ++ char *ret = yaml_utf16_to_str(data->UnicodeName, le64toh(data->UnicodeNameLength)); + if (!ret) { + return false; + } + tpm2_tool_output(" UnicodeName: %s\n", ret); + +- start += data->UnicodeNameLength*2; ++ start += le64toh(data->UnicodeNameLength)*2; + /* Try to parse as much as we can without fail-stop. Bugs in firmware, shim, + * grub could produce inconsistent metadata. As long as it is not preventing + * us from parsing the data, we try to continue while giving a warning + * message. + */ +- if (start + data->VariableDataLength > size) { ++ if (start + le64toh(data->VariableDataLength) > size) { + LOG_ERR("EventSize is inconsistent with actual data\n"); + } + +@@ -533,7 +534,7 @@ static bool yaml_uefi_var(UEFI_VARIABLE_DATA *data, size_t size, UINT32 type, + free(ret); + tpm2_tool_output(" VariableData:\n"); + uint8_t *variable_data = (uint8_t *)&data->UnicodeName[ +- data->UnicodeNameLength]; ++ le64toh(data->UnicodeNameLength)]; + /* iterate through each EFI_SIGNATURE_LIST */ + while (start < size) { + EFI_SIGNATURE_LIST *slist = (EFI_SIGNATURE_LIST *)variable_data; +@@ -542,7 +543,7 @@ static bool yaml_uefi_var(UEFI_VARIABLE_DATA *data, size_t size, UINT32 type, + break; + } + +- if (slist->SignatureSize < 16) { ++ if (le32toh(slist->SignatureSize) < 16) { + LOG_ERR("SignatureSize is too small\n"); + break; + } +@@ -553,67 +554,67 @@ static bool yaml_uefi_var(UEFI_VARIABLE_DATA *data, size_t size, UINT32 type, + " SignatureHeaderSize: %" PRIu32 "\n" + " SignatureSize: %" PRIu32 "\n" + " Keys:\n", +- uuidstr, slist->SignatureListSize, +- slist->SignatureHeaderSize, +- slist->SignatureSize); ++ uuidstr, le32toh(slist->SignatureListSize), ++ le32toh(slist->SignatureHeaderSize), ++ le32toh(slist->SignatureSize)); + +- start += (sizeof(*slist) + slist->SignatureHeaderSize); +- if (start + slist->SignatureSize > size) { ++ start += (sizeof(*slist) + le32toh(slist->SignatureHeaderSize)); ++ if (start + le32toh(slist->SignatureSize) > size) { + LOG_ERR("EventSize is inconsistent with actual data\n"); + break; + } + +- int signature_size = slist->SignatureListSize - +- sizeof(*slist) - slist->SignatureHeaderSize; +- if (signature_size < 0 || signature_size % slist->SignatureSize != 0) { ++ int signature_size = le32toh(slist->SignatureListSize) - ++ sizeof(*slist) - le32toh(slist->SignatureHeaderSize); ++ if (signature_size < 0 || signature_size % le32toh(slist->SignatureSize) != 0) { + LOG_ERR("Malformed EFI_SIGNATURE_LIST\n"); + break; + } + + uint8_t *signature = (uint8_t *)slist + +- sizeof(*slist) + slist->SignatureHeaderSize; +- int signatures = signature_size / slist->SignatureSize; ++ sizeof(*slist) + le32toh(slist->SignatureHeaderSize); ++ int signatures = signature_size / le32toh(slist->SignatureSize); + /* iterate through each EFI_SIGNATURE on the list */ + int i; + for (i = 0; i < signatures; i++) { + EFI_SIGNATURE_DATA *s = (EFI_SIGNATURE_DATA *)signature; + char *sdata = calloc (1, +- BYTES_TO_HEX_STRING_SIZE(slist->SignatureSize - sizeof(EFI_GUID))); ++ BYTES_TO_HEX_STRING_SIZE(le32toh(slist->SignatureSize) - sizeof(EFI_GUID))); + if (sdata == NULL) { + LOG_ERR("Failled to allocate data: %s\n", strerror(errno)); + return false; + } +- bytes_to_str(s->SignatureData, slist->SignatureSize - sizeof(EFI_GUID), +- sdata, BYTES_TO_HEX_STRING_SIZE(slist->SignatureSize - sizeof(EFI_GUID))); ++ bytes_to_str(s->SignatureData, le32toh(slist->SignatureSize) - sizeof(EFI_GUID), ++ sdata, BYTES_TO_HEX_STRING_SIZE(le32toh(slist->SignatureSize) - sizeof(EFI_GUID))); + guid_unparse_lower(s->SignatureOwner, uuidstr); + tpm2_tool_output(" - SignatureOwner: %s\n" + " SignatureData: %s\n", + uuidstr, sdata); + free(sdata); + +- signature += slist->SignatureSize; +- start += slist->SignatureSize; ++ signature += le32toh(slist->SignatureSize); ++ start += le32toh(slist->SignatureSize); + if (start > size) { + LOG_ERR("Malformed EFI_SIGNATURE_DATA\n"); + break; + } + } +- variable_data += slist->SignatureListSize; ++ variable_data += le32toh(slist->SignatureListSize); + } + return true; + } else if ((strlen(ret) == NAME_SECUREBOOT_LEN && strncmp(ret, NAME_SECUREBOOT, NAME_SECUREBOOT_LEN) == 0)) { + free(ret); + tpm2_tool_output(" VariableData:\n" + " Enabled: "); +- if (data->VariableDataLength == 0) { ++ if (le64toh(data->VariableDataLength) == 0) { + tpm2_tool_output("'No'\n"); +- } else if (data->VariableDataLength > 1) { ++ } else if (le64toh(data->VariableDataLength) > 1) { + LOG_ERR("SecureBoot value length %" PRIu64 " is unexpectedly > 1\n", +- data->VariableDataLength); ++ le64toh(data->VariableDataLength)); + return false; + } else { + uint8_t *variable_data = (uint8_t *)&data->UnicodeName[ +- data->UnicodeNameLength]; ++ le64toh(data->UnicodeNameLength)]; + if (*variable_data == 0) { + tpm2_tool_output("'No'\n"); + } else { +@@ -628,15 +629,15 @@ static bool yaml_uefi_var(UEFI_VARIABLE_DATA *data, size_t size, UINT32 type, + free(ret); + tpm2_tool_output(" VariableData:\n" + " Enabled: "); +- if (data->VariableDataLength == 0) { ++ if (le64toh(data->VariableDataLength) == 0) { + tpm2_tool_output("'No'\n"); +- } else if (data->VariableDataLength > 1) { ++ } else if (le64toh(data->VariableDataLength) > 1) { + LOG_ERR("MokListTrusted value length %" PRIu64 " is unexpectedly > 1\n", +- data->VariableDataLength); ++ le64toh(data->VariableDataLength)); + return false; + } else { + uint8_t *variable_data = (uint8_t *)&data->UnicodeName[ +- data->UnicodeNameLength]; ++ le64toh(data->UnicodeNameLength)]; + if (*variable_data == 0) { + tpm2_tool_output("'No'\n"); + } else { +@@ -650,19 +651,19 @@ static bool yaml_uefi_var(UEFI_VARIABLE_DATA *data, size_t size, UINT32 type, + free(ret); + tpm2_tool_output(" VariableData:\n"); + EFI_SIGNATURE_DATA *s= (EFI_SIGNATURE_DATA *)&data->UnicodeName[ +- data->UnicodeNameLength]; +- if (data->VariableDataLength < sizeof(EFI_SIGNATURE_DATA)) { ++ le64toh(data->UnicodeNameLength)]; ++ if (le64toh(data->VariableDataLength) < sizeof(EFI_SIGNATURE_DATA)) { + LOG_ERR("VariableDataLength is too short for EFI_SIGNATURE_DATA"); + return false; + } + char *sdata = calloc (1, +- BYTES_TO_HEX_STRING_SIZE(data->VariableDataLength - sizeof(EFI_GUID))); ++ BYTES_TO_HEX_STRING_SIZE(le64toh(data->VariableDataLength) - sizeof(EFI_GUID))); + if (sdata == NULL) { + LOG_ERR("Failled to allocate data: %s\n", strerror(errno)); + return false; + } +- bytes_to_str(s->SignatureData, data->VariableDataLength - sizeof(EFI_GUID), +- sdata, BYTES_TO_HEX_STRING_SIZE(data->VariableDataLength - sizeof(EFI_GUID))); ++ bytes_to_str(s->SignatureData, le64toh(data->VariableDataLength) - sizeof(EFI_GUID), ++ sdata, BYTES_TO_HEX_STRING_SIZE(le64toh(data->VariableDataLength) - sizeof(EFI_GUID))); + guid_unparse_lower(s->SignatureOwner, uuidstr); + tpm2_tool_output(" - SignatureOwner: %s\n" + " SignatureData: %s\n", +@@ -674,25 +675,25 @@ static bool yaml_uefi_var(UEFI_VARIABLE_DATA *data, size_t size, UINT32 type, + tpm2_tool_output(" VariableData:\n"); + + UINT8 *description = (UINT8 *)&data->UnicodeName[ +- data->UnicodeNameLength]; ++ le64toh(data->UnicodeNameLength)]; + return yaml_split_print_string(" ", "String", +- description, data->VariableDataLength); ++ description, le64toh(data->VariableDataLength)); + } + } else if (type == EV_EFI_VARIABLE_BOOT || type == EV_EFI_VARIABLE_BOOT2) { + if ((strlen(ret) == NAME_BOOTORDER_LEN && strncmp(ret, NAME_BOOTORDER, NAME_BOOTORDER_LEN) == 0)) { + free(ret); + tpm2_tool_output(" VariableData:\n"); + +- if (data->VariableDataLength % 2 != 0) { ++ if (le64toh(data->VariableDataLength) % 2 != 0) { + LOG_ERR("BootOrder value length %" PRIu64 " is not divisible by 2\n", +- data->VariableDataLength); ++ le64toh(data->VariableDataLength)); + return false; + } + + uint8_t *variable_data = (uint8_t *)&data->UnicodeName[ +- data->UnicodeNameLength]; +- for (uint64_t i = 0; i < data->VariableDataLength / 2; i++) { +- tpm2_tool_output(" - Boot%04x\n", *((uint16_t*)variable_data + i)); ++ le64toh(data->UnicodeNameLength)]; ++ for (uint64_t i = 0; i < le64toh(data->VariableDataLength) / 2; i++) { ++ tpm2_tool_output(" - Boot%04x\n", le16toh(*((uint16_t*)variable_data + i))); + } + return true; + } +@@ -706,27 +707,27 @@ static bool yaml_uefi_var(UEFI_VARIABLE_DATA *data, size_t size, UINT32 type, + tpm2_tool_output(" VariableData:\n" + " Enabled: "); + EFI_LOAD_OPTION *loadopt = (EFI_LOAD_OPTION*)&data->UnicodeName[ +- data->UnicodeNameLength]; ++ le64toh(data->UnicodeNameLength)]; + +- if (loadopt->Attributes & 1) { ++ if (le32toh(loadopt->Attributes) & 1) { + tpm2_tool_output("'Yes'\n"); + } else { + tpm2_tool_output("'No'\n"); + } + + tpm2_tool_output(" FilePathListLength: %" PRIu16 "\n", +- loadopt->FilePathListLength); ++ le16toh(loadopt->FilePathListLength)); + + tpm2_tool_output(" Description: \""); + int i; +- for (i = 0; (wchar_t)loadopt->Description[i] != 0; i++) { +- char16_t c = (char16_t)loadopt->Description[i]; ++ for (i = 0; (wchar_t)le16toh(loadopt->Description[i]) != 0; i++) { ++ char16_t c = (char16_t)le16toh(loadopt->Description[i]); + tpm2_tool_output("%lc", c); + } + tpm2_tool_output("\"\n"); + + uint8_t *devpath = (uint8_t*)&loadopt->Description[++i]; +- size_t devpath_len = (data->VariableDataLength - ++ size_t devpath_len = (le64toh(data->VariableDataLength) - + sizeof(EFI_LOAD_OPTION) - sizeof(UINT16) * i) * 2 + 1; + + char *buf = calloc(1, devpath_len); +@@ -742,12 +743,12 @@ static bool yaml_uefi_var(UEFI_VARIABLE_DATA *data, size_t size, UINT32 type, + free(dp); + } else { + /* fallback to printing the raw bytes if devicepath cannot be parsed */ +- bytes_to_str(devpath, data->VariableDataLength - ++ bytes_to_str(devpath, le64toh(data->VariableDataLength) - + sizeof(EFI_LOAD_OPTION) - sizeof(UINT16) * i, buf, devpath_len); + tpm2_tool_output(" DevicePath: '%s'\n", buf); + } + #else +- bytes_to_str(devpath, data->VariableDataLength - ++ bytes_to_str(devpath, le64toh(data->VariableDataLength) - + sizeof(EFI_LOAD_OPTION) - sizeof(UINT16) * i, buf, devpath_len); + tpm2_tool_output(" DevicePath: '%s'\n", buf); + #endif +@@ -767,8 +768,8 @@ bool yaml_uefi_platfwblob(UEFI_PLATFORM_FIRMWARE_BLOB *data) { + tpm2_tool_output(" Event:\n" + " BlobBase: 0x%" PRIx64 "\n" + " BlobLength: 0x%" PRIx64 "\n", +- data->BlobBase, +- data->BlobLength); ++ le64toh(data->BlobBase), ++ le64toh(data->BlobLength)); + return true; + } + +@@ -793,8 +794,8 @@ bool yaml_uefi_platfwblob2(UEFI_PLATFORM_FIRMWARE_BLOB2 *data) { + blobdescsize, + 2*blobdescsize, + eventdesc, +- data2->BlobBase, +- data2->BlobLength); ++ le64toh(data2->BlobBase), ++ le64toh(data2->BlobLength)); + + free(eventdesc); + return true; +@@ -832,17 +833,17 @@ bool yaml_uefi_image_load(UEFI_IMAGE_LOAD_EVENT *data, size_t size) { + LOG_ERR("failed to allocate memory: %s\n", strerror(errno)); + return false; + } +- ++ + tpm2_tool_output(" Event:\n" + " ImageLocationInMemory: 0x%" PRIx64 "\n" + " ImageLengthInMemory: %" PRIu64 "\n" + " ImageLinkTimeAddress: 0x%" PRIx64 "\n" + " LengthOfDevicePath: %" PRIu64 "\n", +- data->ImageLocationInMemory, data->ImageLengthInMemory, +- data->ImageLinkTimeAddress, data->LengthOfDevicePath); ++ le64toh(data->ImageLocationInMemory), le64toh(data->ImageLengthInMemory), ++ le64toh(data->ImageLinkTimeAddress), le64toh(data->LengthOfDevicePath)); + + #ifdef HAVE_EFIVAR_EFIVAR_H +- char *dp = yaml_devicepath(data->DevicePath, data->LengthOfDevicePath); ++ char *dp = yaml_devicepath(data->DevicePath, le64toh(data->LengthOfDevicePath)); + if (dp) { + tpm2_tool_output(" DevicePath: '%s'\n", dp); + free(dp); +@@ -892,24 +893,24 @@ bool yaml_gpt(UEFI_GPT_DATA *data, size_t size, uint32_t eventlog_version) { + " NumberOfPartitions: %" PRIu64 "\n" + " Partitions:\n", + 8, (char*)&header->Signature, /* 8-char ASCII string */ +- header->Revision, +- header->HeaderSize, +- header->HeaderCRC32, +- header->MyLBA, +- header->AlternateLBA, +- header->FirstUsableLBA, +- header->LastUsableLBA, ++ le32toh(header->Revision), ++ le32toh(header->HeaderSize), ++ le32toh(header->HeaderCRC32), ++ le64toh(header->MyLBA), ++ le64toh(header->AlternateLBA), ++ le64toh(header->FirstUsableLBA), ++ le64toh(header->LastUsableLBA), + guid, +- header->PartitionEntryLBA, +- header->NumberOfPartitionEntries, +- header->SizeOfPartitionEntry, +- header->PartitionEntryArrayCRC32, +- data->NumberOfPartitions); ++ le64toh(header->PartitionEntryLBA), ++ le32toh(header->NumberOfPartitionEntries), ++ le32toh(header->SizeOfPartitionEntry), ++ le32toh(header->PartitionEntryArrayCRC32), ++ le64toh(data->NumberOfPartitions)); + + size -= (sizeof(data->UEFIPartitionHeader) + sizeof(data->NumberOfPartitions)); + + UINT64 i; +- for (i = 0; i < data->NumberOfPartitions; i++) { ++ for (i = 0; i < le64toh(data->NumberOfPartitions); i++) { + UEFI_PARTITION_ENTRY *partition = &data->Partitions[i]; + if (size < sizeof(*partition)) { + LOG_ERR("Cannot parse GPT partition entry: insufficient data (%zu)\n", size); +@@ -927,9 +928,9 @@ bool yaml_gpt(UEFI_GPT_DATA *data, size_t size, uint32_t eventlog_version) { + " Attributes: 0x%" PRIx64 "\n" + " PartitionName: \"%s\"\n", + guid, +- partition->StartingLBA, +- partition->EndingLBA, +- partition->Attributes, ++ le64toh(partition->StartingLBA), ++ le64toh(partition->EndingLBA), ++ le64toh(partition->Attributes), + part_name); + free(part_name); + size -= sizeof(*partition); +@@ -968,9 +969,9 @@ bool yaml_event2data(TCG_EVENT2 const *event, UINT32 type, uint32_t eventlog_ver + + char hexstr[EVENT_BUF_MAX] = { 0, }; + +- tpm2_tool_output(" EventSize: %" PRIu32 "\n", event->EventSize); ++ tpm2_tool_output(" EventSize: %" PRIu32 "\n", le32toh(event->EventSize)); + +- if (event->EventSize == 0) { ++ if (le32toh(event->EventSize) == 0) { + return true; + } + +@@ -979,8 +980,8 @@ bool yaml_event2data(TCG_EVENT2 const *event, UINT32 type, uint32_t eventlog_ver + case EV_EFI_VARIABLE_BOOT: + case EV_EFI_VARIABLE_BOOT2: + case EV_EFI_VARIABLE_AUTHORITY: +- return yaml_uefi_var((UEFI_VARIABLE_DATA*)event->Event, +- event->EventSize, type, eventlog_version); ++ return yaml_uefi_var((UEFI_VARIABLE_DATA*)event->Event, ++ le32toh(event->EventSize), type, eventlog_version); + case EV_POST_CODE: + return yaml_uefi_post_code(event); + case EV_S_CRTM_CONTENTS: +@@ -989,23 +990,23 @@ bool yaml_event2data(TCG_EVENT2 const *event, UINT32 type, uint32_t eventlog_ver + case EV_EFI_PLATFORM_FIRMWARE_BLOB2: + return yaml_uefi_platfwblob2((UEFI_PLATFORM_FIRMWARE_BLOB2*)event->Event); + case EV_EFI_ACTION: +- return yaml_uefi_action(event->Event, event->EventSize); ++ return yaml_uefi_action(event->Event, le32toh(event->EventSize)); + case EV_IPL: +- return yaml_ipl(event->Event, event->EventSize); ++ return yaml_ipl(event->Event, le32toh(event->EventSize)); + case EV_EFI_BOOT_SERVICES_APPLICATION: + case EV_EFI_BOOT_SERVICES_DRIVER: + case EV_EFI_RUNTIME_SERVICES_DRIVER: +- return yaml_uefi_image_load((UEFI_IMAGE_LOAD_EVENT*)event->Event, +- event->EventSize); ++ return yaml_uefi_image_load((UEFI_IMAGE_LOAD_EVENT*)event->Event, ++ le32toh(event->EventSize)); + case EV_EFI_GPT_EVENT: + return yaml_gpt((UEFI_GPT_DATA*)event->Event, +- event->EventSize, eventlog_version); ++ le32toh(event->EventSize), eventlog_version); + case EV_NO_ACTION: + return yaml_no_action((EV_NO_ACTION_STRUCT*)event->Event, event->EventSize, eventlog_version); + case EV_EFI_HCRTM_EVENT: + return yaml_uefi_hcrtm(event); + default: +- bytes_to_str(event->Event, event->EventSize, hexstr, sizeof(hexstr)); ++ bytes_to_str(event->Event, le32toh(event->EventSize), hexstr, sizeof(hexstr)); + tpm2_tool_output(" Event: \"%s\"\n", hexstr); + return true; + } +@@ -1071,9 +1072,9 @@ void yaml_eventhdr(TCG_EVENT const *event, size_t *count) { + " EventType: %s\n" + " Digest: \"%s\"\n" + " EventSize: %" PRIu32 "\n", +- (*count)++, event->pcrIndex, +- eventtype_to_string(event->eventType), digest_hex, +- event->eventDataSize); ++ (*count)++, le32toh(event->pcrIndex), ++ eventtype_to_string(le32toh(event->eventType)), digest_hex, ++ le32toh(event->eventDataSize)); + } + + void yaml_specid(TCG_SPECID_EVENT* specid) { +@@ -1092,10 +1093,10 @@ void yaml_specid(TCG_SPECID_EVENT* specid) { + " numberOfAlgorithms: %" PRIu32 "\n" + " Algorithms:\n", + sig_str, +- specid->platformClass, specid->specVersionMinor, ++ le32toh(specid->platformClass), specid->specVersionMinor, + specid->specVersionMajor, specid->specErrata, + specid->uintnSize, +- specid->numberOfAlgorithms); ++ le32toh(specid->numberOfAlgorithms)); + + } + void yaml_specid_algs(TCG_SPECID_ALG const *alg, size_t count) { +@@ -1105,9 +1106,9 @@ void yaml_specid_algs(TCG_SPECID_ALG const *alg, size_t count) { + " algorithmId: %s\n" + " digestSize: %" PRIu16 "\n", + i, +- tpm2_alg_util_algtostr(alg->algorithmId, ++ tpm2_alg_util_algtostr(le16toh(alg->algorithmId), + tpm2_alg_util_flags_hash), +- alg->digestSize); ++ le16toh(alg->digestSize)); + } + } + bool yaml_specid_vendor(TCG_VENDOR_INFO *vendor) { +@@ -1134,11 +1135,11 @@ bool yaml_specid_event(TCG_EVENT const *event, size_t *count) { + + TCG_SPECID_EVENT *specid = (TCG_SPECID_EVENT*)event->event; + TCG_SPECID_ALG *alg = (TCG_SPECID_ALG*)specid->digestSizes; +- TCG_VENDOR_INFO *vendor = (TCG_VENDOR_INFO*)(alg + specid->numberOfAlgorithms); ++ TCG_VENDOR_INFO *vendor = (TCG_VENDOR_INFO*)(alg + le32toh(specid->numberOfAlgorithms)); + + yaml_eventhdr(event, count); + yaml_specid(specid); +- yaml_specid_algs(alg, specid->numberOfAlgorithms); ++ yaml_specid_algs(alg, le32toh(specid->numberOfAlgorithms)); + return yaml_specid_vendor(vendor); + } + bool yaml_specid_callback(TCG_EVENT const *event, void *data) { +-- +2.54.0 + diff --git a/0002-man-tpm2_policysigned-add-parameter-raw-data.patch b/0002-man-tpm2_policysigned-add-parameter-raw-data.patch new file mode 100644 index 0000000..0230394 --- /dev/null +++ b/0002-man-tpm2_policysigned-add-parameter-raw-data.patch @@ -0,0 +1,32 @@ +From 17100afa995900d11c54446acc850a67c2eaaec2 Mon Sep 17 00:00:00 2001 +From: Juergen Repp +Date: Wed, 10 Sep 2025 11:44:02 +0200 +Subject: [PATCH 02/30] man tpm2_policysigned: add parameter --raw-data + +The parameter --raw-data which can be used to compute the hash +which must be signed was not documented. + +Signed-off-by: Juergen Repp +--- + man/tpm2_policysigned.1.md | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/man/tpm2_policysigned.1.md b/man/tpm2_policysigned.1.md +index 2f32d87d..498eb244 100644 +--- a/man/tpm2_policysigned.1.md ++++ b/man/tpm2_policysigned.1.md +@@ -58,6 +58,11 @@ The optional TPM2 parameters being cpHashA, nonceTPM, policyRef and expiration. + The command parameter hash (cpHash), enforcing the TPM command to be + authorized as well as its handle and parameter values. + ++* **\--raw-data**=_FILE_: ++ ++ The raw data, generated based on the selected parameters, serves as the input ++ for computing the hash that must be signed. ++ + * **\--ticket**=_FILE_: + + The ticket file to record the authorization ticket structure. +-- +2.54.0 + diff --git a/0003-tpm2_loadexternal-Add-option-rsa_exponent_zero.patch b/0003-tpm2_loadexternal-Add-option-rsa_exponent_zero.patch new file mode 100644 index 0000000..481afb7 --- /dev/null +++ b/0003-tpm2_loadexternal-Add-option-rsa_exponent_zero.patch @@ -0,0 +1,87 @@ +From e9e1260a4cb5e97650de77d3bf16445a55884504 Mon Sep 17 00:00:00 2001 +From: Juergen Repp +Date: Tue, 26 Aug 2025 10:01:49 +0200 +Subject: [PATCH 03/30] tpm2_loadexternal: Add option rsa_exponent_zero + +If TPM RSA keys are exported to PEM the exponent is set to 0x10001. +The option --rsa_exponent_zero is added to enable compatibility in the +computation of the key name if the TPM key is generated with +exponent zero. +Fixes: #3508 + +Signed-off-by: Juergen Repp +--- + man/tpm2_loadexternal.1.md | 5 +++++ + tools/tpm2_loadexternal.c | 17 ++++++++++++++--- + 2 files changed, 19 insertions(+), 3 deletions(-) + +diff --git a/man/tpm2_loadexternal.1.md b/man/tpm2_loadexternal.1.md +index 01d9bad3..7bf63957 100644 +--- a/man/tpm2_loadexternal.1.md ++++ b/man/tpm2_loadexternal.1.md +@@ -112,6 +112,11 @@ It also saves a context file for future interactions with the object. + It mirrors the -passin option of OSSL and is known to support the pass, + file, env, fd and plain password formats of openssl. + (see *man(1) openssl*) for more. ++ ++ * **-e**, **\--rsa_exponent_zero**: ++ Set the exponent of a public RSA key to zero, to enable compatibility ++ in the computation of the key name if the TPM key is generated with ++ exponent zero. + + ## References + +diff --git a/tools/tpm2_loadexternal.c b/tools/tpm2_loadexternal.c +index 7a7809f7..bddc1b73 100644 +--- a/tools/tpm2_loadexternal.c ++++ b/tools/tpm2_loadexternal.c +@@ -38,6 +38,8 @@ struct tpm_loadexternal_ctx { + TPM2B_SENSITIVE priv; /* Set the AUTH value for sensitive portion */ + TPM2B_PUBLIC pub; /* Load the users specified public object if specified via -u*/ + bool autoflush; /* Flush the object after creation of the ctx file */ ++ bool rsa_exponent_zero; /* Set rsa exponent to zero to support name computation for ++ tpm2 keys with exponent zero. */ + /* + * TSS Privkey related + */ +@@ -258,6 +260,11 @@ static tool_rc process_inputs(ESYS_CONTEXT *ectx) { + if (!result) { + return tool_rc_general_error; + } ++ if (ctx.pub.publicArea.type == TPM2_ALG_RSA && ctx.rsa_exponent_zero) { ++ ctx.pub.publicArea.parameters.rsaDetail.exponent = 0; ++ } else if (ctx.rsa_exponent_zero) { ++ LOG_WARN("Option --rsa_exponent_zero used for key which is no RSA key."); ++ } + } + } else { + LOG_ERR("Unkown internal state"); +@@ -410,7 +417,10 @@ static bool on_option(char key, char *value) { + break; + case 'R': + ctx.autoflush = true; +- break; ++ break; ++ case 'e': ++ ctx.rsa_exponent_zero = true; ++ break; + } + + return true; +@@ -431,10 +441,11 @@ static bool tpm2_tool_onstart(tpm2_options **opts) { + { "name", required_argument, 0, 'n'}, + { "passin", required_argument, 0, 0 }, + { "cphash", required_argument, 0, 1 }, +- { "autoflush", no_argument, 0, 'R' }, ++ { "autoflush", no_argument, 0, 'R'}, ++ { "rsa_exponent_zero", no_argument, 0, 'e'}, + }; + +- *opts = tpm2_options_new("C:u:r:c:a:p:L:g:G:n:R", ARRAY_LEN(topts), topts, ++ *opts = tpm2_options_new("C:u:r:c:a:p:L:g:G:n:Re", ARRAY_LEN(topts), topts, + on_option, 0, 0); + + return *opts != 0; +-- +2.54.0 + diff --git a/0004-tpm2_quote-checkquote-Fix-usage-of-little-endian-ser.patch b/0004-tpm2_quote-checkquote-Fix-usage-of-little-endian-ser.patch new file mode 100644 index 0000000..07ff4a4 --- /dev/null +++ b/0004-tpm2_quote-checkquote-Fix-usage-of-little-endian-ser.patch @@ -0,0 +1,539 @@ +From 2f31c8e4f1ded994823468ab66d1e019b8b89cae Mon Sep 17 00:00:00 2001 +From: Juergen Repp +Date: Wed, 30 Jul 2025 11:34:48 +0200 +Subject: [PATCH 04/30] tpm2_quote/checkquote: Fix usage of little endian + serialization. + +little endian is used for the pcr serialization (-F serialized) by +tpm2_pcrread, tpm2_quote, and tpm2_checkpublic. +After reading a pcr file the format was not adapted. Also on +big endian platforms still the little endian values are used. +To fix this problem new functions were added: +pcr_print_pcr_struct_le, tpm2_openssl_hash_pcr_banks_le. +But this functions are also used if the format value (-F value") +is used. But on big endian platforms the usage of these +_le functions will not work. +Now these _le functions are removed and the little endian format of +the input file is converted to host endian after the reading. +Also a new serialization format is added (-F marshaled) where +the tss2 marshaling unmarshaling functions are used. +Addresses: #3447 + +Signed-off-by: Juergen Repp +--- + lib/pcr.c | 134 +++++++++++++++++++++-------------- + lib/pcr.h | 15 +--- + lib/tpm2_convert.c | 2 + + lib/tpm2_convert.h | 1 + + lib/tpm2_openssl.h | 18 ----- + man/common/pcrs_format.md | 5 +- + man/tpm2_checkquote.1.md | 5 +- + tools/misc/tpm2_checkquote.c | 99 ++++++++++++++++++++++---- + tools/tpm2_pcrread.c | 4 ++ + tools/tpm2_quote.c | 6 ++ + 10 files changed, 192 insertions(+), 97 deletions(-) + +diff --git a/lib/pcr.c b/lib/pcr.c +index 9aa5fbf0..134dc1a3 100644 +--- a/lib/pcr.c ++++ b/lib/pcr.c +@@ -13,6 +13,8 @@ + #include "tpm2_tool.h" + #include "tpm2_alg_util.h" + #include "tpm2_util.h" ++#include "tss2_common.h" ++#include "tss2_mu.h" + + #define MAX(a,b) ((a>b)?a:b) + +@@ -232,58 +234,6 @@ static bool pcr_unset_pcr_sections(TPML_PCR_SELECTION *s) { + return true; + } + +-bool pcr_print_pcr_struct_le(TPML_PCR_SELECTION *pcr_select, tpm2_pcrs *pcrs) { +- +- UINT32 vi = 0, di = 0, i; +- bool result = true; +- +- tpm2_tool_output("pcrs:\n"); +- +- /* Loop through all PCR/hash banks */ +- for (i = 0; i < le32toh(pcr_select->count); i++) { +- const char *alg_name = tpm2_alg_util_algtostr( +- le16toh(pcr_select->pcrSelections[i].hash), tpm2_alg_util_flags_hash); +- +- tpm2_tool_output(" %s:\n", alg_name); +- +- /* Loop through all PCRs in this bank */ +- unsigned int pcr_id; +- for (pcr_id = 0; pcr_id < pcr_select->pcrSelections[i].sizeofSelect * 8u; +- pcr_id++) { +- if (!tpm2_util_is_pcr_select_bit_set(&pcr_select->pcrSelections[i], +- pcr_id)) { +- continue; // skip non-selected banks +- } +- if (vi >= le64toh(pcrs->count) || di >= le32toh(pcrs->pcr_values[vi].count)) { +- LOG_ERR("Something wrong, trying to print but nothing more"); +- return false; +- } +- +- /* Print out PCR ID */ +- tpm2_tool_output(" %-2d: 0x", pcr_id); +- +- /* Print out current PCR digest value */ +- TPM2B_DIGEST *b = &pcrs->pcr_values[vi].digests[di]; +- int k; +- for (k = 0; k < le16toh(b->size); k++) { +- tpm2_tool_output("%02X", b->buffer[k]); +- } +- tpm2_tool_output("\n"); +- +- if (++di < le32toh(pcrs->pcr_values[vi].count)) { +- continue; +- } +- +- di = 0; +- if (++vi < le64toh(pcrs->count)) { +- continue; +- } +- } +- } +- +- return result; +-} +- + bool pcr_fwrite_serialized(const TPML_PCR_SELECTION *pcr_select, + const tpm2_pcrs *ppcrs, FILE *output_file) { + +@@ -329,6 +279,86 @@ bool pcr_fwrite_serialized(const TPML_PCR_SELECTION *pcr_select, + return true; + } + ++bool pcr_fwrite_marshaled(const TPML_PCR_SELECTION *pcr_select, ++ const tpm2_pcrs *ppcrs, FILE *output_file) { ++ TSS2_RC rc; ++ size_t size; ++ uint8_t *buffer; ++ uint8_t num_buffer[sizeof(UINT32)]; ++ ++ size = 0; ++ rc = Tss2_MU_TPML_PCR_SELECTION_Marshal(pcr_select, NULL, ++ TPM2_MAX_COMMAND_SIZE, &size); ++ if (rc) { ++ return false; ++ } ++ buffer = malloc(size); ++ if (!buffer) { ++ return false; ++ } ++ size = 0; ++ rc = Tss2_MU_TPML_PCR_SELECTION_Marshal(pcr_select, buffer, ++ TPM2_MAX_COMMAND_SIZE, &size); ++ if (rc) { ++ free(buffer); ++ return false; ++ } ++ ++ size_t fwrite_len = fwrite(buffer, size, 1, ++ output_file); ++ if (fwrite_len != 1) { ++ LOG_ERR("write to output file failed: %s", strerror(errno)); ++ free(buffer); ++ return false; ++ } ++ free(buffer); ++ ++ // Marshal PCR digests to pcr outfile ++ ++ UINT32 count = ppcrs->count; ++ size = 0; ++ rc = Tss2_MU_UINT32_Marshal(count, &num_buffer[0], ++ sizeof(num_buffer), &size); ++ if (rc) { ++ return false; ++ } ++ fwrite_len = fwrite(&num_buffer[0], size, 1, ++ output_file); ++ if (fwrite_len != 1) { ++ LOG_ERR("write to output file failed: %s", strerror(errno)); ++ return false; ++ } ++ ++ for (size_t j = 0; j < ppcrs->count; j++) { ++ size = 0; ++ rc = Tss2_MU_TPML_DIGEST_Marshal(&ppcrs->pcr_values[j], NULL, ++ TPM2_MAX_COMMAND_SIZE, &size); ++ if (rc) { ++ return false; ++ } ++ buffer = malloc(size); ++ if (!buffer) { ++ return false; ++ } ++ size = 0; ++ rc = Tss2_MU_TPML_DIGEST_Marshal(&ppcrs->pcr_values[j], buffer, ++ TPM2_MAX_COMMAND_SIZE, &size); ++ if (rc) { ++ free(buffer); ++ return false; ++ } ++ fwrite_len = fwrite(buffer, size, 1, ++ output_file); ++ if (fwrite_len != 1) { ++ free(buffer); ++ LOG_ERR("write to output file failed: %s", strerror(errno)); ++ return false; ++ } ++ free(buffer); ++ } ++ return true; ++} ++ + bool pcr_fwrite_values(const TPML_PCR_SELECTION *pcr_select, + const tpm2_pcrs *pcrs, FILE *output_file) { + +diff --git a/lib/pcr.h b/lib/pcr.h +index c584c7d4..73463e18 100644 +--- a/lib/pcr.h ++++ b/lib/pcr.h +@@ -44,18 +44,6 @@ typedef struct tpm2_forwards { + */ + bool pcr_print_pcr_struct(TPML_PCR_SELECTION *pcrSelect, tpm2_pcrs *pcrs); + +-/** +- * Echo out all PCR banks according to g_pcrSelection & g_pcrs->. +- * Assume that data structures are all little endian. +- * @param pcrSelect +- * Description of which PCR registers are selected. +- * @param pcrs +- * Struct containing PCR digests. +- * @return +- * True on success, false otherwise. +- */ +-bool pcr_print_pcr_struct_le(TPML_PCR_SELECTION *pcrSelect, tpm2_pcrs *pcrs); +- + /** + * Set the PCR value into pcrId if string in arg is a valid PCR index. + * @param arg +@@ -100,6 +88,9 @@ bool pcr_fwrite_values(const TPML_PCR_SELECTION *pcr_select, + bool pcr_fwrite_serialized(const TPML_PCR_SELECTION *pcr_select, + const tpm2_pcrs *pcrs, FILE *output_file); + ++bool pcr_fwrite_marshaled(const TPML_PCR_SELECTION *pcr_select, ++ const tpm2_pcrs *ppcrs, FILE *output_file); ++ + bool pcr_parse_selections(const char *arg, TPML_PCR_SELECTION *pcr_selections, + tpm2_forwards *forwards); + +diff --git a/lib/tpm2_convert.c b/lib/tpm2_convert.c +index d3a30eb2..6c922993 100644 +--- a/lib/tpm2_convert.c ++++ b/lib/tpm2_convert.c +@@ -34,6 +34,8 @@ tpm2_convert_pcrs_output_fmt tpm2_convert_pcrs_output_fmt_from_optarg( + return pcrs_output_format_values; + } else if (strcasecmp(label, "serialized") == 0) { + return pcrs_output_format_serialized; ++ } else if (strcasecmp(label, "marshaled") == 0) { ++ return pcrs_output_format_marshaled; + } + + LOG_ERR("Invalid pcrs output format '%s' specified", label); +diff --git a/lib/tpm2_convert.h b/lib/tpm2_convert.h +index 87848ee3..fab9d396 100644 +--- a/lib/tpm2_convert.h ++++ b/lib/tpm2_convert.h +@@ -29,6 +29,7 @@ typedef enum tpm2_convert_pcrs_output_fmt tpm2_convert_pcrs_output_fmt; + enum tpm2_convert_pcrs_output_fmt { + pcrs_output_format_values, + pcrs_output_format_serialized, ++ pcrs_output_format_marshaled, + pcrs_output_format_err + }; + +diff --git a/lib/tpm2_openssl.h b/lib/tpm2_openssl.h +index be14602c..2964d3c3 100644 +--- a/lib/tpm2_openssl.h ++++ b/lib/tpm2_openssl.h +@@ -97,24 +97,6 @@ bool tpm2_openssl_hash_pcr_values(TPMI_ALG_HASH halg, TPML_DIGEST *digests, + bool tpm2_openssl_hash_pcr_banks(TPMI_ALG_HASH hashAlg, + TPML_PCR_SELECTION *pcr_select, tpm2_pcrs *pcrs, TPM2B_DIGEST *digest); + +-/* +- * Hash a list of PCR digests, supporting multiple banks. +- * The data in TPML_PCR_SELECTION and tpm2_pcrs is in little endian format. +- * +- * @param halg +- * The hashing algorithm to use. +- * @param pcr_select +- * The list that specifies which PCRs are selected. +- * @param pcrs +- * The list of PCR banks, each containing a list of PCR digests to hash. +- ^ * @param digest +- ^ * The result of hashing digests with halg. +- * @return +- * true on success, false on error. +- */ +-bool tpm2_openssl_hash_pcr_banks_le(TPMI_ALG_HASH hashAlg, +- TPML_PCR_SELECTION *pcr_select, tpm2_pcrs *pcrs, TPM2B_DIGEST *digest); +- + /** + * Extend a PCR with a new digest. + * @param halg +diff --git a/man/common/pcrs_format.md b/man/common/pcrs_format.md +index 58f40705..d1caac4b 100644 +--- a/man/common/pcrs_format.md ++++ b/man/common/pcrs_format.md +@@ -1,3 +1,6 @@ + * **-F**, **\--pcrs_format**=_FORMAT_: + +- Format selection for the binary blob in the PCR output file. 'values' will output a binary blob of the PCR values. 'serialized' will output a binary blob of the PCR values in the form of serialized data structure in little endian format. Optional. ++ Format selection for the binary blob in the PCR output file. 'values' will output a binary blob of the PCR values. 'serialized' will write or read a binary blob of the PCR values in the form of serialized data structure in little endian format. ++ 'marshaled' will will read or write a binary blob in the binary format used by the TSS2 marshaling and unmarshaling functions. If marshaled is used the data is serialized in the following order: TPML_PCR_SELECTION, ++ UINT32 (the number of following TPML_DIGEST elements) TPML_DIGEST [number of TPML_DIGEST elements]. ++ Optional. +diff --git a/man/tpm2_checkquote.1.md b/man/tpm2_checkquote.1.md +index 34ab8925..635f9555 100644 +--- a/man/tpm2_checkquote.1.md ++++ b/man/tpm2_checkquote.1.md +@@ -53,9 +53,10 @@ must be specified using the **-l** option to interpret the PCR data. + Qualification data for the quote. Can either be a hex string or path. + This is typically used to add a nonce against replay attacks. + +- * **-F**, **\--format**=_FORMAT_: ++[PCR output file format specifiers](common/pcrs_format.md) ++ If -l is provided 'normalized' will be used. Otherwise 'serialized' is ++ the default. + +- **DEPRECATED** and **IGNORED ** as it's superfluous. + + ## References + +diff --git a/tools/misc/tpm2_checkquote.c b/tools/misc/tpm2_checkquote.c +index 498dffbc..0f20ad96 100644 +--- a/tools/misc/tpm2_checkquote.c ++++ b/tools/misc/tpm2_checkquote.c +@@ -1,5 +1,6 @@ + /* SPDX-License-Identifier: BSD-3-Clause */ + ++#include + #include + #include + #include +@@ -19,6 +20,8 @@ + #include "tpm2_systemdeps.h" + #include "tpm2_tool.h" + #include "tpm2_eventlog.h" ++#include "tss2_common.h" ++#include "tss2_mu.h" + + typedef struct tpm2_verifysig_ctx tpm2_verifysig_ctx; + struct tpm2_verifysig_ctx { +@@ -46,12 +49,14 @@ struct tpm2_verifysig_ctx { + char *eventlog_path; + tpm2_loaded_object key_context_object; + const char *pcr_selection_string; ++ tpm2_convert_pcrs_output_fmt pcrs_format; + }; + + static tpm2_verifysig_ctx ctx = { + .halg = TPM2_ALG_SHA256, + .msg_hash = TPM2B_TYPE_INIT(TPM2B_DIGEST, buffer), + .pcr_hash = TPM2B_TYPE_INIT(TPM2B_DIGEST, buffer), ++ .pcrs_format = pcrs_output_format_serialized, + }; + + /** +@@ -340,37 +345,97 @@ static bool parse_selection_data_from_selection_string(FILE *pcr_input, + + static bool parse_selection_data_from_file(FILE *pcr_input, + TPML_PCR_SELECTION *pcr_select, tpm2_pcrs *pcrs) { ++ size_t j, i; + + // Import TPML_PCR_SELECTION structure to pcr outfile + if (fread(pcr_select, sizeof(TPML_PCR_SELECTION), 1, pcr_input) != 1) { + LOG_ERR("Failed to read PCR selection from file"); + return false; + } +- ++ pcr_select->count = le32toh(pcr_select->count); ++ for (i = 0; i < pcr_select->count; i++) { ++ pcr_select->pcrSelections[i].hash = le16toh(pcr_select->pcrSelections[i].hash); ++ } ++ + // Import PCR digests to pcr outfile + if (fread(&pcrs->count, sizeof(UINT32), 1, pcr_input) != 1) { + LOG_ERR("Failed to read PCR digests header from file"); + return false; + } + +- if (le64toh(pcrs->count) > ARRAY_LEN(pcrs->pcr_values)) { ++ pcrs->count = le64toh(pcrs->count); ++ ++ if (pcrs->count > ARRAY_LEN(pcrs->pcr_values)) { + LOG_ERR("Malformed PCR file, pcr count cannot be greater than %zu, got: %" PRIu64 " ", + ARRAY_LEN(pcrs->pcr_values), le64toh((UINT64)pcrs->count)); + return false; + } + +- size_t j; +- for (j = 0; j < le64toh(pcrs->count); j++) { ++ for (j = 0; j < pcrs->count; j++) { + if (fread(&pcrs->pcr_values[j], sizeof(TPML_DIGEST), 1, pcr_input) + != 1) { + LOG_ERR("Failed to read PCR digest from file"); + return false; + } ++ // Convert TPML_DIGEST from little endian to host endian. ++ pcrs->pcr_values[j].count = le32toh( pcrs->pcr_values[j].count); ++ for (i = 0; i < pcrs->pcr_values[j].count; i++) { ++ pcrs->pcr_values[j].digests[i].size = ++ le16toh(pcrs->pcr_values[j].digests[i].size); ++ } + } + + return true; + } + ++static bool parse_marshaled_selection_data(FILE *pcr_input, ++ TPML_PCR_SELECTION *pcr_select, tpm2_pcrs *pcrs, unsigned long fsize) { ++ size_t i; ++ uint8_t *buffer = NULL; ++ UINT16 size = fsize; ++ size_t offset = 0; ++ TSS2_RC rc; ++ UINT32 count; ++ ++ buffer = malloc(fsize); ++ ++ if (!file_read_bytes_from_file(pcr_input, buffer, &size, ctx.pcr_file_path)) { ++ LOG_ERR("Failed to read PCR selection from file"); ++ return false; ++ } ++ ++ rc = Tss2_MU_TPML_PCR_SELECTION_Unmarshal(buffer, size, &offset, pcr_select); ++ if (rc) { ++ LOG_ERR("Failed unmarshal PCR selection."); ++ goto error; ++ } ++ rc = Tss2_MU_UINT32_Unmarshal(buffer, size, &offset, &count); ++ if (rc) { ++ LOG_ERR("Failed unmarshal number of PCR digest lists."); ++ goto error; ++ } ++ pcrs->count = count; ++ if (pcrs->count > ARRAY_LEN(pcrs->pcr_values)) { ++ LOG_ERR("Malformed PCR file, pcr count cannot be greater than %zu, got: %" PRIu64 " ", ++ ARRAY_LEN(pcrs->pcr_values), le64toh((UINT64)pcrs->count)); ++ return false; ++ } ++ ++ for (i = 0; i < pcrs->count; i++) { ++ rc = Tss2_MU_TPML_DIGEST_Unmarshal(buffer, size, &offset, ++ &pcrs->pcr_values[i]); ++ if (rc) { ++ LOG_ERR("Failed unmarshal PCR digest list."); ++ goto error; ++ } ++ } ++ return true; ++ ++ error: ++ free(buffer); ++ return false; ++} ++ + static bool pcrs_from_file(const char *pcr_file_path, + TPML_PCR_SELECTION *pcr_select, tpm2_pcrs *pcrs) { + +@@ -394,9 +459,16 @@ static bool pcrs_from_file(const char *pcr_file_path, + } + + if (!ctx.pcr_selection_string) { +- result = parse_selection_data_from_file(pcr_input, pcr_select, pcrs); +- if (!result) { ++ if (ctx.pcrs_format == pcrs_output_format_marshaled) { ++ result = parse_marshaled_selection_data(pcr_input, pcr_select, pcrs, size); ++ if (!result) { + goto out; ++ } ++ } else { ++ result = parse_selection_data_from_file(pcr_input, pcr_select, pcrs); ++ if (!result) { ++ goto out; ++ } + } + } else { + result = parse_selection_data_from_selection_string(pcr_input, +@@ -519,20 +591,20 @@ static tool_rc init(void) { + goto err; + } + +- if (le32toh(pcr_select.count) > TPM2_NUM_PCR_BANKS) ++ if (pcr_select.count > TPM2_NUM_PCR_BANKS) + goto err; + + UINT32 i; +- for (i = 0; i < le32toh(pcr_select.count); i++) +- if (le16toh(pcr_select.pcrSelections[i].hash) == TPM2_ALG_ERROR) ++ for (i = 0; i < pcr_select.count; i++) ++ if (pcr_select.pcrSelections[i].hash == TPM2_ALG_ERROR) + goto err; + +- if (!tpm2_openssl_hash_pcr_banks_le(ctx.halg, &pcr_select, pcrs, ++ if (!tpm2_openssl_hash_pcr_banks(ctx.halg, &pcr_select, pcrs, + &ctx.pcr_hash)) { + LOG_ERR("Failed to hash PCR values related to quote!"); + goto err; + } +- if (!pcr_print_pcr_struct_le(&pcr_select, pcrs)) { ++ if (!pcr_print_pcr_struct(&pcr_select, pcrs)) { + LOG_ERR("Failed to print PCR values related to quote!"); + goto err; + } +@@ -668,7 +740,10 @@ static bool on_option(char key, char *value) { + } + break; + case 'F': +- LOG_WARN("DEPRECATED: Format ignored"); ++ ctx.pcrs_format = tpm2_convert_pcrs_output_fmt_from_optarg(value); ++ if (ctx.pcrs_format == pcrs_output_format_err) { ++ return false; ++ } + break; + case 'q': + ctx.extra_data.size = sizeof(ctx.extra_data.buffer); +diff --git a/tools/tpm2_pcrread.c b/tools/tpm2_pcrread.c +index 4248cbef..108586a6 100644 +--- a/tools/tpm2_pcrread.c ++++ b/tools/tpm2_pcrread.c +@@ -89,6 +89,10 @@ static tool_rc process_outputs(ESYS_CONTEXT *ectx) { + success = pcr_fwrite_serialized(&ctx.pcr_selections, &ctx.pcrs, + ctx.output_file); + } ++ if (ctx.format == pcrs_output_format_marshaled) { ++ success = pcr_fwrite_marshaled(&ctx.pcr_selections, &ctx.pcrs, ++ ctx.output_file); ++ } + } + return success ? tool_rc_success : tool_rc_general_error; + } +diff --git a/tools/tpm2_quote.c b/tools/tpm2_quote.c +index c0758ff7..a7a20e1c 100644 +--- a/tools/tpm2_quote.c ++++ b/tools/tpm2_quote.c +@@ -105,6 +105,12 @@ static tool_rc write_output_files(void) { + if (!result) { + is_file_op_success = result; + } ++ } else if (ctx.pcrs_format == pcrs_output_format_marshaled) { ++ result = pcr_fwrite_marshaled(&ctx.pcr_selections, &ctx.pcrs, ++ ctx.pcr_output); ++ if (!result) { ++ is_file_op_success = result; ++ } + } + } + +-- +2.54.0 + diff --git a/0005-tpm2_makecredential-Fix-usage-of-name-parameter.patch b/0005-tpm2_makecredential-Fix-usage-of-name-parameter.patch new file mode 100644 index 0000000..b9b0f81 --- /dev/null +++ b/0005-tpm2_makecredential-Fix-usage-of-name-parameter.patch @@ -0,0 +1,93 @@ +From 3e6cba1e5c2b8cdb03fd86e419081e4b9a5c17fa Mon Sep 17 00:00:00 2001 +From: Juergen Repp +Date: Wed, 22 Oct 2025 12:57:18 +0200 +Subject: [PATCH 05/30] tpm2_makecredential: Fix usage of name parameter. + +The man page for tpm2_makecredential states that the -name parameter +can be a file. However, a hex string of the name is expected. Similar +to the procedure used with tpm2_certifycreation, a file with the name +or a hex string can now be passed. +Fixes: #3274 + +Signed-off-by: Juergen Repp +--- + man/tpm2_makecredential.1.md | 5 +++-- + test/integration/tests/makecredential.sh | 5 +++++ + tools/tpm2_makecredential.c | 12 ++++++++---- + 3 files changed, 16 insertions(+), 6 deletions(-) + +diff --git a/man/tpm2_makecredential.1.md b/man/tpm2_makecredential.1.md +index e10d4a92..18896690 100644 +--- a/man/tpm2_makecredential.1.md ++++ b/man/tpm2_makecredential.1.md +@@ -55,9 +55,10 @@ TCTI option. + + The secret which will be protected by the key derived from the random seed. It can be specified as a file or passed from stdin. + +- * **-n**, **\--name**=_FILE_: ++ * **-n**, **\--name**=_FILE\_OR\_HEX_: + +- The name of the key for which certificate is to be created. ++ The name of the key for which certificate is to be created. Can either be ++ a path or hex string. + + * **-o**, **\--credential-blob**=_FILE_: + +diff --git a/test/integration/tests/makecredential.sh b/test/integration/tests/makecredential.sh +index 783422bf..1a26962a 100644 +--- a/test/integration/tests/makecredential.sh ++++ b/test/integration/tests/makecredential.sh +@@ -14,6 +14,7 @@ output_ek_pub=ek_pub.out + output_ak_pub=ak_pub.out + output_ak_pub_name=ak_name_pub.out + output_mkcredential=mkcredential.out ++output_mkcredential2=mkcredentiali2.out + + cleanup() { + rm -f $output_ek_pub $output_ak_pub $output_ak_pub_name \ +@@ -45,6 +46,10 @@ Loadkeyname=`cat $output_ak_pub_name | xxd -p -c $file_size` + tpm2 makecredential -Q -u $output_ek_pub -s $file_input_data -n $Loadkeyname \ + -o $output_mkcredential + ++# Check usage of name file instead of hex string ++tpm2 makecredential -Q -u $output_ek_pub -s $file_input_data -n $output_ak_pub_name \ ++-o $output_mkcredential2 ++ + # use no tpm backend + tpm2 makecredential -T none -Q -u $output_ek_pub -s $file_input_data \ + -n $Loadkeyname -o $output_mkcredential +diff --git a/tools/tpm2_makecredential.c b/tools/tpm2_makecredential.c +index ae2af991..f7ce24fa 100644 +--- a/tools/tpm2_makecredential.c ++++ b/tools/tpm2_makecredential.c +@@ -203,6 +203,7 @@ static tool_rc make_credential_and_save(ESYS_CONTEXT *ectx) { + + static bool on_option(char key, char *value) { + ++ tool_rc rc; + switch (key) { + case 'u': + if (ctx.flags.e) { +@@ -226,12 +227,15 @@ static bool on_option(char key, char *value) { + break; + case 'n': + ctx.object_name.size = BUFFER_SIZE(TPM2B_NAME, name); +- int q; +- if ((q = tpm2_util_hex_to_byte_structure(value, &ctx.object_name.size, +- ctx.object_name.name)) != 0) { +- LOG_ERR("FAILED: %d", q); ++ rc = tpm2_util_bin_from_hex_or_file(value, ++ &ctx.object_name.size, ctx.object_name.name) ? ++ tool_rc_success : tool_rc_general_error; ++ ++ if (rc != tool_rc_success) { ++ LOG_ERR("Could not load name data"); + return false; + } ++ + ctx.flags.n = 1; + break; + case 'o': +-- +2.54.0 + diff --git a/0006-tpm2_checkquote-Fix-indentation-in-parse_marshaled_s.patch b/0006-tpm2_checkquote-Fix-indentation-in-parse_marshaled_s.patch new file mode 100644 index 0000000..7f504f5 --- /dev/null +++ b/0006-tpm2_checkquote-Fix-indentation-in-parse_marshaled_s.patch @@ -0,0 +1,51 @@ +From ceadf429e5f774cd40f96894fea28d32ba18e384 Mon Sep 17 00:00:00 2001 +From: Dimitrios Siganos +Date: Sun, 9 Nov 2025 11:41:34 +0000 +Subject: [PATCH 06/30] tpm2_checkquote: Fix indentation in + parse_marshaled_selection_data + +Signed-off-by: Dimitrios Siganos +--- + tools/misc/tpm2_checkquote.c | 26 +++++++++++++------------- + 1 file changed, 13 insertions(+), 13 deletions(-) + +diff --git a/tools/misc/tpm2_checkquote.c b/tools/misc/tpm2_checkquote.c +index 0f20ad96..bdc54240 100644 +--- a/tools/misc/tpm2_checkquote.c ++++ b/tools/misc/tpm2_checkquote.c +@@ -421,19 +421,19 @@ static bool parse_marshaled_selection_data(FILE *pcr_input, + return false; + } + +- for (i = 0; i < pcrs->count; i++) { +- rc = Tss2_MU_TPML_DIGEST_Unmarshal(buffer, size, &offset, +- &pcrs->pcr_values[i]); +- if (rc) { +- LOG_ERR("Failed unmarshal PCR digest list."); +- goto error; +- } +- } +- return true; +- +- error: +- free(buffer); +- return false; ++ for (i = 0; i < pcrs->count; i++) { ++ rc = Tss2_MU_TPML_DIGEST_Unmarshal(buffer, size, &offset, ++ &pcrs->pcr_values[i]); ++ if (rc) { ++ LOG_ERR("Failed unmarshal PCR digest list."); ++ goto error; ++ } ++ } ++ return true; ++ ++error: ++ free(buffer); ++ return false; + } + + static bool pcrs_from_file(const char *pcr_file_path, +-- +2.54.0 + diff --git a/0007-tpm2_checkquote-Fix-missing-error-checking.patch b/0007-tpm2_checkquote-Fix-missing-error-checking.patch new file mode 100644 index 0000000..4263f98 --- /dev/null +++ b/0007-tpm2_checkquote-Fix-missing-error-checking.patch @@ -0,0 +1,30 @@ +From 3d303cc0eac7d0e57557924964ea795da2fb4c3e Mon Sep 17 00:00:00 2001 +From: Dimitrios Siganos +Date: Sun, 9 Nov 2025 11:50:52 +0000 +Subject: [PATCH 07/30] tpm2_checkquote: Fix missing error checking + +The return of malloc was not checked + +Signed-off-by: Dimitrios Siganos +--- + tools/misc/tpm2_checkquote.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/tools/misc/tpm2_checkquote.c b/tools/misc/tpm2_checkquote.c +index bdc54240..648a383f 100644 +--- a/tools/misc/tpm2_checkquote.c ++++ b/tools/misc/tpm2_checkquote.c +@@ -398,6 +398,10 @@ static bool parse_marshaled_selection_data(FILE *pcr_input, + UINT32 count; + + buffer = malloc(fsize); ++ if (!buffer) { ++ LOG_ERR("OOM"); ++ return false; ++ } + + if (!file_read_bytes_from_file(pcr_input, buffer, &size, ctx.pcr_file_path)) { + LOG_ERR("Failed to read PCR selection from file"); +-- +2.54.0 + diff --git a/0008-tpm2_checkquote-Fix-memory-leaks-malloced-buffer-not.patch b/0008-tpm2_checkquote-Fix-memory-leaks-malloced-buffer-not.patch new file mode 100644 index 0000000..6e12016 --- /dev/null +++ b/0008-tpm2_checkquote-Fix-memory-leaks-malloced-buffer-not.patch @@ -0,0 +1,47 @@ +From 9ae92d6db68bdf7f7691e84a6f95c3e064a2d96f Mon Sep 17 00:00:00 2001 +From: Dimitrios Siganos +Date: Sun, 9 Nov 2025 11:53:21 +0000 +Subject: [PATCH 08/30] tpm2_checkquote: Fix memory leaks, malloced buffer not + always freed + +A buffer allocated with malloc was not freed in all possible paths, +not even in the success path. + +Signed-off-by: Dimitrios Siganos +--- + tools/misc/tpm2_checkquote.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/tools/misc/tpm2_checkquote.c b/tools/misc/tpm2_checkquote.c +index 648a383f..259d9c62 100644 +--- a/tools/misc/tpm2_checkquote.c ++++ b/tools/misc/tpm2_checkquote.c +@@ -405,7 +405,7 @@ static bool parse_marshaled_selection_data(FILE *pcr_input, + + if (!file_read_bytes_from_file(pcr_input, buffer, &size, ctx.pcr_file_path)) { + LOG_ERR("Failed to read PCR selection from file"); +- return false; ++ goto error; + } + + rc = Tss2_MU_TPML_PCR_SELECTION_Unmarshal(buffer, size, &offset, pcr_select); +@@ -422,7 +422,7 @@ static bool parse_marshaled_selection_data(FILE *pcr_input, + if (pcrs->count > ARRAY_LEN(pcrs->pcr_values)) { + LOG_ERR("Malformed PCR file, pcr count cannot be greater than %zu, got: %" PRIu64 " ", + ARRAY_LEN(pcrs->pcr_values), le64toh((UINT64)pcrs->count)); +- return false; ++ goto error; + } + + for (i = 0; i < pcrs->count; i++) { +@@ -433,6 +433,7 @@ static bool parse_marshaled_selection_data(FILE *pcr_input, + goto error; + } + } ++ free(buffer); + return true; + + error: +-- +2.54.0 + diff --git a/0009-tpm2_makecredential-fix-wrong-tcg-ek-templates.patch b/0009-tpm2_makecredential-fix-wrong-tcg-ek-templates.patch new file mode 100644 index 0000000..20f522c --- /dev/null +++ b/0009-tpm2_makecredential-fix-wrong-tcg-ek-templates.patch @@ -0,0 +1,178 @@ +From ed18f6c4daee86f980e02564e6dd5ebc6e9daf25 Mon Sep 17 00:00:00 2001 +From: Silas Meier +Date: Fri, 14 Nov 2025 15:40:07 +0100 +Subject: [PATCH 09/30] tpm2_makecredential: fix wrong tcg ek templates + +The current implementation assumes low-range RSA and ECC keys. Therefore +if another key type is used, the default values are used and either the +tpm2_makecredential call or the tpm2_activatecredential call fails. + +This commit reduces the required template values to a minimum and +supports additional key types. + +Fixes: #3526 + +Signed-off-by: Silas Meier +--- + .../tests/activecredential_ecc_384.sh | 67 +++++++++++++++++++ + tools/tpm2_makecredential.c | 50 +++++++------- + 2 files changed, 93 insertions(+), 24 deletions(-) + create mode 100644 test/integration/tests/activecredential_ecc_384.sh + +diff --git a/test/integration/tests/activecredential_ecc_384.sh b/test/integration/tests/activecredential_ecc_384.sh +new file mode 100644 +index 00000000..dfeddad6 +--- /dev/null ++++ b/test/integration/tests/activecredential_ecc_384.sh +@@ -0,0 +1,67 @@ ++# SPDX-License-Identifier: BSD-3-Clause ++ ++source helpers.sh ++ ++cleanup() { ++ rm -f secret.data ek.pub ak.pub ak.name mkcred.out actcred.out ak.out \ ++ ak.ctx session.ctx policyA.sha384 policyC.sha384 ++ ++ # Evict persistent handles, we want them to always succeed and never trip ++ # the onerror trap. ++ tpm2 evictcontrol -Q -C o -c 0x81010009 2>/dev/null || true ++ ++ if [ "$1" != "no-shut-down" ]; then ++ shut_down ++ fi ++} ++trap cleanup EXIT ++ ++start_up ++ ++cleanup "no-shut-down" ++ ++echo 12345678 > secret.data ++ ++# Policies for high range EKs ++policy_a_hex="8bbf2266537c171cb56e403c4dc1d4b64f432611dc386e6f532050c3278c930e143e8bb1133824ccb431053871c6db53" ++echo -n "$policy_a_hex" | xxd -r -p > policyA.sha384 ++policy_c_hex="d6032ce61f2fb3c240eb3cf6a33237ef2b6a16f4293c22b455e261cffd217ad5b4947c2d73e63005eed2dc2b3593d165" ++echo -n "$policy_c_hex" | xxd -r -p > policyC.sha384 ++ ++tpm2 createek -Q -c 0x81010009 -G ecc384 -u ek.pub ++ ++tpm2 createak -C 0x81010009 -c ak.ctx -G rsa -g sha384 -s rsassa -u ak.pub \ ++-n ak.name -p akpass> ak.out ++ ++file_size=`ls -l ak.name | awk {'print $5'}` ++loaded_key_name=`cat ak.name | xxd -p -c $file_size` # Use -c in xxd so there is no line wrapping ++ ++tpm2 readpublic -c 0x81010009 -o ek.pem -f pem -Q ++ ++tpm2 makecredential -Q -u ek.pem -s secret.data -n $loaded_key_name \ ++-o mkcred.out -G ecc --tcti=none ++ ++# Test the secret data matches after credential activation process ++tpm2 startauthsession --policy-session -S session.ctx -g sha384 ++tpm2 policysecret -S session.ctx -c e ++tpm2 policyor -S session.ctx sha384:policyA.sha384,policyC.sha384 ++tpm2 activatecredential -Q -c ak.ctx -C 0x81010009 -i mkcred.out \ ++-o actcred.out -p akpass -P"session:session.ctx" ++tpm2 flushcontext session.ctx ++ ++diff actcred.out secret.data ++ ++# Capture the yaml output and verify that its the same as the name output ++loaded_key_name_yaml=`python << pyscript ++from __future__ import print_function ++ ++import yaml ++ ++with open('ak.out', 'r') as f: ++ doc = yaml.safe_load(f) ++ print(doc['loaded-key']['name']) ++pyscript` ++ ++test "$loaded_key_name_yaml" == "$loaded_key_name" ++ ++exit 0 +diff --git a/tools/tpm2_makecredential.c b/tools/tpm2_makecredential.c +index f7ce24fa..f91533e8 100644 +--- a/tools/tpm2_makecredential.c ++++ b/tools/tpm2_makecredential.c +@@ -268,50 +268,52 @@ static bool tpm2_tool_onstart(tpm2_options **opts) { + } + + static void set_default_TCG_EK_template(TPMI_ALG_PUBLIC alg) { ++ /* Values for RSA-2048 and ECC-256 keys. */ ++ TPMI_AES_KEY_BITS bits = 128; ++ TPMI_ALG_HASH name_alg = TPM2_ALG_SHA256; ++ ++ ctx.public.publicArea.objectAttributes = 0; + + switch (alg) { + case TPM2_ALG_RSA: ++ if (ctx.public.publicArea.parameters.rsaDetail.keyBits > 2048) { ++ /* Values for RSA-3072 and RSA-4096 keys. */ ++ bits = 256; ++ name_alg = TPM2_ALG_SHA384; ++ ctx.public.publicArea.objectAttributes = TPMA_OBJECT_USERWITHAUTH; ++ } + ctx.public.publicArea.parameters.rsaDetail.symmetric.algorithm = + TPM2_ALG_AES; +- ctx.public.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128; ++ ctx.public.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = bits; + ctx.public.publicArea.parameters.rsaDetail.symmetric.mode.aes = + TPM2_ALG_CFB; +- ctx.public.publicArea.parameters.rsaDetail.scheme.scheme = TPM2_ALG_NULL; +- ctx.public.publicArea.parameters.rsaDetail.keyBits = 2048; +- ctx.public.publicArea.parameters.rsaDetail.exponent = 0; +- ctx.public.publicArea.unique.rsa.size = 256; + break; + case TPM2_ALG_ECC: ++ if (ctx.public.publicArea.unique.ecc.x.size > 32) { ++ /* Values for ECC-384 keys. */ ++ bits = 256; ++ name_alg = TPM2_ALG_SHA384; ++ ctx.public.publicArea.objectAttributes = TPMA_OBJECT_USERWITHAUTH; ++ } ++ if (ctx.public.publicArea.unique.ecc.x.size > 48) { ++ /* Values for ECC-512 keys. */ ++ name_alg = TPM2_ALG_SHA512; ++ } ++ + ctx.public.publicArea.parameters.eccDetail.symmetric.algorithm = + TPM2_ALG_AES; +- ctx.public.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128; ++ ctx.public.publicArea.parameters.eccDetail.symmetric.keyBits.aes = bits; + ctx.public.publicArea.parameters.eccDetail.symmetric.mode.sym = + TPM2_ALG_CFB; +- ctx.public.publicArea.parameters.eccDetail.scheme.scheme = TPM2_ALG_NULL; +- ctx.public.publicArea.parameters.eccDetail.curveID = TPM2_ECC_NIST_P256; +- ctx.public.publicArea.parameters.eccDetail.kdf.scheme = TPM2_ALG_NULL; +- ctx.public.publicArea.unique.ecc.x.size = 32; +- ctx.public.publicArea.unique.ecc.y.size = 32; + break; + } + +- ctx.public.publicArea.objectAttributes = ++ ctx.public.publicArea.objectAttributes |= + TPMA_OBJECT_RESTRICTED | TPMA_OBJECT_ADMINWITHPOLICY + | TPMA_OBJECT_DECRYPT | TPMA_OBJECT_FIXEDTPM + | TPMA_OBJECT_FIXEDPARENT | TPMA_OBJECT_SENSITIVEDATAORIGIN; + +- static const TPM2B_DIGEST auth_policy = { +- .size = 32, +- .buffer = { +- 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xB3, 0xF8, 0x1A, 0x90, 0xCC, +- 0x8D, 0x46, 0xA5, 0xD7, 0x24, 0xFD, 0x52, 0xD7, 0x6E, 0x06, 0x52, +- 0x0B, 0x64, 0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14, 0x69, 0xAA +- } +- }; +- TPM2B_DIGEST *authp = &ctx.public.publicArea.authPolicy; +- *authp = auth_policy; +- +- ctx.public.publicArea.nameAlg = TPM2_ALG_SHA256; ++ ctx.public.publicArea.nameAlg = name_alg; + } + + static tool_rc process_input(tpm2_option_flags flags) { +-- +2.54.0 + diff --git a/0010-tools-Fix-several-clang-tidy-errors.patch b/0010-tools-Fix-several-clang-tidy-errors.patch new file mode 100644 index 0000000..29ed955 --- /dev/null +++ b/0010-tools-Fix-several-clang-tidy-errors.patch @@ -0,0 +1,383 @@ +From eed1dccb18cc92f08567988a9aaab4c3a279fceb Mon Sep 17 00:00:00 2001 +From: Juergen Repp +Date: Tue, 25 Nov 2025 11:18:43 +0100 +Subject: [PATCH 10/30] tools: Fix several clang-tidy errors + +Several clang-tidy error related to conversion from uint to int, +rewind, and realloc were fixed. + +Signed-off-by: Juergen Repp +--- + lib/files.c | 7 +++++-- + lib/pcr.c | 13 ++++++++----- + lib/tpm2_convert.c | 9 +++++---- + lib/tpm2_eventlog_yaml.c | 16 ++++++++-------- + lib/tpm2_identity_util.c | 8 +++++++- + lib/tpm2_openssl.c | 29 +++++++++++++++++++++++++---- + lib/tpm2_util.c | 2 +- + tools/fapi/tss2_gettpm2object.c | 2 +- + tools/fapi/tss2_quote.c | 2 +- + tools/fapi/tss2_template.c | 10 +++++----- + tools/misc/tpm2_print.c | 6 +++++- + 11 files changed, 71 insertions(+), 33 deletions(-) + +diff --git a/lib/files.c b/lib/files.c +index dd93d7d3..1d6e579c 100644 +--- a/lib/files.c ++++ b/lib/files.c +@@ -327,7 +327,10 @@ static bool load_tpm_context_file(FILE *fstream, TPMS_CONTEXT *context) { + LOG_WARN("The loaded tpm context does not appear to be in the proper " + "format, assuming old format, this will be converted on the " + "next save."); +- rewind(fstream); ++ if (fseek(fstream, 0, SEEK_SET) != 0) { ++ LOG_ERR("Could not rewind stream: %s", strerror(errno)); ++ return false; ++ } + result = files_read_bytes(fstream, (UINT8 *) context, sizeof(*context)); + if (!result) { + LOG_ERR("Could not load tpm context file"); +@@ -400,7 +403,7 @@ static bool check_magic(FILE *fstream, bool seek_reset) { + bool match = magic == MAGIC; + + if (seek_reset) { +- int rc = fseek(fstream, -sizeof(magic), SEEK_CUR); ++ int rc = fseek(fstream, -(long)sizeof(magic), SEEK_CUR); + if (rc != 0) { + LOG_ERR("fseek failed: %s", strerror(errno)); + return false; +diff --git a/lib/pcr.c b/lib/pcr.c +index 134dc1a3..bcbd306c 100644 +--- a/lib/pcr.c ++++ b/lib/pcr.c +@@ -74,22 +74,25 @@ static bool pcr_parse_list(const char *str, size_t len, + current_string = str; + str = memchr(current_string, ',', len); + if (str) { +- current_length = str - current_string; ++ ptrdiff_t diff = str - current_string; ++ if (diff > INT_MAX) ++ return false; ++ current_length = (int)diff; + str++; + len -= current_length + 1; + } else { +- current_length = len; ++ current_length = (int)len; + len = 0; + } + + dgst = memchr(current_string, '=', current_length); + if (dgst && ((str == NULL) || (str && dgst < str))) { +- pcr_len = dgst - current_string; ++ pcr_len = (int)(dgst - current_string); + dgst++; + if (str) { +- dgst_len = str - dgst - 1; ++ dgst_len = (int)(str - dgst - 1); + } else { +- dgst_len = current_length - pcr_len - 1; ++ dgst_len = (int)(current_length - pcr_len - 1); + } + } else { + dgst = NULL; +diff --git a/lib/tpm2_convert.c b/lib/tpm2_convert.c +index 6c922993..6c975dd7 100644 +--- a/lib/tpm2_convert.c ++++ b/lib/tpm2_convert.c +@@ -806,7 +806,7 @@ bool tpm2_base64_encode(BYTE *buffer, size_t buffer_length, char *base64) { + EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new(); + EVP_EncodeInit(ctx); + +- int rc = EVP_EncodeUpdate(ctx, out, &outl, buffer, buffer_length); ++ int rc = EVP_EncodeUpdate(ctx, out, &outl, buffer, (int)buffer_length); + if(rc < 0) { + LOG_ERR("EVP_DecodeUpdate failed with %d\n", rc); + EVP_ENCODE_CTX_free(ctx); +@@ -824,19 +824,20 @@ bool tpm2_base64_encode(BYTE *buffer, size_t buffer_length, char *base64) { + + bool tpm2_base64_decode(char *base64, BYTE *buffer, size_t *buffer_length) { + +- bool is_base64_bufferlen_valid = strlen(base64) > 1024 ? false : true; ++ size_t len = strlen(base64); ++ bool is_base64_bufferlen_valid = len > 1024 ? false : true; + if (!is_base64_bufferlen_valid) { + return false; + } + + unsigned char base64u[1024]; +- memcpy(base64u, base64, strlen(base64)); ++ memcpy(base64u, base64, len); + + EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new(); + EVP_DecodeInit(ctx); + unsigned char out[1024]; + int outl; +- int rc = EVP_DecodeUpdate(ctx, out, &outl, base64u, strlen(base64)); ++ int rc = EVP_DecodeUpdate(ctx, out, &outl, base64u, (int)len); + if(rc < 0) { + LOG_ERR("EVP_DecodeUpdate failed with %d\n", rc); + EVP_ENCODE_CTX_free(ctx); +diff --git a/lib/tpm2_eventlog_yaml.c b/lib/tpm2_eventlog_yaml.c +index 0f86aca0..c6efdcd7 100644 +--- a/lib/tpm2_eventlog_yaml.c ++++ b/lib/tpm2_eventlog_yaml.c +@@ -188,7 +188,7 @@ static char *yaml_utf16_to_str(UTF16_CHAR *data, size_t len) { + } + + for(size_t i = 0; i < len; ++i, tmp += ret) { +- ret = c16rtomb(tmp, le16toh(data[i].c), &st); ++ ret = c16rtomb(tmp, (int)le16toh(data[i].c), &st); + if (ret < 0) { + LOG_ERR("c16rtomb failed: %s", strerror(errno)); + free(mbstr); +@@ -277,7 +277,7 @@ static bool yaml_uefi_hcrtm(const TCG_EVENT2* const event) { + #ifdef HAVE_EFIVAR_EFIVAR_H + char *yaml_devicepath(BYTE* dp, UINT64 dp_len) { + int ret; +- ret = efidp_format_device_path(NULL, 0, (const_efidp)dp, dp_len); ++ ret = (int)efidp_format_device_path(NULL, 0, (const_efidp)dp, (ssize_t)dp_len); + if (ret < 0) { + LOG_ERR("failed to allocate memory: %s\n", strerror(errno)); + return NULL; +@@ -293,8 +293,8 @@ char *yaml_devicepath(BYTE* dp, UINT64 dp_len) { + } + + /* The void* cast is a hack to support efivar versions < 38 */ +- ret = efidp_format_device_path((void *)text_path, +- text_path_len, (const_efidp)dp, dp_len); ++ ret = (int)efidp_format_device_path((void *)text_path, ++ text_path_len, (const_efidp)dp, (ssize_t)dp_len); + if (ret < 0) { + free(text_path); + LOG_ERR("cannot parse device path\n"); +@@ -349,7 +349,7 @@ char **yaml_split_escape_string(UINT8 const *description, size_t size) + len = size - i; + } + +- tmp = realloc(lines, sizeof(char *) * (nlines + 2)); ++ tmp = (char **)realloc(lines, sizeof(char *) * (nlines + 2)); + if (!tmp) { + LOG_ERR("failed to allocate memory for description lines: %s\n", + strerror(errno)); +@@ -420,7 +420,7 @@ char **yaml_split_escape_string(UINT8 const *description, size_t size) + } + + if (escape == NULL) { +- lines[nlines][k++] = description[j]; ++ lines[nlines][k++] = (char)description[j]; + } else { + while (*escape) { + lines[nlines][k++] = *escape; +@@ -438,7 +438,7 @@ char **yaml_split_escape_string(UINT8 const *description, size_t size) + for (i = 0; lines != NULL && lines[i] != NULL; i++) { + free(lines[i]); + } +- free(lines); ++ free((char **)lines); + return NULL; + } + +@@ -573,7 +573,7 @@ static bool yaml_uefi_var(UEFI_VARIABLE_DATA *data, size_t size, UINT32 type, + + uint8_t *signature = (uint8_t *)slist + + sizeof(*slist) + le32toh(slist->SignatureHeaderSize); +- int signatures = signature_size / le32toh(slist->SignatureSize); ++ int signatures = (int)(signature_size / le32toh(slist->SignatureSize)); + /* iterate through each EFI_SIGNATURE on the list */ + int i; + for (i = 0; i < signatures; i++) { +diff --git a/lib/tpm2_identity_util.c b/lib/tpm2_identity_util.c +index fbf1e938..03cfd269 100644 +--- a/lib/tpm2_identity_util.c ++++ b/lib/tpm2_identity_util.c +@@ -1,6 +1,7 @@ + /* SPDX-License-Identifier: BSD-3-Clause */ + + #include ++#include + #include + #include + +@@ -249,8 +250,13 @@ static bool aes_encrypt_buffers(TPMT_SYM_DEF_OBJECT *sym, + if (!b) { + continue; + } ++ size_t diff = total_len - offset; ++ if (diff > INT_MAX || l > INT_MAX) { ++ LOG_ERR("Size can't be converted to int"); ++ return false; ++ } + +- int output_len = total_len - offset; ++ int output_len = (int)diff; + + rc = EVP_EncryptUpdate(ctx, &cipher_text->buffer[offset], &output_len, + b, l); +diff --git a/lib/tpm2_openssl.c b/lib/tpm2_openssl.c +index d2f07a7c..669475c2 100644 +--- a/lib/tpm2_openssl.c ++++ b/lib/tpm2_openssl.c +@@ -466,7 +466,15 @@ static bool do_file(const char *path, char **pass) { + static bool do_fd(const char *passin, char **pass) { + + char *end_ptr = NULL; +- int fd = strtoul(passin, &end_ptr, 0); ++ unsigned long tmp = strtoul(passin, &end_ptr, 0); ++ ++ if (tmp > INT_MAX) { ++ LOG_ERR("Invalid fd (out of range), got: \"%s\"", passin); ++ return false; ++ } ++ ++ int fd = (int)tmp; ++ + if (passin[0] != '\0' && end_ptr[0] != '\0') { + LOG_ERR("Invalid fd, got: \"%s\"", passin); + return false; +@@ -793,6 +801,7 @@ static bool load_public_ECC_from_key(EVP_PKEY *key, TPM2B_PUBLIC *pub) { + goto out; + } + pp->curveID = curve_id; ++ unsigned int tmp; + + /* + * Copy the X and Y coordinate data into the ECC unique field, +@@ -813,13 +822,25 @@ static bool load_public_ECC_from_key(EVP_PKEY *key, TPM2B_PUBLIC *pub) { + goto out; + } + +- X->size = BN_bn2binpad(x, X->buffer, keysize); ++ tmp = BN_bn2binpad(x, X->buffer, (int)keysize); ++ ++ if (tmp > INT_MAX) { ++ LOG_ERR("Invalid result of BN_bn2binpad"); ++ return false; ++ } ++ X->size = tmp; + if (X->size != keysize) { + LOG_ERR("Error converting X point BN to binary"); + goto out; + } + +- Y->size = BN_bn2binpad(y, Y->buffer, keysize); ++ tmp = BN_bn2binpad(y, Y->buffer, (int)keysize); ++ if (tmp > INT_MAX) { ++ LOG_ERR("Invalid result of BN_bn2binpad"); ++ return false; ++ } ++ ++ Y->size = (int)tmp; + if (Y->size != keysize) { + LOG_ERR("Error converting Y point BN to binary"); + goto out; +@@ -1073,7 +1094,7 @@ static bool load_private_ECC_from_key(EVP_PKEY *key, TPM2B_SENSITIVE *priv) { + goto out; + } + +- p->size = BN_bn2binpad(b, p->buffer, priv_bytes); ++ p->size = BN_bn2binpad(b, p->buffer, (int)priv_bytes); + if (p->size != priv_bytes) { + goto out; + } +diff --git a/lib/tpm2_util.c b/lib/tpm2_util.c +index c489430d..17a9ca9f 100644 +--- a/lib/tpm2_util.c ++++ b/lib/tpm2_util.c +@@ -188,7 +188,7 @@ int tpm2_util_hex_to_byte_structure(const char *input_string, UINT16 *byte_lengt + int i = 0; + if (input_string == NULL || byte_length == NULL || byte_buffer == NULL) + return -1; +- str_length = strlen(input_string); ++ str_length = (int)strlen(input_string); + if (str_length % 2) + return -2; + for (i = 0; i < str_length; i++) { +diff --git a/tools/fapi/tss2_gettpm2object.c b/tools/fapi/tss2_gettpm2object.c +index 07e816b7..48e80b7f 100644 +--- a/tools/fapi/tss2_gettpm2object.c ++++ b/tools/fapi/tss2_gettpm2object.c +@@ -81,7 +81,7 @@ static int tss2_tool_onrun (FAPI_CONTEXT *fctx) { + return 1; + } + +- if (strcmp(ctx.data, "-")) { ++ if (strcmp(ctx.data, "-") != 0) { + if (!ctx.overwrite) { + FILE *fp = fopen(ctx.data, "rb"); + if (fp) { +diff --git a/tools/fapi/tss2_quote.c b/tools/fapi/tss2_quote.c +index 250d4b2d..7ed8b591 100644 +--- a/tools/fapi/tss2_quote.c ++++ b/tools/fapi/tss2_quote.c +@@ -141,7 +141,7 @@ static int tss2_tool_onrun (FAPI_CONTEXT *fctx) { + + /* Read qualifyingData file */ + TSS2_RC r; +- uint8_t *qualifyingData = NULL; ++ void *qualifyingData = NULL; + size_t qualifyingDataSize = 0; + if (ctx.qualifyingData) { + r = open_read_and_close (ctx.qualifyingData, +diff --git a/tools/fapi/tss2_template.c b/tools/fapi/tss2_template.c +index ecec0f04..a01a1f5b 100644 +--- a/tools/fapi/tss2_template.c ++++ b/tools/fapi/tss2_template.c +@@ -142,7 +142,7 @@ static tpm2_option_code tss2_handle_options ( + case '?': + goto out; + default: +- if (!(*tool_opts)->callbacks.on_opt(c, optarg)) ++ if (!(*tool_opts)->callbacks.on_opt((char)c, optarg)) + goto out; + } + } +@@ -293,8 +293,8 @@ TSS2_RC sign_callback( + int cpy_size = 0; + if (strlen(publicKeyHint) > 0) { + const char* tmp = "the key corresponding to the key hint \"%s\" and"; +- cpy_size = strlen(tmp) - 2 /* remove replaced %s */ + +- strlen(publicKeyHint); ++ cpy_size = (int)(strlen(tmp) - 2 /* remove replaced %s */ + ++ strlen(publicKeyHint)); + rc = snprintf(publicKeyHintStr, cpy_size+1 /* add \0 */, tmp, + publicKeyHint); + if (rc != cpy_size){ +@@ -311,8 +311,8 @@ TSS2_RC sign_callback( + "PEM-encoded public key\n"); + return TSS2_FAPI_RC_GENERAL_FAILURE; + } +- cpy_size = strlen(tmp) - 2 /* remove replaced %s */ + +- strlen(publicKeyHintTmp); ++ cpy_size = (int)(strlen(tmp) - 2 /* remove replaced %s */ + ++ strlen(publicKeyHintTmp)); + rc = snprintf(publicKeyHintStr, cpy_size+1 /* add \0 */, tmp, + publicKeyHintTmp); + if (rc != cpy_size){ +diff --git a/tools/misc/tpm2_print.c b/tools/misc/tpm2_print.c +index faa5ac3a..3e5f5b68 100644 +--- a/tools/misc/tpm2_print.c ++++ b/tools/misc/tpm2_print.c +@@ -4,6 +4,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -304,7 +305,10 @@ static bool print_TPMS_CONTEXT(FILE *fstream) { + if (!result) { + LOG_WARN("The loaded tpm context does not appear to be in the proper " + "format, assuming old format."); +- rewind(fstream); ++ if (fseek(fstream, 0, SEEK_SET) != 0) { ++ LOG_ERR("Could not rewind stream: %s", strerror(errno)); ++ return false; ++ } + result = files_read_bytes(fstream, (UINT8 *) &context, sizeof(context)); + if (!result) { + LOG_ERR("Could not load tpm context file"); +-- +2.54.0 + diff --git a/0011-tools-Fix-wrong-type-usage-of-enums.patch b/0011-tools-Fix-wrong-type-usage-of-enums.patch new file mode 100644 index 0000000..5ad9b0a --- /dev/null +++ b/0011-tools-Fix-wrong-type-usage-of-enums.patch @@ -0,0 +1,166 @@ +From 1b9fb8b89fd4cc39aed530d9504b39cf8270f866 Mon Sep 17 00:00:00 2001 +From: Juergen Repp +Date: Thu, 27 Nov 2025 19:48:08 +0100 +Subject: [PATCH 11/30] tools: Fix wrong type usage of enums. + +enums used as bitmask are now declared as _enum +the bitmaks is now declared with typedef unsigned int + +Signed-off-by: Juergen Repp +--- + lib/tpm2_alg_util.h | 34 ++++++++++++++------------- + lib/tpm2_openssl.h | 12 ++++++---- + lib/tpm2_util.h | 6 +++-- + tools/misc/tpm2_certifyX509certutil.c | 2 +- + tools/tpm2_getekcertificate.c | 24 ++++++++++--------- + 5 files changed, 43 insertions(+), 35 deletions(-) + +diff --git a/lib/tpm2_alg_util.h b/lib/tpm2_alg_util.h +index ae5e3fbb..68ee1fdd 100644 +--- a/lib/tpm2_alg_util.h ++++ b/lib/tpm2_alg_util.h +@@ -9,24 +9,26 @@ + + #include "tool_rc.h" + +-typedef enum tpm2_alg_util_flags tpm2_alg_util_flags; +-enum tpm2_alg_util_flags { +- tpm2_alg_util_flags_none = 0, +- tpm2_alg_util_flags_hash = 1 << 0, +- tpm2_alg_util_flags_keyedhash = 1 << 1, +- tpm2_alg_util_flags_symmetric = 1 << 2, +- tpm2_alg_util_flags_asymmetric = 1 << 3, +- tpm2_alg_util_flags_kdf = 1 << 4, +- tpm2_alg_util_flags_mgf = 1 << 5, +- tpm2_alg_util_flags_sig = 1 << 6, +- tpm2_alg_util_flags_mode = 1 << 7, +- tpm2_alg_util_flags_base = 1 << 8, +- tpm2_alg_util_flags_misc = 1 << 9, +- tpm2_alg_util_flags_enc_scheme = 1 << 10, +- tpm2_alg_util_flags_rsa_scheme = 1 << 11, +- tpm2_alg_util_flags_any = ~0 ++typedef enum tpm2_alg_util_flags_enum tpm2_alg_util_flags_enum; ++enum tpm2_alg_util_flags_enum { ++ tpm2_alg_util_flags_none = 0, ++ tpm2_alg_util_flags_hash = 1 << 0, ++ tpm2_alg_util_flags_keyedhash = 1 << 1, ++ tpm2_alg_util_flags_symmetric = 1 << 2, ++ tpm2_alg_util_flags_asymmetric = 1 << 3, ++ tpm2_alg_util_flags_kdf = 1 << 4, ++ tpm2_alg_util_flags_mgf = 1 << 5, ++ tpm2_alg_util_flags_sig = 1 << 6, ++ tpm2_alg_util_flags_mode = 1 << 7, ++ tpm2_alg_util_flags_base = 1 << 8, ++ tpm2_alg_util_flags_misc = 1 << 9, ++ tpm2_alg_util_flags_enc_scheme = 1 << 10, ++ tpm2_alg_util_flags_rsa_scheme = 1 << 11, ++ tpm2_alg_util_flags_any = ~0 + }; + ++typedef unsigned int tpm2_alg_util_flags; ++ + /** + * Convert a "nice-name" string to an algorithm id. + * @param name +diff --git a/lib/tpm2_openssl.h b/lib/tpm2_openssl.h +index 2964d3c3..3b44f089 100644 +--- a/lib/tpm2_openssl.h ++++ b/lib/tpm2_openssl.h +@@ -113,13 +113,15 @@ bool tpm2_openssl_hash_pcr_banks(TPMI_ALG_HASH hashAlg, + bool tpm2_openssl_pcr_extend(TPMI_ALG_HASH halg, BYTE *pcr, + const BYTE *data, UINT16 length); + +-typedef enum tpm2_openssl_load_rc tpm2_openssl_load_rc; +-enum tpm2_openssl_load_rc { +- lprc_error = 0, /* an error has occurred */ +- lprc_private = 1 << 0, /* successfully loaded a private portion of object */ +- lprc_public = 1 << 1, /* successfully loaded a public portion of object */ ++typedef enum tpm2_openssl_load_rc_enum tpm2_openssl_load_rc_enum; ++enum tpm2_openssl_load_rc_eum { ++ lprc_error = 0, /* an error has occurred */ ++ lprc_private = 1 << 0, /* successfully loaded a private portion of object */ ++ lprc_public = 1 << 1, /* successfully loaded a public portion of object */ + }; + ++typedef unsigned int tpm2_openssl_load_rc; ++ + /** + * Helper routine for gathering if the loading status included a public + * portion of an object. +diff --git a/lib/tpm2_util.h b/lib/tpm2_util.h +index 04f96ad1..4d730364 100644 +--- a/lib/tpm2_util.h ++++ b/lib/tpm2_util.h +@@ -435,8 +435,8 @@ char *tpm2_util_getenv(const char *name); + + bool tpm2_util_env_yes(const char *name); + +-typedef enum tpm2_handle_flags tpm2_handle_flags; +-enum tpm2_handle_flags { ++typedef enum tpm2_handle_flags_enum tpm2_handle_flags_enum; ++enum tpm2_handle_flags_enum { + TPM2_HANDLE_FLAGS_NONE = 0, + TPM2_HANDLE_FLAGS_O = 1 << 0, + TPM2_HANDLE_FLAGS_P = 1 << 1, +@@ -453,6 +453,8 @@ enum tpm2_handle_flags { + TPM2_HANDLE_ALL_W_PCR = 0x17F, + }; + ++typedef unsigned int tpm2_handle_flags; ++ + /** + * Converts an option from the command line into a valid TPM handle, checking + * for errors and if the tool supports it based on flags settings. +diff --git a/tools/misc/tpm2_certifyX509certutil.c b/tools/misc/tpm2_certifyX509certutil.c +index 5eea08e8..85925572 100644 +--- a/tools/misc/tpm2_certifyX509certutil.c ++++ b/tools/misc/tpm2_certifyX509certutil.c +@@ -192,7 +192,7 @@ static tool_rc generate_partial_X509() { + BIO *cert_out = BIO_new_file(ctx.out_path, "wb"); + if (!cert_out) { + LOG_ERR("Can not create file %s", ctx.out_path); +- return -1; ++ return tool_rc_general_error; + } + + X509_EXTENSION *extv3 = NULL; +diff --git a/tools/tpm2_getekcertificate.c b/tools/tpm2_getekcertificate.c +index cd2d86a3..961021cf 100644 +--- a/tools/tpm2_getekcertificate.c ++++ b/tools/tpm2_getekcertificate.c +@@ -69,19 +69,21 @@ enum pubkey_enc_mode { + * + */ + +-typedef enum ek_nv_index ek_nv_index; +-enum ek_nv_index { +- RSA_EK_CERT_NV_INDEX = 0x01C00002, +- ECC_EK_CERT_NV_INDEX = 0x01C0000A, +- RSA_2048_EK_CERT_NV_INDEX = 0x01C00012, +- RSA_3072_EK_CERT_NV_INDEX = 0x01C0001C, +- RSA_4096_EK_CERT_NV_INDEX = 0x01C0001E, +- ECC_NIST_P256_EK_CERT_NV_INDEX = 0x01C00014, +- ECC_NIST_P384_EK_CERT_NV_INDEX = 0x01C00016, +- ECC_NIST_P521_EK_CERT_NV_INDEX = 0x01C00018, +- ECC_SM2_P256_EK_CERT_NV_INDEX = 0x01C0001A, ++typedef enum ek_nv_index_enum ek_nv_index_enum; ++enum ek_nv_index_enum { ++ RSA_EK_CERT_NV_INDEX = 0x01C00002, ++ ECC_EK_CERT_NV_INDEX = 0x01C0000A, ++ RSA_2048_EK_CERT_NV_INDEX = 0x01C00012, ++ RSA_3072_EK_CERT_NV_INDEX = 0x01C0001C, ++ RSA_4096_EK_CERT_NV_INDEX = 0x01C0001E, ++ ECC_NIST_P256_EK_CERT_NV_INDEX = 0x01C00014, ++ ECC_NIST_P384_EK_CERT_NV_INDEX = 0x01C00016, ++ ECC_NIST_P521_EK_CERT_NV_INDEX = 0x01C00018, ++ ECC_SM2_P256_EK_CERT_NV_INDEX = 0x01C0001A, + }; + ++typedef unsigned int ek_nv_index; ++ + #define EK_SERVER_INTEL "https://ekop.intel.com/ekcertservice/" + #define EK_SERVER_AMD "https://ftpm.amd.com/pki/aia/" + +-- +2.54.0 + diff --git a/0012-tpm2_eventlog-Extend-pcrs-using-event-EV_IPL.patch b/0012-tpm2_eventlog-Extend-pcrs-using-event-EV_IPL.patch new file mode 100644 index 0000000..bd8dc85 --- /dev/null +++ b/0012-tpm2_eventlog-Extend-pcrs-using-event-EV_IPL.patch @@ -0,0 +1,33 @@ +From 9bf3121b9567ef031608f72b042411e0cfb39ac4 Mon Sep 17 00:00:00 2001 +From: Juergen Repp +Date: Wed, 17 Dec 2025 11:44:23 +0100 +Subject: [PATCH 12/30] tpm2_eventlog: Extend pcrs using event EV_IPL. + +For TPM2 PCR measurements made by systemd for each PE section of the UKI that is +defined by the UAPI.5 UKI Specification PCR 11 is used. +Addresses: #3511 + +Signed-off-by: Juergen Repp +--- + lib/tpm2_eventlog.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/lib/tpm2_eventlog.c b/lib/tpm2_eventlog.c +index 40148f8b..e6c94347 100644 +--- a/lib/tpm2_eventlog.c ++++ b/lib/tpm2_eventlog.c +@@ -427,6 +427,11 @@ size_t i; + } + break; + ++ /* For TPM2 PCR measurements made by systemd for each PE section of the UKI that is ++ defined by the UAPI.5 UKI Specification PCR 11 is used. */ ++ case 11: ++ return true; ++ + default: + LOG_WARN("Event %zu is unexectedly not extending either PCR 8, 9, 12 or 14", eventnum - 1); + return false; +-- +2.54.0 + diff --git a/0013-tpm2_nvcertify-Set-size-if-the-size-parameter-is-not.patch b/0013-tpm2_nvcertify-Set-size-if-the-size-parameter-is-not.patch new file mode 100644 index 0000000..157a34b --- /dev/null +++ b/0013-tpm2_nvcertify-Set-size-if-the-size-parameter-is-not.patch @@ -0,0 +1,49 @@ +From e37509f142eac4049f6f29473df3032e90ab9cc7 Mon Sep 17 00:00:00 2001 +From: Juergen Repp +Date: Sat, 27 Dec 2025 21:03:04 +0100 +Subject: [PATCH 13/30] tpm2_nvcertify: Set size if the --size parameter is not + specified. + +The man page states: If not specified, the size of the data +as reported by the public portion of the index will be used. +But 0 was used if --size was not specified. +Also an error in the man page is fixed. +Fixes: #3538 + +Signed-off-by: Juergen Repp +--- + man/tpm2_nvcertify.1.md | 2 +- + tools/tpm2_nvcertify.c | 4 ++++ + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/man/tpm2_nvcertify.1.md b/man/tpm2_nvcertify.1.md +index 08662d0e..d1a7aa6c 100644 +--- a/man/tpm2_nvcertify.1.md ++++ b/man/tpm2_nvcertify.1.md +@@ -68,7 +68,7 @@ These options control the certification: + + * **--attestation**=_FILE_: + +- The attestation data of the type TPM2_CREATION_INFO signed with signing key. ++ The attestation data of the type TPMS_ATTEST signed with signing key. + + * **\--cphash**=_FILE_ + +diff --git a/tools/tpm2_nvcertify.c b/tools/tpm2_nvcertify.c +index 72e60470..53729bb8 100644 +--- a/tools/tpm2_nvcertify.c ++++ b/tools/tpm2_nvcertify.c +@@ -387,6 +387,10 @@ static tool_rc check_options(ESYS_CONTEXT *ectx, tpm2_option_flags flags) { + goto is_input_options_args_valid_out; + } + ++ if (ctx.size == 0) { ++ ctx.size = nv_public->nvPublic.dataSize; ++ } ++ + if (ctx.offset + ctx.size > nv_public->nvPublic.dataSize) { + LOG_ERR("Size to read at offset is bigger than nv index size"); + rc = tool_rc_option_error; +-- +2.54.0 + diff --git a/0014-tpm2_create-Fix-creation-of-ctx-file-if-TPM2_CreateL.patch b/0014-tpm2_create-Fix-creation-of-ctx-file-if-TPM2_CreateL.patch new file mode 100644 index 0000000..6b19160 --- /dev/null +++ b/0014-tpm2_create-Fix-creation-of-ctx-file-if-TPM2_CreateL.patch @@ -0,0 +1,265 @@ +From 79544d38a3c1500bbe7aa6b94f1cf25627720f0e Mon Sep 17 00:00:00 2001 +From: Juergen Repp +Date: Wed, 14 Jan 2026 08:34:13 +0100 +Subject: [PATCH 14/30] tpm2_create: Fix creation of ctx file if + TPM2_CreateLoaded is not available. + +If the TPM2 command CreateLoaded is not available the creation of a ctx +file with tpm2_create was not possible. Now a TPM2_Create and TPM2_Load is +used to create a ctx file if CreateLoaded is not available. + +Signed-off-by: Juergen Repp +--- + lib/tpm2.c | 67 ++++++++++++++++++++++++++++++--------- + lib/tpm2.h | 9 ++++-- + lib/tpm2_capability.c | 73 +++++++++++++++++++++++++++++++++---------- + tools/tpm2_create.c | 14 ++------- + 4 files changed, 117 insertions(+), 46 deletions(-) + +diff --git a/lib/tpm2.c b/lib/tpm2.c +index 6098f7a9..ca1227a5 100644 +--- a/lib/tpm2.c ++++ b/lib/tpm2.c +@@ -2060,15 +2060,32 @@ tpm2_create_skip_esapi_call: + tool_rc tpm2_create_loaded(ESYS_CONTEXT *esys_context, + tpm2_loaded_object *parent_obj, + const TPM2B_SENSITIVE_CREATE *in_sensitive, +- const TPM2B_TEMPLATE *in_public, ESYS_TR *object_handle, ++ const TPM2B_PUBLIC *in_public, ESYS_TR *object_handle, + TPM2B_PRIVATE **out_private, TPM2B_PUBLIC **out_public, + TPM2B_DIGEST *cp_hash, TPM2B_DIGEST *rp_hash, + TPMI_ALG_HASH parameter_hash_algorithm, ESYS_TR shandle2, + ESYS_TR shandle3) { +- ++ TPM2B_TEMPLATE template = { .size = 0 }; ++ + TSS2_SYS_CONTEXT *sys_context = NULL; ++ bool create_loaded_exists; + tool_rc rc = tool_rc_success; +- if (cp_hash->size || rp_hash->size) { ++ size_t offset = 0; ++ ++ tool_rc tmp_rc = tpm2_mu_tpmt_public_marshal( ++ &in_public->publicArea, &template.buffer[0], ++ sizeof(TPMT_PUBLIC), &offset); ++ if (tmp_rc != tool_rc_success) { ++ return tmp_rc; ++ } ++ ++ template.size = offset; ++ ++ rc = tpm2_check_cc(esys_context, TPM2_CC_CreateLoaded, &create_loaded_exists); ++ if (rc != tool_rc_success) { ++ return rc; ++ } ++ if ((cp_hash->size || rp_hash->size) && create_loaded_exists) { + rc = tpm2_getsapicontext(esys_context, &sys_context); + + if(rc != tool_rc_success) { +@@ -2077,9 +2094,9 @@ tool_rc tpm2_create_loaded(ESYS_CONTEXT *esys_context, + } + } + +- if (cp_hash->size) { ++ if (cp_hash->size && create_loaded_exists) { + TSS2_RC rval = Tss2_Sys_CreateLoaded_Prepare(sys_context, +- parent_obj->handle, in_sensitive, in_public); ++ parent_obj->handle, in_sensitive, &template); + if (rval != TPM2_RC_SUCCESS) { + LOG_PERR(Tss2_Sys_CreateLoaded_Prepare, rval); + return tool_rc_general_error; +@@ -2115,19 +2132,39 @@ tpm2_createloaded_free_name1: + return rc; + } + +- TSS2_RC rval = Esys_CreateLoaded(esys_context, parent_obj->tr_handle, +- shandle1, shandle2, shandle3, in_sensitive, in_public, ++ if (create_loaded_exists) { ++ TSS2_RC rval = Esys_CreateLoaded(esys_context, parent_obj->tr_handle, ++ shandle1, shandle2, shandle3, in_sensitive, &template, + object_handle, out_private, out_public); +- if (rval != TSS2_RC_SUCCESS) { +- LOG_PERR(Esys_CreateLoaded, rval); +- return tool_rc_from_tpm(rval); +- } ++ if (rval != TSS2_RC_SUCCESS) { ++ LOG_PERR(Esys_CreateLoaded, rval); ++ return tool_rc_from_tpm(rval); ++ } + +- if (rp_hash->size) { +- rc = tpm2_sapi_getrphash(sys_context, rval, rp_hash, +- parameter_hash_algorithm); ++ if (rp_hash->size) { ++ rc = tpm2_sapi_getrphash(sys_context, rval, rp_hash, ++ parameter_hash_algorithm); ++ } ++ } else { ++ TPML_PCR_SELECTION creationPCR = { ++ .count = 0, ++ }; ++ ++ TSS2_RC rval = Esys_Create(esys_context, parent_obj->tr_handle, ++ shandle1, shandle2, shandle3, in_sensitive, in_public, ++ NULL, &creationPCR, out_private, out_public, NULL, NULL, NULL); ++ if (rval != TSS2_RC_SUCCESS) { ++ LOG_PERR(Esys_CreateLoaded, rval); ++ return tool_rc_from_tpm(rval); ++ } ++ rval = Esys_Load(esys_context, parent_obj->tr_handle, ++ shandle1, shandle2, shandle3, *out_private, ++ *out_public, object_handle); ++ if (rval != TPM2_RC_SUCCESS) { ++ LOG_PERR(Esys_Load, rval); ++ return tool_rc_from_tpm(rval); ++ } + } +- + tpm2_createloaded_skip_esapi_call: + return rc; + } +diff --git a/lib/tpm2.h b/lib/tpm2.h +index 67940c0e..0dc826a4 100644 +--- a/lib/tpm2.h ++++ b/lib/tpm2.h +@@ -64,8 +64,11 @@ tool_rc tpm2_sess_get_noncetpm(ESYS_CONTEXT *esys_context, + ESYS_TR session_handle, TPM2B_NONCE **nonce_tpm); + + tool_rc tpm2_policy_restart(ESYS_CONTEXT *esys_context, ESYS_TR session_handle, +- ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, +- TPM2B_DIGEST *cp_hash, TPMI_ALG_HASH parameter_hash_algorithm); ++ ESYS_TR shandle1, ESYS_TR shandle2, ++ ESYS_TR shandle3, TPM2B_DIGEST *cp_hash, ++ TPMI_ALG_HASH parameter_hash_algorithm); ++ ++tool_rc tpm2_check_cc(ESYS_CONTEXT *ectx, uint32_t cc, bool *exists); + + tool_rc tpm2_get_capability(ESYS_CONTEXT *esys_context, ESYS_TR shandle1, + ESYS_TR shandle2, ESYS_TR shandle3, TPM2_CAP capability, +@@ -212,7 +215,7 @@ tool_rc tpm2_create(ESYS_CONTEXT *esys_context, tpm2_loaded_object *parent_obj, + tool_rc tpm2_create_loaded(ESYS_CONTEXT *esys_context, + tpm2_loaded_object *parent_obj, + const TPM2B_SENSITIVE_CREATE *in_sensitive, +- const TPM2B_TEMPLATE *in_public, ESYS_TR *object_handle, ++ const TPM2B_PUBLIC *in_public, ESYS_TR *object_handle, + TPM2B_PRIVATE **out_private, TPM2B_PUBLIC **out_public, + TPM2B_DIGEST *cp_hash, TPM2B_DIGEST *rp_hash, + TPMI_ALG_HASH parameter_hash_algorithm, ESYS_TR shandle2, +diff --git a/lib/tpm2_capability.c b/lib/tpm2_capability.c +index dca35e5a..9ca73c01 100644 +--- a/lib/tpm2_capability.c ++++ b/lib/tpm2_capability.c +@@ -5,27 +5,68 @@ + #include + + #include "log.h" ++#include "tool_rc.h" + #include "tpm2.h" + #include "tpm2_capability.h" + +-#define APPEND_CAPABILITY_INFORMATION(capability, field, subfield, max_count) \ +- if (fetched_data->data.capability.count > max_count - property_count) { \ +- fetched_data->data.capability.count = max_count - property_count; \ +- } \ +-\ +- memmove(&(*capability_data)->data.capability.field[property_count], \ +- fetched_data->data.capability.field, \ +- fetched_data->data.capability.count * sizeof(fetched_data->data.capability.field[0])); \ +- property_count += fetched_data->data.capability.count; \ +-\ +- (*capability_data)->data.capability.count = property_count; \ +-\ +- if (more_data && property_count < count && fetched_data->data.capability.count) { \ +- property = (*capability_data)->data.capability.field[property_count - 1]subfield + 1; \ +- } else { \ +- more_data = false; \ ++#define APPEND_CAPABILITY_INFORMATION(capability, field, subfield, max_count) \ ++ if (fetched_data->data.capability.count > max_count - property_count) { \ ++ fetched_data->data.capability.count = max_count - property_count; \ ++ } \ ++ \ ++ memmove(&(*capability_data)->data.capability.field[property_count], \ ++ fetched_data->data.capability.field, \ ++ fetched_data->data.capability.count * \ ++ sizeof(fetched_data->data.capability.field[0])); \ ++ property_count += fetched_data->data.capability.count; \ ++ \ ++ (*capability_data)->data.capability.count = property_count; \ ++ \ ++ if (more_data && property_count < count && \ ++ fetched_data->data.capability.count) { \ ++ property = (*capability_data) \ ++ ->data.capability.field[property_count - 1] subfield + \ ++ 1; \ ++ } else { \ ++ more_data = false; \ ++ } ++ ++tool_rc tpm2_check_cc(ESYS_CONTEXT *ectx, uint32_t cc, bool *exists) { ++ TPMI_YES_NO more_data = TPM2_NO; ++ TPMS_CAPABILITY_DATA *cap = NULL; ++ uint32_t count = 1; ++ ++ ++ TSS2_RC rc = Esys_GetCapability( ++ ectx, ++ ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ++ TPM2_CAP_COMMANDS, ++ cc, ++ count, ++ &more_data, ++ &cap ++ ); ++ ++ if (rc != TSS2_RC_SUCCESS) { ++ LOG_ERR("Esys_GetCapability(TPM2_CAP_COMMANDS, property=0x%08" PRIX32 " failed: 0x%x", ++ cc, rc); ++ return tool_rc_general_error; + } + ++ const TPML_CCA *cmds = &cap->data.command; ++ ++ if (cmds->count == 1 && (cmds->commandAttributes[0] & 0xffff) == cc) { ++ Esys_Free(cap); ++ *exists = true; ++ return tool_rc_success; ++ } ++ ++ Esys_Free(cap); ++ *exists = false; ++ return tool_rc_success; ++} ++ ++ + tool_rc tpm2_capability_get_ex(ESYS_CONTEXT *ectx, TPM2_CAP capability, + UINT32 property, UINT32 count, bool ignore_more_data, + TPMS_CAPABILITY_DATA **capability_data) { +diff --git a/tools/tpm2_create.c b/tools/tpm2_create.c +index cf25137a..39bbc935 100644 +--- a/tools/tpm2_create.c ++++ b/tools/tpm2_create.c +@@ -134,19 +134,9 @@ static tool_rc create(ESYS_CONTEXT *ectx) { + + /* TPM2_CC_CreateLoaded */ + if (ctx.is_createloaded) { +- size_t offset = 0; +- TPM2B_TEMPLATE template = { .size = 0 }; +- tool_rc tmp_rc = tpm2_mu_tpmt_public_marshal( +- &ctx.object.in_public.publicArea, &template.buffer[0], +- sizeof(TPMT_PUBLIC), &offset); +- if (tmp_rc != tool_rc_success) { +- return tmp_rc; +- } +- +- template.size = offset; +- ++ tool_rc tmp_rc; + tmp_rc = tpm2_create_loaded(ectx, &ctx.parent.object, +- &ctx.object.sensitive, &template, &ctx.object.object_handle, ++ &ctx.object.sensitive, &ctx.object.in_public, &ctx.object.object_handle, + &ctx.object.out_private, &ctx.object.out_public, &ctx.cp_hash, + &ctx.rp_hash, ctx.parameter_hash_algorithm, + ctx.aux_session_handle[0], ctx.aux_session_handle[1]); +-- +2.54.0 + diff --git a/0015-fix-checkquote-fix-off-by-one-in-PCR-digest-list-han.patch b/0015-fix-checkquote-fix-off-by-one-in-PCR-digest-list-han.patch new file mode 100644 index 0000000..1aa6a3a --- /dev/null +++ b/0015-fix-checkquote-fix-off-by-one-in-PCR-digest-list-han.patch @@ -0,0 +1,66 @@ +From a07362ae933a83845410a8ac7cfc46f41ad6a237 Mon Sep 17 00:00:00 2001 +From: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +Date: Tue, 3 Feb 2026 18:43:01 +0900 +Subject: [PATCH 15/30] fix(checkquote): fix off-by-one in PCR digest list + handling + +parse_selection_data_from_selection_string() now updates +TPML_DIGEST.count correctly and rolls over to a new list only after +8 digests, resolving failures with 8+ PCRs in the -f/-l path. + +Fixes: #3542 + +Signed-off-by: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +--- + tools/misc/tpm2_checkquote.c | 30 ++++++++++++------------------ + 1 file changed, 12 insertions(+), 18 deletions(-) + +diff --git a/tools/misc/tpm2_checkquote.c b/tools/misc/tpm2_checkquote.c +index 259d9c62..1264a9a3 100644 +--- a/tools/misc/tpm2_checkquote.c ++++ b/tools/misc/tpm2_checkquote.c +@@ -308,29 +308,23 @@ static bool parse_selection_data_from_selection_string(FILE *pcr_input, + /* + * Read the digest at a selected PCR index. + */ +- pcrs->pcr_values[digest_list_count].digests[pcrs->pcr_values[ +- digest_list_count].count].size = read_size; +- read_count = fread(pcrs->pcr_values[digest_list_count].digests[ +- pcrs->pcr_values[digest_list_count].count].buffer, ++ if (pcrs->pcr_values[digest_list_count].count == 8) { ++ ++digest_list_count; ++ if (digest_list_count >= TPM2_MAX_PCRS) { ++ LOG_ERR("Maximum count for allowed digest lists reached."); ++ return false; ++ } ++ } ++ UINT32 digest_index = pcrs->pcr_values[digest_list_count].count; ++ pcrs->pcr_values[digest_list_count].digests[digest_index].size = read_size; ++ read_count = fread( ++ pcrs->pcr_values[digest_list_count].digests[digest_index].buffer, + read_size, 1, pcr_input); + if (read_count != 1) { + LOG_ERR("Failed to read PCR digests from file"); + return false; + } +- /* +- * Ensure we don't overrun the allowed digest count in a +- * TPML_DIGEST. +- */ +- if (pcrs->pcr_values[digest_list_count].count == 7) { +- digest_list_count++; +- } else { +- /* +- * Ensure we populate the digest in a new list if we +- * exhausted the digest count in the current TPML_DIGEST +- * instance. +- */ +- pcrs->pcr_values[digest_list_count].count++; +- } ++ pcrs->pcr_values[digest_list_count].count++; + } + } + } +-- +2.54.0 + diff --git a/0016-tpm2_checkquote-Add-size-checks-for-PCR-desearializa.patch b/0016-tpm2_checkquote-Add-size-checks-for-PCR-desearializa.patch new file mode 100644 index 0000000..dd0dc4f --- /dev/null +++ b/0016-tpm2_checkquote-Add-size-checks-for-PCR-desearializa.patch @@ -0,0 +1,54 @@ +From 921be44cfc4fe76af49748aa039dd8ff421571e7 Mon Sep 17 00:00:00 2001 +From: Juergen Repp +Date: Tue, 3 Feb 2026 16:18:46 +0100 +Subject: [PATCH 16/30] tpm2_checkquote: Add size checks for PCR + desearialization + +When a binary blob in the PCR file written in the form 'serialized' +is used the size fields are not checked after reading the file. +These field are no checked and an error is produced if the max values +are exceeded. + +Signed-off-by: Juergen Repp +--- + tools/misc/tpm2_checkquote.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/tools/misc/tpm2_checkquote.c b/tools/misc/tpm2_checkquote.c +index 1264a9a3..e91f84f1 100644 +--- a/tools/misc/tpm2_checkquote.c ++++ b/tools/misc/tpm2_checkquote.c +@@ -347,6 +347,10 @@ static bool parse_selection_data_from_file(FILE *pcr_input, + return false; + } + pcr_select->count = le32toh(pcr_select->count); ++ if (pcr_select->count > TPM2_NUM_PCR_BANKS) { ++ LOG_ERR("Failed to read PCR selection from file"); ++ return false; ++ } + for (i = 0; i < pcr_select->count; i++) { + pcr_select->pcrSelections[i].hash = le16toh(pcr_select->pcrSelections[i].hash); + } +@@ -373,9 +377,19 @@ static bool parse_selection_data_from_file(FILE *pcr_input, + } + // Convert TPML_DIGEST from little endian to host endian. + pcrs->pcr_values[j].count = le32toh( pcrs->pcr_values[j].count); ++ if (pcrs->pcr_values[j].count > ARRAY_LEN(pcrs->pcr_values[j].digests)) { ++ LOG_ERR("Malformed PCR file, TPML_DIGEST count cannot be greater than %" PRIu64, ++ ARRAY_LEN(pcrs->pcr_values[j].digests)); ++ return false; ++ } + for (i = 0; i < pcrs->pcr_values[j].count; i++) { + pcrs->pcr_values[j].digests[i].size = + le16toh(pcrs->pcr_values[j].digests[i].size); ++ if (pcrs->pcr_values[j].digests[i].size > sizeof(TPMU_HA)) { ++ LOG_ERR("Malformed PCR file, TPML_DIGEST count cannot be greater than %" PRIu64, ++ sizeof(TPMU_HA)); ++ return false; ++ } + } + } + +-- +2.54.0 + diff --git a/0017-fix-tpm2_send-validate-command_size-before-computing.patch b/0017-fix-tpm2_send-validate-command_size-before-computing.patch new file mode 100644 index 0000000..58163dc --- /dev/null +++ b/0017-fix-tpm2_send-validate-command_size-before-computing.patch @@ -0,0 +1,44 @@ +From 66bcb37ac78d8e0f7324f6a7f02dcff3190ec6a3 Mon Sep 17 00:00:00 2001 +From: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +Date: Sun, 15 Feb 2026 04:39:57 +0900 +Subject: [PATCH 17/30] fix(tpm2_send): validate command_size before computing + data_size + +Signed-off-by: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +--- + tools/tpm2_send.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/tools/tpm2_send.c b/tools/tpm2_send.c +index a4da5692..10751f9b 100644 +--- a/tools/tpm2_send.c ++++ b/tools/tpm2_send.c +@@ -47,15 +47,19 @@ static int read_command_from_file(FILE *f, tpm2_command_header **c, + const tpm2_command_header *header = tpm2_command_header_from_bytes(buffer); + + UINT32 command_size = tpm2_command_header_get_size(header, true); +- UINT32 data_size = tpm2_command_header_get_size(header, false); +- +- if (command_size > TPM2_MAX_SIZE || command_size < data_size) { +- LOG_ERR("Command buffer %"PRIu32" bytes cannot be smaller then the " +- "encapsulated data %"PRIu32" bytes, and can not be bigger than" +- " the maximum buffer size", command_size, data_size); ++ if (command_size < TPM2_COMMAND_HEADER_SIZE) { ++ LOG_ERR("Command buffer size %"PRIu32" is smaller than command header " ++ "size %zu", command_size, TPM2_COMMAND_HEADER_SIZE); ++ return -1; ++ } ++ if (command_size > TPM2_MAX_SIZE) { ++ LOG_ERR("Command buffer size %"PRIu32" exceeds maximum buffer size %u", ++ command_size, TPM2_MAX_SIZE); + return -1; + } + ++ UINT32 data_size = tpm2_command_header_get_size(header, false); ++ + tpm2_command_header *command = (tpm2_command_header *) malloc(command_size); + if (!command) { + LOG_ERR("oom"); +-- +2.54.0 + diff --git a/0018-fix-tpm2_send-avoid-unintended-stdio-closing-in-clos.patch b/0018-fix-tpm2_send-avoid-unintended-stdio-closing-in-clos.patch new file mode 100644 index 0000000..a29981d --- /dev/null +++ b/0018-fix-tpm2_send-avoid-unintended-stdio-closing-in-clos.patch @@ -0,0 +1,27 @@ +From 856abc96e60ec0c7e9a78e7ab6e7111c866e3a88 Mon Sep 17 00:00:00 2001 +From: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +Date: Sun, 15 Feb 2026 05:59:27 +0900 +Subject: [PATCH 18/30] fix(tpm2_send): avoid unintended stdio closing in + close_file() + +Signed-off-by: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +--- + tools/tpm2_send.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/tpm2_send.c b/tools/tpm2_send.c +index 10751f9b..13264ac2 100644 +--- a/tools/tpm2_send.c ++++ b/tools/tpm2_send.c +@@ -111,7 +111,7 @@ static FILE *open_file(const char *path, const char *mode) { + + static void close_file(FILE *f) { + +- if (f && (f != stdin || f != stdout)) { ++ if (f && f != stdin && f != stdout) { + fclose(f); + } + } +-- +2.54.0 + diff --git a/0019-fix-tpm2_util-fix-error-message-in-tpm2_pem_encoded_.patch b/0019-fix-tpm2_util-fix-error-message-in-tpm2_pem_encoded_.patch new file mode 100644 index 0000000..faf412d --- /dev/null +++ b/0019-fix-tpm2_util-fix-error-message-in-tpm2_pem_encoded_.patch @@ -0,0 +1,27 @@ +From 78785206da66bfcb2a48a95c9a9e3800320b465a Mon Sep 17 00:00:00 2001 +From: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +Date: Sun, 15 Feb 2026 13:04:10 +0900 +Subject: [PATCH 19/30] fix(tpm2_util): fix error message in + tpm2_pem_encoded_key_to_fingerprint + +Signed-off-by: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +--- + lib/tpm2_util.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/tpm2_util.c b/lib/tpm2_util.c +index 17a9ca9f..9a1170dc 100644 +--- a/lib/tpm2_util.c ++++ b/lib/tpm2_util.c +@@ -1079,7 +1079,7 @@ bool tpm2_pem_encoded_key_to_fingerprint(const char *pem_encoded_key, + + rc = tpm2_base64_encode(buffer, buffer_length, base64); + if(!rc){ +- LOG_ERR("%s", "tpm2_base64_decode"); ++ LOG_ERR("%s", "tpm2_base64_encode"); + return false; + } + strcpy(fingerprint, "SHA256:"); +-- +2.54.0 + diff --git a/0020-fix-tpm2_util-modify-tpm2_safe_read_from_stdin-to-re.patch b/0020-fix-tpm2_util-modify-tpm2_safe_read_from_stdin-to-re.patch new file mode 100644 index 0000000..1834c23 --- /dev/null +++ b/0020-fix-tpm2_util-modify-tpm2_safe_read_from_stdin-to-re.patch @@ -0,0 +1,70 @@ +From 0bc0a85a077c1299daba5a6fe70be8bea9fd96ae Mon Sep 17 00:00:00 2001 +From: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +Date: Sun, 15 Feb 2026 08:05:58 +0900 +Subject: [PATCH 20/30] fix(tpm2_util)!: modify tpm2_safe_read_from_stdin to + read entire line + +Fixes #3551. + +The tpm2_safe_read_from_stdin function is used exclusively in +tools/fapi/tss2_template.c to read file paths from stdin. The current +implementation however uses the "%s" template in sscanf, which cannot +properly handle file paths containing whitespaces. + +This commit modifies the function's behavior to read the entire line. + +Signed-off-by: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +--- + lib/tpm2_util.c | 25 +++++++------------------ + 1 file changed, 7 insertions(+), 18 deletions(-) + +diff --git a/lib/tpm2_util.c b/lib/tpm2_util.c +index 9a1170dc..962f297a 100644 +--- a/lib/tpm2_util.c ++++ b/lib/tpm2_util.c +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1010,28 +1011,16 @@ out: + } + + bool tpm2_safe_read_from_stdin(int length, char *data) { +- int rc; + +- char *buf = malloc(length); +- char *read_data = malloc(length); +- +- if (buf == fgets(buf, length, stdin)) { +- rc = sscanf(buf, "%s", read_data); +- if (rc != 1) { +- free(buf); +- free(read_data); +- return false; +- } +- } +- else { +- free(buf); +- free(read_data); ++ /* Read line from stdin; at most length-1 bytes + null-termination */ ++ if (!fgets(data, length, stdin)) { + return false; + } + +- strcpy(data, read_data); +- free(buf); +- free(read_data); ++ /* Delete newline character */ ++ size_t end = strcspn(data, "\r\n"); ++ data[end] = '\0'; ++ + return true; + } + +-- +2.54.0 + diff --git a/0021-fix-tools-out-of-bounds-write-with-too-many-auxiliar.patch b/0021-fix-tools-out-of-bounds-write-with-too-many-auxiliar.patch new file mode 100644 index 0000000..85cb3bf --- /dev/null +++ b/0021-fix-tools-out-of-bounds-write-with-too-many-auxiliar.patch @@ -0,0 +1,447 @@ +From df8c01be5f7672a04615b4427b0d92d23c956bd1 Mon Sep 17 00:00:00 2001 +From: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +Date: Tue, 17 Feb 2026 23:36:49 +0900 +Subject: [PATCH 21/30] fix(tools): out-of-bounds write with too many auxiliary + sessions + +Signed-off-by: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +--- + tools/tpm2_activatecredential.c | 9 ++++----- + tools/tpm2_certify.c | 9 ++++----- + tools/tpm2_certifycreation.c | 9 ++++----- + tools/tpm2_changeauth.c | 7 +++---- + tools/tpm2_changeeps.c | 9 ++++----- + tools/tpm2_changepps.c | 9 ++++----- + tools/tpm2_create.c | 9 ++++----- + tools/tpm2_getrandom.c | 9 ++++----- + tools/tpm2_nvcertify.c | 9 ++++----- + tools/tpm2_nvdefine.c | 8 ++++---- + tools/tpm2_nvextend.c | 8 ++++---- + tools/tpm2_nvincrement.c | 9 ++++----- + tools/tpm2_nvread.c | 9 ++++----- + tools/tpm2_nvreadlock.c | 9 ++++----- + tools/tpm2_nvreadpublic.c | 9 ++++----- + tools/tpm2_nvsetbits.c | 9 ++++----- + tools/tpm2_nvundefine.c | 9 ++++----- + tools/tpm2_nvwrite.c | 9 ++++----- + tools/tpm2_nvwritelock.c | 9 ++++----- + 19 files changed, 75 insertions(+), 92 deletions(-) + +diff --git a/tools/tpm2_activatecredential.c b/tools/tpm2_activatecredential.c +index eee8a210..a1cf65fa 100644 +--- a/tools/tpm2_activatecredential.c ++++ b/tools/tpm2_activatecredential.c +@@ -292,13 +292,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + } + +diff --git a/tools/tpm2_certify.c b/tools/tpm2_certify.c +index ca1e468a..e504da09 100644 +--- a/tools/tpm2_certify.c ++++ b/tools/tpm2_certify.c +@@ -282,13 +282,12 @@ static bool on_option(char key, char *value) { + } + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + } + +diff --git a/tools/tpm2_certifycreation.c b/tools/tpm2_certifycreation.c +index b6dbc6fa..7e1d6e24 100644 +--- a/tools/tpm2_certifycreation.c ++++ b/tools/tpm2_certifycreation.c +@@ -358,13 +358,12 @@ static bool on_option(char key, char *value) { + ctx.policy_qualifier_data = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + /* no default */ + } +diff --git a/tools/tpm2_changeauth.c b/tools/tpm2_changeauth.c +index f2ad99fe..373fbd5c 100644 +--- a/tools/tpm2_changeauth.c ++++ b/tools/tpm2_changeauth.c +@@ -326,13 +326,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { + LOG_ERR("Specify a max of 3 sessions"); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + case 'R': + ctx.autoflush = true; +diff --git a/tools/tpm2_changeeps.c b/tools/tpm2_changeeps.c +index c3d6f6df..8c95915f 100644 +--- a/tools/tpm2_changeeps.c ++++ b/tools/tpm2_changeeps.c +@@ -164,13 +164,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + } + +diff --git a/tools/tpm2_changepps.c b/tools/tpm2_changepps.c +index 343dfc9b..fa331cd8 100644 +--- a/tools/tpm2_changepps.c ++++ b/tools/tpm2_changepps.c +@@ -164,13 +164,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + } + +diff --git a/tools/tpm2_create.c b/tools/tpm2_create.c +index 39bbc935..a4dbf569 100644 +--- a/tools/tpm2_create.c ++++ b/tools/tpm2_create.c +@@ -540,13 +540,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + case 'f': + ctx.format = tpm2_convert_pubkey_fmt_from_optarg(value); +diff --git a/tools/tpm2_getrandom.c b/tools/tpm2_getrandom.c +index f4b064d9..a6775a60 100644 +--- a/tools/tpm2_getrandom.c ++++ b/tools/tpm2_getrandom.c +@@ -289,13 +289,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + /* no default */ + } +diff --git a/tools/tpm2_nvcertify.c b/tools/tpm2_nvcertify.c +index 53729bb8..996bbc6c 100644 +--- a/tools/tpm2_nvcertify.c ++++ b/tools/tpm2_nvcertify.c +@@ -484,13 +484,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + case 'n': + ctx.precalc_nvname.size = BUFFER_SIZE(TPM2B_NAME, name); +diff --git a/tools/tpm2_nvdefine.c b/tools/tpm2_nvdefine.c +index 7b14b90f..800190a2 100644 +--- a/tools/tpm2_nvdefine.c ++++ b/tools/tpm2_nvdefine.c +@@ -453,12 +453,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + case 'g': + ctx.halg = tpm2_alg_util_from_optarg(value, +diff --git a/tools/tpm2_nvextend.c b/tools/tpm2_nvextend.c +index 3cf14550..0bf005c5 100644 +--- a/tools/tpm2_nvextend.c ++++ b/tools/tpm2_nvextend.c +@@ -237,12 +237,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + case 'n': + ctx.precalc_nvname.size = BUFFER_SIZE(TPM2B_NAME, name); +diff --git a/tools/tpm2_nvincrement.c b/tools/tpm2_nvincrement.c +index c411fa67..fd8ce195 100644 +--- a/tools/tpm2_nvincrement.c ++++ b/tools/tpm2_nvincrement.c +@@ -258,13 +258,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + case 'n': + ctx.precalc_nvname.size = BUFFER_SIZE(TPM2B_NAME, name); +diff --git a/tools/tpm2_nvread.c b/tools/tpm2_nvread.c +index d1d855d2..4720d48b 100644 +--- a/tools/tpm2_nvread.c ++++ b/tools/tpm2_nvread.c +@@ -336,13 +336,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + case 3: + ctx.is_yaml = true; +diff --git a/tools/tpm2_nvreadlock.c b/tools/tpm2_nvreadlock.c +index 387e6f38..16bd2a3e 100644 +--- a/tools/tpm2_nvreadlock.c ++++ b/tools/tpm2_nvreadlock.c +@@ -244,13 +244,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + case 'n': + ctx.precalc_nvname.size = BUFFER_SIZE(TPM2B_NAME, name); +diff --git a/tools/tpm2_nvreadpublic.c b/tools/tpm2_nvreadpublic.c +index 190749c2..b39d1ac7 100644 +--- a/tools/tpm2_nvreadpublic.c ++++ b/tools/tpm2_nvreadpublic.c +@@ -349,13 +349,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + case 'n': + ctx.precalc_nvname.size = BUFFER_SIZE(TPM2B_NAME, name); +diff --git a/tools/tpm2_nvsetbits.c b/tools/tpm2_nvsetbits.c +index 841753a5..a9a12876 100644 +--- a/tools/tpm2_nvsetbits.c ++++ b/tools/tpm2_nvsetbits.c +@@ -248,13 +248,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + case 'n': + ctx.precalc_nvname.size = BUFFER_SIZE(TPM2B_NAME, name); +diff --git a/tools/tpm2_nvundefine.c b/tools/tpm2_nvundefine.c +index 3e8e77ea..be488574 100644 +--- a/tools/tpm2_nvundefine.c ++++ b/tools/tpm2_nvundefine.c +@@ -355,13 +355,12 @@ static bool on_option(char key, char *value) { + if (!ctx.aux_session_cnt) { + ctx.policy_session.path = value; + } +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + case 0: + ctx.cp_hash_path = value; +diff --git a/tools/tpm2_nvwrite.c b/tools/tpm2_nvwrite.c +index e29ca8a3..220ea5ec 100644 +--- a/tools/tpm2_nvwrite.c ++++ b/tools/tpm2_nvwrite.c +@@ -396,13 +396,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + case 'n': + ctx.precalc_nvname.size = BUFFER_SIZE(TPM2B_NAME, name); +diff --git a/tools/tpm2_nvwritelock.c b/tools/tpm2_nvwritelock.c +index 52be0be1..ac76fcef 100644 +--- a/tools/tpm2_nvwritelock.c ++++ b/tools/tpm2_nvwritelock.c +@@ -278,13 +278,12 @@ static bool on_option(char key, char *value) { + ctx.rp_hash_path = value; + break; + case 'S': +- ctx.aux_session_path[ctx.aux_session_cnt] = value; +- if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) { +- ctx.aux_session_cnt++; +- } else { +- LOG_ERR("Specify a max of 3 sessions"); ++ if (ctx.aux_session_cnt >= MAX_AUX_SESSIONS) { ++ LOG_ERR("Specify a max of %u auxiliary sessions", MAX_AUX_SESSIONS); + return false; + } ++ ctx.aux_session_path[ctx.aux_session_cnt] = value; ++ ++ctx.aux_session_cnt; + break; + case 'n': + ctx.precalc_nvname.size = BUFFER_SIZE(TPM2B_NAME, name); +-- +2.54.0 + diff --git a/0022-fix-tpm2_alg_util-avoid-mutating-input-scheme-in-han.patch b/0022-fix-tpm2_alg_util-avoid-mutating-input-scheme-in-han.patch new file mode 100644 index 0000000..1a67d46 --- /dev/null +++ b/0022-fix-tpm2_alg_util-avoid-mutating-input-scheme-in-han.patch @@ -0,0 +1,127 @@ +From 70671552f843f1a94c04c3a57631ae3f0c20c8eb Mon Sep 17 00:00:00 2001 +From: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +Date: Wed, 18 Feb 2026 23:38:43 +0900 +Subject: [PATCH 22/30] fix(tpm2_alg_util): avoid mutating input scheme in + handle_scheme_sign + +Signed-off-by: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +--- + lib/tpm2_alg_util.c | 56 ++++++++++++++++++++++++--------------------- + 1 file changed, 30 insertions(+), 26 deletions(-) + +diff --git a/lib/tpm2_alg_util.c b/lib/tpm2_alg_util.c +index 65739fc7..42946752 100644 +--- a/lib/tpm2_alg_util.c ++++ b/lib/tpm2_alg_util.c +@@ -152,9 +152,9 @@ static alg_parser_rc handle_sym_common(const char *ext, TPMT_SYM_DEF_OBJECT *s, + * You cannot change all the variables in this, as they are dependent + * on names in that routine; this is for simplicity. + */ +-#define do_scheme_halg(scheme, advance, alg) \ ++#define DO_SCHEME_HALG(scheme_, advance, alg) \ + do { \ +- scheme += advance; \ ++ scheme_ += advance; \ + s->scheme.scheme = alg; \ + do_scheme_hash_alg = true; \ + found = true; \ +@@ -174,16 +174,18 @@ static alg_parser_rc handle_scheme_sign(const char *scheme, + return alg_parser_rc_error; + } + ++ char *buf_ptr = buf; ++ + // Get the scheme and symetric details + TPMS_ASYM_PARMS *s = &public->publicArea.parameters.asymDetail; + +- if (!strcmp(scheme, "null")) { ++ if (!strcmp(buf_ptr, "null")) { + public->publicArea.parameters.asymDetail.scheme.scheme = TPM2_ALG_NULL; + return alg_parser_rc_continue; + } + + char *halg = NULL; +- char *split = strchr(scheme, '-'); ++ char *split = strchr(buf_ptr, '-'); + if (split) { + *split = '\0'; + halg = split + 1; +@@ -193,51 +195,53 @@ static alg_parser_rc handle_scheme_sign(const char *scheme, + bool do_scheme_hash_alg = false; + + if (public->publicArea.type == TPM2_ALG_ECC) { +- if (!strncmp(scheme, "ecdsa", 5)) { +- do_scheme_halg(scheme, 5, TPM2_ALG_ECDSA); +- } else if (!strncmp(scheme, "ecdh", 4)) { +- do_scheme_halg(scheme, 4, TPM2_ALG_ECDH); +- } else if (!strncmp(scheme, "ecschnorr", 9)) { +- do_scheme_halg(scheme, 9, TPM2_ALG_ECSCHNORR); +- } else if (!strncmp(scheme, "sm2", 3)) { +- do_scheme_halg(scheme, 3, TPM2_ALG_SM2); +- } else if (!strncmp(scheme, "ecdaa", 5)) { +- do_scheme_halg(scheme, 5, TPM2_ALG_ECDAA); ++ if (!strncmp(buf_ptr, "ecdsa", 5)) { ++ DO_SCHEME_HALG(buf_ptr, 5, TPM2_ALG_ECDSA); ++ } else if (!strncmp(buf_ptr, "ecdh", 4)) { ++ DO_SCHEME_HALG(buf_ptr, 4, TPM2_ALG_ECDH); ++ } else if (!strncmp(buf_ptr, "ecschnorr", 9)) { ++ DO_SCHEME_HALG(buf_ptr, 9, TPM2_ALG_ECSCHNORR); ++ } else if (!strncmp(buf_ptr, "sm2", 3)) { ++ DO_SCHEME_HALG(buf_ptr, 3, TPM2_ALG_SM2); ++ } else if (!strncmp(buf_ptr, "ecdaa", 5)) { ++ DO_SCHEME_HALG(buf_ptr, 5, TPM2_ALG_ECDAA); + /* + * ECDAA has both a commit-counter value and hashing algorithm. + * The default commit-counter value is set to zero to use the first + * commit-id. + */ +- if (scheme[0] == '\0') { +- scheme = "0"; ++ const char *count_str = buf_ptr; ++ if (buf_ptr[0] == '\0') { ++ count_str = "0"; + } + + TPMS_SIG_SCHEME_ECDAA *e = &s->scheme.details.ecdaa; + +- bool res = tpm2_util_string_to_uint16(scheme, &e->count); ++ bool res = tpm2_util_string_to_uint16(count_str, &e->count); + if (!res) { + return alg_parser_rc_error; + } +- } else if (!strcmp("null", scheme)) { ++ } else if (!strcmp(buf_ptr, "null")) { + s->scheme.scheme = TPM2_ALG_NULL; ++ found = true; + } + } else { +- if (!strcmp(scheme, "rsaes")) { ++ if (!strcmp(buf_ptr, "rsaes")) { + /* + * rsaes has no hash alg or details, so it MUST + * match exactly, notice strcmp and NOT strNcmp! + */ + s->scheme.scheme = TPM2_ALG_RSAES; + found = true; +- } else if (!strcmp("null", scheme)) { ++ } else if (!strcmp(buf_ptr, "null")) { + s->scheme.scheme = TPM2_ALG_NULL; + found = true; +- } else if (!strncmp("rsapss", scheme, 6)) { +- do_scheme_halg(scheme, 6, TPM2_ALG_RSAPSS); +- } else if (!strncmp("rsassa", scheme, 6)) { +- do_scheme_halg(scheme, 6, TPM2_ALG_RSASSA); +- } else if (!strncmp(scheme, "oaep", 4)) { +- do_scheme_halg(scheme, 4, TPM2_ALG_OAEP); ++ } else if (!strncmp(buf_ptr, "rsapss", 6)) { ++ DO_SCHEME_HALG(buf_ptr, 6, TPM2_ALG_RSAPSS); ++ } else if (!strncmp(buf_ptr, "rsassa", 6)) { ++ DO_SCHEME_HALG(buf_ptr, 6, TPM2_ALG_RSASSA); ++ } else if (!strncmp(buf_ptr, "oaep", 4)) { ++ DO_SCHEME_HALG(buf_ptr, 4, TPM2_ALG_OAEP); + } + } + +-- +2.54.0 + diff --git a/0023-fix-tools-add-missing-null-checks-after-malloc.patch b/0023-fix-tools-add-missing-null-checks-after-malloc.patch new file mode 100644 index 0000000..d532a34 --- /dev/null +++ b/0023-fix-tools-add-missing-null-checks-after-malloc.patch @@ -0,0 +1,115 @@ +From 6169f5cf75ea205125b7127b25875f0b2eeb770c Mon Sep 17 00:00:00 2001 +From: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +Date: Sun, 1 Mar 2026 03:41:03 +0900 +Subject: [PATCH 23/30] fix(tools): add missing null checks after malloc + +Signed-off-by: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +--- + tools/tpm2_duplicate.c | 9 +++++++++ + tools/tpm2_encryptdecrypt.c | 4 ++++ + tools/tpm2_getekcertificate.c | 8 ++++++++ + tools/tpm2_nvreadpublic.c | 4 ++++ + tools/tpm2_verifysignature.c | 4 ++++ + 5 files changed, 29 insertions(+) + +diff --git a/tools/tpm2_duplicate.c b/tools/tpm2_duplicate.c +index 2c7c340c..26f4ca1d 100644 +--- a/tools/tpm2_duplicate.c ++++ b/tools/tpm2_duplicate.c +@@ -179,6 +179,11 @@ static tool_rc openssl_create_duplicate(void) { + encrypted_duplicate_sensitive.size); + + ctx.out_private_data = malloc(private.size + sizeof(private.size)); ++ if (!ctx.out_private_data) { ++ LOG_ERR("oom"); ++ rc = tool_rc_general_error; ++ goto out; ++ } + memcpy(ctx.out_private_data, &private, private.size + sizeof(private.size)); + + out: +@@ -231,6 +236,10 @@ static tool_rc process_openssl_duplicate(void) { + + ctx.out_sym_seed = malloc(encrypted_seed.size + + sizeof(encrypted_seed.size)); ++ if (!ctx.out_sym_seed) { ++ LOG_ERR("oom"); ++ return tool_rc_general_error; ++ } + memcpy(ctx.out_sym_seed, &encrypted_seed, + encrypted_seed.size + sizeof(encrypted_seed.size)); + +diff --git a/tools/tpm2_encryptdecrypt.c b/tools/tpm2_encryptdecrypt.c +index e9087dc8..3a6b243a 100644 +--- a/tools/tpm2_encryptdecrypt.c ++++ b/tools/tpm2_encryptdecrypt.c +@@ -375,6 +375,10 @@ static tool_rc process_inputs(ESYS_CONTEXT *ectx) { + ctx.iv_in = 0; + } else { + ctx.iv_in = malloc(iv_start.size + sizeof(iv_start)); ++ if (!ctx.iv_in) { ++ LOG_ERR("oom"); ++ return tool_rc_general_error; ++ } + ctx.iv_in->size = iv_start.size; + memcpy(ctx.iv_in->buffer, &iv_start.buffer, iv_start.size); + } +diff --git a/tools/tpm2_getekcertificate.c b/tools/tpm2_getekcertificate.c +index 961021cf..d962c13c 100644 +--- a/tools/tpm2_getekcertificate.c ++++ b/tools/tpm2_getekcertificate.c +@@ -424,6 +424,10 @@ static char *encode_ek_public_amd(void) { + return NULL; + } + char *hash_str = malloc(AMD_EK_URI_LEN * 2 + NULL_TERM_LEN); ++ if (!hash_str) { ++ LOG_ERR("oom"); ++ return NULL; ++ } + for (size_t i = 0; i < AMD_EK_URI_LEN; i++) + { + sprintf((char*)(hash_str + (i*2)), "%02x", hash[i]); +@@ -845,6 +849,10 @@ static tool_rc process_input(ESYS_CONTEXT *ectx) { + + if (ctx.ek_path) { + ctx.out_public = malloc(sizeof(*ctx.out_public)); ++ if (!ctx.out_public) { ++ LOG_ERR("oom"); ++ return tool_rc_general_error; ++ } + ctx.out_public->size = 0; + bool res = files_load_public(ctx.ek_path, ctx.out_public); + if (!res) { +diff --git a/tools/tpm2_nvreadpublic.c b/tools/tpm2_nvreadpublic.c +index b39d1ac7..597ff6d1 100644 +--- a/tools/tpm2_nvreadpublic.c ++++ b/tools/tpm2_nvreadpublic.c +@@ -243,6 +243,10 @@ static tool_rc process_inputs(ESYS_CONTEXT *ectx) { + */ + if (!ctx.is_command_dispatch) { + ctx.nv_public_list[0] = malloc(sizeof(TPM2B_NV_PUBLIC)); ++ if (!ctx.nv_public_list[0]) { ++ LOG_ERR("oom"); ++ return tool_rc_general_error; ++ } + } + + /* +diff --git a/tools/tpm2_verifysignature.c b/tools/tpm2_verifysignature.c +index 240165d6..5f7c3e96 100644 +--- a/tools/tpm2_verifysignature.c ++++ b/tools/tpm2_verifysignature.c +@@ -196,6 +196,10 @@ static bool on_option(char key, char *value) { + break; + case 'd': { + ctx.msg_hash = malloc(sizeof(TPM2B_DIGEST)); ++ if (!ctx.msg_hash) { ++ LOG_ERR("oom"); ++ return false; ++ } + ctx.msg_hash->size = sizeof(ctx.msg_hash->buffer); + if (!files_load_bytes_from_path(value, ctx.msg_hash->buffer, + &ctx.msg_hash->size)) { +-- +2.54.0 + diff --git a/0024-fix-test-add-missing-null-checks-after-malloc.patch b/0024-fix-test-add-missing-null-checks-after-malloc.patch new file mode 100644 index 0000000..6b265cf --- /dev/null +++ b/0024-fix-test-add-missing-null-checks-after-malloc.patch @@ -0,0 +1,38 @@ +From 73befeab5b79429864a2d1f95e8a94b501913b32 Mon Sep 17 00:00:00 2001 +From: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +Date: Sun, 1 Mar 2026 03:41:45 +0900 +Subject: [PATCH 24/30] fix(test): add missing null checks after malloc + +Signed-off-by: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +--- + test/unit/test_tpm2_auth_util.c | 1 + + test/unit/test_tpm2_session.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/test/unit/test_tpm2_auth_util.c b/test/unit/test_tpm2_auth_util.c +index d88e42ba..c2e07481 100644 +--- a/test/unit/test_tpm2_auth_util.c ++++ b/test/unit/test_tpm2_auth_util.c +@@ -308,6 +308,7 @@ static int setup(void **state) { + ESYS_CONTEXT *ectx; + size_t size = sizeof(TSS2_TCTI_CONTEXT_FAKE); + TSS2_TCTI_CONTEXT *tcti = malloc(size); ++ assert_non_null(tcti); + + rc = tcti_fake_initialize(tcti, &size); + if (rc) { +diff --git a/test/unit/test_tpm2_session.c b/test/unit/test_tpm2_session.c +index dccb87f0..12851569 100644 +--- a/test/unit/test_tpm2_session.c ++++ b/test/unit/test_tpm2_session.c +@@ -77,6 +77,7 @@ TSS2_RC __wrap_Esys_TR_GetName(ESYS_CONTEXT *esysContext, ESYS_TR handle, + UNUSED(handle); + + *name = malloc(sizeof(TPM2B_NAME)); ++ assert_non_null(*name); + size_t offset = 0; + TSS2_RC rc = Tss2_MU_TPM2_HANDLE_Marshal(SESSION_HANDLE, &(*name)->name[0], + sizeof(TPM2_HANDLE), &offset); +-- +2.54.0 + diff --git a/0025-fix-tpm2_util-fix-buffer-overflow-in-string-validati.patch b/0025-fix-tpm2_util-fix-buffer-overflow-in-string-validati.patch new file mode 100644 index 0000000..8bbba40 --- /dev/null +++ b/0025-fix-tpm2_util-fix-buffer-overflow-in-string-validati.patch @@ -0,0 +1,45 @@ +From 7f25b8648b50b025788e1d1d78f6c023eb559b79 Mon Sep 17 00:00:00 2001 +From: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +Date: Sun, 1 Mar 2026 02:47:49 +0900 +Subject: [PATCH 25/30] fix(tpm2_util): fix buffer overflow in string + validation checks + +Signed-off-by: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +--- + lib/tpm2_util.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +diff --git a/lib/tpm2_util.c b/lib/tpm2_util.c +index 962f297a..4a79cfa3 100644 +--- a/lib/tpm2_util.c ++++ b/lib/tpm2_util.c +@@ -1027,22 +1027,20 @@ bool tpm2_safe_read_from_stdin(int length, char *data) { + bool tpm2_pem_encoded_key_to_fingerprint(const char *pem_encoded_key, + char *fingerprint) { + +- bool is_pemkey_len_valid = strlen(pem_encoded_key) > 1024 ? false : true; +- if (!is_pemkey_len_valid) { ++ if (strlen(pem_encoded_key) >= 1024) { + return false; + } + + char str[1024] = ""; +- strcpy(str, pem_encoded_key); ++ strncpy(str, pem_encoded_key, 1023); ++ str[1023] = '\0'; + + /* walk through other tokens */ + char base64[1024] = ""; + char *token = strtok(str, "\n"); + while ( token != NULL ) { + if (!strstr(token, "-----")) { +- bool is_base64_overrun = (strlen(base64) + strlen(token)) > 1024 ? +- true : false; +- if (is_base64_overrun) { ++ if ((strlen(base64) + strlen(token)) >= 1024) { + return false; + } + strcat(base64, token); +-- +2.54.0 + diff --git a/0026-Allocate-the-right-amount-of-memory-for-auth_policy.patch b/0026-Allocate-the-right-amount-of-memory-for-auth_policy.patch new file mode 100644 index 0000000..b0ea1d5 --- /dev/null +++ b/0026-Allocate-the-right-amount-of-memory-for-auth_policy.patch @@ -0,0 +1,30 @@ +From 494ae8b5a8a705c7457df078f476b843b89ae6f1 Mon Sep 17 00:00:00 2001 +From: Andreas Fuchs +Date: Tue, 10 Feb 2026 09:20:05 +0100 +Subject: [PATCH 26/30] Allocate the right amount of memory for auth_policy + +For some reason we did a malloc(UINT16_MAX+...) which is way to much for a policy. + +Signed-off-by: Andreas Fuchs + +Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> +--- + tools/tpm2_setprimarypolicy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/tpm2_setprimarypolicy.c b/tools/tpm2_setprimarypolicy.c +index cdabcd9e..35064ce6 100644 +--- a/tools/tpm2_setprimarypolicy.c ++++ b/tools/tpm2_setprimarypolicy.c +@@ -106,7 +106,7 @@ static tool_rc process_inputs(ESYS_CONTEXT *ectx) { + */ + if (ctx.policy_path) { + +- ctx.auth_policy = malloc(UINT16_MAX + sizeof(uint16_t)); ++ ctx.auth_policy = malloc(sizeof(*ctx.auth_policy)); + if (!ctx.auth_policy) { + LOG_ERR("oom"); + return tool_rc_general_error; +-- +2.54.0 + diff --git a/0027-tpm2_util-add-some-checks-to-tpm2_safe_read_from_std.patch b/0027-tpm2_util-add-some-checks-to-tpm2_safe_read_from_std.patch new file mode 100644 index 0000000..31ae0b4 --- /dev/null +++ b/0027-tpm2_util-add-some-checks-to-tpm2_safe_read_from_std.patch @@ -0,0 +1,37 @@ +From 09cc3b776849fba9d37487bfbc7ae4d725805b0c Mon Sep 17 00:00:00 2001 +From: Juergen Repp +Date: Sun, 8 Mar 2026 15:59:20 +0100 +Subject: [PATCH 27/30] tpm2_util: add some checks to + tpm2_safe_read_from_stdin. + +* Prevent garbage in stdin buffer. +* Add check whether input is empty. + +Signed-off-by: Juergen Repp +--- + lib/tpm2_util.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/lib/tpm2_util.c b/lib/tpm2_util.c +index 4a79cfa3..d4b46c95 100644 +--- a/lib/tpm2_util.c ++++ b/lib/tpm2_util.c +@@ -1021,6 +1021,15 @@ bool tpm2_safe_read_from_stdin(int length, char *data) { + size_t end = strcspn(data, "\r\n"); + data[end] = '\0'; + ++ /* clear fget buffer */ ++ if (strlen(data) == (size_t)length - 1) { ++ int c; ++ while ((c = getchar()) != '\n' && c != EOF); ++ } ++ /* Return false if input is empty */ ++ if (data[0] == '\0') { ++ return false; ++ } + return true; + } + +-- +2.54.0 + diff --git a/0028-tpm2_createak-add-attribute-option.patch b/0028-tpm2_createak-add-attribute-option.patch new file mode 100644 index 0000000..b24d6ad --- /dev/null +++ b/0028-tpm2_createak-add-attribute-option.patch @@ -0,0 +1,150 @@ +From 8776c467e8d5a95fcbcd252ce737dea7b1bd1a8a Mon Sep 17 00:00:00 2001 +From: Juergen Repp +Date: Thu, 5 Mar 2026 21:47:17 +0100 +Subject: [PATCH 28/30] tpm2_createak: add attribute option. + +The default attributes for the ak are: +"restricted|userwithauth|sign|fixedtpm|fixedparent|sensitivedataorigin" +This default setting differs from the settings in the specification +TPM 2.0 Keys for Device Identity and Attestation. "adminwithpolicy" +is not set. Thus the option -a, --attributes is added. +Addresses: #3225 + +Signed-off-by: Juergen Repp +--- + man/tpm2_createak.1.md | 9 +++++++++ + test/integration/tests/createak.sh | 13 +++++++++++-- + tools/tpm2_createak.c | 19 +++++++++++++++++-- + 3 files changed, 37 insertions(+), 4 deletions(-) + +diff --git a/man/tpm2_createak.1.md b/man/tpm2_createak.1.md +index adcda09a..883d7fa5 100644 +--- a/man/tpm2_createak.1.md ++++ b/man/tpm2_createak.1.md +@@ -48,6 +48,15 @@ loaded-key: + * **rsa** - An RSA2048 key. + * **keyedhash** - hmac key. + ++ * **-a**, **\--attributes**=_ATTRIBUTES_: ++ ++ The object attributes, optional. The default for created objects is: ++ "restricted|userwithauth|sign|fixedtpm|fixedparent|sensitivedataorigin" ++ This default setting differs from the settings in the specification ++ TPM 2.0 Keys for Device Identity and Attestation. "adminwithpolicy" is ++ not set and can be added to the default: ++ "restricted|userwithauth|...|adminwithpolicy" ++ + * **-g**, **\--hash-algorithm**=_ALGORITHM_: + + Specifies the digest algorithm used for signing. +diff --git a/test/integration/tests/createak.sh b/test/integration/tests/createak.sh +index 7067f9d2..b8fb1d44 100644 +--- a/test/integration/tests/createak.sh ++++ b/test/integration/tests/createak.sh +@@ -3,7 +3,7 @@ + source helpers.sh + + cleanup() { +- rm -f ek.pub ak.pub ak.name ak.name ak.log ++ rm -f ek.pub ak.pub ak.name ak.name ak.log ek.ctx + + # Evict persistent handles, we want them to always succeed and never trip + # the onerror trap. +@@ -26,7 +26,7 @@ cleanup "no-shut-down" + tpm2 createek -Q -c 0x8101000b -G rsa -u ek.pub + + tpm2 createak -Q -C 0x8101000b -c ak.ctx -G rsa -g sha256 -s rsassa -u ak.pub \ +--n ak.name -q ak.qname ++ -n ak.name -q ak.qname + + # Validate the qname + tpm2 readpublic -c ak.ctx -q ak.qname2 +@@ -45,4 +45,13 @@ tpm2 changeauth -c e endauth + tpm2 createek -Q -P endauth -c 0x8101000b -G rsa -u ek.pub + tpm2 createak -Q -P endauth -C 0x8101000b -c ak.ctx -G rsa -u ak.pub -n ak.name + ++# Check attributes different from default ++tpm2 createak -Q -Q -P endauth -C 0x8101000b -c ak.ctx -u ak.pub -n ak.name \ ++ -a "restricted|userwithauth|sign|fixedtpm|fixedparent|sensitivedataorigin|adminwithpolicy" ++ ++# Check whether non default attribute was set ++tpm2 readpublic -c ak.ctx | grep adminwithpolicy ++ ++ ++ + exit 0 +diff --git a/tools/tpm2_createak.c b/tools/tpm2_createak.c +index 8239e5f4..d9d5a869 100644 +--- a/tools/tpm2_createak.c ++++ b/tools/tpm2_createak.c +@@ -12,6 +12,7 @@ + #include "tpm2_auth_util.h" + #include "tpm2_convert.h" + #include "tpm2_tool.h" ++#include "tpm2_attr_util.h" + + #define ATTRS \ + TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_USERWITHAUTH| \ +@@ -108,6 +109,7 @@ struct createak_context { + const char *qname_file; + } out; + char *auth_str; ++ char *attrs; + } ak; + struct { + UINT8 f :1; +@@ -136,6 +138,7 @@ static tool_rc init_ak_public(TPMI_ALG_HASH name_alg, TPM2B_PUBLIC *public) { + + const char *name_halg; + char alg[256]; ++ TPMA_OBJECT attrs; + + name_halg = tpm2_alg_util_algtostr(name_alg, tpm2_alg_util_flags_hash); + +@@ -159,7 +162,15 @@ static tool_rc init_ak_public(TPMI_ALG_HASH name_alg, TPM2B_PUBLIC *public) { + snprintf(alg, sizeof(alg), "%s:%s-%s:null", ctx.ak.in.alg.type, + ctx.ak.in.alg.sign, ctx.ak.in.alg.digest); + } +- return tpm2_alg_util_public_init(alg, name_halg, NULL, NULL, ATTRS, public); ++ if (ctx.ak.attrs) { ++ if (!tpm2_attr_util_obj_from_optarg(ctx.ak.attrs, &attrs)) { ++ LOG_ERR("Invalid attributes."); ++ return tool_rc_general_error; ++ } ++ } else { ++ attrs = ATTRS; ++ } ++ return tpm2_alg_util_public_init(alg, name_halg, NULL, NULL, attrs, public); + } + + static tool_rc create_ak(ESYS_CONTEXT *ectx) { +@@ -464,6 +475,9 @@ static bool on_option(char key, char *value) { + case 'R': + ctx.autoflush = true; + break; ++ case 'a': ++ ctx.ak.attrs = value; ++ break; + } + + return true; +@@ -479,6 +493,7 @@ static bool tpm2_tool_onstart(tpm2_options **opts) { + { "ak-name", required_argument, NULL, 'n' }, + { "key-algorithm", required_argument, NULL, 'G' }, + { "hash-algorithm", required_argument, NULL, 'g' }, ++ { "attributes", required_argument, NULL, 'a' }, + { "signing-algorithm", required_argument, NULL, 's' }, + { "format", required_argument, NULL, 'f' }, + { "public", required_argument, NULL, 'u' }, +@@ -487,7 +502,7 @@ static bool tpm2_tool_onstart(tpm2_options **opts) { + { "autoflush", no_argument, NULL, 'R' }, + }; + +- *opts = tpm2_options_new("P:p:C:c:n:G:g:s:f:u:r:q:R", ARRAY_LEN(topts), topts, ++ *opts = tpm2_options_new("P:p:C:c:n:G:a:g:s:f:u:r:q:R", ARRAY_LEN(topts), topts, + on_option, NULL, 0); + + return *opts != NULL; +-- +2.54.0 + diff --git a/0029-fix-tpm2_loadexternal-check-return-value-of-RAND_byt.patch b/0029-fix-tpm2_loadexternal-check-return-value-of-RAND_byt.patch new file mode 100644 index 0000000..f495941 --- /dev/null +++ b/0029-fix-tpm2_loadexternal-check-return-value-of-RAND_byt.patch @@ -0,0 +1,41 @@ +From cd3d4df82a4cd629ca36345eb4afb8f5d622f837 Mon Sep 17 00:00:00 2001 +From: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +Date: Fri, 6 Mar 2026 23:45:56 +0900 +Subject: [PATCH 29/30] fix(tpm2_loadexternal): check return value of + RAND_bytes + +RAND_bytes() can fail with return code 0 or -1 when the OpenSSL CSPRNG has +not been sufficiently seeded. + +If RAND_bytes() fails, seed->buffer is left uninitialised. This seed value +is stored in ctx.priv.sensitiveArea.seedValue and used in the consistency +checks by TPM. + +Check the return value and return tool_rc_general_error on failure so that +the operation is aborted rather than proceeding with a bad seed. + +Signed-off-by: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +--- + tools/tpm2_loadexternal.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/tools/tpm2_loadexternal.c b/tools/tpm2_loadexternal.c +index bddc1b73..9c8bab63 100644 +--- a/tools/tpm2_loadexternal.c ++++ b/tools/tpm2_loadexternal.c +@@ -284,7 +284,11 @@ priv_path: + TPM2B_DIGEST *seed = &ctx.priv.sensitiveArea.seedValue; + seed->size = tpm2_alg_util_get_hash_size(ctx.pub.publicArea.nameAlg); + if (seed->size != 0) { +- RAND_bytes(seed->buffer, seed->size); ++ int tmp_rc = RAND_bytes(seed->buffer, seed->size); ++ if (tmp_rc != 1) { ++ LOG_ERR("Failed to generate random seed value"); ++ return tool_rc_general_error; ++ } + } + + tpm2_openssl_load_rc load_status = tpm2_openssl_load_private( +-- +2.54.0 + diff --git a/0030-fix-tpm2_identity_util-add-buffer-size-check.patch b/0030-fix-tpm2_identity_util-add-buffer-size-check.patch new file mode 100644 index 0000000..59329fe --- /dev/null +++ b/0030-fix-tpm2_identity_util-add-buffer-size-check.patch @@ -0,0 +1,154 @@ +From 6dfbb0fd748f51bec179ac28bf8239199155df72 Mon Sep 17 00:00:00 2001 +From: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +Date: Fri, 27 Mar 2026 19:15:29 +0900 +Subject: [PATCH 30/30] fix(tpm2_identity_util)!: add buffer size check + +hmac_outer_integrity() concatenates two caller-supplied buffers into a +fixed-size stack buffer of TPM2_MAX_DIGEST_BUFFER (1024) bytes using +memcpy without checking their combined size. + +This commit adds a buffer size check before memcpy to prevent potential +oob. + +To propagate errors to the caller, the return type of hmac_outer_integrity() +and tpm2_identity_util_calculate_outer_integrity() from void to bool. + +The latter change breaks compatibility with the public API. + +Signed-off-by: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com> +--- + lib/tpm2_identity_util.c | 15 ++++++++++++--- + lib/tpm2_identity_util.h | 2 +- + tools/tpm2_duplicate.c | 5 ++++- + tools/tpm2_import.c | 7 ++++++- + tools/tpm2_makecredential.c | 7 +++++-- + 5 files changed, 28 insertions(+), 8 deletions(-) + +diff --git a/lib/tpm2_identity_util.c b/lib/tpm2_identity_util.c +index 03cfd269..b78413f3 100644 +--- a/lib/tpm2_identity_util.c ++++ b/lib/tpm2_identity_util.c +@@ -285,11 +285,18 @@ out: + return result; + } + +-static void hmac_outer_integrity(TPMI_ALG_HASH parent_name_alg, ++static bool hmac_outer_integrity(TPMI_ALG_HASH parent_name_alg, + uint8_t *buffer1, uint16_t buffer1_size, uint8_t *buffer2, + uint16_t buffer2_size, uint8_t *hmac_key, + TPM2B_DIGEST *outer_integrity_hmac) { + ++ if ((size_t)buffer1_size + buffer2_size > TPM2_MAX_DIGEST_BUFFER) { ++ LOG_ERR("Necessary buffer size (%u) exceeds TPM2_MAX_DIGEST_BUFFER (%zu)", ++ (unsigned)(buffer1_size + buffer2_size), ++ (size_t)TPM2_MAX_DIGEST_BUFFER); ++ return false; ++ } ++ + uint8_t to_hmac_buffer[TPM2_MAX_DIGEST_BUFFER]; + memcpy(to_hmac_buffer, buffer1, buffer1_size); + memcpy(to_hmac_buffer + buffer1_size, buffer2, buffer2_size); +@@ -301,6 +308,7 @@ static void hmac_outer_integrity(TPMI_ALG_HASH parent_name_alg, + to_hmac_buffer, buffer1_size + buffer2_size, + outer_integrity_hmac->buffer, &size); + outer_integrity_hmac->size = size; ++ return true; + } + + bool tpm2_identity_util_calculate_inner_integrity(TPMI_ALG_HASH name_alg, +@@ -376,7 +384,7 @@ bool tpm2_identity_util_calculate_inner_integrity(TPMI_ALG_HASH name_alg, + encrypted_inner_integrity); + } + +-void tpm2_identity_util_calculate_outer_integrity(TPMI_ALG_HASH parent_name_alg, ++bool tpm2_identity_util_calculate_outer_integrity(TPMI_ALG_HASH parent_name_alg, + TPM2B_NAME *pubname, TPM2B_MAX_BUFFER *marshalled_sensitive, + TPM2B_MAX_BUFFER *protection_hmac_key, + TPM2B_MAX_BUFFER *protection_enc_key, TPMT_SYM_DEF_OBJECT *sym_alg, +@@ -390,7 +398,8 @@ void tpm2_identity_util_calculate_outer_integrity(TPMI_ALG_HASH parent_name_alg, + marshalled_sensitive->buffer, marshalled_sensitive->size, + NULL, 0, encrypted_duplicate_sensitive); + //Calculate outerHMAC +- hmac_outer_integrity(parent_name_alg, encrypted_duplicate_sensitive->buffer, ++ return hmac_outer_integrity(parent_name_alg, ++ encrypted_duplicate_sensitive->buffer, + encrypted_duplicate_sensitive->size, pubname->name, pubname->size, + protection_hmac_key->buffer, outer_hmac); + } +diff --git a/lib/tpm2_identity_util.h b/lib/tpm2_identity_util.h +index c298a62f..31ba6e6e 100644 +--- a/lib/tpm2_identity_util.h ++++ b/lib/tpm2_identity_util.h +@@ -95,7 +95,7 @@ bool tpm2_identity_util_calculate_inner_integrity(TPMI_ALG_HASH name_alg, + * @param outer_hmac + * The outer HMAC structure to populate. + */ +-void tpm2_identity_util_calculate_outer_integrity(TPMI_ALG_HASH parent_name_alg, ++bool tpm2_identity_util_calculate_outer_integrity(TPMI_ALG_HASH parent_name_alg, + TPM2B_NAME *pubname, TPM2B_MAX_BUFFER *marshalled_sensitive, + TPM2B_MAX_BUFFER *protection_hmac_key, + TPM2B_MAX_BUFFER *protection_enc_key, TPMT_SYM_DEF_OBJECT *sym_alg, +diff --git a/tools/tpm2_duplicate.c b/tools/tpm2_duplicate.c +index 26f4ca1d..75f7d3c4 100644 +--- a/tools/tpm2_duplicate.c ++++ b/tools/tpm2_duplicate.c +@@ -147,11 +147,14 @@ static tool_rc openssl_create_duplicate(void) { + */ + TPM2B_DIGEST outer_hmac = TPM2B_EMPTY_INIT; + TPM2B_MAX_BUFFER encrypted_duplicate_sensitive = TPM2B_EMPTY_INIT; +- tpm2_identity_util_calculate_outer_integrity( ++ bool outer_res = tpm2_identity_util_calculate_outer_integrity( + ctx.in_parent_public_key_data.publicArea.nameAlg, + &pubname, &marshalled_sensitive, &hmac_key, &enc_key, + &ctx.in_parent_public_key_data.publicArea.parameters.rsaDetail.symmetric, + &encrypted_duplicate_sensitive, &outer_hmac); ++ if (!outer_res) { ++ return tool_rc_general_error; ++ } + + /* + * Build the private data structure for writing out +diff --git a/tools/tpm2_import.c b/tools/tpm2_import.c +index 480ed89a..ac33a05d 100644 +--- a/tools/tpm2_import.c ++++ b/tools/tpm2_import.c +@@ -375,10 +375,15 @@ static tool_rc process_input_ossl_import(ESYS_CONTEXT *ectx) { + + TPM2B_DIGEST outer_hmac = TPM2B_EMPTY_INIT; + TPM2B_MAX_BUFFER encrypted_duplicate_sensitive = TPM2B_EMPTY_INIT; +- tpm2_identity_util_calculate_outer_integrity(parent_pub->publicArea.nameAlg, ++ bool outer_res = tpm2_identity_util_calculate_outer_integrity( ++ parent_pub->publicArea.nameAlg, + &pubname, &encrypted_inner_integrity, &hmac_key, &enc_key, + &parent_pub->publicArea.parameters.rsaDetail.symmetric, + &encrypted_duplicate_sensitive, &outer_hmac); ++ if (!outer_res) { ++ rc = tool_rc_general_error; ++ goto out; ++ } + + result = create_import_key_private_data(parent_pub->publicArea.nameAlg, + &encrypted_duplicate_sensitive, &outer_hmac); +diff --git a/tools/tpm2_makecredential.c b/tools/tpm2_makecredential.c +index f91533e8..b9098ee7 100644 +--- a/tools/tpm2_makecredential.c ++++ b/tools/tpm2_makecredential.c +@@ -137,10 +137,13 @@ static tool_rc make_external_credential_and_save(void) { + */ + TPM2B_DIGEST outer_hmac = TPM2B_EMPTY_INIT; + TPM2B_MAX_BUFFER encrypted_sensitive = TPM2B_EMPTY_INIT; +- tpm2_identity_util_calculate_outer_integrity(name_alg, &ctx.object_name, +- &marshalled_inner_integrity, &hmac_key, &enc_key, ++ bool outer_res = tpm2_identity_util_calculate_outer_integrity(name_alg, ++ &ctx.object_name, &marshalled_inner_integrity, &hmac_key, &enc_key, + &ctx.public.publicArea.parameters.rsaDetail.symmetric, + &encrypted_sensitive, &outer_hmac); ++ if (!outer_res) { ++ return tool_rc_general_error; ++ } + + /* + * Package up the info to save +-- +2.54.0 + diff --git a/tpm2-tools.spec b/tpm2-tools.spec index 430880d..fb1c438 100644 --- a/tpm2-tools.spec +++ b/tpm2-tools.spec @@ -2,7 +2,7 @@ Name: tpm2-tools Version: 5.7 -Release: 5%{?candidate:.%{candidate}}%{?dist} +Release: 6%{?candidate:.%{candidate}}%{?dist} Summary: A bunch of TPM testing toolS build upon tpm2-tss License: BSD-3-Clause @@ -43,9 +43,36 @@ Patch0018: 0018-tpm2_policy-Do-not-overflow-list-of-policy-digests.patch patch0101: 0001-tpm2_clockrateadjust-Fix-segfault.patch Patch0102: 0002-eventlog-TCG-PC-Client-FPF-renumbered-its-sections.patch Patch0106: 0006-build-remove-trailing-comma.patch - - - +Patch0201: 0001-tpm2_eventlog-Fix-parsing-on-big-endian-systems.patch +Patch0202: 0002-man-tpm2_policysigned-add-parameter-raw-data.patch +Patch0203: 0003-tpm2_loadexternal-Add-option-rsa_exponent_zero.patch +Patch0204: 0004-tpm2_quote-checkquote-Fix-usage-of-little-endian-ser.patch +Patch0205: 0005-tpm2_makecredential-Fix-usage-of-name-parameter.patch +Patch0206: 0006-tpm2_checkquote-Fix-indentation-in-parse_marshaled_s.patch +Patch0207: 0007-tpm2_checkquote-Fix-missing-error-checking.patch +Patch0208: 0008-tpm2_checkquote-Fix-memory-leaks-malloced-buffer-not.patch +Patch0209: 0009-tpm2_makecredential-fix-wrong-tcg-ek-templates.patch +Patch0210: 0010-tools-Fix-several-clang-tidy-errors.patch +Patch0211: 0011-tools-Fix-wrong-type-usage-of-enums.patch +Patch0212: 0012-tpm2_eventlog-Extend-pcrs-using-event-EV_IPL.patch +Patch0213: 0013-tpm2_nvcertify-Set-size-if-the-size-parameter-is-not.patch +Patch0214: 0014-tpm2_create-Fix-creation-of-ctx-file-if-TPM2_CreateL.patch +Patch0215: 0015-fix-checkquote-fix-off-by-one-in-PCR-digest-list-han.patch +Patch0216: 0016-tpm2_checkquote-Add-size-checks-for-PCR-desearializa.patch +Patch0217: 0017-fix-tpm2_send-validate-command_size-before-computing.patch +Patch0218: 0018-fix-tpm2_send-avoid-unintended-stdio-closing-in-clos.patch +Patch0219: 0019-fix-tpm2_util-fix-error-message-in-tpm2_pem_encoded_.patch +Patch0220: 0020-fix-tpm2_util-modify-tpm2_safe_read_from_stdin-to-re.patch +Patch0221: 0021-fix-tools-out-of-bounds-write-with-too-many-auxiliar.patch +Patch0222: 0022-fix-tpm2_alg_util-avoid-mutating-input-scheme-in-han.patch +Patch0223: 0023-fix-tools-add-missing-null-checks-after-malloc.patch +Patch0224: 0024-fix-test-add-missing-null-checks-after-malloc.patch +Patch0225: 0025-fix-tpm2_util-fix-buffer-overflow-in-string-validati.patch +Patch0226: 0026-Allocate-the-right-amount-of-memory-for-auth_policy.patch +Patch0227: 0027-tpm2_util-add-some-checks-to-tpm2_safe_read_from_std.patch +Patch0228: 0028-tpm2_createak-add-attribute-option.patch +Patch0229: 0029-fix-tpm2_loadexternal-check-return-value-of-RAND_byt.patch +Patch0230: 0030-fix-tpm2_identity_util-add-buffer-size-check.patch # tpm2-tools is heavily depending on TPM2.0-TSS project, matched tss is required Requires: tpm2-tss%{?_isa} >= 3.1.0 @@ -80,6 +107,10 @@ autoreconf -i %{_mandir}/man1/tss2_*.1.gz %changelog +* Wed Apr 15 2026 Štěpán Horáček - 5.7-6 +- Backport upstream fixes + Resolves: RHEL-164796 + * Thu Aug 21 2025 Štěpán Horáček - 5.7-5 - Backport upstream fixes Resolves: RHEL-94930