Test/App: Add the original test and app codes from tf-m
The version of the tf-m is:
ac9ccf207e153726b8dc1f5569f702f56d94297f
Change-Id: I445ada360540da55bbe74b3e7aa8d622e8fda501
Signed-off-by: Kevin Peng <kevin.peng@arm.com>
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
new file mode 100644
index 0000000..2039a6a
--- /dev/null
+++ b/app/CMakeLists.txt
@@ -0,0 +1,435 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+cmake_minimum_required(VERSION 3.7)
+
+set(TFM_BUILD_IN_SPE OFF)
+
+#Tell cmake where our modules can be found
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../cmake)
+
+set(APP_DIR ${CMAKE_CURRENT_LIST_DIR})
+get_filename_component(TFM_ROOT_DIR ${APP_DIR}/.. ABSOLUTE)
+set(INTERFACE_DIR ${TFM_ROOT_DIR}/interface)
+
+#Include common stuff to control cmake.
+include("Common/BuildSys")
+
+#Start an embedded project.
+embedded_project_start(CONFIG "${TFM_ROOT_DIR}/configs/ConfigDefault.cmake")
+project(tfm_ns LANGUAGES ASM C)
+embedded_project_fixup()
+
+#Include BL2 bootloader related functions
+set(MCUBOOT_DIR "${TFM_ROOT_DIR}/bl2/ext/mcuboot")
+include("${MCUBOOT_DIR}/MCUBoot.cmake")
+
+#CMSIS
+get_filename_component(CMSIS_DIR ${TFM_ROOT_DIR}/../tf-m-tests/CMSIS ABSOLUTE)
+
+if(NOT EXISTS ${CMSIS_DIR})
+ message(FATAL_ERROR "Missing CMSIS. Please clone the tf-m-tests repo.")
+endif()
+
+if (NOT DEFINED BL2)
+ message(FATAL_ERROR "Incomplete build configuration: BL2 is undefined. ")
+endif ()
+
+if (NOT DEFINED TFM_PARTITION_AUDIT_LOG)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_AUDIT_LOG is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_PLATFORM)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_PLATFORM is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_PROTECTED_STORAGE)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_PROTECTED_STORAGE is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_INTERNAL_TRUSTED_STORAGE)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_INTERNAL_TRUSTED_STORAGE is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_CRYPTO)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_CRYPTO is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_INITIAL_ATTESTATION)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_INITIAL_ATTESTATION is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PSA_API)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PSA_API is undefined.")
+endif()
+
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/app ABSOLUTE)
+
+set(NS_APP_SRC "${CMSIS_DIR}/RTOS2/RTX/Config/RTX_Config.c"
+ "${CMSIS_DIR}/RTOS2/RTX/Source/rtx_lib.c"
+ "${APP_DIR}/main_ns.c"
+ "${APP_DIR}/tfm_integ_test.c"
+ "${APP_DIR}/os_wrapper_cmsis_rtos_v2.c"
+ "${TFM_ROOT_DIR}/interface/src/log/tfm_log_raw.c"
+ )
+
+if (NOT DEFINED TFM_MULTI_CORE_TOPOLOGY OR NOT TFM_MULTI_CORE_TOPOLOGY)
+ list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_ns_interface.c")
+endif()
+
+if (TFM_PARTITION_AUDIT_LOG)
+ if (TFM_PSA_API)
+ message(FATAL_ERROR "Audit log has not been supported in IPC model yet.")
+ else()
+ list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_audit_func_api.c")
+ endif()
+endif()
+
+if (TFM_PARTITION_PLATFORM)
+ if (TFM_PSA_API)
+ list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_platform_ipc_api.c")
+ else()
+ list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_platform_func_api.c")
+ endif()
+endif()
+
+if (TFM_PARTITION_PROTECTED_STORAGE)
+ if (TFM_PSA_API)
+ list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_ps_ipc_api.c")
+ else()
+ list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_ps_func_api.c")
+ endif()
+endif()
+
+if (TFM_PARTITION_INTERNAL_TRUSTED_STORAGE)
+ if (TFM_PSA_API)
+ list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_its_ipc_api.c")
+ else()
+ list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_its_func_api.c")
+ endif()
+endif()
+
+if (TFM_PARTITION_CRYPTO)
+ if (TFM_PSA_API)
+ list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_crypto_ipc_api.c")
+ else()
+ list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_crypto_func_api.c")
+ endif()
+endif()
+
+if (TFM_PARTITION_INITIAL_ATTESTATION)
+ if (TFM_PSA_API)
+ list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_initial_attestation_ipc_api.c")
+ else()
+ list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_initial_attestation_func_api.c")
+ endif()
+endif()
+
+if (NOT DEFINED TFM_NS_CLIENT_IDENTIFICATION)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_NS_CLIENT_IDENTIFICATION is undefined.")
+elseif (TFM_NS_CLIENT_IDENTIFICATION)
+ list(APPEND NS_APP_SRC
+ "${INTERFACE_DIR}/src/tfm_nspm_svc_handler.c"
+ "${INTERFACE_DIR}/src/tfm_nspm_api.c"
+ )
+endif()
+
+if (PSA_API_TEST_NS)
+ list(APPEND NS_APP_SRC "${APP_DIR}/psa_api_test.c")
+endif()
+
+if (TFM_PSA_API)
+ if (DEFINED TFM_MULTI_CORE_TOPOLOGY AND TFM_MULTI_CORE_TOPOLOGY)
+ list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_ns_mailbox.c"
+ "${INTERFACE_DIR}/src/tfm_multi_core_api.c"
+ "${INTERFACE_DIR}/src/tfm_multi_core_psa_ns_api.c"
+ )
+
+ if (TFM_MULTI_CORE_TEST)
+ add_definitions(-DTFM_MULTI_CORE_TEST)
+ endif()
+ else()
+ list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_psa_ns_api.c")
+ endif()
+endif()
+
+set(BUILD_CMSIS_CORE On)
+set(BUILD_RETARGET On)
+set(BUILD_NATIVE_DRIVERS On)
+set(BUILD_TIME On)
+set(BUILD_STARTUP On)
+set(BUILD_TARGET_CFG Off)
+set(BUILD_TARGET_HARDWARE_KEYS Off)
+set(BUILD_TARGET_NV_COUNTERS Off)
+set(BUILD_CMSIS_DRIVERS On)
+set(BUILD_UART_STDOUT On)
+set(BUILD_FLASH Off)
+if(CORE_TEST_POSITIVE)
+ set(BUILD_PLAT_TEST On)
+ set(BUILD_TIME On)
+else()
+ set(BUILD_PLAT_TEST Off)
+ set(BUILD_TIME Off)
+endif()
+if(NOT DEFINED PLATFORM_CMAKE_FILE)
+ message (FATAL_ERROR "Platform specific CMake is not defined. Please set PLATFORM_CMAKE_FILE.")
+elseif(NOT EXISTS ${PLATFORM_CMAKE_FILE})
+ message (FATAL_ERROR "Platform specific CMake \"${PLATFORM_CMAKE_FILE}\" file does not exist. Please fix value of PLATFORM_CMAKE_FILE.")
+else()
+ include(${PLATFORM_CMAKE_FILE})
+endif()
+
+if(NOT DEFINED NS_SCATTER_FILE_NAME)
+ message(FATAL_ERROR "ERROR: Incomplete Configuration: NS_SCATTER_FILE_NAME not defined, Include this file from a Config*.cmake")
+endif()
+embedded_set_target_linker_file(TARGET ${PROJECT_NAME} PATH "${NS_SCATTER_FILE_NAME}")
+
+#Create an object library to avoid compiling all source files twice, when two executables
+#with different memory map need to be linked(BL2 non-swapping)
+set(PROJECT_OBJ_LIB ${PROJECT_NAME}_obj_lib)
+add_library(${PROJECT_OBJ_LIB} OBJECT ${ALL_SRC_C} ${ALL_SRC_C_NS} ${ALL_SRC_ASM} ${ALL_SRC_ASM_NS} ${NS_APP_SRC})
+
+#Set common compiler flags
+config_setting_shared_compiler_flags(${PROJECT_OBJ_LIB})
+
+#Set macro definitions
+set(TARGET_COMPILE_DEFINITIONS __thumb2__ __DOMAIN_NS=1 DOMAIN_NS=__DOMAIN_NS)
+target_compile_definitions(${PROJECT_OBJ_LIB} PRIVATE ${TARGET_COMPILE_DEFINITIONS})
+
+#Set include directories.
+embedded_target_include_directories(TARGET ${PROJECT_OBJ_LIB} PATH ${TEST_INTERFACE_DIR}/include ABSOLUTE APPEND)
+embedded_target_include_directories(TARGET ${PROJECT_OBJ_LIB} PATH ${INTERFACE_DIR}/include ABSOLUTE APPEND)
+embedded_target_include_directories(TARGET ${PROJECT_OBJ_LIB} PATH ${TFM_ROOT_DIR} ABSOLUTE APPEND)
+embedded_target_include_directories(TARGET ${PROJECT_OBJ_LIB} PATH ${TFM_ROOT_DIR}/secure_fw/spm ABSOLUTE APPEND)
+embedded_target_include_directories(TARGET ${PROJECT_OBJ_LIB} PATH ${CMSIS_DIR}/RTOS2/RTX/Include ABSOLUTE APPEND)
+embedded_target_include_directories(TARGET ${PROJECT_OBJ_LIB} PATH ${CMSIS_DIR}/RTOS2/Include ABSOLUTE APPEND)
+embedded_target_include_directories(TARGET ${PROJECT_OBJ_LIB} PATH ${CMSIS_DIR}/RTOS2/RTX/Config ABSOLUTE APPEND)
+
+if (NOT DEFINED TFM_NS_CLIENT_IDENTIFICATION)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_NS_CLIENT_IDENTIFICATION is undefined.")
+elseif (TFM_NS_CLIENT_IDENTIFICATION)
+ target_compile_definitions(${PROJECT_OBJ_LIB} PRIVATE TFM_NS_CLIENT_IDENTIFICATION)
+endif()
+
+add_subdirectory(${TEST_DIR} ${CMAKE_BINARY_DIR}/test/non_secure_test)
+
+# For the non-swapping BL2 configuration two executables need to be built.
+# One can be executed from the primary slot flash partition and other from the
+# secondary slot. Only the linking phase is different. This function captures
+# common settings and eliminates copy-paste.
+function(set_up_app_build)
+ set( _OPTIONS_ARGS) #Option (on/off) arguments (e.g. IGNORE_CASE)
+ set( _ONE_VALUE_ARGS NS_TARGET S_TARGET FULL_BIN SIGN_BIN VENEER_NAME POSTFIX) #Single option arguments (e.g. PATH "./foo/bar")
+ set( _MULTI_VALUE_ARGS LINK_DEFINES) #List arguments (e.g. LANGUAGES C ASM CXX)
+ cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+ if (NOT DEFINED _MY_PARAMS_NS_TARGET)
+ message(FATAL_ERROR "set_up_app_build(): mandatory parameter 'NS_TARGET' missing.")
+ endif()
+
+ if (NOT DEFINED _MY_PARAMS_S_TARGET)
+ message(FATAL_ERROR "set_up_app_build(): mandatory parameter 'S_TARGET' missing.")
+ endif()
+
+ if (NOT DEFINED _MY_PARAMS_FULL_BIN)
+ message(FATAL_ERROR "set_up_app_build(): mandatory parameter 'FULL_BIN' missing.")
+ endif()
+
+ if (NOT DEFINED _MY_PARAMS_SIGN_BIN)
+ message(FATAL_ERROR "set_up_app_build(): mandatory parameter 'SIGN_BIN' missing.")
+ endif()
+
+ if (NOT DEFINED _MY_PARAMS_VENEER_NAME)
+ message(FATAL_ERROR "set_up_app_build(): mandatory parameter 'VENEER_NAME' missing.")
+ endif()
+
+ set(EXE_NAME ${_MY_PARAMS_NS_TARGET}${_MY_PARAMS_POSTFIX})
+ set(S_BIN ${_MY_PARAMS_S_TARGET}${_MY_PARAMS_POSTFIX})
+ set(FULL_NAME ${_MY_PARAMS_FULL_BIN}${_MY_PARAMS_POSTFIX})
+ set(SIGN_NAME ${_MY_PARAMS_SIGN_BIN}${_MY_PARAMS_POSTFIX})
+ set(VENEER_NAME ${_MY_PARAMS_VENEER_NAME}${_MY_PARAMS_POSTFIX}.o)
+
+ #Create linker target: add object library to executable
+ add_executable(${EXE_NAME} $<TARGET_OBJECTS:${PROJECT_OBJ_LIB}>)
+
+ #Set common linker flags
+ config_setting_shared_linker_flags(${EXE_NAME})
+
+ #Set individual linker flags per linker target/executable
+ foreach(flag ${_MY_PARAMS_LINK_DEFINES})
+ embedded_set_target_link_defines(TARGET ${EXE_NAME} DEFINES "${flag}")
+ endforeach(flag)
+
+ embedded_set_target_linker_file(TARGET ${EXE_NAME} PATH "${NS_SCATTER_FILE_NAME}")
+
+ #Add the RTX library
+ if(NOT DEFINED RTX_LIB_PATH)
+ message(FATAL_ERROR "ERROR: Incomplete Configuration: RTX_LIB_PATH is not defined.")
+ endif()
+
+ #Add the PSA API compliance test libraries
+ if(PSA_API_TEST_NS)
+ target_link_libraries(${EXE_NAME} "${PSA_API_TEST_BUILD_PATH}/val/val_nspe.a")
+ target_link_libraries(${EXE_NAME} "${PSA_API_TEST_BUILD_PATH}/platform/pal_nspe.a")
+ endif()
+ if(PSA_API_TEST_NS AND (PSA_API_TEST_INTERNAL_TRUSTED_STORAGE OR PSA_API_TEST_PROTECTED_STORAGE OR PSA_API_TEST_STORAGE))
+ target_link_libraries(${EXE_NAME} "${PSA_API_TEST_BUILD_PATH}/dev_apis/storage/test_combine.a")
+ endif()
+ if(PSA_API_TEST_NS AND PSA_API_TEST_CRYPTO)
+ target_link_libraries(${EXE_NAME} "${PSA_API_TEST_BUILD_PATH}/dev_apis/crypto/test_combine.a")
+ endif()
+ if(PSA_API_TEST_NS AND PSA_API_TEST_INITIAL_ATTESTATION)
+ target_link_libraries(${EXE_NAME} "${PSA_API_TEST_BUILD_PATH}/dev_apis/initial_attestation/test_combine.a")
+ endif()
+ if(PSA_API_TEST_NS AND PSA_API_TEST_IPC)
+ target_link_libraries(${EXE_NAME} "${PSA_API_TEST_BUILD_PATH}/ff/ipc/test_combine.a")
+ endif()
+
+ if(NOT DEFINED PLATFORM_LINK_INCLUDES)
+ message(FATAL_ERROR "ERROR: Incomplete Configuration: PLATFORM_LINK_INCLUDES is not defined.")
+ endif()
+ embedded_set_target_link_includes(TARGET ${EXE_NAME} INCLUDES "${PLATFORM_LINK_INCLUDES}")
+
+ #Generate binary file from axf
+ compiler_generate_binary_output(${EXE_NAME})
+
+ #Generate intel hex file from axf
+ compiler_generate_hex_output(${EXE_NAME})
+
+ #Generate elf file from axf
+ compiler_generate_elf_output(${EXE_NAME})
+
+ #Generate MCUBoot compatible payload
+ if (BL2)
+ mcuboot_create_boot_payload(S_BIN ${S_BIN}
+ NS_BIN ${EXE_NAME}
+ FULL_BIN ${FULL_NAME}
+ SIGN_BIN ${SIGN_NAME}
+ POSTFIX ${_MY_PARAMS_POSTFIX})
+ endif()
+
+ if (NOT DEFINED TFM_PARTITION_TEST_CORE)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_TEST_CORE is undefined. ")
+ elseif (TFM_PARTITION_TEST_CORE)
+ embedded_set_target_link_defines(TARGET ${EXE_NAME} DEFINES "TFM_PARTITION_TEST_CORE")
+ endif()
+
+ if (NOT DEFINED TFM_PARTITION_TEST_CORE_IPC)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_TEST_CORE_IPC is undefined.")
+ elseif (TFM_PARTITION_TEST_CORE_IPC)
+ embedded_set_target_link_defines(TARGET ${EXE_NAME} DEFINES "TFM_PARTITION_TEST_CORE_IPC")
+ endif()
+
+ if (NOT DEFINED TFM_PARTITION_TEST_SECURE_SERVICES)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_TEST_SECURE_SERVICES is undefined. ")
+ elseif (TFM_PARTITION_TEST_SECURE_SERVICES)
+ embedded_set_target_link_defines(TARGET ${EXE_NAME} DEFINES "TFM_PARTITION_TEST_SECURE_SERVICES")
+ endif()
+
+ if (NOT DEFINED TEST_FRAMEWORK_S)
+ message(FATAL_ERROR "Incomplete build configuration: TEST_FRAMEWORK_S is undefined.")
+ elseif (TEST_FRAMEWORK_S)
+ embedded_set_target_link_defines(TARGET ${EXE_NAME} DEFINES "TEST_FRAMEWORK_S")
+ endif()
+
+ if (NOT DEFINED TEST_FRAMEWORK_NS)
+ message(FATAL_ERROR "Incomplete build configuration: TEST_FRAMEWORK_NS is undefined.")
+ elseif (TEST_FRAMEWORK_NS)
+ embedded_set_target_link_defines(TARGET ${EXE_NAME} DEFINES "TEST_FRAMEWORK_NS")
+ endif()
+
+ #Set BL2 specific settings.
+ if (BL2)
+ #Add BL2 and MCUBOOT_IMAGE_NUMBER defines to linker to resolve symbols in region_defs.h and flash_layout.h
+ embedded_set_target_link_defines(TARGET ${EXE_NAME} DEFINES "BL2" "MCUBOOT_IMAGE_NUMBER=${MCUBOOT_IMAGE_NUMBER}")
+ endif()
+
+ #We depend on the non secure tests. See if the library target is available.
+ if(TARGET tfm_non_secure_tests)
+ #If yes, then use the library.
+ target_link_libraries(${EXE_NAME} tfm_non_secure_tests)
+ #Ensure library is built first.
+ add_dependencies(${EXE_NAME} tfm_non_secure_tests)
+ endif()
+
+ target_link_libraries(${EXE_NAME} "${RTX_LIB_PATH}")
+
+ #Ensure secure_fw is built before our executable.
+ add_dependencies(${EXE_NAME} ${S_BIN})
+
+ if (NOT DEFINED TFM_MULTI_CORE_TOPOLOGY OR NOT TFM_MULTI_CORE_TOPOLOGY)
+ if (NOT DEFINED S_VENEER_PATH)
+ if (EXISTS ${CMAKE_CURRENT_BINARY_DIR}/../secure_fw)
+ set (S_VENEER_PATH "${CMAKE_CURRENT_BINARY_DIR}/../secure_fw")
+ else()
+ message(FATAL_ERROR "No valid path for S_VENEER_PATH, secure_fw is built?")
+ endif()
+ endif()
+
+ #Add the veneers to the executable.
+ set(S_VENEER_FILE "${S_VENEER_PATH}/${VENEER_NAME}")
+ set_property(TARGET ${EXE_NAME} APPEND PROPERTY LINK_LIBRARIES ${S_VENEER_FILE})
+ endif()
+
+ #Collect executables to common location: build/install/outputs/
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.axf
+ ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.bin
+ ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.hex
+ ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.elf
+ DESTINATION outputs/${TARGET_PLATFORM}/)
+
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.axf
+ ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.bin
+ ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.hex
+ ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.elf
+ DESTINATION outputs/fvp/)
+endfunction()
+
+if (LINK_TO_BOTH_MEMORY_REGION)
+ #Link to primary memory region
+ set_up_app_build(NS_TARGET ${PROJECT_NAME}
+ S_TARGET tfm_s
+ FULL_BIN tfm_full
+ SIGN_BIN tfm_sign
+ VENEER_NAME s_veneers)
+
+ #Link to secondary memory region(add extra linker flag)
+ set_up_app_build(NS_TARGET ${PROJECT_NAME}
+ LINK_DEFINES "LINK_TO_SECONDARY_PARTITION"
+ S_TARGET tfm_s
+ FULL_BIN tfm_full
+ SIGN_BIN tfm_sign
+ VENEER_NAME s_veneers
+ POSTFIX "_1")
+else()
+ #Link to primary memory region only
+ set_up_app_build(NS_TARGET ${PROJECT_NAME}
+ S_TARGET tfm_s
+ FULL_BIN tfm_full
+ SIGN_BIN tfm_sign
+ VENEER_NAME s_veneers)
+endif()
+
+#If the tfm_non_secure_tests target is not available
+if(NOT TARGET tfm_non_secure_tests)
+ #Add the test source to the build.
+ #As of today since secure_fw is built as a sub-project this code will never execute.
+ option(ENABLE_PROTECTED_STORAGE_SERVICE_TESTS "" TRUE)
+ include(${TEST_DIR}/CMakeLists.inc)
+ target_sources(${PROJECT_OBJ_LIB} PUBLIC ${ALL_SRC_C} ${ALL_SRC_C_NS})
+endif()
+
+#Finally let CMake system apply changes after the whole project is defined.
+if (TARGET ${PROJECT_NAME})
+ embedded_project_end(${PROJECT_NAME})
+endif()
+
+if (TARGET ${PROJECT_NAME}_1)
+ embedded_project_end(${PROJECT_NAME}_1)
+endif()
+
+embedded_project_end(${PROJECT_OBJ_LIB})
diff --git a/app/main_ns.c b/app/main_ns.c
new file mode 100644
index 0000000..fc874bd
--- /dev/null
+++ b/app/main_ns.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_api.h"
+#include "cmsis_os2.h"
+#include "tfm_integ_test.h"
+#include "tfm_ns_svc.h"
+#include "tfm_ns_interface.h"
+#ifdef TEST_FRAMEWORK_NS
+#include "test/framework/test_framework_integ_test.h"
+#endif
+#ifdef PSA_API_TEST_NS
+#include "psa_api_test.h"
+#endif
+#include "target_cfg.h"
+#include "tfm_plat_ns.h"
+#include "Driver_USART.h"
+#include "device_cfg.h"
+#ifdef TFM_MULTI_CORE_TOPOLOGY
+#include "tfm_multi_core_api.h"
+#include "tfm_ns_mailbox.h"
+#endif
+#include "log/tfm_assert.h"
+#include "log/tfm_log.h"
+#include "uart_stdout.h"
+#include "region.h"
+
+/**
+ * \brief Modified table template for user defined SVC functions
+ *
+ * \details RTX has a weak definition of osRtxUserSVC, which
+ * is overridden here
+ */
+#if (defined(__ARMCC_VERSION) && (__ARMCC_VERSION == 6110004))
+/* Workaround needed for a bug in Armclang 6.11, more details at:
+ * http://www.keil.com/support/docs/4089.htm
+ */
+__attribute__((section(".gnu.linkonce")))
+#endif
+extern void * const osRtxUserSVC[1+USER_SVC_COUNT];
+ void * const osRtxUserSVC[1+USER_SVC_COUNT] = {
+ (void *)USER_SVC_COUNT,
+
+#define X(SVC_ENUM, SVC_HANDLER) (void*)SVC_HANDLER,
+
+ /* SVC API for Services */
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+ LIST_SVC_NSPM
+#endif
+
+#undef X
+
+/*
+ * (void *)user_function1,
+ * ...
+ */
+};
+
+/**
+ * \brief List of RTOS thread attributes
+ */
+#if defined(TEST_FRAMEWORK_NS) || defined(PSA_API_TEST_NS)
+static uint64_t test_app_stack[(4u * 1024u) / (sizeof(uint64_t))]; /* 4KB */
+static const osThreadAttr_t thread_attr = {
+ .name = "test_thread",
+ .stack_mem = test_app_stack,
+ .stack_size = sizeof(test_app_stack),
+};
+#endif
+
+/**
+ * \brief Static globals to hold RTOS related quantities,
+ * main thread
+ */
+#if defined(TEST_FRAMEWORK_NS) || defined(PSA_API_TEST_NS)
+static osThreadFunc_t thread_func;
+#endif
+
+#ifdef TFM_MULTI_CORE_TOPOLOGY
+static struct ns_mailbox_queue_t ns_mailbox_queue;
+
+static void tfm_ns_multi_core_boot(void)
+{
+ int32_t ret;
+
+ LOG_MSG("Non-secure code running on non-secure core.");
+
+ if (tfm_ns_wait_for_s_cpu_ready()) {
+ LOG_MSG("Error sync'ing with secure core.");
+
+ /* Avoid undefined behavior after multi-core sync-up failed */
+ for (;;) {
+ }
+ }
+
+ ret = tfm_ns_mailbox_init(&ns_mailbox_queue);
+ if (ret != MAILBOX_SUCCESS) {
+ LOG_MSG("Non-secure mailbox initialization failed.");
+
+ /* Avoid undefined behavior after NS mailbox initialization failed */
+ for (;;) {
+ }
+ }
+}
+#endif
+
+/**
+ * \brief Platform peripherals and devices initialization.
+ * Can be overridden for platform specific initialization.
+ *
+ * \return ARM_DRIVER_OK if the initialization succeeds
+ */
+__WEAK int32_t tfm_ns_platform_init(void)
+{
+ stdio_init();
+
+ return ARM_DRIVER_OK;
+}
+
+/**
+ * \brief Platform peripherals and devices de-initialization.
+ * Can be overridden for platform specific initialization.
+ *
+ * \return ARM_DRIVER_OK if the de-initialization succeeds
+ */
+__WEAK int32_t tfm_ns_platform_uninit(void)
+{
+ stdio_uninit();
+
+ return ARM_DRIVER_OK;
+}
+
+/**
+ * \brief main() function
+ */
+#ifndef __GNUC__
+__attribute__((noreturn))
+#endif
+int main(void)
+{
+#if defined(__ARM_ARCH_8_1M_MAIN__) || defined(__ARM_ARCH_8M_MAIN__)
+ /* Set Main Stack Pointer limit */
+ REGION_DECLARE(Image$$, ARM_LIB_STACK_MSP, $$ZI$$Base);
+ __set_MSPLIM((uint32_t)®ION_NAME(Image$$, ARM_LIB_STACK_MSP,
+ $$ZI$$Base));
+#endif
+
+ if (tfm_ns_platform_init() != ARM_DRIVER_OK) {
+ /* Avoid undefined behavior if platform init failed */
+ while(1);
+ }
+
+#ifdef TFM_MULTI_CORE_TOPOLOGY
+ tfm_ns_multi_core_boot();
+#endif
+
+ (void) osKernelInitialize();
+
+ /* Initialize the TFM NS interface */
+ tfm_ns_interface_init();
+
+#if defined(TEST_FRAMEWORK_NS)
+ thread_func = test_app;
+#elif defined(PSA_API_TEST_NS)
+ thread_func = psa_api_test;
+#endif
+
+#if defined(TEST_FRAMEWORK_NS) || defined(PSA_API_TEST_NS)
+ (void) osThreadNew(thread_func, NULL, &thread_attr);
+#endif
+
+ LOG_MSG("Non-Secure system starting...\r\n");
+ (void) osKernelStart();
+
+ /* Reached only in case of error */
+ for (;;) {
+ }
+}
diff --git a/app/os_wrapper_cmsis_rtos_v2.c b/app/os_wrapper_cmsis_rtos_v2.c
new file mode 100644
index 0000000..9892290
--- /dev/null
+++ b/app/os_wrapper_cmsis_rtos_v2.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "os_wrapper/thread.h"
+#include "os_wrapper/mutex.h"
+#include "os_wrapper/semaphore.h"
+
+#include <string.h>
+#include "cmsis_os2.h"
+
+/* This is an example OS abstraction layer for CMSIS-RTOSv2 */
+
+void *os_wrapper_thread_new(const char *name, int32_t stack_size,
+ os_wrapper_thread_func func, void *arg,
+ uint32_t priority)
+{
+ osThreadAttr_t task_attribs = {.tz_module = 1};
+
+ /* By default, the thread starts as osThreadDetached */
+ if (stack_size != OS_WRAPPER_DEFAULT_STACK_SIZE) {
+ task_attribs.stack_size = stack_size;
+ }
+ task_attribs.name = name;
+ task_attribs.priority = (osPriority_t) priority;
+
+ return (void *)osThreadNew(func, arg, &task_attribs);
+}
+
+void *os_wrapper_semaphore_create(uint32_t max_count, uint32_t initial_count,
+ const char *name)
+{
+ osSemaphoreAttr_t sema_attrib = {0};
+
+ sema_attrib.name = name;
+
+ return (void *)osSemaphoreNew(max_count, initial_count, &sema_attrib);
+}
+
+uint32_t os_wrapper_semaphore_acquire(void *handle, uint32_t timeout)
+{
+ osStatus_t status;
+
+ status = osSemaphoreAcquire((osSemaphoreId_t)handle,
+ (timeout == OS_WRAPPER_WAIT_FOREVER) ?
+ osWaitForever : timeout);
+ if (status != osOK) {
+ return OS_WRAPPER_ERROR;
+ }
+
+ return OS_WRAPPER_SUCCESS;
+}
+
+uint32_t os_wrapper_semaphore_release(void *handle)
+{
+ osStatus_t status;
+
+ status = osSemaphoreRelease((osSemaphoreId_t)handle);
+ if (status != osOK) {
+ return OS_WRAPPER_ERROR;
+ }
+
+ return OS_WRAPPER_SUCCESS;
+}
+
+uint32_t os_wrapper_semaphore_delete(void *handle)
+{
+ osStatus_t status;
+
+ status = osSemaphoreDelete((osSemaphoreId_t)handle);
+ if (status != osOK) {
+ return OS_WRAPPER_ERROR;
+ }
+
+ return OS_WRAPPER_SUCCESS;
+}
+
+void *os_wrapper_mutex_create(void)
+{
+ const osMutexAttr_t attr = {
+ .name = NULL,
+ .attr_bits = osMutexPrioInherit, /* Priority inheritance is recommended
+ * to enable if it is supported.
+ * For recursive mutex and the ability
+ * of auto release when owner being
+ * terminated is not required.
+ */
+ .cb_mem = NULL,
+ .cb_size = 0U
+ };
+
+ return (void *)osMutexNew(&attr);
+}
+
+uint32_t os_wrapper_mutex_acquire(void *handle, uint32_t timeout)
+{
+ osStatus_t status = osOK;
+
+ if (!handle) {
+ return OS_WRAPPER_ERROR;
+ }
+
+ status = osMutexAcquire((osMutexId_t)handle,
+ (timeout == OS_WRAPPER_WAIT_FOREVER) ?
+ osWaitForever : timeout);
+ if (status != osOK) {
+ return OS_WRAPPER_ERROR;
+ }
+
+ return OS_WRAPPER_SUCCESS;
+}
+
+uint32_t os_wrapper_mutex_release(void *handle)
+{
+ osStatus_t status = osOK;
+
+ if (!handle) {
+ return OS_WRAPPER_ERROR;
+ }
+
+ status = osMutexRelease((osMutexId_t)handle);
+ if (status != osOK) {
+ return OS_WRAPPER_ERROR;
+ }
+
+ return OS_WRAPPER_SUCCESS;
+}
+
+uint32_t os_wrapper_mutex_delete(void *handle)
+{
+ osStatus_t status = osOK;
+
+ if (!handle) {
+ return OS_WRAPPER_ERROR;
+ }
+
+ status = osMutexDelete((osMutexId_t)handle);
+ if (status != osOK) {
+ return OS_WRAPPER_ERROR;
+ }
+
+ return OS_WRAPPER_SUCCESS;
+}
+
+void *os_wrapper_thread_get_handle(void)
+{
+ return (void *)osThreadGetId();
+}
+
+uint32_t os_wrapper_thread_get_priority(void *handle, uint32_t *priority)
+{
+ osPriority_t prio;
+
+ prio = osThreadGetPriority((osThreadId_t)handle);
+ if (prio == osPriorityError) {
+ return OS_WRAPPER_ERROR;
+ }
+
+ *priority = (uint32_t)prio;
+
+ return OS_WRAPPER_SUCCESS;
+}
+
+void os_wrapper_thread_exit(void)
+{
+ osThreadExit();
+}
+
+uint32_t os_wrapper_thread_set_flag(void *handle, uint32_t flags)
+{
+ uint32_t ret;
+
+ ret = osThreadFlagsSet((osThreadId_t)handle, flags);
+ if (ret & osFlagsError) {
+ return OS_WRAPPER_ERROR;
+ }
+
+ return OS_WRAPPER_SUCCESS;
+}
+
+/*
+ * According to the description of CMSIS-RTOS v2 Thread Flags,
+ * osThreadFlagsSet() can be called inside Interrupt Service Routine.
+ */
+uint32_t os_wrapper_thread_set_flag_isr(void *handle, uint32_t flags)
+{
+ uint32_t ret;
+
+ ret = osThreadFlagsSet((osThreadId_t)handle, flags);
+ if (ret & osFlagsError) {
+ return OS_WRAPPER_ERROR;
+ }
+
+ return OS_WRAPPER_SUCCESS;
+}
+
+uint32_t os_wrapper_thread_wait_flag(uint32_t flags, uint32_t timeout)
+{
+ uint32_t ret;
+
+ ret = osThreadFlagsWait(flags, osFlagsWaitAll,
+ (timeout == OS_WRAPPER_WAIT_FOREVER) ?
+ osWaitForever : timeout);
+ if (ret & osFlagsError) {
+ return OS_WRAPPER_ERROR;
+ }
+
+ return OS_WRAPPER_SUCCESS;
+}
+
+uint32_t os_wrapper_get_tick(void)
+{
+ return osKernelGetTickCount();
+}
diff --git a/app/psa_api_test.c b/app/psa_api_test.c
new file mode 100644
index 0000000..3a6cc53
--- /dev/null
+++ b/app/psa_api_test.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "psa_api_test.h"
+#include "tfm_nspm_api.h"
+#include "tfm_integ_test.h"
+
+/**
+ * \brief This symbol is the entry point provided by the PSA API compliance
+ * test libraries
+ */
+extern void val_entry(void);
+
+__attribute__((noreturn))
+void psa_api_test(void *arg)
+{
+ UNUSED_VARIABLE(arg);
+
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+ tfm_nspm_register_client_id();
+#endif /* TFM_NS_CLIENT_IDENTIFICATION */
+
+ val_entry();
+
+ for (;;) {
+ }
+}
diff --git a/app/psa_api_test.h b/app/psa_api_test.h
new file mode 100644
index 0000000..5423b15
--- /dev/null
+++ b/app/psa_api_test.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PSA_API_TEST_H__
+#define __PSA_API_TEST_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Main test application for the PSA API compliance tests
+ *
+ */
+void psa_api_test(void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_API_TEST_H__ */
diff --git a/app/tfm_integ_test.c b/app/tfm_integ_test.c
new file mode 100644
index 0000000..32cb674
--- /dev/null
+++ b/app/tfm_integ_test.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_integ_test.h"
+
+#ifdef TEST_FRAMEWORK_NS
+#include "test/framework/test_framework_integ_test.h"
+#endif
+
+#ifdef TEST_FRAMEWORK_S
+#include \
+ "test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.h"
+#endif
+
+#if defined(TEST_FRAMEWORK_NS) || defined(TEST_FRAMEWORK_S)
+/**
+ * \brief Services test thread
+ *
+ */
+__attribute__((noreturn))
+void test_app(void *argument)
+{
+ UNUSED_VARIABLE(argument);
+
+#ifdef TEST_FRAMEWORK_S
+ /* FIXME: The non-secure audit log test currently relies on the fact that
+ * the audit log secure test is run first. However the Non-secure tests
+ * represent simpler and more common test cases which would make more sense
+ * to be run first. Therefore if this dependency is removed the execution
+ * order of these test classes should be reversed. */
+ tfm_secure_client_run_tests();
+#endif
+#ifdef TEST_FRAMEWORK_NS
+ tfm_non_secure_client_run_tests();
+#endif
+ /* End of test */
+ for (;;) {
+ }
+}
+#endif /* TEST_FRAMEWORK_NS OR TEST_FRAMEWORK_S */
diff --git a/app/tfm_integ_test.h b/app/tfm_integ_test.h
new file mode 100644
index 0000000..778f2ae
--- /dev/null
+++ b/app/tfm_integ_test.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdio.h>
+#include "cmsis_compiler.h"
+
+#ifndef __TFM_INTEG_TEST_H__
+#define __TFM_INTEG_TEST_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Avoids the semihosting issue */
+#if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
+__asm(" .global __ARM_use_no_argv\n");
+#endif
+
+/**
+ * \brief Simple macro to mark UNUSED variables
+ *
+ */
+#define UNUSED_VARIABLE(X) ((void)(X))
+
+#ifdef TEST_FRAMEWORK_NS
+/**
+ * \brief Main test application for the RTX-TFM core
+ * integration tests
+ *
+ */
+void test_app(void *argument);
+#endif /* TEST_FRAMEWORK_NS */
+
+/**
+ * \brief Execute the interactive test cases (button push)
+ *
+ */
+void execute_ns_interactive_tests(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_INTEG_TEST_H__ */
diff --git a/test/CMakeLists.inc b/test/CMakeLists.inc
new file mode 100644
index 0000000..ac308f2
--- /dev/null
+++ b/test/CMakeLists.inc
@@ -0,0 +1,60 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+if(NOT DEFINED TFM_ROOT_DIR)
+ message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_AUDIT_LOGGING_SERVICE_TESTS)
+ message(FATAL_ERROR "Incomplete build configuration: ENABLE_AUDIT_LOGGING_SERVICE_TESTS is undefined.")
+endif()
+
+if (NOT DEFINED ENABLE_PLATFORM_SERVICE_TESTS)
+ message(FATAL_ERROR "Incomplete build configuration: ENABLE_PLATFORM_SERVICE_TESTS is undefined.")
+endif()
+
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/secure_fw/spm ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/bl2/include ABSOLUTE)
+
+# Include the test framework
+include(${CMAKE_CURRENT_LIST_DIR}/framework/CMakeLists.inc)
+
+# Include the test suites
+include(${CMAKE_CURRENT_LIST_DIR}/suites/core/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/ps/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/its/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/crypto/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/attestation/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/qcbor/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/t_cose/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/ipc/CMakeLists.inc)
+if (ENABLE_AUDIT_LOGGING_SERVICE_TESTS)
+ include(${CMAKE_CURRENT_LIST_DIR}/suites/audit/CMakeLists.inc)
+endif()
+if (ENABLE_PLATFORM_SERVICE_TESTS)
+ include(${CMAKE_CURRENT_LIST_DIR}/suites/platform/CMakeLists.inc)
+endif()
+if (TFM_MULTI_CORE_TEST)
+ include(${CMAKE_CURRENT_LIST_DIR}/suites/multi_core/CMakeLists.inc)
+endif()
+
+# Include the test partitions
+include(${CMAKE_CURRENT_LIST_DIR}/test_services/CMakeLists.inc)
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000..50a7b17
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,221 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+cmake_minimum_required(VERSION 3.7)
+
+#Tell cmake where our modules can be found
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../cmake)
+
+#Tell TFM Root before calling sub cmake
+get_filename_component(TFM_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
+
+#Include common stuff to control cmake.
+include("Common/BuildSys")
+
+#Start an embedded project.
+embedded_project_start(CONFIG "${TFM_ROOT_DIR}/configs/ConfigDefault.cmake")
+project(tfm_tests LANGUAGES ASM C)
+embedded_project_fixup()
+
+#Check incoming configuration options
+if (NOT DEFINED SERVICES_TEST_ENABLED)
+ message(FATAL_ERROR "Incomplete build configuration: SERVICES_TEST_ENABLED is undefined. ")
+endif()
+
+if (NOT DEFINED CORE_TEST_INTERACTIVE)
+ message(FATAL_ERROR "Incomplete build configuration: CORE_TEST_INTERACTIVE is undefined. ")
+endif()
+
+if (NOT DEFINED CORE_TEST_POSITIVE)
+ message(FATAL_ERROR "Incomplete build configuration: CORE_TEST_POSITIVE is undefined. ")
+endif()
+
+if (NOT DEFINED TFM_LVL)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_LVL is undefined. ")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_AUDIT_LOG)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_AUDIT_LOG is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_PROTECTED_STORAGE)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_PROTECTED_STORAGE is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_INTERNAL_TRUSTED_STORAGE)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_INTERNAL_TRUSTED_STORAGE is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_CRYPTO)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_CRYPTO is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_INITIAL_ATTESTATION)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_INITIAL_ATTESTATION is undefined.")
+endif()
+
+if (NOT DEFINED TFM_ENABLE_IRQ_TEST)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_ENABLE_IRQ_TEST is undefined.")
+endif()
+
+#Configure our options as needed.
+if (CORE_TEST_INTERACTIVE OR CORE_TEST_POSITIVE)
+ set(ENABLE_CORE_TESTS True)
+ set(ENABLE_CORE_TESTS_2 True)
+else()
+ set(ENABLE_CORE_TESTS False)
+ set(ENABLE_CORE_TESTS_2 False)
+endif()
+
+if (TFM_ENABLE_IRQ_TEST)
+ set(ENABLE_IRQ_TEST_SERVICES ON)
+else()
+ set(ENABLE_IRQ_TEST_SERVICES OFF)
+endif()
+
+if (ENABLE_CORE_TESTS)
+ # If the platform doesn't specify whether the peripheral test is enabled
+ # or not, select it by default.
+ if (NOT DEFINED TFM_ENABLE_PERIPH_ACCESS_TEST)
+ set(TFM_ENABLE_PERIPH_ACCESS_TEST TRUE)
+ endif()
+
+ if (TFM_ENABLE_PERIPH_ACCESS_TEST)
+ add_definitions(-DTFM_ENABLE_PERIPH_ACCESS_TEST)
+ endif()
+else()
+ set(TFM_ENABLE_PERIPH_ACCESS_TEST FALSE)
+endif()
+
+include(${CMAKE_CURRENT_LIST_DIR}/TestConfig.cmake)
+include(${CMAKE_CURRENT_LIST_DIR}/CMakeLists.inc)
+
+if (ENABLE_PROTECTED_STORAGE_SERVICE_TESTS)
+ embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES ENABLE_PROTECTED_STORAGE_SERVICE_TESTS APPEND)
+ embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_PROTECTED_STORAGE_SERVICE_TESTS APPEND)
+endif()
+
+if (ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS)
+ embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS APPEND)
+ embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS APPEND)
+endif()
+
+if (ENABLE_CRYPTO_SERVICE_TESTS)
+ embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES ENABLE_CRYPTO_SERVICE_TESTS APPEND)
+ embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_CRYPTO_SERVICE_TESTS APPEND)
+endif()
+
+if (ENABLE_ATTESTATION_SERVICE_TESTS)
+ embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES ENABLE_ATTESTATION_SERVICE_TESTS APPEND)
+ embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_ATTESTATION_SERVICE_TESTS APPEND)
+endif()
+
+if (ENABLE_PLATFORM_SERVICE_TESTS)
+ embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES ENABLE_PLATFORM_SERVICE_TESTS APPEND)
+ embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_PLATFORM_SERVICE_TESTS APPEND)
+endif()
+
+if (ENABLE_QCBOR_TESTS)
+ embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES ENABLE_QCBOR_TESTS APPEND)
+ embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_QCBOR_TESTS APPEND)
+endif()
+
+if (ENABLE_T_COSE_TESTS)
+ embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_T_COSE_TESTS APPEND)
+endif()
+
+if (ENABLE_AUDIT_LOGGING_SERVICE_TESTS)
+ embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES ENABLE_AUDIT_LOGGING_SERVICE_TESTS APPEND)
+ embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_AUDIT_LOGGING_SERVICE_TESTS APPEND)
+endif()
+
+if (TFM_MULTI_CORE_TEST)
+ embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES TFM_MULTI_CORE_TEST APPEND)
+endif()
+
+if (NOT DEFINED TFM_BUILD_IN_SPE)
+ message(FATAL_ERROR "TFM_BUILD_IN_SPE is not set. Cannot specify current building status")
+endif()
+
+if (NOT TARGET tfm_t_cose_verify AND (ENABLE_ATTESTATION_SERVICE_TESTS OR ENABLE_T_COSE_TESTS))
+ add_subdirectory(${TFM_ROOT_DIR}/lib/ext/t_cose ${CMAKE_CURRENT_BINARY_DIR}/t_cose)
+endif()
+
+if ((NOT TARGET tfm_qcbor_encode OR NOT TARGET tfm_qcbor_decode) AND (ENABLE_ATTESTATION_SERVICE_TESTS OR ENABLE_QCBOR_TESTS OR ENABLE_T_COSE_TESTS))
+ add_subdirectory(${TFM_ROOT_DIR}/lib/ext/qcbor ${CMAKE_CURRENT_BINARY_DIR}/qcbor)
+endif()
+
+if (TFM_BUILD_IN_SPE)
+ #Build the secure library. Even though secure tests files depend on
+ #tfm_qcbor, this is not expressed here as the tfm_attest library is expected
+ #to hold the compiled tfm_qcbor files.
+ add_library(tfm_secure_tests STATIC ${ALL_SRC_C} ${ALL_SRC_C_S})
+ if (ENABLE_ATTESTATION_SERVICE_TESTS)
+ target_sources(tfm_secure_tests PRIVATE $<TARGET_OBJECTS:tfm_t_cose_verify> $<TARGET_OBJECTS:tfm_qcbor_decode>)
+ endif()
+
+ #Set common compiler and linker flags
+ if (DEFINED CMSE_FLAGS)
+ embedded_set_target_compile_flags(TARGET tfm_secure_tests LANGUAGE C APPEND FLAGS ${CMSE_FLAGS})
+ endif()
+ config_setting_shared_compiler_flags(tfm_secure_tests)
+ config_setting_shared_linker_flags(tfm_secure_tests)
+
+ embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES __thumb2__ TFM_LVL=${TFM_LVL} APPEND)
+
+ if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE
+ PATH "Default install location for tfm_storage."
+ FORCE)
+ endif()
+
+ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/framework/test_framework_integ_test.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_services/tfm_secure_client_service/tfm_secure_client_service_api.h
+ DESTINATION export/tfm/test/inc)
+
+ embedded_project_end(tfm_secure_tests)
+else ()
+ #Build the non-secure library
+ set(CMAKE_STATIC_LIBRARY_PREFIX_C "lib")
+ add_library(tfm_non_secure_tests STATIC ${ALL_SRC_C} ${ALL_SRC_C_NS})
+
+ embedded_target_include_directories(TARGET tfm_non_secure_tests PATH ${CMSIS_DIR}/RTOS2/Include ABSOLUTE APPEND)
+
+ if (ENABLE_ATTESTATION_SERVICE_TESTS OR ENABLE_T_COSE_TESTS)
+ target_sources(tfm_non_secure_tests PRIVATE $<TARGET_OBJECTS:tfm_t_cose_verify> PRIVATE $<TARGET_OBJECTS:tfm_t_cose_sign>)
+ endif()
+ if (ENABLE_ATTESTATION_SERVICE_TESTS OR ENABLE_QCBOR_TESTS OR ENABLE_T_COSE_TESTS)
+ target_sources(tfm_non_secure_tests PRIVATE $<TARGET_OBJECTS:tfm_qcbor_decode> PRIVATE $<TARGET_OBJECTS:tfm_qcbor_encode>)
+ endif()
+
+ #Set common compiler and linker flags
+ config_setting_shared_compiler_flags(tfm_non_secure_tests)
+ config_setting_shared_linker_flags(tfm_non_secure_tests)
+
+ #Set macro definitions
+ set(TARGET_COMPILE_DEFINITIONS __thumb2__ __DOMAIN_NS=1 DOMAIN_NS=__DOMAIN_NS TFM_LVL=${TFM_LVL})
+ embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ${TARGET_COMPILE_DEFINITIONS} APPEND)
+
+ if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE
+ PATH "Default install location for tfm_storage."
+ FORCE)
+ endif()
+
+ install(TARGETS tfm_non_secure_tests
+ DESTINATION export/tfm/test/lib
+ PUBLIC_HEADER DESTINATION export/tfm/test/inc)
+
+ if(ENABLE_PROTECTED_STORAGE_SERVICE_TESTS)
+ #only PS tests are using semaphore and thread APIs
+ install(FILES ${TFM_ROOT_DIR}/interface/include/os_wrapper/semaphore.h
+ ${TFM_ROOT_DIR}/interface/include/os_wrapper/thread.h
+ DESTINATION export/tfm/include/os_wrapper)
+ endif()
+
+ embedded_project_end(tfm_non_secure_tests)
+endif()
diff --git a/test/TestConfig.cmake b/test/TestConfig.cmake
new file mode 100644
index 0000000..0fd3178
--- /dev/null
+++ b/test/TestConfig.cmake
@@ -0,0 +1,48 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+option(ENABLE_PROTECTED_STORAGE_SERVICE_TESTS "Option for protected storage service tests" TRUE)
+option(ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS "Option for internal trusted storage services tests" TRUE)
+option(ENABLE_AUDIT_LOGGING_SERVICE_TESTS "Option for audit logging service tests" TRUE)
+option(ENABLE_CRYPTO_SERVICE_TESTS "Option for crypto service tests" TRUE)
+option(ENABLE_ATTESTATION_SERVICE_TESTS "Option for attestation service tests" TRUE)
+option(ENABLE_PLATFORM_SERVICE_TESTS "Option for platform service tests" TRUE)
+option(ENABLE_QCBOR_TESTS "Option for QCBOR tests" TRUE)
+option(ENABLE_T_COSE_TESTS "Option for T_COSE tests" TRUE)
+
+# If a partition is not enabled, then neither should its tests.
+if (NOT TFM_PARTITION_PROTECTED_STORAGE)
+ set(ENABLE_PROTECTED_STORAGE_SERVICE_TESTS FALSE)
+endif()
+
+if (NOT TFM_PARTITION_INTERNAL_TRUSTED_STORAGE)
+ set(ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS FALSE)
+endif()
+
+if (NOT TFM_PARTITION_CRYPTO)
+ set(ENABLE_CRYPTO_SERVICE_TESTS FALSE)
+endif()
+
+if (NOT TFM_PARTITION_INITIAL_ATTESTATION)
+ set(ENABLE_ATTESTATION_SERVICE_TESTS FALSE)
+ set(ENABLE_QCBOR_TESTS FALSE)
+ set(ENABLE_T_COSE_TESTS FALSE)
+endif()
+
+# Disable external library test if SERVICES_TEST_ENABLED is OFF
+if (NOT SERVICES_TEST_ENABLED)
+ set(ENABLE_QCBOR_TESTS FALSE)
+ set(ENABLE_T_COSE_TESTS FALSE)
+endif()
+
+if (NOT TFM_PARTITION_PLATFORM)
+ set(ENABLE_PLATFORM_SERVICE_TESTS FALSE)
+endif()
+
+if (NOT TFM_PARTITION_AUDIT_LOG)
+ set(ENABLE_AUDIT_LOGGING_SERVICE_TESTS FALSE)
+endif()
diff --git a/test/dir_test.dox b/test/dir_test.dox
new file mode 100644
index 0000000..2ffc86a
--- /dev/null
+++ b/test/dir_test.dox
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+//This file holds description for the current directory. This documentation
+//will be included in the Doxygen output.
+
+/*!
+\dir
+\brief Source code for TF-M tests.
+\details TF-M tests exercise the TF-M core and various services to check correct
+operation.
+
+*/
\ No newline at end of file
diff --git a/test/framework/CMakeLists.inc b/test/framework/CMakeLists.inc
new file mode 100644
index 0000000..50fafb3
--- /dev/null
+++ b/test/framework/CMakeLists.inc
@@ -0,0 +1,45 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "test framework" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(TEST_FRAMEWORK_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+ message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+set (TEST_FRAMEWORK_C_SRC "${TEST_FRAMEWORK_DIR}/test_framework_helpers.c"
+ "${TEST_FRAMEWORK_DIR}/test_framework_integ_test_helper.c"
+ "${TEST_FRAMEWORK_DIR}/test_framework.c"
+ )
+
+set (TEST_FRAMEWORK_C_SRC_NS "${TEST_FRAMEWORK_DIR}/non_secure_suites.c"
+ )
+
+set (TEST_FRAMEWORK_C_SRC_S "${TEST_FRAMEWORK_DIR}/secure_suites.c"
+ )
+
+#Append all our source files to global lists.
+list(APPEND ALL_SRC_C ${TEST_FRAMEWORK_C_SRC})
+list(APPEND ALL_SRC_C_S ${TEST_FRAMEWORK_C_SRC_S})
+list(APPEND ALL_SRC_C_NS ${TEST_FRAMEWORK_C_SRC_NS})
+
+#Setting include directories
+embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
diff --git a/test/framework/non_secure_suites.c b/test/framework/non_secure_suites.c
new file mode 100644
index 0000000..ef3a94c
--- /dev/null
+++ b/test/framework/non_secure_suites.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test_framework_integ_test.h"
+#include "test_framework_integ_test_helper.h"
+#include "test_framework.h"
+
+/* Service specific includes */
+#include "test/suites/ps/non_secure/ps_ns_tests.h"
+#include "test/suites/its/non_secure/its_ns_tests.h"
+#include "test/suites/audit/non_secure/audit_ns_tests.h"
+#include "test/suites/crypto/non_secure/crypto_ns_tests.h"
+#include "test/suites/attestation/non_secure/attestation_ns_tests.h"
+#include "test/suites/qcbor/non_secure/qcbor_ns_tests.h"
+#include "test/suites/t_cose/non_secure/t_cose_ns_tests.h"
+#include "test/suites/core/non_secure/core_ns_tests.h"
+#include "test/suites/ipc/non_secure/ipc_ns_tests.h"
+#include "test/suites/platform/non_secure/platform_ns_tests.h"
+#include "test/suites/multi_core/non_secure/multi_core_ns_test.h"
+
+static struct test_suite_t test_suites[] = {
+#ifdef SERVICES_TEST_NS
+ /* List test cases which are compliant with level 1 isolation */
+
+#ifdef ENABLE_PROTECTED_STORAGE_SERVICE_TESTS
+ {®ister_testsuite_ns_psa_ps_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS
+ /* Non-secure ITS test cases */
+ {®ister_testsuite_ns_psa_its_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_CRYPTO_SERVICE_TESTS
+ /* Non-secure Crypto test cases */
+ {®ister_testsuite_ns_crypto_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_ATTESTATION_SERVICE_TESTS
+ /* Non-secure initial attestation service test cases */
+ {®ister_testsuite_ns_attestation_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_PLATFORM_SERVICE_TESTS
+ /* Non-secure platform service test cases */
+ {®ister_testsuite_ns_platform_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_QCBOR_TESTS
+ /* Non-secure QCBOR library test cases */
+ {®ister_testsuite_ns_qcbor, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_T_COSE_TESTS
+ /* Non-secure T_COSE library test cases */
+ {®ister_testsuite_ns_t_cose, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_AUDIT_LOGGING_SERVICE_TESTS
+ /* Non-secure Audit Logging test cases */
+ {®ister_testsuite_ns_audit_interface, 0, 0, 0},
+#endif
+
+#endif /* SERVICES_TEST_NS */
+
+#ifdef CORE_TEST_POSITIVE
+ /* Non-secure core test cases */
+ {®ister_testsuite_ns_core_positive, 0, 0, 0},
+#endif
+
+#ifdef CORE_TEST_INTERACTIVE
+ /* Non-secure interactive test cases */
+ {®ister_testsuite_ns_core_interactive, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_IPC_TEST
+ /* Non-secure IPC test cases */
+ {®ister_testsuite_ns_ipc_interface, 0, 0, 0},
+#endif
+
+#ifdef TFM_MULTI_CORE_TEST
+ /* Multi-core topology test cases */
+ {®ister_testsuite_multi_core_ns_interface, 0, 0, 0},
+#endif
+
+ /* End of test suites */
+ {0, 0, 0, 0}
+};
+
+enum test_suite_err_t start_integ_test(void)
+{
+ return integ_test("Non-secure", test_suites);
+}
+
+/* Service stand-in for NS tests. To be called from a non-secure context */
+enum test_suite_err_t tfm_non_secure_client_run_tests(void)
+{
+ return start_integ_test();
+}
diff --git a/test/framework/secure_suites.c b/test/framework/secure_suites.c
new file mode 100644
index 0000000..d89d27b
--- /dev/null
+++ b/test/framework/secure_suites.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test_framework_integ_test.h"
+#include "test_framework_integ_test_helper.h"
+#include "test_framework.h"
+
+/* Service specific includes */
+#include "test/suites/ps/secure/ps_tests.h"
+#include "test/suites/its/secure/its_s_tests.h"
+#include "test/suites/audit/secure/audit_s_tests.h"
+#include "test/suites/attestation/secure/attestation_s_tests.h"
+#include "test/suites/crypto/secure/crypto_s_tests.h"
+#include "test/suites/ipc/secure/ipc_s_tests.h"
+#include "test/suites/platform/secure/platform_s_tests.h"
+
+static struct test_suite_t test_suites[] = {
+#ifdef SERVICES_TEST_S
+ /* List test cases which are compliant with level 1 isolation */
+
+#ifdef ENABLE_PROTECTED_STORAGE_SERVICE_TESTS
+ {®ister_testsuite_s_psa_ps_interface, 0, 0, 0},
+ {®ister_testsuite_s_psa_ps_reliability, 0, 0, 0},
+
+#ifdef PS_TEST_NV_COUNTERS
+ {®ister_testsuite_s_rollback_protection, 0, 0, 0},
+#endif
+#endif
+
+#ifdef ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS
+ /* Secure ITS test cases */
+ {®ister_testsuite_s_psa_its_interface, 0, 0, 0},
+ {®ister_testsuite_s_psa_its_reliability, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_CRYPTO_SERVICE_TESTS
+ /* Crypto test cases */
+ {®ister_testsuite_s_crypto_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_ATTESTATION_SERVICE_TESTS
+ /* Secure initial attestation service test cases */
+ {®ister_testsuite_s_attestation_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_PLATFORM_SERVICE_TESTS
+ /* Secure platform service test cases */
+ {®ister_testsuite_s_platform_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_AUDIT_LOGGING_SERVICE_TESTS
+ /* Secure Audit Logging test cases */
+ {®ister_testsuite_s_audit_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_IPC_TEST
+ /* Secure IPC test cases */
+ {®ister_testsuite_s_ipc_interface, 0, 0, 0},
+#endif
+#endif /* SERVICES_TEST_S */
+ /* End of test suites */
+ {0, 0, 0, 0}
+};
+
+static void setup_integ_test(void)
+{
+ /* Left empty intentionally, currently implemented
+ * test suites require no setup
+ */
+}
+
+static void tear_down_integ_test(void)
+{
+ /* Left empty intentionally, currently implemented
+ * test suites require no tear down
+ */
+}
+
+enum test_suite_err_t start_integ_test(void)
+{
+ enum test_suite_err_t retval;
+
+ setup_integ_test();
+ retval = integ_test("Secure", test_suites);
+ tear_down_integ_test();
+ return retval;
+}
diff --git a/test/framework/test_framework.c b/test/framework/test_framework.c
new file mode 100644
index 0000000..85e757f
--- /dev/null
+++ b/test/framework/test_framework.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test_framework.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void test_failed(const struct test_result_t *ret)
+{
+ printf_set_color(RED);
+ if (ret->info_msg != 0) {
+ TEST_LOG(" %s", ret->info_msg);
+ if (ret->filename != 0) {
+ TEST_LOG(" (Failed at %s:%d)\r\n", ret->filename, ret->line);
+ }
+ } else {
+ if (ret->filename != 0) {
+ TEST_LOG(" Failed at %s:%d\r\n", ret->filename, ret->line);
+ }
+ }
+
+ TEST_LOG(" TEST FAILED!\r\n");
+}
+
+static void print_error(const char *err_msg)
+{
+ printf_set_color(RED);
+ TEST_LOG("Error ( %s )\r\n", err_msg);
+}
+
+const char *test_err_to_str(enum test_suite_err_t err)
+{
+ switch (err) {
+ case TEST_SUITE_ERR_NO_ERROR:
+ return "TEST_SUITE_ERR_NO_ERROR";
+ case TEST_SUITE_ERR_INVALID_DATA:
+ return "TEST_SUITE_ERR_INVALID_DATA";
+ case TEST_SUITE_ERR_INVALID_TEST_DATA:
+ return "TEST_SUITE_ERR_INVALID_TEST_DATA";
+ case TEST_SUITE_ERR_TEST_FAILED:
+ return "TEST_SUITE_ERR_TEST_FAILED";
+ /* default: The default is not defined intentionally to force the
+ * compiler to check that all the enumeration values are
+ * covered in the switch.
+ */
+ }
+}
+
+enum test_suite_err_t set_testsuite(const char *name,
+ struct test_t *test_list, uint32_t size,
+ struct test_suite_t *p_ts)
+{
+ if (p_ts == 0) {
+ print_error("TEST_SUITE_ERR_INVALID_DATA!");
+ return TEST_SUITE_ERR_INVALID_DATA;
+ }
+
+ p_ts->name = name;
+ p_ts->test_list = test_list;
+ p_ts->list_size = size;
+
+ return TEST_SUITE_ERR_NO_ERROR;
+}
+
+void set_test_failed(const char *info_msg, const char *filename, uint32_t line,
+ struct test_result_t *ret)
+{
+ if (ret == 0) {
+ print_error("TEST_SUITE_ERR_INVALID_TEST_DATA!");
+ return;
+ }
+
+ ret->val = TEST_FAILED;
+ ret->info_msg = info_msg;
+ ret->filename = filename;
+ ret->line = line;
+}
+
+enum test_suite_err_t run_testsuite(struct test_suite_t *test_suite)
+{
+ uint32_t failed_tests = 0;
+ uint32_t i;
+ struct test_t *p_test;
+
+ if (test_suite == 0 || test_suite->freg == 0) {
+ print_error("TEST_SUITE_ERR_INVALID_DATA!");
+ return TEST_SUITE_ERR_INVALID_DATA;
+ }
+
+ /* Sets test suite parameters */
+ test_suite->freg(test_suite);
+ if (test_suite->name == 0 || test_suite->list_size == 0) {
+ print_error("TEST_SUITE_ERR_INVALID_DATA!");
+ return TEST_SUITE_ERR_INVALID_DATA;
+ }
+
+ printf_set_color(YELLOW);
+ TEST_LOG("Running Test Suite %s...\r\n", test_suite->name);
+
+ /* Sets pointer to the first test */
+ p_test = test_suite->test_list;
+
+ for (i = 0; i < test_suite->list_size; i++) {
+
+ if (p_test->test == 0 || p_test->name == 0) {
+ print_error("TEST_SUITE_ERR_INVALID_TEST_DATA!");
+ return TEST_SUITE_ERR_INVALID_TEST_DATA;
+ }
+
+ printf_set_color(WHITE);
+ TEST_LOG("> Executing '%s' \r\n Description: '%s'\r\n",
+ p_test->name, p_test->desc);
+
+ /* Sets the default value before the test */
+ p_test->ret.val = TEST_PASSED;
+
+ /* Executes the test */
+ p_test->test(&p_test->ret);
+ if (p_test->ret.val == TEST_FAILED) {
+ test_failed(&p_test->ret);
+ failed_tests++;
+ } else {
+ printf_set_color(GREEN);
+ TEST_LOG(" TEST PASSED!\r\n");
+ }
+
+ /* Sets pointer to the next test */
+ p_test++;
+ }
+
+
+ if (failed_tests == 0) {
+ printf_set_color(GREEN);
+ TEST_LOG("TESTSUITE PASSED!\r\n");
+ test_suite->val = TEST_PASSED;
+ } else {
+ printf_set_color(RED);
+ TEST_LOG("TESTSUITE FAILED!\r\n");
+ printf_set_color(YELLOW);
+ TEST_LOG("Number of failed tests: %d of %d\r\n",
+ failed_tests, test_suite->list_size);
+ test_suite->val = TEST_FAILED;
+ }
+
+ return TEST_SUITE_ERR_NO_ERROR;
+}
diff --git a/test/framework/test_framework.h b/test/framework/test_framework.h
new file mode 100644
index 0000000..9772875
--- /dev/null
+++ b/test/framework/test_framework.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TEST_FRAMEWORK_H__
+#define __TEST_FRAMEWORK_H__
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "log/tfm_log_raw.h"
+#include "test_framework_helpers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum test_status_t {
+ TEST_PASSED = 0, /*!< Test has passed */
+ TEST_FAILED = 1, /*!< Test has failed */
+};
+
+struct test_result_t {
+ enum test_status_t val; /*!< Test result \ref test_status_t */
+ const char *info_msg; /*!< Information message to show in case of
+ * failure
+ */
+ const char *filename; /*!< Filename where the failure has occured */
+ uint32_t line; /*!< Line where the failure has occured */
+};
+
+/**
+ * \brief Runs the test.
+ *
+ * \param[out] ret Test result value
+ */
+typedef void TEST_FUN(struct test_result_t *ret);
+
+struct test_t {
+ TEST_FUN * const test; /*!< Test function to call */
+ const char *name; /*!< Test name */
+ const char *desc; /*!< Test description */
+ struct test_result_t ret; /*!< Test result */
+};
+
+struct test_suite_t;
+
+/**
+ * \brief Registers test in the testsuite structure and sets the name.
+ *
+ * \param[in] p_test_suite Pointer to the p_test_suite_location.
+ */
+typedef void TESTSUITE_REG(struct test_suite_t *p_test_suite);
+
+struct test_suite_t {
+ TESTSUITE_REG * const freg; /*!< Function to set all follow fields
+ * of the current test suite
+ */
+ struct test_t *test_list; /*!< List of tests */
+ uint32_t list_size; /*!< List size */
+ const char *name; /*!< Test suite name */
+ enum test_status_t val; /*!< Test suite result \ref test_result_t */
+};
+
+enum test_suite_err_t {
+ TEST_SUITE_ERR_NO_ERROR = 0, /*!< No error */
+ TEST_SUITE_ERR_INVALID_DATA = 1, /*!< Invalid test suite if any of the
+ * pointers is NULL
+ */
+ TEST_SUITE_ERR_INVALID_TEST_DATA = 2, /*!< Invalid test if any of the
+ * pointers is NULL
+ */
+ TEST_SUITE_ERR_TEST_FAILED = 3, /*!< Last executed test has failed */
+};
+
+/**
+ * \brief Translates the test suite error into a string.
+ *
+ * \param[in] err Error value \ref test_suite_err_t
+ *
+ * \returns error as string.
+ */
+const char *test_err_to_str(enum test_suite_err_t err);
+
+/**
+ * \brief Sets test suite parameters.
+ *
+ * \param[in] name Test suite name
+ * \param[in] test_list Pointer to the test list
+ * \param[in] size Test list size
+ * \param[in,out] p_ts Pointer to test suite object to fill in the
+ * parameters
+ *
+ * \returns Returns error code as specified in \ref test_suite_err_t
+ */
+enum test_suite_err_t set_testsuite(const char *name,
+ struct test_t *test_list, uint32_t size,
+ struct test_suite_t *p_ts);
+
+/**
+ * \brief Runs the given test suite.
+ *
+ * \param[in,out] test_suite Test suite to run the list of tests and
+ * store test results.
+ *
+ * \returns Returns error code as specified in \ref test_suite_err_t
+ */
+enum test_suite_err_t run_testsuite(struct test_suite_t *test_suite);
+
+/**
+ * \brief Prints all test in the the given test suite.
+ *
+ * \param[in] ts Test suite to print the list of tests
+ */
+void show_tests(const struct test_suite_t *ts);
+
+/**
+ * \brief Sets test failure state and information in the \ref test_result_t
+ * structure.
+ *
+ * \param[in] info_msg Information message to show
+ * \param[in] filename Filename where the error has ocurred
+ * \param[in] line Line in the file where the error has ocurred
+ * \param[out] ret Pointer to \ref test_result_t structure to
+ * set the values
+ *
+ * \note: If info_msg is "" or , info message is not shown. If filename is "",
+ * filename and line are not shown.
+ */
+void set_test_failed(const char *info_msg, const char *filename, uint32_t line,
+ struct test_result_t *ret);
+
+#define TEST_FAIL(info_msg) set_test_failed(info_msg, __FILE__, __LINE__, ret)
+
+#define TEST_LOG(...) tfm_log_printf(__VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TEST_FRAMEWORK_H__ */
diff --git a/test/framework/test_framework_helpers.c b/test/framework/test_framework_helpers.c
new file mode 100644
index 0000000..5341058
--- /dev/null
+++ b/test/framework/test_framework_helpers.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test_framework_helpers.h"
+
+#include <stdio.h>
+
+const char *asset_perms_to_str(uint8_t permissions)
+{
+ switch (permissions) {
+ case 0:
+ return "No permissions";
+ case 1:
+ return "SECURE_ASSET_REFERENCE";
+ case 2:
+ return "SECURE_ASSET_WRITE";
+ case 3:
+ return "SECURE_ASSET_REFERENCE | SECURE_ASSET_WRITE";
+ case 4:
+ return "SECURE_ASSET_READ";
+ case 5:
+ return "SECURE_ASSET_REFERENCE | SECURE_ASSET_READ";
+ case 6:
+ return "SECURE_ASSET_WRITE | SECURE_ASSET_READ";
+ case 7:
+ return "SECURE_ASSET_REFERENCE | SECURE_ASSET_WRITE | "
+ "SECURE_ASSET_READ";
+ default:
+ return "Unknown permissions";
+ }
+}
+
+void printf_set_color(enum serial_color_t color_id)
+{
+ TEST_LOG("\33[3%dm", color_id);
+}
diff --git a/test/framework/test_framework_helpers.h b/test/framework/test_framework_helpers.h
new file mode 100644
index 0000000..6a99067
--- /dev/null
+++ b/test/framework/test_framework_helpers.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TEST_FRAMEWORK_HELPERS_H__
+#define __TEST_FRAMEWORK_HELPERS_H__
+
+#include <stdint.h>
+#include "psa/protected_storage.h"
+#include "test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum serial_color_t {
+ BLACK = 0,
+ RED = 1,
+ GREEN = 2,
+ YELLOW = 3,
+ BLUE = 4,
+ MAGENDA = 5,
+ CYAN = 6,
+ WHITE = 7,
+};
+
+/**
+ * \brief Translates asset permissions into a string.
+ *
+ * \param[in] permissions Asset permissions value.
+ *
+ * \return asset permissions as string.
+ */
+const char *asset_perms_to_str(uint8_t permissions);
+
+/**
+ * \brief Sets the the text color in the serial port.
+ *
+ * \param[in] color_id Serial foreground color.
+ *
+ * \return 0 if the message is send successfully, 1 otherwise.
+ */
+void printf_set_color(enum serial_color_t color_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TEST_FRAMEWORK_HELPERS_H__ */
diff --git a/test/framework/test_framework_integ_test.h b/test/framework/test_framework_integ_test.h
new file mode 100644
index 0000000..acbcfc0
--- /dev/null
+++ b/test/framework/test_framework_integ_test.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017-2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __INTEG_TEST_H__
+#define __INTEG_TEST_H__
+
+#include "test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Executes integration tests. To be called from appropriate
+ * secure/non-secure service client.
+ *
+ * \returns Returns error code as specified in \ref test_suite_err_t
+ */
+enum test_suite_err_t start_integ_test(void);
+
+/**
+ * \brief Service stand-in shim for non secure tests. To be called from a
+ * non-secure client.
+ *
+ * \returns Returns error code as specified in \ref test_suite_err_t
+ */
+enum test_suite_err_t tfm_non_secure_client_run_tests(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INTEG_TEST_H__ */
diff --git a/test/framework/test_framework_integ_test_helper.c b/test/framework/test_framework_integ_test_helper.c
new file mode 100644
index 0000000..26105cd
--- /dev/null
+++ b/test/framework/test_framework_integ_test_helper.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdio.h>
+
+#include "test_framework.h"
+#include "test_framework_integ_test_helper.h"
+
+enum test_suite_err_t integ_test(const char *suite_type,
+ struct test_suite_t test_suites[])
+{
+ uint32_t i;
+ enum test_suite_err_t retval = TEST_SUITE_ERR_NO_ERROR;
+
+ printf_set_color(YELLOW);
+ TEST_LOG("\r\n#### Execute test suites for the %s area ####\r\n",
+ suite_type);
+
+ /* Executes test suites */
+ for (i = 0; test_suites[i].freg != NULL; i++) {
+ retval = run_testsuite(&test_suites[i]);
+ if (retval != TEST_SUITE_ERR_NO_ERROR) {
+ /* End function execution */
+ return retval;
+ }
+ }
+
+ /* Prints test suites summary */
+ printf_set_color(YELLOW);
+ TEST_LOG("\r\n*** %s test suites summary ***\r\n", suite_type);
+ for (i = 0; test_suites[i].freg != NULL; i++) {
+ printf_set_color(WHITE);
+ TEST_LOG("Test suite '%s' has ", test_suites[i].name);
+ if (test_suites[i].val == TEST_PASSED) {
+ printf_set_color(GREEN);
+ TEST_LOG(" PASSED\r\n");
+ } else {
+ printf_set_color(RED);
+ TEST_LOG(" FAILED\r\n");
+ retval = TEST_SUITE_ERR_TEST_FAILED;
+ }
+ }
+
+ printf_set_color(YELLOW);
+ TEST_LOG("\r\n*** End of %s test suites ***\r\n", suite_type);
+ return retval;
+}
diff --git a/test/framework/test_framework_integ_test_helper.h b/test/framework/test_framework_integ_test_helper.h
new file mode 100644
index 0000000..c37aa14
--- /dev/null
+++ b/test/framework/test_framework_integ_test_helper.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __INTEG_TEST_HELPER_H__
+#define __INTEG_TEST_HELPER_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test_framework.h"
+
+/**
+ * \brief Executes integration test suites provided in the parameters.
+ *
+ * \param[in] suite_type A string containing the type of the suite
+ * (used for printing).
+ * \param[in] test_suites The suites to be executed.
+ * \param[in] test_suite_cnt The number of test suites to be executed.
+ *
+ * \returns Returns error code as specified in \ref test_suite_err_t
+ */
+enum test_suite_err_t integ_test(const char *suite_type,
+ struct test_suite_t test_suites[]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INTEG_TEST_HELPER_H__ */
diff --git a/test/suites/attestation/CMakeLists.inc b/test/suites/attestation/CMakeLists.inc
new file mode 100644
index 0000000..1a739f2
--- /dev/null
+++ b/test/suites/attestation/CMakeLists.inc
@@ -0,0 +1,95 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "secure attestation test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(ATTESTATION_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+ message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ATTEST_INCLUDE_TEST_CODE)
+ message(FATAL_ERROR "Incomplete build configuration: ATTEST_INCLUDE_TEST_CODE is undefined. ")
+endif()
+
+if (NOT DEFINED ATTEST_CLAIM_VALUE_CHECK)
+ message(FATAL_ERROR "Incomplete build configuration: ATTEST_CLAIM_VALUE_CHECK is undefined. ")
+endif()
+
+if (NOT DEFINED ENABLE_ATTESTATION_SERVICE_TESTS)
+ message(FATAL_ERROR "Incomplete build configuration: ENABLE_ATTESTATION_SERVICE_TESTS is undefined. ")
+elseif(ENABLE_ATTESTATION_SERVICE_TESTS)
+ list(APPEND ATTEST_TEST_SRC_S
+ "${ATTESTATION_TEST_DIR}/attest_token_test.c"
+ "${ATTESTATION_TEST_DIR}/attest_token_decode_common.c"
+ "${TFM_ROOT_DIR}/lib/ext/qcbor/util/qcbor_util.c"
+ )
+
+ list(APPEND ATTEST_TEST_SRC_NS
+ "${ATTESTATION_TEST_DIR}/attest_token_test.c"
+ "${ATTESTATION_TEST_DIR}/attest_token_decode_common.c"
+ "${TFM_ROOT_DIR}/lib/ext/qcbor/util/qcbor_util.c"
+ )
+
+ if (SYMMETRIC_INITIAL_ATTESTATION)
+ list(APPEND ATTEST_TEST_SRC_S
+ "${ATTESTATION_TEST_DIR}/secure/symmetric_attest_s_interface_testsuite.c"
+ "${ATTESTATION_TEST_DIR}/attest_symmetric_iat_decode.c"
+ )
+ list(APPEND ATTEST_TEST_SRC_NS
+ "${ATTESTATION_TEST_DIR}/non_secure/symmetric_attest_ns_interface_testsuite.c"
+ "${ATTESTATION_TEST_DIR}/attest_symmetric_iat_decode.c"
+ )
+ else()
+ list(APPEND ATTEST_TEST_SRC_S
+ "${ATTESTATION_TEST_DIR}/secure/attestation_s_interface_testsuite.c"
+ "${ATTESTATION_TEST_DIR}/attest_public_key.c"
+ "${ATTESTATION_TEST_DIR}/attest_token_decode.c"
+ )
+ list(APPEND ATTEST_TEST_SRC_NS
+ "${ATTESTATION_TEST_DIR}/non_secure/attestation_ns_interface_testsuite.c"
+ "${ATTESTATION_TEST_DIR}/attest_public_key.c"
+ "${ATTESTATION_TEST_DIR}/attest_token_decode.c"
+ )
+ endif()
+
+ if (ATTEST_INCLUDE_TEST_CODE)
+ set_property(SOURCE ${ATTEST_TEST_SRC_S} APPEND PROPERTY COMPILE_DEFINITIONS INCLUDE_TEST_CODE)
+ set_property(SOURCE ${ATTEST_TEST_SRC_NS} APPEND PROPERTY COMPILE_DEFINITIONS INCLUDE_TEST_CODE)
+ endif()
+
+ if (ATTEST_CLAIM_VALUE_CHECK)
+ set_property(SOURCE ${ATTEST_TEST_SRC_S} APPEND PROPERTY COMPILE_DEFINITIONS CLAIM_VALUE_CHECK)
+ set_property(SOURCE ${ATTEST_TEST_SRC_NS} APPEND PROPERTY COMPILE_DEFINITIONS CLAIM_VALUE_CHECK)
+ endif()
+
+ #Setting include directories
+ embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+ embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+ embedded_include_directories(PATH ${TFM_ROOT_DIR}/secure_fw/partitions/initial_attestation ABSOLUTE)
+ embedded_include_directories(PATH ${TFM_ROOT_DIR}/lib/ext/qcbor/inc ABSOLUTE)
+ embedded_include_directories(PATH ${TFM_ROOT_DIR}/lib/ext/qcbor/util ABSOLUTE)
+ embedded_include_directories(PATH ${TFM_ROOT_DIR}/lib/ext/t_cose/inc ABSOLUTE)
+
+ #Append all our source files to global lists.
+ list(APPEND ALL_SRC_C_S ${ATTEST_TEST_SRC_S})
+ list(APPEND ALL_SRC_C_NS ${ATTEST_TEST_SRC_NS})
+ unset(ATTEST_TEST_SRC_S)
+ unset(ATTEST_TEST_SRC_NS)
+endif()
diff --git a/test/suites/attestation/attest_public_key.c b/test/suites/attestation/attest_public_key.c
new file mode 100644
index 0000000..d6e5aca
--- /dev/null
+++ b/test/suites/attestation/attest_public_key.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "attest_public_key.h"
+#include "psa/crypto.h"
+#include <stdint.h>
+#include "attestation.h"
+
+/*!
+ * \def ECC_CURVE_SECP256R1_PUBLIC_KEY_LENGTH
+ *
+ * \brief Calculates the size of ECC public key in bytes based on the bit size
+ * of the curve
+ */
+#define ECC_CURVE_SECP256R1_PUBLIC_KEY_LENGTH (1 + 2 * PSA_BITS_TO_BYTES(256))
+
+/*!
+ * Byte string representation of ECC public key according to
+ * psa_export_public_key() in interface/include/psa/crypto.h
+ */
+struct ecc_public_key_t {
+ const uint8_t a;
+ uint8_t public_key[]; /* X-coordinate || Y-coordinate */
+};
+
+/*!
+ * \var public_key_registered
+ *
+ * \brief Indicates whether the public part of the attestation key pair was
+ * registered to Crypto service or not.
+ */
+static uint32_t public_key_registered = 0;
+
+enum psa_attest_err_t
+attest_register_initial_attestation_public_key(psa_key_handle_t *public_key)
+{
+ psa_status_t res;
+ psa_key_attributes_t key_attributes = psa_key_attributes_init();
+ uint8_t public_key_buff[ECC_CURVE_SECP256R1_PUBLIC_KEY_LENGTH] = {0};
+ size_t public_key_len;
+ psa_ecc_curve_t ecc_curve;
+
+ /* Public key should be unregistered at this point */
+ if (public_key_registered != 0) {
+ return PSA_ATTEST_ERR_GENERAL;
+ }
+
+ res = tfm_initial_attest_get_public_key(public_key_buff,
+ sizeof(public_key_buff),
+ &public_key_len,
+ &ecc_curve);
+ if (res != PSA_SUCCESS) {
+ return PSA_ATTEST_ERR_GENERAL;
+ }
+
+ /* Setup the key usage flags, algorithm and key type for public key */
+ psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY);
+ psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
+ psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(ecc_curve));
+
+ /* Register public key to Crypto service */
+ res = psa_import_key(&key_attributes,
+ (const uint8_t *)&public_key_buff,
+ public_key_len,
+ public_key);
+
+ if (res != PSA_SUCCESS) {
+ return PSA_ATTEST_ERR_GENERAL;
+ }
+
+ public_key_registered = 1;
+
+ return PSA_ATTEST_ERR_SUCCESS;
+}
+
+enum psa_attest_err_t
+attest_unregister_initial_attestation_public_key(psa_key_handle_t public_key)
+{
+ psa_status_t crypto_res;
+
+ if (public_key_registered != 1) {
+ return PSA_ATTEST_ERR_GENERAL;
+ }
+
+ crypto_res = psa_destroy_key(public_key);
+ if (crypto_res != PSA_SUCCESS) {
+ return PSA_ATTEST_ERR_GENERAL;
+ }
+ public_key_registered = 0;
+
+ return PSA_ATTEST_ERR_SUCCESS;
+}
diff --git a/test/suites/attestation/attest_public_key.h b/test/suites/attestation/attest_public_key.h
new file mode 100644
index 0000000..fa9d03f
--- /dev/null
+++ b/test/suites/attestation/attest_public_key.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __ATTEST_PUBLIC_KEY_H__
+#define __ATTEST_PUBLIC_KEY_H__
+
+#include "psa/initial_attestation.h"
+#include "psa/crypto.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register the initial attestation public key to Crypto service to
+ * verify the signature of the token.
+ *
+ * \param[out] key_handle_public Pointer to the key handle allocated for the
+ * public key
+ *
+ * \retval PSA_ATTEST_ERR_SUCCESS Key was registered.
+ * \retval PSA_ATTEST_ERR_GENERAL Key could not be registered.
+ */
+enum psa_attest_err_t attest_register_initial_attestation_public_key(
+ psa_key_handle_t *key_handle_public);
+
+/**
+ * \brief Unregister the initial attestation public key from Crypto service
+ * to do not occupy key slot.
+ *
+ * \param[in] key_handle_public Key handle associated to the public key
+ *
+ * \retval PSA_ATTEST_ERR_SUCCESS Key was unregistered.
+ * \retval PSA_ATTEST_ERR_GENERAL Key could not be unregistered.
+ */
+enum psa_attest_err_t attest_unregister_initial_attestation_public_key(
+ psa_key_handle_t key_handle_public);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ATTEST_PUBLIC_KEY_H__ */
diff --git a/test/suites/attestation/attest_symmetric_iat_decode.c b/test/suites/attestation/attest_symmetric_iat_decode.c
new file mode 100644
index 0000000..77312a3
--- /dev/null
+++ b/test/suites/attestation/attest_symmetric_iat_decode.c
@@ -0,0 +1,163 @@
+/*
+ * attest_symmetric_iat_decode.c
+ *
+ * Copyright (c) 2019, Laurence Lundblade.
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "attest_token_decode.h"
+#include "attestation.h"
+#include "psa/crypto.h"
+#include "q_useful_buf.h"
+#include "qcbor_util.h"
+#include "t_cose_common.h"
+#include "t_cose_mac0_verify.h"
+#include "tfm_plat_crypto_keys.h"
+
+/* Only support HMAC as MAC algorithm in COSE_Mac0 so far */
+#define SYMMETRIC_IAK_MAX_SIZE PSA_MAC_MAX_SIZE
+
+#if DOMAIN_NS == 1U
+/*
+ * Public function. See attest_token_decode.h
+ * It is not allowed to let NS side fetch the symmetric IAK and perform the MAC
+ * verification.
+ */
+enum attest_token_err_t
+attest_token_decode_validate_token(struct attest_token_decode_context *me,
+ struct q_useful_buf_c token)
+{
+ enum t_cose_err_t t_cose_error;
+ enum attest_token_err_t return_value;
+ /* Decode only without signature verification */
+ int32_t t_cose_options = T_COSE_OPT_DECODE_ONLY;
+ struct t_cose_mac0_verify_ctx verify_ctx;
+ struct t_cose_key attest_key = T_COSE_NULL_KEY;
+
+ t_cose_mac0_verify_init(&verify_ctx, t_cose_options);
+
+ t_cose_mac0_set_verify_key(&verify_ctx, attest_key);
+
+ t_cose_error = t_cose_mac0_verify(&verify_ctx,
+ token, /* COSE to verify */
+ &me->payload, /* Payload from token */
+ NULL
+ );
+
+ return_value = map_t_cose_errors(t_cose_error);
+ me->last_error = return_value;
+
+ return return_value;
+}
+#else /* DOMAIN_NS == 1U */
+/*
+ * \note The symmetric Initial Attestation key (IAK) will be fetched for
+ * authentication tag verification in secure test cases.
+ * Authentication tag verification in tests is for debug purpose only.
+ * Do not fetch the IAK outside attestation service in real products.
+ */
+static inline enum attest_token_err_t
+decode_register_verify_key(psa_key_handle_t *verify_key_handle)
+{
+ uint8_t key_buf[SYMMETRIC_IAK_MAX_SIZE];
+ psa_algorithm_t key_alg;
+ psa_key_handle_t key_handle;
+ size_t key_len;
+ enum tfm_plat_err_t plat_res;
+ psa_status_t psa_res;
+ psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
+
+ /* Get the symmetric initial attestation key for HMAC operation */
+ plat_res = tfm_plat_get_symmetric_iak(key_buf, sizeof(key_buf),
+ &key_len, &key_alg);
+ if (plat_res != TFM_PLAT_ERR_SUCCESS) {
+ return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
+ }
+
+ /*
+ * Verify if HMAC algorithm is valid.
+ * According to COSE (RFC 8152), only SHA-256, SHA-384 and SHA-512 are
+ * supported in HMAC.
+ */
+ if ((key_alg != PSA_ALG_HMAC(PSA_ALG_SHA_256)) && \
+ (key_alg != PSA_ALG_HMAC(PSA_ALG_SHA_384)) && \
+ (key_alg != PSA_ALG_HMAC(PSA_ALG_SHA_512))) {
+ return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
+ }
+
+ /* Setup the key attributes */
+ psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY);
+ psa_set_key_algorithm(&key_attributes, key_alg);
+ psa_set_key_type(&key_attributes, PSA_KEY_TYPE_HMAC);
+
+ /* Register the symmetric key to Crypto service */
+ psa_res = psa_import_key(&key_attributes, key_buf, key_len, &key_handle);
+ if (psa_res != PSA_SUCCESS) {
+ return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
+ }
+
+ *verify_key_handle = key_handle;
+
+ return ATTEST_TOKEN_ERR_SUCCESS;
+}
+
+static inline enum attest_token_err_t
+decode_unregister_verify_key(psa_key_handle_t verify_key_handle)
+{
+ psa_status_t status;
+
+ status = psa_destroy_key(verify_key_handle);
+ if (status == PSA_SUCCESS) {
+ return ATTEST_TOKEN_ERR_SUCCESS;
+ }
+
+ return ATTEST_TOKEN_ERR_GENERAL;
+}
+
+/*
+ * Public function. See attest_token_decode.h
+ * Decode the received COSE_Mac0 structure and verify the tag.
+ */
+enum attest_token_err_t
+attest_token_decode_validate_token(struct attest_token_decode_context *me,
+ struct q_useful_buf_c token)
+{
+ enum t_cose_err_t t_cose_error;
+ enum attest_token_err_t return_value;
+ int32_t t_cose_options = 0;
+ struct t_cose_mac0_verify_ctx verify_ctx;
+ struct t_cose_key attest_key;
+ psa_key_handle_t key_handle;
+
+ return_value = decode_register_verify_key(&key_handle);
+ if (return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ return return_value;
+ }
+
+ if (me->options & TOKEN_OPT_SHORT_CIRCUIT_SIGN) {
+ t_cose_options |= T_COSE_OPT_ALLOW_SHORT_CIRCUIT;
+ }
+
+ t_cose_mac0_verify_init(&verify_ctx, t_cose_options);
+
+ attest_key.crypto_lib = T_COSE_CRYPTO_LIB_PSA;
+ attest_key.k.key_handle = (uint64_t)key_handle;
+ t_cose_mac0_set_verify_key(&verify_ctx, attest_key);
+
+ t_cose_error = t_cose_mac0_verify(&verify_ctx,
+ token, /* COSE to verify */
+ &me->payload, /* Payload from token */
+ NULL);
+
+ return_value = map_t_cose_errors(t_cose_error);
+ me->last_error = return_value;
+
+ decode_unregister_verify_key(key_handle);
+
+ return return_value;
+}
+#endif /* DOMAIN_NS == 1U */
diff --git a/test/suites/attestation/attest_token_decode.c b/test/suites/attestation/attest_token_decode.c
new file mode 100644
index 0000000..90fbcf5
--- /dev/null
+++ b/test/suites/attestation/attest_token_decode.c
@@ -0,0 +1,71 @@
+/*
+ * attest_token_decode.c
+ *
+ * Copyright (c) 2019, Laurence Lundblade.
+ * Copyright (c) 2020, Arm Limited.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "attest_token_decode.h"
+#include "t_cose_sign1_verify.h"
+#include "q_useful_buf.h"
+#include "qcbor_util.h"
+#include "psa/crypto.h"
+#include "attest_public_key.h"
+#include "attestation.h"
+
+/**
+ * \file attest_token_decode.c
+ *
+ * \brief Attestation token decoder for COSE_Sign1.
+ */
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_validate_token(struct attest_token_decode_context *me,
+ struct q_useful_buf_c token)
+{
+ enum t_cose_err_t t_cose_error;
+ enum attest_token_err_t return_value;
+ enum psa_attest_err_t attest_ret;
+ int32_t t_cose_options = 0;
+ struct t_cose_sign1_verify_ctx verify_ctx;
+ struct t_cose_key attest_key;
+ psa_key_handle_t public_key;
+
+ /* Run the signature verification */
+ if(me->options & TOKEN_OPT_SHORT_CIRCUIT_SIGN) {
+ t_cose_options |= T_COSE_OPT_ALLOW_SHORT_CIRCUIT;
+ }
+ t_cose_sign1_verify_init(&verify_ctx, t_cose_options);
+
+ attest_ret = attest_register_initial_attestation_public_key(&public_key);
+ if (attest_ret != PSA_ATTEST_ERR_SUCCESS) {
+ return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
+ }
+
+ attest_key.crypto_lib = T_COSE_CRYPTO_LIB_PSA;
+ attest_key.k.key_handle = public_key;
+
+ t_cose_sign1_set_verification_key(&verify_ctx, attest_key);
+
+ t_cose_error = t_cose_sign1_verify(&verify_ctx,
+ token, /* COSE to verify */
+ &me->payload, /* Payload from token */
+ NULL); /* Don't return parameters */
+
+ return_value = map_t_cose_errors(t_cose_error);
+ me->last_error = return_value;
+
+ attest_ret = attest_unregister_initial_attestation_public_key(public_key);
+ if (attest_ret != PSA_ATTEST_ERR_SUCCESS) {
+ return ATTEST_TOKEN_ERR_GENERAL;
+ }
+
+ return return_value;
+}
diff --git a/test/suites/attestation/attest_token_decode.h b/test/suites/attestation/attest_token_decode.h
new file mode 100644
index 0000000..880336b
--- /dev/null
+++ b/test/suites/attestation/attest_token_decode.h
@@ -0,0 +1,934 @@
+/*
+ * attest_token_decode.h
+ *
+ * Copyright (c) 2019, Laurence Lundblade.
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+#ifndef __ATTEST_TOKEN_DECODE_H__
+#define __ATTEST_TOKEN_DECODE_H__
+
+#include "q_useful_buf.h"
+#include <stdbool.h>
+#include "attest_token.h"
+#include "attest_eat_defines.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file attest_token_decode.h
+ *
+ * \brief Attestation Token Decoding Interface
+ *
+ * The context and functions here are used to decode an attestation
+ * token as follows:
+ *
+ * -# Create a \ref attest_token_decode_context, most likely as a
+ * stack variable.
+ *
+ * -# Initialize it by calling attest_token_decode_init()
+ *
+ * -# Tell it which public key to use for verification using
+ * attest_token_decode_set_cose_pub_key() or
+ * attest_token_decode_set_pub_key_select().
+ *
+ * -# Pass the token in and validate it by calling
+ * attest_token_decode_validate_token().
+ *
+ * -# Call the various \c attest_token_get_xxx() methods in any
+ * order. The strings returned by the these functions will point into
+ * the token passed to attest_token_decode_validate_token(). A copy is
+ * NOT made.
+ *
+ * The entire token is validated and decoded in place. No copies are
+ * made internally. The data returned by the \c attest_token_get_xxx()
+ * methods is not a copy so the lifetime of the \c struct \c
+ * q_useful_buf_c containing the token must be maintained.
+ *
+ * Aside from the cryptographic functions, this allocates no
+ * memory. It works entirely off the stack. It makes use of t_cose to
+ * validate the signature and QCBOR for CBOR decoding.
+ *
+ * This decoder only works with labels (keys) that are integers even
+ * though labels can be any data type in CBOR. The presumption is that
+ * this is for small embedded use cases where space is a premium and
+ * only integer labels will be used.
+ *
+ * All claims are optional in tokens. This decoder will ignore all
+ * CBOR encoded data that it doesn't understand without error.
+ *
+ * This interface is primarily for the claims defined by Arm for
+ * TF-M. It includes only some of the claims from the EAT IETF draft,
+ * https://tools.ietf.org/html/draft-mandyam-eat-01.
+ *
+ * The claims are not described in detail here. That is left to the
+ * definition documents and eventually an IETF standard.
+ *
+ * If a method to get the claim you are interested in doesn't exist,
+ * there are several methods where you can give the label (the key)
+ * for the claim and have it returned. This only works for simple
+ * claims (strings and integers).
+ *
+ * The entire payload can be retrieved unparsed. Then you can use a
+ * separate CBOR parser to decode the claims out of it. Future work may
+ * include more general facilities for handling claims with complex
+ * structures made up of maps and arrays.
+ *
+ * This should not yet be considered a real commercial
+ * implementation of token decoding. It is
+ * close, but not there yet. It's purpose is to test
+ * token encoding. The main thing this needs to become
+ * a real commercial implementation is code that
+ * tests this. It is a parser / decoder, so a
+ * proper test involves a lot of hostile input.
+ */
+
+
+/**
+ * The context for decoding an attestation token. The caller of must
+ * create one of these and pass it to the functions here. It is small
+ * enough that it can go on the stack. It is most of the memory needed
+ * to create a token except the output buffer and any memory
+ * requirements for the cryptographic operations.
+ *
+ * The structure is opaque for the caller.
+ *
+ */
+struct attest_token_decode_context {
+ /* PRIVATE DATA STRUCTURE. USE ACCESSOR FUNCTIONS. */
+ struct q_useful_buf_c payload;
+ uint32_t options;
+ enum attest_token_err_t last_error;
+ /* FIXME: This will have to expand when the pub key
+ handling functions are implemented */
+};
+
+
+/**
+ * \brief Initialize token decoder.
+ *
+ * \param[in] me The token decoder context to be initialized.
+ * \param[in] options Decoding options.
+ *
+ * Must be called on a \ref attest_token_decode_context before
+ * use. An instance of \ref attest_token_decode_context can
+ * be used again by calling this on it again.
+ **/
+void attest_token_decode_init(struct attest_token_decode_context *me,
+ uint32_t options);
+
+
+
+/**
+ * \brief Set specific public key to use for verification.
+ *
+ * \param[in] me The token decoder context to configure.
+ * \param[in] cose_pub_key A CBOR-encoded \c COSE_Key containing
+ * the public key to use for signature
+ * verification.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * (This has not been implemented yet)
+ *
+ * The key type must work with the signing algorithm in the token
+ * being verified.
+ *
+ * The \c kid in the \c COSE_Key must match the one in the token.
+ *
+ * If there is no kid in the \c COSE_Key it will be used no matter
+ * what kid is indicated in the token.
+ *
+ * Once set, a key can be used for multiple verifications.
+ *
+ * Calling this again will replace the previous key that was
+ * configured. It will also replace the key set by
+ * attest_token_decode_set_pub_key_select().
+ */
+enum attest_token_err_t
+attest_token_decode_set_cose_pub_key(struct attest_token_decode_context *me,
+ struct q_useful_buf cose_pub_key);
+
+
+/**
+ * \brief Set specific public key to use for verification.
+ *
+ * \param[in] me The token decoder context to configure.
+ * \param[in] key_select Selects the key to verify.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * (This has not been implemented yet)
+ *
+ * The key type must work with the signing algorithm in the token
+ * being verified.
+ *
+ * The meaning of key_select depends on the platform this is running
+ * on.
+ *
+ * Once set, a key can be used for multiple verifications.
+ *
+ * Calling this again will replace the previous key that was
+ * configured. It will also replace the key set by
+ * attest_token_decode_set_cose_pub_key().
+ *
+ */
+enum attest_token_err_t
+attest_token_decode_set_pub_key_select(struct attest_token_decode_context *me,
+ int32_t key_select);
+
+
+/**
+ * \brief Set the token to work on and validate its signature.
+ *
+ * \param[in] me The token decoder context to validate with.
+ * \param[in] token The CBOR-encoded token to validate and decode.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * The signature on the token is validated. If it is successful the
+ * token and its payload is remembered in the \ref
+ * attest_token_decode_context \c me so the \c
+ * attest_token_decode_get_xxx() functions can be called to get the
+ * various claims out of it.
+ *
+ * Generally, a public key has to be configured for this to work. It
+ * can however validate short-circuit signatures even if one is not
+ * set.
+ *
+ * The code for any error that occurs during validation is remembered
+ * in decode context. The \c attest_token_decode_get_xxx() functions
+ * can be called and they will just return this error. The \c
+ * attest_token_decode_get_xxx() functions will generally return 0 or
+ * \c NULL if the token is in error.
+ *
+ * It is thus possible to call attest_token_decode_validate_token()
+ * and all the \c attest_token_decode_get_xxx() functions to parse the
+ * token and ignore the error codes as long as
+ * attest_token_decode_get_error() is called before any of the claim
+ * data returned is used.
+ */
+enum attest_token_err_t
+attest_token_decode_validate_token(struct attest_token_decode_context *me,
+ struct q_useful_buf_c token);
+
+
+/**
+ * \brief Get the last decode error.
+ *
+ * \param[in] me The token decoder context.
+ *
+ * \return An error from \ref attest_token_err_t.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_error(struct attest_token_decode_context *me);
+
+
+/**
+ * \brief Get undecoded CBOR payload from the token.
+ *
+ * \param[in] me The token decoder context.
+ * \param[out] payload The returned, verified token payload.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * This will return an error if the signature over the payload did not
+ * validate.
+ *
+ * This allows the caller to parse the payload with any CBOR decoder
+ * they wish to use. It also an "escape hatch" to get to claims in the
+ * token not supported by decoding in this implementation, for example
+ * claims that have non-integer labels.
+ */
+enum attest_token_err_t
+attest_token_decode_get_payload(struct attest_token_decode_context *me,
+ struct q_useful_buf_c *payload);
+
+
+/** Label for bits in \c item_flags in \ref
+ attest_token_iat_simple_t */
+enum attest_token_item_index_t {
+ NONCE_FLAG = 0,
+ UEID_FLAG = 1,
+ BOOT_SEED_FLAG = 2,
+ HW_VERSION_FLAG = 3,
+ IMPLEMENTATION_ID_FLAG = 4,
+ CLIENT_ID_FLAG = 5,
+ SECURITY_LIFECYCLE_FLAG = 6,
+ PROFILE_DEFINITION_FLAG = 7,
+ ORIGINATION_FLAG = 8,
+ NUMBER_OF_ITEMS = 9
+};
+
+
+/**
+ * This structure holds the simple-to-get fields from the
+ * token that can be bundled into one structure.
+ *
+ * This is 7 * 8 + 12 = 72 bytes on a 32-bit machine.
+ */
+struct attest_token_iat_simple_t {
+ struct q_useful_buf_c nonce; /* byte string */
+ struct q_useful_buf_c ueid; /* byte string */
+ struct q_useful_buf_c boot_seed; /* byte string */
+ struct q_useful_buf_c hw_version; /* text string */
+ struct q_useful_buf_c implementation_id; /* byte string */
+ uint32_t security_lifecycle;
+ int32_t client_id;
+ struct q_useful_buf_c profile_definition; /* text string */
+ struct q_useful_buf_c origination; /* text string */
+ uint32_t item_flags;
+};
+
+
+/**
+ * Macro to determine if data item is present in \ref
+ * attest_token_iat_simple_t
+ */
+ #define IS_ITEM_FLAG_SET(item_index, item_flags) \
+ (((0x01U << (item_index))) & (item_flags))
+
+
+/**
+ * \brief Batch fetch of all simple data items in a token.
+ *
+ * \param[in] me The token decoder context.
+ * \param[out] items Structure into which all found items are placed.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * \retval ATTEST_TOKEN_ERR_SUCCESS
+ * Indicates that the token was successfully searched. It
+ * could mean that all the data item were found, only
+ * some were found, or even none were found.
+ *
+ * This searches the token for the simple unstructured data items all
+ * at once. It can be a little more efficient than getting them one by
+ * one.
+ *
+ * Use \ref IS_ITEM_FLAG_SET on \c item_flags in \c
+ * attest_token_iat_simple_t to determine if the data item was found or
+ * not and whether the corresponding member in the structure is valid.
+ */
+enum attest_token_err_t
+attest_token_decode_get_iat_simple(struct attest_token_decode_context *me,
+ struct attest_token_iat_simple_t *items);
+
+
+/**
+ \brief Get the nonce out of the token.
+ *
+ * \param[in] me The token decoder context.
+ * \param[out] nonce Returned pointer and length of nonce.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * The nonce is a byte string. The nonce is also known as the
+ * challenge.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_nonce(struct attest_token_decode_context *me,
+ struct q_useful_buf_c *nonce);
+
+
+/**
+ * \brief Get the boot seed out of the token.
+ *
+ * \param[in] me The token decoder context.
+ * \param[out] boot_seed Returned pointer and length of boot_seed.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * The boot seed is a byte string.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_boot_seed(struct attest_token_decode_context *me,
+ struct q_useful_buf_c *boot_seed);
+
+
+/**
+ * \brief Get the UEID out of the token.
+ *
+ * \param[in] me The token decoder context.
+ * \param[out] ueid Returned pointer and length of ueid.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * The UEID is a byte string.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_ueid(struct attest_token_decode_context *me,
+ struct q_useful_buf_c *ueid);
+
+
+
+/**
+ * \brief Get the HW Version out of the token
+ *
+ * \param[in] me The token decoder context.
+ * \param[out] hw_version Returned pointer and length of
+ * \c hw_version.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * This is also known as the HW ID.
+ *
+ * The HW Version is a UTF-8 text string. It is returned as a pointer
+ * and length. It is NOT \c NULL terminated.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_hw_version(struct attest_token_decode_context *me,
+ struct q_useful_buf_c *hw_version);
+
+
+/**
+ * \brief Get the implementation ID out of the token.
+ *
+ * \param[in] me The token decoder context.
+ * \param[out] implementation_id Returned pointer and length of
+ * implementation_id.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * The implementation ID is a byte string.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_implementation_id(struct attest_token_decode_context*me,
+ struct q_useful_buf_c *implementation_id);
+
+
+/**
+ * \brief Get the origination out of the token.
+ *
+ * \param[in] me The token decoder context.
+ * \param[out] origination Returned pointer and length of origination.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * This is also known as the Verification Service Indicator.
+ *
+ * The \c origination is a UTF-8 text string. It is returned as a
+ * pointer* and length. It is NOT \c NULL terminated.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_origination(struct attest_token_decode_context *me,
+ struct q_useful_buf_c *origination);
+
+
+/**
+ * \brief Get the profile definition out of the token.
+ *
+ * \param[in] me The token decoder context.
+ * \param[out] profile_definition Returned pointer and length of
+ * profile_definition.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * The profile definition is a UTF-8 text string. It is returned as a
+ * pointer and length. It is NOT \c NULL terminated.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_profile_definition(
+ struct attest_token_decode_context *me,
+ struct q_useful_buf_c *profile_definition);
+
+
+/**
+ * \brief Get the client ID out of the token.
+ *
+ * \param[in] me The token decoder context.
+ * \param[out] client_id Returned pointer and length of client_id.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * \retval ATTEST_TOKEN_ERR_INTEGER_VALUE
+ * If integer is larger or smaller than will fit
+ * in an \c int32_t.
+ *
+ * Also called the caller ID.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_client_id(struct attest_token_decode_context *me,
+ int32_t *client_id);
+
+
+/**
+ * \brief Get the security lifecycle out of the token.
+ *
+ * \param[in] me The token decoder context.
+ * \param[out] lifecycle Returned pointer and length of lifecycle.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * \retval ATTEST_TOKEN_ERR_INTEGER_VALUE
+ * If integer is larger
+ * or smaller than will fit in a \c uint32_t.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_security_lifecycle(
+ struct attest_token_decode_context *me,
+ uint32_t *lifecycle);
+
+
+/**
+ * Use \ref IS_ITEM_FLAG_SET macro with these values and \c
+ * attest_token_sw_component_t.item_flags to find out if the
+ * data item is filled in in the attest_token_sw_component_t structure.
+ *
+ * Items that are of type \c struct \c q_useful_buf_c will also be \c
+ * NULL_Q_USEFUL_BUF_C when they are absent.
+ */
+enum attest_token_sw_index_t {
+ SW_MEASUREMENT_TYPE_FLAG = 0,
+ SW_MEASURMENT_VAL_FLAG = 1,
+ /* Reserved: 2 */
+ SW_VERSION_FLAG = 3,
+ SW_SIGNER_ID_FLAG = 5,
+ SW_MEASUREMENT_DESC_FLAG = 6,
+};
+
+/**
+ * Structure to hold one SW component
+ *
+ * This is about 50 bytes on a 32-bit machine and 100 on a 64-bit
+ * machine.
+ *
+ * There will probably be an expanded version of this when more is
+ * added to describe a SW component.
+ */
+struct attest_token_sw_component_t {
+ struct q_useful_buf_c measurement_type; /* text string */
+ struct q_useful_buf_c measurement_val; /* binary string */
+ struct q_useful_buf_c version; /* text string */
+ struct q_useful_buf_c signer_id; /* binary string */
+ struct q_useful_buf_c measurement_desc; /* text string */
+ uint32_t item_flags;
+};
+
+
+/**
+ * \brief Get the number of SW components in the token
+ *
+ * \param[in] me The token decoder context.
+ * \param[out] num_sw_components The number of SW components in the
+ * token.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * If there are explicitly no SW components, this will return successfully
+ * and the \c num_sw_components will be zero.
+ *
+ * Per Arm's IAT specification the only two ways this will succeed
+ * are.
+ * - The SW components array is present and has one or more (not zero)
+ * SW components and the "no SW Components" claim is absent.
+ * - The "no SW Components" integer claim is present, its value
+ * is 1, and the SW Components array is absent.
+ */
+enum attest_token_err_t
+attest_token_get_num_sw_components(struct attest_token_decode_context *me,
+ uint32_t *num_sw_components);
+
+
+/**
+ * \brief Get the nth SW component.
+ *
+ * \param[in] me The token decoder context.
+ * \param[in] requested_index Index, from 0 to num_sw_components,
+ * of request component.
+ * \param[out] sw_components Place to return the details of the
+ * SW component
+ *
+ * \retval ATTEST_TOKEN_ERR_NOT_FOUND
+ * There were not \c requested_index in the token.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_TYPE
+ * The claim labeled to contain SW components is not an array.
+ */
+enum attest_token_err_t
+attest_token_get_sw_component(struct attest_token_decode_context *me,
+ uint32_t requested_index,
+ struct attest_token_sw_component_t *sw_components);
+
+
+/**
+ *
+ * \brief Get a top-level claim, by integer label that is a byte
+ * string.
+ *
+ * \param[in] me The token decoder context.
+ * \param[in] label The integer label identifying the claim.
+ * \param[out] claim The byte string or \c NULL_Q_USEFUL_BUF_C.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_STRUCTURE
+ * General structure of the token is incorrect, for example
+ * the top level is not a map or some map wasn't closed.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED
+ * CBOR syntax is wrong and it is not decodable.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_TYPE
+ * Returned if the claim is not a byte string.
+ *
+ * \retval ATTEST_TOKEN_ERR_NOT_FOUND
+ * Data item for \c label was not found in token.
+ *
+ * If an error occurs, the claim will be set to \c NULL_Q_USEFUL_BUF_C
+ * and the error state inside \c attest_token_decode_context will
+ * be set.
+ */
+enum attest_token_err_t
+attest_token_decode_get_bstr(struct attest_token_decode_context *me,
+ int32_t label,
+ struct q_useful_buf_c *claim);
+
+
+/**
+ * \brief Get a top-level claim, by integer label that is a text
+ * string.
+ *
+ * \param[in] me The token decoder context.
+ * \param[in] label The integer label identifying the claim.
+ * \param[out] claim The byte string or \c NULL_Q_USEFUL_BUF_C.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_STRUCTURE
+ * General structure of the token is incorrect, for example
+ * the top level is not a map or some map wasn't closed.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED
+ * CBOR syntax is wrong and it is not decodable.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_TYPE
+ * Returned if the claim is not a byte string.
+ *
+ * \retval ATTEST_TOKEN_ERR_NOT_FOUND
+ * Data item for \c label was not found in token.
+ *
+ * Even though this is a text string, it is not NULL-terminated.
+ *
+ * If an error occurs, the claim will be set to \c NULL_Q_USEFUL_BUF_C
+ * and the error state inside \c attest_token_decode_context will
+ * be set.
+ */
+enum attest_token_err_t
+attest_token_decode_get_tstr(struct attest_token_decode_context *me,
+ int32_t label,
+ struct q_useful_buf_c *claim);
+
+
+
+/**
+ * \brief Get a top-level claim by integer label who's value is a
+ * signed integer
+ *
+ * \param[in] me The token decoder context.
+ * \param[in] label The integer label identifying the claim.
+ * \param[out] claim The signed integer or 0.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_STRUCTURE
+ * General structure of the token is incorrect, for example
+ * the top level is not a map or some map wasn't closed.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED
+ * CBOR syntax is wrong and it is not decodable.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_TYPE
+ * Returned if the claim is not a byte string.
+ *
+ * \retval ATTEST_TOKEN_ERR_NOT_FOUND
+ * Data item for \c label was not found in token.
+ *
+ * \retval ATTEST_TOKEN_ERR_INTEGER_VALUE
+ * Returned if the integer value is larger
+ * than \c INT64_MAX.
+ *
+ * This will succeed if the CBOR type of the claim is either a
+ * positive or negative integer as long as the value is between \c
+ * INT64_MIN and \c INT64_MAX.
+ *
+ * See also attest_token_decode_get_uint().
+ *
+ * If an error occurs the value 0 will be returned and the error
+ * inside the \c attest_token_decode_context will be set.
+ */
+enum attest_token_err_t
+attest_token_decode_get_int(struct attest_token_decode_context *me,
+ int32_t label,
+ int64_t *claim);
+
+
+/**
+ * \brief Get a top-level claim by integer label who's value is an
+ * unsigned integer
+ *
+ * \param[in] me The token decoder context.
+ * \param[in] label The integer label identifying the claim.
+ * \param[out] claim The unsigned integer or 0.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_STRUCTURE
+ * General structure of the token is incorrect, for example
+ * the top level is not a map or some map wasn't closed.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED
+ * CBOR syntax is wrong and it is not decodable.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_TYPE
+ * Returned if the claim is not a byte string.
+ *
+ * \retval ATTEST_TOKEN_ERR_NOT_FOUND
+ * Data item for \c label was not found in token.
+ *
+ * \retval ATTEST_TOKEN_ERR_INTEGER_VALUE
+ * Returned if the integer value is negative.
+ *
+ * This will succeed if the CBOR type of the claim is either a
+ * positive or negative integer as long as the value is between 0 and
+ * \c MAX_UINT64.
+ *
+ * See also attest_token_decode_get_int().
+ *
+ * If an error occurs the value 0 will be returned and the error
+ * inside the \c attest_token_decode_context will be set.
+ */
+enum attest_token_err_t
+attest_token_decode_get_uint(struct attest_token_decode_context *me,
+ int32_t label,
+ uint64_t *claim);
+
+
+
+
+/* ====================================================================
+ * Inline Implementations
+ * Typically, these are small and called only once.
+ * ==================================================================== */
+
+static inline enum attest_token_err_t
+attest_token_decode_get_error(struct attest_token_decode_context *me)
+{
+ return me->last_error;
+}
+
+
+static inline enum attest_token_err_t
+attest_token_decode_get_nonce(struct attest_token_decode_context *me,
+ struct q_useful_buf_c *nonce)
+{
+ return attest_token_decode_get_bstr(me,
+ EAT_CBOR_ARM_LABEL_CHALLENGE,
+ nonce);
+}
+
+
+static inline enum attest_token_err_t
+attest_token_decode_get_ueid(struct attest_token_decode_context *me,
+ struct q_useful_buf_c *ueid)
+{
+ return attest_token_decode_get_bstr(me, EAT_CBOR_ARM_LABEL_UEID, ueid);
+}
+
+
+static inline enum attest_token_err_t
+attest_token_decode_get_boot_seed(struct attest_token_decode_context *me,
+ struct q_useful_buf_c *boot_seed)
+{
+ return attest_token_decode_get_bstr(me,
+ EAT_CBOR_ARM_LABEL_BOOT_SEED,
+ boot_seed);
+}
+
+
+static inline enum attest_token_err_t
+attest_token_decode_get_hw_version(struct attest_token_decode_context *me,
+ struct q_useful_buf_c *hw_version)
+{
+ return attest_token_decode_get_tstr(me,
+ EAT_CBOR_ARM_LABEL_HW_VERSION,
+ hw_version);
+}
+
+
+static inline enum attest_token_err_t
+attest_token_decode_get_implementation_id(
+ struct attest_token_decode_context *me,
+ struct q_useful_buf_c*implementation_id)
+{
+ return attest_token_decode_get_bstr(me,
+ EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID,
+ implementation_id);
+}
+
+
+static inline enum attest_token_err_t
+attest_token_decode_get_client_id(struct attest_token_decode_context *me,
+ int32_t *caller_id)
+{
+ enum attest_token_err_t return_value;
+ int64_t caller_id_64;
+
+ return_value = attest_token_decode_get_int(me,
+ EAT_CBOR_ARM_LABEL_CLIENT_ID,
+ &caller_id_64);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ goto Done;
+ }
+ if(caller_id_64 > INT32_MAX || caller_id_64 < INT32_MIN) {
+ return_value = ATTEST_TOKEN_ERR_INTEGER_VALUE;
+ goto Done;
+ }
+ *caller_id = (int32_t)caller_id_64;
+
+Done:
+ return return_value;
+}
+
+
+static inline enum attest_token_err_t
+attest_token_decode_get_security_lifecycle(
+ struct attest_token_decode_context *me,
+ uint32_t *security_lifecycle)
+{
+ enum attest_token_err_t return_value;
+ uint64_t security_lifecycle_64;
+
+ return_value = attest_token_decode_get_uint(me,
+ EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE,
+ &security_lifecycle_64);
+ if(security_lifecycle_64 > UINT32_MAX) {
+ return_value = ATTEST_TOKEN_ERR_INTEGER_VALUE;
+ goto Done;
+ }
+
+ *security_lifecycle = (uint32_t)security_lifecycle_64;
+
+Done:
+ return return_value;
+}
+
+static inline enum attest_token_err_t
+attest_token_decode_get_profile_definition(
+ struct attest_token_decode_context *me,
+ struct q_useful_buf_c *profile_definition)
+{
+ return attest_token_decode_get_tstr(me,
+ EAT_CBOR_ARM_LABEL_PROFILE_DEFINITION,
+ profile_definition);
+}
+
+static inline enum attest_token_err_t
+attest_token_decode_get_origination(struct attest_token_decode_context*me,
+ struct q_useful_buf_c *origination)
+{
+ return attest_token_decode_get_tstr(me,
+ EAT_CBOR_ARM_LABEL_ORIGINATION,
+ origination);
+}
+
+/**
+
+ \brief Map t_cose errors into attestation token errors
+
+ \param[in] t_cose_error The t_cose error to map
+
+ \return The attestation token error.
+ */
+static inline enum attest_token_err_t
+map_t_cose_errors(enum t_cose_err_t t_cose_error)
+{
+ switch (t_cose_error) {
+ case T_COSE_SUCCESS:
+ return ATTEST_TOKEN_ERR_SUCCESS;
+ break;
+ case T_COSE_ERR_UNSUPPORTED_SIGNING_ALG:
+ return ATTEST_TOKEN_ERR_UNSUPPORTED_SIG_ALG;
+ break;
+ case T_COSE_ERR_UNSUPPORTED_HASH:
+ return ATTEST_TOKEN_ERR_HASH_UNAVAILABLE;
+ break;
+ case T_COSE_ERR_CBOR_NOT_WELL_FORMED:
+ return ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+ break;
+ case T_COSE_ERR_INSUFFICIENT_MEMORY:
+ return ATTEST_TOKEN_ERR_INSUFFICIENT_MEMORY;
+ break;
+ case T_COSE_ERR_TAMPERING_DETECTED:
+ return ATTEST_TOKEN_ERR_TAMPERING_DETECTED;
+ break;
+ case T_COSE_ERR_CBOR_FORMATTING:
+ return ATTEST_TOKEN_ERR_CBOR_FORMATTING;
+ break;
+ case T_COSE_ERR_TOO_SMALL:
+ return ATTEST_TOKEN_ERR_TOO_SMALL;
+ break;
+
+ case T_COSE_ERR_PARAMETER_CBOR:
+ case T_COSE_ERR_NON_INTEGER_ALG_ID:
+ return ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
+ break;
+
+ case T_COSE_ERR_SIG_VERIFY:
+ case T_COSE_ERR_SHORT_CIRCUIT_SIG:
+ return ATTEST_TOKEN_ERR_COSE_VALIDATION;
+ break;
+
+ case T_COSE_ERR_SIGN1_FORMAT:
+ return ATTEST_TOKEN_ERR_COSE_FORMAT;
+ break;
+
+ case T_COSE_ERR_MAC0_FORMAT:
+ return ATTEST_TOKEN_ERR_COSE_FORMAT;
+ break;
+
+ case T_COSE_ERR_NO_ALG_ID:
+ case T_COSE_ERR_NO_KID:
+ case T_COSE_ERR_BAD_SHORT_CIRCUIT_KID:
+ case T_COSE_ERR_SIG_STRUCT:
+ return ATTEST_TOKEN_ERR_COSE_FORMAT;
+ break;
+
+ case T_COSE_ERR_UNKNOWN_KEY:
+ case T_COSE_ERR_WRONG_TYPE_OF_KEY:
+ return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
+ break;
+
+ case T_COSE_ERR_MAKING_PROTECTED:
+ case T_COSE_ERR_HASH_GENERAL_FAIL:
+ case T_COSE_ERR_HASH_BUFFER_SIZE:
+ case T_COSE_ERR_SIG_BUFFER_SIZE:
+ case T_COSE_ERR_INVALID_ARGUMENT:
+ case T_COSE_ERR_FAIL:
+ case T_COSE_ERR_SIG_FAIL:
+ case T_COSE_ERR_TOO_MANY_PARAMETERS:
+ case T_COSE_ERR_UNKNOWN_CRITICAL_PARAMETER:
+ case T_COSE_ERR_SHORT_CIRCUIT_SIG_DISABLED:
+ case T_COSE_ERR_INCORRECT_KEY_FOR_LIB:
+ case T_COSE_ERR_BAD_CONTENT_TYPE:
+ case T_COSE_ERR_INCORRECTLY_TAGGED:
+ case T_COSE_ERR_EMPTY_KEY:
+ case T_COSE_ERR_DUPLICATE_PARAMETER:
+ case T_COSE_ERR_PARAMETER_NOT_PROTECTED:
+ case T_COSE_ERR_CRIT_PARAMETER:
+ default:
+ return ATTEST_TOKEN_ERR_GENERAL;
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __ATTEST_TOKEN_DECODE_H__ */
diff --git a/test/suites/attestation/attest_token_decode_common.c b/test/suites/attestation/attest_token_decode_common.c
new file mode 100644
index 0000000..52987c9
--- /dev/null
+++ b/test/suites/attestation/attest_token_decode_common.c
@@ -0,0 +1,601 @@
+/*
+ * attest_token_decode_common.c
+ *
+ * Copyright (c) 2019, Laurence Lundblade.
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "attest_token_decode.h"
+#include "attestation.h"
+#include "q_useful_buf.h"
+#include "qcbor_util.h"
+
+/**
+ * \file attest_token_decode_common.c
+ *
+ * \brief Common functions in attestation token decoder.
+ *
+ * This decodes and verifies an attestation token giving access to the
+ * data items in the token. The data items are also known as claims.
+ *
+ * This is written primarily as tests for the token encoder, though it
+ * close to a full commercial token decoder. The main thing missing is
+ * a thorough test suite for it. Test before commercial use is
+ * important as this is a parser / decoder and thus subject to attack
+ * by malicious input. It does however, use QCBOR for most base
+ * parsing, and QCBOR is thoroughly tested and commercial.
+ *
+ * This is oriented around the Arm-defined initial attestation token.
+ *
+ * \c uint_fast8_t is used for type and nest levels. They are
+ * 8-bit quantities, but making using uint8_t variables
+ * and parameters can result in bigger, slower code.
+ * \c uint_fast8_t is part of \c <stdint.h>. It is not
+ * used in structures where it is more important to keep
+ * the size smaller.
+ */
+
+
+/**
+ * Compute the bit indicating a claim is present
+ */
+#define CLAIM_PRESENT_BIT(item_index) (0x01U << (item_index))
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+void attest_token_decode_init(struct attest_token_decode_context *me,
+ uint32_t options)
+{
+ memset(me, 0, sizeof(struct attest_token_decode_context));
+ me->options = options;
+ me->last_error = ATTEST_TOKEN_ERR_NO_VALID_TOKEN;
+}
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_bstr(struct attest_token_decode_context *me,
+ int32_t label,
+ struct q_useful_buf_c *claim)
+{
+ enum attest_token_err_t return_value;
+ QCBORItem item;
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ *claim = NULL_Q_USEFUL_BUF_C;
+ goto Done;
+ }
+
+ return_value = qcbor_util_get_top_level_item_in_map(me->payload,
+ label,
+ QCBOR_TYPE_BYTE_STRING,
+ &item);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ goto Done;
+ }
+
+ *claim = item.val.string;
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_tstr(struct attest_token_decode_context *me,
+ int32_t label,
+ struct q_useful_buf_c *claim)
+{
+ enum attest_token_err_t return_value;
+ QCBORItem item;
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ *claim = NULL_Q_USEFUL_BUF_C;
+ goto Done;
+ }
+
+ return_value = qcbor_util_get_top_level_item_in_map(me->payload,
+ label,
+ QCBOR_TYPE_TEXT_STRING,
+ &item);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ goto Done;
+ }
+
+ *claim = item.val.string;
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_int(struct attest_token_decode_context *me,
+ int32_t label,
+ int64_t *integer)
+{
+ enum attest_token_err_t return_value;
+ QCBORItem item;
+ QCBORDecodeContext decode_context;
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ *integer = 0;
+ goto Done;
+ }
+
+ QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
+
+ return_value = qcbor_util_get_item_in_map(&decode_context,
+ label,
+ &item);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ goto Done;
+ }
+
+ if(QCBORDecode_Finish(&decode_context)) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
+ }
+
+ if(item.uDataType == QCBOR_TYPE_INT64) {
+ *integer = item.val.int64;
+ } else if(item.uDataType == QCBOR_TYPE_UINT64) {
+ if(item.val.uint64 < INT64_MAX) {
+ *integer = (int64_t)item.val.uint64;
+ } else {
+ return_value = ATTEST_TOKEN_ERR_INTEGER_VALUE;
+ }
+ } else {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ }
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_uint(struct attest_token_decode_context *me,
+ int32_t label,
+ uint64_t *integer)
+{
+ enum attest_token_err_t return_value;
+ QCBORItem item;
+ QCBORDecodeContext decode_context;
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ *integer = 0;
+ goto Done;
+ }
+
+ QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
+
+ return_value = qcbor_util_get_item_in_map(&decode_context,
+ label,
+ &item);
+ if(return_value != 0) {
+ goto Done;
+ }
+
+ if(QCBORDecode_Finish(&decode_context)) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
+ }
+
+ if(item.uDataType == QCBOR_TYPE_UINT64) {
+ *integer = item.val.uint64;
+ } else if(item.uDataType == QCBOR_TYPE_INT64) {
+ if(item.val.int64 >= 0) {
+ *integer = (uint64_t)item.val.int64;
+ } else {
+ return_value = ATTEST_TOKEN_ERR_INTEGER_VALUE;
+ }
+ } else {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ }
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_payload(struct attest_token_decode_context *me,
+ struct q_useful_buf_c *payload)
+{
+ enum attest_token_err_t return_value;
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ *payload = NULL_Q_USEFUL_BUF_C;
+ goto Done;
+ }
+
+ if(q_useful_buf_c_is_null_or_empty(me->payload)) {
+ return_value = ATTEST_TOKEN_ERR_NO_VALID_TOKEN;
+ goto Done;
+ }
+
+ *payload = me->payload;
+ return_value = ATTEST_TOKEN_ERR_SUCCESS;
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_iat_simple(struct attest_token_decode_context *me,
+ struct attest_token_iat_simple_t *items)
+{
+ struct qcbor_util_items_to_get_t list[NUMBER_OF_ITEMS+1];
+ QCBORDecodeContext decode_context;
+ int64_t client_id_64;
+ enum attest_token_err_t return_value;
+
+ /* Set all q_useful_bufs to NULL and flags to 0 */
+ memset(items, 0, sizeof(struct attest_token_iat_simple_t));
+
+ /* Re use flags as array indexes because it works nicely */
+ list[NONCE_FLAG].label = EAT_CBOR_ARM_LABEL_CHALLENGE;
+ list[UEID_FLAG].label = EAT_CBOR_ARM_LABEL_UEID;
+ list[BOOT_SEED_FLAG].label = EAT_CBOR_ARM_LABEL_BOOT_SEED;
+ list[HW_VERSION_FLAG].label = EAT_CBOR_ARM_LABEL_HW_VERSION;
+ list[IMPLEMENTATION_ID_FLAG].label = EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID;
+ list[CLIENT_ID_FLAG].label = EAT_CBOR_ARM_LABEL_CLIENT_ID;
+ list[SECURITY_LIFECYCLE_FLAG].label = EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE;
+ list[PROFILE_DEFINITION_FLAG].label = EAT_CBOR_ARM_LABEL_PROFILE_DEFINITION;
+ list[ORIGINATION_FLAG].label = EAT_CBOR_ARM_LABEL_ORIGINATION;
+ list[NUMBER_OF_ITEMS].label = 0; /* terminate the list. */
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ goto Done;
+ }
+
+ QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
+
+ return_value = qcbor_util_get_items_in_map(&decode_context,
+ list);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ goto Done;
+ }
+
+ /* ---- NONCE ---- */
+ if(list[NONCE_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
+ items->nonce = list[NONCE_FLAG].item.val.string;
+ items->item_flags |= CLAIM_PRESENT_BIT(NONCE_FLAG);
+ }
+
+ /* ---- UEID -------*/
+ if(list[UEID_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
+ items->ueid = list[UEID_FLAG].item.val.string;
+ items->item_flags |= CLAIM_PRESENT_BIT(UEID_FLAG);
+ }
+
+ /* ---- BOOT SEED -------*/
+ if(list[BOOT_SEED_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
+ items->boot_seed = list[BOOT_SEED_FLAG].item.val.string;
+ items->item_flags |= CLAIM_PRESENT_BIT(BOOT_SEED_FLAG);\
+ }
+
+ /* ---- HW VERSION -------*/
+ if(list[HW_VERSION_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
+ items->hw_version = list[HW_VERSION_FLAG].item.val.string;
+ items->item_flags |= CLAIM_PRESENT_BIT(HW_VERSION_FLAG);
+
+ }
+
+ /* ----IMPLEMENTATION ID -------*/
+ if(list[IMPLEMENTATION_ID_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
+ items->implementation_id = list[IMPLEMENTATION_ID_FLAG].item.val.string;
+ items->item_flags |= CLAIM_PRESENT_BIT(IMPLEMENTATION_ID_FLAG);
+ }
+
+ /* ----CLIENT ID -------*/
+ if(list[CLIENT_ID_FLAG].item.uDataType == QCBOR_TYPE_INT64) {
+ client_id_64 = list[CLIENT_ID_FLAG].item.val.int64;
+ if(client_id_64 < INT32_MAX || client_id_64 > INT32_MIN) {
+ items->client_id = (int32_t)client_id_64;
+ items->item_flags |= CLAIM_PRESENT_BIT(CLIENT_ID_FLAG);
+ }
+ }
+
+ /* ----SECURITY LIFECYCLE -------*/
+ if(list[SECURITY_LIFECYCLE_FLAG].item.uDataType == QCBOR_TYPE_INT64) {
+ if(list[SECURITY_LIFECYCLE_FLAG].item.val.int64 < UINT32_MAX) {
+ items->security_lifecycle =
+ (uint32_t)list[SECURITY_LIFECYCLE_FLAG].item.val.int64;
+ items->item_flags |=CLAIM_PRESENT_BIT(SECURITY_LIFECYCLE_FLAG);
+ }
+ }
+
+ /* ---- PROFILE_DEFINITION -------*/
+ if(list[PROFILE_DEFINITION_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
+ items->profile_definition=list[PROFILE_DEFINITION_FLAG].item.val.string;
+ items->item_flags |= CLAIM_PRESENT_BIT(PROFILE_DEFINITION_FLAG);
+ }
+
+ /* ---- ORIGINATION -------*/
+ if(list[ORIGINATION_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
+ items->origination = list[ORIGINATION_FLAG].item.val.string;
+ items->item_flags |= CLAIM_PRESENT_BIT(ORIGINATION_FLAG);
+ }
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_get_num_sw_components(struct attest_token_decode_context *me,
+ uint32_t *num_sw_components)
+{
+ enum attest_token_err_t return_value;
+ QCBORItem item;
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ goto Done;
+ }
+
+ return_value = qcbor_util_get_top_level_item_in_map(me->payload,
+ EAT_CBOR_ARM_LABEL_SW_COMPONENTS,
+ QCBOR_TYPE_ARRAY,
+ &item);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ if(return_value != ATTEST_TOKEN_ERR_NOT_FOUND) {
+ /* Something very wrong. Bail out passing on the return_value */
+ goto Done;
+ } else {
+ /* Now decide if it was intentionally left out. */
+ return_value = qcbor_util_get_top_level_item_in_map(me->payload,
+ EAT_CBOR_ARM_LABEL_NO_SW_COMPONENTS,
+ QCBOR_TYPE_INT64,
+ &item);
+ if(return_value == ATTEST_TOKEN_ERR_SUCCESS) {
+ if(item.val.int64 == NO_SW_COMPONENT_FIXED_VALUE) {
+ /* Successful omission of SW components. Pass on the
+ * success return_value */
+ *num_sw_components = 0;
+ } else {
+ /* Indicator for no SW components malformed */
+ return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
+ }
+ } else if(return_value == ATTEST_TOKEN_ERR_NOT_FOUND) {
+ /* Should have been an indicator for no SW components */
+ return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
+ }
+ }
+ } else {
+ /* The SW components claim exists */
+ if(item.val.uCount == 0) {
+ /* Empty SW component not allowed */
+ return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
+ } else {
+ /* SUCESSS! Pass on the success return_value */
+ /* Note that this assumes the array is definite length */
+ *num_sw_components = item.val.uCount;
+ }
+ }
+
+Done:
+ return return_value;
+}
+
+
+/**
+ * \brief Decode a single SW component
+ *
+ * \param[in] decode_context The CBOR decoder context to decode from
+ * \param[in] sw_component_item The top-level map item for this SW
+ * component.
+ * \param[out] sw_component The structure to fill in with decoded data.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ */
+static inline enum attest_token_err_t
+decode_sw_component(QCBORDecodeContext *decode_context,
+ const QCBORItem *sw_component_item,
+ struct attest_token_sw_component_t *sw_component)
+{
+ enum attest_token_err_t return_value;
+ QCBORItem claim_item;
+ QCBORError cbor_error;
+ uint_fast8_t next_nest_level; /* nest levels are 8-bit, but a uint8_t
+ var is often slower and more code */
+
+ if(sw_component_item->uDataType != QCBOR_TYPE_MAP) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
+ goto Done;
+ }
+
+ /* Zero it, setting booleans to false, pointers to NULL and
+ lengths to 0 */
+ memset(sw_component, 0, sizeof(struct attest_token_sw_component_t));
+
+ return_value = ATTEST_TOKEN_ERR_SUCCESS;
+
+ while(1) {
+ cbor_error = QCBORDecode_GetNext(decode_context, &claim_item);
+ if(cbor_error != QCBOR_SUCCESS) {
+ /* no tolerance for any errors here */
+ return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+ goto Done;
+ }
+
+ if(claim_item.uLabelType == QCBOR_TYPE_INT64) {
+ switch(claim_item.label.int64) {
+ case EAT_CBOR_SW_COMPONENT_MEASUREMENT_TYPE:
+ if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ goto Done;
+ }
+ sw_component->measurement_type = claim_item.val.string;
+ sw_component->item_flags |=
+ CLAIM_PRESENT_BIT(SW_MEASUREMENT_TYPE_FLAG);
+
+ break;
+
+ case EAT_CBOR_SW_COMPONENT_MEASUREMENT_VALUE:
+ if(claim_item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ goto Done;
+ }
+ sw_component->measurement_val = claim_item.val.string;
+ sw_component->item_flags |=
+ CLAIM_PRESENT_BIT(SW_MEASURMENT_VAL_FLAG);
+ break;
+
+ case EAT_CBOR_SW_COMPONENT_VERSION:
+ if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ goto Done;
+ }
+ sw_component->version = claim_item.val.string;
+ sw_component->item_flags |=
+ CLAIM_PRESENT_BIT(SW_VERSION_FLAG);
+ break;
+
+ case EAT_CBOR_SW_COMPONENT_SIGNER_ID:
+ if(claim_item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ goto Done;
+ }
+ sw_component->signer_id = claim_item.val.string;
+ sw_component->item_flags |=
+ CLAIM_PRESENT_BIT(SW_SIGNER_ID_FLAG);
+ break;
+
+ case EAT_CBOR_SW_COMPONENT_MEASUREMENT_DESC:
+ if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ goto Done;
+ }
+ sw_component->measurement_desc = claim_item.val.string;
+ sw_component->item_flags |=
+ CLAIM_PRESENT_BIT(SW_MEASUREMENT_DESC_FLAG);
+ break;
+ }
+ }
+
+ if(qcbor_util_consume_item(decode_context,
+ &claim_item,
+ &next_nest_level)) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+ goto Done;
+ }
+ if(next_nest_level < sw_component_item->uNextNestLevel) {
+ /* Got all the items in the map */
+ break;
+ }
+ }
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_get_sw_component(struct attest_token_decode_context *me,
+ uint32_t requested_index,
+ struct attest_token_sw_component_t *sw_components)
+{
+ enum attest_token_err_t return_value;
+ QCBORItem sw_components_array_item;
+ QCBORDecodeContext decode_context;
+ QCBORItem sw_component_item;
+ QCBORError qcbor_error;
+ uint_fast8_t exit_array_level;
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ goto Done;
+ }
+
+ QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
+
+ /* Find the map containing all the SW Components */
+ return_value = qcbor_util_decode_to_labeled_item(&decode_context,
+ EAT_CBOR_ARM_LABEL_SW_COMPONENTS,
+ &sw_components_array_item);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ goto Done;
+ }
+
+ if(sw_components_array_item.uDataType != QCBOR_TYPE_ARRAY) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ goto Done;
+ }
+
+ exit_array_level = sw_components_array_item.uNextNestLevel;
+
+ /* Loop over contents of SW Components array */
+ while(1) {
+ qcbor_error = QCBORDecode_GetNext(&decode_context, &sw_component_item);
+ if(qcbor_error) {
+ /* no tolerance for any errors here */
+ return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+ goto Done;
+ }
+
+ if(sw_component_item.uNextNestLevel <= exit_array_level) {
+ /* Next item will be outside the array */
+ return_value = ATTEST_TOKEN_ERR_NOT_FOUND;
+ /* The end of the array containing SW components
+ and didn't get to the requested_index. */
+ goto Done;
+ }
+
+ if(requested_index == 0) {
+ /* Found the one of interest. Decode it and break out */
+ return_value = decode_sw_component(&decode_context,
+ &sw_component_item,
+ sw_components);
+ break; /* The normal, non-error exit from this loop */
+ }
+
+ /* Every member in the array counts even if they are not
+ * what is expected */
+ requested_index--;
+
+ if(qcbor_util_consume_item(&decode_context, &sw_component_item, NULL)) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+ goto Done;
+ }
+ }
+
+Done:
+ return return_value;
+}
diff --git a/test/suites/attestation/attest_token_test.c b/test/suites/attestation/attest_token_test.c
new file mode 100644
index 0000000..52fc9aa
--- /dev/null
+++ b/test/suites/attestation/attest_token_test.c
@@ -0,0 +1,1011 @@
+/*
+ * attest_token_test.c
+ *
+ * Copyright (c) 2018-2019, Laurence Lundblade.
+ * Copyright (c) 2020, Arm Limited.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "attest_token_test.h"
+#include "q_useful_buf.h"
+#include "psa/initial_attestation.h"
+#include "attest_token_decode.h"
+#include "attest_token_test_values.h"
+#include "psa/crypto.h"
+
+
+/**
+ * \file attest_token_test.c
+ *
+ * \brief Implementation for attestation token tests.
+ *
+ * This uses \c attest_token_decode for the main full
+ * test case. That in turn uses t_cose_sign1_verify.
+ * These implementations of token decoding and COSE
+ * sig verification are still considered test
+ * code even though they are getting close to
+ * real implementations that could be used for
+ * other than test.
+ *
+ * Most of the code here is for comparing the
+ * values in the token to expected good
+ * values.
+ */
+
+
+/**
+ * \brief An alternate token_main() that packs the option flags into the nonce.
+ *
+ * \param[in] option_flags Flag bits to pack into nonce.
+ * \param[in] nonce Pointer and length of the nonce.
+ * \param[in] buffer Pointer and length of buffer to
+ * output the token into.
+ * \param[out] completed_token Place to put pointer and length
+ * of completed token.
+ *
+ * \return various errors. See \ref psa_status_t.
+ *
+ */
+int token_main_alt(uint32_t option_flags,
+ struct q_useful_buf_c nonce,
+ struct q_useful_buf buffer,
+ struct q_useful_buf_c *completed_token)
+{
+ psa_status_t return_value;
+ size_t token_buf_size;
+ size_t completed_token_size;
+ struct q_useful_buf_c actual_nonce;
+ Q_USEFUL_BUF_MAKE_STACK_UB( actual_nonce_storage, 64);
+
+ if(nonce.len == 64 && q_useful_buf_is_value(nonce, 0)) {
+ /* Go into special option-packed nonce mode */
+ actual_nonce = q_useful_buf_copy(actual_nonce_storage, nonce);
+ /* Use memcpy as it always works and avoids type punning */
+ memcpy((uint8_t *)actual_nonce_storage.ptr,
+ &option_flags,
+ sizeof(uint32_t));
+ } else {
+ actual_nonce = nonce;
+ }
+
+ token_buf_size = buffer.len;
+ return_value = psa_initial_attest_get_token(actual_nonce.ptr,
+ actual_nonce.len,
+ buffer.ptr,
+ token_buf_size,
+ &completed_token_size);
+
+ *completed_token =
+ (struct q_useful_buf_c){buffer.ptr, completed_token_size};
+
+ if (return_value != PSA_SUCCESS) {
+ return (int)return_value;
+ }
+
+ return 0;
+}
+
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+#ifdef SYMMETRIC_INITIAL_ATTESTATION
+/**
+ * This is the expected output for the minimal test. It is the result
+ * of creating a token with \ref TOKEN_OPT_SHORT_CIRCUIT_SIGN and \ref
+ * TOKEN_OPT_OMIT_CLAIMS set. The nonce is the above constant string
+ * \ref nonce_bytes. The token output is completely deterministic.
+ *
+ * 17(
+ * [
+ * / protected / h'A10105' / {
+ * \ alg \ 1:5 \ HMAC-SHA256 \
+ * } /,
+ * / unprotected / {},
+ * / payload / h'A13A000124FF5840000000C0000000000000000000000
+ * 00000000000000000000000000000000000000000000000000000000000
+ * 0000000000000000000000000000000000000000' / {
+ * / arm_psa_nonce / -75008: h'000000C00000000000000000000
+ * 0000000000000000000000000000000000000000000000000000000
+ * 0000000000000000000000000000000000000000000000,
+ * } /,
+ * / tag / h'966840FC0A60AE968F906D7092E57B205D3BBE83ED47EBBC2
+ * AD9D1CFB41C87F3'
+ * ]
+ * )
+ *
+ * The above is in CBOR Diagnostic notation. See RFC 8152.
+ */
+static const uint8_t expected_minimal_token_bytes[] = {
+ 0xD1, 0x84, 0x43, 0xA1, 0x01, 0x05, 0xA0, 0x58,
+ 0x48, 0xA1, 0x3A, 0x00, 0x01, 0x24, 0xFF, 0x58,
+ 0x40, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x58, 0x20, 0x96, 0x68, 0x40, 0xFC, 0x0A,
+ 0x60, 0xAE, 0x96, 0x8F, 0x90, 0x6D, 0x70, 0x92,
+ 0xE5, 0x7B, 0x20, 0x5D, 0x3B, 0xBE, 0x83, 0xED,
+ 0x47, 0xEB, 0xBC, 0x2A, 0xD9, 0xD1, 0xCF, 0xB4,
+ 0x1C, 0x87, 0xF3
+};
+#else /* SYMMETRIC_INITIAL_ATTESTATION */
+/**
+ * This is the expected output for the minimal test. It is the result
+ * of creating a token with \ref TOKEN_OPT_SHORT_CIRCUIT_SIGN and \ref
+ * TOKEN_OPT_OMIT_CLAIMS set. The nonce is the above constant string
+ * \ref nonce_bytes. The token output is completely deterministic.
+ *
+ * The implementation of TOKEN_OPT_SHORT_CIRCUIT_SIGN always uses the
+ * kid
+ * EF954B4BD9BDF670D0336082F5EF152AF8F35B6A6C00EFA6A9A71F49517E18C6.
+ *
+ * 18(
+ * [
+ * h'A10126', // protected headers
+ * { // unprotected headers
+ * 4: h'EF954B4BD9BDF670D0336082F5EF152AF8F35B6A6C00EFA6A9
+ * A71F49517E18C6'
+ * },
+ * h'A13A000124FF5840000000C0000000000000000000000000000000000
+ * 000000000000000000000000000000000000000000000000000000000
+ * 000000000000000000000000000000',
+ * h'CE52E46D564F1A6DBCEE106341CC80CDC0A3480999AFA8067747CA255
+ * EEDFD8BCE52E46D564F1A6DBCEE106341CC80CDC0A3480999AFA80677
+ * 47CA255EEDFD8B'
+ * ]
+ * )
+ *
+ * The above is in CBOR Diagnostic notation. See RFC 8152.
+ */
+static const uint8_t expected_minimal_token_bytes[] = {
+ 0xD2, 0x84, 0x43, 0xA1, 0x01, 0x26, 0xA1, 0x04,
+ 0x58, 0x20, 0xEF, 0x95, 0x4B, 0x4B, 0xD9, 0xBD,
+ 0xF6, 0x70, 0xD0, 0x33, 0x60, 0x82, 0xF5, 0xEF,
+ 0x15, 0x2A, 0xF8, 0xF3, 0x5B, 0x6A, 0x6C, 0x00,
+ 0xEF, 0xA6, 0xA9, 0xA7, 0x1F, 0x49, 0x51, 0x7E,
+ 0x18, 0xC6, 0x58, 0x48, 0xA1, 0x3A, 0x00, 0x01,
+ 0x24, 0xFF, 0x58, 0x40, 0x00, 0x00, 0x00, 0xC0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x58, 0x40, 0x45, 0x0B,
+ 0x2C, 0x09, 0x68, 0xA1, 0x92, 0xA8, 0x85, 0xBE,
+ 0x59, 0xE5, 0xA0, 0x9B, 0xDA, 0x4A, 0x8B, 0xA3,
+ 0xA6, 0xFC, 0x7F, 0x51, 0x90, 0x35, 0x2D, 0x3A,
+ 0x16, 0xBC, 0x30, 0x7B, 0x50, 0x3D, 0x45, 0x0B,
+ 0x2C, 0x09, 0x68, 0xA1, 0x92, 0xA8, 0x85, 0xBE,
+ 0x59, 0xE5, 0xA0, 0x9B, 0xDA, 0x4A, 0x8B, 0xA3,
+ 0xA6, 0xFC, 0x7F, 0x51, 0x90, 0x35, 0x2D, 0x3A,
+ 0x16, 0xBC, 0x30, 0x7B, 0x50, 0x3D
+};
+#endif /* SYMMETRIC_INITIAL_ATTESTATION */
+
+
+/*
+ * Public function. See token_test.h
+ */
+int_fast16_t minimal_test()
+{
+ int_fast16_t return_value = 0;
+ Q_USEFUL_BUF_MAKE_STACK_UB(token_storage,
+ sizeof(expected_minimal_token_bytes));
+ struct q_useful_buf_c completed_token;
+ struct q_useful_buf_c expected_token;
+
+ return_value =
+ token_main_alt(TOKEN_OPT_SHORT_CIRCUIT_SIGN |
+ TOKEN_OPT_OMIT_CLAIMS,
+ TOKEN_TEST_VALUE_NONCE,
+ token_storage,
+ &completed_token);
+ if(return_value) {
+ goto Done;
+ }
+
+ expected_token =
+ Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(expected_minimal_token_bytes);
+
+ if(q_useful_buf_compare(completed_token, expected_token)) {
+ return_value = -3;
+ }
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See token_test.h
+ */
+int_fast16_t minimal_get_size_test()
+{
+ int_fast16_t return_value = 0;
+ size_t length;
+ struct q_useful_buf_c expected_token;
+ struct q_useful_buf_c nonce;
+
+ nonce = TOKEN_TEST_VALUE_NONCE;
+ expected_token =
+ Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(expected_minimal_token_bytes);
+
+
+ return_value = psa_initial_attest_get_token_size(nonce.len,
+ &length);
+
+ /*
+ * It is not possible to predict the size of the token returned
+ * here because options like TOKEN_OPT_OMIT_CLAIMS and
+ * TOKEN_OPT_SHORT_CIRCUIT_SIGN cannot be passed to limit what it
+ * does. Instead check to see if the size is in a reasonable
+ * range. The minimal_test() will actually check the size for
+ * exactitude because it can pass those options,
+ */
+ if(length < expected_token.len || length > 10000) {
+ return_value = -1;
+ }
+
+ return return_value;
+}
+
+
+/*
+ * Public function. See token_test.h
+ */
+int_fast16_t buffer_too_small_test()
+{
+ int_fast16_t return_value = 0;
+ /*
+ * Construct a buffer with enough capacity in case buffer size check fails
+ * and the token is generated. If a smaller buffer is allocated, the
+ * incorrectly generated token may overwrite and corrupt the data following
+ * this buffer.
+ */
+ Q_USEFUL_BUF_MAKE_STACK_UB(token_storage,
+ sizeof(expected_minimal_token_bytes));
+ struct q_useful_buf_c completed_token;
+ struct q_useful_buf_c nonce;
+
+
+ nonce = TOKEN_TEST_VALUE_NONCE;
+ /* Fake the size and cheat the token generation process. */
+ token_storage.len = sizeof(expected_minimal_token_bytes) - 1;
+
+ return_value = token_main_alt(TOKEN_OPT_SHORT_CIRCUIT_SIGN |
+ TOKEN_OPT_OMIT_CLAIMS,
+ nonce,
+ token_storage,
+ &completed_token);
+
+ if(return_value != PSA_ERROR_BUFFER_TOO_SMALL) {
+ return_value = -1;
+ } else {
+ return_value = 0;
+ }
+
+ return return_value;
+}
+#endif /* INCLUDE_TEST_CODE */
+
+
+/**
+ * \brief Check the simple IAT claims against compiled-in known values
+ *
+ * \param[in] simple_claims Pointer to claims structure to check
+ *
+ * \return 0 means success. Any other value indicates failure. Since
+ * this is just test code there is no formal definition or
+ * documentation of the detailed values on the assumption
+ * that source code for the test is available.
+ *
+ * The compiled values come from token_test_values.h. They can be
+ * #defined as \c NULL, \c NULL_Q_USEFUL_BUF_C or \c MAX_INT32 to disabled
+ * checking for the presence and value of the claim.
+ *
+ * See \ref attest_token_iat_simple_t for the claims that are checked. All
+ * claims in that structure are checked.
+ */
+static int_fast16_t check_simple_claims(
+ const struct attest_token_iat_simple_t *simple_claims)
+{
+ int_fast16_t return_value;
+ /* Use temp variables to make lines less than 80 columns below. */
+ struct q_useful_buf_c tmp;
+ struct q_useful_buf_c tail;
+ /* Use a temporary string variable to make the static analyzer
+ * happy. It doesn't like comparing a string literal to NULL
+ */
+ const char *tmp_string;
+
+ return_value = 0;
+
+ /* -- check value of the nonce claim -- */
+ if(!IS_ITEM_FLAG_SET(NONCE_FLAG, simple_claims->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_NONCE) {
+ /* It should have been present */
+ return_value = -50;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp = TOKEN_TEST_VALUE_NONCE;
+ if(!q_useful_buf_c_is_null(tmp)) {
+ /* request to check for the nonce */
+ if(q_useful_buf_compare(simple_claims->nonce, tmp)) {
+ /* Didn't match in the standard way. See if it is a
+ * special option-packed nonce by checking for length
+ * 64 and all bytes except the first four are 0.
+ * nonce_tail is everything after the first 4 bytes.
+ */
+ tail = q_useful_buf_tail(simple_claims->nonce, 4);
+ if(simple_claims->nonce.len == 64 &&
+ q_useful_buf_is_value(tail, 0) == SIZE_MAX){
+ /* It is an option-packed nonce.
+ * Don't compare the first four bytes.
+ */
+ if(q_useful_buf_compare(q_useful_buf_tail(tmp, 4), tail)) {
+ /* The option-packed nonce didn't match */
+ return_value = -51;
+ goto Done;
+ }
+ } else {
+ /* Just a normal nonce that didn't match */
+ return_value = -51;
+ goto Done;
+ }
+ }
+ }
+ }
+
+ /* -- check value of the UEID claim -- */
+ if(!IS_ITEM_FLAG_SET(UEID_FLAG, simple_claims->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_UEID) {
+ /* It should have been present */
+ return_value = -52;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp = TOKEN_TEST_VALUE_UEID;
+ if(!q_useful_buf_c_is_null(tmp) &&
+ q_useful_buf_compare(simple_claims->ueid, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -53;
+ goto Done;
+ }
+ }
+
+ /* -- check value of the boot seed claim -- */
+ if(!IS_ITEM_FLAG_SET(BOOT_SEED_FLAG, simple_claims->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_BOOT_SEED) {
+ /* It should have been present */
+ return_value = -54;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp = TOKEN_TEST_VALUE_BOOT_SEED;
+ if(!q_useful_buf_c_is_null(tmp) &&
+ q_useful_buf_compare(simple_claims->boot_seed, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -55;
+ goto Done;
+ }
+ }
+
+ /* -- check value of the hw_version claim -- */
+ if(!IS_ITEM_FLAG_SET(HW_VERSION_FLAG, simple_claims->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_HW_VERSION) {
+ /* It should have been present */
+ return_value = -56;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp_string = TOKEN_TEST_VALUE_HW_VERSION;
+ if(tmp_string != NULL) {
+ tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(TOKEN_TEST_VALUE_HW_VERSION);
+ if(q_useful_buf_compare(simple_claims->hw_version, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -57;
+ goto Done;
+ }
+ }
+ }
+
+ /* -- check value of the implementation ID -- */
+ if(!IS_ITEM_FLAG_SET(IMPLEMENTATION_ID_FLAG,simple_claims->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_IMPLEMENTATION_ID) {
+ return_value = -58;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp = TOKEN_TEST_VALUE_IMPLEMENTATION_ID;
+ if(!q_useful_buf_c_is_null(tmp) &&
+ q_useful_buf_compare(simple_claims->implementation_id, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -59;
+ goto Done;
+ }
+ }
+
+ /* -- check value of the security lifecycle claim -- */
+ if(!IS_ITEM_FLAG_SET(SECURITY_LIFECYCLE_FLAG,simple_claims->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_SECURITY_LIFECYCLE) {
+ /* It should have been present */
+ return_value = -60;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ if(TOKEN_TEST_VALUE_SECURITY_LIFECYCLE != INT32_MAX &&
+ simple_claims->security_lifecycle !=
+ TOKEN_TEST_VALUE_SECURITY_LIFECYCLE) {
+ /* Check of its value was requested and failed */
+ return_value = -61;
+ goto Done;
+ }
+ }
+
+ /* -- check value of the client_id claim -- */
+ if(!IS_ITEM_FLAG_SET(CLIENT_ID_FLAG, simple_claims->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_CLIENT_ID) {
+ return_value = -62;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ if(TOKEN_TEST_VALUE_CLIENT_ID != INT32_MAX &&
+ simple_claims->client_id != TOKEN_TEST_VALUE_CLIENT_ID) {
+#if DOMAIN_NS == 1U
+ /* Non-secure caller client ID has to be negative */
+ if(simple_claims->client_id > -1) {
+#else
+ /* Secure caller client ID has to be positive */
+ if(simple_claims->client_id < 1) {
+#endif
+ return_value = -63;
+ goto Done;
+ }
+ }
+ }
+
+ /* -- check value of the profile_definition claim -- */
+ if(!IS_ITEM_FLAG_SET(PROFILE_DEFINITION_FLAG, simple_claims->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_PROFILE_DEFINITION) {
+ /* It should have been present */
+ return_value = -64;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp_string = TOKEN_TEST_VALUE_PROFILE_DEFINITION;
+ if(tmp_string != NULL) {
+ tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(
+ TOKEN_TEST_VALUE_PROFILE_DEFINITION);
+ if(q_useful_buf_compare(simple_claims->profile_definition, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -65;
+ goto Done;
+ }
+ }
+ }
+
+ /* -- check value of the origination claim -- */
+ if(!IS_ITEM_FLAG_SET(ORIGINATION_FLAG, simple_claims->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_ORIGINATION) {
+ /* It should have been present */
+ return_value = -66;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp_string = TOKEN_TEST_VALUE_ORIGINATION;
+ if(tmp_string != NULL) {
+ tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(TOKEN_TEST_VALUE_ORIGINATION);
+ if(q_useful_buf_compare(simple_claims->origination, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -67;
+ goto Done;
+ }
+ }
+ }
+
+Done:
+ return return_value;
+}
+
+
+/**
+ * \brief Check a SW component claims against compiled-in known values.
+ *
+ * \param[in] sw_component Pointer to SW component claims structure to check.
+ *
+ * \return 0 means success. Any other value indicates failure. Since
+ * this is just test code there is no formal definition or
+ * documentation of the detailed values on the assumption
+ * that source code for the test is available.
+ *
+ * The compiled values come from token_test_values.h. They can be
+ * #defined as \c NULL, \c NULL_Q_USEFUL_BUF_C or \c MAX_INT32 to
+ * disabled checking for the presence and value of the claim.
+ *
+ * See \ref attest_token_sw_component_t for the claims that are checked. All
+ * claims in that structure are checked.
+ *
+ * This checks for the "first" SW component. See check_sw_component_2().
+ */
+static int_fast16_t check_sw_component_1(
+ const struct attest_token_sw_component_t *sw_component)
+{
+ int_fast16_t return_value;
+ /* Use a temp variable to make lines less than 80 columns below. */
+ struct q_useful_buf_c tmp;
+ /* Use a temporary string variable to make the static analyzer
+ * happy. It doesn't like comparing a string literal to NULL
+ */
+ const char *tmp_string;
+
+ return_value = 0;
+
+ /* -- Check first type -- */
+ if(!IS_ITEM_FLAG_SET(SW_MEASUREMENT_TYPE_FLAG, sw_component->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_SWC1_MEASUREMENT_TYPE) {
+ /* It should have been present */
+ return_value = -100;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp_string = TOKEN_TEST_VALUE_SWC1_MEASUREMENT_TYPE;
+ if(tmp_string != NULL) {
+ tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(
+ TOKEN_TEST_VALUE_SWC1_MEASUREMENT_TYPE);
+ if(q_useful_buf_compare(sw_component->measurement_type, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -101;
+ goto Done;
+ }
+ }
+ }
+
+ /* -- Check first measurement -- */
+ if(!IS_ITEM_FLAG_SET(SW_MEASURMENT_VAL_FLAG, sw_component->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_SWC1_MEASUREMENT_VAL) {
+ /* It should have been present */
+ return_value = -102;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp = TOKEN_TEST_VALUE_SWC1_MEASUREMENT_VAL;
+ if(!q_useful_buf_c_is_null(tmp) &&
+ q_useful_buf_compare(sw_component->measurement_val, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -103;
+ goto Done;
+ }
+ }
+
+ /* -- Check first version -- */
+ if(!IS_ITEM_FLAG_SET(SW_VERSION_FLAG, sw_component->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_SWC1_VERSION) {
+ /* It should have been present */
+ return_value = -106;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp_string = TOKEN_TEST_VALUE_SWC1_VERSION;
+ if(tmp_string != NULL) {
+ tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(TOKEN_TEST_VALUE_SWC1_VERSION);
+ if(q_useful_buf_compare(sw_component->version, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -107;
+ goto Done;
+ }
+ }
+ }
+
+ /* -- Check first signer ID -- */
+ if(!IS_ITEM_FLAG_SET(SW_SIGNER_ID_FLAG, sw_component->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_SWC1_SIGNER_ID) {
+ /* It should have been present */
+ return_value = -108;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp = TOKEN_TEST_VALUE_SWC1_SIGNER_ID;
+ if(!q_useful_buf_c_is_null(tmp) &&
+ q_useful_buf_compare(sw_component->signer_id, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -109;
+ goto Done;
+ }
+ }
+
+ /* -- Check first measurement description -- */
+ if(!IS_ITEM_FLAG_SET(SW_MEASUREMENT_DESC_FLAG, sw_component->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_SWC1_MEASUREMENT_DESC) {
+ /* It should have been present */
+ return_value = -110;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp_string = TOKEN_TEST_VALUE_SWC1_MEASUREMENT_DESC;
+ if(tmp_string != NULL) {
+ tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(
+ TOKEN_TEST_VALUE_SWC1_MEASUREMENT_DESC);
+ if(q_useful_buf_compare(sw_component->measurement_desc, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -111;
+ goto Done;
+ }
+ }
+ }
+
+ Done:
+ return return_value;
+}
+
+
+/**
+ * \brief Check a SW component claims against compiled-in known values.
+ *
+ * \param[in] sw_component Pointer to SW component claims structure to check.
+ *
+ * \return 0 means success. Any other value indicates failure. Since
+ * this is just test code there is no formal definition or
+ * documentation of the detailed values on the assumption
+ * that source code for the test is available.
+ *
+ * The compiled values come from token_test_values.h. They can be
+ * #defined as \c NULL, \c NULL_Q_USEFUL_BUF_C or \c MAX_INT32 to disabled
+ * checking for the presence and value of the claim.
+ *
+ * See \ref attest_token_sw_component_t for the claims that are checked. All
+ * claims in that structure are checked.
+ *
+ * This checks for the "second" SW component. See check_sw_component_1().
+ */
+static int_fast16_t check_sw_component_2(
+ const struct attest_token_sw_component_t *sw_component)
+{
+ int_fast16_t return_value;
+
+ /* Use a temp variable to make lines less than 80 columns below. */
+ struct q_useful_buf_c tmp;
+ /* Use a temporary string variable to make the static analyzer
+ * happy. It doesn't like comparing a string literal to NULL
+ */
+ const char *tmp_string;
+
+ return_value = 0;
+
+ /* -- Check second type -- */
+ if(!IS_ITEM_FLAG_SET(SW_MEASUREMENT_TYPE_FLAG, sw_component->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_SWC2_MEASUREMENT_TYPE) {
+ /* It should have been present */
+ return_value = -100;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp_string = TOKEN_TEST_VALUE_SWC2_MEASUREMENT_TYPE;
+ if(tmp_string != NULL) {
+ tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(
+ TOKEN_TEST_VALUE_SWC2_MEASUREMENT_TYPE);
+ if(q_useful_buf_compare(sw_component->measurement_type, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -101;
+ goto Done;
+ }
+ }
+ }
+
+ /* -- Check second measurement -- */
+ if(!IS_ITEM_FLAG_SET(SW_MEASURMENT_VAL_FLAG, sw_component->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_SWC2_MEASUREMENT_VAL) {
+ /* It should have been present */
+ return_value = -102;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp = TOKEN_TEST_VALUE_SWC2_MEASUREMENT_VAL;
+ if(!q_useful_buf_c_is_null(tmp) &&
+ q_useful_buf_compare(sw_component->measurement_val, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -103;
+ goto Done;
+ }
+ }
+
+ /* -- Check second version -- */
+ if(!IS_ITEM_FLAG_SET(SW_VERSION_FLAG, sw_component->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_SWC2_VERSION) {
+ /* It should have been present */
+ return_value = -106;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp_string = TOKEN_TEST_VALUE_SWC2_VERSION;
+ if(tmp_string != NULL) {
+ tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(TOKEN_TEST_VALUE_SWC2_VERSION);
+ if(q_useful_buf_compare(sw_component->version, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -107;
+ goto Done;
+ }
+ }
+ }
+
+ /* -- Check second signer ID -- */
+ if(!IS_ITEM_FLAG_SET(SW_SIGNER_ID_FLAG, sw_component->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_SWC2_SIGNER_ID) {
+ /* It should have been present */
+ return_value = -108;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp = TOKEN_TEST_VALUE_SWC2_SIGNER_ID;
+ if(!q_useful_buf_c_is_null(tmp) &&
+ q_useful_buf_compare(sw_component->signer_id, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -109;
+ goto Done;
+ }
+ }
+
+ /* -- Check second measurement description -- */
+ if(!IS_ITEM_FLAG_SET(SW_MEASUREMENT_DESC_FLAG, sw_component->item_flags)) {
+ /* Claim is not present in token */
+ if(TOKEN_TEST_REQUIRE_SWC2_MEASUREMENT_DESC) {
+ /* It should have been present */
+ return_value = -110;
+ goto Done;
+ }
+ } else {
+ /* Claim is present */
+ /* Don't have to check if its presence is required */
+ tmp_string = TOKEN_TEST_VALUE_SWC2_MEASUREMENT_DESC;
+ if(tmp_string != NULL) {
+ tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(
+ TOKEN_TEST_VALUE_SWC2_MEASUREMENT_DESC);
+ if(q_useful_buf_compare(sw_component->measurement_desc, tmp)) {
+ /* Check of its value was requested and failed */
+ return_value = -111;
+ goto Done;
+ }
+ }
+ }
+
+Done:
+ return return_value;
+}
+
+/**
+ * Modes for decode_test_internal()
+ */
+enum decode_test_mode_t {
+ /** See documentation for decode_test_short_circuit_sig() */
+ SHORT_CIRCUIT_SIGN,
+ /** See documentation for decode_test_normal_sig() */
+ NORMAL_SIGN,
+ /** See documentation for decode_test_symmetric_initial_attest() */
+ COSE_MAC0,
+ /** See documentation for decode_test_symmetric_iat_short_circuit_tag() */
+ COSE_MAC0_SHORT_CIRCUIT_TAG
+};
+
+/**
+ * \brief Run the main decode test
+ *
+ * \param[in] mode Selects test modes
+ *
+ * \return 0 for success, test failure code otherwise.
+ *
+ * See decode_test_normal_sig() and decode_test_short_circuit_sig().
+ */
+static int_fast16_t decode_test_internal(enum decode_test_mode_t mode)
+{
+ int_fast16_t return_value;
+ Q_USEFUL_BUF_MAKE_STACK_UB( token_storage, ATTEST_TOKEN_MAX_SIZE);
+ struct q_useful_buf_c completed_token;
+ struct attest_token_decode_context token_decode;
+ struct attest_token_iat_simple_t simple_claims;
+ struct attest_token_sw_component_t sw_component;
+ uint32_t num_sw_components;
+ int32_t num_sw_components_signed;
+ struct q_useful_buf_c tmp;
+ uint32_t token_encode_options;
+ uint32_t token_decode_options;
+
+ switch(mode) {
+ case SHORT_CIRCUIT_SIGN:
+ token_encode_options = TOKEN_OPT_SHORT_CIRCUIT_SIGN;
+ token_decode_options = TOKEN_OPT_SHORT_CIRCUIT_SIGN;
+ break;
+
+ case NORMAL_SIGN:
+ token_encode_options = 0;
+ token_decode_options = 0;
+ break;
+
+ case COSE_MAC0:
+ token_encode_options = 0;
+ token_decode_options = 0;
+ break;
+
+ case COSE_MAC0_SHORT_CIRCUIT_TAG:
+ token_encode_options = TOKEN_OPT_SHORT_CIRCUIT_SIGN;
+ token_decode_options = TOKEN_OPT_SHORT_CIRCUIT_SIGN;
+ break;
+
+ default:
+ return_value = -1000;
+ goto Done;
+ }
+
+ /* -- Make a token with all the claims -- */
+ tmp = TOKEN_TEST_VALUE_NONCE;
+ return_value = token_main_alt(token_encode_options,
+ tmp,
+ token_storage,
+ &completed_token);
+ if(return_value) {
+ goto Done;
+ }
+
+ /* -- Initialize and validate the signature on the token -- */
+ attest_token_decode_init(&token_decode, token_decode_options);
+ return_value = attest_token_decode_validate_token(&token_decode,
+ completed_token);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ goto Done;
+ }
+
+ /* -- Get the all simple claims at once and check them -- */
+ return_value = attest_token_decode_get_iat_simple(&token_decode,
+ &simple_claims);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ goto Done;
+ }
+
+
+ return_value = check_simple_claims(&simple_claims);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ goto Done;
+ }
+
+ /* -- SW components -- */
+ if(TOKEN_TEST_REQUIRED_NUM_SWC != INT32_MAX) {
+ /* -- Configured to check for SW components, so do that -- */
+
+ /* -- Get num SW components -- */
+ return_value = attest_token_get_num_sw_components(&token_decode,
+ &num_sw_components);
+ if(return_value) {
+ goto Done;
+ }
+ /* This conversion to signed avoids a compiler warning
+ * when TOKEN_TEST_REQUIRED_NUM_SWC is defined as 0 */
+ num_sw_components_signed = (int32_t)num_sw_components;
+ if(num_sw_components_signed < TOKEN_TEST_REQUIRED_NUM_SWC) {
+ return_value = -5;
+ goto Done;
+ }
+
+ if(num_sw_components >= 1) {
+ /* -- Get the first SW component and check it -- */
+ return_value = attest_token_get_sw_component(&token_decode,
+ 0,
+ &sw_component);
+ if(return_value) {
+ goto Done;
+ }
+
+ return_value = check_sw_component_1(&sw_component);
+ if(return_value) {
+ goto Done;
+ }
+
+ if(num_sw_components >= 2) {
+ /* -- Get the second SW component and check it -- */
+ return_value = attest_token_get_sw_component(&token_decode,
+ 1,
+ &sw_component);
+ if(return_value) {
+ goto Done;
+ }
+
+ return_value = check_sw_component_2(&sw_component);
+ if(return_value) {
+ goto Done;
+ }
+ }
+ }
+ }
+ return_value = 0;
+
+Done:
+ return return_value;
+}
+
+#ifdef SYMMETRIC_INITIAL_ATTESTATION
+int_fast16_t decode_test_symmetric_initial_attest(void)
+{
+ return decode_test_internal(COSE_MAC0);
+}
+
+int_fast16_t decode_test_symmetric_iat_short_circuit_tag(void)
+{
+ return decode_test_internal(COSE_MAC0_SHORT_CIRCUIT_TAG);
+}
+#else /* SYMMETRIC_INITIAL_ATTESTATION */
+/*
+ * Public function. See token_test.h
+ */
+int_fast16_t decode_test_short_circuit_sig(void)
+{
+ return decode_test_internal(SHORT_CIRCUIT_SIGN);
+}
+
+
+/*
+ * Public function. See token_test.h
+ */
+int_fast16_t decode_test_normal_sig(void)
+{
+ return decode_test_internal(NORMAL_SIGN);
+}
+#endif /* SYMMETRIC_INITIAL_ATTESTATION */
diff --git a/test/suites/attestation/attest_token_test.h b/test/suites/attestation/attest_token_test.h
new file mode 100644
index 0000000..37b12e0
--- /dev/null
+++ b/test/suites/attestation/attest_token_test.h
@@ -0,0 +1,143 @@
+/*
+ * attest_token_test.h
+ *
+ * Copyright (c) 2018-2019, Laurence Lundblade.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#ifndef __ATTEST_TOKEN_TEST_H__
+#define __ATTEST_TOKEN_TEST_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file attest_token_test.h
+ *
+ * \brief Entry points for attestation token tests.
+ *
+ * Errors codes are in the range of [-32767,32767] so
+ * int_fast16_t is used so they will work nice
+ * even on 16-bit machines. Plain old int could
+ * also be used, but many compilers make it
+ * 32-bits for backwards compatibility with
+ * SW that assume it is always 32 bits and
+ * it isn't efficient. (This code has probably
+ * not yet been tested on a 16-bit machines).
+ *
+ * https://stackoverflow.com/questions/30942107/
+ * whats-the-difference-between-int-and-int-fast16-t
+ */
+
+
+/**
+ * \brief Minimal token creation test using a short-circuit signature.
+ *
+ * \return non-zero on failure.
+ */
+int_fast16_t minimal_test(void);
+
+
+/**
+ * \brief Test token size calculation.
+ *
+ * \return non-zero on failure.
+ */
+int_fast16_t minimal_get_size_test(void);
+
+
+/**
+ * \brief Pass too small a buffer and confirm correct error result.
+ *
+ * \return non-zero on failure.
+ */
+int_fast16_t buffer_too_small_test(void);
+
+#ifdef SYMMETRIC_INITIAL_ATTESTATION
+/**
+ * \brief Test by checking token generated by symmetric key algorithms based
+ * Initial Attestation.
+ *
+ * \return non-zero on failure.
+ *
+ * This is an extensive test that can compare the values in the token
+ * to expected valued compiled into the test app from
+ * token_test_values.h. All the values represented in \ref
+ * attest_token_iat_simple_t and in \ref attest_token_sw_component_t
+ * are checked.
+ *
+ * The generated token will be decoded.
+ * The tag in COSE_Mac0 structure will be verified in secure side when
+ * INCLUDE_TEST_CODE is enabled.
+ */
+int_fast16_t decode_test_symmetric_initial_attest(void);
+
+/**
+ * \brief Test by checking short-circuit tagged values of claims.
+ *
+ * \return non-zero on failure.
+ *
+ * This is an extensive test that can compare the values in the token
+ * to expected valued compiled into the test app from
+ * token_test_values.h. All the values represented in \ref
+ * attest_token_iat_simple_t and in \ref attest_token_sw_component_t
+ * are checked.
+ *
+ * This uses a short-circuit tag rather than real HMAC operation with
+ * symmetric IAK. This tests everything in the implementation except the final
+ * MAC. It can work even without HMAC integration and without
+ * any keys configured.
+ */
+int_fast16_t decode_test_symmetric_iat_short_circuit_tag(void);
+#else /* SYMMETRIC_INITIAL_ATTESTATION */
+/**
+ * \brief Test by checking signed values of claims.
+ *
+ * \return non-zero on failure.
+ *
+ * This is an extensive test that can compare the values in the token
+ * to expected valued compiled into the test app from
+ * token_test_values.h. All the values represented in \ref
+ * attest_token_iat_simple_t and in \ref attest_token_sw_component_t
+ * are checked.
+ *
+ * This uses real ECDSA keys for both signing and verificaiton. It
+ * requires that the t_cose crypto porting layer operates correctly
+ * and that all keys are present. See also
+ * decode_test_short_circuit_sig().
+ */
+int_fast16_t decode_test_normal_sig(void);
+
+
+/**
+ * \brief Test by checking short-circuit signed values of claims.
+ *
+ * \return non-zero on failure.
+ *
+ * This is an extensive test that can compare the values in the token
+ * to expected valued compiled into the test app from
+ * token_test_values.h. All the values represented in \ref
+ * attest_token_iat_simple_t and in \ref attest_token_sw_component_t
+ * are checked.
+ *
+ * This uses a short-circuit signature rather than real ECDSA
+ * keys. This tests everything in the implementation except the final
+ * signing of the final hash with ECDSA and the converse
+ * verification. It is thorough test of everything by ECDSA
+ * integration. It can work even without ECDSA integration and without
+ * any keys configured.
+ */
+int_fast16_t decode_test_short_circuit_sig(void);
+#endif /* SYMMETRIC_INITIAL_ATTESTATION */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TOKEN_TEST_H__ */
diff --git a/test/suites/attestation/attest_token_test_values.h b/test/suites/attestation/attest_token_test_values.h
new file mode 100644
index 0000000..c8ecb21
--- /dev/null
+++ b/test/suites/attestation/attest_token_test_values.h
@@ -0,0 +1,317 @@
+/*
+ * attest_token_test_values.h
+ *
+ * Copyright (c) 2019, Laurence Lundblade.
+ * Copyright (c) 2019-2020, Arm Limited.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#ifndef __ATTEST_TOKEN_TEST_VALUES_H__
+#define __ATTEST_TOKEN_TEST_VALUES_H__
+
+/**
+ * \file attest_token_test_values.h
+ *
+ * \brief Expected values for test suite.
+ *
+ * This is a bunch of made up values for hard-coded test cases for
+ * attestation tokens.
+ *
+ * There are four possible test configurations for testing each claim:
+ *
+ * 1. No checking at all. \c TOKEN_TEST_REQUIRE_XXX is false and
+ * TOKEN_TEST_VALUE_XXX is not given.
+ *
+ * 2. Check for presence only. \c TOKEN_TEST_REQUIRE_XXX is true and
+ * TOKEN_TEST_VALUE_XXX is not given.
+ *
+ * 3. Check value if it is present, but it is not required to be
+ * present. \c TOKEN_TEST_REQUIRE_XXX is false and \c
+ * TOKEN_TEST_VALUE_XXX is given.
+ *
+ * 4. Must be present and of specific value. \c
+ * TOKEN_TEST_REQUIRE_XXX is true and \c TOKEN_TEST_VALUE_XXX is
+ * given.
+ *
+ * TOKEN_TEST_VALUE_XXX is not given as follows:
+ * - #define text strings as \c NULL
+ * - #define binary strings as \c NULL_Q_USEFUL_BUF_C
+ * - #define the integer value as \c INT32_MAX
+ *
+ * It is assumed that the expected value for any test will never be
+ * any of these.
+ *
+ * Individual test can also be made to return values that are not
+ * fixed at compile time by defining them to be a function and
+ * implementing the funciton. Here are examples for the three types:
+ *
+ * struct q_useful_buf_c get_expected_nonce(void);
+ * #define TOKEN_TEST_VALUE_NONCE get_expected_nonce()
+ *
+ * const char *get_expected_hw_version(void);
+ * #define TOKEN_TEST_VALUE_HW_VERSION get_expected_hw_version()
+ *
+ * uint32_t get_expected_client_id(void);
+ * #define TOKEN_TEST_VALUE_CLIENT_ID get_expected_client_id()
+ *
+ * The initialization value for byte strings uses a compound literal
+ * to create the \c ptr and \c len for a \c struct \c q_useful_buf_c.
+ * They are a bit ugly, but they work and setting up this way allows
+ * the literal value to be replaced by a function call for dynamic
+ * expected values.
+ *
+ * The first part of the compound literal is the value of the
+ * bytes. The second is an integer that is the length, the number of
+ * bytes. They length must be the number of bytes in the first.
+ */
+
+/* The 64 byte special option-packed nonce where option flags
+ * are packed in at the start. Binary. */
+#define TOKEN_TEST_NONCE_BYTES \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+#define TOKEN_TEST_VALUE_NONCE \
+ (struct q_useful_buf_c) {\
+ (uint8_t[]){TOKEN_TEST_NONCE_BYTES},\
+ 64\
+ }
+#define TOKEN_TEST_REQUIRE_NONCE true /* Mandatory claim */
+
+/* A 32 byte mostly random value. Binary. Value not checked */
+#define TOKEN_TEST_VALUE_UEID NULL_Q_USEFUL_BUF_C
+
+/* A 32 byte mostly random value. Binary.
+ * platform/ext/common/template/tfm_initial_attestation_key_material.c
+ */
+/*
+#define TOKEN_TEST_VALUE_UEID \
+ (struct q_useful_buf_c) {\
+ (uint8_t[]){ \
+ 0x01, \
+ 0xfa, 0x58, 0x75, 0x5f, 0x65, 0x86, 0x27, 0xce, \
+ 0x54, 0x60, 0xf2, 0x9b, 0x75, 0x29, 0x67, 0x13, \
+ 0x24, 0x8c, 0xae, 0x7a, 0xd9, 0xe2, 0x98, 0x4b, \
+ 0x90, 0x28, 0x0e, 0xfc, 0xbc, 0xb5, 0x02, 0x48 \
+ },\
+ 33\
+ }
+*/
+#define TOKEN_TEST_REQUIRE_UEID true /* Mandatory claim */
+
+/* If defined, check for the constant values defined in
+ * platform/ext/common/template/attest_hal.c
+ */
+#ifdef CLAIM_VALUE_CHECK
+
+/* A 32 byte mostly random value. Binary.
+ * platform/ext/common/template/attest_hal.c
+ */
+#define TOKEN_TEST_VALUE_BOOT_SEED \
+ (struct q_useful_buf_c) {\
+ (uint8_t[]){ \
+ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, \
+ 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, \
+ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, \
+ 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF \
+ },\
+ 32\
+ }
+
+/* A text string in EAN 13 format
+ * platform/ext/common/template/attest_hal.c
+ */
+#define TOKEN_TEST_VALUE_HW_VERSION "060456527282910010" /* Hard-coded value */
+
+/* A 32 byte mostly random value. Binary.
+ * platform/ext/common/template/attest_hal.c
+ */
+#define TOKEN_TEST_VALUE_IMPLEMENTATION_ID \
+ (struct q_useful_buf_c) {\
+ (uint8_t[]){ \
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, \
+ 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, \
+ 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, \
+ 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD \
+ },\
+ 32\
+ }
+
+/* Text string naming the profile definition:
+ * platform/ext/common/template/attest_hal.c
+ */
+#define TOKEN_TEST_VALUE_PROFILE_DEFINITION "PSA_IOT_PROFILE_1"
+
+/* Text string with verification URL or similar
+ * platform/ext/common/template/attest_hal.c
+ */
+#define TOKEN_TEST_VALUE_ORIGINATION "www.trustedfirmware.org"
+
+/* A small unsigned integer
+ * platform/ext/common/template/attest_hal.c
+ */
+#define TOKEN_TEST_VALUE_SECURITY_LIFECYCLE 0x3000u
+#else
+/* Do not check for the constant values defined in
+ * platform/ext/common/template/attest_hal.c
+ */
+
+#define TOKEN_TEST_VALUE_BOOT_SEED NULL_Q_USEFUL_BUF_C
+#define TOKEN_TEST_VALUE_HW_VERSION NULL
+#define TOKEN_TEST_VALUE_IMPLEMENTATION_ID NULL_Q_USEFUL_BUF_C
+#define TOKEN_TEST_VALUE_PROFILE_DEFINITION NULL
+#define TOKEN_TEST_VALUE_ORIGINATION NULL
+#define TOKEN_TEST_VALUE_SECURITY_LIFECYCLE INT32_MAX
+
+#endif /* CLAIM_VALUE_CHECK */
+
+#define TOKEN_TEST_REQUIRE_BOOT_SEED true /* Mandatory claim */
+#define TOKEN_TEST_REQUIRE_HW_VERSION false /* Optional claim */
+#define TOKEN_TEST_REQUIRE_IMPLEMENTATION_ID true /* Mandatory claim */
+#define TOKEN_TEST_REQUIRE_PROFILE_DEFINITION false /* Optional field */
+#define TOKEN_TEST_REQUIRE_ORIGINATION false /* Optional field */
+#define TOKEN_TEST_REQUIRE_SECURITY_LIFECYCLE true /* Mandatory claim */
+
+/* An integer (can be positive or negative) */
+#define TOKEN_TEST_VALUE_CLIENT_ID 0 /* Invalid value to trigger check */
+#define TOKEN_TEST_REQUIRE_CLIENT_ID true /* Mandatory claim */
+
+/**
+ * \c TOKEN_TEST_REQUIRED_NUM_SWC can be either 0, 1, 2 or \c
+ * INT32_MAX
+ *
+ * 0 -- No SW components are required, but if there is 1, its values
+ * must compare to SWC1 correctly and if there are 2, the first must
+ * compare to SWC1 and the second to SWC2.
+ *
+ * 1 -- At least one SW component is required and it must compare
+ * correctly to SWC1. If a second one is present its values will also
+ * be checked.
+ *
+ * 2 -- Two SW components are required and their values must compare
+ * correctly.
+ *
+ * INT32_MAX -- No checking of the SW components of any sort is
+ * performed.
+ *
+ * Note that attest_token_decode() checks for the presence of the the
+ * EAT_CBOR_ARM_LABEL_NO_SW_COMPONENTS CBOR data item for the case of
+ * no SW components and gives an error if it is absent.
+ */
+#define TOKEN_TEST_REQUIRED_NUM_SWC 0
+
+/* Text string */
+#define TOKEN_TEST_VALUE_SWC1_MEASUREMENT_TYPE NULL /* Value not checked */
+#define TOKEN_TEST_REQUIRE_SWC1_MEASUREMENT_TYPE false /* Optional field */
+
+/* A 32 byte mostly random value. Binary. Value not checked */
+#define TOKEN_TEST_VALUE_SWC1_MEASUREMENT_VAL NULL_Q_USEFUL_BUF_C
+/*
+#define TOKEN_TEST_VALUE_SWC1_MEASUREMENT_VAL \
+ (struct q_useful_buf_c) {\
+ (uint8_t[]){ \
+ 0x51, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 \
+ },\
+ 32\
+ }
+*/
+#define TOKEN_TEST_REQUIRE_SWC1_MEASUREMENT_VAL true /* Mandatory field */
+
+
+/* Text string */
+#define TOKEN_TEST_VALUE_SWC1_VERSION NULL /* Value not checked */
+/* This field must be mandatory to ensure PSA compliance -
+ * based on PSA Security Model document.
+ */
+#define TOKEN_TEST_REQUIRE_SWC1_VERSION true /* Mandatory field */
+
+/* A 32 byte mostly random value. Binary. Value not checked */
+#define TOKEN_TEST_VALUE_SWC1_SIGNER_ID NULL_Q_USEFUL_BUF_C
+/*
+#define TOKEN_TEST_VALUE_SWC1_SIGNER_ID \
+ (struct q_useful_buf_c) {\
+ (uint8_t[]){ \
+ 0x61, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 \
+ },\
+ 32\
+ }
+*/
+/* This field must be mandatory to ensure PSA compliance -
+ * based on PSA Security Model document.
+ */
+#define TOKEN_TEST_REQUIRE_SWC1_SIGNER_ID true /* Mandatory field */
+
+/* Text string */
+#define TOKEN_TEST_VALUE_SWC1_MEASUREMENT_DESC "SHA256" /* Hard-coded value */
+#define TOKEN_TEST_REQUIRE_SWC1_MEASUREMENT_DESC false /* Optional field */
+
+/* Text string */
+#define TOKEN_TEST_VALUE_SWC2_MEASUREMENT_TYPE NULL /* Value not checked */
+#define TOKEN_TEST_REQUIRE_SWC2_MEASUREMENT_TYPE false /* Optional field */
+
+/* A 32 byte mostly random value. Binary. Value not checked */
+#define TOKEN_TEST_VALUE_SWC2_MEASUREMENT_VAL NULL_Q_USEFUL_BUF_C
+/*
+#define TOKEN_TEST_VALUE_SWC2_MEASUREMENT_VAL \
+ (struct q_useful_buf_c) {\
+ (uint8_t[]){ \
+ 0x71, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 \
+ },\
+ 32\
+ }
+*/
+#define TOKEN_TEST_REQUIRE_SWC2_MEASUREMENT_VAL true /* Mandatory field */
+
+/* Text string */
+#define TOKEN_TEST_VALUE_SWC2_VERSION NULL /* Value not checked */
+/* This field must be mandatory to ensure PSA compliance -
+ * based on PSA Security Model document.
+ */
+#define TOKEN_TEST_REQUIRE_SWC2_VERSION true /* Mandatory field */
+
+/* A 32 byte mostly random value. Binary. Value not checked */
+#define TOKEN_TEST_VALUE_SWC2_SIGNER_ID NULL_Q_USEFUL_BUF_C
+/*
+#define TOKEN_TEST_VALUE_SWC2_SIGNER_ID \
+ (struct q_useful_buf_c) {\
+ (uint8_t[]){ \
+ 0x81, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 \
+ },\
+ 32\
+ }
+*/
+/* This field must be mandatory to ensure PSA compliance -
+ * based on PSA Security Model document.
+ */
+#define TOKEN_TEST_REQUIRE_SWC2_SIGNER_ID true /* Mandatory field */
+
+/* Text string */
+#define TOKEN_TEST_VALUE_SWC2_MEASUREMENT_DESC "SHA256" /* Hard-coded value */
+#define TOKEN_TEST_REQUIRE_SWC2_MEASUREMENT_DESC false /* Optional field */
+
+/* Attest token maximum size, there are also platform dependent values
+ * defined in region_defs.h
+ */
+#define ATTEST_TOKEN_MAX_SIZE 0x250
+
+#endif /* __ATTEST_TOKEN_TEST_VALUES_H__ */
diff --git a/test/suites/attestation/attestation_tests_common.h b/test/suites/attestation/attestation_tests_common.h
new file mode 100644
index 0000000..03c6199
--- /dev/null
+++ b/test/suites/attestation/attestation_tests_common.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __ATTESTATION_TESTS_COMMON_H__
+#define __ATTESTATION_TESTS_COMMON_H__
+
+#include "psa/initial_attestation.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * \def TEST_TOKEN_SIZE
+ *
+ * \brief Size of token buffer in bytes.
+ */
+#define TEST_TOKEN_SIZE (0x200)
+
+/*!
+ * \def TOO_SMALL_TOKEN_BUFFER
+ *
+ * \brief Smaller buffer size which is not big enough to store the created token
+ * by attestation service.
+ */
+#define TOO_SMALL_TOKEN_BUFFER (16u)
+
+/*!
+ * \def TEST_CHALLENGE_OBJ_SIZE
+ *
+ * \brief Size of challenge object in bytes used for test.
+ */
+#define TEST_CHALLENGE_OBJ_SIZE (PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64)
+
+/*!
+ * \def INVALID_CHALLENGE_OBJECT_SIZE
+ *
+ * \brief Size of challenge object that is invalid.
+ */
+#define INVALID_CHALLENGE_OBJECT_SIZE (PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32 + 1)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ATTESTATION_TESTS_COMMON_H__ */
diff --git a/test/suites/attestation/non_secure/attestation_ns_interface_testsuite.c b/test/suites/attestation/non_secure/attestation_ns_interface_testsuite.c
new file mode 100644
index 0000000..e827ae2
--- /dev/null
+++ b/test/suites/attestation/non_secure/attestation_ns_interface_testsuite.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "attestation_ns_tests.h"
+#include "psa/initial_attestation.h"
+#include "attestation.h"
+#include "../attestation_tests_common.h"
+#include "../attest_token_test_values.h"
+#include "../attest_token_test.h"
+
+static uint8_t token_buffer[TEST_TOKEN_SIZE];
+static const uint8_t challenge_buffer[TEST_CHALLENGE_OBJ_SIZE] = {
+ TOKEN_TEST_NONCE_BYTES};
+
+/* Define test suite for attestation service tests */
+/* List of tests */
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+static void tfm_attest_test_2001(struct test_result_t *ret);
+static void tfm_attest_test_2002(struct test_result_t *ret);
+static void tfm_attest_test_2003(struct test_result_t *ret);
+#endif
+static void tfm_attest_test_2004(struct test_result_t *ret);
+static void tfm_attest_test_2005(struct test_result_t *ret);
+
+static struct test_t attestation_interface_tests[] = {
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+ {&tfm_attest_test_2001, "TFM_ATTEST_TEST_2001",
+ "Minimal token test of attest token", {TEST_PASSED} },
+ {&tfm_attest_test_2002, "TFM_ATTEST_TEST_2002",
+ "Minimal token size test of attest token", {TEST_PASSED} },
+ {&tfm_attest_test_2003, "TFM_ATTEST_TEST_2003",
+ "Short circuit signature test of attest token", {TEST_PASSED} },
+#endif
+ {&tfm_attest_test_2004, "TFM_ATTEST_TEST_2004",
+ "ECDSA signature test of attest token", {TEST_PASSED} },
+ {&tfm_attest_test_2005, "TFM_ATTEST_TEST_2005",
+ "Negative test cases for initial attestation service", {TEST_PASSED} },
+};
+
+void
+register_testsuite_ns_attestation_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(attestation_interface_tests) /
+ sizeof(attestation_interface_tests[0]));
+
+ set_testsuite("Initial Attestation Service non-secure interface tests"
+ "(TFM_ATTEST_TEST_2XXX)",
+ attestation_interface_tests, list_size, p_test_suite);
+}
+
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+/*!
+ * \brief Get minimal token, only include a hard coded challenge, but omit the
+ * rest of the claims
+ *
+ * Calling the minimal_test, which just retrieves a specific token:
+ * - only hard coded challenge is included
+ * - token signature is the hash of the token concatenated twice
+ */
+static void tfm_attest_test_2001(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = minimal_test();
+ if (err != 0) {
+ TEST_LOG("minimal_test() returned: %d\r\n", err);
+ TEST_FAIL("Attest token minimal_test() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get the size of the minimal token, only include a hard coded
+ * challenge, but omit the rest of the claims
+ */
+static void tfm_attest_test_2002(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = minimal_get_size_test();
+ if (err != 0) {
+ TEST_LOG("minimal_get_size_test() returned: %d\r\n", err);
+ TEST_FAIL("Attest token minimal_get_size_test() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get an IAT with short circuit signature (signature is composed of
+ * hash of token). Parse the token, validate presence of claims and
+ * compare them against expected values in token_test_values.h
+ *
+ * More info in token_test.h
+ */
+static void tfm_attest_test_2003(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = decode_test_short_circuit_sig();
+ if (err != 0) {
+ TEST_LOG("decode_test_short_circuit_sig() returned: %d\r\n", err);
+ TEST_FAIL("Attest token decode_test_short_circuit_sig() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+#endif /* INCLUDE_TEST_CODE */
+
+/*!
+ * \brief Get an IAT with proper ECDSA signature. Parse the token, validate
+ * presence of claims and compare them against expected values in
+ * token_test_values.h
+ *
+ * ECDSA signing is currently not supported in TF_M.
+ *
+ * More info in token_test.h
+ */
+static void tfm_attest_test_2004(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = decode_test_normal_sig();
+ if (err != 0) {
+ TEST_LOG("decode_test_normal_sig() returned: %d\r\n", err);
+ TEST_FAIL("Attest token decode_test_normal_sig() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Negative tests for initial attestation service
+ *
+ * - Calling initial attestation service with bigger challenge object than
+ * allowed.
+ * - Calling initial attestation service with smaller buffer size than the
+ * expected size of the token.
+ */
+static void tfm_attest_test_2005(struct test_result_t *ret)
+{
+ psa_status_t err;
+ size_t token_buf_size = TEST_TOKEN_SIZE;
+ size_t token_size;
+
+ /* Call with with bigger challenge object than allowed */
+ err = psa_initial_attest_get_token(challenge_buffer,
+ INVALID_CHALLENGE_OBJECT_SIZE,
+ token_buffer,
+ token_buf_size,
+ &token_size);
+
+ if (err != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Attestation should fail with too big challenge object");
+ return;
+ }
+
+ /* Call with smaller buffer size than size of test token */
+ token_buf_size = TOO_SMALL_TOKEN_BUFFER;
+ err = psa_initial_attest_get_token(challenge_buffer,
+ TEST_CHALLENGE_OBJ_SIZE,
+ token_buffer,
+ token_buf_size,
+ &token_size);
+
+ if (err != PSA_ERROR_BUFFER_TOO_SMALL) {
+ TEST_FAIL("Attestation should fail with too small token buffer");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
diff --git a/test/suites/attestation/non_secure/attestation_ns_tests.h b/test/suites/attestation/non_secure/attestation_ns_tests.h
new file mode 100644
index 0000000..c2d5a3e
--- /dev/null
+++ b/test/suites/attestation/non_secure/attestation_ns_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __ATTESTATION_NS_TESTS_H__
+#define __ATTESTATION_NS_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the initial attestation service.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void
+register_testsuite_ns_attestation_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ATTESTATION_NS_TESTS_H__ */
diff --git a/test/suites/attestation/non_secure/symmetric_attest_ns_interface_testsuite.c b/test/suites/attestation/non_secure/symmetric_attest_ns_interface_testsuite.c
new file mode 100644
index 0000000..4f5d99a
--- /dev/null
+++ b/test/suites/attestation/non_secure/symmetric_attest_ns_interface_testsuite.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "attestation_ns_tests.h"
+#include "psa/initial_attestation.h"
+#include "secure_fw/partitions/initial_attestation/attestation.h"
+#include "../attestation_tests_common.h"
+#include "../attest_token_test_values.h"
+#include "../attest_token_test.h"
+
+/* Define test suite for attestation service tests */
+/* List of tests */
+static void tfm_attest_test_2001(struct test_result_t *ret);
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+static void tfm_attest_test_2002(struct test_result_t *ret);
+static void tfm_attest_test_2003(struct test_result_t *ret);
+static void tfm_attest_test_2004(struct test_result_t *ret);
+static void tfm_attest_test_2005(struct test_result_t *ret);
+#endif
+
+static struct test_t attestation_interface_tests[] = {
+ {&tfm_attest_test_2001, "TFM_ATTEST_TEST_2001",
+ "Symmetric key algorithm based Initial Attestation test", {0} },
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+ {&tfm_attest_test_2002, "TFM_ATTEST_TEST_2002",
+ "Minimal token test of attest token", {0} },
+ {&tfm_attest_test_2003, "TFM_ATTEST_TEST_2003",
+ "Minimal token size test of attest token", {0} },
+ {&tfm_attest_test_2004, "TFM_ATTEST_TEST_2004",
+ "Short circuit tag test of attest token", {0} },
+ {&tfm_attest_test_2005, "TFM_ATTEST_TEST_2005",
+ "Negative test cases for initial attestation service", {0} },
+#endif
+};
+
+void
+register_testsuite_ns_attestation_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(attestation_interface_tests) /
+ sizeof(attestation_interface_tests[0]));
+
+ set_testsuite("Symmetric key algorithm based Initial Attestation Service "
+ "non-secure interface tests (TFM_ATTEST_TEST_2XXX)",
+ attestation_interface_tests, list_size, p_test_suite);
+}
+
+/*!
+ * \brief Get an IAT with symmetric key algorithm based Initial Attestation.
+ */
+static void tfm_attest_test_2001(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = decode_test_symmetric_initial_attest();
+ if (err != 0) {
+ TEST_LOG("tfm_attest_test_2001() returned: %d\r\n", err);
+ TEST_FAIL("Attest token tfm_attest_test_2001() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+/*!
+ * \brief Get minimal token, only include a hard coded challenge, but omit the
+ * rest of the claims
+ *
+ * Calling the minimal_test, which just retrieves a specific token:
+ * - only hard coded challenge is included
+ * - only mandatory claims are included
+ */
+static void tfm_attest_test_2002(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = minimal_test();
+ if (err != 0) {
+ TEST_LOG("minimal_test() returned: %d\r\n", err);
+ TEST_FAIL("Attest token minimal_test() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get the size of the minimal token, only include a hard coded
+ * challenge, but omit the rest of the claims
+ */
+static void tfm_attest_test_2003(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = minimal_get_size_test();
+ if (err != 0) {
+ TEST_LOG("minimal_get_size_test() returned: %d\r\n", err);
+ TEST_FAIL("Attest token minimal_get_size_test() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get an IAT with short circuit tag (tag is hash of token).
+ * Parse the token, validate presence of claims and verify the hash value
+ *
+ * More info in token_test.h
+ */
+static void tfm_attest_test_2004(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = decode_test_symmetric_iat_short_circuit_tag();
+ if (err != 0) {
+ TEST_LOG("decode_test_symmetric_iat_short_circuit_tag() returned: %d\r\n", err);
+ TEST_FAIL("Attest token decode_test_symmetric_iat_short_circuit_tag() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Negative tests for initial attestation service
+ *
+ * - Calling initial attestation service with bigger challenge object than
+ * allowed.
+ * - Calling initial attestation service with smaller buffer size than the
+ * expected size of the token.
+ */
+static void tfm_attest_test_2005(struct test_result_t *ret)
+{
+ psa_status_t err;
+ size_t token_size;
+
+ /* Call with with bigger challenge object than allowed */
+ err = psa_initial_attest_get_token_size(INVALID_CHALLENGE_OBJECT_SIZE,
+ &token_size);
+
+ if (err != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Attestation should fail with too big challenge object");
+ return;
+ }
+
+ /* Call with smaller buffer size than size of test token */
+ err = buffer_too_small_test();
+ if (err != 0) {
+ TEST_LOG("buffer_too_small_test() returned: %d\r\n", err);
+ TEST_FAIL("Attest token buffer_too_small_test() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+#endif /* INCLUDE_TEST_CODE */
diff --git a/test/suites/attestation/secure/attestation_s_interface_testsuite.c b/test/suites/attestation/secure/attestation_s_interface_testsuite.c
new file mode 100644
index 0000000..0ff7df1
--- /dev/null
+++ b/test/suites/attestation/secure/attestation_s_interface_testsuite.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "attestation_s_tests.h"
+#include "psa/initial_attestation.h"
+#include "secure_fw/partitions/initial_attestation/attestation.h"
+#include "../attestation_tests_common.h"
+#include "../attest_token_test_values.h"
+#include "../attest_token_test.h"
+
+static uint8_t token_buffer[TEST_TOKEN_SIZE];
+static const uint8_t challenge_buffer[TEST_CHALLENGE_OBJ_SIZE] = {
+ TOKEN_TEST_NONCE_BYTES};
+
+/* Define test suite for attestation service tests */
+/* List of tests */
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+static void tfm_attest_test_1001(struct test_result_t *ret);
+static void tfm_attest_test_1002(struct test_result_t *ret);
+static void tfm_attest_test_1003(struct test_result_t *ret);
+#endif
+static void tfm_attest_test_1004(struct test_result_t *ret);
+static void tfm_attest_test_1005(struct test_result_t *ret);
+
+static struct test_t attestation_interface_tests[] = {
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+ {&tfm_attest_test_1001, "TFM_ATTEST_TEST_1001",
+ "Minimal token test of attest token", {TEST_PASSED} },
+ {&tfm_attest_test_1002, "TFM_ATTEST_TEST_1002",
+ "Minimal token size test of attest token", {TEST_PASSED} },
+ {&tfm_attest_test_1003, "TFM_ATTEST_TEST_1003",
+ "Short circuit signature test of attest token", {TEST_PASSED} },
+#endif
+ {&tfm_attest_test_1004, "TFM_ATTEST_TEST_1004",
+ "ECDSA signature test of attest token", {TEST_PASSED} },
+ {&tfm_attest_test_1005, "TFM_ATTEST_TEST_1005",
+ "Negative test cases for initial attestation service", {TEST_PASSED} },
+};
+
+void
+register_testsuite_s_attestation_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(attestation_interface_tests) /
+ sizeof(attestation_interface_tests[0]));
+
+ set_testsuite("Initial Attestation Service secure interface tests"
+ "(TFM_ATTEST_TEST_1XXX)",
+ attestation_interface_tests, list_size, p_test_suite);
+}
+
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+/*!
+ * \brief Get minimal token, only include a hard coded challenge, but omit the
+ * rest of the claims
+ *
+ * Calling the minimal_test, which just retrieves a specific token:
+ * - only hard coded challenge is included
+ * - token signature is the hash of the token concatenated twice
+ */
+static void tfm_attest_test_1001(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = minimal_test();
+ if (err != 0) {
+ TEST_LOG("minimal_test() returned: %d\r\n", err);
+ TEST_FAIL("Attest token minimal_test() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get the size of the minimal token, only include a hard coded
+ * challenge, but omit the rest of the claims
+ */
+static void tfm_attest_test_1002(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = minimal_get_size_test();
+ if (err != 0) {
+ TEST_LOG("minimal_get_size_test() returned: %d\r\n", err);
+ TEST_FAIL("Attest token minimal_get_size_test() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get an IAT with short circuit signature (signature is composed of
+ * hash of token). Parse the token, validate presence of claims and
+ * compare them against expected values in token_test_values.h
+ *
+ * More info in token_test.h
+ */
+static void tfm_attest_test_1003(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = decode_test_short_circuit_sig();
+ if (err != 0) {
+ TEST_LOG("decode_test_short_circuit_sig() returned: %d\r\n", err);
+ TEST_FAIL("Attest token decode_test_short_circuit_sig() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+#endif /* INCLUDE_TEST_CODE */
+
+/*!
+ * \brief Get an IAT with proper ECDSA signature. Parse the token, validate
+ * presence of claims and compare them against expected values in
+ * token_test_values.h
+ *
+ * ECDSA signing is currently not supported in TF_M.
+ *
+ * More info in token_test.h
+ */
+static void tfm_attest_test_1004(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = decode_test_normal_sig();
+ if (err != 0) {
+ TEST_LOG("decode_test_normal_sig() returned: %d\r\n", err);
+ TEST_FAIL("Attest token decode_test_normal_sig() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Negative tests for initial attestation service
+ *
+ * - Calling initial attestation service with bigger challenge object than
+ * allowed.
+ * - Calling initial attestation service with smaller buffer size than the
+ * expected size of the token.
+ */
+static void tfm_attest_test_1005(struct test_result_t *ret)
+{
+ psa_status_t err;
+ size_t token_buf_size = TEST_TOKEN_SIZE;
+ size_t token_size;
+
+ /* Call with with bigger challenge object than allowed */
+ err = psa_initial_attest_get_token(challenge_buffer,
+ INVALID_CHALLENGE_OBJECT_SIZE,
+ token_buffer,
+ token_buf_size,
+ &token_size);
+
+ if (err != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Attestation should fail with too big challenge object");
+ return;
+ }
+
+ /* Call with smaller buffer size than size of test token */
+ token_buf_size = TOO_SMALL_TOKEN_BUFFER;
+ err = psa_initial_attest_get_token(challenge_buffer,
+ TEST_CHALLENGE_OBJ_SIZE,
+ token_buffer,
+ token_buf_size,
+ &token_size);
+
+ if (err != PSA_ERROR_BUFFER_TOO_SMALL) {
+ TEST_FAIL("Attestation should fail with too small token buffer");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
diff --git a/test/suites/attestation/secure/attestation_s_tests.h b/test/suites/attestation/secure/attestation_s_tests.h
new file mode 100644
index 0000000..97e8c40
--- /dev/null
+++ b/test/suites/attestation/secure/attestation_s_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __ATTESTATION_S_TESTS_H__
+#define __ATTESTATION_S_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the initial attestation service.
+ *
+ * \param[in] "p_test_suite" The test suite to be executed.
+ */
+void
+register_testsuite_s_attestation_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ATTESTATION_S_TESTS_H__ */
diff --git a/test/suites/attestation/secure/symmetric_attest_s_interface_testsuite.c b/test/suites/attestation/secure/symmetric_attest_s_interface_testsuite.c
new file mode 100644
index 0000000..a2eb1fd
--- /dev/null
+++ b/test/suites/attestation/secure/symmetric_attest_s_interface_testsuite.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "attestation_s_tests.h"
+#include "psa/initial_attestation.h"
+#include "secure_fw/partitions/initial_attestation/attestation.h"
+#include "../attestation_tests_common.h"
+#include "../attest_token_test_values.h"
+#include "../attest_token_test.h"
+
+/* Define test suite for attestation service tests */
+/* List of tests */
+static void tfm_attest_test_1001(struct test_result_t *ret);
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+static void tfm_attest_test_1002(struct test_result_t *ret);
+static void tfm_attest_test_1003(struct test_result_t *ret);
+static void tfm_attest_test_1004(struct test_result_t *ret);
+static void tfm_attest_test_1005(struct test_result_t *ret);
+#endif
+
+static struct test_t attestation_interface_tests[] = {
+ {&tfm_attest_test_1001, "TFM_ATTEST_TEST_1001",
+ "Symmetric key algorithm based Initial Attestation test", {0} },
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+ {&tfm_attest_test_1002, "TFM_ATTEST_TEST_1002",
+ "Minimal token test of attest token", {0} },
+ {&tfm_attest_test_1003, "TFM_ATTEST_TEST_1003",
+ "Minimal token size test of attest token", {0} },
+ {&tfm_attest_test_1004, "TFM_ATTEST_TEST_1004",
+ "Short circuit tag test of attest token", {0} },
+ {&tfm_attest_test_1005, "TFM_ATTEST_TEST_1005",
+ "Negative test cases for initial attestation service", {0} },
+#endif
+};
+
+void
+register_testsuite_s_attestation_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(attestation_interface_tests) /
+ sizeof(attestation_interface_tests[0]));
+
+ set_testsuite("Symmetric key algorithm based Initial Attestation Service "
+ "secure interface tests (TFM_ATTEST_TEST_1XXX)",
+ attestation_interface_tests, list_size, p_test_suite);
+}
+
+/*!
+ * \brief Get an IAT with symmetric key algorithm based Initial Attestation.
+ */
+static void tfm_attest_test_1001(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = decode_test_symmetric_initial_attest();
+ if (err != 0) {
+ TEST_LOG("tfm_attest_test_1001() returned: %d\r\n", err);
+ TEST_FAIL("Attest token tfm_attest_test_1001() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+/*!
+ * \brief Get minimal token, only include a hard coded challenge, but omit the
+ * rest of the claims
+ *
+ * Calling the minimal_test, which just retrieves a specific token:
+ * - only hard coded challenge is included
+ * - only mandatory claims are included
+ */
+static void tfm_attest_test_1002(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = minimal_test();
+ if (err != 0) {
+ TEST_LOG("minimal_test() returned: %d\r\n", err);
+ TEST_FAIL("Attest token minimal_test() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get the size of the minimal token, only include a hard coded
+ * challenge, but omit the rest of the claims
+ */
+static void tfm_attest_test_1003(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = minimal_get_size_test();
+ if (err != 0) {
+ TEST_LOG("minimal_get_size_test() returned: %d\r\n", err);
+ TEST_FAIL("Attest token minimal_get_size_test() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get an IAT with short circuit tag (tag is hash of token).
+ * Parse the token, validate presence of claims and verify the hash value
+ *
+ * More info in token_test.h
+ */
+static void tfm_attest_test_1004(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = decode_test_symmetric_iat_short_circuit_tag();
+ if (err != 0) {
+ TEST_LOG("decode_test_symmetric_iat_short_circuit_tag() returned: %d\r\n", err);
+ TEST_FAIL("Attest token decode_test_symmetric_iat_short_circuit_tag() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Negative tests for initial attestation service
+ *
+ * - Calling initial attestation service with bigger challenge object than
+ * allowed.
+ * - Calling initial attestation service with smaller buffer size than the
+ * expected size of the token.
+ */
+static void tfm_attest_test_1005(struct test_result_t *ret)
+{
+ psa_status_t err;
+ size_t token_size;
+
+ /* Call with with bigger challenge object than allowed */
+ err = psa_initial_attest_get_token_size(INVALID_CHALLENGE_OBJECT_SIZE,
+ &token_size);
+
+ if (err != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Attestation should fail with too big challenge object");
+ return;
+ }
+
+ /* Call with smaller buffer size than size of test token */
+ err = buffer_too_small_test();
+ if (err != 0) {
+ TEST_LOG("buffer_too_small_test() returned: %d\r\n", err);
+ TEST_FAIL("Attest token buffer_too_small_test() has failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+#endif /* INCLUDE_TEST_CODE */
diff --git a/test/suites/audit/CMakeLists.inc b/test/suites/audit/CMakeLists.inc
new file mode 100644
index 0000000..b1a8f46
--- /dev/null
+++ b/test/suites/audit/CMakeLists.inc
@@ -0,0 +1,32 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "audit logging test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(AUDIT_LOGGING_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+ message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+list(APPEND ALL_SRC_C_S "${AUDIT_LOGGING_TEST_DIR}/secure/audit_s_interface_testsuite.c")
+list(APPEND ALL_SRC_C_NS "${AUDIT_LOGGING_TEST_DIR}/non_secure/audit_ns_interface_testsuite.c")
+
+#Setting include directories
+embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
diff --git a/test/suites/audit/audit_tests_common.h b/test/suites/audit/audit_tests_common.h
new file mode 100644
index 0000000..93bd6a8
--- /dev/null
+++ b/test/suites/audit/audit_tests_common.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __AUDIT_TESTS_COMMON_H__
+#define __AUDIT_TESTS_COMMON_H__
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * \def STR(a)
+ *
+ * \brief A standard stringify macro
+ */
+#define STR(a) _STR(a)
+#define _STR(a) #a
+
+/*!
+ * \def LOCAL_BUFFER_SIZE
+ *
+ * \brief Size in bytes of the local buffer. Size accomodates two standard size
+ * (no payload) log items, at maximum
+ */
+#define LOCAL_BUFFER_SIZE (80)
+
+/*!
+ * \def LOCAL_BUFFER_ITEMS
+ *
+ * \brief Number of items which can be held within a buffer of size
+ * LOCAL_BUFFER_SIZE
+ */
+#define LOCAL_BUFFER_ITEMS (2)
+
+/*!
+ * \def STANDARD_LOG_ENTRY_SIZE
+ *
+ * \brief A log item with no payload (standard size) has the following size.
+ * More details can be found observing \ref psa_audit_record
+ * \ref log_tlr and \ref log_hdr
+ */
+#define STANDARD_LOG_ENTRY_SIZE (28)
+
+/*!
+ * \def INITIAL_LOGGING_REQUESTS
+ *
+ * \brief Number of initial consecutive logging requests to perform
+ */
+#define INITIAL_LOGGING_REQUESTS (36)
+
+/*!
+ * \def INITIAL_LOGGING_SIZE
+ *
+ * \brief Size of the initial consecutive logging requests
+ */
+#define INITIAL_LOGGING_SIZE (1008)
+
+/*!
+ * \def FINAL_LOGGING_REQUESTS
+ *
+ * \brief Number of final consecutive logging requests to perform
+ *
+ * \note This defines the state of the log when secure interface tests are
+ * terminated
+ */
+#define FINAL_LOGGING_REQUESTS (2)
+
+/*!
+ * \def FINAL_LOGGING_SIZE
+ *
+ * \brief Size of the final consecutive logging requests
+ *
+ * \note This defines the state of the log when secure interface tests are
+ * terminated
+ */
+#define FINAL_LOGGING_SIZE (56)
+
+/*!
+ * \def DUMMY_TEST_RECORD_ID_BASE
+ *
+ * \brief The log record is initialized with a dummy ID which uses this value as
+ * base value
+ */
+#define DUMMY_TEST_RECORD_ID_BASE (0xABCD0000)
+
+/*!
+ * \def SECOND_ELEMENT_EXPECTED_CONTENT
+ *
+ * \brief Content of the log record in the second log item in the final request
+ *
+ */
+#define SECOND_ELEMENT_EXPECTED_CONTENT ( (DUMMY_TEST_RECORD_ID_BASE) + \
+ (INITIAL_LOGGING_REQUESTS+1+FINAL_LOGGING_REQUESTS) )
+/*!
+ * \def MAX_LOG_SIZE
+ *
+ * \brief The maximum possible log size in the current implementation
+ *
+ * \brief This parameter for tests has to be changed for the tests in case the
+ * implementation is modified
+ */
+#define MAX_LOG_SIZE (1024)
+
+/*!
+ * \def MAX_LOG_RECORD_SIZE
+ *
+ * \brief The maximum possible log line size to fill a MAX_LOG_SIZE bytes log
+ *
+ * \note This takes into account additional fields that are concatenated to the
+ * record in the header and trailer
+ */
+#define MAX_LOG_RECORD_SIZE (1000)
+
+/*!
+ * \def INITIAL_LOG_SIZE
+ *
+ * \brief Initial state of the log size in bytes
+ *
+ * \note This defines the state of the log when non-secure interface tests start
+ */
+#define INITIAL_LOG_SIZE (FINAL_LOGGING_SIZE)
+
+/*!
+ * \def INITIAL_LOG_RECORDS
+ *
+ * \brief Initial state of the log number of records
+ *
+ * \note This defines the state of the log when non-secure interface tests start
+ */
+#define INITIAL_LOG_RECORDS (FINAL_LOGGING_REQUESTS)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __AUDIT_TESTS_COMMON_H__ */
diff --git a/test/suites/audit/non_secure/audit_ns_interface_testsuite.c b/test/suites/audit/non_secure/audit_ns_interface_testsuite.c
new file mode 100644
index 0000000..a6179ac
--- /dev/null
+++ b/test/suites/audit/non_secure/audit_ns_interface_testsuite.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test/framework/test_framework_helpers.h"
+#include "psa_audit_api.h"
+#include "audit_ns_tests.h"
+#include "tfm_api.h"
+#include "secure_fw/partitions/audit_logging/audit_core.h"
+
+#include "../audit_tests_common.h"
+
+/*!
+ * \def EMPTY_RETRIEVED_LOG_SIZE
+ *
+ * \brief Log size when the retrieved buffer is empty
+ */
+#define EMPTY_RETRIEVED_LOG_SIZE (0)
+
+/*!
+ * \def EMPTY_RETRIEVED_LOG_ITEMS
+ *
+ * \brief Number of log items when retrieved buffer is empty
+ */
+#define EMPTY_RETRIEVED_LOG_ITEMS (0)
+
+/*!
+ * \def SINGLE_RETRIEVED_LOG_SIZE
+ *
+ * \brief Log size when the retrieved buffer has 1 item
+ * of standard size (no payload)
+ */
+#define SINGLE_RETRIEVED_LOG_SIZE (STANDARD_LOG_ENTRY_SIZE)
+
+/*!
+ * \def SINGLE_RETRIEVED_LOG_ITEMS
+ *
+ * \brief Number of log items when retrieved buffer has 1 item
+ */
+#define SINGLE_RETRIEVED_LOG_ITEMS (1)
+
+/*!
+ * \def SECOND_ELEMENT_START_INDEX
+ *
+ * \brief Index of the second item in the log
+ */
+#define SECOND_ELEMENT_START_INDEX (1)
+
+/* List of tests */
+static void tfm_audit_test_1001(struct test_result_t *ret);
+
+static struct test_t audit_veneers_tests[] = {
+ {&tfm_audit_test_1001, "TFM_AUDIT_TEST_1001",
+ "Non Secure functional", {TEST_PASSED} },
+};
+
+void register_testsuite_ns_audit_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(audit_veneers_tests) /
+ sizeof(audit_veneers_tests[0]));
+
+ set_testsuite("AuditLog non-secure interface test (TFM_AUDIT_TEST_1XXX)",
+ audit_veneers_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Functional test of NS API
+ *
+ * \note This is a functional test only and doesn't
+ * mean to test all possible combinations of
+ * input parameters and return values.
+ * This tests the current status of the log as
+ * it's been left from the Secure tests. In case
+ * other tests are added in the Secure test suite,
+ * the status of the log will change and these
+ * tests may start failing.
+ */
+static void tfm_audit_test_1001(struct test_result_t *ret)
+{
+ psa_status_t status;
+
+ uint8_t local_buffer[LOCAL_BUFFER_SIZE];
+ uint32_t idx, stored_size, num_records, retrieved_size;
+
+ struct psa_audit_record *retrieved_buffer;
+
+ /* Get the log size (current state) */
+ status = psa_audit_get_info(&num_records, &stored_size);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Getting log info has returned error");
+ return;
+ }
+
+ if (stored_size != INITIAL_LOG_SIZE) {
+ TEST_FAIL("Stored size different from " STR(INITIAL_LOG_SIZE));
+ return;
+ }
+
+ if (num_records != INITIAL_LOG_RECORDS) {
+ TEST_FAIL("Stored records different from " STR(INITIAL_LOG_RECORDS));
+ return;
+ }
+
+ /* Check the length of each record individually */
+ for (idx=0; idx<num_records; idx++) {
+ status = psa_audit_get_record_info(idx, &stored_size);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Getting record size individually has returned error");
+ return;
+ }
+
+ if (stored_size != STANDARD_LOG_ENTRY_SIZE) {
+ TEST_FAIL("Unexpected record size for a single standard record");
+ return;
+ }
+ }
+
+ /* Check that if requesting length of a record which is not there fails */
+ status = psa_audit_get_record_info(num_records, &stored_size);
+ if (status == PSA_SUCCESS) {
+ TEST_FAIL("Getting record size for non-existent record has not failed");
+ return;
+ }
+
+ /* Log contains 2 items. Retrieve into buffer which is able to contain the
+ * the full contents of the log, one record at a time
+ */
+ for (idx=0; idx<INITIAL_LOG_RECORDS; idx++) {
+ status = psa_audit_retrieve_record(
+ idx,
+ LOCAL_BUFFER_SIZE,
+ NULL,
+ 0,
+ &local_buffer[idx*STANDARD_LOG_ENTRY_SIZE],
+ &retrieved_size);
+
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Log retrieval from NS returned error");
+ return;
+ }
+
+ if (retrieved_size != STANDARD_LOG_ENTRY_SIZE) {
+ TEST_FAIL("Expected retrieve size: " STR(STANDARD_LOG_ENTRY_SIZE));
+ return;
+ }
+ }
+
+ /* Retrieve into a small buffer. It's not enough to store a single
+ * item so the provided buffer must be empty after retrieval. We
+ * check the info structure to count how many items and bytes have
+ * been returned, and if they're zeros items / zero bytes, there is
+ * no point in checking the contents of the local_buffer.
+ */
+ status = psa_audit_retrieve_record(0,
+ LOCAL_BUFFER_SIZE/4,
+ NULL,
+ 0,
+ &local_buffer[0],
+ &retrieved_size);
+
+ if (status == PSA_SUCCESS) {
+ TEST_FAIL("Log retrieval from NS should fail, buffer too small");
+ return;
+ }
+
+ if (retrieved_size != EMPTY_RETRIEVED_LOG_SIZE) {
+ TEST_FAIL("Expected log size is " STR(EMPTY_RETRIEVED_LOG_SIZE));
+ return;
+ }
+
+ /* Retrieve into a buffer which can hold a single element, but start from
+ * the second element that is stored in the log
+ */
+ status = psa_audit_retrieve_record(1,
+ STANDARD_LOG_ENTRY_SIZE,
+ NULL,
+ 0,
+ &local_buffer[0],
+ &retrieved_size);
+
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Log retrieval from NS returned error");
+ return;
+ }
+
+ if (retrieved_size != SINGLE_RETRIEVED_LOG_SIZE) {
+ TEST_FAIL("Expected log size is " STR(SINGLE_RETRIEVED_LOG_SIZE));
+ return;
+ }
+
+ /* Inspect the contents of the retrieved buffer, i.e. check the
+ * retrieved log record contents
+ */
+ retrieved_buffer = (struct psa_audit_record *)
+ &local_buffer[offsetof(struct log_hdr, size)];
+
+ if (retrieved_buffer->id != SECOND_ELEMENT_EXPECTED_CONTENT) {
+ TEST_FAIL("Unexpected argument in the first entry");
+ return;
+ }
+
+ /* Delete oldest element in the log */
+ status = psa_audit_delete_record(0, NULL, 0);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Log record deletion from NS returned error");
+ return;
+ }
+
+ /* Get the log size (current state) */
+ status = psa_audit_get_info(&num_records, &stored_size);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Getting log info has returned error");
+ return;
+ }
+
+ if (num_records != 1) {
+ TEST_FAIL("Unexpected number of records in the log after delete");
+ return;
+ }
+
+ if (stored_size != STANDARD_LOG_ENTRY_SIZE) {
+ TEST_FAIL("Unexpected size in the log after deletion");
+ return;
+ }
+
+ /* Delete oldest element in the log. After this, the log will be empty */
+ status = psa_audit_delete_record(0, NULL, 0);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Log record deletion from NS returned error");
+ return;
+ }
+
+ /* Get the log size (current state) */
+ status = psa_audit_get_info(&num_records, &stored_size);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Getting log info has returned error");
+ return;
+ }
+
+ if (num_records != 0) {
+ TEST_FAIL("Unexpected number of records in the log after deletion");
+ return;
+ }
+
+ if (stored_size != 0) {
+ TEST_FAIL("Unexpected size in the log after deletion");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
diff --git a/test/suites/audit/non_secure/audit_ns_tests.h b/test/suites/audit/non_secure/audit_ns_tests.h
new file mode 100644
index 0000000..3578bbe
--- /dev/null
+++ b/test/suites/audit/non_secure/audit_ns_tests.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __AUDIT_NS_TESTS_H__
+#define __AUDIT_NS_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for audit logging non-secure interface.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_ns_audit_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __AUDIT_NS_TESTS_H__ */
diff --git a/test/suites/audit/secure/audit_s_interface_testsuite.c b/test/suites/audit/secure/audit_s_interface_testsuite.c
new file mode 100644
index 0000000..550ec10
--- /dev/null
+++ b/test/suites/audit/secure/audit_s_interface_testsuite.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test/framework/test_framework_helpers.h"
+#include "psa_audit_api.h"
+#include "audit_s_tests.h"
+#include "tfm_api.h"
+#include "psa_audit_api.h"
+#include "secure_fw/partitions/audit_logging/audit_core.h"
+
+#include "../audit_tests_common.h"
+
+/*!
+ * \def BASE_RETRIEVAL_LOG_INDEX
+ *
+ * \brief Base index from where to start elements retrieval
+ */
+#define BASE_RETRIEVAL_LOG_INDEX (6)
+
+/*!
+ * \def FIRST_RETRIEVAL_LOG_INDEX
+ *
+ * \brief Index of the first element in the log
+ */
+#define FIRST_RETRIEVAL_LOG_INDEX (0)
+
+/* List of tests */
+static void tfm_audit_test_1001(struct test_result_t *ret);
+
+static struct test_t audit_veneers_tests[] = {
+ {&tfm_audit_test_1001, "TFM_AUDIT_TEST_1001",
+ "Secure functional", {TEST_PASSED} },
+};
+
+void register_testsuite_s_audit_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(audit_veneers_tests) /
+ sizeof(audit_veneers_tests[0]));
+
+ set_testsuite("Audit Logging secure interface test (TFM_AUDIT_TEST_1XXX)",
+ audit_veneers_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Functional test of the Secure interface
+ *
+ * \note This is a functional test only and doesn't
+ * mean to test all possible combinations of
+ * input parameters and return values.
+ * This tests will leave the log in a certain
+ * status which, in turn, will be evaluated by
+ * the Non Secure functional tests. If any tests
+ * are added here that will leave the log in a
+ * different state, Non Secure functional tests
+ * need to be amended accordingly.
+ */
+static void tfm_audit_test_1001(struct test_result_t *ret)
+{
+ psa_status_t status;
+ uint8_t local_buffer[LOCAL_BUFFER_SIZE], idx;
+ struct psa_audit_record *record = (struct psa_audit_record *)
+ &local_buffer[0];
+ uint32_t num_records, stored_size, record_size;
+ struct psa_audit_record *retrieved_buffer;
+
+ /* Fill the log with 36 records, each record is 28 bytes
+ * we end up filling the log without wrapping
+ */
+ for (idx=0; idx<INITIAL_LOGGING_REQUESTS; idx++) {
+ record->size = sizeof(struct psa_audit_record) - 4;
+ record->id = DUMMY_TEST_RECORD_ID_BASE + idx;
+
+ /* The record doesn't contain any payload */
+ status = psa_audit_add_record(record);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Record addition has returned an error");
+ return;
+ }
+ }
+
+ /* Get the log size */
+ status = psa_audit_get_info(&num_records, &stored_size);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Getting log info has returned error");
+ return;
+ }
+
+ if (stored_size != INITIAL_LOGGING_SIZE) {
+ TEST_FAIL("Expected log size is " STR(INITIAL_LOGGING_SIZE));
+ return;
+ }
+
+ if (num_records != INITIAL_LOGGING_REQUESTS) {
+ TEST_FAIL("Expected log records are " STR(INITIAL_LOGGING_REQUESTS));
+ return;
+ }
+
+ /* Retrieve two log records starting from a given index */
+ for (idx=BASE_RETRIEVAL_LOG_INDEX; idx<BASE_RETRIEVAL_LOG_INDEX+2; idx++) {
+ uint8_t *p_buf =
+ &local_buffer[(idx-BASE_RETRIEVAL_LOG_INDEX)*STANDARD_LOG_ENTRY_SIZE];
+
+ status = psa_audit_retrieve_record(idx,
+ LOCAL_BUFFER_SIZE,
+ NULL,
+ 0,
+ p_buf,
+ &record_size);
+
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Retrieve indexes 6 or 7 has returned an error");
+ return;
+ }
+
+ if (record_size != STANDARD_LOG_ENTRY_SIZE) {
+ TEST_FAIL("Expected log size is " STR(STANDARD_LOG_ENTRY_SIZE));
+ return;
+ }
+ }
+
+ /* Inspect the content of the second log record retrieved */
+ retrieved_buffer = (struct psa_audit_record *)
+ &local_buffer[offsetof(struct log_hdr,size)+STANDARD_LOG_ENTRY_SIZE];
+
+ if (retrieved_buffer->id != ( DUMMY_TEST_RECORD_ID_BASE +
+ (BASE_RETRIEVAL_LOG_INDEX+1) )) {
+ TEST_FAIL("Unexpected argument in the index 7 entry");
+ return;
+ }
+
+ /* Retrieve the last two log records */
+ for (idx=num_records-2; idx<num_records; idx++) {
+ uint8_t *p_buf =
+ &local_buffer[(idx-(num_records-2))*STANDARD_LOG_ENTRY_SIZE];
+
+ status = psa_audit_retrieve_record(idx,
+ LOCAL_BUFFER_SIZE,
+ NULL,
+ 0,
+ p_buf,
+ &record_size);
+
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Retrieve of last two log records has returned error");
+ return;
+ }
+
+ if (record_size != STANDARD_LOG_ENTRY_SIZE) {
+ TEST_FAIL("Expected log size is " STR(STANDARD_LOG_ENTRY_SIZE));
+ return;
+ }
+ }
+
+ /* Inspect the first record retrieved in the local buffer */
+ retrieved_buffer = (struct psa_audit_record *)
+ &local_buffer[offsetof(struct log_hdr,size)];
+
+ if (retrieved_buffer->id != ( DUMMY_TEST_RECORD_ID_BASE +
+ (INITIAL_LOGGING_REQUESTS-2) )) {
+ TEST_FAIL("Unexpected argument in the second last entry");
+ return;
+ }
+
+ /* Retrieve the first log item */
+ status = psa_audit_retrieve_record(0,
+ LOCAL_BUFFER_SIZE,
+ NULL,
+ 0,
+ &local_buffer[0],
+ &record_size);
+
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Retrieve of the first log entry has returned error");
+ return;
+ }
+
+ if (record_size != STANDARD_LOG_ENTRY_SIZE) {
+ TEST_FAIL("Expected log size is " STR(STANDARD_LOG_ENTRY_SIZE));
+ return;
+ }
+
+ if (retrieved_buffer->id != DUMMY_TEST_RECORD_ID_BASE) {
+ TEST_FAIL("Unexpected argument in the first entry");
+ return;
+ }
+
+ status = psa_audit_retrieve_record(num_records - 1,
+ LOCAL_BUFFER_SIZE,
+ NULL,
+ 0,
+ &local_buffer[0],
+ &record_size);
+
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Retrieve of last two log entries has returned error");
+ return;
+ }
+
+ if (record_size != STANDARD_LOG_ENTRY_SIZE) {
+ TEST_FAIL("Expected log size is " STR(STANDARD_LOG_ENTRY_SIZE));
+ return;
+ }
+
+ /* Inspect the item just retrieved */
+ if (retrieved_buffer->id != ( DUMMY_TEST_RECORD_ID_BASE +
+ (INITIAL_LOGGING_REQUESTS-1) )) {
+ TEST_FAIL("Unexpected argument in the second last entry");
+ return;
+ }
+
+ /* Fill one more log record, this will wrap */
+ record->size = sizeof(struct psa_audit_record) - 4;
+ record->id = DUMMY_TEST_RECORD_ID_BASE + INITIAL_LOGGING_REQUESTS;
+
+ /* The addition of this new log item will wrap the log ending */
+ status = psa_audit_add_record(record);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Record addition has returned an error");
+ return;
+ }
+
+ /* Get the log size */
+ status = psa_audit_get_info(&num_records, &stored_size);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Getting log info has returned error");
+ return;
+ }
+
+ /* Check that the log state is the same, the item addition just performed
+ * is resulted into the removal of the oldest entry, so log size and number
+ * of log records is still the same as before
+ */
+ if (stored_size != INITIAL_LOGGING_SIZE) {
+ TEST_FAIL("Expected log size is " STR(INITIAL_LOGGING_SIZE));
+ return;
+ }
+
+ if (num_records != INITIAL_LOGGING_REQUESTS) {
+ TEST_FAIL("Expected log records are " STR(INITIAL_LOGGING_REQUESTS));
+ return;
+ }
+
+ /* Retrieve the last two log records */
+ for (idx=num_records-2; idx<num_records; idx++) {
+ uint8_t *p_buf =
+ &local_buffer[(idx-(num_records-2))*STANDARD_LOG_ENTRY_SIZE];
+
+ /* Retrieve the last two items */
+ status = psa_audit_retrieve_record(idx,
+ LOCAL_BUFFER_SIZE,
+ NULL,
+ 0,
+ p_buf,
+ &record_size);
+
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Retrieve of last two log records has returned error");
+ return;
+ }
+
+ if (record_size != STANDARD_LOG_ENTRY_SIZE) {
+ TEST_FAIL("Expected record size is " STR(STANDARD_LOG_ENTRY_SIZE));
+ return;
+ }
+ }
+
+ /* Inspect the first record retrieved */
+ if (retrieved_buffer->id != ( DUMMY_TEST_RECORD_ID_BASE +
+ (INITIAL_LOGGING_REQUESTS-1) )) {
+ TEST_FAIL("Unexpected argument in the second last entry");
+ return;
+ }
+
+ /* Inspect the second record retrieved in the local buffer */
+ retrieved_buffer = (struct psa_audit_record *)
+ &local_buffer[offsetof(struct log_hdr,size)+STANDARD_LOG_ENTRY_SIZE];
+
+ if (retrieved_buffer->id != ( DUMMY_TEST_RECORD_ID_BASE +
+ (INITIAL_LOGGING_REQUESTS) )) {
+ TEST_FAIL("Unexpected argument in the last entry");
+ return;
+ }
+
+ /* Fill now one big record that will invalidate all existing records */
+ record->size = MAX_LOG_RECORD_SIZE;
+ record->id = DUMMY_TEST_RECORD_ID_BASE + INITIAL_LOGGING_REQUESTS + 1;
+
+ /* The record has maximum possible payload for log size of 1024 */
+ status = psa_audit_add_record(record);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Record addition has returned an error");
+ return;
+ }
+
+ /* Get the log size */
+ status = psa_audit_get_info(&num_records, &stored_size);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Getting log info has returned error");
+ return;
+ }
+
+ /* Check that the log state has one element with maximum size */
+ if (stored_size != MAX_LOG_SIZE) {
+ TEST_FAIL("Expected log size is " STR(MAX_LOG_SIZE));
+ return;
+ }
+
+ if (num_records != 1) {
+ TEST_FAIL("Expected log records are 1");
+ return;
+ }
+
+ /* Try to retrieve the maximum possible size that fits our buffer.
+ * As there is just one big record filling the whole space, nothing
+ * will be returned and the API will fail
+ */
+ status = psa_audit_retrieve_record(0,
+ LOCAL_BUFFER_SIZE,
+ NULL,
+ 0,
+ &local_buffer[0],
+ &record_size);
+
+ if (status == PSA_SUCCESS) {
+ TEST_FAIL("Retrieve of index 0 should fail as it's too big");
+ return;
+ }
+
+ if (record_size != 0) {
+ TEST_FAIL("Retrieved log size has unexpected size instead of 0");
+ return;
+ }
+
+ /* Add two standard length records again */
+ for (idx=0; idx<2; idx++) {
+ record->size = sizeof(struct psa_audit_record) - 4;
+ record->id = DUMMY_TEST_RECORD_ID_BASE +
+ INITIAL_LOGGING_REQUESTS + 2 + idx;
+
+ /* The record doesn't contain any payload */
+ status = psa_audit_add_record(record);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Record addition has returned an error");
+ return;
+ }
+ }
+
+ /* Get the log size */
+ status = psa_audit_get_info(&num_records, &stored_size);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Getting log info has returned error");
+ return;
+ }
+
+ /* As the log was full, the addition of the last two log records results
+ * in the resetting of the log completely. The log will contain only
+ * the last two items we have just added.
+ */
+ if (stored_size != FINAL_LOGGING_SIZE) {
+ TEST_FAIL("Expected log size is " STR(FINAL_LOGGING_SIZE));
+ return;
+ }
+
+ if (num_records != FINAL_LOGGING_REQUESTS) {
+ TEST_FAIL("Expected log records are " STR(FINAL_LOGGING_REQUESTS));
+ return;
+ }
+
+ /* Check the length of each record individually */
+ for (idx=0; idx<num_records; idx++) {
+ status = psa_audit_get_record_info(idx, &stored_size);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Getting record size individually has returned error");
+ return;
+ }
+
+ if (stored_size != STANDARD_LOG_ENTRY_SIZE) {
+ TEST_FAIL("Unexpected log record size for a single standard item");
+ return;
+ }
+ }
+
+ /* Check that if requesting length of a record which is not there fails */
+ status = psa_audit_get_record_info(num_records, &stored_size);
+ if (status == PSA_SUCCESS) {
+ TEST_FAIL("Getting record size for non-existent record has not failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
diff --git a/test/suites/audit/secure/audit_s_tests.h b/test/suites/audit/secure/audit_s_tests.h
new file mode 100644
index 0000000..87bc904
--- /dev/null
+++ b/test/suites/audit/secure/audit_s_tests.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __AUDIT_S_TESTS_H__
+#define __AUDIT_S_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for audit logging secure interface.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_s_audit_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __AUDIT_S_TESTS_H__ */
diff --git a/test/suites/core/CMakeLists.inc b/test/suites/core/CMakeLists.inc
new file mode 100644
index 0000000..3c7d267
--- /dev/null
+++ b/test/suites/core/CMakeLists.inc
@@ -0,0 +1,53 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "core test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(CORE_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+ message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_TEST_CORE)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_TEST_CORE is undefined. ")
+elseif (TFM_PARTITION_TEST_CORE)
+ list(APPEND ALL_SRC_C_NS "${CORE_TEST_DIR}/non_secure/core_test_api.c")
+endif()
+
+if (NOT DEFINED CORE_TEST_POSITIVE)
+ message(FATAL_ERROR "Incomplete build configuration: CORE_TEST_POSITIVE is undefined. ")
+elseif (CORE_TEST_POSITIVE)
+ list(APPEND ALL_SRC_C_NS "${CORE_TEST_DIR}/non_secure/core_ns_positive_testsuite.c")
+endif()
+
+if (NOT DEFINED CORE_TEST_INTERACTIVE)
+ message(FATAL_ERROR "Incomplete build configuration: CORE_TEST_INTERACTIVE is undefined. ")
+elseif (CORE_TEST_INTERACTIVE)
+ list(APPEND ALL_SRC_C_NS "${CORE_TEST_DIR}/non_secure/core_ns_interactive_testsuite.c")
+endif()
+
+# Disable recursion test from core test by default
+if (ENABLE_TFM_CORE_RECURSION_TESTS)
+ add_definitions(-DENABLE_TFM_CORE_RECURSION_TESTS)
+endif()
+
+#Setting include directories
+embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/test/interface/include ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
diff --git a/test/suites/core/non_secure/core_ns_interactive_testsuite.c b/test/suites/core/non_secure/core_ns_interactive_testsuite.c
new file mode 100644
index 0000000..36cd2d1
--- /dev/null
+++ b/test/suites/core/non_secure/core_ns_interactive_testsuite.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "core_ns_tests.h"
+#include "tfm_api.h"
+#include "app/tfm_integ_test.h"
+#include "cmsis_os2.h"
+#include "tfm_nspm_api.h"
+#include "tfm_veneers.h"
+#include "test/suites/core/non_secure/core_test_api.h"
+#include "test/test_services/tfm_core_test/core_test_defs.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+#ifdef TFM_PSA_API
+#include "psa_manifest/sid.h"
+#endif
+
+#ifdef TEST_FRAMEWORK_S
+#include \
+ "test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.h"
+#endif
+
+#define TRY_SFN(fn, ...) \
+ do { \
+ enum tfm_status_e res = (enum tfm_status_e) fn(__VA_ARGS__); \
+ switch(res) { \
+ case TFM_SUCCESS: \
+ TEST_LOG("Secure call to " #fn "(" #__VA_ARGS__") successful!");\
+ break; \
+ case TFM_ERROR_SECURE_DOMAIN_LOCKED: \
+ TEST_LOG("Secure call to " #fn "(" #__VA_ARGS__") failed, " \
+ "S domain locked!");\
+ break; \
+ default: \
+ TEST_LOG("Secure call to " #fn "(" #__VA_ARGS__") failed, " \
+ "generic!");\
+ } \
+ } while(0)
+
+/* Define test suite for core interactive tests */
+/* List of tests */
+static void tfm_core_test_2001(struct test_result_t *ret);
+
+static struct test_t core_tests[] = {
+ {&tfm_core_test_2001, "TFM_CORE_TEST_2001",
+ "Interactive tests", {TEST_PASSED} },
+};
+
+void register_testsuite_ns_core_interactive(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(core_tests) / sizeof(core_tests[0]));
+
+ set_testsuite("Core non-secure interactive tests (TFM_CORE_TEST_2XXX)",
+ core_tests, list_size, p_test_suite);
+}
+
+void execute_ns_interactive_tests(void);
+
+/**
+ * \brief Tests core function with interactive test cases
+ */
+static void tfm_core_test_2001(struct test_result_t *ret)
+{
+ execute_ns_interactive_tests();
+
+ ret->val = TEST_PASSED;
+}
+
+#ifdef TFM_PSA_API
+static psa_status_t psa_test_common(uint32_t sid, uint32_t version,
+ const psa_invec *in_vecs, size_t in_len,
+ psa_outvec *out_vecs, size_t out_len)
+{
+ psa_handle_t handle;
+ psa_status_t status;
+
+ handle = psa_connect(sid, version);
+ if (handle <= 0) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ status = psa_call(handle, PSA_IPC_CALL, in_vecs, in_len, out_vecs, out_len);
+ if (status < 0) {
+ status = CORE_TEST_ERRNO_UNEXPECTED_CORE_BEHAVIOUR;
+ }
+
+ psa_close(handle);
+ return status;
+}
+#endif /* TFM_PSA_API */
+
+/**
+ * \brief secure_decrement_ns_lock_1
+ *
+ */
+void secure_decrement_ns_lock_1(void)
+{
+#ifndef TFM_PSA_API
+ uint32_t testcase_id = CORE_TEST_ID_BLOCK;
+ psa_invec in_vec = {&testcase_id, sizeof(testcase_id)};
+
+ TRY_SFN(tfm_spm_core_test_sfn_veneer, &in_vec, 1, NULL, 0);
+#else
+ psa_status_t err;
+
+ err = psa_test_common(SPM_CORE_TEST_BLOCK_SID,
+ SPM_CORE_TEST_BLOCK_VERSION,
+ NULL, 0, NULL, 0);
+ if (err != PSA_SUCCESS) {
+ TEST_LOG("Secure call to sfn block failed, generic!");
+ }
+#endif
+}
+
+/**
+ * \brief secure_decrement_ns_lock_2
+ *
+ */
+void secure_decrement_ns_lock_2(void)
+{
+#ifndef TFM_PSA_API
+ uint32_t testcase_id = CORE_TEST_ID_BLOCK;
+ psa_invec in_vec = {&testcase_id, sizeof(testcase_id)};
+
+ TRY_SFN(tfm_spm_core_test_sfn_veneer, &in_vec, 1, NULL, 0);
+#else
+ psa_status_t err;
+
+ err = psa_test_common(SPM_CORE_TEST_BLOCK_SID,
+ SPM_CORE_TEST_BLOCK_VERSION,
+ NULL, 0, NULL, 0);
+ if (err != PSA_SUCCESS) {
+ TEST_LOG("Secure call to sfn block failed, generic!");
+ }
+#endif
+}
+/**
+ * \brief Test definition for the RTX - TFM integration tests
+ * scenarios
+ */
+enum test_type {
+ TEST_TYPE_1 = 1, /*!< Sequential test: single task using the NS lock to
+ access TFM */
+ TEST_TYPE_2, /*!< Priority test: high priority tries to preempt TFM,
+ gets delayed */
+ TEST_TYPE_3, /*!< Priority inversion: classical scenario with high
+ priority task waiting on lower priority task
+ undefinitely if NS lock is configured without priority
+ inheritance */
+ TEST_TYPE_4, /*!< non-NS lock: like sequential, but doesn't use any NS
+ lock mechanism */
+ TEST_TYPE_5, /*!< non-NS lock, core locked: high priority tries to
+ overcome the NS lock but finds TFM core locked by
+ lower priority task and fails */
+ TEST_TYPE_6 /*!< Like TEST_TYPE_2, but the high priority task has now a
+ timeout to acquire the NS lock. The timeout will
+ expire only if TFM Core is built with the
+ de-prioritization disabled */
+};
+
+static const osThreadAttr_t tattr_seq = {
+ .name = "seq_task",
+ .stack_size = 1024U,
+ .attr_bits = osThreadJoinable,
+ .tz_module = 1,
+};
+static const osThreadAttr_t tattr_mid = {
+ .name = "mid_task",
+ .stack_size = 512U,
+ .attr_bits = osThreadJoinable,
+ .tz_module = 0,
+ .priority = osPriorityAboveNormal
+};
+static const osThreadAttr_t tattr_pri = {
+ .name = "pri_task",
+ .stack_size = 1024U,
+ .attr_bits = osThreadJoinable,
+ .tz_module = 1,
+ .priority = osPriorityHigh
+};
+
+/**
+ * \brief Mutex id, NS lock
+ */
+static osMutexId_t mutex_id;
+
+/**
+ * \brief Mutex properties, NS lock
+ */
+static const osMutexAttr_t mattr_ns_lock = {
+ .name = "ns_lock",
+ //.attr_bits = osMutexPrioInherit
+};
+
+/**
+ * \brief TFM NS lock options
+ *
+ * \details Options used while acquiring the NS lock
+ */
+struct tfm_ns_lock_options
+{
+ bool use_ns_lock;
+ uint32_t timeout;
+};
+
+/**
+ * \brief tfm_service_request
+ *
+ * \details This function is used to request a TFM service in thread mode.
+ * Optionally uses the NS lock and specifies a timeout for obtaining
+ * the NS lock.
+ */
+static void tfm_service_request(void(*fn)(void),
+ struct tfm_ns_lock_options *ns_lock_options_p)
+{
+ osStatus_t result;
+
+ char buffer[80];
+
+#define LOG_MSG_THREAD(MSG_THREAD) \
+ do { \
+ sprintf(buffer,"%s [%s]", MSG_THREAD, osThreadGetName(osThreadGetId())); \
+ TEST_LOG(buffer); \
+ } \
+ while(0)
+
+ LOG_MSG_THREAD("Trying to acquire the TFM core from NS");
+
+ if (ns_lock_options_p->use_ns_lock) {
+ result = osMutexAcquire(mutex_id,0);
+ if (result == osOK) {
+ LOG_MSG_THREAD("NS Lock: acquired");
+ /* Add a delay here just to let the pri_task try to
+ * acquire the NS lock before seq_task enters secure world
+ */
+ if (!strcmp(osThreadGetName(osThreadGetId()),"seq_task")) {
+ osDelay(100U);
+ }
+ fn();
+ LOG_MSG_THREAD("NS Lock: releasing...");
+ osMutexRelease(mutex_id);
+ } else {
+
+ if (ns_lock_options_p->timeout == osWaitForever) {
+ LOG_MSG_THREAD("Failed to acquire NS lock, keep waiting");
+ } else {
+ LOG_MSG_THREAD("Failed to acquire NS lock, wait with timeout");
+ }
+
+ result = osMutexAcquire(mutex_id,ns_lock_options_p->timeout);
+ if (result == osOK) {
+ LOG_MSG_THREAD("NS Lock: acquired");
+ fn();
+ LOG_MSG_THREAD("NS Lock: releasing...");
+ osMutexRelease(mutex_id);
+ } else if (result == osErrorTimeout) {
+ LOG_MSG_THREAD("NS Lock: failed to acquire, timeout expired");
+ } else {
+ LOG_MSG_THREAD("NS Lock: unexpected failure trying to acquire");
+ }
+ }
+ } else {
+ /* Add a delay here to let the seq_task (which always uses the NS lock)
+ * enter secure world before the pri_task (which can try to overcome the
+ * NS lock in test scenario 5)
+ */
+ if (!strcmp(osThreadGetName(osThreadGetId()),"pri_task")) {
+ osDelay(100U);
+ }
+ fn();
+ }
+}
+
+/**
+ * \brief Non-blocking test thread
+ *
+ */
+__attribute__((noreturn))
+static void mid_task(void *argument)
+{
+ osThreadId_t thread_id_pri;
+ osThreadState_t thread_pri_state;
+ uint32_t idx;
+
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+ tfm_nspm_register_client_id();
+#endif /* TFM_NS_CLIENT_IDENTIFICATION */
+
+ thread_id_pri = *((osThreadId_t *)argument);
+
+ /* go to sleep */
+ osDelay(100U);
+
+ thread_pri_state = osThreadGetState(thread_id_pri);
+
+ if (thread_pri_state == osThreadBlocked) {
+ TEST_LOG("Running [mid_task] while [pri_task] is blocked");
+ } else if (thread_pri_state == osThreadTerminated) {
+ TEST_LOG("Running [mid_task] while [pri_task] is terminated");
+ } else {
+ TEST_LOG("Running [mid_task]");
+ }
+
+ /* Do non TFM related, non blocking, operations */
+ for (idx=0; idx<0x3ffffff; idx++) {
+ }
+
+ TEST_LOG("Exiting [mid_task]");
+
+ osThreadExit();
+}
+
+/**
+ * \brief Priority test thread
+ *
+ */
+__attribute__((noreturn))
+static void pri_task(void *argument)
+{
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+ tfm_nspm_register_client_id();
+#endif /* TFM_NS_CLIENT_IDENTIFICATION */
+
+ /* go to sleep */
+ osDelay(100U);
+
+ /* After wake up, try to get hold of the NS lock */
+ tfm_service_request(secure_decrement_ns_lock_2,
+ (struct tfm_ns_lock_options *)argument);
+
+ osThreadExit();
+}
+
+/**
+ * \brief Sequential test thread
+ *
+ */
+__attribute__((noreturn))
+static void seq_task(void *argument)
+{
+ osThreadId_t thread_id, thread_id_mid;
+ enum test_type test_type;
+
+ /* By default, use NS lock and wait forever if busy, i.e. until unblocked */
+ struct tfm_ns_lock_options ns_lock_opt =
+ {.use_ns_lock=true, .timeout=osWaitForever};
+ struct tfm_ns_lock_options ns_lock_opt_pri =
+ {.use_ns_lock=true, .timeout=osWaitForever};
+
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+ tfm_nspm_register_client_id();
+#endif /* TFM_NS_CLIENT_IDENTIFICATION */
+
+ test_type = *((enum test_type *)argument);
+
+ if (test_type == TEST_TYPE_1) {
+ TEST_LOG("Scenario 1 - Sequential");
+ } else if (test_type == TEST_TYPE_2) {
+ TEST_LOG("Scenario 2 - Priority");
+ thread_id = osThreadNew(pri_task, &ns_lock_opt_pri, &tattr_pri);
+ } else if (test_type == TEST_TYPE_3) {
+ TEST_LOG("Scenario 3 - Priority inversion");
+ thread_id = osThreadNew(pri_task, &ns_lock_opt_pri, &tattr_pri);
+ thread_id_mid = osThreadNew(mid_task, &thread_id, &tattr_mid);
+ } else if (test_type == TEST_TYPE_4) {
+ TEST_LOG("Scenario 4 - non-NS lock");
+ ns_lock_opt.use_ns_lock = false;
+ } else if (test_type == TEST_TYPE_5) {
+ TEST_LOG("Scenario 5 - non-NS lock, core locked");
+ ns_lock_opt_pri.use_ns_lock = false;
+ thread_id = osThreadNew(pri_task, &ns_lock_opt_pri, &tattr_pri);
+ } else if (test_type == TEST_TYPE_6) {
+ TEST_LOG("Scenario 6 - Core prioritization effects on NS world");
+ ns_lock_opt_pri.timeout = 0x10000; /* timed_wait for NS lock */
+ thread_id = osThreadNew(pri_task, &ns_lock_opt_pri, &tattr_pri);
+ } else {
+ TEST_LOG("Scenario not supported");
+ osThreadExit();
+ }
+
+ /* Try to acquire the NS lock */
+ tfm_service_request(secure_decrement_ns_lock_1, &ns_lock_opt);
+
+ if (test_type == TEST_TYPE_1) {
+ TEST_LOG("Scenario 1 - test finished\n");
+ } else if (test_type == TEST_TYPE_2) {
+ osThreadJoin(thread_id);
+ TEST_LOG("Scenario 2 - test finished\n");
+ } else if (test_type == TEST_TYPE_3) {
+ osThreadJoin(thread_id);
+ osThreadJoin(thread_id_mid);
+ TEST_LOG("Scenario 3 - test finished\n");
+ } else if (test_type == TEST_TYPE_4) {
+ TEST_LOG("Scenario 4 - test finished\n");
+ } else if (test_type == TEST_TYPE_5) {
+ osThreadJoin(thread_id);
+ TEST_LOG("Scenario 5 - test finished\n");
+ } else if (test_type == TEST_TYPE_6) {
+ osThreadJoin(thread_id);
+ TEST_LOG("Scenario 6 - test finished\n");
+ }
+
+ osThreadExit();
+}
+
+/**
+ * \brief Execute the interactive tets cases
+ *
+ */
+void execute_ns_interactive_tests(void)
+{
+ uint8_t idx;
+
+ osThreadId_t thread_id;
+
+ /* Test type list */
+ enum test_type test_type[] = {TEST_TYPE_1, TEST_TYPE_2, TEST_TYPE_3,
+ TEST_TYPE_4, TEST_TYPE_5, TEST_TYPE_6};
+
+ /* Create the NS lock -- shared among testing scenarios */
+ mutex_id = osMutexNew(&mattr_ns_lock);
+
+ /* Loop in the test list */
+ for (idx=0; idx<sizeof(test_type); idx++) {
+ /* Spawn the main thread */
+ thread_id = osThreadNew(seq_task, &test_type[idx], &tattr_seq);
+
+ /* Wait for it to finish before moving to the next scenario */
+ osThreadJoin(thread_id);
+ }
+}
diff --git a/test/suites/core/non_secure/core_ns_positive_testsuite.c b/test/suites/core/non_secure/core_ns_positive_testsuite.c
new file mode 100644
index 0000000..82b5fac
--- /dev/null
+++ b/test/suites/core/non_secure/core_ns_positive_testsuite.c
@@ -0,0 +1,853 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2020, Cypress Semiconductor Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "core_ns_tests.h"
+#include "cmsis.h"
+#include "tfm_api.h"
+#include "tfm_plat_test.h"
+#include "test/suites/core/non_secure/core_test_api.h"
+#include "test/test_services/tfm_core_test/core_test_defs.h"
+#ifdef TFM_PSA_API
+#include "psa_manifest/sid.h"
+#else /* TFM_PSA_API */
+#include "tfm_veneers.h"
+#endif /* TFM_PSA_API */
+#ifdef TFM_ENABLE_IRQ_TEST
+#include "tfm_peripherals_def.h"
+#endif
+
+/* Define test suite for core tests */
+/* List of tests */
+
+#define TOSTRING(x) #x
+#define CORE_TEST_DESCRIPTION(number, fn, description) \
+ {fn, "TFM_CORE_TEST_"TOSTRING(number),\
+ description, {TEST_PASSED} }
+
+#ifndef TFM_PSA_API
+static void tfm_core_test_get_caller_client_id(struct test_result_t *ret);
+static void tfm_core_test_spm_request(struct test_result_t *ret);
+#endif /* TFM_PSA_API */
+static void tfm_core_test_ns_thread(struct test_result_t *ret);
+static void tfm_core_test_check_init(struct test_result_t *ret);
+#ifdef ENABLE_TFM_CORE_RECURSION_TESTS
+static void tfm_core_test_recursion(struct test_result_t *ret);
+#endif
+static void tfm_core_test_buffer_check(struct test_result_t *ret);
+static void tfm_core_test_ss_to_ss(struct test_result_t *ret);
+static void tfm_core_test_ss_to_ss_buffer(struct test_result_t *ret);
+#ifdef TFM_ENABLE_PERIPH_ACCESS_TEST
+static void tfm_core_test_peripheral_access(struct test_result_t *ret);
+#endif
+static void tfm_core_test_iovec_sanitization(struct test_result_t *ret);
+static void tfm_core_test_outvec_write(struct test_result_t *ret);
+#ifdef TFM_ENABLE_IRQ_TEST
+static void tfm_core_test_irq(struct test_result_t *ret);
+
+static enum irq_test_scenario_t executing_irq_test_scenario = IRQ_TEST_SCENARIO_NONE;
+static struct irq_test_execution_data_t irq_test_execution_data = {0};
+#endif
+
+static struct test_t core_tests[] = {
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_NS_THREAD, tfm_core_test_ns_thread,
+ "Test service request from NS thread mode"),
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_CHECK_INIT, tfm_core_test_check_init,
+ "Test the success of service init"),
+#ifdef ENABLE_TFM_CORE_RECURSION_TESTS
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_RECURSION, tfm_core_test_recursion,
+ "Test direct recursion of secure services"),
+#endif
+#ifdef TFM_ENABLE_IRQ_TEST
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_SECURE_IRQ,
+ tfm_core_test_irq,
+ "Test secure irq"),
+#endif
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_BUFFER_CHECK, tfm_core_test_buffer_check,
+ "Test secure service buffer accesses"),
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_SS_TO_SS, tfm_core_test_ss_to_ss,
+ "Test secure service to service call"),
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_SS_TO_SS_BUFFER,
+ tfm_core_test_ss_to_ss_buffer,
+ "Test secure service to service call with buffer handling"),
+#ifdef TFM_ENABLE_PERIPH_ACCESS_TEST
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_PERIPHERAL_ACCESS,
+ tfm_core_test_peripheral_access,
+ "Test service peripheral access"),
+#endif
+#ifndef TFM_PSA_API
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_GET_CALLER_CLIENT_ID,
+ tfm_core_test_get_caller_client_id,
+ "Test get caller client ID function"),
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_SPM_REQUEST,
+ tfm_core_test_spm_request,
+ "Test SPM request function"),
+#endif /* TFM_PSA_API */
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_IOVEC_SANITIZATION,
+ tfm_core_test_iovec_sanitization,
+ "Test service parameter sanitization"),
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_OUTVEC_WRITE,
+ tfm_core_test_outvec_write,
+ "Test outvec write"),
+};
+
+void register_testsuite_ns_core_positive(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(core_tests) / sizeof(core_tests[0]));
+
+ set_testsuite("Core non-secure positive tests (TFM_CORE_TEST_1XXX)",
+ core_tests, list_size, p_test_suite);
+}
+
+#ifdef TFM_PSA_API
+static psa_status_t psa_test_common(uint32_t sid, uint32_t version,
+ const psa_invec *in_vecs, size_t in_len,
+ psa_outvec *out_vecs, size_t out_len)
+{
+ psa_handle_t handle;
+ psa_status_t status;
+
+ handle = psa_connect(sid, version);
+ if (handle <= 0) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ status = psa_call(handle, PSA_IPC_CALL, in_vecs, in_len, out_vecs, out_len);
+ if (status < 0) {
+ status = CORE_TEST_ERRNO_UNEXPECTED_CORE_BEHAVIOUR;
+ }
+
+ psa_close(handle);
+ return status;
+}
+#endif /* TFM_PSA_API */
+
+static void tfm_core_test_ns_thread(struct test_result_t *ret)
+{
+ int32_t err;
+#ifndef TFM_PSA_API
+ int32_t test_case_id = CORE_TEST_ID_NS_THREAD;
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+
+ err = tfm_spm_core_test_sfn_veneer(in_vec, 1, NULL, 0);
+#else /* TFM_PSA_API */
+ err = psa_test_common(SPM_CORE_TEST_NS_THREAD_SID,
+ SPM_CORE_TEST_NS_THREAD_VERSION,
+ NULL, 0, NULL, 0);
+#endif /* TFM_PSA_API */
+
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ TEST_FAIL("Secure function call from thread mode should be successful");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+#ifdef TFM_ENABLE_PERIPH_ACCESS_TEST
+static void tfm_core_test_peripheral_access(struct test_result_t *ret)
+{
+ int32_t err;
+
+#ifndef TFM_PSA_API
+ int32_t test_case_id = CORE_TEST_ID_PERIPHERAL_ACCESS;
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+ struct tfm_core_test_call_args_t args = {in_vec, 1, NULL, 0};
+
+ err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
+#else /* TFM_PSA_API */
+ err = psa_test_common(SPM_CORE_TEST_PERIPHERAL_ACCESS_SID,
+ SPM_CORE_TEST_PERIPHERAL_ACCESS_VERSION,
+ NULL, 0, NULL, 0);
+#endif /* TFM_PSA_API */
+
+ switch (err) {
+ case CORE_TEST_ERRNO_SUCCESS:
+ ret->val = TEST_PASSED;
+ return;
+ case CORE_TEST_ERRNO_PERIPHERAL_ACCESS_FAILED:
+ TEST_FAIL("Service peripheral access failed.");
+ return;
+ default:
+ TEST_FAIL("Unexpected return value received.");
+ return;
+ }
+}
+#endif
+
+static void empty_iovecs(psa_invec invec[], psa_outvec outvec[])
+{
+ int i = 0;
+
+ for (i = 0; i < PSA_MAX_IOVEC; ++i) {
+ invec[i].len = 0;
+ invec[i].base = NULL;
+ outvec[i].len = 0;
+ outvec[i].base = NULL;
+ }
+}
+
+static void full_iovecs(psa_invec invec[], psa_outvec outvec[])
+{
+ int i = 0;
+
+ for (i = 0; i < PSA_MAX_IOVEC; ++i) {
+ invec[i].len = sizeof(psa_invec);
+ invec[i].base = invec + i;
+ outvec[i].len = PSA_MAX_IOVEC * sizeof(psa_outvec);
+ outvec[i].base = outvec;
+ }
+}
+
+static void tfm_core_test_iovec_sanitization(struct test_result_t *ret)
+{
+ int32_t err;
+ psa_invec in_vec[PSA_MAX_IOVEC] = {
+ {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0} };
+ psa_outvec out_vec[PSA_MAX_IOVEC] = {
+ {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0} };
+
+#ifndef TFM_PSA_API
+ struct tfm_core_test_call_args_t args = {NULL, 0, NULL, 0};
+#endif /* TFM_PSA_API */
+
+ /* Check a few valid cases */
+
+ /* Execute a call with valid iovecs (empty) */
+ empty_iovecs(in_vec, out_vec);
+
+#ifndef TFM_PSA_API
+ args.in_vec = NULL;
+ args.in_len = 0;
+ args.out_vec = NULL;
+ args.out_len = 0;
+ err = tfm_core_test_call(tfm_spm_core_test_2_slave_service_veneer, &args);
+#else /* TFM_PSA_API */
+ err = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+ SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+ NULL, 0, NULL, 0);
+#endif /* TFM_PSA_API */
+ if (err != CORE_TEST_ERRNO_SUCCESS_2) {
+ TEST_FAIL("iovec sanitization failed on empty vectors.");
+ return;
+ }
+
+ /* Execute a call with valid iovecs (full) */
+ full_iovecs(in_vec, out_vec);
+#ifndef TFM_PSA_API
+ args.in_vec = in_vec;
+ args.in_len = 2;
+ args.out_vec = out_vec;
+ args.out_len = PSA_MAX_IOVEC - args.in_len;
+ err = tfm_core_test_call(tfm_spm_core_test_2_slave_service_veneer, &args);
+#else /* TFM_PSA_API */
+ err = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+ SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+ in_vec, 2, out_vec, 2);
+#endif /* TFM_PSA_API */
+ if (err != CORE_TEST_ERRNO_SUCCESS_2) {
+ TEST_FAIL("iovec sanitization failed on full vectors.");
+ return;
+ }
+
+ /* Execute a call with valid iovecs (different number of vectors) */
+ full_iovecs(in_vec, out_vec);
+#ifndef TFM_PSA_API
+ args.in_vec = in_vec;
+ args.in_len = 2;
+ args.out_vec = out_vec;
+ args.out_len = 1;
+ err = tfm_core_test_call(tfm_spm_core_test_2_slave_service_veneer, &args);
+#else /* TFM_PSA_API */
+ err = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+ SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+ in_vec, 2, out_vec, 1);
+#endif /* TFM_PSA_API */
+ if (err != CORE_TEST_ERRNO_SUCCESS_2) {
+ TEST_FAIL(
+ "iovec sanitization failed on valid, partially full vectors.");
+ return;
+ }
+
+ /* Check some further valid cases to be sure that checks happen only iovecs
+ * that specified valid by the parameters
+ */
+
+ /* Execute a call with base = 0 in single vector in outvec that is out of
+ * range
+ */
+ full_iovecs(in_vec, out_vec);
+ out_vec[1].base = NULL;
+#ifndef TFM_PSA_API
+ args.in_vec = in_vec;
+ args.in_len = 2;
+ args.out_vec = out_vec;
+ args.out_len = 1;
+ err = tfm_core_test_call(tfm_spm_core_test_2_slave_service_veneer, &args);
+#else /* TFM_PSA_API */
+ err = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+ SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+ in_vec, 2, out_vec, 1);
+#endif /* TFM_PSA_API */
+ if (err != CORE_TEST_ERRNO_SUCCESS_2) {
+ TEST_FAIL("content of an outvec out of range should not be checked");
+ return;
+ }
+
+ /* Execute a call with len = 0 in single vector in invec that is out of
+ * range
+ */
+ full_iovecs(in_vec, out_vec);
+ in_vec[2].len = 0;
+#ifndef TFM_PSA_API
+ args.in_vec = in_vec;
+ args.in_len = 2;
+ args.out_vec = out_vec;
+ args.out_len = 1;
+ err = tfm_core_test_call(tfm_spm_core_test_2_slave_service_veneer, &args);
+#else /* TFM_PSA_API */
+ err = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+ SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+ in_vec, 2, out_vec, 1);
+#endif /* TFM_PSA_API */
+ if (err != CORE_TEST_ERRNO_SUCCESS_2) {
+ TEST_FAIL("content of an outvec out of range should not be checked");
+ return;
+ }
+
+ /* Execute a call with len = 0 in single vector in invec */
+ full_iovecs(in_vec, out_vec);
+ in_vec[1].len = 0;
+ in_vec[1].base = NULL;
+#ifndef TFM_PSA_API
+ args.in_vec = in_vec;
+ args.in_len = 2;
+ args.out_vec = out_vec;
+ args.out_len = 2;
+ err = tfm_core_test_call(tfm_spm_core_test_2_slave_service_veneer, &args);
+#else /* TFM_PSA_API */
+ err = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+ SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+ in_vec, 2, out_vec, 2);
+#endif /* TFM_PSA_API */
+ if (err != CORE_TEST_ERRNO_SUCCESS_2) {
+ TEST_FAIL("If the len of an invec is 0, the base should be ignored");
+ return;
+ }
+
+ /* Execute a call with len = 0 in single vector in outvec */
+ full_iovecs(in_vec, out_vec);
+ out_vec[1].len = 0;
+ out_vec[1].base = NULL;
+#ifndef TFM_PSA_API
+ args.in_vec = in_vec;
+ args.in_len = 2;
+ args.out_vec = out_vec;
+ args.out_len = 2;
+ err = tfm_core_test_call(tfm_spm_core_test_2_slave_service_veneer, &args);
+#else /* TFM_PSA_API */
+ err = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+ SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+ in_vec, 2, out_vec, 2);
+#endif /* TFM_PSA_API */
+ if (err != CORE_TEST_ERRNO_SUCCESS_2) {
+ TEST_FAIL("If the len of an outvec is 0, the base should be ignored");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+static void tfm_core_test_outvec_write(struct test_result_t *ret)
+{
+ int32_t err;
+ int i;
+ uint8_t in_buf_0[] = {0, 1, 2, 3, 4};
+ uint8_t in_buf_1[] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89};
+ uint8_t out_buf_0[sizeof(in_buf_0)];
+ uint8_t out_buf_1[sizeof(in_buf_1)];
+
+ psa_invec in_vec[PSA_MAX_IOVEC] = { {in_buf_0, sizeof(in_buf_0)},
+ {in_buf_1, sizeof(in_buf_1)} };
+ psa_outvec out_vec[PSA_MAX_IOVEC] = { {out_buf_0, sizeof(out_buf_0) },
+ {out_buf_1, sizeof(out_buf_1)} };
+#ifndef TFM_PSA_API
+ int32_t test_case_id = CORE_TEST_ID_OUTVEC_WRITE;
+ struct tfm_core_test_call_args_t args1 = {in_vec, 2, out_vec, 2};
+ struct tfm_core_test_call_args_t args2 = {in_vec, 1, NULL, 0};
+
+ err = tfm_core_test_call(tfm_spm_core_test_2_get_every_second_byte_veneer,
+ &args1);
+#else /* TFM_PSA_API */
+ err = psa_test_common(SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE_SID,
+ SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE_VERSION,
+ in_vec, 2, out_vec, 2);
+#endif /* TFM_PSA_API */
+
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ TEST_FAIL("call to secure function should be successful");
+ return;
+ }
+
+ if (out_vec[0].len != sizeof(in_buf_0)/2 ||
+ out_vec[1].len != sizeof(in_buf_1)/2) {
+ TEST_FAIL("Number of elements in outvec is not set properly");
+ return;
+ }
+ for (i = 1; i < sizeof(in_buf_0); i += 2) {
+ if (((uint8_t *)out_vec[0].base)[i/2] != in_buf_0[i]) {
+ TEST_FAIL("result is not correct");
+ return;
+ }
+ }
+ for (i = 1; i < sizeof(in_buf_1); i += 2) {
+ if (((uint8_t *)out_vec[1].base)[i/2] != in_buf_1[i]) {
+ TEST_FAIL("result is not correct");
+ return;
+ }
+ }
+
+ /* do the same test on the secure side */
+#ifndef TFM_PSA_API
+ in_vec[0].base = &test_case_id;
+ in_vec[0].len = sizeof(int32_t);
+ err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args2);
+#else /* TFM_PSA_API */
+ err = psa_test_common(SPM_CORE_TEST_OUTVEC_WRITE_SID,
+ SPM_CORE_TEST_OUTVEC_WRITE_VERSION,
+ in_vec, 0, out_vec, 0);
+#endif /* TFM_PSA_API */
+
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ TEST_FAIL("Failed to execute secure side test");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+#ifdef TFM_ENABLE_IRQ_TEST
+static int32_t prepare_test_scenario_ns(
+ enum irq_test_scenario_t test_scenario,
+ struct irq_test_execution_data_t *execution_data)
+{
+ executing_irq_test_scenario = test_scenario;
+ switch (test_scenario) {
+ case IRQ_TEST_SCENARIO_NONE:
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ case IRQ_TEST_SCENARIO_1:
+ case IRQ_TEST_SCENARIO_2:
+ case IRQ_TEST_SCENARIO_3:
+ case IRQ_TEST_SCENARIO_4:
+ /* nothing to be done here */
+ break;
+ case IRQ_TEST_SCENARIO_5:
+ execution_data->timer1_triggered = 0;
+ tfm_plat_test_non_secure_timer_start();
+ break;
+ default:
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+static int32_t execute_test_scenario_ns(
+ enum irq_test_scenario_t test_scenario,
+ struct irq_test_execution_data_t *execution_data)
+{
+
+ switch (test_scenario) {
+ case IRQ_TEST_SCENARIO_NONE:
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ case IRQ_TEST_SCENARIO_1:
+ if (execution_data->timer0_triggered) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+ while (!execution_data->timer0_triggered) {
+ ;
+ }
+ break;
+ case IRQ_TEST_SCENARIO_2:
+ case IRQ_TEST_SCENARIO_3:
+ case IRQ_TEST_SCENARIO_4:
+ case IRQ_TEST_SCENARIO_5:
+ /* nothing to be done here */
+ break;
+ default:
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+void TIMER1_Handler (void)
+{
+ tfm_plat_test_non_secure_timer_stop();
+
+ switch (executing_irq_test_scenario) {
+ case IRQ_TEST_SCENARIO_NONE:
+ case IRQ_TEST_SCENARIO_1:
+ case IRQ_TEST_SCENARIO_2:
+ case IRQ_TEST_SCENARIO_3:
+ case IRQ_TEST_SCENARIO_4:
+ while (1) {}
+ /* shouldn't happen */
+ break;
+ case IRQ_TEST_SCENARIO_5:
+ irq_test_execution_data.timer1_triggered = 1;
+ break;
+ default:
+ while (1) {}
+ /* shouldn't happen */
+ break;
+ }
+}
+
+static int32_t tfm_core_test_irq_scenario(
+ enum irq_test_scenario_t test_scenario)
+{
+ struct irq_test_execution_data_t *execution_data_address =
+ &irq_test_execution_data;
+ uint32_t scenario = test_scenario;
+
+ psa_invec in_vec[] = {
+ {&scenario, sizeof(uint32_t)},
+ {&execution_data_address,
+ sizeof(struct irq_test_execution_data_t *)} };
+ int32_t err;
+
+#ifdef TFM_PSA_API
+ err = psa_test_common(SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO_SID,
+ SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO_VERSION,
+ in_vec, 2, NULL, 0);
+#else
+ err = tfm_spm_irq_test_1_prepare_test_scenario_veneer(in_vec, 2, NULL, 0);
+#endif
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ return err;
+ }
+
+#ifdef TFM_PSA_API
+ err = psa_test_common(SPM_CORE_TEST_2_PREPARE_TEST_SCENARIO_SID,
+ SPM_CORE_TEST_2_PREPARE_TEST_SCENARIO_VERSION,
+ in_vec, 2, NULL, 0);
+#else
+ err = tfm_spm_core_test_2_prepare_test_scenario_veneer(in_vec, 2, NULL, 0);
+#endif
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ return err;
+ }
+
+ err = prepare_test_scenario_ns(test_scenario, &irq_test_execution_data);
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ return err;
+ }
+
+#ifdef TFM_PSA_API
+ err = psa_test_common(SPM_CORE_IRQ_TEST_1_EXECUTE_TEST_SCENARIO_SID,
+ SPM_CORE_IRQ_TEST_1_EXECUTE_TEST_SCENARIO_VERSION,
+ in_vec, 2, NULL, 0);
+#else
+ err = tfm_spm_irq_test_1_execute_test_scenario_veneer(in_vec, 1, NULL, 0);
+#endif
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ return err;
+ }
+
+#ifdef TFM_PSA_API
+ err = psa_test_common(SPM_CORE_TEST_2_EXECUTE_TEST_SCENARIO_SID,
+ SPM_CORE_TEST_2_EXECUTE_TEST_SCENARIO_VERSION,
+ in_vec, 2, NULL, 0);
+#else
+ err = tfm_spm_core_test_2_execute_test_scenario_veneer(in_vec, 1, NULL, 0);
+#endif
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ return err;
+ }
+
+ err = execute_test_scenario_ns(test_scenario, &irq_test_execution_data);
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ return err;
+ }
+
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+static void tfm_core_test_irq(struct test_result_t *ret)
+{
+ int32_t err;
+
+ struct irq_test_execution_data_t *execution_data_address =
+ &irq_test_execution_data;
+ uint32_t scenario = IRQ_TEST_SCENARIO_NONE;
+
+ psa_invec in_vec[] = {
+ {&scenario, sizeof(uint32_t)},
+ {&execution_data_address,
+ sizeof(struct irq_test_execution_data_t *)} };
+
+ NVIC_EnableIRQ(TFM_TIMER1_IRQ);
+
+ err = tfm_core_test_irq_scenario(IRQ_TEST_SCENARIO_1);
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ TEST_FAIL("Failed to execute IRQ test scenario 1.");
+ return;
+ }
+
+ err = tfm_core_test_irq_scenario(IRQ_TEST_SCENARIO_2);
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ TEST_FAIL("Failed to execute IRQ test scenario 2.");
+ return;
+ }
+
+ err = tfm_core_test_irq_scenario(IRQ_TEST_SCENARIO_3);
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ TEST_FAIL("Failed to execute IRQ test scenario 3.");
+ return;
+ }
+
+ err = tfm_core_test_irq_scenario(IRQ_TEST_SCENARIO_4);
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ TEST_FAIL("Failed to execute IRQ test scenario 4.");
+ return;
+ }
+
+ err = tfm_core_test_irq_scenario(IRQ_TEST_SCENARIO_5);
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ TEST_FAIL("Failed to execute IRQ test scenario 5.");
+ return;
+ }
+
+ /* finally call prepare with scenario none as a teardown */
+#ifdef TFM_PSA_API
+ err = psa_test_common(SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO_SID,
+ SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO_VERSION,
+ in_vec, 2, NULL, 0);
+#else
+ err = tfm_spm_irq_test_1_prepare_test_scenario_veneer(in_vec, 2, NULL, 0);
+#endif
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ TEST_FAIL("Failed to tear down IRQ tests");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+#endif
+
+/*
+ * \brief Tests whether the initialisation of the service was successful.
+ *
+ */
+static void tfm_core_test_check_init(struct test_result_t *ret)
+{
+ int32_t err;
+#ifndef TFM_PSA_API
+ struct tfm_core_test_call_args_t args = {NULL, 0, NULL, 0};
+
+ err = tfm_core_test_call(tfm_spm_core_test_sfn_init_success_veneer, &args);
+#else /* TFM_PSA_API */
+ err = psa_test_common(SPM_CORE_TEST_INIT_SUCCESS_SID,
+ SPM_CORE_TEST_INIT_SUCCESS_VERSION,
+ NULL, 0, NULL, 0);
+#endif /* TFM_PSA_API */
+
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ TEST_FAIL("Failed to initialise test service.");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+#ifdef ENABLE_TFM_CORE_RECURSION_TESTS
+/**
+ * \brief Tests what happens when a service calls itself directly
+ */
+static void tfm_core_test_recursion(struct test_result_t *ret)
+{
+ /*
+ * TODO
+ * Recursive calls will trigger a fatal error. Current test framework cannot
+ * recover from that error.
+ * Leave a test case stub here for further implementation.
+ */
+ TEST_FAIL("The test case is not implemented yet.");
+}
+#endif
+
+static void tfm_core_test_buffer_check(struct test_result_t *ret)
+{
+ int32_t res, i;
+
+ uint32_t inbuf[] = {1, 2, 3, 4, 0xAAAFFF, 0xFFFFFFFF};
+ uint32_t outbuf[16] = {0};
+ int32_t result = 1;
+ psa_invec in_vec[] = { {inbuf, sizeof(inbuf)} };
+ psa_outvec outvec[] = { {outbuf, sizeof(outbuf)},
+ {&result, sizeof(int32_t)} };
+#ifndef TFM_PSA_API
+ struct tfm_core_test_call_args_t args = {in_vec, 1, outvec, 2};
+
+ res = tfm_core_test_call(tfm_spm_core_test_2_sfn_invert_veneer, &args);
+#else /* TFM_PSA_API */
+ res = psa_test_common(SPM_CORE_TEST_2_INVERT_SID,
+ SPM_CORE_TEST_2_INVERT_VERSION,
+ in_vec, 1, outvec, 2);
+#endif /* TFM_PSA_API */
+ if (res != CORE_TEST_ERRNO_SUCCESS) {
+ TEST_FAIL("Call to secure service should be successful.");
+ return;
+ }
+ if (result == 0) {
+ for (i = 0; i < sizeof(inbuf) >> 2; i++) {
+ if (outbuf[i] != ~inbuf[i]) {
+ TEST_FAIL("Secure function failed to modify buffer.");
+ return;
+ }
+ }
+ for (; i < sizeof(outbuf) >> 2; i++) {
+ if (outbuf[i] != 0) {
+ TEST_FAIL("Secure function buffer access overflow.");
+ return;
+ }
+ }
+ } else {
+ TEST_FAIL("Secure service returned error.");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+static void tfm_core_test_ss_to_ss(struct test_result_t *ret)
+{
+ int32_t err;
+
+#ifndef TFM_PSA_API
+ int32_t test_case_id = CORE_TEST_ID_SS_TO_SS;
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+ struct tfm_core_test_call_args_t args = {in_vec, 1, NULL, 0};
+
+ err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
+#else /* TFM_PSA_API */
+ err = psa_test_common(SPM_CORE_TEST_SS_TO_SS_SID,
+ SPM_CORE_TEST_SS_TO_SS_VERSION,
+ NULL, 0, NULL, 0);
+#endif /* TFM_PSA_API */
+
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ TEST_FAIL("The internal service call failed.");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+static void tfm_core_test_ss_to_ss_buffer(struct test_result_t *ret)
+{
+ int32_t res, i;
+
+ uint32_t inbuf[] = {1, 2, 3, 4, 0xAAAFFF, 0xFFFFFFFF};
+ uint32_t outbuf[16] = {0};
+ int32_t len = (int32_t)sizeof(inbuf) >> 2;
+ psa_outvec out_vec[] = { {outbuf, sizeof(outbuf)} };
+#ifndef TFM_PSA_API
+ int32_t test_case_id = CORE_TEST_ID_SS_TO_SS_BUFFER;
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)},
+ {inbuf, sizeof(inbuf)},
+ {&len, sizeof(int32_t)} };
+ struct tfm_core_test_call_args_t args = {in_vec, 3, out_vec, 1};
+
+ res = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
+#else /* TFM_PSA_API */
+ psa_invec in_vec[] = {{inbuf, sizeof(inbuf)},
+ {&len, sizeof(int32_t)} };
+
+ res = psa_test_common(SPM_CORE_TEST_SS_TO_SS_BUFFER_SID,
+ SPM_CORE_TEST_SS_TO_SS_BUFFER_VERSION,
+ in_vec, 2, out_vec, 1);
+#endif /* TFM_PSA_API */
+ switch (res) {
+ case CORE_TEST_ERRNO_SUCCESS:
+ for (i = 0; i < sizeof(inbuf) >> 2; i++) {
+ if (outbuf[i] != ~inbuf[i]) {
+ TEST_FAIL("Secure function failed to modify buffer.");
+ return;
+ }
+ }
+ for (; i < sizeof(outbuf) >> 2; i++) {
+ if (outbuf[i] != 0) {
+ TEST_FAIL("Secure function buffer access overflow.");
+ return;
+ }
+ }
+ ret->val = TEST_PASSED;
+ return;
+ case CORE_TEST_ERRNO_INVALID_BUFFER:
+ TEST_FAIL("NS buffer rejected by TF-M core.");
+ return;
+ case CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE:
+ TEST_FAIL("Slave service call failed.");
+ return;
+ case CORE_TEST_ERRNO_SLAVE_SP_BUFFER_FAILURE:
+ TEST_FAIL("Slave secure function failed to modify buffer.");
+ return;
+ default:
+ TEST_FAIL("Secure service returned error.");
+ return;
+ }
+}
+
+#ifndef TFM_PSA_API
+static void tfm_core_test_get_caller_client_id(struct test_result_t *ret)
+{
+ int32_t err;
+ int32_t test_case_id = CORE_TEST_ID_GET_CALLER_CLIENT_ID;
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+ struct tfm_core_test_call_args_t args = {in_vec, 1, NULL, 0};
+
+ err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
+
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ TEST_FAIL("The internal service call failed.");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+static void tfm_core_test_spm_request(struct test_result_t *ret)
+{
+ int32_t err;
+#ifndef TFM_PSA_API
+ int32_t test_case_id = CORE_TEST_ID_SPM_REQUEST;
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+ struct tfm_core_test_call_args_t args = {in_vec, 1, NULL, 0};
+
+ err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
+#else /* TFM_PSA_API */
+ err = psa_test_common(SPM_CORE_TEST_SPM_REQUEST_SID,
+ SPM_CORE_TEST_SPM_REQUEST_VERSION,
+ NULL, 0, NULL, 0);
+#endif /* TFM_PSA_API */
+
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ TEST_FAIL("The SPM request failed.");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+#endif /* TFM_PSA_API */
diff --git a/test/suites/core/non_secure/core_ns_tests.h b/test/suites/core/non_secure/core_ns_tests.h
new file mode 100644
index 0000000..52d10b5
--- /dev/null
+++ b/test/suites/core/non_secure/core_ns_tests.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CORE_NS_TESTS_H__
+#define __CORE_NS_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for the core positive tests.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_ns_core_positive(struct test_suite_t *p_test_suite);
+
+/**
+ * \brief Register testsuite for the core interactive tests.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_ns_core_interactive(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CORE_NS_TESTS_H__ */
diff --git a/test/suites/core/non_secure/core_test_api.c b/test/suites/core/non_secure/core_test_api.c
new file mode 100644
index 0000000..b49af5d
--- /dev/null
+++ b/test/suites/core/non_secure/core_test_api.c
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "core_test_api.h"
+#include "test/test_services/tfm_core_test/core_test_defs.h"
+
+int32_t tfm_core_test_call(int32_t (*fn_ptr)(struct psa_invec*, size_t,
+ struct psa_outvec*, size_t),
+ struct tfm_core_test_call_args_t *args)
+{
+ return fn_ptr(args->in_vec, args->in_len,
+ args->out_vec, args->out_len);
+}
diff --git a/test/suites/core/non_secure/core_test_api.h b/test/suites/core/non_secure/core_test_api.h
new file mode 100644
index 0000000..eaa8b44
--- /dev/null
+++ b/test/suites/core/non_secure/core_test_api.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CORE_TEST_API_H__
+#define __CORE_TEST_API_H__
+
+#include <stdio.h>
+#include "cmsis_compiler.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief This structure is to pass iovec arguments to the tfm_core_test_call
+ * function.
+ */
+struct tfm_core_test_call_args_t {
+ struct psa_invec *in_vec; /*!< Array of psa_invec objects */
+ size_t in_len; /*!< Number psa_invec objects in in_vec */
+ struct psa_outvec *out_vec; /*!< Array of psa_outvec objects */
+ size_t out_len; /*!< Number psa_outvec objects in out_vec */
+};
+
+/**
+ * \brief Calls the secure function provided in \c fn_ptr
+ *
+ * \param[in] fn_ptr Secure function to be called.
+ * \param[in] args Arguments for fn_ptr.
+ *
+ * \return Returns value depending on fn_ptr.
+ */
+int32_t tfm_core_test_call(int32_t (*fn_ptr)(),
+ struct tfm_core_test_call_args_t *args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CORE_TEST_API_H__ */
diff --git a/test/suites/crypto/CMakeLists.inc b/test/suites/crypto/CMakeLists.inc
new file mode 100644
index 0000000..ae6baf3
--- /dev/null
+++ b/test/suites/crypto/CMakeLists.inc
@@ -0,0 +1,84 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "Crypto test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(CRYPTO_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+ message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_CRYPTO_SERVICE_TESTS)
+ message(FATAL_ERROR "Incomplete build configuration: ENABLE_CRYPTO_SERVICE_TESTS is undefined. ")
+elseif (ENABLE_CRYPTO_SERVICE_TESTS)
+ list(APPEND ALL_SRC_C_S "${CRYPTO_TEST_DIR}/secure/crypto_sec_interface_testsuite.c"
+ "${CRYPTO_TEST_DIR}/crypto_tests_common.c")
+ list(APPEND ALL_SRC_C_NS "${CRYPTO_TEST_DIR}/non_secure/crypto_ns_interface_testsuite.c"
+ "${CRYPTO_TEST_DIR}/crypto_tests_common.c")
+
+ #Enable the test cases by default
+ if (NOT DEFINED TFM_CRYPTO_TEST_ALG_CBC)
+ set(TFM_CRYPTO_TEST_ALG_CBC ON)
+ endif()
+ if (NOT DEFINED TFM_CRYPTO_TEST_ALG_CCM)
+ set(TFM_CRYPTO_TEST_ALG_CCM ON)
+ endif()
+ if (NOT DEFINED TFM_CRYPTO_TEST_ALG_CFB)
+ set(TFM_CRYPTO_TEST_ALG_CFB ON)
+ endif()
+ if (NOT DEFINED TFM_CRYPTO_TEST_ALG_CTR)
+ set(TFM_CRYPTO_TEST_ALG_CTR ON)
+ endif()
+ if (NOT DEFINED TFM_CRYPTO_TEST_ALG_GCM)
+ set(TFM_CRYPTO_TEST_ALG_GCM ON)
+ endif()
+ if (NOT DEFINED TFM_CRYPTO_TEST_ALG_SHA_512)
+ set(TFM_CRYPTO_TEST_ALG_SHA_512 ON)
+ endif()
+ if (NOT DEFINED TFM_CRYPTO_TEST_HKDF)
+ set(TFM_CRYPTO_TEST_HKDF ON)
+ endif()
+
+ if (TFM_CRYPTO_TEST_ALG_CBC)
+ add_definitions(-DTFM_CRYPTO_TEST_ALG_CBC)
+ endif()
+ if (TFM_CRYPTO_TEST_ALG_CCM)
+ add_definitions(-DTFM_CRYPTO_TEST_ALG_CCM)
+ endif()
+ if (TFM_CRYPTO_TEST_ALG_CFB)
+ add_definitions(-DTFM_CRYPTO_TEST_ALG_CFB)
+ endif()
+ if (TFM_CRYPTO_TEST_ALG_CTR)
+ add_definitions(-DTFM_CRYPTO_TEST_ALG_CTR)
+ endif()
+ if (TFM_CRYPTO_TEST_ALG_GCM)
+ add_definitions(-DTFM_CRYPTO_TEST_ALG_GCM)
+ endif()
+ if (TFM_CRYPTO_TEST_ALG_SHA_512)
+ add_definitions(-DTFM_CRYPTO_TEST_ALG_SHA_512)
+ endif()
+ if (TFM_CRYPTO_TEST_HKDF)
+ add_definitions(-DTFM_CRYPTO_TEST_HKDF)
+ endif()
+
+ #Setting include directories
+ embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+ embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+
+endif()
diff --git a/test/suites/crypto/crypto_tests_common.c b/test/suites/crypto/crypto_tests_common.c
new file mode 100644
index 0000000..edbca4d
--- /dev/null
+++ b/test/suites/crypto/crypto_tests_common.c
@@ -0,0 +1,1187 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#if DOMAIN_NS == 1
+#include <string.h>
+#else
+#include "tfm_memory_utils.h"
+#endif
+#include "crypto_tests_common.h"
+
+void psa_key_interface_test(const psa_key_type_t key_type,
+ struct test_result_t *ret)
+{
+ psa_status_t status = PSA_SUCCESS;
+ uint32_t i = 0;
+ psa_key_handle_t key_handle = 0x0u;
+ const uint8_t data[] = "THIS IS MY KEY1";
+ uint8_t exported_data[sizeof(data)] = {0};
+ size_t exported_data_size = 0;
+ psa_key_attributes_t key_attributes = psa_key_attributes_init();
+ psa_key_attributes_t retrieved_attributes = psa_key_attributes_init();
+
+ /* Setup the key policy */
+ psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_EXPORT);
+ psa_set_key_type(&key_attributes, key_type);
+
+ status = psa_import_key(&key_attributes, data, sizeof(data),
+ &key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error importing a key");
+ return;
+ }
+
+ status = psa_get_key_attributes(key_handle, &retrieved_attributes);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error getting key metadata");
+ return;
+ }
+
+ if (psa_get_key_bits(&retrieved_attributes) != BIT_SIZE_TEST_KEY) {
+ TEST_FAIL("The number of key bits is different from expected");
+ return;
+ }
+
+ if (psa_get_key_type(&retrieved_attributes) != key_type) {
+ TEST_FAIL("The type of the key is different from expected");
+ return;
+ }
+
+ psa_reset_key_attributes(&retrieved_attributes);
+
+ status = psa_export_key(key_handle,
+ exported_data,
+ sizeof(data),
+ &exported_data_size);
+
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error exporting a key");
+ return;
+ }
+
+ if (exported_data_size != BYTE_SIZE_TEST_KEY) {
+ TEST_FAIL("Number of bytes of exported key different from expected");
+ return;
+ }
+
+ /* Check that the exported key is the same as the imported one */
+ for (i=0; i<exported_data_size; i++) {
+ if (exported_data[i] != data[i]) {
+ TEST_FAIL("Exported key doesn't match the imported key");
+ return;
+ }
+ }
+
+ status = psa_destroy_key(key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error destroying the key");
+ return;
+ }
+
+ status = psa_get_key_attributes(key_handle, &retrieved_attributes);
+ if (status != PSA_ERROR_INVALID_HANDLE) {
+ TEST_FAIL("Key handle should be invalid now");
+ return;
+ }
+
+ psa_reset_key_attributes(&retrieved_attributes);
+
+ ret->val = TEST_PASSED;
+}
+
+void psa_cipher_test(const psa_key_type_t key_type,
+ const psa_algorithm_t alg,
+ struct test_result_t *ret)
+{
+ psa_cipher_operation_t handle = psa_cipher_operation_init();
+ psa_cipher_operation_t handle_dec = psa_cipher_operation_init();
+ psa_status_t status = PSA_SUCCESS;
+ psa_key_handle_t key_handle;
+ const uint8_t data[] = "THIS IS MY KEY1";
+ const size_t iv_length = PSA_BLOCK_CIPHER_BLOCK_SIZE(key_type);
+ const uint8_t iv[] = "012345678901234";
+ const uint8_t plain_text[BYTE_SIZE_CHUNK] = "Sixteen bytes!!";
+ uint8_t decrypted_data[ENC_DEC_BUFFER_SIZE] = {0};
+ size_t output_length = 0, total_output_length = 0;
+ uint8_t encrypted_data[ENC_DEC_BUFFER_SIZE] = {0};
+ uint32_t comp_result;
+ psa_key_attributes_t key_attributes = psa_key_attributes_init();
+ psa_key_usage_t usage = (PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
+ uint32_t i;
+
+ ret->val = TEST_PASSED;
+
+ /* FIXME: Special override for the CC312 accelerator. Implemented because
+ * there is not yet a generic way to override tests.
+ */
+#ifdef CRYPTO_HW_ACCELERATOR_CC312
+ if (alg == PSA_ALG_CFB) {
+ TEST_LOG("%s %s", "The CC312 does not support CFB mode.",
+ "The test execution was SKIPPED.\r\n");
+ return;
+ }
+#endif /* CRYPTO_HW_ACCELERATOR_CC312 */
+
+ /* Setup the key policy */
+ psa_set_key_usage_flags(&key_attributes, usage);
+ psa_set_key_algorithm(&key_attributes, alg);
+ psa_set_key_type(&key_attributes, key_type);
+
+ /* Import a key */
+ status = psa_import_key(&key_attributes, data, sizeof(data), &key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error importing a key");
+ goto destroy_key;
+ }
+
+ status = psa_get_key_attributes(key_handle, &key_attributes);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error getting key metadata");
+ goto destroy_key;
+ }
+
+ if (psa_get_key_bits(&key_attributes) != BIT_SIZE_TEST_KEY) {
+ TEST_FAIL("The number of key bits is different from expected");
+ goto destroy_key;
+ }
+
+ if (psa_get_key_type(&key_attributes) != key_type) {
+ TEST_FAIL("The type of the key is different from expected");
+ goto destroy_key;
+ }
+
+ psa_reset_key_attributes(&key_attributes);
+
+ /* Setup the encryption object */
+ status = psa_cipher_encrypt_setup(&handle, key_handle, alg);
+ if (status != PSA_SUCCESS) {
+ if (status == PSA_ERROR_NOT_SUPPORTED) {
+ TEST_FAIL("Algorithm NOT SUPPORTED by the implementation");
+ } else {
+ TEST_FAIL("Error setting up cipher operation object");
+ }
+ goto destroy_key;
+ }
+
+ /* Set the IV */
+ status = psa_cipher_set_iv(&handle, iv, iv_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error setting the IV on the cypher operation object");
+ status = psa_cipher_abort(&handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error aborting the operation");
+ }
+ goto destroy_key;
+ }
+
+ /* Encrypt one chunk of information */
+ status = psa_cipher_update(&handle, plain_text, BYTE_SIZE_CHUNK,
+ encrypted_data, ENC_DEC_BUFFER_SIZE,
+ &output_length);
+
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error encrypting one chunk of information");
+ status = psa_cipher_abort(&handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error aborting the operation");
+ }
+ goto destroy_key;
+ }
+
+ if (output_length != BYTE_SIZE_CHUNK) {
+ TEST_FAIL("Expected encrypted data length is different from expected");
+ status = psa_cipher_abort(&handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error aborting the operation");
+ }
+ goto destroy_key;
+ }
+
+ /* Finalise the cipher operation */
+ status = psa_cipher_finish(&handle, &encrypted_data[output_length],
+ ENC_DEC_BUFFER_SIZE - output_length,
+ &output_length);
+
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error finalising the cipher operation");
+ status = psa_cipher_abort(&handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error aborting the operation");
+ }
+ goto destroy_key;
+ }
+
+ if (output_length != 0) {
+ TEST_FAIL("Unexpected output length after finalisation");
+ goto destroy_key;
+ }
+
+ /* Setup the decryption object */
+ if (alg == PSA_ALG_CFB) {
+ /* In CFB mode the object is always in encryption mode */
+ status = psa_cipher_encrypt_setup(&handle_dec, key_handle, alg);
+ } else {
+ status = psa_cipher_decrypt_setup(&handle_dec, key_handle, alg);
+ }
+
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error setting up cipher operation object");
+ goto destroy_key;
+ }
+
+ /* Set the IV for decryption */
+ status = psa_cipher_set_iv(&handle_dec, iv, iv_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error setting the IV for decryption");
+ status = psa_cipher_abort(&handle_dec);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error aborting the operation");
+ }
+ goto destroy_key;
+ }
+
+ /* Decrypt */
+ for (i = 0; i < ENC_DEC_BUFFER_SIZE; i += BYTE_SIZE_CHUNK) {
+ status = psa_cipher_update(&handle_dec,
+ (encrypted_data + i), BYTE_SIZE_CHUNK,
+ (decrypted_data + total_output_length),
+ (ENC_DEC_BUFFER_SIZE - total_output_length),
+ &output_length);
+
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error during decryption");
+ status = psa_cipher_abort(&handle_dec);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error aborting the operation");
+ }
+ goto destroy_key;
+ }
+
+ total_output_length += output_length;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Check that the plain text matches the decrypted data */
+ comp_result = memcmp(plain_text, decrypted_data, sizeof(plain_text));
+#else
+ comp_result = tfm_memcmp(plain_text, decrypted_data, sizeof(plain_text));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Decrypted data doesn't match with plain text");
+ status = psa_cipher_abort(&handle_dec);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error aborting the operation");
+ }
+ goto destroy_key;
+ }
+
+ /* Finalise the cipher operation for decryption (destroys decrypted data) */
+ status = psa_cipher_finish(&handle_dec, decrypted_data, BYTE_SIZE_CHUNK,
+ &output_length);
+
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error finalising the cipher operation");
+ status = psa_cipher_abort(&handle_dec);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error aborting the operation");
+ }
+ goto destroy_key;
+ }
+
+ total_output_length += output_length;
+
+ /* Check that the decrypted length is equal to the original length */
+ if (total_output_length != ENC_DEC_BUFFER_SIZE) {
+ TEST_FAIL("After finalising, unexpected decrypted length");
+ goto destroy_key;
+ }
+
+destroy_key:
+ /* Destroy the key */
+ status = psa_destroy_key(key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error destroying a key");
+ }
+
+}
+
+void psa_invalid_cipher_test(const psa_key_type_t key_type,
+ const psa_algorithm_t alg,
+ const size_t key_size,
+ struct test_result_t *ret)
+{
+ psa_status_t status;
+ psa_cipher_operation_t handle = psa_cipher_operation_init();
+ psa_key_handle_t key_handle;
+ uint8_t data[TEST_MAX_KEY_LENGTH];
+ psa_key_attributes_t key_attributes = psa_key_attributes_init();
+ psa_key_usage_t usage = (PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
+
+ /* Setup the key policy */
+ psa_set_key_usage_flags(&key_attributes, usage);
+ psa_set_key_algorithm(&key_attributes, alg);
+ psa_set_key_type(&key_attributes, key_type);
+
+#if DOMAIN_NS == 1U
+ /* Fill the key data */
+ (void)memset(data, 'A', key_size);
+#else
+ (void)tfm_memset(data, 'A', key_size);
+#endif
+
+ /* Import a key */
+ status = psa_import_key(&key_attributes, data, key_size, &key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error importing a key");
+ return;
+ }
+
+ /* Setup the encryption object */
+ status = psa_cipher_encrypt_setup(&handle, key_handle, alg);
+ if (status == PSA_SUCCESS) {
+ TEST_FAIL("Should not successfully setup an invalid cipher");
+ (void)psa_destroy_key(key_handle);
+ return;
+ }
+
+ /* Destroy the key */
+ status = psa_destroy_key(key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error destroying a key");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void psa_unsupported_hash_test(const psa_algorithm_t alg,
+ struct test_result_t *ret)
+{
+ psa_status_t status;
+ psa_hash_operation_t handle = PSA_HASH_OPERATION_INIT;
+
+ /* Setup the hash object for the unsupported hash algorithm */
+ status = psa_hash_setup(&handle, alg);
+ if (status != PSA_ERROR_NOT_SUPPORTED) {
+ TEST_FAIL("Should not successfully setup an unsupported hash alg");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/*
+ * \brief This is the list of algorithms supported by the current
+ * configuration of the crypto engine used by the crypto
+ * service. In case the crypto engine default capabilities
+ * is changed, this list needs to be updated accordingly
+ */
+static const psa_algorithm_t hash_alg[] = {
+ PSA_ALG_SHA_224,
+ PSA_ALG_SHA_256,
+ PSA_ALG_SHA_384,
+ PSA_ALG_SHA_512,
+};
+
+static const uint8_t hash_val[][PSA_HASH_SIZE(PSA_ALG_SHA_512)] = {
+ {0x00, 0xD2, 0x90, 0xE2, 0x0E, 0x4E, 0xC1, 0x7E, /*!< SHA-224 */
+ 0x7A, 0x95, 0xF5, 0x10, 0x5C, 0x76, 0x74, 0x04,
+ 0x6E, 0xB5, 0x56, 0x5E, 0xE5, 0xE7, 0xBA, 0x15,
+ 0x6C, 0x23, 0x47, 0xF3},
+ {0x6B, 0x22, 0x09, 0x2A, 0x37, 0x1E, 0xF5, 0x14, /*!< SHA-256 */
+ 0xF7, 0x39, 0x4D, 0xCF, 0xAD, 0x4D, 0x17, 0x46,
+ 0x66, 0xCB, 0x33, 0xA0, 0x39, 0xD8, 0x41, 0x4E,
+ 0xF1, 0x2A, 0xD3, 0x4D, 0x69, 0xC3, 0xB5, 0x3E},
+ {0x64, 0x79, 0x11, 0xBB, 0x47, 0x4E, 0x47, 0x59, /*!< SHA-384 */
+ 0x3E, 0x4D, 0xBC, 0x60, 0xA5, 0xF9, 0xBF, 0x9C,
+ 0xC0, 0xBA, 0x55, 0x0F, 0x93, 0xCA, 0x72, 0xDF,
+ 0x57, 0x1E, 0x50, 0x56, 0xF9, 0x4A, 0x01, 0xD6,
+ 0xA5, 0x6F, 0xF7, 0x62, 0x34, 0x4F, 0x48, 0xFD,
+ 0x9D, 0x15, 0x07, 0x42, 0xB7, 0x72, 0x94, 0xB8},
+ {0xB4, 0x1C, 0xA3, 0x6C, 0xA9, 0x67, 0x1D, 0xAD, /*!< SHA-512 */
+ 0x34, 0x1F, 0xBE, 0x1B, 0x83, 0xC4, 0x40, 0x2A,
+ 0x47, 0x42, 0x79, 0xBB, 0x21, 0xCA, 0xF0, 0x60,
+ 0xE4, 0xD2, 0x6E, 0x9B, 0x70, 0x12, 0x34, 0x3F,
+ 0x55, 0x2C, 0x09, 0x31, 0x0A, 0x5B, 0x40, 0x21,
+ 0x01, 0xA8, 0x3B, 0x58, 0xE7, 0x48, 0x13, 0x1A,
+ 0x7E, 0xCD, 0xE1, 0xD2, 0x46, 0x10, 0x58, 0x34,
+ 0x49, 0x14, 0x4B, 0xAA, 0x89, 0xA9, 0xF5, 0xB1},
+};
+
+void psa_hash_test(const psa_algorithm_t alg,
+ struct test_result_t *ret)
+{
+ const char *msg[] = {"This is my test message, ",
+ "please generate a hash for this."};
+
+ const size_t msg_size[] = {25, 32}; /* Length in bytes of msg[0], msg[1] */
+ const uint32_t msg_num = sizeof(msg)/sizeof(msg[0]);
+ uint32_t idx;
+
+ psa_status_t status;
+ psa_hash_operation_t handle = psa_hash_operation_init();
+
+ /* Setup the hash object for the desired hash*/
+ status = psa_hash_setup(&handle, alg);
+
+ if (status != PSA_SUCCESS) {
+ if (status == PSA_ERROR_NOT_SUPPORTED) {
+ TEST_FAIL("Algorithm NOT SUPPORTED by the implementation");
+ return;
+ }
+
+ TEST_FAIL("Error setting up hash operation object");
+ return;
+ }
+
+ /* Update object with all the chunks of message */
+ for (idx=0; idx<msg_num; idx++) {
+ status = psa_hash_update(&handle,
+ (const uint8_t *)msg[idx],msg_size[idx]);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error updating the hash operation object");
+ return;
+ }
+ }
+
+ /* Cycle until idx points to the correct index in the algorithm table */
+ for (idx=0; hash_alg[idx] != alg; idx++);
+
+ /* Finalise and verify that the hash is as expected */
+ status = psa_hash_verify(&handle, &(hash_val[idx][0]), PSA_HASH_SIZE(alg));
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error verifying the hash operation object");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void psa_unsupported_mac_test(const psa_key_type_t key_type,
+ const psa_algorithm_t alg,
+ struct test_result_t *ret)
+{
+ psa_status_t status;
+ psa_key_handle_t key_handle;
+ psa_mac_operation_t handle = PSA_MAC_OPERATION_INIT;
+ psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
+ const uint8_t data[] = "THIS IS MY KEY1";
+
+ ret->val = TEST_PASSED;
+
+ /* Setup the key policy */
+ psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY);
+ psa_set_key_algorithm(&key_attributes, alg);
+ psa_set_key_type(&key_attributes, key_type);
+
+ /* Import key */
+ status = psa_import_key(&key_attributes, data, sizeof(data), &key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error importing a key");
+ return;
+ }
+
+ /* Setup the mac object for the unsupported mac algorithm */
+ status = psa_mac_verify_setup(&handle, key_handle, alg);
+ if (status != PSA_ERROR_NOT_SUPPORTED) {
+ TEST_FAIL("Should not successfully setup an unsupported MAC alg");
+ /* Do not return, to ensure key is destroyed */
+ }
+
+ /* Destroy the key */
+ status = psa_destroy_key(key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error destroying the key");
+ }
+}
+
+static const uint8_t hmac_val[][PSA_HASH_SIZE(PSA_ALG_SHA_512)] = {
+ {0xc1, 0x9f, 0x19, 0xac, 0x05, 0x65, 0x5f, 0x02, /*!< SHA-224 */
+ 0x1b, 0x64, 0x32, 0xd9, 0xb1, 0x49, 0xba, 0x75,
+ 0x05, 0x60, 0x52, 0x4e, 0x78, 0xfa, 0x61, 0xc9,
+ 0x37, 0x5d, 0x7f, 0x58},
+ {0x94, 0x37, 0xbe, 0xb5, 0x7f, 0x7c, 0x5c, 0xb0, /*!< SHA-256 */
+ 0x0a, 0x92, 0x4d, 0xd3, 0xba, 0x7e, 0xb1, 0x1a,
+ 0xdb, 0xa2, 0x25, 0xb2, 0x82, 0x8e, 0xdf, 0xbb,
+ 0x61, 0xbf, 0x91, 0x1d, 0x28, 0x23, 0x4a, 0x04},
+ {0x94, 0x21, 0x9b, 0xc3, 0xd5, 0xed, 0xe6, 0xee, /*!< SHA-384 */
+ 0x42, 0x10, 0x5a, 0x58, 0xa4, 0x4d, 0x67, 0x87,
+ 0x16, 0xa2, 0xa7, 0x6c, 0x2e, 0xc5, 0x85, 0xb7,
+ 0x6a, 0x4c, 0x90, 0xb2, 0x73, 0xee, 0x58, 0x3c,
+ 0x59, 0x16, 0x67, 0xf3, 0x6f, 0x30, 0x99, 0x1c,
+ 0x2a, 0xf7, 0xb1, 0x5f, 0x45, 0x83, 0xf5, 0x9f},
+ {0x8f, 0x76, 0xef, 0x12, 0x0b, 0x92, 0xc2, 0x06, /*!< SHA-512 */
+ 0xce, 0x01, 0x18, 0x75, 0x84, 0x96, 0xd9, 0x6f,
+ 0x23, 0x88, 0xd4, 0xf8, 0xcf, 0x79, 0xf8, 0xcf,
+ 0x27, 0x12, 0x9f, 0xa6, 0x7e, 0x87, 0x9a, 0x68,
+ 0xee, 0xe2, 0xe7, 0x1d, 0x4b, 0xf2, 0x87, 0xc0,
+ 0x05, 0x6a, 0xbd, 0x7f, 0x9d, 0xff, 0xaa, 0xf3,
+ 0x9a, 0x1c, 0xb7, 0xb7, 0xbd, 0x03, 0x61, 0xa3,
+ 0xa9, 0x6a, 0x5d, 0xb2, 0x81, 0xe1, 0x6f, 0x1f},
+};
+
+static const uint8_t long_key_hmac_val[PSA_HASH_SIZE(PSA_ALG_SHA_224)] = {
+ 0x47, 0xa3, 0x42, 0xb1, 0x2f, 0x52, 0xd3, 0x8f, /*!< SHA-224 */
+ 0x1e, 0x02, 0x4a, 0x46, 0x73, 0x0b, 0x77, 0xc1,
+ 0x5e, 0x93, 0x31, 0xa9, 0x3e, 0xc2, 0x81, 0xb5,
+ 0x3d, 0x07, 0x6f, 0x31
+};
+
+void psa_mac_test(const psa_algorithm_t alg,
+ uint8_t use_long_key,
+ struct test_result_t *ret)
+{
+ const char *msg[] = {"This is my test message, ",
+ "please generate a hmac for this."};
+ const size_t msg_size[] = {25, 32}; /* Length in bytes of msg[0], msg[1] */
+ const uint32_t msg_num = sizeof(msg)/sizeof(msg[0]);
+ uint32_t idx;
+
+ psa_key_handle_t key_handle;
+ const uint8_t data[] = "THIS IS MY KEY1";
+ const uint8_t long_data[] = "THIS IS MY UNCOMMONLY LONG KEY1";
+ psa_key_type_t key_type = PSA_KEY_TYPE_HMAC;
+ size_t bit_size_test_key = 0;
+ psa_status_t status;
+ psa_mac_operation_t handle = psa_mac_operation_init();
+ psa_key_attributes_t key_attributes = psa_key_attributes_init();
+ psa_key_attributes_t retrieved_attributes = psa_key_attributes_init();
+ psa_key_usage_t usage = PSA_KEY_USAGE_VERIFY;
+
+ ret->val = TEST_PASSED;
+
+ /* Setup the key policy */
+ psa_set_key_usage_flags(&key_attributes, usage);
+ psa_set_key_algorithm(&key_attributes, alg);
+ psa_set_key_type(&key_attributes, key_type);
+
+ /* Import key */
+ if (use_long_key == 1) {
+ status = psa_import_key(&key_attributes,
+ long_data,
+ sizeof(long_data),
+ &key_handle);
+ } else {
+ status = psa_import_key(&key_attributes,
+ data,
+ sizeof(data),
+ &key_handle);
+ }
+
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error importing a key");
+ return;
+ }
+
+ if (use_long_key == 1) {
+ bit_size_test_key = BIT_SIZE_TEST_LONG_KEY;
+ } else {
+ bit_size_test_key = BIT_SIZE_TEST_KEY;
+ }
+
+ status = psa_get_key_attributes(key_handle, &retrieved_attributes);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error getting key metadata");
+ goto destroy_key_mac;
+ }
+
+ if (psa_get_key_bits(&retrieved_attributes) != bit_size_test_key) {
+ TEST_FAIL("The number of key bits is different from expected");
+ goto destroy_key_mac;
+ }
+
+ if (psa_get_key_type(&retrieved_attributes) != key_type) {
+ TEST_FAIL("The type of the key is different from expected");
+ goto destroy_key_mac;
+ }
+
+ psa_reset_key_attributes(&retrieved_attributes);
+
+ /* Setup the mac object for hmac */
+ status = psa_mac_verify_setup(&handle, key_handle, alg);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error setting up mac operation object");
+ goto destroy_key_mac;
+ }
+
+ /* Update object with all the chunks of message */
+ for (idx=0; idx<msg_num; idx++) {
+ status = psa_mac_update(&handle,
+ (const uint8_t *)msg[idx],
+ msg_size[idx]);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error during mac operation");
+ goto destroy_key_mac;
+ }
+ }
+
+ /* Cycle until idx points to the correct index in the algorithm table */
+ for (idx=0; hash_alg[idx] != PSA_ALG_HMAC_GET_HASH(alg); idx++);
+
+ /* Finalise and verify the mac value */
+ if (use_long_key == 1) {
+ status = psa_mac_verify_finish(
+ &handle,
+ &(long_key_hmac_val[0]),
+ PSA_HASH_SIZE(PSA_ALG_HMAC_GET_HASH(alg)));
+ } else {
+ status = psa_mac_verify_finish(
+ &handle,
+ &(hmac_val[idx][0]),
+ PSA_HASH_SIZE(PSA_ALG_HMAC_GET_HASH(alg)));
+ }
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error during finalising the mac operation");
+ goto destroy_key_mac;
+ }
+
+destroy_key_mac:
+ /* Destroy the key */
+ status = psa_destroy_key(key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error destroying the key");
+ }
+}
+
+void psa_aead_test(const psa_key_type_t key_type,
+ const psa_algorithm_t alg,
+ struct test_result_t *ret)
+{
+ psa_key_handle_t key_handle;
+ const size_t nonce_length = 12;
+ const uint8_t nonce[] = "01234567890";
+ const uint8_t plain_text[BYTE_SIZE_CHUNK] = "Sixteen bytes!!";
+ const uint8_t associated_data[ASSOCIATED_DATA_SIZE] =
+ "This is associated data";
+ uint8_t encrypted_data[ENC_DEC_BUFFER_SIZE] = {0};
+ size_t encrypted_data_length = 0, decrypted_data_length = 0;
+ uint8_t decrypted_data[ENC_DEC_BUFFER_SIZE] = {0};
+ psa_status_t status;
+ const uint8_t data[] = "THIS IS MY KEY1";
+ uint32_t comp_result;
+ psa_key_attributes_t key_attributes = psa_key_attributes_init();
+ psa_key_attributes_t retrieved_attributes = psa_key_attributes_init();
+ psa_key_usage_t usage = (PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
+
+ ret->val = TEST_PASSED;
+
+ /* Setup the key policy */
+ psa_set_key_usage_flags(&key_attributes, usage);
+ psa_set_key_algorithm(&key_attributes, alg);
+ psa_set_key_type(&key_attributes, key_type);
+
+ /* Import a key */
+ status = psa_import_key(&key_attributes, data, sizeof(data), &key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error importing a key");
+ return;
+ }
+
+ status = psa_get_key_attributes(key_handle, &retrieved_attributes);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error getting key metadata");
+ goto destroy_key_aead;
+ }
+
+ if (psa_get_key_bits(&retrieved_attributes) != BIT_SIZE_TEST_KEY) {
+ TEST_FAIL("The number of key bits is different from expected");
+ goto destroy_key_aead;
+ }
+
+ if (psa_get_key_type(&retrieved_attributes) != key_type) {
+ TEST_FAIL("The type of the key is different from expected");
+ goto destroy_key_aead;
+ }
+
+ psa_reset_key_attributes(&retrieved_attributes);
+
+ /* Perform AEAD encryption */
+ status = psa_aead_encrypt(key_handle, alg, nonce, nonce_length,
+ associated_data,
+ sizeof(associated_data),
+ plain_text,
+ sizeof(plain_text),
+ encrypted_data,
+ sizeof(encrypted_data),
+ &encrypted_data_length);
+
+ if (status != PSA_SUCCESS) {
+ if (status == PSA_ERROR_NOT_SUPPORTED) {
+ TEST_FAIL("Algorithm NOT SUPPORTED by the implementation");
+ goto destroy_key_aead;
+ }
+
+ TEST_FAIL("Error performing AEAD encryption");
+ goto destroy_key_aead;
+ }
+
+ if (encrypted_data_length
+ != PSA_AEAD_ENCRYPT_OUTPUT_SIZE(alg, sizeof(plain_text))) {
+ TEST_FAIL("Encrypted data length is different than expected");
+ goto destroy_key_aead;
+ }
+
+ /* Perform AEAD decryption */
+ status = psa_aead_decrypt(key_handle, alg, nonce, nonce_length,
+ associated_data,
+ sizeof(associated_data),
+ encrypted_data,
+ encrypted_data_length,
+ decrypted_data,
+ sizeof(decrypted_data),
+ &decrypted_data_length);
+
+ if (status != PSA_SUCCESS) {
+ if (status == PSA_ERROR_NOT_SUPPORTED) {
+ TEST_FAIL("Algorithm NOT SUPPORTED by the implementation");
+ } else {
+ TEST_FAIL("Error performing AEAD decryption");
+ }
+
+ goto destroy_key_aead;
+ }
+
+ if (sizeof(plain_text) != decrypted_data_length) {
+ TEST_FAIL("Decrypted data length is different from plain text");
+ goto destroy_key_aead;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Check that the decrypted data is the same as the original data */
+ comp_result = memcmp(plain_text, decrypted_data, sizeof(plain_text));
+#else
+ comp_result = tfm_memcmp(plain_text, decrypted_data, sizeof(plain_text));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Decrypted data doesn't match with plain text");
+ goto destroy_key_aead;
+ }
+
+destroy_key_aead:
+ /* Destroy the key */
+ status = psa_destroy_key(key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error destroying a key");
+ }
+}
+
+/*
+ * The list of available AES cipher/AEAD mode for test.
+ * Not all the modes can be available in some use cases and configurations.
+ */
+static const psa_algorithm_t test_aes_mode_array[] = {
+#ifdef TFM_CRYPTO_TEST_ALG_CBC
+ PSA_ALG_CBC_NO_PADDING,
+#endif
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+ PSA_ALG_CCM,
+#endif
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+ PSA_ALG_CFB,
+#endif
+#ifdef TFM_CRYPTO_TEST_ALG_CTR
+ PSA_ALG_CTR,
+#endif
+#ifdef TFM_CRYPTO_TEST_ALG_GCM
+ PSA_ALG_GCM,
+#endif
+ /* In case no AES algorithm is available */
+ PSA_ALG_VENDOR_FLAG,
+};
+
+/* Number of available AES cipher modes */
+#define NR_TEST_AES_MODE (sizeof(test_aes_mode_array) / \
+ sizeof(test_aes_mode_array[0]) - 1)
+
+void psa_invalid_key_length_test(struct test_result_t *ret)
+{
+ psa_status_t status;
+ psa_key_attributes_t key_attributes = psa_key_attributes_init();
+ psa_key_handle_t key_handle;
+ const uint8_t data[19] = {0};
+
+ if (NR_TEST_AES_MODE < 1) {
+ TEST_FAIL("A cipher mode in AES is required in current test case");
+ return;
+ }
+
+ /* Setup the key policy */
+ psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_ENCRYPT);
+ psa_set_key_algorithm(&key_attributes, test_aes_mode_array[0]);
+ psa_set_key_type(&key_attributes, PSA_KEY_TYPE_AES);
+
+ /* AES does not support 152-bit keys */
+ status = psa_import_key(&key_attributes, data, sizeof(data), &key_handle);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Should not successfully import with an invalid key length");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void psa_policy_key_interface_test(struct test_result_t *ret)
+{
+ psa_algorithm_t alg = test_aes_mode_array[0];
+ psa_algorithm_t alg_out;
+ psa_key_lifetime_t lifetime = PSA_KEY_LIFETIME_VOLATILE;
+ psa_key_lifetime_t lifetime_out;
+ psa_key_attributes_t key_attributes = psa_key_attributes_init();
+ psa_key_usage_t usage = PSA_KEY_USAGE_EXPORT;
+ psa_key_usage_t usage_out;
+
+ if (NR_TEST_AES_MODE < 1) {
+ TEST_FAIL("A cipher mode in AES is required in current test case");
+ return;
+ }
+
+ /* Verify that initialised policy forbids all usage */
+ usage_out = psa_get_key_usage_flags(&key_attributes);
+ if (usage_out != 0) {
+ TEST_FAIL("Unexpected usage value");
+ return;
+ }
+
+ alg_out = psa_get_key_algorithm(&key_attributes);
+ if (alg_out != 0) {
+ TEST_FAIL("Unexpected algorithm value");
+ return;
+ }
+
+ /* Set the key policy values */
+ psa_set_key_usage_flags(&key_attributes, usage);
+ psa_set_key_algorithm(&key_attributes, alg);
+
+ /* Check that the key policy has the correct usage */
+ usage_out = psa_get_key_usage_flags(&key_attributes);
+ if (usage_out != usage) {
+ TEST_FAIL("Unexpected usage value");
+ return;
+ }
+
+ /* Check that the key policy has the correct algorithm */
+ alg_out = psa_get_key_algorithm(&key_attributes);
+ if (alg_out != alg) {
+ TEST_FAIL("Unexpected algorithm value");
+ return;
+ }
+
+ /* Check the key handle has the correct key lifetime */
+ lifetime_out = psa_get_key_lifetime(&key_attributes);
+
+ if (lifetime_out != lifetime) {
+ TEST_FAIL("Unexpected key lifetime value");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void psa_policy_invalid_policy_usage_test(struct test_result_t *ret)
+{
+ psa_status_t status;
+ psa_algorithm_t alg, not_permit_alg;
+ psa_cipher_operation_t handle = psa_cipher_operation_init();
+ psa_key_attributes_t key_attributes = psa_key_attributes_init();
+ psa_key_handle_t key_handle;
+ psa_key_usage_t usage = (PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
+ size_t data_len;
+ const uint8_t data[] = "THIS IS MY KEY1";
+ uint8_t data_out[sizeof(data)];
+ uint8_t i, j;
+
+ ret->val = TEST_PASSED;
+
+ if (NR_TEST_AES_MODE < 2) {
+ TEST_LOG("Two cipher modes are required. Skip this test case\r\n");
+ return;
+ }
+
+ /*
+ * Search for two modes for test. Both modes should be Cipher algorithms.
+ * Otherwise, cipher setup may fail before policy permission check.
+ */
+ for (i = 0; i < NR_TEST_AES_MODE - 1; i++) {
+ if (PSA_ALG_IS_CIPHER(test_aes_mode_array[i])) {
+ alg = test_aes_mode_array[i];
+ break;
+ }
+ }
+
+ for (j = i + 1; j < NR_TEST_AES_MODE; j++) {
+ if (PSA_ALG_IS_CIPHER(test_aes_mode_array[j])) {
+ not_permit_alg = test_aes_mode_array[j];
+ break;
+ }
+ }
+
+ if (j == NR_TEST_AES_MODE) {
+ TEST_LOG("Unable to find two Cipher algs. Skip this test case.\r\n");
+ return;
+ }
+
+ /* Setup the key policy */
+ psa_set_key_usage_flags(&key_attributes, usage);
+ psa_set_key_algorithm(&key_attributes, alg);
+ psa_set_key_type(&key_attributes, PSA_KEY_TYPE_AES);
+
+ /* Import a key to the key handle for which policy has been set */
+ status = psa_import_key(&key_attributes, data, sizeof(data), &key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to import a key");
+ return;
+ }
+
+ /* Setup a cipher permitted by the key policy */
+ status = psa_cipher_encrypt_setup(&handle, key_handle, alg);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to setup cipher operation");
+ goto destroy_key;
+ }
+
+ status = psa_cipher_abort(&handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to abort cipher operation");
+ goto destroy_key;
+ }
+
+ /* Attempt to setup a cipher with an alg not permitted by the policy */
+ status = psa_cipher_encrypt_setup(&handle, key_handle, not_permit_alg);
+ if (status != PSA_ERROR_NOT_PERMITTED) {
+ TEST_FAIL("Was able to setup cipher operation with wrong alg");
+ goto destroy_key;
+ }
+
+ /* Attempt to export the key, which is forbidden by the key policy */
+ status = psa_export_key(key_handle, data_out, sizeof(data_out), &data_len);
+ if (status != PSA_ERROR_NOT_PERMITTED) {
+ TEST_FAIL("Should not be able to export key without correct usage");
+ goto destroy_key;
+ }
+
+destroy_key:
+ status = psa_destroy_key(key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to destroy key");
+ }
+}
+
+void psa_persistent_key_test(psa_key_id_t key_id, struct test_result_t *ret)
+{
+ psa_status_t status;
+ int comp_result;
+ psa_key_handle_t key_handle;
+ psa_algorithm_t alg = test_aes_mode_array[0];
+ psa_key_usage_t usage = PSA_KEY_USAGE_EXPORT;
+ psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
+ size_t data_len;
+ const uint8_t data[] = "THIS IS MY KEY1";
+ uint8_t data_out[sizeof(data)] = {0};
+
+ if (NR_TEST_AES_MODE < 1) {
+ TEST_FAIL("A cipher mode in AES is required in current test case");
+ return;
+ }
+
+ /* Setup the key attributes with a key ID to create a persistent key */
+ psa_set_key_id(&key_attributes, key_id);
+ psa_set_key_usage_flags(&key_attributes, usage);
+ psa_set_key_algorithm(&key_attributes, alg);
+ psa_set_key_type(&key_attributes, PSA_KEY_TYPE_AES);
+
+ /* Import key data to create the persistent key */
+ status = psa_import_key(&key_attributes, data, sizeof(data), &key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to import a key");
+ return;
+ }
+
+ /* Close the persistent key handle */
+ status = psa_close_key(key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to close a persistent key handle");
+ return;
+ }
+
+ /* Open the previsously-created persistent key */
+ status = psa_open_key(key_id, &key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to open a persistent key");
+ return;
+ }
+
+ /* Export the persistent key */
+ status = psa_export_key(key_handle, data_out, sizeof(data_out), &data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to export a persistent key");
+ return;
+ }
+
+ if (data_len != sizeof(data)) {
+ TEST_FAIL("Number of bytes of exported key different from expected");
+ return;
+ }
+
+ /* Check that the exported key is the same as the imported one */
+#if DOMAIN_NS == 1U
+ comp_result = memcmp(data_out, data, sizeof(data));
+#else
+ comp_result = tfm_memcmp(data_out, data, sizeof(data));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Exported key does not match the imported key");
+ return;
+ }
+
+ /* Destroy the persistent key */
+ status = psa_destroy_key(key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to destroy a persistent key");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+#define KEY_DERIVE_OUTPUT_LEN 32
+#define KEY_DERIV_SECRET_LEN 16
+#define KEY_DERIV_LABEL_INFO_LEN 8
+#define KEY_DERIV_SEED_SALT_LEN 8
+
+static uint8_t key_deriv_secret[KEY_DERIV_SECRET_LEN];
+static uint8_t key_deriv_label_info[KEY_DERIV_LABEL_INFO_LEN];
+static uint8_t key_deriv_seed_salt[KEY_DERIV_SEED_SALT_LEN];
+
+void psa_key_derivation_test(psa_algorithm_t deriv_alg,
+ struct test_result_t *ret)
+{
+ psa_key_handle_t input_handle = 0, output_handle = 0;
+ psa_key_attributes_t input_key_attr = PSA_KEY_ATTRIBUTES_INIT;
+ psa_key_attributes_t output_key_attr = PSA_KEY_ATTRIBUTES_INIT;
+ psa_key_derivation_operation_t deriv_ops;
+ psa_status_t status;
+ uint8_t counter = 0xA5;
+
+ /* Prepare the parameters */
+#if DOMAIN_NS == 1U
+ memset(key_deriv_secret, counter, KEY_DERIV_SECRET_LEN);
+ memset(key_deriv_label_info, counter++, KEY_DERIV_LABEL_INFO_LEN);
+ memset(key_deriv_seed_salt, counter++, KEY_DERIV_SEED_SALT_LEN);
+#else
+ tfm_memset(key_deriv_secret, counter, KEY_DERIV_SECRET_LEN);
+ tfm_memset(key_deriv_label_info, counter++, KEY_DERIV_LABEL_INFO_LEN);
+ tfm_memset(key_deriv_seed_salt, counter++, KEY_DERIV_SEED_SALT_LEN);
+#endif
+
+ deriv_ops = psa_key_derivation_operation_init();
+
+ psa_set_key_usage_flags(&input_key_attr, PSA_KEY_USAGE_DERIVE);
+ psa_set_key_algorithm(&input_key_attr, deriv_alg);
+ psa_set_key_type(&input_key_attr, PSA_KEY_TYPE_DERIVE);
+
+ /* Force to use HMAC-SHA256 as HMAC operation so far */
+ status = psa_import_key(&input_key_attr, key_deriv_secret,
+ KEY_DERIV_SECRET_LEN, &input_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to import secret");
+ return;
+ }
+
+ status = psa_key_derivation_setup(&deriv_ops, deriv_alg);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to setup derivation operation");
+ goto destroy_key;
+ }
+
+ if (PSA_ALG_IS_TLS12_PRF(deriv_alg) ||
+ PSA_ALG_IS_TLS12_PSK_TO_MS(deriv_alg)) {
+ status = psa_key_derivation_input_bytes(&deriv_ops,
+ PSA_KEY_DERIVATION_INPUT_SEED,
+ key_deriv_seed_salt,
+ KEY_DERIV_SEED_SALT_LEN);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to input seed");
+ goto deriv_abort;
+ }
+
+ status = psa_key_derivation_input_key(&deriv_ops,
+ PSA_KEY_DERIVATION_INPUT_SECRET,
+ input_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to input key");
+ goto deriv_abort;
+ }
+
+ status = psa_key_derivation_input_bytes(&deriv_ops,
+ PSA_KEY_DERIVATION_INPUT_LABEL,
+ key_deriv_label_info,
+ KEY_DERIV_LABEL_INFO_LEN);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to input label");
+ goto deriv_abort;
+ }
+ } else if (PSA_ALG_IS_HKDF(deriv_alg)) {
+ status = psa_key_derivation_input_bytes(&deriv_ops,
+ PSA_KEY_DERIVATION_INPUT_SALT,
+ key_deriv_seed_salt,
+ KEY_DERIV_SEED_SALT_LEN);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to input salt");
+ goto deriv_abort;
+ }
+
+ status = psa_key_derivation_input_key(&deriv_ops,
+ PSA_KEY_DERIVATION_INPUT_SECRET,
+ input_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to input key");
+ goto deriv_abort;
+ }
+
+ status = psa_key_derivation_input_bytes(&deriv_ops,
+ PSA_KEY_DERIVATION_INPUT_INFO,
+ key_deriv_label_info,
+ KEY_DERIV_LABEL_INFO_LEN);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to input info");
+ goto deriv_abort;
+ }
+ } else {
+ TEST_FAIL("Unsupported derivation algorithm");
+ goto deriv_abort;
+ }
+
+ if (NR_TEST_AES_MODE < 1) {
+ TEST_LOG("No AES algorithm to verify. Output raw data instead");
+ psa_set_key_type(&output_key_attr, PSA_KEY_TYPE_RAW_DATA);
+ } else {
+ psa_set_key_usage_flags(&output_key_attr, PSA_KEY_USAGE_ENCRYPT);
+ psa_set_key_algorithm(&output_key_attr, test_aes_mode_array[0]);
+ psa_set_key_type(&output_key_attr, PSA_KEY_TYPE_AES);
+ }
+ psa_set_key_bits(&output_key_attr,
+ PSA_BYTES_TO_BITS(KEY_DERIVE_OUTPUT_LEN));
+
+ status = psa_key_derivation_output_key(&output_key_attr, &deriv_ops,
+ &output_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to output key");
+ goto deriv_abort;
+ }
+
+ ret->val = TEST_PASSED;
+
+deriv_abort:
+ psa_key_derivation_abort(&deriv_ops);
+destroy_key:
+ psa_destroy_key(input_handle);
+ if (output_handle) {
+ psa_destroy_key(output_handle);
+ }
+
+ return;
+}
diff --git a/test/suites/crypto/crypto_tests_common.h b/test/suites/crypto/crypto_tests_common.h
new file mode 100644
index 0000000..67e6a33
--- /dev/null
+++ b/test/suites/crypto/crypto_tests_common.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CRYPTO_TESTS_COMMON_H__
+#define __CRYPTO_TESTS_COMMON_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "psa/crypto.h"
+#include "test/framework/test_framework_helpers.h"
+
+/**
+ * \brief Size of the key to use in tests in bits
+ *
+ */
+#define BIT_SIZE_TEST_KEY (128)
+
+/**
+ * \brief Size of the long key to use in tests in bits
+ *
+ */
+#define BIT_SIZE_TEST_LONG_KEY (256)
+
+/**
+ * \brief Macro to extract the size of the key in bytes
+ *
+ */
+#define BYTE_SIZE_TEST_KEY (BIT_SIZE_TEST_KEY/8)
+
+/**
+ * \brief Size in bytes of a chunk of data to process
+ *
+ */
+#define BYTE_SIZE_CHUNK (16)
+
+/**
+ * \brief Size in bytes of the encryption/decryption buffers
+ *
+ */
+#define ENC_DEC_BUFFER_SIZE (32)
+
+/**
+ * \brief Size in bytes of the associated data to authenticate
+ * in AEAD tests
+ *
+ */
+#define ASSOCIATED_DATA_SIZE (24)
+
+/**
+ * \brief The maximum allowed key length in bytes used in the
+ * tests
+ */
+#define TEST_MAX_KEY_LENGTH (64)
+
+/**
+ * \brief The length of truncated authentication tag for AEAD algorithm
+ */
+#define TRUNCATED_AUTH_TAG_LEN (8)
+
+/**
+ * \brief Tests the key interfaces with different key types
+ *
+ * \param[in] key_type PSA key type
+ * \param[out] ret Test result
+ *
+ */
+void psa_key_interface_test(const psa_key_type_t key_type,
+ struct test_result_t *ret);
+/**
+ * \brief Run block ciphering tests with different algorithms and key types
+ *
+ * \param[in] key_type PSA key type
+ * \param[in] alg PSA algorithm
+ * \param[out] ret Test result
+ *
+ */
+void psa_cipher_test(const psa_key_type_t key_type,
+ const psa_algorithm_t alg,
+ struct test_result_t *ret);
+/**
+ * \brief Tests invalid key type and algorithm combinations for block ciphers
+ *
+ * \param[in] key_type PSA key type
+ * \param[in] alg PSA algorithm
+ * \param[in] key_size Key size
+ * \param[out] ret Test result
+ *
+ */
+void psa_invalid_cipher_test(const psa_key_type_t key_type,
+ const psa_algorithm_t alg,
+ const size_t key_size,
+ struct test_result_t *ret);
+/**
+ * \brief Tests setup of an unsupported hash algorithm
+ *
+ * \param[in] alg PSA algorithm
+ * \param[out] ret Test result
+ *
+ */
+void psa_unsupported_hash_test(const psa_algorithm_t alg,
+ struct test_result_t *ret);
+/**
+ * \brief Tests different hashing algorithms
+ *
+ * \param[in] alg PSA algorithm
+ * \param[out] ret Test result
+ *
+ */
+void psa_hash_test(const psa_algorithm_t alg,
+ struct test_result_t *ret);
+/**
+ * \brief Tests setup of an unsupported MAC algorithm
+ *
+ * \param[in] key_type PSA key type
+ * \param[in] alg PSA algorithm
+ * \param[out] ret Test result
+ *
+ */
+void psa_unsupported_mac_test(const psa_key_type_t key_type,
+ const psa_algorithm_t alg,
+ struct test_result_t *ret);
+/**
+ * \brief Tests different MAC algorithms
+ *
+ * \param[in] alg PSA algorithm
+ * \param[in] use_long_key Flag used to indicate to use the long test key
+ * \param[out] ret Test result
+ *
+ */
+void psa_mac_test(const psa_algorithm_t alg,
+ uint8_t use_long_key,
+ struct test_result_t *ret);
+/**
+ * \brief Run AEAD tests with different algorithms and key types
+ *
+ * \param[in] key_type PSA key type
+ * \param[in] alg PSA algorithm
+ * \param[out] ret Test result
+ *
+ */
+void psa_aead_test(const psa_key_type_t key_type,
+ const psa_algorithm_t alg,
+ struct test_result_t *ret);
+/**
+ * \brief Tests invalid key length
+ *
+ * \param[out] ret Test result
+ *
+ */
+void psa_invalid_key_length_test(struct test_result_t *ret);
+
+/**
+ * \brief Tests the policy key interface
+ *
+ * \param[out] ret Test result
+ *
+ */
+void psa_policy_key_interface_test(struct test_result_t *ret);
+
+/**
+ * \brief Tests invalid policy usage
+ *
+ * \param[out] ret Test result
+ *
+ */
+void psa_policy_invalid_policy_usage_test(struct test_result_t *ret);
+
+/**
+ * \brief Tests persistent keys
+ *
+ * \param[out] ret Test result
+ *
+ */
+void psa_persistent_key_test(psa_key_id_t key_id, struct test_result_t *ret);
+
+/**
+ * \brief Key derivation test
+ *
+ * \param[in] deriv_alg Key derivation algorithm
+ * \param[out] ret Test result
+ */
+void psa_key_derivation_test(psa_algorithm_t deriv_alg,
+ struct test_result_t *ret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CRYPTO_TESTS_COMMON__ */
diff --git a/test/suites/crypto/non_secure/crypto_ns_interface_testsuite.c b/test/suites/crypto/non_secure/crypto_ns_interface_testsuite.c
new file mode 100644
index 0000000..22a0c9d
--- /dev/null
+++ b/test/suites/crypto/non_secure/crypto_ns_interface_testsuite.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test/framework/test_framework_helpers.h"
+#include "tfm_api.h"
+#include "../crypto_tests_common.h"
+
+/* List of tests */
+static void tfm_crypto_test_6001(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CBC
+static void tfm_crypto_test_6002(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CBC */
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_6003(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+#ifdef TFM_CRYPTO_TEST_ALG_CTR
+static void tfm_crypto_test_6005(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CTR */
+static void tfm_crypto_test_6007(struct test_result_t *ret);
+static void tfm_crypto_test_6008(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_6009(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+static void tfm_crypto_test_6010(struct test_result_t *ret);
+static void tfm_crypto_test_6011(struct test_result_t *ret);
+static void tfm_crypto_test_6012(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_6013(struct test_result_t *ret);
+static void tfm_crypto_test_6014(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+static void tfm_crypto_test_6019(struct test_result_t *ret);
+static void tfm_crypto_test_6020(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_6021(struct test_result_t *ret);
+static void tfm_crypto_test_6022(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+static void tfm_crypto_test_6024(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_6030(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+#ifdef TFM_CRYPTO_TEST_ALG_GCM
+static void tfm_crypto_test_6031(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+static void tfm_crypto_test_6032(struct test_result_t *ret);
+static void tfm_crypto_test_6033(struct test_result_t *ret);
+static void tfm_crypto_test_6034(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_6035(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+static void tfm_crypto_test_6036(struct test_result_t *ret);
+static void tfm_crypto_test_6037(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_HKDF
+static void tfm_crypto_test_6038(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_HKDF */
+
+static struct test_t crypto_tests[] = {
+ {&tfm_crypto_test_6001, "TFM_CRYPTO_TEST_6001",
+ "Non Secure Key management interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CBC
+ {&tfm_crypto_test_6002, "TFM_CRYPTO_TEST_6002",
+ "Non Secure Symmetric encryption (AES-128-CBC) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CBC */
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+ {&tfm_crypto_test_6003, "TFM_CRYPTO_TEST_6003",
+ "Non Secure Symmetric encryption (AES-128-CFB) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+#ifdef TFM_CRYPTO_TEST_ALG_CTR
+ {&tfm_crypto_test_6005, "TFM_CRYPTO_TEST_6005",
+ "Non Secure Symmetric encryption (AES-128-CTR) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CTR */
+ {&tfm_crypto_test_6007, "TFM_CRYPTO_TEST_6007",
+ "Non Secure Symmetric encryption invalid cipher", {TEST_PASSED} },
+ {&tfm_crypto_test_6008, "TFM_CRYPTO_TEST_6008",
+ "Non Secure Symmetric encryption invalid cipher (AES-152)", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+ {&tfm_crypto_test_6009, "TFM_CRYPTO_TEST_6009",
+ "Non Secure Symmetric encryption invalid cipher (HMAC-128-CFB)", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+ {&tfm_crypto_test_6010, "TFM_CRYPTO_TEST_6010",
+ "Non Secure Unsupported Hash (SHA-1) interface", {TEST_PASSED} },
+ {&tfm_crypto_test_6011, "TFM_CRYPTO_TEST_6011",
+ "Non Secure Hash (SHA-224) interface", {TEST_PASSED} },
+ {&tfm_crypto_test_6012, "TFM_CRYPTO_TEST_6012",
+ "Non Secure Hash (SHA-256) interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+ {&tfm_crypto_test_6013, "TFM_CRYPTO_TEST_6013",
+ "Non Secure Hash (SHA-384) interface", {TEST_PASSED} },
+ {&tfm_crypto_test_6014, "TFM_CRYPTO_TEST_6014",
+ "Non Secure Hash (SHA-512) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+ {&tfm_crypto_test_6019, "TFM_CRYPTO_TEST_6019",
+ "Non Secure Unsupported HMAC (SHA-1) interface", {TEST_PASSED} },
+ {&tfm_crypto_test_6020, "TFM_CRYPTO_TEST_6020",
+ "Non Secure HMAC (SHA-256) interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+ {&tfm_crypto_test_6021, "TFM_CRYPTO_TEST_6021",
+ "Non Secure HMAC (SHA-384) interface", {TEST_PASSED} },
+ {&tfm_crypto_test_6022, "TFM_CRYPTO_TEST_6022",
+ "Non Secure HMAC (SHA-512) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+ {&tfm_crypto_test_6024, "TFM_CRYPTO_TEST_6024",
+ "Non Secure HMAC with long key (SHA-224) interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+ {&tfm_crypto_test_6030, "TFM_CRYPTO_TEST_6030",
+ "Non Secure AEAD (AES-128-CCM) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+#ifdef TFM_CRYPTO_TEST_ALG_GCM
+ {&tfm_crypto_test_6031, "TFM_CRYPTO_TEST_6031",
+ "Non Secure AEAD (AES-128-GCM) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+ {&tfm_crypto_test_6032, "TFM_CRYPTO_TEST_6032",
+ "Non Secure key policy interface", {TEST_PASSED} },
+ {&tfm_crypto_test_6033, "TFM_CRYPTO_TEST_6033",
+ "Non Secure key policy check permissions", {TEST_PASSED} },
+ {&tfm_crypto_test_6034, "TFM_CRYPTO_TEST_6034",
+ "Non Secure persistent key interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+ {&tfm_crypto_test_6035, "TFM_CRYPTO_TEST_6035",
+ "Non Secure AEAD interface with truncated auth tag (AES-128-CCM-8)",
+ {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+ {&tfm_crypto_test_6036, "TFM_CRYPTO_TEST_6036",
+ "Non Secure TLS 1.2 PRF key derivation", {TEST_PASSED} },
+ {&tfm_crypto_test_6037, "TFM_CRYPTO_TEST_6037",
+ "Non Secure TLS-1.2 PSK-to-MasterSecret key derivation", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_HKDF
+ {&tfm_crypto_test_6038, "TFM_CRYPTO_TEST_6038",
+ "Non Secure HKDF key derivation", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_HKDF */
+};
+
+void register_testsuite_ns_crypto_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size = (sizeof(crypto_tests) / sizeof(crypto_tests[0]));
+
+ set_testsuite("Crypto non-secure interface test (TFM_CRYPTO_TEST_6XXX)",
+ crypto_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Non-Secure interface test for Crypto
+ *
+ * \details The scope of this set of tests is to functionally verify
+ * the interfaces specified by psa/crypto.h are working
+ * as expected. This is not meant to cover all possible
+ * scenarios and corner cases.
+ *
+ */
+static void tfm_crypto_test_6001(struct test_result_t *ret)
+{
+ psa_key_interface_test(PSA_KEY_TYPE_AES, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CBC
+static void tfm_crypto_test_6002(struct test_result_t *ret)
+{
+ psa_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_CBC_NO_PADDING, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CBC */
+
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_6003(struct test_result_t *ret)
+{
+ psa_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_CFB, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+
+#ifdef TFM_CRYPTO_TEST_ALG_CTR
+static void tfm_crypto_test_6005(struct test_result_t *ret)
+{
+ psa_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_CTR, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CTR */
+
+static void tfm_crypto_test_6007(struct test_result_t *ret)
+{
+ psa_invalid_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_HMAC(PSA_ALG_SHA_256),
+ 16, ret);
+}
+
+static void tfm_crypto_test_6008(struct test_result_t *ret)
+{
+ psa_invalid_key_length_test(ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_6009(struct test_result_t *ret)
+{
+ /* HMAC is not a block cipher */
+ psa_invalid_cipher_test(PSA_KEY_TYPE_HMAC, PSA_ALG_CFB, 16, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+
+static void tfm_crypto_test_6010(struct test_result_t *ret)
+{
+ psa_unsupported_hash_test(PSA_ALG_SHA_1, ret);
+}
+
+static void tfm_crypto_test_6011(struct test_result_t *ret)
+{
+ psa_hash_test(PSA_ALG_SHA_224, ret);
+}
+
+static void tfm_crypto_test_6012(struct test_result_t *ret)
+{
+ psa_hash_test(PSA_ALG_SHA_256, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_6013(struct test_result_t *ret)
+{
+ psa_hash_test(PSA_ALG_SHA_384, ret);
+}
+
+static void tfm_crypto_test_6014(struct test_result_t *ret)
+{
+ psa_hash_test(PSA_ALG_SHA_512, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+
+static void tfm_crypto_test_6019(struct test_result_t *ret)
+{
+ psa_unsupported_mac_test(PSA_KEY_TYPE_HMAC, PSA_ALG_HMAC(PSA_ALG_SHA_1),
+ ret);
+}
+
+static void tfm_crypto_test_6020(struct test_result_t *ret)
+{
+ psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_256), 0, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_6021(struct test_result_t *ret)
+{
+ psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_384), 0, ret);
+}
+
+static void tfm_crypto_test_6022(struct test_result_t *ret)
+{
+ psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_512), 0, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+
+static void tfm_crypto_test_6024(struct test_result_t *ret)
+{
+ psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_224), 1, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_6030(struct test_result_t *ret)
+{
+ psa_aead_test(PSA_KEY_TYPE_AES, PSA_ALG_CCM, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+
+#ifdef TFM_CRYPTO_TEST_ALG_GCM
+static void tfm_crypto_test_6031(struct test_result_t *ret)
+{
+ psa_aead_test(PSA_KEY_TYPE_AES, PSA_ALG_GCM, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+
+static void tfm_crypto_test_6032(struct test_result_t *ret)
+{
+ psa_policy_key_interface_test(ret);
+}
+
+static void tfm_crypto_test_6033(struct test_result_t *ret)
+{
+ psa_policy_invalid_policy_usage_test(ret);
+}
+
+static void tfm_crypto_test_6034(struct test_result_t *ret)
+{
+ psa_persistent_key_test(1, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_6035(struct test_result_t *ret)
+{
+ psa_algorithm_t alg = PSA_ALG_AEAD_WITH_TAG_LENGTH(PSA_ALG_CCM,
+ TRUNCATED_AUTH_TAG_LEN);
+
+ psa_aead_test(PSA_KEY_TYPE_AES, alg, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+
+static void tfm_crypto_test_6036(struct test_result_t *ret)
+{
+ psa_key_derivation_test(PSA_ALG_TLS12_PRF(PSA_ALG_SHA_256), ret);
+}
+
+static void tfm_crypto_test_6037(struct test_result_t *ret)
+{
+ psa_key_derivation_test(PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256), ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_HKDF
+static void tfm_crypto_test_6038(struct test_result_t *ret)
+{
+ psa_key_derivation_test(PSA_ALG_HKDF(PSA_ALG_SHA_256), ret);
+}
+#endif /* TFM_CRYPTO_TEST_HKDF */
diff --git a/test/suites/crypto/non_secure/crypto_ns_tests.h b/test/suites/crypto/non_secure/crypto_ns_tests.h
new file mode 100644
index 0000000..aba8c87
--- /dev/null
+++ b/test/suites/crypto/non_secure/crypto_ns_tests.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CRYPTO_NS_TESTS_H__
+#define __CRYPTO_NS_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for Crypto non-secure interface.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_ns_crypto_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CRYPTO_NS_TESTS_H__ */
diff --git a/test/suites/crypto/secure/crypto_s_tests.h b/test/suites/crypto/secure/crypto_s_tests.h
new file mode 100644
index 0000000..6f5f359
--- /dev/null
+++ b/test/suites/crypto/secure/crypto_s_tests.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CRYPTO_S_TESTS_H__
+#define __CRYPTO_S_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+void register_testsuite_s_crypto_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CRYPTO_S_TESTS_H__ */
diff --git a/test/suites/crypto/secure/crypto_sec_interface_testsuite.c b/test/suites/crypto/secure/crypto_sec_interface_testsuite.c
new file mode 100644
index 0000000..bd314e0
--- /dev/null
+++ b/test/suites/crypto/secure/crypto_sec_interface_testsuite.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test/framework/test_framework_helpers.h"
+#include "test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.h"
+#include "tfm_api.h"
+#include "../crypto_tests_common.h"
+
+/* List of tests */
+static void tfm_crypto_test_5001(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CBC
+static void tfm_crypto_test_5002(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CBC */
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_5003(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+#ifdef TFM_CRYPTO_TEST_ALG_CTR
+static void tfm_crypto_test_5005(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CTR */
+static void tfm_crypto_test_5007(struct test_result_t *ret);
+static void tfm_crypto_test_5008(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_5009(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+static void tfm_crypto_test_5010(struct test_result_t *ret);
+static void tfm_crypto_test_5011(struct test_result_t *ret);
+static void tfm_crypto_test_5012(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_5013(struct test_result_t *ret);
+static void tfm_crypto_test_5014(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+static void tfm_crypto_test_5019(struct test_result_t *ret);
+static void tfm_crypto_test_5020(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_5021(struct test_result_t *ret);
+static void tfm_crypto_test_5022(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+static void tfm_crypto_test_5024(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_5030(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+#ifdef TFM_CRYPTO_TEST_ALG_GCM
+static void tfm_crypto_test_5031(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+static void tfm_crypto_test_5032(struct test_result_t *ret);
+static void tfm_crypto_test_5033(struct test_result_t *ret);
+static void tfm_crypto_test_5034(struct test_result_t *ret);
+static void tfm_crypto_test_5035(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_5036(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+static void tfm_crypto_test_5037(struct test_result_t *ret);
+static void tfm_crypto_test_5038(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_HKDF
+static void tfm_crypto_test_5039(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_HKDF */
+
+static struct test_t crypto_tests[] = {
+ {&tfm_crypto_test_5001, "TFM_CRYPTO_TEST_5001",
+ "Secure Key management interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CBC
+ {&tfm_crypto_test_5002, "TFM_CRYPTO_TEST_5002",
+ "Secure Symmetric encryption (AES-128-CBC) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CBC */
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+ {&tfm_crypto_test_5003, "TFM_CRYPTO_TEST_5003",
+ "Secure Symmetric encryption (AES-128-CFB) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+#ifdef TFM_CRYPTO_TEST_ALG_CTR
+ {&tfm_crypto_test_5005, "TFM_CRYPTO_TEST_5005",
+ "Secure Symmetric encryption (AES-128-CTR) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CTR */
+ {&tfm_crypto_test_5007, "TFM_CRYPTO_TEST_5007",
+ "Secure Symmetric encryption invalid cipher", {TEST_PASSED} },
+ {&tfm_crypto_test_5008, "TFM_CRYPTO_TEST_5008",
+ "Secure Symmetric encryption invalid cipher (AES-152)", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+ {&tfm_crypto_test_5009, "TFM_CRYPTO_TEST_5009",
+ "Secure Symmetric encryption invalid cipher (HMAC-128-CFB)", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+ {&tfm_crypto_test_5010, "TFM_CRYPTO_TEST_5010",
+ "Secure Unsupported Hash (SHA-1) interface", {TEST_PASSED} },
+ {&tfm_crypto_test_5011, "TFM_CRYPTO_TEST_5011",
+ "Secure Hash (SHA-224) interface", {TEST_PASSED} },
+ {&tfm_crypto_test_5012, "TFM_CRYPTO_TEST_5012",
+ "Secure Hash (SHA-256) interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+ {&tfm_crypto_test_5013, "TFM_CRYPTO_TEST_5013",
+ "Secure Hash (SHA-384) interface", {TEST_PASSED} },
+ {&tfm_crypto_test_5014, "TFM_CRYPTO_TEST_5014",
+ "Secure Hash (SHA-512) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+ {&tfm_crypto_test_5019, "TFM_CRYPTO_TEST_5019",
+ "Secure Unsupported HMAC (SHA-1) interface", {TEST_PASSED} },
+ {&tfm_crypto_test_5020, "TFM_CRYPTO_TEST_5020",
+ "Secure HMAC (SHA-256) interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+ {&tfm_crypto_test_5021, "TFM_CRYPTO_TEST_5021",
+ "Secure HMAC (SHA-384) interface", {TEST_PASSED} },
+ {&tfm_crypto_test_5022, "TFM_CRYPTO_TEST_5022",
+ "Secure HMAC (SHA-512) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+ {&tfm_crypto_test_5024, "TFM_CRYPTO_TEST_5024",
+ "Secure HMAC with long key (SHA-224) interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+ {&tfm_crypto_test_5030, "TFM_CRYPTO_TEST_5030",
+ "Secure AEAD (AES-128-CCM) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+#ifdef TFM_CRYPTO_TEST_ALG_GCM
+ {&tfm_crypto_test_5031, "TFM_CRYPTO_TEST_5031",
+ "Secure AEAD (AES-128-GCM) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+ {&tfm_crypto_test_5032, "TFM_CRYPTO_TEST_5032",
+ "Secure key policy interface", {TEST_PASSED} },
+ {&tfm_crypto_test_5033, "TFM_CRYPTO_TEST_5033",
+ "Secure key policy check permissions", {TEST_PASSED} },
+ {&tfm_crypto_test_5034, "TFM_CRYPTO_TEST_5034",
+ "Secure persistent key interface", {TEST_PASSED} },
+ {&tfm_crypto_test_5035, "TFM_CRYPTO_TEST_5035",
+ "Key access control", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+ {&tfm_crypto_test_5036, "TFM_CRYPTO_TEST_6036",
+ "Secure AEAD interface with truncated auth tag (AES-128-CCM-8)",
+ {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+ {&tfm_crypto_test_5037, "TFM_CRYPTO_TEST_5037",
+ "Secure TLS 1.2 PRF key derivation", {TEST_PASSED} },
+ {&tfm_crypto_test_5038, "TFM_CRYPTO_TEST_5038",
+ "Secure TLS-1.2 PSK-to-MasterSecret key derivation", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_HKDF
+ {&tfm_crypto_test_5039, "TFM_CRYPTO_TEST_5039",
+ "Secure HKDF key derivation", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_HKDF */
+};
+
+void register_testsuite_s_crypto_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size = (sizeof(crypto_tests) / sizeof(crypto_tests[0]));
+
+ set_testsuite("Crypto secure interface tests (TFM_CRYPTO_TEST_5XXX)",
+ crypto_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Secure interface test for Crypto
+ *
+ * \details The scope of this set of tests is to functionally verify
+ * the interfaces specified by psa/crypto.h are working
+ * as expected. This is not meant to cover all possible
+ * scenarios and corner cases.
+ *
+ */
+static void tfm_crypto_test_5001(struct test_result_t *ret)
+{
+ psa_key_interface_test(PSA_KEY_TYPE_AES, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CBC
+static void tfm_crypto_test_5002(struct test_result_t *ret)
+{
+ psa_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_CBC_NO_PADDING, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CBC */
+
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_5003(struct test_result_t *ret)
+{
+ psa_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_CFB, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+
+#ifdef TFM_CRYPTO_TEST_ALG_CTR
+static void tfm_crypto_test_5005(struct test_result_t *ret)
+{
+ psa_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_CTR, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CTR */
+
+static void tfm_crypto_test_5007(struct test_result_t *ret)
+{
+ psa_invalid_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_HMAC(PSA_ALG_SHA_256),
+ 16, ret);
+}
+
+static void tfm_crypto_test_5008(struct test_result_t *ret)
+{
+ psa_invalid_key_length_test(ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_5009(struct test_result_t *ret)
+{
+ /* HMAC is not a block cipher */
+ psa_invalid_cipher_test(PSA_KEY_TYPE_HMAC, PSA_ALG_CFB, 16, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+
+static void tfm_crypto_test_5010(struct test_result_t *ret)
+{
+ psa_unsupported_hash_test(PSA_ALG_SHA_1, ret);
+}
+
+static void tfm_crypto_test_5011(struct test_result_t *ret)
+{
+ psa_hash_test(PSA_ALG_SHA_224, ret);
+}
+
+static void tfm_crypto_test_5012(struct test_result_t *ret)
+{
+ psa_hash_test(PSA_ALG_SHA_256, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_5013(struct test_result_t *ret)
+{
+ psa_hash_test(PSA_ALG_SHA_384, ret);
+}
+
+static void tfm_crypto_test_5014(struct test_result_t *ret)
+{
+ psa_hash_test(PSA_ALG_SHA_512, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+
+static void tfm_crypto_test_5019(struct test_result_t *ret)
+{
+ psa_unsupported_mac_test(PSA_KEY_TYPE_HMAC, PSA_ALG_HMAC(PSA_ALG_SHA_1),
+ ret);
+}
+
+static void tfm_crypto_test_5020(struct test_result_t *ret)
+{
+ psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_256), 0, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_5021(struct test_result_t *ret)
+{
+ psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_384), 0, ret);
+}
+
+static void tfm_crypto_test_5022(struct test_result_t *ret)
+{
+ psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_512), 0, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+
+static void tfm_crypto_test_5024(struct test_result_t *ret)
+{
+ psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_224), 1, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_5030(struct test_result_t *ret)
+{
+ psa_aead_test(PSA_KEY_TYPE_AES, PSA_ALG_CCM, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+
+#ifdef TFM_CRYPTO_TEST_ALG_GCM
+static void tfm_crypto_test_5031(struct test_result_t *ret)
+{
+ psa_aead_test(PSA_KEY_TYPE_AES, PSA_ALG_GCM, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+
+static void tfm_crypto_test_5032(struct test_result_t *ret)
+{
+ psa_policy_key_interface_test(ret);
+}
+
+static void tfm_crypto_test_5033(struct test_result_t *ret)
+{
+ psa_policy_invalid_policy_usage_test(ret);
+}
+
+static void tfm_crypto_test_5034(struct test_result_t *ret)
+{
+ psa_persistent_key_test(1, ret);
+}
+
+/**
+ * \brief Tests key access control based on partition ID
+ *
+ * \param[out] ret Test result
+ */
+static void tfm_crypto_test_5035(struct test_result_t *ret)
+{
+ psa_status_t status;
+ psa_key_handle_t key_handle;
+ const uint8_t data[] = "THIS IS MY KEY1";
+ psa_key_attributes_t key_attributes = psa_key_attributes_init();
+
+ /* Set key sage and type */
+ psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_EXPORT);
+ psa_set_key_type(&key_attributes, PSA_KEY_TYPE_AES);
+
+ status = psa_import_key(&key_attributes, data, sizeof(data),
+ &key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Failed to import key");
+ return;
+ }
+
+ /* Attempt to destroy the key handle from the Secure Client 2 partition */
+ status = tfm_secure_client_2_call_test(
+ TFM_SECURE_CLIENT_2_ID_CRYPTO_ACCESS_CTRL,
+ &key_handle, sizeof(key_handle));
+ if (status != PSA_ERROR_NOT_PERMITTED) {
+ TEST_FAIL("Should not be able to destroy key from another partition");
+ return;
+ }
+
+ /* Destroy the key */
+ status = psa_destroy_key(key_handle);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Error destroying a key");
+ }
+ return;
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_5036(struct test_result_t *ret)
+{
+ psa_algorithm_t alg = PSA_ALG_AEAD_WITH_TAG_LENGTH(PSA_ALG_CCM,
+ TRUNCATED_AUTH_TAG_LEN);
+
+ psa_aead_test(PSA_KEY_TYPE_AES, alg, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+
+static void tfm_crypto_test_5037(struct test_result_t *ret)
+{
+ psa_key_derivation_test(PSA_ALG_TLS12_PRF(PSA_ALG_SHA_256), ret);
+}
+
+static void tfm_crypto_test_5038(struct test_result_t *ret)
+{
+ psa_key_derivation_test(PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256), ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_HKDF
+static void tfm_crypto_test_5039(struct test_result_t *ret)
+{
+ psa_key_derivation_test(PSA_ALG_HKDF(PSA_ALG_SHA_256), ret);
+}
+#endif /* TFM_CRYPTO_TEST_HKDF */
diff --git a/test/suites/ipc/CMakeLists.inc b/test/suites/ipc/CMakeLists.inc
new file mode 100644
index 0000000..c7a48be
--- /dev/null
+++ b/test/suites/ipc/CMakeLists.inc
@@ -0,0 +1,36 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "ipc test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(IPC_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+ message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED IPC_TEST)
+ message(FATAL_ERROR "Incomplete build configuration: IPC_TEST is undefined. ")
+elseif(IPC_TEST)
+ list(APPEND ALL_SRC_C_S "${IPC_TEST_DIR}/secure/ipc_s_interface_testsuite.c")
+ list(APPEND ALL_SRC_C_NS "${IPC_TEST_DIR}/non_secure/ipc_ns_interface_testsuite.c")
+
+ #Setting include directories
+ embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+ embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+endif()
diff --git a/test/suites/ipc/non_secure/ipc_ns_interface_testsuite.c b/test/suites/ipc/non_secure/ipc_ns_interface_testsuite.c
new file mode 100644
index 0000000..d051b78
--- /dev/null
+++ b/test/suites/ipc/non_secure/ipc_ns_interface_testsuite.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdio.h>
+#include "ipc_ns_tests.h"
+#include "psa/client.h"
+#include "test/framework/test_framework_helpers.h"
+#ifdef TFM_PSA_API
+#include "psa_manifest/sid.h"
+#endif
+
+/* List of tests */
+static void tfm_ipc_test_1001(struct test_result_t *ret);
+static void tfm_ipc_test_1002(struct test_result_t *ret);
+static void tfm_ipc_test_1003(struct test_result_t *ret);
+static void tfm_ipc_test_1004(struct test_result_t *ret);
+static void tfm_ipc_test_1005(struct test_result_t *ret);
+static void tfm_ipc_test_1006(struct test_result_t *ret);
+
+#ifdef TFM_IPC_ISOLATION_2_TEST_READ_ONLY_MEM
+static void tfm_ipc_test_1007(struct test_result_t *ret);
+#endif
+
+#ifdef TFM_IPC_ISOLATION_2_APP_ACCESS_PSA_MEM
+static void tfm_ipc_test_1008(struct test_result_t *ret);
+#endif
+
+#ifdef TFM_IPC_ISOLATION_2_MEM_CHECK
+static void tfm_ipc_test_1009(struct test_result_t *ret);
+#endif
+
+static void tfm_ipc_test_1010(struct test_result_t *ret);
+
+static struct test_t ipc_veneers_tests[] = {
+ {&tfm_ipc_test_1001, "TFM_IPC_TEST_1001",
+ "Get PSA framework version", {TEST_PASSED}},
+ {&tfm_ipc_test_1002, "TFM_IPC_TEST_1002",
+ "Get version of an RoT Service", {TEST_PASSED}},
+ {&tfm_ipc_test_1003, "TFM_IPC_TEST_1003",
+ "Connect to an RoT Service", {TEST_PASSED}},
+ {&tfm_ipc_test_1004, "TFM_IPC_TEST_1004",
+ "Call an RoT Service", {TEST_PASSED}},
+ {&tfm_ipc_test_1005, "TFM_IPC_TEST_1005",
+ "Call IPC_INIT_BASIC_TEST service", {TEST_PASSED}},
+ {&tfm_ipc_test_1006, "TFM_IPC_TEST_1006",
+ "Call PSA RoT access APP RoT memory test service", {TEST_PASSED}},
+#ifdef TFM_IPC_ISOLATION_2_TEST_READ_ONLY_MEM
+ {&tfm_ipc_test_1007, "TFM_IPC_TEST_1007",
+ "Call PSA RoT access APP RoT readonly memory test service", {TEST_PASSED}},
+#endif
+#ifdef TFM_IPC_ISOLATION_2_APP_ACCESS_PSA_MEM
+ {&tfm_ipc_test_1008, "TFM_IPC_TEST_1008",
+ "Call APP RoT access PSA RoT memory test service", {TEST_PASSED}},
+#endif
+#ifdef TFM_IPC_ISOLATION_2_MEM_CHECK
+ {&tfm_ipc_test_1009, "TFM_IPC_TEST_1009",
+ "Call APP RoT memory check test service", {TEST_PASSED}},
+#endif
+ {&tfm_ipc_test_1010, "TFM_IPC_TEST_1010",
+ "Test psa_call with the status of PSA_ERROR_PROGRAMMER_ERROR", {TEST_PASSED}},
+};
+
+void register_testsuite_ns_ipc_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(ipc_veneers_tests) / sizeof(ipc_veneers_tests[0]));
+
+ set_testsuite("IPC non-secure interface test (TFM_IPC_TEST_1XXX)",
+ ipc_veneers_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Retrieve the version of the PSA Framework API.
+ *
+ * \note This is a functional test only and doesn't
+ * mean to test all possible combinations of
+ * input parameters and return values.
+ */
+static void tfm_ipc_test_1001(struct test_result_t *ret)
+{
+ uint32_t version;
+
+ version = psa_framework_version();
+ if (version == PSA_FRAMEWORK_VERSION) {
+ TEST_LOG("The version of the PSA Framework API is %d.\r\n", version);
+ } else {
+ TEST_FAIL("The version of the PSA Framework API is not valid!\r\n");
+ return;
+ }
+}
+
+/**
+ * \brief Retrieve the version of an RoT Service.
+ */
+static void tfm_ipc_test_1002(struct test_result_t *ret)
+{
+ uint32_t version;
+
+ version = psa_version(IPC_SERVICE_TEST_BASIC_SID);
+ if (version == PSA_VERSION_NONE) {
+ TEST_FAIL("RoT Service is not implemented or caller is not authorized" \
+ "to access it!\r\n");
+ return;
+ } else {
+ /* Valid version number */
+ TEST_LOG("The service version is %d.\r\n", version);
+ }
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Connect to an RoT Service by its SID.
+ */
+static void tfm_ipc_test_1003(struct test_result_t *ret)
+{
+ psa_handle_t handle;
+
+ handle = psa_connect(IPC_SERVICE_TEST_BASIC_SID,
+ IPC_SERVICE_TEST_BASIC_VERSION);
+ if (handle > 0) {
+ TEST_LOG("Connect success!\r\n");
+ } else {
+ TEST_FAIL("The RoT Service has refused the connection!\r\n");
+ return;
+ }
+ psa_close(handle);
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Call an RoT Service.
+ */
+static void tfm_ipc_test_1004(struct test_result_t *ret)
+{
+ char str1[] = "str1";
+ char str2[] = "str2";
+ char str3[128], str4[128];
+ struct psa_invec invecs[2] = {{str1, sizeof(str1)/sizeof(char)},
+ {str2, sizeof(str2)/sizeof(char)}};
+ struct psa_outvec outvecs[2] = {{str3, sizeof(str3)/sizeof(char)},
+ {str4, sizeof(str4)/sizeof(char)}};
+ psa_handle_t handle;
+ psa_status_t status;
+ uint32_t version;
+
+ version = psa_version(IPC_SERVICE_TEST_BASIC_SID);
+ TEST_LOG("TFM service support version is %d.\r\n", version);
+ handle = psa_connect(IPC_SERVICE_TEST_BASIC_SID,
+ IPC_SERVICE_TEST_BASIC_VERSION);
+ status = psa_call(handle, PSA_IPC_CALL, invecs, 2, outvecs, 2);
+ if (status >= 0) {
+ TEST_LOG("psa_call is successful!\r\n");
+ } else {
+ TEST_FAIL("psa_call is failed!\r\n");
+ return;
+ }
+
+ TEST_LOG("outvec1 is: %s\r\n", outvecs[0].base);
+ TEST_LOG("outvec2 is: %s\r\n", outvecs[1].base);
+ psa_close(handle);
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Call IPC_CLIENT_TEST_BASIC_SID RoT Service to run the IPC basic test.
+ */
+static void tfm_ipc_test_1005(struct test_result_t *ret)
+{
+ psa_handle_t handle;
+ psa_status_t status;
+ int test_result;
+ struct psa_outvec outvecs[1] = {{&test_result, sizeof(test_result)}};
+
+ handle = psa_connect(IPC_CLIENT_TEST_BASIC_SID,
+ IPC_CLIENT_TEST_BASIC_VERSION);
+ if (handle > 0) {
+ TEST_LOG("Connect success!\r\n");
+ } else {
+ TEST_LOG("The RoT Service has refused the connection!\r\n");
+ ret->val = TEST_FAILED;
+ return;
+ }
+
+ status = psa_call(handle, PSA_IPC_CALL, NULL, 0, outvecs, 1);
+ if (status >= 0) {
+ TEST_LOG("Call success!\r\n");
+ if (test_result > 0) {
+ ret->val = TEST_PASSED;
+ } else {
+ ret->val = TEST_FAILED;
+ }
+ } else {
+ TEST_LOG("Call failed!\r\n");
+ ret->val = TEST_FAILED;
+ }
+
+ psa_close(handle);
+}
+
+/**
+ * \brief Call IPC_CLIENT_TEST_PSA_ACCESS_APP_MEM_SID RoT Service
+ * to run the IPC PSA access APP mem test.
+ */
+static void tfm_ipc_test_1006(struct test_result_t *ret)
+{
+ psa_handle_t handle;
+ psa_status_t status;
+ int test_result;
+ struct psa_outvec outvecs[1] = {{&test_result, sizeof(test_result)}};
+
+ handle = psa_connect(IPC_CLIENT_TEST_PSA_ACCESS_APP_MEM_SID,
+ IPC_CLIENT_TEST_PSA_ACCESS_APP_MEM_VERSION);
+ if (handle > 0) {
+ TEST_LOG("Connect success!\r\n");
+ } else {
+ TEST_LOG("The RoT Service has refused the connection!\r\n");
+ ret->val = TEST_FAILED;
+ return;
+ }
+
+ status = psa_call(handle, PSA_IPC_CALL, NULL, 0, outvecs, 1);
+ if (status >= 0) {
+ TEST_LOG("Call success!\r\n");
+ if (test_result > 0) {
+ ret->val = TEST_PASSED;
+ } else {
+ ret->val = TEST_FAILED;
+ }
+ } else {
+ TEST_LOG("Call failed!\r\n");
+ ret->val = TEST_FAILED;
+ }
+
+ psa_close(handle);
+}
+
+#ifdef TFM_IPC_ISOLATION_2_TEST_READ_ONLY_MEM
+/**
+ * \brief Call IPC_CLIENT_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SID RoT Service
+ * to run the IPC PSA access APP readonly mem test.
+ */
+static void tfm_ipc_test_1007(struct test_result_t *ret)
+{
+ psa_handle_t handle;
+ int test_result;
+ struct psa_outvec outvecs[1] = {{&test_result, sizeof(test_result)}};
+
+ handle = psa_connect(IPC_CLIENT_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SID,
+ IPC_CLIENT_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_VERSION);
+ if (handle > 0) {
+ TEST_LOG("Connect success!\r\n");
+ } else {
+ TEST_LOG("The RoT Service has refused the connection!\r\n");
+ ret->val = TEST_FAILED;
+ return;
+ }
+
+ psa_call(handle, PSA_IPC_CALL, NULL, 0, outvecs, 1);
+
+ /* The system should panic in psa_call. If runs here, the test fails. */
+ ret->val = TEST_FAILED;
+ psa_close(handle);
+}
+#endif
+
+#ifdef TFM_IPC_ISOLATION_2_APP_ACCESS_PSA_MEM
+/**
+ * \brief Call IPC_CLIENT_TEST_APP_ACCESS_PSA_MEM_SID RoT Service
+ * to run the IPC APP access PSA mem test.
+ */
+static void tfm_ipc_test_1008(struct test_result_t *ret)
+{
+ psa_handle_t handle;
+ int test_result;
+ struct psa_outvec outvecs[1] = {{&test_result, sizeof(test_result)}};
+
+ handle = psa_connect(IPC_CLIENT_TEST_APP_ACCESS_PSA_MEM_SID,
+ IPC_CLIENT_TEST_APP_ACCESS_PSA_MEM_VERSION);
+ if (handle > 0) {
+ TEST_LOG("Connect success!\r\n");
+ } else {
+ TEST_LOG("The RoT Service has refused the connection!\r\n");
+ ret->val = TEST_FAILED;
+ return;
+ }
+
+ psa_call(handle, PSA_IPC_CALL, NULL, 0, outvecs, 1);
+
+ /* The system should panic in psa_call. If runs here, the test fails. */
+ ret->val = TEST_FAILED;
+ psa_close(handle);
+}
+#endif
+
+#ifdef TFM_IPC_ISOLATION_2_MEM_CHECK
+/**
+ * \brief Call IPC_CLIENT_TEST_MEM_CHECK_SID RoT Service
+ * to run the IPC mem check test.
+ */
+static void tfm_ipc_test_1009(struct test_result_t *ret)
+{
+ psa_handle_t handle;
+ int test_result;
+ struct psa_outvec outvecs[1] = {{&test_result, sizeof(test_result)}};
+
+ handle = psa_connect(IPC_CLIENT_TEST_MEM_CHECK_SID,
+ IPC_CLIENT_TEST_MEM_CHECK_VERSION);
+ if (handle > 0) {
+ TEST_LOG("Connect success!\r\n");
+ } else {
+ TEST_LOG("The RoT Service has refused the connection!\r\n");
+ ret->val = TEST_FAILED;
+ return;
+ }
+
+ psa_call(handle, PSA_IPC_CALL, NULL, 0, outvecs, 1);
+
+ /* The system should panic in psa_call. If runs here, the test fails. */
+ ret->val = TEST_FAILED;
+ psa_close(handle);
+}
+#endif
+
+/**
+ * \brief Call IPC_SERVICE_TEST_CLIENT_PREGRAMMER_ERROR RoT Service to
+ * test psa_call with the status of PSA_ERROR_PROGRAMMER_ERROR.
+ */
+static void tfm_ipc_test_1010(struct test_result_t *ret)
+{
+ psa_handle_t handle;
+ psa_status_t status;
+ handle = psa_connect(IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SID,
+ IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_VERSION);
+ if (handle > 0) {
+ TEST_LOG("Connect success!\r\n");
+ } else {
+ TEST_LOG("The RoT Service has refused the connection!\r\n");
+ ret->val = TEST_FAILED;
+ return;
+ }
+ status = psa_call(handle, PSA_IPC_CALL, NULL, 0, NULL, 0);
+ if (status == PSA_ERROR_PROGRAMMER_ERROR) {
+ TEST_LOG("The first time call success!\r\n");
+ } else {
+ TEST_LOG("The first time call failed!\r\n");
+ ret->val = TEST_FAILED;
+ }
+ status = psa_call(handle, PSA_IPC_CALL, NULL, 0, NULL, 0);
+ if (status == PSA_ERROR_PROGRAMMER_ERROR) {
+ TEST_LOG("The second time call success!\r\n");
+ } else {
+ TEST_LOG("The second time call failed!\r\n");
+ ret->val = TEST_FAILED;
+ }
+
+ psa_close(handle);
+}
diff --git a/test/suites/ipc/non_secure/ipc_ns_tests.h b/test/suites/ipc/non_secure/ipc_ns_tests.h
new file mode 100644
index 0000000..12f3bb6
--- /dev/null
+++ b/test/suites/ipc/non_secure/ipc_ns_tests.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __IPC_NS_TESTS_H__
+#define __IPC_NS_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for ipc non-secure interface.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_ns_ipc_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __IPC_NS_TESTS_H__ */
diff --git a/test/suites/ipc/secure/ipc_s_interface_testsuite.c b/test/suites/ipc/secure/ipc_s_interface_testsuite.c
new file mode 100644
index 0000000..213dfff
--- /dev/null
+++ b/test/suites/ipc/secure/ipc_s_interface_testsuite.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "ipc_s_tests.h"
+#include "psa/client.h"
+#include "test/framework/test_framework_helpers.h"
+
+/* List of tests */
+static void tfm_ipc_test_1001(struct test_result_t *ret);
+
+static struct test_t ipc_veneers_tests[] = {
+ {&tfm_ipc_test_1001, "TFM_IPC_TEST_1001", "Secure functional", {TEST_PASSED} },
+};
+
+void register_testsuite_s_ipc_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(ipc_veneers_tests) / sizeof(ipc_veneers_tests[0]));
+
+ set_testsuite("IPC secure interface test (TFM_IPC_TEST_1XXX)",
+ ipc_veneers_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Functional test of the Secure interface
+ *
+ * \note This is a functional test only and doesn't
+ * mean to test all possible combinations of
+ * input parameters and return values.
+ */
+static void tfm_ipc_test_1001(struct test_result_t *ret)
+{
+ ret->val = TEST_PASSED;
+}
diff --git a/test/suites/ipc/secure/ipc_s_tests.h b/test/suites/ipc/secure/ipc_s_tests.h
new file mode 100644
index 0000000..3b6a755
--- /dev/null
+++ b/test/suites/ipc/secure/ipc_s_tests.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __IPC_S_TESTS_H__
+#define __IPC_S_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for ipc secure interface.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_s_ipc_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __IPC_S_TESTS_H__ */
diff --git a/test/suites/its/CMakeLists.inc b/test/suites/its/CMakeLists.inc
new file mode 100644
index 0000000..b79140c
--- /dev/null
+++ b/test/suites/its/CMakeLists.inc
@@ -0,0 +1,40 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "internal trusted storage test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(ITS_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+ message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS)
+ message(FATAL_ERROR "Incomplete build configuration: ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS is undefined. ")
+elseif (ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS)
+ list(APPEND ALL_SRC_C_NS "${ITS_TEST_DIR}/non_secure/psa_its_ns_interface_testsuite.c"
+ "${ITS_TEST_DIR}/its_tests_common.c")
+
+ list(APPEND ALL_SRC_C_S "${ITS_TEST_DIR}/secure/psa_its_s_interface_testsuite.c"
+ "${ITS_TEST_DIR}/secure/psa_its_s_reliability_testsuite.c"
+ "${ITS_TEST_DIR}/its_tests_common.c")
+
+ #Setting include directories
+ embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+ embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+endif()
diff --git a/test/suites/its/its_tests_common.c b/test/suites/its/its_tests_common.c
new file mode 100644
index 0000000..eb4e21c
--- /dev/null
+++ b/test/suites/its/its_tests_common.c
@@ -0,0 +1,1051 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "its_tests_common.h"
+#include "psa/internal_trusted_storage.h"
+#if DOMAIN_NS == 1
+#include <string.h>
+#else
+#include "tfm_memory_utils.h"
+#endif
+#include "flash_layout.h"
+
+#define TEST_019_CYCLES 3U
+
+static const uint8_t write_asset_data[ITS_MAX_ASSET_SIZE] = {0xBF};
+static uint8_t read_asset_data[ITS_MAX_ASSET_SIZE] = {0};
+
+void tfm_its_test_common_001(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t data_len = 0;
+ const uint8_t write_data[] = {0};
+
+ /* Set with no data and no flags and a valid UID */
+ status = psa_its_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Attempt to set a second time */
+ status = psa_its_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail the second time with valid UID");
+ return;
+ }
+
+ /* Set with an invalid UID */
+ status = psa_its_set(INVALID_UID, data_len, write_data, flags);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Set should not succeed with an invalid UID");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_002(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ /* Set with no flags */
+ status = psa_its_set(WRITE_ONCE_UID, data_len, write_data, flags);
+ if (status == PSA_SUCCESS) {
+ /* Set with valid flag: PSA_STORAGE_FLAG_WRITE_ONCE (with previously
+ * created UID)
+ * Note: Once created, WRITE_ONCE_UID cannot be deleted. It is reused
+ * across multiple tests.
+ */
+ status = psa_its_set(WRITE_ONCE_UID, WRITE_ONCE_DATA_SIZE,
+ WRITE_ONCE_DATA, PSA_STORAGE_FLAG_WRITE_ONCE);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid flags (existing UID)");
+ return;
+ }
+ } else if (status == PSA_ERROR_NOT_PERMITTED) {
+ /* The UID has already been created with the PSA_STORAGE_FLAG_WRITE_ONCE
+ * flag in a previous test run, so skip creating it again and emit a
+ * warning.
+ */
+ TEST_LOG("Note: The UID in this test has already been created with\r\n"
+ "the PSA_STORAGE_FLAG_WRITE_ONCE flag in a previous test\r\n"
+ "run. Wipe the storage area to run the full test.\r\n");
+ } else {
+ TEST_FAIL("Set should not fail with no flags");
+ return;
+ }
+
+ /* Set with invalid flags */
+ status = psa_its_set(uid, data_len, write_data, INVALID_FLAG);
+ if (status != PSA_ERROR_NOT_SUPPORTED) {
+ TEST_FAIL("Set should not succeed with invalid flags");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_003(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t data_len = 0;
+
+ /* Set with NULL data pointer */
+ status = psa_its_set(uid, data_len, NULL, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should succeed with NULL data pointer and zero length");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_004(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = WRITE_ONCE_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t write_len = WRITE_DATA_SIZE;
+ const size_t read_len = WRITE_ONCE_DATA_SIZE;
+ const size_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = WRITE_ONCE_READ_DATA;
+ size_t read_data_length = 0;
+ int comp_result;
+
+ /* Set a write once UID a second time */
+ status = psa_its_set(uid, write_len, write_data, flags);
+ if (status != PSA_ERROR_NOT_PERMITTED) {
+ TEST_FAIL("Set should not rewrite a write once UID");
+ return;
+ }
+
+ /* Get write once data */
+ status = psa_its_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Check that write once data has not changed */
+ comp_result = memcmp(read_data, WRITE_ONCE_RESULT_DATA, sizeof(read_data));
+#else
+ comp_result = tfm_memcmp(read_data, WRITE_ONCE_RESULT_DATA,
+ sizeof(read_data));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Write once data should not have changed");
+ return;
+ }
+
+ /* Check that write once data length has not changed */
+ if (read_data_length != read_len) {
+ TEST_FAIL("Write once data length should not have changed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_005(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ size_t data_len = WRITE_DATA_SIZE;
+ size_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ const uint8_t *p_read_data = read_data;
+ size_t read_data_length = 0;
+ int comp_result;
+
+ status = psa_its_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get the entire data */
+ status = psa_its_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+ &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Check that the data is correct, including no illegal pre- or post-data */
+ comp_result = memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#else
+ comp_result = tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read data should be equal to result data");
+ return;
+ }
+
+ /* Check that the length of data is correct */
+ if (read_data_length != data_len) {
+ TEST_FAIL("Read data length should be equal to requested data length");
+ return;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Reset read data */
+ memcpy(read_data, READ_DATA, sizeof(read_data));
+#else
+ tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+#endif
+
+ /* Read from offset 2 to 2 bytes before end of the data */
+ offset = 2;
+ data_len -= offset + 2;
+
+ status = psa_its_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+ &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Check that the correct data was read */
+ comp_result = memcmp(p_read_data, "____", HALF_PADDING_SIZE);
+#else
+ comp_result = tfm_memcmp(p_read_data, "____", HALF_PADDING_SIZE);
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read data contains illegal pre-data");
+ return;
+ }
+
+ p_read_data += HALF_PADDING_SIZE;
+
+#if DOMAIN_NS == 1U
+ comp_result = memcmp(p_read_data, write_data + offset, data_len);
+#else
+ comp_result = tfm_memcmp(p_read_data, write_data + offset, data_len);
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read data incorrect");
+ return;
+ }
+
+ p_read_data += data_len;
+
+#if DOMAIN_NS == 1U
+ comp_result = memcmp(p_read_data, "____", HALF_PADDING_SIZE);
+#else
+ comp_result = tfm_memcmp(p_read_data, "____", HALF_PADDING_SIZE);
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read data contains illegal post-data");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_006(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t write_len = WRITE_DATA_SIZE;
+ const size_t read_len = 0;
+ size_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_length = 1;
+ int comp_result;
+
+ status = psa_its_set(uid, write_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get zero data from zero offset */
+ status = psa_its_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail with zero data len");
+ return;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Check that the read data is unchanged */
+ comp_result = memcmp(read_data, READ_DATA, sizeof(read_data));
+#else
+ comp_result = tfm_memcmp(read_data, READ_DATA, sizeof(read_data));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+ /* Check that the read data length is zero */
+ if (read_data_length != 0) {
+ TEST_FAIL("Read data length should be equal to zero");
+ return;
+ }
+
+ offset = 5;
+ read_data_length = 1;
+
+ /* Get zero data from non-zero offset */
+ status = psa_its_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Check that the read data is unchanged */
+ comp_result = memcmp(read_data, READ_DATA, sizeof(read_data));
+#else
+ comp_result = tfm_memcmp(read_data, READ_DATA, sizeof(read_data));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+ /* Check that the read data length is zero */
+ if (read_data_length != 0) {
+ TEST_FAIL("Read data length should be equal to zero");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_007(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const size_t data_len = 1;
+ const size_t offset = 0;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_length = 0;
+ int comp_result;
+
+ /* Get with UID that has not yet been set */
+ status = psa_its_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+ &read_data_length);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get succeeded with non-existant UID");
+ return;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Check that the read data is unchanged */
+ comp_result = memcmp(read_data, READ_DATA, sizeof(read_data));
+#else
+ comp_result = tfm_memcmp(read_data, READ_DATA, sizeof(read_data));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read data not equal to original read data");
+ return;
+ }
+
+ /* Get with invalid UID */
+ status = psa_its_get(INVALID_UID, offset, data_len,
+ read_data + HALF_PADDING_SIZE, &read_data_length);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Get succeeded with invalid UID");
+ return;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Check that the read data is unchanged */
+ comp_result = memcmp(read_data, READ_DATA, sizeof(read_data));
+#else
+ comp_result = tfm_memcmp(read_data, READ_DATA, sizeof(read_data));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read data not equal to original read data");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_008(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t write_len = WRITE_DATA_SIZE;
+ size_t read_len;
+ size_t offset;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_length = 0;
+ int comp_result;
+
+ status = psa_its_set(uid, write_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get with offset greater than UID's length */
+ read_len = 1;
+ offset = write_len + 1;
+
+ status = psa_its_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_length);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Get should not succeed with offset too large");
+ return;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Check that the read data is unchanged */
+ comp_result = memcmp(read_data, READ_DATA, sizeof(read_data));
+#else
+ comp_result = tfm_memcmp(read_data, READ_DATA, sizeof(read_data));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+ /* Get with data length greater than UID's length */
+ read_len = write_len + 1;
+ offset = 0;
+
+ status = psa_its_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should succeed with data length larger than UID's "
+ "length");
+ return;
+ }
+
+ if (read_data_length != write_len) {
+ TEST_FAIL("Read data length should be equal to UID's length");
+ return;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Check that the read data is changed */
+ comp_result = memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#else
+ comp_result = tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read data should be equal to newly read data");
+ return;
+ }
+
+ /* Get with offset + data length greater than UID's length, but individually
+ * valid
+ */
+#if DOMAIN_NS == 1U
+ /* Reset read_data to original READ_DATA */
+ memcpy(read_data, READ_DATA, sizeof(read_data));
+#else
+ tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+#endif
+ read_len = write_len;
+ offset = 1;
+
+ status = psa_its_get(uid, offset, read_len,
+ read_data + HALF_PADDING_SIZE,
+ &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should succeed with offset + data length too large, "
+ "but individually valid");
+ return;
+ }
+
+ if (read_data_length != write_len - offset) {
+ TEST_FAIL("Read data length should be equal to the UID's remaining "
+ "size starting from offset");
+ return;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Check that the read data is changed */
+ comp_result = memcmp(read_data, OFFSET_RESULT_DATA, sizeof(read_data));
+#else
+ comp_result = tfm_memcmp(read_data, OFFSET_RESULT_DATA, sizeof(read_data));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read data should be equal to newly read data starting at "
+ "offset");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_009(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t data_len = WRITE_DATA_SIZE;
+ const size_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ size_t read_data_length = 0;
+
+ status = psa_its_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get with NULL data pointer */
+ status = psa_its_get(uid, offset, 0, NULL, &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should succeed with NULL data pointer and zero length");
+ return;
+ }
+
+ /* Check that the read data length is unchanged */
+ if (read_data_length != 0) {
+ TEST_FAIL("Read data length should be 0 with NULL data pointer and "
+ "zero length");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_010(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = WRITE_ONCE_UID;
+ struct psa_storage_info_t info = {0};
+
+ /* Get info for write once UID */
+ status = psa_its_get_info(uid, &info);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get info should not fail for write once UID");
+ return;
+ }
+
+ /* Check that the info struct contains the correct values */
+ if (info.size != WRITE_ONCE_DATA_SIZE) {
+ TEST_FAIL("Size incorrect for write once UID");
+ return;
+ }
+
+ if (info.capacity != WRITE_ONCE_DATA_SIZE) {
+ TEST_FAIL("Capacity incorrect for write once UID");
+ return;
+ }
+
+ if (info.flags != PSA_STORAGE_FLAG_WRITE_ONCE) {
+ TEST_FAIL("Flags incorrect for write once UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_011(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ struct psa_storage_info_t info = {0};
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ status = psa_its_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get info for valid UID */
+ status = psa_its_get_info(uid, &info);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get info should not fail with valid UID");
+ return;
+ }
+
+ /* Check that the info struct contains the correct values */
+ if (info.size != data_len) {
+ TEST_FAIL("Size incorrect for valid UID");
+ return;
+ }
+
+ if (info.capacity != data_len) {
+ TEST_FAIL("Capacity incorrect for valid UID");
+ return;
+ }
+
+ if (info.flags != flags) {
+ TEST_FAIL("Flags incorrect for valid UID");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_012(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ struct psa_storage_info_t info = {0};
+
+ /* Get info with UID that has not yet been set */
+ status = psa_its_get_info(uid, &info);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get info should not succeed with unset UID");
+ return;
+ }
+
+ /* Check that the info struct has not been modified */
+ if (info.size != 0) {
+ TEST_FAIL("Size should not have changed");
+ return;
+ }
+
+ /* Get info with invalid UID */
+ status = psa_its_get_info(INVALID_UID, &info);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Get info should not succeed with invalid UID");
+ return;
+ }
+
+ /* Check that the info struct has not been modified */
+ if (info.size != 0) {
+ TEST_FAIL("Size should not have changed");
+ return;
+ }
+
+ if (info.capacity != 0) {
+ TEST_FAIL("Capacity should not have changed");
+ return;
+ }
+
+ if (info.flags != PSA_STORAGE_FLAG_NONE) {
+ TEST_FAIL("Flags should not have changed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_013(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ struct psa_storage_info_t info = {0};
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t data_len = WRITE_DATA_SIZE;
+ const size_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_length = 0;
+
+ status = psa_its_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Call remove with valid ID */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ /* Check that get info fails for removed UID */
+ status = psa_its_get_info(uid, &info);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get info should not succeed with removed UID");
+ return;
+ }
+
+ /* Check that get fails for removed UID */
+ status = psa_its_get(uid, offset, data_len, read_data, &read_data_length);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get should not succeed with removed UID");
+ return;
+ }
+
+ /* Check that remove fails for removed UID */
+ status = psa_its_remove(uid);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Remove should not succeed with removed UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_014(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = WRITE_ONCE_UID;
+
+ /* Call remove with write once UID */
+ status = psa_its_remove(uid);
+ if (status != PSA_ERROR_NOT_PERMITTED) {
+ TEST_FAIL("Remove should not succeed with write once UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_015(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = INVALID_UID;
+
+ /* Call remove with an invalid UID */
+ status = psa_its_remove(uid);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Remove should not succeed with invalid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_016(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid_1 = TEST_UID_2;
+ const psa_storage_uid_t uid_2 = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t data_len_2 = WRITE_DATA_SIZE;
+ const size_t offset = 0;
+ const uint8_t write_data_1[] = "UID 1 DATA";
+ const uint8_t write_data_2[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_length = 0;
+ int comp_result;
+
+ /* Set UID 1 */
+ status = psa_its_set(uid_1, sizeof(write_data_1), write_data_1, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail for UID 1");
+ return;
+ }
+
+ /* Set UID 2 */
+ status = psa_its_set(uid_2, data_len_2, write_data_2, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail for UID 2");
+ return;
+ }
+
+ /* Remove UID 1. This should cause UID 2 to be compacted to the beginning of
+ * the block.
+ */
+ status = psa_its_remove(uid_1);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail for UID 1");
+ return;
+ }
+
+ /* If the compact worked as expected, the test should be able to read back
+ * the data from UID 2 correctly.
+ */
+ status = psa_its_get(uid_2, offset, data_len_2,
+ read_data + HALF_PADDING_SIZE, &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail for UID 2");
+ return;
+ }
+
+#if DOMAIN_NS == 1U
+ comp_result = memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#else
+ comp_result = tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read buffer has incorrect data");
+ return;
+ }
+
+ if (read_data_length != WRITE_DATA_SIZE) {
+ TEST_FAIL("Read data length should be equal to result data length");
+ return;
+ }
+
+ /* Remove UID 2 to clean up storage for the next test */
+ status = psa_its_remove(uid_2);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail for UID 2");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_017(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t data_len = WRITE_DATA_SIZE;
+ size_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_length = 0;
+ int comp_result;
+
+ /* Set the entire data into UID */
+ status = psa_its_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get the data from UID one byte at a time */
+ for (offset = 0; offset < data_len; ++offset) {
+ status = psa_its_get(uid, offset, 1,
+ (read_data + HALF_PADDING_SIZE + offset),
+ &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail for partial read");
+ return;
+ }
+ }
+
+#if DOMAIN_NS == 1U
+ comp_result = memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#else
+ comp_result = tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read buffer has incorrect data");
+ return;
+ }
+
+ /* Remove UID to clean up storage for the next test */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_018(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t offset = 0;
+ const uint8_t write_data_1[] = "ONE";
+ const uint8_t write_data_2[] = "TWO";
+ const uint8_t write_data_3[] = "THREE";
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_length = 0;
+ int comp_result;
+
+ /* Set write data 1 into UID */
+ status = psa_its_set(uid, sizeof(write_data_1), write_data_1, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("First set should not fail");
+ return;
+ }
+
+ /* Set write data 2 into UID */
+ status = psa_its_set(uid, sizeof(write_data_2), write_data_2, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Second set should not fail");
+ return;
+ }
+
+ /* Set write data 3 into UID */
+ status = psa_its_set(uid, sizeof(write_data_3), write_data_3, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Third set should not fail");
+ return;
+ }
+
+ status = psa_its_get(uid, offset, sizeof(write_data_3), read_data,
+ &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Check that get returns the last data to be set */
+ comp_result = memcmp(read_data, write_data_3, sizeof(write_data_3));
+#else
+ comp_result = tfm_memcmp(read_data, write_data_3, sizeof(write_data_3));
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read buffer has incorrect data");
+ return;
+ }
+
+ if (read_data_length != sizeof(write_data_3)) {
+ TEST_FAIL("Read data length should be equal to result data length");
+ return;
+ }
+
+ /* Remove UID to clean up storage for the next test */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_019(struct test_result_t *ret)
+{
+ uint8_t cycle;
+ psa_status_t status;
+ int comp_result;
+ size_t read_data_length = 0;
+ const psa_storage_uid_t test_uid[TEST_019_CYCLES] = {
+ TEST_UID_1,
+ TEST_UID_2,
+ TEST_UID_3};
+ const size_t test_asset_sizes[TEST_019_CYCLES] = {
+ ITS_MAX_ASSET_SIZE >> 2,
+ ITS_MAX_ASSET_SIZE >> 1,
+ ITS_MAX_ASSET_SIZE};
+
+ /* Loop to test different asset sizes and UID's*/
+ for (cycle = 0; cycle < TEST_019_CYCLES; cycle++) {
+ size_t data_size = test_asset_sizes[cycle];
+ psa_storage_uid_t uid = test_uid[cycle];
+ struct psa_storage_info_t info = {0};
+
+#if DOMAIN_NS == 1U
+ memset(read_asset_data, 0x00, sizeof(read_asset_data));
+#else
+ tfm_memset(read_asset_data, 0x00, sizeof(read_asset_data));
+#endif
+
+ /* Set with data and no flags and a valid UID */
+ status = psa_its_set(uid,
+ data_size,
+ write_asset_data,
+ PSA_STORAGE_FLAG_NONE);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Get info for valid UID */
+ status = psa_its_get_info(uid, &info);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get info should not fail with valid UID");
+ return;
+ }
+
+ /* Check that the info struct contains the correct values */
+ if (info.size != data_size) {
+ TEST_FAIL("Size incorrect for valid UID");
+ return;
+ }
+
+ if (info.flags != PSA_STORAGE_FLAG_NONE) {
+ TEST_FAIL("Flags incorrect for valid UID");
+ return;
+ }
+
+ /* Check that thread can still get UID */
+ status = psa_its_get(uid, 0, data_size, read_asset_data,
+ &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail with valid UID");
+ return;
+ }
+
+#if DOMAIN_NS == 1U
+ /* Check that get returns the last data which was set */
+ comp_result = memcmp(read_asset_data, write_asset_data, data_size);
+#else
+ comp_result = tfm_memcmp(read_asset_data, write_asset_data, data_size);
+#endif
+ if (comp_result != 0) {
+ TEST_FAIL("Read data should be equal to original write data");
+ return;
+ }
+
+ if (read_data_length != data_size) {
+ TEST_FAIL("Read data length should be equal to result data length");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+ }
+
+ ret->val = TEST_PASSED;
+}
diff --git a/test/suites/its/its_tests_common.h b/test/suites/its/its_tests_common.h
new file mode 100644
index 0000000..f1b0845
--- /dev/null
+++ b/test/suites/its/its_tests_common.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __ITS_TESTS_COMMON_H__
+#define __ITS_TESTS_COMMON_H__
+
+#include "test/framework/test_framework_helpers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Test UIDs */
+#define WRITE_ONCE_UID 1U /* Cannot be modified or deleted once created */
+#define TEST_UID_1 2U
+#define TEST_UID_2 3U
+#define TEST_UID_3 4U
+
+/* Invalid values */
+#define INVALID_UID 0U
+#define INVALID_DATA_LEN UINT32_MAX
+#define INVALID_OFFSET UINT32_MAX
+#define INVALID_FLAG (1U << 31)
+
+/* Write once data */
+#define WRITE_ONCE_DATA "THE_FIVE_BOXING_WIZARDS_JUMP_QUICKLY"
+#define WRITE_ONCE_DATA_SIZE (sizeof(WRITE_ONCE_DATA) - 1)
+#define WRITE_ONCE_READ_DATA "############################################"
+#define WRITE_ONCE_RESULT_DATA ("####" WRITE_ONCE_DATA "####")
+
+#define WRITE_DATA "THEQUICKBROWNFOXJUMPSOVERALAZYDOG"
+#define WRITE_DATA_SIZE (sizeof(WRITE_DATA) - 1)
+#define READ_DATA "_________________________________________"
+#define RESULT_DATA ("____" WRITE_DATA "____")
+#define OFFSET_READ_DATA "HEQUICKBROWNFOXJUMPSOVERALAZYDOG"
+#define OFFSET_RESULT_DATA ("____" OFFSET_READ_DATA "_____")
+
+/**
+ * Several tests use a buffer to read back data from an asset. This buffer is
+ * larger than the size of the asset data by PADDING_SIZE bytes. This allows
+ * us to ensure that only the expected data is read back and that it is read
+ * back correctly.
+ *
+ * For example if the buffer and asset are as follows:
+ * Buffer - "XXXXXXXXXXXX", Asset data - "AAAA"
+ *
+ * Then a correct and successful read would give this result: "XXXXAAAAXXXX"
+ * (Assuming a PADDING_SIZE of 8)
+ */
+#define BUFFER_SIZE 24
+#define PADDING_SIZE 8
+#define HALF_PADDING_SIZE 4
+#define BUFFER_PLUS_PADDING_SIZE (BUFFER_SIZE + PADDING_SIZE)
+#define BUFFER_PLUS_HALF_PADDING_SIZE (BUFFER_SIZE + HALF_PADDING_SIZE)
+
+/**
+ * \brief Tests set function with:
+ * - Valid UID, no data, no flags
+ * - Invalid UID, no data, no flags
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_001(struct test_result_t *ret);
+
+/**
+ * \brief Tests set function with:
+ * - Zero create flags
+ * - Valid create flags (with previously created UID)
+ * - Invalid create flags
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_002(struct test_result_t *ret);
+
+/**
+ * \brief Tests set function with:
+ * - NULL data pointer and zero data length
+ *
+ * \param[out] ret Test result
+ *
+ * \note A request with a null data pointer and data length not equal to zero is
+ * treated as a secure violation. TF-M framework will reject such requests
+ * and not return to the caller so this case is not tested here.
+ *
+ */
+void tfm_its_test_common_003(struct test_result_t *ret);
+
+/**
+ * \brief Tests set function with:
+ * - Write once UID that has already been created
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_004(struct test_result_t *ret);
+
+/**
+ * \brief Tests get function with:
+ * - Valid data, zero offset
+ * - Valid data, non-zero offset
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_005(struct test_result_t *ret);
+
+/**
+ * \brief Tests get function with:
+ * - Zero data length, zero offset
+ * - Zero data length, non-zero offset
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_006(struct test_result_t *ret);
+
+/**
+ * \brief Tests get function with:
+ * - Unset UID
+ * - Invalid UID
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_007(struct test_result_t *ret);
+
+/**
+ * \brief Tests get function with:
+ * - Offset greater than UID length
+ * - Data length greater than UID length
+ * - Data length + offset greater than UID length
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_008(struct test_result_t *ret);
+
+/**
+ * \brief Tests get function with:
+ * - NULL data pointer and zero data length
+ *
+ * \param[out] ret Test result
+ *
+ * \note A request with a null data pointer and data length not equal to zero is
+ * treated as a secure violation. TF-M framework will reject such requests
+ * and not return to the caller so this case is not tested here.
+ *
+ */
+void tfm_its_test_common_009(struct test_result_t *ret);
+
+/**
+ * \brief Tests get info function with:
+ * - Write once UID
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_010(struct test_result_t *ret);
+
+/**
+ * \brief Tests get info function with:
+ * - Valid UID
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_011(struct test_result_t *ret);
+
+/**
+ * \brief Tests get info function with:
+ * - Unset UID
+ * - Invalid UID
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_012(struct test_result_t *ret);
+
+/**
+ * \brief Tests remove function with:
+ * - Valid UID
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_013(struct test_result_t *ret);
+
+/**
+ * \brief Tests remove function with:
+ * - Write once UID
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_014(struct test_result_t *ret);
+
+/**
+ * \brief Tests remove function with:
+ * - Invalid UID
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_015(struct test_result_t *ret);
+
+/**
+ * \brief Tests data block compact feature.
+ * Set UID 1 to locate it at the beginning of the block. Then set UID 2
+ * to be located after UID 1 and remove UID 1. UID 2 will be compacted to
+ * the beginning of the block. This test verifies that the compaction
+ * works correctly by reading back UID 2.
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_016(struct test_result_t *ret);
+
+/**
+ * \brief Tests set and multiple partial gets.
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_017(struct test_result_t *ret);
+
+/**
+ * \brief Tests multiple sets to the same UID.
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_018(struct test_result_t *ret);
+
+/**
+ * \brief Tests set, get_info, get and remove function with:
+ * - Valid UID's, No Flags
+ * - Data length of different asset sizes
+ *
+ * \param[out] ret Test result
+ */
+void tfm_its_test_common_019(struct test_result_t *ret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ITS_TESTS_COMMON_H__ */
diff --git a/test/suites/its/non_secure/its_ns_tests.h b/test/suites/its/non_secure/its_ns_tests.h
new file mode 100644
index 0000000..534dd17
--- /dev/null
+++ b/test/suites/its/non_secure/its_ns_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __ITS_NS_TESTS_H__
+#define __ITS_NS_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the PSA internal trusted storage NS interface
+ * tests.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_ns_psa_its_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ITS_NS_TESTS_H__ */
diff --git a/test/suites/its/non_secure/psa_its_ns_interface_testsuite.c b/test/suites/its/non_secure/psa_its_ns_interface_testsuite.c
new file mode 100644
index 0000000..3891600
--- /dev/null
+++ b/test/suites/its/non_secure/psa_its_ns_interface_testsuite.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "its_ns_tests.h"
+#include "test/framework/test_framework_helpers.h"
+#include "../its_tests_common.h"
+
+static struct test_t psa_its_ns_tests[] = {
+ {&tfm_its_test_common_001, "TFM_ITS_TEST_1001",
+ "Set interface"},
+ {&tfm_its_test_common_002, "TFM_ITS_TEST_1002",
+ "Set interface with create flags"},
+ {&tfm_its_test_common_003, "TFM_ITS_TEST_1003",
+ "Set interface with NULL data pointer"},
+ {&tfm_its_test_common_004, "TFM_ITS_TEST_1004",
+ "Set interface with write once UID"},
+ {&tfm_its_test_common_005, "TFM_ITS_TEST_1005",
+ "Get interface with valid data"},
+ {&tfm_its_test_common_006, "TFM_ITS_TEST_1006",
+ "Get interface with zero data length"},
+ {&tfm_its_test_common_007, "TFM_ITS_TEST_1007",
+ "Get interface with invalid UIDs"},
+ {&tfm_its_test_common_008, "TFM_ITS_TEST_1008",
+ "Get interface with invalid data lengths and offsets"},
+ {&tfm_its_test_common_009, "TFM_ITS_TEST_1009",
+ "Get interface with NULL data pointer"},
+ {&tfm_its_test_common_010, "TFM_ITS_TEST_1010",
+ "Get info interface with write once UID"},
+ {&tfm_its_test_common_011, "TFM_ITS_TEST_1011",
+ "Get info interface with valid UID"},
+ {&tfm_its_test_common_012, "TFM_ITS_TEST_1012",
+ "Get info interface with invalid UIDs"},
+ {&tfm_its_test_common_013, "TFM_ITS_TEST_1013",
+ "Remove interface with valid UID"},
+ {&tfm_its_test_common_014, "TFM_ITS_TEST_1014",
+ "Remove interface with write once UID"},
+ {&tfm_its_test_common_015, "TFM_ITS_TEST_1015",
+ "Remove interface with invalid UID"},
+ {&tfm_its_test_common_016, "TFM_ITS_TEST_1016",
+ "Block compaction after remove"},
+ {&tfm_its_test_common_017, "TFM_ITS_TEST_1017",
+ "Multiple partial gets"},
+ {&tfm_its_test_common_018, "TFM_ITS_TEST_1018",
+ "Multiple sets to same UID from same thread"},
+ {&tfm_its_test_common_019, "TFM_ITS_TEST_1019",
+ "Set, get and remove interface with different asset sizes"},
+};
+
+void register_testsuite_ns_psa_its_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(psa_its_ns_tests) / sizeof(psa_its_ns_tests[0]));
+
+ set_testsuite("PSA internal trusted storage NS interface tests "
+ "(TFM_ITS_TEST_1XXX)",
+ psa_its_ns_tests, list_size, p_test_suite);
+}
diff --git a/test/suites/its/secure/its_s_tests.h b/test/suites/its/secure/its_s_tests.h
new file mode 100644
index 0000000..42d739f
--- /dev/null
+++ b/test/suites/its/secure/its_s_tests.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __ITS_S_TESTS_H__
+#define __ITS_S_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the PSA internal trusted storage S interface
+ * tests.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_s_psa_its_interface(struct test_suite_t *p_test_suite);
+
+/**
+ * \brief Register testsuite for the ITS reliability tests.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_s_psa_its_reliability(struct test_suite_t
+ *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ITS_S_TESTS_H__ */
diff --git a/test/suites/its/secure/psa_its_s_interface_testsuite.c b/test/suites/its/secure/psa_its_s_interface_testsuite.c
new file mode 100644
index 0000000..187ee48
--- /dev/null
+++ b/test/suites/its/secure/psa_its_s_interface_testsuite.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "its_s_tests.h"
+#include "psa/internal_trusted_storage.h"
+#include "test/framework/test_framework_helpers.h"
+#include "test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.h"
+#include "../its_tests_common.h"
+#include "tfm_memory_utils.h"
+
+/* UID to test partition access control */
+#define TEST_UID_ACCESS_CONTROL 42U
+
+/* List of tests */
+static void tfm_its_test_2020(struct test_result_t *ret);
+static void tfm_its_test_2021(struct test_result_t *ret);
+static void tfm_its_test_2022(struct test_result_t *ret);
+static void tfm_its_test_2023(struct test_result_t *ret);
+
+static struct test_t psa_its_s_tests[] = {
+ {&tfm_its_test_common_001, "TFM_ITS_TEST_2001",
+ "Set interface"},
+ {&tfm_its_test_common_002, "TFM_ITS_TEST_2002",
+ "Set interface with create flags"},
+ {&tfm_its_test_common_003, "TFM_ITS_TEST_2003",
+ "Set interface with NULL data pointer"},
+ {&tfm_its_test_common_004, "TFM_ITS_TEST_2004",
+ "Set interface with write once UID"},
+ {&tfm_its_test_common_005, "TFM_ITS_TEST_2005",
+ "Get interface with valid data"},
+ {&tfm_its_test_common_006, "TFM_ITS_TEST_2006",
+ "Get interface with zero data length"},
+ {&tfm_its_test_common_007, "TFM_ITS_TEST_2007",
+ "Get interface with invalid UIDs"},
+ {&tfm_its_test_common_008, "TFM_ITS_TEST_2008",
+ "Get interface with data lengths and offsets greater than UID length"},
+ {&tfm_its_test_common_009, "TFM_ITS_TEST_2009",
+ "Get interface with NULL data pointer"},
+ {&tfm_its_test_common_010, "TFM_ITS_TEST_2010",
+ "Get info interface with write once UID"},
+ {&tfm_its_test_common_011, "TFM_ITS_TEST_2011",
+ "Get info interface with valid UID"},
+ {&tfm_its_test_common_012, "TFM_ITS_TEST_2012",
+ "Get info interface with invalid UIDs"},
+ {&tfm_its_test_common_013, "TFM_ITS_TEST_2013",
+ "Remove interface with valid UID"},
+ {&tfm_its_test_common_014, "TFM_ITS_TEST_2014",
+ "Remove interface with write once UID"},
+ {&tfm_its_test_common_015, "TFM_ITS_TEST_2015",
+ "Remove interface with invalid UID"},
+ {&tfm_its_test_common_016, "TFM_ITS_TEST_2016",
+ "Block compaction after remove"},
+ {&tfm_its_test_common_017, "TFM_ITS_TEST_2017",
+ "Multiple partial gets"},
+ {&tfm_its_test_common_018, "TFM_ITS_TEST_2018",
+ "Multiple sets to same UID from same thread"},
+ {&tfm_its_test_common_019, "TFM_ITS_TEST_2019",
+ "Set, get and remove interface with different asset sizes"},
+ {&tfm_its_test_2020, "TFM_ITS_TEST_2020",
+ "Set interface with invalid data length"},
+ {&tfm_its_test_2021, "TFM_ITS_TEST_2021",
+ "Get interface with invalid data lengths and offsets"},
+ {&tfm_its_test_2022, "TFM_ITS_TEST_2022",
+ "Get info interface with NULL info pointer"},
+ {&tfm_its_test_2023, "TFM_ITS_TEST_2023",
+ "Attempt to get a UID set by a different partition"},
+};
+
+void register_testsuite_s_psa_its_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(psa_its_s_tests) / sizeof(psa_its_s_tests[0]));
+
+ set_testsuite("PSA internal trusted storage S interface tests "
+ "(TFM_ITS_TEST_2XXX)",
+ psa_its_s_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Tests set function with:
+ * - Data length longer than maximum permitted
+ *
+ * \param[out] ret Test result
+ */
+static void tfm_its_test_2020(struct test_result_t *ret)
+{
+#ifndef TFM_PSA_API
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t data_len = INVALID_DATA_LEN;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ /* A parameter with a buffer pointer where its data length is longer than
+ * maximum permitted, it is treated as a secure violation.
+ * TF-M framework rejects the request with a proper error code.
+ * The ITS secure PSA implementation returns
+ * PSA_ERROR_INVALID_ARGUMENT in that case.
+ */
+
+ /* Set with data length longer than the maximum supported */
+ status = psa_its_set(uid, data_len, write_data, flags);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Set should not succeed with invalid data length");
+ return;
+ }
+
+#endif
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Invalid data len and offset
+ * - NULL read data length pointer
+ *
+ * \param[out] ret Test result
+ */
+static void tfm_its_test_2021(struct test_result_t *ret)
+{
+#ifndef TFM_PSA_API
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t write_len = WRITE_DATA_SIZE;
+ size_t read_len;
+ size_t offset;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_length = 0;
+
+ status = psa_its_set(uid, write_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get with data length and offset set to invalid values */
+ read_len = INVALID_DATA_LEN;
+ offset = INVALID_OFFSET;
+
+ /* A parameter with a buffer pointer where its data length is longer than
+ * maximum permitted, it is treated as a secure violation.
+ * TF-M framework rejects the request with a proper error code.
+ * The ITS secure PSA implementation returns
+ * PSA_ERROR_INVALID_ARGUMENT in that case.
+ */
+
+ status = psa_its_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_length);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Get should not succeed with invalid arguments");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+ read_len = 1;
+ offset = 0;
+
+ status = psa_its_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ NULL);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Get should not succeed with invalid arguments");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+#endif
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - NULL info pointer
+ *
+ * \param[out] ret Test result
+ */
+static void tfm_its_test_2022(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ status = psa_its_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* A parameter with a null pointer is treated as a secure violation.
+ * TF-M framework rejects the request with a proper error code.
+ * The secure PSA ITS implementation returns
+ * PSA_ERROR_INVALID_ARGUMENT in that case.
+ */
+
+ /* Get info with NULL info pointer */
+#ifndef TFM_PSA_API
+ status = psa_its_get_info(uid, NULL);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Get info should not succeed with NULL info pointer");
+ return;
+ }
+#endif
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Attempt to get a UID set by a different partition.
+ *
+ * \param[out] ret Test result
+ */
+static void tfm_its_test_2023(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_ACCESS_CONTROL;
+
+ /* Set the UID from this partition's context */
+ status = psa_its_set(uid, WRITE_DATA_SIZE, WRITE_DATA,
+ PSA_STORAGE_FLAG_NONE);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Attempt to get the UID from the Secure Client 2 partition */
+ status = tfm_secure_client_2_call_test(
+ TFM_SECURE_CLIENT_2_ID_ITS_ACCESS_CTRL,
+ &uid, sizeof(uid));
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get should not succeed from a different partition");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
diff --git a/test/suites/its/secure/psa_its_s_reliability_testsuite.c b/test/suites/its/secure/psa_its_s_reliability_testsuite.c
new file mode 100644
index 0000000..4528424
--- /dev/null
+++ b/test/suites/its/secure/psa_its_s_reliability_testsuite.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "its_s_tests.h"
+#include "psa/internal_trusted_storage.h"
+#include "test/framework/test_framework_helpers.h"
+#include "tfm_memory_utils.h"
+
+/* Test UIDs */
+#define TEST_UID 2UL /* UID 1 cannot be used as it references a write once
+ * asset, created in psa_its_s_interface_testsuite.c
+ */
+
+/* Test suite defines */
+#define LOOP_ITERATIONS_001 15U
+#define LOOP_ITERATIONS_002 15U
+
+/* Write data */
+#define WRITE_DATA "THEQUICKBROWNFOXJUMPSOVERALAZYDOG"
+#define WRITE_DATA_SIZE (sizeof(WRITE_DATA) - 1)
+#define READ_DATA "_________________________________________"
+#define RESULT_DATA ("____" WRITE_DATA "____")
+
+/* Size of ____ from RESULT_DATA */
+#define HALF_PADDING_SIZE 4
+
+/* Define test suite for ITS reliability tests */
+/* List of tests */
+static void tfm_its_test_3001(struct test_result_t *ret);
+static void tfm_its_test_3002(struct test_result_t *ret);
+
+static struct test_t reliability_tests[] = {
+ {&tfm_its_test_3001, "TFM_ITS_TEST_3001",
+ "repetitive sets and gets in/from an asset", {TEST_PASSED} },
+ {&tfm_its_test_3002, "TFM_ITS_TEST_3002",
+ "repetitive sets, gets and removes", {TEST_PASSED} },
+};
+
+void register_testsuite_s_psa_its_reliability(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size = (sizeof(reliability_tests) /
+ sizeof(reliability_tests[0]));
+
+ set_testsuite("ITS reliability tests (TFM_ITS_TEST_3XXX)",
+ reliability_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Tests repetitive sets and gets in/from an asset.
+ *
+ * \param[out] ret Test result
+ */
+static void tfm_its_test_3001(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t data_len = WRITE_DATA_SIZE;
+ const size_t offset = 0;
+ uint32_t itr;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_length = 0;
+
+ for (itr = 0; itr < LOOP_ITERATIONS_001; itr++) {
+ TEST_LOG(" > Iteration %d of %d\r", itr + 1, LOOP_ITERATIONS_001);
+
+ /* Set a data in the asset */
+ status = psa_its_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Get data from the asset */
+ status = psa_its_get(uid, offset, data_len, (read_data +
+ HALF_PADDING_SIZE),
+ &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Check that the data has not changed */
+ if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("The data should not have changed");
+ return;
+ }
+
+ /* Set the original data into read buffer */
+ tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+ }
+
+ TEST_LOG("\n");
+
+ /* Remove the asset to clean up storage for the next test */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests repetitive sets, gets and removes.
+ *
+ * \param[out] ret Test result
+ */
+static void tfm_its_test_3002(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const size_t data_len = WRITE_DATA_SIZE;
+ const size_t offset = 0;
+ uint32_t itr;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_length = 0;
+
+ for (itr = 0; itr < LOOP_ITERATIONS_002; itr++) {
+ TEST_LOG(" > Iteration %d of %d\r", itr + 1, LOOP_ITERATIONS_002);
+
+ /* Set a data in the asset */
+ status = psa_its_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Get data from the asset */
+ status = psa_its_get(uid, offset, data_len, (read_data +
+ HALF_PADDING_SIZE),
+ &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Check that the data has not changed */
+ if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("The data should not have changed");
+ return;
+ }
+
+ /* Remove the asset from the secure storage */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ /* Set the original data into read buffer */
+ tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+ }
+
+ TEST_LOG("\n");
+
+ ret->val = TEST_PASSED;
+}
diff --git a/test/suites/multi_core/CMakeLists.inc b/test/suites/multi_core/CMakeLists.inc
new file mode 100644
index 0000000..521354a
--- /dev/null
+++ b/test/suites/multi_core/CMakeLists.inc
@@ -0,0 +1,31 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "multi-core test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(MULTI_CORE_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+ message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+list(APPEND ALL_SRC_C_NS "${MULTI_CORE_TEST_DIR}/non_secure/multi_core_ns_interface_testsuite.c")
+
+#Setting include directories
+embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
diff --git a/test/suites/multi_core/non_secure/multi_core_ns_interface_testsuite.c b/test/suites/multi_core/non_secure/multi_core_ns_interface_testsuite.c
new file mode 100644
index 0000000..3b83487
--- /dev/null
+++ b/test/suites/multi_core/non_secure/multi_core_ns_interface_testsuite.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "os_wrapper/mutex.h"
+#include "os_wrapper/thread.h"
+#include "os_wrapper/tick.h"
+#include "psa/client.h"
+#include "psa/internal_trusted_storage.h"
+#include "psa_manifest/sid.h"
+#include "test/framework/test_framework_helpers.h"
+#include "tfm_ns_mailbox.h"
+
+#ifdef TFM_MULTI_CORE_MULTI_CLIENT_CALL
+/* Max number of child threads for multiple outstanding PSA client call test */
+#define NR_MULTI_CALL_CHILD (NUM_MAILBOX_QUEUE_SLOT * 2)
+#else
+#define NR_MULTI_CALL_CHILD 0
+#endif
+
+/* The event flag to sync up between parent thread and child threads */
+#define TEST_CHILD_EVENT_FLAG(x) (uint32_t)(0x1UL << (x))
+
+/* Max number of test rounds */
+#define MAX_NR_LIGHT_TEST_ROUND 0x200
+#define MAX_NR_HEAVY_TEST_ROUND 0x20
+
+/* Default stack size for child thread */
+#define MULTI_CALL_LIGHT_TEST_STACK_SIZE 0x200
+#define MULTI_CALL_HEAVY_TEST_STACK_SIZE 0x300
+
+/* Test UID copied from ITS test cases */
+#define TEST_UID_1 2U
+/* ITS data for multiple PSA client call heavy tests */
+#define ITS_DATA "ITSDataForMultiCore"
+#define ITS_DATA_LEN sizeof(ITS_DATA)
+
+/* Structure passed to test threads */
+struct test_params {
+ void *parent_handle; /* The thread handle of parent thread */
+ uint32_t child_idx; /* The index of current child thread */
+ uint32_t nr_rounds; /* The number of test rounds */
+ uint32_t nr_calls; /* The number of PSA client calls */
+ void *mutex_handle; /* Mutex to protect is_complete flag */
+ enum test_status_t ret; /* The test result */
+ uint32_t total_ticks; /* The total ticks cost to complete tests */
+ bool is_complete; /* Whether current test thread completes */
+ bool is_parent; /* Whether executed in parent thread */
+};
+
+/* List of tests */
+static void multi_client_call_light_test(struct test_result_t *ret);
+static void multi_client_call_heavy_test(struct test_result_t *ret);
+static void multi_client_call_ooo_test(struct test_result_t *ret);
+
+static struct test_t multi_core_tests[] = {
+ {&multi_client_call_light_test,
+ "MULTI_CLIENT_CALL_LIGHT_TEST",
+ "Multiple outstanding NS PSA client calls lightweight test",
+ {TEST_PASSED}},
+ {&multi_client_call_heavy_test,
+ "MULTI_CLIENT_CALL_HEAVY_TEST",
+ "Multiple outstanding NS PSA client calls heavyweight test",
+ {TEST_PASSED}},
+ {&multi_client_call_ooo_test,
+ "MULTI_CLIENT_CALL_OOO_TEST",
+ "Multiple outstanding NS PSA client calls test with out-of-order calls",
+ {TEST_PASSED}},
+};
+
+void register_testsuite_multi_core_ns_interface(
+ struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ if (!sizeof(multi_core_tests)) {
+ return;
+ }
+
+ list_size = (sizeof(multi_core_tests) / sizeof(multi_core_tests[0]));
+
+ set_testsuite("TF-M test cases for multi-core topology",
+ multi_core_tests, list_size, p_test_suite);
+}
+
+static void wait_child_thread_completion(struct test_params *params_array,
+ uint8_t child_idx)
+{
+ bool is_complete;
+ uint8_t i;
+ void *mutex = params_array[0].mutex_handle;
+
+ for (i = 0; i < child_idx; i++) {
+ while (1) {
+ os_wrapper_mutex_acquire(mutex, OS_WRAPPER_WAIT_FOREVER);
+ is_complete = params_array[i].is_complete;
+ os_wrapper_mutex_release(mutex);
+
+ if (is_complete) {
+ break;
+ }
+ }
+ }
+}
+
+static void multi_client_call_test(struct test_result_t *ret,
+ os_wrapper_thread_func test_runner,
+ int32_t stack_size,
+ int32_t nr_rounds,
+ bool is_mixed)
+{
+ uint8_t i, nr_child;
+ void *current_thread_handle;
+ uint32_t current_thread_priority, err, total_ticks, total_calls, avg_ticks;
+ void *mutex_handle;
+ void *child_ids[NR_MULTI_CALL_CHILD];
+ struct ns_mailbox_stats_res_t stats_res;
+ struct test_params parent_params, params[NR_MULTI_CALL_CHILD];
+
+ tfm_ns_mailbox_tx_stats_init();
+
+ current_thread_handle = os_wrapper_thread_get_handle();
+ if (!current_thread_handle) {
+ TEST_FAIL("Failed to get current thread ID\r\n");
+ return;
+ }
+
+ err = os_wrapper_thread_get_priority(current_thread_handle,
+ ¤t_thread_priority);
+ if (err == OS_WRAPPER_ERROR) {
+ TEST_FAIL("Failed to get current thread priority\r\n");
+ return;
+ }
+
+ /*
+ * Create a mutex to protect the synchronization between child test thread
+ * about the completion status.
+ * The best way is to use os_wrapper_thread_wait/set_flag(). However, due to
+ * the implementation of the wait event functions in some RTOS, if the
+ * child test threads already exit before the main thread starts to wait for
+ * event (main thread itself has to perform test too), the main thread
+ * cannot receive the event flags.
+ * As a result, use a flag and a mutex to make sure the main thread can
+ * capture the completion event of child threads.
+ */
+ mutex_handle = os_wrapper_mutex_create();
+ if (!mutex_handle) {
+ TEST_FAIL("Failed to create a mutex\r\n");
+ return;
+ }
+
+ /* Create test threads one by one */
+ for (i = 0; i < NR_MULTI_CALL_CHILD; i++) {
+ params[i].parent_handle = current_thread_handle;
+ params[i].child_idx = i;
+ params[i].nr_rounds = nr_rounds;
+ params[i].mutex_handle = mutex_handle;
+ params[i].is_complete = false;
+ params[i].is_parent = false;
+
+ child_ids[i] = os_wrapper_thread_new(NULL,
+ stack_size,
+ test_runner,
+ ¶ms[i],
+ current_thread_priority);
+ if (!child_ids[i]) {
+ break;
+ }
+ }
+
+ nr_child = i;
+ TEST_LOG("Totally %d threads for test start\r\n", nr_child + 1);
+ if (!is_mixed) {
+ TEST_LOG("Each thread run 0x%x rounds tests\r\n", nr_rounds);
+ }
+
+ /*
+ * Activate test threads one by one.
+ * Try to make test threads to run together.
+ */
+ for (i = 0; i < nr_child; i++) {
+ os_wrapper_thread_set_flag(child_ids[i], TEST_CHILD_EVENT_FLAG(i));
+ }
+
+ /* Use current thread to execute a test instance */
+ parent_params.child_idx = nr_child;
+ parent_params.nr_rounds = nr_rounds;
+ parent_params.is_parent = true;
+ test_runner(&parent_params);
+
+ /* Wait for all the test threads completes */
+ wait_child_thread_completion(params, nr_child);
+
+ os_wrapper_mutex_delete(mutex_handle);
+
+ if (parent_params.ret != TEST_PASSED) {
+ ret->val = TEST_FAILED;
+ return;
+ }
+
+ total_ticks = parent_params.total_ticks;
+ total_calls = parent_params.nr_calls;
+
+ /* Check the test result of each child thread */
+ for (i = 0; i < nr_child; i++) {
+ if (params[i].ret != TEST_PASSED) {
+ ret->val = TEST_FAILED;
+ return;
+ }
+
+ total_ticks += params[i].total_ticks;
+ total_calls += params[i].nr_calls;
+ }
+
+ tfm_ns_mailbox_stats_avg_slot(&stats_res);
+ TEST_LOG("Totally %d NS mailbox queue slots\r\n", NUM_MAILBOX_QUEUE_SLOT);
+ TEST_LOG("%d.%d NS mailbox queue slots are occupied each time in average.\r\n",
+ stats_res.avg_nr_slots, stats_res.avg_nr_slots_tenths);
+
+ TEST_LOG("Cost %d ticks totally\r\n", total_ticks);
+ avg_ticks = total_ticks / total_calls;
+ total_ticks %= total_calls;
+ TEST_LOG("Each PSA client call cost %d.%d ticks in average\r\n", avg_ticks,
+ total_ticks * 10 / total_calls);
+
+ ret->val = TEST_PASSED;
+}
+
+static inline
+enum test_status_t multi_client_call_light_loop(struct test_params *params)
+{
+ uint32_t i, version, total_ticks;
+ uint32_t nr_rounds = params->nr_rounds;
+
+ total_ticks = os_wrapper_get_tick();
+
+ for (i = 0; i < nr_rounds; i++) {
+ version = psa_framework_version();
+ if (version != PSA_FRAMEWORK_VERSION) {
+ TEST_LOG("Incorrect PSA framework version!\r\n");
+ return TEST_FAILED;
+ }
+ }
+
+ params->total_ticks = os_wrapper_get_tick() - total_ticks;
+ params->nr_calls = nr_rounds;
+
+ return TEST_PASSED;
+}
+
+static void multi_client_call_light_runner(void *argument)
+{
+ struct test_params *params = (struct test_params *)argument;
+
+ if (!params->is_parent) {
+ /* Wait for the signal to kick-off the test */
+ os_wrapper_thread_wait_flag(TEST_CHILD_EVENT_FLAG(params->child_idx),
+ OS_WRAPPER_WAIT_FOREVER);
+ }
+
+ params->ret = multi_client_call_light_loop(params);
+
+ if (!params->is_parent) {
+ /* Mark this child thread has completed */
+ os_wrapper_mutex_acquire(params->mutex_handle, OS_WRAPPER_WAIT_FOREVER);
+ params->is_complete = true;
+ os_wrapper_mutex_release(params->mutex_handle);
+ }
+}
+
+/**
+ * \brief Lightweight test case to verify multiple outstanding PSA client calls
+ * feature.
+ */
+static void multi_client_call_light_test(struct test_result_t *ret)
+{
+ multi_client_call_test(ret, multi_client_call_light_runner,
+ MULTI_CALL_LIGHT_TEST_STACK_SIZE,
+ MAX_NR_LIGHT_TEST_ROUND,
+ false);
+}
+
+static inline
+enum test_status_t multi_client_call_heavy_loop(const psa_storage_uid_t uid,
+ struct test_params *params)
+{
+ uint32_t i, total_ticks, rounds = params->nr_rounds;
+ psa_status_t status;
+ size_t rd_data_len;
+ char rd_data[ITS_DATA_LEN];
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+
+ total_ticks = os_wrapper_get_tick();
+
+ for (i = 0; i < rounds; i++) {
+ /* Set a data in the asset */
+ status = psa_its_set(uid, ITS_DATA_LEN, ITS_DATA, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_LOG("Fail to write ITS asset\r\n");
+ return TEST_FAILED;
+ }
+
+ /* Get data from the asset */
+ status = psa_its_get(uid, 0, ITS_DATA_LEN, rd_data, &rd_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_LOG("Fail to read ITS asset\r\n");
+ return TEST_FAILED;
+ }
+
+ /* Remove the asset to clean up storage */
+ status = psa_its_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_LOG("Fail to remove ITS asset\r\n");
+ return TEST_FAILED;
+ }
+ }
+
+ params->total_ticks = os_wrapper_get_tick() - total_ticks;
+ params->nr_calls = rounds * 3;
+
+ return TEST_PASSED;
+}
+
+static void multi_client_call_heavy_runner(void *argument)
+{
+ struct test_params *params = (struct test_params *)argument;
+ const psa_storage_uid_t uid = TEST_UID_1 + params->child_idx;
+
+ if (!params->is_parent) {
+ /* Wait for the signal to kick-off the test */
+ os_wrapper_thread_wait_flag(TEST_CHILD_EVENT_FLAG(params->child_idx),
+ OS_WRAPPER_WAIT_FOREVER);
+ }
+
+ params->ret = multi_client_call_heavy_loop(uid, params);
+
+ if (!params->is_parent) {
+ /* Mark this child thread has completed */
+ os_wrapper_mutex_acquire(params->mutex_handle, OS_WRAPPER_WAIT_FOREVER);
+ params->is_complete = true;
+ os_wrapper_mutex_release(params->mutex_handle);
+ }
+}
+
+/**
+ * \brief NS interface to verify multiple outstanding PSA client calls feature
+ * by calling heavyweight secure services.
+ */
+static void multi_client_call_heavy_test(struct test_result_t *ret)
+{
+ multi_client_call_test(ret, multi_client_call_heavy_runner,
+ MULTI_CALL_HEAVY_TEST_STACK_SIZE,
+ MAX_NR_HEAVY_TEST_ROUND,
+ false);
+}
+
+static void multi_client_call_ooo_runner(void *argument)
+{
+ struct test_params *params = (struct test_params *)argument;
+ const psa_storage_uid_t uid = TEST_UID_1 + params->child_idx;
+
+ if (!params->is_parent) {
+ /* Wait for the signal to kick-off the test */
+ os_wrapper_thread_wait_flag(TEST_CHILD_EVENT_FLAG(params->child_idx),
+ OS_WRAPPER_WAIT_FOREVER);
+ }
+
+ if (!params->child_idx % 2) {
+ params->ret = multi_client_call_heavy_loop(uid, params);
+ } else {
+ params->nr_rounds *= 20;
+ params->ret = multi_client_call_light_loop(params);
+ }
+
+ if (!params->is_parent) {
+ /* Mark this child thread has completed */
+ os_wrapper_mutex_acquire(params->mutex_handle, OS_WRAPPER_WAIT_FOREVER);
+ params->is_complete = true;
+ os_wrapper_mutex_release(params->mutex_handle);
+ }
+}
+
+/**
+ * \brief Mix lightweight test and heavyweight test to verify multiple
+ * outstanding PSA client calls feature with out-of-order calls.
+ */
+static void multi_client_call_ooo_test(struct test_result_t *ret)
+{
+ multi_client_call_test(ret, multi_client_call_ooo_runner,
+ MULTI_CALL_HEAVY_TEST_STACK_SIZE,
+ MAX_NR_HEAVY_TEST_ROUND,
+ true);
+}
diff --git a/test/suites/multi_core/non_secure/multi_core_ns_test.h b/test/suites/multi_core/non_secure/multi_core_ns_test.h
new file mode 100644
index 0000000..44dda5d
--- /dev/null
+++ b/test/suites/multi_core/non_secure/multi_core_ns_test.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __MULTI_CORE_NS_TESTS_H__
+#define __MULTI_CORE_NS_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for multi-core topology.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_multi_core_ns_interface(
+ struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MULTI_CORE_NS_TESTS_H__ */
diff --git a/test/suites/platform/CMakeLists.inc b/test/suites/platform/CMakeLists.inc
new file mode 100644
index 0000000..82d19ba
--- /dev/null
+++ b/test/suites/platform/CMakeLists.inc
@@ -0,0 +1,52 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "secure platform service test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(PLATFORM_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+ message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_PLATFORM_SERVICE_TESTS)
+ message(FATAL_ERROR "Incomplete build configuration: ENABLE_PLATFORM_SERVICE_TESTS is undefined. ")
+elseif(ENABLE_PLATFORM_SERVICE_TESTS)
+ list(APPEND PLATFORM_TEST_SRC_S
+ "${PLATFORM_TEST_DIR}/secure/platform_s_interface_testsuite.c"
+ )
+
+ list(APPEND PLATFORM_TEST_SRC_NS
+ "${PLATFORM_TEST_DIR}/non_secure/platform_ns_interface_testsuite.c"
+ )
+
+ list(APPEND PLATFORM_TEST_SRC
+ "${PLATFORM_TEST_DIR}/platform_tests_common.c"
+ )
+
+ #Setting include directories
+ embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+ embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+ embedded_include_directories(PATH ${PLATFORM_TEST_DIR} ABSOLUTE)
+
+ #Append all our source files to global lists.
+ list(APPEND ALL_SRC_C_S ${PLATFORM_TEST_SRC_S} ${PLATFORM_TEST_SRC})
+ list(APPEND ALL_SRC_C_NS ${PLATFORM_TEST_SRC_NS} ${PLATFORM_TEST_SRC})
+ unset(PLATFORM_TEST_SRC_S)
+ unset(PLATFORM_TEST_SRC_NS)
+endif()
diff --git a/test/suites/platform/non_secure/platform_ns_interface_testsuite.c b/test/suites/platform/non_secure/platform_ns_interface_testsuite.c
new file mode 100644
index 0000000..3d664bb
--- /dev/null
+++ b/test/suites/platform/non_secure/platform_ns_interface_testsuite.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "platform_ns_tests.h"
+#include "tfm_platform_api.h"
+#include "platform_tests_common.h"
+
+static struct test_t platform_interface_tests[] = {
+ {&tfm_platform_test_common_001, "TFM_PLATFORM_TEST_2001",
+ "Minimal platform service test", {TEST_PASSED} },
+};
+
+void
+register_testsuite_ns_platform_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(platform_interface_tests) /
+ sizeof(platform_interface_tests[0]));
+
+ set_testsuite("Platform Service Non-Secure interface tests"
+ "(TFM_PLATFORM_TEST_2XXX)",
+ platform_interface_tests, list_size, p_test_suite);
+}
diff --git a/test/suites/platform/non_secure/platform_ns_tests.h b/test/suites/platform/non_secure/platform_ns_tests.h
new file mode 100644
index 0000000..ac54aca
--- /dev/null
+++ b/test/suites/platform/non_secure/platform_ns_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PLATFORM_NS_TESTS_H__
+#define __PLATFORM_NS_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the platform service.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void
+register_testsuite_ns_platform_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PLATFORM_NS_TESTS_H__ */
diff --git a/test/suites/platform/platform_tests_common.c b/test/suites/platform/platform_tests_common.c
new file mode 100644
index 0000000..52d388a
--- /dev/null
+++ b/test/suites/platform/platform_tests_common.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_platform_api.h"
+#include "platform_tests_common.h"
+
+/*!
+ * \brief Call the platform service with an invalid request
+ */
+void tfm_platform_test_common_001(struct test_result_t *ret)
+{
+ int32_t err;
+
+ err = tfm_platform_ioctl((tfm_platform_ioctl_req_t) INVALID_REQUEST,
+ NULL,
+ NULL);
+ if (err != TFM_PLATFORM_ERR_NOT_SUPPORTED) {
+ TEST_FAIL("Call with invalid request should fail.");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
diff --git a/test/suites/platform/platform_tests_common.h b/test/suites/platform/platform_tests_common.h
new file mode 100644
index 0000000..348788c
--- /dev/null
+++ b/test/suites/platform/platform_tests_common.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PLATFORM_TESTS_COMMON_H__
+#define __PLATFORM_TESTS_COMMON_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+#define INVALID_REQUEST 0xffffffff
+
+/*!
+ * \brief Call the platform service with an invalid request
+ *
+ * \param[out] ret Test results
+ */
+void tfm_platform_test_common_001(struct test_result_t *ret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PLATFORM_TESTS_COMMON_H__ */
diff --git a/test/suites/platform/secure/platform_s_interface_testsuite.c b/test/suites/platform/secure/platform_s_interface_testsuite.c
new file mode 100644
index 0000000..3bd1872
--- /dev/null
+++ b/test/suites/platform/secure/platform_s_interface_testsuite.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "platform_s_tests.h"
+#include "tfm_platform_api.h"
+#include "platform_tests_common.h"
+
+static struct test_t platform_interface_tests[] = {
+ {&tfm_platform_test_common_001, "TFM_PLATFORM_TEST_1001",
+ "Minimal platform service test", {TEST_PASSED} },
+};
+
+void
+register_testsuite_s_platform_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(platform_interface_tests) /
+ sizeof(platform_interface_tests[0]));
+
+ set_testsuite("Platform Service Secure interface tests"
+ "(TFM_PLATFORM_TEST_1XXX)",
+ platform_interface_tests, list_size, p_test_suite);
+}
diff --git a/test/suites/platform/secure/platform_s_tests.h b/test/suites/platform/secure/platform_s_tests.h
new file mode 100644
index 0000000..2ca23dc
--- /dev/null
+++ b/test/suites/platform/secure/platform_s_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PLATFORM_S_TESTS_H__
+#define __PLATFORM_S_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the platform service.
+ *
+ * \param[in] "p_test_suite" The test suite to be executed.
+ */
+void
+register_testsuite_s_platform_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PLATFORM_S_TESTS_H__ */
diff --git a/test/suites/ps/CMakeLists.inc b/test/suites/ps/CMakeLists.inc
new file mode 100644
index 0000000..41a29c9
--- /dev/null
+++ b/test/suites/ps/CMakeLists.inc
@@ -0,0 +1,52 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "protected storage test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(PROTECTED_STORAGE_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+ message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_PROTECTED_STORAGE_SERVICE_TESTS)
+ message(FATAL_ERROR "Incomplete build configuration: ENABLE_PROTECTED_STORAGE_SERVICE_TESTS is undefined. ")
+elseif (ENABLE_PROTECTED_STORAGE_SERVICE_TESTS)
+ list(APPEND ALL_SRC_C_NS "${PROTECTED_STORAGE_TEST_DIR}/non_secure/ns_test_helpers.c"
+ "${PROTECTED_STORAGE_TEST_DIR}/non_secure/psa_ps_ns_interface_testsuite.c")
+
+ list(APPEND ALL_SRC_C_S "${PROTECTED_STORAGE_TEST_DIR}/secure/psa_ps_s_interface_testsuite.c"
+ "${PROTECTED_STORAGE_TEST_DIR}/secure/psa_ps_s_reliability_testsuite.c")
+
+ if (PS_ENCRYPTION AND PS_ROLLBACK_PROTECTION AND PS_TEST_NV_COUNTERS)
+ list(APPEND ALL_SRC_C_S "${PROTECTED_STORAGE_TEST_DIR}/secure/ps_rollback_protection_testsuite.c"
+ "${PROTECTED_STORAGE_TEST_DIR}/secure/nv_counters/test_ps_nv_counters.c")
+ set_property(SOURCE ${ALL_SRC_C_S} APPEND PROPERTY COMPILE_DEFINITIONS PS_TEST_NV_COUNTERS)
+ endif()
+
+ if (NOT DEFINED TFM_NS_CLIENT_IDENTIFICATION)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_NS_CLIENT_IDENTIFICATION is undefined.")
+ elseif (TFM_NS_CLIENT_IDENTIFICATION)
+ set_property(SOURCE ${ALL_SRC_C_NS} APPEND PROPERTY COMPILE_DEFINITIONS TFM_NS_CLIENT_IDENTIFICATION)
+ endif()
+
+ #Setting include directories
+ embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+ embedded_include_directories(PATH ${TFM_ROOT_DIR}/test/interface/include ABSOLUTE)
+ embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+endif()
diff --git a/test/suites/ps/non_secure/ns_test_helpers.c b/test/suites/ps/non_secure/ns_test_helpers.c
new file mode 100644
index 0000000..149b767
--- /dev/null
+++ b/test/suites/ps/non_secure/ns_test_helpers.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "ns_test_helpers.h"
+
+#include "os_wrapper/thread.h"
+#include "os_wrapper/semaphore.h"
+
+#include "tfm_nspm_api.h"
+
+#define PS_TEST_TASK_STACK_SIZE (768)
+
+struct test_task_t {
+ test_func_t *func;
+ struct test_result_t *ret;
+};
+
+static void *test_semaphore;
+
+/**
+ * \brief Executes the supplied test task and then releases the test semaphore.
+ *
+ * \param[in,out] arg Pointer to the test task. Must be a pointer to a
+ * struct test_task_t
+ */
+static void test_task_runner(void *arg)
+{
+ struct test_task_t *test = arg;
+
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+ tfm_nspm_register_client_id();
+#endif /* TFM_NS_CLIENT_IDENTIFICATION */
+
+ /* Call the test function */
+ test->func(test->ret);
+
+ /* Release the semaphore to unblock the parent thread */
+ os_wrapper_semaphore_release(test_semaphore);
+
+ /* Signal to the RTOS that the thread is finished */
+ os_wrapper_thread_exit();
+}
+
+void tfm_ps_run_test(const char *thread_name, struct test_result_t *ret,
+ test_func_t *test_func)
+{
+ void *current_thread_handle;
+ uint32_t current_thread_priority;
+ uint32_t err;
+ void *thread;
+ struct test_task_t test_task = { .func = test_func, .ret = ret };
+
+ /* Create a binary semaphore with initial count of 0 tokens available */
+ test_semaphore = os_wrapper_semaphore_create(1, 0, "ps_tests_sema");
+ if (!test_semaphore) {
+ TEST_FAIL("Semaphore creation failed");
+ return;
+ }
+
+ current_thread_handle = os_wrapper_thread_get_handle();
+ if (!current_thread_handle) {
+ os_wrapper_semaphore_delete(test_semaphore);
+ TEST_FAIL("Failed to get current thread ID");
+ return;
+ }
+
+ err = os_wrapper_thread_get_priority(current_thread_handle,
+ ¤t_thread_priority);
+ if (err == OS_WRAPPER_ERROR) {
+ os_wrapper_semaphore_delete(test_semaphore);
+ TEST_FAIL("Failed to get current thread priority");
+ return;
+ }
+
+ /* Start test thread */
+ thread = os_wrapper_thread_new(thread_name, PS_TEST_TASK_STACK_SIZE,
+ test_task_runner, &test_task,
+ current_thread_priority);
+ if (!thread) {
+ os_wrapper_semaphore_delete(test_semaphore);
+ TEST_FAIL("Failed to create test thread");
+ return;
+ }
+
+ /* Signal semaphore, wait indefinitely until unblocked by child thread */
+ err = os_wrapper_semaphore_acquire(test_semaphore, OS_WRAPPER_WAIT_FOREVER);
+
+ /* At this point, it means the binary semaphore has been released by the
+ * test and re-acquired by this thread, so just finally release it and
+ * delete it
+ */
+ os_wrapper_semaphore_release(test_semaphore);
+
+ os_wrapper_semaphore_delete(test_semaphore);
+}
diff --git a/test/suites/ps/non_secure/ns_test_helpers.h b/test/suites/ps/non_secure/ns_test_helpers.h
new file mode 100644
index 0000000..22226f4
--- /dev/null
+++ b/test/suites/ps/non_secure/ns_test_helpers.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __NS_TEST_HELPERS_H__
+#define __NS_TEST_HELPERS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CONCAT_(x, y) x ## y
+#define CONCAT(x, y) CONCAT_(x, y)
+
+/**
+ * Several tests use a buffer to read back data from an asset. This buffer is
+ * larger than the size of the asset data by PADDING_SIZE bytes. This allows
+ * us to ensure that the only the expected data is read back and that it is read
+ * back correctly.
+ *
+ * For example if the buffer and asset are as follows:
+ * Buffer - "XXXXXXXXXXXX", Asset data - "AAAA"
+ *
+ * Then a correct and successful read would give this result: "XXXXAAAAXXXX"
+ * (Assuming a PADDING_SIZE of 8)
+ */
+#define BUFFER_SIZE 24
+#define PADDING_SIZE 8
+#define HALF_PADDING_SIZE 4
+
+#define BUFFER_PLUS_PADDING_SIZE (BUFFER_SIZE + PADDING_SIZE)
+#define BUFFER_PLUS_HALF_PADDING_SIZE (BUFFER_SIZE + HALF_PADDING_SIZE)
+
+/**
+ * \brief Expands to the prototype of a test function.
+ *
+ * \param[in] test_name Name of the test function
+ */
+#define TFM_PS_TEST_PROTO(test_name) \
+ static void test_name(struct test_result_t *ret)
+
+/**
+ * \brief Expands to the standard name of a test function.
+ *
+ * \param[in] test_num Identification number of the test
+ */
+#define TFM_PS_TEST_NAME(test_num) CONCAT(tfm_ps_test_, test_num)
+
+/**
+ * \brief Expands to the standard name of a task function.
+ *
+ * \param[in] test_num Identification number of the task
+ */
+#define TFM_PS_TASK_NAME(test_num) CONCAT(TFM_PS_TEST_NAME(test_num), _task)
+
+/**
+ * \brief Expands to a test function declaration.
+ *
+ * \param[in] test_num Identification number of the test
+ */
+#define TFM_PS_TEST(test_num) TFM_PS_TEST_PROTO(TFM_PS_TEST_NAME(test_num))
+
+/**
+ * \brief Expands to a task function declaration.
+ *
+ * \param[in] test_num Identification number of the task
+ */
+#define TFM_PS_TASK(test_num) TFM_PS_TEST_PROTO(TFM_PS_TASK_NAME(test_num))
+
+/**
+ * \brief Defines a single-threaded PS NS test function and declares the
+ * corresponding task function.
+ *
+ * \param[in] test_num Identification number of the test
+ * \param[in] thread_name Name of the thread in which to run the test
+ */
+#define TFM_PS_NS_TEST(test_num, thread_name) \
+ TFM_PS_TASK(test_num); \
+ TFM_PS_TEST(test_num) \
+ { \
+ tfm_ps_run_test(thread_name, ret, TFM_PS_TASK_NAME(test_num)); \
+ } \
+ TFM_PS_TASK(test_num)
+
+/* The type of a test function */
+typedef void test_func_t(struct test_result_t *ret);
+
+/**
+ * \brief Executes the given test function from the specified thread context.
+ *
+ * \param[in] thread_name Name of the thread to be created for test
+ * \param[out] ret Result of the test
+ * \param[in] test_func Test function to be run in the new thread
+ */
+void tfm_ps_run_test(const char *thread_name, struct test_result_t *ret,
+ test_func_t *test_func);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NS_TEST_HELPERS_H__ */
diff --git a/test/suites/ps/non_secure/ps_ns_tests.h b/test/suites/ps/non_secure/ps_ns_tests.h
new file mode 100644
index 0000000..7153827
--- /dev/null
+++ b/test/suites/ps/non_secure/ps_ns_tests.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PS_NS_TESTS_H__
+#define __PS_NS_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the PSA protected storage NS interface tests.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_ns_psa_ps_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PS_NS_TESTS_H__ */
diff --git a/test/suites/ps/non_secure/psa_ps_ns_interface_testsuite.c b/test/suites/ps/non_secure/psa_ps_ns_interface_testsuite.c
new file mode 100644
index 0000000..638ab8e
--- /dev/null
+++ b/test/suites/ps/non_secure/psa_ps_ns_interface_testsuite.c
@@ -0,0 +1,1726 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "ps_ns_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "ns_test_helpers.h"
+#include "psa/protected_storage.h"
+#include "test/framework/test_framework_helpers.h"
+#include "flash_layout.h"
+
+/* Test UIDs */
+#define WRITE_ONCE_UID 1U /* Cannot be modified or deleted once created */
+#define TEST_UID_1 2U
+#define TEST_UID_2 3U
+#define TEST_UID_3 4U
+
+/* Invalid values */
+#define INVALID_UID 0U
+#define INVALID_DATA_LEN UINT32_MAX
+#define INVALID_OFFSET UINT32_MAX
+#define INVALID_FLAG (1U << 31)
+#define INVALID_THREAD_NAME "Thread_INVALID"
+
+/* Write once data */
+#define WRITE_ONCE_DATA "THE_FIVE_BOXING_WIZARDS_JUMP_QUICKLY"
+#define WRITE_ONCE_DATA_SIZE (sizeof(WRITE_ONCE_DATA) - 1)
+#define WRITE_ONCE_READ_DATA "############################################"
+#define WRITE_ONCE_RESULT_DATA ("####" WRITE_ONCE_DATA "####")
+#define OFFSET_READ_DATA "HEQUICKBROWNFOXJUMPSOVERALAZYDOG"
+#define OFFSET_RESULT_DATA ("____" OFFSET_READ_DATA "_____")
+
+#define WRITE_DATA "THEQUICKBROWNFOXJUMPSOVERALAZYDOG"
+#define WRITE_DATA_SIZE (sizeof(WRITE_DATA) - 1)
+#define READ_DATA "_________________________________________"
+#define RESULT_DATA ("____" WRITE_DATA "____")
+
+#define TEST_1025_CYCLES 3U
+
+static const uint8_t write_asset_data[PS_MAX_ASSET_SIZE] = {0xAF};
+static uint8_t read_asset_data[PS_MAX_ASSET_SIZE] = {0};
+static size_t read_asset_data_len = 0;
+
+/* List of tests */
+static void tfm_ps_test_1001(struct test_result_t *ret);
+static void tfm_ps_test_1002(struct test_result_t *ret);
+static void tfm_ps_test_1003(struct test_result_t *ret);
+static void tfm_ps_test_1004(struct test_result_t *ret);
+static void tfm_ps_test_1005(struct test_result_t *ret);
+static void tfm_ps_test_1006(struct test_result_t *ret);
+static void tfm_ps_test_1007(struct test_result_t *ret);
+static void tfm_ps_test_1008(struct test_result_t *ret);
+static void tfm_ps_test_1009(struct test_result_t *ret);
+static void tfm_ps_test_1010(struct test_result_t *ret);
+static void tfm_ps_test_1011(struct test_result_t *ret);
+static void tfm_ps_test_1012(struct test_result_t *ret);
+static void tfm_ps_test_1013(struct test_result_t *ret);
+static void tfm_ps_test_1014(struct test_result_t *ret);
+static void tfm_ps_test_1015(struct test_result_t *ret);
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+static void tfm_ps_test_1016(struct test_result_t *ret);
+static void tfm_ps_test_1017(struct test_result_t *ret);
+static void tfm_ps_test_1018(struct test_result_t *ret);
+static void tfm_ps_test_1019(struct test_result_t *ret);
+static void tfm_ps_test_1020(struct test_result_t *ret);
+#endif /* TFM_NS_CLIENT_IDENTIFICATION */
+static void tfm_ps_test_1021(struct test_result_t *ret);
+static void tfm_ps_test_1022(struct test_result_t *ret);
+static void tfm_ps_test_1023(struct test_result_t *ret);
+static void tfm_ps_test_1024(struct test_result_t *ret);
+static void tfm_ps_test_1025(struct test_result_t *ret);
+
+static struct test_t psa_ps_ns_tests[] = {
+ {&tfm_ps_test_1001, "TFM_PS_TEST_1001",
+ "Set interface"},
+ {&tfm_ps_test_1002, "TFM_PS_TEST_1002",
+ "Set interface with create flags"},
+ {&tfm_ps_test_1003, "TFM_PS_TEST_1003",
+ "Set interface with NULL data pointer"},
+ {&tfm_ps_test_1004, "TFM_PS_TEST_1004",
+ "Set interface with write once UID"},
+ {&tfm_ps_test_1005, "TFM_PS_TEST_1005",
+ "Get interface with valid data"},
+ {&tfm_ps_test_1006, "TFM_PS_TEST_1006",
+ "Get interface with zero data length"},
+ {&tfm_ps_test_1007, "TFM_PS_TEST_1007",
+ "Get interface with invalid UIDs"},
+ {&tfm_ps_test_1008, "TFM_PS_TEST_1008",
+ "Get interface with invalid data lengths and offsets"},
+ {&tfm_ps_test_1009, "TFM_PS_TEST_1009",
+ "Get interface with NULL data pointer"},
+ {&tfm_ps_test_1010, "TFM_PS_TEST_1010",
+ "Get info interface with write once UID"},
+ {&tfm_ps_test_1011, "TFM_PS_TEST_1011",
+ "Get info interface with valid UID"},
+ {&tfm_ps_test_1012, "TFM_PS_TEST_1012",
+ "Get info interface with invalid UIDs"},
+ {&tfm_ps_test_1013, "TFM_PS_TEST_1013",
+ "Remove interface with valid UID"},
+ {&tfm_ps_test_1014, "TFM_PS_TEST_1014",
+ "Remove interface with write once UID"},
+ {&tfm_ps_test_1015, "TFM_PS_TEST_1015",
+ "Remove interface with invalid UID"},
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+ {&tfm_ps_test_1016, "TFM_PS_TEST_1016",
+ "Get interface with invalid thread name"},
+ {&tfm_ps_test_1017, "TFM_PS_TEST_1017",
+ "Get info interface with invalid thread name"},
+ {&tfm_ps_test_1018, "TFM_PS_TEST_1018",
+ "Remove interface with invalid thread name"},
+ {&tfm_ps_test_1019, "TFM_PS_TEST_1019",
+ "Attempt to access UID belonging to another thread"},
+ {&tfm_ps_test_1020, "TFM_PS_TEST_1020",
+ "Set UID alternately from two threads"},
+#endif /* TFM_NS_CLIENT_IDENTIFICATION */
+ {&tfm_ps_test_1021, "TFM_PS_TEST_1021",
+ "Block compaction after remove"},
+ {&tfm_ps_test_1022, "TFM_PS_TEST_1022",
+ "Multiple partial gets"},
+ {&tfm_ps_test_1023, "TFM_PS_TEST_1023",
+ "Multiple sets to same UID from same thread"},
+ {&tfm_ps_test_1024, "TFM_PS_TEST_1024",
+ "Get support interface"},
+ {&tfm_ps_test_1025, "TFM_PS_TEST_1025",
+ "Set, get and remove interface with different asset sizes"},
+};
+
+void register_testsuite_ns_psa_ps_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(psa_ps_ns_tests) / sizeof(psa_ps_ns_tests[0]));
+
+ set_testsuite("PSA protected storage NS interface tests (TFM_PS_TEST_1XXX)",
+ psa_ps_ns_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Tests set function with:
+ * - Valid UID, no data, no flags
+ * - Invalid UID, no data, no flags
+ */
+TFM_PS_NS_TEST(1001, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = 0;
+ const uint8_t write_data[] = {0};
+
+ /* Set with no data and no flags and a valid UID */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Attempt to set a second time */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail the second time with valid UID");
+ return;
+ }
+
+ /* Set with an invalid UID */
+ status = psa_ps_set(INVALID_UID, data_len, write_data, flags);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Set should not succeed with an invalid UID");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set function with:
+ * - Zero create flags
+ * - Valid create flags (with previously created UID)
+ * - Invalid create flags
+ */
+TFM_PS_NS_TEST(1002, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ /* Set with no flags */
+ status = psa_ps_set(WRITE_ONCE_UID, data_len, write_data, flags);
+ if (status == PSA_SUCCESS) {
+ /* Set with valid flag: PSA_STORAGE_FLAG_WRITE_ONCE (with previously
+ * created UID)
+ * Note: Once created, WRITE_ONCE_UID cannot be deleted. It is reused
+ * across multiple tests.
+ */
+ status = psa_ps_set(WRITE_ONCE_UID, WRITE_ONCE_DATA_SIZE,
+ WRITE_ONCE_DATA, PSA_STORAGE_FLAG_WRITE_ONCE);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid flags (existing UID)");
+ return;
+ }
+ } else if (status == PSA_ERROR_NOT_PERMITTED) {
+ /* The UID has already been created with the PSA_STORAGE_FLAG_WRITE_ONCE
+ * flag in a previous test run, so skip creating it again and emit a
+ * warning.
+ */
+ TEST_LOG("Note: The UID in this test has already been created with\r\n"
+ "the PSA_STORAGE_FLAG_WRITE_ONCE flag in a previous test\r\n"
+ "run. Wipe the storage area to run the full test.\r\n");
+ } else {
+ TEST_FAIL("Set should not fail with no flags");
+ return;
+ }
+
+ /* Set with invalid flags */
+ status = psa_ps_set(uid, data_len, write_data, INVALID_FLAG);
+ if (status != PSA_ERROR_NOT_SUPPORTED) {
+ TEST_FAIL("Set should not succeed with invalid flags");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set function with:
+ * - NULL data pointer and zero data length
+ *
+ * \note A request with a null data pointer and data length not equal to zero is
+ * treated as a secure violation. TF-M framework will reject such requests
+ * and not return to the NSPE so this case is not tested here.
+ *
+ */
+TFM_PS_NS_TEST(1003, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = 0;
+
+ /* Set with NULL data pointer */
+ status = psa_ps_set(uid, data_len, NULL, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should succeed with NULL data pointer and zero length");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set function with:
+ * - Write once UID that has already been created
+ */
+TFM_PS_NS_TEST(1004, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = WRITE_ONCE_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t write_len = WRITE_DATA_SIZE;
+ const uint32_t read_len = WRITE_ONCE_DATA_SIZE;
+ const uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = WRITE_ONCE_READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Set a write once UID a second time */
+ status = psa_ps_set(uid, write_len, write_data, flags);
+ if (status != PSA_ERROR_NOT_PERMITTED) {
+ TEST_FAIL("Set should not rewrite a write once UID");
+ return;
+ }
+
+ /* Get write once data */
+ status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Check that write once data has not changed */
+ if (memcmp(read_data, WRITE_ONCE_RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Write once data should not have changed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Valid data, zero offset
+ * - Valid data, non-zero offset
+ */
+TFM_PS_NS_TEST(1005, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ uint32_t data_len = WRITE_DATA_SIZE;
+ uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ const uint8_t *p_read_data = read_data;
+
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get the entire data */
+ status = psa_ps_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Check that the data is correct, including no illegal pre- or post-data */
+ if (memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to result data");
+ return;
+ }
+
+ /* Reset read data */
+ memcpy(read_data, READ_DATA, sizeof(read_data));
+
+ /* Read from offset 2 to 2 bytes before end of the data */
+ offset = 2;
+ data_len -= offset + 2;
+
+ status = psa_ps_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Check that the correct data was read */
+ if (memcmp(p_read_data, "____", HALF_PADDING_SIZE) != 0) {
+ TEST_FAIL("Read data contains illegal pre-data");
+ return;
+ }
+
+ p_read_data += HALF_PADDING_SIZE;
+
+ if (memcmp(p_read_data, write_data + offset, data_len) != 0) {
+ TEST_FAIL("Read data incorrect");
+ return;
+ }
+
+ p_read_data += data_len;
+
+ if (memcmp(p_read_data, "____", HALF_PADDING_SIZE) != 0) {
+ TEST_FAIL("Read data contains illegal post-data");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Zero data length, zero offset
+ * - Zero data length, non-zero offset
+ */
+TFM_PS_NS_TEST(1006, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t write_len = WRITE_DATA_SIZE;
+ const uint32_t read_len = 0;
+ uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ status = psa_ps_set(uid, write_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get zero data from zero offset */
+ status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail with zero data len");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+ offset = 5;
+
+ /* Get zero data from non-zero offset */
+ status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Unset UID
+ * - Invalid UID
+ */
+TFM_PS_NS_TEST(1007, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const uint32_t data_len = 1;
+ const uint32_t offset = 0;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Get with UID that has not yet been set */
+ status = psa_ps_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get succeeded with non-existant UID");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data not equal to original read data");
+ return;
+ }
+
+ /* Get with invalid UID */
+ status = psa_ps_get(INVALID_UID, offset, data_len,
+ read_data + HALF_PADDING_SIZE, &read_data_len);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Get succeeded with invalid UID");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data not equal to original read data");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Offset greater than UID length
+ * - Data length greater than UID length
+ * - Data length + offset greater than UID length
+ */
+TFM_PS_NS_TEST(1008, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t write_len = WRITE_DATA_SIZE;
+ uint32_t read_len;
+ uint32_t offset;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ status = psa_ps_set(uid, write_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get with offset greater than UID's length */
+ read_len = 1;
+ offset = write_len + 1;
+
+ status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Get should not succeed with offset too large");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+ /* Get with data length greater than UID's length */
+ read_len = write_len + 1;
+ offset = 0;
+
+ status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should succeed with data length larger than UID's "
+ "length");
+ return;
+ }
+
+ if (read_data_len != write_len) {
+ TEST_FAIL("Read data length should be equal to UID's length");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+ /* Get with offset + data length greater than UID's length, but individually
+ * valid
+ */
+ read_len = write_len;
+ offset = 1;
+
+ /* Reset read_data to original READ_DATA */
+ memcpy(read_data, READ_DATA, sizeof(read_data));
+
+ status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should succeed with offset + data length too large, "
+ "but individually valid");
+ return;
+ }
+
+ if (read_data_len != write_len - offset) {
+ TEST_FAIL("Read data length should be equal to the UID's remaining "
+ "size starting from offset");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (memcmp(read_data, OFFSET_RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - NULL data pointer and zero data length
+ *
+ * \note A request with a null data pointer and data length not equal to zero is
+ * treated as a secure violation. TF-M framework will reject such requests
+ * and not return to the NSPE so this case is not tested here.
+ *
+ */
+TFM_PS_NS_TEST(1009, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ size_t read_data_length = 0;
+
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get with NULL data pointer */
+ status = psa_ps_get(uid, offset, 0, NULL, &read_data_length);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should succeed with NULL data pointer and zero length");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - Write once UID
+ */
+TFM_PS_NS_TEST(1010, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = WRITE_ONCE_UID;
+ struct psa_storage_info_t info = {0};
+
+ /* Get info for write once UID */
+ status = psa_ps_get_info(uid, &info);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get info should not fail for write once UID");
+ return;
+ }
+
+ /* Check that the info struct contains the correct values */
+ if (info.size != WRITE_ONCE_DATA_SIZE) {
+ TEST_FAIL("Size incorrect for write once UID");
+ return;
+ }
+
+ if (info.flags != PSA_STORAGE_FLAG_WRITE_ONCE) {
+ TEST_FAIL("Flags incorrect for write once UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - Valid UID
+ */
+TFM_PS_NS_TEST(1011, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ struct psa_storage_info_t info = {0};
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get info for valid UID */
+ status = psa_ps_get_info(uid, &info);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get info should not fail with valid UID");
+ return;
+ }
+
+ /* Check that the info struct contains the correct values */
+ if (info.size != data_len) {
+ TEST_FAIL("Size incorrect for valid UID");
+ return;
+ }
+
+ if (info.flags != flags) {
+ TEST_FAIL("Flags incorrect for valid UID");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - Unset UID
+ * - Invalid UID
+ */
+TFM_PS_NS_TEST(1012, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ struct psa_storage_info_t info = {0};
+
+ /* Get info with UID that has not yet been set */
+ status = psa_ps_get_info(uid, &info);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get info should not succeed with unset UID");
+ return;
+ }
+
+ /* Check that the info struct has not been modified */
+ if (info.size != 0) {
+ TEST_FAIL("Size should not have changed");
+ return;
+ }
+
+ if (info.flags != 0) {
+ TEST_FAIL("Flags should not have changed");
+ return;
+ }
+
+ /* Get info with invalid UID */
+ status = psa_ps_get_info(INVALID_UID, &info);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Get info should not succeed with invalid UID");
+ return;
+ }
+
+ /* Check that the info struct has not been modified */
+ if (info.size != 0) {
+ TEST_FAIL("Size should not have changed");
+ return;
+ }
+
+ if (info.flags != 0) {
+ TEST_FAIL("Flags should not have changed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests remove function with:
+ * - Valid UID
+ */
+TFM_PS_NS_TEST(1013, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ struct psa_storage_info_t info = {0};
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Call remove with valid ID */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ /* Check that get info fails for removed UID */
+ status = psa_ps_get_info(uid, &info);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get info should not succeed with removed UID");
+ return;
+ }
+
+ /* Check that get fails for removed UID */
+ status = psa_ps_get(uid, offset, data_len, read_data, &read_data_len);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get should not succeed with removed UID");
+ return;
+ }
+
+ /* Check that remove fails for removed UID */
+ status = psa_ps_remove(uid);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Remove should not succeed with removed UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests remove function with:
+ * - Write once UID
+ */
+TFM_PS_NS_TEST(1014, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = WRITE_ONCE_UID;
+
+ /* Call remove with write once UID */
+ status = psa_ps_remove(uid);
+ if (status != PSA_ERROR_NOT_PERMITTED) {
+ TEST_FAIL("Remove should not succeed with write once UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests remove function with:
+ * - Invalid UID
+ */
+TFM_PS_NS_TEST(1015, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = INVALID_UID;
+
+ /* Call remove with an invalid UID */
+ status = psa_ps_remove(uid);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Remove should not succeed with invalid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+/**
+ * \brief Sets UID with a valid thread name.
+ */
+static void tfm_ps_test_1016_task_1(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid thread name");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Calls get with an invalid thread name.
+ */
+static void tfm_ps_test_1016_task_2(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ status = psa_ps_get(uid, offset, data_len, read_data, &read_data_len);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get should not succeed with invalid thread name");
+ return;
+ }
+
+ /* Check that read data has not been modified */
+ if (memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should not have changed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Removes UID with a valid thread name to clean up storage.
+ */
+static void tfm_ps_test_1016_task_3(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid thread name");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with an invalid thread name.
+ */
+static void tfm_ps_test_1016(struct test_result_t *ret)
+{
+ tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1016_task_1);
+ if (ret->val != TEST_PASSED) {
+ return;
+ }
+
+ tfm_ps_run_test(INVALID_THREAD_NAME, ret, tfm_ps_test_1016_task_2);
+ if (ret->val != TEST_PASSED) {
+ return;
+ }
+
+ tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1016_task_3);
+}
+
+/**
+ * \brief Sets UID with a valid thread name.
+ */
+static void tfm_ps_test_1017_task_1(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid thread name");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Calls get info with an invalid thread name.
+ */
+static void tfm_ps_test_1017_task_2(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ struct psa_storage_info_t info = {0};
+
+ status = psa_ps_get_info(uid, &info);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get info should not succeed with invalid thread name");
+ return;
+ }
+
+ /* Check that info has not been modified */
+ if (info.size != 0 || info.flags != 0) {
+ TEST_FAIL("Info should not have changed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Removes UID with a valid thread name to clean up storage.
+ */
+static void tfm_ps_test_1017_task_3(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid thread name");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with an invalid thread name.
+ */
+static void tfm_ps_test_1017(struct test_result_t *ret)
+{
+ tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1017_task_1);
+ if (ret->val != TEST_PASSED) {
+ return;
+ }
+
+ tfm_ps_run_test(INVALID_THREAD_NAME, ret, tfm_ps_test_1017_task_2);
+ if (ret->val != TEST_PASSED) {
+ return;
+ }
+
+ tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1017_task_3);
+}
+
+/**
+ * \brief Sets UID with a valid thread name.
+ */
+static void tfm_ps_test_1018_task_1(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid thread name");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Calls remove with an invalid thread name.
+ */
+static void tfm_ps_test_1018_task_2(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+
+ status = psa_ps_remove(uid);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Remove should not succeed with invalid thread name");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Removes UID with a valid thread name to clean up storage.
+ */
+static void tfm_ps_test_1018_task_3(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid thread name");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests remove function with an invalid thread name.
+ */
+static void tfm_ps_test_1018(struct test_result_t *ret)
+{
+ tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1018_task_1);
+ if (ret->val != TEST_PASSED) {
+ return;
+ }
+
+ tfm_ps_run_test(INVALID_THREAD_NAME, ret, tfm_ps_test_1018_task_2);
+ if (ret->val != TEST_PASSED) {
+ return;
+ }
+
+ tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1018_task_3);
+}
+
+/**
+ * \brief Sets UID with first thread.
+ */
+static void tfm_ps_test_1019_task_1(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint8_t write_data[] = "Thread A data";
+
+ status = psa_ps_set(uid, sizeof(write_data), write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Attempts to access same UID from second thread.
+ */
+static void tfm_ps_test_1019_task_2(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ struct psa_storage_info_t info = {0};
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Attempt to access the other thread's UID */
+ status = psa_ps_get(uid, offset, data_len, read_data, &read_data_len);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get should not find another thread's UID");
+ return;
+ }
+
+ /* Check that read data has not been modified */
+ if (memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should not have changed");
+ return;
+ }
+
+ status = psa_ps_get_info(uid, &info);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get info should not find another thread's UID");
+ return;
+ }
+
+ /* Check that info has not been modified */
+ if (info.size != 0 || info.flags != 0) {
+ TEST_FAIL("Info should not have changed");
+ return;
+ }
+
+ status = psa_ps_remove(uid);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Remove should not find another thread's UID");
+ return;
+ }
+
+ /* Create the same UID, but belonging to this thread */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ status = psa_ps_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail with valid UID");
+ return;
+ }
+
+ /* Check that the data read belongs to this thread, not the other one */
+ if (memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to result data");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Checks that first thread's UID has not been modified.
+ */
+static void tfm_ps_test_1019_task_3(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ struct psa_storage_info_t info = {0};
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t offset = 0;
+ const uint8_t write_data[] = "Thread A data";
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ const uint32_t data_len = sizeof(write_data);
+
+ /* Check that first thread can still get info for UID */
+ status = psa_ps_get_info(uid, &info);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get info should not fail with valid UID");
+ return;
+ }
+
+ /* Check that first thread's UID info has not been modified */
+ if (info.size != data_len || info.flags != flags) {
+ TEST_FAIL("Info should be equal to original info");
+ return;
+ }
+
+ /* Check that first thread can still get UID */
+ status = psa_ps_get(uid, offset, data_len, read_data, &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail with valid UID");
+ return;
+ }
+
+ /* Check that first thread's UID data has not been modified */
+ if (memcmp(read_data, write_data, data_len) != 0) {
+ TEST_FAIL("Read data should be equal to original write data");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests attempting to access UID belonging to another thread.
+ */
+static void tfm_ps_test_1019(struct test_result_t *ret)
+{
+ tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1019_task_1);
+ if (ret->val != TEST_PASSED) {
+ return;
+ }
+
+ tfm_ps_run_test("Thread_B", ret, tfm_ps_test_1019_task_2);
+ if (ret->val != TEST_PASSED) {
+ return;
+ }
+
+ tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1019_task_3);
+}
+
+/**
+ * \brief Sets TEST_UID_1 from Thread_A.
+ */
+static void tfm_ps_test_1020_task_1(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint8_t write_data[] = "A";
+
+ status = psa_ps_set(uid, sizeof(write_data), write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should succeed for Thread_A");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Sets TEST_UID_1 from Thread_B.
+ */
+static void tfm_ps_test_1020_task_2(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint8_t write_data[] = "B";
+
+ status = psa_ps_set(uid, sizeof(write_data), write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should succeed for Thread_B");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Sets TEST_UID_1 again from Thread_A.
+ */
+static void tfm_ps_test_1020_task_3(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint8_t write_data[] = "HELLO";
+
+ status = psa_ps_set(uid, sizeof(write_data), write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Second set should succeed for Thread_A");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Sets TEST_UID_1 again from Thread_B.
+ */
+static void tfm_ps_test_1020_task_4(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint8_t write_data[] = "WORLD_1234";
+
+ status = psa_ps_set(uid, sizeof(write_data), write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Second set should succeed for Thread_B");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Gets TEST_UID_1 from Thread_A.
+ */
+static void tfm_ps_test_1020_task_5(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const uint32_t offset = 0;
+ const uint8_t write_data[] = "HELLO";
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ status = psa_ps_get(uid, offset, sizeof(write_data), read_data,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should succeed for Thread_A");
+ return;
+ }
+
+ /* Check that UID contains Thread_A's data */
+ if (memcmp(read_data, write_data, sizeof(write_data)) != 0) {
+ TEST_FAIL("Read data incorrect for Thread_A");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Gets TEST_UID_1 from Thread_B.
+ */
+static void tfm_ps_test_1020_task_6(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const uint32_t offset = 0;
+ const uint8_t write_data[] = "WORLD_1234";
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ status = psa_ps_get(uid, offset, sizeof(write_data), read_data,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should succeed for Thread_B");
+ return;
+ }
+
+ /* Check that UID contains Thread_B's data */
+ if (memcmp(read_data, write_data, sizeof(write_data)) != 0) {
+ TEST_FAIL("Read data incorrect for Thread_B");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should work form Thread_B");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Calls removes from Thread_B to clean up storage for the next test.
+ */
+static void tfm_ps_test_1020_task_7(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should work form Thread_B");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests writing data to a UID alternately from two threads before
+ * read-back.
+ */
+static void tfm_ps_test_1020(struct test_result_t *ret)
+{
+ tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1020_task_1);
+ if (ret->val != TEST_PASSED) {
+ return;
+ }
+
+ tfm_ps_run_test("Thread_B", ret, tfm_ps_test_1020_task_2);
+ if (ret->val != TEST_PASSED) {
+ return;
+ }
+
+ tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1020_task_3);
+ if (ret->val != TEST_PASSED) {
+ return;
+ }
+
+ tfm_ps_run_test("Thread_B", ret, tfm_ps_test_1020_task_4);
+ if (ret->val != TEST_PASSED) {
+ return;
+ }
+
+ tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1020_task_5);
+ if (ret->val != TEST_PASSED) {
+ return;
+ }
+
+ tfm_ps_run_test("Thread_B", ret, tfm_ps_test_1020_task_6);
+ if (ret->val != TEST_PASSED) {
+ return;
+ }
+
+ tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1020_task_7);
+}
+#endif /* TFM_NS_CLIENT_IDENTIFICATION */
+
+/**
+ * \brief Tests data block compact feature.
+ * Set UID 1 to locate it at the beginning of the block. Then set UID 2
+ * to be located after UID 1 and remove UID 1. UID 2 will be compacted to
+ * the beginning of the block. This test verifies that the compaction
+ * works correctly by reading back UID 2.
+ */
+TFM_PS_NS_TEST(1021, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid_1 = TEST_UID_2;
+ const psa_storage_uid_t uid_2 = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len_2 = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ const uint8_t write_data_1[] = "UID 1 DATA";
+ const uint8_t write_data_2[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Set UID 1 */
+ status = psa_ps_set(uid_1, sizeof(write_data_1), write_data_1, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail for UID 1");
+ return;
+ }
+
+ /* Set UID 2 */
+ status = psa_ps_set(uid_2, data_len_2, write_data_2, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail for UID 2");
+ return;
+ }
+
+ /* Remove UID 1. This should cause UID 2 to be compacted to the beginning of
+ * the block.
+ */
+ status = psa_ps_remove(uid_1);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail for UID 1");
+ return;
+ }
+
+ /* If the compact worked as expected, the test should be able to read back
+ * the data from UID 2 correctly.
+ */
+ status = psa_ps_get(uid_2, offset, data_len_2,
+ read_data + HALF_PADDING_SIZE, &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail for UID 2");
+ return;
+ }
+
+ if (memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read buffer has incorrect data");
+ return;
+ }
+
+ /* Remove UID 2 to clean up storage for the next test */
+ status = psa_ps_remove(uid_2);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail for UID 2");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set and multiple partial gets.
+ */
+TFM_PS_NS_TEST(1022, "Thread_A")
+{
+ psa_status_t status;
+ uint32_t i;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Set the entire data into UID */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get the data from UID one byte at a time */
+ for (i = 0; i < data_len; ++i) {
+ status = psa_ps_get(uid, offset, 1,
+ (read_data + HALF_PADDING_SIZE + i),
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail for partial read");
+ return;
+ }
+
+ ++offset;
+ }
+
+ if (memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read buffer has incorrect data");
+ return;
+ }
+
+ /* Remove UID to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests multiple sets to the same UID.
+ */
+TFM_PS_NS_TEST(1023, "Thread_A")
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t offset = 0;
+ const uint8_t write_data_1[] = "ONE";
+ const uint8_t write_data_2[] = "TWO";
+ const uint8_t write_data_3[] = "THREE";
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Set write data 1 into UID */
+ status = psa_ps_set(uid, sizeof(write_data_1), write_data_1, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("First set should not fail");
+ return;
+ }
+
+ /* Set write data 2 into UID */
+ status = psa_ps_set(uid, sizeof(write_data_2), write_data_2, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Second set should not fail");
+ return;
+ }
+
+ /* Set write data 3 into UID */
+ status = psa_ps_set(uid, sizeof(write_data_3), write_data_3, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Third set should not fail");
+ return;
+ }
+
+ status = psa_ps_get(uid, offset, sizeof(write_data_3), read_data,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Check that get returns the last data to be set */
+ if (memcmp(read_data, write_data_3, sizeof(write_data_3)) != 0) {
+ TEST_FAIL("Read buffer has incorrect data");
+ return;
+ }
+
+ /* Remove UID to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get support function.
+ */
+TFM_PS_NS_TEST(1024, "Thread_A")
+{
+ uint32_t support_flags;
+
+ support_flags = psa_ps_get_support();
+ if (support_flags != 0) {
+ TEST_FAIL("Support flags should be 0");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set, get_info, get and remove function with:
+ * - Valid UID's
+ * - Data length of different asset sizes
+ * - No flags
+ */
+TFM_PS_NS_TEST(1025, "Thread_A")
+{
+ uint8_t cycle;
+ psa_status_t status;
+ const psa_storage_uid_t test_uid[TEST_1025_CYCLES] = {
+ TEST_UID_1,
+ TEST_UID_2,
+ TEST_UID_3};
+ const uint32_t test_asset_sizes[TEST_1025_CYCLES] = {
+ PS_MAX_ASSET_SIZE >> 2,
+ PS_MAX_ASSET_SIZE >> 1,
+ PS_MAX_ASSET_SIZE};
+
+ /* Loop to test different asset sizes and UID's*/
+ for (cycle = 0; cycle < TEST_1025_CYCLES; cycle++) {
+ uint32_t data_size = test_asset_sizes[cycle];
+ psa_storage_uid_t uid = test_uid[cycle];
+ struct psa_storage_info_t info = {0};
+
+ memset(read_asset_data, 0x00, sizeof(read_asset_data));
+
+ /* Set with data and no flags and a valid UID */
+ status = psa_ps_set(uid,
+ data_size,
+ write_asset_data,
+ PSA_STORAGE_FLAG_NONE);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Get info for valid UID */
+ status = psa_ps_get_info(uid, &info);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get info should not fail with valid UID");
+ return;
+ }
+
+ /* Check that the info struct contains the correct values */
+ if (info.size != data_size) {
+ TEST_FAIL("Size incorrect for valid UID");
+ return;
+ }
+
+ if (info.flags != PSA_STORAGE_FLAG_NONE) {
+ TEST_FAIL("Flags incorrect for valid UID");
+ return;
+ }
+
+ /* Check that thread can still get UID */
+ status = psa_ps_get(uid, 0, data_size, read_asset_data,
+ &read_asset_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail with valid UID");
+ return;
+ }
+
+ /* Check that thread's UID data has not been modified */
+ if (memcmp(read_asset_data, write_asset_data, data_size) != 0) {
+ TEST_FAIL("Read data should be equal to original write data");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+ }
+
+ ret->val = TEST_PASSED;
+}
diff --git a/test/suites/ps/secure/nv_counters/test_ps_nv_counters.c b/test/suites/ps/secure/nv_counters/test_ps_nv_counters.c
new file mode 100644
index 0000000..a2a86b3
--- /dev/null
+++ b/test/suites/ps/secure/nv_counters/test_ps_nv_counters.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test_ps_nv_counters.h"
+
+#include <limits.h>
+#include "secure_fw/partitions/protected_storage/nv_counters/ps_nv_counters.h"
+#include "secure_fw/partitions/protected_storage/ps_utils.h"
+
+#define DISABLE_INCREMENT 0
+#define ENABLE_INCREMENT 1
+
+#define TOTAL_PS_NV_COUNTERS 3
+#define INIT_NV_COUNTERS_VALUE 42
+
+static uint8_t nv_increment_status = ENABLE_INCREMENT;
+static uint32_t test_nv_counters[TOTAL_PS_NV_COUNTERS] = {
+ [0] = INIT_NV_COUNTERS_VALUE,
+ [1] = INIT_NV_COUNTERS_VALUE,
+ [2] = INIT_NV_COUNTERS_VALUE
+ };
+
+static uint32_t get_nv_counter_position(enum tfm_nv_counter_t counter_id)
+{
+ switch (counter_id) {
+ case TFM_PS_NV_COUNTER_1:
+ return 0;
+ case TFM_PS_NV_COUNTER_2:
+ return 1;
+ case TFM_PS_NV_COUNTER_3:
+ return 2;
+ default:
+ return TOTAL_PS_NV_COUNTERS;
+ }
+}
+
+psa_status_t ps_read_nv_counter(enum tfm_nv_counter_t counter_id,
+ uint32_t *val)
+{
+ uint32_t nv_pos;
+
+ nv_pos = get_nv_counter_position(counter_id);
+ if (nv_pos >= TOTAL_PS_NV_COUNTERS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Reads counter value */
+ *val = test_nv_counters[nv_pos];
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t ps_increment_nv_counter(enum tfm_nv_counter_t counter_id)
+{
+ uint32_t nv_pos;
+
+ if (nv_increment_status == DISABLE_INCREMENT) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ nv_pos = get_nv_counter_position(counter_id);
+ if (nv_pos >= TOTAL_PS_NV_COUNTERS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ if (test_nv_counters[nv_pos] == UINT32_MAX) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Increments counter value */
+ test_nv_counters[nv_pos]++;
+
+ return PSA_SUCCESS;
+}
+
+/* Implementation of PS NV counter interfaces defined by
+ * test_ps_nv_counters.h
+ */
+void test_ps_disable_increment_nv_counter(void)
+{
+ nv_increment_status = DISABLE_INCREMENT;
+}
+
+void test_ps_enable_increment_nv_counter(void)
+{
+ nv_increment_status = ENABLE_INCREMENT;
+}
+
+psa_status_t test_ps_read_nv_counter(enum tfm_nv_counter_t counter_id,
+ uint32_t *val)
+{
+ return ps_read_nv_counter(counter_id, val);
+}
+
+psa_status_t test_ps_increment_nv_counter(enum tfm_nv_counter_t counter_id)
+{
+ return ps_increment_nv_counter(counter_id);
+}
+
+psa_status_t test_ps_decrement_nv_counter(enum tfm_nv_counter_t counter_id)
+{
+ uint32_t nv_pos;
+
+ nv_pos = get_nv_counter_position(counter_id);
+ if (nv_pos >= TOTAL_PS_NV_COUNTERS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ if (test_nv_counters[nv_pos] == 0) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Decrements counter value */
+ test_nv_counters[nv_pos]--;
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t test_ps_set_nv_counter(enum tfm_nv_counter_t counter_id,
+ uint32_t value)
+{
+ uint32_t nv_pos;
+
+ nv_pos = get_nv_counter_position(counter_id);
+ if (nv_pos >= TOTAL_PS_NV_COUNTERS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Sets counter value */
+ test_nv_counters[nv_pos] = value;
+
+ return PSA_SUCCESS;
+}
diff --git a/test/suites/ps/secure/nv_counters/test_ps_nv_counters.h b/test/suites/ps/secure/nv_counters/test_ps_nv_counters.h
new file mode 100644
index 0000000..2f3b332
--- /dev/null
+++ b/test/suites/ps/secure/nv_counters/test_ps_nv_counters.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TEST_PS_NV_COUNTERS_H__
+#define __TEST_PS_NV_COUNTERS_H__
+
+#include <stdint.h>
+#include "psa/protected_storage.h"
+#include "platform/include/tfm_plat_nv_counters.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Reads the given non-volatile (NV) counter.
+ *
+ * \param[in] counter_id NV counter ID.
+ * \param[out] val Pointer to store the current NV counter value.
+ *
+ * \return PSA_SUCCESS if the value is read correctly, otherwise
+ * PSA_ERROR_GENERIC_ERROR
+ */
+psa_status_t test_ps_read_nv_counter(enum tfm_nv_counter_t counter_id,
+ uint32_t *val);
+
+/**
+ * \brief Increments the given non-volatile (NV) counter.
+ *
+ * \param[in] counter_id NV counter ID.
+ *
+ * \return When the NV counter reaches its maximum value, the
+ * PSA_ERROR_GENERIC_ERROR error is returned to indicate the
+ * value cannot be incremented. Otherwise, PSA_SUCCESS.
+ */
+psa_status_t test_ps_increment_nv_counter(enum tfm_nv_counter_t counter_id);
+
+/**
+ * \brief Decrements the given non-volatile (NV) counter.
+ *
+ * \param[in] counter_id NV counter ID.
+ *
+ * \return When the NV counter reaches its minimum value, the
+ * PSA_ERROR_GENERIC_ERROR error is returned to indicate the
+ * value cannot be decremented. Otherwise, PSA_SUCCESS.
+ */
+psa_status_t test_ps_decrement_nv_counter(enum tfm_nv_counter_t counter_id);
+
+/**
+ * \brief Disables PS increment nv counter function to force
+ * PSA_ERROR_GENERIC_ERROR return value as an indication that NV
+ * counter reaches its maximum value.
+ */
+void test_ps_disable_increment_nv_counter(void);
+
+/**
+ * \brief Enables PS increment nv counter function to work normally.
+ */
+void test_ps_enable_increment_nv_counter(void);
+
+/**
+ * \brief Sets a new value into the given non-volatile (NV) counter.
+ *
+ * \param[in] counter_id NV counter Id.
+ * \param[in] value New NV counter value.
+ *
+ * \return When the NV counter reaches its maximum value, the
+ * PSA_ERROR_GENERIC_ERROR error is returned to indicate the
+ * value cannot be set. Otherwise, PSA_SUCCESS.
+ */
+psa_status_t test_ps_set_nv_counter(enum tfm_nv_counter_t counter_id,
+ uint32_t value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TEST_PS_NV_COUNTERS_H__ */
diff --git a/test/suites/ps/secure/ps_rollback_protection_testsuite.c b/test/suites/ps/secure/ps_rollback_protection_testsuite.c
new file mode 100644
index 0000000..d8c1bd9
--- /dev/null
+++ b/test/suites/ps/secure/ps_rollback_protection_testsuite.c
@@ -0,0 +1,819 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "ps_tests.h"
+
+#include <stdio.h>
+
+#include "secure_fw/partitions/protected_storage/nv_counters/ps_nv_counters.h"
+#include "nv_counters/test_ps_nv_counters.h"
+#include "psa/protected_storage.h"
+#include "tfm_memory_utils.h"
+#include "s_test_helpers.h"
+
+/* This include is required to expose the ps_system_prepare function, via the
+ * tfm_ps_test_system_prepare API, to simulate a reboot in the system.
+ * ps_system_prepare is called when the PS service is initialized.
+ */
+#include "test/test_services/tfm_ps_test_service/tfm_ps_test_service_api.h"
+
+#include "test/framework/test_framework_helpers.h"
+
+/* Test UIDs */
+#define TEST_UID 2UL /* UID 1 cannot be used as it references a write once
+ * asset, created in psa_ps_s_interface_testsuite.c
+ */
+
+/* Write data */
+#define WRITE_DATA "THE_FIVE_BOXING_WIZARDS_JUMP_QUICKLY"
+#define WRITE_DATA_SIZE (sizeof(WRITE_DATA) - 1)
+#define READ_DATA "############################################"
+#define RESULT_DATA ("####" WRITE_DATA "####")
+
+/*
+ * Summary of tests covered by the test suite.
+ *
+ * PS version | NVC1 | NVC2 | NVC3 | Result | Test Num
+ * ------------|------|------|------|----------|------------
+ * X | X | X | X | Valid | 1
+ * N | X | X | X | Invalid | 2
+ * X | X | X | N | Valid | 3
+ * N | X | N | N | Valid | 4
+ * X | X | N | N | Valid | 5
+ * X | X | M | N | Valid | 6
+ * M | X | M | N | Invalid | 7
+ * N | X | M | N | Invalid | 8
+ *
+ * Test 9 checks the PS result when the non-volatile (NV) counter 1 cannot be
+ * incremented (e.g it has reached its maximum value).
+ */
+
+/* List of tests */
+static void tfm_ps_test_4001(struct test_result_t *ret);
+static void tfm_ps_test_4002(struct test_result_t *ret);
+static void tfm_ps_test_4003(struct test_result_t *ret);
+static void tfm_ps_test_4004(struct test_result_t *ret);
+static void tfm_ps_test_4005(struct test_result_t *ret);
+static void tfm_ps_test_4006(struct test_result_t *ret);
+static void tfm_ps_test_4007(struct test_result_t *ret);
+static void tfm_ps_test_4008(struct test_result_t *ret);
+static void tfm_ps_test_4009(struct test_result_t *ret);
+
+static struct test_t interface_tests[] = {
+ {&tfm_ps_test_4001, "TFM_PS_TEST_4001",
+ "Check PS area version when NV counters 1/2/3 have the same value", {TEST_PASSED}},
+ {&tfm_ps_test_4002, "TFM_PS_TEST_4002",
+ "Check PS area version when it is different from NV counters 1/2/3", {TEST_PASSED}},
+ {&tfm_ps_test_4003, "TFM_PS_TEST_4003",
+ "Check PS area version when NV counters 1 and 2 are equals, 3 is "
+ "different, and PS area version match NV counters 1 and 2", {TEST_PASSED}},
+ {&tfm_ps_test_4004, "TFM_PS_TEST_4004",
+ "Check PS area version when NV counters 2 and 3 are equals, 1 is "
+ "different and PS area version match NV counter 2 and 3", {TEST_PASSED}},
+ {&tfm_ps_test_4005, "TFM_PS_TEST_4005",
+ "Check PS area version when NV counters 2 and 3 are equals, 1 is "
+ "different and PS area version match NV counter 1", {TEST_PASSED}},
+ {&tfm_ps_test_4006, "TFM_PS_TEST_4006",
+ "Check PS area version when NV counters 1, 2 and 3 have different values "
+ "and PS area version match NV counter 1 value", {TEST_PASSED}},
+ {&tfm_ps_test_4007, "TFM_PS_TEST_4007",
+ "Check PS area version when NV counters 1, 2 and 3 have different values "
+ "and PS area version match NV counter 2 value", {TEST_PASSED}},
+ {&tfm_ps_test_4008, "TFM_PS_TEST_4008",
+ "Check PS area version when NV counters 1, 2 and 3 have different values "
+ "and PS area version match NV counter 3 value", {TEST_PASSED}},
+ {&tfm_ps_test_4009, "TFM_PS_TEST_4009",
+ "Check PS area version when NV counter 1 cannot be incremented", {TEST_PASSED}},
+};
+
+void register_testsuite_s_rollback_protection(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size = (sizeof(interface_tests) / sizeof(interface_tests[0]));
+
+ set_testsuite("PS rollback protection tests (TFM_PS_TEST_4XXX)",
+ interface_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Check PS area version when NV counters 1/2/3 have the same value.
+ * It also checks that the 3 NV counters are aligned and they have been
+ * increased by 1 unit.
+ */
+static void tfm_ps_test_4001(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ uint32_t old_nvc_1, nvc_1, nvc_2, nvc_3;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Creates an asset in the PS area to generate a new PS area version */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Reads NV counter 1 to get the saved value to compare it later */
+ status = test_ps_read_nv_counter(TFM_PS_NV_COUNTER_1, &old_nvc_1);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Read should not fail");
+ return;
+ }
+
+ /* Sets new data in the asset to generate a new PS area version */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Validates the 3 NV counters have the same value and it has been increased
+ * by 1 unit.
+ */
+
+ /* Reads NV counter 1 to get the current value */
+ status = test_ps_read_nv_counter(TFM_PS_NV_COUNTER_1, &nvc_1);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Read should not fail");
+ return;
+ }
+
+ /* Checks if NV counter 1 value has been increased by 1 unit as result of
+ * process the write request.
+ */
+ if (nvc_1 != (old_nvc_1 + 1)) {
+ TEST_FAIL("NV counter 1 has been increased more than 1 unit");
+ return;
+ }
+
+ /* Reads NV counter 2 to get the current value */
+ status = test_ps_read_nv_counter(TFM_PS_NV_COUNTER_2, &nvc_2);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Read should not fail");
+ return;
+ }
+
+ if (nvc_1 != nvc_2) {
+ TEST_FAIL("NV counter 1 and 2 should have the same value");
+ return;
+ }
+
+ /* Reads NV counter 3 to get the current value */
+ status = test_ps_read_nv_counter(TFM_PS_NV_COUNTER_3, &nvc_3);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Read should not fail");
+ return;
+ }
+
+ if (nvc_2 != nvc_3) {
+ TEST_FAIL("NV counter 2 and 3 should have the same value");
+ return;
+ }
+
+ /* Simulates a reboot in the system by calling ps_system_prepare(). This
+ * function is called when the PS service is initialized.
+ *
+ * Prepare should not fail as the NV counters has the same values and
+ * the PS area authentication is aligned with those values.
+ */
+ status = tfm_ps_test_system_prepare();
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("AM prepare should not fail");
+ return;
+ }
+
+ /* Gets data from the asset */
+ status = psa_ps_get(uid, offset, data_len, (read_data + HALF_PADDING_SIZE),
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Checks that the data has not changed */
+ if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("The data should not have changed");
+ return;
+ }
+
+ /* Removes the asset to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when it is different from NV counters
+ * 1/2/3.
+ */
+static void tfm_ps_test_4002(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ /* Creates an asset in the PS area to generate a new PS area version */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Increments all counters to make that PS area version old/invalid */
+ status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_1);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Increment should not fail");
+ return;
+ }
+
+ status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_2);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Increment should not fail");
+ return;
+ }
+
+ status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_3);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Increment should not fail");
+ return;
+ }
+
+ /* Simulates a reboot in the system by calling ps_system_prepare(). This
+ * function is called when the PS service is initialized.
+ *
+ * Prepare should fail as the PS area version does not match the
+ * NV counters values.
+ */
+ status = tfm_ps_test_system_prepare();
+ if (status != PSA_ERROR_GENERIC_ERROR) {
+ TEST_FAIL("PS system prepare should fail as version is old");
+ return;
+ }
+
+ /* Removes the asset to clean up storage for the next test.
+ *
+ * To be able to remove the asset, the PS area version should match
+ * with the counter values. So, it is required to:
+ *
+ * 1. align the counters with the PS area version
+ * 2. re-call ps_system_prepare to mark the PS area as a valid image
+ * 3. remove the asset.
+ */
+
+ /* Aligns NV counters with the PS area version */
+ status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_1);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Decrement should not fail");
+ return;
+ }
+
+ status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_2);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Decrement should not fail");
+ return;
+ }
+
+ status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_3);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Decrement should not fail");
+ return;
+ }
+
+ /* Calls ps_system_prepare to mark the PS area as a valid image */
+ status = tfm_ps_test_system_prepare();
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("PS system prepare should not fail");
+ return;
+ }
+
+ /* Removes the asset to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when NV counters 1 and 2 are equals, 3 is
+ * different and PS area version match NV counter 1 and 2 values.
+ * It simulates a power cut during write action while the counter 3 is
+ * being increased.
+ */
+static void tfm_ps_test_4003(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Creates an asset in the PS area to generate a new PS area version */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Decrements NV counters 3 to make it different from the other two counters
+ * and make the current PS area version match NV counter 1 and 2 values.
+ */
+ status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_3);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Decrement should not fail");
+ return;
+ }
+
+ /* Simulates a reboot in the system by calling ps_system_prepare(). This
+ * function is called when the PS service is initialized.
+ *
+ * Prepare should not fail as the PS area version match NV counters 1 and
+ * 2 values.
+ */
+ status = tfm_ps_test_system_prepare();
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("PS system prepare should not fail");
+ return;
+ }
+
+ /* Gets the data from the asset */
+ status = psa_ps_get(uid, offset, data_len, (read_data + HALF_PADDING_SIZE),
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Checks that the data has not changed */
+ if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("The data should not have changed");
+ return;
+ }
+
+ /* Removes the asset to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when NV counters 2 and 3 are equals, 1 is
+ * different and PS area version match NV counter 2 and 3 values.
+ * It simulates a power cut during write action before increment counter
+ * 2 and 3, and the new PS area version is corrupted and only the old
+ * version match the NV counters.
+ */
+static void tfm_ps_test_4004(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Creates an asset in the PS area to generate a new PS area version */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Increments NV counters 1 to make it different from the other two counters
+ * and make the current PS area version match NV counter 2 and 3 values.
+ */
+ status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_1);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Increment should not fail");
+ return;
+ }
+
+ /* Simulates a reboot in the system by calling ps_system_prepare(). This
+ * function is called when the PS service is initialized.
+ *
+ * Prepare should not fail as the PS area version match the NV counter 2
+ * and 3 values.
+ */
+ status = tfm_ps_test_system_prepare();
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("PS system prepare should not fail");
+ return;
+ }
+
+ /* Gets the data from the asset */
+ status = psa_ps_get(uid, offset, data_len, (read_data + HALF_PADDING_SIZE),
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Checks that the data has not changed */
+ if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("The data should not have changed");
+ return;
+ }
+
+ /* Removes the asset to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when NV counters 2 and 3 are equals, 1 is
+ * different and PS area version match NV counter 1 value.
+ * It simulates a power cut during write action before increment counter
+ * 2 and 3, and the new PS area version is corrupted and only the old
+ * version match the NV counters.
+ */
+static void tfm_ps_test_4005(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Creates an asset in the PS area to generate a new PS area version */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Decrements NV counter 2 and 3 to make the PS area version match NV
+ * counter 1 only.
+ */
+ status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_2);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Decrement should not fail");
+ return;
+ }
+
+ status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_3);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Decrement should not fail");
+ return;
+ }
+
+ /* Simulates a reboot in the system by calling ps_system_prepare(). This
+ * function is called when the PS service is initialized.
+ *
+ * Prepare should not fail as the PS area version match the NV counter 1.
+ */
+ status = tfm_ps_test_system_prepare();
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("PS system prepare should not fail");
+ return;
+ }
+
+ /* Gets the data from the asset */
+ status = psa_ps_get(uid, offset, data_len, (read_data + HALF_PADDING_SIZE),
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Checks that the data has not changed */
+ if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("The data should not have changed");
+ return;
+ }
+
+ /* Removes the asset to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when NV counters 1, 2 and 3 have different
+ * values and PS area version match NV counter 1 value.
+ */
+static void tfm_ps_test_4006(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Creates an asset in the PS area to generate a new PS area version */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Decrements NV counter 2 (1 time) and 3 (2 times) to make the PS area
+ * version match NV counter 1 only.
+ */
+ status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_2);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Decrement should not fail");
+ return;
+ }
+
+ status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_3);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Decrement should not fail");
+ return;
+ }
+
+ status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_3);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Decrement should not fail");
+ return;
+ }
+
+ /* Simulates a reboot in the system by calling ps_system_prepare(). This
+ * function is called when the PS service is initialized.
+ *
+ * Prepare should not fail as the PS area version match the NV counter 1.
+ */
+ status = tfm_ps_test_system_prepare();
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("PS system prepare should not fail");
+ return;
+ }
+
+ /* Gets data from the asset */
+ status = psa_ps_get(uid, offset, data_len, (read_data + HALF_PADDING_SIZE),
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Checks that the data has not changed */
+ if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("The data should not have changed");
+ return;
+ }
+
+ /* Removes the asset to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when NV counters 1, 2 and 3 have different
+ * values and PS area version match NV counter 2 value.
+ */
+static void tfm_ps_test_4007(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ /* Creates an asset in the PS area to generate a new PS area version */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Increments NV counter 1 and decrements 3 to make the PS area
+ * version match NV counter 2 only.
+ */
+ status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_1);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Increment should not fail");
+ return;
+ }
+
+ status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_3);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Decrement should not fail");
+ return;
+ }
+
+ /* Simulates a reboot in the system by calling ps_system_prepare(). This
+ * function is called when the PS service is initialized.
+ *
+ * Prepare should fail as the PS area version match the NV counter 2 and
+ * the other counters are different.
+ */
+ status = tfm_ps_test_system_prepare();
+ if (status != PSA_ERROR_GENERIC_ERROR) {
+ TEST_FAIL("PS system prepare should fail");
+ return;
+ }
+
+ /* Removes the asset to clean up storage for the next test.
+ *
+ * To be able to remove the asset, the PS area version should match
+ * with the counter values. So, it is required to:
+ *
+ * 1. align the counters with the PS area version
+ * 2. re-call ps_system_prepare to mark the PS area as a valid image
+ * 3. remove the asset.
+ */
+
+ /* Aligns NV counters with the PS area version */
+ status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_1);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Decrement should not fail");
+ return;
+ }
+
+ status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_3);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Increment should not fail");
+ return;
+ }
+
+ /* Calls ps_system_prepare to mark the PS area as a valid image */
+ status = tfm_ps_test_system_prepare();
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("PS system prepare should not fail");
+ return;
+ }
+
+ /* Removes the asset to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when NV counters 1, 2 and 3 have different
+ * values and PS area version match NV counter 3 value.
+ */
+static void tfm_ps_test_4008(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ /* Creates an asset in the PS area to generate a new PS area version */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Increments NV counter 1 (2 times) and 2 (1 time) to make the PS area
+ * version match NV counter 3 only.
+ */
+ status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_1);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Increment should not fail");
+ return;
+ }
+
+ status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_1);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Increment should not fail");
+ return;
+ }
+
+ status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_2);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Increment should not fail");
+ return;
+ }
+
+ /* Simulates a reboot in the system by calling ps_system_prepare(). This
+ * function is called when the PS service is initialized.
+ *
+ * Prepare should fail as the PS area version match the NV counter 2 and
+ * the other counters are different.
+ */
+ status = tfm_ps_test_system_prepare();
+ if (status != PSA_ERROR_GENERIC_ERROR) {
+ TEST_FAIL("AM prepare should fail");
+ return;
+ }
+
+ /* Removes the asset to clean up storage for the next test.
+ *
+ * To be able to remove the asset, the PS area version should match
+ * with the counter values. So, it is required to:
+ *
+ * 1. align the counters with the PS area version
+ * 2. re-call ps_system_prepare to mark the PS area as a valid image
+ * 3. remove the asset.
+ */
+
+ /* Align NV counters with the PS area version */
+ status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_1);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Decrement should not fail");
+ return;
+ }
+
+ status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_1);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Decrement should not fail");
+ return;
+ }
+
+ status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_2);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Decrement should not fail");
+ return;
+ }
+
+ /* Calls ps_system_prepare to mark the PS area as a valid image */
+ status = tfm_ps_test_system_prepare();
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("PS system prepare should not fail");
+ return;
+ }
+
+ /* Removes the asset to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when NV counter 1 cannot be incremented
+ * (e.g it has reached its maximum value)
+ */
+static void tfm_ps_test_4009(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ /* Disables increment function to simulate that NV counter 1 has
+ * reached its maximum value.
+ */
+ test_ps_disable_increment_nv_counter();
+
+ /* Creates an asset in the PS area to generate a new PS area version */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_ERROR_GENERIC_ERROR) {
+ TEST_FAIL("Set should fail as the non-volatile counters can not be"
+ " increased");
+ return;
+ }
+
+ /* Enables counter again to not affect the next tests, if any */
+ test_ps_enable_increment_nv_counter();
+
+ ret->val = TEST_PASSED;
+}
diff --git a/test/suites/ps/secure/ps_tests.h b/test/suites/ps/secure/ps_tests.h
new file mode 100644
index 0000000..f2b9959
--- /dev/null
+++ b/test/suites/ps/secure/ps_tests.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PS_TESTS_H__
+#define __PS_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the PSA protected storage S interface tests.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_s_psa_ps_interface(struct test_suite_t *p_test_suite);
+
+/**
+ * \brief Register testsuite for the ps reliability tests.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_s_psa_ps_reliability(struct test_suite_t *p_test_suite);
+
+#ifdef PS_TEST_NV_COUNTERS
+/**
+ * \brief Register testsuite for the ps rollback protection tests.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_s_rollback_protection(
+ struct test_suite_t *p_test_suite);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PS_TESTS_H__ */
diff --git a/test/suites/ps/secure/psa_ps_s_interface_testsuite.c b/test/suites/ps/secure/psa_ps_s_interface_testsuite.c
new file mode 100644
index 0000000..7b44d52
--- /dev/null
+++ b/test/suites/ps/secure/psa_ps_s_interface_testsuite.c
@@ -0,0 +1,1220 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "ps_tests.h"
+
+#include <stdio.h>
+
+#include "s_test_helpers.h"
+#include "tfm_memory_utils.h"
+#include "psa/protected_storage.h"
+#include "test/framework/test_framework_helpers.h"
+#include "flash_layout.h"
+
+/* Test UIDs */
+#define WRITE_ONCE_UID 1U /* Cannot be modified or deleted once created */
+#define TEST_UID_1 2U
+#define TEST_UID_2 3U
+#define TEST_UID_3 4U
+
+/* Invalid values */
+#define INVALID_UID 0U
+#define INVALID_DATA_LEN UINT32_MAX
+#define INVALID_OFFSET UINT32_MAX
+#define INVALID_FLAG (1U << 31)
+#define INVALID_THREAD_NAME "Thread_INVALID"
+
+/* Write once data */
+#define WRITE_ONCE_DATA "THE_FIVE_BOXING_WIZARDS_JUMP_QUICKLY"
+#define WRITE_ONCE_DATA_SIZE (sizeof(WRITE_ONCE_DATA) - 1)
+#define WRITE_ONCE_READ_DATA "############################################"
+#define WRITE_ONCE_RESULT_DATA ("####" WRITE_ONCE_DATA "####")
+
+#define WRITE_DATA "THEQUICKBROWNFOXJUMPSOVERALAZYDOG"
+#define WRITE_DATA_SIZE (sizeof(WRITE_DATA) - 1)
+#define READ_DATA "_________________________________________"
+#define RESULT_DATA ("____" WRITE_DATA "____")
+#define OFFSET_READ_DATA "HEQUICKBROWNFOXJUMPSOVERALAZYDOG"
+#define OFFSET_RESULT_DATA ("____" OFFSET_READ_DATA "_____")
+
+#define TEST_1022_CYCLES 3U
+
+static const uint8_t write_asset_data[PS_MAX_ASSET_SIZE] = {0xBF};
+static uint8_t read_asset_data[PS_MAX_ASSET_SIZE] = {0};
+static size_t read_asset_datal_len = 0;
+
+/* List of tests */
+static void tfm_ps_test_2001(struct test_result_t *ret);
+static void tfm_ps_test_2002(struct test_result_t *ret);
+static void tfm_ps_test_2003(struct test_result_t *ret);
+static void tfm_ps_test_2004(struct test_result_t *ret);
+static void tfm_ps_test_2005(struct test_result_t *ret);
+static void tfm_ps_test_2006(struct test_result_t *ret);
+static void tfm_ps_test_2007(struct test_result_t *ret);
+static void tfm_ps_test_2008(struct test_result_t *ret);
+static void tfm_ps_test_2009(struct test_result_t *ret);
+static void tfm_ps_test_2010(struct test_result_t *ret);
+static void tfm_ps_test_2011(struct test_result_t *ret);
+static void tfm_ps_test_2012(struct test_result_t *ret);
+static void tfm_ps_test_2013(struct test_result_t *ret);
+static void tfm_ps_test_2014(struct test_result_t *ret);
+static void tfm_ps_test_2015(struct test_result_t *ret);
+static void tfm_ps_test_2016(struct test_result_t *ret);
+static void tfm_ps_test_2017(struct test_result_t *ret);
+static void tfm_ps_test_2018(struct test_result_t *ret);
+static void tfm_ps_test_2019(struct test_result_t *ret);
+static void tfm_ps_test_2020(struct test_result_t *ret);
+static void tfm_ps_test_2021(struct test_result_t *ret);
+static void tfm_ps_test_2022(struct test_result_t *ret);
+
+static struct test_t psa_ps_s_tests[] = {
+ {&tfm_ps_test_2001, "TFM_PS_TEST_2001",
+ "Set interface"},
+ {&tfm_ps_test_2002, "TFM_PS_TEST_2002",
+ "Set interface with create flags"},
+ {&tfm_ps_test_2003, "TFM_PS_TEST_2003",
+ "Set interface with NULL data pointer"},
+ {&tfm_ps_test_2004, "TFM_PS_TEST_2004",
+ "Set interface with invalid data length"},
+ {&tfm_ps_test_2005, "TFM_PS_TEST_2005",
+ "Set interface with write once UID"},
+ {&tfm_ps_test_2006, "TFM_PS_TEST_2006",
+ "Get interface with valid data"},
+ {&tfm_ps_test_2007, "TFM_PS_TEST_2007",
+ "Get interface with zero data length"},
+ {&tfm_ps_test_2008, "TFM_PS_TEST_2008",
+ "Get interface with invalid UIDs"},
+ {&tfm_ps_test_2009, "TFM_PS_TEST_2009",
+ "Get interface with invalid data lengths and offsets"},
+ {&tfm_ps_test_2010, "TFM_PS_TEST_2010",
+ "Get interface with NULL data pointer"},
+ {&tfm_ps_test_2011, "TFM_PS_TEST_2011",
+ "Get info interface with write once UID"},
+ {&tfm_ps_test_2012, "TFM_PS_TEST_2012",
+ "Get info interface with valid UID"},
+ {&tfm_ps_test_2013, "TFM_PS_TEST_2013",
+ "Get info interface with invalid UIDs"},
+ {&tfm_ps_test_2014, "TFM_PS_TEST_2014",
+ "Get info interface with NULL info pointer"},
+ {&tfm_ps_test_2015, "TFM_PS_TEST_2015",
+ "Remove interface with valid UID"},
+ {&tfm_ps_test_2016, "TFM_PS_TEST_2016",
+ "Remove interface with write once UID"},
+ {&tfm_ps_test_2017, "TFM_PS_TEST_2017",
+ "Remove interface with invalid UID"},
+ {&tfm_ps_test_2018, "TFM_PS_TEST_2018",
+ "Block compaction after remove"},
+ {&tfm_ps_test_2019, "TFM_PS_TEST_2019",
+ "Multiple partial gets"},
+ {&tfm_ps_test_2020, "TFM_PS_TEST_2020",
+ "Multiple sets to same UID from same thread"},
+ {&tfm_ps_test_2021, "TFM_PS_TEST_2021",
+ "Get support interface"},
+ {&tfm_ps_test_2022, "TFM_PS_TEST_2022",
+ "Set, get and remove interface with different asset sizes"},
+};
+
+void register_testsuite_s_psa_ps_interface(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(psa_ps_s_tests) / sizeof(psa_ps_s_tests[0]));
+
+ set_testsuite("PSA protected storage S interface tests (TFM_PS_TEST_2XXX)",
+ psa_ps_s_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Tests set function with:
+ * - Valid UID, no data, no flags
+ * - Invalid UID, no data, no flags
+ */
+static void tfm_ps_test_2001(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = 0;
+ const uint8_t write_data[] = {0};
+
+ /* Set with no data and no flags and a valid UID */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Attempt to set a second time */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail the second time with valid UID");
+ return;
+ }
+
+ /* Set with an invalid UID */
+ status = psa_ps_set(INVALID_UID, data_len, write_data, flags);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Set should not succeed with an invalid UID");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set function with:
+ * - Zero create flags
+ * - Valid create flags (with previously created UID)
+ * - Invalid create flags
+ */
+static void tfm_ps_test_2002(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ /* Set with no flags */
+ status = psa_ps_set(WRITE_ONCE_UID, data_len, write_data, flags);
+ if (status == PSA_SUCCESS) {
+ /* Set with valid flag: PSA_STORAGE_FLAG_WRITE_ONCE (with previously
+ * created UID)
+ * Note: Once created, WRITE_ONCE_UID cannot be deleted. It is reused
+ * across multiple tests.
+ */
+ status = psa_ps_set(WRITE_ONCE_UID, WRITE_ONCE_DATA_SIZE,
+ WRITE_ONCE_DATA, PSA_STORAGE_FLAG_WRITE_ONCE);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid flags (existing UID)");
+ return;
+ }
+ } else if (status == PSA_ERROR_NOT_PERMITTED) {
+ /* The UID has already been created with the PSA_STORAGE_FLAG_WRITE_ONCE
+ * flag in a previous test run, so skip creating it again and emit a
+ * warning.
+ */
+ TEST_LOG("Note: The UID in this test has already been created with\r\n"
+ "the PSA_STORAGE_FLAG_WRITE_ONCE flag in a previous test\r\n"
+ "run. Wipe the storage area to run the full test.\r\n");
+ } else {
+ TEST_FAIL("Set should not fail with no flags");
+ return;
+ }
+
+ /* Set with invalid flags */
+ status = psa_ps_set(uid, data_len, write_data, INVALID_FLAG);
+ if (status != PSA_ERROR_NOT_SUPPORTED) {
+ TEST_FAIL("Set should not succeed with invalid flags");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set function with:
+ * - NULL data pointer and zero data length
+ *
+ * \note A request with a null data pointer and data length not equal to zero is
+ * treated as a secure violation. TF-M framework will reject such requests
+ * so this case is not tested here.
+ *
+ */
+static void tfm_ps_test_2003(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = 0;
+
+ /* Set with NULL data pointer */
+ status = psa_ps_set(uid, data_len, NULL, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should succeed with NULL data pointer and zero length");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set function with:
+ * - Data length longer than maximum permitted
+ */
+static void tfm_ps_test_2004(struct test_result_t *ret)
+{
+#ifndef TFM_PSA_API
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = INVALID_DATA_LEN;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ /* A parameter with a buffer pointer where its data length is longer than
+ * maximum permitted, it is treated as a secure violation.
+ * TF-M framework rejects the request with a proper error code.
+ * The PS secure PSA PS implementation returns
+ * PSA_ERROR_INVALID_ARGUMENT in that case.
+ */
+
+ /* Set with data length longer than the maximum supported */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Set should not succeed with invalid data length");
+ return;
+ }
+
+#endif
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set function with:
+ * - Write once UID that has already been created
+ */
+static void tfm_ps_test_2005(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = WRITE_ONCE_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t write_len = WRITE_DATA_SIZE;
+ const uint32_t read_len = WRITE_ONCE_DATA_SIZE;
+ const uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = WRITE_ONCE_READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Set a write once UID a second time */
+ status = psa_ps_set(uid, write_len, write_data, flags);
+ if (status != PSA_ERROR_NOT_PERMITTED) {
+ TEST_FAIL("Set should not rewrite a write once UID");
+ return;
+ }
+
+ /* Get write once data */
+ status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Check that write once data has not changed */
+ if (tfm_memcmp(read_data, WRITE_ONCE_RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Write once data should not have changed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Valid data, zero offset
+ * - Valid data, non-zero offset
+ */
+static void tfm_ps_test_2006(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ uint32_t data_len = WRITE_DATA_SIZE;
+ uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ const uint8_t *p_read_data = read_data;
+ size_t read_data_len = 0;
+
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get the entire data */
+ status = psa_ps_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Check that the data is correct, including no illegal pre- or post-data */
+ if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to result data");
+ return;
+ }
+
+ /* Reset read data */
+ tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+
+ /* Read from offset 2 to 2 bytes before end of the data */
+ offset = 2;
+ data_len -= offset + 2;
+
+ status = psa_ps_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Check that the correct data was read */
+ if (tfm_memcmp(p_read_data, "____", HALF_PADDING_SIZE) != 0) {
+ TEST_FAIL("Read data contains illegal pre-data");
+ return;
+ }
+
+ p_read_data += HALF_PADDING_SIZE;
+
+ if (tfm_memcmp(p_read_data, write_data + offset, data_len) != 0) {
+ TEST_FAIL("Read data incorrect");
+ return;
+ }
+
+ p_read_data += data_len;
+
+ if (tfm_memcmp(p_read_data, "____", HALF_PADDING_SIZE) != 0) {
+ TEST_FAIL("Read data contains illegal post-data");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Zero data length, zero offset
+ * - Zero data length, non-zero offset
+ */
+static void tfm_ps_test_2007(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t write_len = WRITE_DATA_SIZE;
+ const uint32_t read_len = 0;
+ uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ status = psa_ps_set(uid, write_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get zero data from zero offset */
+ status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail with zero data len");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+ offset = 5;
+
+ /* Get zero data from non-zero offset */
+ status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Unset UID
+ * - Invalid UID
+ */
+static void tfm_ps_test_2008(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const uint32_t data_len = 1;
+ const uint32_t offset = 0;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Get with UID that has not yet been set */
+ status = psa_ps_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get succeeded with non-existant UID");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data not equal to original read data");
+ return;
+ }
+
+ /* Get with invalid UID */
+ status = psa_ps_get(INVALID_UID, offset, data_len,
+ read_data + HALF_PADDING_SIZE, &read_data_len);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Get succeeded with invalid UID");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data not equal to original read data");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Offset greater than UID length
+ * - Data length greater than UID length
+ * - Data length + offset greater than UID length
+ * - Invalid data len and offset
+ */
+static void tfm_ps_test_2009(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t write_len = WRITE_DATA_SIZE;
+ uint32_t read_len;
+ uint32_t offset;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ status = psa_ps_set(uid, write_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get with offset greater than UID's length */
+ read_len = 1;
+ offset = write_len + 1;
+
+ status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Get should not succeed with offset too large");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+ /* Get with data length greater than UID's length */
+ read_len = write_len + 1;
+ offset = 0;
+
+ status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should succeed with data length larger than UID's "
+ "length");
+ return;
+ }
+
+ if (read_data_len != write_len) {
+ TEST_FAIL("Read data length should be equal to UID's length");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+ /* Get with offset + data length greater than UID's length, but individually
+ * valid
+ */
+ read_len = write_len;
+ offset = 1;
+
+ /* Reset read_data to original READ_DATA */
+ tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+
+ status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should succeed with offset + data length too large, "
+ "but individually valid");
+ return;
+ }
+
+ if (read_data_len != write_len - offset) {
+ TEST_FAIL("Read data length should be equal to the UID's remaining "
+ "size starting from offset");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (tfm_memcmp(read_data, OFFSET_RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+
+#ifndef TFM_PSA_API
+ /* Get with data length and offset set to invalid values */
+ read_len = INVALID_DATA_LEN;
+ offset = INVALID_OFFSET;
+
+ /* Reset read_data to original READ_DATA */
+ tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+
+ /* A parameter with a buffer pointer where its data length is longer than
+ * maximum permitted, it is treated as a secure violation.
+ * TF-M framework rejects the request with a proper error code.
+ * The PS secure PSA PS implementation returns
+ * PSA_ERROR_INVALID_ARGUMENT in that case.
+ */
+
+ status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+ &read_data_len);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Get should not succeed with invalid arguments");
+ return;
+ }
+
+ /* Check that the read data is unchanged */
+ if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read data should be equal to original read data");
+ return;
+ }
+#endif
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - NULL data pointer and zero data length
+ *
+ * \note A request with a null data pointer and data length not equal to zero is
+ * treated as a secure violation. TF-M framework will reject such requests
+ * so this case is not tested here.
+ *
+ */
+static void tfm_ps_test_2010(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ size_t read_data_len = 0;
+
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get with NULL data pointer */
+ status = psa_ps_get(uid, offset, 0, NULL, &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should succeed with NULL data pointer and zero length");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - Write once UID
+ */
+static void tfm_ps_test_2011(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = WRITE_ONCE_UID;
+ struct psa_storage_info_t info = {0};
+
+ /* Get info for write once UID */
+ status = psa_ps_get_info(uid, &info);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get info should not fail for write once UID");
+ return;
+ }
+
+ /* Check that the info struct contains the correct values */
+ if (info.size != WRITE_ONCE_DATA_SIZE) {
+ TEST_FAIL("Size incorrect for write once UID");
+ return;
+ }
+
+ if (info.flags != PSA_STORAGE_FLAG_WRITE_ONCE) {
+ TEST_FAIL("Flags incorrect for write once UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - Valid UID
+ */
+static void tfm_ps_test_2012(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ struct psa_storage_info_t info = {0};
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get info for valid UID */
+ status = psa_ps_get_info(uid, &info);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get info should not fail with valid UID");
+ return;
+ }
+
+ /* Check that the info struct contains the correct values */
+ if (info.size != data_len) {
+ TEST_FAIL("Size incorrect for valid UID");
+ return;
+ }
+
+ if (info.flags != flags) {
+ TEST_FAIL("Flags incorrect for valid UID");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - Unset UID
+ * - Invalid UID
+ */
+static void tfm_ps_test_2013(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ struct psa_storage_info_t info = {0};
+
+ /* Get info with UID that has not yet been set */
+ status = psa_ps_get_info(uid, &info);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get info should not succeed with unset UID");
+ return;
+ }
+
+ /* Check that the info struct has not been modified */
+ if (info.size != 0) {
+ TEST_FAIL("Size should not have changed");
+ return;
+ }
+
+ if (info.flags != 0) {
+ TEST_FAIL("Flags should not have changed");
+ return;
+ }
+
+ /* Get info with invalid UID */
+ status = psa_ps_get_info(INVALID_UID, &info);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Get info should not succeed with invalid UID");
+ return;
+ }
+
+ /* Check that the info struct has not been modified */
+ if (info.size != 0) {
+ TEST_FAIL("Size should not have changed");
+ return;
+ }
+
+ if (info.flags != 0) {
+ TEST_FAIL("Flags should not have changed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - NULL info pointer
+ */
+static void tfm_ps_test_2014(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint8_t write_data[] = WRITE_DATA;
+
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* A parameter with a null pointer is treated as a secure violation.
+ * TF-M framework rejects the request with a proper error code.
+ * The PS secure PSA PS implementation returns
+ * PSA_ERROR_GENERIC_ERROR in that case.
+ */
+
+ /* Get info with NULL info pointer */
+#ifndef TFM_PSA_API
+ status = psa_ps_get_info(uid, NULL);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Get info should not succeed with NULL info pointer");
+ return;
+ }
+#endif
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests remove function with:
+ * - Valid UID
+ */
+static void tfm_ps_test_2015(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ struct psa_storage_info_t info = {0};
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Call remove with valid ID */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ /* Check that get info fails for removed UID */
+ status = psa_ps_get_info(uid, &info);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get info should not succeed with removed UID");
+ return;
+ }
+
+ /* Check that get fails for removed UID */
+ status = psa_ps_get(uid, offset, data_len, read_data, &read_data_len);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Get should not succeed with removed UID");
+ return;
+ }
+
+ /* Check that remove fails for removed UID */
+ status = psa_ps_remove(uid);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ TEST_FAIL("Remove should not succeed with removed UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests remove function with:
+ * - Write once UID
+ */
+static void tfm_ps_test_2016(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = WRITE_ONCE_UID;
+
+ /* Call remove with write once UID */
+ status = psa_ps_remove(uid);
+ if (status != PSA_ERROR_NOT_PERMITTED) {
+ TEST_FAIL("Remove should not succeed with write once UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests remove function with:
+ * - Invalid UID
+ */
+static void tfm_ps_test_2017(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = INVALID_UID;
+
+ /* Call remove with an invalid UID */
+ status = psa_ps_remove(uid);
+ if (status != PSA_ERROR_INVALID_ARGUMENT) {
+ TEST_FAIL("Remove should not succeed with invalid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests data block compact feature.
+ * Set UID 1 to locate it at the beginning of the block. Then set UID 2
+ * to be located after UID 1 and remove UID 1. UID 2 will be compacted to
+ * the beginning of the block. This test verifies that the compaction
+ * works correctly by reading back UID 2.
+ */
+static void tfm_ps_test_2018(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid_1 = TEST_UID_2;
+ const psa_storage_uid_t uid_2 = TEST_UID_3;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len_2 = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ const uint8_t write_data_1[] = "UID 1 DATA";
+ const uint8_t write_data_2[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Set UID 1 */
+ status = psa_ps_set(uid_1, sizeof(write_data_1), write_data_1, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail for UID 1");
+ return;
+ }
+
+ /* Set UID 2 */
+ status = psa_ps_set(uid_2, data_len_2, write_data_2, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail for UID 2");
+ return;
+ }
+
+ /* Remove UID 1. This should cause UID 2 to be compacted to the beginning of
+ * the block.
+ */
+ status = psa_ps_remove(uid_1);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail for UID 1");
+ return;
+ }
+
+ /* If the compact worked as expected, the test should be able to read back
+ * the data from UID 2 correctly.
+ */
+ status = psa_ps_get(uid_2, offset, data_len_2,
+ read_data + HALF_PADDING_SIZE, &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail for UID 2");
+ return;
+ }
+
+ if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read buffer has incorrect data");
+ return;
+ }
+
+ /* Remove UID 2 to clean up storage for the next test */
+ status = psa_ps_remove(uid_2);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail for UID 2");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set and multiple partial gets.
+ */
+static void tfm_ps_test_2019(struct test_result_t *ret)
+{
+ psa_status_t status;
+ uint32_t i;
+ const psa_storage_uid_t uid = TEST_UID_1;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ uint32_t offset = 0;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Set the entire data into UID */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail");
+ return;
+ }
+
+ /* Get the data from UID one byte at a time */
+ for (i = 0; i < data_len; ++i) {
+ status = psa_ps_get(uid, offset, 1,
+ (read_data + HALF_PADDING_SIZE + i),
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail for partial read");
+ return;
+ }
+
+ ++offset;
+ }
+
+ if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("Read buffer has incorrect data");
+ return;
+ }
+
+ /* Remove UID to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests multiple sets to the same UID.
+ */
+static void tfm_ps_test_2020(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID_2;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t offset = 0;
+ const uint8_t write_data_1[] = "ONE";
+ const uint8_t write_data_2[] = "TWO";
+ const uint8_t write_data_3[] = "THREE";
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ /* Set write data 1 into UID */
+ status = psa_ps_set(uid, sizeof(write_data_1), write_data_1, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("First set should not fail");
+ return;
+ }
+
+ /* Set write data 2 into UID */
+ status = psa_ps_set(uid, sizeof(write_data_2), write_data_2, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Second set should not fail");
+ return;
+ }
+
+ /* Set write data 3 into UID */
+ status = psa_ps_set(uid, sizeof(write_data_3), write_data_3, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Third set should not fail");
+ return;
+ }
+
+ status = psa_ps_get(uid, offset, sizeof(write_data_3), read_data,
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Check that get returns the last data to be set */
+ if (tfm_memcmp(read_data, write_data_3, sizeof(write_data_3)) != 0) {
+ TEST_FAIL("Read buffer has incorrect data");
+ return;
+ }
+
+ /* Remove UID to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get support function.
+ */
+static void tfm_ps_test_2021(struct test_result_t *ret)
+{
+ uint32_t support_flags;
+
+ support_flags = psa_ps_get_support();
+ if (support_flags != 0) {
+ TEST_FAIL("Support flags should be 0");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set, get_info, get and remove function with:
+ * - Valid UID's
+ * - Data length of different asset sizes
+ * - No flags
+ */
+static void tfm_ps_test_2022(struct test_result_t *ret)
+{
+ uint8_t cycle;
+ psa_status_t status;
+ const psa_storage_uid_t test_uid[TEST_1022_CYCLES] = {
+ TEST_UID_1,
+ TEST_UID_2,
+ TEST_UID_3};
+ const uint32_t test_asset_sizes[TEST_1022_CYCLES] = {
+ PS_MAX_ASSET_SIZE >> 2,
+ PS_MAX_ASSET_SIZE >> 1,
+ PS_MAX_ASSET_SIZE};
+
+ /* Loop to test different asset sizes and UID's*/
+ for (cycle = 0; cycle < TEST_1022_CYCLES; cycle++) {
+ uint32_t data_size = test_asset_sizes[cycle];
+ psa_storage_uid_t uid = test_uid[cycle];
+ struct psa_storage_info_t info = {0};
+
+ tfm_memset(read_asset_data, 0x00, sizeof(read_asset_data));
+
+ /* Set with data and no flags and a valid UID */
+ status = psa_ps_set(uid,
+ data_size,
+ write_asset_data,
+ PSA_STORAGE_FLAG_NONE);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Get info for valid UID */
+ status = psa_ps_get_info(uid, &info);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get info should not fail with valid UID");
+ return;
+ }
+
+ /* Check that the info struct contains the correct values */
+ if (info.size != data_size) {
+ TEST_FAIL("Size incorrect for valid UID");
+ return;
+ }
+
+ if (info.flags != PSA_STORAGE_FLAG_NONE) {
+ TEST_FAIL("Flags incorrect for valid UID");
+ return;
+ }
+
+ /* Check that thread can still get UID */
+ status = psa_ps_get(uid, 0, data_size, read_asset_data,
+ &read_asset_datal_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail with valid UID");
+ return;
+ }
+
+ /* Check that thread's UID data has not been modified */
+ if (tfm_memcmp(read_asset_data, write_asset_data, data_size) != 0) {
+ TEST_FAIL("Read data should be equal to original write data");
+ return;
+ }
+
+ /* Call remove to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+ }
+
+ ret->val = TEST_PASSED;
+}
diff --git a/test/suites/ps/secure/psa_ps_s_reliability_testsuite.c b/test/suites/ps/secure/psa_ps_s_reliability_testsuite.c
new file mode 100644
index 0000000..658f4f5
--- /dev/null
+++ b/test/suites/ps/secure/psa_ps_s_reliability_testsuite.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "ps_tests.h"
+
+#include <stdio.h>
+
+#include "psa/protected_storage.h"
+#include "tfm_memory_utils.h"
+#include "s_test_helpers.h"
+#include "test/framework/test_framework_helpers.h"
+
+/* Test UIDs */
+#define TEST_UID 2UL /* UID 1 cannot be used as it references a write once
+ * asset, created in psa_ps_s_interface_testsuite.c
+ */
+
+/* Test suite defines */
+#define LOOP_ITERATIONS_001 15U
+#define LOOP_ITERATIONS_002 15U
+
+/* Write data */
+#define WRITE_DATA "THE_FIVE_BOXING_WIZARDS_JUMP_QUICKLY"
+#define WRITE_DATA_SIZE (sizeof(WRITE_DATA) - 1)
+#define READ_DATA "############################################"
+#define RESULT_DATA ("####" WRITE_DATA "####")
+
+/* Define test suite for PS reliability tests */
+/* List of tests */
+static void tfm_ps_test_3001(struct test_result_t *ret);
+static void tfm_ps_test_3002(struct test_result_t *ret);
+
+static struct test_t reliability_tests[] = {
+ {&tfm_ps_test_3001, "TFM_PS_TEST_3001",
+ "repetitive sets and gets in/from an asset", {TEST_PASSED} },
+ {&tfm_ps_test_3002, "TFM_PS_TEST_3002",
+ "repetitive sets, gets and removes", {TEST_PASSED} },
+};
+
+void register_testsuite_s_psa_ps_reliability(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size = (sizeof(reliability_tests) /
+ sizeof(reliability_tests[0]));
+
+ set_testsuite("PS reliability tests (TFM_PS_TEST_3XXX)",
+ reliability_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Tests repetitive sets and gets in/from an asset.
+ */
+static void tfm_ps_test_3001(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ uint32_t itr;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ for (itr = 0; itr < LOOP_ITERATIONS_001; itr++) {
+ TEST_LOG(" > Iteration %d of %d\r", itr + 1, LOOP_ITERATIONS_001);
+
+ /* Set a data in the asset */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Get data from the asset */
+ status = psa_ps_get(uid, offset, data_len, (read_data +
+ HALF_PADDING_SIZE),
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Check that the data has not changed */
+ if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("The data should not have changed");
+ return;
+ }
+
+ /* Set the original data into read buffer */
+ tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+ }
+
+ TEST_LOG("\n");
+
+ /* Remove the asset to clean up storage for the next test */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests repetitive sets, gets and removes.
+ */
+static void tfm_ps_test_3002(struct test_result_t *ret)
+{
+ psa_status_t status;
+ const psa_storage_uid_t uid = TEST_UID;
+ const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+ const uint32_t data_len = WRITE_DATA_SIZE;
+ const uint32_t offset = 0;
+ uint32_t itr;
+ const uint8_t write_data[] = WRITE_DATA;
+ uint8_t read_data[] = READ_DATA;
+ size_t read_data_len = 0;
+
+ for (itr = 0; itr < LOOP_ITERATIONS_002; itr++) {
+ TEST_LOG(" > Iteration %d of %d\r", itr + 1, LOOP_ITERATIONS_002);
+
+ /* Set a data in the asset */
+ status = psa_ps_set(uid, data_len, write_data, flags);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Set should not fail with valid UID");
+ return;
+ }
+
+ /* Get data from the asset */
+ status = psa_ps_get(uid, offset, data_len, (read_data +
+ HALF_PADDING_SIZE),
+ &read_data_len);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Get should not fail");
+ return;
+ }
+
+ /* Check that the data has not changed */
+ if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+ TEST_FAIL("The data should not have changed");
+ return;
+ }
+
+ /* Remove the asset from the protected storage */
+ status = psa_ps_remove(uid);
+ if (status != PSA_SUCCESS) {
+ TEST_FAIL("Remove should not fail with valid UID");
+ return;
+ }
+
+ /* Set the original data into read buffer */
+ tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+ }
+
+ TEST_LOG("\n");
+
+ ret->val = TEST_PASSED;
+}
diff --git a/test/suites/ps/secure/s_test_helpers.h b/test/suites/ps/secure/s_test_helpers.h
new file mode 100644
index 0000000..1da7390
--- /dev/null
+++ b/test/suites/ps/secure/s_test_helpers.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __S_TEST_HELPERS_H__
+#define __S_TEST_HELPERS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Several tests use a buffer to read back data from an asset. This buffer is
+ * larger than the size of the asset data by PADDING_SIZE bytes. This allows
+ * us to ensure that the only the expected data is read back and that it is read
+ * back correctly.
+ *
+ * For example if the buffer and asset are as follows:
+ * Buffer - "XXXXXXXXXXXX", Asset data - "AAAA"
+ *
+ * Then a correct and successful read would give this result: "XXXXAAAAXXXX"
+ * (Assuming a PADDING_SIZE of 8)
+ */
+#define BUFFER_SIZE 24
+#define PADDING_SIZE 8
+#define HALF_PADDING_SIZE 4
+
+#define BUFFER_PLUS_PADDING_SIZE (BUFFER_SIZE + PADDING_SIZE)
+#define BUFFER_PLUS_HALF_PADDING_SIZE (BUFFER_SIZE + HALF_PADDING_SIZE)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __S_TEST_HELPERS_H__ */
diff --git a/test/suites/qcbor/CMakeLists.inc b/test/suites/qcbor/CMakeLists.inc
new file mode 100644
index 0000000..d8fa1f7
--- /dev/null
+++ b/test/suites/qcbor/CMakeLists.inc
@@ -0,0 +1,36 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the QCBOR module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(QCBOR_TEST_SUIT_DIR ${CMAKE_CURRENT_LIST_DIR})
+
+#Verify input parameters
+if(NOT DEFINED TFM_ROOT_DIR)
+ message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_QCBOR_TESTS)
+ message(FATAL_ERROR "Incomplete build configuration: ENABLE_QCBOR_TESTS is undefined.")
+elseif(ENABLE_QCBOR_TESTS)
+ list(APPEND ALL_SRC_C_NS
+ "${QCBOR_TEST_SUIT_DIR}/non_secure/qcbor_ns_testsuite.c"
+ $<TARGET_OBJECTS:tfm_qcbor_test> #See in lib/ext/qcbor/CMakeList.txt
+ )
+endif()
diff --git a/test/suites/qcbor/non_secure/qcbor_ns_tests.h b/test/suites/qcbor/non_secure/qcbor_ns_tests.h
new file mode 100644
index 0000000..03d2e46
--- /dev/null
+++ b/test/suites/qcbor/non_secure/qcbor_ns_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __QCBOR_NS_TESTS_H__
+#define __QCBOR_NS_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register test suite for the QCBOR library
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void
+register_testsuite_ns_qcbor(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __QCBOR_NS_TESTS_H__ */
diff --git a/test/suites/qcbor/non_secure/qcbor_ns_testsuite.c b/test/suites/qcbor/non_secure/qcbor_ns_testsuite.c
new file mode 100644
index 0000000..f44994b
--- /dev/null
+++ b/test/suites/qcbor/non_secure/qcbor_ns_testsuite.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "qcbor_ns_tests.h"
+#include <stdio.h>
+#include "lib/ext/qcbor/test/run_tests.h"
+
+/*
+ * The QCBOR test cases are defined in: lib/ext/qcbor/test/run_test.c
+ *
+ * There are two types of configuration to execute test cases:
+ * - All tests: Just pass an array containing a NULL pointer to RunTest():
+ * qcbor_test_cases_all[]. This is the default configuration.
+ *
+ * - Selected tests: qcbor_test_cases[] array contains the name of all tests.
+ * To execute only selected test cases, just remove/comment
+ * out the unwanted tests from the array and pass it to
+ * RunTests(). This configuration disables all tests with a
+ * long execution time to speed up development.
+ */
+
+/* Define test suite for QCBOR library */
+static void tfm_qcbor_test_7001(struct test_result_t *ret);
+
+static struct test_t qcbor_regression_test[] = {
+ {&tfm_qcbor_test_7001, "TFM_QCBOR_TEST_7001",
+ "Regression test of QCBOR library", {TEST_PASSED} },
+};
+
+/* To execute only selected test cases, then remove unwanted ones from the array
+ * and pass it to RunTests().
+ * Not static to avoid compiler warning due to non-usage
+ */
+const char *qcbor_test_cases[] = {
+ "ParseMapAsArrayTest",
+ "AllocAllStringsTest",
+ "IndefiniteLengthNestTest",
+ "NestedMapTestIndefLen",
+ "ParseSimpleTest",
+ "EncodeRawTest",
+ "RTICResultsTest",
+ "MapEncodeTest",
+ "ArrayNestingTest1",
+ "ArrayNestingTest2",
+ "ArrayNestingTest3",
+ "EncodeDateTest",
+ "SimpleValuesTest1",
+ "IntegerValuesTest1",
+ "AllAddMethodsTest",
+ "ParseTooDeepArrayTest",
+ "ComprehensiveInputTest",
+ "ParseMapTest",
+ "IndefiniteLengthArrayMapTest",
+ "BasicEncodeTest",
+ "NestedMapTest",
+ "BignumParseTest",
+ "OptTagParseTest",
+ "DateParseTest",
+ "ShortBufferParseTest2",
+ "ShortBufferParseTest",
+ "ParseDeepArrayTest",
+ "SimpleArrayTest",
+ "IntegerValuesParseTest",
+ "MemPoolTest",
+ "IndefiniteLengthStringTest", /**/
+ "HalfPrecisionDecodeBasicTests",
+ "DoubleAsSmallestTest",
+ "HalfPrecisionAgainstRFCCodeTest",
+ "BstrWrapTest",
+ "BstrWrapErrorTest",
+ "BstrWrapNestTest", /**/
+ "CoseSign1TBSTest",
+ "StringDecoderModeFailTest",
+ /* "BigComprehensiveInputTest", */ /* Takes too long to execute */
+ "EncodeErrorTests",
+ "UBUTest_CopyUtil",
+ "UOBTest_NonAdversarial",
+ "TestBasicSanity",
+ "UOBTest_BoundaryConditionsTest",
+ "UBMacroConversionsTest",
+ "UBUtilTests",
+ "UIBTest_IntegerFormat",
+};
+
+/* To execute all test cases, then pass this array to RunTests()
+ * Test cases are defined in: lib/ext/qcbor/test/run_tests.c
+ */
+const static char *qcbor_test_cases_all[] = {
+ 0, /* Indicates to run all enabled test case */
+};
+
+/**
+ * \brief Print QCBOR test results per test case
+ *
+ * \param[in] szString String to print out
+ * \param[in] ctx Where to print
+ */
+
+static void fputs_wrapper(const char *string, void *out_ctx, int new_line)
+{
+ (void)out_ctx;
+ (void)new_line;
+ /*
+ * To get test result per test case, change
+ * the preprocessor '#if 0' to '#if 1'.
+ */
+# if 0
+ TEST_LOG("%s\r\n", string);
+#else
+ (void)string;
+#endif
+}
+
+static void tfm_qcbor_test_7001(struct test_result_t *ret)
+{
+ int32_t test_failed_cnt = 0;
+
+ test_failed_cnt = RunTestsQCBOR(qcbor_test_cases_all, fputs_wrapper,
+ NULL, NULL);
+ if (test_failed_cnt != 0) {
+ TEST_FAIL("QCBOR test failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void
+register_testsuite_ns_qcbor(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(qcbor_regression_test) /
+ sizeof(qcbor_regression_test[0]));
+
+ set_testsuite("QCBOR regression test"
+ "(TFM_QCBOR_TEST_7XXX)",
+ qcbor_regression_test, list_size, p_test_suite);
+}
diff --git a/test/suites/t_cose/CMakeLists.inc b/test/suites/t_cose/CMakeLists.inc
new file mode 100644
index 0000000..c030683
--- /dev/null
+++ b/test/suites/t_cose/CMakeLists.inc
@@ -0,0 +1,36 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the T_COSE module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(T_COSE_TEST_SUIT_DIR ${CMAKE_CURRENT_LIST_DIR})
+
+#Verify input parameters
+if(NOT DEFINED TFM_ROOT_DIR)
+ message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_T_COSE_TESTS)
+ message(FATAL_ERROR "Incomplete build configuration: ENABLE_T_COSE_TESTS is undefined.")
+elseif(ENABLE_T_COSE_TESTS)
+ list(APPEND ALL_SRC_C_NS
+ "${T_COSE_TEST_SUIT_DIR}/non_secure/t_cose_ns_testsuite.c"
+ $<TARGET_OBJECTS:tfm_t_cose_test> #See in lib/ext/t_cose/CMakeList.txt
+ )
+endif()
diff --git a/test/suites/t_cose/non_secure/t_cose_ns_tests.h b/test/suites/t_cose/non_secure/t_cose_ns_tests.h
new file mode 100644
index 0000000..8313797
--- /dev/null
+++ b/test/suites/t_cose/non_secure/t_cose_ns_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __T_COSE_NS_TESTS_H__
+#define __T_COSE_NS_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register test suite for the t_cose library
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void
+register_testsuite_ns_t_cose(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __T_COSE_NS_TESTS_H__ */
diff --git a/test/suites/t_cose/non_secure/t_cose_ns_testsuite.c b/test/suites/t_cose/non_secure/t_cose_ns_testsuite.c
new file mode 100644
index 0000000..3428a08
--- /dev/null
+++ b/test/suites/t_cose/non_secure/t_cose_ns_testsuite.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "t_cose_ns_tests.h"
+#include <stdio.h>
+#include "lib/ext/t_cose/test/run_tests.h"
+
+/*
+ * The t_cose test cases are defined in: lib/ext/t_cose/test/run_test.c
+ *
+ * There are two types of configuration to execute test cases:
+ * - All tests: Just pass an array containing a NULL pointer to RunTest():
+ * t_cose_test_cases_all[]. This is the default configuration.
+ *
+ * - Selected tests: Edit the s_tests[] array in
+ * lib/ext/t_cose/test/run_tests.c.
+ */
+
+/* Define test suite for t_cose library */
+static void tfm_t_cose_test_8001(struct test_result_t *ret);
+
+static struct test_t t_cose_regression_test[] = {
+ {&tfm_t_cose_test_8001, "TFM_T_COSE_TEST_8001",
+ "Regression test of t_cose library", {TEST_PASSED} },
+};
+
+/* To execute all test cases, then pass this array to RunTestsTCose()
+ * Test cases are defined in: lib/ext/t_cose/test/run_tests.c
+ */
+const static char *t_cose_test_cases_all[] = {
+ 0, /* Indicates to run all enabled test case */
+};
+
+/**
+ * \brief Print t_cose test results per test case
+ *
+ * \param[in] szString String to print out
+ * \param[in] ctx Where to print
+ */
+
+static void fputs_wrapper(const char *string, void *out_ctx, int new_line)
+{
+ (void)out_ctx;
+ (void)new_line;
+ /*
+ * To get test result per test case, change
+ * the preprocessor '#if 0' to '#if 1'.
+ */
+# if 0
+ TEST_LOG("%s\r\n", string);
+#else
+ (void)string;
+#endif
+}
+
+static void tfm_t_cose_test_8001(struct test_result_t *ret)
+{
+ int32_t test_failed_cnt = 0;
+
+ test_failed_cnt = RunTestsTCose(t_cose_test_cases_all, fputs_wrapper,
+ NULL, NULL);
+ if (test_failed_cnt != 0) {
+ TEST_FAIL("t_cose test failed");
+ return;
+ }
+
+ ret->val = TEST_PASSED;
+}
+
+void
+register_testsuite_ns_t_cose(struct test_suite_t *p_test_suite)
+{
+ uint32_t list_size;
+
+ list_size = (sizeof(t_cose_regression_test) /
+ sizeof(t_cose_regression_test[0]));
+
+ set_testsuite("T_COSE regression test"
+ "(TFM_T_COSE_TEST_8XXX)",
+ t_cose_regression_test, list_size, p_test_suite);
+}
diff --git a/test/test_services/CMakeLists.inc b/test/test_services/CMakeLists.inc
new file mode 100644
index 0000000..f4f0482
--- /dev/null
+++ b/test/test_services/CMakeLists.inc
@@ -0,0 +1,101 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "secure services" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repository.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(CORE_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+
+#Check input variables
+if (NOT DEFINED TFM_ROOT_DIR)
+ message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_CORE_TESTS)
+ message(FATAL_ERROR "Incomplete build configuration: ENABLE_CORE_TESTS is undefined. ")
+elseif(ENABLE_CORE_TESTS)
+ list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_core_test/tfm_ss_core_test.c")
+endif()
+
+if (NOT DEFINED ENABLE_CORE_TESTS_2)
+ message(FATAL_ERROR "Incomplete build configuration: ENABLE_CORE_TESTS_2 is undefined. ")
+elseif(ENABLE_CORE_TESTS_2)
+ list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_core_test_2/tfm_ss_core_test_2.c")
+endif()
+
+if (NOT DEFINED ENABLE_IRQ_TEST_SERVICES)
+ message(FATAL_ERROR "Incomplete build configuration: ENABLE_IRQ_TEST_SERVICES is undefined. ")
+elseif(ENABLE_IRQ_TEST_SERVICES)
+ list(APPEND ALL_SRC_C_S
+ "${CORE_TEST_DIR}/tfm_irq_test_service_1/tfm_irq_test_service_1.c")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_TEST_SECURE_SERVICES)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_TEST_SECURE_SERVICES is undefined. ")
+elseif (TFM_PARTITION_TEST_SECURE_SERVICES)
+ list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_secure_client_service/tfm_secure_client_service.c"
+ "${CORE_TEST_DIR}/tfm_secure_client_2/tfm_secure_client_2.c"
+ "${CORE_TEST_DIR}/tfm_secure_client_2/tfm_secure_client_2_api.c")
+
+ list(APPEND ALL_SRC_C_NS "${CORE_TEST_DIR}/tfm_secure_client_service/tfm_secure_client_service_api.c")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_TEST_CORE_IPC)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_TEST_CORE_IPC is undefined. ")
+elseif (TFM_PARTITION_TEST_CORE_IPC)
+ list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_ipc_service/tfm_ipc_service_test.c"
+ "${CORE_TEST_DIR}/tfm_ipc_client/tfm_ipc_client_test.c"
+ )
+endif()
+
+if (NOT DEFINED TFM_PARTITION_TEST_PS)
+ message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_TEST_PS is undefined.")
+elseif (TFM_PARTITION_TEST_PS)
+ list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_ps_test_service/tfm_ps_test_service.c"
+ "${CORE_TEST_DIR}/tfm_ps_test_service/tfm_ps_test_service_api.c")
+endif()
+
+embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/platform/include ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/secure_fw/include ABSOLUTE)
+
+set(BUILD_CMSIS_CORE Off)
+set(BUILD_RETARGET Off)
+set(BUILD_NATIVE_DRIVERS Off)
+set(BUILD_STARTUP Off)
+set(BUILD_TARGET_CFG Off)
+set(BUILD_TARGET_HARDWARE_KEYS Off)
+set(BUILD_TARGET_NV_COUNTERS Off)
+set(BUILD_CMSIS_DRIVERS Off)
+set(BUILD_UART_STDOUT Off)
+set(BUILD_FLASH Off)
+if (CORE_TEST_POSITIVE OR CORE_TEST_INTERACTIVE)
+ set(BUILD_PLAT_TEST On)
+ set(BUILD_TIME On)
+else()
+ set(BUILD_PLAT_TEST Off)
+ set(BUILD_TIME Off)
+endif()
+if(NOT DEFINED PLATFORM_CMAKE_FILE)
+ message (FATAL_ERROR "Platform specific CMake is not defined. Please set PLATFORM_CMAKE_FILE.")
+elseif(NOT EXISTS ${PLATFORM_CMAKE_FILE})
+ message (FATAL_ERROR "Platform specific CMake \"${PLATFORM_CMAKE_FILE}\" file does not exist. Please fix value of PLATFORM_CMAKE_FILE.")
+else()
+ include(${PLATFORM_CMAKE_FILE})
+endif()
diff --git a/test/test_services/tfm_core_test/core_test_defs.h b/test/test_services/tfm_core_test/core_test_defs.h
new file mode 100644
index 0000000..819d2a8
--- /dev/null
+++ b/test/test_services/tfm_core_test/core_test_defs.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CORE_TEST_DEFS_H__
+#define __CORE_TEST_DEFS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <inttypes.h>
+#include <limits.h>
+#include "tfm_api.h"
+
+/* These definitions are used in symbols, only digits are permitted */
+#define CORE_TEST_ID_NS_THREAD 1001
+#define CORE_TEST_ID_CHECK_INIT 1003
+#define CORE_TEST_ID_RECURSION 1004
+#define CORE_TEST_ID_BUFFER_CHECK 1007
+#define CORE_TEST_ID_SS_TO_SS 1008
+#define CORE_TEST_ID_SS_TO_SS_BUFFER 1010
+#define CORE_TEST_ID_PERIPHERAL_ACCESS 1012
+#define CORE_TEST_ID_GET_CALLER_CLIENT_ID 1013
+#define CORE_TEST_ID_SPM_REQUEST 1014
+#define CORE_TEST_ID_IOVEC_SANITIZATION 1015
+#define CORE_TEST_ID_OUTVEC_WRITE 1016
+#define CORE_TEST_ID_SECURE_IRQ 1017
+#define CORE_TEST_ID_BLOCK 2001
+
+enum irq_test_scenario_t {
+ IRQ_TEST_SCENARIO_NONE,
+ IRQ_TEST_SCENARIO_1,
+ IRQ_TEST_SCENARIO_2,
+ IRQ_TEST_SCENARIO_3,
+ IRQ_TEST_SCENARIO_4,
+ IRQ_TEST_SCENARIO_5,
+};
+
+struct irq_test_execution_data_t {
+ volatile int32_t timer0_triggered;
+ volatile int32_t timer1_triggered;
+};
+
+/* Use lower 16 bits in return value for error code, upper 16 for line number
+ * in test service
+ */
+#define CORE_TEST_RETURN_ERROR(x) return (((__LINE__) << 16) | x)
+#define CORE_TEST_ERROR_GET_EXTRA(x) (x >> 16)
+#define CORE_TEST_ERROR_GET_CODE(x) (x & 0xFFFF)
+
+enum core_test_errno_t {
+ CORE_TEST_ERRNO_TEST_NOT_SUPPORTED = -13,
+ CORE_TEST_ERRNO_SP_NOT_INITED = -12,
+ CORE_TEST_ERRNO_UNEXPECTED_CORE_BEHAVIOUR = -11,
+ CORE_TEST_ERRNO_SP_RECURSION_NOT_REJECTED = -10,
+ CORE_TEST_ERRNO_INVALID_BUFFER = -9,
+ CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE = -8,
+ CORE_TEST_ERRNO_SLAVE_SP_BUFFER_FAILURE = -7,
+ CORE_TEST_ERRNO_FIRST_CALL_FAILED = -6,
+ CORE_TEST_ERRNO_SECOND_CALL_FAILED = -5,
+ CORE_TEST_ERRNO_PERIPHERAL_ACCESS_FAILED = -4,
+ CORE_TEST_ERRNO_TEST_FAULT = -3,
+ CORE_TEST_ERRNO_INVALID_TEST_ID = -2,
+ CORE_TEST_ERRNO_INVALID_PARAMETER = -1,
+
+ CORE_TEST_ERRNO_SUCCESS = 0,
+
+ CORE_TEST_ERRNO_SUCCESS_2 = 1,
+
+ /* Following entry is only to ensure the error code of int size */
+ CORE_TEST_ERRNO_FORCE_INT_SIZE = INT_MAX
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CORE_TEST_DEFS_H__ */
diff --git a/test/test_services/tfm_core_test/psa_manifest/tfm_test_core.h b/test/test_services/tfm_core_test/psa_manifest/tfm_test_core.h
new file mode 100644
index 0000000..e133c46
--- /dev/null
+++ b/test/test_services/tfm_core_test/psa_manifest/tfm_test_core.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_TEST_CORE_H__
+#define __PSA_MANIFEST_TFM_TEST_CORE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SPM_CORE_TEST_INIT_SUCCESS_SIGNAL (1U << (0 + 4))
+#define SPM_CORE_TEST_DIRECT_RECURSION_SIGNAL (1U << (1 + 4))
+#define SPM_CORE_TEST_SS_TO_SS_SIGNAL (1U << (2 + 4))
+#define SPM_CORE_TEST_SS_TO_SS_BUFFER_SIGNAL (1U << (3 + 4))
+#define SPM_CORE_TEST_OUTVEC_WRITE_SIGNAL (1U << (4 + 4))
+#define SPM_CORE_TEST_PERIPHERAL_ACCESS_SIGNAL (1U << (5 + 4))
+#define SPM_CORE_TEST_GET_CALLER_CLIENT_ID_SIGNAL (1U << (6 + 4))
+#define SPM_CORE_TEST_SPM_REQUEST_SIGNAL (1U << (7 + 4))
+#define SPM_CORE_TEST_BLOCK_SIGNAL (1U << (8 + 4))
+#define SPM_CORE_TEST_NS_THREAD_SIGNAL (1U << (9 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_TEST_CORE_H__ */
diff --git a/test/test_services/tfm_core_test/tfm_ss_core_test.c b/test/test_services/tfm_core_test/tfm_ss_core_test.c
new file mode 100644
index 0000000..7390fc3
--- /dev/null
+++ b/test/test_services/tfm_core_test/tfm_ss_core_test.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stddef.h>
+#include "tfm_ss_core_test.h"
+#include "tfm_api.h"
+#include "test/test_services/tfm_core_test/core_test_defs.h"
+#include "test/framework/test_framework.h"
+#include "tfm_veneers.h"
+#include "tfm_secure_api.h"
+#include "secure_fw/include/tfm/tfm_spm_services.h"
+#include "psa/service.h"
+#include "tfm_plat_test.h"
+#include "psa_manifest/pid.h"
+#include "psa_manifest/tfm_test_core.h"
+#ifdef TFM_PSA_API
+#include "psa_manifest/sid.h"
+#endif
+
+static int32_t partition_init_done;
+
+#define INVALID_NS_CLIENT_ID 0x49abcdef
+#define EXPECTED_NS_CLIENT_ID (-1)
+
+#define IRQ_TEST_TOOL_CODE_LOCATION(name)
+
+#ifndef TFM_PSA_API
+/* Don't initialise caller_partition_id_zi and expect it to be linked in the
+ * zero-initialised data area
+ */
+static int32_t caller_client_id_zi;
+
+/* Initialise caller_partition_id_rw and expect it to be linked in the
+ * read-write data area
+ */
+static int32_t caller_client_id_rw = INVALID_NS_CLIENT_ID;
+
+static int32_t* invalid_addresses [] = {(int32_t*)0x0, (int32_t*)0xFFF12000};
+
+#else /* !defined(TFM_PSA_API) */
+
+static psa_status_t psa_test_common(uint32_t sid, uint32_t version,
+ const psa_invec *in_vecs, size_t in_len,
+ psa_outvec *out_vecs, size_t out_len)
+{
+ psa_handle_t handle;
+ psa_status_t status;
+
+ handle = psa_connect(sid, version);
+ if (handle <= 0) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ status = psa_call(handle, PSA_IPC_CALL, in_vecs, in_len, out_vecs, out_len);
+ if (status < 0) {
+ status = CORE_TEST_ERRNO_UNEXPECTED_CORE_BEHAVIOUR;
+ }
+
+ psa_close(handle);
+ return status;
+}
+#endif /* !defined(TFM_PSA_API) */
+
+psa_status_t spm_core_test_sfn_init_success(
+ struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len)
+{
+ if ((in_len != 0) || (out_len != 0)) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ if (partition_init_done) {
+ return CORE_TEST_ERRNO_SUCCESS;
+ } else {
+ return CORE_TEST_ERRNO_SP_NOT_INITED;
+ }
+}
+
+#ifndef TFM_PSA_API
+psa_status_t spm_core_test_sfn_direct_recursion(
+ struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len)
+{
+ uint32_t depth;
+ struct psa_invec new_vec = {NULL, sizeof(uint32_t)};
+
+ if ((in_len != 1) || (out_len != 0) ||
+ (in_vec[0].len != sizeof(uint32_t))) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ depth = *((uint32_t *)in_vec[0].base);
+
+ if (depth != 0) {
+ /* Protect against scenario where TF-M core fails to block recursion */
+ return CORE_TEST_ERRNO_SP_RECURSION_NOT_REJECTED;
+ }
+ /* Call to the same service again, should be rejected */
+ depth += 1;
+ new_vec.base = &depth;
+ int32_t ret = tfm_spm_core_test_sfn_direct_recursion_veneer(&new_vec,
+ 1, NULL, 0);
+
+ if (ret == CORE_TEST_ERRNO_SUCCESS) {
+ /* This is an unexpected return value */
+ return CORE_TEST_ERRNO_UNEXPECTED_CORE_BEHAVIOUR;
+ } else if (ret == CORE_TEST_ERRNO_SP_RECURSION_NOT_REJECTED) {
+ /* This means that service was started in recursion */
+ return CORE_TEST_ERRNO_SP_RECURSION_NOT_REJECTED;
+ } else {
+ return CORE_TEST_ERRNO_SUCCESS;
+ }
+}
+#endif /* !defined(TFM_PSA_API) */
+
+static psa_status_t test_peripheral_access(void)
+{
+#ifdef TFM_ENABLE_PERIPH_ACCESS_TEST
+ uint32_t leds;
+ uint32_t invleds;
+ uint32_t userled_mask;
+
+ leds = tfm_plat_test_get_led_status();
+ tfm_plat_test_set_led_status(~leds);
+ invleds = tfm_plat_test_get_led_status();
+ userled_mask = tfm_plat_test_get_userled_mask();
+
+ if ((invleds & userled_mask) != (~leds & userled_mask)) {
+ /* Code failed to invert value in peripheral reg */
+ return CORE_TEST_ERRNO_PERIPHERAL_ACCESS_FAILED;
+ }
+
+ return CORE_TEST_ERRNO_SUCCESS;
+#else
+ return CORE_TEST_ERRNO_TEST_NOT_SUPPORTED;
+#endif
+}
+
+#define SS_BUFFER_LEN 16
+
+static psa_status_t test_ss_to_ss_buffer(uint32_t *in_ptr, uint32_t *out_ptr,
+ int32_t len)
+{
+ int32_t i;
+ /* Service internal buffer */
+ uint32_t ss_buffer[SS_BUFFER_LEN] = {0};
+ uint32_t slave_buffer [len];
+ int32_t result;
+ int32_t *result_ptr = &result;
+ int32_t res;
+ psa_invec in_vec[] = { {slave_buffer, len*sizeof(uint32_t)} };
+ psa_outvec outvec[] = { {slave_buffer, len*sizeof(uint32_t)},
+ {result_ptr, sizeof(int32_t)} };
+
+ if (len > SS_BUFFER_LEN) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+
+ for (i = 0; i < len; i++) {
+ ss_buffer[i] = in_ptr[i];
+ }
+
+ for (i = 0; i < len; i++) {
+ slave_buffer[i] = ss_buffer[i];
+ }
+
+ /* Call internal service with buffer handling */
+
+#ifdef TFM_PSA_API
+ res = psa_test_common(SPM_CORE_TEST_2_INVERT_SID,
+ SPM_CORE_TEST_2_INVERT_VERSION,
+ in_vec, 1, outvec, 2);
+#else /* defined(TFM_PSA_API) */
+ res = tfm_spm_core_test_2_sfn_invert_veneer(in_vec, 1, outvec, 2);
+#endif /* defined(TFM_PSA_API) */
+
+ if (res != CORE_TEST_ERRNO_SUCCESS) {
+ return CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (slave_buffer[i] != ~ss_buffer[i]) {
+ return CORE_TEST_ERRNO_SLAVE_SP_BUFFER_FAILURE;
+ }
+ ss_buffer[i] = slave_buffer[i];
+ }
+
+ for (i = 0; i < len; i++) {
+ out_ptr[i] = ss_buffer[i];
+ }
+
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+static psa_status_t test_outvec_write(void)
+{
+ int32_t err;
+ int i;
+ uint8_t data_buf [36]; /* (6 + 12) * 2 = 36 plus some alignment */
+ uint8_t *data_buf_ptr = data_buf;
+ psa_invec in_vec [2];
+ psa_outvec out_vec [2];
+ uint8_t *in_buf_0;
+ uint8_t *in_buf_1;
+ uint8_t *out_buf_0;
+ uint8_t *out_buf_1;
+
+ in_buf_0 = data_buf_ptr;
+ for (i = 0; i < 5; ++i, ++data_buf_ptr)
+ {
+ *data_buf_ptr = i;
+ }
+ in_vec[0].base = in_buf_0;
+ in_vec[0].len = data_buf_ptr - in_buf_0;
+
+ in_buf_1 = data_buf_ptr;
+ *(data_buf_ptr++) = 1;
+ *(data_buf_ptr++) = 1;
+ for (i = 2; i < 11; ++i, ++data_buf_ptr)
+ {
+ *data_buf_ptr = *(data_buf_ptr-1) + *(data_buf_ptr-2);
+ }
+ in_vec[1].base = in_buf_1;
+ in_vec[1].len = data_buf_ptr - in_buf_1;
+
+ out_buf_0 = data_buf_ptr;
+ data_buf_ptr += in_vec[0].len;
+ out_vec[0].base = out_buf_0;
+ out_vec[0].len = data_buf_ptr - out_buf_0;
+
+ out_buf_1 = data_buf_ptr;
+ data_buf_ptr += in_vec[1].len;
+ out_vec[1].base = out_buf_1;
+ out_vec[1].len = data_buf_ptr - out_buf_1;
+
+#ifdef TFM_PSA_API
+ err = psa_test_common(SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE_SID,
+ SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE_VERSION,
+ in_vec, 2, out_vec, 2);
+#else /* defined(TFM_PSA_API) */
+ err = tfm_spm_core_test_2_get_every_second_byte_veneer(in_vec, 2,
+ out_vec, 2);
+#endif /* defined(TFM_PSA_API) */
+
+ if (err != CORE_TEST_ERRNO_SUCCESS) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+
+ if (out_vec[0].len != in_vec[0].len/2 ||
+ out_vec[1].len != in_vec[1].len/2) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+ for (i = 1; i < sizeof(in_buf_0); i += 2) {
+ if (((uint8_t *)out_vec[0].base)[i/2] != in_buf_0[i]) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+ }
+ for (i = 1; i < sizeof(in_buf_1); i += 2) {
+ if (((uint8_t *)out_vec[1].base)[i/2] != in_buf_1[i]) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+ }
+
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+static psa_status_t test_ss_to_ss(void)
+{
+ int32_t ret;
+ /* Call to a different service, should be successful */
+ IRQ_TEST_TOOL_CODE_LOCATION(example_secure_service_start);
+#ifdef TFM_PSA_API
+ ret = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+ SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+ NULL, 0, NULL, 0);
+#else /* defined(TFM_PSA_API) */
+ ret = tfm_spm_core_test_2_slave_service_veneer(NULL, 0, NULL, 0);
+#endif /* defined(TFM_PSA_API) */
+ IRQ_TEST_TOOL_CODE_LOCATION(example_secure_service_end);
+ if (ret == CORE_TEST_ERRNO_SUCCESS_2) {
+ return CORE_TEST_ERRNO_SUCCESS;
+ } else {
+ return CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE;
+ }
+}
+
+#ifndef TFM_PSA_API
+static psa_status_t test_get_caller_client_id(void)
+{
+ /* Call to a special service that checks the caller service ID */
+ size_t i;
+ int32_t ret;
+ int32_t caller_client_id_stack = INVALID_NS_CLIENT_ID;
+
+ caller_client_id_zi = INVALID_NS_CLIENT_ID;
+
+ ret = tfm_spm_core_test_2_check_caller_client_id_veneer(NULL, 0, NULL, 0);
+ if (ret != CORE_TEST_ERRNO_SUCCESS) {
+ return CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE;
+ }
+
+ /* test with invalid output pointers */
+ for (i = 0; i < sizeof(invalid_addresses)/sizeof(invalid_addresses[0]); ++i)
+ {
+ ret = tfm_core_get_caller_client_id(invalid_addresses[i]);
+ if (ret != TFM_ERROR_INVALID_PARAMETER) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+ }
+
+ /* test with valid output pointers */
+ ret = tfm_core_get_caller_client_id(&caller_client_id_zi);
+ if (ret != TFM_SUCCESS || caller_client_id_zi != EXPECTED_NS_CLIENT_ID) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+
+ ret = tfm_core_get_caller_client_id(&caller_client_id_rw);
+ if (ret != TFM_SUCCESS || caller_client_id_rw != EXPECTED_NS_CLIENT_ID) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+
+ ret = tfm_core_get_caller_client_id(&caller_client_id_stack);
+ if (ret != TFM_SUCCESS ||
+ caller_client_id_stack != EXPECTED_NS_CLIENT_ID) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+static psa_status_t test_spm_request(void)
+{
+ /* Request a reset vote, should be successful */
+ int32_t ret = tfm_spm_request_reset_vote();
+
+ if (ret != TFM_SUCCESS) {
+ return CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE;
+ }
+
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+#endif /* !defined(TFM_PSA_API) */
+
+#ifdef CORE_TEST_INTERACTIVE
+static void wait_button_event(void)
+{
+ tfm_plat_test_wait_user_button_pressed();
+ /*
+ * The follow wait is required to skip multiple continues in one go due to
+ * the fast execution of the code and time used by the user to
+ * release button.
+ */
+
+ tfm_plat_test_wait_user_button_released();
+}
+
+psa_status_t test_wait_button(void)
+{
+ TEST_LOG("Inside the service, press button to continue...");
+ wait_button_event();
+ TEST_LOG("Leaving the service");
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+#endif /* defined(CORE_TEST_INTERACTIVE) */
+
+static psa_status_t test_block(void)
+{
+#ifdef CORE_TEST_INTERACTIVE
+ /* Only block if interactive test is turned on */
+ return test_wait_button();
+#else /* defined(CORE_TEST_INTERACTIVE) */
+ /* This test should not be run if interactive tests are disabled */
+ return CORE_TEST_ERRNO_TEST_FAULT;
+#endif /* defined(CORE_TEST_INTERACTIVE) */
+}
+
+#ifndef TFM_PSA_API
+psa_status_t spm_core_test_sfn(struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len)
+{
+ uint32_t tc;
+ int32_t arg1;
+ int32_t arg2;
+ int32_t arg3;
+
+ if ((in_len < 1) || (in_vec[0].len != sizeof(uint32_t))) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+ tc = *((uint32_t *)in_vec[0].base);
+
+ switch (tc) {
+ case CORE_TEST_ID_SS_TO_SS:
+ return test_ss_to_ss();
+ case CORE_TEST_ID_SS_TO_SS_BUFFER:
+ if ((in_len != 3) || (out_len != 1) ||
+ (in_vec[2].len != sizeof(int32_t))) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+ arg3 = *((int32_t *)in_vec[2].base);
+ if ((in_vec[1].len < arg3*sizeof(int32_t)) ||
+ (out_vec[0].len < arg3*sizeof(int32_t))) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+ arg1 = (int32_t)in_vec[1].base;
+ arg2 = (int32_t)out_vec[0].base;
+ return test_ss_to_ss_buffer((uint32_t *)arg1, (uint32_t *)arg2, arg3);
+ case CORE_TEST_ID_OUTVEC_WRITE:
+ return test_outvec_write();
+ case CORE_TEST_ID_PERIPHERAL_ACCESS:
+ return test_peripheral_access();
+ case CORE_TEST_ID_GET_CALLER_CLIENT_ID:
+ return test_get_caller_client_id();
+ case CORE_TEST_ID_SPM_REQUEST:
+ return test_spm_request();
+ case CORE_TEST_ID_BLOCK:
+ return test_block();
+ case CORE_TEST_ID_NS_THREAD:
+ /* dummy service call is enough */
+ return CORE_TEST_ERRNO_SUCCESS;
+ default:
+ return CORE_TEST_ERRNO_INVALID_TEST_ID;
+ }
+}
+
+#else /* !defined(TFM_PSA_API) */
+
+#define SS_TO_SS_BUFFER_SIZE (16*4)
+
+typedef psa_status_t (*core_test_func_t)(psa_msg_t *msg);
+
+static psa_status_t tfm_core_test_sfn_wrap_init_success(psa_msg_t *msg)
+{
+ return spm_core_test_sfn_init_success(NULL, 0, NULL, 0);
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_direct_recursion(psa_msg_t *msg)
+{
+ return CORE_TEST_ERRNO_TEST_FAULT;
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_ss_to_ss(psa_msg_t *msg)
+{
+ return test_ss_to_ss();
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_ss_to_ss_buffer(psa_msg_t *msg)
+{
+ size_t num;
+ uint32_t inbuf[SS_TO_SS_BUFFER_SIZE/sizeof(uint32_t)] = {0};
+ uint32_t outbuf[SS_TO_SS_BUFFER_SIZE/sizeof(uint32_t)] = {0};
+ uint32_t len;
+ psa_status_t res;
+
+ if ((msg->in_size[0] > SS_TO_SS_BUFFER_SIZE) ||
+ (msg->in_size[1] != sizeof(int32_t)) ||
+ (msg->out_size[0] > SS_TO_SS_BUFFER_SIZE)) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ num = psa_read(msg->handle, 0, inbuf, msg->in_size[0]);
+ if (num != msg->in_size[0]) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ num = psa_read(msg->handle, 1, &len, sizeof(int32_t));
+ if (num != sizeof(int32_t)) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ if (len > SS_TO_SS_BUFFER_SIZE) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ res = test_ss_to_ss_buffer(inbuf, outbuf, len);
+ if (res < 0) {
+ return res;
+ }
+
+ psa_write(msg->handle, 0, outbuf, len * sizeof(uint32_t));
+
+ return res;
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_outvec_write(psa_msg_t *msg)
+{
+ return test_outvec_write();
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_peripheral_access(psa_msg_t *msg)
+{
+ return test_peripheral_access();
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_get_caller_client_id(psa_msg_t *msg)
+{
+ return CORE_TEST_ERRNO_TEST_NOT_SUPPORTED;
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_spm_request(psa_msg_t *msg)
+{
+ return CORE_TEST_ERRNO_TEST_NOT_SUPPORTED;
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_block(psa_msg_t *msg)
+{
+ return test_block();
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_ns_thread(psa_msg_t *msg)
+{
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+static void core_test_signal_handle(psa_signal_t signal, core_test_func_t pfn)
+{
+ psa_msg_t msg;
+ psa_status_t status;
+
+ status = psa_get(signal, &msg);
+ if (status) {
+ return;
+ }
+
+ switch (msg.type) {
+ case PSA_IPC_CONNECT:
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ case PSA_IPC_CALL:
+ status = pfn(&msg);
+ psa_reply(msg.handle, status);
+ break;
+ case PSA_IPC_DISCONNECT:
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ default:
+ break;
+ }
+}
+#endif /* !defined(TFM_PSA_API) */
+
+psa_status_t core_test_init(void)
+{
+#ifdef TFM_PSA_API
+ psa_signal_t signals = 0;
+#endif /* defined(TFM_PSA_API) */
+
+ partition_init_done = 1;
+
+#ifdef TFM_PSA_API
+ while (1) {
+ signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK);
+ if (signals & SPM_CORE_TEST_INIT_SUCCESS_SIGNAL) {
+ core_test_signal_handle(SPM_CORE_TEST_INIT_SUCCESS_SIGNAL,
+ tfm_core_test_sfn_wrap_init_success);
+ } else if (signals & SPM_CORE_TEST_DIRECT_RECURSION_SIGNAL) {
+ core_test_signal_handle(SPM_CORE_TEST_DIRECT_RECURSION_SIGNAL,
+ tfm_core_test_sfn_wrap_direct_recursion);
+ } else if (signals & SPM_CORE_TEST_SS_TO_SS_SIGNAL) {
+ core_test_signal_handle(SPM_CORE_TEST_SS_TO_SS_SIGNAL,
+ tfm_core_test_sfn_wrap_ss_to_ss);
+ } else if (signals & SPM_CORE_TEST_SS_TO_SS_BUFFER_SIGNAL) {
+ core_test_signal_handle(SPM_CORE_TEST_SS_TO_SS_BUFFER_SIGNAL,
+ tfm_core_test_sfn_wrap_ss_to_ss_buffer);
+ } else if (signals & SPM_CORE_TEST_OUTVEC_WRITE_SIGNAL) {
+ core_test_signal_handle(SPM_CORE_TEST_OUTVEC_WRITE_SIGNAL,
+ tfm_core_test_sfn_wrap_outvec_write);
+ } else if (signals & SPM_CORE_TEST_PERIPHERAL_ACCESS_SIGNAL) {
+ core_test_signal_handle(SPM_CORE_TEST_PERIPHERAL_ACCESS_SIGNAL,
+ tfm_core_test_sfn_wrap_peripheral_access);
+ } else if (signals & SPM_CORE_TEST_GET_CALLER_CLIENT_ID_SIGNAL) {
+ core_test_signal_handle(SPM_CORE_TEST_GET_CALLER_CLIENT_ID_SIGNAL,
+ tfm_core_test_sfn_wrap_get_caller_client_id);
+ } else if (signals & SPM_CORE_TEST_SPM_REQUEST_SIGNAL) {
+ core_test_signal_handle(SPM_CORE_TEST_SPM_REQUEST_SIGNAL,
+ tfm_core_test_sfn_wrap_spm_request);
+ } else if (signals & SPM_CORE_TEST_BLOCK_SIGNAL) {
+ core_test_signal_handle(SPM_CORE_TEST_BLOCK_SIGNAL,
+ tfm_core_test_sfn_wrap_block);
+ } else if (signals & SPM_CORE_TEST_NS_THREAD_SIGNAL) {
+ core_test_signal_handle(SPM_CORE_TEST_NS_THREAD_SIGNAL,
+ tfm_core_test_sfn_wrap_ns_thread);
+ } else {
+ ; /* do nothing */
+ }
+ }
+#else
+ return CORE_TEST_ERRNO_SUCCESS;
+#endif /* defined(TFM_PSA_API) */
+}
diff --git a/test/test_services/tfm_core_test/tfm_ss_core_test.h b/test/test_services/tfm_core_test/tfm_ss_core_test.h
new file mode 100644
index 0000000..eb9598f
--- /dev/null
+++ b/test/test_services/tfm_core_test/tfm_ss_core_test.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_SS_CORE_TEST_H__
+#define __TFM_SS_CORE_TEST_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <inttypes.h>
+#include <limits.h>
+#include "tfm_api.h"
+
+/**
+ * \brief Tests whether the initialisation of the service was successful.
+ *
+ * \param[in] in_vec Array of psa_invec objects
+ * \param[in] in_len Number psa_invec objects in in_vec
+ * \param[in] out_vec Array of psa_outvec objects
+ * \param[in] out_len Number psa_outvec objects in out_vec
+ *
+ * \param[in] in_vec Array of psa_invec objects
+ * \param[in] in_len Number psa_invec objects in in_vec
+ * \param[in] out_vec Array of psa_outvec objects
+ * \param[in] out_len Number psa_outvec objects in out_vec
+ *
+ * The function expects 0 in_vec objects.
+ * The function expects 0 out_vec objects.
+ *
+ * \return Returns \ref CORE_TEST_ERRNO_SUCCESS on success, and
+ * \ref CORE_TEST_ERRNO_SP_NOT_INITED on failure.
+ */
+psa_status_t spm_core_test_sfn_init_success(
+ struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len);
+
+/**
+ * \brief Tests what happens when a service calls itself directly.
+ *
+ * \param[in] in_vec Array of psa_invec objects
+ * \param[in] in_len Number psa_invec objects in in_vec
+ * \param[in] out_vec Array of psa_outvec objects
+ * \param[in] out_len Number psa_outvec objects in out_vec
+ *
+ * The function expects 1 in_vec object:
+ * in_vec[0].base: A buffer containing a pointer to an uint32_t value
+ * containing the current depth of the call (the value of the
+ * depth is 0 when first called).
+ * in_vec[0].len: The size of a pointer in bytes.
+
+ * The function expects 0 out_vec objects.
+ *
+ * \return Returns \ref CORE_TEST_ERRNO_SUCCESS.
+ */
+psa_status_t spm_core_test_sfn_direct_recursion(
+ struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len);
+
+/**
+ * \brief Entry point for multiple test cases to be executed on the secure side.
+ *
+ * \param[in] in_vec Array of psa_invec objects
+ * \param[in] in_len Number psa_invec objects in in_vec
+ * \param[in] out_vec Array of psa_outvec objects
+ * \param[in] out_len Number psa_outvec objects in out_vec
+ *
+ * The function expects at least 1 in_vec object:
+ *
+ * in_vec[0].base: A buffer containing a pointer to an uint32_t value
+ * containing the testcase id to be executed.
+ * in_vec[0].len: The size of a pointer in bytes.
+ *
+ * The number of expected additional in_vecs and out_vecs is dependent on the id
+ * of the test case. For details see the function implementation.
+ *
+ * \return Can return various error codes.
+ */
+psa_status_t spm_core_test_sfn(struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_SS_CORE_TEST_H__ */
diff --git a/test/test_services/tfm_core_test/tfm_test_core.yaml b/test/test_services/tfm_core_test/tfm_test_core.yaml
new file mode 100644
index 0000000..a7e6a81
--- /dev/null
+++ b/test/test_services/tfm_core_test/tfm_test_core.yaml
@@ -0,0 +1,121 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+ "psa_framework_version": 1.0,
+ "name": "TFM_SP_CORE_TEST",
+ "type": "PSA-ROT",
+ "priority": "NORMAL",
+ "entry_point": "core_test_init",
+ "stack_size": "0x0380",
+ "mmio_regions": [
+ {
+ "name": "TFM_PERIPHERAL_FPGA_IO",
+ "permission": "READ-WRITE"
+ }
+ ],
+ "secure_functions": [
+ {
+ "name": "TFM_CORE_TEST_SFN",
+ "signal": "SPM_CORE_TEST_SFN",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_CORE_TEST_SFN_INIT_SUCCESS",
+ "signal": "SPM_CORE_TEST_SFN_INIT_SUCCESS",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_CORE_TEST_SFN_DIRECT_RECURSION",
+ "signal": "SPM_CORE_TEST_SFN_DIRECT_RECURSION",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ }
+ ],
+ "services": [
+ {
+ "name": "SPM_CORE_TEST_INIT_SUCCESS",
+ "sid": "0x0000F020",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_TEST_DIRECT_RECURSION",
+ "sid": "0x0000F021",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_TEST_SS_TO_SS",
+ "sid": "0x0000F024",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_TEST_SS_TO_SS_BUFFER",
+ "sid": "0x0000F025",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_TEST_OUTVEC_WRITE",
+ "sid": "0x0000F026",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_TEST_PERIPHERAL_ACCESS",
+ "sid": "0x0000F027",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_TEST_GET_CALLER_CLIENT_ID",
+ "sid": "0x0000F028",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_TEST_SPM_REQUEST",
+ "sid": "0x0000F029",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_TEST_BLOCK",
+ "sid": "0x0000F02A",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_TEST_NS_THREAD",
+ "sid": "0x0000F02B",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ }
+ ],
+ "dependencies": [
+ "SPM_CORE_TEST_2_INVERT",
+ "SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE",
+ "SPM_CORE_TEST_2_SLAVE_SERVICE"
+ ]
+}
diff --git a/test/test_services/tfm_core_test_2/psa_manifest/tfm_test_core_2.h b/test/test_services/tfm_core_test_2/psa_manifest/tfm_test_core_2.h
new file mode 100644
index 0000000..c636835
--- /dev/null
+++ b/test/test_services/tfm_core_test_2/psa_manifest/tfm_test_core_2.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_TEST_CORE_2_H__
+#define __PSA_MANIFEST_TFM_TEST_CORE_2_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SPM_CORE_TEST_2_SLAVE_SERVICE_SIGNAL (1U << (0 + 4))
+#define SPM_CORE_TEST_2_CHECK_CALLER_CLIENT_ID_SIGNAL (1U << (1 + 4))
+#define SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE_SIGNAL (1U << (2 + 4))
+#define SPM_CORE_TEST_2_INVERT_SIGNAL (1U << (3 + 4))
+#define SPM_CORE_TEST_2_PREPARE_TEST_SCENARIO_SIGNAL (1U << (4 + 4))
+#define SPM_CORE_TEST_2_EXECUTE_TEST_SCENARIO_SIGNAL (1U << (5 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_TEST_CORE_2_H__ */
diff --git a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.c b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.c
new file mode 100644
index 0000000..e9d8c95
--- /dev/null
+++ b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stddef.h>
+#include "test/test_services/tfm_core_test/core_test_defs.h"
+#include "tfm_ss_core_test_2.h"
+#include "tfm_api.h"
+#include "tfm_secure_api.h"
+#include "psa/service.h"
+#include "psa_manifest/pid.h"
+#include "psa_manifest/tfm_test_core_2.h"
+
+#define INVALID_NS_CLIENT_ID 0x49abcdef
+#define INVERT_BUFFER_SIZE (16*4)
+
+#ifndef TFM_PSA_API
+/* Don't initialise caller_partition_id_zi and expect it to be linked in the
+ * zero-initialised data area
+ */
+static int32_t caller_client_id_zi;
+
+/* Initialise caller_partition_id_rw and expect it to be linked in the
+ * read-write data area
+ */
+static int32_t caller_client_id_rw = INVALID_NS_CLIENT_ID;
+
+static int32_t* invalid_addresses [] = {(int32_t*)0x0, (int32_t*)0xFFF12000};
+#endif /* !defined(TFM_PSA_API) */
+
+/* structures for secure IRQ testing */
+static struct irq_test_execution_data_t *current_execution_data;
+
+psa_status_t spm_core_test_2_slave_service(struct psa_invec *in_vec,
+ size_t in_len,
+ struct psa_outvec *out_vec,
+ size_t out_len)
+{
+ /* This function doesn't do any sanity check on the input parameters, nor
+ * makes any expectation of them, always returns successfully, with a
+ * non-zero return value.
+ * This is to test the parameter sanitization mechanisms implemented in SPM,
+ * and the handling of non-zero success codes.
+ */
+
+ return CORE_TEST_ERRNO_SUCCESS_2;
+}
+
+#ifndef TFM_PSA_API
+psa_status_t spm_core_test_2_check_caller_client_id(struct psa_invec *in_vec,
+ size_t in_len,
+ struct psa_outvec *out_vec,
+ size_t out_len)
+{
+ size_t i;
+ int32_t caller_client_id_stack = INVALID_NS_CLIENT_ID;
+ int32_t ret;
+
+ caller_client_id_zi = INVALID_NS_CLIENT_ID;
+
+ /* test with invalid output pointers */
+ for (i = 0; i < sizeof(invalid_addresses)/sizeof(invalid_addresses[0]); ++i)
+ {
+ ret = tfm_core_get_caller_client_id(invalid_addresses[i]);
+ if (ret != TFM_ERROR_INVALID_PARAMETER) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+ }
+
+ /* test with valid output pointers */
+ ret = tfm_core_get_caller_client_id(&caller_client_id_zi);
+ if (ret != TFM_SUCCESS || caller_client_id_zi != TFM_SP_CORE_TEST) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+
+ ret = tfm_core_get_caller_client_id(&caller_client_id_rw);
+ if (ret != TFM_SUCCESS || caller_client_id_rw != TFM_SP_CORE_TEST) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+
+ ret = tfm_core_get_caller_client_id(&caller_client_id_stack);
+ if (ret != TFM_SUCCESS ||
+ caller_client_id_stack != TFM_SP_CORE_TEST) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+#endif /* !defined(TFM_PSA_API) */
+
+psa_status_t spm_core_test_2_get_every_second_byte_internal(
+ const uint8_t *inbuf,
+ uint8_t *outbuf,
+ size_t in_size,
+ size_t *out_size)
+{
+ int j;
+
+ if (in_size/2 > *out_size) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+ for (j = 1; j < in_size; j += 2) {
+ outbuf[j/2] = inbuf[j];
+ }
+ *out_size = in_size/2;
+
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+psa_status_t spm_core_test_2_get_every_second_byte(
+ struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len)
+{
+ int i;
+ psa_status_t res;
+
+ if (in_len != out_len) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+ for (i = 0; i < in_len; ++i) {
+ res = spm_core_test_2_get_every_second_byte_internal(
+ in_vec[i].base, out_vec[i].base,
+ in_vec[i].len, &out_vec[i].len);
+ if (res < 0) {
+ return res;
+ }
+ }
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+/* Invert function */
+#define SFN_INVERT_MAX_LEN 128
+static psa_status_t spm_core_test_2_sfn_invert_internal(uint32_t *in_ptr,
+ uint32_t *out_ptr,
+ int32_t *res_ptr,
+ int32_t len)
+{
+ int32_t i;
+ static uint32_t invert_buffer[SFN_INVERT_MAX_LEN];
+
+ *res_ptr = -1;
+
+ if (len > SFN_INVERT_MAX_LEN) {
+ return CORE_TEST_ERRNO_INVALID_BUFFER;
+ }
+
+ for (i = 0; i < len; i++) {
+ invert_buffer[i] = in_ptr[i];
+ }
+ for (i = 0; i < len; i++) {
+ invert_buffer[i] = ~invert_buffer[i];
+ }
+ for (i = 0; i < len; i++) {
+ out_ptr[i] = invert_buffer[i];
+ }
+
+ *res_ptr = 0;
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+psa_status_t spm_core_test_2_sfn_invert(
+ struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len)
+{
+ int32_t len;
+ uint32_t *in_ptr;
+ uint32_t *out_ptr;
+ int32_t *res_ptr;
+
+ if ((in_len != 1) || (out_len != 2)) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ if ((out_vec[0].len < in_vec[0].len) || (in_vec[0].len%4 != 0) ||
+ (out_vec[1].len < sizeof(int32_t))) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ len = in_vec[0].len / 4;
+
+ in_ptr = (uint32_t *)in_vec[0].base;
+ out_ptr = (uint32_t *)out_vec[0].base;
+ res_ptr = (int32_t *)out_vec[1].base;
+
+ return spm_core_test_2_sfn_invert_internal(in_ptr, out_ptr, res_ptr, len);
+}
+
+static psa_status_t spm_core_test_2_prepare_test_scenario_internal(
+ enum irq_test_scenario_t irq_test_scenario,
+ struct irq_test_execution_data_t *execution_data)
+{
+ current_execution_data = execution_data;
+
+ switch (irq_test_scenario) {
+ case IRQ_TEST_SCENARIO_NONE:
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ case IRQ_TEST_SCENARIO_1:
+ case IRQ_TEST_SCENARIO_2:
+ case IRQ_TEST_SCENARIO_3:
+ case IRQ_TEST_SCENARIO_4:
+ case IRQ_TEST_SCENARIO_5:
+ /* No action is necessary*/
+ break;
+ default:
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+psa_status_t spm_core_test_2_prepare_test_scenario(
+ struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_size)
+{
+ if ((in_len != 2) ||
+ (in_vec[0].len != sizeof(uint32_t)) ||
+ (in_vec[1].len != sizeof(struct irq_test_execution_data_t *))) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ enum irq_test_scenario_t irq_test_scenario =
+ (enum irq_test_scenario_t) *(uint32_t *)in_vec[0].base;
+
+ struct irq_test_execution_data_t *execution_data =
+ *(struct irq_test_execution_data_t **)in_vec[1].base;
+
+ return spm_core_test_2_prepare_test_scenario_internal(irq_test_scenario,
+ execution_data);
+}
+
+static psa_status_t spm_core_test_2_execute_test_scenario_internal(
+ enum irq_test_scenario_t irq_test_scenario)
+{
+ switch (irq_test_scenario) {
+ case IRQ_TEST_SCENARIO_NONE:
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ case IRQ_TEST_SCENARIO_1:
+ /* No action is necessary*/
+ break;
+ case IRQ_TEST_SCENARIO_2:
+ if (current_execution_data->timer0_triggered) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+ while (!current_execution_data->timer0_triggered) {
+ ;
+ }
+ break;
+ case IRQ_TEST_SCENARIO_3:
+ case IRQ_TEST_SCENARIO_4:
+ /* No action is necessary*/
+ break;
+ case IRQ_TEST_SCENARIO_5:
+ if (current_execution_data->timer1_triggered) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+ while (!current_execution_data->timer1_triggered) {
+ ;
+ }
+ break;
+ default:
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+psa_status_t spm_core_test_2_execute_test_scenario(
+ struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_size)
+{
+ enum irq_test_scenario_t irq_test_scenario =
+ (enum irq_test_scenario_t) *(uint32_t *)in_vec[0].base;
+
+ return spm_core_test_2_execute_test_scenario_internal(irq_test_scenario);
+}
+
+
+#ifdef TFM_PSA_API
+
+typedef psa_status_t (*core_test_2_func_t)(psa_msg_t *msg);
+
+static void core_test_2_signal_handle(psa_signal_t signal,
+ core_test_2_func_t pfn)
+{
+ psa_msg_t msg;
+ psa_status_t status;
+
+ status = psa_get(signal, &msg);
+ if (status) {
+ return;
+ }
+
+ switch (msg.type) {
+ case PSA_IPC_CONNECT:
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ case PSA_IPC_CALL:
+ status = pfn(&msg);
+ psa_reply(msg.handle, status);
+ break;
+ case PSA_IPC_DISCONNECT:
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ default:
+ break;
+ }
+}
+
+psa_status_t spm_core_test_2_wrap_slave_service(psa_msg_t *msg)
+{
+ return spm_core_test_2_slave_service(NULL, 0, NULL, 0);
+}
+
+psa_status_t spm_core_test_2_wrap_check_caller_client_id(psa_msg_t *msg)
+{
+ return CORE_TEST_ERRNO_TEST_NOT_SUPPORTED;
+}
+
+psa_status_t spm_core_test_2_wrap_get_every_second_byte(psa_msg_t *msg)
+{
+ uint32_t inbuf[INVERT_BUFFER_SIZE/sizeof(uint32_t)] = {0};
+ uint32_t outbuf[INVERT_BUFFER_SIZE/sizeof(uint32_t)] = {0};
+
+ int i;
+ size_t num;
+ size_t out_len;
+ psa_status_t res;
+
+ for (i = 0; i < PSA_MAX_IOVEC; ++i) {
+ if (msg->in_size[i] > INVERT_BUFFER_SIZE) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ if (msg->in_size[i] == 0) {
+ continue;
+ }
+
+ num = psa_read(msg->handle, i, inbuf, msg->in_size[i]);
+ if (num != msg->in_size[i]) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ out_len = msg->out_size[i];
+
+ res = spm_core_test_2_get_every_second_byte_internal((uint8_t *)inbuf,
+ (uint8_t *)outbuf, msg->in_size[i], &out_len);
+ if (res < 0) {
+ return res;
+ }
+
+ psa_write(msg->handle, i, outbuf, out_len);
+ }
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+psa_status_t spm_core_test_2_wrap_sfn_invert(psa_msg_t *msg)
+{
+ uint32_t inbuf[INVERT_BUFFER_SIZE/sizeof(uint32_t)] = {0};
+ uint32_t outbuf[INVERT_BUFFER_SIZE/sizeof(uint32_t)] = {0};
+ size_t num;
+ int32_t res_ptr;
+ psa_status_t ret;
+
+ if ((msg->out_size[0] < msg->in_size[0]) ||
+ (msg->in_size[0] > INVERT_BUFFER_SIZE) ||
+ (msg->in_size[0]%4 != 0) ||
+ (msg->out_size[1] < sizeof(int32_t))) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ num = psa_read(msg->handle, 0, inbuf, msg->in_size[0]);
+ if (num != msg->in_size[0]) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ ret = spm_core_test_2_sfn_invert_internal(inbuf, outbuf,
+ &res_ptr, msg->in_size[0] / 4);
+ if (ret < 0) {
+ return ret;
+ }
+
+ psa_write(msg->handle, 0, outbuf, msg->in_size[0]);
+ psa_write(msg->handle, 1, &res_ptr, sizeof(int32_t));
+
+ return ret;
+}
+
+psa_status_t spm_core_test_2_wrap_prepare_test_scenario(psa_msg_t *msg)
+{
+ uint32_t irq_test_scenario;
+ struct irq_test_execution_data_t *execution_data;
+ size_t num;
+
+ if ((msg->in_size[0] != sizeof(uint32_t)) ||
+ (msg->in_size[1] != sizeof(struct irq_test_execution_data_t*))) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ num = psa_read(msg->handle, 0, &irq_test_scenario, sizeof(irq_test_scenario));
+ if (num != msg->in_size[0]) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ num = psa_read(msg->handle, 1, &execution_data, sizeof(
+ struct irq_test_execution_data_t*));
+ if (num != msg->in_size[1]) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ return spm_core_test_2_prepare_test_scenario_internal((enum irq_test_scenario_t)
+ irq_test_scenario,
+ execution_data);
+}
+
+psa_status_t spm_core_test_2_wrap_execute_test_scenario(psa_msg_t *msg)
+{
+ uint32_t irq_test_scenario;
+ size_t num;
+
+ if (msg->in_size[0] != sizeof(uint32_t)) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ num = psa_read(msg->handle, 0, &irq_test_scenario, sizeof(irq_test_scenario));
+ if (num != msg->in_size[0]) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ return spm_core_test_2_execute_test_scenario_internal((enum irq_test_scenario_t)
+ irq_test_scenario);
+}
+
+#endif /* defined(TFM_PSA_API) */
+
+/* FIXME: Add a testcase to test that a failed init makes the secure partition
+ * closed, and none of its functions can be called.
+ * A new test service for this purpose is to be added.
+ */
+psa_status_t core_test_2_init(void)
+{
+#ifdef TFM_PSA_API
+ psa_signal_t signals = 0;
+
+ while (1) {
+ signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK);
+ if (signals & SPM_CORE_TEST_2_SLAVE_SERVICE_SIGNAL) {
+ core_test_2_signal_handle(SPM_CORE_TEST_2_SLAVE_SERVICE_SIGNAL,
+ spm_core_test_2_wrap_slave_service);
+ } else if (signals & SPM_CORE_TEST_2_CHECK_CALLER_CLIENT_ID_SIGNAL) {
+ core_test_2_signal_handle(
+ SPM_CORE_TEST_2_CHECK_CALLER_CLIENT_ID_SIGNAL,
+ spm_core_test_2_wrap_check_caller_client_id);
+ } else if (signals & SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE_SIGNAL) {
+ core_test_2_signal_handle(
+ SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE_SIGNAL,
+ spm_core_test_2_wrap_get_every_second_byte);
+ } else if (signals & SPM_CORE_TEST_2_INVERT_SIGNAL) {
+ core_test_2_signal_handle(SPM_CORE_TEST_2_INVERT_SIGNAL,
+ spm_core_test_2_wrap_sfn_invert);
+ } else if (signals & SPM_CORE_TEST_2_PREPARE_TEST_SCENARIO_SIGNAL) {
+ core_test_2_signal_handle(
+ SPM_CORE_TEST_2_PREPARE_TEST_SCENARIO_SIGNAL,
+ spm_core_test_2_wrap_prepare_test_scenario);
+ } else if (signals & SPM_CORE_TEST_2_EXECUTE_TEST_SCENARIO_SIGNAL) {
+ core_test_2_signal_handle(
+ SPM_CORE_TEST_2_EXECUTE_TEST_SCENARIO_SIGNAL,
+ spm_core_test_2_wrap_execute_test_scenario);
+ } else {
+ ; /* do nothing */
+ }
+ }
+#else
+ return CORE_TEST_ERRNO_SUCCESS;
+#endif /* defined(TFM_PSA_API) */
+}
diff --git a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.h b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.h
new file mode 100644
index 0000000..44b12df
--- /dev/null
+++ b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_SS_CORE_TEST_2_H__
+#define __TFM_SS_CORE_TEST_2_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <inttypes.h>
+#include <limits.h>
+
+#include "tfm_api.h"
+
+/**
+ * \brief A minimal test service to be called from another service.
+ *
+ * \param[in] in_vec Array of psa_invec objects
+ * \param[in] in_len Number psa_invec objects in in_vec
+ * \param[in] out_vec Array of psa_outvec objects
+ * \param[in] out_len Number psa_outvec objects in out_vec
+ *
+ * The function expects 0 in_vec objects.
+ * The function expects 0 out_vec objects.
+ *
+ * \return Returns \ref TFM_SUCCESS.
+ */
+psa_status_t spm_core_test_2_slave_service(
+ struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len);
+
+
+/**
+ * \brief Bitwise inverts the buffer received as input.
+ *
+ * \param[in] in_vec Array of psa_invec objects
+ * \param[in] in_len Number psa_invec objects in in_vec
+ * \param[in] out_vec Array of psa_outvec objects
+ * \param[in] out_len Number psa_outvec objects in out_vec
+ *
+ * The function expects 2 in_vec objects:
+ * in_vec[0].base: A pointer to the buffer containing the data to be inverted
+ * in_vec[0].len: The length of the buffer
+ * in_vec[1].base: A pointer to an int32_t object
+ * in_vec[1].len: The size of int32_t object
+ *
+ * The function expects 1 out_vec object:
+ * out_vec[0].base: A pointer to the buffer to put the result to
+ * out_vec[0].len: The length of the buffer
+ *
+ * \return Returns \ref TFM_SUCCESS on success, TFM_PARTITION_BUSY otherwise.
+ */
+psa_status_t spm_core_test_2_sfn_invert(struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len);
+
+/**
+ * \brief A minimal test secure function to be called from another partition.
+ *
+ * Checks the functionality of querying the client ID of the caller service.
+ *
+ * \param[in] in_vec Array of psa_invec objects
+ * \param[in] in_len Number psa_invec objects in in_vec
+ * \param[in] out_vec Array of psa_outvec objects
+ * \param[in] out_len Number psa_outvec objects in out_vec
+ *
+ * The function expects 0 in_vec objects.
+ * The function expects 0 out_vec objects.
+ *
+ * \return Returns \ref TFM_SUCCESS on success, \ref CORE_TEST_ERRNO_TEST_FAULT
+ * othervise.
+ */
+ psa_status_t spm_core_test_2_check_caller_client_id(
+ struct psa_invec *in_vec, size_t in_len, struct psa_outvec *out_vec,
+ size_t out_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_SS_CORE_TEST_2_H__ */
diff --git a/test/test_services/tfm_core_test_2/tfm_test_core_2.yaml b/test/test_services/tfm_core_test_2/tfm_test_core_2.yaml
new file mode 100644
index 0000000..8b5d43c
--- /dev/null
+++ b/test/test_services/tfm_core_test_2/tfm_test_core_2.yaml
@@ -0,0 +1,103 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+ "psa_framework_version": 1.0,
+ "name": "TFM_SP_CORE_TEST_2",
+ "type": "APPLICATION-ROT",
+ "priority": "NORMAL",
+ "entry_point": "core_test_2_init",
+ "stack_size": "0x0280",
+ "secure_functions": [
+ {
+ "name": "TFM_CORE_TEST_2_SFN_SLAVE_SERVICE",
+ "signal": "SPM_CORE_TEST_2_SLAVE_SERVICE",
+ "non_secure_clients": false,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_CORE_TEST_2_SFN_INVERT",
+ "signal": "SPM_CORE_TEST_2_SFN_INVERT",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_CORE_TEST_2_SFN_CHECK_CALLER_CLIENT_ID",
+ "signal": "SPM_CORE_TEST_2_CHECK_CALLER_CLIENT_ID",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_CORE_TEST_2_SFN_GET_EVERY_SECOND_BYTE",
+ "signal": "SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_CORE_TEST_2_SFN_PREPARE_TEST_SCENARIO",
+ "signal": "SPM_CORE_TEST_2_PREPARE_TEST_SCENARIO",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_CORE_TEST_2_SFN_EXECUTE_TEST_SCENARIO",
+ "signal": "SPM_CORE_TEST_2_EXECUTE_TEST_SCENARIO",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ }
+ ],
+ "services": [
+ {
+ "name": "SPM_CORE_TEST_2_SLAVE_SERVICE",
+ "sid": "0x0000F040",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_TEST_2_CHECK_CALLER_CLIENT_ID",
+ "sid": "0x0000F041",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE",
+ "sid": "0x0000F042",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_TEST_2_INVERT",
+ "sid": "0x0000F043",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_TEST_2_PREPARE_TEST_SCENARIO",
+ "sid": "0x0000F044",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_TEST_2_EXECUTE_TEST_SCENARIO",
+ "sid": "0x0000F045",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ ]
+}
diff --git a/test/test_services/tfm_ipc_client/psa_manifest/tfm_ipc_client_partition.h b/test/test_services/tfm_ipc_client/psa_manifest/tfm_ipc_client_partition.h
new file mode 100644
index 0000000..d6d7513
--- /dev/null
+++ b/test/test_services/tfm_ipc_client/psa_manifest/tfm_ipc_client_partition.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_IPC_CLIENT_PARTITION_H__
+#define __PSA_MANIFEST_TFM_IPC_CLIENT_PARTITION_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IPC_CLIENT_TEST_BASIC_SIGNAL (1U << (0 + 4))
+#define IPC_CLIENT_TEST_PSA_ACCESS_APP_MEM_SIGNAL (1U << (1 + 4))
+#define IPC_CLIENT_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL (1U << (2 + 4))
+#define IPC_CLIENT_TEST_APP_ACCESS_PSA_MEM_SIGNAL (1U << (3 + 4))
+#define IPC_CLIENT_TEST_MEM_CHECK_SIGNAL (1U << (4 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_IPC_CLIENT_PARTITION_H__ */
diff --git a/test/test_services/tfm_ipc_client/tfm_ipc_client_partition.yaml b/test/test_services/tfm_ipc_client/tfm_ipc_client_partition.yaml
new file mode 100644
index 0000000..e66139e
--- /dev/null
+++ b/test/test_services/tfm_ipc_client/tfm_ipc_client_partition.yaml
@@ -0,0 +1,60 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+ "psa_framework_version": 1.0,
+ "name": "TFM_SP_IPC_CLIENT_TEST",
+ "type": "APPLICATION-ROT",
+ "priority": "NORMAL",
+ "entry_point": "ipc_client_test_main",
+ "stack_size": "0x0300",
+ "secure_functions": [
+ ],
+ "services" : [
+ {
+ "name": "IPC_CLIENT_TEST_BASIC",
+ "sid": "0x0000F060",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "IPC_CLIENT_TEST_PSA_ACCESS_APP_MEM",
+ "sid": "0x0000F061",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "IPC_CLIENT_TEST_PSA_ACCESS_APP_READ_ONLY_MEM",
+ "sid": "0x0000F062",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "IPC_CLIENT_TEST_APP_ACCESS_PSA_MEM",
+ "sid": "0x0000F063",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "IPC_CLIENT_TEST_MEM_CHECK",
+ "sid": "0x0000F064",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ }
+ ],
+ "dependencies": [
+ "IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM",
+ "IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM",
+ "IPC_SERVICE_TEST_BASIC",
+ "IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM"
+ ]
+}
diff --git a/test/test_services/tfm_ipc_client/tfm_ipc_client_test.c b/test/test_services/tfm_ipc_client/tfm_ipc_client_test.c
new file mode 100644
index 0000000..40c5560
--- /dev/null
+++ b/test/test_services/tfm_ipc_client/tfm_ipc_client_test.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include "psa/client.h"
+#include "psa/service.h"
+#include "psa_manifest/tfm_ipc_client_partition.h"
+#include "utilities.h"
+#include "psa_manifest/sid.h"
+
+/* Define the return status */
+#define IPC_SP_TEST_SUCCESS (1)
+#define IPC_SP_TEST_FAILED (-1)
+
+/*
+ * The bit corresponding to service signal indicates whether
+ * the service is in use.
+ */
+uint32_t service_in_use = 0;
+
+/*
+ * Create a global const data, so that it is stored in code
+ * section which is read only.
+ */
+char const client_data_read_only = 'A';
+
+/*
+ * Fixme: Temporarily implement abort as infinite loop,
+ * will replace it later.
+ */
+static void tfm_abort(void)
+{
+ while (1)
+ ;
+}
+
+#ifdef TFM_IPC_ISOLATION_2_TEST_READ_ONLY_MEM
+static int ipc_isolation_2_psa_access_app_readonly_memory(void)
+{
+ psa_handle_t handle;
+ psa_status_t status;
+ char const *client_data_p = &client_data_read_only;
+ struct psa_invec invecs[1] = {{&client_data_p, sizeof(client_data_p)}};
+
+ handle = psa_connect(IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SID,
+ IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_VERSION);
+
+ if (handle <= 0) {
+ return IPC_SP_TEST_FAILED;
+ }
+
+ status = psa_call(handle, PSA_IPC_CALL, invecs, 1, NULL, 0);
+
+ /* The system should panic before here. */
+ psa_close(handle);
+ return IPC_SP_TEST_FAILED;
+}
+#endif
+
+static int ipc_isolation_2_psa_access_app_memory(void)
+{
+ psa_handle_t handle;
+ psa_status_t status;
+ int32_t result = IPC_SP_TEST_FAILED;
+ char client_data = 'A';
+ char *client_data_p = &client_data;
+ struct psa_invec invecs[1] = {{&client_data_p, sizeof(client_data_p)}};
+
+ handle = psa_connect(IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SID,
+ IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_VERSION);
+
+ if (handle <= 0) {
+ return result;
+ }
+
+ status = psa_call(handle, PSA_IPC_CALL, invecs, 1, NULL, 0);
+
+ if ((client_data == 'B') && (status >= 0)) {
+ result = IPC_SP_TEST_SUCCESS;
+ }
+
+ psa_close(handle);
+ return result;
+}
+
+static int ipc_client_base_test(void)
+{
+ psa_handle_t handle;
+ psa_status_t status;
+ int32_t result = IPC_SP_TEST_FAILED;
+ char str1[] = "123";
+ char str2[] = "456";
+ char str3[32], str4[32];
+ struct psa_invec invecs[2] = {{str1, sizeof(str1)/sizeof(char)},
+ {str2, sizeof(str2)/sizeof(char)}};
+ struct psa_outvec outvecs[2] = {{str3, sizeof(str3)/sizeof(char)},
+ {str4, sizeof(str4)/sizeof(char)}};
+
+ handle = psa_connect(IPC_SERVICE_TEST_BASIC_SID,
+ IPC_SERVICE_TEST_BASIC_VERSION);
+ if (handle <= 0) {
+ return result;
+ }
+
+ status = psa_call(handle, PSA_IPC_CALL, invecs, 2, outvecs, 2);
+ if (status >= 0) {
+ result = IPC_SP_TEST_SUCCESS;
+ }
+
+ psa_close(handle);
+ return result;
+}
+
+#ifdef TFM_IPC_ISOLATION_2_APP_ACCESS_PSA_MEM
+static int ipc_client_app_access_psa_mem_test(void)
+{
+ psa_handle_t handle;
+ psa_status_t status;
+ uint8_t *outvec_data[1] = {0};
+ struct psa_outvec outvecs[1] = {{outvec_data, sizeof(outvec_data[0])}};
+
+ handle = psa_connect(IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SID,
+ IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_VERSION);
+
+ if (handle <= 0) {
+ return IPC_SP_TEST_FAILED;
+ }
+
+ status = psa_call(handle, PSA_IPC_CALL, NULL, 0, outvecs, 1);
+ if (status >= 0) {
+ /*
+ * outvecs should contain the pointer pointed to ipc service parition
+ * memory. Read the pointed memory should cause panic.
+ */
+ uint8_t *psa_data_p = outvec_data[0];
+ if (psa_data_p) {
+ (*psa_data_p)++;
+ }
+ }
+
+ /* The system should panic before here. */
+ psa_close(handle);
+ return IPC_SP_TEST_FAILED;
+}
+#endif
+
+#ifdef TFM_IPC_ISOLATION_2_MEM_CHECK
+static int ipc_client_mem_check_test(void)
+{
+ psa_handle_t handle;
+ psa_status_t status;
+ uint8_t *outvec_data[1] = {0};
+ struct psa_outvec outvecs[1] = {{outvec_data, sizeof(outvec_data[0])}};
+ struct psa_invec invecs[1] = {{NULL, 0}};
+
+ handle = psa_connect(IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SID,
+ IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_VERSION);
+
+ if (handle <= 0) {
+ return IPC_SP_TEST_FAILED;
+ }
+
+ status = psa_call(handle, PSA_IPC_CALL, NULL, 0, outvecs, 1);
+ if (status >= 0) {
+ /*
+ * outvecs should contain the pointer pointed to ipc service parition
+ * memory. In psa_call, it checks whether the target partition has the
+ * access right to the invecs indicated memory. If no, the system will
+ * panic.
+ */
+ uint8_t *psa_data_p = outvec_data[0];
+ if (psa_data_p) {
+ invecs[0].base = psa_data_p;
+ invecs[0].len = sizeof(psa_data_p);
+ psa_call(handle, PSA_IPC_CALL, invecs, 1, NULL, 0);
+ }
+ }
+
+ /* The system should panic before here. */
+ psa_close(handle);
+ return IPC_SP_TEST_FAILED;
+}
+#endif
+
+static void ipc_client_handle_ser_req(psa_msg_t msg, uint32_t signals,
+ int (*fn)(void))
+{
+ psa_status_t r;
+ int32_t ret;
+
+ switch (msg.type) {
+ case PSA_IPC_CONNECT:
+ if (service_in_use & signals) {
+ r = PSA_ERROR_CONNECTION_REFUSED;
+ } else {
+ service_in_use |= signals;
+ r = PSA_SUCCESS;
+ }
+ psa_reply(msg.handle, r);
+ break;
+ case PSA_IPC_CALL:
+ ret = (*fn)();
+ if (msg.out_size[0] != 0) {
+ psa_write(msg.handle, 0, &ret, sizeof(ret));
+ }
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ case PSA_IPC_DISCONNECT:
+ assert((service_in_use & signals) != 0);
+ service_in_use &= ~(signals);
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ default:
+ /* cannot get here? [broken SPM]. TODO*/
+ tfm_abort();
+ break;
+ }
+}
+
+void ipc_client_test_main(void)
+{
+ psa_msg_t msg;
+ uint32_t signals = 0;
+
+ while (1) {
+ signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK);
+ psa_get(signals, &msg);
+ if ((signals & IPC_CLIENT_TEST_BASIC_SIGNAL)) {
+ ipc_client_handle_ser_req(msg, IPC_CLIENT_TEST_BASIC_SIGNAL,
+ &ipc_client_base_test);
+ } else if (signals & IPC_CLIENT_TEST_PSA_ACCESS_APP_MEM_SIGNAL) {
+ ipc_client_handle_ser_req(msg,
+ IPC_CLIENT_TEST_PSA_ACCESS_APP_MEM_SIGNAL,
+ &ipc_isolation_2_psa_access_app_memory);
+#ifdef TFM_IPC_ISOLATION_2_TEST_READ_ONLY_MEM
+ } else if (signals &
+ IPC_CLIENT_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL) {
+ ipc_client_handle_ser_req(msg,
+ IPC_CLIENT_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL,
+ &ipc_isolation_2_psa_access_app_readonly_memory);
+#endif
+#ifdef TFM_IPC_ISOLATION_2_APP_ACCESS_PSA_MEM
+ } else if (signals & IPC_CLIENT_TEST_APP_ACCESS_PSA_MEM_SIGNAL) {
+ ipc_client_handle_ser_req(msg,
+ IPC_CLIENT_TEST_APP_ACCESS_PSA_MEM_SIGNAL,
+ &ipc_client_app_access_psa_mem_test);
+#endif
+#ifdef TFM_IPC_ISOLATION_2_MEM_CHECK
+ } else if (signals & IPC_CLIENT_TEST_MEM_CHECK_SIGNAL) {
+ ipc_client_handle_ser_req(msg, IPC_CLIENT_TEST_MEM_CHECK_SIGNAL,
+ &ipc_client_mem_check_test);
+#endif
+ } else {
+ /* Should not go here. */
+ tfm_abort();
+ }
+ }
+}
diff --git a/test/test_services/tfm_ipc_service/psa_manifest/tfm_ipc_service_partition.h b/test/test_services/tfm_ipc_service/psa_manifest/tfm_ipc_service_partition.h
new file mode 100644
index 0000000..47427a0
--- /dev/null
+++ b/test/test_services/tfm_ipc_service/psa_manifest/tfm_ipc_service_partition.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_IPC_SERVICE_PARTITION_H__
+#define __PSA_MANIFEST_TFM_IPC_SERVICE_PARTITION_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IPC_SERVICE_TEST_BASIC_SIGNAL (1U << (0 + 4))
+#define IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SIGNAL (1U << (1 + 4))
+#define IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL (1U << (2 + 4))
+#define IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SIGNAL (1U << (3 + 4))
+#define IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SIGNAL (1U << (4 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_IPC_SERVICE_PARTITION_H__ */
diff --git a/test/test_services/tfm_ipc_service/tfm_ipc_service_partition.yaml b/test/test_services/tfm_ipc_service/tfm_ipc_service_partition.yaml
new file mode 100644
index 0000000..d06bddb
--- /dev/null
+++ b/test/test_services/tfm_ipc_service/tfm_ipc_service_partition.yaml
@@ -0,0 +1,54 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+ "psa_framework_version": 1.0,
+ "name": "TFM_SP_IPC_SERVICE_TEST",
+ "type": "PSA-ROT",
+ "priority": "HIGH",
+ "entry_point": "ipc_service_test_main",
+ "stack_size": "0x0220",
+ "secure_functions": [
+ ],
+ "services" : [
+ {
+ "name": "IPC_SERVICE_TEST_BASIC",
+ "sid": "0x0000F080",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM",
+ "sid": "0x0000F081",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM",
+ "sid": "0x0000F082",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM",
+ "sid": "0x0000F083",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR",
+ "sid": "0x0000F084",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ }
+ ]
+}
diff --git a/test/test_services/tfm_ipc_service/tfm_ipc_service_test.c b/test/test_services/tfm_ipc_service/tfm_ipc_service_test.c
new file mode 100644
index 0000000..ad7d1f2
--- /dev/null
+++ b/test/test_services/tfm_ipc_service/tfm_ipc_service_test.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include "psa/client.h"
+#include "psa/service.h"
+#include "tfm_secure_api.h"
+#include "tfm_api.h"
+#include "psa_manifest/tfm_ipc_service_partition.h"
+
+#define IPC_SERVICE_BUFFER_LEN 32
+
+/* Define the whether the service is inuse flag. */
+static uint32_t service_in_use = 0;
+
+/* Define the global variable for the IPC_APP_ACCESS_PSA_MEM_SID service. */
+uint8_t ipc_servic_data;
+uint8_t *ipc_service_data_p = &ipc_servic_data;
+
+/*
+ * Fixme: Temporarily implement abort as infinite loop,
+ * will replace it later.
+ */
+static void tfm_abort(void)
+{
+ while (1)
+ ;
+}
+
+static void ipc_service_basic(void)
+{
+ psa_msg_t msg;
+ psa_status_t r;
+ int i;
+ uint8_t rec_buf[IPC_SERVICE_BUFFER_LEN];
+ uint8_t send_buf[IPC_SERVICE_BUFFER_LEN] = "It is just for IPC call test.";
+
+ psa_get(IPC_SERVICE_TEST_BASIC_SIGNAL, &msg);
+ switch (msg.type) {
+ case PSA_IPC_CONNECT:
+ if (service_in_use & IPC_SERVICE_TEST_BASIC_SIGNAL) {
+ r = PSA_ERROR_CONNECTION_REFUSED;
+ } else {
+ service_in_use |= IPC_SERVICE_TEST_BASIC_SIGNAL;
+ r = PSA_SUCCESS;
+ }
+ psa_reply(msg.handle, r);
+ break;
+ case PSA_IPC_CALL:
+ for (i = 0; i < PSA_MAX_IOVEC; i++) {
+ if (msg.in_size[i] != 0) {
+ psa_read(msg.handle, i, rec_buf, IPC_SERVICE_BUFFER_LEN);
+ }
+ if (msg.out_size[i] != 0) {
+ psa_write(msg.handle, i, send_buf, IPC_SERVICE_BUFFER_LEN);
+ }
+ }
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ case PSA_IPC_DISCONNECT:
+ assert((service_in_use & IPC_SERVICE_TEST_BASIC_SIGNAL) != 0);
+ service_in_use &= ~IPC_SERVICE_TEST_BASIC_SIGNAL;
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ default:
+ /* cannot get here? [broken SPM]. TODO*/
+ tfm_abort();
+ break;
+ }
+}
+
+static void ipc_service_psa_access_app_mem(void)
+{
+ psa_msg_t msg;
+ psa_status_t r;
+ char rec_data;
+ uint32_t rec_buf;
+
+ psa_get(IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SIGNAL, &msg);
+ switch (msg.type) {
+ case PSA_IPC_CONNECT:
+ if (service_in_use & IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SIGNAL) {
+ r = PSA_ERROR_CONNECTION_REFUSED;
+ } else {
+ service_in_use |= IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SIGNAL;
+ r = PSA_SUCCESS;
+ }
+ psa_reply(msg.handle, r);
+ break;
+ case PSA_IPC_CALL:
+ /*
+ * rec_buf is a pointer pointed to a char type memory in client stack.
+ */
+ if (msg.in_size[0] != 0) {
+ psa_read(msg.handle, 0, &rec_buf, 4);
+ rec_data = *(char *)rec_buf;
+
+ /* rec_data is assigned to 'A' by the client side. */
+ if (rec_data != 'A') {
+ psa_reply(msg.handle, -1);
+ break;
+ }
+
+ /* Change the char type client stack memory to 'B'. */
+ *((char *)rec_buf) = 'B';
+ }
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ case PSA_IPC_DISCONNECT:
+ assert((service_in_use & IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SIGNAL)
+ != 0);
+ service_in_use &= ~IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SIGNAL;
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ default:
+ /* cannot get here? [broken SPM]. TODO*/
+ tfm_abort();
+ break;
+ }
+}
+
+#ifdef TFM_IPC_ISOLATION_2_TEST_READ_ONLY_MEM
+static void ipc_service_psa_access_app_readonly_mem(void)
+{
+ psa_msg_t msg;
+ psa_status_t r;
+ char rec_data;
+ uint32_t rec_buf;
+
+ psa_get(IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL, &msg);
+ switch (msg.type) {
+ case PSA_IPC_CONNECT:
+ if (service_in_use &
+ IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL) {
+ r = PSA_ERROR_CONNECTION_REFUSED;
+ } else {
+ service_in_use |=
+ IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL;
+ r = PSA_SUCCESS;
+ }
+ psa_reply(msg.handle, r);
+ break;
+ case PSA_IPC_CALL:
+ /*
+ * rec_buf is a pointer pointed to a char type memory in client
+ * code section. Write the memory will cause MemManage fault.
+ */
+ if (msg.in_size[0] != 0) {
+ psa_read(msg.handle, 0, &rec_buf, 4);
+ rec_data = *(char *)rec_buf;
+
+ /* rec_data is assigned to 'A' by the client side. */
+ if (rec_data != 'A') {
+ psa_reply(msg.handle, -1);
+ break;
+ }
+
+ /* Write the char type read only memory. */
+ *((char *)rec_buf) = 'B';
+ }
+
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ case PSA_IPC_DISCONNECT:
+ assert((service_in_use &
+ IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL) != 0);
+ service_in_use &=
+ ~IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL;
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ default:
+ /* cannot get here? [broken SPM]. TODO*/
+ tfm_abort();
+ break;
+ }
+}
+#endif
+
+#if defined TFM_IPC_ISOLATION_2_MEM_CHECK \
+ || defined TFM_IPC_ISOLATION_2_APP_ACCESS_PSA_MEM
+static void ipc_service_app_access_psa_mem(void)
+{
+ psa_msg_t msg;
+ psa_status_t r;
+
+ psa_get(IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SIGNAL, &msg);
+ switch (msg.type) {
+ case PSA_IPC_CONNECT:
+ if (service_in_use & IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SIGNAL) {
+ r = PSA_ERROR_CONNECTION_REFUSED;
+ } else {
+ service_in_use |= IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SIGNAL;
+ r = PSA_SUCCESS;
+ }
+
+ psa_reply(msg.handle, r);
+ break;
+ case PSA_IPC_CALL:
+ if (msg.out_size[0] != 0) {
+
+ /*
+ * Write a pointer to outvec. The pointer points to uint8_t
+ * memory in ipc servive partition.
+ */
+ psa_write(msg.handle, 0, &ipc_service_data_p,
+ sizeof(ipc_service_data_p));
+ }
+
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ case PSA_IPC_DISCONNECT:
+ assert((service_in_use & IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SIGNAL)
+ != 0);
+ service_in_use &= ~IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SIGNAL;
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ default:
+ /* cannot get here? [broken SPM]. TODO*/
+ tfm_abort();
+ break;
+ }
+}
+#endif
+
+static void ipc_service_programmer_error(void)
+{
+ psa_msg_t msg;
+ psa_status_t r;
+
+ psa_get(IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SIGNAL, &msg);
+ switch (msg.type) {
+ case PSA_IPC_CONNECT:
+ if (service_in_use & IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SIGNAL) {
+ r = PSA_ERROR_CONNECTION_REFUSED;
+ } else {
+ service_in_use |= IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SIGNAL;
+ r = PSA_SUCCESS;
+ }
+ psa_reply(msg.handle, r);
+ break;
+ case PSA_IPC_CALL:
+ psa_reply(msg.handle, PSA_ERROR_PROGRAMMER_ERROR);
+ break;
+ case PSA_IPC_DISCONNECT:
+ assert((service_in_use
+ & IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SIGNAL) != 0);
+ service_in_use &= ~IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SIGNAL;
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ default:
+ /* cannot get here? [broken SPM]. TODO*/
+ tfm_abort();
+ break;
+ }
+}
+
+/* Test thread */
+void ipc_service_test_main(void *param)
+{
+ uint32_t signals = 0;
+
+ while (1) {
+ signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK);
+ if (signals & IPC_SERVICE_TEST_BASIC_SIGNAL) {
+ ipc_service_basic();
+ } else if (signals & IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SIGNAL) {
+ ipc_service_psa_access_app_mem();
+#ifdef TFM_IPC_ISOLATION_2_TEST_READ_ONLY_MEM
+ } else if (signals
+ & IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL) {
+ ipc_service_psa_access_app_readonly_mem();
+#endif
+#if defined TFM_IPC_ISOLATION_2_MEM_CHECK \
+ || defined TFM_IPC_ISOLATION_2_APP_ACCESS_PSA_MEM
+ } else if (signals & IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SIGNAL) {
+ ipc_service_app_access_psa_mem();
+#endif
+ } else if (signals & IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SIGNAL) {
+ ipc_service_programmer_error();
+ } else {
+ /* Should not come here */
+ tfm_abort();
+ }
+ }
+}
diff --git a/test/test_services/tfm_irq_test_service_1/psa_manifest/tfm_irq_test_service_1.h b/test/test_services/tfm_irq_test_service_1/psa_manifest/tfm_irq_test_service_1.h
new file mode 100644
index 0000000..18ef22a
--- /dev/null
+++ b/test/test_services/tfm_irq_test_service_1/psa_manifest/tfm_irq_test_service_1.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_IRQ_TEST_SERVICE_1_H__
+#define __PSA_MANIFEST_TFM_IRQ_TEST_SERVICE_1_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO_SIGNAL (1U << (0 + 4))
+#define SPM_CORE_IRQ_TEST_1_EXECUTE_TEST_SCENARIO_SIGNAL (1U << (1 + 4))
+
+#define SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ (1U << (27 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_IRQ_TEST_SERVICE_1_H__ */
diff --git a/test/test_services/tfm_irq_test_service_1/tfm_irq_test_service_1.c b/test/test_services/tfm_irq_test_service_1/tfm_irq_test_service_1.c
new file mode 100644
index 0000000..559a3ec
--- /dev/null
+++ b/test/test_services/tfm_irq_test_service_1/tfm_irq_test_service_1.c
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stddef.h>
+#include "tfm_api.h"
+#include "tfm_veneers.h"
+#include "tfm_secure_api.h"
+#include "tfm/tfm_spm_services.h"
+#include "test/test_services/tfm_core_test/core_test_defs.h"
+#include "psa/service.h"
+#include "psa_manifest/pid.h"
+#include "psa_manifest/tfm_irq_test_service_1.h"
+#include "tfm_plat_test.h"
+
+#define IRQ_TEST_TOOL_CODE_LOCATION(name)
+
+static enum irq_test_scenario_t current_scenario = IRQ_TEST_SCENARIO_NONE;
+static struct irq_test_execution_data_t *current_execution_data;
+
+#ifdef TFM_PSA_API
+static psa_handle_t execute_msg_handle = -1;
+#endif
+
+/**
+ * \brief unrecoverable error during test execution.
+ *
+ * Called from places, where error code would be very difficult, or impossible
+ * to return.
+ */
+static void halt_test_execution(void)
+{
+ while (1) {
+ ; /* Test fail */
+ }
+}
+
+/**
+ * \brief Stop the timer, and disable and clear interrupts
+ */
+static void stop_timer(void)
+{
+ IRQ_TEST_TOOL_CODE_LOCATION(stop_secure_timer);
+ tfm_plat_test_secure_timer_stop();
+}
+
+int32_t spm_irq_test_1_prepare_test_scenario_internal(
+ enum irq_test_scenario_t irq_test_scenario,
+ struct irq_test_execution_data_t *execution_data)
+{
+ current_scenario = irq_test_scenario;
+ current_execution_data = execution_data;
+
+ current_execution_data->timer0_triggered = 0;
+
+ switch (irq_test_scenario) {
+ case IRQ_TEST_SCENARIO_NONE:
+ break; /* uninitialised scenario */
+ case IRQ_TEST_SCENARIO_1:
+ case IRQ_TEST_SCENARIO_2:
+ case IRQ_TEST_SCENARIO_3:
+ case IRQ_TEST_SCENARIO_4:
+ tfm_plat_test_secure_timer_start();
+ break;
+ case IRQ_TEST_SCENARIO_5:
+ /* Do nothing */
+ break;
+ default:
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+int32_t spm_irq_test_1_execute_test_scenario(
+ struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len)
+{
+ enum irq_test_scenario_t irq_test_scenario =
+ (enum irq_test_scenario_t) *(uint32_t *)in_vec[0].base;
+ psa_signal_t signals = 0;
+
+ if (irq_test_scenario != current_scenario) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ switch (irq_test_scenario) {
+ case IRQ_TEST_SCENARIO_NONE:
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ case IRQ_TEST_SCENARIO_1:
+ case IRQ_TEST_SCENARIO_2:
+ /* nothing to do*/
+ break;
+ case IRQ_TEST_SCENARIO_3:
+ if (current_execution_data->timer0_triggered) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+ while (!current_execution_data->timer0_triggered) {
+ /* Wait for the timer to be triggered */
+ ;
+ }
+ break;
+ case IRQ_TEST_SCENARIO_4:
+ if (current_execution_data->timer0_triggered) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+ while ((signals & SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ) == 0) {
+ signals = psa_wait(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ,
+ PSA_BLOCK);
+ }
+ if (!current_execution_data->timer0_triggered) {
+ return CORE_TEST_ERRNO_TEST_FAULT;
+ }
+ psa_eoi(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+ break;
+ case IRQ_TEST_SCENARIO_5:
+ /* nothing to do*/
+ break;
+ default:
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ return CORE_TEST_ERRNO_SUCCESS;
+}
+
+uint32_t spm_irq_test_1_prepare_test_scenario(
+ struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len)
+{
+ enum irq_test_scenario_t irq_test_scenario;
+ struct irq_test_execution_data_t *execution_data;
+
+ if ((in_len != 2) || (out_len != 0)) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ if ((in_vec[0].len != sizeof(uint32_t)) ||
+ (in_vec[1].len != sizeof(struct irq_test_execution_data_t *))) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ irq_test_scenario =
+ (enum irq_test_scenario_t) *(uint32_t *)in_vec[0].base;
+
+ execution_data =
+ *(struct irq_test_execution_data_t **)in_vec[1].base;
+
+ return spm_irq_test_1_prepare_test_scenario_internal(irq_test_scenario,
+ execution_data);
+}
+
+#ifndef TFM_PSA_API
+
+void SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ_isr(void)
+{
+ stop_timer();
+
+ if ((current_execution_data == NULL) ||
+ (current_execution_data->timer0_triggered != 0)) {
+ halt_test_execution();
+ }
+
+ current_execution_data->timer0_triggered = 1;
+
+ switch (current_scenario) {
+ case IRQ_TEST_SCENARIO_NONE:
+ psa_eoi(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+ break;
+ case IRQ_TEST_SCENARIO_1:
+ case IRQ_TEST_SCENARIO_2:
+ case IRQ_TEST_SCENARIO_3:
+ psa_eoi(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+ break;
+ case IRQ_TEST_SCENARIO_4:
+ /* nothing to do*/
+ break;
+ case IRQ_TEST_SCENARIO_5:
+ halt_test_execution();
+ /* No secure interrups are used in this scenario */
+ break;
+ default:
+ halt_test_execution();
+ break;
+ }
+
+ __asm("DSB");
+}
+
+#else /* TFM_PSA_API */
+
+typedef psa_status_t (*irq_test_1_func_t)(psa_msg_t *msg);
+
+static void spm_irq_test_1_signal_handle(psa_signal_t signal,
+ irq_test_1_func_t pfn)
+{
+ psa_msg_t msg;
+ psa_status_t status;
+
+ status = psa_get(signal, &msg);
+ if (status) {
+ return;
+ }
+
+ switch (msg.type) {
+ case PSA_IPC_CONNECT:
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ case PSA_IPC_CALL:
+ status = pfn(&msg);
+ psa_reply(msg.handle, status);
+ break;
+ case PSA_IPC_DISCONNECT:
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ default:
+ break;
+ }
+}
+
+void TIMER_0_isr_ipc(void)
+{
+ current_execution_data->timer0_triggered = 1;
+
+ stop_timer();
+
+ switch (current_scenario) {
+ case IRQ_TEST_SCENARIO_NONE:
+ psa_eoi(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+ break;
+ case IRQ_TEST_SCENARIO_1:
+ case IRQ_TEST_SCENARIO_2:
+ psa_eoi(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+ break;
+ case IRQ_TEST_SCENARIO_3:
+ /* execute_msg_handle have to be valid at this point */
+ if (execute_msg_handle <= 0) {
+ halt_test_execution();
+ }
+ /* reply to the execute message, to unblock NS side */
+ psa_reply(execute_msg_handle, CORE_TEST_ERRNO_SUCCESS);
+ execute_msg_handle = -1;
+ psa_eoi(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+ break;
+ case IRQ_TEST_SCENARIO_4:
+ /* This case should never be executed as in this scenario the 'execute
+ * function does the psa wait, and calls psa_eoi immediately'. So when
+ * execution gets to the psa_wait in the main loop, the IRQ signal is
+ * unset.
+ */
+ halt_test_execution();
+ break;
+ case IRQ_TEST_SCENARIO_5:
+ /* No secure interrups are used in this scenario */
+ halt_test_execution();
+ break;
+ default:
+ halt_test_execution();
+ break;
+ }
+}
+
+static psa_status_t spm_irq_test_1_wrap_prepare_test_scenario(psa_msg_t *msg)
+{
+ uint32_t irq_test_scenario;
+ struct irq_test_execution_data_t *execution_data;
+ size_t num;
+
+ if ((msg->in_size[0] != sizeof(uint32_t)) ||
+ (msg->in_size[1] != sizeof(struct irq_test_execution_data_t *))) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+
+ num = psa_read(msg->handle, 0, &irq_test_scenario, sizeof(uint32_t));
+ if (num != msg->in_size[0]) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ num = psa_read(msg->handle, 1, &execution_data,
+ sizeof(struct irq_test_execution_data_t *));
+ if (num != msg->in_size[1]) {
+ return CORE_TEST_ERRNO_INVALID_PARAMETER;
+ }
+
+ return spm_irq_test_1_prepare_test_scenario_internal((enum irq_test_scenario_t)
+ irq_test_scenario,
+ execution_data);
+}
+
+static void spm_irq_test_1_execute_test_scenario_ipc_call(psa_msg_t *msg)
+{
+ size_t num;
+ uint32_t irq_test_scenario;
+ psa_signal_t signals = 0;
+
+ num = psa_read(msg->handle, 0, &irq_test_scenario, sizeof(uint32_t));
+ if ((num != msg->in_size[0]) ||
+ (irq_test_scenario != current_scenario)) {
+ psa_reply(msg->handle, CORE_TEST_ERRNO_INVALID_PARAMETER);
+ return;
+ }
+
+ switch (irq_test_scenario) {
+ case IRQ_TEST_SCENARIO_NONE:
+ psa_reply(msg->handle, CORE_TEST_ERRNO_INVALID_PARAMETER);
+ return;
+ case IRQ_TEST_SCENARIO_1:
+ case IRQ_TEST_SCENARIO_2:
+ case IRQ_TEST_SCENARIO_5:
+ /* nothing to do, return success */
+ psa_reply(msg->handle, CORE_TEST_ERRNO_SUCCESS);
+ return;
+ case IRQ_TEST_SCENARIO_3:
+ if (current_execution_data->timer0_triggered) {
+ psa_reply(msg->handle, CORE_TEST_ERRNO_TEST_FAULT);
+ return;
+ }
+ /* We need the ISR to be able to run. So we do a wait to let
+ * it run and set timer0_triggered. This message will be replied
+ * from the ISR, so the NS side remains blocked for now. To be able
+ * to reply, we also save the handle of the message.
+ */
+ if (execute_msg_handle > 0) {
+ /* execute_msg_handle should be uninitialised at this point */
+ psa_reply(msg->handle, CORE_TEST_ERRNO_TEST_FAULT);
+ return;
+ }
+ execute_msg_handle = msg->handle;
+ while (!(signals & SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ)) {
+ signals = psa_wait(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ,
+ PSA_BLOCK);
+ }
+ return;
+ case IRQ_TEST_SCENARIO_4:
+ while (!(signals & SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ)) {
+ signals = psa_wait(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ,
+ PSA_BLOCK);
+ }
+ /* There is no need to call the ISR in this scenario, so we can
+ * clear the IRQ signal
+ */
+ stop_timer();
+ psa_eoi(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+ psa_reply(msg->handle, CORE_TEST_ERRNO_SUCCESS);
+ break;
+ default:
+ psa_reply(msg->handle, CORE_TEST_ERRNO_INVALID_PARAMETER);
+ return;
+ }
+}
+
+static void spm_irq_test_1_execute_test_scenario_ipc(psa_signal_t signal)
+{
+ psa_msg_t msg;
+ psa_status_t status;
+
+ status = psa_get(signal, &msg);
+ if (status) {
+ return;
+ }
+
+ if (signal != SPM_CORE_IRQ_TEST_1_EXECUTE_TEST_SCENARIO_SIGNAL) {
+ psa_reply(msg.handle, CORE_TEST_ERRNO_INVALID_PARAMETER);
+ }
+
+ switch (msg.type) {
+ case PSA_IPC_CONNECT:
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ case PSA_IPC_CALL:
+ spm_irq_test_1_execute_test_scenario_ipc_call(&msg);
+ break;
+ case PSA_IPC_DISCONNECT:
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ default:
+ break;
+ }
+}
+#endif /* TFM_PSA_API */
+
+int32_t tfm_irq_test_1_init(void)
+{
+ tfm_enable_irq(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+#ifdef TFM_PSA_API
+ psa_signal_t signals = 0;
+
+ while (1) {
+ signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK);
+ if (signals & SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ) {
+ TIMER_0_isr_ipc();
+ } else if (signals & SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO_SIGNAL) {
+ spm_irq_test_1_signal_handle(
+ SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO_SIGNAL,
+ spm_irq_test_1_wrap_prepare_test_scenario);
+ } else if (signals & SPM_CORE_IRQ_TEST_1_EXECUTE_TEST_SCENARIO_SIGNAL) {
+ spm_irq_test_1_execute_test_scenario_ipc(
+ SPM_CORE_IRQ_TEST_1_EXECUTE_TEST_SCENARIO_SIGNAL);
+ } else {
+ ; /* do nothing */
+ }
+ }
+#else
+ return TFM_SUCCESS;
+#endif /* TFM_PSA_API */
+}
diff --git a/test/test_services/tfm_irq_test_service_1/tfm_irq_test_service_1.yaml b/test/test_services/tfm_irq_test_service_1/tfm_irq_test_service_1.yaml
new file mode 100644
index 0000000..1825666
--- /dev/null
+++ b/test/test_services/tfm_irq_test_service_1/tfm_irq_test_service_1.yaml
@@ -0,0 +1,60 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+ "psa_framework_version": 1.0,
+ "name": "TFM_IRQ_TEST_1",
+ "type": "APPLICATION-ROT",
+ "priority": "NORMAL",
+ "entry_point": "tfm_irq_test_1_init",
+ "stack_size": "0x0400",
+ "mmio_regions": [
+ {
+ "name": "TFM_PERIPHERAL_TIMER0",
+ "permission": "READ-WRITE"
+ }
+ ],
+ "secure_functions": [
+ {
+ "name": "SPM_IRQ_TEST_1_PREPARE_TEST_SCENARIO",
+ "signal": "SPM_IRQ_TEST_1_PREPARE_TEST_SCENARIO",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_IRQ_TEST_1_EXECUTE_TEST_SCENARIO",
+ "signal": "SPM_IRQ_TEST_1_EXECUTE_TEST_SCENARIO",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ }
+ ],
+ "irqs": [
+ {
+ "source": "TFM_TIMER0_IRQ",
+ "signal": "SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ",
+ "tfm_irq_priority": 64,
+ }
+ ],
+ "services": [
+ {
+ "name": "SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO",
+ "sid": "0x0000F0A0",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "SPM_CORE_IRQ_TEST_1_EXECUTE_TEST_SCENARIO",
+ "sid": "0x0000F0A1",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ ]
+}
diff --git a/test/test_services/tfm_multi_core_test/psa_manifest/tfm_multi_core_test.h b/test/test_services/tfm_multi_core_test/psa_manifest/tfm_multi_core_test.h
new file mode 100644
index 0000000..ad7ccb5
--- /dev/null
+++ b/test/test_services/tfm_multi_core_test/psa_manifest/tfm_multi_core_test.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_MULTI_CORE_TEST_H__
+#define __PSA_MANIFEST_TFM_MULTI_CORE_TEST_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_SIGNAL (1U << (0 + 4))
+#define MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_SIGNAL (1U << (1 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_MULTI_CORE_TEST_H__ */
diff --git a/test/test_services/tfm_multi_core_test/tfm_multi_core_test.c b/test/test_services/tfm_multi_core_test/tfm_multi_core_test.c
new file mode 100644
index 0000000..fe2238e
--- /dev/null
+++ b/test/test_services/tfm_multi_core_test/tfm_multi_core_test.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "psa/client.h"
+#include "psa/service.h"
+#include "psa_manifest/tfm_multi_core_test.h"
+
+static uint32_t nr_psa_call;
+
+/*
+ * Fixme: Temporarily implement abort as infinite loop,
+ * will replace it later.
+ */
+static void tfm_abort(void)
+{
+ while (1)
+ ;
+}
+
+static void multi_core_multi_client_call_test(uint32_t signal)
+{
+ psa_msg_t msg;
+ psa_status_t status;
+
+ status = psa_get(signal, &msg);
+ if (status != PSA_SUCCESS) {
+ return;
+ }
+
+ switch(msg.type) {
+ case PSA_IPC_CONNECT:
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ case PSA_IPC_CALL:
+ nr_psa_call++;
+ /* Write current number of calls to outvec. */
+ psa_write(msg.handle, 0, &nr_psa_call, sizeof(nr_psa_call));
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ case PSA_IPC_DISCONNECT:
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ default:
+ /* Unsupported operations */
+ tfm_abort();
+ }
+}
+
+/* Test thread */
+void multi_core_test_main(void *param)
+{
+ uint32_t signals = 0;
+
+ (void)param;
+
+ while (1) {
+ signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK);
+
+ if (signals & MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_SIGNAL) {
+ multi_core_multi_client_call_test(
+ MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_SIGNAL);
+ } else if (signals & MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_SIGNAL) {
+ multi_core_multi_client_call_test(
+ MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_SIGNAL);
+ } else {
+ /* Should not come here */
+ tfm_abort();
+ }
+ }
+}
diff --git a/test/test_services/tfm_multi_core_test/tfm_multi_core_test.yaml b/test/test_services/tfm_multi_core_test/tfm_multi_core_test.yaml
new file mode 100644
index 0000000..0b910bc
--- /dev/null
+++ b/test/test_services/tfm_multi_core_test/tfm_multi_core_test.yaml
@@ -0,0 +1,36 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+ "psa_framework_version": 1.0,
+ "name": "TFM_SP_MULTI_CORE_TEST",
+ "type": "APPLICATION-ROT",
+ "priority": "NORMAL",
+ "id": "0x00000007",
+ "entry_point": "multi_core_test_main",
+ "stack_size": "0x0100",
+ "secure_functions": [
+ ],
+ "services" : [
+ {
+ "name": "MULTI_CORE_MULTI_CLIENT_CALL_TEST_0",
+ "sid": "0x0000F100",
+ "signal": "MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_SIGNAL",
+ "non_secure_clients": true,
+ "minor_version": 1,
+ "minor_policy": "STRICT"
+ },
+ {
+ "name": "MULTI_CORE_MULTI_CLIENT_CALL_TEST_1",
+ "sid": "0x0000F101",
+ "signal": "MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_SIGNAL",
+ "non_secure_clients": true,
+ "minor_version": 1,
+ "minor_policy": "STRICT"
+ }
+ ]
+}
diff --git a/test/test_services/tfm_ps_test_service/psa_manifest/tfm_ps_test_service.h b/test/test_services/tfm_ps_test_service/psa_manifest/tfm_ps_test_service.h
new file mode 100644
index 0000000..e748ddf
--- /dev/null
+++ b/test/test_services/tfm_ps_test_service/psa_manifest/tfm_ps_test_service.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_PS_TEST_SERVICE_H__
+#define __PSA_MANIFEST_TFM_PS_TEST_SERVICE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TFM_PS_TEST_PREPARE_SIGNAL (1U << (0 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_PS_TEST_SERVICE_H__ */
diff --git a/test/test_services/tfm_ps_test_service/tfm_ps_test_service.c b/test/test_services/tfm_ps_test_service/tfm_ps_test_service.c
new file mode 100644
index 0000000..2e30796
--- /dev/null
+++ b/test/test_services/tfm_ps_test_service/tfm_ps_test_service.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifdef TFM_PSA_API
+#include "psa/service.h"
+#include "psa_manifest/tfm_ps_test_service.h"
+#else
+#include "psa/client.h"
+#endif
+
+#include "secure_fw/partitions/protected_storage/ps_object_system.h"
+
+psa_status_t tfm_ps_test_prepare(psa_invec *in_vec, size_t in_len,
+ psa_outvec *out_vec, size_t out_len)
+{
+ (void)in_vec;
+ (void)in_len;
+ (void)out_vec;
+ (void)out_len;
+
+ return ps_system_prepare();
+}
+
+psa_status_t tfm_ps_test_init(void)
+{
+#ifdef TFM_PSA_API
+ psa_msg_t msg;
+
+ while (1) {
+ (void)psa_wait(TFM_PS_TEST_PREPARE_SIGNAL, PSA_BLOCK);
+ (void)psa_get(TFM_PS_TEST_PREPARE_SIGNAL, &msg);
+ switch (msg.type) {
+ case PSA_IPC_CONNECT:
+ case PSA_IPC_DISCONNECT:
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ case PSA_IPC_CALL:
+ psa_reply(msg.handle, ps_system_prepare());
+ break;
+ }
+ }
+#else
+ return PSA_SUCCESS;
+#endif
+}
diff --git a/test/test_services/tfm_ps_test_service/tfm_ps_test_service.yaml b/test/test_services/tfm_ps_test_service/tfm_ps_test_service.yaml
new file mode 100644
index 0000000..eddc3ed
--- /dev/null
+++ b/test/test_services/tfm_ps_test_service/tfm_ps_test_service.yaml
@@ -0,0 +1,38 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+ "psa_framework_version": 1.0,
+ "name": "TFM_SP_PS_TEST",
+ "type": "PSA-ROT",
+ "priority": "NORMAL",
+ "entry_point": "tfm_ps_test_init",
+ "stack_size": "0x500",
+ "secure_functions": [
+ {
+ "name": "TFM_PS_TEST_PREPARE",
+ "signal": "TFM_PS_TEST_PREPARE",
+ "non_secure_clients": false,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ ],
+ "services": [
+ {
+ "name": "TFM_PS_TEST_PREPARE",
+ "sid": "0x0000F0C0",
+ "non_secure_clients": false,
+ "version": 1,
+ "version_policy": "STRICT"
+ }
+ ],
+ "dependencies": [
+ "TFM_CRYPTO",
+ "TFM_ITS_GET",
+ "TFM_ITS_REMOVE"
+ ]
+}
diff --git a/test/test_services/tfm_ps_test_service/tfm_ps_test_service_api.c b/test/test_services/tfm_ps_test_service/tfm_ps_test_service_api.c
new file mode 100644
index 0000000..1377e93
--- /dev/null
+++ b/test/test_services/tfm_ps_test_service/tfm_ps_test_service_api.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_ps_test_service_api.h"
+
+#ifdef TFM_PSA_API
+#include "psa/client.h"
+#include "psa_manifest/sid.h"
+#else
+#include "tfm_veneers.h"
+#endif
+
+__attribute__((section("SFN")))
+psa_status_t tfm_ps_test_system_prepare(void)
+{
+#ifdef TFM_PSA_API
+ psa_handle_t handle;
+ psa_status_t status;
+
+ handle = psa_connect(TFM_PS_TEST_PREPARE_SID,
+ TFM_PS_TEST_PREPARE_VERSION);
+ if (!PSA_HANDLE_IS_VALID(handle)) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ status = psa_call(handle, PSA_IPC_CALL, NULL, 0, NULL, 0);
+ psa_close(handle);
+
+ return status;
+#else
+ return tfm_tfm_ps_test_prepare_veneer(NULL, 0, NULL, 0);
+#endif
+}
diff --git a/test/test_services/tfm_ps_test_service/tfm_ps_test_service_api.h b/test/test_services/tfm_ps_test_service/tfm_ps_test_service_api.h
new file mode 100644
index 0000000..50aaa69
--- /dev/null
+++ b/test/test_services/tfm_ps_test_service/tfm_ps_test_service_api.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_PS_TEST_SERVICE_API_H__
+#define __TFM_PS_TEST_SERVICE_API_H__
+
+#include "psa/error.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Requests the PS Test Service to call ps_system_prepare().
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t tfm_ps_test_system_prepare(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_PS_TEST_SERVICE_API_H__ */
diff --git a/test/test_services/tfm_secure_client_2/psa_manifest/tfm_secure_client_2.h b/test/test_services/tfm_secure_client_2/psa_manifest/tfm_secure_client_2.h
new file mode 100644
index 0000000..a8fdf47
--- /dev/null
+++ b/test/test_services/tfm_secure_client_2/psa_manifest/tfm_secure_client_2.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_SECURE_CLIENT_2_H__
+#define __PSA_MANIFEST_TFM_SECURE_CLIENT_2_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TFM_SECURE_CLIENT_2_SIGNAL (1U << (0 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_SECURE_CLIENT_2_H__ */
diff --git a/test/test_services/tfm_secure_client_2/tfm_secure_client_2.c b/test/test_services/tfm_secure_client_2/tfm_secure_client_2.c
new file mode 100644
index 0000000..153caac
--- /dev/null
+++ b/test/test_services/tfm_secure_client_2/tfm_secure_client_2.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_secure_client_2_api.h"
+#include "psa/internal_trusted_storage.h"
+#include "psa/crypto.h"
+
+#ifdef TFM_PSA_API
+#include "psa/service.h"
+#include "psa_manifest/tfm_secure_client_2.h"
+#else
+#include "psa/client.h"
+#endif
+
+#ifdef ENABLE_CRYPTO_SERVICE_TESTS
+/**
+ * \brief Tests calling psa_destroy_key() with the supplied key handle.
+ *
+ * \param[in] arg Pointer to key handle
+ * \param[in] arg_len Length of arg in bytes
+ *
+ * \return Returns test result as specified in \ref psa_status_t
+ */
+static psa_status_t secure_client_2_test_crypto_access_ctrl(const void *arg,
+ size_t arg_len)
+{
+ psa_key_handle_t key_handle;
+
+ if (arg_len != sizeof(key_handle)) {
+ return PSA_ERROR_PROGRAMMER_ERROR;
+ }
+
+ key_handle = *((psa_key_handle_t *)arg);
+
+ /* Attempt to destroy the key handle */
+ return psa_destroy_key(key_handle);
+}
+#endif /* ENABLE_CRYPTO_SERVICE_TESTS */
+
+#ifdef ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS
+/**
+ * \brief Tests calling psa_its_get() with the supplied uid.
+ *
+ * \param[in] arg Pointer to uid
+ * \param[in] arg_len Length of arg in bytes
+ *
+ * \return Returns test result as specified in \ref psa_status_t
+ */
+static psa_status_t secure_client_2_test_its_access_ctrl(const void *arg,
+ size_t arg_len)
+{
+ psa_storage_uid_t uid;
+ size_t p_data_length;
+ uint8_t data[1];
+
+ if (arg_len != sizeof(uid)) {
+ return PSA_ERROR_PROGRAMMER_ERROR;
+ }
+
+ uid = *((psa_storage_uid_t *)arg);
+
+ /* Attempt to get one byte from the UID and return the resulting status */
+ return psa_its_get(uid, 0, sizeof(data), data, &p_data_length);
+}
+#endif /* ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS */
+
+/**
+ * \brief Calls the test function with the supplied ID and returns the result
+ * from the test function.
+ *
+ * \param[in] id The ID of the test function
+ * \param[in] arg Pointer to argument to pass to test function
+ * \param[in] arg_len Length of argument in bytes
+ *
+ * \return Returns test result as specified in \ref psa_status_t
+ */
+static psa_status_t secure_client_2_dispatch(int32_t id, const void *arg,
+ size_t arg_len)
+{
+ switch (id) {
+#ifdef ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS
+ case TFM_SECURE_CLIENT_2_ID_ITS_ACCESS_CTRL:
+ return secure_client_2_test_its_access_ctrl(arg, arg_len);
+#endif
+#ifdef ENABLE_CRYPTO_SERVICE_TESTS
+ case TFM_SECURE_CLIENT_2_ID_CRYPTO_ACCESS_CTRL:
+ return secure_client_2_test_crypto_access_ctrl(arg, arg_len);
+#endif
+ default:
+ return PSA_ERROR_PROGRAMMER_ERROR;
+ }
+}
+
+#ifdef TFM_PSA_API
+#define SECURE_CLIENT_2_MAX_ARG_LEN 8U
+
+void tfm_secure_client_2_init(void)
+{
+ psa_msg_t msg;
+ size_t len;
+ char arg[SECURE_CLIENT_2_MAX_ARG_LEN] __attribute__((__aligned__(8)));
+
+ while (1) {
+ (void)psa_wait(TFM_SECURE_CLIENT_2_SIGNAL, PSA_BLOCK);
+ if (psa_get(TFM_SECURE_CLIENT_2_SIGNAL, &msg) != PSA_SUCCESS) {
+ continue;
+ }
+ switch (msg.type) {
+ case PSA_IPC_CONNECT:
+ case PSA_IPC_DISCONNECT:
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ default:
+ len = psa_read(msg.handle, 0, arg, SECURE_CLIENT_2_MAX_ARG_LEN);
+ psa_reply(msg.handle, secure_client_2_dispatch(msg.type, arg, len));
+ break;
+ }
+ }
+}
+#else /* TFM_PSA_API */
+psa_status_t tfm_secure_client_2_init(void)
+{
+ return PSA_SUCCESS;
+}
+
+psa_status_t tfm_secure_client_2_call(psa_invec *in_vec, size_t in_len,
+ psa_outvec *out_vec, size_t out_len)
+{
+ int32_t id;
+
+ (void)out_vec;
+
+ if (in_len != 2 || out_len != 0 || in_vec[0].len != sizeof(id)) {
+ return PSA_ERROR_PROGRAMMER_ERROR;
+ }
+
+ id = *((int32_t *)in_vec[0].base);
+
+ return secure_client_2_dispatch(id, in_vec[1].base, in_vec[1].len);
+}
+#endif /* TFM_PSA_API */
diff --git a/test/test_services/tfm_secure_client_2/tfm_secure_client_2.yaml b/test/test_services/tfm_secure_client_2/tfm_secure_client_2.yaml
new file mode 100644
index 0000000..3399933
--- /dev/null
+++ b/test/test_services/tfm_secure_client_2/tfm_secure_client_2.yaml
@@ -0,0 +1,37 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+ "psa_framework_version": 1.0,
+ "name": "TFM_SP_SECURE_CLIENT_2",
+ "type": "APPLICATION-ROT",
+ "priority": "NORMAL",
+ "entry_point": "tfm_secure_client_2_init",
+ "stack_size": "0x300",
+ "secure_functions": [
+ {
+ "name": "TFM_SECURE_CLIENT_2_CALL",
+ "signal": "TFM_SECURE_CLIENT_2_CALL",
+ "non_secure_clients": false,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ ],
+ "services": [
+ {
+ "name": "TFM_SECURE_CLIENT_2",
+ "sid": "0x0000F0E0",
+ "non_secure_clients": false,
+ "version": 1,
+ "version_policy": "STRICT"
+ }
+ ],
+ "dependencies": [
+ "TFM_ITS_GET",
+ "TFM_CRYPTO"
+ ]
+}
diff --git a/test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.c b/test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.c
new file mode 100644
index 0000000..b5beb9a
--- /dev/null
+++ b/test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_secure_client_2_api.h"
+#include "psa/client.h"
+
+#ifdef TFM_PSA_API
+#include "psa_manifest/sid.h"
+#else
+#include "tfm_veneers.h"
+#endif
+
+__attribute__((section("SFN")))
+psa_status_t tfm_secure_client_2_call_test(int32_t id, const void *arg,
+ size_t arg_len)
+{
+#ifdef TFM_PSA_API
+ psa_handle_t handle;
+ psa_status_t status;
+ psa_invec in_vec[] = {
+ { .base = arg, .len = arg_len },
+ };
+
+ handle = psa_connect(TFM_SECURE_CLIENT_2_SID, TFM_SECURE_CLIENT_2_VERSION);
+ if (!PSA_HANDLE_IS_VALID(handle)) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Pass the ID through the type parameter of psa_call() */
+ status = psa_call(handle, id, in_vec, 1, NULL, 0);
+ psa_close(handle);
+
+ return status;
+#else
+ /* Pack the ID in the invec */
+ psa_invec in_vec[] = {
+ { .base = &id, .len = sizeof(id) },
+ { .base = arg, .len = arg_len },
+ };
+
+ return tfm_tfm_secure_client_2_call_veneer(in_vec, 2, NULL, 0);
+#endif
+}
diff --git a/test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.h b/test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.h
new file mode 100644
index 0000000..a6018c5
--- /dev/null
+++ b/test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_SECURE_CLIENT_2_API_H__
+#define __TFM_SECURE_CLIENT_2_API_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "psa/error.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TFM_SECURE_CLIENT_2_ID_ITS_ACCESS_CTRL 1001
+#define TFM_SECURE_CLIENT_2_ID_CRYPTO_ACCESS_CTRL 2001
+
+/**
+ * \brief Calls the test function with the supplied ID within the execution
+ * context of the Secure Client 2 partition and returns the resulting
+ * status.
+ *
+ * \param[in] id The ID of the test function
+ * \param[in] arg Pointer to argument to pass to test function
+ * \param[in] arg_len Length of argument in bytes
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t tfm_secure_client_2_call_test(int32_t id, const void *arg,
+ size_t arg_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_SECURE_CLIENT_2_API_H__ */
diff --git a/test/test_services/tfm_secure_client_service/psa_manifest/tfm_test_client_service.h b/test/test_services/tfm_secure_client_service/psa_manifest/tfm_test_client_service.h
new file mode 100644
index 0000000..2e03069
--- /dev/null
+++ b/test/test_services/tfm_secure_client_service/psa_manifest/tfm_test_client_service.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_TEST_CLIENT_SERVICE_H__
+#define __PSA_MANIFEST_TFM_TEST_CLIENT_SERVICE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TFM_SECURE_CLIENT_SFN_RUN_TESTS_SIGNAL (1U << (0 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_TEST_CLIENT_SERVICE_H__ */
diff --git a/test/test_services/tfm_secure_client_service/tfm_secure_client_service.c b/test/test_services/tfm_secure_client_service/tfm_secure_client_service.c
new file mode 100644
index 0000000..62a8b5d
--- /dev/null
+++ b/test/test_services/tfm_secure_client_service/tfm_secure_client_service.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_secure_client_service.h"
+#include "test/framework/test_framework_integ_test.h"
+#ifdef TFM_PSA_API
+#include "psa/client.h"
+#include "psa/service.h"
+#include "psa_manifest/tfm_test_client_service.h"
+#endif
+
+/**
+ * \brief Service initialisation function. No special initialisation is
+ * required.
+ *
+ * \return Returns 0 on success
+ */
+int32_t tfm_secure_client_service_init(void)
+{
+#ifdef TFM_PSA_API
+ psa_msg_t msg;
+
+ while (1) {
+ psa_wait(TFM_SECURE_CLIENT_SFN_RUN_TESTS_SIGNAL, PSA_BLOCK);
+ psa_get(TFM_SECURE_CLIENT_SFN_RUN_TESTS_SIGNAL, &msg);
+ switch (msg.type) {
+ case PSA_IPC_CONNECT:
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ case PSA_IPC_CALL:
+ psa_reply(msg.handle, tfm_secure_client_service_sfn_run_tests());
+ break;
+ case PSA_IPC_DISCONNECT:
+ psa_reply(msg.handle, PSA_SUCCESS);
+ break;
+ default:
+ /* cannot get here? [broken SPM]. TODO*/
+ break;
+ }
+ }
+#else
+ return 0;
+#endif
+}
+
+int32_t tfm_secure_client_service_sfn_run_tests(void)
+{
+ start_integ_test();
+ return 0;
+}
diff --git a/test/test_services/tfm_secure_client_service/tfm_secure_client_service.h b/test/test_services/tfm_secure_client_service/tfm_secure_client_service.h
new file mode 100644
index 0000000..baa650a
--- /dev/null
+++ b/test/test_services/tfm_secure_client_service/tfm_secure_client_service.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_SECURE_CLIENT_SERVICE_H__
+#define __TFM_SECURE_CLIENT_SERVICE_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Runs the secure integration tests.
+ *
+ * \return Returns 0 on success.
+ */
+int32_t tfm_secure_client_service_sfn_run_tests(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_SECURE_CLIENT_SERVICE_H__ */
diff --git a/test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.c b/test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.c
new file mode 100644
index 0000000..3a80446
--- /dev/null
+++ b/test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_secure_client_service_api.h"
+#ifdef TFM_PSA_API
+#include "psa/client.h"
+#include "tfm_api.h"
+#include "psa_manifest/sid.h"
+#else /* TFM_PSA_API */
+#include "tfm_veneers.h"
+#endif /* TFM_PSA_API */
+
+int32_t tfm_secure_client_run_tests(void)
+{
+#ifdef TFM_PSA_API
+ psa_handle_t handle;
+ psa_status_t status;
+
+ handle = psa_connect(TFM_SECURE_CLIENT_SFN_RUN_TESTS_SID,
+ TFM_SECURE_CLIENT_SFN_RUN_TESTS_VERSION);
+ if (handle <= 0) {
+ return TFM_ERROR_GENERIC;
+ }
+
+ status = psa_call(handle, PSA_IPC_CALL, NULL, 0, NULL, 0);
+ psa_close(handle);
+
+ if (status != PSA_SUCCESS) {
+ return TFM_ERROR_GENERIC;
+ }
+#else
+ tfm_tfm_secure_client_service_sfn_run_tests_veneer(NULL, 0, NULL, 0);
+#endif
+
+ return 0;
+}
diff --git a/test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.h b/test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.h
new file mode 100644
index 0000000..35e9004
--- /dev/null
+++ b/test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_SECURE_CLIENT_SERVICE_API_H__
+#define __TFM_SECURE_CLIENT_SERVICE_API_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Runs the secure integration tests.
+ *
+ * \return Returns 0 on success
+ */
+int32_t tfm_secure_client_run_tests(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_SECURE_CLIENT_SERVICE_API_H__ */
diff --git a/test/test_services/tfm_secure_client_service/tfm_test_client_service.yaml b/test/test_services/tfm_secure_client_service/tfm_test_client_service.yaml
new file mode 100644
index 0000000..7931f16
--- /dev/null
+++ b/test/test_services/tfm_secure_client_service/tfm_test_client_service.yaml
@@ -0,0 +1,58 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+ "psa_framework_version": 1.0,
+ "name": "TFM_SP_SECURE_TEST_PARTITION",
+ "type": "PSA-ROT",
+ "priority": "NORMAL",
+ "entry_point": "tfm_secure_client_service_init",
+ "stack_size": "0x0D00",
+ "mmio_regions": [
+ {
+ "name": "TFM_PERIPHERAL_STD_UART",
+ "permission": "READ-WRITE"
+ }
+ ],
+ "secure_functions": [
+ {
+ "name": "TFM_SECURE_CLIENT_SFN_RUN_TESTS",
+ "signal": "TFM_SECURE_CLIENT_SERVICE_SFN_RUN_TESTS",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ ],
+ "services": [
+ {
+ "name": "TFM_SECURE_CLIENT_SFN_RUN_TESTS",
+ "sid": "0x0000F000",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ }
+ ],
+ "dependencies": [
+ "TFM_SECURE_CLIENT_2",
+ "TFM_CRYPTO",
+ "TFM_PS_SET",
+ "TFM_PS_GET",
+ "TFM_PS_GET_INFO",
+ "TFM_PS_REMOVE",
+ "TFM_PS_GET_SUPPORT",
+ "TFM_ITS_SET",
+ "TFM_ITS_GET",
+ "TFM_ITS_GET_INFO",
+ "TFM_ITS_REMOVE",
+ "TFM_ATTEST_GET_TOKEN",
+ "TFM_ATTEST_GET_TOKEN_SIZE",
+ "TFM_ATTEST_GET_PUBLIC_KEY",
+ "TFM_PS_TEST_PREPARE",
+ "TFM_SP_PLATFORM_SYSTEM_RESET",
+ "TFM_SP_PLATFORM_IOCTL"
+ ]
+}