samples: Add TF-M sample for custom secure partitions

The sample implements a dummy ARoT (Application Root of Trust) partition
in TF-M and calls it from the app.

Signed-off-by: Øyvind Rønningstad <oyvind.ronningstad@nordicsemi.no>
Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
This commit is contained in:
Øyvind Rønningstad 2021-06-25 10:31:18 +02:00 committed by Christopher Friedt
commit 449cb60815
13 changed files with 522 additions and 1 deletions

View file

@ -1,3 +1,5 @@
.. _tfm_build_system:
TF-M Build System TF-M Build System
################# #################

View file

@ -0,0 +1,36 @@
#
# Copyright (c) 2021 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
cmake_minimum_required(VERSION 3.20)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
get_target_property(TFM_BINARY_DIR tfm TFM_BINARY_DIR)
configure_file(
${CMAKE_CURRENT_LIST_DIR}/dummy_partition/tfm_manifest_list.yaml.in
${CMAKE_CURRENT_BINARY_DIR}/dummy_partition/tfm_manifest_list.yaml
)
set_property(TARGET zephyr_property_target
APPEND PROPERTY TFM_CMAKE_OPTIONS
-DTFM_EXTRA_MANIFEST_LIST_FILES=${CMAKE_CURRENT_BINARY_DIR}/dummy_partition/tfm_manifest_list.yaml
-DTFM_EXTRA_PARTITION_PATHS=${CMAKE_CURRENT_LIST_DIR}/dummy_partition
)
project(tfm_secure_partition)
target_sources(app PRIVATE
src/main.c
src/dummy_partition.c
)
target_include_directories(app PRIVATE
${ZEPHYR_TRUSTED_FIRMWARE_M_MODULE_DIR}/interface/include
)
target_compile_definitions(app
PRIVATE TFM_PARTITION_DUMMY_PARTITION
)

View file

@ -0,0 +1,51 @@
.. _tfm_secure_partition:
TF-M Secure Partition Sample
############################
Overview
********
A Secure Partition is an isolated module that resides in TF-M. It exposes a number of functions or "secure services" to other partitions and/or to the non-secure firmware.
TF-M already contains standard partitions such as crypto, protected_storage, or firmware_update, but it's also possible to create your own partitions.
This sample creates a dummy secure partition and secure service for TF-M and instructs the TF-M build system to build it into the secure firmware.
The dummy secure service is then called in the main file (in the non-secure firmware).
This dummy partition has a single secure service, which can index one of 5 dummy secrets inside the partition, and retrieve a hash of the secret.
The partition is located in the ``dummy_partition`` directory. It contains the partition sources, build files and build configuration files.
The partition is built by the TF-M build system, refer to :ref:`tfm_build_system` for more details.
For more information on how to add custom secure partitions refer to TF-M's guide: https://tf-m-user-guide.trustedfirmware.org/docs/integration_guide/services/tfm_secure_partition_addition.html
When adapting this partition for your own purposes, please change all occurences of "dummy_partition", "DUMMY_PARTITION", "dp", and "DP" to your own partition name.
Also, look through both the secure and non-secure CMakeLists.txt file and make relevant changes, as well as the yaml files inside "partition".
Building and Running
********************
This sample can be built with or without CONFIG_TFM_IPC, since it contains code for both.
On Target
=========
Refer to :ref:`tfm_ipc` for detailed instructions.
On QEMU:
========
Refer to :ref:`tfm_ipc` for detailed instructions.
Sample Output
=============
.. code-block:: console
*** Booting Zephyr OS build v2.6.0-rc1-ncs1-1-g58213e91eef1 ***
Digest: be45cb2605bf36bebde684841a28f0fd43c69850a3dce5fedba69928ee3a8991
Digest: 1452c8f04245d355722fdbfb03c69bcfd380b7dff911a3e425013397251f6a4e
Digest: d3b4349010abb691b9584b6fd6b41ec54596ef7b98d853fb4f5bfa690f50f222
Digest: 5afbcfede855ca834ff5b4e8a44a32206a51381f3cf52f5001a3241f017ac41a
Digest: 983318380c325099da63de2e7ca57c1630693b28b4754e08817533295dbfcfbb
Status: -135

View file

@ -0,0 +1,54 @@
#
# Copyright (c) 2021 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
add_library(tfm_app_rot_partition_dp STATIC)
target_include_directories(tfm_app_rot_partition_dp
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
PRIVATE
${CMAKE_BINARY_DIR}/dummy_partition
)
target_include_directories(tfm_partitions
INTERFACE
${CMAKE_BINARY_DIR}/dummy_partition
)
target_sources(tfm_app_rot_partition_dp
PRIVATE
dummy_partition.c
)
# The generated sources
target_sources(tfm_app_rot_partition_dp
PRIVATE
$<$<BOOL:${TFM_PSA_API}>:
${CMAKE_BINARY_DIR}/dummy_partition/auto_generated/intermedia_tfm_dummy_partition.c>
)
target_sources(tfm_partitions
INTERFACE
$<$<BOOL:${TFM_PSA_API}>:
${CMAKE_BINARY_DIR}/dummy_partition/auto_generated/load_info_tfm_dummy_partition.c>
)
target_link_libraries(tfm_app_rot_partition_dp
PRIVATE
tfm_secure_api
psa_interface
secure_fw
platform_s
tfm_psa_rot_partition_crypto
)
target_link_libraries(tfm_partitions
INTERFACE
tfm_app_rot_partition_dp
)
target_compile_definitions(tfm_partition_defs
INTERFACE
TFM_PARTITION_DUMMY_PARTITION
)

View file

@ -0,0 +1,169 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <psa/crypto.h>
#include <stdbool.h>
#include <stdint.h>
#include "tfm_secure_api.h"
#include "tfm_api.h"
#define NUM_SECRETS 5
struct dp_secret {
uint8_t secret[16];
};
struct dp_secret secrets[NUM_SECRETS] = {
{ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} },
{ {1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} },
{ {2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} },
{ {3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} },
{ {4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} },
};
typedef void (*psa_write_callback_t)(void *handle, uint8_t *digest,
uint32_t digest_size);
static psa_status_t tfm_dp_secret_digest(uint32_t secret_index,
size_t digest_size, size_t *p_digest_size,
psa_write_callback_t callback, void *handle)
{
uint8_t digest[32];
psa_status_t status;
/* Check that secret_index is valid. */
if (secret_index >= NUM_SECRETS) {
return PSA_ERROR_INVALID_ARGUMENT;
}
/* Check that digest_size is valid. */
if (digest_size != sizeof(digest)) {
return PSA_ERROR_INVALID_ARGUMENT;
}
status = psa_hash_compute(PSA_ALG_SHA_256, secrets[secret_index].secret,
sizeof(secrets[secret_index].secret), digest,
digest_size, p_digest_size);
if (status != PSA_SUCCESS) {
return status;
}
if (*p_digest_size != digest_size) {
return PSA_ERROR_PROGRAMMER_ERROR;
}
callback(handle, digest, digest_size);
return PSA_SUCCESS;
}
#ifndef TFM_PSA_API
#include "tfm_memory_utils.h"
void psa_write_digest(void *handle, uint8_t *digest, uint32_t digest_size)
{
tfm_memcpy(handle, digest, digest_size);
}
psa_status_t tfm_dp_secret_digest_req(psa_invec *in_vec, size_t in_len,
psa_outvec *out_vec, size_t out_len)
{
uint32_t secret_index;
if ((in_len != 1) || (out_len != 1)) {
/* The number of arguments are incorrect */
return PSA_ERROR_PROGRAMMER_ERROR;
}
if (in_vec[0].len != sizeof(secret_index)) {
/* The input argument size is incorrect */
return PSA_ERROR_PROGRAMMER_ERROR;
}
secret_index = *((uint32_t *)in_vec[0].base);
return tfm_dp_secret_digest(secret_index, out_vec[0].len,
&out_vec[0].len, psa_write_digest,
(void *)out_vec[0].base);
}
#else /* !defined(TFM_PSA_API) */
#include "psa/service.h"
#include "psa_manifest/tfm_dummy_partition.h"
typedef psa_status_t (*dp_func_t)(psa_msg_t *);
static void psa_write_digest(void *handle, uint8_t *digest,
uint32_t digest_size)
{
psa_write((psa_handle_t)handle, 0, digest, digest_size);
}
static psa_status_t tfm_dp_secret_digest_ipc(psa_msg_t *msg)
{
size_t num = 0;
uint32_t secret_index;
if (msg->in_size[0] != sizeof(secret_index)) {
/* The size of the argument is incorrect */
return PSA_ERROR_PROGRAMMER_ERROR;
}
num = psa_read(msg->handle, 0, &secret_index, msg->in_size[0]);
if (num != msg->in_size[0]) {
return PSA_ERROR_PROGRAMMER_ERROR;
}
return tfm_dp_secret_digest(secret_index, msg->out_size[0],
&msg->out_size[0], psa_write_digest,
(void *)msg->handle);
}
static void dp_signal_handle(psa_signal_t signal, dp_func_t pfn)
{
psa_status_t status;
psa_msg_t msg;
status = psa_get(signal, &msg);
switch (msg.type) {
case PSA_IPC_CONNECT:
psa_reply(msg.handle, PSA_SUCCESS);
break;
case PSA_IPC_CALL:
status = pfn(&msg);
psa_reply(msg.handle, status);
break;
case PSA_IPC_DISCONNECT:
psa_reply(msg.handle, PSA_SUCCESS);
break;
default:
psa_panic();
}
}
#endif /* !defined(TFM_PSA_API) */
psa_status_t tfm_dp_req_mngr_init(void)
{
#ifdef TFM_PSA_API
psa_signal_t signals = 0;
while (1) {
signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK);
if (signals & TFM_DP_SECRET_DIGEST_SIGNAL) {
dp_signal_handle(TFM_DP_SECRET_DIGEST_SIGNAL,
tfm_dp_secret_digest_ipc);
} else {
psa_panic();
}
}
return PSA_ERROR_SERVICE_FAILURE;
#else
return PSA_SUCCESS;
#endif
}

View file

@ -0,0 +1,43 @@
#-------------------------------------------------------------------------------
# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
# Copyright (c) 2021, Nordic Semiconductor ASA. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
#-------------------------------------------------------------------------------
{
"psa_framework_version": 1.0,
"name": "TFM_SP_DP",
"type": "APPLICATION-ROT",
"priority": "NORMAL",
"entry_point": "tfm_dp_req_mngr_init",
"stack_size": "0x800",
# Service definitions for library mode.
"secure_functions": [
{
"name": "TFM_DP_SECRET_DIGEST",
"signal": "TFM_DP_SECRET_DIGEST_REQ",
"non_secure_clients": true,
"version": 1,
"version_policy": "STRICT"
}
],
# Service definitions for IPC mode.
"services" : [{
"name": "TFM_DP_SECRET_DIGEST",
"sid": "0xFFFFF001", # Bits [31:12] denote the vendor (change this),
# bits [11:0] are arbitrary at the discretion of the
# vendor.
"non_secure_clients": true,
"version": 1,
"version_policy": "STRICT"
}
],
"dependencies": [
"TFM_CRYPTO"
]
}

View file

@ -0,0 +1,30 @@
#-------------------------------------------------------------------------------
# Copyright (c) 2018-2021, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
#-------------------------------------------------------------------------------
{
"name": "TF-M secure partition manifests",
"type": "manifest_list",
"version_major": 0,
"version_minor": 1,
"manifest_list": [
{
"name": "Dummy Partition",
"short_name": "TFM_DP",
"manifest": "${APPLICATION_SOURCE_DIR}/dummy_partition/tfm_dummy_partition.yaml",
"output_path": "${TFM_BINARY_DIR}/dummy_partition",
"tfm_partition_ipc": true,
"conditional": "TFM_PARTITION_DUMMY_PARTITION",
"version_major": 0,
"version_minor": 1,
"linker_pattern": {
"library_list": [
"*tfm_*partition_dp.*"
],
}
},
]
}

View file

@ -0,0 +1,7 @@
#
# Copyright (c) 2021 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
CONFIG_BUILD_WITH_TFM=y

View file

@ -0,0 +1,23 @@
common:
tags: tfm
platform_allow: mps2_an521_nonsecure v2m_musca_s1_nonsecure
nrf5340dk_nrf5340_cpuappns nrf9160dk_nrf9160ns
harness: console
harness_config:
type: multi_line
regex:
- "Digest: be45cb2605bf36bebde684841a28f0fd43c69850a3dce5fedba69928ee3a8991"
- "Digest: 1452c8f04245d355722fdbfb03c69bcfd380b7dff911a3e425013397251f6a4e"
- "Digest: d3b4349010abb691b9584b6fd6b41ec54596ef7b98d853fb4f5bfa690f50f222"
- "Digest: 5afbcfede855ca834ff5b4e8a44a32206a51381f3cf52f5001a3241f017ac41a"
- "Digest: 983318380c325099da63de2e7ca57c1630693b28b4754e08817533295dbfcfbb"
- "Status: -135"
sample:
name: "TFM Secure Partition Sample"
tests:
sample.tfm.secure_partition:
tags: tfm
sample.tfm.secure_partition.ipc:
tags: tfm
extra_args: "CONFIG_TFM_IPC=y"

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <tfm_veneers.h>
#include <tfm_ns_interface.h>
#include "dummy_partition.h"
#if defined(CONFIG_TFM_IPC)
#include "psa/client.h"
#include "psa_manifest/sid.h"
psa_status_t dp_secret_digest(uint32_t secret_index,
void *p_digest,
size_t digest_size)
{
psa_status_t status;
psa_handle_t handle;
psa_invec in_vec[] = {
{ .base = &secret_index, .len = sizeof(secret_index) },
};
psa_outvec out_vec[] = {
{ .base = p_digest, .len = digest_size }
};
handle = psa_connect(TFM_DP_SECRET_DIGEST_SID,
TFM_DP_SECRET_DIGEST_VERSION);
if (!PSA_HANDLE_IS_VALID(handle)) {
return PSA_ERROR_GENERIC_ERROR;
}
status = psa_call(handle, PSA_IPC_CALL, in_vec, IOVEC_LEN(in_vec),
out_vec, IOVEC_LEN(out_vec));
psa_close(handle);
return status;
}
#else /* defined(CONFIG_TFM_IPC) */
psa_status_t dp_secret_digest(uint32_t secret_index,
void *p_digest,
size_t digest_size)
{
psa_status_t status;
psa_invec in_vec[] = {
{ .base = &secret_index, .len = sizeof(secret_index) },
};
psa_outvec out_vec[] = {
{ .base = p_digest, .len = digest_size }
};
status = tfm_ns_interface_dispatch(
(veneer_fn)tfm_dp_secret_digest_req_veneer,
(uint32_t)in_vec, IOVEC_LEN(in_vec),
(uint32_t)out_vec, IOVEC_LEN(out_vec));
return status;
}
#endif /* defined(CONFIG_TFM_IPC) */

View file

@ -0,0 +1,11 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "tfm_api.h"
psa_status_t dp_secret_digest(uint32_t secret_index,
void *p_digest,
size_t digest_size);

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <tfm_veneers.h>
#include <tfm_ns_interface.h>
#include "dummy_partition.h"
void main(void)
{
uint8_t digest[32];
for (int key = 0; key < 6; key++) {
psa_status_t status = dp_secret_digest(key, digest, sizeof(digest));
if (status != PSA_SUCCESS) {
printk("Status: %d\n", status);
} else {
printk("Digest: ");
for (int i = 0; i < 32; i++) {
printk("%02x", digest[i]);
}
printk("\n");
}
}
}

View file

@ -218,7 +218,7 @@ manifest:
groups: groups:
- debug - debug
- name: trusted-firmware-m - name: trusted-firmware-m
revision: 400f58ff53eb1dea5234925e96c5279154515cc7 revision: 7c88781953ce7146878bd8bd724e175fdbc298c5
path: modules/tee/tf-m/trusted-firmware-m path: modules/tee/tf-m/trusted-firmware-m
groups: groups:
- tee - tee