Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmake: mcuboot: Use imgtool instead of west for signing #78983

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
86 changes: 46 additions & 40 deletions cmake/mcuboot.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,30 @@ function(zephyr_mcuboot_tasks)
return()
endif()

# Basic 'west sign' command and output format independent arguments.
separate_arguments(west_sign_extra UNIX_COMMAND ${CONFIG_MCUBOOT_CMAKE_WEST_SIGN_PARAMS})
set(west_sign ${WEST} sign ${west_sign_extra}
--tool imgtool
--tool-path "${imgtool_path}"
--build-dir "${APPLICATION_BINARY_DIR}")
# Fetch devicetree details for flash and slot information
dt_chosen(flash_node PROPERTY "zephyr,flash")
dt_nodelabel(slot0_flash NODELABEL "slot0_partition" REQUIRED)
dt_prop(slot_size PATH "${slot0_flash}" PROPERTY "reg" INDEX 1 REQUIRED)
dt_prop(write_block_size PATH "${flash_node}" PROPERTY "write-block-size")

if(NOT write_block_size)
set(write_block_size 4)
message(WARNING "slot0_partition write block size devicetree parameter is missing, assuming write block size is 4")
endif()

# If single slot mode, or if in firmware updater mode and this is the firmware updater image,
# use slot 0 information
if(NOT CONFIG_MCUBOOT_BOOTLOADER_MODE_SINGLE_APP AND (NOT CONFIG_MCUBOOT_BOOTLOADER_MODE_FIRMWARE_UPDATER OR CONFIG_MCUBOOT_APPLICATION_FIRMWARE_UPDATER))
Copy link
Collaborator

Choose a reason for hiding this comment

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

minor nit, quite a long line.

# Slot 1 size is used instead of slot 0 size
set(slot_size)
dt_nodelabel(slot1_flash NODELABEL "slot1_partition" REQUIRED)
dt_prop(slot_size PATH "${slot1_flash}" PROPERTY "reg" INDEX 1 REQUIRED)
endif()

# Basic 'imgtool sign' command with known image information.
set(imgtool_sign ${PYTHON_EXECUTABLE} ${imgtool_path} sign
--version ${CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION} --header-size ${CONFIG_ROM_START_OFFSET}
--slot-size ${slot_size})

# Arguments to imgtool.
if(NOT CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS STREQUAL "")
Expand All @@ -87,102 +105,90 @@ function(zephyr_mcuboot_tasks)
#
# Use UNIX_COMMAND syntax for uniform results across host
# platforms.
separate_arguments(imgtool_extra UNIX_COMMAND ${CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS})
separate_arguments(imgtool_args UNIX_COMMAND ${CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS})
else()
set(imgtool_extra)
set(imgtool_args)
endif()

if(NOT "${keyfile}" STREQUAL "")
set(imgtool_extra --key "${keyfile}" ${imgtool_extra})
set(imgtool_args --key "${keyfile}" ${imgtool_args})
endif()

# Use overwrite-only instead of swap upgrades.
if(CONFIG_MCUBOOT_IMGTOOL_OVERWRITE_ONLY)
set(imgtool_extra --overwrite-only --align 1 ${imgtool_extra})
set(imgtool_args --overwrite-only --align 1 ${imgtool_args})
else()
set(imgtool_args --align ${write_block_size} ${imgtool_args})
endif()

set(imgtool_args -- ${imgtool_extra})

# Extensionless prefix of any output file.
set(output ${ZEPHYR_BINARY_DIR}/${KERNEL_NAME})

# List of additional build byproducts.
set(byproducts)

# 'west sign' arguments for confirmed, unconfirmed and encrypted images.
set(unconfirmed_args)
set(confirmed_args)
set(encrypted_args)

# Set up .bin outputs.
if(CONFIG_BUILD_OUTPUT_BIN)
list(APPEND unconfirmed_args --bin --sbin ${output}.signed.bin)
list(APPEND byproducts ${output}.signed.bin)
zephyr_runner_file(bin ${output}.signed.bin)
set(BYPRODUCT_KERNEL_SIGNED_BIN_NAME "${output}.signed.bin"
CACHE FILEPATH "Signed kernel bin file" FORCE
)
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
${imgtool_sign} ${imgtool_args} ${output}.bin ${output}.signed.bin)

if(CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE)
list(APPEND confirmed_args --bin --sbin ${output}.signed.confirmed.bin)
list(APPEND byproducts ${output}.signed.confirmed.bin)
set(BYPRODUCT_KERNEL_SIGNED_CONFIRMED_BIN_NAME "${output}.signed.confirmed.bin"
CACHE FILEPATH "Signed and confirmed kernel bin file" FORCE
)
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
${imgtool_sign} ${imgtool_args} --pad --confirm ${output}.bin
${output}.signed.confirmed.bin)
endif()

if(NOT "${keyfile_enc}" STREQUAL "")
list(APPEND encrypted_args --bin --sbin ${output}.signed.encrypted.bin)
list(APPEND byproducts ${output}.signed.encrypted.bin)
set(BYPRODUCT_KERNEL_SIGNED_ENCRYPTED_BIN_NAME "${output}.signed.encrypted.bin"
CACHE FILEPATH "Signed and encrypted kernel bin file" FORCE
)
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
${imgtool_sign} ${imgtool_args} --encrypt "${keyfile_enc}" ${output}.bin
${output}.signed.encrypted.bin)
endif()
endif()

# Set up .hex outputs.
if(CONFIG_BUILD_OUTPUT_HEX)
list(APPEND unconfirmed_args --hex --shex ${output}.signed.hex)
list(APPEND byproducts ${output}.signed.hex)
zephyr_runner_file(hex ${output}.signed.hex)
set(BYPRODUCT_KERNEL_SIGNED_HEX_NAME "${output}.signed.hex"
CACHE FILEPATH "Signed kernel hex file" FORCE
)
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
${imgtool_sign} ${imgtool_args} ${output}.hex ${output}.signed.hex)

if(CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE)
list(APPEND confirmed_args --hex --shex ${output}.signed.confirmed.hex)
list(APPEND byproducts ${output}.signed.confirmed.hex)
set(BYPRODUCT_KERNEL_SIGNED_CONFIRMED_HEX_NAME "${output}.signed.confirmed.hex"
CACHE FILEPATH "Signed and confirmed kernel hex file" FORCE
)
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
${imgtool_sign} ${imgtool_args} --pad --confirm ${output}.hex
${output}.signed.confirmed.hex)
endif()

if(NOT "${keyfile_enc}" STREQUAL "")
list(APPEND encrypted_args --hex --shex ${output}.signed.encrypted.hex)
list(APPEND byproducts ${output}.signed.encrypted.hex)
set(BYPRODUCT_KERNEL_SIGNED_ENCRYPTED_HEX_NAME "${output}.signed.encrypted.hex"
CACHE FILEPATH "Signed and encrypted kernel hex file" FORCE
)
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
${imgtool_sign} ${imgtool_args} --encrypt "${keyfile_enc}" ${output}.hex
${output}.signed.encrypted.hex)
endif()
endif()

# Add the west sign calls and their byproducts to the post-processing
# steps for zephyr.elf.
#
# CMake guarantees that multiple COMMANDs given to
# add_custom_command() are run in order, so adding the 'west sign'
# calls to the "extra_post_build_commands" property ensures they run
# after the commands which generate the unsigned versions.
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
${west_sign} ${unconfirmed_args} ${imgtool_args})
if(confirmed_args)
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
${west_sign} ${confirmed_args} ${imgtool_args} --pad --confirm)
endif()
if(encrypted_args)
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
${west_sign} ${encrypted_args} ${imgtool_args} --encrypt "${keyfile_enc}")
endif()
set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts ${byproducts})
endfunction()

Expand Down
1 change: 1 addition & 0 deletions doc/build/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ Build and Configuration Systems
sysbuild/index.rst
version/index.rst
flashing/index.rst
signing/index.rst
108 changes: 108 additions & 0 deletions doc/build/signing/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
.. _build-signing:

Signing Binaries
################

Binaries can be optionally signed as part of a build automatically using CMake code, there is
also the ability to use ``west sign`` to sign binaries too, this page describes the former, the
latter is documented on :ref:`west-sign`.

MCUboot / imgtool
*****************

The Zephyr build system has special support for signing binaries for use with the `MCUboot`_
bootloader using the `imgtool`_ program provided by its developers. You can both build and sign
this type of application binary in one step by setting some Kconfig options. If you do,
``west flash`` will use the signed binaries.

Here is an example workflow, which builds and flashes MCUboot, as well as the
:zephyr:code-sample:`hello_world` application for chain-loading by MCUboot. Run these commands
from the :file:`zephyrproject` workspace you created in the :ref:`getting_started`.

.. code-block:: console

west build -b YOUR_BOARD zephyr/samples/hello_world --sysbuild -d build-hello-signed -- \
-DSB_CONFIG_BOOTLOADER_MCUBOOT=y

west flash -d build-hello-signed

Notes on the above commands:

- ``YOUR_BOARD`` should be changed to match your board
- The singing key value is the insecure default provided and used by MCUboot for development
and testing
- You can change the ``hello_world`` application directory to any other application that can be
loaded by MCUboot, such as the :zephyr:code-sample:`smp-svr` sample.

For more information on these and other related configuration options, see:

- ``SB_CONFIG_BOOTLOADER_MCUBOOT``: build the application for loading by MCUboot
- ``SB_CONFIG_BOOT_SIGNATURE_KEY_FILE``: the key file to use when singing images. If you have
your own key, change this appropriately
- :kconfig:option:`CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS`: optional additional command line arguments
for ``imgtool``
- :kconfig:option:`CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE`: also generate a confirmed image,
which may be more useful for flashing in production environments than the OTA-able default image
- On Windows, if you get "Access denied" issues, the recommended fix is to run
``pip3 install imgtool``, then retry with a pristine build directory.

If your ``west flash`` :ref:`runner <west-runner>` uses an image format supported by imgtool, you
should see something like this on your device's serial console when you run
``west flash -d build-hello-signed``:

.. code-block:: none

*** Booting Zephyr OS build zephyr-v2.3.0-2310-gcebac69c8ae1 ***
[00:00:00.004,669] <inf> mcuboot: Starting bootloader
[00:00:00.011,169] <inf> mcuboot: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
[00:00:00.021,636] <inf> mcuboot: Boot source: none
[00:00:00.027,374] <inf> mcuboot: Swap type: none
[00:00:00.115,142] <inf> mcuboot: Bootloader chainload address offset: 0xc000
[00:00:00.123,168] <inf> mcuboot: Jumping to the first image slot
*** Booting Zephyr OS build zephyr-v2.3.0-2310-gcebac69c8ae1 ***
Hello World! nrf52840dk_nrf52840

Whether ``west flash`` supports this feature depends on your runner. The ``nrfjprog`` and
``pyocd`` runners work with the above flow. If your runner does not support this flow and you
would like it to, please send a patch or file an issue for adding support.

.. _west-extending-signing:

Extending signing externally
****************************

The signing script used when running ``west flash`` can be extended or replaced to change features
or introduce different signing mechanisms. By default with MCUboot enabled, signing is setup by
the :file:`cmake/mcuboot.cmake` file in Zephyr which adds extra post build commands for generating
the signed images. The file used for signing can be replaced from a sysbuild scope (if being used)
or from a zephyr/zephyr module scope, the priority of which is:

* Sysbuild
* Zephyr property
* Default MCUboot script (if enabled)

From sysbuild, ``-D<target>_SIGNING_SCRIPT`` can be used to set a signing script for a specific
image or ``-DSIGNING_SCRIPT`` can be used to set a signing script for all images, for example:

.. code-block:: console

west build -b <board> <application> -DSIGNING_SCRIPT=<file>

The zephyr property method is achieved by adjusting the ``SIGNING_SCRIPT`` property on the
``zephyr_property_target``, ideally from by a module by using:

.. code-block:: cmake

if(CONFIG_BOOTLOADER_MCUBOOT)
set_target_properties(zephyr_property_target PROPERTIES SIGNING_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/custom_signing.cmake)
endif()

This will include the custom signing CMake file instead of the default Zephyr one when projects
are built with MCUboot signing support enabled. The base Zephyr MCUboot signing file can be
used as a reference for creating a new signing system or extending the default behaviour.

.. _MCUboot:
https://mcuboot.com/

.. _imgtool:
https://pypi.org/project/imgtool/
Loading
Loading