From 28f9ec50d22c8f0a6b602e7a629113ca8d0073db Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 31 Aug 2023 12:35:45 -0700 Subject: [PATCH] Support for sealing/unseal a secret based on an externally signed PCR policy. * Added new `WOLFBOOT_TPM_SEAL` and `WOLFBOOT_TPM_SEAL_NV_BASE` config options. * Added new `tools/tpm/policy_create` tool for assisting with creation of a policy digest. The sign keytool `--policy=file` signs the policy. * Added new `WOLFBOOT_TPM_VERIFY` option to enable offloading of the asymmetric verification to the TPM. By default wolfCrypt will be used. * Added example seal/unseal to update_flash for ARCH_SIM. * Renamed `WOLFBOOT_TPM_KEYSTORE_NV_INDEX` to `WOLFBOOT_TPM_KEYSTORE_NV_BASE` to support multiple public keys. * Refactored most TPM code into tpm.c. * Refactored the keystore ROT to use new `wolfBoot_check_rot` API. * Refactored the sign keytool to have a sign_digest function to allow signing firmware and policy for sealing/unsealing. * Fix for make distclean && make using the wrong key tools. --- .github/workflows/test-build-sim-tpm.yml | 15 +- .github/workflows/test-tpm.yml | 27 + .gitignore | 4 + IDE/XilinxSDK/README.md | 3 +- Makefile | 2 - config/examples/sim-tpm-keystore.config | 2 +- config/examples/sim-tpm-seal.config | 35 + config/examples/sim-tpm.config | 2 + config/examples/x86_fsp_qemu.config | 2 +- .../examples/x86_fsp_qemu_stage1_auth.config | 2 +- config/examples/x86_fsp_qemu_tpm.config | 2 +- .../examples/x86_fsp_qemu_tpm_keystore.config | 2 +- docs/Signing.md | 15 +- docs/TPM.md | 143 ++- include/image.h | 4 + include/loader.h | 5 - include/tpm.h | 90 ++ include/user_settings.h | 30 +- include/wolfboot/wolfboot.h | 1 + lib/wolfTPM | 2 +- lib/wolfssl | 2 +- options.mk | 40 +- src/image.c | 503 +------- src/loader.c | 4 + src/tpm.c | 1035 +++++++++++++++++ src/update_flash.c | 80 +- test-app/app_sim.c | 6 +- tools/config.mk | 8 +- tools/keytools/Makefile | 4 +- tools/keytools/sign.c | 350 +++--- tools/keytools/user_settings.h | 1 - tools/test-renode.mk | 20 +- tools/test.mk | 20 +- tools/tpm/Makefile | 28 +- tools/tpm/README.md | 17 +- tools/tpm/policy_create.c | 273 +++++ tools/tpm/rot.c | 41 +- tools/tpm/user_settings.h | 1 - 38 files changed, 2099 insertions(+), 722 deletions(-) create mode 100644 config/examples/sim-tpm-seal.config create mode 100644 include/tpm.h create mode 100644 src/tpm.c create mode 100644 tools/tpm/policy_create.c diff --git a/.github/workflows/test-build-sim-tpm.yml b/.github/workflows/test-build-sim-tpm.yml index f70fefece..9cfe693b9 100644 --- a/.github/workflows/test-build-sim-tpm.yml +++ b/.github/workflows/test-build-sim-tpm.yml @@ -53,9 +53,10 @@ jobs: run: | make -C tools/keytools && make -C tools/bin-assemble - - name: Build wolfboot + # needed for tpm tools + - name: Build keystore.c run: | - make ${{inputs.make-args}} WOLFBOOT_TPM_KEYSTORE_AUTH="${{inputs.authstr}}" + make keys ${{inputs.make-args}} - name: Build TPM tools run: | @@ -65,6 +66,16 @@ jobs: run: | ./tools/tpm/rot -write ${{inputs.rot-args}} -auth="${{inputs.authstr}}" + - name: Create a PCR Policy + run: | + echo aaa > aaa.bin + ./tools/tpm/pcr_extend 0 aaa.bin + ./tools/tpm/policy_create -pcr=0 -out=policy.bin + + - name: Build wolfboot + run: | + make ${{inputs.make-args}} WOLFBOOT_TPM_KEYSTORE_AUTH="${{inputs.authstr}}" + - name: Run wolfBoot run: | ./wolfboot.elf get_version diff --git a/.github/workflows/test-tpm.yml b/.github/workflows/test-tpm.yml index e4dfeac16..c27f66ba5 100644 --- a/.github/workflows/test-tpm.yml +++ b/.github/workflows/test-tpm.yml @@ -36,6 +36,7 @@ jobs: config-file: ./config/examples/sim-tpm.config make-args: SIGN=RSA2048 HASH=SHA256 + sim_tpm_measured_ecc256: uses: ./.github/workflows/test-build-sim-tpm.yml with: @@ -82,3 +83,29 @@ jobs: config-file: ./config/examples/sim-tpm-keystore.config make-args: SIGN=RSA2048 HASH=SHA256 authstr: TestAuth + + + sim_tpm_seal_ecc256: + uses: ./.github/workflows/test-build-sim-tpm.yml + with: + arch: host + config-file: ./config/examples/sim-tpm-seal.config + make-args: SIGN=ECC256 HASH=SHA256 POLICY_FILE=policy.bin + authstr: TestAuth + + sim_tpm_seal_ecc384: + uses: ./.github/workflows/test-build-sim-tpm.yml + with: + arch: host + config-file: ./config/examples/sim-tpm-seal.config + make-args: SIGN=ECC384 HASH=SHA384 POLICY_FILE=policy.bin + rot-args: -sha384 + authstr: TestAuth + + sim_tpm_seal_rsa2048: + uses: ./.github/workflows/test-build-sim-tpm.yml + with: + arch: host + config-file: ./config/examples/sim-tpm-seal.config + make-args: SIGN=RSA2048 HASH=SHA256 POLICY_FILE=policy.bin + authstr: TestAuth diff --git a/.gitignore b/.gitignore index c94c5b381..29f9c1a7b 100644 --- a/.gitignore +++ b/.gitignore @@ -101,6 +101,10 @@ tools/unit-tests/unit-parser tools/bin-assemble/bin-assemble tools/elf-parser/elf-parser tools/tpm/rot +tools/tpm/pcr_read +tools/tpm/pcr_reset +tools/tpm/pcr_extend +tools/tpm/policy_create config/*.ld # Generated confiuguration file diff --git a/IDE/XilinxSDK/README.md b/IDE/XilinxSDK/README.md index a736afb7b..07349619d 100644 --- a/IDE/XilinxSDK/README.md +++ b/IDE/XilinxSDK/README.md @@ -37,7 +37,8 @@ Note: If not using Position Independent Code (PIC) the linker script `ldscript.l ## Signing Example ```sh -python3 ./tools/keytools/sign.py --rsa4096 --sha3 ../helloworld/Debug/helloworld.elf ./rsa4096.der 1 +make keytools +./tools/keytools/sign --rsa4096 --sha3 ../helloworld/Debug/helloworld.elf ./rsa4096.der 1 ``` ## Bootgen diff --git a/Makefile b/Makefile index c3be9f631..c4c4d69c9 100644 --- a/Makefile +++ b/Makefile @@ -154,10 +154,8 @@ include tools/test-enc.mk include tools/test-delta.mk include tools/test-renode.mk -PYTHON?=python3 keytools_check: keytools FORCE - $(PRIVATE_KEY): $(Q)$(MAKE) keytools_check $(Q)(test $(SIGN) = NONE) || ("$(KEYGEN_TOOL)" $(KEYGEN_OPTIONS) -g $(PRIVATE_KEY)) || true diff --git a/config/examples/sim-tpm-keystore.config b/config/examples/sim-tpm-keystore.config index add3382e0..efd28516a 100644 --- a/config/examples/sim-tpm-keystore.config +++ b/config/examples/sim-tpm-keystore.config @@ -19,7 +19,7 @@ WOLFBOOT_FIXED_PARTITIONS=1 # Use NV for TPM based Root of Trust WOLFBOOT_TPM_KEYSTORE?=1 -WOLFBOOT_TPM_KEYSTORE_NV_INDEX?=0x01400200 +WOLFBOOT_TPM_KEYSTORE_NV_BASE?=0x01400200 #WOLFBOOT_TPM_KEYSTORE_AUTH?=TestAuth # TPM Logging diff --git a/config/examples/sim-tpm-seal.config b/config/examples/sim-tpm-seal.config new file mode 100644 index 000000000..c22f12cbe --- /dev/null +++ b/config/examples/sim-tpm-seal.config @@ -0,0 +1,35 @@ +ARCH=sim +TARGET=sim +SIGN?=ECC256 +HASH?=SHA256 +SPI_FLASH=0 +DEBUG=0 +WOLFTPM=1 + +# sizes should be multiple of system page size +WOLFBOOT_PARTITION_SIZE=0x40000 +WOLFBOOT_SECTOR_SIZE=0x1000 +WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000 +# if on external flash, it should be multiple of system page size +WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x100000 +WOLFBOOT_PARTITION_SWAP_ADDRESS=0x180000 + +# required for keytools +WOLFBOOT_FIXED_PARTITIONS=1 + +# Use NV for TPM based Root of Trust +WOLFBOOT_TPM_KEYSTORE?=1 +WOLFBOOT_TPM_KEYSTORE_NV_BASE?=0x01400200 +#WOLFBOOT_TPM_KEYSTORE_AUTH?=TestAuth + +# Measured boot at test PCR index 16 +MEASURED_BOOT?=1 +MEASURED_PCR_A?=16 + +# Sealing a secret into TPM based on external PCR policy signed by the sign tool +WOLFBOOT_TPM_SEAL?=1 +WOLFBOOT_TPM_SEAL_NV_BASE=0x01400300 + +# TPM Logging +#CFLAGS_EXTRA+=-DDEBUG_WOLFTPM +#CFLAGS_EXTRA+=-DWOLFTPM_DEBUG_VERBOSE diff --git a/config/examples/sim-tpm.config b/config/examples/sim-tpm.config index e5c6ead58..b00e5a879 100644 --- a/config/examples/sim-tpm.config +++ b/config/examples/sim-tpm.config @@ -5,6 +5,8 @@ HASH?=SHA256 SPI_FLASH=0 DEBUG=0 WOLFTPM=1 +# enable offloading of asymmetric verify to TPM +WOLFBOOT_TPM_VERIFY?=1 # sizes should be multiple of system page size WOLFBOOT_PARTITION_SIZE=0x40000 diff --git a/config/examples/x86_fsp_qemu.config b/config/examples/x86_fsp_qemu.config index 48b64c3b8..1e92c7c8f 100644 --- a/config/examples/x86_fsp_qemu.config +++ b/config/examples/x86_fsp_qemu.config @@ -13,7 +13,7 @@ WOLFTPM=0 # TPM Keystore options #WOLFBOOT_TPM_KEYSTORE?=1 -#WOLFBOOT_TPM_KEYSTORE_NV_INDEX?=0x01800200 +#WOLFBOOT_TPM_KEYSTORE_NV_BASE?=0x01800200 #WOLFBOOT_TPM_POLICY_NV_INDEX?=0x01800201 # 4gb - 8mb diff --git a/config/examples/x86_fsp_qemu_stage1_auth.config b/config/examples/x86_fsp_qemu_stage1_auth.config index 18aab291f..fcbcb3409 100644 --- a/config/examples/x86_fsp_qemu_stage1_auth.config +++ b/config/examples/x86_fsp_qemu_stage1_auth.config @@ -13,7 +13,7 @@ WOLFTPM=0 # TPM Keystore options #WOLFBOOT_TPM_KEYSTORE?=1 -#WOLFBOOT_TPM_KEYSTORE_NV_INDEX?=0x01800200 +#WOLFBOOT_TPM_KEYSTORE_NV_BASE?=0x01800200 #WOLFBOOT_TPM_POLICY_NV_INDEX?=0x01800201 # 4gb - 8mb diff --git a/config/examples/x86_fsp_qemu_tpm.config b/config/examples/x86_fsp_qemu_tpm.config index bccdd8291..869e1af5e 100644 --- a/config/examples/x86_fsp_qemu_tpm.config +++ b/config/examples/x86_fsp_qemu_tpm.config @@ -13,7 +13,7 @@ WOLFTPM=1 # TPM Keystore options #WOLFBOOT_TPM_KEYSTORE?=1 -#WOLFBOOT_TPM_KEYSTORE_NV_INDEX?=0x01800200 +#WOLFBOOT_TPM_KEYSTORE_NV_BASE?=0x01800200 #WOLFBOOT_TPM_POLICY_NV_INDEX?=0x01800201 # 4gb - 8mb diff --git a/config/examples/x86_fsp_qemu_tpm_keystore.config b/config/examples/x86_fsp_qemu_tpm_keystore.config index 76d7fc366..52100d91e 100644 --- a/config/examples/x86_fsp_qemu_tpm_keystore.config +++ b/config/examples/x86_fsp_qemu_tpm_keystore.config @@ -13,7 +13,7 @@ WOLFTPM=1 # TPM Keystore options WOLFBOOT_TPM_KEYSTORE?=1 -WOLFBOOT_TPM_KEYSTORE_NV_INDEX?=0x01800200 +WOLFBOOT_TPM_KEYSTORE_NV_BASE?=0x01800200 WOLFBOOT_TPM_POLICY_NV_INDEX?=0x01800201 # 4gb - 8mb diff --git a/docs/Signing.md b/docs/Signing.md index ba2796810..62e059cdd 100644 --- a/docs/Signing.md +++ b/docs/Signing.md @@ -157,7 +157,6 @@ be used for the key, 16 for the initialization of the IV. SHAREDKEY.BIN is expected to be exactly 48 bytes in size, of which 32 will be used for the key, 16 for the initialization of the IV. - #### Delta updates (incremental updates from a known version) An incremental update is created using the sign tool when the following option @@ -167,6 +166,16 @@ is provided: `BASE_SIGNED_IMG.BIN` and the new image signed starting from `IMAGE.BIN`. The result is stored in a file ending in `_signed_diff.bin`. +#### Policy signing (for sealing/unsealing with a TPM) + +Provides a PCR mask and digest to be signed and included in the header. The signing key is used to sign the digest. + + * `--policy policy.bin`: This argument is multi-purpose. + By default the file should contain a 4-byte PCR mask and SHA2-256 PCR digest to be signed. + If using `--manual-sign` then the file should contain the 4-byte PCR mask and signature. + The PCR mask and signature will be included in the `HDR_POLICY_SIGNATURE` header tag. + Note: This may require increasing the `IMAGE_HEADER_SIZE` as two signatures will be stored in the header. + #### Three-steps signing using external provisioning tools If the private key is not accessible, while it's possible to sign payloads using @@ -222,13 +231,13 @@ openssl rsa -inform DER -outform DER -in my_key.der -out rsa2048_pub.der -pubout ./tools/keytools/keygen --rsa2048 -i rsa2048_pub.der # Generate Hash to Sign -./tools/keytools/sign --rsa2048 --sha-only --sha256 test-app/image.bin rsa2048_pub.der 1 +./tools/keytools/sign --rsa2048 --sha-only --sha256 test-app/image.bin rsa2048_pub.der 1 # Sign hash Example (here is where you would use an HSM) openssl pkeyutl -sign -keyform der -inkey my_key.der -in test-app/image_v1_digest.bin > test-app/image_v1.sig # Generate final signed binary -./tools/keytools/sign --rsa2048 --sha256 --manual-sign test-app/image.bin rsa2048_pub.der 1 test-app/image_v1.sig +./tools/keytools/sign --rsa2048 --sha256 --manual-sign test-app/image.bin rsa2048_pub.der 1 test-app/image_v1.sig # Combine into factory image (0xc0000 is the WOLFBOOT_PARTITION_BOOT_ADDRESS) tools/bin-assemble/bin-assemble factory.bin 0x0 wolfboot.bin \ diff --git a/docs/TPM.md b/docs/TPM.md index 78671862d..befff1a68 100644 --- a/docs/TPM.md +++ b/docs/TPM.md @@ -6,11 +6,14 @@ In wolfBoot we support TPM based root of trust, sealing/unsealing, cryptographic | Config Option | Preprocessor Macro | Description | | ------------- | ------------------ | ----------------------------------- | -| `WOLFTPM=1` | `WOLFBOOT_TPM` | Enables wolfTPM support and cryptographic offloading for RSA2048 and ECC256/384 | +| `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_INDEX=0x` | `WOLFBOOT_TPM_KEYSTORE_NV_INDEX=0x` | NV index in platform range 0x1400000 - 0x17FFFFF | +| `WOLFBOOT_TPM_KEYSTORE_NV_BASE=0x` | `WOLFBOOT_TPM_KEYSTORE_NV_BASE=0x` | NV index in platform range 0x1400000 - 0x17FFFFF. | | `MEASURED_BOOT=1` | `WOLFBOOT_MEASURED_BOOT` | Enable measured boot. Extend PCR with wolfBoot hash. | -| `MEASURED_PCR_A=16` | `WOLFBOOT_MEASURED_PCR_A=16` | The PCR index to use. See [docs/measured_boot.md](/docs/measured_boot.md) | +| `MEASURED_PCR_A=16` | `WOLFBOOT_MEASURED_PCR_A=16` | The PCR index to use. See [docs/measured_boot.md](/docs/measured_boot.md). | +| `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. | ## Root of Trust (ROT) @@ -20,12 +23,140 @@ The design uses a platform NV handle that has been locked. The NV stores a hash ## Cryptographic offloading -The RSA2048 and ECC256/384 bit verification can be offloaded to a TPM for code size reduction or performance improvement. +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` ## Measured Boot -The wolfBoot image 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. +The wolfBoot image 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`. ## Sealing and Unsealing a secret -API's for this will be available soon. +See the wolfTPM Sealing/Unsealing example [here](https://github.com/wolfSSL/wolfTPM/tree/secret_seal/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) and a digest to sign will be printed for use with the sign tool. + +This exposes two new wolfBoot API's for sealing and unsealing data with blob stored to NV index: +```c +int wolfBoot_seal(struct wolfBoot_image* img, int index, const uint8_t* secret, int secret_sz); +int wolfBoot_unseal(struct wolfBoot_image* img, int index, uint8_t* secret, int* secret_sz); +``` + +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`. + + +### Testing seal/unseal with simulator + +```sh +cp config/examples/sim-tpm-seal.config .config +make keytools +make tpmtools +echo aaa > aaa.bin +./tools/tpm/pcr_extend 0 aaa.bin +./tools/tpm/policy_create -pcr=0 +# if ROT enabled +./tools/tpm/rot -write +make clean && make POLICY_FILE=policy.bin +./wolfboot.elf get_version +Simulator assigned ./internal_flash.dd to base 0x103797000 +Mfg IBM (0), Vendor SW TPM, Fw 8217.4131 (0x163636), FIPS 140-2 1, CC-EAL4 0 +Unlocking disk... +Boot partition: 0x103817000 +Image size 54400 +Sealing 32 bytes +11c6ac0ec972ae567c541750c6ecccd426f131dad3eeca5e6540d901d9f0c336 +Unsealed 32 bytes +11c6ac0ec972ae567c541750c6ecccd426f131dad3eeca5e6540d901d9f0c336 +Boot partition: 0x103817000 +Image size 54400 +TPM Root of Trust valid (id 0) +Simulator assigned ./internal_flash.dd to base 0x103962000 +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 using: + +```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... +Output image(s) successfully created. +``` diff --git a/include/image.h b/include/image.h index ed6e3f124..65a8cedbb 100644 --- a/include/image.h +++ b/include/image.h @@ -569,7 +569,11 @@ uint8_t* wolfBoot_peek_image(struct wolfBoot_image *img, uint32_t offset, /* Defined in libwolfboot */ uint16_t wolfBoot_find_header(uint8_t *haystack, uint16_t type, uint8_t **ptr); +/* get header type for image */ +uint16_t wolfBoot_get_header(struct wolfBoot_image *img, uint16_t type, uint8_t **ptr); +/* Find the key slot ID based on the SHA hash of the key. */ +int keyslot_id_by_sha(const uint8_t *hint); #ifdef EXT_FLASH # ifdef PART_BOOT_EXT diff --git a/include/loader.h b/include/loader.h index 0b6c898e4..dd76e1ddd 100644 --- a/include/loader.h +++ b/include/loader.h @@ -71,11 +71,6 @@ extern "C" { # error "No public key available for given signing algorithm." #endif /* Algorithm selection */ -#ifdef WOLFBOOT_TPM - int wolfBoot_tpm2_init(void); - void wolfBoot_tpm2_deinit(void); -#endif - void wolfBoot_start(void); #if defined(ARCH_ARM) && defined(WOLFBOOT_ARMORED) diff --git a/include/tpm.h b/include/tpm.h new file mode 100644 index 000000000..2bf5fe901 --- /dev/null +++ b/include/tpm.h @@ -0,0 +1,90 @@ +/* tpm.h + * + * Copyright (C) 2023 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef _WOLFBOOT_TPM_H_ +#define _WOLFBOOT_TPM_H_ + +#ifdef WOLFBOOT_TPM + +#include "wolftpm/tpm2.h" +#include "wolftpm/tpm2_wrap.h" + +extern WOLFTPM2_DEV wolftpm_dev; +#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL) +extern WOLFTPM2_SESSION wolftpm_session; +extern WOLFTPM2_KEY wolftpm_srk; +#endif + +#ifndef WOLFBOOT_TPM_KEYSTORE_NV_BASE + #define WOLFBOOT_TPM_KEYSTORE_NV_BASE 0x01400200 +#endif +#ifndef WOLFBOOT_TPM_SEAL_NV_BASE + #define WOLFBOOT_TPM_SEAL_NV_BASE 0x01400300 +#endif +#ifndef WOLFBOOT_TPM_PCR_ALG + /* Prefer SHA2-256 for PCR's, and all TPM 2.0 devices support it */ + #define WOLFBOOT_TPM_PCR_ALG TPM_ALG_SHA256 + #define WOLFBOOT_TPM_PCR_DIG_SZ 32 +#endif + +#define WOLFBOOT_MAX_SEAL_SZ MAX_SYM_DATA + + +int wolfBoot_tpm2_init(void); +void wolfBoot_tpm2_deinit(void); + +#ifdef WOLFBOOT_TPM_KEYSTORE +int wolfBoot_check_rot(int key_slot, uint8_t* pubkey_hint); +#endif + +#ifdef WOLFBOOT_TPM_SEAL +int wolfBoot_get_random(uint8_t* buf, int sz); +int wolfBoot_get_pcr_active(uint8_t pcrAlg, uint32_t* pcrMask, uint8_t pcrMax); +int wolfBoot_build_policy(uint8_t pcrAlg, uint32_t pcrMask, uint8_t* policy, uint32_t* policySz, uint8_t* policyRef, uint32_t policyRefSz); +int wolfBoot_get_policy(struct wolfBoot_image* img, uint8_t** policy, uint16_t* policySz); +int wolfBoot_seal(struct wolfBoot_image* img, int index, const uint8_t* secret, int secret_sz); +int wolfBoot_seal_blob(struct wolfBoot_image* img, WOLFTPM2_KEYBLOB* seal_blob, const uint8_t* secret, int secret_sz); +int wolfBoot_unseal(struct wolfBoot_image* img, int index, uint8_t* secret, int* secret_sz); +int wolfBoot_unseal_blob(struct wolfBoot_image* img, WOLFTPM2_KEYBLOB* seal_blob, uint8_t* secret, int* secret_sz); +#endif + +#ifdef WOLFBOOT_MEASURED_BOOT +int wolfBoot_tpm2_extend(uint8_t pcrIndex, uint8_t* hash, int line); + +/* helper for measuring boot at line */ +#define measure_boot(hash) \ + wolfBoot_tpm2_extend(WOLFBOOT_MEASURED_PCR_A, (hash), __LINE__) +#endif /* WOLFBOOT_MEASURED_BOOT */ + +/* debugging */ +void wolfBoot_print_hexstr(const unsigned char* bin, unsigned long sz, + unsigned long maxLine); +void wolfBoot_print_bin(const byte* buffer, word32 length); + + +#else + +/* stubs */ +#define measure_boot(hash) + +#endif /* WOLFBOOT_TPM */ + +#endif /* !_WOLFBOOT_TPM_H_ */ diff --git a/include/user_settings.h b/include/user_settings.h index 6ea02d8ec..2f0994350 100644 --- a/include/user_settings.h +++ b/include/user_settings.h @@ -44,6 +44,10 @@ extern int tolower(int c); # define WC_NO_HARDEN #endif +#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL) +# define WOLFBOOT_TPM_PARMENC /* used in this file to gate features */ +#endif + /* ED25519 and SHA512 */ #ifdef WOLFBOOT_SIGN_ED25519 # define HAVE_ED25519 @@ -110,7 +114,7 @@ extern int tolower(int c); # define WOLFSSL_SP_384 # define WOLFSSL_SP_NO_256 # endif -# if !defined(WOLFBOOT_TPM_KEYSTORE) +# if !defined(WOLFBOOT_TPM_PARMENC) # define NO_ECC256 # endif #elif defined(WOLFBOOT_SIGN_ECC521) @@ -120,7 +124,7 @@ extern int tolower(int c); # define WOLFSSL_SP_521 # define WOLFSSL_SP_NO_256 # endif -# if !defined(WOLFBOOT_TPM_KEYSTORE) +# if !defined(WOLFBOOT_TPM_PARMENC) # define NO_ECC256 # endif #endif @@ -135,7 +139,7 @@ extern int tolower(int c); # define WOLFSSL_RSA_VERIFY_INLINE # define WOLFSSL_RSA_VERIFY_ONLY # endif -# ifndef WOLFBOOT_TPM_KEYSTORE +# if !defined(WOLFBOOT_TPM_PARMENC) # define WC_NO_RSA_OAEP # endif # define FP_MAX_BITS (2048 * 2) @@ -190,14 +194,14 @@ extern int tolower(int c); #ifdef WOLFBOOT_HASH_SHA3_384 # define WOLFSSL_SHA3 -# if defined(NO_RSA) && !defined(WOLFBOOT_TPM_KEYSTORE) +# if defined(NO_RSA) && !defined(WOLFBOOT_TPM_PARMENC) # define NO_SHA256 # endif #endif #ifdef WOLFBOOT_HASH_SHA384 # define WOLFSSL_SHA384 -# if defined(NO_RSA) && !defined(WOLFBOOT_TPM_KEYSTORE) +# if defined(NO_RSA) && !defined(WOLFBOOT_TPM_PARMENC) # define NO_SHA256 # endif #endif @@ -232,8 +236,16 @@ extern int tolower(int c); #ifdef WOLFBOOT_TPM /* Do not use heap */ #define WOLFTPM2_NO_HEAP + /* small stack options */ + #ifdef WOLFTPM_SMALL_STACK + #define MAX_COMMAND_SIZE 1024 + #define MAX_RESPONSE_SIZE 1350 + #define WOLFTPM2_MAX_BUFFER 1500 + #define MAX_SESSION_NUM 2 + #define MAX_DIGEST_BUFFER 973 + #endif - #ifdef WOLFBOOT_TPM_KEYSTORE + #ifdef WOLFBOOT_TPM_PARMENC /* Enable AES CFB (parameter encryption) and HMAC (for KDF) */ #define WOLFSSL_AES_CFB @@ -241,7 +253,7 @@ extern int tolower(int c); #define WOLFSSL_PUBLIC_MP /* Configure RNG seed */ - #define CUSTOM_RAND_GENERATE_SEED(buf, sz) 0 /* stub, not used */ + #define CUSTOM_RAND_GENERATE_SEED(buf, sz) ({(void)buf; (void)sz; 0;}) /* stub, not used */ #define WC_RNG_SEED_CB #define HAVE_HASHDRBG #endif @@ -269,10 +281,10 @@ extern int tolower(int c); /* Disables - For minimum wolfCrypt build */ #if !defined(ENCRYPT_WITH_AES128) && !defined(ENCRYPT_WITH_AES256) && \ - !defined(WOLFBOOT_TPM_KEYSTORE) + !defined(WOLFBOOT_TPM_PARMENC) #define NO_AES #endif -#if !defined(WOLFBOOT_TPM_KEYSTORE) +#if !defined(WOLFBOOT_TPM_PARMENC) #define NO_HMAC #define WC_NO_RNG #define WC_NO_HASHDRBG diff --git a/include/wolfboot/wolfboot.h b/include/wolfboot/wolfboot.h index 892051be8..4bc507590 100644 --- a/include/wolfboot/wolfboot.h +++ b/include/wolfboot/wolfboot.h @@ -209,6 +209,7 @@ extern "C" { #endif /* defined WOLFBOOT */ + #define PART_BOOT 0 #define PART_UPDATE 1 #define PART_SWAP 2 diff --git a/lib/wolfTPM b/lib/wolfTPM index 5b7e50a19..50bfac48a 160000 --- a/lib/wolfTPM +++ b/lib/wolfTPM @@ -1 +1 @@ -Subproject commit 5b7e50a19c79ef51530df6f32912c60490336fb4 +Subproject commit 50bfac48a970a61afa1463ec6514bf9b404830cb diff --git a/lib/wolfssl b/lib/wolfssl index 05b692d01..cd02d5140 160000 --- a/lib/wolfssl +++ b/lib/wolfssl @@ -1 +1 @@ -Subproject commit 05b692d01cb2fa172d8a86e04a1dbbdb5eb88703 +Subproject commit cd02d5140fccd2478dcd4845d1b7cef6be881dde diff --git a/options.mk b/options.mk index 714ae41ba..3b03b6618 100644 --- a/options.mk +++ b/options.mk @@ -1,3 +1,8 @@ +ifeq ($(WOLFBOOT_TPM_VERIFY),1) + WOLFTPM:=1 + CFLAGS+=-D"WOLFBOOT_TPM_VERIFY" +endif + ## Measured boot requires TPM to be present ifeq ($(MEASURED_BOOT),1) WOLFTPM:=1 @@ -7,15 +12,28 @@ endif ## TPM keystore ifeq ($(WOLFBOOT_TPM_KEYSTORE),1) - WOLFCRYPT_OBJS+=./lib/wolfssl/wolfcrypt/src/random.o - ifneq ($(WOLFBOOT_TPM_KEYSTORE_NV_INDEX),) - WOLFTPM:=1 - CFLAGS+=-DWOLFBOOT_TPM_KEYSTORE - CFLAGS+=-DWOLFBOOT_TPM_KEYSTORE_NV_INDEX=$(WOLFBOOT_TPM_KEYSTORE_NV_INDEX) + WOLFTPM:=1 + CFLAGS+=-D"WOLFBOOT_TPM_KEYSTORE" + ifneq ($(WOLFBOOT_TPM_KEYSTORE_AUTH),) CFLAGS+=-DWOLFBOOT_TPM_KEYSTORE_AUTH='"$(WOLFBOOT_TPM_KEYSTORE_AUTH)"' endif + ifneq ($(WOLFBOOT_TPM_KEYSTORE_NV_BASE),) + CFLAGS+=-D"WOLFBOOT_TPM_KEYSTORE_NV_BASE=$(WOLFBOOT_TPM_KEYSTORE_NV_BASE)" + endif endif +## Sealing a secret into the TPM +ifeq ($(WOLFBOOT_TPM_SEAL),1) + WOLFTPM:=1 + CFLAGS+=-D"WOLFBOOT_TPM_SEAL" + ifneq ($(WOLFBOOT_TPM_SEAL_NV_BASE),) + CFLAGS+=-D"WOLFBOOT_TPM_SEAL_NV_BASE=$(WOLFBOOT_TPM_SEAL_NV_BASE)" + endif + + ifneq ($(POLICY_FILE),) + SIGN_OPTIONS+=--policy $(POLICY_FILE) + endif +endif ## DSA Settings ifeq ($(SIGN),NONE) @@ -503,15 +521,16 @@ ifeq ($(WOLFBOOT_HUGE_STACK),1) endif ifeq ($(WOLFTPM),1) - OBJS += lib/wolfTPM/src/tpm2.o \ + OBJS+=\ + ./src/tpm.o \ + lib/wolfTPM/src/tpm2.o \ lib/wolfTPM/src/tpm2_packet.o \ lib/wolfTPM/src/tpm2_tis.o \ lib/wolfTPM/src/tpm2_wrap.o \ lib/wolfTPM/src/tpm2_param_enc.o - CFLAGS+=-D"WOLFBOOT_TPM" -D"SIZEOF_LONG=4" -Ilib/wolfTPM \ - -D"MAX_COMMAND_SIZE=1024" -D"MAX_RESPONSE_SIZE=1024" -D"WOLFTPM2_MAX_BUFFER=1500" \ - -D"MAX_SESSION_NUM=2" -D"MAX_DIGEST_BUFFER=973" \ - -D"WOLFTPM_SMALL_STACK" + CFLAGS+=-Ilib/wolfTPM + CFLAGS+=-D"WOLFBOOT_TPM" + CFLAGS+=-D"WOLFTPM_SMALL_STACK" CFLAGS+=-D"WOLFTPM_AUTODETECT" ifneq ($(SPI_FLASH),1) # don't use spi if we're using simulator @@ -534,6 +553,7 @@ ifeq ($(WOLFTPM),1) endif WOLFCRYPT_OBJS+=./lib/wolfssl/wolfcrypt/src/aes.o WOLFCRYPT_OBJS+=./lib/wolfssl/wolfcrypt/src/hmac.o + WOLFCRYPT_OBJS+=./lib/wolfssl/wolfcrypt/src/random.o ifeq ($(DEBUG),1) CFLAGS+=-DWOLFBOOT_DEBUG_TPM=1 endif diff --git a/src/image.c b/src/image.c index ab39818b6..b981a95fd 100644 --- a/src/image.c +++ b/src/image.c @@ -25,45 +25,24 @@ */ #ifndef IMAGE_H_ #define IMAGE_H_ + +#include /* for wolfCrypt hash/sign routines */ + +#include +#include + #include "loader.h" #include "image.h" #include "hal.h" #include "spi_drv.h" -#include #include "printf.h" - -#include -#include - -#include "image.h" - #ifdef WOLFBOOT_TPM -#include -#include "wolftpm/tpm2.h" -#include "wolftpm/tpm2_wrap.h" -#include "wolftpm/tpm2_tis.h" /* for TIS header size and wait state */ -static WOLFTPM2_DEV wolftpm_dev; -#ifdef WOLFBOOT_TPM_KEYSTORE -static WOLFTPM2_SESSION wolftpm_session; -static WOLFTPM2_KEY wolftpm_srk; -#endif -#endif /* WOLFBOOT_TPM */ - -#if defined(WOLFBOOT_TPM_KEYSTORE) && !defined(WOLFBOOT_TPM) -#error For TPM keystore please make sure WOLFBOOT_TPM is also defined +#include "tpm.h" #endif /* Globals */ static uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE]; -/* Forward declarations */ -/** - * @brief Find the key slot ID based on the SHA hash of the key. - * - * @param hint The SHA hash to find the key slot ID for. - * @return The key slot ID corresponding to the provided SHA hash. - */ -static int keyslot_id_by_sha(const uint8_t *hint); #ifdef WOLFBOOT_SIGN_ED25519 #include @@ -144,7 +123,7 @@ static void wolfBoot_verify_signature(uint8_t key_slot, uint8_t *pubkey = keystore_get_buffer(key_slot); int pubkey_sz = keystore_get_size(key_slot); int point_sz = pubkey_sz/2; -#ifdef WOLFBOOT_TPM +#if defined(WOLFBOOT_TPM) && defined(WOLFBOOT_TPM_VERIFY) WOLFTPM2_KEY tpmKey; #else ecc_key ecc; @@ -155,7 +134,7 @@ static void wolfBoot_verify_signature(uint8_t key_slot, return; } -#ifdef WOLFBOOT_TPM +#if defined(WOLFBOOT_TPM) && defined(WOLFBOOT_TPM_VERIFY) /* Use TPM for ECC verify */ /* Load public key into TPM */ memset(&tpmKey, 0, sizeof(tpmKey)); @@ -203,10 +182,10 @@ static void wolfBoot_verify_signature(uint8_t key_slot, } wc_ecc_free(&ecc); } -#endif /* WOLFBOOT_TPM */ +#endif /* WOLFBOOT_TPM && WOLFBOOT_TPM_VERIFY*/ } -#endif /* WOLFBOOT_SIGN_ECC256 */ +#endif /* WOLFBOOT_SIGN_ECC256 || WOLFBOOT_SIGN_ECC384 || WOLFBOOT_SIGN_ECC521 */ #if defined(WOLFBOOT_SIGN_RSA2048) || \ @@ -268,7 +247,7 @@ static int RsaDecodeSignature(uint8_t** pInput, int inputSz) } #endif /* !NO_RSA_SIG_ENCODING */ -#ifdef WOLFBOOT_TPM +#if defined(WOLFBOOT_TPM) && defined(WOLFBOOT_TPM_VERIFY) /* RSA PKCSV15 un-padding with RSA_BLOCK_TYPE_1 (public) */ /* UnPad plaintext, set start to *output, return length of plaintext or error */ static int RsaUnPad(const byte *pkcsBlock, int pkcsBlockLen, byte **output) @@ -291,7 +270,7 @@ static int RsaUnPad(const byte *pkcsBlock, int pkcsBlockLen, byte **output) ret = pkcsBlockLen - i; return ret; } -#endif /* WOLFBOOT_TPM */ +#endif /* WOLFBOOT_TPM && WOLFBOOT_TPM_VERIFY*/ static void wolfBoot_verify_signature(uint8_t key_slot, struct wolfBoot_image *img, uint8_t *sig) @@ -303,7 +282,7 @@ static void wolfBoot_verify_signature(uint8_t key_slot, uint8_t *pubkey = keystore_get_buffer(key_slot); int pubkey_sz = keystore_get_size(key_slot); word32 inOutIdx = 0; -#ifdef WOLFBOOT_TPM +#if defined(WOLFBOOT_TPM) && defined(WOLFBOOT_TPM_VERIFY) WOLFTPM2_KEY tpmKey; const byte *n = NULL, *e = NULL; word32 nSz = 0, eSz = 0; @@ -315,7 +294,7 @@ static void wolfBoot_verify_signature(uint8_t key_slot, return; } -#ifdef WOLFBOOT_TPM +#if defined(WOLFBOOT_TPM) && defined(WOLFBOOT_TPM_VERIFY) /* Extract DER RSA key struct */ memset(&tpmKey, 0, sizeof(tpmKey)); ret = wc_RsaPublicKeyDecode_ex(pubkey, &inOutIdx, pubkey_sz, @@ -378,7 +357,7 @@ static void wolfBoot_verify_signature(uint8_t key_slot, } #endif /* SCE || TSIP */ wc_FreeRsaKey(&rsa); -#endif /* WOLFBOOT_TPM */ +#endif /* WOLFBOOT_TPM && WOLFBOOT_TPM_VERIFY*/ #ifndef NO_RSA_SIG_ENCODING if (ret > WOLFBOOT_SHA_DIGEST_SIZE) { @@ -480,7 +459,8 @@ static uint16_t get_header_ext(struct wolfBoot_image *img, uint16_t type, * @param ptr A pointer to store the position of the header. * @return The size of the data if found, otherwise 0. */ -static uint16_t get_header(struct wolfBoot_image *img, uint16_t type, +#define get_header wolfBoot_get_header /* internal reference to function */ +uint16_t wolfBoot_get_header(struct wolfBoot_image *img, uint16_t type, uint8_t **ptr) { if (PART_IS_EXT(img)) @@ -560,36 +540,6 @@ static uint8_t *get_img_hdr(struct wolfBoot_image *img) #if defined(WOLFBOOT_HASH_SHA256) #include -#ifdef WOLFBOOT_MEASURED_BOOT -static int self_sha256(uint8_t *hash) -{ - uintptr_t p = (uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS; - uint32_t sz = (uint32_t)WOLFBOOT_PARTITION_SIZE; - uint32_t blksz, position = 0; - wc_Sha256 sha256_ctx; - - wc_InitSha256(&sha256_ctx); - do { - blksz = WOLFBOOT_SHA_BLOCK_SIZE; - if (position + blksz > sz) - blksz = sz - position; - #if defined(EXT_FLASH) && defined(NO_XIP) - rc = ext_flash_read(p, ext_hash_block, WOLFBOOT_SHA_BLOCK_SIZE); - if (rc != WOLFBOOT_SHA_BLOCK_SIZE) - return -1; - wc_Sha256Update(&sha256_ctx, ext_hash_block, blksz); - #else - wc_Sha256Update(&sha256_ctx, (uint8_t*)p, blksz); - #endif - position += blksz; - p += blksz; - } while (position < sz); - wc_Sha256Final(&sha256_ctx, hash); - - return 0; -} -#endif /* WOLFBOOT_MEASURED_BOOT */ - /** * @brief Calculate the SHA256 hash of the image. * @@ -672,36 +622,6 @@ static void key_sha256(uint8_t key_slot, uint8_t *hash) #if defined(WOLFBOOT_HASH_SHA384) #include -#ifdef WOLFBOOT_MEASURED_BOOT -static int self_sha384(uint8_t *hash) -{ - uintptr_t p = (uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS; - uint32_t sz = (uint32_t)WOLFBOOT_PARTITION_SIZE; - uint32_t blksz, position = 0; - wc_Sha384 sha384_ctx; - - wc_InitSha384(&sha384_ctx); - do { - blksz = WOLFBOOT_SHA_BLOCK_SIZE; - if (position + blksz > sz) - blksz = sz - position; - #if defined(EXT_FLASH) && defined(NO_XIP) - rc = ext_flash_read(p, ext_hash_block, WOLFBOOT_SHA_BLOCK_SIZE); - if (rc != WOLFBOOT_SHA_BLOCK_SIZE) - return -1; - wc_Sha384Update(&sha384_ctx, ext_hash_block, blksz); - #else - wc_Sha384Update(&sha384_ctx, (uint8_t*)p, blksz); - #endif - position += blksz; - p += blksz; - } while (position < sz); - wc_Sha384Final(&sha384_ctx, hash); - - return 0; -} -#endif /* WOLFBOOT_MEASURED_BOOT */ - /** * @brief Calculate SHA-384 hash of the image. * @@ -876,340 +796,6 @@ static void key_sha3_384(uint8_t key_slot, uint8_t *hash) #endif /* WOLFBOOT_NO_SIGN */ #endif /* SHA3-384 */ -#ifdef WOLFBOOT_TPM -#if defined(DEBUG_WOLFTPM) || defined(WOLFTPM_DEBUG_IO) || \ - defined(WOLFBOOT_DEBUG_TPM) -#define LINE_LEN 16 -static void wolfBoot_PrintBin(const byte* buffer, word32 length) -{ - word32 i, sz; - - if (!buffer) { - wolfBoot_printf("\tNULL\n"); - return; - } - - while (length > 0) { - sz = length; - if (sz > LINE_LEN) - sz = LINE_LEN; - - wolfBoot_printf("\t"); - for (i = 0; i < LINE_LEN; i++) { - if (i < length) - wolfBoot_printf("%02x ", buffer[i]); - else - wolfBoot_printf(" "); - } - wolfBoot_printf("| "); - for (i = 0; i < sz; i++) { - if (buffer[i] > 31 && buffer[i] < 127) - wolfBoot_printf("%c", buffer[i]); - else - wolfBoot_printf("."); - } - wolfBoot_printf("\r\n"); - - buffer += sz; - length -= sz; - } -} -#endif /* WOLFTPM_DEBUG_IO || WOLFBOOT_DEBUG_TPM */ - -#if !defined(ARCH_SIM) && !defined(WOLFTPM_MMIO) -#ifdef WOLFTPM_ADV_IO -static int TPM2_IoCb(TPM2_CTX* ctx, int isRead, word32 addr, byte* buf, - word16 size, void* userCtx) -#else - -/** - * @brief TPM2 I/O callback function for communication with TPM2 device. - * - * This function is used as the I/O callback function for communication - * with the TPM2 device. It is called during TPM operations to send and - * receive data from the TPM2 device. - * - * @param ctx The pointer to the TPM2 context. - * @param txBuf The buffer containing data to be sent to the TPM2 device. - * @param rxBuf The buffer to store the received data from the TPM2 device. - * @param xferSz The size of the data to be transferred. - * @param userCtx The user context (not used in this implementation). - * @return The return code from the TPM2 device operation. - */ -static int TPM2_IoCb(TPM2_CTX* ctx, const byte* txBuf, byte* rxBuf, - word16 xferSz, void* userCtx) -#endif -{ - int ret; -#ifdef WOLFTPM_CHECK_WAIT_STATE - int timeout = TPM_SPI_WAIT_RETRY; -#endif -#ifdef WOLFTPM_ADV_IO - byte txBuf[MAX_SPI_FRAMESIZE+TPM_TIS_HEADER_SZ]; - byte rxBuf[MAX_SPI_FRAMESIZE+TPM_TIS_HEADER_SZ]; - int xferSz = TPM_TIS_HEADER_SZ + size; - -#ifdef WOLFTPM_DEBUG_IO - wolfBoot_printf("TPM2_IoCb (Adv): Read %d, Addr %x, Size %d\n", - isRead ? 1 : 0, addr, size); - if (!isRead) { - wolfBoot_PrintBin(buf, size); - } -#endif - - /* Build TPM header */ - txBuf[1] = (addr>>16) & 0xFF; - txBuf[2] = (addr>>8) & 0xFF; - txBuf[3] = (addr) & 0xFF; - if (isRead) { - txBuf[0] = TPM_TIS_READ | ((size & 0xFF) - 1); - memset(&txBuf[TPM_TIS_HEADER_SZ], 0, size); - } - else { - txBuf[0] = TPM_TIS_WRITE | ((size & 0xFF) - 1); - memcpy(&txBuf[TPM_TIS_HEADER_SZ], buf, size); - } - memset(rxBuf, 0, sizeof(rxBuf)); -#endif /* WOLFTPM_ADV_IO */ - -#ifdef WOLFTPM_CHECK_WAIT_STATE /* Handle TIS wait states */ - /* Send header - leave CS asserted */ - ret = spi_xfer(SPI_CS_TPM, txBuf, rxBuf, TPM_TIS_HEADER_SZ, - 0x1 /* 1=SPI_XFER_FLAG_CONTINUE */ - ); - - /* Handle wait states */ - while (ret == 0 && - --timeout > 0 && - (rxBuf[TPM_TIS_HEADER_SZ-1] & TPM_TIS_READY_MASK) == 0) - { - /* clock additional byte until 0x01 LSB is set (keep CS asserted) */ - ret = spi_xfer(SPI_CS_TPM, - &txBuf[TPM_TIS_HEADER_SZ-1], - &rxBuf[TPM_TIS_HEADER_SZ-1], 1, - 0x1 /* 1=SPI_XFER_FLAG_CONTINUE */ - ); - } - /* Check for timeout */ - if (ret == 0 && timeout <= 0) { - ret = TPM_RC_FAILURE; - } - - /* Transfer remainder of payload (command / response) */ - if (ret == 0) { - ret = spi_xfer(SPI_CS_TPM, - &txBuf[TPM_TIS_HEADER_SZ], - &rxBuf[TPM_TIS_HEADER_SZ], - xferSz-TPM_TIS_HEADER_SZ, - 0 /* de-assert CS*/ ); - } - /* On error make sure SPI is de-asserted */ - else { - spi_xfer(SPI_CS_TPM, NULL, NULL, 0, 0); - return ret; - } -#else /* Send Entire Message - no wait states */ - ret = spi_xfer(SPI_CS_TPM, txBuf, rxBuf, xferSz, 0); - - #ifdef WOLFTPM_DEBUG_IO - wolfBoot_printf("TPM2_IoCb: Ret %d, Sz %d\n", ret, xferSz); - wolfBoot_PrintBin(txBuf, xferSz); - wolfBoot_PrintBin(rxBuf, xferSz); - #endif -#endif /* !WOLFTPM_CHECK_WAIT_STATE */ - -#ifdef WOLFTPM_ADV_IO - if (isRead) { - memcpy(buf, &rxBuf[TPM_TIS_HEADER_SZ], size); - #ifdef WOLFTPM_DEBUG_IO - wolfBoot_PrintBin(buf, size); - #endif - } -#endif - - return ret; -} -#endif /* !ARCH_SIM && !WOLFTPM_MMIO */ - -#ifdef WOLFBOOT_MEASURED_BOOT -#define measure_boot(hash) wolfBoot_tpm2_extend((hash), __LINE__) -/** - * @brief Extends a PCR in the TPM with a hash. - * - * Extends a specified PCR's value in the TPM with a given hash. Uses - * TPM2_PCR_Extend. Optionally, if DEBUG_WOLFTPM or WOLFBOOT_DEBUG_TPM defined, - * prints debug info. - * - * @param[in] hash Pointer to the hash value to extend into the PCR. - * @param[in] line Line number where the function is called (for debugging). - * @return 0 on success, an error code on failure. - * - */ -static int wolfBoot_tpm2_extend(uint8_t* hash, int line) -{ - int rc; - PCR_Extend_In pcrExtend; -#ifdef WOLFBOOT_DEBUG_TPM - PCR_Read_In pcrReadCmd; - PCR_Read_Out pcrReadResp; -#endif - - pcrExtend.pcrHandle = WOLFBOOT_MEASURED_PCR_A; - pcrExtend.digests.count = 1; - pcrExtend.digests.digests[0].hashAlg = TPM_ALG_SHA256; - XMEMCPY(pcrExtend.digests.digests[0].digest.H, - hash, TPM_SHA256_DIGEST_SIZE); - - rc = TPM2_PCR_Extend(&pcrExtend); -#ifdef DEBUG_WOLFTPM - wolfBoot_printf("Measured boot: Res %d, Index %d, Line %d\n", - rc, pcrExtend.pcrHandle, line); -#endif - -#ifdef WOLFBOOT_DEBUG_TPM - if (rc == 0) { - memset(&pcrReadCmd, 0, sizeof(pcrReadCmd)); - memset(&pcrReadResp, 0, sizeof(pcrReadResp)); - TPM2_SetupPCRSel(&pcrReadCmd.pcrSelectionIn, TPM_ALG_SHA256, - pcrExtend.pcrHandle); - rc = TPM2_PCR_Read(&pcrReadCmd, &pcrReadResp); - - wolfBoot_printf("PCR %d: Res %d, Digest Sz %d, Update Counter %d\n", - pcrExtend.pcrHandle, rc, - (int)pcrReadResp.pcrValues.digests[0].size, - (int)pcrReadResp.pcrUpdateCounter); - wolfBoot_PrintBin(pcrReadResp.pcrValues.digests[0].buffer, - pcrReadResp.pcrValues.digests[0].size); - } -#endif - (void)line; - - return rc; -} -#endif /* WOLFBOOT_MEASURED_BOOT */ - -#if defined(WOLFBOOT_TPM_KEYSTORE) && defined(WC_RNG_SEED_CB) -static int wolfRNG_GetSeedCB(OS_Seed* os, byte* seed, word32 sz) -{ - (void)os; - return wolfTPM2_GetRandom(&wolftpm_dev, seed, sz); -} -#endif - -/** - * @brief Initialize the TPM2 device and retrieve its capabilities. - * - * This function initializes the TPM2 device and retrieves its capabilities. - * - * @return 0 on success, an error code on failure. - */ -int wolfBoot_tpm2_init(void) -{ - int rc; - word32 idx; - WOLFTPM2_CAPS caps; -#ifdef WOLFBOOT_TPM_KEYSTORE - TPM_ALG_ID alg; -#endif - -#if !defined(ARCH_SIM) && !defined(WOLFTPM_MMIO) - spi_init(0,0); -#endif - - /* Init the TPM2 device */ - /* simulator should use the network connection, not spi */ -#if defined(ARCH_SIM) || defined(WOLFTPM_MMIO) - rc = wolfTPM2_Init(&wolftpm_dev, NULL, NULL); -#else - rc = wolfTPM2_Init(&wolftpm_dev, TPM2_IoCb, NULL); -#endif - if (rc == 0) { - /* Get device capabilities + options */ - rc = wolfTPM2_GetCapabilities(&wolftpm_dev, &caps); - } - if (rc == 0) { - wolfBoot_printf("Mfg %s (%d), Vendor %s, Fw %u.%u (0x%x), " - "FIPS 140-2 %d, CC-EAL4 %d\n", - caps.mfgStr, caps.mfg, caps.vendorStr, caps.fwVerMajor, - caps.fwVerMinor, caps.fwVerVendor, caps.fips140_2, caps.cc_eal4); - } - else { - wolfBoot_printf("TPM Init failed! %d\n", rc); - } - -#ifdef WOLFBOOT_TPM_KEYSTORE - memset(&wolftpm_session, 0, sizeof(wolftpm_session)); - -#ifdef WC_RNG_SEED_CB - /* setup callback for RNG seed to use TPM */ - wc_SetSeed_Cb(wolfRNG_GetSeedCB); -#endif - - /* Create a primary storage key - no auth (used for parameter encryption) */ -#ifdef HAVE_ECC - alg = TPM_ALG_ECC; -#elif !defined(NO_RSA) - alg = TPM_ALG_RSA; -#else - alg = TPM_ALG_NULL; -#endif - rc = wolfTPM2_CreateSRK(&wolftpm_dev, &wolftpm_srk, alg, NULL, 0); - if (rc == 0) { - /* Setup a TPM session that can be used for parameter encryption */ - rc = wolfTPM2_StartSession(&wolftpm_dev, &wolftpm_session, &wolftpm_srk, - NULL, TPM_SE_HMAC, TPM_ALG_CFB); - } - if (rc != 0) { - wolfBoot_printf("TPM Create SRK or Start Session error %d (%s)!\n", - rc, wolfTPM2_GetRCString(rc)); - wolfTPM2_UnloadHandle(&wolftpm_dev, &wolftpm_session.handle); - wolfTPM2_UnloadHandle(&wolftpm_dev, &wolftpm_srk.handle); - } -#endif - -#ifdef WOLFBOOT_MEASURED_BOOT - /* hash wolfBoot and extend PCR */ - rc = self_hash(digest); - if (rc == 0) { - rc = measure_boot(digest); - } - if (rc != 0) { - wolfBoot_printf("Error %d performing wolfBoot measurement!\n", rc); - } -#endif - - return rc; -} - -/** - * @brief Deinitialize the TPM2 device. - * - * This function deinitializes the TPM2 device and cleans up any resources. - * - * @return None. - */ -void wolfBoot_tpm2_deinit(void) -{ -#ifdef WOLFBOOT_TPM_KEYSTORE - #if !defined(ARCH_SIM) && !defined(WOLFBOOT_TPM_NO_CHG_PLAT_AUTH) - /* Change platform auth to random value, to prevent application from being - * able to use platform hierarchy. This is defined in section 10 of the - * TCG PC Client Platform specification. - */ - int rc = wolfTPM2_ChangePlatformAuth(&wolftpm_dev, &wolftpm_session); - if (rc != 0) { - wolfBoot_printf("Error %d setting platform auth\n", rc); - } - #endif - wolfTPM2_UnloadHandle(&wolftpm_dev, &wolftpm_session.handle); - wolfTPM2_UnloadHandle(&wolftpm_dev, &wolftpm_srk.handle); -#endif /* WOLFBOOT_TPM_KEYSTORE */ - - wolfTPM2_Cleanup(&wolftpm_dev); -} - -#endif /* WOLFBOOT_TPM */ - /** * @brief Convert a 32-bit integer from little-endian to native byte order. * @@ -1482,8 +1068,15 @@ int wolfBoot_verify_authenticity(struct wolfBoot_image *img) if (key_slot < 0) { return -1; /* Key was not found */ } + + #ifdef WOLFBOOT_TPM_KEYSTORE + if (wolfBoot_check_rot(key_slot, pubkey_hint) != 0) { + return -1; /* TPM root of trust failed! */ + } + #endif #endif - } else { + } + else { return -1; /* Invalid hash size for public key hint */ } image_type_size = get_header(img, HDR_IMG_TYPE, &image_type_buf); @@ -1501,8 +1094,9 @@ int wolfBoot_verify_authenticity(struct wolfBoot_image *img) image_part = image_type & HDR_IMG_TYPE_PART_MASK; /* Check if the key permission mask matches the current partition id */ - if (((1U << image_part) & key_mask) != (1U << image_part)) + if (((1U << image_part) & key_mask) != (1U << image_part)) { return -1; /* Key not allowed to verify this partition id */ + } CONFIRM_MASK_VALID(image_part, key_mask); @@ -1552,51 +1146,22 @@ uint8_t* wolfBoot_peek_image(struct wolfBoot_image *img, uint32_t offset, * @param hint The SHA hash of the public key to search for. * @return The key slot ID if found, -1 if the key was not found. */ -static int keyslot_id_by_sha(const uint8_t *hint) +int keyslot_id_by_sha(const uint8_t *hint) { + int id; #ifdef STAGE1_AUTH /* Override global */ uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE]; #endif -#if defined(WOLFBOOT_TPM) && defined(WOLFBOOT_TPM_KEYSTORE) - /* use public key hash (hint) */ - int rc; - WOLFTPM2_NV nv; - word32 digestSz = (word32)TPM2_GetHashDigestSize(WOLFBOOT_TPM_HASH_ALG); - XMEMSET(&nv, 0, sizeof(nv)); - nv.handle.hndl = WOLFBOOT_TPM_KEYSTORE_NV_INDEX; - -#ifdef WOLFBOOT_TPM_KEYSTORE_AUTH - nv.handle.auth.size = (UINT16)strlen(WOLFBOOT_TPM_KEYSTORE_AUTH); - memcpy(nv.handle.auth.buffer, WOLFBOOT_TPM_KEYSTORE_AUTH, nv.handle.auth.size); -#endif - - rc = wolfTPM2_NVReadAuth(&wolftpm_dev, &nv, WOLFBOOT_TPM_KEYSTORE_NV_INDEX, - digest, &digestSz, 0); - if (rc == 0 && memcmp(digest, hint, WOLFBOOT_SHA_DIGEST_SIZE) == 0) { - #ifdef DEBUG_WOLFTPM - wolfBoot_printf("TPM Root of Trust valid\n"); - #endif - return 0; - } - else { - #ifdef DEBUG_WOLFTPM - wolfBoot_printf("TPM Root of Trust failed! %d (%s)\n", - rc, wolfTPM2_GetRCString(rc)); - wolfBoot_printf("Expected Hash %d\n", WOLFBOOT_SHA_DIGEST_SIZE); - wolfBoot_PrintBin(hint, WOLFBOOT_SHA_DIGEST_SIZE); - #endif - } -#else - int id = 0; for (id = 0; id < keystore_num_pubkeys(); id++) { key_hash(id, digest); - if (memcmp(digest, hint, WOLFBOOT_SHA_DIGEST_SIZE) == 0) + if (memcmp(digest, hint, WOLFBOOT_SHA_DIGEST_SIZE) == 0) { return id; + } } -#endif return -1; } -#endif +#endif /* !WOLFBOOT_NO_SIGN && !WOLFBOOT_RENESAS_SCEPROTECT */ + #endif /* IMAGE_H_ */ diff --git a/src/loader.c b/src/loader.c index 50d163296..7ee3003a1 100644 --- a/src/loader.c +++ b/src/loader.c @@ -36,6 +36,10 @@ #include "uart_flash.h" #include "wolfboot/wolfboot.h" +#ifdef WOLFBOOT_TPM +#include "tpm.h" +#endif + #ifdef RAM_CODE /** * @brief Start address of the text section in RAM code. diff --git a/src/tpm.c b/src/tpm.c new file mode 100644 index 000000000..120e4e232 --- /dev/null +++ b/src/tpm.c @@ -0,0 +1,1035 @@ +/* tpm.c + * + * Copyright (C) 2023 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +/** + * @file tpm.c + * @brief This file contains functions related to TPM handling. + */ + +#ifdef WOLFBOOT_TPM + +#include + +#include "image.h" +#include "printf.h" +#include "tpm.h" +#include "wolftpm/tpm2_tis.h" /* for TIS header size and wait state */ + +WOLFTPM2_DEV wolftpm_dev; +#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL) +WOLFTPM2_SESSION wolftpm_session; +WOLFTPM2_KEY wolftpm_srk; +#endif + +#if defined(WOLFBOOT_TPM_KEYSTORE) && !defined(WOLFBOOT_TPM) +#error For TPM keystore please make sure WOLFBOOT_TPM is also defined +#endif + +#if defined(WOLFBOOT_TPM_SEAL) || defined(WOLFBOOT_TPM_KEYSTORE) +void wolfBoot_print_hexstr(const unsigned char* bin, unsigned long sz, + unsigned long maxLine) +{ + unsigned long i; + if (maxLine == 0) maxLine = sz; + for (i = 0; i < sz; i++) { + wolfBoot_printf("%02x", bin[i]); + if (((i+1) % maxLine) == 0 && i+1 != sz) + wolfBoot_printf("\n"); + } + wolfBoot_printf("\n"); +} +#endif + +#if defined(DEBUG_WOLFTPM) || defined(WOLFTPM_DEBUG_IO) || \ + defined(WOLFBOOT_DEBUG_TPM) +#define LINE_LEN 16 +void wolfBoot_print_bin(const uint8_t* buffer, uint32_t length) +{ + uint32_t i, sz; + + if (!buffer) { + wolfBoot_printf("\tNULL\n"); + return; + } + + while (length > 0) { + sz = length; + if (sz > LINE_LEN) + sz = LINE_LEN; + + wolfBoot_printf("\t"); + for (i = 0; i < LINE_LEN; i++) { + if (i < length) + wolfBoot_printf("%02x ", buffer[i]); + else + wolfBoot_printf(" "); + } + wolfBoot_printf("| "); + for (i = 0; i < sz; i++) { + if (buffer[i] > 31 && buffer[i] < 127) + wolfBoot_printf("%c", buffer[i]); + else + wolfBoot_printf("."); + } + wolfBoot_printf("\r\n"); + + buffer += sz; + length -= sz; + } +} +#endif /* WOLFTPM_DEBUG_IO || WOLFBOOT_DEBUG_TPM */ + +#if !defined(ARCH_SIM) && !defined(WOLFTPM_MMIO) +#ifdef WOLFTPM_ADV_IO +static int TPM2_IoCb(TPM2_CTX* ctx, int isRead, uint32_t addr, uint8_t* buf, + word16 size, void* userCtx) +#else + +/** + * @brief TPM2 I/O callback function for communication with TPM2 device. + * + * This function is used as the I/O callback function for communication + * with the TPM2 device. It is called during TPM operations to send and + * receive data from the TPM2 device. + * + * @param ctx The pointer to the TPM2 context. + * @param txBuf The buffer containing data to be sent to the TPM2 device. + * @param rxBuf The buffer to store the received data from the TPM2 device. + * @param xferSz The size of the data to be transferred. + * @param userCtx The user context (not used in this implementation). + * @return The return code from the TPM2 device operation. + */ +static int TPM2_IoCb(TPM2_CTX* ctx, const uint8_t* txBuf, uint8_t* rxBuf, + word16 xferSz, void* userCtx) +#endif +{ + int ret; +#ifdef WOLFTPM_CHECK_WAIT_STATE + int timeout = TPM_SPI_WAIT_RETRY; +#endif +#ifdef WOLFTPM_ADV_IO + uint8_t txBuf[MAX_SPI_FRAMESIZE+TPM_TIS_HEADER_SZ]; + uint8_t rxBuf[MAX_SPI_FRAMESIZE+TPM_TIS_HEADER_SZ]; + int xferSz = TPM_TIS_HEADER_SZ + size; + +#ifdef WOLFTPM_DEBUG_IO + wolfBoot_printf("TPM2_IoCb (Adv): Read %d, Addr %x, Size %d\n", + isRead ? 1 : 0, addr, size); + if (!isRead) { + wolfBoot_print_bin(buf, size); + } +#endif + + /* Build TPM header */ + txBuf[1] = (addr>>16) & 0xFF; + txBuf[2] = (addr>>8) & 0xFF; + txBuf[3] = (addr) & 0xFF; + if (isRead) { + txBuf[0] = TPM_TIS_READ | ((size & 0xFF) - 1); + memset(&txBuf[TPM_TIS_HEADER_SZ], 0, size); + } + else { + txBuf[0] = TPM_TIS_WRITE | ((size & 0xFF) - 1); + memcpy(&txBuf[TPM_TIS_HEADER_SZ], buf, size); + } + memset(rxBuf, 0, sizeof(rxBuf)); +#endif /* WOLFTPM_ADV_IO */ + +#ifdef WOLFTPM_CHECK_WAIT_STATE /* Handle TIS wait states */ + /* Send header - leave CS asserted */ + ret = spi_xfer(SPI_CS_TPM, txBuf, rxBuf, TPM_TIS_HEADER_SZ, + 0x1 /* 1=SPI_XFER_FLAG_CONTINUE */ + ); + + /* Handle wait states */ + while (ret == 0 && + --timeout > 0 && + (rxBuf[TPM_TIS_HEADER_SZ-1] & TPM_TIS_READY_MASK) == 0) + { + /* clock additional uint8_t until 0x01 LSB is set (keep CS asserted) */ + ret = spi_xfer(SPI_CS_TPM, + &txBuf[TPM_TIS_HEADER_SZ-1], + &rxBuf[TPM_TIS_HEADER_SZ-1], 1, + 0x1 /* 1=SPI_XFER_FLAG_CONTINUE */ + ); + } + /* Check for timeout */ + if (ret == 0 && timeout <= 0) { + ret = TPM_RC_FAILURE; + } + + /* Transfer remainder of payload (command / response) */ + if (ret == 0) { + ret = spi_xfer(SPI_CS_TPM, + &txBuf[TPM_TIS_HEADER_SZ], + &rxBuf[TPM_TIS_HEADER_SZ], + xferSz-TPM_TIS_HEADER_SZ, + 0 /* de-assert CS*/ ); + } + /* On error make sure SPI is de-asserted */ + else { + spi_xfer(SPI_CS_TPM, NULL, NULL, 0, 0); + return ret; + } +#else /* Send Entire Message - no wait states */ + ret = spi_xfer(SPI_CS_TPM, txBuf, rxBuf, xferSz, 0); + + #ifdef WOLFTPM_DEBUG_IO + wolfBoot_printf("TPM2_IoCb: Ret %d, Sz %d\n", ret, xferSz); + wolfBoot_print_bin(txBuf, xferSz); + wolfBoot_print_bin(rxBuf, xferSz); + #endif +#endif /* !WOLFTPM_CHECK_WAIT_STATE */ + +#ifdef WOLFTPM_ADV_IO + if (isRead) { + memcpy(buf, &rxBuf[TPM_TIS_HEADER_SZ], size); + #ifdef WOLFTPM_DEBUG_IO + wolfBoot_print_bin(buf, size); + #endif + } +#endif + + return ret; +} +#endif /* !ARCH_SIM && !WOLFTPM_MMIO */ + +#ifdef WOLFBOOT_MEASURED_BOOT + +#ifdef WOLFBOOT_HASH_SHA256 +#include +static int self_sha256(uint8_t *hash) +{ + uintptr_t p = (uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS; + uint32_t sz = (uint32_t)WOLFBOOT_PARTITION_SIZE; + uint32_t blksz, position = 0; + wc_Sha256 sha256_ctx; + + wc_InitSha256(&sha256_ctx); + do { + blksz = WOLFBOOT_SHA_BLOCK_SIZE; + if (position + blksz > sz) + blksz = sz - position; + #if defined(EXT_FLASH) && defined(NO_XIP) + rc = ext_flash_read(p, ext_hash_block, WOLFBOOT_SHA_BLOCK_SIZE); + if (rc != WOLFBOOT_SHA_BLOCK_SIZE) + return -1; + wc_Sha256Update(&sha256_ctx, ext_hash_block, blksz); + #else + wc_Sha256Update(&sha256_ctx, (uint8_t*)p, blksz); + #endif + position += blksz; + p += blksz; + } while (position < sz); + wc_Sha256Final(&sha256_ctx, hash); + + return 0; +} + +#elif defined(WOLFBOOT_HASH_SHA384) +#include +static int self_sha384(uint8_t *hash) +{ + uintptr_t p = (uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS; + uint32_t sz = (uint32_t)WOLFBOOT_PARTITION_SIZE; + uint32_t blksz, position = 0; + wc_Sha384 sha384_ctx; + + wc_InitSha384(&sha384_ctx); + do { + blksz = WOLFBOOT_SHA_BLOCK_SIZE; + if (position + blksz > sz) + blksz = sz - position; + #if defined(EXT_FLASH) && defined(NO_XIP) + rc = ext_flash_read(p, ext_hash_block, WOLFBOOT_SHA_BLOCK_SIZE); + if (rc != WOLFBOOT_SHA_BLOCK_SIZE) + return -1; + wc_Sha384Update(&sha384_ctx, ext_hash_block, blksz); + #else + wc_Sha384Update(&sha384_ctx, (uint8_t*)p, blksz); + #endif + position += blksz; + p += blksz; + } while (position < sz); + wc_Sha384Final(&sha384_ctx, hash); + + return 0; +} +#endif /* HASH type */ + +/** + * @brief Extends a PCR in the TPM with a hash. + * + * Extends a specified PCR's value in the TPM with a given hash. Uses + * TPM2_PCR_Extend. Optionally, if DEBUG_WOLFTPM or WOLFBOOT_DEBUG_TPM defined, + * prints debug info. + * + * @param[in] pcrIndex The PCR Index (0-24 is valid range). + * @param[in] hash Pointer to the hash value to extend into the PCR. + * @param[in] line Line number where the function is called (for debugging). + * @return 0 on success, an error code on failure. + * + */ +int wolfBoot_tpm2_extend(uint8_t pcrIndex, uint8_t* hash, int line) +{ + int rc; +#ifdef WOLFBOOT_DEBUG_TPM + uint8_t digest[WOLFBOOT_TPM_PCR_DIG_SZ]; + int digestSz = 0; +#endif + + /* clear auth session for PCR */ + wolfTPM2_SetAuthPassword(&wolftpm_dev, 0, NULL); + +#ifdef ARCH_SIM + if (pcrIndex >= 16) { + /* reset the PCR for testing */ + wolfTPM2_ResetPCR(&wolftpm_dev, pcrIndex); + } +#endif + + rc = wolfTPM2_ExtendPCR(&wolftpm_dev, pcrIndex, WOLFBOOT_TPM_PCR_ALG, hash, + TPM2_GetHashDigestSize(WOLFBOOT_TPM_PCR_ALG)); +#ifdef DEBUG_WOLFTPM + if (rc == 0) { + wolfBoot_printf("Measured boot: Index %d, Line %d\n", pcrIndex, line); + + #ifdef WOLFBOOT_DEBUG_TPM + rc = wolfTPM2_ReadPCR(&wolftpm_dev, pcrIndex, WOLFBOOT_TPM_PCR_ALG, + digest, &digestSz); + + wolfBoot_printf("PCR %d: Res %d, Digest Sz %d\n", + pcrIndex, rc, digestSz); + wolfBoot_print_bin(digest, digestSz); + #endif + } + else { + wolfBoot_printf("Measure boot failed! Index %d, %x (%s)\n", + pcrIndex, rc, wolfTPM2_GetRCString(rc)); + } +#endif + (void)line; + + return rc; +} +#endif /* WOLFBOOT_MEASURED_BOOT */ + +#ifdef WOLFBOOT_TPM_SEAL +int wolfBoot_get_random(uint8_t* buf, int sz) +{ + return wolfTPM2_GetRandom(&wolftpm_dev, buf, sz); +} + +static int is_zero_digest(uint8_t* buf, size_t sz) +{ + while (sz--) { + if (*buf++) + return 0; + } + return 1; +} + +int wolfBoot_get_pcr_active(uint8_t pcrAlg, uint32_t* pcrMask, uint8_t pcrMax) +{ + int rc = 0; + PCR_Read_In pcrReadIn; + PCR_Read_Out pcrReadOut; + uint8_t pcrIndex, count = 0; + + /* PCR0-15 are best for policy because they cannot be reset manually */ + if (pcrMax == 0) { + #ifdef ARCH_SIM /* allow use of testing PCR's on simulator */ + pcrMax = 16; + #else + pcrMax = 15; + #endif + } + *pcrMask = 0; + +#ifdef WOLFBOOT_DEBUG_TPM + wolfBoot_printf("Getting active PCR's (0-%d)\n", pcrMax); +#endif + for (pcrIndex=0; pcrIndex<=pcrMax; pcrIndex++) { + memset(&pcrReadIn, 0, sizeof(pcrReadIn)); + memset(&pcrReadOut, 0, sizeof(pcrReadOut)); + wolfTPM2_SetupPCRSel(&pcrReadIn.pcrSelectionIn, pcrAlg, pcrIndex); + rc = TPM2_PCR_Read(&pcrReadIn, &pcrReadOut); + if (rc == 0 && !is_zero_digest( + pcrReadOut.pcrValues.digests[0].buffer, + pcrReadOut.pcrValues.digests[0].size)) + { + *pcrMask |= (1 << pcrIndex); + count++; + #ifdef WOLFBOOT_DEBUG_TPM + wolfBoot_printf("PCR %d (counter %d)\n", + pcrIndex, pcrReadOut.pcrUpdateCounter); + wolfBoot_print_hexstr(pcrReadOut.pcrValues.digests[0].buffer, + pcrReadOut.pcrValues.digests[0].size, 0); + #endif + } + } +#ifdef WOLFBOOT_DEBUG_TPM + wolfBoot_printf("Found %d active PCR's (mask 0x%08x)\n", count, *pcrMask); +#endif + return rc; +} + +static void tpm_mask_sel_pcr(uint32_t pcrMask, uint8_t* pcrArray, + uint32_t* pcrArraySz) +{ + int i; + *pcrArraySz = 0; + for (i=0; i= PCR_SELECT_MAX*2) { + break; + } + } + } +} + +int wolfBoot_build_policy(uint8_t pcrAlg, uint32_t pcrMask, + uint8_t* policy, uint32_t* policySz, + uint8_t* policyRef, uint32_t policyRefSz) +{ + int rc, i; + uint8_t pcrArray[PCR_SELECT_MAX*2]; + uint32_t pcrArraySz; + uint8_t digest[WOLFBOOT_TPM_PCR_DIG_SZ]; + uint32_t digestSz = 0; + uint8_t pcrDigest[WOLFBOOT_TPM_PCR_DIG_SZ]; + uint32_t pcrDigestSz = 0; + + /* populate PCR selection array */ + memset(pcrArray, 0, sizeof(pcrArray)); + tpm_mask_sel_pcr(pcrMask, pcrArray, &pcrArraySz); + + /* Create a Policy PCR digest to sign externally */ + memcpy(policy, (uint8_t*)&pcrMask, sizeof(pcrMask)); + *policySz = (uint32_t)sizeof(pcrMask); + + rc = wolfTPM2_PCRGetDigest(&wolftpm_dev, pcrAlg, + pcrArray, pcrArraySz, pcrDigest, &pcrDigestSz); + if (rc == 0) { + #ifdef WOLFBOOT_DEBUG_TPM + wolfBoot_printf("PCR Digest (%d bytes):\n", pcrDigestSz); + wolfBoot_print_hexstr(pcrDigest, pcrDigestSz, 0); + #endif + + /* Build PCR Policy */ + memset(digest, 0, sizeof(digest)); + digestSz = TPM2_GetHashDigestSize(pcrAlg); + rc = wolfTPM2_PolicyPCRMake(pcrAlg, pcrArray, pcrArraySz, + pcrDigest, pcrDigestSz, digest, &digestSz); + } + if (rc == 0) { + /* Add policyRef (if blank just re-hash) */ + rc = wolfTPM2_PolicyRefMake(pcrAlg, digest, &digestSz, + policyRef, policyRefSz); + } + if (rc == 0) { + memcpy(&policy[*policySz], digest, digestSz); + *policySz += digestSz; + } + return rc; +} + +int wolfBoot_get_policy(struct wolfBoot_image* img, + uint8_t** policy, uint16_t* policySz) +{ + int rc = 0; + *policySz = wolfBoot_get_header(img, HDR_POLICY_SIGNATURE, policy); + if (*policySz <= 0) { + uint32_t pcrMask = 0; + TPM_ALG_ID pcrAlg = WOLFBOOT_TPM_PCR_ALG; + + /* Report the status of the PCR's for signing externally */ + wolfBoot_printf("Policy header not found!\n"); + wolfBoot_printf("Generating policy based on active PCR's!\n"); + + /* Discover and print active PCR's */ + rc = wolfBoot_get_pcr_active(pcrAlg, &pcrMask, 0); + if (rc == 0 && pcrMask > 0) { + uint8_t newPolicy[sizeof(pcrMask) + WOLFBOOT_TPM_PCR_DIG_SZ]; + uint32_t newPolicySz = 0; + rc = wolfBoot_build_policy(pcrAlg, pcrMask, newPolicy, &newPolicySz, + NULL, 0); + if (rc == 0) { + wolfBoot_printf("PCR Mask (0x%08x) and " + "PCR Policy Digest (%d bytes):\n", + pcrMask, newPolicySz); + wolfBoot_print_hexstr(newPolicy, newPolicySz, 0); + + wolfBoot_printf("Use this policy with the sign tool " + "(--policy arg) or POLICY_FILE config\n"); + } + else { + wolfBoot_printf("Error building policy! %d\n", rc); + } + } + else { + wolfBoot_printf("No PCR's have been extended!\n"); + } + rc = -TPM_RC_POLICY_FAIL; /* failure */ + } + return rc; +} + +int wolfBoot_load_pubkey(struct wolfBoot_image* img, WOLFTPM2_KEY* pubKey, + TPM_ALG_ID* pAlg) +{ + int rc = 0; + uint32_t key_type; + int key_slot = -1; + uint8_t *hdr; + uint16_t hdrSz; + + *pAlg = TPM_ALG_NULL; + + /* get public key */ + hdrSz = wolfBoot_get_header(img, HDR_PUBKEY, &hdr); + if (hdrSz == WOLFBOOT_SHA_DIGEST_SIZE) + key_slot = keyslot_id_by_sha(hdr); + if (key_slot < 0) + rc = -1; + + if (rc == 0) { + key_type = keystore_get_key_type(key_slot); + hdr = keystore_get_buffer(key_slot); + hdrSz = keystore_get_size(key_slot); + if (hdr == NULL || hdrSz <= 0) + rc = -1; + } + /* Parse public key to TPM public key. Note: this loads as temp handle, + * however we don't use the handle. We still need to unload it. */ + if (rc == 0) { + #if defined(WOLFBOOT_SIGN_ECC256) || \ + defined(WOLFBOOT_SIGN_ECC384) || \ + defined(WOLFBOOT_SIGN_ECC521) + int tpmcurve; + int point_sz = hdrSz/2; + if ( key_type == AUTH_KEY_ECC256) tpmcurve = TPM_ECC_NIST_P256; + else if (key_type == AUTH_KEY_ECC384) tpmcurve = TPM_ECC_NIST_P384; + else if (key_type == AUTH_KEY_ECC521) tpmcurve = TPM_ECC_NIST_P521; + else rc = -1; /* not supported algorithm */ + if (rc == 0) { + *pAlg = TPM_ALG_ECC; + rc = wolfTPM2_LoadEccPublicKey(&wolftpm_dev, pubKey, + tpmcurve, /* Curve */ + hdr, point_sz, /* Public X */ + hdr + point_sz, point_sz /* Public Y */ + ); + } + #elif defined(WOLFBOOT_SIGN_RSA2048) || \ + defined(WOLFBOOT_SIGN_RSA3072) || \ + defined(WOLFBOOT_SIGN_RSA4096) + uint32_t inOutIdx = 0; + const uint8_t*n = NULL, *e = NULL; + uint32_t nSz = 0, eSz = 0; + if (key_type != AUTH_KEY_RSA2048 && key_type != AUTH_KEY_RSA3072 && + key_type != AUTH_KEY_RSA4096) { + rc = -1; + } + if (rc == 0) { + *pAlg = TPM_ALG_RSA; + rc = wc_RsaPublicKeyDecode_ex(hdr, &inOutIdx, hdrSz, + &n, &nSz, /* modulus */ + &e, &eSz /* exponent */ + ); + } + if (rc == 0) { + /* Load public key into TPM */ + ret = wolfTPM2_LoadRsaPublicKey_ex(&wolftpm_dev, pubKey, + n, nSz, *((uint32_t*)e), + TPM_ALG_NULL, WOLFBOOT_TPM_HASH_ALG); + } + #else + rc = -1; /* not supported */ + #endif + } + return rc; +} + +/* The secret is sealed based on a policy authorization from a public key. */ +int wolfBoot_seal_blob(struct wolfBoot_image* img, WOLFTPM2_KEYBLOB* seal_blob, + const uint8_t* secret, int secret_sz) +{ + int rc; + WOLFTPM2_SESSION policy_session; + TPM_ALG_ID pcrAlg = WOLFBOOT_TPM_PCR_ALG; + TPM_ALG_ID alg; + TPMT_PUBLIC template; + WOLFTPM2_KEY authKey; + uint8_t *hdr; + uint16_t hdrSz; + + if (secret == NULL || secret_sz > WOLFBOOT_MAX_SEAL_SZ) { + return -1; + } + + /* make sure we have a HDR_POLICY_SIGNATURE defined */ + rc = wolfBoot_get_policy(img, &hdr, &hdrSz); + if (rc != 0) { + /* Technically we can seal a secret without the signed policy, but it + * can't be unsealed until a signed policy exists. For now consider this + * a failure */ + return rc; + } + + memset(&authKey, 0, sizeof(authKey)); + memset(&template, 0, sizeof(template)); + memset(&policy_session, 0, sizeof(policy_session)); + + /* get public key for policy authorization */ + rc = wolfBoot_load_pubkey(img, &authKey, &alg); + + /* The handle for the public key if not needed, so unload it. + * For seal only a populated TPM2B_PUBLIC is required */ + wolfTPM2_UnloadHandle(&wolftpm_dev, &authKey.handle); + + if (rc == 0) { + /* Setup a TPM session that can be used for parameter encryption */ + rc = wolfTPM2_StartSession(&wolftpm_dev, &policy_session, &wolftpm_srk, + NULL, TPM_SE_POLICY, TPM_ALG_CFB); + } + if (rc == 0) { + /* enable parameter encryption for seal */ + rc = wolfTPM2_SetAuthSession(&wolftpm_dev, 1, &policy_session, + (TPMA_SESSION_decrypt | TPMA_SESSION_encrypt | + TPMA_SESSION_continueSession)); + } + if (rc == 0) { + /* build authorization policy based on public key */ + /* digest here is input and output, must be zero'd */ + uint32_t digestSz = TPM2_GetHashDigestSize(pcrAlg); + memset(template.authPolicy.buffer, 0, digestSz); + rc = wolfTPM2_PolicyAuthorizeMake(pcrAlg, &authKey.pub, + template.authPolicy.buffer, &digestSz, NULL, 0); + template.authPolicy.size = digestSz; + } + if (rc == 0) { + #ifdef WOLFBOOT_DEBUG_TPM + wolfBoot_printf("Policy Authorize Digest (%d bytes):\n", + template.authPolicy.size); + wolfBoot_print_hexstr(template.authPolicy.buffer, + template.authPolicy.size, 0); + #endif + /* Create a new key for sealing using external signing auth */ + wolfTPM2_GetKeyTemplate_KeySeal(&template, pcrAlg); + rc = wolfTPM2_CreateKeySeal_ex(&wolftpm_dev, seal_blob, + &wolftpm_srk.handle, &template, NULL, 0, pcrAlg, NULL, 0, + secret, secret_sz); + } + + wolfTPM2_UnloadHandle(&wolftpm_dev, &policy_session.handle); + + return rc; +} + +/* Index (0-X) determines location in NV from WOLFBOOT_TPM_SEAL_NV_BASE to + * store sealed blob */ +int wolfBoot_seal(struct wolfBoot_image* img, int index, + const uint8_t* secret, int secret_sz) +{ + int rc; + WOLFTPM2_KEYBLOB seal_blob; + memset(&seal_blob, 0, sizeof(seal_blob)); + + rc = wolfBoot_seal_blob(img, &seal_blob, secret, secret_sz); + if (rc == 0) { + #ifdef WOLFBOOT_DEBUG_TPM + wolfBoot_printf("Sealed keyed hash (pub %d, priv %d bytes):\n", + seal_blob.pub.size, seal_blob.priv.size); + //wolfBoot_print_bin((uint8_t*)&seal_blob.pub, (uint32_t)sizeof(seal_blob.pub)); + //wolfBoot_print_bin(seal_blob.priv.buffer, seal_blob.priv.size); + #endif + + /* TODO: store sealed blob in TPM NV */ + (void)index; + //wolfTPM2_GetNvAttributesTemplate + //wolfTPM2_NVCreateAuth + //wolfTPM2_NVWriteAuth + } + return rc; +} + +/* The unseal requires a signed policy from HDR_POLICY_SIGNATURE */ +int wolfBoot_unseal_blob(struct wolfBoot_image* img, WOLFTPM2_KEYBLOB* seal_blob, + uint8_t* secret, int* secret_sz) +{ + int rc, i; + WOLFTPM2_SESSION policy_session; + uint32_t key_type; + int key_slot = -1; + TPM_ALG_ID pcrAlg = WOLFBOOT_TPM_PCR_ALG; + TPM_ALG_ID alg = TPM_ALG_NULL, sigAlg; + TPMT_PUBLIC template; + WOLFTPM2_KEY authKey; + TPMT_TK_VERIFIED checkTicket; + Unseal_In unsealIn; + Unseal_Out unsealOut; + uint8_t *hdr; + uint16_t hdrSz; + uint32_t pcrMask; + uint8_t pcrDigest[WOLFBOOT_TPM_PCR_DIG_SZ]; + uint32_t pcrDigestSz; + uint8_t policyDigest[WOLFBOOT_TPM_PCR_DIG_SZ]; + uint32_t policyDigestSz; + uint8_t pcrArray[PCR_SELECT_MAX*2]; + uint32_t pcrArraySz = 0; + uint8_t* policyRef = NULL; /* optional nonce */ + uint32_t policyRefSz = 0; + + if (secret == NULL || secret_sz == NULL) { + return -1; + } + + *secret_sz = 0; /* init */ + + /* make sure we have a HDR_POLICY_SIGNATURE defined */ + rc = wolfBoot_get_policy(img, &hdr, &hdrSz); + if (rc != 0) { + return rc; + } + + /* extract pcrMask and populate PCR selection array */ + memcpy(&pcrMask, hdr, sizeof(pcrMask)); + memset(pcrArray, 0, sizeof(pcrArray)); + tpm_mask_sel_pcr(pcrMask, pcrArray, &pcrArraySz); + + /* skip to signature */ + hdr += sizeof(pcrMask); + hdrSz -= sizeof(pcrMask); + + memset(&authKey, 0, sizeof(authKey)); + memset(&template, 0, sizeof(template)); + memset(&policy_session, 0, sizeof(policy_session)); + + /* Setup a TPM session that can be used for parameter encryption */ + rc = wolfTPM2_StartSession(&wolftpm_dev, &policy_session, &wolftpm_srk, + NULL, TPM_SE_POLICY, TPM_ALG_CFB); + if (rc == 0) { + /* enable parameter encryption for unseal */ + rc = wolfTPM2_SetAuthSession(&wolftpm_dev, 1, &policy_session, + (TPMA_SESSION_decrypt | TPMA_SESSION_encrypt | + TPMA_SESSION_continueSession)); + } + if (rc == 0) { + /* Get PCR policy digest */ + rc = wolfTPM2_PolicyPCR(&wolftpm_dev, policy_session.handle.hndl, + pcrAlg, pcrArray, pcrArraySz); + } + if (rc == 0) { + pcrDigestSz = (uint32_t)sizeof(pcrDigest); + rc = wolfTPM2_GetPolicyDigest(&wolftpm_dev, policy_session.handle.hndl, + pcrDigest, &pcrDigestSz); + } + if (rc == 0) { + #ifdef WOLFBOOT_DEBUG_TPM + wolfBoot_printf("PCR Policy Digest (%d bytes):\n", pcrDigestSz); + wolfBoot_print_hexstr(pcrDigest, pcrDigestSz, pcrDigestSz); + #endif + + /* Add policyRef (if blank just re-hash) */ + policyDigestSz = pcrDigestSz; + memcpy(policyDigest, pcrDigest, pcrDigestSz); + rc = wolfTPM2_PolicyRefMake(pcrAlg, policyDigest, &policyDigestSz, + policyRef, policyRefSz); + } + if (rc == 0) { + #ifdef WOLFBOOT_DEBUG_TPM + wolfBoot_printf("PCR Policy (%d bytes):\n", policyDigestSz); + wolfBoot_print_hexstr(policyDigest, policyDigestSz, policyDigestSz); + #endif + + /* get public key for policy authorization */ + rc = wolfBoot_load_pubkey(img, &authKey, &alg); + } + if (rc == 0) { + sigAlg = alg == TPM_ALG_RSA ? TPM_ALG_RSASSA : TPM_ALG_ECDSA; + rc = wolfTPM2_VerifyHashTicket(&wolftpm_dev, &authKey, + hdr, hdrSz, policyDigest, policyDigestSz, + sigAlg, pcrAlg, &checkTicket); + } + if (rc == 0) { + #ifdef WOLFBOOT_DEBUG_TPM + wolfBoot_printf("Verify ticket: tag 0x%x, hi 0x%x, digest %d\n", + checkTicket.tag, checkTicket.hierarchy, checkTicket.digest.size); + wolfBoot_print_hexstr(checkTicket.digest.buffer, + checkTicket.digest.size, 32); + #endif + rc = wolfTPM2_PolicyAuthorize(&wolftpm_dev, policy_session.handle.hndl, + &authKey.pub, &checkTicket, pcrDigest, pcrDigestSz, + policyRef, policyRefSz); + } + else { + wolfBoot_printf("Policy signature failed!\n"); + wolfBoot_printf("Expected PCR Mask (0x%08x) and PCR Digest (%d)\n", + pcrMask, pcrDigestSz); + wolfBoot_print_hexstr(pcrDigest, pcrDigestSz, 0); + + wolfBoot_printf("PCR Policy (%d bytes):\n", policyDigestSz); + wolfBoot_print_hexstr(policyDigest, policyDigestSz, policyDigestSz); + } + + /* done with authorization public key */ + wolfTPM2_UnloadHandle(&wolftpm_dev, &authKey.handle); + + if (rc == 0) { + /* load the seal blob */ + rc = wolfTPM2_LoadKey(&wolftpm_dev, seal_blob, &wolftpm_srk.handle); + } + if (rc == 0) { + #ifdef WOLFBOOT_DEBUG_TPM + wolfBoot_printf("Loaded seal blob to 0x%x\n", + (uint32_t)seal_blob->handle.hndl); + #endif + wolfTPM2_SetAuthHandle(&wolftpm_dev, 0, &seal_blob->handle); + + /* unseal */ + unsealIn.itemHandle = seal_blob->handle.hndl; + rc = TPM2_Unseal(&unsealIn, &unsealOut); + } + if (rc == 0) { + *secret_sz = unsealOut.outData.size; + memcpy(secret, unsealOut.outData.buffer, *secret_sz); + } + + wolfTPM2_UnloadHandle(&wolftpm_dev, &seal_blob->handle); + wolfTPM2_UnloadHandle(&wolftpm_dev, &policy_session.handle); + + return rc; +} + +int wolfBoot_unseal(struct wolfBoot_image* img, int index, uint8_t* secret, int* secret_sz) +{ + int rc; + WOLFTPM2_KEYBLOB seal_blob; + memset(&seal_blob, 0, sizeof(seal_blob)); + + /* TODO: get sealed blob in TPM NV */ + (void)index; + //wolfTPM2_NVReadPublic + //wolfTPM2_NVReadAuth + + rc = wolfBoot_unseal_blob(img, &seal_blob, secret, secret_sz); + if (rc == 0) { + #ifdef WOLFBOOT_DEBUG_TPM + wolfBoot_printf("Unsealed keyed hash (pub %d, priv %d bytes):\n", + seal_blob.pub.size, seal_blob.priv.size); + //wolfBoot_print_bin((uint8_t*)&seal_blob.pub, (uint32_t)sizeof(seal_blob.pub)); + //wolfBoot_print_bin(seal_blob.priv.buffer, seal_blob.priv.size); + #endif + } + return rc; +} +#endif /* WOLFBOOT_TPM_SEAL */ + +#if (defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL)) && \ + defined(WC_RNG_SEED_CB) +static int wolfRNG_GetSeedCB(OS_Seed* os, uint8_t* seed, uint32_t 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); + } + return rc; +} +#endif + +/** + * @brief Initialize the TPM2 device and retrieve its capabilities. + * + * This function initializes the TPM2 device and retrieves its capabilities. + * + * @return 0 on success, an error code on failure. + */ +int wolfBoot_tpm2_init(void) +{ + int rc; + WOLFTPM2_CAPS caps; +#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL) + TPM_ALG_ID alg; +#endif +#ifdef WOLFBOOT_MEASURED_BOOT + uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE]; +#endif + +#if !defined(ARCH_SIM) && !defined(WOLFTPM_MMIO) + spi_init(0,0); +#endif + +#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL) + memset(&wolftpm_session, 0, sizeof(wolftpm_session)); + memset(&wolftpm_srk, 0, sizeof(wolftpm_srk)); +#endif + + /* Init the TPM2 device */ + /* simulator should use the network connection, not spi */ +#if defined(ARCH_SIM) || defined(WOLFTPM_MMIO) + rc = wolfTPM2_Init(&wolftpm_dev, NULL, NULL); +#else + rc = wolfTPM2_Init(&wolftpm_dev, TPM2_IoCb, NULL); +#endif + if (rc == 0) { + /* Get device capabilities + options */ + rc = wolfTPM2_GetCapabilities(&wolftpm_dev, &caps); + } + if (rc == 0) { + wolfBoot_printf("Mfg %s (%d), Vendor %s, Fw %u.%u (0x%x), " + "FIPS 140-2 %d, CC-EAL4 %d\n", + caps.mfgStr, caps.mfg, caps.vendorStr, caps.fwVerMajor, + caps.fwVerMinor, caps.fwVerVendor, caps.fips140_2, caps.cc_eal4); + } + + + if (rc != 0) { + wolfBoot_printf("TPM Init failed! %d\n", rc); + } + +#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL) + if (rc == 0) { + #ifdef WC_RNG_SEED_CB + /* setup callback for RNG seed to use TPM */ + wc_SetSeed_Cb(wolfRNG_GetSeedCB); + #endif + + /* Create a primary storage key - no auth needed for param enc to work */ + /* Prefer ECC as its faster */ + #ifdef HAVE_ECC + alg = TPM_ALG_ECC; + #elif !defined(NO_RSA) + alg = TPM_ALG_RSA; + #else + alg = TPM_ALG_NULL; + #endif + rc = wolfTPM2_CreateSRK(&wolftpm_dev, &wolftpm_srk, alg, NULL, 0); + if (rc == 0) { + /* Setup a TPM session that can be used for parameter encryption */ + rc = wolfTPM2_StartSession(&wolftpm_dev, &wolftpm_session, + &wolftpm_srk, NULL, TPM_SE_HMAC, TPM_ALG_CFB); + } + if (rc != 0) { + wolfBoot_printf("TPM Create SRK or Session error %d (%s)!\n", + rc, wolfTPM2_GetRCString(rc)); + } + } +#endif /* WOLFBOOT_TPM_KEYSTORE | WOLFBOOT_TPM_SEAL */ + +#ifdef WOLFBOOT_MEASURED_BOOT + /* hash wolfBoot and extend PCR */ + if (rc == 0) { + rc = self_hash(digest); + if (rc == 0) { + rc = measure_boot(digest); + } + if (rc != 0) { + wolfBoot_printf("Error %d performing wolfBoot measurement!\n", rc); + } + } +#endif + + return rc; +} + +/** + * @brief Deinitialize the TPM2 device. + * + * This function deinitializes the TPM2 device and cleans up any resources. + * + * @return None. + */ +void wolfBoot_tpm2_deinit(void) +{ +#ifdef WOLFBOOT_TPM_KEYSTORE + #if !defined(ARCH_SIM) && !defined(WOLFBOOT_TPM_NO_CHG_PLAT_AUTH) + /* Enable parameter encryption for session */ + int rc = wolfTPM2_SetAuthSession(&wolftpm_dev, 0, &wolftpm_session, + (TPMA_SESSION_decrypt | TPMA_SESSION_encrypt | + TPMA_SESSION_continueSession)); + if (rc == 0) { + /* Change platform auth to random value, to prevent application + * from being able to use platform hierarchy. This is defined in + * section 10 of the TCG PC Client Platform specification. */ + rc = wolfTPM2_ChangePlatformAuth(&wolftpm_dev, &wolftpm_session); + } + if (rc != 0) { + wolfBoot_printf("Error %d setting platform auth\n", rc); + } + #endif + wolfTPM2_UnloadHandle(&wolftpm_dev, &wolftpm_session.handle); + wolfTPM2_UnloadHandle(&wolftpm_dev, &wolftpm_srk.handle); +#endif /* WOLFBOOT_TPM_KEYSTORE */ + + wolfTPM2_Cleanup(&wolftpm_dev); +} + + +#ifdef WOLFBOOT_TPM_KEYSTORE +int wolfBoot_check_rot(int key_slot, uint8_t* pubkey_hint) +{ + int rc; + uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE]; + uint32_t digestSz = WOLFBOOT_SHA_DIGEST_SIZE; + WOLFTPM2_NV nv; + + memset(&nv, 0, sizeof(nv)); + + #ifdef WOLFBOOT_TPM_KEYSTORE_AUTH + nv.handle.auth.size = (UINT16)strlen(WOLFBOOT_TPM_KEYSTORE_AUTH); + memcpy(nv.handle.auth.buffer, WOLFBOOT_TPM_KEYSTORE_AUTH, nv.handle.auth.size); + #endif + + /* Enable parameter encryption for session - to protect auth */ + rc = wolfTPM2_SetAuthSession(&wolftpm_dev, 1, &wolftpm_session, + (TPMA_SESSION_decrypt | TPMA_SESSION_encrypt | + TPMA_SESSION_continueSession)); + if (rc == 0) + { + /* find index with matching digest */ + nv.handle.hndl = WOLFBOOT_TPM_KEYSTORE_NV_BASE + key_slot; + rc = wolfTPM2_NVReadAuth(&wolftpm_dev, &nv, nv.handle.hndl, + digest, &digestSz, 0); + if (rc == 0 && digestSz == WOLFBOOT_SHA_DIGEST_SIZE && + memcmp(digest, pubkey_hint, WOLFBOOT_SHA_DIGEST_SIZE) == 0) { + wolfBoot_printf("TPM Root of Trust valid (id %d)\n", key_slot); + } + else { + wolfBoot_printf("TPM Root of Trust failed! %d (%s)\n", + rc, wolfTPM2_GetRCString(rc)); + wolfBoot_printf("Expected Hash %d\n", digestSz); + wolfBoot_print_hexstr(pubkey_hint, digestSz, 0); + if (rc >= 0) rc = -1; /* failure */ + } + } + wolfTPM2_UnsetAuth(&wolftpm_dev, 1); + + return rc; +} +#endif + +#endif /* WOLFBOOT_TPM */ diff --git a/src/update_flash.c b/src/update_flash.c index 5e63b2af6..4fb765db6 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -30,7 +30,7 @@ #include "wolfboot/wolfboot.h" #include "delta.h" #include "printf.h" - +#include "tpm.h" #ifdef RAM_CODE extern unsigned int _start_text; @@ -568,11 +568,89 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) return 0; } + + +#if defined(ARCH_SIM) && defined(WOLFBOOT_TPM) && defined(WOLFBOOT_TPM_SEAL) +int wolfBoot_unlock_disk(void) +{ + int ret; + struct wolfBoot_image img; + WOLFTPM2_KEYBLOB seal_blob; + uint8_t secret[WOLFBOOT_MAX_SEAL_SZ]; + int secretSz; + uint8_t* policy = NULL; + uint16_t policySz = 0; + + memset(&seal_blob, 0, sizeof(seal_blob)); + memset(secret, 0, sizeof(secret)); + + wolfBoot_printf("Unlocking disk...\n"); + + /* check policy */ + ret = wolfBoot_open_image(&img, PART_BOOT); + if (ret == 0) { + ret = wolfBoot_get_policy(&img, &policy, &policySz); + if (ret == -TPM_RC_POLICY_FAIL) { + /* the image is not signed with a policy */ + wolfBoot_printf("Image policy signature missing!\n"); + return ret; + } + } + if (ret == 0) { + /* for testing seal and unseal */ + /* create secret to seal */ + secretSz = 32; + ret = wolfBoot_get_random(secret, secretSz); + if (ret == 0) { + wolfBoot_printf("Sealing %d bytes\n", secretSz); + wolfBoot_print_hexstr(secret, secretSz, 0); + + /* seal new secret */ + ret = wolfBoot_seal_blob(&img, &seal_blob, secret, secretSz); + } + if (ret == 0) { + uint8_t secretCheck[WOLFBOOT_MAX_SEAL_SZ]; + int secretCheckSz = 0; + + /* unseal again to make sure it works */ + memset(secretCheck, 0, sizeof(secretCheck)); + ret = wolfBoot_unseal_blob(&img, &seal_blob, secretCheck, &secretCheckSz); + if (ret == 0) { + if (secretSz != secretCheckSz || memcmp(secret, secretCheck, secretSz) != 0) { + wolfBoot_printf("secret check mismatch!\n"); + ret = -1; + } + } + + wolfBoot_printf("Unsealed %d bytes\n", secretCheckSz); + wolfBoot_print_hexstr(secretCheck, secretCheckSz, 0); + TPM2_ForceZero(secretCheck, sizeof(secretCheck)); + } + } + + if (ret == 0) { + /* TODO: Unlock disk */ + + } + else { + wolfBoot_printf("unlock disk failed! %d (%s)\n", + ret, wolfTPM2_GetRCString(ret)); + } + TPM2_ForceZero(secret, sizeof(secretSz)); + return ret; +} +#endif + + void RAMFUNCTION wolfBoot_start(void) { uint8_t st; struct wolfBoot_image boot; +#if defined(ARCH_SIM) && defined(WOLFBOOT_TPM) && defined(WOLFBOOT_TPM_SEAL) + wolfBoot_unlock_disk(); +#endif + #ifdef RAM_CODE wolfBoot_check_self_update(); #endif diff --git a/test-app/app_sim.c b/test-app/app_sim.c index 3227e84ae..454fb779e 100644 --- a/test-app/app_sim.c +++ b/test-app/app_sim.c @@ -44,23 +44,21 @@ void hal_init(void); int do_cmd(const char *cmd) { - if (strcmp(cmd, "powerfail") == 0) + if (strcmp(cmd, "powerfail") == 0) { return 1; + } if (strcmp(cmd, "get_version") == 0) { printf("%d\n", wolfBoot_current_firmware_version()); return 0; } - if (strcmp(cmd, "success") == 0) { wolfBoot_success(); return 0; } - if (strcmp(cmd, "update_trigger") == 0) { wolfBoot_update_trigger(); return 0; } - if (strcmp(cmd, "reset") == 0) { exit(0); } diff --git a/tools/config.mk b/tools/config.mk index d5870b7bd..096852b28 100644 --- a/tools/config.mk +++ b/tools/config.mk @@ -48,6 +48,10 @@ ifeq ($(ARCH),) PKA?=1 PSOC6_CRYPTO?=1 WOLFTPM?=0 + WOLFBOOT_TPM_VERIFY?=0 + MEASURED_BOOT?=0 + WOLFBOOT_TPM_SEAL?=0 + WOLFBOOT_TPM_KEYSTORE?=0 TZEN?=0 WOLFBOOT_PARTITION_SIZE?=0x20000 WOLFBOOT_SECTOR_SIZE?=0x20000 @@ -73,7 +77,8 @@ CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO MCUXPRESSO_CMSIS FREEDOM_E_SDK STM32CUBE CYPRESS_PDL CYPRESS_CORE_LIB CYPRESS_TARGET_LIB DEBUG VTOR \ CORTEX_M0 CORTEX_M7 CORTEX_M33 NO_ASM EXT_FLASH SPI_FLASH NO_XIP UART_FLASH ALLOW_DOWNGRADE NVM_FLASH_WRITEONCE \ DISABLE_BACKUP WOLFBOOT_VERSION V NO_MPU ENCRYPT FLAGS_HOME FLAGS_INVERT \ - SPMATH SPMATHALL RAM_CODE DUALBANK_SWAP IMAGE_HEADER_SIZE PKA TZEN PSOC6_CRYPTO WOLFTPM \ + SPMATH SPMATHALL RAM_CODE DUALBANK_SWAP IMAGE_HEADER_SIZE PKA TZEN PSOC6_CRYPTO \ + WOLFTPM WOLFBOOT_TPM_VERIFY MEASURED_BOOT WOLFBOOT_TPM_SEAL WOLFBOOT_TPM_KEYSTORE \ WOLFBOOT_PARTITION_SIZE WOLFBOOT_SECTOR_SIZE \ WOLFBOOT_PARTITION_BOOT_ADDRESS WOLFBOOT_PARTITION_UPDATE_ADDRESS \ WOLFBOOT_PARTITION_SWAP_ADDRESS WOLFBOOT_LOAD_ADDRESS \ @@ -83,4 +88,3 @@ CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO ENCRYPT_WITH_CHACHA ENCRYPT_WITH_AES128 ENCRYPT_WITH_AES256 ARMORED \ LMS_LEVELS LMS_HEIGHT LMS_WINTERNITZ \ ELF - diff --git a/tools/keytools/Makefile b/tools/keytools/Makefile index e66b8abe7..1a691bd41 100644 --- a/tools/keytools/Makefile +++ b/tools/keytools/Makefile @@ -26,11 +26,13 @@ ifeq ($(SIGN),LMS) endif # option variables -DEBUG_FLAGS = -g -DDEBUG -DDEBUG_SIGNTOOL -DDEBUG_WOLFSSL -DDEBUG_WOLFSSL_VERBOSE -fsanitize=address +DEBUG_FLAGS = -g -DDEBUG -DDEBUG_SIGNTOOL -DDEBUG_WOLFSSL -DDEBUG_WOLFSSL_VERBOSE +SANITIZE_FLAGS = -fsanitize=address OPTIMIZE = -O2 # Options #CFLAGS+=$(DEBUG_FLAGS) +#CFLAGS+=$(SANITIZE_FLAGS) CFLAGS+=$(OPTIMIZE) ifeq ($(IMAGE_HEADER_SIZE),) diff --git a/tools/keytools/sign.c b/tools/keytools/sign.c index b24273a35..06b6094b7 100644 --- a/tools/keytools/sign.c +++ b/tools/keytools/sign.c @@ -101,7 +101,7 @@ static inline int fp_truncate(FILE *f, size_t len) #include #include -#if defined(WOLFSSL_HAVE_LMS) +#ifdef WOLFSSL_HAVE_LMS #include #ifdef HAVE_LIBLMS #include @@ -199,7 +199,7 @@ static void header_append_tag(uint8_t* header, uint32_t* idx, uint16_t tag, *idx += len; } -#if defined(WOLFSSL_HAVE_LMS) +#ifdef WOLFSSL_HAVE_LMS #include "../lms/lms_common.h" #endif @@ -219,7 +219,7 @@ static union { #ifndef NO_RSA RsaKey rsa; #endif -#if defined(WOLFSSL_HAVE_LMS) +#ifdef WOLFSSL_HAVE_LMS LmsKey lms; #endif } key; @@ -238,7 +238,7 @@ struct cmd_options { const char *key_file; const char *fw_version; const char *signature_file; - const char* policy_signature_file; + const char *policy_file; const char *encrypt_key_file; const char *delta_base_file; char output_image_file[PATH_MAX]; @@ -247,6 +247,7 @@ struct cmd_options { uint32_t pubkey_sz; uint32_t header_sz; uint32_t signature_sz; + uint32_t policy_sz; uint8_t partition_id; }; @@ -691,7 +692,7 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz, break; } } -#if defined(WOLFSSL_HAVE_LMS) +#ifdef WOLFSSL_HAVE_LMS FALL_THROUGH; case SIGN_LMS: ret = -1; @@ -728,7 +729,7 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz, *key_buffer_sz ); } } -#endif /* defined(WOLFSSL_HAVE_LMS) */ +#endif /* WOLFSSL_HAVE_LMS */ break; } /* end switch (CMD.sign) */ @@ -759,6 +760,115 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz, return NULL; } +/* Sign the digest */ +static int sign_digest(int sign, int hash_algo, + uint8_t* signature, uint32_t* signature_sz, + uint8_t* digest, uint32_t digest_sz) +{ + int ret; + WC_RNG rng; + + if ((ret = wc_InitRng(&rng)) != 0) { + return ret; + } + +#ifdef HAVE_ED25519 + if (sign == SIGN_ED25519) { + ret = wc_ed25519_sign_msg(digest, digest_sz, signature, + signature_sz, &key.ed); + } + else +#endif +#ifdef HAVE_ED448 + if (sign == SIGN_ED448) { + ret = wc_ed448_sign_msg(digest, digest_sz, signature, + signature_sz, &key.ed4, NULL, 0); + } + else +#endif +#ifdef HAVE_ECC + if (sign == SIGN_ECC256 || + sign == SIGN_ECC384 || + sign == SIGN_ECC521) + { + mp_int r, s; + int sigSz; + if (sign == SIGN_ECC256) sigSz = 32; + if (sign == SIGN_ECC384) sigSz = 48; + if (sign == SIGN_ECC521) sigSz = 66; + + mp_init(&r); mp_init(&s); + ret = wc_ecc_sign_hash_ex(digest, digest_sz, &rng, &key.ecc, + &r, &s); + mp_to_unsigned_bin(&r, &signature[0]); + mp_to_unsigned_bin(&s, &signature[sigSz]); + mp_clear(&r); mp_clear(&s); + *signature_sz = sigSz*2; + } + else +#endif +#ifndef NO_RSA + if (sign == SIGN_RSA2048 || + sign == SIGN_RSA3072 || + sign == SIGN_RSA4096) + { + #ifndef WC_MAX_ENCODED_DIG_ASN_SZ + #define WC_MAX_ENCODED_DIG_ASN_SZ 9 /* enum(bit or octet) + length(4) */ + #endif + uint8_t buf[WC_MAX_DIGEST_SIZE + WC_MAX_ENCODED_DIG_ASN_SZ]; + uint32_t enchash_sz = digest_sz; + uint8_t* enchash = digest; + if (CMD.sign_wenc) { + /* add ASN.1 signature encoding */ + int hashOID = 0; + if (hash_algo == HASH_SHA256) + hashOID = SHA256h; + else if (hash_algo == HASH_SHA384) + hashOID = SHA384h; + else if (hash_algo == HASH_SHA3) + hashOID = SHA3_384h; + enchash_sz = wc_EncodeSignature(buf, digest, digest_sz, hashOID); + enchash = buf; + } + ret = wc_RsaSSL_Sign(enchash, enchash_sz, signature, *signature_sz, + &key.rsa, &rng); + if (ret > 0) { + *signature_sz = ret; + ret = 0; + } + } + else +#endif +#ifdef WOLFSSL_HAVE_LMS + if (sign == SIGN_LMS) { + /* Set the callbacks, so LMS can update the private key while signing */ + ret = wc_LmsKey_SetWriteCb(&key.lms, lms_write_key); + if (ret == 0) { + ret = wc_LmsKey_SetReadCb(&key.lms, lms_read_key); + } + if (ret == 0) { + ret = wc_LmsKey_SetContext(&key.lms, (void*)CMD.key_file); + } + if (ret == 0) { + ret = wc_LmsKey_Reload(&key.lms); + } + if (ret == 0) { + ret = wc_LmsKey_Sign(&key.lms, signature, signature_sz, digest, + digest_sz); + } + if (ret != 0) { + fprintf(stderr, "error signing with LMS: %d\n", ret); + } + } + else +#endif /* WOLFSSL_HAVE_LMS */ + { + ret = NOT_COMPILED_IN; + } + wc_FreeRng(&rng); + return ret; +} + static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, const char *image_file, const char *outfile, uint32_t delta_base_version, uint32_t patch_len, uint32_t patch_inv_off, @@ -771,7 +881,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, struct stat attrib; uint16_t image_type; uint8_t* signature = NULL; - uint8_t* policy_signature = NULL; + uint8_t* policy = NULL; int ret = -1; uint8_t buf[1024]; uint32_t read_sz, pos; @@ -999,7 +1109,6 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, /* Add image hash to header */ header_append_tag(header, &header_idx, CMD.hash_algo, digest_sz, digest); if (CMD.sign != NO_SIGN) { - WC_RNG rng; /* Add Pubkey Hash to header */ header_append_tag(header, &header_idx, HDR_PUBKEY, digest_sz, buf); @@ -1015,137 +1124,27 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, printf("Digest image %s successfully created.\n", outfile); exit(0); } + /* save max sig size */ + CMD.policy_sz = CMD.signature_sz; - /* Sign the digest */ - ret = NOT_COMPILED_IN; /* default error */ + /* Signing Image */ signature = malloc(CMD.signature_sz); if (signature == NULL) { printf("Signature malloc error!\n"); goto failure; } memset(signature, 0, CMD.signature_sz); - if (CMD.manual_sign && CMD.policy_sign) { - policy_signature = malloc(CMD.signature_sz); - if (policy_signature == NULL) { - printf("Policy Signature malloc error!\n"); - goto failure; - } - memset(policy_signature, 0, CMD.signature_sz); - } + if (!CMD.manual_sign) { printf("Signing the digest...\n"); -#ifdef DEBUG_SIGTOOL +#ifdef DEBUG_SIGNTOOL printf("Digest %d\n", digest_sz); WOLFSSL_BUFFER(digest, digest_sz); #endif - wc_InitRng(&rng); - if (CMD.sign == SIGN_ED25519) { -#ifdef HAVE_ED25519 - ret = wc_ed25519_sign_msg(digest, digest_sz, signature, - &CMD.signature_sz, &key.ed); -#endif - } - else if (CMD.sign == SIGN_ED448) { -#ifdef HAVE_ED448 - ret = wc_ed448_sign_msg(digest, digest_sz, signature, - &CMD.signature_sz, &key.ed4, NULL, 0); -#endif - } -#ifdef HAVE_ECC - else if (CMD.sign == SIGN_ECC256) { - mp_int r, s; - mp_init(&r); mp_init(&s); - ret = wc_ecc_sign_hash_ex(digest, digest_sz, &rng, &key.ecc, - &r, &s); - mp_to_unsigned_bin(&r, &signature[0]); - mp_to_unsigned_bin(&s, &signature[32]); - mp_clear(&r); mp_clear(&s); - } - else if (CMD.sign == SIGN_ECC384) { - mp_int r, s; - mp_init(&r); mp_init(&s); - ret = wc_ecc_sign_hash_ex(digest, digest_sz, &rng, &key.ecc, - &r, &s); - mp_to_unsigned_bin(&r, &signature[0]); - mp_to_unsigned_bin(&s, &signature[48]); - mp_clear(&r); mp_clear(&s); - } - else if (CMD.sign == SIGN_ECC521) { - mp_int r, s; - mp_init(&r); mp_init(&s); - ret = wc_ecc_sign_hash_ex(digest, digest_sz, &rng, &key.ecc, - &r, &s); - mp_to_unsigned_bin(&r, &signature[0]); - mp_to_unsigned_bin(&s, &signature[66]); - mp_clear(&r); mp_clear(&s); - } -#endif - else if (CMD.sign == SIGN_RSA2048 || - CMD.sign == SIGN_RSA3072 || - CMD.sign == SIGN_RSA4096) { - -#ifndef NO_RSA - uint32_t enchash_sz = digest_sz; - uint8_t* enchash = digest; - if (CMD.sign_wenc) { - /* add ASN.1 signature encoding */ - int hashOID = 0; - if (CMD.hash_algo == HASH_SHA256) - hashOID = SHA256h; - else if (CMD.hash_algo == HASH_SHA3) - hashOID = SHA3_384h; - enchash_sz = wc_EncodeSignature(buf, digest, digest_sz, - hashOID); - enchash = buf; - } - ret = wc_RsaSSL_Sign(enchash, enchash_sz, signature, - CMD.signature_sz, - &key.rsa, &rng); - if (ret > 0) { - CMD.signature_sz = ret; - ret = 0; - } -#endif - } - else if (CMD.sign == SIGN_LMS) { -#if defined(WOLFSSL_HAVE_LMS) - /* Set the callbacks, so LMS can update the private key - * while signing. */ - ret = wc_LmsKey_SetWriteCb(&key.lms, lms_write_key); - if (ret != 0) { - fprintf(stderr, "error: wc_LmsKey_SetWriteCb returned %d\n", ret); - goto failure; - } - - ret = wc_LmsKey_SetReadCb(&key.lms, lms_read_key); - if (ret != 0) { - fprintf(stderr, "error: wc_LmsKey_SetReadCb returned %d\n", ret); - goto failure; - } - - ret = wc_LmsKey_SetContext(&key.lms, (void *) CMD.key_file); - if (ret != 0) { - fprintf(stderr, "error: wc_LmsKey_SetContext returned %d\n", ret); - goto failure; - } - - ret = wc_LmsKey_Reload(&key.lms); - if (ret != 0) { - fprintf(stderr, "error: wc_LmsKey_Reload returned %d\n", ret); - goto failure; - } - - ret = wc_LmsKey_Sign(&key.lms, signature, &CMD.signature_sz, digest, - digest_sz); - if (ret != 0) { - fprintf(stderr, "error: wc_LmsKey_Sign returned %d\n", ret); - goto failure; - } -#endif /* defined(WOLFSSL_HAVE_LMS) */ - } - - wc_FreeRng(&rng); + /* Sign the digest */ + ret = sign_digest(CMD.sign, CMD.hash_algo, + signature, &CMD.signature_sz, digest, digest_sz); if (ret != 0) { printf("Signing error %d\n", ret); goto failure; @@ -1161,36 +1160,83 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, } io_sz = (int)fread(signature, 1, CMD.signature_sz, f); fclose(f); - if (io_sz != (int)CMD.signature_sz) { + if (io_sz <= 0) { printf("Error reading file %s\n", CMD.signature_file); goto failure; } + CMD.signature_sz = io_sz; + } + + /* Signing Policy */ + if (CMD.policy_sign) { + /* Policy is always SHA2-256 */ + digest_sz = HDR_SHA256_LEN; + + policy = malloc(CMD.policy_sz + sizeof(uint32_t)); + if (policy == NULL) { + printf("Policy Signature malloc error!\n"); + goto failure; + } + memset(policy, 0, CMD.policy_sz); + + /* open policy file */ + printf("Opening policy file %s\n", CMD.policy_file); + f = fopen(CMD.policy_file, "rb"); + if (f == NULL) { + printf("Open policy digest file %s failed\n", CMD.policy_file); + goto failure; + } + /* policy file starts with 4 byte PCR mask */ + io_sz = (int)fread(policy, 1, sizeof(uint32_t), f); + if (io_sz != sizeof(uint32_t)) { + printf("Error reading file %s\n", CMD.policy_file); + fclose(f); + goto failure; + } - if (CMD.policy_sign) { - printf("Opening signature file %s\n", - CMD.policy_signature_file); + if (!CMD.manual_sign) { + /* in normal sign mode PCR digest (32 bytes) */ + io_sz = (int)fread(digest, 1, digest_sz, f); + fclose(f); + if (io_sz != (int)digest_sz) { + printf("Error reading file %s\n", CMD.policy_file); + goto failure; + } + + printf("Signing the policy digest...\n"); +#ifdef DEBUG_SIGNTOOL + printf("Policy Digest %d\n", digest_sz); + WOLFSSL_BUFFER(digest, digest_sz); +#endif - f = fopen(CMD.policy_signature_file, "rb"); - if (f == NULL) { - printf("Open signature file %s failed\n", - CMD.policy_signature_file); + /* Policy is always SHA2-256 */ + ret = sign_digest(CMD.sign, HASH_SHA256, + policy + sizeof(uint32_t), &CMD.policy_sz, + digest, digest_sz); + if (ret != 0) { + printf("Signing policy error %d\n", ret); goto failure; } - io_sz = (int)fread(policy_signature, 1, CMD.signature_sz, f); + } + else { + /* in manual mode PCR signature */ + io_sz = (int)fread(policy, 1, CMD.policy_sz, f); fclose(f); - if (io_sz != (int)CMD.signature_sz) { - printf("Error reading file %s\n", - CMD.policy_signature_file); + if (io_sz <= 0) { + printf("Error reading file %s\n", CMD.policy_file); goto failure; } + CMD.policy_sz = io_sz; } } + #ifdef DEBUG_SIGNTOOL printf("Signature %d\n", CMD.signature_sz); WOLFSSL_BUFFER(signature, CMD.signature_sz); - if (CMD.manual_sign && CMD.policy_sign) { - printf("Policy Signature %d\n", CMD.signature_sz); - WOLFSSL_BUFFER(policy_signature, CMD.signature_sz); + if (CMD.policy_sign) { + printf("PCR Mask 0x%08x\n", *((uint32_t*)policy)); + printf("Policy Signature %d\n", CMD.policy_sz); + WOLFSSL_BUFFER(policy + sizeof(uint32_t), CMD.policy_sz); } #endif @@ -1198,9 +1244,10 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, header_append_tag(header, &header_idx, HDR_SIGNATURE, CMD.signature_sz, signature); - if (CMD.manual_sign && CMD.policy_sign) { + if (CMD.policy_sign) { + /* Add policy signature to header */ header_append_tag(header, &header_idx, HDR_POLICY_SIGNATURE, - CMD.signature_sz, policy_signature); + CMD.policy_sz + sizeof(uint32_t), policy); } } /* end if(sign != NO_SIGN) */ @@ -1322,6 +1369,8 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, fclose(f2); fclose(f); failure: + if (policy) + free(policy); if (header) free(header); return ret; @@ -1697,7 +1746,7 @@ int main(int argc, char** argv) CMD.sign = SIGN_RSA4096; sign_str = "RSA4096"; } -#if defined(WOLFSSL_HAVE_LMS) +#ifdef WOLFSSL_HAVE_LMS else if (strcmp(argv[i], "--lms") == 0) { CMD.sign = SIGN_LMS; sign_str = "LMS"; @@ -1753,8 +1802,9 @@ int main(int argc, char** argv) CMD.delta = 1; CMD.delta_base_file = argv[++i]; } - else if (strcmp(argv[i], "--policy-signed") == 0) { + else if (strcmp(argv[i], "--policy") == 0) { CMD.policy_sign = 1; + CMD.policy_file = argv[++i]; } else { i--; @@ -1768,10 +1818,6 @@ int main(int argc, char** argv) CMD.fw_version = argv[i+3]; if (CMD.manual_sign) { CMD.signature_file = argv[i+4]; - - if (CMD.policy_sign) { - CMD.policy_signature_file = argv[i+5]; - } } } else { CMD.image_file = argv[i+1]; @@ -1877,7 +1923,7 @@ int main(int argc, char** argv) CMD.header_sz = 1024; CMD.signature_sz = 512; } -#if defined(WOLFSSL_HAVE_LMS) +#ifdef WOLFSSL_HAVE_LMS else if (CMD.sign == SIGN_LMS) { int lms_ret = 0; word32 sig_sz = 0; @@ -1912,7 +1958,7 @@ int main(int argc, char** argv) CMD.header_sz = 2 * sig_sz; CMD.signature_sz = sig_sz; } -#endif /* defined(WOLFSSL_HAVE_LMS) */ +#endif /* WOLFSSL_HAVE_LMS */ if (((CMD.sign != NO_SIGN) && (CMD.signature_sz == 0)) || CMD.header_sz == 0) { @@ -1959,7 +2005,7 @@ int main(int argc, char** argv) wc_FreeRsaKey(&key.rsa); #endif } else if (CMD.sign == SIGN_LMS) { -#if defined(WOLFSSL_HAVE_LMS) +#ifdef WOLFSSL_HAVE_LMS wc_LmsKey_Free(&key.lms); #endif } diff --git a/tools/keytools/user_settings.h b/tools/keytools/user_settings.h index 1981dc2c5..4e4e6c852 100644 --- a/tools/keytools/user_settings.h +++ b/tools/keytools/user_settings.h @@ -101,7 +101,6 @@ #define NO_PWDBASED #define NO_WRITEV #define NO_FILESYSTEM -#define NO_MAIN_DRIVER #define NO_OLD_RNGNAME #define NO_WOLFSSL_DIR #define WOLFSSL_NO_SOCK diff --git a/tools/test-renode.mk b/tools/test-renode.mk index d9c643cb0..6484c03e2 100644 --- a/tools/test-renode.mk +++ b/tools/test-renode.mk @@ -20,23 +20,23 @@ RENODE_BINASSEMBLE=tools/bin-assemble/bin-assemble LMS_OPTS=LMS_LEVELS=2 LMS_HEIGHT=5 LMS_WINTERNITZ=8 WOLFBOOT_SMALL_STACK=0 \ IMAGE_SIGNATURE_SIZE=2644 IMAGE_HEADER_SIZE=5288 -ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/keygen)","") - KEYGEN_TOOL=$(WOLFBOOT_ROOT)/tools/keytools/keygen -else +# python version only supported using +# KEYGEN_TOOL="python3 $(WOLFBOOT_ROOT)/tools/keytools/keygen.py" +ifeq ("$(KEYGEN_TOOL)","") ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/keygen.exe)","") KEYGEN_TOOL=$(WOLFBOOT_ROOT)/tools/keytools/keygen.exe else - KEYGEN_TOOL=python3 $(WOLFBOOT_ROOT)/tools/keytools/keygen.py + KEYGEN_TOOL=$(WOLFBOOT_ROOT)/tools/keytools/keygen endif endif -ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/sign)","") - SIGN_TOOL=$(WOLFBOOT_ROOT)/tools/keytools/sign -else +# python version only supported using +# SIGN_TOOL="python3 $(WOLFBOOT_ROOT)/tools/keytools/sign.py" +ifeq ("$(SIGN_TOOL)","") ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/sign.exe)","") SIGN_TOOL=$(WOLFBOOT_ROOT)/tools/keytools/sign.exe else - SIGN_TOOL=python3 $(WOLFBOOT_ROOT)/tools/keytools/sign.py + SIGN_TOOL=$(WOLFBOOT_ROOT)/tools/keytools/sign endif endif @@ -139,7 +139,7 @@ $(RENODE_UPDATE_FILE): test-app/image.bin FORCE of=$@ bs=1 conv=notrunc ${Q}printf "pBOOT" >> $@ -renode-factory: factory.bin test-app/image.bin $(RENODE_UPDATE_FILE) $(EXPVER) FORCE +renode-factory: factory.bin test-app/image.bin $(RENODE_UPDATE_FILE) $(EXPVER) FORCE ${Q}rm -f $(RENODE_UART) ${Q}$(SIGN_TOOL) $(SIGN_ARGS) test-app/image.bin $(PRIVATE_KEY) 1 ${Q}cp test-app/image_v1_signed.bin $(TMP)/renode-test-v1.bin @@ -346,7 +346,7 @@ renode-corrupted-lms: FORCE make renode-corrupted SIGN=LMS $(LMS_OPTS) renode-boot-time-all: FORCE - tools/scripts/renode-test-all.sh 2>/dev/null |grep "BOOT TIME" + tools/scripts/renode-test-all.sh 2>/dev/null |grep "BOOT TIME" renode-update-all: FORCE ${Q}make keysclean diff --git a/tools/test.mk b/tools/test.mk index c7280e207..463cf5c8f 100644 --- a/tools/test.mk +++ b/tools/test.mk @@ -8,23 +8,23 @@ SPI_OPTIONS=SPI_FLASH=1 WOLFBOOT_PARTITION_SIZE=0x80000 WOLFBOOT_PARTITION_UPDAT SIGN_ARGS= SIGN_ENC_ARGS= -ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/keygen)","") - KEYGEN_TOOL=$(WOLFBOOT_ROOT)/tools/keytools/keygen -else +# python version only supported using +# KEYGEN_TOOL="python3 $(WOLFBOOT_ROOT)/tools/keytools/keygen.py" +ifeq ("$(KEYGEN_TOOL)","") ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/keygen.exe)","") KEYGEN_TOOL=$(WOLFBOOT_ROOT)/tools/keytools/keygen.exe else - KEYGEN_TOOL=python3 $(WOLFBOOT_ROOT)/tools/keytools/keygen.py + KEYGEN_TOOL=$(WOLFBOOT_ROOT)/tools/keytools/keygen endif endif -ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/sign)","") - SIGN_TOOL=$(WOLFBOOT_ROOT)/tools/keytools/sign -else +# python version only supported using +# SIGN_TOOL="python3 $(WOLFBOOT_ROOT)/tools/keytools/sign.py" +ifeq ("$(SIGN_TOOL)","") ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/sign.exe)","") SIGN_TOOL=$(WOLFBOOT_ROOT)/tools/keytools/sign.exe else - SIGN_TOOL=python3 $(WOLFBOOT_ROOT)/tools/keytools/sign.py + SIGN_TOOL=$(WOLFBOOT_ROOT)/tools/keytools/sign endif endif @@ -941,11 +941,11 @@ test-all: clean test-size-all: - make test-size SIGN=NONE LIMIT=4722 + make test-size SIGN=NONE LIMIT=4730 make keysclean make test-size SIGN=ED25519 LIMIT=11398 make keysclean - make test-size SIGN=ECC256 LIMIT=22266 + make test-size SIGN=ECC256 LIMIT=22270 make keysclean make test-size SIGN=ECC256 NO_ASM=1 LIMIT=13702 make keysclean diff --git a/tools/tpm/Makefile b/tools/tpm/Makefile index 482f58fff..b9444d63c 100644 --- a/tools/tpm/Makefile +++ b/tools/tpm/Makefile @@ -13,16 +13,19 @@ WOLFBOOTDIR = ../.. WOLFDIR = $(WOLFBOOTDIR)/lib/wolfssl/ WOLFTPMDIR = $(WOLFBOOTDIR)/lib/wolfTPM/ CFLAGS = -Wall -Wextra -Werror -CFLAGS += -I. -DWOLFSSL_USER_SETTINGS -DWOLFTPM_USER_SETTINGS -I$(WOLFDIR) -I$(WOLFTPMDIR) -I$(WOLFBOOTDIR)/include +CFLAGS += -DWOLFSSL_USER_SETTINGS -DWOLFTPM_USER_SETTINGS -DWOLFBOOT_TPM +CFLAGS += -I. -I$(WOLFDIR) -I$(WOLFTPMDIR) -I$(WOLFBOOTDIR)/include LDFLAGS = OBJDIR = ./ # option variables -DEBUG_FLAGS = -g -DDEBUG -DDEBUG_SIGNTOOL -DDEBUG_WOLFSSL -DDEBUG_WOLFSSL_VERBOSE -fsanitize=address +DEBUG_FLAGS = -g -DDEBUG -DDEBUG_WOLFTPM -DDEBUG_WOLFSSL -DWOLFTPM_DEBUG_VERBOSE +SANITIZE_FLAGS = -fsanitize=address OPTIMIZE = -O2 # Options #CFLAGS+=$(DEBUG_FLAGS) +#CFLAGS+=$(SANITIZE_FLAGS) CFLAGS+=$(OPTIMIZE) ifeq ($(TARGET),sim) @@ -68,11 +71,12 @@ vpath %.c $(WOLFDIR)/wolfcrypt/src/ vpath %.c $(WOLFBOOTDIR)/src/ vpath %.c $(WOLFTPMDIR)/src/ vpath %.c $(WOLFTPMDIR)/hal/ +vpath %.c $(WOLFTPMDIR)/examples/pcr vpath %.c ./ .PHONY: clean all -all: rot +all: rot policy_create pcr_extend pcr_read pcr_reset debug: CFLAGS+=$(DEBUG_FLAGS) debug: all @@ -94,5 +98,21 @@ rot: $(OBJS_VIRT) rot.o @echo "Building Root of Trust (ROT) tool" $(Q)$(LD) -o $@ $@.o $(OBJS_VIRT) $(LDFLAGS) +policy_create: $(OBJS_VIRT) policy_create.o + @echo "Building Policy Creation Tool" + $(Q)$(LD) -o $@ $@.o $(OBJS_VIRT) $(LDFLAGS) + +pcr_extend: $(OBJS_VIRT) $(WOLFTPMDIR)/examples/pcr/extend.o + @echo "Building PCR Extend Tool" + $(Q)$(LD) -o $@ $(WOLFTPMDIR)/examples/pcr/extend.o $(OBJS_VIRT) $(LDFLAGS) + +pcr_read: $(OBJS_VIRT) $(WOLFTPMDIR)/examples/pcr/read_pcr.o + @echo "Building PCR Read Tool" + $(Q)$(LD) -o $@ $(WOLFTPMDIR)/examples/pcr/read_pcr.o $(OBJS_VIRT) $(LDFLAGS) + +pcr_reset: $(OBJS_VIRT) $(WOLFTPMDIR)/examples/pcr/reset.o + @echo "Building PCR Reset Tool" + $(Q)$(LD) -o $@ $(WOLFTPMDIR)/examples/pcr/reset.o $(OBJS_VIRT) $(LDFLAGS) + clean: - rm -f rot *.o + rm -f rot policy_create pcr_extend pcr_read pcr_reset *.o diff --git a/tools/tpm/README.md b/tools/tpm/README.md index 123ba7b57..780b23ebb 100644 --- a/tools/tpm/README.md +++ b/tools/tpm/README.md @@ -4,7 +4,7 @@ Tools to help with TPM setup. For wolfBoot TPM features see (docs/TPM.md)[/docs/TPM.md]. -# Secure Boot Root of Trust (ROT) +## Secure Boot Root of Trust (ROT) Examples: @@ -13,3 +13,18 @@ Examples: * `./tools/tpm/rot -write -lock`: Write and lock the NV * `./tools/tpm/rot -write -sha384`: Write NV using SHA2-384 * `./tools/tpm/rot -write -auth=test`: Use password for NV access + +## Sealing / Unsealing + +A PCR policy is signed with the `tools/keytool/sign` tool using the --policy argument. +The policy file comes from the `tools/tpm/policy_create` tool and contains a 4-byte PCR mask and PCR digest. +The authorization for the sealing/unsealing comes from the signed policy and its public key (stored in `keystore.c`). +Typically PCR's 0-15 are used for boot measurements and sealing/unsealing, since they can only be reset on a reboot. The PCR 16 is used for testing. + +Examples: + +* `./tools/tpm/pcr_reset 16`: Reset PCR 16 +* `./tools/tpm/pcr_extend 16 aaa.bin`: Extend PCR 16 with the SHA2-256 hash of "aaa" +* `./tools/tpm/policy_create -pcr=16 -pcrdigest=eca4e8eda468b8667244ae972b8240d3244ea72341b2bf2383e79c66643bbecc -out=policy.bin`: Create a policy.bin file with 4-byte PCR mask and 32-byte PCR digest. +* `./tools/keytools/sign --ecc256 --policy policy.bin test-app/image.elf wolfboot_signing_private_key.der 1`: Sign the PCR policy and include in the `HDR_POLICY_SIGNATURE` image header. +* `make POLICY_FILE=policy.bin`: This will perform a build and include the policy file argument to the sign tool. This can also go into the .config file. diff --git a/tools/tpm/policy_create.c b/tools/tpm/policy_create.c new file mode 100644 index 000000000..c0ff4e39f --- /dev/null +++ b/tools/tpm/policy_create.c @@ -0,0 +1,273 @@ +/* policy_create.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfTPM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfTPM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Tool for creating a policy digest file that is then signed by the key tool + * and included in the image header using HDR_POLICY_SIGNATURE. + */ + + +#include +#include +#include +#include +#include +#include "keystore.h" +#include "tpm.h" + +#define DEFAULT_PCR 16 + +static void usage(void) +{ + printf("Expected usage:\n"); + printf("./examples/pcr/policy_create [-pcr=/-pcrmask] [-pcrdigest=] [-out=]\n"); + printf("* -pcr=index: SHA2-256 PCR index < 24 (multiple can be supplied) (default %d)\n", DEFAULT_PCR); + printf("* -pcrmask=0x00000000: PCR mask (or -pcr= args)\n"); + printf("* -pcrdigest=hexstr: PCR Digest (default=Read actual PCR's)\n"); + printf("* -out=file: Policy Digest to sign (default policy.bin)\n"); +} + +int writeBin(const char* filename, const uint8_t*buf, word32 bufSz) +{ + int rc = TPM_RC_FAILURE; + + if (filename == NULL || buf == NULL) + return BAD_FUNC_ARG; + + XFILE fp = NULL; + size_t fileSz = 0; + + fp = XFOPEN(filename, "wt"); + if (fp != XBADFILE) { + fileSz = XFWRITE(buf, 1, bufSz, fp); + /* sanity check */ + if (fileSz == (word32)bufSz) { + rc = TPM_RC_SUCCESS; + } + printf("Wrote %d bytes to %s\n", (int)fileSz, filename); + XFCLOSE(fp); + } + return rc; +} + +static signed char hexCharToByte(signed char ch) +{ + signed char ret = (signed char)ch; + if (ret >= '0' && ret <= '9') + ret -= '0'; + else if (ret >= 'A' && ret <= 'F') + ret -= 'A' - 10; + else if (ret >= 'a' && ret <= 'f') + ret -= 'a' - 10; + else + ret = -1; /* error case - return code must be signed */ + return ret; +} +static int hexToByte(const char *hex, unsigned char *output, unsigned long sz) +{ + int outSz = 0; + word32 i; + for (i = 0; i < sz; i+=2) { + signed char ch1, ch2; + ch1 = hexCharToByte(hex[i]); + ch2 = hexCharToByte(hex[i+1]); + if ((ch1 < 0) || (ch2 < 0)) { + return -1; + } + output[outSz++] = (unsigned char)((ch1 << 4) + ch2); + } + return outSz; +} +static void printHexString(const unsigned char* bin, unsigned long sz, + unsigned long maxLine) +{ + unsigned long i; + printf("\t"); + if (maxLine == 0) maxLine = sz; + for (i = 0; i < sz; i++) { + printf("%02x", bin[i]); + if (((i+1) % maxLine) == 0 && i+1 != sz) + printf("\n\t"); + } + printf("\n"); +} + +static void tpm_mask_sel_pcr(uint32_t pcrMask, uint8_t* pcrArray, + uint32_t* pcrArraySz) +{ + int i; + *pcrArraySz = 0; + for (i=0; i= PCR_SELECT_MAX*2) { + break; + } + } + } +} + +int TPM2_PCR_Policy_Create(TPM_ALG_ID pcrAlg, + uint8_t* pcrArray, word32 pcrArraySz, + const char* outFile, + uint8_t* pcrDigest, word32 pcrDigestSz, + uint8_t* policyRef, word32 policyRefSz) +{ + int rc = -1, i; + word32 pcrMask = 0; + byte policy[sizeof(pcrMask) + WC_MAX_DIGEST_SIZE]; /* pcrmask + digest */ + word32 policySz = 0; + byte* digest = &policy[sizeof(pcrMask)]; + word32 digestSz; + + printf("Policy Create Tool\n"); + + /* calculate pcrMask */ + printf("PCR Index(s) (%s): ", TPM2_GetAlgName(pcrAlg)); + for (i = 0; i < (int)pcrArraySz; i++) { + printf("%d ", pcrArray[i]); + pcrMask |= (1 << pcrArray[i]); + } + printf(" (mask 0x%08x)\n", pcrMask); + XMEMSET(policy, 0, sizeof(policy)); + XMEMCPY(policy, &pcrMask, sizeof(pcrMask)); + policySz += (word32)sizeof(pcrMask); + + /* PCR Hash - Use provided hash or read PCR's and get hash */ + if (pcrDigestSz == 0) { + WOLFTPM2_DEV dev; + XMEMSET(&dev, 0, sizeof(WOLFTPM2_DEV)); + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + if (rc == 0) { + rc = wolfTPM2_PCRGetDigest(&dev, pcrAlg, pcrArray, pcrArraySz, + pcrDigest, &pcrDigestSz); + wolfTPM2_Cleanup(&dev); + } + if (rc != TPM_RC_SUCCESS) { + printf("Error getting PCR's! 0x%x: %s\n", rc, TPM2_GetRCString(rc)); + goto exit; + } + } + printf("PCR Digest (%d bytes):\n", pcrDigestSz); + printHexString(pcrDigest, pcrDigestSz, 0); + + /* Build PCR Policy to Sign */ + digestSz = TPM2_GetHashDigestSize(pcrAlg); + /* Note: digest is used an input here to allow chaining policies, + * but for this use-case should be zero'd */ + rc = wolfTPM2_PolicyPCRMake(pcrAlg, pcrArray, pcrArraySz, + pcrDigest, pcrDigestSz, digest, &digestSz); + if (rc == 0) { + /* Add policyRef (if blank just re-hash) */ + rc = wolfTPM2_PolicyRefMake(pcrAlg, digest, &digestSz, + policyRef, policyRefSz); + } + if (rc == 0) { + policySz += digestSz; + + printf("PCR Mask (0x%08x) and PCR Policy Digest (%d bytes):\n", + pcrMask, digestSz); + printHexString(digest, digestSz, 0); + + /* Write pcrMask (4) and digest */ + writeBin(outFile, policy, policySz); + } + +exit: + if (rc != 0) { + printf("Failure 0x%x: %s\n", rc, wolfTPM2_GetRCString(rc)); + } + + return rc; +} + +int main(int argc, char *argv[]) +{ + TPM_ALG_ID pcrAlg = WOLFBOOT_TPM_PCR_ALG; + byte pcrArray[PCR_SELECT_MAX*2]; + word32 pcrArraySz = 0; + const char* outFile = "policy.bin"; + byte pcrDigest[WC_MAX_DIGEST_SIZE]; + word32 pcrDigestSz = 0; + uint8_t* policyRef = NULL; /* optional nonce */ + word32 policyRefSz = 0; + word32 pcrMask = 0; + + if (argc >= 2) { + if (XSTRCMP(argv[1], "-?") == 0 || + XSTRCMP(argv[1], "-h") == 0 || + XSTRCMP(argv[1], "--help") == 0) { + usage(); + return 0; + } + } + while (argc > 1) { + if (XSTRNCMP(argv[argc-1], "-pcr=", XSTRLEN("-pcr=")) == 0) { + const char* pcrStr = argv[argc-1] + XSTRLEN("-pcr="); + byte pcrIndex = (byte)XATOI(pcrStr); + if (pcrIndex > PCR_LAST) { + printf("PCR index is out of range (0-23)\n"); + usage(); + return 0; + } + pcrArray[pcrArraySz++] = pcrIndex; + } + else if (XSTRNCMP(argv[argc-1], "-pcrmask=", XSTRLEN("-pcrmask=")) == 0) { + const char* pcrMaskStr = argv[argc-1] + XSTRLEN("-pcrmask="); + pcrMask = (word32)XSTRTOL(pcrMaskStr, NULL, 0); + } + else if (XSTRNCMP(argv[argc-1], "-pcrdigest=", XSTRLEN("-pcrdigest=")) == 0) { + const char* hashHexStr = argv[argc-1] + XSTRLEN("-pcrdigest="); + int hashHexStrLen = (int)XSTRLEN(hashHexStr); + if (hashHexStrLen > (int)sizeof(pcrDigest)*2+1) + pcrDigestSz = -1; + else + pcrDigestSz = hexToByte(hashHexStr, pcrDigest, hashHexStrLen); + if (pcrDigestSz <= 0) { + fprintf(stderr, "Invalid PCR hash length\n"); + usage(); + return -1; + } + } + else if (XSTRNCMP(argv[argc-1], "-out=", + XSTRLEN("-out=")) == 0) { + outFile = argv[argc-1] + XSTRLEN("-out="); + } + else { + printf("Warning: Unrecognized option: %s\n", argv[argc-1]); + } + argc--; + } + + /* Determine PCR's based on mask or default (if none set) */ + if (pcrArraySz == 0) { + if (pcrMask == 0) { + pcrArray[pcrArraySz++] = DEFAULT_PCR; + } + else { + tpm_mask_sel_pcr(pcrMask, pcrArray, &pcrArraySz); + } + } + + return TPM2_PCR_Policy_Create(pcrAlg, + pcrArray, pcrArraySz, outFile, + pcrDigest, pcrDigestSz, policyRef, policyRefSz); +} diff --git a/tools/tpm/rot.c b/tools/tpm/rot.c index 8a529eb0f..b128ff5db 100644 --- a/tools/tpm/rot.c +++ b/tools/tpm/rot.c @@ -24,7 +24,8 @@ #include #include #include -#include +#include "keystore.h" +#include "tpm.h" #include #include @@ -32,13 +33,11 @@ #include #include -#define TPM2_DEMO_NV_SECURE_ROT_INDEX 0x01400200 - static void usage(void) { printf("Expected usage:\n"); - printf("./tools/tpm/rot [-nvindex] [-write] [-auth] [-sha384] [-lock]\n"); - printf("* -nvindex=[handle] (default 0x%x)\n", TPM2_DEMO_NV_SECURE_ROT_INDEX); + printf("./tools/tpm/rot [-nvbase] [-write] [-auth] [-sha384] [-lock]\n"); + printf("* -nvbase=[handle] (default 0x%x)\n", WOLFBOOT_TPM_KEYSTORE_NV_BASE); printf("* -write: Using keystore.c API's hashes each public key and stores into NV\n"); printf("* -auth=password: Optional password for NV\n"); printf("* -sha384: Use SHA2-384 (default is SHA2-256)\n"); @@ -48,7 +47,7 @@ static void usage(void) printf("\t./tools/tpm/rot -write\n"); } -int TPM2_Boot_SecureROT_Example(TPMI_RH_NV_AUTH authHandle, word32 nvIndex, +static int TPM2_Boot_SecureROT_Example(TPMI_RH_NV_AUTH authHandle, word32 nvBaseIdx, enum wc_HashType hashType, int doWrite, int doLock, const char *authBuf, int authBufSz) { @@ -93,17 +92,17 @@ int TPM2_Boot_SecureROT_Example(TPMI_RH_NV_AUTH authHandle, word32 nvIndex, if (rc != 0) goto exit; printf("NV Auth (%d)\n", authBufSz); - TPM2_PrintBin((const byte*)authBuf, authBufSz); + TPM2_PrintBin((const uint8_t*)authBuf, authBufSz); for (id = 0; id < keystore_num_pubkeys(); id++) { - TPM_HANDLE handle = nvIndex + id; + TPM_HANDLE handle = nvBaseIdx + id; uint32_t keyType = keystore_get_key_type(id); int bufSz = keystore_get_size(id); - byte *buf = keystore_get_buffer(id); + uint8_t*buf = keystore_get_buffer(id); (void)keyType; /* not used */ - printf("Computing keystore has for index %d\n", id); + printf("Computing keystore hash for index %d\n", id); /* hash public key */ digestSz = wc_HashGetDigestSize(hashType); @@ -124,7 +123,7 @@ int TPM2_Boot_SecureROT_Example(TPMI_RH_NV_AUTH authHandle, word32 nvIndex, /* Create NV - NV struct populated */ rc = wolfTPM2_NVCreateAuth(&dev, &parent, &nv, handle, - nvAttributes, digestSz, (const byte*)authBuf, authBufSz); + nvAttributes, digestSz, (const uint8_t*)authBuf, authBufSz); if (rc == TPM_RC_NV_DEFINED) { printf("Warning: NV Index 0x%x already exists!\n", handle); rc = 0; @@ -192,7 +191,7 @@ int main(int argc, char *argv[]) { /* use platform handle to prevent TPM2_Clear from removing */ TPMI_RH_NV_AUTH authHandle = TPM_RH_PLATFORM; - word32 nvIndex = TPM2_DEMO_NV_SECURE_ROT_INDEX; + word32 nvBaseIdx = WOLFBOOT_TPM_KEYSTORE_NV_BASE; int doWrite = 0, doLock = 0; enum wc_HashType hashType = WC_HASH_TYPE_SHA256; const char* authBuf = NULL; @@ -207,17 +206,17 @@ int main(int argc, char *argv[]) } } while (argc > 1) { - if (XSTRNCMP(argv[argc-1], "-nvindex=", XSTRLEN("-nvindex=")) == 0) { - const char* nvIndexStr = argv[argc-1] + XSTRLEN("-nvindex="); - nvIndex = (word32)XSTRTOL(nvIndexStr, NULL, 0); + if (XSTRNCMP(argv[argc-1], "-nvbase=", XSTRLEN("-nvbase=")) == 0) { + const char* nvBaseIdxStr = argv[argc-1] + XSTRLEN("-nvbase="); + nvBaseIdx = (word32)XSTRTOL(nvBaseIdxStr, NULL, 0); if (!(authHandle == TPM_RH_PLATFORM && ( - nvIndex > TPM_20_PLATFORM_MFG_NV_SPACE && - nvIndex < TPM_20_OWNER_NV_SPACE)) && + nvBaseIdx > TPM_20_PLATFORM_MFG_NV_SPACE && + nvBaseIdx < TPM_20_OWNER_NV_SPACE)) && !(authHandle == TPM_RH_OWNER && ( - nvIndex > TPM_20_OWNER_NV_SPACE && - nvIndex < TPM_20_TCG_NV_SPACE))) + nvBaseIdx > TPM_20_OWNER_NV_SPACE && + nvBaseIdx < TPM_20_TCG_NV_SPACE))) { - fprintf(stderr, "Invalid NV Index %s\n", nvIndexStr); + fprintf(stderr, "Invalid NV Index %s\n", nvBaseIdxStr); fprintf(stderr, "\tPlatform Range: 0x%x -> 0x%x\n", TPM_20_PLATFORM_MFG_NV_SPACE, TPM_20_OWNER_NV_SPACE); fprintf(stderr, "\tOwner Range: 0x%x -> 0x%x\n", @@ -247,7 +246,7 @@ int main(int argc, char *argv[]) return TPM2_Boot_SecureROT_Example( authHandle, - nvIndex, + nvBaseIdx, hashType, doWrite, doLock, diff --git a/tools/tpm/user_settings.h b/tools/tpm/user_settings.h index 1cb690d1d..e6f601782 100644 --- a/tools/tpm/user_settings.h +++ b/tools/tpm/user_settings.h @@ -83,7 +83,6 @@ #define NO_DES3 #define NO_PWDBASED #define NO_WRITEV -#define NO_MAIN_DRIVER #define NO_OLD_RNGNAME #define NO_WOLFSSL_DIR #define WOLFSSL_NO_SOCK