LAN969x Secure Boot

1. Introduction

The LAN969x SoC contains an internal CPU which has secure boot capabilities with an implementation based on TF-A (Trusted Firmware for ARM).

This document uses many abbreviations, see the terms and abbreviations section for details.

The BootROM in LAN969x offers the following high-level features:

  1. Boot firmware from:

    1. SPI-NOR

    2. SD-Card

    3. eMMC

  2. Firmware authentication based on public key ECSDA algorithm

  3. Firmware encryption using AES encryption with

    1. Secret Symmetric Key (SSK)

    2. Per-device unique key (BSSK)

  4. A bootrom UART monitor (fwu.html) with support for:

    1. Secure JTAG unlock

    2. Firmware update over UART

    3. OTP programming

LAN969x always boots from the BootROM using the TF-A based boot stages. This is the case regardless of if secure boot is needed (enabled) or not. This means that it will be the TF-A Bootloaders that boots either U-Boot, the Linux kernel or another OS directly.

This document, and the referenced sources are provided under the BSD-3-Clause license. This means that the content is provided as-is, and that Microchip cannot be held liable. See the License section for details.
In LAN969x the DDR initialization is done as part of TF-A/BL2 and not in U-Boot. If you need to change DDR settings, then this shall be done in TF-A.
The sources and binaries provided to enable secure boot contains demo keys which are not handled in a secure way. The symmetric shared keys, and the private keys are publicly available. If any of the advertised security features in this document are needed, then the demo keys must be replaced with new keys which shall then be kept confidential.

1.1. TF-A architecture

The Trusted Firmware-A (TF-A) SW project is a multi-stage bootloader which utilizes the ARM TrustZone feature in ARM CPUs as an important part of the security concept.

The TrustZone feature in ARM CPUs introduce the concept of a Secure-World (S) and Non-Secure-World (NS) and the ability to switch between these worlds. The various CPU peripherals inside the SoC, or connected to the SoC, are then classified as secure or non-secure. Secure-World has access to everything, while Non-Secure world only has access to the non-secure peripherals.

In HW this is implemented as an additional bit in the address bus to specify the Secure/Non-Secure mode. Only Secure-World bus controllers can issue transactions with the Secure-Bit set.

                              |   S-Peripheral     NS-Peripheral
    --------------------------+---------------------------------
    S-World Bus Controller    |   Access           Access
    NS-World Bus Controller   |   No Access        Access

When zooming in on the CPU, the S/NS worlds are often seen as an additional dimension to the exception levels (EL3 - EL0). This is illustrated below:

Bootflow

The CPU can jump between the secure and non-secure worlds, and the two worlds can communicate with each other by using a shared buffer. This allows the same kind of application and use-cases as when having a dedicated security processor.

1.2. TF-A Bootloaders (BLx)

The TF-A framework provides a multi-stage bootloader concept. There are many good reasons to go with a multi stage approach. To name a few points:

  1. Make the amount of code/functionality in the ROM as small as possible.

  2. Make components running within a given exception level (or having different life-time) self-contained. TF-A provides different bootloader stages for the different levels.

  3. Breaking complex stuff into smaller problems is always a good idea.

Here is a list with an explanation of the different boot-loader stages:

  1. Trusted ROM Firmware (BL1)

    1. This is a binary built from the TF-A sources.

    2. Embedded as ROM code in the chip, execute in secure world EL3/SVC-MON.

    3. Purpose:

      1. Do basic platform initialization

      2. Read BL2 from boot media into secure memory, authenticate it, run it.

  2. Trusted Boot Firmware (BL2)

    1. This is a binary built from the TF-A sources.

    2. Loaded from a FIP (see below), and runs from on-chip secure memory, execute in secure world EL1/SVC-MON.

    3. Purpose:

      1. Will do additional platform initialization including:

        1. TrustZone configuration

        2. DDR initialization

      2. Load BL3x images into DDR memory, authenticate, decrypt etc.

      3. Run EL3 Runtime Software

  3. EL3 Runtime Software (BL31)

    1. This is a binary built from the TF-A sources.

    2. Loaded from from a FIP and runs as Secure EL3/SVC. This firmware stays resident in memory, and the services offered here can be called later on.

    3. Purpose:

      1. Initialize the secure run-time service, typically PSCI and Trusted HW RNG.

      2. Authenticate, decrypt and run BL33.

  4. Non-trusted Firmware (BL33)

    1. BL33 is not built from the TF-A sources, but is a binary which is stored in a FIP and will be loaded (and executed) by TF-A. This is typically U-Boot, a direct booting Linux kernel or some other OS.

    2. Runs from DDR memory in non-secure world as EL1/SVC

  5. Firmware update image (BL2u)

    1. This is a binary built from the TF-A sources.

    2. Compared to the other images, this is special, as it is not used during normal boot, and it is not loaded from a FIP stored in flash.

    3. This image replaces the normal BL2. It is loaded by BL1 via the UART-monitor using a Serial port. Like BL2, it is authenticated by BL1 before it is being executed.

    4. Purpose: Provide an updateable stepping stone for board provisioning.

1.3. Firmware Image Package (FIP)

The FIP or Firmware Image Package is the file format TF-A uses to contain all the artifacts. This includes the BL2 and BL3x images, certificates, and configuration files.

The FIP is generated as part of the build process. It is written in flash in binary format without any transformation, and the BL1 ROM knows how to locate and load a particular image type in the FIP.

It can be compared to a very simple read-only file-system, as it contains only very basic information of the individual files: type, offset and size.

The TF-A project includes 2 host tools:

  • fiptool: which makes it easy to perform common operations (create, display, add/del/mod individual images) in fip files.

  • cert_create: which makes it easy to generate the keys and certificates needed to establish the chain of trust.

In the build process the make target is called certtool, but the resulting binary is called cert_create.

The FIP generation process where the fiptool and cert_create are used is shown below:

Bootflow

As shown above, the cert_create tool reads the BLOBs as input, it reads the keys from the disk (or generate them on the fly), and creates the needed certificates. Once the certificates have been generated, the normal fiptool can bundle both the original BLOBs and the associated certificates into the FIP which can then be authenticated. To bind devices to this chain of trust, the SHA or the public root key needs to be written into OTP.

1.3.1. FIP Image IDs

When reading console output, browsing the source code, or when using the fiptool or the cert_create tool, it is often necessary to reference the individual images. The table below provide the mapping between the defines used in the C code, the numeric values as they will appear in the trace output, and finally the command-line argument which is understood by the fiptool (and cert_create).

C-Define Num fiptool arg

BL2_IMAGE_ID

1

--tb-fw

BL31_IMAGE_ID

3

--tos-fw

BL33_IMAGE_ID

5

--nt-fw

TRUSTED_BOOT_FW_CERT_ID

6

--tb-fw-cert

TRUSTED_KEY_CERT_ID

7

--trusted-key-cert

TRUSTED_OS_FW_KEY_CERT_ID

10

--tos-fw-key-cert

NON_TRUSTED_FW_KEY_CERT_ID

11

--nt-fw-key-cert

TRUSTED_OS_FW_CONTENT_CERT_ID

14

--tos-fw-cert

NON_TRUSTED_FW_CONTENT_CERT_ID

15

--nt-fw-cert

FW_CONFIG_ID

31

--fw-config

2. Getting started with TF-A for LAN969x

This section provides the hands-on steps needed to build TF-A for LAN969x.

2.1. Build environment

Most modern Linux distributions can be used for building TF-A, but to ensure consistent results a docker image can be used for running all the build-commands.

This project provides a dr (docker run) script to make it easy to run a command in the desired docker container.

The dr script is provided by the https://github.com/microchip-ung/docker-run project which also offers more details on how it works.

The folder /opt/mscc must exist before you can use the dr script. This folder is used for caching BSP packages.

The short story is that prefixing a command with dr will cause it to run in a docker container.

You can test if it works just by typing dr ls and check the result.

2.2. Getting sources and releases

Sources can be obtained from https://github.com/microchip-ung/arm-trusted-firmware using git clone.

A release with both source packages and binaries are provided as release artifacts and can be found at https://github.com/microchip-ung/arm-trusted-firmware/releases.

2.3. Building

To build the TF-A binaries for LAN969x, navigate to the root of the project and run the following command:

$ dr ./scripts/build.rb -p lan969x_a0

This will by default do incremental builds. The --clean flag will delete the artifacts for a given platform/variant and cause the next build to be complete rebuild.

This will use the demo keys from the ./keys folder, which are not kept confidential. Any secure products must generate new keys and keep them secret.
By default the build script will load a pre-build U-Boot binary into the BL33 blob.

The build.rb script offers a set of options to tweak the default settings. The --help option shown below provides an overview of possible options, and the following sub-sections provides some additional details.

$ dr ./scripts/build.rb --help
Usage: build.rb [options]
    -p, --platform <platform>        Build for given platform: lan966x_b0, lan966x_lm, lan969x_sr, lan969x_a0, lan969x_svb, lan969x_lm, lan969x_pcie
    -r, --root-of-trust <keyfile>    Set ROT key file
        --create_keys                Create new keys
        --bl31-key <keyfile>         Set BL31 key
        --bl32-key <keyfile>         Set BL32 key
        --bl33-key <keyfile>         Set BL33 key
        --non_trusted_world-key <keyfile>
                                     Set non_trusted_world key
        --scp_bl2-key <keyfile>      Set scp_bl2 key
        --trusted_world-key <keyfile>
                                     Set trusted_world key
        --encrypt-images <imagelist> List of encrypted images, eg BL2,BL32,BL33
        --encrypt-ssk <keyfile>      Enable encryption with SSK
        --encrypt-bssk <keyfile>     Enable encryption with BSSK
        --fw-nvctr <counter>         Set Secure FW NV counter for FIP
        --ntfw-nvctr <counter>       Set Non-trusted FW NV counter for FIP
        --bl33_blob <file>           BL33 binary
    -d, --debug                      Enable DEBUG
        --release                    Disable DEBUG
    -g, --[no-]gptimg                Create a GPT image file with the FIP (obsoleted)
        --gpt-data <file>            Add GPT payload
    -c, --clean                      Do a 'make clean' instead of normal build
    -C, --coverity stream            Enable coverity scan
    -R, --[no-]ramusage              Report RAM usage
        --extra1 <file>              Add BL32 EXTRA1 image to FIP
        --extra2 <file>              Add BL32 EXTRA2 image to FIP

2.3.1. Platform selection

The --platform option is used to select the desired platform. This argument must be provided, and when building for LAN969x it must be set to: lan969x_a0:

2.3.2. BL33 content

By default the U-Boot binary provided by the BSP will be used as BL33 content. This is available as the fip.bin image file in the output folder of the build. Likewise the build also produces a fip_linux.bin image file where the BSP provided brsdk_standalone_arm.itb blob from the BSP is used as the BL33. This itb file contains a Linux kernel and a Device Tree, and will allow the system to boot Linux without U-Boot.

Sometimes it is desirable to provide the BL33 content directly, this can be done using --bl33-blob <file>. The blob can be either a itb/fit image or a executable binary. If it is not a fit then it will be relocated to address 0x60200000 (in DDR) and once authenticated the BL31 will do a memory-jump to that address. If it is a fit then is is handled as described in the following section.

Be aware that the BL33 blob will be aggregated into the FIP, and if the blob is sufficiently large the resulting image may not fit into the NOR flash (in this case eMMC can be used for storing the FIP).

2.3.2.1. Loading FIT/ITBs as BL33

When using a fit image, the FIT image is placed in DDR NS start (0x60200000), just like the "flat" binary type image.

When the fit image is parsed, individual components are identified in this order:

  1. Device tree

  2. Root FS (optional)

  3. Kernel

The two first components need to be moved to an area outside where the fit image itself resides - i.e. the load address cannot overwrite the fit image itself.

The kernel (being loaded last) is allowed to overwrite the fit image itself, so the load address will typically be the NS DDR start.

Apart from the above guides, the following fit image restrictions apply:

  • Compression is not supported for any data (the kernel loader itself may use compression).

  • A load address outside NS DDR area is not allowed.

Configuration selection

If the fit image contain more than one configuration node, the fit_config OTP tag can be use to name the fit configuration node to use. Otherwise, the default config will be used - as is custom with fit images.

Device Tree modifications

Once the device tree according to the chosen configuration is loaded into the proper place, the device tree is "fixed up" with these changes:

  • A /memory node is added/modified to containing the memory size according to the platform NS DDR location and size.

  • If the device tree property /chosen/bootargs is not present, a default command line according to the platform is added. (LAN969X: console=ttyAT0,115200 root=/dev/mmcblk0p5 rw rootwait loglevel=8).

  • The load and entry address information in the kernel node is changed to match the current SoC. This is needed as the FIT image produced by the BSP supports both the Sparx5 and LAN969x SoCs.

FIT example
/dts-v1/;
/ {
	description = "Image Tree Source file for Sparx5/LAN969x";
	#address-cells = <1>;

	images {
		kernel {
			description = "Kernel";
			data = /incbin/("images/mscc-linux-kernel.bin.gz");
			type = "kernel";
			arch = "arm64";
			os = "linux";
			compression = "gzip";
			load = /bits/ 64 <0x700080000> /* Change this to 0x60000000 for lan969x */;
			entry = /bits/ 64 <0x700080000> /* Change this to 0x60000000 for lan969x */;
		};

		fdt_pcb134 {
			description = "Flattened Device Tree";
			data = /incbin/("images/sparx5_pcb134.dtb");
			type = "flat_dt";
			arch = "arm64";
			compression = "none";
		};

		... more device trees ...

		ramdisk {
			description = "Ramdisk";
			data = /incbin/("rootfs.squashfs");
			type = "ramdisk";
			arch = "arm64";
			os = "linux";
			compression = "none";
		};

	};

	configurations {
		default = "pcb134";

		pcb134 {
			description = "Kernel with DT fdt_pcb134";
			kernel = "kernel";
			fdt = "fdt_pcb134";
			ramdisk = "ramdisk";
		};

		lan9698_ev23x71a_0_at_lan969x {
			description = "Kernel with DT fdt_lan9698_ev23x71a_0_at_lan969x";
			kernel = "kernel";
			fdt = "fdt_lan9698_ev23x71a_0_at_lan969x";
			ramdisk = "ramdisk";
		};

		... more configuration nodes for other device trees ...

	};
};

The unpacking of the fit image will display trace log messages if trace is enabled. And example is shown here.

FIT boot trace log
NOTICE:  BL1: Booting BL31
INFO:    Entry point address = 0x200000
INFO:    SPSR = 0x3cd
NOTICE:  BL31: lts-v2.8.8(debug):v2.6-mchp1-2067-g2609d6b5a
NOTICE:  BL31: Built : 11:36:08, Dec  4 2023
NOTICE:  Preparing to boot 64-bit Linux kernel
[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034]

2.3.3. Authentication keys

By default the keys used for authentication are loaded from the files in the ./keys folder. The following table provides an overview of the command-line options, the default file and a description of its usage:

CMD-option Default value Description

--rot

keys/rotprivk_ecdsa.pem

Confidential key used to sign the TRUSTED_BOOT_FW_CERT and the TRUSTED_KEY_CERT. This file contains the ROT_PRIV_KEY and the ROT_PUB_KEY. The public key is embedded into the both the TRUSTED_BOOT_FW_CERT and the TRUSTED_KEY_CERT - which makes these certificates self-signed.

--rot_pub

keys/rotpk_ecdsa.der

Non-confidential public key derived from keys/rotprivk_ecdsa.pem.

--rot_sha

keys/rotpk_ecdsa_sha256.bin

Non-confidential SHA of the public key. This is the key which needs to be loaded into the OTP_TBBR_ROTPK otp field.

--bl31_key

keys/bl31_ecdsa.pem

Confidential key used to sign the bl31 blob (BL31_IMAGE_ID). This file contains the TRUSTED_OS_FW_CONTENT_CERT_PRIV_KEY and the TRUSTED_OS_FW_CONTENT_CERT_PUB_KEY. The public key is embedded into the TRUSTED_OS_FW_KEY_CERT.

--bl32_key

keys/bl32_ecdsa.pem

Not in use for LAN969x, but part of TF-A framework.

--bl33_key

keys/bl33_ecdsa.pem

Confidential key used to sign the bl33 blob (BL33_IMAGE_ID). This file contains the NON_TRUSTED_FW_CONTENT_CERT_PRIV_KEY and the NON_TRUSTED_FW_CONTENT_CERT_PUB_KEY. The public key is embedded into the NON_TRUSTED_FW_KEY_CERT.

--non_trusted_world_key

keys/non_trusted_world_ecdsa.pem

Confidential key used to sign the NON_TRUSTED_FW_KEY_CERT certificate. This file contains the NON_TRUSTED_WORLD_PRIV_KEY and the NON_TRUSTED_WORLD_PUB_KEY. The public key is embedded into the TRUSTED_KEY_CERT.

--scp_bl2_key

keys/scp_bl2_ecdsa.pem

Not in use for LAN969x, but part of TF-A framework.

--trusted_world_key

keys/trusted_world_ecdsa.pem

Confidential key used to sign the TRUSTED_OS_FW_CONTENT_CERT certificate.

The list of keys above are provided in demo versions as part of the sources and build artifacts. The demo keys are not kept confidential (in fact they are published), and must be replaced with a new set of keys.

To generate new keys, delete they above keys in the ./keys folder, and run the build script with the --create_keys options, like show below:

$ dr ./scripts/build.rb --create_keys
The tool will not update existing keys. Existing keys must be deleted before new keys can be generated.

Check the time stamp and the content of the newly generated keys to confirm that they have been updated.

The key content can be show using the openssl command:

$ dr openssl ec -in ./keys/rotprivk_ecdsa.pem -text
read EC key
Private-Key: (256 bit)
priv:
    cb:eb:29:43:74:12:54:96:43:e6:42:48:13:8f:5b:
    cf:8d:c7:8b:da:b8:c8:64:ae:f7:43:c1:94:ce:a6:
    11:fb
pub:
    04:02:a6:68:7e:f2:fe:c9:07:1a:2c:a9:c2:33:b4:
    bb:89:34:99:7d:b6:30:f9:1f:6c:a8:ae:5c:6d:c5:
    28:04:48:47:66:1e:e4:36:26:ad:5d:1d:83:45:41:
    7d:f2:62:e6:e1:a9:2e:d1:c7:cd:0e:c2:62:01:db:
    94:53:ca:46:c5
ASN1 OID: prime256v1
NIST CURVE: P-256
writing EC key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIMvrKUN0ElSWQ+ZCSBOPW8+Nx4vauMhkrvdDwZTOphH7oAoGCCqGSM49
AwEHoUQDQgAEAqZofvL+yQcaLKnCM7S7iTSZfbYw+R9sqK5cbcUoBEhHZh7kNiat
XR2DRUF98mLm4aku0cfNDsJiAduUU8pGxQ==
-----END EC PRIVATE KEY-----

2.3.4. Rollback protection (NV-Counters)

By default the build script is using the default trusted and non-trusted counters for the platform. (2 and 3 for LAN969X).

In order to create a new firmware version that cannot be downgraded, you can set the specifically by incrementing the counter for the environment that has been fixed.

Use

  • --fw-nvctr <counter> to explicitly set the "Trusted FW NV counter"

  • --ntfw-nvctr <counter> to explicitly set the "Non-Trusted FW NV counter"

to explictly set the version counter embedded in the FIP.

Example:

$ dr ./scripts/build.rb -p lan969x_a0 --fw-nvctr 3 --ntfw-nvctr 4

2.3.5. Image encryption

By default images are signed but not encrypted. Image encryption can be enabled using the --encrypt-images <imagelist> argument. The listed images will then be encrypted using the key provided with --encrypt-ssk <keyfile>.

Example:

$ dr ./scripts/build.rb -p lan969x_a0                  \
                        --encrypt-images BL2,BL32,BL33 \
                        --encrypt-ssk ./keys/ssk.bin

The ssk key is symmetric, which means that the same key used for encryption must also be programmed into the OTP (OTP_TBBR_SSK) where it is used for decryption. The key can be any 32byte value, and is typically randomly generated.

The build.rb tool offer a --encrypt-bssk flag. This is for debug/development usage and should never be used in production. Instead the image should be bound by the bl2u utility or via bl32 using the HUK generated in OTP.

2.3.6. Image generation

The build.rb script will always build a FIP image. This FIP can be programmed directly into the NOR image, but to be able to boot from eMMC, the eMMC image needs to be prepared with a partition table (gpt) , and the FIP needs to be written into a partition (as opposed to being written to a fixed offset in the eMMC). The build script can prepare a complete image file with gpt partition table and the FIP image filled in the fip and the fip.bak partition.

the -n, --[no-]norimg is only for internal prototypes and should not be used.

3. TF-A in Details

This chapter provides a more in-depth description of TF-A technology used in LAN969x. Understanding these details are the essential prerequisites to design secure products based on LAN969x, and to understand the following sections with LAN969x specific implementation.

3.1. Certificate hierarchy

TF-A uses a hierarchy of certificates to authenticate the different images in the FIP. This hierarchy is shown at the following image:

Bootflow

As shown on the drawing, BL2 is authenticated by TRUSTED_BOOT_FW_CERT. TRUSTED_BOOT_FW_CERT is authenticated by the root of trust (the ROT_PUB_KEY). And the ROT_PUB_KEY is finally authenticated by the OTP SHA provided by the platform.

The key hierarchy limits the usage of the root key, and therefore also the attack surface. If one of the downstream keys is leaked, it is possible to do key revocation.

3.2. Revocation and Rollback protection

Besides the certificate based authentication, the authentication framework will also check the version number of the certificates against a non-volatile counter, provided by the platform. The version number in the certificate is covered by the signature which ensures the integrity, and the platform provides non-volatile counters that are created in such way that they can only increment.

The authentication process will first validate the signature and then continue to compare the version of the certificate against the platform counter. Here is how the comparison is performed:

  • certificate_version == platform_counter: All is good: The boot process can continue.

  • certificate_version > platform_counter: The platform_counter will be incremented to the value of certificate_version: The boot process can continue.

  • certificate_version < platform_counter: Fail as this is considered a rollback attack: The boot process will stop here, and the system needs to be updated with a new image matching the version of the platform counter.

The purpose of this mechanism is to have a way of performing revocation of certificates. If one of the private keys has been revealed, new certificates needs to be issued and provisioned. But the old firmware will still pass the public key authentication, and the only way to prevent an attacker from rolling back the firmware, is to have such a counter.

The ROT (root of trust) key does not have any version numbers as it cannot be updated.

TF-A uses 2 none-volatile counters: trusted_nv_ctr and non_trusted_nv_ctr. The non_trusted_fw_key_cert and non_trusted_fw_content_cert certificates are checked against the non_trusted_nv_ctr, all other (except ROT) are evaluated against trusted_nv_ctr.

3.3. Image signing in details

To sign a certificate the private key is needed, but to authenticate a certificate only the public key is needed. The cert_create tool needs access to both the public and private keys. The public keys are needed, because they are aggregated into the certificates, and the private keys are needed to do the signing.

The following picture illustrates the entire signing process done by cert_create.

Bootflow

The green boxes are binaries generated by the compiler. cert_create will calculate the SHA, feed the SHA into the certificate, and sign the certificate. The trusted and non-trusted counters is version numbers. These version numbers are given at the command-line, and will be stored in the certificates as well. The trust hierarchy is created by feeding the SHA of 1 public key, into a parent certificate. The 2 certificates: TRUSTED_BOOT_FW_CERT and TRUSTED_KEY_CERT are authenticated by the root-of-trust public certificate.

3.4. BL1 Chain of Trust verification

When BL1 loads the BL2 and the FW_CONFIG images from the FIP, it needs to authenticate the images, and the related certificates to establish a chain of trust. This is shown in the image below:

Bootflow

3.5. BL2 Chain of Trust verification

When BL2 loads the BL31 and BL33 images from the FIP, it needs to authenticate the images, and the related certificates to establish a chain of trust. This is shown in the image below:

Bootflow

3.6. Firmware encryption

Firmware encryption can be used to keep the firmware confidential, and protect against an attacker reading the content of the flash devices and extracting information from that.

When enabled, firmware will be encrypted using AES-128-GCM, which uses symmetric keys.

Two types of firmware keys are supported:

  1. ssk (secret symmetric key): Here the same key is used to encrypt the firmware (when creating the FIP image) and decrypting the firmware when loading running on the target.

  2. bssk (binding secret symmetric key): Here the ssk is used when encrypting the image and generating the FIP. But when the image is loaded on the board, it is bound to the board before it is being programmed to the flash. The binding operation will use the ssk to decrypt the image in memory, and then encrypt the image using a key derived from the huk (hardware unique key). When the system is booting, the BL1/BL2 will decrypt the image using the key derived from the huk.

Only the individual images are encrypted, but the FIP container stays unencrypted. This is not a security risk, as the FIP is just a container that does not have any sensitive information.

When images are encrypted an encryption header is prepended to the image. This header contains a flag indicating if the image has been bound or not. (Indicating using ssk or bssk).

Using bssk for firmware encryption instead of ssk have the following advantages:

  • Firmware cannot be copied/cloned from one board to another using an external flash programmer. If the firmware are licensed to a specific board, then it is an advantage to ensure that board firmware cannot be copied.

  • If the ssk is leaked, an attacker cannot decrypt the image found in the flash memory of a board - the attacker will have to find another way to get the image before it is programmed (intercept an update session).

  • If a bssk key is leaked, it will only impact one board.

The disadvantage of using bssk is the additional complications of manufacturing the boards, as it is not possible to do this with pre-programmed images.

Firmware binding can be done from the fwu.html tool.

4. LAN969x TF-A Platform support

The core of TF-A is platform independent, and each platform needs to provide the platform specific implementation and drivers.

The platform specific layer defines how the SW (and ROM) interface with the hardware, and how the keys in the system are kept safe.

4.1. Strapping modes

See table in LAN969x Strapping Pins

4.2. Booting

See this section and the following.

4.3. OTP

The one-time-programmable memory provided by the SoC is an important source of configuration data. It is the only persistent memory not depending on any external components and it is used for many different purposes.

The OTP can be divided into regions, and each region can be write protected, and if write-protected, also read-protected.

The OTP contains secret keys, which must be protected to keep firmware confidential. The offered mechanism to keep such keys confidential is the read-protect setting on the given region. The region containing the keys must be marked as read-protected by the BL2 before it hands over control to BL31.

Each OTP region contains a number of OTP attributes, which may then contain a number of OTP fields.

This is all covered in the following subsections.

4.3.1. OTP Regions

The OTP shall be divided into the following 8 regions:

REG.  START   END     Secure  Emu  Description
----  ------  ------  ------  ---  -----------------------------------------
0     0x0000  0x003F  No      No   Manufacturing data
1     0x0040  0x0043  No      No   OTP Write protect
2     0x0044  0x0063  No      No   OTP Region Definitions
3     0x0064  0x00FF  No      No   Insecure configuration without emulation
4     0x0100  0x01FF  Yes     Yes  Keys and other security related
5     0x0200  0x023F  No      Yes  Non-volatile secure counters
6     0x0240  0x07FF  No      No   Fixed position user-space / custom usage
7     0x0800  0x2000  No      No   Key-value store (semi-updateable)

The secure flag indicates if the area contains secret information, and hence must be configured to remove read access before leaving the BL2. The Emu flag indicate if OTP emulation can be supported.

The following sub-section documents the intended use of the regions, and highlights some important points for each of these.

The configuration of OTP-regions are kept in the OTP itself. It is the customer responsibility to program the regions. The Linux user-space otp tool can be used for this purpose.

4.3.2. OTP attributes

The following OTP table shows the pre-defined OTP attributes, used by either HW, BL1 and/or BL2.

BL3x, U-Boot, Linux or other clients can also access the OTP, if the access control of the given regions allow it.

Begin    End     Size  RTL  EMU  REG.  Name
------   ------  ----  ---  ---  ----  ----------------
0x0000   0x0003     4    X          0  OTP_PRG
0x0004   0x0004     1    X          0  FEAT_DIS
0x0005*  0x0006     2    X          0  PARTID
0x0007   0x0007     1               0  TST_TRK
0x0008   0x000f     8               0  SERIAL_NUMBER
0x0010   0x0013     4    X          0  SECURE_JTAG
0x0014   0x001a     7               0  WAFER_TRK
reserved 5 bytes
0x0020   0x0029    10    X          0  JTAG_UUID
reserved 6 bytes
0x0030   0x0037     8               0  TRIM
reserved 8 bytes
0x0040   0x0043     4    X          1  PROTECT_OTP_WRITE
0x0044   0x0063    32    X          2  PROTECT_REGION_ADDR
0x0064   0x0067     4               3  Reserved
0x0068   0x006B     4               3  Reserved
0x006c   0x0073     8               3  Reserved
0x0074   0x009b    40               3  Reserved
reserved 100 bytes
0x0100   0x011f    32         X     4  OTP_TBBR_ROTPK
0x0120   0x013f    32         X     4  OTP_TBBR_HUK
0x0140   0x015f    32         X     4  OTP_TBBR_EK
0x0160   0x017f    32         X     4  OTP_TBBR_SSK
0x0180   0x019f    32         X     4  OTP_SJTAG_SSK
reserved 4 bytes
0x01a4   0x01a5     2         X     4  OTP_STRAP_DISABLE_MASK
reserved 90 bytes
0x0200   0x021f    32         X     5  OTP_TBBR_NTNVCT
0x0220   0x023f    32         X     5  OTP_TBBR_TNVCT

In the above table the RTL column indicate that the OTP value is read and used by the digital HW in the SoC before SW is running (under reset).

4.3.3. OTP attribute: OTP_STRAP_DISABLE_MASK

This attribute is a 16 bit mask. Each bit in the mask corresponds to one of the strapping modes. By default the mask is set to all zeroes which means that all strapping modes are allowed. If a customer wants to disable one or more strapping modes the corresponding bits can be set in the mask and the ROM code will refuse to boot into the indicated modes.

4.3.4. OTP attribute: TRIM

This 8 byte attribute consists of a set of bitfields that are programmed in the factory at production time, based on measurements on the device.

These values will be read at boot and applied as 'corrections' to various hardware features.

This is the table of bitfields in the TRIM attribute:

Bits Name Description

63:43

Reserved

Not programmed

42:37

UVOV_GPIOB_TRIM

Trimming value for UVOV_GPIOB. Corresponding register: UVOV:UVOV_CFG[0]

36:31

UVOV_BOOT_TRIM

Trimming value for UVOV_BOOT. Corresponding register: UVOV:UVOV_CFG[1]

30:25

UVOV_RGMII_TRIM

Trimming value for UVOV_RGMII. Corresponding register: UVOV:UVOV_CFG[4]

24:19

UVOV_GPIOA_TRIM

Trimming value for UVOV_GPIOA. Corresponding register: UVOV:UVOV_CFG[5]

4.4. Firmware Config (FW_CONFIG)

In LAN969x FW_CONFIG is a binary blob, created during firmware compilation.

The format of the blob is as follows:

Byte range   Size    Description
===========  ======  ==================
0x000-0x17F  0x0180  OTP-Emulation data
0x180-0x1FF  0x0080  Config space

The image must at-least be at 512 (0x200) bytes. Only the first 512 bytes are used by BL1. The image can be extended at a later point in time, but this will only affect the BL2 usage.

The OTP Emulation portion is covered in details in the OTP Emulation section.

The content of the configuration space is generated based on the content from the scripts/fw_data.yaml file.

- field: LAN969X_FW_CONF_MMC_CLK_RATE
  size: 32
  value: 25000000
- field: LAN969X_FW_CONF_MMC_BUS_WIDTH
  size: 8
  value: 0      # MMC_BUS_WIDTH_4
- field: LAN969X_FW_CONF_QSPI_CLK
  size: 8
  value: 30
- field: LAN969X_FW_CONF_MMC_SPEED_MODE
  size: 8
  value: 3      # SDMMC_TIMING_HS
- field: LAN969X_FW_CONF_MMC_TX_TUNING_PHASE
  size: 8
  value: 12     # Maserati HW Tx phase default value

4.4.1. OTP Emulation

OTP Emulation is a facility to experiment with OTP settings, without making them permanent. The settings are stored in flash memory, and logical or-ed with the data from the HW OTP. If the HW OTP is empty (all zeros), then all bits can be emulated.

The OTP emulation loads data from the FW_CONFIG_ID image.

This feature is intended for 2 scenarios:

  • Developers wanting to try out many different OTP configurations, but only having a limited amount of boards: OTP emulation makes it possible to test many new sets of configurations before selecting the optimal set.

  • Users that are about to program the OTP, but want to test out the settings before committing changes that cannot be reversed.

The OTP emulation data can be comitted using the fwu.html tool, or manually applied using the Linux otp command.

Only the fields marked in the EMU column in the OTP attributes table can be emulated.

4.4.2. Loading of FW_CONFIG_ID

Loading the FW_CONFIG_ID image is tricky because:

  • When the board is provisioned for secure boot, this image must be authenticated.

  • This image includes OTP emulation data, which may influence if the board shall be considered as provisioned for secure boot.

The procedure to load this image is illustrated at the figure below:

The procedure to load FW_CONFIG_ID

4.5. Secure RAM

To ensure that keys are kept private, and that the execution of BL1, BL2 and BL31 cannot be tampered with these images must be run from secure memory.

LAN969x have 2MB of on-chip SRAM (Secure RAM), and on-the-fly DDR memory encryption.

BL1 executes from ROM directly, but uses the SRAM for static RW data and stack. BL1 will then load BL2 from flash memory into SRAM, and BL2 will then execute from SRAM and also use it for its static RW data and stack.

BL2 will initialize the DDR memory, and setup a secure and a non-secure partition. The secure partition uses on-the-fly DDR encryption, and can only be accessed from the secure world. Once completed the BL2 loads the BL31 from flash memory into the secure DDR memory.

DDR encryption causes a longer latency to the DDR memory, and is therefore not used for the entire DDR memory.

BL31, which is executing from secure DDR memory, will then wipe the contents of the SRAM, and reconfigure the Trust Zone Controller to let all of the SRAM be accessible from the Non-Secure world.

This is important for users of the RTE, as the RTE feature needs access to the SRAM.

4.6. Secure JTAG

LAN969x supports a secure JTAG port which is configured via the SECURE_JTAG OTP attribute. The data-sheet documents the encoding of SECURE_JTAG. Once programmed the JTAG port HW will wake up in one of the following modes:

  1. Open: The JTAG port is operating normally.

  2. Secure mode 1: The JTAG port will respond to a boundary scan, but is otherwise unresponsive (locked down).

    1. In this mode it is possible to unlock the port (move the JTAG port to the open state) using a challenge-response mechanism running over JTAG.

  3. Secure mode 2: The JTAG port will be completely unresponsive (locked down).

    1. In this mode it is possible to unlock it (move it to open state) using a challenge-response mechanism running over JTAG.

  4. Closed: The JTAG port is disabled and cannot be enabled.

If one of the secure-modes is enabled, then the JTAG port can be unlocked using a challenge/response. The challenges is a random 32 byte blob generated by the LAN996x device, and the response to unlock is a sha256(challenge-data
OTP_SJTAG_SSK)
(where OTP_SJTAG_SSK is the content of the OTP field, and + is a concatenation of the 2 blobs).

The scripts/sjtag-challenge.rb in the TF-A repository can be used to calculate the response to a challenge.

The fwu.html tool can be used to generate the challenge and unlock the JTAG port if the correct response is given.

5. Guidelines for preparing secure artifacts

The TF-A project for LAN969x provides a template to lower the effort to build a set of secure artifacts. But the deliverables from Microchip are from an open and transparent system, which with a few changes can be turned into a secure set of artifacts.

Following is a set of guidelines on how to turn the open-system into a set of secure boot artifacts:

  1. Understand what you are doing, and do not blindly trust us.

  2. Generate new keys and keep the private and the shared/symmetric keys secret (but still available as they will be needed to build potential new versions).

    1. Review how keys are generated, and evaluate if the methods provided in here are good enough for you need.

    2. How to keep keys secret is beyond the scope of this document, but look for good praxis in the industry.

  3. Use the certificate hierarchy to limit the exposure of the private keys. The root-of-trust should only be used very rarely and can be kept offline.

  4. Only increment the NV counters when a released version poses a security breach.

  5. The BL2U functionality provided in this project is a potential security breach, as it can expose the content of the OTP. Do one of the following:

    1. Change it to make it such that it will not be able to dump the OTP content.

    2. Do not generate it (delete it after each build).

    3. Treat it as a shared/symmetric key as it can be used to read out the keys from OTP. Do not give it to anyone who do not have these keys already.

6. Guideline Secure provisioning

To build a secure product the manufacturing procedure must include the steps to program the OTP with needed keys and settings.

A pre-request for this is to generate the keys and secure artifacts as explained in Secure artifacts
  1. Move to a trusted environment, where private and shared keys can be handled without risk of leaks.

  2. Initialize the OTP with the following content (either via BL2U / fwu.html or my using the otp Linux user-space utility.

    1. Regions must be defined (program PROTECT_REGION_ADDR)

    2. Following keys shall be programmed with the prepared content: OTP_TBBR_ROTPK, OTP_TBBR_SSK and OTP_SJTAG_SSK.

    3. Enable secure JTAG by programming SECURE_JTAG.

    4. Program the OTP_TBBR_HUK with unknown random data.

    5. Optional: disable any strap modes not needed by writing OTP_STRAP_DISABLE_MASK.

    6. Write protect region 4 by updating the content of PROTECT_OTP_WRITE.

      1. IMPORTANT: This is not only for write-protect, this is also needed to prevent non-secure read-access of region 4.

      2. NOTE: Additional regions can be write protected, but region 5 must remain writeable!

  3. Provision the firmware image in flash as needed.

7. TERMS and ABBREVIATIONS

AES             Advanced Encryption Standards
ATE             Automatic Test Equipment. Device used in SoC production and test.
BL1             Boot Loader 1: In secure immutable ROM (Trusted ROM Firmware)
BL2             Boot Loader 2: In secure on-chip RAM, temporary (Trusted Boot
                Firmware)
BL31            Boot Loader 31: Resident secure services (ARM v8)
BL32            Boot Loader 32: Resident secure services (ARM v7)
BL33            Boot Loader 33: Typically U-Boot.
                trusted Firmware)
BSSK            Binding Secret Symmetric Key
DT              Device Tree
eMMC            Embedded MultiMediaCard
FIP             Firmware Image Package
FIT             Flattened Image Tree
GPT             GUID Partition Table
HUK             Hardware Unique Key
JTAG            Joint Test Action Group, interface for testing and debugging
NS              Non-Secure World
OP-TEE          Open Portable Trusted Execution Environment
OS              Operation System
OTP             One-Time-Programmable Memory
PCIe            Peripheral Component Interconnect Express
PSCI            Power State Coordination Interface
RNG             Random Number Generator
ROT             Root of Trust
RTE             Real-Time Engine. Device used for real-time equipment control.
RTL             Register-Transfer Level
S               Secure World
SD-Card         Secure Digital Card
SHA             Secure Hash Algorithm family
SJTAG           Secure JTAG
SPI             Serial Peripheral Interface
SRAM            Secure Ram
SSK             Secret Symmetric Key
TBBR            Trusted Board Boot Requirements
TF-A            Trusted Firmware for ARM
UART            Universal Asynchronous Receiver-Transmitter

8. License

This document is provided under the BSP-3-Clause license (where swoftware is replaced with document). The BSD-3-Clause license has been chosen here as it is the same license used by the TF-A project.

Here is the license covering this file/document:

Copyright (c) 2022, Microchip Technology Inc. and its subsidiaries.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

-  Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

-  Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

-  Neither the name of Arm nor the names of its contributors may be used to
endorse or promote products derived from this document without specific
prior written permission.

THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
DOCUMENT, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.