Skip to content

mcuboot: support multiple signing keys#1

Open
JPHutchins wants to merge 1 commit into
mainfrom
mcuboot/multiple-signing-keys
Open

mcuboot: support multiple signing keys#1
JPHutchins wants to merge 1 commit 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
Comment thread tests/boot/mcuboot_multiple_keys/testcase.yaml Outdated
Comment thread tests/boot/mcuboot_multiple_keys/testcase.yaml Outdated
Comment thread tests/cmake/sysbuild_mcuboot_keys/CMakeLists.txt Outdated

@JPHutchins JPHutchins left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Pretty good. Hoping to reduce LOC, espcially around verbose LLM-style comments!

@JPHutchins JPHutchins force-pushed the mcuboot/multiple-signing-keys branch from 838eb5d to 78d2dfe Compare June 13, 2026 00:14
@JPHutchins

Copy link
Copy Markdown
Collaborator Author

Addressed all 25 review comments from this round in 78d2dfe4226 (every thread has an individual reply).

Net LOC: −178 (PR total +1263/−9 → +1085/−9, a 14% cut) — all prose, comments, and duplication; no coverage lost.

Highlights beyond comment trims:

  • New sysbuild_mcuboot_application_signature_key_file() helper hoists the resolve-then-first-entry block out of MAIN/FIRMWARE_LOADER_image_default.cmake (16 → 4 lines each), with 3 new asserts in tests/cmake/sysbuild_mcuboot_keys (14/14 pass).
  • mcuboot.conf board-specifics fixed at the root: CONFIG_BOOT_IMAGE_EXECUTABLE_RAM_* is MCUboot's deprecated path — replaced with the mcuboot,image-ram devicetree chosen node in sysbuild/mcuboot.overlay.
  • Test harness: --slot0-load-addr / --qemu-timeout options removed, machine/CPU/addr hardcoded; conftest.py now matches with_mcumgr plus one option.
  • Commit message updated to match ("a common use", both helpers named).

Verified: both QEMU scenarios pass (key_id 0 and key_id 1), ruff/mypy --strict clean, gitlint clean. smp-server-fixtures attribution kept in the test README and common dtsi.

Left open for your call: the string(CONFIGURE) edge-case thread.

@JPHutchins JPHutchins force-pushed the mcuboot/multiple-signing-keys branch from 1b5f693 to 6af2720 Compare June 23, 2026 00:04
JPHutchins pushed a commit that referenced this pull request Jul 7, 2026
struct bt_uuid was used for storage instead of struct bt_uuid_any
resulting in overflow when converting BTP UUID to bt_uuid.

==3608402==ERROR: AddressSanitizer: stack-buffer-overflow on address
    0xe02f5da2 at pc 0x0805a750 bp 0xe14e2048 sp 0xe14e2038
WRITE of size 2 at 0xe02f5da2 thread T7
    #0 0x805a74f in btp2bt_uuid zephyr/tests/bluetooth/tester/src/btp_gatt.c:212
    #1 0x805d31c in get_handle_from_uuid zephyr/tests/bluetooth/tester/src/btp_gatt.c:2149
    #2 0x804f12c in cmd_handler zephyr/tests/bluetooth/tester/src/btp.c:122
    #3 0x807c582 in z_thread_entry zephyr/kernel/sys/thread_entry.c:60
    zephyrproject-rtos#4 0x807cdd2 in posix_arch_thread_entry zephyr/arch/posix/core/thread.c:124
    zephyrproject-rtos#5 0x80ff2bd in nct_thread_starter zephyr/scripts/native_simulator//common/src/nct.c:291
    zephyrproject-rtos#6 0xeb1949a3 in asan_thread_start ../../../../../src/libsanitizer/asan/asan_interceptors.cpp:234
    zephyrproject-rtos#7 0xeaea5136  (/lib32/libc.so.6+0x8b136) (BuildId: 798ae816ef14b98dc77560340190445fb200634a)
    zephyrproject-rtos#8 0xeaf39de7  (/lib32/libc.so.6+0x11fde7) (BuildId: 798ae816ef14b98dc77560340190445fb200634a)

Signed-off-by: Szymon Janc <szymon.janc@codecoup.pl>
@JPHutchins JPHutchins force-pushed the mcuboot/multiple-signing-keys branch from 6af2720 to 0324e75 Compare July 7, 2026 23:17
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 two
keys are MCUboot's own ed25519 test keys: a development key (key_id 0)
and a production key embedded by its public half only (key_id 1). The
application is signed at build time with the development key; the second
scenario re-signs the built image with the production key afterwards,
the way a production custodian signs a release binary out-of-band, so
the production private key never enters the build configuration.

Signed-off-by: JP Hutchins <jp@intercreate.io>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JPHutchins JPHutchins force-pushed the mcuboot/multiple-signing-keys branch from 0324e75 to 3299247 Compare July 8, 2026 22:42
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.

3 participants