[crypto] PSA API: introduce example native ITS implementations

This commit implements two example native ITS for linux-based platform.
This commit is contained in:
Łukasz Duda
2025-11-04 21:53:31 +01:00
parent 24e0504b7d
commit 31cf8eac65
6 changed files with 964 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
# Copyright (c) 2025, The OpenThread Authors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
config("openthread-native-its-config") {
include_dirs = [
"include",
]
}
static_library("openthread-native-its-file") {
sources = [
"src/file/its_file.c"
]
configs += [
":openthread-native-its-config",
]
defines = [ "OPENTHREAD_PSA_CRYPTO_NATIVE_ITS_FILE=1" ]
}
static_library("openthread-native-its-ram") {
sources = [
"src/ram/its_ram.c"
]
configs += [
":openthread-native-its-config",
]
defines = [ "OPENTHREAD_PSA_CRYPTO_NATIVE_ITS_RAM=1" ]
}
+60
View File
@@ -0,0 +1,60 @@
#
# Copyright (c) 2025, The OpenThread Authors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
add_library(openthread-native-its-file STATIC)
target_sources(openthread-native-its-file PRIVATE
src/file/its_file.c
)
target_include_directories(openthread-native-its-file PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/include"
${OT_PUBLIC_INCLUDES}
$<TARGET_PROPERTY:ot-config,INTERFACE_INCLUDE_DIRECTORIES>
)
target_compile_definitions(openthread-native-its-file
PRIVATE
$<TARGET_PROPERTY:ot-config,INTERFACE_COMPILE_DEFINITIONS>
PUBLIC
OPENTHREAD_PSA_CRYPTO_NATIVE_ITS_FILE=1
)
add_library(openthread-native-its-ram STATIC)
target_sources(openthread-native-its-ram PRIVATE
src/ram/its_ram.c
)
target_include_directories(openthread-native-its-ram PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/include"
${OT_PUBLIC_INCLUDES}
$<TARGET_PROPERTY:ot-config,INTERFACE_INCLUDE_DIRECTORIES>
)
target_compile_definitions(openthread-native-its-ram
PRIVATE
$<TARGET_PROPERTY:ot-config,INTERFACE_COMPILE_DEFINITIONS>
PUBLIC
OPENTHREAD_PSA_CRYPTO_NATIVE_ITS_RAM=1
)
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2025, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PSA_ERROR_H__
#define PSA_ERROR_H__
#include <stdint.h>
typedef int32_t psa_status_t;
#define PSA_SUCCESS ((psa_status_t)0)
#define PSA_ERROR_GENERIC_ERROR ((psa_status_t)-132)
#define PSA_ERROR_NOT_PERMITTED ((psa_status_t)-133)
#define PSA_ERROR_NOT_SUPPORTED ((psa_status_t)-134)
#define PSA_ERROR_INVALID_ARGUMENT ((psa_status_t)-135)
#define PSA_ERROR_ALREADY_EXISTS ((psa_status_t)-139)
#define PSA_ERROR_DOES_NOT_EXIST ((psa_status_t)-140)
#define PSA_ERROR_INSUFFICIENT_STORAGE ((psa_status_t)-142)
#define PSA_ERROR_STORAGE_FAILURE ((psa_status_t)-146)
#define PSA_ERROR_INVALID_SIGNATURE ((psa_status_t)-149)
#define PSA_ERROR_DATA_CORRUPT ((psa_status_t)-152)
#endif /* PSA_ERROR_H__ */
@@ -0,0 +1,149 @@
/*
* Copyright (c) 2025, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PSA_INTERNAL_TRUSTED_STORAGE_H__
#define PSA_INTERNAL_TRUSTED_STORAGE_H__
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "psa/error.h"
/** \brief Flags used when creating a data entry
*/
typedef uint32_t psa_storage_create_flags_t;
/** \brief A type for UIDs used for identifying data
*/
typedef uint64_t psa_storage_uid_t;
#define PSA_STORAGE_FLAG_NONE 0 /**< No flags to pass */
#define PSA_STORAGE_FLAG_WRITE_ONCE (1 << 0) /**< The data associated with the uid will not be able to be modified or deleted. Intended to be used to set bits in `psa_storage_create_flags_t`*/
/**
* \brief A container for metadata associated with a specific uid
*/
struct psa_storage_info_t {
uint32_t size; /**< The size of the data associated with a uid **/
psa_storage_create_flags_t flags; /**< The flags set when the uid was created **/
};
/** Flag indicating that \ref psa_storage_create and \ref psa_storage_set_extended are supported */
#define PSA_STORAGE_SUPPORT_SET_EXTENDED (1 << 0)
#define PSA_ITS_API_VERSION_MAJOR 1 /**< The major version number of the PSA ITS API. It will be incremented on significant updates that may include breaking changes */
#define PSA_ITS_API_VERSION_MINOR 1 /**< The minor version number of the PSA ITS API. It will be incremented in small updates that are unlikely to include breaking changes */
/**
* \brief Create a new or modify an existing uid/value pair
*
* \param[in] aUid The identifier for the data
* \param[in] aDataLength The size in bytes of the data in `p_data`
* \param[in] aData A buffer containing the data
* \param[in] aCreateFlags The flags that the data will be stored with
*
* \return A status indicating the success/failure of the operation
*
* \retval #PSA_SUCCESS The operation completed successfully
* \retval #PSA_ERROR_NOT_PERMITTED The operation failed because the provided `uid` value was already created with PSA_STORAGE_FLAG_WRITE_ONCE
* \retval #PSA_ERROR_NOT_SUPPORTED The operation failed because one or more of the flags provided in `create_flags` is not supported or is not valid
* \retval #PSA_ERROR_INSUFFICIENT_STORAGE The operation failed because there was insufficient space on the storage medium
* \retval #PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has failed (Fatal error)
* \retval #PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided pointers(`p_data`)
* is invalid, for example is `NULL` or references memory the caller cannot access
*/
psa_status_t psa_its_set(psa_storage_uid_t aUid,
uint32_t aDataLength,
const void *aData,
psa_storage_create_flags_t aCreateFlags);
/**
* \brief Retrieve the value associated with a provided uid
*
* \param[in] aUid The uid value
* \param[in] aDataOffset The starting offset of the data requested
* \param[in] aDataLength The amount of data requested (and the minimum allocated size of the `p_data` buffer)
* \param[out] aData The buffer where the data will be placed upon successful completion
* \param[out] aDataLengthOut The amount of data returned in the p_data buffer
*
*
* \return A status indicating the success/failure of the operation
*
* \retval #PSA_SUCCESS The operation completed successfully
* \retval #PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided `uid` value was not found in the storage
* \retval #PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has failed (Fatal error)
* \retval #PSA_ERROR_DATA_CORRUPT The operation failed because stored data has been corrupted
* \retval #PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided pointers(`p_data`, `p_data_length`)
* is invalid. For example is `NULL` or references memory the caller cannot access.
* In addition, this can also happen if an invalid offset was provided.
*/
psa_status_t psa_its_get(psa_storage_uid_t aUid,
uint32_t aDataOffset,
uint32_t aDataLength,
void *aData,
size_t *aDataLengthOut);
/**
* \brief Retrieve the metadata about the provided uid
*
* \param[in] aUid The uid value
* \param[out] aInfo A pointer to the `psa_storage_info_t` struct that will be populated with the metadata
*
* \return A status indicating the success/failure of the operation
*
* \retval #PSA_SUCCESS The operation completed successfully
* \retval #PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided uid value was not found in the storage
* \retval #PSA_ERROR_DATA_CORRUPT The operation failed because stored data has been corrupted
* \retval #PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided pointers(`p_info`)
* is invalid, for example is `NULL` or references memory the caller cannot access
*/
psa_status_t psa_its_get_info(psa_storage_uid_t aUid,
struct psa_storage_info_t *aInfo);
/**
* \brief Remove the provided key and its associated data from the storage
*
* \param[in] aUid The uid value
*
* \return A status indicating the success/failure of the operation
*
* \retval #PSA_SUCCESS The operation completed successfully
* \retval #PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided key value was not found in the storage
* \retval #PSA_ERROR_NOT_PERMITTED The operation failed because the provided key value was created with PSA_STORAGE_FLAG_WRITE_ONCE
* \retval #PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has failed (Fatal error)
*/
psa_status_t psa_its_remove(psa_storage_uid_t aUid);
#ifdef __cplusplus
}
#endif
#endif /* PSA_INTERNAL_TRUSTED_STORAGE_H__ */
+384
View File
@@ -0,0 +1,384 @@
/*
* Copyright (c) 2025, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements an exemplary ITS working on files.
*/
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_PSA
#include "psa/error.h"
#include "psa/internal_trusted_storage.h"
#include <stdio.h>
#include <string.h>
#include <libgen.h>
#include <sys/stat.h>
#include <unistd.h>
#define VerifyOrExit(aCondition, aAction) \
do \
{ \
if (!(aCondition)) \
{ \
aAction; \
goto exit; \
} \
} while (0)
/**
* @def ITS_FILE_DEFAULT_FILE_PREFIX
*
* The default directory prefix if the user does not override it by changing
* the global variable @c gItsFileNamePrefix.
*/
#define ITS_FILE_DEFAULT_FILE_PREFIX "tmp/"
/**
* @def ITS_FILE_PATH_MAX
*
* The maximum allowed length (in bytes) for file paths.
*/
#define ITS_FILE_PATH_MAX 256
/**
* @def ITS_DIR_MODE
*
* The mode used when creating directories (0777 gives full permissions to owner,
* group, and others).
*/
#define ITS_DIR_MODE 0777
/**
* @def ITS_FILE_HEADER_SIZE
*
* The size (in bytes) of the file header: 4 bytes for flags plus 4 bytes for total data length.
*/
#define ITS_FILE_HEADER_SIZE (sizeof(uint32_t) + sizeof(uint32_t))
/**
* @def ITS_FILE_NAME_FORMAT
*
* The format string for building the file path.
*/
#define ITS_FILE_NAME_FORMAT "%suid_%llu.psa_its"
/**
* A global variable that determines where PSA ITS files are stored.
*
* By default, it is @ref ITS_FILE_DEFAULT_FILE_PREFIX. You can override
* it at runtime:
* @code
* gItsFileNamePrefix = "tmp/its_node_3_offset_12";
* @endcode
*/
const char *gItsFileNamePrefix = ITS_FILE_DEFAULT_FILE_PREFIX;
/**
* Ensures that the directory (specified by `gItsFileNamePrefix`) exists.
*/
static bool ensureDirectoryExists(void)
{
bool success = true;
struct stat st;
char filePrefix[ITS_FILE_PATH_MAX];
char *dir;
// Copy prefix to local buffer, because dirname() modifies its argument
strncpy(filePrefix, gItsFileNamePrefix, sizeof(filePrefix));
filePrefix[sizeof(filePrefix) - 1] = '\0';
dir = dirname(filePrefix);
// 1) Check if path already exists
if (stat(dir, &st) == 0)
{
// Exists - must be a directory
if (!S_ISDIR(st.st_mode))
{
success = false;
}
}
else
{
// Path doesn't exist, attempt to create one.
if (mkdir(dir, ITS_DIR_MODE) != 0)
{
// Retry with stat again.
if (stat(dir, &st) != 0 || !S_ISDIR(st.st_mode))
{
success = false;
}
}
}
return success;
}
/**
* Builds the file path for a given UID.
* Returns 0 on success, -1 on error.
*/
static int buildFilePath(psa_storage_uid_t aUid, char *aPath, size_t aPathSize)
{
int result = 0;
// Attempt to format
int ret = snprintf(aPath, aPathSize, ITS_FILE_NAME_FORMAT,
gItsFileNamePrefix, (unsigned long long)aUid);
// If ret < 0 or ret >= aPathSize, return an error.
VerifyOrExit((ret >= 0) && ((size_t)ret < aPathSize), result = -1);
exit:
return result;
}
/**
* Reads an 8-byte header from the given file:
* - 4 bytes for flags (psa_storage_create_flags_t)
* - 4 bytes for data length
*
* Returns 0 on success, -1 on error.
*/
static int readHeader(FILE *aFile, psa_storage_create_flags_t *aFlags, uint32_t *aDataLen)
{
int result = 0;
uint32_t flagsTmp;
uint32_t lenTmp;
// Read flags
VerifyOrExit(fread(&flagsTmp, sizeof(flagsTmp), 1, aFile) == 1, result = -1);
// Read length
VerifyOrExit(fread(&lenTmp, sizeof(lenTmp), 1, aFile) == 1, result = -1);
*aFlags = flagsTmp;
*aDataLen = lenTmp;
exit:
return result;
}
/**
* Writes an 8-byte header to the given file:
* - 4 bytes for flags
* - 4 bytes for data length
*
* Returns 0 on success, -1 on error.
*/
static int writeHeader(FILE *aFile, psa_storage_create_flags_t aFlags, uint32_t aDataLen)
{
int result = 0;
// Write flags
VerifyOrExit(fwrite(&aFlags, sizeof(aFlags), 1, aFile) == 1, result = -1);
// Write length
VerifyOrExit(fwrite(&aDataLen, sizeof(aDataLen), 1, aFile) == 1, result = -1);
exit:
return result;
}
psa_status_t psa_its_set(psa_storage_uid_t aUid,
uint32_t aDataLength,
const void *aData,
psa_storage_create_flags_t aCreateFlags)
{
psa_status_t status = PSA_SUCCESS;
FILE *file = NULL;
char path[ITS_FILE_PATH_MAX];
// Validate arguments
VerifyOrExit((aData != NULL && aDataLength > 0), status = PSA_ERROR_INVALID_ARGUMENT);
// Only NONE or WRITE_ONCE => no other flags supported
VerifyOrExit((aCreateFlags & ~(PSA_STORAGE_FLAG_WRITE_ONCE)) == 0, status = PSA_ERROR_NOT_SUPPORTED);
// Ensure directory
VerifyOrExit(ensureDirectoryExists(), status = PSA_ERROR_GENERIC_ERROR);
// Build path
VerifyOrExit(buildFilePath(aUid, path, sizeof(path)) == 0, status = PSA_ERROR_GENERIC_ERROR);
// If file exists, check WRITE_ONCE
file = fopen(path, "rb");
if (file)
{
psa_storage_create_flags_t oldFlags;
uint32_t oldLen;
if (readHeader(file, &oldFlags, &oldLen) == 0)
{
VerifyOrExit(!(oldFlags & PSA_STORAGE_FLAG_WRITE_ONCE), status = PSA_ERROR_NOT_PERMITTED);
}
fclose(file);
file = NULL;
}
// Create/overwrite
file = fopen(path, "wb");
VerifyOrExit(file != NULL, status = PSA_ERROR_GENERIC_ERROR);
// Write header
VerifyOrExit(writeHeader(file, aCreateFlags, aDataLength) == 0, status = PSA_ERROR_GENERIC_ERROR);
// Write data
if (aDataLength > 0 && aData != NULL)
{
size_t written = fwrite(aData, 1, aDataLength, file);
VerifyOrExit(written == aDataLength, status = PSA_ERROR_GENERIC_ERROR);
}
exit:
if (file)
{
fclose(file);
}
return status;
}
psa_status_t psa_its_get(psa_storage_uid_t aUid,
uint32_t aDataOffset,
uint32_t aDataLength,
void *aData,
size_t *aDataLengthOut)
{
psa_status_t status = PSA_SUCCESS;
FILE *file = NULL;
char path[ITS_FILE_PATH_MAX];
VerifyOrExit(aDataLengthOut != NULL, status = PSA_ERROR_INVALID_ARGUMENT);
if (aDataLength > 0)
{
VerifyOrExit(aData != NULL, status = PSA_ERROR_INVALID_ARGUMENT);
}
VerifyOrExit(buildFilePath(aUid, path, sizeof(path)) == 0, status = PSA_ERROR_GENERIC_ERROR);
file = fopen(path, "rb");
VerifyOrExit(file != NULL, status = PSA_ERROR_DOES_NOT_EXIST);
{
psa_storage_create_flags_t flags;
uint32_t totalLen;
VerifyOrExit(readHeader(file, &flags, &totalLen) == 0, status = PSA_ERROR_GENERIC_ERROR);
VerifyOrExit(aDataOffset <= totalLen, status = PSA_ERROR_INVALID_ARGUMENT);
size_t available = totalLen - aDataOffset;
size_t toCopy = (aDataLength <= available) ? aDataLength : available;
VerifyOrExit(fseek(file, aDataOffset, SEEK_CUR) == 0, status = PSA_ERROR_GENERIC_ERROR);
if (toCopy > 0 && aData != NULL)
{
VerifyOrExit(fread(aData, 1, toCopy, file) == toCopy, status = PSA_ERROR_GENERIC_ERROR);
}
*aDataLengthOut = toCopy;
}
exit:
if (file)
{
fclose(file);
}
return status;
}
psa_status_t psa_its_get_info(psa_storage_uid_t aUid,
struct psa_storage_info_t *aInfo)
{
psa_status_t status = PSA_SUCCESS;
FILE *file = NULL;
char path[ITS_FILE_PATH_MAX];
psa_storage_create_flags_t flags;
uint32_t totalLen;
VerifyOrExit(aInfo != NULL, status = PSA_ERROR_INVALID_ARGUMENT);
VerifyOrExit(buildFilePath(aUid, path, sizeof(path)) == 0, status = PSA_ERROR_GENERIC_ERROR);
file = fopen(path, "rb");
VerifyOrExit(file != NULL, status = PSA_ERROR_DOES_NOT_EXIST);
VerifyOrExit(readHeader(file, &flags, &totalLen) == 0, status = PSA_ERROR_GENERIC_ERROR);
aInfo->size = totalLen;
aInfo->flags = flags;
exit:
if (file)
{
fclose(file);
}
return status;
}
psa_status_t psa_its_remove(psa_storage_uid_t aUid)
{
psa_status_t status = PSA_SUCCESS;
FILE *file = NULL;
char path[ITS_FILE_PATH_MAX];
psa_storage_create_flags_t flags;
uint32_t totalLen;
VerifyOrExit(buildFilePath(aUid, path, sizeof(path)) == 0, status = PSA_ERROR_GENERIC_ERROR);
file = fopen(path, "rb");
VerifyOrExit(file != NULL, status = PSA_ERROR_DOES_NOT_EXIST);
VerifyOrExit(readHeader(file, &flags, &totalLen) == 0, status = PSA_ERROR_GENERIC_ERROR);
// If WRITE_ONCE is set, we cannot remove it.
VerifyOrExit(!(flags & PSA_STORAGE_FLAG_WRITE_ONCE), status = PSA_ERROR_NOT_PERMITTED);
fclose(file);
file = NULL;
VerifyOrExit((unlink(path) == 0), status = PSA_ERROR_GENERIC_ERROR);
exit:
if (file)
{
fclose(file);
}
return status;
}
#endif // #if OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_PSA
+265
View File
@@ -0,0 +1,265 @@
/*
* Copyright (c) 2025, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements a simple in-RAM PSA ITS backend for demonstration and testing.
*
* All data is stored in memory and is not persisted after process termination.
*/
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_PSA
#include "psa/error.h"
#include "psa/internal_trusted_storage.h"
#include <string.h>
#define VerifyOrExit(aCondition, aAction) \
do \
{ \
if (!(aCondition)) \
{ \
aAction; \
goto exit; \
} \
} while (0)
/**
* @def RAM_ITS_MAX_KEYS
*
* The maximum number of PSA ITS entries that can be stored in RAM.
*/
#define RAM_ITS_MAX_KEYS 64
/**
* @def RAM_ITS_MAX_DATA_SIZE
*
* The maximum size (in bytes) of data that can be stored for each entry.
*/
#define RAM_ITS_MAX_DATA_SIZE 128
/**
* Represents a single PSA ITS record stored in RAM.
*
*/
typedef struct
{
bool mInUse; // Whether this slot is occupied
psa_storage_uid_t mUid; // Unique ID
psa_storage_create_flags_t mFlags; // Storage flags (e.g. WRITE_ONCE)
uint32_t mDataLen; // Current size of stored data
uint8_t mData[RAM_ITS_MAX_DATA_SIZE]; // Raw data storage
} RamItsEntry;
/**
* Array of entries for RAM-based ITS storage.
*
*/
static RamItsEntry sRamItsEntries[RAM_ITS_MAX_KEYS];
/**
* Finds an existing entry by UID.
*
* @param[in] aUid The UID value to search for.
*
* @returns The index of the matching entry, or -1 if not found.
*
*/
static int RamItsFindEntry(psa_storage_uid_t aUid)
{
for (int i = 0; i < RAM_ITS_MAX_KEYS; i++)
{
if (sRamItsEntries[i].mInUse && (sRamItsEntries[i].mUid == aUid))
{
return i;
}
}
return -1;
}
/**
* Finds a free slot in the storage array.
*
* @returns The index of a free slot, or -1 if none is available.
*
*/
static int RamItsFindFreeSlot(void)
{
for (int i = 0; i < RAM_ITS_MAX_KEYS; i++)
{
if (!sRamItsEntries[i].mInUse)
{
return i;
}
}
return -1;
}
psa_status_t psa_its_set(psa_storage_uid_t aUid,
uint32_t aDataLength,
const void *aData,
psa_storage_create_flags_t aCreateFlags)
{
psa_status_t status = PSA_SUCCESS;
int idx = -1;
VerifyOrExit(!(aData == NULL && aDataLength > 0), status = PSA_ERROR_INVALID_ARGUMENT);
// Allow only NONE or WRITE_ONCE flags. Any others => Not supported.
VerifyOrExit((aCreateFlags & ~(PSA_STORAGE_FLAG_WRITE_ONCE)) == 0,
status = PSA_ERROR_NOT_SUPPORTED);
// Data length must not exceed our internal buffer size.
VerifyOrExit(aDataLength <= RAM_ITS_MAX_DATA_SIZE, status = PSA_ERROR_INVALID_ARGUMENT);
// Find existing entry, if any.
idx = RamItsFindEntry(aUid);
if (idx >= 0)
{
// Entry already exists.
// If WRITE_ONCE is set, we cannot overwrite it.
VerifyOrExit(!(sRamItsEntries[idx].mFlags & PSA_STORAGE_FLAG_WRITE_ONCE), status = PSA_ERROR_NOT_PERMITTED);
// Overwrite existing entry.
sRamItsEntries[idx].mDataLen = aDataLength;
if (aDataLength > 0 && aData != NULL)
{
memcpy(sRamItsEntries[idx].mData, aData, aDataLength);
}
sRamItsEntries[idx].mFlags = aCreateFlags;
}
else
{
// Need a new entry.
idx = RamItsFindFreeSlot();
VerifyOrExit(idx >= 0, status = PSA_ERROR_INSUFFICIENT_STORAGE);
sRamItsEntries[idx].mInUse = true;
sRamItsEntries[idx].mUid = aUid;
sRamItsEntries[idx].mFlags = aCreateFlags;
sRamItsEntries[idx].mDataLen = aDataLength;
if (aDataLength > 0 && aData != NULL)
{
memcpy(sRamItsEntries[idx].mData, aData, aDataLength);
}
}
exit:
return status;
}
psa_status_t psa_its_get(psa_storage_uid_t aUid,
uint32_t aDataOffset,
uint32_t aDataLength,
void *aData,
size_t *aDataLengthOut)
{
psa_status_t status = PSA_SUCCESS;
int idx;
// Validate pointers.
VerifyOrExit(aDataLengthOut != NULL, status = PSA_ERROR_INVALID_ARGUMENT);
if (aDataLength > 0)
{
VerifyOrExit(aData != NULL, status = PSA_ERROR_INVALID_ARGUMENT);
}
idx = RamItsFindEntry(aUid);
VerifyOrExit(idx >= 0, status = PSA_ERROR_DOES_NOT_EXIST);
// Check offset validity.
VerifyOrExit(aDataOffset <= sRamItsEntries[idx].mDataLen, status = PSA_ERROR_INVALID_ARGUMENT);
// Determine how many bytes to copy.
{
size_t available = sRamItsEntries[idx].mDataLen - aDataOffset;
size_t toCopy = (aDataLength <= available) ? aDataLength : available;
if (toCopy > 0)
{
memcpy(aData, &sRamItsEntries[idx].mData[aDataOffset], toCopy);
}
*aDataLengthOut = toCopy;
}
exit:
return status;
}
psa_status_t psa_its_get_info(psa_storage_uid_t aUid,
struct psa_storage_info_t *aInfo)
{
psa_status_t status = PSA_SUCCESS;
int idx;
VerifyOrExit(aInfo != NULL, status = PSA_ERROR_INVALID_ARGUMENT);
idx = RamItsFindEntry(aUid);
VerifyOrExit(idx >= 0, status = PSA_ERROR_DOES_NOT_EXIST);
aInfo->size = sRamItsEntries[idx].mDataLen;
aInfo->flags = sRamItsEntries[idx].mFlags;
exit:
return status;
}
psa_status_t psa_its_remove(psa_storage_uid_t aUid)
{
psa_status_t status = PSA_SUCCESS;
int idx;
idx = RamItsFindEntry(aUid);
VerifyOrExit(idx >= 0, status = PSA_ERROR_DOES_NOT_EXIST);
// If WRITE_ONCE is set, we cannot remove it.
VerifyOrExit(!(sRamItsEntries[idx].mFlags & PSA_STORAGE_FLAG_WRITE_ONCE),
status = PSA_ERROR_NOT_PERMITTED);
// Clear the slot.
memset(sRamItsEntries[idx].mData, 0, sizeof(sRamItsEntries[idx].mData));
sRamItsEntries[idx].mDataLen = 0;
sRamItsEntries[idx].mUid = 0;
sRamItsEntries[idx].mFlags = 0;
sRamItsEntries[idx].mInUse = false;
exit:
return status;
}
#endif // #if OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_PSA