Skip to content

Add new Firmware TPM (fwTPM)#474

Open
dgarske wants to merge 7 commits intowolfSSL:masterfrom
dgarske:fwtpm
Open

Add new Firmware TPM (fwTPM)#474
dgarske wants to merge 7 commits intowolfSSL:masterfrom
dgarske:fwtpm

Conversation

@dgarske
Copy link
Copy Markdown
Contributor

@dgarske dgarske commented Mar 21, 2026

Summary

  • Add firmware TPM 2.0 server (fwTPM) implementing TPM 2.0 spec v1.38 (105/113 commands, 93% coverage)
  • Spec-compliant primary key derivation: deterministic RSA (iterative KDFa prime generation), ECC (KDFa scalar, Q=d*G), KEYEDHASH/SYMCIPHER keys — same seed + template always produces the same key
  • ChangePPS / ChangeEPS hierarchy commands with seed regeneration and cache flush
  • NV storage with TLV journal format (flash-friendly, append-only)
  • Socket (mssim) and TIS/SHM transports for desktop, UART transport for embedded
  • STM32 Cortex-M33 bare-metal port with TrustZone (CMSE) support
  • Shared crypto refactoring: extract KDFa/KDFe, AES-CFB, HMAC/Hash to tpm2_crypto.c
  • Bounds-checked TPM2_Packet_ParseU16Buf variant for safer response parsing
  • Auth validation: reject oversized auth values instead of silent truncation
  • SIGTERM/SIGINT signal handler for graceful NV save on server kill

fwTPM Server

Core TPM 2.0 command processing in src/fwtpm/:

  • fwtpm_command.c — 105 command handlers with full auth, sessions, parameter encryption
  • fwtpm_nv.c — TLV journal NV storage (file-based default, HAL-abstracted for flash)
  • fwtpm_io.c — Socket transport (mssim + swtpm protocol auto-detection)
  • fwtpm_tis.c / fwtpm_tis_shm.c — TIS register interface via POSIX shared memory
  • fwtpm_crypto.c — Key generation, sign/verify, seed encrypt/decrypt helpers
  • Clock HAL with NV-persisted clockOffset

Build: ./configure --enable-fwtpm && make

Example: wolfSSL/wolftpm-examples#1

Primary Key Derivation (TPM 2.0 Part 1 Section 26)

  • RSA: iterative KDFa prime generation with labels "RSA p" / "RSA q"
  • ECC: KDFa scalar derivation, public point Q = d*G
  • KEYEDHASH/SYMCIPHER: KDFa with algorithm-specific labels
  • hashUnique = H(sensitiveCreate.data || unique) per Section 26.1
  • Primary cache retained as performance optimization, no longer required for correctness

UART Transport (--enable-swtpm=uart)

New transport option for wolfTPM client library to communicate with embedded fwTPM over serial:

  • ./configure --enable-swtpm=uart — uses termios serial I/O instead of TCP sockets
  • TPM2_SWTPM_HOST env var selects serial device at runtime
  • Same mssim protocol as socket transport (compatible with all wolfTPM examples)

Testing

  • 311 tpm2-tools compatibility tests (scripts/tpm2_tools_test.sh)
  • Full wolfTPM example suite (examples/run_examples.sh)
  • libFuzzer harness with corpus generator (tests/fuzz/)
  • m33mu Cortex-M33 emulator CI test (scripts/fwtpm_emu_test.sh)
  • ASan / UBSan clean
  • 20 CI matrix configurations (pedantic gcc/clang -Werror, sanitizers, feature-disable variants)

wolfSSL-Fenrir-bot

This comment was marked as resolved.

@dgarske dgarske force-pushed the fwtpm branch 3 times, most recently from 9c0208f to eae465e Compare March 21, 2026 23:51
@dgarske dgarske changed the title Add fwTPM firmware TPM 2.0 server with STM32 port Add fwTPM firmware TPM 2.0 server Mar 21, 2026
@dgarske dgarske changed the title Add fwTPM firmware TPM 2.0 server Add new Firmware TPM (fwTPM) Mar 22, 2026
@dgarske dgarske force-pushed the fwtpm branch 2 times, most recently from b186ecc to f529484 Compare March 23, 2026 21:28
@dgarske dgarske assigned wolfSSL-Bot and unassigned dgarske and aidangarske Mar 24, 2026
@dgarske dgarske assigned dgarske and unassigned wolfSSL-Bot Mar 25, 2026
wolfSSL-Fenrir-bot

This comment was marked as resolved.

wolfSSL-Fenrir-bot

This comment was marked as resolved.

This comment was marked as resolved.

@aidangarske aidangarske requested a review from danielinux March 25, 2026 20:51
FwFlushAllSessions(ctx);
}

FWTPM_NV_Save(ctx);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return value is discarded here, this functions will return success regardless of FWTPM_NV_Save result. Review error handling in this function.

Copilot AI review requested due to automatic review settings April 3, 2026 17:42

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings April 6, 2026 22:12

This comment was marked as resolved.

Copy link
Copy Markdown
Member

@danielinux danielinux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWTPM_NV_Save() return value is still discarded in several places, unsure if the error propagation has been addressed when FWTPM_NV_Save() fails

@danielinux danielinux self-requested a review April 7, 2026 17:55
@danielinux
Copy link
Copy Markdown
Member

checking the fixes more closely

Copilot AI review requested due to automatic review settings April 7, 2026 20:36
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 60 out of 62 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (5)

src/tpm2_crypto.c:1

  • TPM2_KDFa_ex omits the required label terminator (single 0x00 byte) when label is NULL. Per the TPM 2.0 KDFa definition, the input includes label || 0x00 even for an empty label; skipping it changes derived keys depending on whether callers pass NULL vs "". Consider treating NULL as an empty label and always hashing exactly one 0x00 byte for the label separator.
    src/tpm2_crypto.c:1
  • TPM2_KDFe_ex has the same issue as KDFa: when label is NULL, the function does not hash the mandatory 0x00 label separator. This makes KDFe outputs depend on NULL vs empty-string usage. Fix by always hashing a single zero byte when label is NULL (or by normalizing NULL to "" and hashing the terminator unconditionally).
    src/tpm2_packet.c:1
  • TPM2_Packet_ParseU16Buf silently truncates oversized fields and overwrites the caller’s size output with the truncated length. This can cause higher-level code to accept attacker-controlled responses/structures that are malformed (e.g., truncated names/digests/policies) without any error signal. Consider changing this helper to return a status (e.g., TPM_RC_SIZE / BUFFER_E) when wireSize > maxBufSz, and update call sites to fail parsing rather than clamping.
    tests/unit_tests.c:1
  • The updated unit test now exercises only TPM2_KDFa_ex and no longer validates the backward-compatible TPM2_KDFa wrapper. Since the wrapper remains part of the public API, add an assertion that TPM2_KDFa(...) returns the same output as TPM2_KDFa_ex(...) for the same inputs (and/or restore direct coverage of TPM2_KDFa).
    src/fwtpm/fwtpm_tis_shm.c:1
  • Unconditionally calling sem_unlink() on fixed global semaphore names can break a concurrently running fwTPM instance (or an instance started by another user/session on the same host), since it removes the names out from under the other process. Consider using instance-unique semaphore names (e.g., include PID/UID), or only unlink when you detect stale resources that belong to this instance (e.g., via a unique shared-memory header + ownership/nonce check).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings April 7, 2026 23:34
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 58 out of 72 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (1)

src/tpm2_swtpm.c:1

  • In UART mode, SwTpmDisconnect never closes the file descriptor and never resets ctx->tcpCtx.fd to -1. If higher-level code expects ...Disconnect() to release resources (e.g., during TPM2_Cleanup) or to force a reconnect after an I/O error, the UART path will leak the FD and may prevent recovery. Consider closing/resetting the UART FD during final cleanup and/or on transmit/receive errors (while still allowing “keep open across commands” during normal operation).
/* tpm2_swtpm.c

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@dgarske dgarske assigned dgarske and unassigned danielinux Apr 8, 2026
Copilot AI review requested due to automatic review settings April 8, 2026 19:05
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 58 out of 74 changed files in this pull request and generated 11 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings April 8, 2026 22:06
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 58 out of 75 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +204 to +207

/* 8N1: 8 data bits, no parity, 1 stop bit */
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
tty.c_cflag &= ~(PARENB | PARODD | CSTOPB | CRTSCTS);
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The B230400, B460800, and B921600 baud constants are not available on all platforms/termios implementations, which can cause compile failures when WOLFTPM_SWTPM_UART is enabled. Guard these case labels with #ifdef Bxxxxxx (or provide a compatibility mapping/fallback) so the UART transport remains portable across Linux distros and macOS/BSD variants.

Suggested change
/* 8N1: 8 data bits, no parity, 1 stop bit */
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
tty.c_cflag &= ~(PARENB | PARODD | CSTOPB | CRTSCTS);
case 115200: baud = B115200; break;
#ifdef B230400
case 230400: baud = B230400; break;
#endif
#ifdef B460800
case 460800: baud = B460800; break;
#endif
#ifdef B921600
case 921600: baud = B921600; break;
#endif

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +53
# Wait for a TCP port to be listening (for servers without ready-file support)
# Uses ss to check without connecting (nc -z would consume the accept slot)
wait_for_port() {
local port="$1" timeout="${2:-500}" elapsed=0
while [ $elapsed -lt $timeout ]; do
if ss -tln 2>/dev/null | grep -q ":${port} "; then
return 0
fi
sleep 0.01
elapsed=$((elapsed + 1))
done
return 1
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait_for_port hard-depends on ss, which is typically unavailable on macOS and some minimal CI/container images. Since this function is now used to gate TLS tests, missing ss will cause spurious test failures. Consider adding a fallback chain (e.g., ss -> netstat -> lsof -> nc -z) or detect missing tooling and revert to a bounded sleep approach.

Copilot uses AI. Check for mistakes.
Comment on lines +449 to +456
echo -e "./examples/server/server -v $3 -p $port -w -g -A ./certs/tpm-ca-$1-cert.pem -R $READY_FILE"
./examples/server/server -v $3 -p $port -w -g -A ./certs/tpm-ca-$1-cert.pem -R "$READY_FILE" >> $TPMPWD/run.out 2>&1 &
popd >> $TPMPWD/run.out 2>&1
sleep 0.1
if ! wait_for_ready "$READY_FILE" 500; then
echo -e "wolfSSL server failed to start for $1 $2"
rm -f "$READY_FILE"
exit 1
fi
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TLS test now assumes wolfSSL’s server example supports the -R ready-file flag. If a user’s wolfSSL tree/system install lacks that option, the server will exit immediately and the test will fail (even though the rest of wolfTPM might be fine). To avoid brittle coupling, consider probing for -R support (e.g., via ./examples/server/server --help) and falling back to wait_for_port when unsupported.

Copilot uses AI. Check for mistakes.
Comment on lines +81 to +84
if (len >= 4) {
val |= (UINT32)regs->reg_data[2] << 16;
val |= (UINT32)regs->reg_data[3] << 24;
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When reg_len == 3, the third byte (reg_data[2]) is ignored because the code only incorporates bytes 2/3 when len >= 4. Even if most TIS accesses are 1/2/4 bytes, this is a correctness gap in the register emulation. Update the packing logic to include reg_data[2] when len >= 3 and reg_data[3] when len >= 4.

Suggested change
if (len >= 4) {
val |= (UINT32)regs->reg_data[2] << 16;
val |= (UINT32)regs->reg_data[3] << 24;
}
if (len >= 3) val |= (UINT32)regs->reg_data[2] << 16;
if (len >= 4) val |= (UINT32)regs->reg_data[3] << 24;

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings April 9, 2026 02:47
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 58 out of 75 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

CMakeLists.txt:1

  • wolftpm_wolfssl_dep is an INTERFACE library. Installing it via install(TARGETS ...) while also specifying LIBRARY/ARCHIVE/RUNTIME destinations can be rejected or behave inconsistently across CMake versions/toolchains. To maximize compatibility, install/export the interface target separately (export-only), or omit artifact destinations for it and keep destinations only for wolftpm.
# CMakeList.txt

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +33 to +61
wait_for_port() {
local port="$1" timeout="${2:-500}" elapsed=0
while [ $elapsed -lt $timeout ]; do
if ss -tln 2>/dev/null | grep -q ":${port} "; then
return 0
fi
sleep 0.01
elapsed=$((elapsed + 1))
done
return 1
}

# Pick an available random port (returns port on stdout)
pick_available_port() {
local port attempts=0
while [ $attempts -lt 20 ]; do
if command -v shuf > /dev/null 2>&1; then
port=$(shuf -i 10000-65000 -n 1)
else
port=$(( (RANDOM % 55000) + 10000 ))
fi
if ! nc -z localhost "$port" 2>/dev/null; then
echo "$port"
return 0
fi
attempts=$((attempts + 1))
done
return 1
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait_for_port hard-depends on ss; if ss isn’t installed, the loop will always time out even when the server is listening. Also pick_available_port uses nc -z without verifying nc exists; when nc is missing, the command returns 127 and the script will incorrectly treat the port as “available”. Add tool detection + fallback (e.g., ssnetstat, and for port checks use ss/netstat or a small Python socket bind probe when nc isn’t available).

Copilot uses AI. Check for mistakes.
Comment on lines 445 to +459
run_tpm_tls_client() { # Usage: run_tpm_tls_client [ecc/rsa] [tpmargs] [tlsversion]
echo -e "TLS test (TPM as client) $1 $2 $3"
generate_port
READY_FILE="/tmp/wolftpm_tls_ready_$$"
rm -f "$READY_FILE"
pushd $WOLFSSL_PATH >> $TPMPWD/run.out 2>&1
echo -e "./examples/server/server -v $3 -p $port -w -g -A ./certs/tpm-ca-$1-cert.pem"
./examples/server/server -v $3 -p $port -w -g -A ./certs/tpm-ca-$1-cert.pem >> $TPMPWD/run.out 2>&1 &
RESULT=$?
[ $RESULT -ne 0 ] && echo -e "tls server $1 $2 failed! $RESULT" && exit 1
echo -e "./examples/server/server -v $3 -p $port -w -g -A ./certs/tpm-ca-$1-cert.pem -R $READY_FILE"
./examples/server/server -v $3 -p $port -w -g -A ./certs/tpm-ca-$1-cert.pem -R "$READY_FILE" >> $TPMPWD/run.out 2>&1 &
popd >> $TPMPWD/run.out 2>&1
sleep 0.1
if ! wait_for_ready "$READY_FILE" 500; then
echo -e "wolfSSL server failed to start for $1 $2"
rm -f "$READY_FILE"
exit 1
fi
rm -f "$READY_FILE"
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TLS server is started in the background but its PID isn’t captured/cleaned up on failure. If the -R flag isn’t supported by the wolfSSL server binary (or if it crashes before creating the ready file), this path exits and can leave a stray server process bound to the port, causing cascading failures in later tests. Capture $! and ensure it’s terminated on the timeout path; additionally consider falling back to wait_for_port if the ready file never appears (to keep compatibility with wolfSSL versions/builds that don’t support -R).

Copilot uses AI. Check for mistakes.

/* 8N1: 8 data bits, no parity, 1 stop bit */
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
tty.c_cflag &= ~(PARENB | PARODD | CSTOPB | CRTSCTS);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRTSCTS is not defined on all platforms that have termios.h (it’s common on Linux, but not guaranteed elsewhere). As written, enabling WOLFTPM_SWTPM_UART can fail to compile on platforms lacking CRTSCTS. Guard this flag (e.g., #ifdef CRTSCTS) or use the platform-specific flow-control flags where applicable.

Suggested change
tty.c_cflag &= ~(PARENB | PARODD | CSTOPB | CRTSCTS);
tty.c_cflag &= ~(PARENB | PARODD | CSTOPB);
#ifdef CRTSCTS
tty.c_cflag &= ~CRTSCTS;
#endif

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants