Skip to content

zephyr: support embedding a second signing key (CONFIG_BOOT_SIGNATURE_KEY_FILE_2) #2700

@JPHutchins

Description

@JPHutchins

Summary

The Zephyr port of MCUboot currently supports embedding only a single signing-verification public key, even though MCUboot's core key-management infrastructure (bootutil_keys[] + bootutil_key_cnt) already supports multiple keys. This is stated explicitly in docs/readme-zephyr.md:

Currently, the Zephyr RTOS port limits its support to one keypair at the time, although MCUboot's key management infrastructure supports multiple keypairs.

And the multi-key use case is already described as a design intent in docs/signed_images.md:

This facility allows you to use multiple signing keys. This would be useful when you want to prevent production units from booting development images, but want development units to be able to boot both production images and development images.

This issue tracks extending the Zephyr port to expose that capability via Kconfig, so users no longer have to fork MCUboot or patch the SDK to achieve it.

Use case

The canonical motivation, paraphrased from signed_images.md:

  • Production bootloaders must accept only images signed by the production key. (No change from today.)
  • Development bootloaders (same hardware, same firmware image format, different bootloader binary) must accept images signed by either the production key or a separate development key. This lets engineers run dev builds on dev units and flash production builds onto those same units without re-signing, while ensuring production units will never boot a dev image.

Today, achieving this on Zephyr requires either (a) forking/patching boot/zephyr/keys.c and boot/zephyr/CMakeLists.txt, or (b) falling back to CONFIG_BOOT_SIGNATURE_TYPE_NONE on dev units, which removes all signature protection. Neither is acceptable as a long-term path.

This is a real and recurring user need. For example, the Nordic DevZone thread Multiple MCUBoot Keys and Securing a Private Key arrives at exactly the same conclusion:

MCU Boot allows an array of keys, but it looks like zephyr hardcodes it to one key, and I would have to modify keys.c to accommodate an array of public keys. Is this correct? Might there be a simpler way to handle this than having to patch the SDK?

Proposed approach (high level)

Add CONFIG_BOOT_SIGNATURE_KEY_FILE_2 as an optional second PEM file. When empty (the default), behaviour is bit-identical to current upstream. When set, the bootloader embeds both public keys and accepts images signed by either, via the existing bootutil_find_key() loop in boot/bootutil/src/bootutil_find_key.c.

Scope of the change:

  • A new imgtool option to disambiguate emitted C/Rust symbol names when two keys of the same type are embedded.
  • The new Kconfig option plus corresponding changes in boot/zephyr/{CMakeLists.txt, keys.c}.
  • Simulator support and a multi-key test matrix (because boot/zephyr/keys.c is compiled by the simulator too).
  • Docs updates to readme-zephyr.md, signed_images.md, and imgtool.md.
  • Updated twister coverage at Zephyr when in the same commit that updates the mcuboot lock.

Non-goals

  • No change to key-matching semantics. Core bootutil_find_key() already handles N keys correctly by hashing the KEYHASH TLV against each embedded key.
  • No image-format changes. Images are signed exactly as today.
  • Not a replacement for MCUBOOT_HW_KEY / MCUBOOT_BUILTIN_KEY. Those decouple the key from the bootloader binary; this option is for deployments that embed keys in the bootloader and want more than one. The new Kconfig is mutually exclusive with both.
  • No increase beyond two keys for now. The _2 naming leaves room for _3, _4 if demand justifies it. In practice multi-key fleets are almost always "prod + dev", so two is the right first step.

Implementation

Draft impl here: #2701

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions