diff --git a/examples/firmware/README.md b/examples/firmware/README.md index 66d0c60d..6c1fde08 100644 --- a/examples/firmware/README.md +++ b/examples/firmware/README.md @@ -199,3 +199,42 @@ 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]; + +/* start a policy session using the desired policy hash (SHA2-512 for PQC) */ +XMEMSET(&session, 0, sizeof(session)); +wolfTPM2_StartSession_ex(&dev, &session, NULL, NULL, + TPM_SE_POLICY, TPM_ALG_NULL, TPM_ALG_SHA512); + +/* satisfy one branch (PCR, PolicySigned/Authorize, PolicyAuthValue, ...), + * then OR against the full branch list that the platform authPolicy encodes */ +/* ... build orList.digests[0..count-1] with your pre-computed branch digests ... */ +orList.count = 2; +wolfTPM2_PolicyOR(&dev, &session, &orList); + +/* hash the manifest with the matching algorithm, then start the upgrade under + * the caller-satisfied session (NULL would use the library-default auth) */ +wc_Sha512Hash(manifest, manifest_sz, manifest_hash); +wolfTPM2_FirmwareUpgradeHash_ex(&dev, TPM_ALG_SHA512, + manifest_hash, (uint32_t)sizeof(manifest_hash), + manifest, manifest_sz, fwDataCb, fwCbCtx, &session); +``` + +Passing `NULL` for the final `startSession` argument makes `wolfTPM2_FirmwareUpgradeHash_ex()` behave exactly like `wolfTPM2_FirmwareUpgradeHash()` (library-managed authorization), so existing code is unaffected. diff --git a/examples/firmware/ifx_fw_update.c b/examples/firmware/ifx_fw_update.c index 9934e229..5410dcf4 100644 --- a/examples/firmware/ifx_fw_update.c +++ b/examples/firmware/ifx_fw_update.c @@ -44,7 +44,11 @@ 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|--policyor] [--sha256|--sha384|--sha512] \n"); + printf("\t --policy: authorize start with a caller-provisioned PolicyCommandCode\n"); + printf("\t --policyor: authorize start with a caller-provisioned PolicyOR (multi-branch)\n"); + printf("\t./ifx_fw_update (default library auth)\n"); } typedef struct { @@ -106,6 +110,237 @@ static void TPM2_IFX_PrintInfo(WOLFTPM2_CAPS* caps) caps->keyGroupId, caps->fwCounter, caps->fwCounterSame); } +/* Build a PolicyCommandCode branch digest offline. This matches the running + * policy digest of a fresh policy session after wolfTPM2_PolicyCommandCode. */ +static int BuildPolicyCommandCode(TPMI_ALG_HASH hashAlg, + byte* digest, word32* digestSz, TPM_CC cc) +{ + byte val[4]; /* command code in big-endian, matching the TPM wire format */ + 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)); +} + +/* Non-destructive self-test for the caller-supplied policy authorization + * added for firmware upgrade. Exercises wolfTPM2_PolicyOR at the requested + * hash algorithm and verifies the TPM's running policy digest matches an + * offline computation. This does NOT invoke FieldUpgradeStart and never + * changes TPM state persistently, so it is safe to run on a live TPM. + * Returns 0 on match, -1 on digest mismatch, or a TPM rc if the TPM does + * not support the requested hash for policy sessions. */ +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, bSz, expSz, gotSz; + + XMEMSET(&sess, 0, sizeof(sess)); + XMEMSET(&orList, 0, sizeof(orList)); + + if (hsz == 0 || hsz > TPM_MAX_DIGEST_SIZE) { + return BAD_FUNC_ARG; + } + + /* Offline: two distinct PolicyCommandCode branch digests */ + XMEMSET(branchA, 0, sizeof(branchA)); + aSz = hsz; + rc = BuildPolicyCommandCode(hashAlg, branchA, &aSz, TPM_CC_NV_Read); + if (rc == 0) { + XMEMSET(branchB, 0, sizeof(branchB)); + bSz = hsz; + rc = BuildPolicyCommandCode(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 (hash not supported?)\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: "); + TPM2_PrintBin(expected, expSz); + printf(" got: "); + TPM2_PrintBin(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; +} + +/* Run the policy authorization self-test across SHA-256/384/512, reporting + * each. A digest mismatch (-1) is a real failure; a StartSession failure is + * treated as an unsupported-hash skip. Returns 0 unless a mismatch occurred. */ +static 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); + if (rc == -1) { + hardFail = 1; /* digest mismatch is a real defect */ + } + } + return hardFail ? -1 : 0; +} + +/* 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 library default. When useOr is set, the platform policy is a + * PolicyOR of two branches (the customer's multi-branch scenario); otherwise it + * is 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). */ +static int firmware_policy_session_setup(WOLFTPM2_DEV* dev, + TPMI_ALG_HASH hashAlg, int useOr, TPM_CC fuStartCC, + WOLFTPM2_SESSION* session) +{ + int rc; + SetPrimaryPolicy_In policyIn; + 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)); + + printf("Provisioning platform policy (%s, %s)\n", + useOr ? "PolicyOR" : "PolicyCommandCode", + TPM2_GetAlgName(hashAlg)); + + /* Branch A: PolicyCommandCode(FieldUpgradeStart) - required to start FU */ + XMEMSET(branchA, 0, sizeof(branchA)); + aSz = hsz; + rc = BuildPolicyCommandCode(hashAlg, branchA, &aSz, fuStartCC); + + /* Compute the platform authPolicy digest */ + if (rc == 0) { + if (useOr) { + /* Branch B: a second, distinct policy branch */ + XMEMSET(branchB, 0, sizeof(branchB)); + bSz = hsz; + rc = BuildPolicyCommandCode(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 (authorized by empty platformAuth) */ + if (rc == 0) { + XMEMSET(&policyIn, 0, sizeof(policyIn)); + policyIn.authHandle = TPM_RH_PLATFORM; + policyIn.hashAlg = hashAlg; + policyIn.authPolicy.size = (UINT16)polSz; + XMEMCPY(policyIn.authPolicy.buffer, platformPolicy, polSz); + rc = TPM2_SetPrimaryPolicy(&policyIn); + if (rc != 0) { + printf(" SetPrimaryPolicy failed 0x%x: %s\n", + rc, TPM2_GetRCString(rc)); + } + } + + /* 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 (hash not supported?)\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 && session->handle.hndl != 0) { + wolfTPM2_UnloadHandle(dev, &session->handle); + } + return rc; +} + int TPM2_IFX_Firmware_Update(void* userCtx, int argc, char *argv[]) { int rc; @@ -115,24 +350,48 @@ 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 policytest = 0; + int policyMode = 0; /* 0=default auth, 1=--policy, 2=--policyor */ + TPMI_ALG_HASH policyHash = TPM_ALG_SHA256; + WOLFTPM2_SESSION policySession; + int i; XMEMSET(&fwinfo, 0, sizeof(fwinfo)); + XMEMSET(&policySession, 0, sizeof(policySession)); - 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; } - else { - manifest_file = argv[1]; - if (argc >= 3) { - firmware_file = argv[2]; - } + else if (XSTRCMP(argv[i], "--policytest") == 0) { + policytest = 1; + } + else if (XSTRCMP(argv[i], "--policy") == 0) { + policyMode = 1; + } + else if (XSTRCMP(argv[i], "--policyor") == 0) { + policyMode = 2; + } + 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; + } + else if (manifest_file == NULL) { + manifest_file = argv[i]; + } + else if (firmware_file == NULL) { + firmware_file = argv[i]; } } @@ -148,6 +407,13 @@ int TPM2_IFX_Firmware_Update(void* userCtx, int argc, char *argv[]) goto exit; } + if (policytest) { + /* Non-destructive validation of caller-supplied policy authorization. + * Does not touch firmware upgrade state. */ + rc = firmware_policy_selftest_all(&dev); + goto exit; + } + rc = wolfTPM2_GetCapabilities(&dev, &caps); if (rc != TPM_RC_SUCCESS) { goto exit; @@ -187,18 +453,29 @@ int TPM2_IFX_Firmware_Update(void* userCtx, int argc, char *argv[]) rc = loadFile(firmware_file, &fwinfo.firmware_buf, &fwinfo.firmware_bufSz); } + /* 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 != 0) { + rc = firmware_policy_session_setup(&dev, policyHash, + (policyMode == 2), TPM_CC_FieldUpgradeStartVendor, &policySession); + } if (rc == 0) { if (recovery) { - printf("Firmware Update (recovery mode):\n"); - rc = wolfTPM2_FirmwareUpgradeRecover(&dev, + printf("Firmware Update (recovery mode%s):\n", + policyMode ? ", 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, + policyMode ? &policySession : NULL); } else { - printf("Firmware Update (normal mode):\n"); - rc = wolfTPM2_FirmwareUpgrade(&dev, + printf("Firmware Update (normal mode%s):\n", + policyMode ? ", 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, + policyMode ? &policySession : NULL); } } if (rc == 0) { @@ -212,6 +489,11 @@ int TPM2_IFX_Firmware_Update(void* userCtx, int argc, char *argv[]) rc, TPM2_GetRCString(rc)); } + /* The session is normally consumed by the TPM entering upgrade mode; free + * it if it is still loaded (for example on an authorization failure). */ + if (policySession.handle.hndl != 0) { + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + } 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/st33_fw_update.c b/examples/firmware/st33_fw_update.c index 2120df3d..480fc3e1 100644 --- a/examples/firmware/st33_fw_update.c +++ b/examples/firmware/st33_fw_update.c @@ -47,7 +47,11 @@ 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|--policyor] [--sha256|--sha384|--sha512] \n"); + printf("\t --policy: authorize start with a caller-provisioned PolicyCommandCode\n"); + printf("\t --policyor: authorize start with a caller-provisioned PolicyOR (multi-branch)\n"); + printf("\t./st33_fw_update (default password auth)\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"); } @@ -173,6 +177,236 @@ static void TPM2_ST33_PrintInfo(WOLFTPM2_CAPS* caps) } } +/* Build a PolicyCommandCode branch digest offline. This matches the running + * policy digest of a fresh policy session after wolfTPM2_PolicyCommandCode. */ +static int BuildPolicyCommandCode(TPMI_ALG_HASH hashAlg, + byte* digest, word32* digestSz, TPM_CC cc) +{ + byte val[4]; /* command code in big-endian, matching the TPM wire format */ + 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)); +} + +/* Non-destructive self-test for the caller-supplied policy authorization + * added for firmware upgrade. Exercises wolfTPM2_PolicyOR at the requested + * hash algorithm and verifies the TPM's running policy digest matches an + * offline computation. This does NOT invoke FieldUpgradeStart and never + * changes TPM state persistently, so it is safe to run on a live TPM. + * Returns 0 on match. */ +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, bSz, expSz, gotSz; + + XMEMSET(&sess, 0, sizeof(sess)); + XMEMSET(&orList, 0, sizeof(orList)); + + if (hsz == 0 || hsz > TPM_MAX_DIGEST_SIZE) { + return BAD_FUNC_ARG; + } + + /* Offline: two distinct PolicyCommandCode branch digests */ + XMEMSET(branchA, 0, sizeof(branchA)); + aSz = hsz; + rc = BuildPolicyCommandCode(hashAlg, branchA, &aSz, TPM_CC_NV_Read); + if (rc == 0) { + XMEMSET(branchB, 0, sizeof(branchB)); + bSz = hsz; + rc = BuildPolicyCommandCode(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 (hash not supported?)\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: "); + TPM2_PrintBin(expected, expSz); + printf(" got: "); + TPM2_PrintBin(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; +} + +/* Run the policy authorization self-test across SHA-256/384/512, reporting + * each. A digest mismatch (-1) is a real failure; a StartSession failure is + * treated as an unsupported-hash skip. Returns 0 unless a mismatch occurred. */ +static 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); + if (rc == -1) { + hardFail = 1; /* digest mismatch is a real defect */ + } + } + return hardFail ? -1 : 0; +} + +/* 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 default password (TPM_RS_PW). When useOr is set, the platform + * policy is a PolicyOR of two branches (the customer's multi-branch scenario); + * otherwise it is 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). */ +static int firmware_policy_session_setup(WOLFTPM2_DEV* dev, + TPMI_ALG_HASH hashAlg, int useOr, TPM_CC fuStartCC, + WOLFTPM2_SESSION* session) +{ + int rc; + SetPrimaryPolicy_In policyIn; + 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)); + + printf("Provisioning platform policy (%s, %s)\n", + useOr ? "PolicyOR" : "PolicyCommandCode", + TPM2_GetAlgName(hashAlg)); + + /* Branch A: PolicyCommandCode(FieldUpgradeStart) - required to start FU */ + XMEMSET(branchA, 0, sizeof(branchA)); + aSz = hsz; + rc = BuildPolicyCommandCode(hashAlg, branchA, &aSz, fuStartCC); + + /* Compute the platform authPolicy digest */ + if (rc == 0) { + if (useOr) { + /* Branch B: a second, distinct policy branch */ + XMEMSET(branchB, 0, sizeof(branchB)); + bSz = hsz; + rc = BuildPolicyCommandCode(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 (authorized by empty platformAuth) */ + if (rc == 0) { + XMEMSET(&policyIn, 0, sizeof(policyIn)); + policyIn.authHandle = TPM_RH_PLATFORM; + policyIn.hashAlg = hashAlg; + policyIn.authPolicy.size = (UINT16)polSz; + XMEMCPY(policyIn.authPolicy.buffer, platformPolicy, polSz); + rc = TPM2_SetPrimaryPolicy(&policyIn); + if (rc != 0) { + printf(" SetPrimaryPolicy failed 0x%x: %s\n", + rc, TPM2_GetRCString(rc)); + } + } + + /* 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 (hash not supported?)\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 && session->handle.hndl != 0) { + wolfTPM2_UnloadHandle(dev, &session->handle); + } + return rc; +} + /* Forward declaration */ int TPM2_ST33_Firmware_Update(void* userCtx, int argc, char *argv[]); @@ -184,23 +418,47 @@ int TPM2_ST33_Firmware_Update(void* userCtx, int argc, char *argv[]) const char* fi_file = NULL; fw_info_t fwinfo; int abandon = 0; + int policytest = 0; + int policyMode = 0; /* 0=default auth, 1=--policy, 2=--policyor */ + TPMI_ALG_HASH policyHash = TPM_ALG_SHA256; + WOLFTPM2_SESSION policySession; size_t blob0_size; + int i; XMEMSET(&fwinfo, 0, sizeof(fwinfo)); XMEMSET(&caps, 0, sizeof(caps)); + XMEMSET(&policySession, 0, sizeof(policySession)); - 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; } - else { - fi_file = argv[1]; + else if (XSTRCMP(argv[i], "--policytest") == 0) { + policytest = 1; + } + else if (XSTRCMP(argv[i], "--policy") == 0) { + policyMode = 1; + } + else if (XSTRCMP(argv[i], "--policyor") == 0) { + policyMode = 2; + } + 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; + } + else if (fi_file == NULL) { + fi_file = argv[i]; } } @@ -245,6 +503,16 @@ int TPM2_ST33_Firmware_Update(void* userCtx, int argc, char *argv[]) goto exit; } + 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; + } + rc = wolfTPM2_GetCapabilities(&dev, &caps); if (rc != TPM_RC_SUCCESS) { printf("wolfTPM2_GetCapabilities failed 0x%x: %s\n", @@ -351,10 +619,23 @@ int TPM2_ST33_Firmware_Update(void* userCtx, int argc, char *argv[]) rc = TPM2_ST33_SendFirmwareData(&fwinfo); } else { + /* 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 != 0) { + rc = firmware_policy_session_setup(&dev, policyHash, + (policyMode == 2), TPM_CC_FieldUpgradeStartVendor_ST33, + &policySession); + } /* 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); + if (rc == 0) { + printf("Firmware Update (normal mode%s):\n", + policyMode ? ", caller policy" : ""); + rc = wolfTPM2_FirmwareUpgrade_ex(&dev, + fwinfo.manifest_buf, (uint32_t)fwinfo.manifest_bufSz, + TPM2_ST33_FwData_Cb, &fwinfo, + policyMode ? &policySession : NULL); + } } if (rc == 0) { printf("\nFirmware update completed successfully.\n"); @@ -377,6 +658,11 @@ int TPM2_ST33_Firmware_Update(void* userCtx, int argc, char *argv[]) rc, TPM2_GetRCString(rc)); } + /* The session is normally consumed by the TPM entering upgrade mode; free + * it if it is still loaded (for example on an authorization failure). */ + if (policySession.handle.hndl != 0) { + wolfTPM2_UnloadHandle(&dev, &policySession.handle); + } /* 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/src/tpm2_wrap.c b/src/tpm2_wrap.c index d6e17961..0f419bf0 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -10650,6 +10650,29 @@ 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; + + 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; + } + + XMEMSET(&policyOR, 0, sizeof(policyOR)); + policyOR.policySession = tpmSession->handle.hndl; + XMEMCPY(&policyOR.pHashList, pHashList, sizeof(policyOR.pHashList)); + return TPM2_PolicyOR(&policyOR); +} + #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, @@ -11077,52 +11100,70 @@ 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 - } - else { - wolfTPM2_UnloadHandle(dev, &tpmSession.handle); + ownSession = 1; + sessionHandle = tpmSession.handle.hndl; + 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(sessionHandle, + cmd, 1 + 2 + 2 + manifest_hash_sz); + } + + 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 + } + else if (ownSession) { + /* only release a session we started ourselves */ + wolfTPM2_UnloadHandle(dev, &tpmSession.handle); + } #ifdef DEBUG_WOLFTPM if (rc != TPM_RC_SUCCESS) { printf("Firmware upgrade start failed 0x%x: %s\n", @@ -11278,7 +11319,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 @@ -11286,6 +11327,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; @@ -11303,7 +11355,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 @@ -11319,10 +11371,15 @@ 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); + /* 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) { @@ -11352,9 +11409,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; @@ -11363,31 +11420,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 */ @@ -11461,17 +11536,23 @@ 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) { /* 300ms delay: ST reference implementation uses this delay to allow @@ -11630,7 +11711,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; @@ -11704,7 +11785,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/wolftpm/tpm2_wrap.h b/wolftpm/tpm2_wrap.h index 108b3093..983611a8 100644 --- a/wolftpm/tpm2_wrap.h +++ b/wolftpm/tpm2_wrap.h @@ -4976,6 +4976,28 @@ 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 and SHA2-512 policy branches. Up to 8 branches. + + \return TPM_RC_SUCCESS: successful + \return BAD_FUNC_ARG: check the provided arguments (count 0 or > 8) + + \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); + /* Pre-provisioned IAK and IDevID key/cert from TPM vendor */ /* Tested with ST33KTPM devices */ @@ -5108,6 +5130,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 @@ -5135,6 +5196,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 */ /*! @@ -5159,6 +5244,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