Signing mcuboot, b0n and applications with custom private key on the nRF5340

Hi,

I used the default keys while developing my application. But for production, we want to use our proprietary key for the bootloader(s) and signing the application.

Our project uses mcuboot without TFM (CONFIG_BOOTLOADER_MCUBOOT=y, CONFIG_BUILD_WITH_TFM=n). We use both the APP core and the NET core (which runs b0n). All we want to do for now is sign our application binaries correctly.

I added the following configuration to my main project (prj.conf):

CONFIG_SB_SIGNING_KEY_FILE="C:/my_path/my_priv_key.pem"

I added the following code to child_image\mcuboot.conf:

CONFIG_BOOT_SIGNATURE_KEY_FILE="C:/my_path/my_priv_key.pem"
CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y

Question 1: CONFIG_SECURE_BOOT

When I add CONFIG_SB_SIGNING_KEY_FILE, I am forced to also add CONFIG_SECURE_BOOT=y. Why is this option needed? If I understand correctly, secure boot forces the application to be verified against its certificate each time before running. I didn't want to lose time during startup, I just need the image to be signed correctly for the flashing process. Or is CONFIG_SECURE_BOOT doing something else?

And just to be sure: Do I still only use mcuboot with the CONFIG_SECURE_BOOT option? No Secure Immutable Bootloader?

Question 2: Key error for hci_rpmsg

When the hci_rpmsg target for CPUNET is built, I get some strange error message which says I still use the wrong key:

      --------------------------------------------------------------
      --- WARNING: Using generated NSIB public/private key-pair. ---
      --- It should not be used for production.                  ---
      --- See CONFIG_SB_SIGNING_KEY_FILE                         ---
      --------------------------------------------------------------

It happens just after the b0n part within hci_rpmsg was built. Here is the complete log, for reference:

=== child image hci_rpmsg - CPUNET begin ===
loading initial cache file C:/my_proj_dir/build/hci_rpmsg/child_image_preload.cmake
Loading Zephyr default modules (Zephyr base).
-- Application: C:/ncs/v2.5.3/zephyr/samples/bluetooth/hci_rpmsg
-- CMake version: 3.20.5
-- Found Python3: C:/ncs/toolchains/c57af46cb7/opt/bin/python.exe (found suitable version "3.8.2", minimum required is "3.8") found components: Interpreter 
-- Cache files will be written to: C:/ncs/v2.5.3/zephyr/.cache
-- Zephyr version: 3.4.99 (C:/ncs/v2.5.3/zephyr)
-- Found west (found suitable version "1.1.0", minimum required is "0.14.0")
-- Board: nrf5340dk_nrf5340_cpunet
-- Found host-tools: zephyr 0.16.1 (C:/ncs/toolchains/c57af46cb7/opt/zephyr-sdk)
-- Found toolchain: zephyr 0.16.1 (C:/ncs/toolchains/c57af46cb7/opt/zephyr-sdk)
-- Found Dtc: C:/ncs/toolchains/c57af46cb7/opt/bin/dtc.exe (found suitable version "1.4.7", minimum required is "1.4.6") 
-- Found BOARD.dts: C:/ncs/v2.5.3/zephyr/boards/arm/nrf5340dk_nrf5340/nrf5340dk_nrf5340_cpunet.dts
-- Generated zephyr.dts: C:/my_proj_dir/build/hci_rpmsg/zephyr/zephyr.dts
-- Generated devicetree_generated.h: C:/my_proj_dir/build/hci_rpmsg/zephyr/include/generated/devicetree_generated.h
-- Including generated dts.cmake file: C:/my_proj_dir/build/hci_rpmsg/zephyr/dts.cmake
Parsing C:/ncs/v2.5.3/zephyr/Kconfig
Loaded configuration 'C:/ncs/v2.5.3/zephyr/boards/arm/nrf5340dk_nrf5340/nrf5340dk_nrf5340_cpunet_defconfig'
Merged configuration 'C:/ncs/v2.5.3/zephyr/samples/bluetooth/hci_rpmsg/prj.conf'
Merged configuration 'C:/ncs/v2.5.0/nrf/subsys/partition_manager/partition_manager_enabled.conf'
Merged configuration 'C:/ncs/v2.5.2/nrf/subsys/partition_manager/partition_manager_enabled.conf'
Merged configuration 'C:/ncs/v2.5.2/nrf/subsys/bootloader/image/secure_boot.conf'
Merged configuration 'C:/ncs/v2.5.3/nrf/subsys/bootloader/image/secure_boot.conf'
Merged configuration 'C:/ncs/v2.5.3/nrf/subsys/partition_manager/partition_manager_enabled.conf'
Merged configuration 'C:/my_proj_dir/build/hci_rpmsg/zephyr/misc/generated/extra_kconfig_options.conf'
Configuration saved to 'C:/my_proj_dir/build/hci_rpmsg/zephyr/.config'
Kconfig header saved to 'C:/my_proj_dir/build/hci_rpmsg/zephyr/include/generated/autoconf.h'
-- Found GnuLd: c:/ncs/toolchains/c57af46cb7/opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/12.2.0/../../../../arm-zephyr-eabi/bin/ld.bfd.exe (found version "2.38") 
-- The C compiler identification is GNU 12.2.0
-- The CXX compiler identification is GNU 12.2.0
-- The ASM compiler identification is GNU
-- Found assembler: C:/ncs/toolchains/c57af46cb7/opt/zephyr-sdk/arm-zephyr-eabi/bin/arm-zephyr-eabi-gcc.exe

=== child image b0n - CPUNET (inherited) begin ===
loading initial cache file C:/my_proj_dir/build/hci_rpmsg/b0n/child_image_preload.cmake
Loading Zephyr default modules (Zephyr base).
-- Application: C:/ncs/v2.5.3/nrf/samples/nrf5340/netboot
-- CMake version: 3.20.5
-- Found Python3: C:/ncs/toolchains/c57af46cb7/opt/bin/python.exe (found suitable version "3.8.2", minimum required is "3.8") found components: Interpreter 
-- Cache files will be written to: C:/ncs/v2.5.3/zephyr/.cache
-- Zephyr version: 3.4.99 (C:/ncs/v2.5.3/zephyr)
-- Found west (found suitable version "1.1.0", minimum required is "0.14.0")
-- Board: nrf5340dk_nrf5340_cpunet
-- Found host-tools: zephyr 0.16.1 (C:/ncs/toolchains/c57af46cb7/opt/zephyr-sdk)
-- Found toolchain: zephyr 0.16.1 (C:/ncs/toolchains/c57af46cb7/opt/zephyr-sdk)
-- Found Dtc: C:/ncs/toolchains/c57af46cb7/opt/bin/dtc.exe (found suitable version "1.4.7", minimum required is "1.4.6") 
-- Found BOARD.dts: C:/ncs/v2.5.3/zephyr/boards/arm/nrf5340dk_nrf5340/nrf5340dk_nrf5340_cpunet.dts
-- Generated zephyr.dts: C:/my_proj_dir/build/hci_rpmsg/b0n/zephyr/zephyr.dts
-- Generated devicetree_generated.h: C:/my_proj_dir/build/hci_rpmsg/b0n/zephyr/include/generated/devicetree_generated.h
-- Including generated dts.cmake file: C:/my_proj_dir/build/hci_rpmsg/b0n/zephyr/dts.cmake
Parsing C:/ncs/v2.5.3/nrf/samples/nrf5340/netboot/Kconfig
Loaded configuration 'C:/ncs/v2.5.3/zephyr/boards/arm/nrf5340dk_nrf5340/nrf5340dk_nrf5340_cpunet_defconfig'
Merged configuration 'C:/ncs/v2.5.3/nrf/samples/nrf5340/netboot/prj.conf'
Merged configuration 'C:/ncs/v2.5.3/nrf/subsys/partition_manager/partition_manager_enabled.conf'
Configuration saved to 'C:/my_proj_dir/build/hci_rpmsg/b0n/zephyr/.config'
Kconfig header saved to 'C:/my_proj_dir/build/hci_rpmsg/b0n/zephyr/include/generated/autoconf.h'
-- Found GnuLd: c:/ncs/toolchains/c57af46cb7/opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/12.2.0/../../../../arm-zephyr-eabi/bin/ld.bfd.exe (found version "2.38") 
-- The C compiler identification is GNU 12.2.0
-- The CXX compiler identification is GNU 12.2.0
-- The ASM compiler identification is GNU
-- Found assembler: C:/ncs/toolchains/c57af46cb7/opt/zephyr-sdk/arm-zephyr-eabi/bin/arm-zephyr-eabi-gcc.exe
CMake Warning at C:/ncs/v2.5.3/zephyr/CMakeLists.txt:1958 (message):
  __ASSERT() statements are globally ENABLED


-- Configuring done
-- Generating done
-- Build files have been written to: C:/my_proj_dir/build/hci_rpmsg/b0n
=== child image b0n - CPUNET (inherited) end ===

CMake Warning at C:/ncs/v2.5.3/nrf/subsys/bootloader/cmake/debug_keys.cmake:36 (message):
  

      --------------------------------------------------------------
      --- WARNING: Using generated NSIB public/private key-pair. ---
      --- It should not be used for production.                  ---
      --- See CONFIG_SB_SIGNING_KEY_FILE                         ---
      --------------------------------------------------------------
      

Call Stack (most recent call first):
  C:/ncs/v2.5.3/nrf/subsys/bootloader/cmake/provision_hex.cmake:47 (include)
  C:/ncs/v2.5.3/nrf/subsys/CMakeLists.txt:20 (include)


-- libmetal version: 1.4.0 (C:/ncs/v2.5.3/zephyr/samples/bluetooth/hci_rpmsg)
-- Build type:  
-- Host:    Windows/AMD64
-- Target:  Generic/arm
-- Machine: arm
-- Looking for include file stdatomic.h
-- Looking for include file stdatomic.h - found
-- open-amp version: 1.4.0 (C:/ncs/v2.5.3/modules/lib/open-amp/open-amp)
-- Host:    Windows/AMD64
-- Target:  Generic/arm
-- Machine: arm
-- C_FLAGS :  -Wall -Wextra
-- Looking for include file fcntl.h
-- Looking for include file fcntl.h - found
CMake Warning at C:/ncs/v2.5.3/nrf/cmake/partition_manager.cmake:79 (message):
  

          ---------------------------------------------------------------------
          --- WARNING: Using a bootloader without pm_static.yml.            ---
          --- There are cases where a deployed product can consist of       ---
          --- multiple images, and only a subset of these images can be     ---
          --- upgraded through a firmware update mechanism. In such cases,  ---
          --- the upgradable images must have partitions that are static    ---
          --- and are matching the partition map used by the bootloader     ---
          --- programmed onto the device.                                   ---
          ---------------------------------------------------------------------
          

Call Stack (most recent call first):
  C:/ncs/v2.5.3/zephyr/cmake/modules/kernel.cmake:247 (include)
  C:/ncs/v2.5.3/zephyr/cmake/modules/zephyr_default.cmake:138 (include)
  C:/ncs/v2.5.3/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:66 (include)
  C:/ncs/v2.5.3/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:92 (include_boilerplate)
  CMakeLists.txt:5 (find_package)


-- Configuring done
-- Generating done
-- Build files have been written to: C:/my_proj_dir/build/hci_rpmsg
=== child image hci_rpmsg - CPUNET end ===

(By the way, I already know about the pm_static issue for the NET core from my other ticket, I'm going to fix this later.)

So, what is the key related error message telling me? Do I have to add a key for signing the NET core separately to the configuration? How/where do I add it correctly? Maybe some additional configuration file in the child_image directory (e.g. hci_rpmsg.conf)? I couldn't find this in the documentation yet.

Question 3: Understanding key usage

Please help me understand the process of deployment. As far as I know:

  • mcuboot on the APP core needs the private key to recognize correctly signed images.
  • mcuboot checks both the app_update.bin and net_core_app_update.bin for correct signatures
  • both APP core and NET core binaries must be signed at the end of the build process. I assumed that CONFIG_SB_SIGNING_KEY_FILE in my prj.con would be used by the build system to sign both images, as signing is a separate action at the end of the build process and not during child image building. Is my assumption here wrong?
  • Big question: Is the signature for firmware updates checked exclusively by mcuboot, and b0n is not aware of the signature? Or will I have to add the private key information also to b0n when I use dual core MCU's like the nRF5340? (If so, how do I do it correctly?)

Question 4: Multiple keys for mcuboot

Is my assumption correct that mcuboot only supports one key? No multiple keys with key revoking possible like with the NSIB?

Question 5: Handling of invalid images by mcuboot

What does mcuboot do when I try to flash a wrongly signed image? I tried it out and it appeared that our system was not running for around 20 seconds (similar to the time it needs to flash a new firmware). But the firmware running afterwards was still the old firmware, and the secondary slot was empty after the old firmware started again. My assumption would be that mcuboot just erased all secondary slot pages (containing the invalid image). Is this correct? I was expecting to find some flags set by mcuboot if the new image was invalid, which would be simple feedback for a flashing application.

Finally, it would be really helpful if you could add the information how to set customer-specific key files correctly for multicore systems to the NCS documentation, e.g. here:
https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/config_and_build/bootloaders/bootloader_signature_keys.html

Best regards,
Michael

  • puz_md said:
    No key information is needed in the main application

    Your understanding is correct, just wnat to add that the build system will use the MCUBoot key to sign the application update image. This happens automatically has a part of the build process with the help of 'imgtool'.

    puz_md said:
    And if you use two different files for signing, does this mean that the NET core app is signed twice (one time with the SB_SIGNING_KEY for b0n and one time with the mcuboot signing key)?

    Yes, MCUBOOT metadata such as the image hash, etc is not transferred to the netcore. 

    puz_md said:
    Also thanks for the hint with CMakeLists.txt. I didn't know you could configure settings for child projects there. It seems like it does the same as creating chld_image/mcuboot.conf and hci_rpmsg.conf with the mentioned information.

    Yes, the end result is the same. You can check the generated configs to confirm that the path to the signature file is set correctly. E.g. in build/hci_rpmsg/zephyr/.config.

    Best regards,

    Vidar

Related