This document describes a simple approach for booting an authenticated Linux kernel on PolarFire SoC.
- PolarFire SoC Icicle Kit
- SoftConsole v2022.2 or later
- Serial Terminal Program
- Ubuntu Host PC configured as shown in the "Build Instructions" of the PolarFire SoC Yocto BSP README
Authenticated boot is a mechanism by which software is checked for integrity and source authentication before execution. Verification of integrity ensures that data has not been modified. Modification includes the insertion, deletion, and substitution of data/information. Authentication verifies the identity of the entity that created the data/information.
Most modern embedded systems have a sequence of software components executed as part of the boot process. For example a Linux system may have a bootloader, kernel, device tree, initial RAM filesystem (initramfs), root file system, etc.
Having a verification scheme, from power up to userspace, involves establishing a chain of trust.
The fundamental concept of the chain of trust, is that each stage in the process verifies that the subsequent stage is valid and unmodified, before initiating that stage. If the verification of any stage fails, the device will not continue to boot.
The chain of trust that will be demonstrated in this application note is shown in the image below:
The boot-up sequence starts when the PolarFire SoC FPGA is powered-up or reset. It ends when the processor is ready to execute a user application. This boot sequence runs through several stages before it begins the execution of user programs.
The MSS Core Complex can be booted in one of four modes. There are two boot modes relevant for authenticated boot; User Secure Boot and Factory Secure Boot.
The User Secure Boot methodology is intended for implementing user-defined secure boot authentication.
The Factory Secure Boot mode implements Microchip supplied factory secure boot authentication of the eNVM content.
This document describes how to configure a secure boot system using Factory Secure Boot.
Secure boot mode 3 implements Microchip supplied factory secure boot authentication of the eNVM content. It uses the Elliptic Curve Digital Signature Algorithm (ECDSA) to authenticate the signature of a Secure Boot Image Certificate (SBIC) as part of booting the system. The PolarFire SoC RISC-V monitor and application processors will not be started if authentication fails.
Secure boot mode 3 only supports authentication of the eNVM content. No encryption/decryption of the eNVM content is used.
For detailed information on Factory Secure Boot please refer to the Boot Mode 3 Fundamentals documentation.
Key management is often the critical link in building a secure system. Key management includes generating, distributing, and storing keys.
In this document we will generate three key pairs
-
User Code Signing Keys (UCSK and UCSQ): The User Code Signing Key (UCSK) is the private key used to sign the SBIC. The User Code Signing Public Key (UCSQ) is a public key stored in the private Non-volatile Memory (pNVM) and is used to verify the SBIC during the boot process.
-
HSS Payload Signing Keys (HPSK and HPSQ): THe HSS Payload Signing Key (HPSK) is the private key used to sign the HSS Payload, e.g, U-Boot. The HSS Payload Signing Public Key (HPSQ) is a public key used to verify the HSS payload and is embedded into the HSS binary and stored in eNVM.
-
FIT Image Signing Keys: used to sign and verify the Linux FIT image containing all the boot artifacts, i.e., kernel, initramfs, device tree blob and FIT image configuration. The public key is stored as part of the U-Boot binary and stored in persistant storage e.g., mmc/SD or QSPI flash device.
Note: A private key is a confidential key used during the signing process only. It is never stored in the embedded device.
The image below shows the key generation process, as well as the signing and programming tools that will be used for each of the boot stages, that conforms to this chain of trust example.
The activities necessary to enable an authenticated boot flow include:
- Prepare the PolarFire SoC Linux build system. This includes:
- Configuring U-Boot with FIT signature verification
- Creating the FIT Image Signing Keys to sign and verify the FIT image
- Creating a signed FIT image containing a Linux kernel, device tree and initramfs
-
Generate the cryptographic material to sign and verify the HSS payload
-
Sign the HSS payload containing the U-boot binary using the HSS Payload Generator tool
-
Deploy the Yocto Linux image to a storage device (eMMC, SD or QSPI flash)
-
Configure the Hart Software Services (HSS) with crypto signing support
-
Deploy the HSS binary to the eNVM using Factory Secure Boot mode in SoftConsole
The flow of this document will be presented in a somewhat reversed order – starting from a completely open, unsigned platform, then securing pieces from the running operating system back to the root of trust (the System Controller). This allows the addition of security features, one layer at a time, and prevents having to get everything working together at the beginning.
This document relies on the Yocto build system to manage the process of building a working Linux image that can be run from eMMC/SD on the Icicle Kit.
Before continuing with this document, please make sure you have set up Yocto in a host PC as shown in the "Build Instructions" section of the PolarFire SoC Yocto BSP README.
Verified Boot is the process that U-Boot uses to verify that an image is correct and is allowed to run on the platform. Using Verified Boot requires:
- Special U-Boot configuration
- Creating the FIT Image Signing keys
- Storing the FIT Image Signing Public Key in the U-Boot control DTB
- Creating a FIT image
- Signing a FIT image using mkimage tool
The FIT image that will be configured in this document contains a configuration that is hashed using SHA256 and encrypted using RSA encryption.
Flattened Image Tree (FIT) files are special instances of Flattened Device Tree (FDT) files. Instead of describing hardware, U-Boot FIT files contain files and metadata in the form of nodes and properties that are used to boot applications such as Linux. The FIT files for Verified Boot contain a Linux kernel, Linux device tree, configuration data, and hashes that U-Boot uses to authenticate the data contained within the FIT file. U-Boot uses the “bootm” command to verify and boot an image in the FIT format.
Since U-Boot is extremely flexible, there are many features that should be carefully evaluated to
help ensure security. One configuration setting, CONFIG_FIT_SIGNATURE
, allows image signature
checking and is the heart of U-Boot’s Verified Boot methodology. Without the CONFIG_FIT_SIGNATURE
setting, it is not possible to check, or even generate, a properly signed FIT file.
The CONFIG_FIT_SIGNATURE
option is configured in the icicle-kit-es-auth
machine
included in our Yocto BSP.
Although this document focuses on the boot authentication process, there are many other configurations that need to be considered for enforcing U-Boot security. For example, we do not want someone to be able to tamper with the U-Boot environment script or arbitrarily execute whatever U-Boot commands they would like. For this reason, it is recommended to lock-down U-Boot by disabling some configurations, such as autoboot, command line and the use of U-Boot boot scripts, since they could be used by an attacker to alter the boot sequence. For additional details on securing U-Boot see the document by F-Secure.
This application note will not go into the details of Public Key Infrastructure (PKI), but will introduce and describe the components necessary to perform an authenticated boot. When performing PKI operations, there are several important components that must be obtained or constructed ahead of time.
The easiest way to generate a public/private key pairs is to use the OpenSSL library, available for Windows and Linux from openssl.org. OpenSSL is a robust, commercial-grade, fully-featured toolkit for general-purpose cryptography and secure communication.
OpenSSL can be compiled from source, installed via third-party community contributed binaries for Windows etc. (available from openssl's website or installed via a Linux distribution package manager (e..g., for Ubuntu, apt install openssl).
This section will describe how to generate the HSS Payload Signing Keys (HPSK and HPSQ) and FIT Image Signing Keys.
Before continuing with this document, please make sure you have set up Yocto in a host PC as shown in the "Build Instructions" section of the PolarFire SoC Yocto BSP README.
-
First, setup the Bitbake environment if you haven't already
$ . ./meta-polarfire-soc-yocto-bsp/polarfire-soc_yocto_setup.sh
-
Run the PolarFire SoC Authenticated Boot setup script
$ . ../meta-polarfire-soc-yocto-bsp/meta-polarfire-soc-extras/scripts/polarfire-soc_auth_setup.sh
By default, the script will generate the HSS Payload Signing Keys (HPSK and HPSQ) and FIT Image Signging Keys in the repo workspace directory. For example:
├── conf
├── keys
│ ├── mpfs_fitimage_key.crt
│ ├── mpfs_fitimage_key.key
│ ├── x509-ec-secp384r1-private.pem
│ └── x509-ec-secp384r1-public.der
├── meta-openembedded
├── meta-polarfire-soc-yocto-bsp
├── openembedded-core
Additionally, the script will export the key names and paths to Bitbake using environment variables. For this reason, it is important that this script gets sourced each time a new terminal session is initiated.
Note: U-Boot requires the names of the key and certificate to be identical, except the filename extension of the key must be .key, and the certificate .crt.
The PolarFire SoC Yocto BSP layer uses the kernel-fitimage
class to automatically pack a kernel
image, device tree(s), and initramfs into a single FIT image. This FIT image can be signed when the
variables UBOOT_SIGN_ENABLE
, UBOOT_MKIMAGE_DTCOPTS
, UBOOT_SIGN_KEYDIR
and UBOOT_SIGN_KEYNAME
are set appropriately.
The UBOOT_SIGN_KEYNAME
variable should contain the name of the key used for signing the FIT
Image, this should be located in the directory specified in the UBOOT_SIGN_KEYDIR
variable.
For signature validation to be of any use, the public key has to be available before loading the FIT
image. This is why U-Boot must include the public key in the U-Boot's device tree (often called
flattened device tree or FDT). The UBOOT_MKIMAGE_DTCOPTS
variable can be used to tell the Device
Tree Compiler (DTC) that we need space for the public keys. As recommended by the uboot-sign
bitbake class, we use 2kBytes of padding for the public key and certificate.
It is worth mentioning that the default hash algorithm and signing algorithm for the FIT Image are
SHA256 and RSA 4086, respectively. If needed, the FIT_SIGN_ALG
and FIT_SIGN_NUMBITS
variables
can be use to adjust this configuration. In this document, we will use RSA encryption with 4096 bits.
For more information please refer to the Yocto Project Reference Manual.
The HSS_PAYLOAD_GEN_KEYNAME
variable should contain the name of the private key to sign the HSS
payload, this should be located in the directory specified in the HSS_PAYLOAD_GEN_KEYDIR
variable.
Our Yocto BSP provides an icicle-kit-es-auth
machine that has all the options above configured by default.
To build an image with authenticated boot:
- Build the
mpfs-initramfs-image
for the Icicle using theicicle-kit-es-auth
machine:
$ MACHINE=icicle-kit-es-auth bitbake -R conf/initramfs.conf mpfs-initramfs-image
Note: The chain of trust demonstrated in this document uses an initramfs, this ensures that only an authenticated initramfs that is part of the FIT image can be used. However, when using a root filesystem, it is recommended to authenticate the rootfs using other tools such as dm-verity.
-
Copy the image to the eMMC/SD card
bmaptool copy tmp-glibc/deploy/images/icicle-kit-es-auth/mpfs-initramfs-image-icicle-kit-es-auth.wic /dev/sdX
Be very careful while picking /dev/sdX device! Look at dmesg, lsblk, GNOME Disks, etc. before and after plugging in your usb flash device/uSD/SD to find a proper device. Double check it to avoid overwriting any of system disks/partitions!
-
Power-cycle the Icicle Kit and observe U-Boot's boot log. Notice that the “Verifying Hash Integrity” message shows “rsa4096:mpfs_fitimage_key+”. This indicates that the FIT signature was indeed checked, and found to be valid.
## Loading kernel from FIT Image at 1008300000 ... Using 'conf-microchip_mpfs-icicle-kit.dtb' configuration Verifying Hash Integrity ... sha256,rsa4096:mpfs_fitimage_key+ OK Trying 'kernel-1' kernel subimage Description: Linux kernel Type: Kernel Image Compression: gzip compressed Data Start: 0x10083000fc Data Size: 4816479 Bytes = 4.6 MiB Architecture: RISC-V OS: Linux Load Address: 0x1000200000 Entry Point: 0x1000200000 Hash algo: sha256 Hash value: 8da0226da5c62c57acbbeaf86e3ea807e5ff8c2dac0c5ad21bea41bfee564ebb Verifying Hash Integrity ... sha256+ OK ## Loading ramdisk from FIT Image at 1008300000 ... Using 'conf-microchip_mpfs-icicle-kit.dtb' configuration Verifying Hash Integrity ... sha256,rsa4096:mpfs_fitimage_key+ OK Trying 'ramdisk-1' ramdisk subimage Description: mpfs-core-image-base Type: RAMDisk Image Compression: uncompressed Data Start: 0x100879ceb4 Data Size: 43696904 Bytes = 41.7 MiB Architecture: RISC-V OS: Linux Load Address: unavailable Entry Point: unavailable Hash algo: sha256 Hash value: 7fbcc959d1470846a285776862e4d2985ad5cccb19916aac8ec3cad5bf2f708a Verifying Hash Integrity ... sha256+ OK ## Loading fdt from FIT Image at 1008300000 ... Using 'conf-microchip_mpfs-icicle-kit.dtb' configuration Verifying Hash Integrity ... sha256,rsa4096:mpfs_fitimage_key+ OK Trying 'fdt-microchip_mpfs-icicle-kit.dtb' fdt subimage Description: Flattened Device Tree blob Type: Flat Device Tree Compression: uncompressed Data Start: 0x1008798084 Data Size: 16823 Bytes = 16.4 KiB Architecture: RISC-V Load Address: 0x1002200000 Hash algo: sha256 Hash value: 00ecbea21657a5593b1ab78065e86d95b6378ef4a2749e40f30764fcae9e2710 Verifying Hash Integrity ... sha256+ OK Loading fdt from 0x1008798084 to 0x1002200000 Booting using the fdt blob at 0x1002200000 Uncompressing Kernel Image Loading Ramdisk to 100d653000, end 100ffff308 ... OK Loading Device Tree to 000000100d64b000, end 000000100d6521b6 ... OK
This section describes how to:
- Build the HSS with crypto signing support
- Deploy the HSS along with the HSS Payload Signing Public Key (HSPQ) to PolarFire SoC
For more information on how the HSS crypto signing and validation works, please refer to the HSS secure boot documentation.
Firstly, the HSS needs to be compiled with support for code signing enabled and with the public-key embedded in it.
-
Clone or download the hart-software-services project from GitHub
$ git clone https://github.com/polarfire-soc/hart-software-services.git
-
Launch SoftConsole IDE and open either a new or an existing workspace
-
In the Project Explorer window, click
Import projects...
as shown in the following figure. Expand theGeneral
drop-down list and selectExisting Projects into Workspace
, then click Next. -
Click
Browse...
, navigate to the location where the hart-software-services project folder is located, select the folder and then, clickFinish
. -
Launch a terminal emulator and navigate to the hart-software-services repository
$ cd hart-software-services
-
Copy the default config of your board. For example:
$ cp boards/mpfs-icicle-kit-es/def_config .config
-
Edit the default HSS configuration using menuconfig
$ make menuconfig
After running the command shown above, a configuration menu should pop-up.
Navigate to the Crypto category
Enable Cryptographic Signing support
Navigate to the Signing Configuration category
Enter the path to the HSS Payload Signing Public Key (HPSQ). For example
/home/user/yocto-dev/conf/keys/x509-ec-secp384r1-public.der"
:Exit menuconfig by pressing the "Q" in the keyboard and save the configuration when prompted.
-
Build the HSS by pressing the Build button in SoftConsole or by using the command line
$ make
For more information on how to build the HSS using the command line, please refer to the Hart Software Services README.
The Boot Mode Programmer Tool, which is built into SoftConsole, provides the ability to deploy an application and configure the boot mode on PolarFire SoC.
When setting Boot Mode 3 (Factory Secure Boot), the Boot Mode Programmer Tool embedded in SoftConsole will perform the following actions:
- Read ECDSA NIST P-384 keys (UCSK and UCSQ) from a given directory
- Generate and sign the SBIC (Secure Boot Image Certificate)
- Generate a HEX file containing the application binary and SBIC data
- Generate the bitstream and program it to the target device
To generate the User Signing Keys (UCSK and UCSQ) which are used to sign and verify the SBIC:
$ openssl ecparam -name secp384r1 -genkey -noout -out combined.ec.key
$ openssl pkcs8 -topk8 -nocrypt -in combined.ec.key -out ucsk-private.pem
$ openssl ec -in ucsk-private.pem -pubout -out ucsq-public.pem
Flash the HSS to your board using factory secure boot mode 3:
make program BOOTMODE=3 BOOTMODE_ARGS="--keys SEPARATED ucsk-private.pem ucsq-public.pem"
If the application was successfully programmed to the device, the Boot Mode Programmer Tool should output the following:
15:09:51 INFO - Generating BIN file...
15:09:51 INFO - Generating SBIC (Secure Boot Image Certificate)...
15:09:51 INFO - Reading ECDSA NIST P-384 keys from files...
15:09:51 INFO - Generating HEX file...
15:09:51 INFO - Preparing for bitstream generation...
15:09:51 INFO - Generating bitstream...
15:09:58 INFO - Programming the target...
15:10:09 INFO - Verifying the target...
15:10:16 INFO - mpfsBootmodeProgrammer completed successfully.
Once programmed, the HSS console now shows a “ECDSA verification passed” message. This indicates that the HSS payload signature was verified and found to be valid.
[6.264467] PolarFire(R) SoC Hart Software Services (HSS) - version 0.99.33-dev-build
MPFS HAL version 2.0.101 / DDR Driver version 0.4.018 / Mi-V IHC version 0.1.1 / BOARD=mpfs-icicle-s
(c) Copyright 2017-2022 Microchip FPGA Embedded Systems Solutions
...
Press a key to enter CLI, ESC to skip
Timeout in 1 second
..
[7.589296] CLI boot interrupt timeout
[7.594261] Initializing Mi-V IHC
[7.598748] Initializing IPI Queues (6056 bytes @ 0xa037ce0)...
[7.606100] Initializing PMPs
[7.610206] Initializing Boot Image ...
[7.615266] Trying to boot via MMC ...
[7.620231] Preparing to copy from MMC to DDR ...
[7.629007] Validated GPT Header ...
[7.663805] Validated GPT Partition Entries ...
[7.669967] Boot Partition found at index 1
[7.675514] Attempting to read image header (1632 bytes) ...
[7.685626] Copying 705024 bytes to 0xa0000000
[7.720598] MMC: Boot Image registered ...
[9.220997] ECDSA verification passed
...
In this example, the generated bistream is encrypted and authenticated using the default factory key, known as Key Loading Key (KLK).
The KLK is common to a relatively large number of devices of the same type and version, and resides within the programming tool software. This makes it the easiest key to use but is not as secure as the other options. Although this document focuses on the authentication process, there are many other configurations such as user keys and passcodes that should be considered to enforce security. For more information please refer to the PolarFire SoC FPGA Security User Guide.