Newport CN803x secure boot
Trusted Mode Boot is the hardware and firmware implementation of secure boot for the CN803x based on the ARM Trusted Board Boot Requirements (TBBR) specification described in the ARM Trusted Firmware. It utilizes the ARM TrustZone architecture and the ARM Trusted Firmware (ATF).
Note that trusted boot on CN803x requires the -AUC (Secure Boot without Crypto) or -AUS (Secure Boot with Crypto) variant of the CN80XX processor which is not on Newport standard product boards. The standard Newport boards have the -SCP variant which has Crypto but no Secure Boot. Non AUC/AUS parts will come with the OTP fuses locked from the factory such that the ROTPK (Root of Trust Private Key) and others can not be programmed. Contact sales@… if you wish to use Trusted boot on a Gateworks Custom or Special
- -AUC (Secure Boot without Crypto)
- -AUS (Secure Boot with Crypto)
- -SCP (No Secure Boot with Crypto)
See also secure_boot for information on securing the rest of your firmware.
For more information see:
- Cavium OCTEON TX Trusted-Mode Boot PDF (Obtain from Cavium under NDA)
- ARM Trusted Board Boot Requirements (TBBR) specification
- ARM Trusted Firmware
- ARM TrustZone
Marvell BDK as the SPL
The boot code loaded by the CN803x BOOT ROM is the Marvell BDK which we use as a Secondary Program Loader (SPL) in order to load the board DTB, ARM Trusted Firmware (ATF), and U-boot propper.
The BDK is always built with signed images using keys in the bdk/trust-keys directory:
- hw-rot-public.pem ROTPK (root of trust private key)
- a 256bit hash of this is blown into One Time Programmable fuses for verification of the BDK boot stub and the BDK public key
- erased with a 'make -C bdk distclean' and re-created if needed
- created with:
openssl ecparam -name prime256v1 -genkey -noout -out bdk/trust-keys/hw-rot-private.pem
- bdk-sign-private.pem BDK private key
- used to sign and validate all files within the FATFS including board DTB's, various applications of the BDK, and the ATF and U-Boot FIP package
- erased with a 'make -C bdk distclean' and re-created if needed
- created with:
openssl ecparam -name prime256v1 -genkey -noout -out bdk/trust-keys/bdk-sign-private.pem
The hw-rot-private.pem and bdk-sign-private.pem files must be stored in a secure database preferably indexed by board serial number in case you even need to change them.
To build a version of boot firmware in the Newport BSP directory using keys that you create and manage in the ~/keys directory:
KEY_DIR=~/keys # cd to where you have the Newport BSP cd /usr/src/newport/bsp # setup your enviroment . ./setup-environment # perform a distclean to remove everything including keys make distclean # create your private keys (if not already created) openssl ecparam -name prime256v1 -genkey -noout -out $KEY_DIR/hw-rot-private.pem openssl ecparam -name prime256v1 -genkey -noout -out $KEY_DIR/bdk-sign-private.pem # copy your keys to the bdk cp $KEY_DIR/hw-rot-private.pem $KEY_DIR/bdk-sign-private.pem bdk/trust-keys # build the bdk and firmware make bdk make firmware-image
To easily obtain the SHA256 of the ROTPK public key you can use:
./newport/getkey.py bdk/trust-keys/hw-rot-private.pem
Blowing Fuses to lock a device to use trusted boot
Important Note: Once boards have been configured for trusted-mode boot by hard blowing fuses the JTAG chain is disabled and boards can not be re-tested by Gateworks and as a result returns (RMA) will not be accepted. It is recommended that OEM's test board functionality before permanently setting trusted-mode boot fuses.
In order to enable trusted boot you must:
- program the ROTPK 256bit hash into OTP
- enable the tz-force bit
- enable the fj-dis bit if you want to disable a fallback to insecure boot firmware if secure boot fails
The 256bit ROTPK that needs to be used for trusted boot can be found using the following in the Newport BSP directory:
./newport/getkey.py bdk/trust-keys/hw-rot-private.pem
Before doing this permanently there is a way to 'soft' blow the fuses where you can perform a warm reset to verify everything boots as expected.
Gateworks provides an octeontxotp application on its pre-built buildroot based minimal kernel+ramdisk that can be used for provisioning boards.
To boot this on a Newport board download the kernel Image (which contains a ramdisk rootfs) and boot via something like:
tftpboot $kernel_addr_r 192.168.1.146:newport/Image.rescue && booti $kernel_addr_r - $fdtcontroladdr
This program can be used to blow the fuses necessary to enable trusted boot:
- soft blow (for testing)
octeontxotp --rotpk $ROTPK octeontxotp --tz_force octeontxotp --fj_dis i2cset -f -y 0 0x20 0 0 # disable power-cycle on reset octeontxotp --reset
- replace $ROTPK above with the value output from './newport/getkey.py bdk/trust-keys/hw-rot-private.pem' in your BSP build directory
- the i2cset command above disables a board power-cycle on CPU reset so that the soft fuses are used
After the octeontxotp --reset
you should see the following which indicates a clean and successful trusted boot:
WARNING: WARNING: ******************************************************** WARNING: * Configured for soft blow of secure NV counter. This WARNING: * build is not suitable for production trusted boot. WARNING: ******************************************************** WARNING: Gateworks Newport SPL (12.7.0-7647df8 Wed Sep 4 20:36:41 UTC 2024) GSC : v61 0xfa4b RST:VIN Thermal Protection Enabled at board temp of 96C Temp : Board:29C/96C CPU:56C/95C Model : GW6905-SP435-A2 MFGDate : 07-11-2024 Serial : 448336 RTC : 51 SoC : CN8030-1500BG676-AUS-P12-G 1024KB 1500/550MHz 0xa2 Pass 1.2 MMC1 : not detected MMC0 : eMMC Boot : eMMC trusted, Secure Boot DTB : gw6404.dtb DRAM : 4096 MB, 2133 MT/s, DDR4 UDIMM J9 : PCI J10 : PCI (hwconfig options:PCI,SATA,USB2) J11 : PCI QLM0 : PCIE_X1@8000MHz J9 QLM1 : QSGMII@5000MHz QLM2 : PCIE_X1@8000MHz J11 QLM3 : PCIE_X1@8000MHz J10 Serial : 2x RS232 without flow control MDIO0 : DP83867 (RGMII) MDIO1 : VSC8574 (QSGMII) temp : 29.4C vdd_bat : 0.000V fan_tach: 0 RPM vdd_vin : 17.070V vdd_5p0 : 5.062V vdd_3p3 : 3.348V vdd_2p5 : 2.506V vdd_core: 0.891V vdd_0p9 : 0.902V vdd_1p0 : 0.999V vdd_1p2 : 1.193V vdd_1p5 : 1.483V vdd_an1 : 1.394V vdd_gsc : 3.162V NOTICE: Booting Trusted Firmware NOTICE: BL1: v1.5(release):e1ffc601 (Marvell-12.1.0) NOTICE: BL1: Built : 20:36:52, Sep 4 2024 NOTICE: CHIP UniqueID not set NOTICE: BL1: Booting BL2 NOTICE: BL2: v1.5(release):e1ffc601 (Marvell-12.1.0) NOTICE: BL2: Built : 20:37:09, Sep 4 2024 NOTICE: BL1: Booting BL31 NOTICE: BL31: v1.5(release):e1ffc601 (Marvell-12.1.0) NOTICE: BL31: Built : 20:37:17, Sep 4 2024 U-Boot 2021.01-g4ba8e461cc (Sep 04 2024 - 20:37:51 +0000) Model: Gateworks Newport CN80XX GW6404 Board: GW6905-SP435-A2 DRAM: 4 GiB WDT: Started with servicing (60s timeout) MMC: octeontx-mmc0: 0 Loading Environment from MMC... OK In: serial@87e028000000 Out: serial@87e028000000 Err: serial@87e028000000 BGX0 QLM1 LMAC2 mode: QSGMII BGX0 LMACs: 4 XCV_DLL_CTL: TX_BYP=0 RX_BYP=1 BGX2 LMAC0 mode: RGMII BGX2 LMACs: 1 Net: eth0: vnic0, eth1: vnic1, eth2: vnic2, eth3: vnic3, eth4: vnic4 Thermal protection:enabled at 96C Hit any key to stop autoboot: 0
If you use a $ROTPK value that differs from that which was used to build your image you will see no output from the UART as the board does not boot.
After testing via soft blow above you can now hard blow the fuses (this is permanent and you will no longer be able to JTAG program or RMA your board). Repeat the boot to the rescue image and blow the fuses with:
octeontxotp --hard --force --rotpk $ROTPK
octeontxotp --hard --force --lock rotpk
octeontxotp --hard --force --tz_force
octeontxotp --hard --force --fj_dis
- replace $ROTPK above with the value output from './newport/getkey.py bdk/trust-keys/hw-rot-private.pem' in your BSP build directory
At this point you have trusted boot all the way to the bootloader and can follow-up with a signed FIT image.
Signed FIT Image (kernel and optional ramdisk)
To add the kernel and an optional ramdisk to the chain of trust you can use a signed FIT image (see secure_boot). This requires that the FDT controlling U-Boot have a signature node containing the key data used to verify the FIT image.
For this you need the following:
- A key for your FIT image
- Add a signature node with the key details in the linux dtb used by U-Boot which resides in the FATFS
- Create a FIT image with a kernel, optional ramdisk and optional configuration
Example Procedure:
- Define some env variables used in commands below (altered for your needs):
DTB=gw6404-linux.dtb KEY_DIR=$PWD/bdk/trust-keys/ KEY_NAME=fit
- DTB should match the board linux DTB you are using
- KEY_NAME - what you named your key (fit) above
- KEY_DIR is where your key is
- Create the KEY:
# create key openssl genpkey -algorithm RSA -out $KEY_DIR/$KEY_NAME.key -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 # create cert for key openssl req -batch -new -x509 -key $KEY_DIR/$KEY_NAME.key -out $KEY_DIR/$KEY_NAME.crt
- you will want to save this away on a trusted source along with the keys used for secure boot above; note that a make clean will wipe any keys in bdk/trust-keys
- Alter the linux dtb (which U-Boot uses) in the boot firmware to add the signature node. This is done using the U-Boot mkimage tool then using the fatfs-tool tool to copy it into the embedded FATFS in the firmware image. Create an ITS template where each image is defined to have a has and each configuration is signed:
cat << EOF > fit.its /dts-v1/; / { description = "Image for Linux Kernel"; #address-cells = <1>; images { kernel { description = "Linux Kernel"; data = /incbin/("./linux/arch/arm64/boot/Image.gz"); type = "kernel"; arch = "arm64"; os = "linux"; compression = "gzip"; load =<0x48200000> ; entry = <0x48200000>; hash-1 { algo = "sha256"; }; }; }; configurations { default = "config-1"; config-1 { description = "Linux configuration"; kernel = "kernel"; signature-1 { algo = "sha1,rsa2048"; key-name-hint = "$KEY_NAME"; sign-images = "kernel"; }; }; }; }; EOF # add signature node to your dtb using mkimage cp dts/$DTB . mkimage -r -k $KEY_DIR -K $DTB -f fit.its fit.itb # pad and sign the dtb ./bdk/bin/bdk-aes-pad $DTB BDK_ROOT=$PWD/bdk ./bdk/bin/bdk-sign bdk-sign-private ./$DTB.sign ./$DTB # copy the new dtb and its signature into the FATFS fatfs-tool -i firmware-newport.img cp ./$DTB ./$DTB.sign / # copy the FIT image into the FATFS fatfs-tool -i firmware-newport.img cp fit.itb / # create a JTAG binary image ./mkimage_jtag --soc cn803x --emmc -s --partconf=user firmware-newport.img@user:erase_part:0-32768 > firmware-newport.bin
- Note placing the FIT image containing the kernel in the FATFS is just one option. You may want to put it someplace dedicated in FLASH due to size or other prefrences.
Now you can JTAG program the updated firmware-newport.bin and boot it via:
- verify the signature node is in the controlling dtb
GW6905-SP435-A2> fdt addr $fdtcontroladdr && fdt print /signature signature { key-fit { required = "conf"; algo = "sha256,rsa2048"; rsa,r-squared = <0xaf3ca3f0 0x73f28178 0x88f0aee0 0xaa2d9d2d 0xb1f4a926 0xfb94185f 0xf006b370 0xfe24b6f3 0x0f93dc67 0x49638257 0x33d2b04e 0xcf70c6b4 0x3abfda78 0xdbe6a1d7 0xd2a119fa 0x6e2efea8 0x359492a2 0x634dd1bf 0x5e3f1248 0x135cc54a 0x8b92f034 0xdf4b3525 0x99668d2b 0xd1b5c5cf 0xeb5a84a6 0x958edc0f 0x37bca5ee 0xb64b4b56 0x28c7b114 0x2771edde 0xd87b1b68 0x1b2c22d9 0xd44242d9 0xa3f5749d 0xa3ca7b86 0xe31923d7 0x6cf1d2c9 0x685a79f5 0x27576cea 0xa33a4be0 0x3ce85721 0xb8a1b9a1 0xe54acf5b 0x098d2201 0x0b768b19 0x9be17c61 0xb5540e51 0xe9f89f54 0xc6d69841 0x7a4a79f2 0xf9e10893 0x992f120d 0xeb874b8e 0xa1017e7c 0x5f60874a 0xf78d9b6a 0xc6b8b64f 0x4fcad653 0xdaa9f618 0x1ff8e9fc 0x5ee19772 0x5c3c66f0 0x5a006dec 0xddc182ff>; a6d 0x5a4e6f90 0xe31b9d42 0xcefa7d1f 0x6b757a25 0xcafe13b5 0x3b0eb7ff 0xfc281541 0xd8765ac3 0x5f19110a 0x381aac24 0xaa69c335 0x3aaddfe1 0x48bc4576 0x7c725b55 0x3a006181 0x4439a5d6 0x6a6523f2 0xeff42f8f 0x9f927be6 0xbc88d703 0xd264bc4c 0x1be6fad9 0x24e157e8 0x365f4765 0x7c63c31d 0xac83d338 0x0da0eedb 0xa2102d3a 0xf826be86 0xcd6d5e02 0x6bacb3f3 0xb82f7982 0x8fedc29e 0x81764654 0x6c4a8ce0 0x5d8bfdda 0xcb2b3973 0x8247809e 0x9abdbdd1 0x98155cc1 0x1ac51a84 0xcd6e87b2 0xf1a656e4 0x68b3cc9f 0x655ba517 0x9f6841dd 0x48de6550 0x5869179a 0x84e68b6d 0xeabd5b4c 0x146448ec 0x37d11af4 0x8e715f05>; rsa,exponent = <0x00000000 0x00010001>; rsa,n0-inverse = <0xde6a6a33>; rsa,num-bits = <0x00000800>; key-name-hint = "fit"; }; };
- Boot the FIT image
# boot FIT image setenv bootargs "root=/dev/mmcblk0p2 rootwait rw" load mmc 0:1 $loadaddr fit.itb && bootm $loadaddr - $fdtcontroladdr
Note that Newport's U-Boot already has the things defined necessary for signed FIT images (CONFIG_FIT_SIGNATURE, CONFIG_RSA, CONFIG_LEGACY_IMAGE_FORMAT)
You can continue the process of securing your firmware image by locking down U-Boot's environment and using a ramdisk to mount an encrypted filesystem. For detailed examples of that see venice/secure_boot