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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ set(TPM_SOURCES
src/tpm2_packet.c
src/tpm2_param_enc.c
src/tpm2_swtpm.c
src/tpm2_optee.c
src/tpm2_tis.c
src/tpm2_winapi.c
src/tpm2_wrap.c
Expand Down
28 changes: 28 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,33 @@ fi
AC_SUBST([SWTPM_PORT])
AC_SUBST([DISTCHECK_SWTPM_PORT_FLAG])

# fwTPM as an OP-TEE trusted application, reached over libteec
AC_ARG_ENABLE([optee],
[AS_HELP_STRING([--enable-optee],[Enable use of the wolfTPM fwTPM running as an OP-TEE trusted application, reached over libteec (default: disabled)])],
[ ENABLED_OPTEE=$enableval ],
[ ENABLED_OPTEE=no ]
)

if test "x$ENABLED_OPTEE" = "xyes"
then
if test "x$ENABLED_DEVTPM" = "xyes"
then
AC_MSG_ERROR([Cannot enable both optee and devtpm])
fi
# swtpm is the default on Linux x86_64/aarch64, which is where an OP-TEE
# client runs, so say which flag to pass rather than just refusing.
if test "x$ENABLED_SWTPM" = "xyes" || test "x$ENABLED_SWTPM" = "xuart"
then
AC_MSG_ERROR([Cannot enable both optee and swtpm; add --disable-swtpm])
fi
AC_CHECK_HEADER([tee_client_api.h], [],
[AC_MSG_ERROR([--enable-optee requires tee_client_api.h from the OP-TEE client])])
AC_CHECK_LIB([teec], [TEEC_InitializeContext], [],
[AC_MSG_ERROR([--enable-optee requires libteec from the OP-TEE client])])
AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_OPTEE"
fi
AM_CONDITIONAL([BUILD_OPTEE], [test "x$ENABLED_OPTEE" = "xyes"])

# Firmware TPM (fwTPM) - software TPM 2.0 simulator
AC_ARG_ENABLE([fwtpm],
[AS_HELP_STRING([--enable-fwtpm],[Enable firmware TPM (fwTPM) server (default: enabled on Linux x86_64/aarch64, disabled elsewhere or when any --enable-<vendor>/--enable-spi/--enable-i2c/--enable-mmio/--enable-devtpm/--enable-autodetect is explicitly set)])],
Expand Down Expand Up @@ -1261,6 +1288,7 @@ echo " * SPI: $SPI_ACTUAL"
echo " * I2C: $ENABLED_I2C"
echo " * Linux kernel TPM device: $ENABLED_DEVTPM"
echo " * SWTPM: $ENABLED_SWTPM"
echo " * OP-TEE fwTPM TA: $ENABLED_OPTEE"
if test "x$ENABLED_SWTPM" = "xyes"; then
echo " * SWTPM Port: $SWTPM_PORT"
fi
Expand Down
4 changes: 3 additions & 1 deletion hal/tpm_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@
/* --- BEGIN IO Callback Logic -- */
/******************************************************************************/

/* Native Windows, native Linux and TPM Simulator do not need an IO callback */
/* Native Windows, native Linux, TPM Simulator and the OP-TEE trusted
* application do not need an IO callback */
#if ! (defined(WOLFTPM_LINUX_DEV) || \
defined(WOLFTPM_SWTPM) || \
defined(WOLFTPM_OPTEE) || \
defined(WOLFTPM_WINAPI) ) && \
(defined(WOLFTPM_EXAMPLE_HAL) || defined(WOLFTPM_MMIO) || \
defined(WOLFTPM_FWTPM_HAL))
Expand Down
2 changes: 1 addition & 1 deletion hal/tpm_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
*/

#if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_SWTPM) || \
defined(WOLFTPM_WINAPI)
defined(WOLFTPM_OPTEE) || defined(WOLFTPM_WINAPI)

/* HAL not required, so use NULL */
#define TPM2_IoCb NULL
Expand Down
2 changes: 1 addition & 1 deletion src/fwtpm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ compile-time macros (feature groups, algorithm flags, size limits, stack/heap
control), spec version targeting, and HAL abstraction details.

Key options: `--enable-fwtpm`, `--enable-fwtpm-only`, `--enable-swtpm`,
`--enable-fwtpm-small-ctx`, `--enable-fuzz`. Feature disable macros:
`--enable-fwtpm-nv-appendonly`, `--enable-fuzz`. Feature disable macros:
`FWTPM_NO_ATTESTATION`, `FWTPM_NO_NV`, `FWTPM_NO_POLICY`, `FWTPM_NO_CREDENTIAL`,
`FWTPM_NO_DA`, `FWTPM_NO_PARAM_ENC`, `FWTPM_NO_KEY_MIGRATION`, `FWTPM_NO_ECDH`,
`FWTPM_NO_HASH_CMDS`, `FWTPM_NO_CONTEXT`, `FWTPM_NO_SYM_ENCRYPT`,
Expand Down
2 changes: 0 additions & 2 deletions src/fwtpm/fwtpm_command.c
Original file line number Diff line number Diff line change
Expand Up @@ -20782,8 +20782,6 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx,
sizeof(ctx->activeCmdAuthIsPolicy));
#endif

/* rspSize is in/out: capacity in, bytes written out. Callers that leave
* it unset get the historic FWTPM_MAX_COMMAND_SIZE assumption. */
/* Handlers commit state before marshalling and some write outside the
* packet API, so they are only ever run with a full-size buffer. A
* caller with a smaller transport buffer must stage through one.
Expand Down
13 changes: 12 additions & 1 deletion src/fwtpm/fwtpm_nv.c
Original file line number Diff line number Diff line change
Expand Up @@ -2331,9 +2331,20 @@ int FWTPM_NV_Init(FWTPM_CTX* ctx)
/* No valid NV image — generate fresh hierarchy seeds */
if (rc == TPM_RC_SUCCESS && hdr.magic == FWTPM_NV_MAGIC &&
hdr.version != FWTPM_NV_VERSION) {
/* Unguarded on purpose: it reports that all NV state was just
* discarded. libutee has no stdio streams, so a trusted
* application cannot link fprintf; every other build keeps
* stderr, so nothing starts writing diagnostics to a stdout that
* the caller may be using as a data channel. */
#ifdef WOLFSSL_OPTEE
printf("WARNING: fwTPM NV version mismatch "
"(found %d, expected %d), regenerating seeds\n",
(int)hdr.version, (int)FWTPM_NV_VERSION);
#else
fprintf(stderr, "WARNING: fwTPM NV version mismatch "
"(found %d, expected %d) — regenerating seeds\n",
"(found %d, expected %d), regenerating seeds\n",
(int)hdr.version, (int)FWTPM_NV_VERSION);
#endif
}
rc = FwNvGenFreshState(ctx);
}
Expand Down
122 changes: 122 additions & 0 deletions src/fwtpm/ports/optee/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# fwTPM on OP-TEE

The wolfTPM fwTPM running as an OP-TEE trusted application, with the TPM 2.0
command processor in the secure world and the client in the normal world.

Platform independent: these two sources need only the TEE Internal Core API
and OP-TEE secure storage. A board port supplies `user_ta_header_defines.h`
(UUID, flags, stack and heap sizes), the wolfCrypt platform shims and the
build, sign and deploy steps.

| File | Role |
|------|------|
| `wolftpm/fwtpm/fwtpm_optee.h` | The trusted application's UUID, the command IDs shared with the normal-world client, and the NV HAL and clock HAL declarations |
| `fwtpm_optee_ta.c` | `TA_*EntryPoint` functions; one invocation carries one whole TPM command |
| `fwtpm_optee_nv.c` | NV journal on OP-TEE secure storage, with the journal integrity key derived from the hardware unique key |

## Transport

`FWTPM_ProcessCommand` takes a command buffer and returns a response buffer,
which is exactly the shape of `TA_InvokeCommandEntryPoint`, so no IO HAL and
no TIS layer is involved. `FWTPM_IO_ServerLoop` and `FWTPM_TIS_ServerLoop`
both block and cannot be used in a trusted application, so `fwtpm_io.c`,
`fwtpm_tis.c`, `fwtpm_tis_shm.c` and `fwtpm_main.c` are left out of the
build.

Both directions are staged through static buffers of `FWTPM_MAX_COMMAND_SIZE`.
The response buffer must be that large whatever the client's capacity, and
staging the command as well means the normal world cannot change it after it
has been parsed.

`locality` is fixed at `FWTPM_OPTEE_LOCALITY` and never taken from the
client: it is stored verbatim into the command context without validation.

## NV storage

Secure storage is byte addressable and fully rewritable, so the journal runs
in its default mode with `appendOnly` and `writeAlign` left at 0. The
append-only build option is for write-once flash and is not needed here.

A read past the end of the stream is reported as erased (`0xFF`), which is how
the journal recognizes unwritten NV and decides to generate fresh seeds on
first boot.

`get_integrity_key` asks the system PTA for a key derived from the hardware
unique key and bound to the TA UUID. Platforms built without the system PTA
fall back to a random key in its own secure storage object. Secure storage is
itself hardware-unique-key encrypted and TA bound, so the journal MAC is
defense in depth.

## wolfCrypt platform layer

None is needed here. wolfSSL maps `XMALLOC`, `XFREE` and `XREALLOC` onto
`TEE_Malloc`, `TEE_Free` and `TEE_Realloc`, and seeds its DRBG from
`TEE_GenerateRandom`, whenever `WOLFSSL_OPTEE` is defined. A port does not
supply shims or set `XMALLOC_OVERRIDE`.

Where wolfCrypt is not compiled in at all (`WOLFTPM2_NO_WOLFCRYPT`),
`wolftpm/tpm2_types.h` does the same for its own `XMALLOC` and `XFREE`.
`WOLFTPM2_NO_HEAP` still wins there and leaves both undefined.

## Required wolfCrypt options

A trusted application is built from a `user_settings.h` with no configure
step, so nothing checks the wolfCrypt configuration the way wolfTPM's
configure does. The fwTPM needs, beyond the obvious RSA, ECC, AES-CFB,
HMAC/HKDF, SHA-1/2/3 and ASN.1:

| Option | Why |
|--------|-----|
| `WOLFSSL_OPTEE` | names the environment, and brings in the heap and RNG mapping above |
| `WOLFSSL_KEY_GEN` | the TPM generates its own primary keys |
| `WC_RSA_PSS` | TPM 2.0 RSA signing schemes |
| `WC_RSA_NO_PADDING` | raw RSA; without it those operations return `TPM_RC_SCHEME` |
| `WOLFSSL_AES_CFB`, `WOLFSSL_AES_DIRECT` | TPM 2.0 parameter encryption |
| `WOLFSSL_NO_DER_TO_PEM` | `WOLFSSL_KEY_GEN` otherwise pulls in `wc_DerToPemEx`, which needs a `strncat` libutee does not provide |
| `USE_WOLF_STRCASECMP`, `USE_WOLF_STRNCASECMP` | libutee has neither |
| `WOLFSSL_HAVE_MLDSA`, `WOLFSSL_HAVE_MLKEM` | the v1.85 post-quantum commands |

`WOLFSSL_OPTEE` is not optional: the trusted application toolchain is a Linux
cross compiler and predefines `__unix__`, so without it wolfCrypt compiles
descriptor and socket code a trusted application cannot provide.

OP-TEE's `compiler.h` defines a `fallthrough` macro. wolfCrypt only notices
it if `compiler.h` was included first and otherwise settles on
`__attribute__((fallthrough))`, which then expands through OP-TEE's macro and
nests the attribute inside itself, so define `FALL_THROUGH` explicitly.

## Client

Build wolfTPM with `WOLFTPM_OPTEE` to reach the TA over `libteec`. One
`TEEC_InvokeCommand` maps onto one `FWTPM_ProcessCommand`, following the
`WOLFTPM_SWTPM` whole-command pattern rather than emulating TIS registers,
which would cost a world switch per register access.

One session per process: the client holds the TEE session in file scope, so
two `TPM2_CTX` in one process share it and `TPM2_Cleanup` on either closes it.
The other reconnects on its next command, but the trusted application flushes
transient objects on session close, so transient handles do not survive. Create
and use a key in one context, or make the handle persistent with
`TPM2_EvictControl`.

## UUID

OP-TEE identifies a trusted application by UUID, and the same value has to
appear in the trusted application, in the signed binary's filename and in the
client, which are built separately. `WOLFTPM_OPTEE_TA_UUID_STR` and the `_P1`
to `_P4` macros beside it in `wolftpm/fwtpm/fwtpm_optee.h` are the one
definition; the client builds `WOLFTPM_OPTEE_TA_UUID` from them and a board
port generates its `TA_UUID` and binary name from them. A mismatch appears
only at run time, as `TEEC_OpenSession` returning `0xffff0008`.

## Build order

XMEMSET(&ctx, 0, sizeof(ctx));
FWTPM_OPTEE_NV_SetHAL(&ctx, &nv); /* before FWTPM_Init */
FWTPM_Clock_SetHAL(&ctx, FWTPM_OPTEE_Clock_GetMs, NULL); /* optional */
FWTPM_Init(&ctx);

## Reference port

NVIDIA Jetson Orin: see `NVIDIA/fwtpm-jetson-optee` in
[wolftpm-examples](https://github.com/wolfSSL/wolftpm-examples).
Loading
Loading