From 31cf8eac654113f9abef1599adcea4ee683c4928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Duda?= Date: Sun, 9 Feb 2025 17:08:07 +0100 Subject: [PATCH] [crypto] PSA API: introduce example native ITS implementations This commit implements two example native ITS for linux-based platform. --- third_party/mbedtls/native_its/BUILD.gn | 58 +++ third_party/mbedtls/native_its/CMakeLists.txt | 60 +++ .../mbedtls/native_its/include/psa/error.h | 48 +++ .../include/psa/internal_trusted_storage.h | 149 +++++++ .../mbedtls/native_its/src/file/its_file.c | 384 ++++++++++++++++++ .../mbedtls/native_its/src/ram/its_ram.c | 265 ++++++++++++ 6 files changed, 964 insertions(+) create mode 100644 third_party/mbedtls/native_its/BUILD.gn create mode 100644 third_party/mbedtls/native_its/CMakeLists.txt create mode 100644 third_party/mbedtls/native_its/include/psa/error.h create mode 100644 third_party/mbedtls/native_its/include/psa/internal_trusted_storage.h create mode 100644 third_party/mbedtls/native_its/src/file/its_file.c create mode 100644 third_party/mbedtls/native_its/src/ram/its_ram.c diff --git a/third_party/mbedtls/native_its/BUILD.gn b/third_party/mbedtls/native_its/BUILD.gn new file mode 100644 index 000000000..130f28fb2 --- /dev/null +++ b/third_party/mbedtls/native_its/BUILD.gn @@ -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" ] +} + diff --git a/third_party/mbedtls/native_its/CMakeLists.txt b/third_party/mbedtls/native_its/CMakeLists.txt new file mode 100644 index 000000000..c03adc6b4 --- /dev/null +++ b/third_party/mbedtls/native_its/CMakeLists.txt @@ -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_compile_definitions(openthread-native-its-file + PRIVATE + $ + 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_compile_definitions(openthread-native-its-ram + PRIVATE + $ + PUBLIC + OPENTHREAD_PSA_CRYPTO_NATIVE_ITS_RAM=1 +) + diff --git a/third_party/mbedtls/native_its/include/psa/error.h b/third_party/mbedtls/native_its/include/psa/error.h new file mode 100644 index 000000000..83740a60b --- /dev/null +++ b/third_party/mbedtls/native_its/include/psa/error.h @@ -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 + +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__ */ diff --git a/third_party/mbedtls/native_its/include/psa/internal_trusted_storage.h b/third_party/mbedtls/native_its/include/psa/internal_trusted_storage.h new file mode 100644 index 000000000..7492fcfc6 --- /dev/null +++ b/third_party/mbedtls/native_its/include/psa/internal_trusted_storage.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 + +#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__ */ diff --git a/third_party/mbedtls/native_its/src/file/its_file.c b/third_party/mbedtls/native_its/src/file/its_file.c new file mode 100644 index 000000000..9b8b8458c --- /dev/null +++ b/third_party/mbedtls/native_its/src/file/its_file.c @@ -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 +#include +#include +#include +#include + +#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 diff --git a/third_party/mbedtls/native_its/src/ram/its_ram.c b/third_party/mbedtls/native_its/src/ram/its_ram.c new file mode 100644 index 000000000..60f0fda71 --- /dev/null +++ b/third_party/mbedtls/native_its/src/ram/its_ram.c @@ -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 + +#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