Building wolfTPM
To build the wolfTPM library, it's required to first build and install the wolfSSL library. This can be downloaded from the downloads page, or through a "git clone" command, shown below:
$ git clone https://github.com/wolfssl/wolfssl
Once the wolfSSL library has been downloaded, it needs to be built with the following option being passed to the configure script:
$ ./configure --enable-wolftpm
Or equivalently, with the following options:
$ ./configure --enable-certgen --enable-certreq --enable-certext
--enable-pkcs7 --enable-cryptocb --enable-aescfb
Then the wolfSSL library just needs to be built and installed however the user prefers.
The next step is to download and install the wolfTPM library. wolfTPM can similarly be downloaded from the downloads page or be cloned from GitHub. The following commands show how to clone and install wolfTPM:
$ git clone https://github.com/wolfssl/wolftpm
$ cd wolftpm
$ ./autogen.sh
$ ./configure
$ make
Build options and defines
--enable-debug Add debug code/turns off optimizations (yes|no|verbose|io)
- DEBUG_WOLFTPM, WOLFTPM_DEBUG_VERBOSE, WOLFTPM_DEBUG_IO
--enable-examples Enable Examples (default: enabled)
--enable-wrapper Enable wrapper code (default: enabled) - WOLFTPM2_NO_WRAPPER
--enable-wolfcrypt Enable wolfCrypt hooks for RNG, Auth Sessions and Parameter encryption
(default: enabled) - WOLFTPM2_NO_WOLFCRYPT
--enable-advio Enable Advanced IO (default: disabled) - WOLFTPM_ADV_IO
--enable-i2c Enable I2C TPM Support (default: disabled, requires advio) - WOLFTPM_I2C
--enable-checkwaitstate Enable TIS / SPI Check Wait State support (default: depends on chip)
- WOLFTPM_CHECK_WAIT_STATE
--enable-smallstack Enable options to reduce stack usage
--enable-tislock Enable Linux Named Semaphore for locking access to SPI device for
concurrent access between processes - WOLFTPM_TIS_LOCK
--enable-autodetect Enable Runtime Module Detection (default: enable - when no module
specified) - WOLFTPM_AUTODETECT
--enable-infineon Enable Infineon SLB9670 TPM Support (default: disabled)
--enable-st Enable ST ST33TPM Support (default: disabled) - WOLFTPM_ST33
--enable-microchip Enable Microchip ATTPM20 Support (default: disabled) - WOLFTPM_MCHP
--enable-nuvoton Enable Nuvoton NPCT65x/NPCT75x Support (default: disabled)
- WOLFTPM_NUVOTON
--enable-devtpm Enable using Linux kernel driver for /dev/tpmX (default: disabled)
- WOLFTPM_LINUX_DEV
--enable-swtpm Enable using SWTPM TCP protocol. For use with simulator.
(default: disabled) - WOLFTPM_SWTPM
--enable-winapi Use Windows TBS API. (default: disabled) - WOLFTPM_WINAPI
WOLFTPM_USE_SYMMETRIC Enables symmetric AES/Hashing/HMAC support for TLS examples.
WOLFTPM2_USE_SW_ECDHE Disables use of TPM for ECC ephemeral key generation and shared secret
for TLS examples.
TLS_BENCH_MODE Enables TLS benchmarking mode.
NO_TPM_BENCH Disables the TPM benchmarking example.
Building Infineon SLB9670
Build wolfTPM:
git clone https://github.com/wolfSSL/wolfTPM.git
cd wolfTPM
./autogen.sh
./configure
make
Building ST ST33TP*
Build wolfTPM:
./autogen.sh
./configure --enable-st33 [--enable-i2c]
make
For the I2C support on Raspberry Pi you may need to enable I2C. Here are the steps:
1. Edit sudo vim /boot/config.txt
2. Uncomment dtparam=i2c_arm=on
3. Reboot sudo reboot
Building Microchip ATTPM20
Build wolfTPM:
./autogen.sh
./configure --enable-microchip
make
Building Nuvoton
Build wolfTPM:
./autogen.sh
./configure --enable-nuvoton
make
Building for "/dev/tpmX"
This build option allows you to talk to any TPM vendor supported by the Linux TIS kernel driver
Build wolfTPM:
./autogen.sh
./configure --enable-devtpm
make
Note: When using a TPM device through the Linux kernel driver make sure sufficient permissions are given to the application that uses wolfTPM, because the "/dev/tpmX" typically has read-write permissions only for the "tss" user group. Either run wolfTPM examples and your application using sudo or add your user to the "tss" group like this:
sudo adduser yourusername tss
With QEMU and swtpm
This demonstrates using wolfTPM in QEMU to communicate using the linux kernel device "/dev/tpmX". You will need to install or build swtpm. Below are a short method to build. You may need to consult the instructions for libtpms and swtpm
PREFIX=$PWD/inst
git clone git@github.com:stefanberger/libtpms.git
cd libtpms/
./autogen.sh --with-openssl --with-tpm2 --prefix=$PREFIX && make install
cd ..
git clone git@github.com:stefanberger/swtpm.git
cd swtpm
PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig/ ./autogen.sh --with-openssl --with-tpm2 \
--prefix=$PREFIX && \
make install
cd ..
You can setup a basic linux installation. Other installation bases can be used. This step will take some time to install the base linux system.
# download mini install image
curl -O http://archive.ubuntu.com/ubuntu/dists/bionic-updates/main/installer-amd64/current/images/netboot/mini.iso
# create qemu image file
qemu-img create -f qcow2 lubuntu.qcow2 5G
# create directory for tpm state and socket
mkdir $PREFIX/mytpm
# start swtpm
$PREFIX/bin/swtpm socket --tpm2 --tpmstate dir=$PREFIX/mytpm \
--ctrl type=unixio,path=$PREFIX/mytpm/swtpm-sock --log level=20 &
# start qemu for installation
qemu-system-x86_64 -m 1024 -boot d -bios bios-256k.bin -boot menu=on \
-chardev socket,id=chrtpm,path=$PREFIX/mytpm/swtpm-sock \
-tpmdev emulator,id=tpm0,chardev=chrtpm \
-device tpm-tis,tpmdev=tpm0 -hda lubuntu.qcow2 -cdrom mini.iso
Once a base system is installed it's ready to start the qemu and build wolfSSL and wolfTPM in the qemu instance.
# start swtpm again
$PREFIX/bin/swtpm socket --tpm2 --tpmstate dir=$PREFIX/mytpm \
--ctrl type=unixio,path=$PREFIX/mytpm/swtpm-sock --log level=20 &
# start qemu system to install and run wolfTPM
qemu-system-x86_64 -m 1024 -boot d -bios bios-256k.bin -boot menu=on \
-chardev socket,id=chrtpm,path=$PREFIX/mytpm/swtpm-sock \
-tpmdev emulator,id=tpm0,chardev=chrtpm \
-device tpm-tis,tpmdev=tpm0 -hda lubuntu.qcow2
To build checkout and build wolfTPM, in the QEMU terminal
sudo apt install automake libtool gcc git make
# get and build wolfSSL
git clone https://github.com/wolfssl/wolfssl.git
pushd wolfssl
./autogen.sh && \
./configure --enable-wolftpm --disable-examples --prefix=$PWD/../inst && \
make install
popd
# get and build wolfTPM
git clone https://github.com/wolfssl/wolftpm.git
pushd wolftpm
./autogen.sh && \
./configure --enable-devtpm --prefix=$PWD/../inst --enable-debug && \
make install
sudo make check
popd
You can now run the examples such as sudo ./examples/wrap/wrap
within QEMU. Using sudo maybe required for access to /dev/tpm0.
Building for SWTPM
wolfTPM is to be able to interface with SW TPM interfaces defined by section D.3 of TPM-Rev-2.0-Part-4-Supporting-Routines-01.38-code
Using the socket connection for SWTPM is exclusive and not compatible with TIS or devtpm.
Only a subset of functionality is implemented to support testing of wolfTPM. The platform requests are not used by wolfTPM.
Two implementations were used in testing:
- https://sourceforge.net/projects/ibmswtpm2/files/
- https://github.com/stefanberger/swtpm
To enable this functionality, build wolfTPM as shown below:
./configure --enable-swtpm
make
SWTPM simulator setup
ibmswtpm2
Checkout and Build
git clone https://github.com/kgoldman/ibmswtpm2.git
cd ibmswtpm2/src/
make
Running:
./tpm_server --rm
The rm switch is optional and remove the cache file
NVChip. Alternately you can rm NVChip
swtpm
Build libtpms
git clone git@github.com:stefanberger/libtpms.git
(cd libtpms && ./autogen.sh --with-tpm2 --with-openssl --prefix=/usr && make install)
Build swtpm
git clone git@github.com:stefanberger/swtpm.git
(cd swtpm && ./autogen.sh && make install)
Note: On Mac OS X had to do the following first:
brew install openssl socat
pip3 install cryptography
export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib"
export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include"
# libtpms had to use --prefix=/usr/local
Running swtpm
mkdir -p /tmp/myvtpm
swtpm socket --tpmstate dir=/tmp/myvtpm --tpm2 --ctrl type=tcp,port=2322 --server type=tcp,port=2321 --flags not-need-init
Running examples
./examples/pcr/extend
./examples/wrap/wrap_test
Building for Windows TBS API
wolfTPM can be built to use Windows native TBS (TPM Base Services)
When using the Windows TBS interface the NV access is blocked by default. TPM NV storage space is very limited and when filled can cause undefined behaviors, such as failures loading key handles. These are not managed by TBS.
The TPM is designed to return an encrypted private key blob on key creation using TPM2_Create, which you can safely store on the disk and load when needed. The symmetric encryption key used to protect the private key blob is only known by the TPM. When you load a key using TPM2_Load you get a transient handle, which can be used for signing and even encryption/decryption.
For primary keys created with TPM2_CreatePrimary you get back a handle. There is no encrypted private data returned. That handle will remain loaded until TPM2_FlushContext is called.
For normal key creation using TPM2_Create you get back a TPM2B_PRIVATE outPrivate, which is the encrypted blob that you can store and load anytime using TPM2_Load.
Limitations
wolfTPM has been tested on Windows 10 with TPM 2.0 devices. While Windows does support TPM 1.2, functionality is limited and not supported by wolfTPM.
Presence of TPM 2.0 can be checked by opening PowerShell
and running Get-PnpDevice -Class SecurityDevices
Status Class FriendlyName
------ ----- ------------
OK SecurityDevices Trusted Platform Module 2.0
Unknown SecurityDevices Trusted Platform Module 2.0
Building in MSYS2
Tested using MSYS2
export PREFIX=$PWD/tmp_install
cd wolfssl
./autogen.sh
./configure --prefix="$PREFIX" --enable-wolftpm
make
make install
cd ../wolftpm/
./autogen.sh
./configure --prefix="$PREFIX" --enable-winapi
make
Building on linux
Tested using mingw-w32-bin_x86_64-linux_20131221.tar.bz2 source
Extract the tools and add them to the PATH
mkdir mingw_tools
cd mingw_tools
tar xjvf ../mingw-w32-bin_x86_64-linux_20131221.tar.bz2
export PATH=$PWD/bin/:$PWD/i686-w64-mingw32/bin:$PATH
cd ..
Build
export PREFIX=$PWD/tmp_install
export CFLAGS="-DWIN32 -DMINGW -D_WIN32_WINNT=0x0600 -DUSE_WOLF_STRTOK"
export LIBS="-lws2_32"
cd wolfssl
./autogen.sh
./configure --host=i686 CC=i686-w64-mingw32-gcc --prefix="$PREFIX" --enable-wolftpm
make
make install
cd ../wolftpm/
./autogen.sh
./configure --host=i686 CC=i686-w64-mingw32-gcc --prefix="$PREFIX" --enable-winapi
make
cd ..
Running on Windows
To confirm presence and status of TPM on the machine run tpm.msc
Building for Bare-Metal
wolfTPM can be built for bare-metal embedded environments where no operating system is present. This section covers the steps needed to integrate wolfTPM directly into your project by compiling the source files rather than using autotools or CMake.
This approach 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 the following 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 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 */
#define NO_SHA /* on by default */
#define NO_SHA256 /* on by default */
#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
You will need to implement your own RNG seed function if using CUSTOM_RAND_GENERATE_SEED:
int my_rng_seed(byte* seed, word32 sz)
{
int rc;
(void)os;
/* 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 the following directories to your project's include paths:
- wolfSSL root directory - e.g.,
/path/to/wolfssl - wolfTPM root directory - e.g.,
/path/to/wolftpm - Your user_settings.h location - must be discoverable by the compiler (in your include path)
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_packet.c
wolftpm/src/tpm2_tis.c
wolftpm/src/tpm2_wrap.c
wolftpm/src/tpm2_param_enc.c
Step 5: Implement the SPI HAL Callback
wolfTPM requires a single SPI transmit/receive callback to communicate with the TPM module. You need to implement this function for your specific hardware platform.
Reference implementations are available in the wolfTPM repository under hal/:
- hal/tpm_io_xilinx.c - Xilinx Microblaze
- hal/tpm_io_stm32.c - STM32
- hal/tpm_io_infineon.c - Infineon Tricore
- hal/tpm_io_microchip.c - Microchip
Standard I/O Callback
The standard SPI callback has the following 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 - simultaneously send txBuf and receive to 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 provides access to the register address and read/write direction for platforms that require separate read and write operations.
Step 6: Initialize and Use wolfTPM
After completing the setup, you can 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
Reducing Memory Footprint
For 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_BITS 2048
Selecting TPM Module Type
If you know your TPM module type at compile time:
/* For Infineon */
#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 not specified, wolfTPM will attempt to auto-detect the module at runtime using WOLFTPM_AUTODETECT (default behavior).
I2C Support
For TPM modules connected via I2C instead of SPI:
#define WOLFTPM_I2C
#define WOLFTPM_ADV_IO
You will need to implement the advanced I/O callback for I2C communication.
Cryptographic Key Storage
In bare-metal environments, the TPM provides secure storage for cryptographic keys that is isolated from main processor memory. The key material never leaves the TPM in plaintext form.
- Keys created with
TPM2_CreatePrimaryreside in the TPM and return a handle - Keys created with
TPM2_Createreturn an encrypted blob that can be stored in non-volatile memory and reloaded usingTPM2_Load - Use
TPM2_EvictControlto store keys persistently in the TPM's NVRAM
This separation ensures that cryptographic keys are protected even if the main processor's memory is compromised.
Troubleshooting
SPI Communication Issues
- Verify SPI clock polarity and phase (typically CPOL=0, CPHA=0 for TPM)
- Check SPI clock speed - start with a slower speed (1-10 MHz) and increase
- Verify chip select is asserted low during entire send/recieve
- Some TPMs require wait states during SPI operations which requires extra bytes until the MSB is set to signal response readiness (enabled with
WOLFTPM_CHECK_WAIT_STATE) - Enable debug output with
#define DEBUG_WOLFTPM(general),WOLFTPM_DEBUG_VERBOSE(detailed), orWOLFTPM_DEBUG_IO(SPI/I2C transactions)
Build Errors
- Ensure
WOLFSSL_USER_SETTINGSandWOLFTPM_USER_SETTINGSare defined - Verify include paths are correctly set
- Check that all required source files are included in the build