Immutable bootloader

The bootloader sample implements an immutable first stage bootloader that has the capability to verify and boot a second stage bootloader. If the second stage bootloader is upgradable, it can reside in one of two slots. In this case, the sample chooses the second stage bootloader with the highest version number.

See Secure bootloader chain for more information about the full bootloader chain. For information about creating a second stage bootloader, see MCUboot.

Overview

The bootloader sample provides a simple root of trust (RoT) as well as support for an upgradable second stage bootloader.

This is accomplished by the following steps:

  1. Lock the flash.

    To enable the RoT, the bootloader sample locks the flash that contains the sample bootloader and its configuration. Locking is done using the hardware that is available on the given architecture. For details on locking, see the Hardware flash write protection driver.

  2. Select the next stage in the boot chain.

    The next stage in the boot chain can be another bootloader or the application. When the bootloader sample is enabled and MCUboot is used as second stage bootloader, there are two slots in which the second stage bootloader can reside. The second stage bootloader in each slot has a version number associated with it, and the bootloader sample selects the second stage bootloader that has the highest version number.

  3. Verify the next stage in the boot chain.

    After selecting the image to be booted next, the bootloader sample verifies its validity using one of the provisioned public keys hashes. The image for the next boot stage has a set of metadata associated with it. This metadata contains the full public key corresponding to the private key that was used to sign the firmware. The bootloader sample checks the public key against a set of provisioned keys. Note that to save space, only the hashes of the provisioned keys are stored, and only the hashes of the keys are compared. If the public key in the metadata matches one of the valid provisioned public key hashes, the image is considered valid. All public key hashes at lower indices than the matching hash are permanently invalidated at this point, which means that images can no longer be validated with those public keys. For example, if an image is successfully validated with the public key at index 2, the public keys 0 and 1 are invalidated. This mechanism can be used to decommission broken keys. If the public key does not match any of the still valid provisioned hashes, validation fails.

  4. Boot the next stage in the boot chain.

    After verifying the next boot stage, the bootloader sample uninitializes all peripherals that it used and boots the next boot stage.

  5. Share the cryptographic library over EXT_API.

    The bootloader shares some of its functionality through an external API (EXT_API, see External APIs). For more information, see bl_crypto.h.

Flash layout

The flash layout is defined by samples/bootloader/pm.yml.

The bootloader sample defines four main areas:

  1. B0 - Contains the bootloader sample.

  2. Provision - Stores the provisioned data.

  3. S0 - One of two potential storage areas for the second stage bootloader.

  4. S1 - One of two potential storage areas for the second stage bootloader.

Provisioning

The public key hashes are not compiled in with the source code of the bootloader sample. Instead, they must be stored in a separate memory region through a process called provisioning.

By default, the bootloader sample will automatically generate and provision public key hashes directly into the bootloader HEX file, based on the specified private key and additional public keys. Alternatively, to facilitate the manufacturing process of a device with the bootloader sample, it is possible to decouple this process and program the sample HEX file and the HEX file containing the public key hashes separately. If you choose to do so, use the Python scripts in scripts\bootloader to create and provision the keys manually.

Note

On some chips (for example, nRF9160 or nRF5340), the provisioned data is held in the OTP region in UICR. Because of this, you must erase the UICR before programming the bootloader. On nRF9160, the UICR can only be erased by erasing the whole chip. To do so on the command line, call west flash with the --erase option. This will erase the whole chip before programming the new image. In SEGGER Embedded Studio, choose Target -> Connect J-Link and then Target -> Erase All to erase the whole chip.

Note

On some chips (for example, nRF9160 or nRF5340), the provisioned data is held in the OTP region in UICR. Because of this, the public key hash cannot contain half-words with the value 0xFFFF, because half-words are writeable when they are 0xFFFF, so such hashes cannot be guaranteed to be immutable. The bootloader will refuse to boot if any hash contains a half-word with the value 0xFFFF. If your public key hash is found to have 0xFFFF, please regenerate it or use another public key.

The bootloader uses the Bootloader storage library to access provisioned data.

Requirements

The sample supports the following development kits:

Hardware platforms

PCA

Board name

Build target

nRF9160 DK

PCA10090

nrf9160dk_nrf9160

nrf9160dk_nrf9160ns

nRF5340 DK

PCA10095

nrf5340dk_nrf5340

nrf5340dk_nrf5340_cpuapp

nrf5340dk_nrf5340_cpuappns

nRF52840 DK

PCA10056

nrf52840dk_nrf52840

nrf52840dk_nrf52840

nRF52 DK

PCA10040

nrf52dk_nrf52832

nrf52dk_nrf52832

Building and running

The source code of the sample can be found under samples/bootloader/ in the nRF Connect SDK folder structure.

The most common use case for the bootloader sample is to be included as a child image in a multi-image build, rather than being built stand-alone. Complete the following steps to add the bootloader sample as child image to your application:

  1. Create a private key in PEM format. To do so, run the following command, which stores your private key in a file name priv.pem in the current folder:

    openssl ecparam -name prime256v1 -genkey -noout -out priv.pem
    

    OpenSSL is installed with GIT, so it should be available in your GIT bash. See openSSL for more information.

    Note

    This step is optional for testing the bootloader chain. If you do not provide your own keys, debug keys are created automatically. However, you should never go into production with an application that is not protected by secure keys.

  2. Run menuconfig on your application to enable Secure Boot:

    1. Select Project -> Configure nRF Connect SDK project.

    2. Go to Modules -> Nordic nRF Connect and select Use Secure Bootloader to enable CONFIG_SECURE_BOOT.

    3. Under Private key PEM file (CONFIG_SB_SIGNING_KEY_FILE), enter the path to the private key that you created. If you choose to run the sample with default debug keys, you can skip this step.

      There are additional configuration options that you can modify, but it is not recommended to do so. The default settings are suitable for most use cases.

      Note

      If you need more flexibility with signing, or if you do not want the build system to handle your private key, choose CONFIG_SB_SIGNING_CUSTOM. This option allows you to define the signing command. In this case, you must also specify CONFIG_SB_SIGNING_COMMAND and CONFIG_SB_SIGNING_PUBLIC_KEY.

    4. Click Configure.

  3. Select Build -> Build Solution to compile your application. The build process creates two images, one for the bootloader and one for the application, and merges them together.

  4. Select Build -> Build and Run to program the resulting image to your device.

Testing

To test the bootloader sample, add it to any other sample and build and program that sample it as described above. Then test it by performing the following steps:

  1. Connect to the kit with a terminal emulator (for example, PuTTY). See How to connect with PuTTY for the required settings.

  2. Reset the board.

  3. Observe that the kit prints the following information:

    Attempting to boot from address 0x8000.
    
    Verifying signature against key 0.
    
    Signature verified.
    
    Booting (0x8000).
    

Dependencies

This sample uses the following nRF Connect SDK libraries:

The sample also uses drivers from nrfx.