From f29c7d6dbea5deeee97cf817e4f3c4e6dee4b77a Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 4 Aug 2026 14:09:27 -0700 Subject: [PATCH] Add caller-supplied policy authorization for TPM firmware upgrade --- CMakeLists.txt | 9 +- examples/firmware/README.md | 56 +++++ examples/firmware/firmware_policy.c | 293 +++++++++++++++++++++++++ examples/firmware/firmware_policy.h | 66 ++++++ examples/firmware/ifx_fw_update.c | 135 ++++++++++-- examples/firmware/include.am | 8 + examples/firmware/st33_fw_update.c | 121 ++++++++++- examples/nvram/extend.c | 20 +- src/tpm2_wrap.c | 320 ++++++++++++++++++++++------ tests/unit_tests.c | 167 +++++++++++++++ wolftpm/tpm2_wrap.h | 186 ++++++++++++++++ 11 files changed, 1278 insertions(+), 103 deletions(-) create mode 100644 examples/firmware/firmware_policy.c create mode 100644 examples/firmware/firmware_policy.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a3469ad17..bf78d9ee5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -480,8 +480,13 @@ if (WOLFTPM_EXAMPLES AND BUILD_WOLFTPM_LIB) endif() function(add_tpm_example name src) + # Optional additional sources may be passed after 'src' (ARGN) + set(_example_srcs examples/${src}) + foreach(_extra ${ARGN}) + list(APPEND _example_srcs examples/${_extra}) + endforeach() add_executable(${name} - examples/${src} + ${_example_srcs} ) target_link_libraries(${name} PRIVATE wolftpm tpm_test_lib wolftpm_wolfssl_dep) if(WIN32) @@ -705,7 +710,7 @@ if (WOLFTPM_EXAMPLES AND BUILD_WOLFTPM_LIB) add_tpm_example(secure_rot boot/secure_rot.c) add_tpm_example(csr csr/csr.c) add_tpm_example(get_ek_certs endorsement/get_ek_certs.c) - add_tpm_example(ifx_fw_update firmware/ifx_fw_update.c) + add_tpm_example(ifx_fw_update firmware/ifx_fw_update.c firmware/firmware_policy.c) add_tpm_example(gpio_config gpio/gpio_config.c) add_tpm_example(gpio_read gpio/gpio_read.c) add_tpm_example(gpio_set gpio/gpio_set.c) diff --git a/examples/firmware/README.md b/examples/firmware/README.md index 66d0c60d6..7a839fba1 100644 --- a/examples/firmware/README.md +++ b/examples/firmware/README.md @@ -199,3 +199,59 @@ Success: Please reset or power cycle TPM ``` **Note**: Firmware files cannot be made public and must be obtained separately from STMicroelectronics. + +## Policy-Based Authorization (Advanced) + +By default wolfTPM manages the platform-hierarchy authorization for the firmware-update *start* command internally: on Infineon it installs and satisfies a `PolicyCommandCode(TPM_CC_FieldUpgradeStartVendor)` policy on the platform primary policy, and on ST33 it uses password authorization (`TPM_RS_PW`) with an empty platform password. This assumes the platform hierarchy has default/empty authorization. + +Deployments that gate firmware upgrade behind their own platform policy (for example a signed-policy check, a PCR state, or a multi-branch `PolicyOR`) can supply an already-satisfied authorization session using `wolfTPM2_FirmwareUpgradeHash_ex()`. When a session is supplied: + +- **Infineon**: the library does **not** overwrite your platform primary policy. You provision the platform `authPolicy` yourself (via `TPM2_SetPrimaryPolicy` with `authHandle = TPM_RH_PLATFORM`, using SHA2-256 or SHA2-512) and pass a session that satisfies it. +- **ST33**: the supplied session replaces the default `TPM_RS_PW` password authorization. + +Both SHA2-256 (non-PQC) and SHA2-512 (PQC) policy digests are supported, because the session hash is chosen with `wolfTPM2_StartSession_ex(..., authHash)` and `wolfTPM2_PolicyOR()` carries per-branch digest sizes. + +Example: satisfy a multi-branch `PolicyOR` (up to 8 branches, SHA2-512 shown) and start the upgrade under it: + +```c +WOLFTPM2_SESSION session; +TPML_DIGEST orList; +uint8_t manifest_hash[TPM_SHA512_DIGEST_SIZE]; +int rc; + +/* zero both structs - orList must not carry uninitialized branch sizes */ +XMEMSET(&session, 0, sizeof(session)); +XMEMSET(&orList, 0, sizeof(orList)); + +/* start a policy session using the desired policy hash (SHA2-512 for PQC) */ +rc = wolfTPM2_StartSession_ex(&dev, &session, NULL, NULL, + TPM_SE_POLICY, TPM_ALG_NULL, TPM_ALG_SHA512); +if (rc != TPM_RC_SUCCESS) goto cleanup; + +/* Satisfy one branch (PCR, PolicySigned/Authorize, PolicyAuthValue, ...), then + * OR against the full branch list the platform authPolicy encodes. Set count + * and each digests[i].size/buffer for every branch you populate. */ +orList.count = 2; +/* orList.digests[0].size = ...; XMEMCPY(orList.digests[0].buffer, ...); */ +/* orList.digests[1].size = ...; XMEMCPY(orList.digests[1].buffer, ...); */ +rc = wolfTPM2_PolicyOR(&dev, &session, &orList); +if (rc != TPM_RC_SUCCESS) goto cleanup; + +/* hash the manifest with the matching algorithm, then start the upgrade under + * the caller-satisfied session (NULL would use the library-default auth) */ +rc = wc_Sha512Hash(manifest, manifest_sz, manifest_hash); +if (rc != 0) goto cleanup; +rc = wolfTPM2_FirmwareUpgradeHash_ex(&dev, TPM_ALG_SHA512, + manifest_hash, (uint32_t)sizeof(manifest_hash), + manifest, manifest_sz, fwDataCb, fwCbCtx, &session); + +cleanup: +/* On a successful FieldUpgradeStart the library zeroes session.handle.hndl (the + * TPM consumed the session), so this only releases a still-loaded session. */ +if (session.handle.hndl != 0) + wolfTPM2_UnloadHandle(&dev, &session.handle); +``` + +Passing `NULL` for the final `startSession` argument makes `wolfTPM2_FirmwareUpgradeHash_ex()` behave exactly like `wolfTPM2_FirmwareUpgradeHash()` (library-managed authorization), so existing code is unaffected. + +**Note:** the example `--policy`/`--policyor` modes provision the platform hierarchy `authPolicy` via `TPM2_SetPrimaryPolicy` before the upgrade. On failure the example restores the default (clears the policy) so a later default-auth run is not locked out; on success the required TPM reset clears it. If a run is interrupted before that cleanup, the platform hierarchy may still require the policy until the TPM is reset/power-cycled. diff --git a/examples/firmware/firmware_policy.c b/examples/firmware/firmware_policy.c new file mode 100644 index 000000000..8b67bb7f5 --- /dev/null +++ b/examples/firmware/firmware_policy.c @@ -0,0 +1,293 @@ +/* firmware_policy.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfTPM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +/* These helpers are built on the wolfTPM2 wrapper API and use wolfCrypt hashing */ +#if defined(WOLFTPM_FIRMWARE_UPGRADE) && !defined(WOLFTPM2_NO_WRAPPER) && \ + !defined(WOLFTPM2_NO_WOLFCRYPT) + +#include + +/* Print a digest as hex. Unlike TPM2_PrintBin (a no-op unless DEBUG_WOLFTPM), + * this is always available so the self-test failure report is usable in a + * stock build. */ +static void firmware_print_hex(const byte* buf, word32 len) +{ + word32 j; + for (j = 0; j < len; j++) { + printf("%02x", buf[j]); + } + printf("\n"); +} + +/* Exercise wolfTPM2_PolicyOR at the requested hash and verify the TPM's + * running policy digest matches an offline computation. Non-destructive. + * Returns 0 on match, 1 if the hash is not implemented (intentional skip), + * -1 on digest mismatch, or a TPM rc / BAD_FUNC_ARG on other errors. */ +static int firmware_policy_selftest(WOLFTPM2_DEV* dev, TPMI_ALG_HASH hashAlg, + const char* name) +{ + int rc; + WOLFTPM2_SESSION sess; + TPML_DIGEST orList; + word32 hsz = (word32)TPM2_GetHashDigestSize(hashAlg); + byte branchA[TPM_MAX_DIGEST_SIZE]; + byte branchB[TPM_MAX_DIGEST_SIZE]; + byte concat[2 * TPM_MAX_DIGEST_SIZE]; + byte expected[TPM_MAX_DIGEST_SIZE]; + byte got[TPM_MAX_DIGEST_SIZE]; + word32 aSz = 0, bSz = 0, expSz = 0, gotSz = 0; + + XMEMSET(&sess, 0, sizeof(sess)); + XMEMSET(&orList, 0, sizeof(orList)); + + if (hsz == 0 || hsz > TPM_MAX_DIGEST_SIZE) { + return BAD_FUNC_ARG; + } + + /* Skip cleanly if the TPM does not implement this hash; a negative rc is a + * query failure (reported), distinct from "not implemented" (0). */ + rc = wolfTPM2_IsAlgSupported(dev, hashAlg); + if (rc == 0) { + printf(" %s: skipped (not implemented by this TPM)\n", name); + return 1; /* intentional skip, not a failure */ + } + if (rc != 1) { + printf(" %s: capability query failed 0x%x: %s\n", + name, rc, TPM2_GetRCString(rc)); + return rc; + } + + /* Offline: two distinct PolicyCommandCode branch digests */ + aSz = hsz; + rc = wolfTPM2_PolicyCommandCodeMake(hashAlg, branchA, &aSz, TPM_CC_NV_Read); + if (rc == 0) { + bSz = hsz; + rc = wolfTPM2_PolicyCommandCodeMake(hashAlg, branchB, &bSz, + TPM_CC_Unseal); + } + /* Offline PolicyOR digest = H(zeros || TPM_CC_PolicyOR || A || B) */ + if (rc == 0) { + XMEMCPY(concat, branchA, aSz); + XMEMCPY(&concat[aSz], branchB, bSz); + XMEMSET(expected, 0, sizeof(expected)); + expSz = hsz; + rc = wolfTPM2_PolicyHash(hashAlg, expected, &expSz, + TPM_CC_PolicyOR, concat, aSz + bSz); + } + + /* On-TPM: start a policy session using the requested hash algorithm */ + if (rc == 0) { + rc = wolfTPM2_StartSession_ex(dev, &sess, NULL, NULL, + TPM_SE_POLICY, TPM_ALG_NULL, hashAlg); + if (rc != 0) { + printf(" %s: StartSession failed 0x%x: %s\n", + name, rc, TPM2_GetRCString(rc)); + return rc; + } + } + /* Satisfy branch A, then OR against {A,B} with the new wrapper */ + if (rc == 0) { + rc = wolfTPM2_PolicyCommandCode(dev, &sess, TPM_CC_NV_Read); + } + if (rc == 0) { + orList.count = 2; + orList.digests[0].size = (UINT16)aSz; + XMEMCPY(orList.digests[0].buffer, branchA, aSz); + orList.digests[1].size = (UINT16)bSz; + XMEMCPY(orList.digests[1].buffer, branchB, bSz); + rc = wolfTPM2_PolicyOR(dev, &sess, &orList); + } + if (rc == 0) { + gotSz = (word32)sizeof(got); + rc = wolfTPM2_GetPolicyDigest(dev, sess.handle.hndl, got, &gotSz); + } + + if (rc == 0) { + if (gotSz == expSz && XMEMCMP(got, expected, expSz) == 0) { + printf(" %s PolicyOR: PASS (%u byte digest matches)\n", + name, expSz); + } + else { + printf(" %s PolicyOR: FAIL (digest mismatch)\n", name); + printf(" expected: "); + firmware_print_hex(expected, expSz); + printf(" got: "); + firmware_print_hex(got, gotSz); + rc = -1; + } + } + else { + printf(" %s PolicyOR: ERROR 0x%x: %s\n", + name, rc, TPM2_GetRCString(rc)); + } + + wolfTPM2_UnloadHandle(dev, &sess.handle); + return rc; +} + +int firmware_policy_selftest_all(WOLFTPM2_DEV* dev) +{ + int i, rc, hardFail = 0; + struct { TPMI_ALG_HASH alg; const char* name; } hashes[3]; + + hashes[0].alg = TPM_ALG_SHA256; hashes[0].name = "SHA2-256"; + hashes[1].alg = TPM_ALG_SHA384; hashes[1].name = "SHA2-384"; + hashes[2].alg = TPM_ALG_SHA512; hashes[2].name = "SHA2-512"; + + printf("Firmware policy authorization self-test " + "(no firmware changes):\n"); + for (i = 0; i < 3; i++) { + rc = firmware_policy_selftest(dev, hashes[i].alg, hashes[i].name); + /* rc == 1 is an intentional "hash not implemented" skip. Any other + * non-zero (digest mismatch, bad arg, or a TPM rc) is a failure. */ + if (rc != 0 && rc != 1) { + hardFail = 1; + } + } + return hardFail ? -1 : 0; +} + +void firmware_policy_clear(WOLFTPM2_DEV* dev) +{ + if (wolfTPM2_SetPrimaryPolicy(dev, TPM_RH_PLATFORM, TPM_ALG_NULL, + NULL, 0) == TPM_RC_SUCCESS) { + printf("Cleared platform policy (restored default auth)\n"); + } +} + +int firmware_policy_session_setup(WOLFTPM2_DEV* dev, + TPMI_ALG_HASH hashAlg, int useOr, TPM_CC fuStartCC, + WOLFTPM2_SESSION* session) +{ + int rc; + int provisioned = 0; + TPML_DIGEST orList; + word32 hsz = (word32)TPM2_GetHashDigestSize(hashAlg); + byte branchA[TPM_MAX_DIGEST_SIZE]; + byte branchB[TPM_MAX_DIGEST_SIZE]; + byte concat[2 * TPM_MAX_DIGEST_SIZE]; + byte platformPolicy[TPM_MAX_DIGEST_SIZE]; + word32 aSz, bSz = 0, polSz = 0; + + if (hsz == 0 || hsz > TPM_MAX_DIGEST_SIZE) { + return BAD_FUNC_ARG; + } + XMEMSET(session, 0, sizeof(*session)); + XMEMSET(&orList, 0, sizeof(orList)); + + /* Fail early (before provisioning) if the TPM can't use this policy hash. + * rc==0 means not implemented; a negative rc is a query failure. */ + rc = wolfTPM2_IsAlgSupported(dev, hashAlg); + if (rc == 0) { + printf("Policy hash %s not implemented by this TPM\n", + TPM2_GetAlgName(hashAlg)); + return BAD_FUNC_ARG; + } + if (rc != 1) { + printf("Capability query failed 0x%x: %s\n", + rc, TPM2_GetRCString(rc)); + return rc; + } + + printf("Provisioning platform policy (%s, %s)\n", + useOr ? "PolicyOR" : "PolicyCommandCode", + TPM2_GetAlgName(hashAlg)); + + /* Branch A: PolicyCommandCode(FieldUpgradeStart) - required to start FU */ + aSz = hsz; + rc = wolfTPM2_PolicyCommandCodeMake(hashAlg, branchA, &aSz, fuStartCC); + + /* Compute the platform authPolicy digest */ + if (rc == 0) { + if (useOr) { + /* Branch B: a second, distinct policy branch */ + bSz = hsz; + rc = wolfTPM2_PolicyCommandCodeMake(hashAlg, branchB, &bSz, + TPM_CC_NV_Read); + if (rc == 0) { + XMEMCPY(concat, branchA, aSz); + XMEMCPY(&concat[aSz], branchB, bSz); + XMEMSET(platformPolicy, 0, sizeof(platformPolicy)); + polSz = hsz; + rc = wolfTPM2_PolicyHash(hashAlg, platformPolicy, &polSz, + TPM_CC_PolicyOR, concat, aSz + bSz); + } + } + else { + XMEMCPY(platformPolicy, branchA, aSz); + polSz = aSz; + } + } + + /* Provision the platform primary policy (empty platformAuth) */ + if (rc == 0) { + rc = wolfTPM2_SetPrimaryPolicy(dev, TPM_RH_PLATFORM, hashAlg, + platformPolicy, polSz); + if (rc != 0) { + printf(" SetPrimaryPolicy failed 0x%x: %s\n", + rc, TPM2_GetRCString(rc)); + } + else { + provisioned = 1; + } + } + + /* Start a policy session and satisfy the platform policy */ + if (rc == 0) { + rc = wolfTPM2_StartSession_ex(dev, session, NULL, NULL, + TPM_SE_POLICY, TPM_ALG_NULL, hashAlg); + if (rc != 0) { + printf(" StartSession failed 0x%x: %s\n", + rc, TPM2_GetRCString(rc)); + } + } + if (rc == 0) { + rc = wolfTPM2_PolicyCommandCode(dev, session, fuStartCC); + } + if (rc == 0 && useOr) { + orList.count = 2; + orList.digests[0].size = (UINT16)aSz; + XMEMCPY(orList.digests[0].buffer, branchA, aSz); + orList.digests[1].size = (UINT16)bSz; + XMEMCPY(orList.digests[1].buffer, branchB, bSz); + rc = wolfTPM2_PolicyOR(dev, session, &orList); + } + + if (rc != 0) { + if (session->handle.hndl != 0) { + wolfTPM2_UnloadHandle(dev, &session->handle); + } + /* Restore default platform auth so a later run is not locked out (the + * platform policy is otherwise cleared only on TPM reset). */ + if (provisioned) { + firmware_policy_clear(dev); + } + } + return rc; +} + +#endif /* WOLFTPM_FIRMWARE_UPGRADE && !NO_WRAPPER && !NO_WOLFCRYPT */ diff --git a/examples/firmware/firmware_policy.h b/examples/firmware/firmware_policy.h new file mode 100644 index 000000000..5aa327274 --- /dev/null +++ b/examples/firmware/firmware_policy.h @@ -0,0 +1,66 @@ +/* firmware_policy.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfTPM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Shared caller-supplied policy authorization helpers for the firmware update + * examples (ifx_fw_update and st33_fw_update). The vendor difference is carried + * by the fuStartCC parameter, so there is no per-vendor logic here. */ + +#ifndef WOLFTPM_EXAMPLE_FIRMWARE_POLICY_H +#define WOLFTPM_EXAMPLE_FIRMWARE_POLICY_H + +#include + +/* These helpers are built on the wolfTPM2 wrapper API and use wolfCrypt hashing */ +#if defined(WOLFTPM_FIRMWARE_UPGRADE) && !defined(WOLFTPM2_NO_WRAPPER) && \ + !defined(WOLFTPM2_NO_WOLFCRYPT) + +#ifdef __cplusplus +extern "C" { +#endif + +/* Non-destructive self-test: exercises wolfTPM2_PolicyOR at SHA-256/384/512 and + * checks the TPM's running policy digest against an offline computation. A hash + * the TPM does not implement is reported and skipped. Returns 0 on overall + * success (all supported hashes matched), -1 if any supported hash failed. */ +int firmware_policy_selftest_all(WOLFTPM2_DEV* dev); + +/* Provision the platform authPolicy and return a session that satisfies it, so + * the firmware-start command can be authorized by a caller-controlled policy + * instead of the vendor default. When useOr is set the platform policy is a + * PolicyOR of two branches; otherwise a single PolicyCommandCode branch. + * fuStartCC is the vendor FieldUpgrade start command code. On success *session + * is started and satisfied (the caller passes it to wolfTPM2_FirmwareUpgrade_ex + * and must UnloadHandle it). On failure any provisioned platform policy is + * cleared so a later default-auth run is not locked out. */ +int firmware_policy_session_setup(WOLFTPM2_DEV* dev, + TPMI_ALG_HASH hashAlg, int useOr, TPM_CC fuStartCC, + WOLFTPM2_SESSION* session); + +/* Clear the platform authPolicy (restore default auth) so a later default-auth + * run is not locked out. Call after a failed policy-mode upgrade. */ +void firmware_policy_clear(WOLFTPM2_DEV* dev); + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFTPM_FIRMWARE_UPGRADE && !NO_WRAPPER && !NO_WOLFCRYPT */ +#endif /* WOLFTPM_EXAMPLE_FIRMWARE_POLICY_H */ diff --git a/examples/firmware/ifx_fw_update.c b/examples/firmware/ifx_fw_update.c index 9934e229b..cd8a0df71 100644 --- a/examples/firmware/ifx_fw_update.c +++ b/examples/firmware/ifx_fw_update.c @@ -32,9 +32,24 @@ (defined(WOLFTPM_SLB9672) || defined(WOLFTPM_SLB9673)) #include +#include #include #include +/* The caller-supplied policy helpers require wolfCrypt (policy digest hashing) */ +#ifndef WOLFTPM2_NO_WOLFCRYPT + #define HAVE_FW_POLICY +#endif + +/* Caller-supplied policy authorization modes */ +#define IFX_POLICY_NONE 0 /* library-managed authorization */ +#define IFX_POLICY_CMDCODE 1 /* --policy: single PolicyCommandCode */ +#define IFX_POLICY_OR 2 /* --policyor: multi-branch PolicyOR */ + +/* Infineon operational modes (subset used here) */ +#define IFX_OPMODE_NORMAL 0x00 /* normal; FieldUpgradeStart reached */ +#define IFX_OPMODE_FINALIZE 0x03 /* update done, finalize only */ + /******************************************************************************/ /* --- BEGIN TPM2.0 Firmware Update tool -- */ /******************************************************************************/ @@ -44,7 +59,14 @@ static void usage(void) printf("Infineon Firmware Update Usage:\n"); printf("\t./ifx_fw_update (get info)\n"); printf("\t./ifx_fw_update --abandon (cancel)\n"); - printf("\t./ifx_fw_update \n"); + printf("\t./ifx_fw_update --policytest (safe policy auth self-test)\n"); + printf("\t./ifx_fw_update [policy opts] \n"); + printf("\t./ifx_fw_update " + "(default auth)\n"); + printf("Policy options (caller-supplied authorization):\n"); + printf("\t--policy provision+satisfy a PolicyCommandCode\n"); + printf("\t--policyor provision+satisfy a PolicyOR (multi-branch)\n"); + printf("\t--sha256|--sha384|--sha512 policy hash (default SHA-256)\n"); } typedef struct { @@ -115,24 +137,64 @@ int TPM2_IFX_Firmware_Update(void* userCtx, int argc, char *argv[]) const char* firmware_file = NULL; fw_info_t fwinfo; int abandon = 0, recovery = 0; + int i; +#ifdef HAVE_FW_POLICY + int policytest = 0; + int policyMode = IFX_POLICY_NONE; + TPMI_ALG_HASH policyHash = TPM_ALG_SHA256; + WOLFTPM2_SESSION policySession; +#endif XMEMSET(&fwinfo, 0, sizeof(fwinfo)); +#ifdef HAVE_FW_POLICY + XMEMSET(&policySession, 0, sizeof(policySession)); +#endif - if (argc >= 2) { - if (XSTRCMP(argv[1], "-?") == 0 || - XSTRCMP(argv[1], "-h") == 0 || - XSTRCMP(argv[1], "--help") == 0) { + for (i = 1; i < argc; i++) { + if (XSTRCMP(argv[i], "-?") == 0 || + XSTRCMP(argv[i], "-h") == 0 || + XSTRCMP(argv[i], "--help") == 0) { usage(); return 0; } - if (XSTRCMP(argv[1], "--abandon") == 0) { + else if (XSTRCMP(argv[i], "--abandon") == 0) { abandon = 1; } +#ifdef HAVE_FW_POLICY + else if (XSTRCMP(argv[i], "--policytest") == 0) { + policytest = 1; + } + else if (XSTRCMP(argv[i], "--policy") == 0) { + policyMode = IFX_POLICY_CMDCODE; + } + else if (XSTRCMP(argv[i], "--policyor") == 0) { + policyMode = IFX_POLICY_OR; + } + else if (XSTRCMP(argv[i], "--sha256") == 0) { + policyHash = TPM_ALG_SHA256; + } + else if (XSTRCMP(argv[i], "--sha384") == 0) { + policyHash = TPM_ALG_SHA384; + } + else if (XSTRCMP(argv[i], "--sha512") == 0) { + policyHash = TPM_ALG_SHA512; + } +#endif /* HAVE_FW_POLICY */ + else if (argv[i][0] == '-') { + printf("Unrecognized option: %s\n", argv[i]); + usage(); + return BAD_FUNC_ARG; + } + else if (manifest_file == NULL) { + manifest_file = argv[i]; + } + else if (firmware_file == NULL) { + firmware_file = argv[i]; + } else { - manifest_file = argv[1]; - if (argc >= 3) { - firmware_file = argv[2]; - } + printf("Unexpected extra argument: %s\n", argv[i]); + usage(); + return BAD_FUNC_ARG; } } @@ -148,6 +210,16 @@ int TPM2_IFX_Firmware_Update(void* userCtx, int argc, char *argv[]) goto exit; } +#ifdef HAVE_FW_POLICY + if (policytest) { + /* Non-destructive validation of caller-supplied policy authorization. + * Does not touch firmware upgrade state. */ + rc = firmware_policy_selftest_all(&dev); + wolfTPM2_Cleanup(&dev); + return rc; + } +#endif + rc = wolfTPM2_GetCapabilities(&dev, &caps); if (rc != TPM_RC_SUCCESS) { goto exit; @@ -187,18 +259,36 @@ int TPM2_IFX_Firmware_Update(void* userCtx, int argc, char *argv[]) rc = loadFile(firmware_file, &fwinfo.firmware_buf, &fwinfo.firmware_bufSz); } +#ifdef HAVE_FW_POLICY + /* When a policy mode is requested, provision the platform authPolicy and + * build a session that satisfies it, then drive the upgrade under that + * caller-supplied session instead of the library-managed authorization. */ + if (rc == 0 && policyMode != IFX_POLICY_NONE) { + rc = firmware_policy_session_setup(&dev, policyHash, + (policyMode == IFX_POLICY_OR), TPM_CC_FieldUpgradeStartVendor, + &policySession); + } +#endif if (rc == 0) { + WOLFTPM2_SESSION* startSess = NULL; + #ifdef HAVE_FW_POLICY + if (policyMode != IFX_POLICY_NONE) { + startSess = &policySession; + } + #endif if (recovery) { - printf("Firmware Update (recovery mode):\n"); - rc = wolfTPM2_FirmwareUpgradeRecover(&dev, + printf("Firmware Update (recovery mode%s):\n", + startSess ? ", caller policy" : ""); + rc = wolfTPM2_FirmwareUpgradeRecover_ex(&dev, fwinfo.manifest_buf, (uint32_t)fwinfo.manifest_bufSz, - TPM2_IFX_FwData_Cb, &fwinfo); + TPM2_IFX_FwData_Cb, &fwinfo, startSess); } else { - printf("Firmware Update (normal mode):\n"); - rc = wolfTPM2_FirmwareUpgrade(&dev, + printf("Firmware Update (normal mode%s):\n", + startSess ? ", caller policy" : ""); + rc = wolfTPM2_FirmwareUpgrade_ex(&dev, fwinfo.manifest_buf, (uint32_t)fwinfo.manifest_bufSz, - TPM2_IFX_FwData_Cb, &fwinfo); + TPM2_IFX_FwData_Cb, &fwinfo, startSess); } } if (rc == 0) { @@ -212,6 +302,19 @@ int TPM2_IFX_Firmware_Update(void* userCtx, int argc, char *argv[]) rc, TPM2_GetRCString(rc)); } +#ifdef HAVE_FW_POLICY + if (policyMode != IFX_POLICY_NONE) { + /* The library zeroes the session handle when the TPM consumes it on a + * successful start; a non-zero handle means it was not consumed. */ + if (policySession.handle.hndl != 0) { + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + } + /* Clear a provisioned platform policy if the upgrade did not complete */ + if (rc != 0) { + firmware_policy_clear(&dev); + } + } +#endif XFREE(fwinfo.firmware_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(fwinfo.manifest_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); wolfTPM2_Cleanup(&dev); diff --git a/examples/firmware/include.am b/examples/firmware/include.am index 4c355b856..aaccc9116 100644 --- a/examples/firmware/include.am +++ b/examples/firmware/include.am @@ -7,13 +7,20 @@ EXTRA_DIST += examples/firmware/Makefile # Host side tool for extracting the firmware manifest and data EXTRA_DIST += examples/firmware/ifx_fw_extract.c +# Shared caller-supplied policy authorization helpers for the firmware examples +EXTRA_DIST += examples/firmware/firmware_policy.c +EXTRA_DIST += examples/firmware/firmware_policy.h + if BUILD_EXAMPLES if BUILD_FIRMWARE +noinst_HEADERS += examples/firmware/firmware_policy.h + if BUILD_INFINEON noinst_PROGRAMS += examples/firmware/ifx_fw_update noinst_HEADERS += examples/firmware/ifx_fw_update.h examples_firmware_ifx_fw_update_SOURCES = examples/firmware/ifx_fw_update.c \ + examples/firmware/firmware_policy.c \ examples/tpm_test_keys.c examples_firmware_ifx_fw_update_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD) examples_firmware_ifx_fw_update_DEPENDENCIES = src/libwolftpm.la @@ -22,6 +29,7 @@ endif if BUILD_ST33 noinst_PROGRAMS += examples/firmware/st33_fw_update examples_firmware_st33_fw_update_SOURCES = examples/firmware/st33_fw_update.c \ + examples/firmware/firmware_policy.c \ examples/tpm_test_keys.c examples_firmware_st33_fw_update_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD) examples_firmware_st33_fw_update_DEPENDENCIES = src/libwolftpm.la diff --git a/examples/firmware/st33_fw_update.c b/examples/firmware/st33_fw_update.c index 2120df3db..7460f355a 100644 --- a/examples/firmware/st33_fw_update.c +++ b/examples/firmware/st33_fw_update.c @@ -31,9 +31,20 @@ #if defined(WOLFTPM_FIRMWARE_UPGRADE) && \ (defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT)) +#include #include #include +/* The caller-supplied policy helpers require wolfCrypt (policy digest hashing) */ +#ifndef WOLFTPM2_NO_WOLFCRYPT + #define HAVE_FW_POLICY +#endif + +/* Caller-supplied policy authorization modes */ +#define ST33_POLICY_NONE 0 /* default password (TPM_RS_PW) auth */ +#define ST33_POLICY_CMDCODE 1 /* --policy: single PolicyCommandCode */ +#define ST33_POLICY_OR 2 /* --policyor: multi-branch PolicyOR */ + /******************************************************************************/ /* --- BEGIN ST33 TPM2.0 Firmware Update tool -- */ /******************************************************************************/ @@ -47,7 +58,13 @@ static void usage(void) printf("ST33 Firmware Update Usage:\n"); printf("\t./st33_fw_update (get info)\n"); printf("\t./st33_fw_update --abandon (cancel)\n"); - printf("\t./st33_fw_update \n"); + printf("\t./st33_fw_update --policytest (safe policy auth self-test)\n"); + printf("\t./st33_fw_update [policy opts] \n"); + printf("\t./st33_fw_update (default password auth)\n"); + printf("Policy options (caller-supplied authorization):\n"); + printf("\t--policy provision+satisfy a PolicyCommandCode\n"); + printf("\t--policyor provision+satisfy a PolicyOR (multi-branch)\n"); + printf("\t--sha256|--sha384|--sha512 policy hash (default SHA-256)\n"); printf("\nFirmware format is auto-detected from the TPM firmware version.\n"); printf("Just provide the correct .fi file for your TPM and it will be handled automatically.\n"); } @@ -185,22 +202,62 @@ int TPM2_ST33_Firmware_Update(void* userCtx, int argc, char *argv[]) fw_info_t fwinfo; int abandon = 0; size_t blob0_size; + int i; +#ifdef HAVE_FW_POLICY + int policytest = 0; + int policyMode = ST33_POLICY_NONE; + TPMI_ALG_HASH policyHash = TPM_ALG_SHA256; + WOLFTPM2_SESSION policySession; +#endif XMEMSET(&fwinfo, 0, sizeof(fwinfo)); XMEMSET(&caps, 0, sizeof(caps)); +#ifdef HAVE_FW_POLICY + XMEMSET(&policySession, 0, sizeof(policySession)); +#endif - if (argc >= 2) { - if (XSTRCMP(argv[1], "-?") == 0 || - XSTRCMP(argv[1], "-h") == 0 || - XSTRCMP(argv[1], "--help") == 0) { + for (i = 1; i < argc; i++) { + if (XSTRCMP(argv[i], "-?") == 0 || + XSTRCMP(argv[i], "-h") == 0 || + XSTRCMP(argv[i], "--help") == 0) { usage(); return 0; } - if (XSTRCMP(argv[1], "--abandon") == 0) { + else if (XSTRCMP(argv[i], "--abandon") == 0) { abandon = 1; } +#ifdef HAVE_FW_POLICY + else if (XSTRCMP(argv[i], "--policytest") == 0) { + policytest = 1; + } + else if (XSTRCMP(argv[i], "--policy") == 0) { + policyMode = ST33_POLICY_CMDCODE; + } + else if (XSTRCMP(argv[i], "--policyor") == 0) { + policyMode = ST33_POLICY_OR; + } + else if (XSTRCMP(argv[i], "--sha256") == 0) { + policyHash = TPM_ALG_SHA256; + } + else if (XSTRCMP(argv[i], "--sha384") == 0) { + policyHash = TPM_ALG_SHA384; + } + else if (XSTRCMP(argv[i], "--sha512") == 0) { + policyHash = TPM_ALG_SHA512; + } +#endif /* HAVE_FW_POLICY */ + else if (argv[i][0] == '-') { + printf("Unrecognized option: %s\n", argv[i]); + usage(); + return BAD_FUNC_ARG; + } + else if (fi_file == NULL) { + fi_file = argv[i]; + } else { - fi_file = argv[1]; + printf("Unexpected extra argument: %s\n", argv[i]); + usage(); + return BAD_FUNC_ARG; } } @@ -245,6 +302,18 @@ int TPM2_ST33_Firmware_Update(void* userCtx, int argc, char *argv[]) goto exit; } +#ifdef HAVE_FW_POLICY + if (policytest) { + /* Non-destructive validation of caller-supplied policy authorization. + * Runs SHA2-256/384/512 PolicyOR digest checks (a hash the TPM does + * not support is reported and skipped). Does not touch firmware + * upgrade state. */ + rc = firmware_policy_selftest_all(&dev); + wolfTPM2_Cleanup(&dev); + return rc; + } +#endif + rc = wolfTPM2_GetCapabilities(&dev, &caps); if (rc != TPM_RC_SUCCESS) { printf("wolfTPM2_GetCapabilities failed 0x%x: %s\n", @@ -351,10 +420,27 @@ int TPM2_ST33_Firmware_Update(void* userCtx, int argc, char *argv[]) rc = TPM2_ST33_SendFirmwareData(&fwinfo); } else { - /* Normal mode - use unified API which auto-detects format from manifest size */ - rc = wolfTPM2_FirmwareUpgrade(&dev, - fwinfo.manifest_buf, (uint32_t)fwinfo.manifest_bufSz, - TPM2_ST33_FwData_Cb, &fwinfo); + WOLFTPM2_SESSION* startSess = NULL; + #ifdef HAVE_FW_POLICY + /* When a policy mode is requested, provision the platform authPolicy + * and build a session that satisfies it, then drive the upgrade under + * that caller-supplied session instead of the default password auth. */ + if (policyMode != ST33_POLICY_NONE) { + rc = firmware_policy_session_setup(&dev, policyHash, + (policyMode == ST33_POLICY_OR), + TPM_CC_FieldUpgradeStartVendor_ST33, &policySession); + if (rc == 0) { + printf("Using caller-supplied policy session\n"); + startSess = &policySession; + } + } + #endif + /* Normal mode - unified API auto-detects format from manifest size */ + if (rc == 0) { + rc = wolfTPM2_FirmwareUpgrade_ex(&dev, + fwinfo.manifest_buf, (uint32_t)fwinfo.manifest_bufSz, + TPM2_ST33_FwData_Cb, &fwinfo, startSess); + } } if (rc == 0) { printf("\nFirmware update completed successfully.\n"); @@ -377,6 +463,19 @@ int TPM2_ST33_Firmware_Update(void* userCtx, int argc, char *argv[]) rc, TPM2_GetRCString(rc)); } +#ifdef HAVE_FW_POLICY + if (policyMode != ST33_POLICY_NONE) { + /* The library zeroes the session handle when the TPM consumes it on a + * successful start; a non-zero handle means it was not consumed. */ + if (policySession.handle.hndl != 0) { + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + } + /* Clear a provisioned platform policy if the upgrade did not complete */ + if (rc != 0) { + firmware_policy_clear(&dev); + } + } +#endif /* Only free the main fi_buf - manifest_buf and firmware_buf point into it */ XFREE(fwinfo.fi_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); wolfTPM2_Cleanup(&dev); diff --git a/examples/nvram/extend.c b/examples/nvram/extend.c index 1601bdd0e..0b07e1ee1 100644 --- a/examples/nvram/extend.c +++ b/examples/nvram/extend.c @@ -50,14 +50,6 @@ static void usage(void) printf("* -aes/xor: Use Parameter Encryption\n");; } -static int BuildPolicyCommandCode(TPMI_ALG_HASH hashAlg, - byte* digest, word32* digestSz, TPM_CC cc) -{ - word32 val = cpu_to_be32(cc); - return wolfTPM2_PolicyHash(hashAlg, digest, digestSz, - TPM_CC_PolicyCommandCode, (byte*)&val, sizeof(val)); -} - static int PolicyOrApply(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* policySession, byte** hashList, word32 hashListSz, word32 digestSz) { @@ -161,7 +153,9 @@ int TPM2_NVRAM_Extend_Example(void* userCtx, int argc, char *argv[]) /* Policy A: TPM2_PolicyCommandCode -> TPM_CC_NV_Read */ /* 47ce3032d8bad1f3089cb0c09088de43501491d460402b90cd1b7fc0b68ca92f */ policy[0] = &policyDigest[policyDigestSz]; - BuildPolicyCommandCode(hashAlg, policy[0], &nvSize, TPM_CC_NV_Read); + rc = wolfTPM2_PolicyCommandCodeMake(hashAlg, policy[0], &nvSize, + TPM_CC_NV_Read); + if (rc != 0) goto exit; printf("PolicyA: %d\n", nvSize); TPM2_PrintBin(policy[0], nvSize); policyDigestSz += nvSize; @@ -169,7 +163,9 @@ int TPM2_NVRAM_Extend_Example(void* userCtx, int argc, char *argv[]) /* Policy B: TPM2_PolicyCommandCode -> TPM_CC_NV_Extend */ /* b6a2e7142ee56fd978047488483daa5b42b8dc4cc7ddcceddfb91793cf1ff1b7 */ policy[1] = &policyDigest[policyDigestSz]; - BuildPolicyCommandCode(hashAlg, policy[1], &nvSize, TPM_CC_NV_Extend); + rc = wolfTPM2_PolicyCommandCodeMake(hashAlg, policy[1], &nvSize, + TPM_CC_NV_Extend); + if (rc != 0) goto exit; printf("PolicyB: %d\n", nvSize); TPM2_PrintBin(policy[1], nvSize); policyDigestSz += nvSize; @@ -177,7 +173,9 @@ int TPM2_NVRAM_Extend_Example(void* userCtx, int argc, char *argv[]) /* Policy C: TPM2_PolicyCommandCode -> TPM_CC_PolicyNV */ /* 203e4bd5d0448c9615cc13fa18e8d39222441cc40204d99a77262068dbd55a43 */ policy[2] = &policyDigest[policyDigestSz]; - BuildPolicyCommandCode(hashAlg, policy[2], &nvSize, TPM_CC_PolicyNV); + rc = wolfTPM2_PolicyCommandCodeMake(hashAlg, policy[2], &nvSize, + TPM_CC_PolicyNV); + if (rc != 0) goto exit; printf("PolicyC: %d\n", nvSize); TPM2_PrintBin(policy[2], nvSize); policyDigestSz += nvSize; diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 041931b09..be75c6830 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -1010,6 +1010,37 @@ int wolfTPM2_GetCapabilities(WOLFTPM2_DEV* dev, WOLFTPM2_CAPS* cap) return wolfTPM2_GetCapabilities_NoDev(cap); } +/* Return 1 if the TPM implements the given algorithm, 0 otherwise. + * Queries TPM_CAP_ALGS: the TPM returns algorithms with ID >= property, so a + * match at index 0 for a single-property query means it is implemented. */ +int wolfTPM2_IsAlgSupported(WOLFTPM2_DEV* dev, TPM_ALG_ID alg) +{ + int rc; + GetCapability_In in; + GetCapability_Out out; + TPML_ALG_PROPERTY* algs; + + if (dev == NULL) { + return BAD_FUNC_ARG; + } + XMEMSET(&in, 0, sizeof(in)); + XMEMSET(&out, 0, sizeof(out)); + in.capability = TPM_CAP_ALGS; + in.property = alg; + in.propertyCount = 1; + rc = TPM2_GetCapability(&in, &out); + if (rc != TPM_RC_SUCCESS) { + return rc; /* propagate the query failure, distinct from "unsupported" */ + } + /* The TPM returns algorithms with ID >= property; a match at index 0 + * means the requested algorithm is implemented. */ + algs = &out.capabilityData.data.algorithms; + if (algs->count >= 1 && algs->algProperties[0].alg == alg) { + return 1; + } + return 0; +} + int wolfTPM2_GetHandles(TPM_HANDLE handle, TPML_HANDLE* handles) { int rc; @@ -10663,6 +10694,69 @@ int wolfTPM2_PolicyCommandCode(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* tpmSession, return TPM2_PolicyCommandCode(&policyCC); } +/* Satisfy a policy session with a compound OR of pre-computed policy digests. + * The digest list is hash-agnostic (each branch carries its own size), so it + * works for SHA2-256 as well as SHA2-512 policy branches. */ +int wolfTPM2_PolicyOR(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* tpmSession, + const TPML_DIGEST* pHashList) +{ + PolicyOR_In policyOR; + word32 i; + + if (dev == NULL || tpmSession == NULL || pHashList == NULL) { + return BAD_FUNC_ARG; + } + if (pHashList->count == 0 || + pHashList->count > (word32)(sizeof(pHashList->digests) / + sizeof(pHashList->digests[0]))) { + return BAD_FUNC_ARG; + } + /* Validate each branch digest size against its buffer so TPM2_PolicyOR + * cannot marshal past the fixed digest buffer (out-of-bounds read). */ + for (i = 0; i < pHashList->count; i++) { + if (pHashList->digests[i].size > + (UINT16)sizeof(pHashList->digests[i].buffer)) { + return BAD_FUNC_ARG; + } + } + + XMEMSET(&policyOR, 0, sizeof(policyOR)); + policyOR.policySession = tpmSession->handle.hndl; + XMEMCPY(&policyOR.pHashList, pHashList, sizeof(policyOR.pHashList)); + return TPM2_PolicyOR(&policyOR); +} + +/* Set (or clear) the authPolicy for a hierarchy (owner/endorsement/platform/ + * lockout). Pass authPolicy=NULL/authPolicySz=0 with hashAlg=TPM_ALG_NULL to + * clear an existing policy. */ +int wolfTPM2_SetPrimaryPolicy(WOLFTPM2_DEV* dev, + TPMI_RH_HIERARCHY_AUTH authHandle, TPM_ALG_ID hashAlg, + const byte* authPolicy, word32 authPolicySz) +{ + SetPrimaryPolicy_In in; + + if (dev == NULL) { + return BAD_FUNC_ARG; + } + if (authPolicySz > (word32)sizeof(in.authPolicy.buffer)) { + return BAD_FUNC_ARG; + } + /* Reject NULL policy with a non-zero size: setting size 0 here would clear + * the policy, silently downgrading "set" to "remove all policy" */ + if (authPolicy == NULL && authPolicySz > 0) { + return BAD_FUNC_ARG; + } + + XMEMSET(&in, 0, sizeof(in)); + in.authHandle = authHandle; + in.hashAlg = hashAlg; + if (authPolicy != NULL && authPolicySz > 0) { + in.authPolicy.size = (UINT16)authPolicySz; + XMEMCPY(in.authPolicy.buffer, authPolicy, authPolicySz); + } + return TPM2_SetPrimaryPolicy(&in); +} + #ifndef WOLFTPM2_NO_WOLFCRYPT /* Authorize a policy based on external key for a verified policy digiest signature */ int wolfTPM2_PolicyAuthorize(WOLFTPM2_DEV* dev, TPM_HANDLE sessionHandle, @@ -10829,6 +10923,33 @@ int wolfTPM2_PolicyHash(TPM_ALG_ID hashAlg, return rc; } +/* Assemble a PolicyCommandCode digest for a fresh policy session */ +/* policyDigest = hash(zeroDigest || TPM_CC_PolicyCommandCode || cc) */ +int wolfTPM2_PolicyCommandCodeMake(TPM_ALG_ID hashAlg, + byte* digest, word32* digestSz, TPM_CC cc) +{ + int hashSz; + byte val[4]; /* command code big-endian, matching the TPM wire format */ + + if (digest == NULL || digestSz == NULL) { + return BAD_FUNC_ARG; + } + hashSz = TPM2_GetHashDigestSize(hashAlg); + if (hashSz <= 0) { + return BAD_FUNC_ARG; + } + /* fresh policy session starts from a zero digest of the hash size */ + XMEMSET(digest, 0, hashSz); + *digestSz = (word32)hashSz; + + val[0] = (byte)((cc >> 24) & 0xFF); + val[1] = (byte)((cc >> 16) & 0xFF); + val[2] = (byte)((cc >> 8) & 0xFF); + val[3] = (byte)(cc & 0xFF); + return wolfTPM2_PolicyHash(hashAlg, digest, digestSz, + TPM_CC_PolicyCommandCode, val, sizeof(val)); +} + /* Assemble a PCR policy */ /* policyDigestnew = hash(policyDigestOld || TPM_CC_PolicyPCR || PCRS || * pcrDigest) */ @@ -11055,11 +11176,12 @@ int wolfTPM2_SetIdentityAuth(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* handle, static int tpm2_ifx_firmware_enable_policy(WOLFTPM2_DEV* dev) { int rc; - SetPrimaryPolicy_In policy; + byte policyDigest[TPM_MAX_DIGEST_SIZE]; + word32 policySz = (word32)sizeof(policyDigest); WOLFTPM2_SESSION tpmSession; XMEMSET(&tpmSession, 0, sizeof(tpmSession)); - XMEMSET(&policy, 0, sizeof(policy)); + XMEMSET(policyDigest, 0, sizeof(policyDigest)); rc = wolfTPM2_StartSession(dev, &tpmSession, NULL, NULL, TPM_SE_POLICY, TPM_ALG_NULL); @@ -11067,17 +11189,15 @@ static int tpm2_ifx_firmware_enable_policy(WOLFTPM2_DEV* dev) rc = wolfTPM2_PolicyCommandCode(dev, &tpmSession, TPM_CC_FieldUpgradeStartVendor); if (rc == TPM_RC_SUCCESS) { - word32 policySz = (word32)sizeof(policy.authPolicy.buffer); + policySz = (word32)sizeof(policyDigest); rc = wolfTPM2_GetPolicyDigest(dev, tpmSession.handle.hndl, - policy.authPolicy.buffer, &policySz); - policy.authPolicy.size = policySz; + policyDigest, &policySz); } wolfTPM2_UnloadHandle(dev, &tpmSession.handle); } if (rc == TPM_RC_SUCCESS) { - policy.authHandle = TPM_RH_PLATFORM; - policy.hashAlg = TPM_ALG_SHA256; - rc = TPM2_SetPrimaryPolicy(&policy); + rc = wolfTPM2_SetPrimaryPolicy(dev, TPM_RH_PLATFORM, TPM_ALG_SHA256, + policyDigest, policySz); } #ifdef DEBUG_WOLFTPM @@ -11090,51 +11210,75 @@ static int tpm2_ifx_firmware_enable_policy(WOLFTPM2_DEV* dev) } static int tpm2_ifx_firmware_start(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg, - uint8_t* manifest_hash, uint32_t manifest_hash_sz) + uint8_t* manifest_hash, uint32_t manifest_hash_sz, + WOLFTPM2_SESSION* startSession) { int rc; WOLFTPM2_SESSION tpmSession; + TPM_HANDLE sessionHandle = TPM_RH_NULL; + int ownSession = 0; XMEMSET(&tpmSession, 0, sizeof(tpmSession)); - rc = wolfTPM2_StartSession(dev, &tpmSession, NULL, NULL, - TPM_SE_POLICY, TPM_ALG_NULL); - if (rc == TPM_RC_SUCCESS) { - rc = wolfTPM2_PolicyCommandCode(dev, &tpmSession, - TPM_CC_FieldUpgradeStartVendor); - if (rc == TPM_RC_SUCCESS) { - /* build command for manifest header */ - uint16_t val16; - /* max cmd: type (1) + data sz (2) + hash alg (2) + max digest (64) */ - uint8_t cmd[1 + 2 + 2 + TPM_SHA512_DIGEST_SIZE]; - cmd[0] = 0x01; /* type */ - val16 = be16_to_cpu(manifest_hash_sz + 2); - XMEMCPY(&cmd[1], &val16, sizeof(val16)); /* data size */ - val16 = be16_to_cpu(hashAlg); - XMEMCPY(&cmd[3], &val16, sizeof(val16)); /* hash algorithm */ - XMEMCPY(&cmd[5], manifest_hash, manifest_hash_sz); - - rc = TPM2_IFX_FieldUpgradeStart(tpmSession.handle.hndl, - cmd, 1 + 2 + 2 + manifest_hash_sz); - } + if (startSession != NULL) { + /* Caller has already satisfied the platform policy on this session */ + sessionHandle = startSession->handle.hndl; + rc = TPM_RC_SUCCESS; + } + else { + /* Default: internal policy session asserting the firmware start + * command code, matching the policy installed on the platform + * hierarchy by tpm2_ifx_firmware_enable_policy */ + rc = wolfTPM2_StartSession(dev, &tpmSession, NULL, NULL, + TPM_SE_POLICY, TPM_ALG_NULL); if (rc == TPM_RC_SUCCESS) { - /* delay to give the TPM time to switch modes */ - XSLEEP_MS(300); - /* it is not required to release session handle, - * since TPM reset into firmware upgrade mode */ - - #if !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \ - !defined(WOLFTPM_WINAPI) - /* Do chip startup and request locality again */ - #ifdef WOLFTPM_LINUX_DEV_AUTODETECT - if (dev->ctx.fd < 0) /* Only needed for SPI path */ - #endif - rc = TPM2_ChipStartup(&dev->ctx, 10); - #endif + ownSession = 1; + sessionHandle = tpmSession.handle.hndl; + rc = wolfTPM2_PolicyCommandCode(dev, &tpmSession, + TPM_CC_FieldUpgradeStartVendor); } - else { - wolfTPM2_UnloadHandle(dev, &tpmSession.handle); + } + + if (rc == TPM_RC_SUCCESS) { + /* build command for manifest header */ + uint16_t val16; + /* max cmd: type (1) + data sz (2) + hash alg (2) + max digest (64) */ + uint8_t cmd[1 + 2 + 2 + TPM_SHA512_DIGEST_SIZE]; + cmd[0] = 0x01; /* type */ + val16 = be16_to_cpu(manifest_hash_sz + 2); + XMEMCPY(&cmd[1], &val16, sizeof(val16)); /* data size */ + val16 = be16_to_cpu(hashAlg); + XMEMCPY(&cmd[3], &val16, sizeof(val16)); /* hash algorithm */ + XMEMCPY(&cmd[5], manifest_hash, manifest_hash_sz); + + rc = TPM2_IFX_FieldUpgradeStart(sessionHandle, + cmd, 1 + 2 + 2 + manifest_hash_sz); + } + + if (rc == TPM_RC_SUCCESS) { + /* The TPM consumed the session entering firmware upgrade mode; mark a + * caller-supplied session as released so the caller does not flush it */ + if (startSession != NULL) { + startSession->handle.hndl = TPM_RH_NULL; } + + /* delay to give the TPM time to switch modes */ + XSLEEP_MS(300); + /* it is not required to release session handle, + * since TPM reset into firmware upgrade mode */ + + #if !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \ + !defined(WOLFTPM_WINAPI) + /* Do chip startup and request locality again */ + #ifdef WOLFTPM_LINUX_DEV_AUTODETECT + if (dev->ctx.fd < 0) /* Only needed for SPI path */ + #endif + rc = TPM2_ChipStartup(&dev->ctx, 10); + #endif + } + else if (ownSession) { + /* only release a session we started ourselves */ + wolfTPM2_UnloadHandle(dev, &tpmSession.handle); } #ifdef DEBUG_WOLFTPM if (rc != TPM_RC_SUCCESS) { @@ -11291,7 +11435,7 @@ static int tpm2_ifx_firmware_final(WOLFTPM2_DEV* dev) static int tpm2_st33_firmware_upgrade_hash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg, uint8_t* manifest_hash, uint32_t manifest_hash_sz, uint8_t* manifest, uint32_t manifest_sz, - wolfTPM2FwDataCb cb, void* cb_ctx); + wolfTPM2FwDataCb cb, void* cb_ctx, WOLFTPM2_SESSION* startSession); static int tpm2_st33_firmware_cancel(WOLFTPM2_DEV* dev); #endif @@ -11299,6 +11443,17 @@ int wolfTPM2_FirmwareUpgradeHash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg, uint8_t* manifest_hash, uint32_t manifest_hash_sz, uint8_t* manifest, uint32_t manifest_sz, wolfTPM2FwDataCb cb, void* cb_ctx) +{ + /* Default behavior: library-managed platform authorization */ + return wolfTPM2_FirmwareUpgradeHash_ex(dev, hashAlg, + manifest_hash, manifest_hash_sz, manifest, manifest_sz, + cb, cb_ctx, NULL); +} + +int wolfTPM2_FirmwareUpgradeHash_ex(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg, + uint8_t* manifest_hash, uint32_t manifest_hash_sz, + uint8_t* manifest, uint32_t manifest_sz, + wolfTPM2FwDataCb cb, void* cb_ctx, WOLFTPM2_SESSION* startSession) { int rc; WOLFTPM2_CAPS caps; @@ -11316,7 +11471,7 @@ int wolfTPM2_FirmwareUpgradeHash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg, return tpm2_st33_firmware_upgrade_hash(dev, hashAlg, manifest_hash, manifest_hash_sz, manifest, manifest_sz, - cb, cb_ctx); + cb, cb_ctx, startSession); } #endif @@ -11332,10 +11487,18 @@ int wolfTPM2_FirmwareUpgradeHash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg, return tpm2_ifx_firmware_final(dev); } if (caps.opMode == 0x00) { - rc = tpm2_ifx_firmware_enable_policy(dev); + /* Ensure rc is assigned in this scope regardless of the branch + * below (the caller-session path does not call enable_policy). */ + rc = TPM_RC_SUCCESS; + /* When the caller supplies a session it must already satisfy the + * platform authPolicy, so do not overwrite the platform primary + * policy - only manage it for the library-default path */ + if (startSession == NULL) { + rc = tpm2_ifx_firmware_enable_policy(dev); + } if (rc == TPM_RC_SUCCESS) { rc = tpm2_ifx_firmware_start(dev, hashAlg, - manifest_hash, manifest_hash_sz); + manifest_hash, manifest_hash_sz, startSession); } } if (rc == TPM_RC_SUCCESS) { @@ -11365,9 +11528,9 @@ int wolfTPM2_FirmwareUpgradeHash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg, } #ifndef WOLFTPM2_NO_WOLFCRYPT -int wolfTPM2_FirmwareUpgrade(WOLFTPM2_DEV* dev, +int wolfTPM2_FirmwareUpgrade_ex(WOLFTPM2_DEV* dev, uint8_t* manifest, uint32_t manifest_sz, - wolfTPM2FwDataCb cb, void* cb_ctx) + wolfTPM2FwDataCb cb, void* cb_ctx, WOLFTPM2_SESSION* startSession) { #ifdef WOLFSSL_SHA384 int rc; @@ -11376,31 +11539,49 @@ int wolfTPM2_FirmwareUpgrade(WOLFTPM2_DEV* dev, /* hash the manifest */ rc = wc_Sha384Hash(manifest, manifest_sz, manifest_hash); if (rc == 0) { - rc = wolfTPM2_FirmwareUpgradeHash(dev, TPM_ALG_SHA384, + rc = wolfTPM2_FirmwareUpgradeHash_ex(dev, TPM_ALG_SHA384, manifest_hash, (uint32_t)sizeof(manifest_hash), - manifest, manifest_sz, cb, cb_ctx); + manifest, manifest_sz, cb, cb_ctx, startSession); } return rc; #else (void)dev; (void)manifest; (void)manifest_sz; - (void)cb; (void)cb_ctx; + (void)cb; (void)cb_ctx; (void)startSession; return NOT_COMPILED_IN; #endif } -#endif -int wolfTPM2_FirmwareUpgradeRecover(WOLFTPM2_DEV* dev, +int wolfTPM2_FirmwareUpgrade(WOLFTPM2_DEV* dev, uint8_t* manifest, uint32_t manifest_sz, wolfTPM2FwDataCb cb, void* cb_ctx) +{ + /* Default behavior: library-managed platform authorization */ + return wolfTPM2_FirmwareUpgrade_ex(dev, manifest, manifest_sz, + cb, cb_ctx, NULL); +} +#endif + +int wolfTPM2_FirmwareUpgradeRecover_ex(WOLFTPM2_DEV* dev, + uint8_t* manifest, uint32_t manifest_sz, + wolfTPM2FwDataCb cb, void* cb_ctx, WOLFTPM2_SESSION* startSession) { uint8_t manifest_hash[TPM_SHA384_DIGEST_SIZE]; /* recovery mode manifest hash is all 0x3C */ XMEMSET(manifest_hash, 0x3C, sizeof(manifest_hash)); - return wolfTPM2_FirmwareUpgradeHash(dev, TPM_ALG_SHA384, + return wolfTPM2_FirmwareUpgradeHash_ex(dev, TPM_ALG_SHA384, manifest_hash, (uint32_t)sizeof(manifest_hash), - manifest, manifest_sz, cb, cb_ctx); + manifest, manifest_sz, cb, cb_ctx, startSession); +} + +int wolfTPM2_FirmwareUpgradeRecover(WOLFTPM2_DEV* dev, + uint8_t* manifest, uint32_t manifest_sz, + wolfTPM2FwDataCb cb, void* cb_ctx) +{ + /* Default behavior: library-managed platform authorization */ + return wolfTPM2_FirmwareUpgradeRecover_ex(dev, manifest, manifest_sz, + cb, cb_ctx, NULL); } /* terminate a firmware update */ @@ -11474,19 +11655,31 @@ int wolfTPM2_FirmwareUpgradeCancel(WOLFTPM2_DEV* dev) * 300ms delay: ST reference implementation uses this delay to allow * TPM to switch modes after FieldUpgradeStart command */ static int tpm2_st33_firmware_start_common(WOLFTPM2_DEV* dev, - uint8_t* manifest, uint32_t manifest_sz, int is_lms) + uint8_t* manifest, uint32_t manifest_sz, int is_lms, + WOLFTPM2_SESSION* startSession) { int rc; + TPM_HANDLE sessionHandle; (void)dev; - /* ST33 uses password auth (TPM_RS_PW) for FieldUpgradeStart. - * This matches the ST reference implementation behavior. + /* By default ST33 uses password auth (TPM_RS_PW) for FieldUpgradeStart, + * matching the ST reference implementation behavior. When the caller + * supplies a session (for example a policy session that satisfies a + * custom platform authPolicy), use it instead. * For LMS format, the manifest (blob0) already contains the embedded * LMS signature. Send the full manifest directly. */ - rc = TPM2_ST33_FieldUpgradeStart(TPM_RS_PW, manifest, manifest_sz); + sessionHandle = (startSession != NULL) ? + startSession->handle.hndl : (TPM_HANDLE)TPM_RS_PW; + rc = TPM2_ST33_FieldUpgradeStart(sessionHandle, manifest, manifest_sz); if (rc == TPM_RC_SUCCESS) { + /* The TPM consumed the session entering firmware upgrade mode; mark a + * caller-supplied session as released so the caller does not flush it */ + if (startSession != NULL) { + startSession->handle.hndl = TPM_RH_NULL; + } + /* 300ms delay: ST reference implementation uses this delay to allow * TPM to switch modes after FieldUpgradeStart command */ XSLEEP_MS(300); @@ -11643,7 +11836,7 @@ static int tpm2_st33_firmware_data(WOLFTPM2_DEV* dev, static int tpm2_st33_firmware_upgrade_hash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg, uint8_t* manifest_hash, uint32_t manifest_hash_sz, uint8_t* manifest, uint32_t manifest_sz, - wolfTPM2FwDataCb cb, void* cb_ctx) + wolfTPM2FwDataCb cb, void* cb_ctx, WOLFTPM2_SESSION* startSession) { int rc; WOLFTPM2_CAPS caps; @@ -11717,7 +11910,8 @@ static int tpm2_st33_firmware_upgrade_hash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg } /* Send manifest - the common function handles both LMS and non-LMS */ - rc = tpm2_st33_firmware_start_common(dev, manifest, manifest_sz, is_lms); + rc = tpm2_st33_firmware_start_common(dev, manifest, manifest_sz, is_lms, + startSession); if (rc == TPM_RC_SUCCESS) { rc = tpm2_st33_firmware_data(dev, cb, cb_ctx); diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 8dd121176..f492935fb 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -281,6 +281,7 @@ static void test_wolfTPM2_ReadPublicKey(void) static void test_wolfTPM2_ST33_FirmwareUpgrade(void) { int rc; + int rcEx; WOLFTPM2_DEV dev; WOLFTPM2_CAPS caps; #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFSSL_SHA384) @@ -322,10 +323,30 @@ static void test_wolfTPM2_ST33_FirmwareUpgrade(void) rc = wolfTPM2_FirmwareUpgradeRecover(NULL, NULL, 0, NULL, NULL); AssertIntNE(rc, 0); + /* _ex variants with caller session - NULL dev */ + rc = wolfTPM2_FirmwareUpgradeHash_ex(NULL, TPM_ALG_SHA384, NULL, 0, NULL, + 0, NULL, NULL, NULL); + AssertIntNE(rc, 0); + rc = wolfTPM2_FirmwareUpgradeRecover_ex(NULL, NULL, 0, NULL, NULL, NULL); + AssertIntNE(rc, 0); + + /* startSession == NULL delegates to the legacy call (same rc). Use a NULL + * dev so this never reaches the TPM (a live dev under autodetect could + * otherwise push an Infineon part into firmware-upgrade mode). */ + rc = wolfTPM2_FirmwareUpgradeHash(NULL, TPM_ALG_SHA384, + NULL, 0, NULL, 0, NULL, NULL); + rcEx = wolfTPM2_FirmwareUpgradeHash_ex(NULL, TPM_ALG_SHA384, + NULL, 0, NULL, 0, NULL, NULL, NULL); + AssertIntEQ(rc, rcEx); + #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFSSL_SHA384) /* wolfTPM2_FirmwareUpgrade - NULL dev */ rc = wolfTPM2_FirmwareUpgrade(NULL, NULL, 0, NULL, NULL); AssertIntNE(rc, 0); + + /* wolfTPM2_FirmwareUpgrade_ex - NULL dev */ + rc = wolfTPM2_FirmwareUpgrade_ex(NULL, NULL, 0, NULL, NULL, NULL); + AssertIntNE(rc, 0); #endif /* !WOLFTPM2_NO_WOLFCRYPT && WOLFSSL_SHA384 */ /* ===== Test NULL/invalid parameter combinations ===== */ @@ -375,6 +396,146 @@ static void test_wolfTPM2_ST33_FirmwareUpgrade(void) #endif /* WOLFTPM_ST33 || WOLFTPM_AUTODETECT */ #endif /* WOLFTPM_FIRMWARE_UPGRADE */ +/* Argument-validation coverage for wolfTPM2_PolicyOR (host-side, no TPM). */ +static void test_wolfTPM2_PolicyOR(void) +{ + WOLFTPM2_DEV dev; + WOLFTPM2_SESSION sess; + TPML_DIGEST list; + word32 cap = (word32)(sizeof(list.digests) / sizeof(list.digests[0])); + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&sess, 0, sizeof(sess)); + XMEMSET(&list, 0, sizeof(list)); + list.count = 1; + list.digests[0].size = TPM_SHA256_DIGEST_SIZE; + + /* NULL pointer arguments */ + AssertIntEQ(wolfTPM2_PolicyOR(NULL, &sess, &list), BAD_FUNC_ARG); + AssertIntEQ(wolfTPM2_PolicyOR(&dev, NULL, &list), BAD_FUNC_ARG); + AssertIntEQ(wolfTPM2_PolicyOR(&dev, &sess, NULL), BAD_FUNC_ARG); + + /* count of 0 is invalid */ + list.count = 0; + AssertIntEQ(wolfTPM2_PolicyOR(&dev, &sess, &list), BAD_FUNC_ARG); + + /* count beyond the TPML_DIGEST capacity is invalid */ + list.count = cap + 1; + AssertIntEQ(wolfTPM2_PolicyOR(&dev, &sess, &list), BAD_FUNC_ARG); + + /* a branch digest size larger than the buffer is invalid (CWE-125) */ + list.count = 1; + list.digests[0].size = (UINT16)(sizeof(list.digests[0].buffer) + 1); + AssertIntEQ(wolfTPM2_PolicyOR(&dev, &sess, &list), BAD_FUNC_ARG); + + printf("Test PolicyOR: %-40s Passed\n", "Arg Validation:"); +} + +#ifndef WOLFTPM2_NO_WOLFCRYPT +/* Known-answer + arg-validation for wolfTPM2_PolicyCommandCodeMake (no TPM). + * Requires wolfCrypt for the policy hash. Vectors are the offline digest + * H(zeros(hashSz) || TPM_CC_PolicyCommandCode || TPM_CC_NV_Read). */ +static void test_wolfTPM2_PolicyCommandCodeMake(void) +{ + int rc; + byte digest[TPM_MAX_DIGEST_SIZE]; + word32 digestSz = 0; + /* SHA2-256 (also in examples/nvram/extend.c) */ + static const byte expected256[] = { + 0x47,0xce,0x30,0x32,0xd8,0xba,0xd1,0xf3, + 0x08,0x9c,0xb0,0xc0,0x90,0x88,0xde,0x43, + 0x50,0x14,0x91,0xd4,0x60,0x40,0x2b,0x90, + 0xcd,0x1b,0x7f,0xc0,0xb6,0x8c,0xa9,0x2f + }; +#ifdef WOLFSSL_SHA384 + static const byte expected384[] = { + 0xfb,0xdd,0x14,0x92,0x1c,0x8b,0xd9,0x5c, + 0x9f,0x35,0x96,0x79,0xd2,0xbf,0x75,0x78, + 0xb1,0x47,0xe8,0x29,0x83,0x21,0xf8,0xe9, + 0xea,0xc4,0x4c,0x11,0x77,0x2f,0xfa,0x6e, + 0xe5,0x91,0x78,0x43,0x47,0x83,0x9b,0xef, + 0xf1,0x22,0xf2,0x14,0x4d,0xd0,0xb0,0xf0 + }; +#endif +#ifdef WOLFSSL_SHA512 + static const byte expected512[] = { + 0x31,0x38,0x6a,0xba,0x16,0xd8,0xf0,0x64, + 0xbd,0x51,0x4d,0x1d,0xd9,0x48,0x1c,0x65, + 0x6d,0x0e,0x32,0xe2,0xad,0x84,0x8e,0x1b, + 0xe9,0xb9,0xab,0x1d,0xd6,0x6f,0xfa,0xd2, + 0xc5,0xc0,0x2d,0x22,0x1c,0x61,0xd2,0x01, + 0x99,0x4e,0xd8,0x30,0x6b,0x77,0x0e,0x56, + 0xbb,0x13,0x05,0x32,0xdf,0x62,0xea,0x8d, + 0x06,0xc6,0xdf,0x53,0x5f,0x19,0xb8,0x21 + }; +#endif + + /* NULL argument rejection */ + AssertIntEQ(wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA256, NULL, &digestSz, + TPM_CC_NV_Read), BAD_FUNC_ARG); + AssertIntEQ(wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA256, digest, NULL, + TPM_CC_NV_Read), BAD_FUNC_ARG); + /* Unsupported hash algorithm rejection */ + AssertIntEQ(wolfTPM2_PolicyCommandCodeMake(TPM_ALG_NULL, digest, &digestSz, + TPM_CC_NV_Read), BAD_FUNC_ARG); + + /* SHA2-256 known-answer */ + digestSz = 0; + rc = wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA256, digest, &digestSz, + TPM_CC_NV_Read); + AssertIntEQ(rc, 0); + AssertIntEQ((int)digestSz, (int)sizeof(expected256)); + AssertIntEQ(XMEMCMP(digest, expected256, sizeof(expected256)), 0); +#ifdef WOLFSSL_SHA384 + digestSz = 0; + rc = wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA384, digest, &digestSz, + TPM_CC_NV_Read); + AssertIntEQ(rc, 0); + AssertIntEQ((int)digestSz, (int)sizeof(expected384)); + AssertIntEQ(XMEMCMP(digest, expected384, sizeof(expected384)), 0); +#endif +#ifdef WOLFSSL_SHA512 + digestSz = 0; + rc = wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA512, digest, &digestSz, + TPM_CC_NV_Read); + AssertIntEQ(rc, 0); + AssertIntEQ((int)digestSz, (int)sizeof(expected512)); + AssertIntEQ(XMEMCMP(digest, expected512, sizeof(expected512)), 0); +#endif + + printf("Test PolicyCCMake:%-40s Passed\n", "Known Vectors:"); +} +#endif /* !WOLFTPM2_NO_WOLFCRYPT */ + +/* Arg-validation for wolfTPM2_SetPrimaryPolicy (no TPM). */ +static void test_wolfTPM2_SetPrimaryPolicy(void) +{ + WOLFTPM2_DEV dev; + byte pol[TPM_MAX_DIGEST_SIZE + 4]; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(pol, 0, sizeof(pol)); + + /* NULL dev */ + AssertIntEQ(wolfTPM2_SetPrimaryPolicy(NULL, TPM_RH_PLATFORM, + TPM_ALG_SHA256, pol, TPM_SHA256_DIGEST_SIZE), BAD_FUNC_ARG); + /* policy digest larger than the buffer */ + AssertIntEQ(wolfTPM2_SetPrimaryPolicy(&dev, TPM_RH_PLATFORM, + TPM_ALG_SHA256, pol, (word32)sizeof(pol)), BAD_FUNC_ARG); + /* NULL policy with a non-zero size must not silently clear the policy */ + AssertIntEQ(wolfTPM2_SetPrimaryPolicy(&dev, TPM_RH_PLATFORM, + TPM_ALG_SHA256, NULL, TPM_SHA256_DIGEST_SIZE), BAD_FUNC_ARG); + + printf("Test SetPrimPol: %-40s Passed\n", "Arg Validation:"); +} + +/* NULL-dev handling for wolfTPM2_IsAlgSupported (no TPM). */ +static void test_wolfTPM2_IsAlgSupported(void) +{ + AssertIntEQ(wolfTPM2_IsAlgSupported(NULL, TPM_ALG_SHA256), BAD_FUNC_ARG); + printf("Test IsAlgSupp: %-40s Passed\n", "NULL dev:"); +} + static void test_wolfTPM2_GetRandom(void) { int rc; @@ -7515,6 +7676,12 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_ST33_FirmwareUpgrade(); #endif #endif + test_wolfTPM2_PolicyOR(); + #ifndef WOLFTPM2_NO_WOLFCRYPT + test_wolfTPM2_PolicyCommandCodeMake(); + #endif + test_wolfTPM2_SetPrimaryPolicy(); + test_wolfTPM2_IsAlgSupported(); #if defined(WOLFTPM_MLDSA) && defined(WOLFTPM_MLKEM) /* Run non-TPM-dependent tests first */ test_wolfTPM2_PQC_KeyTemplates(); diff --git a/wolftpm/tpm2_wrap.h b/wolftpm/tpm2_wrap.h index 4f40378b1..928fcd9af 100644 --- a/wolftpm/tpm2_wrap.h +++ b/wolftpm/tpm2_wrap.h @@ -399,6 +399,27 @@ WOLFTPM_API int wolfTPM2_SelfTest(WOLFTPM2_DEV* dev); */ WOLFTPM_API int wolfTPM2_GetCapabilities(WOLFTPM2_DEV* dev, WOLFTPM2_CAPS* caps); +/*! + \ingroup wolfTPM2_Wrappers + + \brief Report whether the TPM implements a given algorithm + + \note Queries TPM_CAP_ALGS. Useful to skip a hash the TPM does not support + (for example SHA2-512 on parts limited to SHA2-256/384) before starting + a session with it. + + \return 1 if the algorithm is supported + \return 0 if it is not supported + \return BAD_FUNC_ARG if dev is NULL + \return a TPM_RC (or other negative/non-zero error) if the capability query fails + + \param dev pointer to a TPM2_DEV struct + \param alg the algorithm identifier to test (for example TPM_ALG_SHA512) + + \sa wolfTPM2_GetCapabilities +*/ +WOLFTPM_API int wolfTPM2_IsAlgSupported(WOLFTPM2_DEV* dev, TPM_ALG_ID alg); + /*! \ingroup wolfTPM2_Wrappers \brief Gets a list of handles @@ -4875,6 +4896,31 @@ WOLFTPM_API int wolfTPM2_PolicyPCRMake(TPM_ALG_ID pcrAlg, byte* pcrArray, word32 pcrArraySz, const byte* pcrDigest, word32 pcrDigestSz, byte* digest, word32* digestSz); +/*! + \ingroup wolfTPM2_Wrappers + + \brief Compute the policy digest for PolicyCommandCode on a fresh session + + \note policyDigest = hash(zeroDigest || TPM_CC_PolicyCommandCode || cc). The + digest buffer is written and *digestSz set to the hash size; the caller + does not need to pre-initialize them. Mirrors the running digest of a + new policy session after wolfTPM2_PolicyCommandCode. + + \return TPM_RC_SUCCESS: successful + \return BAD_FUNC_ARG: NULL digest/digestSz or unsupported hashAlg + + \param hashAlg hash algorithm for the policy digest + \param digest output policy digest buffer (>= hash size) + \param digestSz output digest size + \param cc the command code to bind (for example TPM_CC_NV_Read) + + \sa wolfTPM2_PolicyCommandCode + \sa wolfTPM2_PolicyHash + \sa wolfTPM2_PolicyPCRMake +*/ +WOLFTPM_API int wolfTPM2_PolicyCommandCodeMake(TPM_ALG_ID hashAlg, + byte* digest, word32* digestSz, TPM_CC cc); + /*! \ingroup wolfTPM2_Wrappers @@ -4979,6 +5025,59 @@ WOLFTPM_API int wolfTPM2_PolicyAuthValue(WOLFTPM2_DEV* dev, WOLFTPM_API int wolfTPM2_PolicyCommandCode(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* tpmSession, TPM_CC cc); +/*! + \ingroup wolfTPM2_Wrappers + + \brief Wrapper for satisfying a policy session with a compound OR of digests + + \note The digest list is hash-agnostic (each branch carries its own size), + so it supports SHA2-256 through SHA2-512 policy branches. The number of + branches (pHashList->count) must be between 1 and the TPML_DIGEST + capacity (the digests[] array length), and each branch's size must not + exceed the digest buffer length; branches beyond count are ignored. + + \return TPM_RC_SUCCESS: successful + \return BAD_FUNC_ARG: bad pointer, count out of range, or a branch size that + exceeds the digest buffer + + \param dev pointer to a TPM2_DEV struct + \param tpmSession pointer to a WOLFTPM2_SESSION struct used with wolfTPM2_StartSession and wolfTPM2_SetAuthSession + \param pHashList list of pre-computed policy branch digests to OR together + + \sa wolfTPM2_PolicyPCR + \sa wolfTPM2_PolicyAuthorize + \sa wolfTPM2_GetPolicyDigest +*/ +WOLFTPM_API int wolfTPM2_PolicyOR(WOLFTPM2_DEV* dev, + WOLFTPM2_SESSION* tpmSession, const TPML_DIGEST* pHashList); + +/*! + \ingroup wolfTPM2_Wrappers + + \brief Set (or clear) the authPolicy of a hierarchy + + \note Wraps TPM2_SetPrimaryPolicy for owner/endorsement/platform/lockout. + Pass authPolicy=NULL, authPolicySz=0 and hashAlg=TPM_ALG_NULL to clear + an existing policy. The command itself is authorized by the hierarchy's + current auth (set it on the device's active session beforehand). + + \return TPM_RC_SUCCESS: successful + \return BAD_FUNC_ARG: NULL dev, authPolicySz exceeds the digest buffer, or + authPolicy is NULL with a non-zero authPolicySz + + \param dev pointer to a TPM2_DEV struct + \param authHandle the hierarchy (for example TPM_RH_PLATFORM) + \param hashAlg the policy digest hash algorithm (TPM_ALG_NULL to clear) + \param authPolicy the policy digest to set (NULL to clear) + \param authPolicySz size of the policy digest (0 to clear) + + \sa wolfTPM2_PolicyOR + \sa wolfTPM2_GetPolicyDigest +*/ +WOLFTPM_API int wolfTPM2_SetPrimaryPolicy(WOLFTPM2_DEV* dev, + TPMI_RH_HIERARCHY_AUTH authHandle, TPM_ALG_ID hashAlg, + const byte* authPolicy, word32 authPolicySz); + /* Pre-provisioned IAK and IDevID key/cert from TPM vendor */ /* Tested with ST33KTPM devices */ @@ -5111,6 +5210,45 @@ WOLFTPM_API int wolfTPM2_FirmwareUpgradeHash(WOLFTPM2_DEV* dev, uint8_t* manifest, uint32_t manifest_sz, wolfTPM2FwDataCb cb, void* cb_ctx); +/*! + \ingroup wolfTPM2_Wrappers + \brief Perform TPM firmware upgrade using a caller-supplied authorization session + \note Identical to wolfTPM2_FirmwareUpgradeHash except the caller controls how + the firmware-start command is authorized against the platform hierarchy. + \note When startSession is NULL this behaves exactly like + wolfTPM2_FirmwareUpgradeHash (library-managed platform authorization). + \note When startSession is non-NULL the caller is responsible for having + satisfied the platform authPolicy on that session (for example via + wolfTPM2_PolicyPCR / wolfTPM2_PolicyAuthorize / wolfTPM2_PolicyOR using + SHA2-256 or SHA2-512). For Infineon the platform primary policy is left + untouched (the caller provisions it); for ST33 the session replaces the + default TPM_RS_PW password authorization. + + \return TPM_RC_SUCCESS: successful + \return TPM_RC_FAILURE: generic failure (check TPM IO and TPM return code) + \return BAD_FUNC_ARG: check the provided arguments + + \param dev pointer to a TPM2_DEV struct + \param hashAlg hash algorithm to use (TPM_ALG_SHA384 or TPM_ALG_SHA512) + \param manifest_hash buffer to store computed manifest hash + \param manifest_hash_sz size of manifest hash buffer + \param manifest pointer to firmware manifest data + \param manifest_sz size of firmware manifest + \param cb callback function for firmware data access + \param cb_ctx context pointer passed to callback + \param startSession optional caller-satisfied session authorizing the + firmware-start command (NULL for library-managed authorization) + + \sa wolfTPM2_FirmwareUpgradeHash + \sa wolfTPM2_PolicyOR + \sa wolfTPM2_StartSession_ex +*/ +WOLFTPM_API int wolfTPM2_FirmwareUpgradeHash_ex(WOLFTPM2_DEV* dev, + TPM_ALG_ID hashAlg, /* Can use SHA2-384 or SHA2-512 for manifest hash */ + uint8_t* manifest_hash, uint32_t manifest_hash_sz, + uint8_t* manifest, uint32_t manifest_sz, + wolfTPM2FwDataCb cb, void* cb_ctx, WOLFTPM2_SESSION* startSession); + #ifndef WOLFTPM2_NO_WOLFCRYPT /*! \ingroup wolfTPM2_Wrappers @@ -5138,6 +5276,30 @@ WOLFTPM_API int wolfTPM2_FirmwareUpgradeHash(WOLFTPM2_DEV* dev, WOLFTPM_API int wolfTPM2_FirmwareUpgrade(WOLFTPM2_DEV* dev, uint8_t* manifest, uint32_t manifest_sz, wolfTPM2FwDataCb cb, void* cb_ctx); + +/*! + \ingroup wolfTPM2_Wrappers + \brief Perform TPM firmware upgrade using a caller-supplied authorization session + \note Same as wolfTPM2_FirmwareUpgrade but the caller controls how the + firmware-start command is authorized (see wolfTPM2_FirmwareUpgradeHash_ex). + startSession NULL preserves the default library-managed behavior. + + \return TPM_RC_SUCCESS: successful + \return NOT_COMPILED_IN: wolfSSL not built with WOLFSSL_SHA384 + + \param dev pointer to a TPM2_DEV struct + \param manifest pointer to firmware manifest data + \param manifest_sz size of firmware manifest + \param cb callback function for firmware data access + \param cb_ctx context pointer passed to callback + \param startSession optional caller-satisfied session (NULL for default) + + \sa wolfTPM2_FirmwareUpgrade + \sa wolfTPM2_FirmwareUpgradeHash_ex +*/ +WOLFTPM_API int wolfTPM2_FirmwareUpgrade_ex(WOLFTPM2_DEV* dev, + uint8_t* manifest, uint32_t manifest_sz, + wolfTPM2FwDataCb cb, void* cb_ctx, WOLFTPM2_SESSION* startSession); #endif /* !WOLFTPM2_NO_WOLFCRYPT */ /*! @@ -5162,6 +5324,30 @@ WOLFTPM_API int wolfTPM2_FirmwareUpgradeRecover(WOLFTPM2_DEV* dev, uint8_t* manifest, uint32_t manifest_sz, wolfTPM2FwDataCb cb, void* cb_ctx); +/*! + \ingroup wolfTPM2_Wrappers + \brief Recover from a failed firmware upgrade using a caller-supplied session + \note Same as wolfTPM2_FirmwareUpgradeRecover but with caller-controlled + authorization (see wolfTPM2_FirmwareUpgradeHash_ex). startSession NULL + preserves the default library-managed behavior. + + \return TPM_RC_SUCCESS: successful + \return BAD_FUNC_ARG: check the provided arguments + + \param dev pointer to a TPM2_DEV struct + \param manifest pointer to firmware manifest data + \param manifest_sz size of firmware manifest + \param cb callback function for firmware data access + \param cb_ctx context pointer passed to callback + \param startSession optional caller-satisfied session (NULL for default) + + \sa wolfTPM2_FirmwareUpgradeRecover + \sa wolfTPM2_FirmwareUpgradeHash_ex +*/ +WOLFTPM_API int wolfTPM2_FirmwareUpgradeRecover_ex(WOLFTPM2_DEV* dev, + uint8_t* manifest, uint32_t manifest_sz, + wolfTPM2FwDataCb cb, void* cb_ctx, WOLFTPM2_SESSION* startSession); + /*! \ingroup wolfTPM2_Wrappers \brief Cancel ongoing TPM firmware upgrade