nRF54L15 + NCS 3.2.3: Secure boot chain with upgradable MCUboot fails in B0/NSIB validation

Hi,

I am trying to set up a secure boot chain on nRF54L15dk with NCS 3.2.3 and an upgradable bootloader.

My intended boot chain is:

  • B0 / NSIB
  • S0 / S1 for the second-stage bootloader (MCUboot, so that MCUboot itself is upgradable)
  • mcuboot_primary / mcuboot_secondary for the application

The application secondary slot is located in external flash.

I am currently using a static partition layout and building with sysbuild.

b0:
  address: 0x00000
  region: flash_primary
  size: 0x0C000

provision:
  address: 0x0C000
  region: flash_primary
  size: 0x04000

b0_container:
  address: 0x00000
  region: flash_primary
  size: 0x10000
  span:
    - b0
    - provision

s0_pad:
  address: 0x10000
  region: flash_primary
  size: 0x0800

mcuboot:
  address: 0x10800
  region: flash_primary
  size: 0x0F800

s0_image:
  address: 0x10800
  region: flash_primary
  size: 0x0F800
  span:
    - mcuboot

s0:
  address: 0x10000
  region: flash_primary
  size: 0x10000
  span:
    - s0_pad
    - s0_image

s1_pad:
  address: 0x20000
  region: flash_primary
  size: 0x0800

s1_image:
  address: 0x20800
  region: flash_primary
  size: 0x0F800

s1:
  address: 0x20000
  region: flash_primary
  size: 0x10000
  span:
    - s1_pad
    - s1_image

mcuboot_pad:
  address: 0x30000
  region: flash_primary
  size: 0x0800

mcuboot_primary:
  address: 0x30000
  region: flash_primary
  size: 0x0E4000
  span:
    - mcuboot_pad
    - app

mcuboot_primary_app:
  address: 0x30800
  region: flash_primary
  size: 0x0E3800
  span:
    - app

settings_storage:
  address: 0x155000
  region: flash_primary
  size: 0x10000

mcuboot_secondary:
  address: 0x000000
  region: external_flash
  size: 0x0E4000

external_reserved:
  address: 0x0E4000
  region: external_flash
  size: 0x0DC000

littlefs_storage:
  address: 0x1C0000
  region: external_flash
  size: 0x040000


My sysbuild.conf:

# Enabele partition manager for sysbuild multi-image support
SB_CONFIG_PARTITION_MANAGER=y

# Immutable first stage + upgradable second stage
SB_CONFIG_SECURE_BOOT_APPCORE=y
SB_CONFIG_BOOTLOADER_MCUBOOT=y

# Put the application secondary slot into external SPI NOR
SB_CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y
SB_CONFIG_PM_OVERRIDE_EXTERNAL_DRIVER_CHECK=y

# nRF54L secure boot / signing
# Use real keys in production. Absolute paths are the least painful.
SB_CONFIG_SECURE_BOOT_SIGNATURE_TYPE_ED25519=y
#SB_CONFIG_BOOT_SIGNATURE_TYPE_ED25519=y

# Use swap-based updates, which are more robust than overwrite-based updates.
SB_CONFIG_MCUBOOT_MODE_OVERWRITE_ONLY=y
#SB_CONFIG_MCUBOOT_MODE_SWAP_USING_MOVE=y

SB_CONFIG_SECURE_BOOT_SIGNING_KEY_FILE="b0_dev_key.pem"

# Optional, but recommended on nRF54L if you want MCUboot to validate
# with keys provisioned in KMU too, instead of only compiled-in keys.
#SB_CONFIG_MCUBOOT_SIGNATURE_USING_KMU=y

# Helpful during development:
# generate keyfile.json so provisioning can be tied to flashing
#SB_CONFIG_SECURE_BOOT_GENERATE_DEFAULT_KMU_KEYFILE=n
#SB_CONFIG_MCUBOOT_GENERATE_DEFAULT_KMU_KEYFILE=y

# Protect S0/S1 bootloader updates in B0
SB_CONFIG_SECURE_BOOT_MCUBOOT_VERSION="1.0.0+0"
SB_CONFIG_SECURE_BOOT_MONOTONIC_COUNTER=n

# Do NOT protect application downgrades in MCUboot
#SB_CONFIG_MCUBOOT_HARDWARE_DOWNGRADE_PREVENTION=n
# SB_CONFIG_MCUBOOT_HW_DOWNGRADE_PREVENTION_COUNTER_VALUE=1

My b0.conf:


# b0 logging
CONFIG_LOG=y
CONFIG_LOG_MODE_MINIMAL=y
CONFIG_SECURE_BOOT_VALIDATION_LOG_LEVEL_INF=y

CONFIG_CONSOLE=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_RTT_CONSOLE=y
CONFIG_UART_CONSOLE=n

# Flash support for secure bootloader
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y

# Disable SPI and SPI NOR in b0
CONFIG_SPI=n
CONFIG_SPI_NOR=n
#CONFIG_PM_DEVICE=n

Goal

I want:

  1. B0/NSIB as first-stage bootloader
  2. MCUboot as upgradable second-stage bootloader in S0/S1
  3. Application in mcuboot_primary
  4. Application update image in mcuboot_secondary in external flash

Current configuration idea

I currently want to use:

  • Ed25519 key for NSIB/B0
  • MCUboot signed with your dev key
  • No KMU for MCUboot yet
  • KMU only where required for NSIB/B0 on nRF54L15

I also temporarily disabled the monotonic counter to simplify bring-up.

Problem

Initially I got this error:

00> Fprotect disabled. No protection applied.
00> Attempting to boot slot 0.
00> Attempting to boot from address 0x10800.
00> I: Trying to get Firmware version
00> E: Error getting monotonic counter
00> E: Cannot read the firmware version. -22
00> Failed to validate, permanently invalidating!
00> Attempting to boot slot 1.
00> Attempting to boot from address 0x20800.
00> I: Trying to get Firmware version
00> E: Error getting monotonic counter
00> E: Cannot read the firmware version. -22
00> Failed to validate, permanently invalidating!
00> No bootable image found. Aborting boot.

After disabling the monotonic counter, I now get:

00> Attempting to boot from address 0x10800.
00> I: Trying to get Firmware version
00> E: verify_public_keys() returned -113.
00> I: A public key contains 0xFFFF, which is unsupported
00> Failed to validate, permanently invalidating!
00> Attempting to boot slot 1.
00> Attempting to boot from address 0x20800.
00> I: Trying to get Firmware version
00> E: verify_public_keys() returned -113.
00> I: A public key contains 0xFFFF, which is unsupported
00> Failed to validate, permanently invalidating!
00> No bootable image found. Aborting boot.

What I tried:

west build -> ok

nrfjprog --recover -> ok

build$ west ncs-provision upload -s nrf54l15 -k ../b0_dev_key.pem --keyname BL_PUBKEY
nrfutil device x-provision-keys --key-file /tmp/nrfutil__o9cxm52/keyfile.json --traits jlink
Uploaded! -> ok

west flash -> ok

I also tried to use different ED25519 keys.

I also tried to use: 

SB_CONFIG_SECURE_BOOT_GENERATE_DEFAULT_KMU_KEYFILE=y -> reflash with erase but still the same issue.
I also compared the keys:
diff build/nrf/subsys/bootloader/generated/public.pem <(openssl ec -in b0_dev_key.pem -pubout) -> they are ident

If you could help me out here, I would be very happy.

Thanks,

Daniel

  • EDIT: I resolved the issue: 

    At first, it looked like a key generation or provisioning problem, but the real root cause was my pm_static.yml. I had placed the provision partition in flash_primary, while the working Nordic layout expects it in the otp region for this setup. Because of that, B0/NSIB effectively read invalid or erased values like 0xFFFF from the provisioned data and rejected the images. After adjusting the partition layout to match the correctly generated one, the bootloader chain worked as expected.

Related