.. _ug_fw_update: Firmware updates ################ .. contents:: :local: :depth: 2 This user guide provides an overview of firmware updates with |NCS|. .. _ug_fw_update_keys: Signature keys ************** You can find information on the key types supported in the |NCS| in the :ref:`nRF Secure Immutable Bootloader ` and :doc:`mcuboot:imgtool` documentation. Generating private keys ======================= The |NCS| supports the following most common ways to generate private/public key pairs: * :ref:`ug_fw_update_keys_openssl` * :ref:`ug_fw_update_keys_imgtool` * :ref:`ug_fw_update_keys_python` .. _ug_fw_update_keys_openssl: Using OpenSSL to generate keys ------------------------------ `OpenSSL`_ is installed by default with some packages and programs, such as Git. It supports many different types of keys, but not all of them are supported by |NSIB| and MCUboot. To see a complete list of the key types supported by OpenSSL, call ``openssl help`` and ``openssl -help`` from a terminal. For a complete list of the key types supported by each bootloader, see the table below: .. list-table:: :header-rows: 1 * - Bootloader - Supported key types * - |NSIB| - ECDSA256 * - MCUboot - RSA-2048, RSA-3072, ECDSA-P256, ED25519 See below for examples of using OpenSSL to create some commonly used key types: .. tabs:: .. group-tab:: ECDSA .. code-block:: console openssl ecparam -name prime256v1 -genkey -noout -out priv.pem .. group-tab:: RSA-2048 .. code-block:: console openssl genrsa -out priv.pem 2048 .. note:: ``priv.pem``, ``priv_*.pem``, and ``pub_*.pem`` are keys named arbitrarily and used as an example in this documentation. You can name private and public keys used with Zephyr and the |NCS| as you prefer, as long as the files are in the ``.pem`` format. .. _ug_fw_update_keys_imgtool: Using Imgtool to generate keys ------------------------------ :doc:`mcuboot:imgtool` is a Python tool maintained by MCUboot that handles public/private key pairs. It is also available as a PyPI package that you can install using ``pip``. However, when working within the |NCS| framework, it is recommended to use the script that is included in the fork of MCUboot used by the |NCS|. See below for examples of imgtool used to create some commonly used key types: .. tabs:: .. group-tab:: ECDSA .. code-block:: console python3 bootloader/mcuboot/scripts/imgtool.py keygen -t ecdsa-p256 -k priv.pem .. group-tab:: RSA-2048 .. code-block:: console python3 bootloader/mcuboot/scripts/imgtool.py keygen -t rsa-2048 -k priv.pem For a full list of supported types, use the ``--help`` argument with the tool or any of its commands. .. _ug_fw_update_keys_python: Using Python to generate keys ----------------------------- The |NCS| includes an internal script to interact with private and public keys with the |NSIB|. This script is maintained by the |NCS| team and uses the ``ecdsa`` package available from PyPI. It is only valid for use with ECDSA keys. See below for an example of this internal Python script used to generate keys. .. code-block:: console python3 nrf/scripts/bootloader/keygen.py --private -o priv.pem .. _ug_fw_update_development_keys: Using development keys ====================== When testing the bootloader chain, you can optionally generate and use custom signing keys. If you do not provide your own keys through Kconfig options, the build system automatically creates debug keys, depending on the bootloaders compiled into the application. .. caution:: Keys that are automatically used or generated by bootloaders for image signature validation are intended for *development or debug use only*. You should *never* send applications into production when they are not protected by secure keys. You must always create and store these keys in a safe location, not only to protect the security of the application but also to ensure that the hardware can receive firmware updates throughout the project lifecycle. While the default keys for MCUboot are tracked in its repository and are therefore publicly visible, the development/debug keys autogenerated by |NSIB| change whenever the build directory is removed and rebuilt from scratch. If you are not programming the |NSIB| when this happens, relying on the default ECDSA key to sign an application or an upgradable second-stage bootloader image results in a failed boot chain validation. You can avoid this issue by storing a custom private key outside of the build directory during development. .. _ug_fw_update_key_revocation: Revoking private keys ===================== The |NSIB| allows you to revoke public verification keys used to validate the next image in the secure boot chain. Key revocation can be a useful security measure for devices that have already been deployed to the field. If a private key has been compromised or lost, you can invalidate its public key by uploading a new firmware image signed by another key known to the bootloader. These keys are kept internally by the bootloader, so the list of available public keys cannot change once it is deployed. See :kconfig:option:`CONFIG_SB_PUBLIC_KEY_FILES` for details on how this mechanism is implemented. You can add this feature to your own project and check its functionality as follows: 1. Use the :ref:`bootloader Python script ` to generate two or more private keys for the application and extract a public key for each one: .. code-block:: console python3 nrf/scripts/bootloader/keygen.py --public -i priv.pem -o pub.pem #. Compile the application and bootloader with the relevant configurations, using only absolute paths: .. code-block:: console CONFIG_SECURE_BOOT=y CONFIG_SB_SIGNING_KEY_FILE="/path/to/priv_a.pem" CONFIG_SB_PUBLIC_KEY_FILES="/path/to/pub_b.pem,/path/to/pub_c.pem" .. caution:: The public key associated with the original private signing key must not be included in the public key list. #. Program the application to the target development kit and :ref:`check its console output `. With the first firmware version, ``priv_a.pem`` and ``pub_a.pem`` are used for signing and validating the image. .. code-block:: console *** Booting Zephyr OS build ... *** Attempting to boot slot 0. Attempting to boot from address 0x9000. Verifying signature against key 0. Hash: 0xda...4f Firmware signature verified. Firmware version 1 *** Booting Zephyr OS build ... *** ... #. To revoke keys, rebuild the application modifying the configuration setting to use the private key associated with a key listed *after* the currently used key in the list. .. code-block:: console CONFIG_BUILD_S1_VARIANT=y CONFIG_SB_SIGNING_KEY_FILE="/path/to/priv_c.pem" CONFIG_FW_INFO_FIRMWARE_VERSION=2 In this example, when compiling with the ``priv_c.pem`` key, images signed with ``priv_a.pem`` or ``priv_b.pem`` no longer boot when uploaded into an image slot. Additionally, a firmware version higher than the previous one has been set. #. Deploy the firmware update. #. Observe the bootloader checking the hashes of the public keys against the new image, then invalidating the earlier keys: .. code-block:: console *** Booting Zephyr OS build ... *** Attempting to boot slot 1. Attempting to boot from address 0x84800. Verifying signature against key 0. Hash: 0xda...4f Public key didn't match, try next. Verifying signature against key 1. Hash: 0x5c...f5 Public key didn't match, try next. Verifying signature against key 2. Hash: 0x19...73 Invalidating key 0. Invalidating key 1. Firmware signature verified. Firmware version 2 Setting monotonic counter (version: 2, slot: 1) *** Booting Zephyr OS build ... *** ... You can test that the bootloader no longer boots images signed with the earlier keys by uploading an image signed with one of them. 1. To do this, recompile the application with the following options: .. code-block:: console CONFIG_SB_SIGNING_KEY_FILE="/path/to/priv_b.pem" CONFIG_FW_INFO_FIRMWARE_VERSION=3 #. To facilitate testing, you can use nrfjprog to program this image directly into a slot: .. code-block:: console nrfjprog -f nRF52 -r --verify --program build/zephyr/signed_by_b0_s0_image.hex --sectorerase #. Observe the bootloader skipping the invalid image and booting the valid image in the other slot: .. code-block:: console *** Booting Zephyr OS build ... *** Attempting to boot slot 0. Attempting to boot from address 0x9000. Key 0 has been invalidated, try next. Key 1 has been invalidated, try next. Verifying signature against key 2. Hash: 0x19...73 Public key didn't match, try next. Failed to validate signature. Failed to validate, permanently invalidating! Attempting to boot slot 1. Attempting to boot from address 0x84800. Key 0 has been invalidated, try next. Key 1 has been invalidated, try next. Verifying signature against key 2. Hash: 0x19...73 Invalidating key 0. Invalidating key 1. Firmware signature verified. Firmware version 2 *** Booting Zephyr OS build ... *** ... Recompile with ``priv_c.pem`` and the incremented firmware version to correctly boot the new image. .. _ug_fw_update_image_versions: Configuring image versions ************************** With the |NCS|, you must choose the method for versioning an image for use in firmware releases and updates depending on the type of bootloader that will be booting into the new firmware release. .. _ug_fw_update_image_versions_b0: Using |NSIB| ============ .. include:: ../../../../samples/bootloader/README.rst :start-after: bootloader_monotonic_counter_start :end-before: bootloader_monotonic_counter_end Special handling is needed when updating the S1 variant of an image. See :ref:`ug_bootloader_adding_presigned_variants` for details. .. _ug_fw_update_image_versions_mcuboot: Using MCUboot ============= Currently, the |NCS| supports only *semantic versioning* for all the applications verified by MCUboot. .. note:: Within the |NCS| framework, specifying this semantic version for an application serves no purpose and does not prevent downgrades from normal firmware updates. To prevent downgrades from firmware upgrades in the field, see :ref:`ug_fw_update_image_versions_mcuboot_downgrade`. See also the Kconfig documentation for the ``CONFIG_MCUBOOT_IMAGE_VERSION`` option for more details about its use within the |NCS|. To assign a semantic version number to your application, pass the version string into the ``CONFIG_MCUBOOT_IMAGE_VERSION`` option for the application: .. code-block:: console CONFIG_MCUBOOT_IMAGE_VERSION="0.1.2+3" See the `Semantic versioning`_ webpage or :doc:`mcuboot:imgtool` for details on version syntax. .. _ug_fw_update_image_versions_mcuboot_downgrade: Preventing downgrades using MCUboot ----------------------------------- The |NCS| supports MCUboot's software-based downgrade prevention for application images, using semantic versioning. This feature offers protection against any outdated firmware that is uploaded to a device. You can enable this feature by setting the ``CONFIG_MCUBOOT_DOWNGRADE_PREVENTION`` and ``CONFIG_BOOT_UPGRADE_ONLY`` options for the MCUboot image. .. warning:: Enabling ``CONFIG_BOOT_UPGRADE_ONLY`` prevents the fallback recovery of application images. Consult its Kconfig description and the :doc:`MCUboot Design documentation ` for more information on how to use it. You can compile your application with this feature as follows: .. parsed-literal:: :class: highlight west build -b *board* *application* -- \\ -DCONFIG_BOOTLOADER_MCUBOOT=y \\ -DCONFIG_MCUBOOT_IMAGE_VERSION=\\"0.1.2\\+3\\" \\ -Dmcuboot_CONFIG_MCUBOOT_DOWNGRADE_PREVENTION=y \\ -Dmcuboot_CONFIG_BOOT_UPGRADE_ONLY=y |how_to_configure| After you upload a new image and reset the development kit, MCUboot attempts to boot the secondary image. If this image has, in order of precedence, a *major*, *minor*, or *revision* value that is lower than the primary application image, it is considered invalid and the existing primary application boots instead. .. note:: The optional label or build number specified after the ``+`` character is ignored when evaluating the version. An existing application image with version ``0.1.2+3`` can be overwritten by an uploaded image with ``0.1.2+2``, but not by one with ``0.1.1+2``.