TF-M: PSA template

This sample provides a template for Arm’s Platform Security Architecture (PSA) best practices on Nordic Semiconductor devices. For more information about the PSA framework and how it is implemented in the nRF Connect SDK, see the PSA Certified Security Framework overview page.

Requirements

The sample supports the following development kits:

Hardware platforms

PCA

Board name

Board target

nRF9161 DK

PCA10153

nrf9161dk

nrf9161dk/nrf9161/ns

nRF9160 DK

PCA10090

nrf9160dk

nrf9160dk/nrf9160/ns

nRF9151 DK

PCA10171

nrf9151dk

nrf9151dk/nrf9151/ns

nRF54LV10 DK

PCA10188

nrf54lv10dk

nrf54lv10dk/nrf54lv10a/cpuapp/ns

nRF54LM20 DK

PCA10184

nrf54lm20dk

nrf54lm20dk/nrf54lm20b/cpuapp/ns nrf54lm20dk/nrf54lm20a/cpuapp/ns

nRF54L15 DK

PCA10156

nrf54l15dk

nrf54l15dk/nrf54l15/cpuapp/ns

nRF5340 DK

PCA10095

nrf5340dk

nrf5340dk/nrf5340/cpuapp/ns

For more security, it is recommended to use the */ns variant of the board target. When built for this variant, the sample is configured to compile and run as a non-secure application using security by separation. Therefore, it automatically includes Trusted Firmware-M that prepares the required peripherals and secure services to be available for the application.

Overview

This sample uses Trusted Firmware-M, MCUboot and nRF Secure Immutable Bootloader (NSIB) to demonstrate how to implement the best practices that comply with the Arm PSA requirements. It includes provisioning the device with keys and being able to perform a device firmware update. The sample prints information about the identity of the device and the firmware versions that are currently running.

When built for the nRF5340 devices, this sample also includes the B0n bootloader and the empty_net_core image for demonstrating the network core firmware update process.

Configuration

See Configuring and building for information about how to permanently or temporarily change the configuration.

Setup

The sample requires that the device is provisioned with the PSA root-of-trust security parameters before it is programmed for the first time. Provisioning is a separate, one-time step performed with the provisioning image sample; this application does not provision the device itself. On nRF54L Series devices, the provisioning image is built together with this application by default and is the primary way to provision the device (see Provisioning image built with the application (nRF54L)). On the nRF5340 and nRF91 Series, you build and flash the provisioning image separately, as described in the following section. For a description of the provisioning step and the lifecycle transitions it performs, see Relationship to the TF-M application.

Building and flashing the provisioning image

Building and flashing the provisioning image depends on the target device and its lifecycle state:

  • On the nRF5340 and nRF91x SoCs, a factory-fresh device is already in lifecycle state ASSEMBLY. As such, programming without the erase option is sufficient to provision the device. For example, if you are using the nRF5340 DK, you can run the following commands:

    west build -b nrf5340dk/nrf5340/cpuapp nrf/samples/tfm/provisioning_image -d build_provisioning_image
    west flash -d build_provisioning_image
    
  • On an already-provisioned device, program using the --erase --recover arguments to wipe the OTP back to ASSEMBLY first.

  • On nRF54L Series devices, you do not build the provisioning image separately, because it is built together with this application by default. west flash --recover then provisions the NSIB Ed25519 public key to the CRACEN KMU from the keyfile.json generated by this application’s build. See Provisioning image built with the application (nRF54L).

Using own NSIB signing key

By default, the sample signs the NSIB image with the sample key in samples/tfm/common/keys/. This is a publicly known development key and must not be used in production.

To use your own NSIB signing key, set the SB_CONFIG_SECURE_BOOT_SIGNING_KEY_FILE Kconfig option to your key file. The application build then signs the NSIB image with this key and generates the matching keyfile.json that provisions the public key to the CRACEN KMU. See Configuring Kconfig for more information about how to configure the Kconfig options.

If the signing key and the provisioned public key do not match, b0 rejects MCUboot with error -102 (ESIGINV).

Provisioning image built with the application (nRF54L)

On nRF54L Series devices, the provisioning image is the primary way to provision the device for this application, and it is built together with the application by default (SB_CONFIG_SAMPLE_PROVISIONING_IMAGE). It is added as an additional, build-only image built for the secure board target: it is not programmed by west flash and is not part of any merged hex file. This application does not provision the device itself; the build-only provisioning image performs that step.

The application build also generates the keyfile.json file that provisions the NSIB public key, so the provisioning image does not need to generate its own when built this way.

Provision the device once, and then program the application:

west build -b nrf54l15dk/nrf54l15/cpuapp/ns nrf/samples/tfm/tfm_psa_template

# Provision the device (runs once):
west flash --recover --domain provisioning_image

# Program the application (skips the build-only provisioning image):
west flash

Because provisioning is a separate image, this also supports split-production flows, where provisioning is performed in a secure environment that is separate from where the application firmware is programmed.

If you provision the device in another way, for example with a standalone build of the provisioning image, you can disable building it by setting the option to n:

west build -b nrf54l15dk/nrf54l15/cpuapp/ns nrf/samples/tfm/tfm_psa_template \
  -- -DSB_CONFIG_SAMPLE_PROVISIONING_IMAGE=n

Configuring firmware update

This sample supports firmware update of the application and TF-M on all supported targets. On nRF5340 and nRF91 Series devices, you can additionally update the second stage bootloader (NSIB) and the network core image. On nRF54L devices, only the application + TF-M slot is updatable; the NSIB image and its signing key are pinned at provisioning time and are not part of the in-field update flow.

The firmware update process requires signature verification keys to sign the images used in the process. The default signing keys built into the nRF Connect SDK must not be used in production. The signature scheme depends on the target:

  • On nRF5340 and nRF91 Series, MCUboot and NSIB use ECDSA with secp256r1-sha256, which is supported by the nRF Connect SDK cryptographic libraries nrf_oberon crypto library and nRF CC310 bootloader crypto library.

  • On nRF54L, MCUboot uses Ed25519 in pure mode (SB_CONFIG_BOOT_SIGNATURE_TYPE_ED25519 and SB_CONFIG_BOOT_SIGNATURE_TYPE_PURE) and NSIB uses Ed25519, both verified by CRACEN against keys provisioned to the KMU.

The following steps are an example of how to generate the keys and update the configuration files with them:

  1. Generate security keys if you have not already done so. The following examples generate keys using Imgtool, but you can also use OpenSSL.

    • On nRF5340 and nRF91 Series:

      mkdir _keys
      python3 bootloader/mcuboot/scripts/imgtool.py keygen -t ecdsa-p256 -k ~/ncs/_keys/mcuboot_priv.pem
      python3 bootloader/mcuboot/scripts/imgtool.py keygen -t ecdsa-p256 -k ~/ncs/_keys/nsib_priv.pem
      
    • On nRF54L:

      mkdir _keys
      python3 bootloader/mcuboot/scripts/imgtool.py keygen -t ed25519 -k ~/ncs/_keys/mcuboot_priv.pem
      python3 bootloader/mcuboot/scripts/imgtool.py keygen -t ed25519 -k ~/ncs/_keys/nsib_priv.pem
      
  2. Update the sysbuild.conf file to set the private signing keys for MCUboot and NSIB. Paths may be absolute or relative to the application configuration directory (the sample directory):

    SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="/home/user/ncs/_keys/mcuboot_priv.pem"
    SB_CONFIG_SECURE_BOOT_SIGNING_KEY_FILE="/home/user/ncs/_keys/nsib_priv.pem"
    
  3. Rebuild the sample with the updated keys before proceeding with the firmware update as described in the next sections.

    On nRF54L, SB_CONFIG_SECURE_BOOT_SIGNING_KEY_FILE is used both to sign the NSIB image and to generate the keyfile.json that provisions the matching public key to the CRACEN KMU, so the key does not need to be passed to the provisioning image separately. If the NSIB key in the KMU does not match the key used to sign the upgraded image chain, NSIB will reject MCUboot on the next reset.

You can update the bootloader and the application using the MCUmgr command-line tool. See MCUmgr Command-line tool for installation and usage instructions.

Updating application and TF-M firmware

You can use firmware update to update the application and TF-M firmware on all supported targets. For the image to be updatable, the firmware image version CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION must be updated to a higher version. See Downgrade protection for information on downgrade protection in MCUboot.

When the sample is built, it uses MCUboot in the overwrite-only mode (SB_CONFIG_MCUBOOT_MODE_OVERWRITE_ONLY), so the upgraded image is copied into the primary slot during the next boot and the previous image is discarded.

Note

On nRF54L the application + TF-M slot is the only slot that can be updated in the field. The NSIB image, the b0 public key in the CRACEN KMU, and the lifecycle state are pinned at provisioning time.

To upload a new application image, complete the following steps:

  1. Build an application with an updated image version, replacing <board_target> with the target you use for Building and running:

    west build -b <board_target> nrf/samples/tfm/tfm_psa_template -d build_update \
    -DCONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION=\"1.2.3\"
    
  2. Install the MCUmgr command-line tool if you have not already done so. See MCUmgr Command-line tool for installation and usage instructions.

  3. Upload the new application image to the device using MCUmgr:

    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image list
    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image upload \
    build_update/tfm_psa_template/zephyr/zephyr.signed.bin
    

    Once the new application image is uploaded, the hash of the image is shown in the image list.

  4. Flag the image to be tested on next reboot using its hash:

    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image list
    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image test <hash>
    
  5. Trigger the application update by initiating a reset:

    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 reset
    

    The verification of the image happens during the update process.

Updating bootloader firmware (nRF5340 and nRF91 Series only)

Note

This flow is not supported on nRF54L. The boot chain on nRF54L does not maintain S0/S1 NSIB upgrade slots, so the signed_by_mcuboot_and_b0_s1_image.bin artifact referenced below is not produced and there is no in-place mechanism to swap the NSIB image. To rotate the NSIB image or its signing key on nRF54L, the device must be re-provisioned through the Building and flashing the provisioning image flow with west flash --recover.

To upload a new bootloader image, complete the following steps:

  1. Build a bootloader targeting the correct bootloader slot with an updated firmware image version:

    west build -b nrf5340dk/nrf5340/cpuapp/ns nrf/samples/tfm/tfm_psa_template -d build_update \
    -Dmcuboot_CONFIG_FW_INFO_FIRMWARE_VERSION=2
    
  2. Install the MCUmgr command-line tool if you have not already done so. See MCUmgr Command-line tool for installation and usage instructions.

  3. List the current firmware images and upload a bootloader image that targets the non-active bootloader slot.

    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image list
    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image upload \
    build_update/signed_by_mcuboot_and_b0_s1_image.bin
    

    Once the new bootloader image is uploaded, the hash of the image is shown in the image list.

  4. Flag the image to be tested on next reboot using its hash:

    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image test <hash>
    
  5. Trigger the bootloader update by initiating a reset. The verification of the image will happen during the update process.

    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 reset
    

Updating the network core (nRF5340 only)

To upload a new network core image, complete the following steps:

  1. Build the empty_net_core image with an updated firmware image version:

    west build -b nrf5340dk/nrf5340/cpuapp/ns nrf/samples/tfm/tfm_psa_template -d build_update \
    -Dempty_net_core_CONFIG_FW_INFO_FIRMWARE_VERSION=2
    
  2. Install the MCUmgr command-line tool if you have not already done so. See MCUmgr Command-line tool for installation and usage instructions.

  3. Upload the new network core image to the device:

    Note

    The image is uploaded to the network core slot.

    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image upload \
    build_update/signed_by_mcuboot_and_b0_empty_net_core.bin -e -n 1
    
    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image list
    

    Once the network core image is uploaded, the hash of the image is shown in the image list as image 1 in slot 1.

  4. Flag the image to be tested on next reboot using its hash:

    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image test <hash>
    
  5. Trigger the network core update by initiating a reset. The verification of the image will happen during the update process.

    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 reset
    

Alternatively, you can conduct a manual reset to trigger the network core update. This allows you to observe the update process in the application and network core console outputs.

Updating application and network cores simultaneously (nRF5340 only)

When the interface between the application core and the network core is updated, both the application and network core images must be updated simultaneously. To do this, complete the following steps:

  1. Build the application image with an updated image version and the network core image with an updated firmware image version:

    west build -b nrf5340dk/nrf5340/cpuapp/ns nrf/samples/tfm/tfm_psa_template -d build_update \
    -DCONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION=\"1.2.4\" -Dempty_net_core_CONFIG_FW_INFO_FIRMWARE_VERSION=3
    
  2. Install the MCUmgr command-line tool if you have not already done so. See MCUmgr Command-line tool for installation and usage instructions.

  3. Upload the new application and network core images to the device.

    Note

    The application image is uploaded to the application slot, and the network core image is uploaded to the network core slot.

    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image upload \
    build_update/tfm_psa_template/zephyr/zephyr.signed.bin -e -n 0
    
    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image upload \
    build_update/signed_by_mcuboot_and_b0_empty_net_core.bin -e -n 1
    
    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image list
    

    Once the images are uploaded, the hash of the images is shown in the image list. The application image is image 1 in slot 0, and the network core image is image 1 in slot 1.

  4. To allow the application and network core images to be updated simultaneously, first confirm the network core image and then the application image:

    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image confirm <network core image hash>
    
    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 image confirm <application core image hash>
    
  5. Trigger the core updates by initiating a reset:

    mcumgr --conntype serial --connstring dev=/dev/ttyACM1,baud=115200,mtu=512 reset
    

    The verification of the images happens during the update process.

Alternatively, you can conduct a manual reset to trigger the core updates. This allows you to observe the update process in the application and network core console outputs.

Building and running

Warning

Make sure to program the device with the provisioning image before building and flashing the PSA template sample - see Building and flashing the provisioning image. When re-flashing the application after provisioning, use plain programming command (west flash) without any additional arguments, such as --erase or --recover. These additional arguments wipe the provisioned lifecycle state and keys.

This sample can be found under samples/tfm/tfm_psa_template in the nRF Connect SDK folder structure.

For more security, it is recommended to use the */ns variant of the board target (see the Requirements section above.) When built for this variant, the sample is configured to compile and run as a non-secure application using security by separation. Therefore, it automatically includes Trusted Firmware-M that prepares the required peripherals and secure services to be available for the application.

To build the sample, follow the instructions in Building an application for your preferred building environment. See also Programming an application for programming steps and Testing and optimization for general information about testing and debugging in the nRF Connect SDK.

Note

When building repository applications in the SDK repositories, building with sysbuild is enabled by default. If you work with out-of-tree freestanding applications, you need to manually pass the --sysbuild parameter to every build command or configure west to always use it.

Testing

After programming the sample, the boot chain runs in this order on every supported target:

msc {
hscale = "1.3";
NSIB, MCUboot, "TF-M", Application;
NSIB=>MCUboot [label="Validate and chain load"];
MCUboot=>"TF-M" [label="Validate and chain load TF-M + App"];
"TF-M"=>Application [label="Finish provisioning if needed and start non-secure application"];
Application=>"TF-M" [label="Request initial attestation public key"];
"TF-M">>Application [label="Return public key"];
Application=>"TF-M" [label="Request PSA initial attestation token"];
"TF-M">>Application [label="Return attestation token"];
Application note Application [label="Prints public key and token as hex dumps"];
Application note Application [label="Waits for DFU requests from mcumgr"];
}

The exact console output differs between board targets because of the different boot chains and partition layouts.

For more information about the PSA Attestation API and the Initial Attestation Key (IAK) on each device, see PSA Attestation API.

Sample output on nRF5340 DK

The example below is taken from an nRF5340 DK in the SECURED lifecycle state. Both FW info blocks and the -10100 PSA certificate reference embedded in the token are nRF5340-specific:

*** Booting nRF Connect SDK v2.9.99-ad7a4f0e68fa ***
*** Using Zephyr OS v3.7.99-f5efb381b8af ***
Attempting to boot slot 0.
Attempting to boot from address 0x8200.
I: Trying to get Firmware version
I: Verifying signature against key 0.
I: Hash: 0xf0...99
I: Firmware signature verified.
Firmware version 1
*** Booting My Application v2.1.0-dev-b82206c15fff ***
*** Using nRF Connect SDK v2.9.99-ad7a4f0e68fa ***
*** Using Zephyr OS v3.7.99-f5efb381b8af ***
I: Starting bootloader
I: Image index: 0, Swap type: none
I: Image index: 1, Swap type: none
I: Image index: 2, Swap type: none
I: Bootloader chainload address offset: 0x20000
*** Booting nRF Connect SDK v2.9.99-ad7a4f0e68fa ***
*** Using Zephyr OS v3.7.99-f5efb381b8af ***
build time: Dec 19 2024 08:31:12

FW info S0:
Magic: 0x281ee6de8fcebb4c00003502
Total Size: 140
Size: 0x0000805c
Version: 1
Address: 0x00008200
Boot address: 0x00008200
Valid: 0x9102ffff (CONFIG_FW_INFO_VALID_VAL=0x9102ffff)

FW info S1:
Magic: 0x281ee6de8fcebb4c00003502
Total Size: 140
Size: 0x0000805c
Version: 1
Address: 0x00014200
Boot address: 0x00014200
Valid: 0x9102ffff (CONFIG_FW_INFO_VALID_VAL=0x9102ffff)

Active slot: S0

Requesting initial attestation public key.
Received initial attestation public key of 65 bytes.

0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
04 8B 4C 2F 19 A7 D0 E6  35 14 BF 8A 2C 7E 91 D4  |  ..L/....5...,~..
56 38 F0 C1 AB 29 E7 5D  83 40 B6 C2 91 7F AE 5D  |  V8...).].@.....]
03 6D 19 E4 B8 7C 0A 5F  23 18 DC 94 E6 51 2B AF  |  .m...|._#....Q+.
70 C8 3D 1E 5A 94 F6 27  B0 D8 A3 1C 6E 45 92 7F  |  p.=.Z..'....nE..
B1                                                |  .


Requesting initial attestation token with 64 byte challenge.
Received initial attestation token of 360 bytes.

0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
D2 84 43 A1 01 26 A0 59  01 1C AA 3A 00 01 24 FF  |  ..C..&.Y...:..$.
58 40 00 11 22 33 44 55  66 77 88 99 AA BB CC DD  |  X@.."3DUfw......
EE FF 00 11 22 33 44 55  66 77 88 99 AA BB CC DD  |  ...."3DUfw......
EE FF 00 11 22 33 44 55  66 77 88 99 AA BB CC DD  |  ...."3DUfw......
EE FF 00 11 22 33 44 55  66 77 88 99 AA BB CC DD  |  ...."3DUfw......
EE FF 3A 00 01 24 FB 58  20 20 E7 18 5E 6F F1 0A  |  ..:..$.X  ..^o..
F7 C0 04 63 A9 6C 73 78  65 92 11 02 EC DF 09 EF  |  ...c.lsxe.......
4C BD 13 AD 69 A3 33 AD  AE 3A 00 01 25 00 58 21  |  L...i.3..:..%.X!
01 69 48 1A 3C 87 E2 6E  48 D3 15 8A 45 F1 D8 E4  |  .iH.<..nH...E...
6A 00 A0 14 0C 87 E5 5E  3F B3 B6 98 45 8F 36 5E  |  j......^?...E.6^
BB 3A 00 01 24 FA 58 20  AA AA AA AA AA AA AA AA  |  .:..$.X ........
BB BB BB BB BB BB BB BB  CC CC CC CC CC CC CC CC  |  ................
DD DD DD DD DD DD DD DD  3A 00 01 24 F8 3A 3B FF  |  ........:..$.:;.
FF FF 3A 00 01 24 F9 19  30 00 3A 00 01 24 FE 01  |  ..:..$..0.:..$..
3A 00 01 24 F7 71 50 53  41 5F 49 4F 54 5F 50 52  |  :..$.qPSA_IOT_PR
4F 46 49 4C 45 5F 31 3A  00 01 25 01 70 76 65 72  |  OFILE_1:..%.pver
69 66 69 63 61 74 69 6F  6E 5F 75 72 6C 3A 00 01  |  ification_url:..
24 FC 73 30 36 33 32 37  39 33 35 31 39 35 33 39  |  $.s0632793519539
2D 31 30 31 30 30 58 40  F9 04 F8 56 4F 89 A0 67  |  [email protected]
2C 21 31 6C 88 EF D6 34  D1 02 4F 6D EA 17 54 9F  |  ,!1l...4..Om..T.
B6 90 E4 6E D6 55 4E D3  62 8C 9D 6A 0F 67 8D 9E  |  ...n.UN.b..j.g..
D7 05 45 3C 89 BE C2 9B  2B D0 ED 05 F1 AC 42 21  |  ..E<....+.....B!
F0 05 00 CE B7 B9 47 E9                           |  ......G.

Sample output on nRF91 Series

The sample output is similar to Sample output on nRF5340 DK, but with version banners and addresses adjusted to match the nRF91 Series devices.

Sample output on nRF54L

On nRF54L, the boot chain is as follows:

msc {
hscale = "1.3";
NSIB, MCUboot, "TF-M + App";
NSIB=>MCUboot [label="Verify and hand off"];
MCUboot=>"TF-M + App" [label="Verify and boot (single slot)"];
"TF-M + App"=>"TF-M + App" [label="Run"];
}

Compared to the Sample output on nRF5340 DK above, the differences are as follows:

  • The two FW info blocks (S0 or S1, or both) and the Active slot line are absent. These rely on the S0 and S1 NSIB upgrade slot pair, which only exists on nRF5340 and nRF91 Series. On nRF54L, the get_fw_info() block is compiled out.

  • MCUboot prints two harmless warnings on every boot:

    W: Cannot upgrade: slots have non-compatible sectors
    I: Secondary image of image pair (1.) is unreachable. Treat it as empty
    

    These warnings appear because MCUboot still iterates over the secondary image entry even though the sample is built with overwrite-only mode and a single slot.

  • The PSA certificate reference embedded in the attestation token is a placeholder 0000000000000-00000, not the 0632793519539-10100 value like in the nRF5340 output.

  • The signature key index reported by NSIB and MCUboot reflects CRACEN KMU slot assignments rather than the on-flash key 0/1 pair used on nRF5340.

  • Everything from the Requesting initial attestation public key. line in the Sample output on nRF5340 DK onward has the same shape; only the public key and token bytes themselves differ (every device’s identity key is unique and the optional fields encode device-specific values.)

Dependencies

This sample uses the following nRF Connect SDK components: