Skip to content

Commit 01bc093

Browse files
committed
Add caller-supplied policy authorization for TPM firmware upgrade
1 parent 6b63b67 commit 01bc093

11 files changed

Lines changed: 1109 additions & 104 deletions

File tree

CMakeLists.txt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,13 @@ if (WOLFTPM_EXAMPLES AND BUILD_WOLFTPM_LIB)
480480
endif()
481481

482482
function(add_tpm_example name src)
483+
# Optional additional sources may be passed after 'src' (ARGN)
484+
set(_example_srcs examples/${src})
485+
foreach(_extra ${ARGN})
486+
list(APPEND _example_srcs examples/${_extra})
487+
endforeach()
483488
add_executable(${name}
484-
examples/${src}
489+
${_example_srcs}
485490
)
486491
target_link_libraries(${name} PRIVATE wolftpm tpm_test_lib wolftpm_wolfssl_dep)
487492
if(WIN32)
@@ -705,7 +710,7 @@ if (WOLFTPM_EXAMPLES AND BUILD_WOLFTPM_LIB)
705710
add_tpm_example(secure_rot boot/secure_rot.c)
706711
add_tpm_example(csr csr/csr.c)
707712
add_tpm_example(get_ek_certs endorsement/get_ek_certs.c)
708-
add_tpm_example(ifx_fw_update firmware/ifx_fw_update.c)
713+
add_tpm_example(ifx_fw_update firmware/ifx_fw_update.c firmware/firmware_policy.c)
709714
add_tpm_example(gpio_config gpio/gpio_config.c)
710715
add_tpm_example(gpio_read gpio/gpio_read.c)
711716
add_tpm_example(gpio_set gpio/gpio_set.c)

examples/firmware/README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,58 @@ Success: Please reset or power cycle TPM
199199
```
200200

201201
**Note**: Firmware files cannot be made public and must be obtained separately from STMicroelectronics.
202+
203+
## Policy-Based Authorization (Advanced)
204+
205+
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.
206+
207+
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:
208+
209+
- **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.
210+
- **ST33**: the supplied session replaces the default `TPM_RS_PW` password authorization.
211+
212+
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.
213+
214+
Example: satisfy a multi-branch `PolicyOR` (up to 8 branches, SHA2-512 shown) and start the upgrade under it:
215+
216+
```c
217+
WOLFTPM2_SESSION session;
218+
TPML_DIGEST orList;
219+
uint8_t manifest_hash[TPM_SHA512_DIGEST_SIZE];
220+
int rc;
221+
222+
/* zero both structs - orList must not carry uninitialized branch sizes */
223+
XMEMSET(&session, 0, sizeof(session));
224+
XMEMSET(&orList, 0, sizeof(orList));
225+
226+
/* start a policy session using the desired policy hash (SHA2-512 for PQC) */
227+
rc = wolfTPM2_StartSession_ex(&dev, &session, NULL, NULL,
228+
TPM_SE_POLICY, TPM_ALG_NULL, TPM_ALG_SHA512);
229+
if (rc != TPM_RC_SUCCESS) goto cleanup;
230+
231+
/* Satisfy one branch (PCR, PolicySigned/Authorize, PolicyAuthValue, ...), then
232+
* OR against the full branch list the platform authPolicy encodes. Set count
233+
* and each digests[i].size/buffer for every branch you populate. */
234+
orList.count = 2;
235+
/* orList.digests[0].size = ...; XMEMCPY(orList.digests[0].buffer, ...); */
236+
/* orList.digests[1].size = ...; XMEMCPY(orList.digests[1].buffer, ...); */
237+
rc = wolfTPM2_PolicyOR(&dev, &session, &orList);
238+
if (rc != TPM_RC_SUCCESS) goto cleanup;
239+
240+
/* hash the manifest with the matching algorithm, then start the upgrade under
241+
* the caller-satisfied session (NULL would use the library-default auth) */
242+
rc = wc_Sha512Hash(manifest, manifest_sz, manifest_hash);
243+
if (rc != 0) goto cleanup;
244+
rc = wolfTPM2_FirmwareUpgradeHash_ex(&dev, TPM_ALG_SHA512,
245+
manifest_hash, (uint32_t)sizeof(manifest_hash),
246+
manifest, manifest_sz, fwDataCb, fwCbCtx, &session);
247+
248+
cleanup:
249+
/* the TPM consumes the session on a successful start; release it otherwise */
250+
if (session.handle.hndl != 0)
251+
wolfTPM2_UnloadHandle(&dev, &session.handle);
252+
```
253+
254+
Passing `NULL` for the final `startSession` argument makes `wolfTPM2_FirmwareUpgradeHash_ex()` behave exactly like `wolfTPM2_FirmwareUpgradeHash()` (library-managed authorization), so existing code is unaffected.
255+
256+
**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.
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
/* firmware_policy.c
2+
*
3+
* Copyright (C) 2006-2026 wolfSSL Inc.
4+
*
5+
* This file is part of wolfTPM.
6+
*
7+
* wolfTPM is free software; you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation; either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* wolfTPM is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
20+
*/
21+
22+
#ifdef HAVE_CONFIG_H
23+
#include <config.h>
24+
#endif
25+
26+
#include <examples/firmware/firmware_policy.h>
27+
28+
#ifdef WOLFTPM_FIRMWARE_UPGRADE
29+
30+
#include <stdio.h>
31+
32+
/* Print a digest as hex. Unlike TPM2_PrintBin (a no-op unless DEBUG_WOLFTPM),
33+
* this is always available so the self-test failure report is usable in a
34+
* stock build. */
35+
static void firmware_print_hex(const byte* buf, word32 len)
36+
{
37+
word32 j;
38+
for (j = 0; j < len; j++) {
39+
printf("%02x", buf[j]);
40+
}
41+
printf("\n");
42+
}
43+
44+
/* Exercise wolfTPM2_PolicyOR at the requested hash and verify the TPM's
45+
* running policy digest matches an offline computation. Non-destructive.
46+
* Returns 0 on match, 1 if the hash is not implemented (intentional skip),
47+
* -1 on digest mismatch, or a TPM rc / BAD_FUNC_ARG on other errors. */
48+
static int firmware_policy_selftest(WOLFTPM2_DEV* dev, TPMI_ALG_HASH hashAlg,
49+
const char* name)
50+
{
51+
int rc;
52+
WOLFTPM2_SESSION sess;
53+
TPML_DIGEST orList;
54+
word32 hsz = (word32)TPM2_GetHashDigestSize(hashAlg);
55+
byte branchA[TPM_MAX_DIGEST_SIZE];
56+
byte branchB[TPM_MAX_DIGEST_SIZE];
57+
byte concat[2 * TPM_MAX_DIGEST_SIZE];
58+
byte expected[TPM_MAX_DIGEST_SIZE];
59+
byte got[TPM_MAX_DIGEST_SIZE];
60+
word32 aSz, bSz, expSz, gotSz;
61+
62+
XMEMSET(&sess, 0, sizeof(sess));
63+
XMEMSET(&orList, 0, sizeof(orList));
64+
65+
if (hsz == 0 || hsz > TPM_MAX_DIGEST_SIZE) {
66+
return BAD_FUNC_ARG;
67+
}
68+
69+
/* Skip cleanly if the TPM firmware does not implement this hash */
70+
if (!wolfTPM2_IsAlgSupported(dev, hashAlg)) {
71+
printf(" %s: skipped (not implemented by this TPM)\n", name);
72+
return 1; /* intentional skip, not a failure */
73+
}
74+
75+
/* Offline: two distinct PolicyCommandCode branch digests */
76+
aSz = hsz;
77+
rc = wolfTPM2_PolicyCommandCodeMake(hashAlg, branchA, &aSz, TPM_CC_NV_Read);
78+
if (rc == 0) {
79+
bSz = hsz;
80+
rc = wolfTPM2_PolicyCommandCodeMake(hashAlg, branchB, &bSz,
81+
TPM_CC_Unseal);
82+
}
83+
/* Offline PolicyOR digest = H(zeros || TPM_CC_PolicyOR || A || B) */
84+
if (rc == 0) {
85+
XMEMCPY(concat, branchA, aSz);
86+
XMEMCPY(&concat[aSz], branchB, bSz);
87+
XMEMSET(expected, 0, sizeof(expected));
88+
expSz = hsz;
89+
rc = wolfTPM2_PolicyHash(hashAlg, expected, &expSz,
90+
TPM_CC_PolicyOR, concat, aSz + bSz);
91+
}
92+
93+
/* On-TPM: start a policy session using the requested hash algorithm */
94+
if (rc == 0) {
95+
rc = wolfTPM2_StartSession_ex(dev, &sess, NULL, NULL,
96+
TPM_SE_POLICY, TPM_ALG_NULL, hashAlg);
97+
if (rc != 0) {
98+
printf(" %s: StartSession failed 0x%x: %s\n",
99+
name, rc, TPM2_GetRCString(rc));
100+
return rc;
101+
}
102+
}
103+
/* Satisfy branch A, then OR against {A,B} with the new wrapper */
104+
if (rc == 0) {
105+
rc = wolfTPM2_PolicyCommandCode(dev, &sess, TPM_CC_NV_Read);
106+
}
107+
if (rc == 0) {
108+
orList.count = 2;
109+
orList.digests[0].size = (UINT16)aSz;
110+
XMEMCPY(orList.digests[0].buffer, branchA, aSz);
111+
orList.digests[1].size = (UINT16)bSz;
112+
XMEMCPY(orList.digests[1].buffer, branchB, bSz);
113+
rc = wolfTPM2_PolicyOR(dev, &sess, &orList);
114+
}
115+
if (rc == 0) {
116+
gotSz = (word32)sizeof(got);
117+
rc = wolfTPM2_GetPolicyDigest(dev, sess.handle.hndl, got, &gotSz);
118+
}
119+
120+
if (rc == 0) {
121+
if (gotSz == expSz && XMEMCMP(got, expected, expSz) == 0) {
122+
printf(" %s PolicyOR: PASS (%u byte digest matches)\n",
123+
name, expSz);
124+
}
125+
else {
126+
printf(" %s PolicyOR: FAIL (digest mismatch)\n", name);
127+
printf(" expected: ");
128+
firmware_print_hex(expected, expSz);
129+
printf(" got: ");
130+
firmware_print_hex(got, gotSz);
131+
rc = -1;
132+
}
133+
}
134+
else {
135+
printf(" %s PolicyOR: ERROR 0x%x: %s\n",
136+
name, rc, TPM2_GetRCString(rc));
137+
}
138+
139+
wolfTPM2_UnloadHandle(dev, &sess.handle);
140+
return rc;
141+
}
142+
143+
int firmware_policy_selftest_all(WOLFTPM2_DEV* dev)
144+
{
145+
int i, rc, hardFail = 0;
146+
struct { TPMI_ALG_HASH alg; const char* name; } hashes[3];
147+
148+
hashes[0].alg = TPM_ALG_SHA256; hashes[0].name = "SHA2-256";
149+
hashes[1].alg = TPM_ALG_SHA384; hashes[1].name = "SHA2-384";
150+
hashes[2].alg = TPM_ALG_SHA512; hashes[2].name = "SHA2-512";
151+
152+
printf("Firmware policy authorization self-test "
153+
"(no firmware changes):\n");
154+
for (i = 0; i < 3; i++) {
155+
rc = firmware_policy_selftest(dev, hashes[i].alg, hashes[i].name);
156+
/* rc == 1 is an intentional "hash not implemented" skip. Any other
157+
* non-zero (digest mismatch, bad arg, or a TPM rc) is a failure. */
158+
if (rc != 0 && rc != 1) {
159+
hardFail = 1;
160+
}
161+
}
162+
return hardFail ? -1 : 0;
163+
}
164+
165+
int firmware_policy_session_setup(WOLFTPM2_DEV* dev,
166+
TPMI_ALG_HASH hashAlg, int useOr, TPM_CC fuStartCC,
167+
WOLFTPM2_SESSION* session)
168+
{
169+
int rc;
170+
int provisioned = 0;
171+
TPML_DIGEST orList;
172+
word32 hsz = (word32)TPM2_GetHashDigestSize(hashAlg);
173+
byte branchA[TPM_MAX_DIGEST_SIZE];
174+
byte branchB[TPM_MAX_DIGEST_SIZE];
175+
byte concat[2 * TPM_MAX_DIGEST_SIZE];
176+
byte platformPolicy[TPM_MAX_DIGEST_SIZE];
177+
word32 aSz, bSz = 0, polSz = 0;
178+
179+
if (hsz == 0 || hsz > TPM_MAX_DIGEST_SIZE) {
180+
return BAD_FUNC_ARG;
181+
}
182+
XMEMSET(session, 0, sizeof(*session));
183+
XMEMSET(&orList, 0, sizeof(orList));
184+
185+
/* Fail early (before provisioning) if the TPM can't use this policy hash */
186+
if (!wolfTPM2_IsAlgSupported(dev, hashAlg)) {
187+
printf("Policy hash %s not implemented by this TPM\n",
188+
TPM2_GetAlgName(hashAlg));
189+
return BAD_FUNC_ARG;
190+
}
191+
192+
printf("Provisioning platform policy (%s, %s)\n",
193+
useOr ? "PolicyOR" : "PolicyCommandCode",
194+
TPM2_GetAlgName(hashAlg));
195+
196+
/* Branch A: PolicyCommandCode(FieldUpgradeStart) - required to start FU */
197+
aSz = hsz;
198+
rc = wolfTPM2_PolicyCommandCodeMake(hashAlg, branchA, &aSz, fuStartCC);
199+
200+
/* Compute the platform authPolicy digest */
201+
if (rc == 0) {
202+
if (useOr) {
203+
/* Branch B: a second, distinct policy branch */
204+
bSz = hsz;
205+
rc = wolfTPM2_PolicyCommandCodeMake(hashAlg, branchB, &bSz,
206+
TPM_CC_NV_Read);
207+
if (rc == 0) {
208+
XMEMCPY(concat, branchA, aSz);
209+
XMEMCPY(&concat[aSz], branchB, bSz);
210+
XMEMSET(platformPolicy, 0, sizeof(platformPolicy));
211+
polSz = hsz;
212+
rc = wolfTPM2_PolicyHash(hashAlg, platformPolicy, &polSz,
213+
TPM_CC_PolicyOR, concat, aSz + bSz);
214+
}
215+
}
216+
else {
217+
XMEMCPY(platformPolicy, branchA, aSz);
218+
polSz = aSz;
219+
}
220+
}
221+
222+
/* Provision the platform primary policy (empty platformAuth) */
223+
if (rc == 0) {
224+
rc = wolfTPM2_SetPrimaryPolicy(dev, TPM_RH_PLATFORM, hashAlg,
225+
platformPolicy, polSz);
226+
if (rc != 0) {
227+
printf(" SetPrimaryPolicy failed 0x%x: %s\n",
228+
rc, TPM2_GetRCString(rc));
229+
}
230+
else {
231+
provisioned = 1;
232+
}
233+
}
234+
235+
/* Start a policy session and satisfy the platform policy */
236+
if (rc == 0) {
237+
rc = wolfTPM2_StartSession_ex(dev, session, NULL, NULL,
238+
TPM_SE_POLICY, TPM_ALG_NULL, hashAlg);
239+
if (rc != 0) {
240+
printf(" StartSession failed 0x%x: %s\n",
241+
rc, TPM2_GetRCString(rc));
242+
}
243+
}
244+
if (rc == 0) {
245+
rc = wolfTPM2_PolicyCommandCode(dev, session, fuStartCC);
246+
}
247+
if (rc == 0 && useOr) {
248+
orList.count = 2;
249+
orList.digests[0].size = (UINT16)aSz;
250+
XMEMCPY(orList.digests[0].buffer, branchA, aSz);
251+
orList.digests[1].size = (UINT16)bSz;
252+
XMEMCPY(orList.digests[1].buffer, branchB, bSz);
253+
rc = wolfTPM2_PolicyOR(dev, session, &orList);
254+
}
255+
256+
if (rc != 0) {
257+
if (session->handle.hndl != 0) {
258+
wolfTPM2_UnloadHandle(dev, &session->handle);
259+
}
260+
/* Restore default platform auth so a later run is not locked out (the
261+
* platform policy is otherwise cleared only on TPM reset). */
262+
if (provisioned &&
263+
wolfTPM2_SetPrimaryPolicy(dev, TPM_RH_PLATFORM, TPM_ALG_NULL,
264+
NULL, 0) == TPM_RC_SUCCESS) {
265+
printf(" Cleared platform policy after setup failure\n");
266+
}
267+
}
268+
return rc;
269+
}
270+
271+
#endif /* WOLFTPM_FIRMWARE_UPGRADE */

0 commit comments

Comments
 (0)