From b763ae5a9488e697a6768a83bebfcfd68dbe10e6 Mon Sep 17 00:00:00 2001
From: Ricardo Casallas <77841255+rcasallas-silabs@users.noreply.github.com>
Date: Fri, 3 May 2024 08:03:08 -0400
Subject: [PATCH] PSA Mutex patch.
---
.../sl_mbedtls_support/inc/threading_alt.h | 232 ++
.../sl_psa_driver/src/sl_psa_its_nvm3.c | 3181 +++++++++++++++++
2 files changed, 3413 insertions(+)
create mode 100644 platform/security/sl_component/sl_mbedtls_support/inc/threading_alt.h
create mode 100644 platform/security/sl_component/sl_psa_driver/src/sl_psa_its_nvm3.c
diff --git a/platform/security/sl_component/sl_mbedtls_support/inc/threading_alt.h b/platform/security/sl_component/sl_mbedtls_support/inc/threading_alt.h
new file mode 100644
index 0000000000..6422f22b45
--- /dev/null
+++ b/platform/security/sl_component/sl_mbedtls_support/inc/threading_alt.h
@@ -0,0 +1,232 @@
+/*******************************************************************************
+ * @file
+ * @brief Threading primitive implementation for mbed TLS
+ *******************************************************************************
+ * # License
+ * Copyright 2021 Silicon Laboratories Inc. www.silabs.com
+ *******************************************************************************
+ *
+ * SPDX-License-Identifier: Zlib
+ *
+ * The licensor of this software is Silicon Laboratories Inc.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ ******************************************************************************/
+
+#ifndef THREADING_ALT_H
+#define THREADING_ALT_H
+
+/*******************************************************************************
+ * \addtogroup sl_crypto
+ * \{
+ ******************************************************************************/
+
+/*******************************************************************************
+ * \addtogroup sl_crypto_threading Threading Primitives
+ * \brief Threading primitive implementation for mbed TLS
+ *
+ * This file contains the glue logic between the mbed TLS threading API
+ * and CMSIS RTOS2 API.
+ *
+ * In order to enable support for Micrium OS backend
+ * the user must make sure SL_CATALOG_MICRIUMOS_KERNEL_PRESENT is defined.
+ * In order to enable support for FreeRTOS backend the user must make sure
+ * SL_CATALOG_FREERTOS_KERNEL_PRESENT is defined.
+ *
+ * Applications created using Simplicity Studio 5 the sl_component_catalog.h
+ * file will define one of the above in order to declare the presence
+ * of a specific RTOS.
+ *
+ * \note
+ * In order to use the Silicon Labs Hardware Acceleration plugins in
+ * multi-threaded applications, select
+ * Mbed TLS support for EFM32/EFR32 crypto acceleration component.
+ *
+ * \{
+ ******************************************************************************/
+
+#include "mbedtls/threading.h"
+#include
+
+#if defined(MBEDTLS_THREADING_ALT) && defined(MBEDTLS_THREADING_C)
+
+#if defined(SL_COMPONENT_CATALOG_PRESENT)
+#include "sl_component_catalog.h"
+#endif
+
+#if defined(SL_CATALOG_MICRIUMOS_KERNEL_PRESENT) || \
+ defined(SL_CATALOG_FREERTOS_KERNEL_PRESENT)
+
+#include "cmsis_os2.h"
+#include "sl_assert.h"
+
+#if defined(SL_CATALOG_FREERTOS_KERNEL_PRESENT)
+#include "FreeRTOSConfig.h"
+#if (configSUPPORT_STATIC_ALLOCATION == 1)
+#include "FreeRTOS.h" // StaticSemaphore_t
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SL_THREADING_ALT
+
+#define MUTEX_INIT = {0}
+
+/// SE manager mutex definition for CMSIS RTOS2.
+typedef struct mbedtls_threading_mutex {
+#if defined(SL_CATALOG_FREERTOS_KERNEL_PRESENT) && \
+ (configSUPPORT_STATIC_ALLOCATION == 1)
+ StaticSemaphore_t static_sem_object;
+#endif
+ osMutexAttr_t mutex_attr;
+ osMutexId_t mutex_ID;
+} mbedtls_threading_mutex_t;
+
+/**
+ * \brief Initialize a given mutex
+ *
+ * \param mutex Pointer to the mutex needing initialization
+ */
+static inline void THREADING_InitMutex(mbedtls_threading_mutex_t *mutex) {
+ if (mutex == NULL) {
+ return;
+ }
+
+#if defined(SL_CATALOG_FREERTOS_KERNEL_PRESENT) && \
+ (configSUPPORT_STATIC_ALLOCATION == 1)
+ mutex->mutex_attr.cb_mem = &mutex->static_sem_object;
+ mutex->mutex_attr.cb_size = sizeof(mutex->static_sem_object);
+#endif
+
+ mutex->mutex_ID = osMutexNew(&mutex->mutex_attr);
+
+ EFM_ASSERT(mutex->mutex_ID != NULL);
+}
+
+/**
+ * \brief Free a given mutex
+ *
+ * \param mutex Pointer to the mutex being freed
+ */
+static inline void THREADING_FreeMutex(mbedtls_threading_mutex_t *mutex) {
+ if (mutex == NULL) {
+ return;
+ }
+
+ osStatus_t status = osMutexDelete(mutex->mutex_ID);
+ EFM_ASSERT(status == osOK);
+}
+
+/**
+ * \brief Pend on a mutex
+ *
+ * \param mutex Pointer to the mutex being pended on
+ *
+ * \return RTOS_ERR_NONE on success, error code otherwise.
+ */
+static inline int
+THREADING_TakeMutexBlocking(mbedtls_threading_mutex_t *mutex) {
+ if (mutex == NULL) {
+ return MBEDTLS_ERR_THREADING_BAD_INPUT_DATA;
+ }
+
+ osStatus_t status = osOK;
+ if (osKernelGetState() == osKernelRunning) {
+ status = osMutexAcquire(mutex->mutex_ID, osWaitForever);
+ }
+ return (status == osOK ? 0 : MBEDTLS_ERR_THREADING_MUTEX_ERROR);
+}
+
+/**
+ * \brief Try to own a mutex without waiting
+ *
+ * \param mutex Pointer to the mutex being tested
+ *
+ * \return RTOS_ERR_NONE on success (= mutex successfully owned), error code otherwise.
+ */
+static inline int
+THREADING_TakeMutexNonBlocking(mbedtls_threading_mutex_t *mutex) {
+ if (mutex == NULL) {
+ return MBEDTLS_ERR_THREADING_BAD_INPUT_DATA;
+ }
+
+ osStatus_t status = osOK;
+ if (osKernelGetState() == osKernelRunning) {
+ status = osMutexAcquire(mutex->mutex_ID, 0u);
+ }
+ return (status == osOK ? 0 : MBEDTLS_ERR_THREADING_MUTEX_ERROR);
+}
+
+/**
+ * \brief Release a mutex
+ *
+ * \param mutex Pointer to the mutex being released
+ *
+ * \return RTOS_ERR_NONE on success, error code otherwise.
+ */
+static inline int THREADING_GiveMutex(mbedtls_threading_mutex_t *mutex) {
+ if (mutex == NULL) {
+ return MBEDTLS_ERR_THREADING_BAD_INPUT_DATA;
+ }
+
+ osStatus_t status = osOK;
+ if (osKernelGetState() == osKernelRunning) {
+ status = osMutexRelease(mutex->mutex_ID);
+ }
+ return (status == osOK ? 0 : MBEDTLS_ERR_THREADING_MUTEX_ERROR);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SL_CATALOG_MICRIUMOS_KERNEL_PRESENT ||
+ // SL_CATALOG_FREERTOS_KERNEL_PRESENT
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Forward declaration of threading_set_alt */
+void mbedtls_threading_set_alt(
+ void (*mutex_init)(mbedtls_threading_mutex_t *),
+ void (*mutex_free)(mbedtls_threading_mutex_t *),
+ int (*mutex_lock)(mbedtls_threading_mutex_t *),
+ int (*mutex_unlock)(mbedtls_threading_mutex_t *));
+
+/**
+ * \brief Helper function for setting up the mbed TLS threading subsystem
+ */
+static inline void THREADING_setup(void) {
+ mbedtls_threading_set_alt(&THREADING_InitMutex, &THREADING_FreeMutex,
+ &THREADING_TakeMutexBlocking, &THREADING_GiveMutex);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MBEDTLS_THREADING_ALT && MBEDTLS_THREADING_C */
+
+/** \} (end addtogroup sl_crypto_threading) */
+/** \} (end addtogroup sl_crypto) */
+
+#endif /* THREADING_ALT_H */
diff --git a/platform/security/sl_component/sl_psa_driver/src/sl_psa_its_nvm3.c b/platform/security/sl_component/sl_psa_driver/src/sl_psa_its_nvm3.c
new file mode 100644
index 0000000000..c12d0f1bcb
--- /dev/null
+++ b/platform/security/sl_component/sl_psa_driver/src/sl_psa_its_nvm3.c
@@ -0,0 +1,3181 @@
+/*******************************************************************************
+ * @file
+ * @brief PSA ITS implementation based on Silicon Labs NVM3
+ *******************************************************************************
+ * # License
+ * Copyright 2020 Silicon Laboratories Inc. www.silabs.com
+ *******************************************************************************
+ *
+ * SPDX-License-Identifier: Zlib
+ *
+ * The licensor of this software is Silicon Laboratories Inc.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ ******************************************************************************/
+
+// The psa_driver_wrappers.h file that we're including here assumes that it has
+// access to private struct members. Define this here in order to avoid
+// compilation errors.
+#define MBEDTLS_ALLOW_PRIVATE_ACCESS
+
+// -------------------------------------
+// Includes
+
+#include
+
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) && !defined(MBEDTLS_PSA_ITS_FILE_C)
+
+#include "mbedtls/platform.h"
+#include "nvm3_default.h"
+#include "psa/internal_trusted_storage.h"
+#include "psa/sli_internal_trusted_storage.h"
+#include
+#include
+
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+#include "psa/storage_common.h"
+#include
+#endif // TFM_CONFIG_SL_SECURE_LIBRARY
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+#include "psa_crypto_core.h"
+#include "psa_crypto_driver_wrappers.h"
+#if defined(SEMAILBOX_PRESENT)
+#include "psa/crypto_extra.h"
+#include "sl_psa_values.h"
+#include "sli_se_opaque_functions.h"
+#endif // defined(SEMAILBOX_PRESENT)
+#endif // defined(SLI_PSA_ITS_ENCRYPTED)
+
+// SLI_STATIC_TESTABLE is used to expose otherwise-static variables during
+// internal testing.
+#if defined(SLI_STATIC_TESTABLE)
+#define SLI_STATIC
+#else
+#define SLI_STATIC static
+#endif
+
+// -------------------------------------
+// Threading support
+
+#if defined(MBEDTLS_THREADING_C)
+#include "cmsis_os2.h"
+#include "mbedtls/threading.h"
+
+// Mutex for protecting access to the ITS instance
+SLI_STATIC mbedtls_threading_mutex_t its_mutex MUTEX_INIT;
+static volatile bool its_mutex_inited = false;
+
+/**
+ * \brief Lock all task switches
+ *
+ * \return Previous lock state
+ *
+ */
+static inline int32_t lock_task_switches(void) {
+ int32_t kernel_lock_state = 0;
+ osKernelState_t kernel_state = osKernelGetState();
+ if (kernel_state != osKernelInactive && kernel_state != osKernelReady) {
+ kernel_lock_state = osKernelLock();
+ }
+ return kernel_lock_state;
+}
+
+/**
+ * \brief Restores the previous lock state
+ */
+static inline void restore_lock_state(int32_t kernel_lock_state) {
+ osKernelState_t kernel_state = osKernelGetState();
+ if (kernel_state != osKernelInactive && kernel_state != osKernelReady) {
+ if (osKernelRestoreLock(kernel_lock_state) < 0) {
+ EFM_ASSERT(false);
+ }
+ }
+}
+
+#endif // defined(MBEDTLS_THREADING_C)
+
+/**
+ * \brief Pend on the ITS mutex
+ */
+void sli_its_acquire_mutex(void) {
+#if defined(MBEDTLS_THREADING_C)
+ if (!its_mutex_inited) {
+ int32_t kernel_lock_state = lock_task_switches();
+ if (!its_mutex_inited) {
+ // The ITS mutex needs to be recursive since the same thread may need
+ // to acquire it more than one time.
+ its_mutex.mutex_attr.attr_bits |= osMutexRecursive;
+ mbedtls_mutex_init(&its_mutex);
+ its_mutex_inited = true;
+ }
+ restore_lock_state(kernel_lock_state);
+ }
+ if (mbedtls_mutex_lock(&its_mutex) != 0) {
+ EFM_ASSERT(false);
+ }
+#endif
+}
+
+/**
+ * \brief Free the ITS mutex.
+ */
+void sli_its_release_mutex(void) {
+#if defined(MBEDTLS_THREADING_C)
+ if (its_mutex_inited) {
+ mbedtls_mutex_unlock(&its_mutex);
+ }
+#endif
+}
+
+// -------------------------------------
+// Defines
+
+#if (!SL_PSA_ITS_SUPPORT_V3_DRIVER)
+#define SLI_PSA_ITS_NVM3_RANGE_START SLI_PSA_ITS_NVM3_RANGE_BASE
+#define SLI_PSA_ITS_NVM3_RANGE_END \
+ SLI_PSA_ITS_NVM3_RANGE_START + SL_PSA_ITS_MAX_FILES
+
+#define SLI_PSA_ITS_NVM3_INVALID_KEY (0)
+#define SLI_PSA_ITS_NVM3_UNKNOWN_KEY (1)
+
+#if SL_PSA_ITS_MAX_FILES > SLI_PSA_ITS_NVM3_RANGE_SIZE
+#error "Trying to store more ITS files then our NVM3 range allows for"
+#endif
+
+#define SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE 16
+
+// Enable backwards-compatibility with keys stored with a v1 header unless
+// disabled.
+#if !defined(SL_PSA_ITS_REMOVE_V1_HEADER_SUPPORT)
+#define SLI_PSA_ITS_SUPPORT_V1_FORMAT
+#endif
+
+// Internal error codes local to this compile unit
+#define SLI_PSA_ITS_ECODE_NO_VALID_HEADER (ECODE_EMDRV_NVM3_BASE - 1)
+#define SLI_PSA_ITS_ECODE_NEEDS_UPGRADE (ECODE_EMDRV_NVM3_BASE - 2)
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+// Define some cryptographic constants if not already set. This depends on the
+// underlying crypto accelerator in use (CRYPTOACC has these defines, but not
+// SEMAILBOX).
+#if !defined(AES_MAC_SIZE)
+#define AES_MAC_SIZE 16
+#endif
+
+#if !defined(AES_IV_GCM_SIZE)
+#define AES_IV_GCM_SIZE 12
+#endif
+
+#define SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD (AES_IV_GCM_SIZE + AES_MAC_SIZE)
+#endif // defined(SLI_PSA_ITS_ENCRYPTED)
+
+// -------------------------------------
+// Local global static variables
+
+SLI_STATIC bool nvm3_uid_set_cache_initialized = false;
+SLI_STATIC uint32_t nvm3_uid_set_cache[(SL_PSA_ITS_MAX_FILES + 31) / 32] = {0};
+
+typedef struct {
+ psa_storage_uid_t uid;
+ nvm3_ObjectKey_t object_id;
+ bool set;
+} previous_lookup_t;
+
+static previous_lookup_t previous_lookup = {0, 0, false};
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+// The root key is an AES-256 key, and is therefore 32 bytes.
+#define ROOT_KEY_SIZE (32)
+// The session key is derived from CMAC, which means it is equal to the AES
+// block size, i.e. 16 bytes
+#define SESSION_KEY_SIZE (16)
+
+#if !defined(SEMAILBOX_PRESENT)
+typedef struct {
+ bool initialized;
+ uint8_t data[ROOT_KEY_SIZE];
+} root_key_t;
+
+static root_key_t g_root_key = {
+ .initialized = false,
+ .data = {0},
+};
+#endif // !defined(SEMAILBOX_PRESENT)
+
+typedef struct {
+ bool active;
+ psa_storage_uid_t uid;
+ uint8_t data[SESSION_KEY_SIZE];
+} session_key_t;
+
+static session_key_t g_cached_session_key = {
+ .active = false,
+ .uid = 0,
+ .data = {0},
+};
+#endif // defined(SLI_PSA_ITS_ENCRYPTED)
+
+// -------------------------------------
+// Structs
+
+#if defined(SLI_PSA_ITS_SUPPORT_V1_FORMAT)
+typedef struct {
+ uint32_t magic;
+ psa_storage_uid_t uid;
+ psa_storage_create_flags_t flags;
+} sl_its_file_meta_v1_t;
+#endif // defined(SLI_PSA_ITS_SUPPORT_V1_FORMAT)
+
+// Due to alignment constraints on the 64-bit UID, the v2 header struct is
+// serialized to 16 bytes instead of the 24 bytes the v1 header compiles to.
+typedef struct {
+ uint32_t magic;
+ psa_storage_create_flags_t flags;
+ psa_storage_uid_t uid;
+} sli_its_file_meta_v2_t;
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+typedef struct {
+ uint8_t iv[AES_IV_GCM_SIZE];
+ // When encrypted & authenticated, MAC is stored at the end of the data array
+ uint8_t data[];
+} sli_its_encrypted_blob_t;
+#endif // defined(SLI_PSA_ITS_ENCRYPTED)
+
+// -------------------------------------
+// Local function prototypes
+
+static nvm3_ObjectKey_t get_nvm3_id(psa_storage_uid_t uid,
+ bool find_empty_slot);
+static nvm3_ObjectKey_t prepare_its_get_nvm3_id(psa_storage_uid_t uid);
+
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+static inline bool object_lives_in_s(const void *object, size_t object_size);
+#endif // defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+static psa_status_t derive_session_key(uint8_t *iv, size_t iv_size,
+ uint8_t *session_key,
+ size_t session_key_size);
+
+static psa_status_t encrypt_its_file(sli_its_file_meta_v2_t *metadata,
+ uint8_t *plaintext, size_t plaintext_size,
+ sli_its_encrypted_blob_t *blob,
+ size_t blob_size, size_t *blob_length);
+
+static psa_status_t decrypt_its_file(sli_its_file_meta_v2_t *metadata,
+ sli_its_encrypted_blob_t *blob,
+ size_t blob_size, uint8_t *plaintext,
+ size_t plaintext_size,
+ size_t *plaintext_length);
+
+static psa_status_t authenticate_its_file(nvm3_ObjectKey_t nvm3_object_id,
+ psa_storage_uid_t *authenticated_uid);
+#endif // defined(SLI_PSA_ITS_ENCRYPTED)
+
+// -------------------------------------
+// Local function definitions
+
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+// If an object of given size is fully encapsulated in a region of
+// secure domain the function returns true.
+static inline bool object_lives_in_s(const void *object, size_t object_size) {
+ cmse_address_info_t cmse_flags;
+
+ for (size_t i = 0u; i < object_size; i++) {
+ cmse_flags = cmse_TTA((uint32_t *)object + i);
+ if (!cmse_flags.flags.secure) {
+ return false;
+ }
+ }
+
+ return true;
+}
+#endif // defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+
+static inline void cache_set(nvm3_ObjectKey_t key) {
+ uint32_t i = key - SLI_PSA_ITS_NVM3_RANGE_START;
+ uint32_t bin = i / 32;
+ uint32_t offset = i - 32 * bin;
+ nvm3_uid_set_cache[bin] |= (1 << offset);
+}
+
+static inline void cache_clear(nvm3_ObjectKey_t key) {
+ uint32_t i = key - SLI_PSA_ITS_NVM3_RANGE_START;
+ uint32_t bin = i / 32;
+ uint32_t offset = i - 32 * bin;
+ nvm3_uid_set_cache[bin] ^= (1 << offset);
+}
+
+static inline bool cache_lookup(nvm3_ObjectKey_t key) {
+ uint32_t i = key - SLI_PSA_ITS_NVM3_RANGE_START;
+ uint32_t bin = i / 32;
+ uint32_t offset = i - 32 * bin;
+ return (bool)((nvm3_uid_set_cache[bin] >> offset) & 0x1);
+}
+
+static void init_cache(void) {
+ size_t num_keys_referenced_by_nvm3;
+ nvm3_ObjectKey_t keys_referenced_by_nvm3[SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE] =
+ {0};
+
+ for (nvm3_ObjectKey_t range_start = SLI_PSA_ITS_NVM3_RANGE_START;
+ range_start < SLI_PSA_ITS_NVM3_RANGE_END;
+ range_start += SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE) {
+ nvm3_ObjectKey_t range_end =
+ range_start + SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE;
+ if (range_end > SLI_PSA_ITS_NVM3_RANGE_END) {
+ range_end = SLI_PSA_ITS_NVM3_RANGE_END;
+ }
+
+ num_keys_referenced_by_nvm3 = nvm3_enumObjects(
+ nvm3_defaultHandle, keys_referenced_by_nvm3,
+ sizeof(keys_referenced_by_nvm3) / sizeof(nvm3_ObjectKey_t), range_start,
+ range_end - 1);
+
+ for (size_t i = 0; i < num_keys_referenced_by_nvm3; i++) {
+ cache_set(keys_referenced_by_nvm3[i]);
+ }
+ }
+
+ nvm3_uid_set_cache_initialized = true;
+}
+
+// Read the file metadata for a specific NVM3 ID
+static Ecode_t get_file_metadata(nvm3_ObjectKey_t key,
+ sli_its_file_meta_v2_t *metadata,
+ size_t *its_file_offset,
+ size_t *its_file_size) {
+ // Initialize output variables to safe default
+ if (its_file_offset != NULL) {
+ *its_file_offset = 0;
+ }
+ if (its_file_size != NULL) {
+ *its_file_size = 0;
+ }
+
+ Ecode_t status = nvm3_readPartialData(nvm3_defaultHandle, key, metadata, 0,
+ sizeof(sli_its_file_meta_v2_t));
+ if (status != ECODE_NVM3_OK) {
+ return status;
+ }
+
+#if defined(SLI_PSA_ITS_SUPPORT_V1_FORMAT)
+ // Re-read in v1 header format and translate to the latest structure version
+ if (metadata->magic == SLI_PSA_ITS_META_MAGIC_V1) {
+ sl_its_file_meta_v1_t key_meta_v1 = {0};
+ status = nvm3_readPartialData(nvm3_defaultHandle, key, &key_meta_v1, 0,
+ sizeof(sl_its_file_meta_v1_t));
+
+ if (status != ECODE_NVM3_OK) {
+ return status;
+ }
+
+ metadata->flags = key_meta_v1.flags;
+ metadata->uid = key_meta_v1.uid;
+ metadata->magic = SLI_PSA_ITS_META_MAGIC_V2;
+
+ if (its_file_offset != NULL) {
+ *its_file_offset = sizeof(sl_its_file_meta_v1_t);
+ }
+
+ status = SLI_PSA_ITS_ECODE_NEEDS_UPGRADE;
+ } else
+#endif
+ {
+ if (its_file_offset != NULL) {
+ *its_file_offset = sizeof(sli_its_file_meta_v2_t);
+ }
+ }
+
+ if (metadata->magic != SLI_PSA_ITS_META_MAGIC_V2) {
+ // No valid header found in this object
+ return SLI_PSA_ITS_ECODE_NO_VALID_HEADER;
+ }
+
+ if (its_file_offset != NULL && its_file_size != NULL) {
+ // Calculate the ITS file size if requested
+ uint32_t obj_type;
+ Ecode_t info_status =
+ nvm3_getObjectInfo(nvm3_defaultHandle, key, &obj_type, its_file_size);
+ if (info_status != ECODE_NVM3_OK) {
+ return info_status;
+ }
+
+ *its_file_size = *its_file_size - *its_file_offset;
+ }
+
+ return status;
+}
+
+// Search through NVM3 for uid
+static nvm3_ObjectKey_t get_nvm3_id(psa_storage_uid_t uid,
+ bool find_empty_slot) {
+ Ecode_t status;
+ sli_its_file_meta_v2_t key_meta;
+
+ if (find_empty_slot) {
+ for (size_t i = 0; i < SL_PSA_ITS_MAX_FILES; i++) {
+ if (!cache_lookup(i + SLI_PSA_ITS_NVM3_RANGE_START)) {
+ return i + SLI_PSA_ITS_NVM3_RANGE_START;
+ }
+ }
+ } else {
+ if (previous_lookup.set) {
+ if (previous_lookup.uid == uid) {
+ return previous_lookup.object_id;
+ }
+ }
+
+ for (size_t i = 0; i < SL_PSA_ITS_MAX_FILES; i++) {
+ if (!cache_lookup(i + SLI_PSA_ITS_NVM3_RANGE_START)) {
+ continue;
+ }
+ nvm3_ObjectKey_t object_id = i + SLI_PSA_ITS_NVM3_RANGE_START;
+
+ status = get_file_metadata(object_id, &key_meta, NULL, NULL);
+
+ if (status == ECODE_NVM3_OK ||
+ status == SLI_PSA_ITS_ECODE_NEEDS_UPGRADE) {
+ if (key_meta.uid == uid) {
+ previous_lookup.set = true;
+ previous_lookup.object_id = object_id;
+ previous_lookup.uid = uid;
+
+ return object_id;
+ } else {
+ continue;
+ }
+ }
+
+ if (status == SLI_PSA_ITS_ECODE_NO_VALID_HEADER ||
+ status == ECODE_NVM3_ERR_READ_DATA_SIZE) {
+ // we don't expect any other data in our range then PSA ITS files.
+ // delete the file if the magic doesn't match or the object on disk
+ // is too small to even have full metadata.
+ status = nvm3_deleteObject(nvm3_defaultHandle, object_id);
+ if (status != ECODE_NVM3_OK) {
+ return SLI_PSA_ITS_NVM3_RANGE_END + 1U;
+ }
+ }
+ }
+ }
+
+ return SLI_PSA_ITS_NVM3_RANGE_END + 1U;
+}
+
+// Perform NVM3 open and fill the look-up table.
+// Try to find the mapping NVM3 object ID with PSA ITS UID.
+static nvm3_ObjectKey_t prepare_its_get_nvm3_id(psa_storage_uid_t uid) {
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+ // With SKL the NVM3 instance must be initialized by the NS app. We therefore
+ // check that it has been opened (which is done on init) rather than actually
+ // doing the init.
+ if (!nvm3_defaultHandle->hasBeenOpened) {
+#else
+ if (nvm3_initDefault() != ECODE_NVM3_OK) {
+#endif
+ return SLI_PSA_ITS_NVM3_RANGE_END + 1U;
+ }
+
+ if (nvm3_uid_set_cache_initialized == false) {
+ init_cache();
+ }
+
+ return get_nvm3_id(uid, false);
+}
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+static inline void cache_session_key(uint8_t *session_key,
+ psa_storage_uid_t uid) {
+ // Cache the session key
+ memcpy(g_cached_session_key.data, session_key,
+ sizeof(g_cached_session_key.data));
+ g_cached_session_key.uid = uid;
+ g_cached_session_key.active = true;
+}
+
+/**
+ * \brief Derive a session key for ITS file encryption from the initialized root key and provided IV.
+ *
+ * \param[in] iv Pointer to array containing the initialization vector to be used in the key derivation.
+ * \param[in] iv_size Size of the IV buffer in bytes. Must be 12 bytes (AES-GCM IV size).
+ * \param[out] session_key Pointer to array where derived session key shall be stored.
+ * \param[out] session_key_size Size of the derived session key output array. Must be at least 32 bytes (AES-256 key size).
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_BAD_STATE The root key has not been initialized.
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because iv or session_key is NULL, or their sizes are incorrect.
+ * \retval PSA_ERROR_HARDWARE_FAILURE The operation failed because an internal cryptographic operation failed.
+ */
+static psa_status_t derive_session_key(uint8_t *iv, size_t iv_size,
+ uint8_t *session_key,
+ size_t session_key_size) {
+ if (iv == NULL || iv_size != AES_IV_GCM_SIZE || session_key == NULL ||
+ session_key_size < SESSION_KEY_SIZE) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+#if defined(SEMAILBOX_PRESENT)
+ // For HSE devices, use the builtin TrustZone Root Key
+ psa_set_key_id(&attributes, SL_SE_BUILTIN_KEY_TRUSTZONE_ID);
+
+ psa_key_lifetime_t reported_lifetime;
+ psa_drv_slot_number_t reported_slot;
+ status = mbedtls_psa_platform_get_builtin_key(
+ psa_get_key_id(&attributes), &reported_lifetime, &reported_slot);
+
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ psa_set_key_lifetime(&attributes, reported_lifetime);
+
+ uint8_t key_buffer[sizeof(sli_se_opaque_key_context_header_t)];
+ size_t key_buffer_size;
+ status = sli_se_opaque_get_builtin_key(reported_slot, &attributes, key_buffer,
+ sizeof(key_buffer), &key_buffer_size);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+#else // defined(SEMAILBOX_PRESENT)
+ // For VSE devices, use the previously initialized root key
+ if (!g_root_key.initialized) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ // Prepare root key attributes
+ psa_set_key_algorithm(&attributes, PSA_ALG_CMAC);
+ psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
+ psa_set_key_bits(&attributes, ROOT_KEY_SIZE * 8);
+
+ // Point the key buffer to the global root key
+ uint8_t *key_buffer = (uint8_t *)g_root_key.data;
+ size_t key_buffer_size = sizeof(g_root_key.data);
+#endif // defined(SEMAILBOX_PRESENT)
+
+ // Use CMAC as a key derivation function
+ size_t session_key_length;
+ status = psa_driver_wrapper_mac_compute(
+ &attributes, key_buffer, key_buffer_size, PSA_ALG_CMAC, iv, iv_size,
+ session_key, session_key_size, &session_key_length);
+
+ // Verify that the key derivation was successful before transferring the key
+ // to the caller
+ if (status != PSA_SUCCESS || session_key_length != SESSION_KEY_SIZE) {
+ memset(session_key, 0, session_key_size);
+ return PSA_ERROR_HARDWARE_FAILURE;
+ }
+
+ return status;
+}
+
+/**
+ * \brief Encrypt and authenticate ITS data with AES-128-GCM, storing the result in an encrypted blob.
+ *
+ * \param[in] metadata ITS metadata to be used as authenticated additional data.
+ * \param[in] plaintext Pointer to array containing data to be encrypted.
+ * \param[in] plaintext_size Size of provided plaintext data array.
+ * \param[out] blob Pointer to array where the resulting encrypted blob shall be placed.
+ * \param[in] blob_size Size of the output array. Must be at least as big as plaintext_size + SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD
+ * \param[out] blob_length Resulting size of the output blob.
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_BAD_STATE The root key has not been initialized.
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one or more arguments are NULL or of invalid size.
+ * \retval PSA_ERROR_HARDWARE_FAILURE The operation failed because an internal cryptographic operation failed.
+ */
+static psa_status_t encrypt_its_file(sli_its_file_meta_v2_t *metadata,
+ uint8_t *plaintext, size_t plaintext_size,
+ sli_its_encrypted_blob_t *blob,
+ size_t blob_size, size_t *blob_length) {
+ if (metadata == NULL || (plaintext == NULL && plaintext_size > 0) ||
+ blob == NULL ||
+ blob_size < plaintext_size + SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD ||
+ blob_length == NULL) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ // Generate IV
+ size_t iv_length = 0;
+ psa_status_t psa_status = mbedtls_psa_external_get_random(
+ NULL, blob->iv, AES_IV_GCM_SIZE, &iv_length);
+
+ if (psa_status != PSA_SUCCESS || iv_length != AES_IV_GCM_SIZE) {
+ return PSA_ERROR_HARDWARE_FAILURE;
+ }
+
+ // Prepare encryption key
+ psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+ psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);
+ psa_set_key_algorithm(&attributes, PSA_ALG_GCM);
+ psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
+ psa_set_key_bits(&attributes, SESSION_KEY_SIZE * 8);
+
+ uint8_t session_key[SESSION_KEY_SIZE];
+ psa_status = derive_session_key(blob->iv, AES_IV_GCM_SIZE, session_key,
+ sizeof(session_key));
+ if (psa_status != PSA_SUCCESS) {
+ return psa_status;
+ }
+
+ cache_session_key(session_key, metadata->uid);
+
+ // Retrieve data to be encrypted
+ if (plaintext_size != 0U) {
+ memcpy(blob->data, ((uint8_t *)plaintext), plaintext_size);
+ }
+
+ // Encrypt and authenticate blob
+ size_t output_length = 0;
+ psa_status = psa_driver_wrapper_aead_encrypt(
+ &attributes, session_key, sizeof(session_key), PSA_ALG_GCM, blob->iv,
+ sizeof(blob->iv), (uint8_t *)metadata,
+ sizeof(sli_its_file_meta_v2_t), // metadata is AAD
+ blob->data, plaintext_size, blob->data,
+ plaintext_size + AES_MAC_SIZE, // output == input for in-place encryption
+ &output_length);
+
+ // Clear the local session key immediately after we're done using it
+ memset(session_key, 0, sizeof(session_key));
+
+ if (psa_status != PSA_SUCCESS) {
+ return PSA_ERROR_HARDWARE_FAILURE;
+ }
+
+ if (output_length != plaintext_size + AES_MAC_SIZE) {
+ return PSA_ERROR_HARDWARE_FAILURE;
+ }
+
+ *blob_length = output_length + AES_IV_GCM_SIZE;
+
+ return PSA_SUCCESS;
+}
+
+/**
+ * \brief Decrypt and authenticate encrypted ITS data.
+ *
+ * \param[in] metadata ITS metadata to be used as authenticated additional data. Must be identical to the metadata used during encryption.
+ * \param[in] blob Encrypted blob containing data to be decrypted.
+ * \param[in] blob_size Size of the encrypted blob in bytes.
+ * \param[out] plaintext Pointer to array where the decrypted plaintext shall be placed.
+ * \param[in] plaintext_size Size of the plaintext array. Must be equal to sizeof(blob->data) - AES_MAC_SIZE.
+ * \param[out] plaintext_length Resulting length of the decrypted plaintext.
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_INVALID_SIGANTURE The operation failed because authentication of the decrypted data failed.
+ * \retval PSA_ERROR_BAD_STATE The root key has not been initialized.
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one or more arguments are NULL or of invalid size.
+ * \retval PSA_ERROR_HARDWARE_FAILURE The operation failed because an internal cryptographic operation failed.
+ */
+static psa_status_t decrypt_its_file(sli_its_file_meta_v2_t *metadata,
+ sli_its_encrypted_blob_t *blob,
+ size_t blob_size, uint8_t *plaintext,
+ size_t plaintext_size,
+ size_t *plaintext_length) {
+ if (metadata == NULL || blob == NULL ||
+ blob_size < plaintext_size + SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD ||
+ (plaintext == NULL && plaintext_size > 0) || plaintext_length == NULL) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ // Prepare decryption key
+ psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+ psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT);
+ psa_set_key_algorithm(&attributes, PSA_ALG_GCM);
+ psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
+ psa_set_key_bits(&attributes, SESSION_KEY_SIZE * 8);
+
+ psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ uint8_t session_key[SESSION_KEY_SIZE];
+
+ if (g_cached_session_key.active &&
+ g_cached_session_key.uid == metadata->uid) {
+ // Use cached session key if it's already set and UID matches
+ memcpy(session_key, g_cached_session_key.data, sizeof(session_key));
+ } else {
+ psa_status = derive_session_key(blob->iv, AES_IV_GCM_SIZE, session_key,
+ sizeof(session_key));
+ if (psa_status != PSA_SUCCESS) {
+ return psa_status;
+ }
+ cache_session_key(session_key, metadata->uid);
+ }
+
+ // Decrypt and authenticate blob
+ size_t output_length = 0;
+ psa_status = psa_driver_wrapper_aead_decrypt(
+ &attributes, session_key, sizeof(session_key), PSA_ALG_GCM, blob->iv,
+ sizeof(blob->iv), (uint8_t *)metadata,
+ sizeof(sli_its_file_meta_v2_t), // metadata is AAD
+ blob->data, plaintext_size + AES_MAC_SIZE, plaintext, plaintext_size,
+ &output_length);
+
+ // Clear the session key immediately after we're done using it
+ memset(session_key, 0, sizeof(session_key));
+
+ // Invalid signature likely means that NVM data was tampered with
+ if (psa_status == PSA_ERROR_INVALID_SIGNATURE) {
+ return PSA_ERROR_INVALID_SIGNATURE;
+ }
+
+ if (psa_status != PSA_SUCCESS || output_length != plaintext_size) {
+ return PSA_ERROR_HARDWARE_FAILURE;
+ }
+
+ *plaintext_length = output_length;
+
+ return PSA_SUCCESS;
+}
+
+/**
+ * \brief Authenticate encrypted ITS data and return the UID of the ITS file that was authenticated.
+ *
+ * \details NOTE: This function will run decrypt_its_file() internally. The difference from the decrypt_its_file()
+ * function is that authenticate_its_file() reads the NVM3 data, decrypts it in order to authenticate the
+ * stored data, and then discards the plaintext. This is needed since PSA Crypto doesn't support the
+ * GMAC primitive directly, which means we have to run a full GCM decrypt for authentication.
+ *
+ * \param[in] nvm3_object_id The NVM3 id corresponding to the stored ITS file.
+ * \param[out] authenticated_uid UID for the authenticated ITS file.
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_INVALID_SIGANTURE The operation failed because authentication of the decrypted data failed.
+ * \retval PSA_ERROR_BAD_STATE The root key has not been initialized.
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one or more arguments are NULL or of invalid size.
+ * \retval PSA_ERROR_HARDWARE_FAILURE The operation failed because an internal cryptographic operation failed.
+ */
+static psa_status_t
+authenticate_its_file(nvm3_ObjectKey_t nvm3_object_id,
+ psa_storage_uid_t *authenticated_uid) {
+ psa_status_t ret = PSA_ERROR_CORRUPTION_DETECTED;
+ sli_its_file_meta_v2_t *its_file_meta = NULL;
+ sli_its_encrypted_blob_t *blob = NULL;
+
+ uint32_t obj_type;
+ size_t its_file_size = 0;
+ Ecode_t status = nvm3_getObjectInfo(nvm3_defaultHandle, nvm3_object_id,
+ &obj_type, &its_file_size);
+ if (status != ECODE_NVM3_OK) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+
+ uint8_t *its_file_buffer = mbedtls_calloc(1, its_file_size);
+ if (its_file_buffer == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ memset(its_file_buffer, 0, its_file_size);
+
+ status = nvm3_readData(nvm3_defaultHandle, nvm3_object_id, its_file_buffer,
+ its_file_size);
+ if (status != ECODE_NVM3_OK) {
+ ret = PSA_ERROR_STORAGE_FAILURE;
+ goto cleanup;
+ }
+
+ its_file_meta = (sli_its_file_meta_v2_t *)its_file_buffer;
+ blob = (sli_its_encrypted_blob_t *)(its_file_buffer +
+ sizeof(sli_its_file_meta_v2_t));
+
+ // Decrypt and authenticate blob
+ size_t plaintext_length;
+ ret = decrypt_its_file(its_file_meta, blob,
+ its_file_size - sizeof(sli_its_file_meta_v2_t),
+ blob->data,
+ its_file_size - sizeof(sli_its_file_meta_v2_t) -
+ SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD,
+ &plaintext_length);
+
+ if (ret != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ if (plaintext_length != (its_file_size - sizeof(sli_its_file_meta_v2_t) -
+ SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD)) {
+ ret = PSA_ERROR_INVALID_SIGNATURE;
+ goto cleanup;
+ }
+
+ if (authenticated_uid != NULL) {
+ *authenticated_uid = its_file_meta->uid;
+ }
+
+ ret = PSA_SUCCESS;
+
+cleanup:
+
+ // Discard output, as we're only interested in whether the authentication
+ // check passed or not.
+ memset(its_file_buffer, 0, its_file_size);
+ mbedtls_free(its_file_buffer);
+
+ return ret;
+}
+#endif // defined(SLI_PSA_ITS_ENCRYPTED)
+
+// -------------------------------------
+// Global function definitions
+
+/**
+ * \brief create a new or modify an existing uid/value pair
+ *
+ * \param[in] uid the identifier for the data
+ * \param[in] data_length The size in bytes of the data in `p_data`
+ * \param[in] p_data A buffer containing the data
+ * \param[in] create_flags The flags that the data will be stored with
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_NOT_PERMITTED The operation failed because the provided `uid` value was already created with PSA_STORAGE_FLAG_WRITE_ONCE
+ * \retval PSA_ERROR_NOT_SUPPORTED The operation failed because one or more of the flags provided in `create_flags` is not supported or is not valid
+ * \retval PSA_ERROR_INSUFFICIENT_STORAGE The operation failed because there was insufficient space on the storage medium
+ * \retval PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has failed (Fatal error)
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided pointers(`p_data`)
+ * is invalid, for example is `NULL` or references memory the caller cannot access
+ * \retval PSA_ERROR_HARDWARE_FAILURE The operation failed because an internal cryptographic operation failed.
+ */
+psa_status_t psa_its_set(psa_storage_uid_t uid, uint32_t data_length,
+ const void *p_data,
+ psa_storage_create_flags_t create_flags) {
+ if (data_length > NVM3_MAX_OBJECT_SIZE) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+ if ((data_length != 0U) && (p_data == NULL)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (create_flags != PSA_STORAGE_FLAG_WRITE_ONCE &&
+ create_flags != PSA_STORAGE_FLAG_NONE
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+ && create_flags != PSA_STORAGE_FLAG_WRITE_ONCE_SECURE_ACCESSIBLE
+#endif
+ ) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+ if ((create_flags == PSA_STORAGE_FLAG_WRITE_ONCE_SECURE_ACCESSIBLE) &&
+ (!object_lives_in_s(p_data, data_length))) {
+ // The flag indicates that this data should not be set by the non-secure
+ // domain
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+#endif
+ sli_its_acquire_mutex();
+ nvm3_ObjectKey_t nvm3_object_id = prepare_its_get_nvm3_id(uid);
+ Ecode_t status;
+ psa_status_t ret = PSA_SUCCESS;
+ sli_its_file_meta_v2_t *its_file_meta;
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ psa_storage_uid_t authenticated_uid;
+ sli_its_encrypted_blob_t *blob = NULL;
+ size_t blob_length = 0u;
+ psa_status_t psa_status;
+
+ size_t its_file_size = data_length + SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD;
+#else
+ size_t its_file_size = data_length;
+#endif
+
+ uint8_t *its_file_buffer =
+ mbedtls_calloc(1, its_file_size + sizeof(sli_its_file_meta_v2_t));
+ if (its_file_buffer == NULL) {
+ ret = PSA_ERROR_INSUFFICIENT_MEMORY;
+ goto exit;
+ }
+ memset(its_file_buffer, 0, its_file_size + sizeof(sli_its_file_meta_v2_t));
+
+ its_file_meta = (sli_its_file_meta_v2_t *)its_file_buffer;
+ if (nvm3_object_id > SLI_PSA_ITS_NVM3_RANGE_END) {
+ // ITS UID was not found. Request a new.
+ nvm3_object_id = get_nvm3_id(0ULL, true);
+ if (nvm3_object_id > SLI_PSA_ITS_NVM3_RANGE_END) {
+ // The storage is full, or an error was returned during cleanup.
+ ret = PSA_ERROR_INSUFFICIENT_STORAGE;
+ } else {
+ its_file_meta->uid = uid;
+ its_file_meta->magic = SLI_PSA_ITS_META_MAGIC_V2;
+ }
+ } else {
+ // ITS UID was found. Read ITS meta data.
+ status = get_file_metadata(nvm3_object_id, its_file_meta, NULL, NULL);
+
+ if (status != ECODE_NVM3_OK && status != SLI_PSA_ITS_ECODE_NEEDS_UPGRADE) {
+ ret = PSA_ERROR_STORAGE_FAILURE;
+ goto exit;
+ }
+
+ if (its_file_meta->flags == PSA_STORAGE_FLAG_WRITE_ONCE
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+ || its_file_meta->flags == PSA_STORAGE_FLAG_WRITE_ONCE_SECURE_ACCESSIBLE
+#endif
+ ) {
+ ret = PSA_ERROR_NOT_PERMITTED;
+ goto exit;
+ }
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // If the UID already exists, authenticate the existing value and make sure
+ // the stored UID is the same.
+ ret = authenticate_its_file(nvm3_object_id, &authenticated_uid);
+ if (ret != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (authenticated_uid != uid) {
+ ret = PSA_ERROR_NOT_PERMITTED;
+ goto exit;
+ }
+#endif
+ }
+
+ its_file_meta->flags = create_flags;
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // Everything after the file metadata will make up the encrypted &
+ // authenticated blob
+ blob = (sli_its_encrypted_blob_t *)(its_file_buffer +
+ sizeof(sli_its_file_meta_v2_t));
+
+ // Encrypt and authenticate the provided data
+ psa_status = encrypt_its_file(its_file_meta, (uint8_t *)p_data, data_length,
+ blob, its_file_size, &blob_length);
+
+ if (psa_status != PSA_SUCCESS) {
+ ret = psa_status;
+ goto exit;
+ }
+
+ if (blob_length != its_file_size) {
+ ret = PSA_ERROR_HARDWARE_FAILURE;
+ goto exit;
+ }
+
+#else
+ if (data_length != 0U) {
+ memcpy(its_file_buffer + sizeof(sli_its_file_meta_v2_t),
+ ((uint8_t *)p_data), data_length);
+ }
+#endif
+
+ status = nvm3_writeData(nvm3_defaultHandle, nvm3_object_id, its_file_buffer,
+ its_file_size + sizeof(sli_its_file_meta_v2_t));
+
+ if (status == ECODE_NVM3_OK) {
+ // Power-loss might occur, however upon boot, the look-up table will be
+ // re-filled as long as the data has been successfully written to NVM3.
+ cache_set(nvm3_object_id);
+ } else {
+ ret = PSA_ERROR_STORAGE_FAILURE;
+ }
+
+exit:
+ if (its_file_buffer != NULL) {
+ // Clear and free key buffer before return.
+ memset(its_file_buffer, 0, its_file_size + sizeof(sli_its_file_meta_v2_t));
+ mbedtls_free(its_file_buffer);
+ }
+ sli_its_release_mutex();
+ return ret;
+}
+
+/**
+ * \brief Retrieve the value associated with a provided uid
+ *
+ * \param[in] uid The uid value
+ * \param[in] data_offset The starting offset of the data requested
+ * \param[in] data_length the amount of data requested (and the minimum allocated size of the `p_data` buffer)
+ * \param[out] p_data The buffer where the data will be placed upon successful completion
+ * \param[out] p_data_length The amount of data returned in the p_data buffer
+ *
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided `uid` value was not found in the storage
+ * \retval PSA_ERROR_BUFFER_TOO_SMALL The operation failed because the data associated with provided uid is larger than `data_size`
+ * \retval PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has failed (Fatal error)
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided pointers(`p_data`, `p_data_length`)
+ * is invalid. For example is `NULL` or references memory the caller cannot access.
+ * In addition, this can also happen if an invalid offset was provided.
+ */
+psa_status_t psa_its_get(psa_storage_uid_t uid, uint32_t data_offset,
+ uint32_t data_length, void *p_data,
+ size_t *p_data_length) {
+ psa_status_t ret = PSA_ERROR_CORRUPTION_DETECTED;
+
+ if ((data_length != 0U) && (p_data_length == NULL)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (data_length != 0U) {
+ // If the request amount of data is 0, allow invalid pointer of the output
+ // buffer.
+ if ((p_data == NULL) || ((uint32_t)p_data < SRAM_BASE) ||
+ ((uint32_t)p_data > (SRAM_BASE + SRAM_SIZE - data_length))) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ }
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ sli_its_encrypted_blob_t *blob = NULL;
+ size_t plaintext_length;
+ psa_status_t psa_status;
+#endif
+ size_t its_file_data_size = 0u;
+ Ecode_t status;
+ sli_its_file_meta_v2_t its_file_meta = {0};
+ size_t its_file_size = 0;
+ size_t its_file_offset = 0;
+
+ sli_its_acquire_mutex();
+ nvm3_ObjectKey_t nvm3_object_id = prepare_its_get_nvm3_id(uid);
+ if (nvm3_object_id > SLI_PSA_ITS_NVM3_RANGE_END) {
+ ret = PSA_ERROR_DOES_NOT_EXIST;
+ goto exit;
+ }
+
+ status = get_file_metadata(nvm3_object_id, &its_file_meta, &its_file_offset,
+ &its_file_size);
+ if (status == SLI_PSA_ITS_ECODE_NO_VALID_HEADER) {
+ ret = PSA_ERROR_DOES_NOT_EXIST;
+ goto exit;
+ }
+ if (status != ECODE_NVM3_OK && status != SLI_PSA_ITS_ECODE_NEEDS_UPGRADE) {
+ ret = PSA_ERROR_STORAGE_FAILURE;
+ goto exit;
+ }
+
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+ if (its_file_meta.flags == PSA_STORAGE_FLAG_WRITE_ONCE_SECURE_ACCESSIBLE &&
+ !object_lives_in_s(p_data, data_length)) {
+ // The flag indicates that this data should not be read back to the
+ // non-secure domain
+ ret = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+#endif
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // Subtract IV and MAC from ITS file as the below checks concern the actual
+ // data size
+ its_file_data_size = its_file_size - SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD;
+#else
+ its_file_data_size = its_file_size;
+#endif
+
+ if (data_length != 0U) {
+ if ((data_offset >= its_file_data_size) && (its_file_data_size != 0U)) {
+ ret = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ if ((its_file_data_size == 0U) && (data_offset != 0U)) {
+ ret = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+ } else {
+ // Allow the offset at the data size boundary if the requested amount of
+ // data is zero.
+ if (data_offset > its_file_data_size) {
+ ret = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+ }
+
+ if (data_length > (its_file_data_size - data_offset)) {
+ *p_data_length = its_file_data_size - data_offset;
+ } else {
+ *p_data_length = data_length;
+ }
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // its_file_size includes size of sli_its_encrypted_blob_t struct
+ blob = (sli_its_encrypted_blob_t *)mbedtls_calloc(1, its_file_size);
+ if (blob == NULL) {
+ ret = PSA_ERROR_INSUFFICIENT_MEMORY;
+ goto exit;
+ }
+ memset(blob, 0, its_file_size);
+
+ status = nvm3_readPartialData(nvm3_defaultHandle, nvm3_object_id, blob,
+ its_file_offset, its_file_size);
+ if (status != ECODE_NVM3_OK) {
+ ret = PSA_ERROR_STORAGE_FAILURE;
+ goto exit;
+ }
+
+ // Decrypt and authenticate blob
+ psa_status = decrypt_its_file(
+ &its_file_meta, blob, its_file_size, blob->data,
+ its_file_size - SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD, &plaintext_length);
+
+ if (psa_status != PSA_SUCCESS) {
+ ret = psa_status;
+ goto exit;
+ }
+
+ if (plaintext_length !=
+ (its_file_size - SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD)) {
+ ret = PSA_ERROR_INVALID_SIGNATURE;
+ goto exit;
+ }
+
+ // Verify that the requested UID is equal to the retrieved and authenticated
+ // UID
+ if (uid != its_file_meta.uid) {
+ ret = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ if (*p_data_length > 0) {
+ memcpy(p_data, blob->data + data_offset, *p_data_length);
+ }
+ ret = PSA_SUCCESS;
+
+exit:
+ if (blob != NULL) {
+ memset(blob, 0, its_file_size);
+ mbedtls_free(blob);
+ }
+ sli_its_release_mutex();
+#else
+ // If no encryption is used, just read out the data and write it directly to
+ // the output buffer
+ status = nvm3_readPartialData(nvm3_defaultHandle, nvm3_object_id, p_data,
+ its_file_offset + data_offset, *p_data_length);
+
+ if (status != ECODE_NVM3_OK) {
+ ret = PSA_ERROR_STORAGE_FAILURE;
+ } else {
+ ret = PSA_SUCCESS;
+ }
+
+exit:
+ sli_its_release_mutex();
+#endif
+
+ return ret;
+}
+
+/**
+ * \brief Retrieve the metadata about the provided uid
+ *
+ * \param[in] uid The uid value
+ * \param[out] p_info A pointer to the `psa_storage_info_t` struct that will be populated with the metadata
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided uid value was not found in the storage
+ * \retval PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has failed (Fatal error)
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided pointers(`p_info`)
+ * is invalid, for example is `NULL` or references memory the caller cannot access
+ * \retval PSA_ERROR_INVALID_SIGANTURE The operation failed because authentication of the stored metadata failed.
+ */
+psa_status_t psa_its_get_info(psa_storage_uid_t uid,
+ struct psa_storage_info_t *p_info) {
+ psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ if (p_info == NULL) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ Ecode_t status;
+ sli_its_file_meta_v2_t its_file_meta = {0};
+ size_t its_file_size = 0;
+ size_t its_file_offset = 0;
+
+ sli_its_acquire_mutex();
+ nvm3_ObjectKey_t nvm3_object_id = prepare_its_get_nvm3_id(uid);
+ if (nvm3_object_id > SLI_PSA_ITS_NVM3_RANGE_END) {
+ psa_status = PSA_ERROR_DOES_NOT_EXIST;
+ goto exit;
+ }
+
+ status = get_file_metadata(nvm3_object_id, &its_file_meta, &its_file_offset,
+ &its_file_size);
+ if (status == SLI_PSA_ITS_ECODE_NO_VALID_HEADER) {
+ psa_status = PSA_ERROR_DOES_NOT_EXIST;
+ goto exit;
+ }
+ if (status != ECODE_NVM3_OK && status != SLI_PSA_ITS_ECODE_NEEDS_UPGRADE) {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ goto exit;
+ }
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // Authenticate the ITS file (both metadata and ciphertext) before returning
+ // the metadata. Note that this can potentially induce a significant
+ // performance hit.
+ psa_storage_uid_t authenticated_uid;
+ psa_status = authenticate_its_file(nvm3_object_id, &authenticated_uid);
+ if (psa_status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (authenticated_uid != uid) {
+ psa_status = PSA_ERROR_INVALID_SIGNATURE;
+ goto exit;
+ }
+#endif
+
+ p_info->flags = its_file_meta.flags;
+ p_info->size = its_file_size;
+
+ psa_status = PSA_SUCCESS;
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // Remove IV and MAC size from file size
+ p_info->size = its_file_size - SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD;
+#endif
+exit:
+ sli_its_release_mutex();
+ return psa_status;
+}
+
+/**
+ * \brief Remove the provided key and its associated data from the storage
+ *
+ * \param[in] uid The uid value
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided key value was not found in the storage
+ * \retval PSA_ERROR_NOT_PERMITTED The operation failed because the provided key value was created with PSA_STORAGE_FLAG_WRITE_ONCE
+ * \retval PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has failed (Fatal error)
+ */
+psa_status_t psa_its_remove(psa_storage_uid_t uid) {
+ psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ Ecode_t status;
+ sli_its_file_meta_v2_t its_file_meta = {0};
+ size_t its_file_size = 0;
+ size_t its_file_offset = 0;
+
+ sli_its_acquire_mutex();
+ nvm3_ObjectKey_t nvm3_object_id = prepare_its_get_nvm3_id(uid);
+ if (nvm3_object_id > SLI_PSA_ITS_NVM3_RANGE_END) {
+ psa_status = PSA_ERROR_DOES_NOT_EXIST;
+ goto exit;
+ }
+
+ status = get_file_metadata(nvm3_object_id, &its_file_meta, &its_file_offset,
+ &its_file_size);
+ if (status == SLI_PSA_ITS_ECODE_NO_VALID_HEADER) {
+ psa_status = PSA_ERROR_DOES_NOT_EXIST;
+ goto exit;
+ }
+ if (status != ECODE_NVM3_OK && status != SLI_PSA_ITS_ECODE_NEEDS_UPGRADE) {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ goto exit;
+ }
+
+ if (its_file_meta.flags == PSA_STORAGE_FLAG_WRITE_ONCE
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+ || its_file_meta.flags == PSA_STORAGE_FLAG_WRITE_ONCE_SECURE_ACCESSIBLE
+#endif
+ ) {
+ psa_status = PSA_ERROR_NOT_PERMITTED;
+ goto exit;
+ }
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // If the UID already exists, authenticate the existing value and make sure
+ // the stored UID is the same.
+ psa_storage_uid_t authenticated_uid;
+ psa_status = authenticate_its_file(nvm3_object_id, &authenticated_uid);
+ if (psa_status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (authenticated_uid != uid) {
+ psa_status = PSA_ERROR_NOT_PERMITTED;
+ goto exit;
+ }
+#endif
+
+ status = nvm3_deleteObject(nvm3_defaultHandle, nvm3_object_id);
+
+ if (status == ECODE_NVM3_OK) {
+ // Power-loss might occur, however upon boot, the look-up table will be
+ // re-filled as long as the data has been successfully written to NVM3.
+ if (previous_lookup.set && previous_lookup.uid == uid) {
+ previous_lookup.set = false;
+ }
+ cache_clear(nvm3_object_id);
+
+ psa_status = PSA_SUCCESS;
+ } else {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ }
+
+exit:
+ sli_its_release_mutex();
+ return psa_status;
+}
+
+// -------------------------------------
+// Silicon Labs extensions
+static psa_storage_uid_t psa_its_identifier_of_slot(mbedtls_svc_key_id_t key) {
+#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
+ // Encode the owner in the upper 32 bits. This means that if
+ // owner values are nonzero (as they are on a PSA platform),
+ // no key file will ever have a value less than 0x100000000, so
+ // the whole range 0..0xffffffff is available for non-key files.
+ uint32_t unsigned_owner_id = MBEDTLS_SVC_KEY_ID_GET_OWNER_ID(key);
+ return ((uint64_t)unsigned_owner_id << 32) |
+ MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key);
+#else
+ // Use the key id directly as a file name.
+ // psa_is_key_id_valid() in psa_crypto_slot_management.c
+ // is responsible for ensuring that key identifiers do not have a
+ // value that is reserved for non-key files.
+ return key;
+#endif
+}
+
+psa_status_t sli_psa_its_change_key_id(mbedtls_svc_key_id_t old_id,
+ mbedtls_svc_key_id_t new_id) {
+ psa_storage_uid_t old_uid = psa_its_identifier_of_slot(old_id);
+ psa_storage_uid_t new_uid = psa_its_identifier_of_slot(new_id);
+ Ecode_t status;
+ uint32_t obj_type;
+ size_t its_file_size = 0;
+ psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ int8_t *its_file_buffer = NULL;
+ sli_its_file_meta_v2_t *metadata = NULL;
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ sli_its_encrypted_blob_t *blob = NULL;
+ size_t plaintext_length;
+ size_t blob_length;
+ psa_status_t encrypt_status;
+ psa_status_t decrypt_status;
+#endif
+ sli_its_acquire_mutex();
+
+ // Check whether the key to migrate exists on disk
+ nvm3_ObjectKey_t nvm3_object_id = prepare_its_get_nvm3_id(old_uid);
+ if (nvm3_object_id > SLI_PSA_ITS_NVM3_RANGE_END) {
+ psa_status = PSA_ERROR_DOES_NOT_EXIST;
+ goto exit;
+ }
+
+ // Get total length to allocate
+ status = nvm3_getObjectInfo(nvm3_defaultHandle, nvm3_object_id, &obj_type,
+ &its_file_size);
+ if (status != ECODE_NVM3_OK) {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ goto exit;
+ }
+
+ // Allocate temporary buffer and cast it to the metadata format
+ its_file_buffer = mbedtls_calloc(1, its_file_size);
+ if (its_file_buffer == NULL) {
+ psa_status = PSA_ERROR_INSUFFICIENT_MEMORY;
+ goto exit;
+ }
+ metadata = (sli_its_file_meta_v2_t *)its_file_buffer;
+
+ // Read contents of pre-existing key into the temporary buffer
+ status = nvm3_readData(nvm3_defaultHandle, nvm3_object_id, its_file_buffer,
+ its_file_size);
+ if (status != ECODE_NVM3_OK) {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ goto exit;
+ }
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // Decrypt and authenticate blob
+ blob = (sli_its_encrypted_blob_t *)(its_file_buffer +
+ sizeof(sli_its_file_meta_v2_t));
+ decrypt_status = decrypt_its_file(
+ metadata, blob, its_file_size - sizeof(sli_its_file_meta_v2_t),
+ blob->data,
+ its_file_size - sizeof(sli_its_file_meta_v2_t) -
+ SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD,
+ &plaintext_length);
+
+ if (decrypt_status != PSA_SUCCESS) {
+ psa_status = decrypt_status;
+ goto exit;
+ }
+
+ if (plaintext_length != (its_file_size - sizeof(sli_its_file_meta_v2_t) -
+ SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD)) {
+ psa_status = PSA_ERROR_INVALID_SIGNATURE;
+ goto exit;
+ }
+#endif
+
+ // Swap out the old UID for the new one
+#if defined(SLI_PSA_ITS_SUPPORT_V1_FORMAT)
+ if (metadata->magic == SLI_PSA_ITS_META_MAGIC_V1) {
+ // Recast as v1 metadata
+ sl_its_file_meta_v1_t *metadata_v1 =
+ (sl_its_file_meta_v1_t *)its_file_buffer;
+ if (metadata_v1->uid != old_uid) {
+ psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ goto exit;
+ }
+ metadata_v1->uid = new_uid;
+ } else
+#endif
+ if (metadata->magic == SLI_PSA_ITS_META_MAGIC_V2) {
+ if (metadata->uid != old_uid) {
+ psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ goto exit;
+ }
+ metadata->uid = new_uid;
+ } else {
+ psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ goto exit;
+ }
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // Encrypt and authenticate the modified data data
+ encrypt_status = encrypt_its_file(
+ metadata, blob->data, plaintext_length, blob,
+ its_file_size - sizeof(sli_its_file_meta_v2_t), &blob_length);
+
+ if (encrypt_status != PSA_SUCCESS) {
+ psa_status = encrypt_status;
+ goto exit;
+ }
+
+ if (blob_length != (its_file_size - sizeof(sli_its_file_meta_v2_t))) {
+ psa_status = PSA_ERROR_HARDWARE_FAILURE;
+ goto exit;
+ }
+#endif
+
+ // Overwrite the NVM3 token with the changed buffer
+ status = nvm3_writeData(nvm3_defaultHandle, nvm3_object_id, its_file_buffer,
+ its_file_size);
+ if (status == ECODE_NVM3_OK) {
+ // Update last lookup and report success
+ if (previous_lookup.set) {
+ if (previous_lookup.uid == old_uid) {
+ previous_lookup.uid = new_uid;
+ }
+ }
+ psa_status = PSA_SUCCESS;
+ } else {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ }
+
+exit:
+ if (its_file_buffer != NULL) {
+ // Clear and free key buffer before return.
+ memset(its_file_buffer, 0, its_file_size);
+ mbedtls_free(its_file_buffer);
+ }
+ sli_its_release_mutex();
+ return psa_status;
+}
+
+/**
+ * \brief Check if the ITS encryption is enabled
+ */
+psa_status_t sli_psa_its_encrypted(void) {
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ return PSA_SUCCESS;
+#else
+ return PSA_ERROR_NOT_SUPPORTED;
+#endif
+}
+
+#if defined(SLI_PSA_ITS_ENCRYPTED) && !defined(SEMAILBOX_PRESENT)
+/**
+ * \brief Set the root key to be used when deriving session keys for ITS encryption.
+ *
+ * \param[in] root_key Buffer containing the root key.
+ * \param[in] root_key_size Size of the root key in bytes. Must be 32 (256 bits).
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The key was successfully set.
+ * \retval PSA_ERROR_INVALID_ARGUMENT The root key was NULL or had an invalid size.
+ * \retval PSA_ERROR_ALREADY_EXISTS The root key has already been initialized.
+ */
+psa_status_t sli_psa_its_set_root_key(uint8_t *root_key, size_t root_key_size) {
+ // Check that arguments are valid
+ if (root_key == NULL || root_key_size != sizeof(g_root_key.data)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ // Check that the root key has not already been set
+ // (This is possibly too restrictive. For TrustZone usage this can be enforced
+ // by not exposing the function to NS instead.)
+ if (g_root_key.initialized) {
+ return PSA_ERROR_ALREADY_EXISTS;
+ }
+
+ // Store the provided root key and mark it as initialized
+ memcpy(g_root_key.data, root_key, sizeof(g_root_key.data));
+ g_root_key.initialized = true;
+
+ return PSA_SUCCESS;
+}
+#endif // defined(SLI_PSA_ITS_ENCRYPTED) && !defined(SEMAILBOX_PRESENT)
+
+#else // (!SL_PSA_ITS_SUPPORT_V3_DRIVER)
+
+// -------------------------------------
+// Defines
+#define SLI_PSA_ITS_V3_DRIVER (0x3A)
+#define SLI_PSA_ITS_V2_DRIVER (0x74)
+#define SLI_PSA_ITS_NOT_CHECKED (0xE8)
+#define SLI_PSA_ITS_V2_DRIVER_FLAG_NVM3_ID (SLI_PSA_ITS_NVM3_RANGE_START - 1)
+#define SLI_PSA_ITS_NVM3_INVALID_KEY (0)
+#define SLI_PSA_ITS_NVM3_UNKNOWN_KEY (1)
+
+#if SL_PSA_ITS_MAX_FILES > SLI_PSA_ITS_NVM3_RANGE_SIZE
+#error "Trying to store more ITS files then our NVM3 range allows for"
+#endif
+
+#define SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE 16
+
+// Internal error codes local to this compile unit
+#define SLI_PSA_ITS_ECODE_NO_VALID_HEADER (ECODE_EMDRV_NVM3_BASE - 1)
+#define SLI_PSA_ITS_ECODE_NEEDS_UPGRADE (ECODE_EMDRV_NVM3_BASE - 2)
+
+// -------------------------------------
+// Local global static variables
+
+SLI_STATIC bool nvm3_uid_set_cache_initialized = false;
+SLI_STATIC uint32_t nvm3_uid_set_cache[(SL_PSA_ITS_MAX_FILES + 31) / 32] = {0};
+SLI_STATIC uint32_t nvm3_uid_tomb_cache[(SL_PSA_ITS_MAX_FILES + 31) / 32] = {0};
+#if SL_PSA_ITS_SUPPORT_V2_DRIVER
+SLI_STATIC uint32_t its_driver_version = SLI_PSA_ITS_NOT_CHECKED;
+#endif // SL_PSA_ITS_SUPPORT_V2_DRIVER
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+// The root key is an AES-256 key, and is therefore 32 bytes.
+#define ROOT_KEY_SIZE (32)
+// The session key is derived from CMAC, which means it is equal to the AES
+// block size, i.e. 16 bytes
+#define SESSION_KEY_SIZE (16)
+
+#if !defined(SEMAILBOX_PRESENT)
+typedef struct {
+ bool initialized;
+ uint8_t data[ROOT_KEY_SIZE];
+} root_key_t;
+
+static root_key_t g_root_key = {
+ .initialized = false,
+ .data = {0},
+};
+#endif // !defined(SEMAILBOX_PRESENT)
+
+typedef struct {
+ bool active;
+ psa_storage_uid_t uid;
+ uint8_t data[SESSION_KEY_SIZE];
+} session_key_t;
+
+static session_key_t g_cached_session_key = {
+ .active = false,
+ .uid = 0,
+ .data = {0},
+};
+#endif // defined(SLI_PSA_ITS_ENCRYPTED)
+
+// -------------------------------------
+// Structs
+
+#if defined(SLI_PSA_ITS_SUPPORT_V1_FORMAT_INTERNAL)
+typedef struct {
+ uint32_t magic;
+ psa_storage_uid_t uid;
+ psa_storage_create_flags_t flags;
+} sl_its_file_meta_v1_t;
+#endif // defined(SLI_PSA_ITS_SUPPORT_V1_FORMAT_INTERNAL)
+
+// -------------------------------------
+// Local function prototypes
+
+static psa_status_t find_nvm3_id(psa_storage_uid_t uid, bool find_empty_slot,
+ sli_its_file_meta_v2_t *its_file_meta,
+ size_t *its_file_offset, size_t *its_file_size,
+ nvm3_ObjectKey_t *output_nvm3_id);
+static nvm3_ObjectKey_t derive_nvm3_id(psa_storage_uid_t uid);
+
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+static inline bool object_lives_in_s(const void *object, size_t object_size);
+#endif
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+static psa_status_t derive_session_key(uint8_t *iv, size_t iv_size,
+ uint8_t *session_key,
+ size_t session_key_size);
+
+static psa_status_t sli_decrypt_its_file(sli_its_file_meta_v2_t *metadata,
+ sli_its_encrypted_blob_t *blob,
+ size_t blob_size, uint8_t *plaintext,
+ size_t plaintext_size,
+ size_t *plaintext_length);
+
+static psa_status_t authenticate_its_file(nvm3_ObjectKey_t nvm3_object_id,
+ psa_storage_uid_t *authenticated_uid);
+#endif
+
+#if SL_PSA_ITS_SUPPORT_V2_DRIVER
+static psa_status_t psa_its_get_legacy(nvm3_ObjectKey_t nvm3_object_id,
+ sli_its_file_meta_v2_t *its_file_meta,
+ size_t its_file_size,
+ size_t its_file_offset, void *p_data);
+static psa_status_t detect_legacy_versions();
+static psa_status_t upgrade_all_keys();
+
+#if defined(SLI_PSA_ITS_SUPPORT_V1_FORMAT_INTERNAL)
+psa_status_t psa_its_set_v1(psa_storage_uid_t uid, uint32_t data_length,
+ const void *p_data,
+ psa_storage_create_flags_t create_flags);
+#endif // SLI_PSA_ITS_SUPPORT_V1_FORMAT_INTERNAL
+#endif // SL_PSA_ITS_SUPPORT_V2_DRIVER
+
+// -------------------------------------
+// Local function definitions
+static inline uint32_t get_index(nvm3_ObjectKey_t key) {
+ return (key - (SLI_PSA_ITS_NVM3_RANGE_START)) / 32;
+}
+
+static inline uint32_t get_offset(nvm3_ObjectKey_t key) {
+ return (key - (SLI_PSA_ITS_NVM3_RANGE_START)) % 32;
+}
+
+static inline void set_cache(nvm3_ObjectKey_t key) {
+ nvm3_uid_set_cache[get_index(key)] |= (1 << get_offset(key));
+ nvm3_uid_tomb_cache[get_index(key)] &= ~(1 << get_offset(key));
+}
+
+static inline void set_tomb(nvm3_ObjectKey_t key) {
+ nvm3_uid_tomb_cache[get_index(key)] |= (1 << get_offset(key));
+
+ uint32_t cache_not_empty = 0;
+ for (size_t i = 0; i < (((SL_PSA_ITS_MAX_FILES) + 31) / 32); i++) {
+ cache_not_empty += nvm3_uid_set_cache[i];
+ }
+ if (cache_not_empty == 0) {
+ for (size_t i = 0; i < (((SL_PSA_ITS_MAX_FILES) + 31) / 32); i++) {
+ nvm3_uid_tomb_cache[i] = 0;
+ }
+ }
+}
+
+#if SL_PSA_ITS_SUPPORT_V2_DRIVER
+static inline psa_status_t write_driver_v3() {
+ uint8_t driver_verison = SLI_PSA_ITS_V3_DRIVER;
+ Ecode_t status;
+ status =
+ nvm3_writeData(nvm3_defaultHandle, SLI_PSA_ITS_V2_DRIVER_FLAG_NVM3_ID,
+ &driver_verison, sizeof(uint8_t));
+ if (status != ECODE_NVM3_OK) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+ return PSA_SUCCESS;
+}
+#endif
+
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+// If an object of given size is fully encapsulated in a region of
+// secure domain the function returns true.
+static inline bool object_lives_in_s(const void *object, size_t object_size) {
+ cmse_address_info_t cmse_flags;
+
+ for (size_t i = 0u; i < object_size; i++) {
+ cmse_flags = cmse_TTA((uint32_t *)object + i);
+ if (!cmse_flags.flags.secure) {
+ return false;
+ }
+ }
+
+ return true;
+}
+#endif
+
+static inline void clear_cache(nvm3_ObjectKey_t key) {
+ nvm3_uid_set_cache[get_index(key)] ^= (1 << get_offset(key));
+}
+
+static inline bool lookup_cache(nvm3_ObjectKey_t key) {
+ return (bool)((nvm3_uid_set_cache[get_index(key)] >> get_offset(key)) & 0x1);
+}
+
+static inline bool lookup_tomb(nvm3_ObjectKey_t key) {
+ return (bool)((nvm3_uid_tomb_cache[get_index(key)] >> get_offset(key)) & 0x1);
+}
+
+static inline nvm3_ObjectKey_t increment_obj_id(nvm3_ObjectKey_t id) {
+ return SLI_PSA_ITS_NVM3_RANGE_START +
+ ((id - SLI_PSA_ITS_NVM3_RANGE_START + 1) % SL_PSA_ITS_MAX_FILES);
+}
+static inline nvm3_ObjectKey_t prng(psa_storage_uid_t uid) {
+ // Squash uid down to a 32 bit word
+ nvm3_ObjectKey_t uid_32 = uid & 0xFFFFFFFF;
+ nvm3_ObjectKey_t xored_32 = (uid >> 32) ^ uid_32;
+ nvm3_ObjectKey_t temp;
+ // Accumulate all "entropy" towards the LSB, since that is where we need it
+ for (size_t i = 1; i < 4; i++) {
+ temp = xored_32 ^ (xored_32 >> (8 * i));
+ if ((temp & 0x3) != 0) {
+ temp = temp << 2;
+ }
+ uid_32 = (uid_32 + temp);
+ }
+ return uid_32;
+}
+
+static inline nvm3_ObjectKey_t derive_nvm3_id(psa_storage_uid_t uid) {
+ return SLI_PSA_ITS_NVM3_RANGE_START + (prng(uid) % (SL_PSA_ITS_MAX_FILES));
+}
+
+static void init_cache(void) {
+ size_t num_keys_referenced_by_nvm3;
+ nvm3_ObjectKey_t keys_referenced_by_nvm3[SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE] =
+ {0};
+ size_t num_del_keys_from_nvm3;
+ nvm3_ObjectKey_t deleted_keys_from_nvm3[SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE] = {
+ 0};
+ for (nvm3_ObjectKey_t range_start = SLI_PSA_ITS_NVM3_RANGE_START;
+ range_start < SLI_PSA_ITS_NVM3_RANGE_END;
+ range_start += SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE) {
+ nvm3_ObjectKey_t range_end =
+ range_start + SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE;
+ if (range_end > SLI_PSA_ITS_NVM3_RANGE_END) {
+ range_end = SLI_PSA_ITS_NVM3_RANGE_END;
+ }
+
+ num_keys_referenced_by_nvm3 = nvm3_enumObjects(
+ nvm3_defaultHandle, keys_referenced_by_nvm3,
+ sizeof(keys_referenced_by_nvm3) / sizeof(nvm3_ObjectKey_t), range_start,
+ range_end - 1);
+
+ for (size_t i = 0; i < num_keys_referenced_by_nvm3; i++) {
+ set_cache(keys_referenced_by_nvm3[i]);
+ }
+ num_del_keys_from_nvm3 = nvm3_enumDeletedObjects(
+ nvm3_defaultHandle, deleted_keys_from_nvm3,
+ sizeof(deleted_keys_from_nvm3) / sizeof(nvm3_ObjectKey_t), range_start,
+ range_end - 1);
+ for (size_t i = 0; i < num_del_keys_from_nvm3; i++) {
+ set_tomb(deleted_keys_from_nvm3[i]);
+ }
+ }
+ nvm3_uid_set_cache_initialized = true;
+}
+
+// Read the file metadata for a specific NVM3 ID
+static Ecode_t get_file_metadata(nvm3_ObjectKey_t key,
+ sli_its_file_meta_v2_t *metadata,
+ size_t *its_file_offset,
+ size_t *its_file_size) {
+ // Initialize output variables to safe default
+ if (its_file_offset != NULL) {
+ *its_file_offset = 0;
+ }
+ if (its_file_size != NULL) {
+ *its_file_size = 0;
+ }
+
+ Ecode_t status = nvm3_readPartialData(nvm3_defaultHandle, key, metadata, 0,
+ sizeof(sli_its_file_meta_v2_t));
+ if (status != ECODE_NVM3_OK) {
+ return status;
+ }
+
+#if defined(SLI_PSA_ITS_SUPPORT_V1_FORMAT_INTERNAL)
+ // Re-read in v1 header format and translate to the latest structure version
+ if (metadata->magic == SLI_PSA_ITS_META_MAGIC_V1) {
+ sl_its_file_meta_v1_t key_meta_v1 = {0};
+ status = nvm3_readPartialData(nvm3_defaultHandle, key, &key_meta_v1, 0,
+ sizeof(sl_its_file_meta_v1_t));
+
+ if (status != ECODE_NVM3_OK) {
+ return status;
+ }
+
+ metadata->flags = key_meta_v1.flags;
+ metadata->uid = key_meta_v1.uid;
+ metadata->magic = SLI_PSA_ITS_META_MAGIC_V2;
+
+ if (its_file_offset != NULL) {
+ *its_file_offset = sizeof(sl_its_file_meta_v1_t);
+ }
+
+ status = SLI_PSA_ITS_ECODE_NEEDS_UPGRADE;
+ } else
+#endif
+ {
+ if (its_file_offset != NULL) {
+ *its_file_offset = sizeof(sli_its_file_meta_v2_t);
+ }
+ }
+
+ if (metadata->magic != SLI_PSA_ITS_META_MAGIC_V2) {
+ // No valid header found in this object
+ return SLI_PSA_ITS_ECODE_NO_VALID_HEADER;
+ }
+
+ if (its_file_offset != NULL && its_file_size != NULL) {
+ // Calculate the ITS file size if requested
+ uint32_t obj_type;
+ Ecode_t info_status =
+ nvm3_getObjectInfo(nvm3_defaultHandle, key, &obj_type, its_file_size);
+ if (info_status != ECODE_NVM3_OK) {
+ return info_status;
+ }
+
+ *its_file_size = *its_file_size - *its_file_offset;
+ }
+
+ return status;
+}
+
+#if SL_PSA_ITS_SUPPORT_V2_DRIVER
+static psa_status_t psa_its_get_legacy(nvm3_ObjectKey_t nvm3_object_id,
+ sli_its_file_meta_v2_t *its_file_meta,
+ size_t its_file_size,
+ size_t its_file_offset, void *p_data) {
+ Ecode_t status;
+ if (its_file_size == 0) {
+ if (its_file_meta != NULL) {
+ return PSA_ERROR_DATA_INVALID;
+ }
+ }
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ sli_its_encrypted_blob_t *blob = NULL;
+ size_t plaintext_length;
+
+ // its_file_size includes size of sli_its_encrypted_blob_t struct
+ blob = (sli_its_encrypted_blob_t *)mbedtls_calloc(1, its_file_size);
+ if (blob == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ memset(blob, 0, its_file_size);
+
+ status = nvm3_readPartialData(nvm3_defaultHandle, nvm3_object_id, blob,
+ its_file_offset, its_file_size);
+ if (status != ECODE_NVM3_OK) {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ goto cleanup;
+ }
+
+ // Decrypt and authenticate blob
+ psa_status = sli_decrypt_its_file(
+ its_file_meta, blob, its_file_size, blob->data,
+ its_file_size - SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD, &plaintext_length);
+
+ if (psa_status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ if (plaintext_length !=
+ (its_file_size - SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD)) {
+ psa_status = PSA_ERROR_INVALID_SIGNATURE;
+ goto cleanup;
+ }
+
+ if (its_file_size + its_file_offset > 0) {
+ memcpy(p_data, blob->data, its_file_size + its_file_offset);
+ }
+ psa_status = PSA_SUCCESS;
+
+cleanup:
+ if (blob != NULL) {
+ memset(blob, 0, its_file_size);
+ mbedtls_free(blob);
+ }
+ return psa_status;
+#else
+ // If no encryption is used, just read out the data and write it directly to
+ // the output buffer
+ status = nvm3_readPartialData(nvm3_defaultHandle, nvm3_object_id, p_data,
+ its_file_offset, its_file_size);
+
+ if (status != ECODE_NVM3_OK) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ } else {
+ return PSA_SUCCESS;
+ }
+#endif
+}
+
+// Function sets detect the presence of v1 and v2 its driver. If there is
+// something stored in v1/v2 driver range, it sets its_driver_version to
+// SLI_PSA_ITS_V2_DRIVER.
+static psa_status_t detect_legacy_versions() {
+ uint8_t driver_verison = 0;
+ Ecode_t status;
+ status = nvm3_readData(nvm3_defaultHandle, SLI_PSA_ITS_V2_DRIVER_FLAG_NVM3_ID,
+ &driver_verison, sizeof(uint8_t));
+ if ((status != ECODE_NVM3_OK) && (status != ECODE_NVM3_ERR_KEY_NOT_FOUND)) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+ if (driver_verison == SLI_PSA_ITS_V3_DRIVER) {
+ its_driver_version = SLI_PSA_ITS_V3_DRIVER;
+ return PSA_SUCCESS;
+ }
+
+ size_t num_keys_referenced_by_nvm3;
+
+ nvm3_ObjectKey_t keys_referenced_by_nvm3[SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE] =
+ {0};
+
+ for (nvm3_ObjectKey_t range_start = SLI_PSA_ITS_NVM3_RANGE_START_V2_DRIVER;
+ range_start < SLI_PSA_ITS_NVM3_RANGE_END_V2_DRIVER;
+ range_start += SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE) {
+ nvm3_ObjectKey_t range_end =
+ range_start + SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE;
+ if (range_end > SLI_PSA_ITS_NVM3_RANGE_END_V2_DRIVER) {
+ range_end = SLI_PSA_ITS_NVM3_RANGE_END_V2_DRIVER;
+ }
+
+ num_keys_referenced_by_nvm3 = nvm3_enumObjects(
+ nvm3_defaultHandle, keys_referenced_by_nvm3,
+ sizeof(keys_referenced_by_nvm3) / sizeof(nvm3_ObjectKey_t), range_start,
+ range_end - 1);
+
+ if (num_keys_referenced_by_nvm3 > 0) {
+ sli_its_file_meta_v2_t its_file_meta = {0};
+ size_t its_file_size = 0;
+ size_t its_file_offset = 0;
+ status = get_file_metadata(keys_referenced_by_nvm3[0], &its_file_meta,
+ &its_file_offset, &its_file_size);
+ if (status == SLI_PSA_ITS_ECODE_NO_VALID_HEADER) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+ if (status != ECODE_NVM3_OK &&
+ status != SLI_PSA_ITS_ECODE_NEEDS_UPGRADE) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+
+ if ((its_file_meta.magic == SLI_PSA_ITS_META_MAGIC_V1) ||
+ (its_file_meta.magic == SLI_PSA_ITS_META_MAGIC_V2)) {
+ its_driver_version = SLI_PSA_ITS_V2_DRIVER;
+ return PSA_SUCCESS;
+ } else {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+ }
+ }
+ its_driver_version = SLI_PSA_ITS_V3_DRIVER;
+ return PSA_SUCCESS;
+}
+
+static psa_status_t upgrade_all_keys() {
+ size_t num_keys_referenced_by_nvm3;
+ nvm3_ObjectKey_t keys_referenced_by_nvm3[SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE] =
+ {0};
+ Ecode_t status;
+ psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ sli_its_file_meta_v2_t its_file_meta = {0};
+ size_t its_file_data_size;
+ uint8_t *its_file_buffer = NULL;
+
+ size_t its_file_size = 0;
+ size_t its_file_offset;
+
+ for (nvm3_ObjectKey_t range_start = SLI_PSA_ITS_NVM3_RANGE_START_V2_DRIVER;
+ range_start < SLI_PSA_ITS_NVM3_RANGE_END_V2_DRIVER;
+ range_start += SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE) {
+ nvm3_ObjectKey_t range_end =
+ range_start + SLI_PSA_ITS_CACHE_INIT_CHUNK_SIZE;
+ if (range_end >= SLI_PSA_ITS_NVM3_RANGE_END_V2_DRIVER) {
+ range_end = SLI_PSA_ITS_NVM3_RANGE_END_V2_DRIVER;
+ }
+
+ num_keys_referenced_by_nvm3 = nvm3_enumObjects(
+ nvm3_defaultHandle, keys_referenced_by_nvm3,
+ sizeof(keys_referenced_by_nvm3) / sizeof(nvm3_ObjectKey_t), range_start,
+ range_end - 1);
+ for (size_t i = 0; i < num_keys_referenced_by_nvm3; i++) {
+ its_file_size = 0;
+ its_file_offset = 0;
+ status = get_file_metadata(keys_referenced_by_nvm3[i], &(its_file_meta),
+ &its_file_offset, &its_file_size);
+ if (status == SLI_PSA_ITS_ECODE_NO_VALID_HEADER) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+ if (status != ECODE_NVM3_OK &&
+ status != SLI_PSA_ITS_ECODE_NEEDS_UPGRADE) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // Subtract IV and MAC from ITS file as the below checks concern the
+ // actual data size
+ its_file_data_size = its_file_size - SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD;
+#else
+ its_file_data_size = its_file_size;
+#endif
+
+ if ((its_file_meta.magic != SLI_PSA_ITS_META_MAGIC_V2) &&
+ (its_file_meta.magic != SLI_PSA_ITS_META_MAGIC_V1)) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+ its_file_buffer =
+ mbedtls_calloc(1, its_file_size + sizeof(sli_its_file_meta_v2_t));
+ if (its_file_buffer == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ psa_status =
+ psa_its_get_legacy(keys_referenced_by_nvm3[i], &(its_file_meta),
+ its_file_size, its_file_offset, its_file_buffer);
+#else
+ psa_status =
+ psa_its_get_legacy(keys_referenced_by_nvm3[i], NULL, its_file_size,
+ its_file_offset, its_file_buffer);
+#endif
+ if (psa_status != PSA_SUCCESS) {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ goto exit;
+ }
+
+#if defined(SLI_PSA_ITS_SUPPORT_V1_FORMAT_INTERNAL)
+ if (its_file_meta.magic == SLI_PSA_ITS_META_MAGIC_V1) {
+ psa_status = psa_its_set_v1(its_file_meta.uid, its_file_data_size,
+ its_file_buffer, its_file_meta.flags);
+ } else if (its_file_meta.magic == SLI_PSA_ITS_META_MAGIC_V2)
+#endif
+ {
+ psa_status = psa_its_set(its_file_meta.uid, its_file_data_size,
+ its_file_buffer, its_file_meta.flags);
+ }
+
+ if ((psa_status != PSA_SUCCESS) &&
+ (psa_status != PSA_ERROR_NOT_PERMITTED)) {
+ goto exit;
+ }
+ status =
+ nvm3_deleteObject(nvm3_defaultHandle, keys_referenced_by_nvm3[i]);
+
+ if (status != ECODE_NVM3_OK) {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ goto exit;
+ }
+ memset(its_file_buffer, 0,
+ its_file_size + sizeof(sli_its_file_meta_v2_t));
+ mbedtls_free(its_file_buffer);
+ }
+ }
+ return PSA_SUCCESS;
+
+exit:
+ // Clear and free key buffer before return.
+ memset(its_file_buffer, 0, its_file_size + sizeof(sli_its_file_meta_v2_t));
+ mbedtls_free(its_file_buffer);
+ return psa_status;
+}
+
+#if defined(SLI_PSA_ITS_SUPPORT_V1_FORMAT_INTERNAL)
+psa_status_t psa_its_set_v1(psa_storage_uid_t uid, uint32_t data_length,
+ const void *p_data,
+ psa_storage_create_flags_t create_flags) {
+ if ((data_length != 0U) && (p_data == NULL)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (create_flags != PSA_STORAGE_FLAG_WRITE_ONCE &&
+ create_flags != PSA_STORAGE_FLAG_NONE
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+ && create_flags != PSA_STORAGE_FLAG_WRITE_ONCE_SECURE_ACCESSIBLE
+#endif
+ ) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+ if ((create_flags == PSA_STORAGE_FLAG_WRITE_ONCE_SECURE_ACCESSIBLE) &&
+ (!object_lives_in_s(p_data, data_length))) {
+ // The flag indicates that this data should not be set by the non-secure
+ // domain
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+#endif
+
+ Ecode_t status;
+ psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ sl_its_file_meta_v1_t *its_file_meta;
+ nvm3_ObjectKey_t nvm3_object_id = 0;
+ size_t its_file_size = data_length;
+
+ uint8_t *its_file_buffer =
+ mbedtls_calloc(1, its_file_size + sizeof(sl_its_file_meta_v1_t));
+ if (its_file_buffer == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ memset(its_file_buffer, 0, its_file_size + sizeof(sl_its_file_meta_v1_t));
+
+ its_file_meta = (sl_its_file_meta_v1_t *)its_file_buffer;
+ sli_its_file_meta_v2_t its_file_meta_v2;
+
+ sli_its_acquire_mutex();
+ psa_status =
+ find_nvm3_id(uid, true, &its_file_meta_v2, NULL, NULL, &nvm3_object_id);
+ if (psa_status != PSA_SUCCESS) {
+ if (psa_status == PSA_ERROR_DOES_NOT_EXIST) {
+ psa_status = PSA_ERROR_INSUFFICIENT_STORAGE;
+ }
+ goto exit;
+ }
+
+ its_file_meta->magic = SLI_PSA_ITS_META_MAGIC_V1;
+ its_file_meta->uid = uid;
+ its_file_meta->flags = create_flags;
+
+ if (data_length != 0U) {
+ memcpy(its_file_buffer + sizeof(sl_its_file_meta_v1_t), ((uint8_t *)p_data),
+ data_length);
+ }
+
+ status = nvm3_writeData(nvm3_defaultHandle, nvm3_object_id, its_file_buffer,
+ its_file_size + sizeof(sl_its_file_meta_v1_t));
+
+ if (status == ECODE_NVM3_OK) {
+ // Power-loss might occur, however upon boot, the look-up table will be
+ // re-filled as long as the data has been successfully written to NVM3.
+ set_cache(nvm3_object_id);
+ } else {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ }
+
+exit:
+ // Clear and free key buffer before return.
+ memset(its_file_buffer, 0, its_file_size + sizeof(sl_its_file_meta_v1_t));
+ mbedtls_free(its_file_buffer);
+ sli_its_release_mutex();
+ return psa_status;
+}
+#endif // SLI_PSA_ITS_SUPPORT_V1_FORMAT_INTERNAL
+#endif // SL_PSA_ITS_SUPPORT_V1_DRIVER
+
+/**
+ * \brief Search through NVM3 for correct uid
+ *
+ * \param[in] uid UID under what we want to store the data
+ * \param[in] find_empty_slot Indicates whether we want to find existing data or empty space for storing new.
+ * \param[out] its_file_meta Meta information of ITS file
+ * \param[out] its_file_offset Offset of ITS file
+ * \param[out] its_file_size Size of ITS file
+ * \param[out] output_nvm3_id NVM3 ID corresponding to UID.
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_DOES_NOT_EXIST The data with this UID are not stored in NVM3
+ * \retval PSA_ERROR_NOT_PERMITTED The requested operation is not permitted
+ */
+static psa_status_t find_nvm3_id(psa_storage_uid_t uid, bool find_empty_slot,
+ sli_its_file_meta_v2_t *its_file_meta,
+ size_t *its_file_offset, size_t *its_file_size,
+ nvm3_ObjectKey_t *output_nvm3_id) {
+ Ecode_t status;
+ nvm3_ObjectKey_t tmp_id = 0;
+ nvm3_ObjectKey_t nvm3_object_id = 0;
+ nvm3_object_id = derive_nvm3_id(uid);
+
+ if (nvm3_uid_set_cache_initialized == false) {
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+ // With SKL the NVM3 instance must be initialized by the NS app. We
+ // therefore check that it has been opened (which is done on init) rather
+ // than actually doing the init.
+ if (!nvm3_defaultHandle->hasBeenOpened) {
+#else
+ if (nvm3_initDefault() != ECODE_NVM3_OK) {
+#endif
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+
+#if SL_PSA_ITS_SUPPORT_V2_DRIVER
+ if (its_driver_version == SLI_PSA_ITS_NOT_CHECKED) {
+ if (detect_legacy_versions() != PSA_SUCCESS) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+ if (its_driver_version == SLI_PSA_ITS_V2_DRIVER) {
+ psa_status_t psa_status = upgrade_all_keys();
+ if (psa_status != PSA_SUCCESS) {
+ return psa_status;
+ }
+ psa_status = write_driver_v3();
+ if (psa_status != PSA_SUCCESS) {
+ return psa_status;
+ }
+ } else {
+ init_cache();
+ }
+ } else {
+ init_cache();
+ }
+#else
+ init_cache();
+#endif
+ }
+
+ for (size_t i = 0; i < SL_PSA_ITS_MAX_FILES; ++i) {
+ if (!lookup_cache(nvm3_object_id)) {
+ // dont exist
+ if (lookup_tomb(nvm3_object_id)) {
+ // tombstone
+ if (tmp_id == 0) {
+ // mark first empty space
+ tmp_id = nvm3_object_id;
+ }
+ nvm3_object_id = increment_obj_id(nvm3_object_id);
+ continue;
+ } else {
+ // empty space
+ if (find_empty_slot) {
+ if (tmp_id != 0) {
+ *output_nvm3_id = tmp_id;
+ return PSA_SUCCESS;
+ }
+ *output_nvm3_id = nvm3_object_id;
+ return PSA_SUCCESS;
+ } else {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+ }
+ }
+ status = get_file_metadata(nvm3_object_id, its_file_meta, its_file_offset,
+ its_file_size);
+
+ if (status == SLI_PSA_ITS_ECODE_NO_VALID_HEADER ||
+ status == ECODE_NVM3_ERR_READ_DATA_SIZE) {
+ // we don't expect any other data in our range then PSA ITS files.
+ // delete the file if the magic doesn't match or the object on disk
+ // is too small to even have full metadata.
+ status = nvm3_deleteObject(nvm3_defaultHandle, nvm3_object_id);
+ if (status != ECODE_NVM3_OK) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+ }
+
+ if (status != ECODE_NVM3_OK && status != SLI_PSA_ITS_ECODE_NEEDS_UPGRADE) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+
+ if (its_file_meta->uid != uid) {
+ nvm3_object_id = increment_obj_id(nvm3_object_id);
+ } else {
+ if (find_empty_slot) {
+ if (its_file_meta->flags == PSA_STORAGE_FLAG_WRITE_ONCE
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+ || its_file_meta->flags ==
+ PSA_STORAGE_FLAG_WRITE_ONCE_SECURE_ACCESSIBLE
+#endif
+ ) {
+ return PSA_ERROR_NOT_PERMITTED;
+ }
+ }
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // If the UID already exists, authenticate the existing value and make
+ // sure the stored UID is the same. Note that this can potentially induce
+ // a significant performance hit.
+ psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_storage_uid_t authenticated_uid = 0;
+ psa_status = authenticate_its_file(nvm3_object_id, &authenticated_uid);
+ if (psa_status != PSA_SUCCESS) {
+ return psa_status;
+ }
+
+ if (authenticated_uid != uid) {
+ return PSA_ERROR_INVALID_SIGNATURE;
+ }
+#endif
+ *output_nvm3_id = nvm3_object_id;
+ return PSA_SUCCESS;
+ }
+ }
+ if (find_empty_slot) {
+ if (tmp_id != 0) {
+ *output_nvm3_id = tmp_id;
+ return PSA_SUCCESS;
+ }
+ }
+ return PSA_ERROR_DOES_NOT_EXIST;
+}
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+static inline void cache_session_key(uint8_t *session_key,
+ psa_storage_uid_t uid) {
+ // Cache the session key
+ memcpy(g_cached_session_key.data, session_key,
+ sizeof(g_cached_session_key.data));
+ g_cached_session_key.uid = uid;
+ g_cached_session_key.active = true;
+}
+
+/**
+ * \brief Derive a session key for ITS file encryption from the initialized root key and provided IV.
+ *
+ * \param[in] iv Pointer to array containing the initialization vector to be used in the key derivation.
+ * \param[in] iv_size Size of the IV buffer in bytes. Must be 12 bytes (AES-GCM IV size).
+ * \param[out] session_key Pointer to array where derived session key shall be stored.
+ * \param[out] session_key_size Size of the derived session key output array. Must be at least 32 bytes (AES-256 key size).
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_BAD_STATE The root key has not been initialized.
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because iv or session_key is NULL, or their sizes are incorrect.
+ * \retval PSA_ERROR_HARDWARE_FAILURE The operation failed because an internal cryptographic operation failed.
+ */
+static psa_status_t derive_session_key(uint8_t *iv, size_t iv_size,
+ uint8_t *session_key,
+ size_t session_key_size) {
+ if (iv == NULL || iv_size != AES_GCM_IV_SIZE || session_key == NULL ||
+ session_key_size < SESSION_KEY_SIZE) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+#if defined(SEMAILBOX_PRESENT)
+ // For HSE devices, use the builtin TrustZone Root Key
+ psa_set_key_id(&attributes, SL_SE_BUILTIN_KEY_TRUSTZONE_ID);
+
+ psa_key_lifetime_t reported_lifetime;
+ psa_drv_slot_number_t reported_slot;
+ status = mbedtls_psa_platform_get_builtin_key(
+ psa_get_key_id(&attributes), &reported_lifetime, &reported_slot);
+
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ psa_set_key_lifetime(&attributes, reported_lifetime);
+
+ uint8_t key_buffer[sizeof(sli_se_opaque_key_context_header_t)];
+ size_t key_buffer_size;
+ status = sli_se_opaque_get_builtin_key(reported_slot, &attributes, key_buffer,
+ sizeof(key_buffer), &key_buffer_size);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+#else // defined(SEMAILBOX_PRESENT)
+ // For VSE devices, use the previously initialized root key
+ if (!g_root_key.initialized) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ // Prepare root key attributes
+ psa_set_key_algorithm(&attributes, PSA_ALG_CMAC);
+ psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
+ psa_set_key_bits(&attributes, ROOT_KEY_SIZE * 8);
+
+ // Point the key buffer to the global root key
+ uint8_t *key_buffer = (uint8_t *)g_root_key.data;
+ size_t key_buffer_size = sizeof(g_root_key.data);
+#endif // defined(SEMAILBOX_PRESENT)
+
+ // Use CMAC as a key derivation function
+ size_t session_key_length;
+ status = psa_driver_wrapper_mac_compute(
+ &attributes, key_buffer, key_buffer_size, PSA_ALG_CMAC, iv, iv_size,
+ session_key, session_key_size, &session_key_length);
+
+ // Verify that the key derivation was successful before transferring the key
+ // to the caller
+ if (status != PSA_SUCCESS || session_key_length != SESSION_KEY_SIZE) {
+ memset(session_key, 0, session_key_size);
+ return PSA_ERROR_HARDWARE_FAILURE;
+ }
+
+ return status;
+}
+
+/**
+ * \brief Encrypt and authenticate ITS data with AES-128-GCM, storing the result in an encrypted blob.
+ *
+ * \param[in] metadata ITS metadata to be used as authenticated additional data.
+ * \param[in] plaintext Pointer to array containing data to be encrypted.
+ * \param[in] plaintext_size Size of provided plaintext data array.
+ * \param[out] blob Pointer to array where the resulting encrypted blob shall be placed.
+ * \param[in] blob_size Size of the output array. Must be at least as big as plaintext_size + SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD
+ * \param[out] blob_length Resulting size of the output blob.
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_BAD_STATE The root key has not been initialized.
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one or more arguments are NULL or of invalid size.
+ * \retval PSA_ERROR_HARDWARE_FAILURE The operation failed because an internal cryptographic operation failed.
+ */
+psa_status_t sli_encrypt_its_file(sli_its_file_meta_v2_t *metadata,
+ uint8_t *plaintext, size_t plaintext_size,
+ sli_its_encrypted_blob_t *blob,
+ size_t blob_size, size_t *blob_length) {
+ if (metadata == NULL || (plaintext == NULL && plaintext_size > 0) ||
+ blob == NULL ||
+ blob_size < plaintext_size + SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD ||
+ blob_length == NULL) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ // Generate IV
+ size_t iv_length = 0;
+ psa_status_t psa_status = mbedtls_psa_external_get_random(
+ NULL, blob->iv, AES_GCM_IV_SIZE, &iv_length);
+
+ if (psa_status != PSA_SUCCESS || iv_length != AES_GCM_IV_SIZE) {
+ return PSA_ERROR_HARDWARE_FAILURE;
+ }
+
+ // Prepare encryption key
+ psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+ psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);
+ psa_set_key_algorithm(&attributes, PSA_ALG_GCM);
+ psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
+ psa_set_key_bits(&attributes, SESSION_KEY_SIZE * 8);
+
+ uint8_t session_key[SESSION_KEY_SIZE];
+ psa_status = derive_session_key(blob->iv, AES_GCM_IV_SIZE, session_key,
+ sizeof(session_key));
+ if (psa_status != PSA_SUCCESS) {
+ return psa_status;
+ }
+
+ cache_session_key(session_key, metadata->uid);
+
+ // Retrieve data to be encrypted
+ if (plaintext_size != 0U) {
+ memcpy(blob->data, ((uint8_t *)plaintext), plaintext_size);
+ }
+
+ // Encrypt and authenticate blob
+ size_t output_length = 0;
+ psa_status = psa_driver_wrapper_aead_encrypt(
+ &attributes, session_key, sizeof(session_key), PSA_ALG_GCM, blob->iv,
+ sizeof(blob->iv), (uint8_t *)metadata,
+ sizeof(sli_its_file_meta_v2_t), // metadata is AAD
+ blob->data, plaintext_size, blob->data,
+ plaintext_size +
+ AES_GCM_MAC_SIZE, // output == input for in-place encryption
+ &output_length);
+
+ // Clear the local session key immediately after we're done using it
+ memset(session_key, 0, sizeof(session_key));
+
+ if (psa_status != PSA_SUCCESS) {
+ return PSA_ERROR_HARDWARE_FAILURE;
+ }
+
+ if (output_length != plaintext_size + AES_GCM_MAC_SIZE) {
+ return PSA_ERROR_HARDWARE_FAILURE;
+ }
+
+ *blob_length = output_length + AES_GCM_IV_SIZE;
+
+ return PSA_SUCCESS;
+}
+
+/**
+ * \brief Decrypt and authenticate encrypted ITS data.
+ *
+ * \param[in] metadata ITS metadata to be used as authenticated additional data. Must be identical to the metadata used during encryption.
+ * \param[in] blob Encrypted blob containing data to be decrypted.
+ * \param[in] blob_size Size of the encrypted blob in bytes.
+ * \param[out] plaintext Pointer to array where the decrypted plaintext shall be placed.
+ * \param[in] plaintext_size Size of the plaintext array. Must be equal to sizeof(blob->data) - AES_GCM_MAC_SIZE.
+ * \param[out] plaintext_length Resulting length of the decrypted plaintext.
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_INVALID_SIGANTURE The operation failed because authentication of the decrypted data failed.
+ * \retval PSA_ERROR_BAD_STATE The root key has not been initialized.
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one or more arguments are NULL or of invalid size.
+ * \retval PSA_ERROR_HARDWARE_FAILURE The operation failed because an internal cryptographic operation failed.
+ */
+static psa_status_t sli_decrypt_its_file(sli_its_file_meta_v2_t *metadata,
+ sli_its_encrypted_blob_t *blob,
+ size_t blob_size, uint8_t *plaintext,
+ size_t plaintext_size,
+ size_t *plaintext_length) {
+ if (metadata == NULL || blob == NULL ||
+ blob_size < plaintext_size + SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD ||
+ (plaintext == NULL && plaintext_size > 0) || plaintext_length == NULL) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ // Prepare decryption key
+ psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+ psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT);
+ psa_set_key_algorithm(&attributes, PSA_ALG_GCM);
+ psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
+ psa_set_key_bits(&attributes, SESSION_KEY_SIZE * 8);
+
+ psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ uint8_t session_key[SESSION_KEY_SIZE];
+
+ if (g_cached_session_key.active &&
+ g_cached_session_key.uid == metadata->uid) {
+ // Use cached session key if it's already set and UID matches
+ memcpy(session_key, g_cached_session_key.data, sizeof(session_key));
+ } else {
+ psa_status = derive_session_key(blob->iv, AES_GCM_IV_SIZE, session_key,
+ sizeof(session_key));
+ if (psa_status != PSA_SUCCESS) {
+ return psa_status;
+ }
+ cache_session_key(session_key, metadata->uid);
+ }
+
+ // Decrypt and authenticate blob
+ size_t output_length = 0;
+ psa_status = psa_driver_wrapper_aead_decrypt(
+ &attributes, session_key, sizeof(session_key), PSA_ALG_GCM, blob->iv,
+ sizeof(blob->iv), (uint8_t *)metadata,
+ sizeof(sli_its_file_meta_v2_t), // metadata is AAD
+ blob->data, plaintext_size + AES_GCM_MAC_SIZE, plaintext, plaintext_size,
+ &output_length);
+
+ // Clear the session key immediately after we're done using it
+ memset(session_key, 0, sizeof(session_key));
+
+ // Invalid signature likely means that NVM data was tampered with
+ if (psa_status == PSA_ERROR_INVALID_SIGNATURE) {
+ return PSA_ERROR_INVALID_SIGNATURE;
+ }
+
+ if (psa_status != PSA_SUCCESS || output_length != plaintext_size) {
+ return PSA_ERROR_HARDWARE_FAILURE;
+ }
+
+ *plaintext_length = output_length;
+
+ return PSA_SUCCESS;
+}
+
+/**
+ * \brief Authenticate encrypted ITS data and return the UID of the ITS file that was authenticated.
+ *
+ * \details NOTE: This function will run sli_decrypt_its_file() internally. The difference from the sli_decrypt_its_file()
+ * function is that authenticate_its_file() reads the NVM3 data, decrypts it in order to authenticate the
+ * stored data, and then discards the plaintext. This is needed since PSA Crypto doesn't support the
+ * GMAC primitive directly, which means we have to run a full GCM decrypt for authentication.
+ *
+ * \param[in] nvm3_object_id The NVM3 id corresponding to the stored ITS file.
+ * \param[out] authenticated_uid UID for the authenticated ITS file.
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_INVALID_SIGANTURE The operation failed because authentication of the decrypted data failed.
+ * \retval PSA_ERROR_BAD_STATE The root key has not been initialized.
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one or more arguments are NULL or of invalid size.
+ * \retval PSA_ERROR_HARDWARE_FAILURE The operation failed because an internal cryptographic operation failed.
+ */
+static psa_status_t
+authenticate_its_file(nvm3_ObjectKey_t nvm3_object_id,
+ psa_storage_uid_t *authenticated_uid) {
+ psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ sli_its_file_meta_v2_t *its_file_meta = NULL;
+ sli_its_encrypted_blob_t *blob = NULL;
+
+ uint32_t obj_type;
+ size_t its_file_size = 0;
+ Ecode_t status = nvm3_getObjectInfo(nvm3_defaultHandle, nvm3_object_id,
+ &obj_type, &its_file_size);
+ if (status != ECODE_NVM3_OK) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+
+ uint8_t *its_file_buffer = mbedtls_calloc(1, its_file_size);
+ if (its_file_buffer == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ memset(its_file_buffer, 0, its_file_size);
+
+ status = nvm3_readData(nvm3_defaultHandle, nvm3_object_id, its_file_buffer,
+ its_file_size);
+ if (status != ECODE_NVM3_OK) {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ goto cleanup;
+ }
+
+ its_file_meta = (sli_its_file_meta_v2_t *)its_file_buffer;
+ blob = (sli_its_encrypted_blob_t *)(its_file_buffer +
+ sizeof(sli_its_file_meta_v2_t));
+
+ // Decrypt and authenticate blob
+ size_t plaintext_length;
+ psa_status = sli_decrypt_its_file(
+ its_file_meta, blob, its_file_size - sizeof(sli_its_file_meta_v2_t),
+ blob->data,
+ its_file_size - sizeof(sli_its_file_meta_v2_t) -
+ SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD,
+ &plaintext_length);
+
+ if (psa_status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ if (plaintext_length != (its_file_size - sizeof(sli_its_file_meta_v2_t) -
+ SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD)) {
+ psa_status = PSA_ERROR_INVALID_SIGNATURE;
+ goto cleanup;
+ }
+
+ if (authenticated_uid != NULL) {
+ *authenticated_uid = its_file_meta->uid;
+ }
+
+ psa_status = PSA_SUCCESS;
+
+cleanup:
+
+ // Discard output, as we're only interested in whether the authentication
+ // check passed or not.
+ memset(its_file_buffer, 0, its_file_size);
+ mbedtls_free(its_file_buffer);
+
+ return psa_status;
+}
+#endif // defined(SLI_PSA_ITS_ENCRYPTED)
+
+// -------------------------------------
+// Global function definitions
+
+/**
+ * \brief create a new or modify an existing uid/value pair
+ *
+ * \param[in] uid the identifier for the data
+ * \param[in] data_length The size in bytes of the data in `p_data`
+ * \param[in] p_data A buffer containing the data
+ * \param[in] create_flags The flags that the data will be stored with
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_NOT_PERMITTED The operation failed because the provided `uid` value was already created with PSA_STORAGE_FLAG_WRITE_ONCE
+ * \retval PSA_ERROR_NOT_SUPPORTED The operation failed because one or more of the flags provided in `create_flags` is not supported or is not valid
+ * \retval PSA_ERROR_INSUFFICIENT_STORAGE The operation failed because there was insufficient space on the storage medium
+ * \retval PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has failed (Fatal error)
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided pointers(`p_data`)
+ * is invalid, for example is `NULL` or references memory the caller cannot access
+ * \retval PSA_ERROR_HARDWARE_FAILURE The operation failed because an internal cryptographic operation failed.
+ * \retval PSA_ERROR_INVALID_SIGNATURE The operation failed because the provided `uid` doesnt match the autenticated uid from the storage
+ */
+psa_status_t psa_its_set(psa_storage_uid_t uid, uint32_t data_length,
+ const void *p_data,
+ psa_storage_create_flags_t create_flags) {
+ if ((data_length != 0U) && (p_data == NULL)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ if ((data_length > NVM3_MAX_OBJECT_SIZE)) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+
+ if (create_flags != PSA_STORAGE_FLAG_WRITE_ONCE &&
+ create_flags != PSA_STORAGE_FLAG_NONE
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+ && create_flags != PSA_STORAGE_FLAG_WRITE_ONCE_SECURE_ACCESSIBLE
+#endif
+ ) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+ if ((create_flags == PSA_STORAGE_FLAG_WRITE_ONCE_SECURE_ACCESSIBLE) &&
+ (!object_lives_in_s(p_data, data_length))) {
+ // The flag indicates that this data should not be set by the non-secure
+ // domain
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+#endif
+
+ Ecode_t status;
+ psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ sli_its_file_meta_v2_t *its_file_meta;
+ nvm3_ObjectKey_t nvm3_object_id = 0;
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ sli_its_encrypted_blob_t *blob = NULL;
+ size_t its_file_size = data_length + SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD;
+ size_t blob_length = 0u;
+#else
+ size_t its_file_size = data_length;
+#endif
+
+ uint8_t *its_file_buffer =
+ mbedtls_calloc(1, its_file_size + sizeof(sli_its_file_meta_v2_t));
+ if (its_file_buffer == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ memset(its_file_buffer, 0, its_file_size + sizeof(sli_its_file_meta_v2_t));
+
+ its_file_meta = (sli_its_file_meta_v2_t *)its_file_buffer;
+
+ sli_its_acquire_mutex();
+ psa_status =
+ find_nvm3_id(uid, true, its_file_meta, NULL, NULL, &nvm3_object_id);
+ if (psa_status != PSA_SUCCESS) {
+ if (psa_status == PSA_ERROR_DOES_NOT_EXIST) {
+ psa_status = PSA_ERROR_INSUFFICIENT_STORAGE;
+ }
+ goto exit;
+ }
+
+ its_file_meta->magic = SLI_PSA_ITS_META_MAGIC_V2;
+ its_file_meta->uid = uid;
+ its_file_meta->flags = create_flags;
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // Everything after the the file metadata will make up the encrypted &
+ // authenticated blob
+ blob = (sli_its_encrypted_blob_t *)(its_file_buffer +
+ sizeof(sli_its_file_meta_v2_t));
+
+ // Encrypt and authenticate the provided data
+ psa_status =
+ sli_encrypt_its_file(its_file_meta, (uint8_t *)p_data, data_length, blob,
+ its_file_size, &blob_length);
+
+ if (psa_status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (blob_length != its_file_size) {
+ psa_status = PSA_ERROR_HARDWARE_FAILURE;
+ goto exit;
+ }
+
+#else
+ if (data_length != 0U) {
+ memcpy(its_file_buffer + sizeof(sli_its_file_meta_v2_t),
+ ((uint8_t *)p_data), data_length);
+ }
+#endif
+
+ status = nvm3_writeData(nvm3_defaultHandle, nvm3_object_id, its_file_buffer,
+ its_file_size + sizeof(sli_its_file_meta_v2_t));
+
+ if (status == ECODE_NVM3_OK) {
+ // Power-loss might occur, however upon boot, the look-up table will be
+ // re-filled as long as the data has been successfully written to NVM3.
+ set_cache(nvm3_object_id);
+ } else {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ }
+
+exit:
+ // Clear and free key buffer before return.
+ memset(its_file_buffer, 0, its_file_size + sizeof(sli_its_file_meta_v2_t));
+ mbedtls_free(its_file_buffer);
+ sli_its_release_mutex();
+ return psa_status;
+}
+
+/**
+ * \brief Retrieve the value associated with a provided uid
+ *
+ * \param[in] uid The uid value
+ * \param[in] data_offset The starting offset of the data requested
+ * \param[in] data_length the amount of data requested (and the minimum allocated size of the `p_data` buffer)
+ * \param[out] p_data The buffer where the data will be placed upon successful completion
+ * \param[out] p_data_length The amount of data returned in the p_data buffer
+ *
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided `uid` value was not found in the storage
+ * \retval PSA_ERROR_BUFFER_TOO_SMALL The operation failed because the data associated with provided uid is larger than `data_size`
+ * \retval PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has failed (Fatal error)
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided pointers(`p_data`, `p_data_length`)
+ * is invalid. For example is `NULL` or references memory the caller cannot access.
+ * In addition, this can also happen if an invalid offset was provided.
+ */
+psa_status_t psa_its_get(psa_storage_uid_t uid, uint32_t data_offset,
+ uint32_t data_length, void *p_data,
+ size_t *p_data_length) {
+ if ((data_length != 0U) && (p_data_length == NULL)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (data_length != 0U) {
+ // If the request amount of data is 0, allow invalid pointer of the output
+ // buffer.
+ if ((p_data == NULL) || ((uint32_t)p_data < SRAM_BASE) ||
+ ((uint32_t)p_data > (SRAM_BASE + SRAM_SIZE - data_length))) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ }
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ size_t plaintext_length;
+ sli_its_encrypted_blob_t *blob = NULL;
+#endif
+ psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ Ecode_t status;
+ sli_its_file_meta_v2_t its_file_meta = {0};
+ size_t its_file_size = 0u;
+ size_t its_file_data_size = 0u;
+ size_t its_file_offset = 0u;
+ nvm3_ObjectKey_t nvm3_object_id;
+
+ sli_its_acquire_mutex();
+ psa_status = find_nvm3_id(uid, false, &its_file_meta, &its_file_offset,
+ &its_file_size, &nvm3_object_id);
+ if (psa_status != PSA_SUCCESS) {
+ goto exit;
+ }
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+ if (its_file_meta.flags == PSA_STORAGE_FLAG_WRITE_ONCE_SECURE_ACCESSIBLE &&
+ !object_lives_in_s(p_data, data_length)) {
+ // The flag indicates that this data should not be read back to the
+ // non-secure domain
+ psa_status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+#endif
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // Subtract IV and MAC from ITS file as the below checks concern the actual
+ // data size
+ its_file_data_size = its_file_size - SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD;
+#else
+ its_file_data_size = its_file_size;
+#endif
+
+ if (data_length != 0U) {
+ if ((data_offset >= its_file_data_size) && (its_file_data_size != 0U)) {
+ psa_status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ if ((its_file_data_size == 0U) && (data_offset != 0U)) {
+ psa_status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+ } else {
+ // Allow the offset at the data size boundary if the requested amount of
+ // data is zero.
+ if (data_offset > its_file_data_size) {
+ psa_status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+ }
+
+ if (data_length > (its_file_data_size - data_offset)) {
+ *p_data_length = its_file_data_size - data_offset;
+ } else {
+ *p_data_length = data_length;
+ }
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // its_file_size includes size of sli_its_encrypted_blob_t struct
+ blob = (sli_its_encrypted_blob_t *)mbedtls_calloc(1, its_file_size);
+ if (blob == NULL) {
+ psa_status = PSA_ERROR_INSUFFICIENT_MEMORY;
+ goto exit;
+ }
+ memset(blob, 0, its_file_size);
+
+ status = nvm3_readPartialData(nvm3_defaultHandle, nvm3_object_id, blob,
+ its_file_offset, its_file_size);
+ if (status != ECODE_NVM3_OK) {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ goto exit;
+ }
+
+ // Decrypt and authenticate blob
+ psa_status = sli_decrypt_its_file(
+ &its_file_meta, blob, its_file_size, blob->data,
+ its_file_size - SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD, &plaintext_length);
+
+ if (psa_status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (plaintext_length !=
+ (its_file_size - SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD)) {
+ psa_status = PSA_ERROR_INVALID_SIGNATURE;
+ goto exit;
+ }
+
+ // Verify that the requested UID is equal to the retrieved and authenticated
+ // UID
+ if (uid != its_file_meta.uid) {
+ psa_status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ if (*p_data_length > 0) {
+ memcpy(p_data, blob->data + data_offset, *p_data_length);
+ }
+ psa_status = PSA_SUCCESS;
+
+exit:
+ if (blob != NULL) {
+ memset(blob, 0, its_file_size);
+ mbedtls_free(blob);
+ }
+ sli_its_release_mutex();
+#else
+ // If no encryption is used, just read out the data and write it directly to
+ // the output buffer
+ status = nvm3_readPartialData(nvm3_defaultHandle, nvm3_object_id, p_data,
+ its_file_offset + data_offset, *p_data_length);
+
+ if (status != ECODE_NVM3_OK) {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ } else {
+ psa_status = PSA_SUCCESS;
+ }
+
+exit:
+ sli_its_release_mutex();
+#endif
+
+ return psa_status;
+}
+
+/**
+ * \brief Retrieve the metadata about the provided uid
+ *
+ * \param[in] uid The uid value
+ * \param[out] p_info A pointer to the `psa_storage_info_t` struct that will be populated with the metadata
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided uid value was not found in the storage
+ * \retval PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has failed (Fatal error)
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided pointers(`p_info`)
+ * is invalid, for example is `NULL` or references memory the caller cannot access
+ * \retval PSA_ERROR_INVALID_SIGANTURE The operation failed because authentication of the stored metadata failed.
+ */
+psa_status_t psa_its_get_info(psa_storage_uid_t uid,
+ struct psa_storage_info_t *p_info) {
+ if (p_info == NULL) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ sli_its_file_meta_v2_t its_file_meta = {0};
+ size_t its_file_size = 0;
+ size_t its_file_offset = 0;
+ nvm3_ObjectKey_t nvm3_object_id;
+
+ sli_its_acquire_mutex();
+ psa_status = find_nvm3_id(uid, false, &its_file_meta, &its_file_offset,
+ &its_file_size, &nvm3_object_id);
+ if (psa_status != PSA_SUCCESS) {
+ sli_its_release_mutex();
+ return psa_status;
+ }
+
+ p_info->flags = its_file_meta.flags;
+ p_info->size = its_file_size;
+
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ // Remove IV and MAC size from file size
+ p_info->size = its_file_size - SLI_ITS_ENCRYPTED_BLOB_SIZE_OVERHEAD;
+#endif
+ sli_its_release_mutex();
+ return PSA_SUCCESS;
+}
+
+/**
+ * \brief Remove the provided key and its associated data from the storage
+ *
+ * \param[in] uid The uid value
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided key value was not found in the storage
+ * \retval PSA_ERROR_NOT_PERMITTED The operation failed because the provided key value was created with PSA_STORAGE_FLAG_WRITE_ONCE
+ * \retval PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has failed (Fatal error)
+ */
+psa_status_t psa_its_remove(psa_storage_uid_t uid) {
+ psa_status_t psa_status = PSA_ERROR_CORRUPTION_DETECTED;
+ Ecode_t status;
+ sli_its_file_meta_v2_t its_file_meta = {0};
+ size_t its_file_size = 0;
+ size_t its_file_offset = 0;
+ nvm3_ObjectKey_t nvm3_object_id;
+
+ sli_its_acquire_mutex();
+ psa_status = find_nvm3_id(uid, false, &its_file_meta, &its_file_offset,
+ &its_file_size, &nvm3_object_id);
+ if (psa_status != PSA_SUCCESS) {
+ goto exit;
+ }
+ if (its_file_meta.flags == PSA_STORAGE_FLAG_WRITE_ONCE
+#if defined(TFM_CONFIG_SL_SECURE_LIBRARY)
+ || (its_file_meta.flags == PSA_STORAGE_FLAG_WRITE_ONCE_SECURE_ACCESSIBLE)
+#endif
+ ) {
+ psa_status = PSA_ERROR_NOT_PERMITTED;
+ goto exit;
+ }
+ status = nvm3_deleteObject(nvm3_defaultHandle, nvm3_object_id);
+ if (status == ECODE_NVM3_OK) {
+ // Power-loss might occur, however upon boot, the look-up table will be
+ // re-filled as long as the data has been successfully written to NVM3.
+ clear_cache(nvm3_object_id);
+ set_tomb(nvm3_object_id);
+ psa_status = PSA_SUCCESS;
+ } else {
+ psa_status = PSA_ERROR_STORAGE_FAILURE;
+ }
+
+exit:
+ sli_its_release_mutex();
+ return psa_status;
+}
+
+// -------------------------------------
+// Silicon Labs extensions
+
+static psa_storage_uid_t psa_its_identifier_of_slot(mbedtls_svc_key_id_t key) {
+#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
+ /* Encode the owner in the upper 32 bits. This means that if
+ * owner values are nonzero (as they are on a PSA platform),
+ * no key file will ever have a value less than 0x100000000, so
+ * the whole range 0..0xffffffff is available for non-key files. */
+ uint32_t unsigned_owner_id = MBEDTLS_SVC_KEY_ID_GET_OWNER_ID(key);
+ return ((uint64_t)unsigned_owner_id << 32) |
+ MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key);
+#else
+ /* Use the key id directly as a file name.
+ * psa_is_key_id_valid() in psa_crypto_slot_management.c
+ * is responsible for ensuring that key identifiers do not have a
+ * value that is reserved for non-key files. */
+ return key;
+#endif
+}
+
+psa_status_t sli_psa_its_change_key_id(mbedtls_svc_key_id_t old_id,
+ mbedtls_svc_key_id_t new_id) {
+ psa_storage_uid_t old_uid = psa_its_identifier_of_slot(old_id);
+ psa_storage_uid_t new_uid = psa_its_identifier_of_slot(new_id);
+ size_t its_file_size = 0;
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ if (old_id == new_id) {
+ return PSA_SUCCESS;
+ }
+ // Check whether the key to migrate exists on disk
+ struct psa_storage_info_t p_info;
+ status = psa_its_get_info(old_uid, &p_info);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ // Allocate temporary buffer and cast it to the metadata format
+ uint8_t *its_file_buffer = mbedtls_calloc(1, p_info.size);
+ if (its_file_buffer == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ // Read contents of pre-existing key into the temporary buffer
+ status =
+ psa_its_get(old_uid, 0, p_info.size, its_file_buffer, &its_file_size);
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_its_set(new_uid, its_file_size, its_file_buffer, p_info.flags);
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_its_remove(old_uid);
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+exit:
+ // Clear and free key buffer before return.
+ memset(its_file_buffer, 0, its_file_size);
+ mbedtls_free(its_file_buffer);
+ return status;
+}
+
+/**
+ * \brief Check if the ITS encryption is enabled
+ */
+psa_status_t sli_psa_its_encrypted(void) {
+#if defined(SLI_PSA_ITS_ENCRYPTED)
+ return PSA_SUCCESS;
+#else
+ return PSA_ERROR_NOT_SUPPORTED;
+#endif
+}
+
+#if defined(SLI_PSA_ITS_ENCRYPTED) && !defined(SEMAILBOX_PRESENT)
+/**
+ * \brief Set the root key to be used when deriving session keys for ITS encryption.
+ *
+ * \param[in] root_key Buffer containing the root key.
+ * \param[in] root_key_size Size of the root key in bytes. Must be 32 (256 bits).
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The key was successfully set.
+ * \retval PSA_ERROR_INVALID_ARGUMENT The root key was NULL or had an invalid size.
+ * \retval PSA_ERROR_ALREADY_EXISTS The root key has already been initialized.
+ */
+psa_status_t sli_psa_its_set_root_key(uint8_t *root_key, size_t root_key_size) {
+ // Check that arguments are valid
+ if (root_key == NULL || root_key_size != sizeof(g_root_key.data)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ // Check that the root key has not already been set
+ // (This is possibly too restrictive. For TrustZone usage this can be enforced
+ // by not exposing the function to NS instead.)
+ if (g_root_key.initialized) {
+ return PSA_ERROR_ALREADY_EXISTS;
+ }
+
+ // Store the provided root key and mark it as initialized
+ memcpy(g_root_key.data, root_key, sizeof(g_root_key.data));
+ g_root_key.initialized = true;
+
+ return PSA_SUCCESS;
+}
+#endif // defined(SLI_PSA_ITS_ENCRYPTED) && !defined(SEMAILBOX_PRESENT)
+#endif // (!SL_PSA_ITS_SUPPORT_V3_DRIVER)
+#endif // MBEDTLS_PSA_CRYPTO_STORAGE_C && !MBEDTLS_PSA_ITS_FILE_C