Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/test-configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,18 @@ jobs:
arch: arm
config-file: ./config/examples/stm32h5-tz-tpm.config

stm32h5_tz_tpm_mfgid_test:
uses: ./.github/workflows/test-build.yml
with:
arch: arm
config-file: ./config/examples/stm32h5-tz-tpm-mfgid.config

stm32h5_tz_tpm_mfgid_precomputed_test:
uses: ./.github/workflows/test-build.yml
with:
arch: arm
config-file: ./config/examples/stm32h5-tz-tpm-mfgid-precomputed.config

stm32h5_tz_dualbank_test:
uses: ./.github/workflows/test-build.yml
with:
Expand Down
44 changes: 44 additions & 0 deletions config/examples/stm32h5-tz-tpm-mfgid-precomputed.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
ARCH?=ARM
TZEN?=1
TARGET?=stm32h5
SIGN?=ECC256
HASH?=SHA256
DEBUG?=0
VTOR?=1
CORTEX_M0?=0
CORTEX_M33?=1
NO_ASM?=0
NO_MPU=1
EXT_FLASH?=0
SPI_FLASH?=0
ALLOW_DOWNGRADE?=0
NVM_FLASH_WRITEONCE?=1
WOLFBOOT_VERSION?=1
V?=0
SPMATH?=1
RAM_CODE?=1
DUALBANK_SWAP?=0
WOLFBOOT_PARTITION_SIZE?=0xA0000
WOLFBOOT_SECTOR_SIZE?=0x2000
WOLFBOOT_KEYVAULT_ADDRESS?=0x0C040000
WOLFBOOT_KEYVAULT_SIZE?=0x1C000
WOLFBOOT_NSC_ADDRESS?=0x0C05C000
WOLFBOOT_NSC_SIZE?=0x4000
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08060000
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x0C100000
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x0C1A0000
FLAGS_HOME=0
DISABLE_BACKUP=0
WOLFCRYPT_TZ=1
WOLFCRYPT_TZ_PKCS11=1
IMAGE_HEADER_SIZE?=1024
ARMORED=1
WOLFTPM=1
# Exercise the pre-provisioned ST33KTPM identity keys (IAK/IDevID).
# ST33 vendor support is required (wolfTPM2_SetIdentityAuth -> TPM2_GetProductInfo).
CFLAGS_EXTRA+=-DWOLFTPM_ST33
CFLAGS_EXTRA+=-DWOLFTPM_MFG_IDENTITY
# Default precomputed mode (WOLFBOOT_TPM_MFG_AUTH_DERIVE unset): the per-device
# authValue is set directly from the WOLFBOOT_TPM_MFG_AIK_AUTH/EH_AUTH macros.
# Ships a 0xFF placeholder that fails TPM auth until provisioned per-device; this
# config exists to build-test the precomputed branch in CI.
44 changes: 44 additions & 0 deletions config/examples/stm32h5-tz-tpm-mfgid.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
ARCH?=ARM
TZEN?=1
TARGET?=stm32h5
SIGN?=ECC256
HASH?=SHA256
DEBUG?=0
VTOR?=1
CORTEX_M0?=0
CORTEX_M33?=1
NO_ASM?=0
NO_MPU=1
EXT_FLASH?=0
SPI_FLASH?=0
ALLOW_DOWNGRADE?=0
NVM_FLASH_WRITEONCE?=1
WOLFBOOT_VERSION?=1
V?=0
SPMATH?=1
RAM_CODE?=1
DUALBANK_SWAP?=0
WOLFBOOT_PARTITION_SIZE?=0xA0000
WOLFBOOT_SECTOR_SIZE?=0x2000
WOLFBOOT_KEYVAULT_ADDRESS?=0x0C040000
WOLFBOOT_KEYVAULT_SIZE?=0x1C000
WOLFBOOT_NSC_ADDRESS?=0x0C05C000
WOLFBOOT_NSC_SIZE?=0x4000
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08060000
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x0C100000
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x0C1A0000
FLAGS_HOME=0
DISABLE_BACKUP=0
WOLFCRYPT_TZ=1
WOLFCRYPT_TZ_PKCS11=1
IMAGE_HEADER_SIZE?=1024
ARMORED=1
WOLFTPM=1
# Exercise the pre-provisioned ST33KTPM identity keys (IAK/IDevID).
# ST33 vendor support is required (wolfTPM2_SetIdentityAuth -> TPM2_GetProductInfo).
CFLAGS_EXTRA+=-DWOLFTPM_ST33
CFLAGS_EXTRA+=-DWOLFTPM_MFG_IDENTITY
# Derive the authValue on-device so the test-app works on a sample TPM. The
# default precomputed mode ships a 0xFF placeholder that must be provisioned
# per-device first.
WOLFBOOT_TPM_MFG_AUTH_DERIVE=1
36 changes: 36 additions & 0 deletions docs/TPM.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,42 @@ In wolfBoot we support TPM based root of trust, sealing/unsealing, cryptographic
| `WOLFBOOT_TPM_SEAL=1` | `WOLFBOOT_TPM_SEAL` | Enables support for sealing/unsealing based on PCR policy signed externally. |
| `WOLFBOOT_TPM_SEAL_NV_BASE=0x01400300` | `WOLFBOOT_TPM_SEAL_NV_BASE` | To override the default sealed blob storage location in the platform hierarchy. |
| `WOLFBOOT_TPM_SEAL_AUTH=secret` | `WOLFBOOT_TPM_SEAL_AUTH` | Password for sealing/unsealing secrets, if omitted the PCR policy will be used |
| `WOLFBOOT_TPM_MFG_AUTH_DERIVE=1` | `WOLFBOOT_TPM_MFG_AUTH_DERIVE` | MFG identity: opt into on-device derive-from-master. The default is a precomputed per-device authValue (no master secret on device). Requires `WOLFTPM_MFG_IDENTITY`. |
| (header macro) | `WOLFBOOT_TPM_MFG_AIK_AUTH` / `WOLFBOOT_TPM_MFG_EH_AUTH` | Default (precomputed) mode: the 16-byte per-device AIK / EH authValues (placeholder `0xFF` default). |
| (header macro) | `WOLFBOOT_TPM_MFG_EH_MASTER` | Derive mode: override the endorsement-hierarchy master value (16-byte initializer list, sample default). |

## TPM manufacturing identity (IAK / IDevID authValue)

When `WOLFTPM_MFG_IDENTITY` is enabled, `wolfBoot_tpm2_get_aik()` and
`wolfBoot_tpm2_get_timestamp()` authorize the pre-provisioned ST33KTPM identity
keys. There are two ways to supply the required `authValue`:

- **Precomputed mode (default, recommended).** The final **per-device**
`authValue` is set directly into the key handle; no master secret is present on
the device. Defaults to a `0xFF` placeholder (fails TPM auth until
provisioned). Per-device values are computed off-device at provisioning
(`SHA-256(CPSN || master)`, low 16 bytes) and baked in via
`WOLFBOOT_TPM_MFG_AIK_AUTH` / `WOLFBOOT_TPM_MFG_EH_AUTH`. When `WOLFBOOT_TPM_MFG_AUTH_DERIVE`
is not enabled, the `authOverride` argument to `wolfBoot_tpm2_get_aik()` is
treated as an optional override for the final AIK `authValue` (not a master secret).

- **Derive mode (`WOLFBOOT_TPM_MFG_AUTH_DERIVE`).** The `authValue` is computed
on-device as the low 16 bytes of `SHA-256(TPM serial || master)`.
The endorsement master defaults to a sample and is overridable with
`WOLFBOOT_TPM_MFG_EH_MASTER`; the AIK master is passed to
`wolfBoot_tpm2_get_aik()` (NULL = sample). Convenient, but the master is
**shared across the whole reel/batch** — extracting it
from one device's firmware lets an attacker derive the `authValue` for every
sibling device.

The byte-array macros are header defaults (overridable via `-D` / `CFLAGS_EXTRA`);
they are not plain `options.mk` variables because brace initializers contain
commas. `WOLFTPM_MFG_IDENTITY` itself is supplied via `CFLAGS_EXTRA`.

> Note: because precomputed mode is the default and ships a `0xFF` placeholder, a
> build that enables `WOLFTPM_MFG_IDENTITY` without provisioning the authValue (or
> selecting `WOLFBOOT_TPM_MFG_AUTH_DERIVE`) fails TPM auth by design. The test-app
> builds with `WOLFBOOT_TPM_MFG_AUTH_DERIVE` so it works on a sample TPM.

## Root of Trust (ROT)

Expand Down
36 changes: 35 additions & 1 deletion include/tpm.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,42 @@ int CSME_NSE_API wolfBoot_tpm2_read_pcr(uint8_t pcrIndex, uint8_t* digest, int*
int CSME_NSE_API wolfBoot_tpm2_read_cert(uint32_t handle, uint8_t* cert, uint32_t* certSz);

#ifdef WOLFTPM_MFG_IDENTITY

/* MFG identity auth provisioning.
* Precomputed mode (default): the final per-device authValue is set directly,
* no master secret on the device. In this mode, wolfBoot_tpm2_get_aik() treats
* the authOverride argument as an optional *authValue* override.
* Derive mode (WOLFBOOT_TPM_MFG_AUTH_DERIVE): authValue = low 16 bytes of
* SHA-256(TPM serial || master); the master is shared across the reel.
* For wolfBoot_tpm2_get_aik() the master secret is provided via the
* authOverride argument (NULL = sample). */
#ifdef WOLFBOOT_TPM_MFG_AUTH_DERIVE
/* EH master for derive mode (sample - override in production) */
#ifndef WOLFBOOT_TPM_MFG_EH_MASTER
#define WOLFBOOT_TPM_MFG_EH_MASTER { \
0xDE, 0xEF, 0x8C, 0xDF, 0x1B, 0x77, 0xBD, 0x00, \
0x30, 0x58, 0x5E, 0x47, 0xB8, 0x21, 0x46, 0x0B }
#endif
#else
/* 16-byte per-device authValues. Placeholder defaults (all 0xFF) fail TPM auth
* until overwritten per-device by the provisioning tool. */
#ifndef WOLFBOOT_TPM_MFG_AIK_AUTH
#define WOLFBOOT_TPM_MFG_AIK_AUTH { \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
#endif
#ifndef WOLFBOOT_TPM_MFG_EH_AUTH
#define WOLFBOOT_TPM_MFG_EH_AUTH { \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
#endif
#endif

/* authOverride meaning depends on WOLFBOOT_TPM_MFG_AUTH_DERIVE:
* derive mode -> master secret hashed into the authValue (NULL = sample)
* precomputed mode -> optional literal authValue override (NULL = built-in) */
int CSME_NSE_API wolfBoot_tpm2_get_aik(WOLFTPM2_KEY* aik,
uint8_t* masterPassword, uint16_t masterPasswordSz);
uint8_t* authOverride, uint16_t authOverrideSz);
int CSME_NSE_API wolfBoot_tpm2_get_timestamp(WOLFTPM2_KEY* aik, GetTime_Out* getTime);
int CSME_NSE_API wolfBoot_tpm2_quote(WOLFTPM2_KEY* aik,
byte* pcrArray, word32 pcrArraySz, Quote_Out* quoteResult);
Expand Down
6 changes: 6 additions & 0 deletions options.mk
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ ifeq ($(WOLFBOOT_TPM_KEYSTORE),1)
endif
endif

## TPM manufacturing identity: precomputed per-device authValue is the default
## (no master secret on device). Opt into on-device derive-from-master mode:
ifeq ($(WOLFBOOT_TPM_MFG_AUTH_DERIVE),1)
CFLAGS+=-D"WOLFBOOT_TPM_MFG_AUTH_DERIVE"
endif

ifeq ($(WOLFBOOT_ATTESTATION_IAK),1)
CFLAGS+=-D"WOLFBOOT_ATTESTATION_IAK"
endif
Expand Down
59 changes: 47 additions & 12 deletions src/tpm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1345,8 +1345,22 @@ int CSME_NSE_API wolfBoot_tpm2_read_cert(uint32_t handle, uint8_t* cert, uint32_
}

#ifdef WOLFTPM_MFG_IDENTITY
#ifndef WOLFBOOT_TPM_MFG_AUTH_DERIVE
/* Copy a precomputed authValue directly into a TPM handle. */
static int wolfBoot_tpm2_set_handle_auth(WOLFTPM2_HANDLE* handle,
const uint8_t* auth, uint16_t authSz)
{
if (authSz > (uint16_t)sizeof(handle->auth.buffer)) {
return BAD_FUNC_ARG;
}
handle->auth.size = authSz;
XMEMCPY(handle->auth.buffer, auth, authSz);
return 0;
}
#endif

int CSME_NSE_API wolfBoot_tpm2_get_aik(WOLFTPM2_KEY* aik,
uint8_t* masterPassword, uint16_t masterPasswordSz)
uint8_t* authOverride, uint16_t authOverrideSz)
{
int rc;
if (aik == NULL) {
Expand All @@ -1355,19 +1369,30 @@ int CSME_NSE_API wolfBoot_tpm2_get_aik(WOLFTPM2_KEY* aik,
if (WOLFBOOT_TPM_NS_RW(aik, sizeof(*aik)) == NULL) {
return BAD_FUNC_ARG;
}
if (masterPassword != NULL &&
WOLFBOOT_TPM_NS_R(masterPassword, masterPasswordSz) == NULL) {
if (authOverride != NULL &&
WOLFBOOT_TPM_NS_R(authOverride, authOverrideSz) == NULL) {
return BAD_FUNC_ARG;
}

/* Load existing AIK and set auth */
Comment thread
dgarske marked this conversation as resolved.
rc = wolfTPM2_ReadPublicKey(&wolftpm_dev, aik, TPM2_IAK_KEY_HANDLE);
if (rc == 0) {
/* Custom should supply their own custom master password used during
* device provisioning. If using a sample TPM supply NULL to use the
* default password. */
#ifdef WOLFBOOT_TPM_MFG_AUTH_DERIVE
/* Derives the authValue on-device from a master secret shared across the
* reel; the precomputed default is preferred. Supply NULL for
* authOverride to use the sample default. */
rc = wolfTPM2_SetIdentityAuth(&wolftpm_dev, &aik->handle,
masterPassword, masterPasswordSz);
authOverride, authOverrideSz);
#else
Comment thread
dgarske marked this conversation as resolved.
/* Precomputed (default): set the final per-device authValue directly (no
* master secret on device). Caller may override the default via
* authOverride. */
static const uint8_t aikAuth[] = WOLFBOOT_TPM_MFG_AIK_AUTH;
const uint8_t* auth = (authOverride != NULL) ? authOverride : aikAuth;
uint16_t authSz = (authOverride != NULL) ?
authOverrideSz : (uint16_t)sizeof(aikAuth);
rc = wolfBoot_tpm2_set_handle_auth(&aik->handle, auth, authSz);
#endif
}
return rc;
}
Comment thread
dgarske marked this conversation as resolved.
Expand All @@ -1376,11 +1401,13 @@ int CSME_NSE_API wolfBoot_tpm2_get_timestamp(WOLFTPM2_KEY* aik, GetTime_Out* get
{
int rc;
WOLFTPM2_HANDLE eh_handle;
/* sample master password for EH */
uint8_t Master_EH_AuthValue[] = {
0xDE, 0xEF, 0x8C, 0xDF, 0x1B, 0x77, 0xBD, 0x00,
0x30, 0x58, 0x5E, 0x47, 0xB8, 0x21, 0x46, 0x0B
};
#ifdef WOLFBOOT_TPM_MFG_AUTH_DERIVE
Comment thread
dgarske marked this conversation as resolved.
/* EH master secret (shared across the reel) */
uint8_t Master_EH_AuthValue[] = WOLFBOOT_TPM_MFG_EH_MASTER;
#else
/* final per-device EH authValue */
static const uint8_t eh_auth[] = WOLFBOOT_TPM_MFG_EH_AUTH;
#endif

if (aik == NULL || getTime == NULL) {
return BAD_FUNC_ARG;
Expand All @@ -1395,9 +1422,17 @@ int CSME_NSE_API wolfBoot_tpm2_get_timestamp(WOLFTPM2_KEY* aik, GetTime_Out* get

eh_handle.hndl = TPM_RH_ENDORSEMENT;

#ifdef WOLFBOOT_TPM_MFG_AUTH_DERIVE
/* Calculate EH auth value */
rc = wolfTPM2_SetIdentityAuth(&wolftpm_dev, &eh_handle,
Master_EH_AuthValue, (uint16_t)sizeof(Master_EH_AuthValue));
/* master secret consumed; clear it from the stack */
TPM2_ForceZero(Master_EH_AuthValue, sizeof(Master_EH_AuthValue));
#else
/* Set EH authValue directly */
rc = wolfBoot_tpm2_set_handle_auth(&eh_handle, eh_auth,
(uint16_t)sizeof(eh_auth));
#endif
if (rc == 0) {
/* Set EH auth */
wolfTPM2_SetAuthHandle(&wolftpm_dev, 0, &eh_handle);
Expand Down
2 changes: 2 additions & 0 deletions tools/config.mk
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ ifeq ($(ARCH),)
MEASURED_BOOT?=0
WOLFBOOT_TPM_SEAL?=0
WOLFBOOT_TPM_KEYSTORE?=0
WOLFBOOT_TPM_MFG_AUTH_DERIVE?=0
WOLFBOOT_ATTESTATION_IAK?=0
WOLFBOOT_ATTESTATION_TEST?=0
WOLFBOOT_UNIVERSAL_KEYSTORE?=0
Expand Down Expand Up @@ -106,6 +107,7 @@ CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO
DISABLE_BACKUP WOLFBOOT_VERSION V NO_MPU ENCRYPT FLAGS_HOME FLAGS_INVERT \
SPMATH SPMATHALL RAM_CODE DUALBANK_SWAP IMAGE_HEADER_SIZE PKA TZEN PSOC6_CRYPTO \
WOLFTPM WOLFBOOT_TPM_VERIFY MEASURED_BOOT WOLFBOOT_TPM_SEAL WOLFBOOT_TPM_KEYSTORE \
WOLFBOOT_TPM_MFG_AUTH_DERIVE \
WOLFBOOT_ATTESTATION_IAK \
WOLFBOOT_ATTESTATION_TEST \
WOLFBOOT_UDS_UID_FALLBACK_FORTEST \
Expand Down
Loading