Skip to content

Building wolfTPM

wolfTPM is built on top of wolfSSL (wolfCrypt) and can be built with autotools, CMake, or directly into a bare-metal project using a user_settings.h file. This page covers each build method. Per-vendor build steps are on the Supported Hardware page, and the full list of configure switches is on the Build Options page.

Building wolfSSL

wolfSSL must be built and installed first. It can be downloaded from the downloads page or cloned from GitHub:

git clone https://github.com/wolfSSL/wolfssl.git
cd wolfssl
./autogen.sh
./configure --enable-wolftpm --enable-pkcallbacks --enable-keygen CFLAGS="-DWC_RSA_NO_PADDING"
make
sudo make install
sudo ldconfig

autogen.sh requires automake and libtool: sudo apt-get install automake libtool.

The --enable-wolftpm option is equivalent to passing these options:

./configure --enable-certgen --enable-certreq --enable-certext \
    --enable-pkcs7 --enable-cryptocb --enable-aescfb

Using an alternate wolfSSL directory

To build wolfTPM against a wolfSSL installed in a non-default location, install wolfSSL to a prefix and point wolfTPM at it with --with-wolfcrypt:

# cd /your-wolfssl-repo
./autogen.sh # as necessary
./configure --prefix=~/workspace/my_wolfssl_bin --enable-all
make install

# then for some other library such as wolfTPM:

# cd /your-wolftpm-repo
./configure --enable-swtpm --with-wolfcrypt=~/workspace/my_wolfssl_bin

Building with autotools

Once wolfSSL is installed, download wolfTPM from the downloads page or clone it from GitHub, then build it:

git clone https://github.com/wolfSSL/wolfTPM.git
cd wolfTPM
./autogen.sh
./configure
make

Add the options you need to ./configure. For example, --enable-devtpm uses the Linux kernel TPM device, and --enable-swtpm uses a TPM simulator. See Build Options for the list, and Supported Hardware for the options each TPM module needs.

On Linux x86_64 and aarch64, a bare ./configure auto-enables the software TPM backends so that make check works without hardware. See System Interfaces.

Building using CMake

CMake supports compiling in many environments, including Visual Studio if CMake support is installed. The commands below can be run in a Developer Command Prompt.

mkdir build
cd build
# to use installed wolfSSL location (library and headers)
cmake .. -DWITH_WOLFSSL=/prefix/to/wolfssl/install/
# OR to use a wolfSSL source tree
cmake .. -DWITH_WOLFSSL_TREE=/path/to/wolfssl/
# build
cmake --build .

Use -DWITH_WOLFSSL= when wolfSSL is already installed (library and headers), or -DWITH_WOLFSSL_TREE= to build against a wolfSSL source tree.

Bare-metal build

wolfTPM can be built for bare-metal embedded environments where no operating system is present. In this approach you compile the wolfTPM source files directly into your project instead of using autotools or CMake. It is common for microcontrollers such as ARM Cortex-M, RISC-V, UltraScale+/Versal, Microblaze, and others.

Prerequisites

  • wolfCrypt library source code
  • wolfTPM library source code
  • A TPM 2.0 module connected via SPI (or I2C)

Step 1: Define preprocessor macros

Add these preprocessor macros to your project build settings or compiler command line:

WOLFTPM_USER_SETTINGS
WOLFSSL_USER_SETTINGS

These macros tell wolfTPM and wolfSSL to look for a user_settings.h file instead of using the autoconf-generated options.h file.

Step 2: Create a user_settings.h file

Create a user_settings.h file in your project that contains the build configuration options for both wolfSSL and wolfTPM. A reference configuration file is available in the wolfSSL repository: examples/configs/user_settings_wolftpm.h.

Example user_settings.h for wolfTPM:

/* System */
#define WOLFSSL_GENERAL_ALIGNMENT 4
#define SINGLE_THREADED
#define WOLFCRYPT_ONLY
#define SIZEOF_LONG_LONG 8

/* Platform - bare metal */
#define NO_FILESYSTEM
#define NO_WRITEV
#define NO_MAIN_DRIVER
#define NO_DEV_RANDOM
#define NO_ERROR_STRINGS
#define NO_SIG_WRAPPER

/* wolfTPM required features */
#define WOLF_CRYPTO_CB
#define WOLFSSL_PUBLIC_MP
#define WOLFSSL_AES_CFB
#define HAVE_AES_DECRYPT

/* ECC options */
#define HAVE_ECC
#define ECC_TIMING_RESISTANT

/* RSA options */
#undef NO_RSA
#define WOLFSSL_KEY_GEN
#define WC_RSA_BLINDING

/* Big math library */
#define WOLFSSL_SP_MATH_ALL /* sp_int.c */
#define WOLFSSL_SP_SMALL
#define SP_INT_BITS 4096
/* #define SP_WORD_SIZE 32 */

/* SHA options: SHA-256 stays enabled, so do not define NO_SHA256 */
#define WOLFSSL_SHA512
#define WOLFSSL_SHA384

/* Disable unneeded features to reduce footprint */
#define NO_PKCS8
#define NO_PKCS12
#define NO_PWDBASED
#define NO_DSA
#define NO_DES3
#define NO_RC4
#define NO_PSK
#define NO_MD4
#define NO_MD5
#define WOLFSSL_NO_SHAKE128
#define WOLFSSL_NO_SHAKE256
#define NO_DH

/* Other interesting size reduction options */
#if 0
    #define RSA_LOW_MEM
    #define WOLFSSL_AES_SMALL_TABLES
    #define USE_SLOW_SHA
    #define USE_SLOW_SHA256
    #define USE_SLOW_SHA512
    #define NO_AES_192
#endif

/* Custom random seed source - implement your own */
#define HAVE_HASHDRBG
#define CUSTOM_RAND_GENERATE_SEED my_rng_seed

Warning

The NO_* macros disable algorithms. wolfTPM wrappers and sessions need SHA-256, so never define NO_SHA256 in this file.

If you use CUSTOM_RAND_GENERATE_SEED, implement your own RNG seed function. This example gets the seed from the TPM with parameter encryption enabled:

int my_rng_seed(byte* seed, word32 sz)
{
    int rc;

    /* enable parameter encryption for the RNG request */
    rc = wolfTPM2_SetAuthSession(&wolftpm_dev, 0, &wolftpm_session,
        (TPMA_SESSION_decrypt | TPMA_SESSION_encrypt |
        TPMA_SESSION_continueSession));
    if (rc == 0) {
        rc = wolfTPM2_GetRandom(&wolftpm_dev, seed, sz);
    }
    wolfTPM2_UnsetAuthSession(&wolftpm_dev, 0, &wolftpm_session);
    return rc;
}

Step 3: Configure include paths

Add these directories to your project's include paths:

  1. The wolfSSL root directory, for example /path/to/wolfssl
  2. The wolfTPM root directory, for example /path/to/wolftpm
  3. The directory that holds your user_settings.h

Example compiler flags:

-I/path/to/wolfssl
-I/path/to/wolftpm
-I/path/to/your/project/include

Step 4: Add source files

Add the required source files from wolfSSL and wolfTPM to your project.

wolfCrypt source files (minimum required for wolfTPM):

wolfssl/wolfcrypt/src/aes.c
wolfssl/wolfcrypt/src/asn.c
wolfssl/wolfcrypt/src/cryptocb.c
wolfssl/wolfcrypt/src/ecc.c
wolfssl/wolfcrypt/src/hash.c
wolfssl/wolfcrypt/src/hmac.c
wolfssl/wolfcrypt/src/random.c
wolfssl/wolfcrypt/src/rsa.c
wolfssl/wolfcrypt/src/sha.c
wolfssl/wolfcrypt/src/sha256.c
wolfssl/wolfcrypt/src/sha512.c
wolfssl/wolfcrypt/src/sp_int.c
wolfssl/wolfcrypt/src/wc_port.c
wolfssl/wolfcrypt/src/wolfmath.c

wolfTPM source files:

wolftpm/src/tpm2.c
wolftpm/src/tpm2_util.c
wolftpm/src/tpm2_packet.c
wolftpm/src/tpm2_tis.c
wolftpm/src/tpm2_wrap.c
wolftpm/src/tpm2_asn.c
wolftpm/src/tpm2_crypto.c
wolftpm/src/tpm2_param_enc.c
wolftpm/src/tpm2_cryptocb.c
wolftpm/src/tpm2_linux.c

This list matches src_libwolftpm_la_SOURCES in src/include.am. tpm2_swtpm.c, tpm2_winapi.c and tpm2_spdm.c are only needed for their respective optional builds. The HAL source (one of the hal/tpm_io*.c files, see below) is added separately.

Step 5: Implement the SPI HAL callback

wolfTPM needs a single SPI transmit and receive callback to communicate with the TPM module. Implement it for your hardware platform. Reference implementations are in the hal/ directory of the wolfTPM repository:

Standard I/O callback

The standard SPI callback has this signature:

typedef int (*TPM2HalIoCb)(
    TPM2_CTX* ctx,
    const byte* txBuf, byte* rxBuf,
    word16 xferSz,
    void* userCtx
);

Example implementation:

#include <wolftpm/tpm2.h>
#include <wolftpm/tpm2_tis.h>

int TPM2_IoCb(TPM2_CTX* ctx,
    const byte* txBuf, byte* rxBuf, word16 xferSz,
    void* userCtx)
{
    int ret = TPM_RC_FAILURE;

    /* TODO: Assert SPI chip select */
    spi_cs_assert();

    /* Perform SPI transfer: send txBuf and receive into rxBuf */
    if (spi_transfer(txBuf, rxBuf, xferSz) == 0) {
        ret = TPM_RC_SUCCESS;
    }

    /* TODO: De-assert SPI chip select */
    spi_cs_deassert();

    (void)ctx;
    (void)userCtx;

    return ret;
}

Advanced I/O callback

For platforms that need more control, enable WOLFTPM_ADV_IO to use the advanced callback:

typedef int (*TPM2HalIoCb)(
    TPM2_CTX* ctx,
    INT32 isRead, UINT32 addr,
    BYTE* xferBuf, UINT16 xferSz,
    void* userCtx
);

This gives access to the register address and the read or write direction, for platforms that need separate read and write operations.

Step 6: Initialize and use wolfTPM

After the setup is complete, initialize wolfTPM and start communicating with the TPM:

#include <wolftpm/tpm2_wrap.h>

int main(void)
{
    int rc;
    WOLFTPM2_DEV dev;

    /* Initialize wolfTPM */
    rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
    if (rc != TPM_RC_SUCCESS) {
        /* Handle error */
        return rc;
    }

    /* Get TPM capabilities */
    WOLFTPM2_CAPS caps;
    rc = wolfTPM2_GetCapabilities(&dev, &caps);
    if (rc == TPM_RC_SUCCESS) {
        /* Use TPM ... */
    }

    /* Cleanup */
    wolfTPM2_Cleanup(&dev);

    return 0;
}

Optional build configurations

To reduce the memory footprint in constrained environments, consider these options in user_settings.h:

/* Reduce stack usage */
#define WOLFTPM_SMALL_STACK

/* Disable wrapper layer if using native API only */
#define WOLFTPM2_NO_WRAPPER

/* Use smaller RSA key sizes only */
#define MAX_RSA_KEY_BITS 2048

If you know your TPM module type at compile time, select it. Select exactly one module variant, not several:

/* For Infineon, pick exactly one of these */
#define WOLFTPM_SLB9670
/* #define WOLFTPM_SLB9672 */
/* #define WOLFTPM_SLB9673 */

/* For ST ST33 */
#define WOLFTPM_ST33

/* For Nuvoton */
#define WOLFTPM_NUVOTON

/* For Microchip ATTPM20 */
#define WOLFTPM_MICROCHIP

If no module is specified, define WOLFTPM_AUTODETECT to detect the module at runtime. That is the default under ./configure, but a user_settings.h build with no module and no WOLFTPM_AUTODETECT falls back to the Infineon SLB9672 (SPI) or SLB9673 (I2C), so define WOLFTPM_AUTODETECT explicitly for runtime detection.

For TPM modules connected via I2C instead of SPI:

#define WOLFTPM_I2C
#define WOLFTPM_ADV_IO

You must implement the advanced I/O callback for I2C communication.

Cryptographic key storage

In bare-metal environments, the TPM provides secure storage for cryptographic keys, isolated from main processor memory. Key material never leaves the TPM in plaintext form.

  • Keys created with TPM2_CreatePrimary reside in the TPM and return a handle.
  • Keys created with TPM2_Create return an encrypted blob that can be stored in non-volatile memory and reloaded using TPM2_Load.
  • Use TPM2_EvictControl to store keys persistently in the TPM NVRAM.

This keeps cryptographic keys protected even if the main processor memory is compromised.

Troubleshooting

SPI communication issues:

  1. Verify SPI clock polarity and phase (typically CPOL=0, CPHA=0 for a TPM).
  2. Check the SPI clock speed. Start slow (1 to 10 MHz) and increase.
  3. Verify chip select is asserted low during the entire send and receive.
  4. Some TPMs require wait states during SPI operations, which means extra bytes are read until the MSB is set to signal response readiness (enabled with WOLFTPM_CHECK_WAIT_STATE).
  5. Enable debug output with DEBUG_WOLFTPM (general), WOLFTPM_DEBUG_VERBOSE (detailed), or WOLFTPM_DEBUG_IO (SPI and I2C transactions).

Build errors:

  1. Ensure WOLFSSL_USER_SETTINGS and WOLFTPM_USER_SETTINGS are defined.
  2. Verify the include paths are correct.
  3. Check that all required source files are included in the build.

See Also