Skip to content

mcuboot: support multiple signing keys#1

Open
JPHutchins wants to merge 1103 commits into
mainfrom
mcuboot/multiple-signing-keys
Open

mcuboot: support multiple signing keys#1
JPHutchins wants to merge 1103 commits into
mainfrom
mcuboot/multiple-signing-keys

Conversation

@JPHutchins

@JPHutchins JPHutchins commented Apr 18, 2026

Copy link
Copy Markdown
Collaborator

What

SB_CONFIG_BOOT_SIGNATURE_KEY_FILE now accepts a comma-separated list of PEM paths
in addition to a single path. The MCUboot bootloader embeds the public half of every
listed key and accepts an image signed by any of them; the application is signed with
the first entry, and every entry past the first must be a public-only PEM (enforced at
build time with imgtool keyinfo --require public). The canonical use is a development
bootloader that boots both development- and production-signed images while never holding
the production private key.

Both the keys the bootloader embeds and the single key the application is signed with
derive from this one list, resolved through a single sysbuild helper
(sysbuild_mcuboot_resolve_signature_key_files()) against the application source
directory, so the application is always signed with exactly the key whose public half is
embedded — including for relative paths.

Dependency

Requires the multiple-key support in MCUboot (mcu-tools/mcuboot#2701, merged). The
manifest is pinned to zephyrproject-rtos/mcuboot@5497c95f.

Testing

  • tests/cmake/sysbuild_mcuboot_keys — HW-free unit test of the key-path resolver
    (absolute / relative / ${VAR} / multi-key / whitespace forms) at CMake configure time.
  • tests/boot/mcuboot_multiple_keys — runtime acceptance test on the mps2/an385 QEMU
    machine (pytest harness): a two-key bootloader boots an application signed with either
    embedded key (key_id 0 and key_id 1), with no DFU, swap or SMP.
  • tests/boot/test_mcuboot — gains a two-key bootloader swap scenario (hardware).
  • New samples/sysbuild/mcuboot_signing_keys demonstrates the development/production
    custody split.

@github-actions

github-actions Bot commented Apr 18, 2026

Copy link
Copy Markdown

The following west manifest projects have changed revision in this Pull Request:

Name Old Revision New Revision Diff
hal_adi zephyrproject-rtos/hal_adi@c8aa8d5 zephyrproject-rtos/hal_adi@35d874c (msdk-export) zephyrproject-rtos/hal_adi@c8aa8d54..35d874cc
hal_atmel zephyrproject-rtos/hal_atmel@8cd5750 zephyrproject-rtos/hal_atmel@8f95c79 (master) zephyrproject-rtos/hal_atmel@8cd57504..8f95c79f
hal_bouffalolab zephyrproject-rtos/hal_bouffalolab@6f8ee38 zephyrproject-rtos/hal_bouffalolab@f011e43 zephyrproject-rtos/hal_bouffalolab@6f8ee38a..f011e433
hal_espressif zephyrproject-rtos/hal_espressif@047d454 zephyrproject-rtos/hal_espressif@8b30e4e zephyrproject-rtos/hal_espressif@047d4547..8b30e4e3
hal_infineon zephyrproject-rtos/hal_infineon@d9e6846 zephyrproject-rtos/hal_infineon@a5c0f89 zephyrproject-rtos/hal_infineon@d9e68467..a5c0f895
hal_nordic zephyrproject-rtos/hal_nordic@1a6de0a zephyrproject-rtos/hal_nordic@18da0cc (master) zephyrproject-rtos/hal_nordic@1a6de0af..18da0cc9
hal_nxp zephyrproject-rtos/hal_nxp@0f7ae59 zephyrproject-rtos/hal_nxp@dd01dc6 (master) zephyrproject-rtos/hal_nxp@0f7ae59e..dd01dc65
hal_silabs zephyrproject-rtos/hal_silabs@12a1d77 zephyrproject-rtos/hal_silabs@d91d604 (main) zephyrproject-rtos/hal_silabs@12a1d77c..d91d6046
hal_stm32 zephyrproject-rtos/hal_stm32@ad6ac61 zephyrproject-rtos/hal_stm32@ff19fb0 zephyrproject-rtos/hal_stm32@ad6ac613..ff19fb0b
hal_xtensa zephyrproject-rtos/hal_xtensa@0495a1a zephyrproject-rtos/hal_xtensa@614d7dd (master) zephyrproject-rtos/hal_xtensa@0495a1af..614d7dd1
hostap zephyrproject-rtos/hostap@1eae1d6 zephyrproject-rtos/hostap@e97765d (main) zephyrproject-rtos/hostap@1eae1d6f..e97765d2
lvgl zephyrproject-rtos/lvgl@85aa60d zephyrproject-rtos/lvgl@bbedf26 (master) zephyrproject-rtos/lvgl@85aa60d1..bbedf265
mcuboot zephyrproject-rtos/mcuboot@511dc9e zephyrproject-rtos/mcuboot@0fae892 (upstream-sync) zephyrproject-rtos/mcuboot@511dc9e2..0fae8920
nrf_wifi zephyrproject-rtos/nrf_wifi@47bd5b4 zephyrproject-rtos/nrf_wifi@21184a8 (main) zephyrproject-rtos/nrf_wifi@47bd5b4e..21184a8f
percepio zephyrproject-rtos/percepio@f9159fe zephyrproject-rtos/percepio@57358a3 (main_zephyr) zephyrproject-rtos/percepio@f9159fe3..57358a35
trusted-firmware-a zephyrproject-rtos/trusted-firmware-a@4aef38a zephyrproject-rtos/trusted-firmware-a@b7025fb zephyrproject-rtos/trusted-firmware-a@4aef38a5..b7025fb9
trusted-firmware-m zephyrproject-rtos/trusted-firmware-m@ee0acbb zephyrproject-rtos/trusted-firmware-m@2b2b177 (zephyr_tf-m_v2.3.0) zephyrproject-rtos/trusted-firmware-m@ee0acbb1..2b2b1773

Additional metadata changed:

Name URL Submodules West cmds module.yml Blobs
hal_bouffalolab 30x ✏, 9x 🆕
hal_infineon 1x 🆕
hal_silabs 1x ❌, 1x 🆕

DNM label due to: 3 projects with metadata changes and 42 blob changes

Note: This message is automatically posted and updated by the Manifest GitHub Action.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Draft integration of MCUboot “multiple signing keys” support into Zephyr’s sysbuild flow, including new Kconfig/CMake plumbing, documentation, and a sample demonstrating the dual-key verification use case.

Changes:

  • Switch MCUboot module source to a fork/branch carrying the multi-key feature.
  • Add *_KEY_FILE_2 support across sysbuild Kconfig/CMake and expose it to app configuration.
  • Add a new sysbuild sample + docs/release-notes updates and a new test scenario.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
west.yml Points MCUboot module to a fork/feature branch for the multi-key work.
tests/boot/test_mcuboot/testcase.yaml Adds a new test variant enabling the second key setting.
tests/boot/test_mcuboot/root-ed25519-2.pem Adds an ED25519 key file used by the new test variant.
share/sysbuild/images/bootloader/Kconfig Introduces BOOT_SIGNATURE_KEY_FILE_2 and clarifies key path semantics.
share/sysbuild/images/bootloader/CMakeLists.txt Normalizes signing key paths (incl. second key) before passing to MCUboot image.
share/sysbuild/image_configurations/MAIN_image_default.cmake Forwards second key path into main app Kconfig (CONFIG_MCUBOOT_SIGNATURE_KEY_FILE_2).
share/sysbuild/image_configurations/FIRMWARE_LOADER_image_default.cmake Forwards second key path into firmware-loader app Kconfig.
scripts/ci/check_compliance.py Allows the new Kconfig symbol in compliance checks.
samples/sysbuild/mcuboot_dual_key/sysbuild/mcuboot.conf Sample-specific MCUboot config fragment.
samples/sysbuild/mcuboot_dual_key/sysbuild.conf Sysbuild configuration demonstrating dual accepted verification keys.
samples/sysbuild/mcuboot_dual_key/src/main.c Minimal app used by the new sysbuild sample.
samples/sysbuild/mcuboot_dual_key/sample.yaml Adds test definition for the new sample.
samples/sysbuild/mcuboot_dual_key/prj.conf Placeholder application config for the sample.
samples/sysbuild/mcuboot_dual_key/keys/prod_pubkey.pem Public-only PEM embedded as the “production” verification key in the sample.
samples/sysbuild/mcuboot_dual_key/README.rst End-to-end documentation for the dual-key sysbuild sample.
samples/sysbuild/mcuboot_dual_key/CMakeLists.txt Sysbuild-enabled sample CMake wiring.
modules/Kconfig.mcuboot Adds MCUBOOT_SIGNATURE_KEY_FILE_2 to expose the second key path to app builds.
doc/releases/release-notes-4.5.rst Release note entry for the new sysbuild option and sample.
doc/build/signing/index.rst Documents SB_CONFIG_BOOT_SIGNATURE_KEY_FILE_2 in the signing guide.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/boot/test_mcuboot/root-ed25519-2.pem Outdated
Comment thread samples/sysbuild/mcuboot_dual_key/README.rst Outdated
Comment thread west.yml Outdated
Comment thread tests/boot/test_mcuboot/testcase.yaml Outdated
@JPHutchins

Copy link
Copy Markdown
Collaborator Author

Good results by following the readme!

*** Using Zephyr OS build v4.4.0-3680-g5598d5b364cc ***
I: Starting bootloader
D: context_boot_go
I: Image index: 0, Swap type: none
D: boot_validate_slot: slot 0, expected_swap_type 0
D: bootutil_img_validate: flash area 0x8680
D: bootutil_img_hash
D: bootutil_tlv_iter_begin: type 65535, prot == 0
D: bootutil_img_validate: TLV off 25816, end 25956
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25816 ending at 25956
D: bootutil_tlv_iter_next: TLV 16 found at 25820 (size 32)
D: bootutil_img_validate: EXPECTED_HASH_TLV == 16
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25852 ending at 25956
D: bootutil_tlv_iter_next: TLV 1 found at 25856 (size 32)
D: bootutil_img_validate: EXPECTED_KEY_TLV == 1
D: bootutil_find_key
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25888 ending at 25956
D: bootutil_tlv_iter_next: TLV 36 found at 25892 (size 64)
D: bootutil_img_validate: EXPECTED_SIG_TLV == 36
D: bootutil_verify_sig: ED25519 key_id 0
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25956 ending at 25956
D: bootutil_tlv_iter_next: TLV 65535 not found
D: Left boot_go with success == 1
I: Bootloader chainload address offset: 0xc000
I: Image version: v0.0.0
I: Jumping to the first image slot
*** Booting Zephyr OS build v4.4.0-3680-g5598d5b364cc ***
[00:00:00.005,096] <inf> app: Address of sample 0xc000
[00:00:00.010,589] <inf> app: Hello mcuboot signing keys! nrf52840dk
*** Using Zephyr OS build v4.4.0-3680-g5598d5b364cc ***
I: Starting bootloader
D: context_boot_go
I: Image index: 0, Swap type: none
D: boot_validate_slot: slot 0, expected_swap_type 0
D: bootutil_img_validate: flash area 0x8680
D: bootutil_img_hash
D: bootutil_tlv_iter_begin: type 65535, prot == 0
D: bootutil_img_validate: TLV off 25816, end 25956
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25816 ending at 25956
D: bootutil_tlv_iter_next: TLV 16 found at 25820 (size 32)
D: bootutil_img_validate: EXPECTED_HASH_TLV == 16
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25852 ending at 25956
D: bootutil_tlv_iter_next: TLV 1 found at 25856 (size 32)
D: bootutil_img_validate: EXPECTED_KEY_TLV == 1
D: bootutil_find_key
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25888 ending at 25956
D: bootutil_tlv_iter_next: TLV 36 found at 25892 (size 64)
D: bootutil_img_validate: EXPECTED_SIG_TLV == 36
D: bootutil_verify_sig: ED25519 key_id 1
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25956 ending at 25956
D: bootutil_tlv_iter_next: TLV 65535 not found
D: Left boot_go with success == 1
I: Bootloader chainload address offset: 0xc000
I: Image version: v0.0.0
I: Jumping to the first image slot
*** Booting Zephyr OS build v4.4.0-3680-g5598d5b364cc ***
[00:00:00.005,065] <inf> app: Address of sample 0xc000
[00:00:00.010,559] <inf> app: Hello mcuboot signing keys! nrf52840dk
*** Using Zephyr OS build v4.4.0-3680-g5598d5b364cc ***
I: Starting bootloader
D: context_boot_go
I: Image index: 0, Swap type: none
D: boot_validate_slot: slot 0, expected_swap_type 0
D: bootutil_img_validate: flash area 0x8680
D: bootutil_img_hash
D: bootutil_tlv_iter_begin: type 65535, prot == 0
D: bootutil_img_validate: TLV off 25816, end 25956
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25816 ending at 25956
D: bootutil_tlv_iter_next: TLV 16 found at 25820 (size 32)
D: bootutil_img_validate: EXPECTED_HASH_TLV == 16
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25852 ending at 25956
D: bootutil_tlv_iter_next: TLV 1 found at 25856 (size 32)
D: bootutil_img_validate: EXPECTED_KEY_TLV == 1
D: bootutil_find_key
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25888 ending at 25956
D: bootutil_tlv_iter_next: TLV 36 found at 25892 (size 64)
D: bootutil_img_validate: EXPECTED_SIG_TLV == 36
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25956 ending at 25956
D: bootutil_tlv_iter_next: TLV 65535 not found
E: Image in the primary slot is not valid!
D: Left boot_go with success == 0
E: Unable to find bootable image
*** Using Zephyr OS build v4.4.0-3680-g5598d5b364cc ***
I: Starting bootloader
D: context_boot_go
I: Image index: 0, Swap type: none
D: boot_validate_slot: slot 0, expected_swap_type 0
D: bootutil_img_validate: flash area 0x8680
D: bootutil_img_hash
D: bootutil_tlv_iter_begin: type 65535, prot == 0
D: bootutil_img_validate: TLV off 25816, end 25956
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25816 ending at 25956
D: bootutil_tlv_iter_next: TLV 16 found at 25820 (size 32)
D: bootutil_img_validate: EXPECTED_HASH_TLV == 16
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25852 ending at 25956
D: bootutil_tlv_iter_next: TLV 1 found at 25856 (size 32)
D: bootutil_img_validate: EXPECTED_KEY_TLV == 1
D: bootutil_find_key
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25888 ending at 25956
D: bootutil_tlv_iter_next: TLV 36 found at 25892 (size 64)
D: bootutil_img_validate: EXPECTED_SIG_TLV == 36
D: bootutil_verify_sig: ED25519 key_id 0
D: bootutil_tlv_iter_next: searching for 65535 (65535 is any) starting at 25956 ending at 25956
D: bootutil_tlv_iter_next: TLV 65535 not found
D: Left boot_go with success == 1
I: Bootloader chainload address offset: 0xc000
I: Image version: v0.0.0
I: Jumping to the first image slot
*** Booting Zephyr OS build v4.4.0-3680-g5598d5b364cc ***
[00:00:00.005,096] <inf> app: Address of sample 0xc000
[00:00:00.010,589] <inf> app: Hello mcuboot signing keys! nrf52840dk

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.

Comment thread samples/sysbuild/mcuboot_signing_keys/CMakeLists.txt Outdated
Comment thread samples/sysbuild/mcuboot_signing_keys/CMakeLists.txt Outdated
Comment thread share/sysbuild/images/bootloader/CMakeLists.txt Outdated
@JPHutchins JPHutchins force-pushed the mcuboot/multiple-signing-keys branch from 6ea44fe to 5c070ce Compare June 1, 2026 19:51
@JPHutchins JPHutchins force-pushed the mcuboot/multiple-signing-keys branch from 5c070ce to 5b45aad Compare June 10, 2026 01:12
Comment thread samples/sysbuild/mcuboot_signing_keys/prj.conf Outdated
Comment thread samples/sysbuild/mcuboot_signing_keys/README.rst Outdated
Comment thread share/sysbuild/cmake/modules/sysbuild_extensions.cmake
Comment thread tests/boot/mcuboot_multiple_keys/boards/mps2_an385.conf Outdated
Comment thread tests/boot/mcuboot_multiple_keys/boards/mps2_an385.overlay Outdated
Comment thread tests/boot/mcuboot_multiple_keys/pytest/conftest.py
Comment thread tests/boot/mcuboot_multiple_keys/pytest/test_multiple_keys.py
mathieuchopstm and others added 10 commits June 22, 2026 18:05
Enable testing of the STM32 HASH driver on HAL2 series.

Signed-off-by: Mathieu Choplain <mathieu.choplain-ext@st.com>
Align the closing parenthesis of the two ARMFVP_FLAGS set() calls with
the function, matching the project's CMake style. Cosmetic only.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
Fix incorrectly formatted/indented bullet lists.

Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
Fix incorrectly formatted/indented bullet lists.

Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
This test depends on hardware, no need to try and run it on simulators.

Signed-off-by: Anas Nashif <anas.nashif@intel.com>
zephyr sdk does not have newlib support.

Signed-off-by: Anas Nashif <anas.nashif@intel.com>
zephyr sdk does not support newlib, so skip this toolchain on the newlib
scenarios.

Signed-off-by: Anas Nashif <anas.nashif@intel.com>
no need to spend type checking if simulators can run those
samples/tests, filter on platform type and vendors to speed things up.

Signed-off-by: Anas Nashif <anas.nashif@intel.com>
dma_bee.h documentation was "polluting" top-level doxygen groups.
Fix by reparenting under DMA (will need to properly organize all other
DMA headers at some point, btw).

Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
SB_CONFIG_BOOT_SIGNATURE_KEY_FILE now accepts a comma-separated list of
PEM paths in addition to a single path. When a list is given, the MCUboot
bootloader embeds the public half of every key and accepts an image
signed with any of them. The first entry is also the key the application
is signed with; every entry past the first must be a public-only PEM of
the same signature type (MCUboot enforces this at build time with
'imgtool keyinfo --require public'). A common use is a development
bootloader that boots both development- and production-signed images
while never holding the production private key.

The keys the bootloader embeds and the single key the application is
signed with are both derived from this one list through the sysbuild
helpers sysbuild_mcuboot_resolve_signature_key_files() and
sysbuild_mcuboot_application_signature_key_file(). Entries are forwarded
verbatim for each image to resolve, so a single-key value behaves exactly
as before and no previously working configuration breaks.

The feature is exercised at runtime by tests/boot/mcuboot_multiple_keys,
which builds a two-key bootloader on the mps2/an385 QEMU machine and boots
an application signed with either embedded key, asserting MCUboot
validates it against the matching key (key_id 0 and key_id 1). The keys
live in the application's own keys/ directory, referenced with ${APP_DIR},
to demonstrate the development/production custody the feature enables
rather than MCUboot's insecure built-in defaults.

Signed-off-by: JP Hutchins <jp@intercreate.io>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.