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.
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:
- 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.
- 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.
- 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.
- 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.
- 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
The flash layout is defined by
The bootloader sample defines four main areas:
B0 - Contains the bootloader sample.
Provision - Stores the provisioned data.
S0 - One of two potential storage areas for the second stage bootloader.
S1 - One of two potential storage areas for the second stage bootloader.
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.
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 flashwith the
--eraseoption. 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.
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.
The sample supports the following development kits:
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:
Create a private key in PEM format. To do so, run the following command, which stores your private key in a file name
priv.pemin 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.
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.
menuconfigon your application to enable Secure Boot:
Select Project > Configure nRF Connect SDK project.
Go to Modules > Nordic nRF Connect and select Use Secure Bootloader to enable
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.
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.
Select Build > Build and Run to program the resulting image to your device.
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:
Connect to the board with a terminal emulator (for example, PuTTY). See How to connect with PuTTY for the required settings.
Reset the board.
Observe that the kit prints the following information:
Attempting to boot from address 0x8000. Verifying signature against key 0. Signature verified. Booting (0x8000).