mirror of https://github.com/wolfSSL/wolfBoot.git
272 lines
13 KiB
Markdown
272 lines
13 KiB
Markdown
# wolfBoot TPM support
|
|
|
|
In wolfBoot we support TPM based root of trust, sealing/unsealing, cryptographic offloading and measured boot using a TPM.
|
|
|
|
## Build Options
|
|
|
|
| Config Option | Preprocessor Macro | Description |
|
|
| ------------- | ------------------ | ----------------------------------- |
|
|
| `WOLFTPM=1` | `WOLFBOOT_TPM` | Enables wolfTPM support |
|
|
| `WOLFBOOT_TPM_VERIFY=1` | `WOLFBOOT_TPM_VERIFY` | Enables cryptographic offloading for RSA2048 and ECC256/384 to the TPM. |
|
|
| `WOLFBOOT_TPM_KEYSTORE=1` | `WOLFBOOT_TPM_KEYSTORE` | Enables TPM based root of trust. NV Index must store a hash of the trusted public key. |
|
|
| `WOLFBOOT_TPM_KEYSTORE_NV_BASE=0x` | `WOLFBOOT_TPM_KEYSTORE_NV_BASE=0x` | NV index in platform range 0x1400000 - 0x17FFFFF. |
|
|
| `WOLFBOOT_TPM_KEYSTORE_AUTH=secret` | `WOLFBOOT_TPM_KEYSTORE_AUTH` | Password for NV access |
|
|
| `MEASURED_BOOT=1` | `WOLFBOOT_MEASURED_BOOT` | Enable measured boot. Extends PCR with a hash of the wolfBoot bootloader code. |
|
|
| `MEASURED_PCR_A=16` | `WOLFBOOT_MEASURED_PCR_A=16` | The PCR index to use. See [docs/measured_boot.md](/docs/measured_boot.md). |
|
|
| `MEASURED_BOOT_APP_PARTITION=1` | `WOLFBOOT_MEASURED_BOOT_APP_PARTITION` | Legacy: measure the boot (application) partition instead of wolfBoot code. |
|
|
| `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). |
|
|
|
|
The TPM primary key (SRK) and parameter encryption need ECC or RSA in
|
|
wolfCrypt. When `WOLFTPM=1` and neither `SIGN`, `SIGN_SECONDARY`, nor
|
|
`AUX_PK_ALGOS` provides one (e.g. `SIGN=ML_DSA` or `SIGN=ED25519`), the build
|
|
automatically adds `ecc256` to `AUX_PK_ALGOS`. See "Auxiliary crypto
|
|
algorithms" in [docs/compile.md](compile.md).
|
|
|
|
## 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)
|
|
|
|
See wolfTPM Secure Root of Trust (ROT) example [here](https://github.com/wolfSSL/wolfTPM/tree/master/examples/boot).
|
|
|
|
The design uses a platform NV handle that has been locked. The NV stores a hash of the public key. It is recommended to supply a derived "authentication" value to prevent TPM tampering. This authentication value is encrypted on the bus.
|
|
|
|
## Cryptographic offloading
|
|
|
|
The RSA2048 and ECC256/384 bit verification can be offloaded to a TPM for code size reduction or performance improvement. Enabled using `WOLFBOOT_TPM_VERIFY`.
|
|
NOTE: The TPM's RSA verify requires ASN.1 encoding, so use SIGN=RSA2048ENC
|
|
|
|
## Measured Boot
|
|
|
|
The wolfBoot bootloader code is hashed and extended to the indicated PCR. This can be used later in the application to prove the boot process was not tampered with. Enabled with `WOLFBOOT_MEASURED_BOOT` and exposes API `wolfBoot_tpm2_extend`.
|
|
|
|
By default, the measurement covers wolfBoot's own code region (from `_start_text` to `_stored_data` linker symbols). To use the legacy behavior of measuring the boot (application) partition instead, set `MEASURED_BOOT_APP_PARTITION=1`.
|
|
|
|
## Sealing and Unsealing a secret
|
|
|
|
See the wolfTPM Sealing/Unsealing example [here](https://github.com/wolfSSL/wolfTPM/tree/master/examples/boot#secure-boot-encryption-key-storage)
|
|
|
|
Known PCR values must be signed to seal/unseal a secret. The signature for the authorization policy resides in the signed header using the `--policy` argument.
|
|
If a signed policy is not in the header then a value cannot be sealed. Instead the PCR(s) values and a PCR policy digest will be printed to sign. You can use `./tools/keytools/sign` or `./tools/tpm/policy_sign` to sign the policy externally.
|
|
|
|
This exposes two new wolfBoot API's for sealing and unsealing data with blob stored to NV index:
|
|
```c
|
|
int wolfBoot_seal_auth(const uint8_t* pubkey_hint, const uint8_t* policy, uint16_t policySz,
|
|
int index, const uint8_t* secret, int secret_sz, const byte* auth, int authSz);
|
|
int wolfBoot_unseal_auth(const uint8_t* pubkey_hint, const uint8_t* policy, uint16_t policySz,
|
|
int index, uint8_t* secret, int* secret_sz, const byte* auth, int authSz);
|
|
```
|
|
|
|
For `wolfBoot_unseal_auth()`, `*secret_sz` is an in/out parameter: set it to the
|
|
capacity of `secret` before the call, and on success it is updated to the
|
|
number of bytes unsealed.
|
|
|
|
By default this index will be based on an NV Index at `(0x01400300 + index)`.
|
|
The default NV base can be overridden with `WOLFBOOT_TPM_SEAL_NV_BASE`.
|
|
|
|
NOTE: The TPM's RSA verify requires ASN.1 encoding, so use SIGN=RSA2048ENC
|
|
|
|
### Testing seal/unseal with simulator
|
|
|
|
```sh
|
|
% cp config/examples/sim-tpm-seal.config .config
|
|
% make keytools
|
|
% make tpmtools
|
|
% echo aaa > aaa.bin
|
|
% echo bbb > bbb.bin
|
|
% ./tools/tpm/pcr_extend 0 aaa.bin
|
|
% ./tools/tpm/pcr_extend 1 bbb.bin
|
|
# hash for policy PCR is done 1 then 0
|
|
% ./tools/tpm/policy_create -pcr=1 -pcr=0 -out=policy.bin
|
|
# if ROT enabled
|
|
% ./tools/tpm/rot -write [-auth=TestAuth]
|
|
% make clean
|
|
$ make POLICY_FILE=policy.bin [WOLFBOOT_TPM_KEYSTORE_AUTH=TestAuth] [WOLFBOOT_TPM_SEAL_AUTH=SealAuth]
|
|
|
|
% ./wolfboot.elf get_version
|
|
Simulator assigned ./internal_flash.dd to base 0x107175000
|
|
Mfg IBM (0), Vendor SW TPM, Fw 8228.293 (0x120000), FIPS 140-2 1, CC-EAL4 0
|
|
Unlocking disk...
|
|
Boot partition: 0x1071f5000 (size 21288, version 0x1)
|
|
Error 395 reading blob from NV index 1400300 (error TPM_RC_HANDLE)
|
|
Error 395 unsealing secret! (TPM_RC_HANDLE)
|
|
Sealed secret does not exist!
|
|
Creating new secret (32 bytes)
|
|
7801a7fb716371c975a9a1bca6159a223bc7dba6adb2acf82781421062e498a5
|
|
Error 395 deleting blob from NV index 1400300 (error TPM_RC_HANDLE)
|
|
Wrote 242 bytes to NV index 0x1400300
|
|
Read 242 bytes from NV index 0x1400300
|
|
Secret Check 32 bytes
|
|
7801a7fb716371c975a9a1bca6159a223bc7dba6adb2acf82781421062e498a5
|
|
Secret 32 bytes
|
|
7801a7fb716371c975a9a1bca6159a223bc7dba6adb2acf82781421062e498a5
|
|
Boot partition: 0x1071f5000 (size 21288, version 0x1)
|
|
Boot header magic 0x00000000 invalid at 0x107275000
|
|
Boot partition: 0x1071f5000 (size 21288, version 0x1)
|
|
Booting version: 0x1
|
|
TPM Root of Trust valid (id 0)
|
|
Simulator assigned ./internal_flash.dd to base 0x1073cc000
|
|
1
|
|
% ./wolfboot.elf get_version
|
|
Simulator assigned ./internal_flash.dd to base 0x102f38000
|
|
Mfg IBM (0), Vendor SW TPM, Fw 8228.293 (0x120000), FIPS 140-2 1, CC-EAL4 0
|
|
Unlocking disk...
|
|
Boot partition: 0x102fb8000 (size 21288, version 0x1)
|
|
Read 242 bytes from NV index 0x1400300
|
|
Secret 32 bytes
|
|
7801a7fb716371c975a9a1bca6159a223bc7dba6adb2acf82781421062e498a5
|
|
Boot partition: 0x102fb8000 (size 21288, version 0x1)
|
|
Boot header magic 0x00000000 invalid at 0x103038000
|
|
Boot partition: 0x102fb8000 (size 21288, version 0x1)
|
|
Booting version: 0x1
|
|
TPM Root of Trust valid (id 0)
|
|
Simulator assigned ./internal_flash.dd to base 0x10318f000
|
|
1
|
|
```
|
|
|
|
### Testing seal/unseal on actual hardware
|
|
|
|
1) Get the actual PCR digest for policy.
|
|
2) Sign policy and include in firmware image header.
|
|
|
|
|
|
#### Getting PCR values
|
|
|
|
If no signed policy exists, then the seal function will generate and display the active PCR's, PCR digest and policy digest (to sign)
|
|
|
|
```sh
|
|
% make tpmtools
|
|
% ./tools/tpm/rot -write
|
|
% ./tools/tpm/pcr_reset 16
|
|
% ./wolfboot.elf get_version
|
|
Simulator assigned ./internal_flash.dd to base 0x101a64000
|
|
Mfg IBM (0), Vendor SW TPM, Fw 8217.4131 (0x163636), FIPS 140-2 1, CC-EAL4 0
|
|
Boot partition: 0x101ae4000
|
|
Image size 57192
|
|
Policy header not found!
|
|
Generating policy based on active PCR's!
|
|
Getting active PCR's (0-16)
|
|
PCR 16 (counter 20)
|
|
8f7ac1d5a5eac58a2305ca459f27c35705a9212c0fb2a9088b1df761f3d5f842
|
|
Found 1 active PCR's (mask 0x00010000)
|
|
PCR Digest (32 bytes):
|
|
f84085631f85333ad0338b06c82f16888b7923abaccffb881d5416e389be256c
|
|
PCR Mask (0x00010000) and PCR Policy Digest (36 bytes):
|
|
0000010034ba061436aba2e9a167a1ee46af4a9578a8c6b9f71fdece21607a0cb40468ec
|
|
Use this policy with the sign tool (--policy arg) or POLICY_FILE config
|
|
Image policy signature missing!
|
|
Boot partition: 0x101ae4000
|
|
Image size 57192
|
|
TPM Root of Trust valid (id 0)
|
|
Simulator assigned ./internal_flash.dd to base 0x101c2f000
|
|
1
|
|
```
|
|
|
|
The `0000010034ba061436aba2e9a167a1ee46af4a9578a8c6b9f71fdece21607a0cb40468ec` above can be directly used by the keytool. The
|
|
|
|
`echo "0000010034ba061436aba2e9a167a1ee46af4a9578a8c6b9f71fdece21607a0cb40468ec" | xxd -r -p > policy.bin`
|
|
|
|
OR use the `tools/tpm/policy_create` tool to generate a digest to be signed. The used PCR(s) must be set using "-pcr=#". The PCR digest can be supplied using "-pcrdigest=" or if not supplied will be read from the TPM directly.
|
|
|
|
```sh
|
|
% ./tools/tpm/policy_create -pcr=16 -pcrdigest=f84085631f85333ad0338b06c82f16888b7923abaccffb881d5416e389be256c -out=policy.bin
|
|
# OR
|
|
% ./tools/tpm/policy_create -pcrmask=0x00010000 -pcrdigest=f84085631f85333ad0338b06c82f16888b7923abaccffb881d5416e389be256c -out=policy.bin
|
|
Policy Create Tool
|
|
PCR Index(s) (SHA256): 16 (mask 0x00010000)
|
|
PCR Digest (32 bytes):
|
|
f84085631f85333ad0338b06c82f16888b7923abaccffb881d5416e389be256c
|
|
PCR Mask (0x00010000) and PCR Policy Digest (36 bytes):
|
|
0000010034ba061436aba2e9a167a1ee46af4a9578a8c6b9f71fdece21607a0cb40468ec
|
|
Wrote 36 bytes to policy.bin
|
|
```
|
|
|
|
#### Signing Policy
|
|
|
|
Building firmware with the policy digest to sign:
|
|
|
|
```sh
|
|
% make POLICY_FILE=policy.bin
|
|
```
|
|
|
|
OR manually sign the policy using the `tools/tpm/policy_sign` or `tools/keytools/sign` tools.
|
|
These tools do not need access to a TPM, they are signing a policy digest. The result is a 32-bit PCR mask + signature.
|
|
|
|
Sign with policy_sign tool:
|
|
|
|
```sh
|
|
% ./tools/tpm/policy_sign -pcr=0 -pcrdigest=eca4e8eda468b8667244ae972b8240d3244ea72341b2bf2383e79c66643bbecc
|
|
Sign PCR Policy Tool
|
|
Signing Algorithm: ECC256
|
|
PCR Index(s) (SHA256): 0
|
|
Policy Signing Key: wolfboot_signing_private_key.der
|
|
PCR Digest (32 bytes):
|
|
eca4e8eda468b8667244ae972b8240d3244ea72341b2bf2383e79c66643bbecc
|
|
PCR Policy Digest (32 bytes):
|
|
2d401eb05f45ba2b15c35f628b5896cc7de9745bb6e722363e2dbee804e0500f
|
|
PCR Policy Digest (w/PolicyRef) (32 bytes):
|
|
749b3139ece21449a7828f11ee05303b0473ff1a26cf41d6f9ff28b24c717f02
|
|
PCR Mask (0x1) and Policy Signature (68 bytes):
|
|
01000000
|
|
5b5f875b3f7ce78b5935abe4fc5a4d8a6e87c4b4ac0836fbab909e232b6d7ca2
|
|
3ecfc6be723b695b951ba2886d3c7b83ab2f8cc0e96d766bc84276eaf3f213ee
|
|
Wrote PCR Mask + Signature (68 bytes) to policy.bin.sig
|
|
```
|
|
|
|
Sign using the signing key tool:
|
|
|
|
```sh
|
|
% ./tools/keytools/sign --ecc256 --policy policy.bin test-app/image.elf wolfboot_signing_private_key.der 1
|
|
wolfBoot KeyTools (Compiled C version)
|
|
wolfBoot version 1100000
|
|
Update type: Firmware
|
|
Input image: test-app/image.elf
|
|
Selected cipher: ECC256
|
|
Selected hash : SHA256
|
|
Public key: wolfboot_signing_private_key.der
|
|
Output image: test-app/image_v1_signed.bin
|
|
Target partition id : 1
|
|
image header size calculated at runtime (256 bytes)
|
|
Calculating SHA256 digest...
|
|
Signing the digest...
|
|
Opening policy file policy.bin
|
|
Signing the policy digest...
|
|
Saving policy signature to policy.bin.sig
|
|
Output image(s) successfully created.
|
|
```
|