[[PageOutline]] See also [http://trac.gateworks.com/wiki/secure_boot Generic Secure Boot Wiki Page] for information on securing the rest of your firmware. = i.MX8M High Assurance Boot (HAB) The i.MX family of processors provides a High Assurance Boot (HAB) feature in the on-chip BOOT ROM responsible for loading the initial program image from the boot media. HAB enables the BOOT ROM to authenticate and/or decrypt the program image by using crypto operations. The HABv4 secure boot feature uses digital signatures to prevent unauthorized code execution during the device boot sequence. This authentication is based on public key cryptography using RSA where the firmware image data is signed offline using a private key and the resulting signed image data is verified on the processor using the corresponding public key hash value programmed into the SoC fuses for establishing the root of trust. See also: - https://elixir.bootlin.com/u-boot/latest/source/doc/imx/habv4/guides/mx8m_spl_secure_boot.txt  - https://elixir.bootlin.com/u-boot/latest/source/doc/imx/habv4/introduction_habv4.txt  - NXP AN4581 - i.MX Secure Boot on HABv4 Supported Devices Terminology:  - CSF: Command Sequence File (generated off-line using the HAB CST)  - CST: Code-Signing Tool  - DCD: Device Configuration Data  - DEK: Data Encryption Key  - HAB: High Assurance Boot  - IVT: Image Vector Table  - SRK: Super Root Key In order to use High Assurance Boot (HAB) features you must have the NXP Code Signing Tool (CST): https://www.nxp.com/webapp/Download?colCode=IMX_CST_TOOL_NEW == i.MX secure boot Boards using U-Boot for boot firmware support using HABv4 authentication for both the SPL and U-Boot stages. The HAB library is a sub-component of the boot ROM on i.MX processors. It is responsible for verifying the digital signatures included as part of the product software and ensures that, when the processor is configured as a secure device, no unauthenticated code is allowed to run. On an 'open' device you can see HAB events which will tell you if the image would pass the authentication process. This is useful to test before you 'close' the device. In General you must:  - Build boot firmware that contains HABv4 support - CONFIG_IMX_HAB=y ('hab_auto_img', 'hab_status' and 'hab_version' cmds) - CONFIG_SPL_LOAD_FIT_ADDRESS=0x44000000  - Create a PKI tree and SRK table via the NXP Code Signing Tool  - Construct boot firmware with a proper Command Sequence File (CSF) (CSF blobs are created with the NXP Code Signing Tool)  - Blow One Time Programmable (OTP) fuses on the target board with public keys  - Flash signed firmware  - Boot and verify no HAB events via 'hab_status' U-Boot command  - Close the device to force trusted boot Detailed Procedure (for Venice) on x86 development host machine with an existing venice bsp build directory on it (VENICE_BSP env var): - this configuration is for using HABv4 for image authentication only on the Gateworks 2023.04 U-Boot branch. If you instead want to use DEK blob encap/decap support you need to to also add a Trusted Execution Environment (TEE) (see below)  1. Creation of Code Signing Key: '''This is an example - read the CST documentation and tailor to your needs'''   a. Retrieve the NXP Code Signing Tool (CST): https://www.nxp.com/webapp/Download?colCode=IMX_CST_TOOL_NEW (Account required on NXP site) b. Unpack the CST : {{{#!bash tar xvf cst-3.3.2.tgz cd cst-3.3.2/keys }}}   c. Create a text file named "serial", which contains 8 digits. OpenSSL uses the contents of this file for the 'certificate serial numbers'. {{{#!bash echo "12345678" > serial }}}   d.  Create a text file named "key_pass.txt which contains your pass phrase that will protect the HAB code signing private keys. The format is the first pass phrase repeated on the first and second lines: {{{#!bash PASS=mypassphrase printf "$PASS\n$PASS" > key_pass.txt }}}   e. Create the signature keys (PKI tree) with hab4_pki_tree.sh (Must do this in the keys dir as the script hard-codes a relative path to certs) {{{#!bash ./hab4_pki_tree.sh ... Do you want to use an existing CA key (y/n)?: n Select the key type (possible values: rsa, rsa-pss, ecc)?: rsa Enter key length in bits for PKI tree: 4096 Enter PKI tree duration (years): 10 How many Super Root Keys should be generated? 4 Do you want the SRK certificates to have the CA flag set? (y/n)?: y ... }}}    * this creates the files in the ../crts directory which you can archive away as your 'PKI tree'  f. Create the fuse table and binary (to be programmed to IMX OPT fuse blocks) using the SRK*_ca_crt.pem files created in the crts dir with srktool: {{{#!bash cd ../crts ../linux64/bin/srktool -h 4 -t SRK_1_2_3_4_table.bin -e SRK_1_2_3_4_fuse.bin -d sha256 -c ./SRK1_sha256_4096_65537_v3_ca_crt.pem,./SRK2_sha256_4096_65537_v3_ca_crt.pem,./SRK3_sha256_4096_65537_v3_ca_crt.pem,./SRK4_sha256_4096_65537_v3_ca_crt.pem }}}   * creates SRK_1_2_3_4_table.bin SRK_1_2_3_4_fuse.bin and   g. Use hexdump to obtain the fuse table (8x 32bit fuse values) in the correct endianness for programming with u-boot 'fuse prog': {{{#!bash $ hexdump -e '/4 "0x"' -e '/4 "%X""\n"' < SRK_1_2_3_4_fuse.bin 0xDCE644DB 0x3900ABA 0x1D00ECF6 0xC4EE5E23 0x5BCA8A8 0x75B0AB86 0xF88753CC 0xDB9B5895 }}} * Note the above fuse values will differ per your serial/passphrase 2. Build U-boot with HABv4 enabled and a single DTB: * Pre-requisite: Build Venice BSP using this link here: [wiki:venice/bsp] * Export VENICE_BSP variable to your directory: {{{export VENICE_BSP=/path/to/your/bsp/directory}}} {{{#!bash # checkout a fresh u-boot git clone https://github.com/Gateworks/uboot-venice.git cd u-boot # setup cross toolchain environment (ie source setup-environment in Venice BSP dir) export PATH=$VENICE_BSP/buildroot/output/host/bin:$PATH export CROSS_COMPILE="aarch64-linux-" export ARCH=arm64 # copy necessary artifacts from bsp cp $VENICE_BSP/u-boot/lpddr4*.bin . # DDR firmware cp $VENICE_BSP/atf/build/imx8mm/release/bl31.bin . # ATF # configure for venice board make imx8mm_venice_defconfig make menuconfig # select CONFIG_IMX_HAB=y and CONFIG_SPL_LOAD_FIT_ADDRESS=0x44000000 make flash.bin }}} - for clarity here are the differences in defconfig: {{{#!bash $ make savedefconfig && diff defconfig configs/imx8mm_venice_defconfig scripts/kconfig/conf --savedefconfig=defconfig Kconfig 21,22d20 < CONFIG_IMX_HAB=y < # CONFIG_CMD_DEKBLOB is not set 31d28 < CONFIG_SPL_LOAD_FIT_ADDRESS=0x44000000 }}}  3. create a signed_flash.bin {{{#!bash # setup env to point to the CST export CST_DIR=/usr/src/nxp/cst-3.3.2/ export CST_BIN=$CST_DIR/linux64/bin/cst export CSF_KEY=$CST_DIR/crts/CSF1_1_sha256_4096_65537_v3_usr_crt.pem export IMG_KEY=$CST_DIR/crts/IMG1_1_sha256_4096_65537_v3_usr_crt.pem export SRK_TABLE=$CST_DIR/crts/SRK_1_2_3_4_table.bin export PATH=$CST_DIR/linux64/bin:$PATH # sign flash.bin /bin/sh doc/imx/habv4/csf_examples/mx8m/csf.sh # create a JTAG image (if needed) using one of the following (dependent on which SoC you are using) mkimage_jtag --emmc -s --partconf=boot0 \ flash.bin@boot0:erase_none:66-8192 > signed_u-boot_spl-imx8mm.bin # imx8mm emmc boot0 partition mkimage_jtag --emmc -s --partconf=boot0 \ flash.bin@boot0:erase_none:0-8192 > signed_u-boot_spl-imx8mp.bin # imx8mp/imx8mn emmc boot0 partition }}} 4. Program signed firmware image: {{{#!bash jtag_usbv4 -p signed_u-boot_spl-imx8mm.bin }}} * Booting this would look something like the following: {{{#!bash U-Boot SPL 2023.04-00034-g1f567dfbe119 (Jun 23 2023 - 15:53:20 -0700) GSCv3 : v61 0x1d6f RST:VIN Thermal protection:disabled RTC : 1970-01-01 0:00:31 UTC Model : GW7200-01-B1F Serial : 935180 MFGDate : 04-05-2023 PMIC : MP5416 (IMX8MM) DRAM : LPDDR4 4 GiB 3000MT/s 1500MHz Failed to initialize caam_jr: -19 WDT: Started watchdog@30280000 with servicing every 1000ms (60s timeout) Trying to boot from eMMC hab fuse not enabled Authenticate image from DDR location 0x44000000... DTB : imx8mm-venice-gw72xx-0x NOTICE: BL31: v2.4(release):f884ad7b0ba2 NOTICE: BL31: Built : 13:06:09, Oct 20 2021 U-Boot 2023.04-00034-g1f567dfbe119 (Jun 23 2023 - 15:53:20 -0700) }}} - Note the 'hab fuse not enabled' message which means the SEC_CONFIG[1] fuse is not blown and the device is not locked - Note the 'Authenticate image from DDR location' messages which shows that image authentication is able to be used  5. Program SRK Hash fuses from Step 1 into IMX OTP (using U-Boot and the keys from fuse bin) {{{#!bash fuse prog -y 6 0 0xDCE644DB fuse prog -y 6 1 0x3900ABA fuse prog -y 6 2 0x1D00ECF6 fuse prog -y 6 3 0xC4EE5E23 fuse prog -y 7 0 0x5BCA8A8 fuse prog -y 7 1 0x75B0AB86 fuse prog -y 7 2 0xF88753CC fuse prog -y 7 3 0xDB9B5895 }}} * **Do not use the above fuse values - use values generated above from your serial/passphrase** * **OTP fuses can only be programmed once - be careful to use the correct values**   6. Boot it again and verify no HAB events: {{{#!bash u-boot=> hab_status Secure boot disabled HAB Configuration: 0xf0, HAB State: 0x66 No HAB Events Found! }}}  7 Close the device (lock it down!) - this step is irreversible, make sure there are no HAB events from the prior step {{{#!bash u-boot=> fuse prog -y 1 3 0x2000000 }}} * This sets the SEC_CONFIG[1] fuse on the i.MX8M and once done the processor will not load an image that has not been signed using the correct PKI tree For more info see: - https://elixir.bootlin.com/u-boot/latest/source/doc/imx/habv4/introduction_habv4.txt - https://elixir.bootlin.com/u-boot/latest/source/doc/imx/habv4/guides/mx8m_spl_secure_boot.txt [=#tee] == Trusted Execution Environment (TEE) The Trusted Execution Environment (TEE) is a set of specifications published by the [http://www.globalplatform.org GlobalPlatform association]. The purpose of the TEE is to provide a safe environment within the application processor for developing and executing secure applications. We call an application processor a system running a Rich OS like Android or Linux. A Rich environment represents a huge amount of code. It is open to third-party applications and it is an open ecosystem: it makes a Rich OS hard to audit. It is prone to bugs/vulnerability, which may compromise the security and integrity of the entire system. The TEE offers another level of protection against attacks from the rich OS. The TEE is only open to trusted partners, which makes it easier to audit. It executes only trusted and authorized software. All sensitive data are protected from the rest of the application processor and from the outside world. Many modern devices make use of a Trusted Execution Environment, including smartphones, set-top-boxes, game consoles and Smart TVs. Some example use cases of a TEE: - biometric authentication (ie facial, fingerprint, voice recognition) code and sensitive data - e-commerce digital wallet code and sensitive data - DRM credentials [=#optee] === Open Portable Trusted Execution Environment (OP-TEE) OP-TEE (Open Portable Trusted Execution Environment) is an open-source TEE designed as a companion to a non-secure Linux kernel running on ARM Cortex-A cores using ARM's !TrustZone technology. The TEE relies on the Arm !TrustZone technology. The !TrustZone is a system-on-chip security feature available on most Arm Cortex A/M processors. It provides a strict hardware isolation between the secure world (TEE) and the normal world (REE). This technology allows each physical processor core to provide two virtual cores: one for the normal world and one for the secure world. OP-TEE is an open source stack of the Trusted Execution Environment which includes: * OP-TEE OS: Trusted side of the TEE * OP-TEE Client: Normal world client side of the TEE * OP-TEE Test (or xtest): OP-TEE Test Suite The OP-TEE project is developed and maintained by Linaro under BSD 2-Clause. The source code is available at https:// github.com/OP-TEE. This stack supports Arm-v7 and Arm-v8 architectures. The TEE exposes its features through a tandem operation between a Client Application and a Trusted Application. The client application runs in the Rich OS and always initiates the communication with the Trusted Application that runs in the Trusted OS. The Client application interacts with the TEE through the TEE client API interface. The Secure Application interacts with the TEE Core through the TEE Internal API. OP-TEE is a Trusted Execution Environment (TEE) designed as a companion to a non-secure Linux kernel running on Arm cores using the !TrustZone technology. OP-TEE implements [https://optee.readthedocs.io/en/latest/architecture/globalplatform_api.html#tee-internal-core-api TEE Internal Core API v1.1.x] which is the API exposed to Trusted Applications and the [https://optee.readthedocs.io/en/latest/architecture/globalplatform_api.html#tee-client-api TEE Client API v1.0], which is the API describing how to communicate with a TEE. Those APIs are defined in the [https://optee.readthedocs.io/en/latest/architecture/globalplatform_api.html#globalplatform-api GlobalPlatform API] specifications. The non-secure OS is referred to as the Rich Execution Environment (REE) in TEE specifications. It is typically a Linux OS flavor as a GNU/Linux distribution or the AOSP. OP-TEE is designed primarily to rely on the Arm !TrustZone technology as the underlying hardware isolation mechanism. However, it has been structured to be compatible with any isolation technology suitable for the TEE concept and goals, such as running as a virtual machine or on a dedicated CPU. The main design goals for OP-TEE are: * Isolation - the TEE provides isolation from the non-secure OS and protects the loaded Trusted Applications (TAs) from each other using underlying hardware support, * Small footprint - the TEE should remain small enough to reside in a reasonable amount of on-chip memory as found on Arm based systems, * Portability - the TEE aims at being easily pluggable to different architectures and available HW and has to support various setups such as multiple client OSes or multiple TEEs. For more info:  - see - see http://optee.readthedocs.io  - https://www.nxp.com/docs/en/user-guide/IMX_PORTING_GUIDE.pdf === OP-TEE on venice In general the following things need to be done to use OP-TEE in the 'SPL -> ATF -> OP-TEE -> U-Boot -> Linux' boot flow: * build ATF (bl31.bin) with OP-TEE support: - Add 'SPD=opteed' to the env to use a Secure Payload Dispatcher (SPD) - Add 'BL32_BASE=' to the env to tell the ATF (BL31) where TEE is in memory (BL32) * build TEE (tee.bin): - PLATFORM=imx - PLATFORM_FLAVOR=mx8mmevk|mx8mnevk|mv8mpevk - Use one of these depending on which SOC (imx8mm/imx8mn/imx8mp) you are using. These define CFG_UART_BASE and some memory config which we will override to suit our needs - CFG_DDR_SIZE= - Specify the DRAM size of the board you are building for. For example 1GiB=0x40000000, 2GiB=0x80000000, 4GiB=0x10000000 - CFG_CORE_LARGE_PHYS_ADDR= - n for 3GiB or less DRAM, and y for larger than 3GiB DRAM - CFG_CORE_ARM64_PA_BITS= - 32 for 3GiB or less DRAM, and 36 for larger than 3GiB DRAM - CFG_TZDRAM_START= - the (link/load) address where TEE should run at * build u-Boot with the following configuration: - CONFIG_IMX_HAB=y - CONFIG_CMD_DEKBLOB=y - CONFIG_SPL_LOAD_FIT_ADDRESS=0x44000000 - CONFIG_OPTEE=y - CONFIG_OPTEE_LOAD_ADDRESS= the (link/load) address for TEE that should match BL32_BASE for ATF and CFG_TZDRAM_START for OPTEE Note that you have to tailor the firmware specifically for the DRAM size of your board and that it is very important to ensure that BL32_BASE (used when building ATF), CFG_TZDRAM_START (used when building OPTEE), and CONFIG_OPTEE_LOAD_ADDRESS (U-Boot's .config file) all match. This address should be the top 32MiB of DRAM but because U-Boot is loading this from its FIT image into DRAM it needs to be a 32bit address and should be adjusted down if you are on a board with 4GiB: - 1GiB DRAM use 0x7e000000 - 2GiB DRAM use 0xbe000000 - 3GiB or larger use 0xfe000000 Here are the detailed steps to build secure boot firmware for imx8mm: 1. setup cross compile environment {{{#!bash # setup cross compile environment - here we will use the venice bsp toolchain/config cd venice/bsp . ./setup-environment }}} 1. setup additional SOC and Board specific environment: - for imx8mm: {{{#!bash export PLATFORM_FLAVOR=mx8mmevk # used for OPTEE build export PLAT=imx8mm # used for ATF build }}} - for imx8mp: {{{#!bash export PLATFORM_FLAVOR=mx8mpevk # used for OPTEE build export PLAT=imx8mp # used for ATF build }}} - for 1GiB DRAM boards: {{{#!bash export CFG_DDR_SIZE=0x40000000 export CFG_CORE_LARGE_PHYS_ADDR=n export CFG_CORE_ARM64_PA_BITS=32 export CFG_TZDRAM_START=0x7f000000 }}} - for 2GiB DRAM boards: {{{#!bash export CFG_DDR_SIZE=0x80000000 export CFG_CORE_LARGE_PHYS_ADDR=n export CFG_CORE_ARM64_PA_BITS=32 export CFG_TZDRAM_START=0xbf000000 }}} - for 4GiB DRAM boards: {{{#!bash export CFG_DDR_SIZE=0x100000000 export CFG_CORE_LARGE_PHYS_ADDR=y export CFG_CORE_ARM64_PA_BITS=36 export CFG_TZDRAM_START=0xfe000000 }}} 1. Create directories: We will create a 'secure-boot' directory containing u-boot, atf, and optee: {{{#!bash git clone https://github.com/Gateworks/uboot-venice.git secure-boot git clone https://github.com/nxp-imx/imx-optee-os -b lf-6.1.1_1.0.0 secure-boot/tee git clone http://github.com/Gateworks/atf-venice -b lf_v2.6 secure-boot/atf cd secure-boot }}} 1. Get IMX DDR training firmware: {{{#!bash wget https://www.nxp.com/lgfiles/NMG/MAD/YOCTO/firmware-imx-8.10.bin /bin/sh firmware-imx-8.10.bin cp firmware-imx-8.10/firmware/ddr/synopsys/lpddr4*.bin . }}} 1. Build OP-TEE {{{#!bash make -j8 -C tee \ ARCH=arm \ CROSS_COMPILE64=$CROSS_COMPILE \ CFG_TEE_CORE_LOG_LEVEL=2 \ PLATFORM=imx \ PLATFORM_FLAVOR=$PLATFORM_FLAVOR \ O=out && ${CROSS_COMPILE}objcopy -O binary tee/out/core/tee.elf ./tee.bin }}} 1. Build ATF: {{{#!bash make -j8 -C atf SPD=opteed PLAT=$PLAT BL32_BASE=$CFG_TZDRAM_START && \ cp atf/build/$PLAT/release/bl31.bin ./bl31.bin }}} 1. Build U-Boot: {{{#!bash # start with standard venice defconfig make imx8mm_venice_defconfig # choose imx8mm/imx8mn/imx8mp depending on board SOC # enable IMX_HAB/OPTEE make menuconfig # search for (with /) and set CONFIG_IMX_HAB=y CONFIG_CMD_DEKBLOB=y CONFIG_SPL_LOAD_FIT_ADDRESS=0x44000000 CONFIG_OPTEE=y and CONFIG_OPTEE_LOAD_ADDRESS= set to the value of CFG_TZDRAM_START make -j8 flash.bin }}} - for clarity here are the differences in defconfig for imx8mm with 4GB configuration: {{{#!bash $ make savedefconfig && diff defconfig configs/imx8mm_venice_defconfig scripts/kconfig/conf --savedefconfig=defconfig Kconfig 21d20 < CONFIG_IMX_HAB=y 30d28 < CONFIG_SPL_LOAD_FIT_ADDRESS=0x44000000 139d136 < CONFIG_OPTEE_LOAD_ADDRESS=0xfe000000 161d157 < # CONFIG_OPTEE_LIB is not set }}} 1. Sign it: {{{#!bash # setup env to point to the CST export CST_DIR=/usr/src/nxp/cst-3.3.2/ export CST_BIN=$CST_DIR/linux64/bin/cst export CSF_KEY=$CST_DIR/crts/CSF1_1_sha256_4096_65537_v3_usr_crt.pem export IMG_KEY=$CST_DIR/crts/IMG1_1_sha256_4096_65537_v3_usr_crt.pem export SRK_TABLE=$CST_DIR/crts/SRK_1_2_3_4_table.bin export PATH=$CST_DIR/linux64/bin:$PATH # sign flash.bin /bin/sh doc/imx/habv4/csf_examples/mx8m/csf.sh # create a JTAG image (if needed) using one of the following depending on your SoC mkimage_jtag --emmc -s --partconf=boot0 \ flash.bin@boot0:erase_all:66-8192 > signed_u-boot_spl-imx8mm.bin # imx8mm emmc boot0 partition mkimage_jtag --emmc -s --partconf=boot0 \ flash.bin@boot0:erase_all:0-8192 > signed_u-boot_spl-imx8mp.bin # imx8mp emmc boot0 partition mkimage_jtag --emmc -s --partconf=boot0 \ flash.bin@boot0:erase_all:0-8192 > signed_u-boot_spl-imx8mn.bin # imx8mn emmc boot0 partition }}} == HABv4 encrypted boot architecture The IMX HABv4 also provides an extra optional security operation by using cryptography (AES-CCM) to obscure the boot image so it can not be seen or used by unauthorized users. Encrypted boot adds an extra layer of security to the boot sequence using cryptographic techniques to obscure the bootloader data (which can be extended to the entire firmware image)so that it can not be seen or used by unauthorized users. This mechanism protects and conceals the bootloader code residing in flash. The Data Encryption Key (DEK) is an AES key used to encrypt the boot image (via the Code Signing Tool) and decrypt the boot image (using the DEK blob appended to the image). The DEK blob is used as a security layer to wrap and store the DEK off-chip which is unique to the chip that generated the blob. Generation of the DEK blob that gets appended to your image must be done on the IMX via the U-Boot dek_blob command which is enabled with CONFIG_CMD_DEKBLOB=y. References:  - https://elixir.bootlin.com/u-boot/latest/source/doc/imx/habv4/introduction_habv4.txt  - https://elixir.bootlin.com/u-boot/v2021.07/source/doc/imx/habv4/guides/encrypted_boot.txt  - NXP AN12056 - Encrypted Boot on HABv4 and CAAM Enabled devices