en / de
Expertisen
Methoden
Dienstleistungen
Referenzen
Jobs & Karriere
Firma
Technologie-Trends TechCast WebCast TechBlog News Events Academy

Focus on the Secure Storage service of Trusted Firmware (TFM)

The Secure Storage is one of the main services of the Trusted Firmware and is quite useful to store data in a secure way. Let’s focus on the Secure Storage service of Trusted Firmware (TFM) and modify the current software to write more data into the Secure Storage and read it back.

The third article of the series on Trusted Firmware focuses of the Secure Storage service and extends the regular TFM demo application by writing more pieces of data into the Secure Storage and reading them.

The other articles of the series are:

  1. Security management with Trusted Firmware presents the theoretical part of Trusted Firmware and Secure Boot and Secure Firmware Update
  2. First run of the Trusted Firmware (TFM) application presents the first run of the TFM application
  3. (This article) Focus on the Secure Storage service of Trusted Firmware (TFM) focuses on the Secure Storage service

Memory structure

The TFM demo application uses the internal and external flash memories for data and code areas as shown in Figure 8 below (taken from [UM2671]).

STMicroelectronics UM2671 Figure 8

STMicroelectronics UM2671 Figure 8

The secure application, containing the PSA updatable Root of Trust and the application updatable Root of Trust, is in the area 0 which is located in the internal flash memory. The non-secure application is in the area 1 which is located in the external flash memory. If the non-secure application binary is encrypted, the OTFDEC module decrypts it on-the-fly while the processor fetches the instructions.

The Secure Storage area is also located in the internal flash memory in the secure area and has a size of 8 kbytes.

Secure Storage API

Figure 4 (taken from [UM2671]) presents a clear view of which entity can access which assets.

STMicroelectronics UM2671 Figure 4 - SST

STMicroelectronics UM2671 Figure 4 – SST

The Secure Storage Service accesses the crypto operations, encryption key and NVM data storage. The implementation of the current TFM application is based on the TF-M v1.0-RC2 release [UM2671, p.1] which includes the Secure Storage API v1.0.0 available in PDF format. This document presents the Internal Trusted Storage API and the Secure Storage API (see chapter 5.2 Protected Storage API).

The use of TFM makes it difficult to follow the function calls in the source code. The TFM Middleware, which is available on the non-secure side, implements the functions of the API but they end up making PSA Calls (using the IPC – or interprocess communication – mode) as we have seen in the second article to access the secure side.

Functions implemented in TFM are (descriptions partly taken from API doc):

  1. psa_ps_set: Create a new or modify an existing key/value pair by associating an UID and a data of known size
  2. psa_ps_get: Read part or all of the data (depends on read offset and read size) associated with the provided UID
  3. psa_ps_get_info: Read the metadata associated with an UID (storage size and flags)
  4. psa_ps_remove: Remove the UID and its associated data from the storage
  5. psa_ps_get_support: Return the flags set for all of the optional features supported by the implementation

In the API, function psa_ps_get_info uses struct psa_storage_info_t as container for metadata. This structure contains three fields: the capacity of the storage, the size of the actual stored data and the flags of the storage. However, the implementation uses struct psa_ps_info_t instead which contains only the size of the actual stored data and the flags.

Function psa_ps_set is able to modify the data already stored. As the metadata does not contain a capacity field, it is not possible to reserve more storage space than what the initial call to psa_ps_set stored. Therefore, a modified data must have a size equal to or lower than the previous data stored within the same UID.

Code modified for this article

The modification of the TFM demo application required only a few changes. The biggest part was of course the code developed to run the new tests.

Changes to main.c:

diff --git a/TFM_Appli/NonSecure/Src/main.c b/TFM_Appli/NonSecure/Src/main.c
index eee3deb..df0c98c 100644
--- a/TFM_Appli/NonSecure/Src/main.c
+++ b/TFM_Appli/NonSecure/Src/main.c
@@ -34,6 +34,7 @@ __asm("  .global __ARM_use_no_argv\n");
 #include "test_protections.h"
 #include "fw_update_app.h"
 #include "tfm_app.h"
+#include "tfm_blog.h"
 /** @defgroup  USER_APP  exported variable
    * @{
   */
@@ -168,6 +169,7 @@ void FW_APP_PrintMainMenu(void)
 #if !defined(TFM_EXTERNAL_FLASH_ENABLE)
   printf("  Download a new Fw Image ------------------------------- 3\r\n\n");
 #endif /* !TFM_EXTERNAL_FLASH_ENABLE */
+  printf("  Test TFM BLOG ----------------------------------------- 4\r\n\n");
   printf("  Selection :\r\n\n");
 }
 
@@ -204,6 +206,9 @@ void FW_APP_Run(void)
           FW_UPDATE_Run();
           break;
 #endif /* !TFM_EXTERNAL_FLASH_ENABLE */
+        case '4':
+        	tfm_blog_menu();
+        	break;
         default:
           printf("Invalid Number !\r");
           break;

Changes to project file:

diff --git a/TFM_Appli/STM32CubeIDE/NonSecure/.project b/TFM_Appli/STM32CubeIDE/NonSecure/.project
index 7ab20fa..a7769a4 100644
--- a/TFM_Appli/STM32CubeIDE/NonSecure/.project
+++ b/TFM_Appli/STM32CubeIDE/NonSecure/.project
@@ -81,6 +81,11 @@
 			<type>1</type>
 			<locationURI>PARENT-2-PROJECT_LOC/NonSecure/Src/tfm_app.c</locationURI>
 		</link>
+		<link>
+			<name>Application/User/tfm_blog.c</name>
+			<type>1</type>
+			<locationURI>PARENT-2-PROJECT_LOC/NonSecure/Src/tfm_blog.c</locationURI>
+		</link>
 		<link>
 			<name>Application/User/tfm_ns_lock.c</name>
 			<type>1</type>

Fix of regression.sh:

diff --git a/TFM_SBSFU_Boot/STM32CubeIDE/regression.sh b/TFM_SBSFU_Boot/STM32CubeIDE/regression.sh
index ce975e7..1286f43 100644
--- a/TFM_SBSFU_Boot/STM32CubeIDE/regression.sh
+++ b/TFM_SBSFU_Boot/STM32CubeIDE/regression.sh
@@ -1,6 +1,6 @@
 #!/bin/bash - 
 echo "regression script started"
-PATH="/C/Program Files/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/":$PATH
+PATH="/C/ST/STM32CubeProgrammer/bin/":$PATH
 stm32programmercli="STM32_Programmer_CLI"
 secbootadd0=0x180052
 connect="-c port=SWD mode=UR --hardRst"

New file tfm_blog.h:

/**
  ********************************************************************
  * @file    tfm_blog.h
  * @author  Yorick Brunet
  * @brief   TFM application examples module for bloging purposes.
  ********************************************************************
  */

/* Define to prevent recursive inclusion ---------------------------*/
#ifndef TFM_BLOG_H
#define TFM_BLOG_H

#ifdef __cplusplus
extern "C" {
#endif


#define INVOKE_SCHEDULE_NEEDS()                                      \
      do {                                                           \
        ;                                                            \
      } while(0);



/* Exported prototypes -------------------------------------------- */
void tfm_blog_menu(void);

#ifdef __cplusplus
}
#endif

#endif /* TFM_BLOG_H */

New file tfm_blog.c:

/**
  ********************************************************************
  * @file    tfm_blog.c
  * @author  Yorick Brunet
  * @brief   TFM application examples module for bloging purposes.
  ********************************************************************
  */

/* Includes --------------------------------------------------------*/
#include <string.h>
#include "tfm_blog.h"
#include "psa/error.h"
#include "psa/protected_storage.h"
#include "com.h"


/** @defgroup  TFM_Blog_Private_Defines Private Defines
  * @{
  */

/* Private define  -------------------------------------------------*/
const uint8_t DATA1[] = "HELLO BLOG !";
const uint32_t DATA1_SIZE = sizeof(DATA1) - 1;
const uint8_t DATA2[] = "HOWTO WRITE AND READ SST !";
const uint32_t DATA2_SIZE = sizeof(DATA2) - 1;

typedef enum {
    UID1 = 3U,
    UID2,
} UID_t;

/**
  * @}
  */

/** @defgroup  TFM_Blog_Private_Functions Private Functions
  * @{
  */
static void print_menu(void);
static void sst_set_uid(const UID_t ex_uid);
static void sst_remove_uid(const UID_t ex_uid);
static void sst_read_uid(const UID_t ex_uid);
static void sst_readinfo_uid(const UID_t ex_uid);

/**
  * @}
  */

/** @defgroup  TFM_Blog_Exported_Functions Exported Functions
  * @{
  */

/**
  * @brief  Display the TFM Blog TEST Main Menu choices on HyperTerminal
  * @param  None.
  * @retval None.
  */
void tfm_blog_menu(void)
{
  uint8_t key = 0;
  uint8_t exit = 0;
  print_menu();

  while (exit == 0U)
  {
    key = 0U;

    INVOKE_SCHEDULE_NEEDS();

    /* Clean the user input path */
    COM_Flush();
    /* Receive key */
    if (COM_Receive(&key, 1U, COM_UART_TIMEOUT_MAX) == HAL_OK)
    {
      switch (key)
      {
        case '1' :
          sst_set_uid(UID1);
          print_menu();
          break;
        case '2' :
          sst_read_uid(UID1);
          print_menu();
          break;
        case '3' :
          sst_remove_uid(UID1);
          print_menu();
          break;
        case '4':
          sst_readinfo_uid(UID1);
          print_menu();
          break;
        case '5' :
          sst_set_uid(UID2);
          print_menu();
          break;
        case '6' :
          sst_read_uid(UID2);
          print_menu();
          break;
        case '7' :
          sst_remove_uid(UID2);
          print_menu();
          break;
        case '8':
          sst_readinfo_uid(UID2);
          print_menu();
          break;

        case 'x':
          exit = 1;
          break;

        default:
          printf("Invalid Number !\r");
          print_menu();
          break;
      }
    }
  }
}
/**
  * @}
  */

/** @addtogroup  TFM_Blog_Private_Functions
  * @{
  */

/**
  * @brief  Display the TEST TFM Blog Menu choices on HyperTerminal
  * @param  None.
  * @retval None.
  */
static void print_menu(void)
{
  printf("\r\n======================= TFM Examples Menu ===========================\r\n\n");
  printf("  TFM - Test SST set UID 1                         --------------------- 1\r\n\n");
  printf("  TFM - Test SST read / check UID 1                --------------------- 2\r\n\n");
  printf("  TFM - Test SST remove UID 1                      --------------------- 3\r\n\n");
  printf("  TFM - Test SST info UID 1                        --------------------- 4\r\n\n");
  printf("  TFM - Test SST set UID 2                         --------------------- 5\r\n\n");
  printf("  TFM - Test SST read / check UID 2                --------------------- 6\r\n\n");
  printf("  TFM - Test SST remove UID 2                      --------------------- 7\r\n\n");
  printf("  TFM - Test SST info UID 2                        --------------------- 8\r\n\n");
  printf("  Exit TFM Examples Menu                           --------------------- x\r\n\n");
}
/**
  * @brief  Write in SST a TEST UID
  */
static void sst_set_uid(const UID_t ex_uid)
{
    psa_ps_status_t status;
    const psa_ps_uid_t uid = (psa_ps_uid_t)ex_uid;
    const psa_ps_create_flags_t flags = PSA_PS_FLAG_NONE;
    uint32_t write_len = 0;
    uint8_t* write_data = NULL;
    if( ex_uid == UID1 )
    {
        write_data = (uint8_t*)DATA1;
        write_len = DATA1_SIZE;
    }
    else if( ex_uid == UID2 )
    {
        write_data = (uint8_t*)DATA2;
        write_len = DATA2_SIZE;
    }
    else
    {
        printf("UNKNOWN UID\r\n");
        return;
    }
    status = psa_ps_set(uid, write_len, write_data, flags);
    printf("SST set UID: \"%s\": %s\r\n", write_data, (status == PSA_SUCCESS) ? "SUCCESSFUL" : "FAILED");
}

/**
  * @brief  Remove from SST a TEST UID
  */
static void sst_remove_uid(const UID_t ex_uid)
{
    psa_ps_status_t status;
    const psa_ps_uid_t uid = (psa_ps_uid_t)ex_uid;
    status = psa_ps_remove(uid);
    printf("SST remove UID %s\r\n", (status == PSA_SUCCESS) ? "SUCCESSFUL" : "FAILED");
}

/**
  * @brief  Read SST TEST UID and compare its value
  */
static void sst_read_uid(const UID_t ex_uid)
{
    psa_ps_status_t status;
    const psa_ps_uid_t uid = (psa_ps_uid_t)ex_uid;
    uint8_t* expected_data = NULL;
    uint8_t read_data[100] = { '\0' };
    uint32_t read_len = 0;
    if( ex_uid == UID1 )
    {
        expected_data = (uint8_t*)DATA1;
        read_len = DATA1_SIZE;
    }
    else if( ex_uid == UID2 )
    {
        expected_data = (uint8_t*)DATA2;
        read_len = DATA2_SIZE;
    }
    else
    {
        printf("UNKNOWN UID\r\n");
        return;
    }
    // Read
    status = psa_ps_get(uid, 0, read_len, read_data);
    if ((status == PSA_SUCCESS) && (!memcmp(read_data, expected_data, read_len)))
    {
        printf("SST read UID: \"%s\" : SUCCESSFUL\r\n", read_data);
    }
    else
    {
        printf("SST read UID: \"%s\" : FAILED\r\n", read_data);
    }
}

/**
  * @brief Read storage info of a TEST UID
  */
static void sst_readinfo_uid(const UID_t ex_uid)
{
    psa_ps_status_t status;
    const psa_ps_uid_t uid = (psa_ps_uid_t)ex_uid;
    struct psa_ps_info_t info = { 0 };
    // Read
    status = psa_ps_get_info(uid, &info);
    printf("SST info UID %s\r\n", (status == PSA_SUCCESS) ? "SUCCESSFUL" : "FAILED");
    if (status == PSA_SUCCESS)
    {
        printf("SST info UID: size = %u, flags = %x\r\n", info.size, info.flags);
    }
}

/**
  * @}
  */

Run of the modified application

To run the modified application, and assuming that you already followed all steps presented in the second article, you can execute only the following steps. Otherwise follow all steps presented in the second article.

  1. Build TFM_Appli_NonSecure
  2. Open Tera Term and configure the Serial Port
  3. Run regression.sh (located in project TFM_SBSFU_Boot) – necessary to clean TFM flags and allow the board to be programmed again
  4. Run TFM_UPDATE.sh (located in project TFM_SBSFU_Boot)
  5. Press the reset button on the board

The non-secure application should display:

======================================================================
=              (C) COPYRIGHT 2019 STMicroelectronics                 =
=                                                                    =
=                          User App #A                               =
======================================================================

=================== Main Menu ============================
  Test Protections -------------------------------------- 1
  Test TFM ---------------------------------------------- 2
  Test TFM BLOG ----------------------------------------- 4
  Selection :

Type 4 to display the different options added for this article:

======================= TFM Examples Menu ===========================
  TFM - Test SST set UID 1                    --------------------- 1
  TFM - Test SST read / check UID 1           --------------------- 2
  TFM - Test SST remove UID 1                 --------------------- 3
  TFM - Test SST info UID 1                   --------------------- 4
  TFM - Test SST set UID 2                    --------------------- 5
  TFM - Test SST read / check UID 2           --------------------- 6
  TFM - Test SST remove UID 2                 --------------------- 7
  TFM - Test SST info UID 2                   --------------------- 8
  Exit TFM Examples Menu                      --------------------- x

Tests 1 to 4 respectively set, read, remove data and get the metadata associated to UID 1 while tests 5 to 8 do the same operations for UID 2.

Lines 23 and 25 of tfm_blog.c define the data that is stored in both storages. DATA1 is stored in the first storage and associated with UID 1

const uint8_t DATA1[] = "HELLO BLOG !";

while DATA2 is stored in the second storage and associated with UID 2

const uint8_t DATA2[] = "HOWTO WRITE AND READ SST !"

The listing below presents a flow of operations and the results:

Theses tests show that several pieces of data can be stored in the Secure Storage, up to the maximum size of the Secure Storage itself. However, as metadata is also stored for each data, the total size of data must be lower than the size of the Secure Storage memory area. It may not be very efficient to store only a few bytes, though.

Conclusion

This article added new features to the TFM demo application by extending the tests on the Secure Storage. The implementation of TFM and the API of the Secure Storage do not completely match but using the existing code as example allows anyway the user to reach its goal.

A (better) documentation of the interprocess communication between the non-secure and secure worlds would definitely be helpful to understand in detail how TFM works.

Kommentare

Eine Antwort zu “Focus on the Secure Storage service of Trusted Firmware (TFM)”

  1. […] Focus on the Secure Storage service of Trusted Firmware (TFM) focuses on the Secure Storage service […]

Schreiben Sie einen Kommentar

Ihre E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Newsletter - aktuelle Angebote, exklusive Tipps und spannende Neuigkeiten

 Jetzt anmelden
NACH OBEN
Zur Webcast Übersicht