From df77399ca883f5b4ea35b7083044bbd3a4571b29 Mon Sep 17 00:00:00 2001 From: Tomi Fontanilles Date: Fri, 13 Sep 2024 08:35:38 +0300 Subject: [PATCH] secure_storage: introduce the secure storage subsystem Implements RFC https://github.com/zephyrproject-rtos/zephyr/issues/75275. See also the PR (https://github.com/zephyrproject-rtos/zephyr/pull/76222) for more information. Signed-off-by: Tomi Fontanilles --- MAINTAINERS.yml | 8 + subsys/CMakeLists.txt | 1 + subsys/Kconfig | 1 + subsys/secure_storage/CMakeLists.txt | 48 ++++ subsys/secure_storage/Kconfig | 102 ++++++++ subsys/secure_storage/Kconfig.its_store | 34 +++ subsys/secure_storage/Kconfig.its_transform | 134 ++++++++++ .../internal/zephyr/secure_storage/common.h | 19 ++ .../internal/zephyr/secure_storage/its.h | 31 +++ .../zephyr/secure_storage/its/common.h | 30 +++ .../zephyr/secure_storage/its/store.h | 52 ++++ .../zephyr/secure_storage/its/transform.h | 62 +++++ .../secure_storage/its/transform/aead_get.h | 45 ++++ .../internal/zephyr/secure_storage/ps.h | 39 +++ subsys/secure_storage/include/psa/error.h | 29 +++ .../include/psa/internal_trusted_storage.h | 126 +++++++++ .../include/psa/protected_storage.h | 241 ++++++++++++++++++ .../include/psa/storage_common.h | 51 ++++ subsys/secure_storage/src/CMakeLists.txt | 6 + subsys/secure_storage/src/its/CMakeLists.txt | 29 +++ .../secure_storage/src/its/implementation.c | 228 +++++++++++++++++ .../secure_storage/src/its/store_settings.c | 116 +++++++++ .../secure_storage/src/its/transform/aead.c | 131 ++++++++++ .../src/its/transform/aead_get.c | 144 +++++++++++ subsys/secure_storage/src/log.c | 6 + 25 files changed, 1713 insertions(+) create mode 100644 subsys/secure_storage/CMakeLists.txt create mode 100644 subsys/secure_storage/Kconfig create mode 100644 subsys/secure_storage/Kconfig.its_store create mode 100644 subsys/secure_storage/Kconfig.its_transform create mode 100644 subsys/secure_storage/include/internal/zephyr/secure_storage/common.h create mode 100644 subsys/secure_storage/include/internal/zephyr/secure_storage/its.h create mode 100644 subsys/secure_storage/include/internal/zephyr/secure_storage/its/common.h create mode 100644 subsys/secure_storage/include/internal/zephyr/secure_storage/its/store.h create mode 100644 subsys/secure_storage/include/internal/zephyr/secure_storage/its/transform.h create mode 100644 subsys/secure_storage/include/internal/zephyr/secure_storage/its/transform/aead_get.h create mode 100644 subsys/secure_storage/include/internal/zephyr/secure_storage/ps.h create mode 100644 subsys/secure_storage/include/psa/error.h create mode 100644 subsys/secure_storage/include/psa/internal_trusted_storage.h create mode 100644 subsys/secure_storage/include/psa/protected_storage.h create mode 100644 subsys/secure_storage/include/psa/storage_common.h create mode 100644 subsys/secure_storage/src/CMakeLists.txt create mode 100644 subsys/secure_storage/src/its/CMakeLists.txt create mode 100644 subsys/secure_storage/src/its/implementation.c create mode 100644 subsys/secure_storage/src/its/store_settings.c create mode 100644 subsys/secure_storage/src/its/transform/aead.c create mode 100644 subsys/secure_storage/src/its/transform/aead_get.c create mode 100644 subsys/secure_storage/src/log.c diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index 0e168504467ae8a..2bf251a255621d0 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -4115,6 +4115,14 @@ RTIO: tests: - rtio +Secure storage: + status: maintained + maintainers: + - tomi-font + files: + - subsys/secure_storage/ + labels: + - "area: Secure storage" Storage: status: odd fixes files: diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index 58f0cf6ffc3e23d..96148910a92c564 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -50,6 +50,7 @@ add_subdirectory_ifdef(CONFIG_MODEM_MODULES modem) add_subdirectory_ifdef(CONFIG_NET_BUF net) add_subdirectory_ifdef(CONFIG_PROFILING profiling) add_subdirectory_ifdef(CONFIG_RETENTION retention) +add_subdirectory_ifdef(CONFIG_SECURE_STORAGE secure_storage) add_subdirectory_ifdef(CONFIG_SENSING sensing) add_subdirectory_ifdef(CONFIG_SETTINGS settings) add_subdirectory_ifdef(CONFIG_SHELL shell) diff --git a/subsys/Kconfig b/subsys/Kconfig index 59c7fa1721018c3..561d5263422688e 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -38,6 +38,7 @@ source "subsys/random/Kconfig" source "subsys/retention/Kconfig" source "subsys/rtio/Kconfig" source "subsys/sd/Kconfig" +source "subsys/secure_storage/Kconfig" source "subsys/sensing/Kconfig" source "subsys/settings/Kconfig" source "subsys/shell/Kconfig" diff --git a/subsys/secure_storage/CMakeLists.txt b/subsys/secure_storage/CMakeLists.txt new file mode 100644 index 000000000000000..a15c1b24f441dfb --- /dev/null +++ b/subsys/secure_storage/CMakeLists.txt @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_link_libraries_ifdef(CONFIG_MBEDTLS mbedTLS) +zephyr_library_include_directories(include/internal) # secure_storage headers +add_subdirectory(src) + +# Make the subsystem's PSA Secure Storage API headers available only when it's enabled. +zephyr_include_directories( + include +) + +# Make the secure_storage headers available to the application only when it's implementing the relevant APIs. +function(make_available header) + if (NOT header STREQUAL "common.h") + make_available(common.h) + endif() + if ((header MATCHES "^its") AND NOT (header STREQUAL "its/common.h")) + make_available(its/common.h) + endif() + configure_file(include/internal/zephyr/secure_storage/${header} + ${CMAKE_BINARY_DIR}/zephyr/include/generated/zephyr/secure_storage/${header} + COPYONLY) +endfunction() + +if (CONFIG_SECURE_STORAGE_ITS_IMPLEMENTATION_CUSTOM) + make_available(its.h) +endif() + +if (CONFIG_SECURE_STORAGE_PS_IMPLEMENTATION_CUSTOM) + make_available(ps.h) +endif() + +if (CONFIG_SECURE_STORAGE_ITS_TRANSFORM_IMPLEMENTATION_CUSTOM + OR (CONFIG_SECURE_STORAGE_ITS_STORE_IMPLEMENTATION_CUSTOM + AND CONFIG_SECURE_STORAGE_ITS_TRANSFORM_MODULE)) + make_available(its/transform.h) +endif() + +if (CONFIG_SECURE_STORAGE_ITS_STORE_IMPLEMENTATION_CUSTOM) + make_available(its/store.h) +endif() + +if (CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_SCHEME_CUSTOM + OR CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER_CUSTOM + OR CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_PROVIDER_CUSTOM) + make_available(its/transform/aead_get.h) +endif() diff --git a/subsys/secure_storage/Kconfig b/subsys/secure_storage/Kconfig new file mode 100644 index 000000000000000..11e78a7dbd2ce88 --- /dev/null +++ b/subsys/secure_storage/Kconfig @@ -0,0 +1,102 @@ +# Copyright (c) 2024 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +menuconfig SECURE_STORAGE + bool "Secure storage subsystem" + depends on !BUILD_WITH_TFM + select EXPERIMENTAL + help + The secure storage subsystem provides an implementation of the PSA Secure Storage API + functions on board targets that don't already have one. + It allows making use of the PSA Secure Storage API and persistent keys in the PSA Crypto + API in a standard and portable way. + It is configurable and different implementations can be used to accommodate the varying + capabilities of different devices. + In addition to providing functional support for the PSA Secure Storage API, depending on + the device-specific security features that are available and used, the subsystem may + secure the data stored through it at rest. + This is however highly dependent on the device and configuration in use, and not a + guarantee of the subsystem. + +if SECURE_STORAGE + +module = SECURE_STORAGE +module-str = secure_storage +source "subsys/logging/Kconfig.template.log_config" + +choice SECURE_STORAGE_ITS_IMPLEMENTATION + prompt "Internal Trusted Storage (ITS) API implementation" + +config SECURE_STORAGE_ITS_IMPLEMENTATION_ZEPHYR + bool "Zephyr's ITS implementation" + select SECURE_STORAGE_ITS_TRANSFORM_MODULE + select SECURE_STORAGE_ITS_STORE_MODULE + help + Use Zephyr's implementation of the ITS API. + It calls into the transform and store modules, which + can be configured and have custom implementations. + +config SECURE_STORAGE_ITS_IMPLEMENTATION_CUSTOM + bool "Custom ITS implementation" + help + A custom implementation of the ITS API is present. + Implement the functions declared in . + The header is made available when this Kconfig option is enabled. + +endchoice # SECURE_STORAGE_ITS_IMPLEMENTATION + +config SECURE_STORAGE_ITS_MAX_DATA_SIZE + int "Maximum data size of an ITS entry in bytes" + default 128 + help + The maximum size, in bytes, that the data of an ITS entry can be. + Increasing this value increases the stack usage when serving PSA ITS API calls. + +menuconfig SECURE_STORAGE_ITS_TRANSFORM_MODULE + bool "ITS transform module" + help + The module that handles the transformation and validation of the + ITS data before it's written to and after it's read from NVM. + Zephyr's ITS implementation calls into it. + +if SECURE_STORAGE_ITS_TRANSFORM_MODULE +rsource "Kconfig.its_transform" +endif + +menuconfig SECURE_STORAGE_ITS_STORE_MODULE + bool "ITS store module" + imply FLASH # for FLASH_HAS_DRIVER_ENABLED + help + The module that handles the storage/retrieval of the ITS data to/from NVM. + Zephyr's ITS implementation calls into it. + +if SECURE_STORAGE_ITS_STORE_MODULE +rsource "Kconfig.its_store" +endif + +choice SECURE_STORAGE_PS_IMPLEMENTATION + prompt "Protected Storage (PS) API implementation" + default SECURE_STORAGE_PS_IMPLEMENTATION_ITS + +config SECURE_STORAGE_PS_IMPLEMENTATION_ITS + bool "PS calls directly into the ITS" + help + The PS API doesn't have an implementation of its own, and directly calls into the ITS API. + This means that the implementation of the PS API will be identical to that of the ITS API. + +config SECURE_STORAGE_PS_IMPLEMENTATION_CUSTOM + bool "Custom PS implementation" + help + A custom implementation of the PS API is present. + Implement the functions declared in . + The header is made available when this Kconfig option is enabled. + +endchoice # SECURE_STORAGE_PS_IMPLEMENTATION + +config SECURE_STORAGE_PS_SUPPORTS_SET_EXTENDED + bool "PS API implementation supports psa_ps_create() and psa_ps_set_extended()" + depends on SECURE_STORAGE_PS_IMPLEMENTATION_CUSTOM + help + Whether the psa_ps_create() and psa_ps_set_extended() functions are implemented. + +endif # SECURE_STORAGE diff --git a/subsys/secure_storage/Kconfig.its_store b/subsys/secure_storage/Kconfig.its_store new file mode 100644 index 000000000000000..9e4d9b650205931 --- /dev/null +++ b/subsys/secure_storage/Kconfig.its_store @@ -0,0 +1,34 @@ +# Copyright (c) 2024 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +choice SECURE_STORAGE_ITS_STORE_IMPLEMENTATION + prompt "ITS store module implementation" + +config SECURE_STORAGE_ITS_STORE_IMPLEMENTATION_SETTINGS + bool "ITS store module implementation using the settings subsystem for storage" + DT_STORAGE_PARTITION := $(dt_nodelabel_path,storage_partition) + depends on FLASH_HAS_DRIVER_ENABLED \ + && $(dt_path_enabled,$(DT_STORAGE_PARTITION)) \ + && $(dt_node_has_compat,$(dt_node_parent,$(DT_STORAGE_PARTITION)),fixed-partitions) + imply FLASH_MAP + imply NVS + select SETTINGS + +config SECURE_STORAGE_ITS_STORE_IMPLEMENTATION_NONE + bool "No ITS store module implementation" + +config SECURE_STORAGE_ITS_STORE_IMPLEMENTATION_CUSTOM + bool "Custom ITS store module implementation" + help + Implement the functions declared in . + The header is made available when this Kconfig option is enabled. + +endchoice # SECURE_STORAGE_ITS_STORE_IMPLEMENTATION + +if SECURE_STORAGE_ITS_STORE_IMPLEMENTATION_SETTINGS + +config SECURE_STORAGE_ITS_STORE_SETTINGS_PREFIX + string "Subtree in which to store the settings, with a trailing slash. Can be empty." + default "its/" + +endif # SECURE_STORAGE_ITS_STORE_IMPLEMENTATION_SETTINGS diff --git a/subsys/secure_storage/Kconfig.its_transform b/subsys/secure_storage/Kconfig.its_transform new file mode 100644 index 000000000000000..5fcdb2bdb6bc4e5 --- /dev/null +++ b/subsys/secure_storage/Kconfig.its_transform @@ -0,0 +1,134 @@ +# Copyright (c) 2024 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +choice SECURE_STORAGE_ITS_TRANSFORM_IMPLEMENTATION + prompt "ITS transform module implementation" + +config SECURE_STORAGE_ITS_TRANSFORM_IMPLEMENTATION_AEAD + bool "ITS transform module implementation using AEAD to protect the data" + imply HWINFO # for HWINFO_HAS_DRIVER + +config SECURE_STORAGE_ITS_TRANSFORM_IMPLEMENTATION_CUSTOM + bool "Custom ITS transform module implementation" + help + Implement the functions declared in + and set CONFIG_SECURE_STORAGE_ITS_TRANSFORM_OUTPUT_OVERHEAD appropriately. + The header is made available when this Kconfig option is enabled. + +endchoice # SECURE_STORAGE_ITS_TRANSFORM_IMPLEMENTATION + +config SECURE_STORAGE_ITS_TRANSFORM_OUTPUT_OVERHEAD + int "Overhead, in bytes, associated with the transformation of an entry's data for storage" + range 0 1000 + # authentication tag (16) + nonce (12) + default 28 if SECURE_STORAGE_ITS_TRANSFORM_IMPLEMENTATION_AEAD \ + && SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_SIZE = 12 + default -1 + help + This indicates how many more bytes an ITS entry's data will be once it + has been processed by the secure_storage_its_transform_to_store() function. + +if SECURE_STORAGE_ITS_TRANSFORM_IMPLEMENTATION_AEAD + +choice SECURE_STORAGE_ITS_TRANSFORM_AEAD_SCHEME + prompt "AEAD ITS transform module scheme" + default SECURE_STORAGE_ITS_TRANSFORM_AEAD_SCHEME_AES_GCM + help + The AEAD scheme used to encrypt and authenticate the data. + +config SECURE_STORAGE_ITS_TRANSFORM_AEAD_SCHEME_AES_GCM + bool "AES-GCM AEAD scheme" + select PSA_WANT_KEY_TYPE_AES + select PSA_WANT_ALG_GCM + +config SECURE_STORAGE_ITS_TRANSFORM_AEAD_SCHEME_CHACHA20_POLY1305 + bool "ChaCha20-Poly1305 AEAD scheme" + depends on SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_SIZE = 12 + select PSA_WANT_KEY_TYPE_CHACHA20 + select PSA_WANT_ALG_CHACHA20_POLY1305 + +config SECURE_STORAGE_ITS_TRANSFORM_AEAD_SCHEME_CUSTOM + bool "Custom AEAD scheme" + help + Implement the secure_storage_its_transform_aead_get_scheme() function + declared in + and set CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_SIZE appropriately. + The header is made available when this Kconfig option is enabled. + +endchoice # SECURE_STORAGE_ITS_TRANSFORM_AEAD_SCHEME + +choice SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER + prompt "AEAD ITS transform module encryption key provider" + default SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER_DEVICE_ID_HASH if HWINFO_HAS_DRIVER + default SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER_ENTRY_UID_HASH if !HWINFO_HAS_DRIVER + +config SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER_DEVICE_ID_HASH + bool "Hash of the device ID returned by the HW info API (not necessarily secure)" + depends on HWINFO_HAS_DRIVER + select PSA_WANT_ALG_SHA_256 + help + This key provider generates keys by hashing the following: + - the device EUI64 as returned by hwinfo_get_device_eui64() as first choice; + - the device ID as returned by hwinfo_get_device_uuid() as second choice. + In addition to the device ID, it adds the UID of the ITS entry + for which it is generating a key to the data hashed as a salt. + This is not necessarily secure as the device ID may be easily readable + by an attacker, not unique, and/or guessable, depending on the device. + +config SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER_ENTRY_UID_HASH + bool "Hash of the ITS entry UID (not secure)" + select PSA_WANT_ALG_SHA_256 + help + This key provider generates keys by hashing the UID of the ITS entry for which it is + generating a key. This is not secure, and only intended for functional support, + because the UIDs are easily guessable and even stored in clear by the store module. + Use a secure key provider if possible. + +config SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER_CUSTOM + bool "Custom key provider" + help + Implement the secure_storage_its_transform_aead_get_key() function + declared in . + The header is made available when this Kconfig option is enabled. + +endchoice # SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER + +config SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_SIZE + int "AEAD ITS transform module encryption key size in bytes" + default 32 + +if !SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER_CUSTOM + +config SECURE_STORAGE_ITS_TRANSFORM_AEAD_NO_INSECURE_KEY_WARNING + bool "Silence the insecure ITS encryption key warnings" + +endif + +choice SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_PROVIDER + prompt "AEAD ITS transform module nonce provider" + +config SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_PROVIDER_DEFAULT + bool "Default nonce provider" + help + The default nonce provider generates a random number for the first nonce with + psa_generate_random(), then increments it for every subsequent nonce. A random + source that doesn't repeat values between reboots is required for this to be secure. + +config SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_PROVIDER_CUSTOM + bool "Custom nonce provider" + help + Implement the secure_storage_its_transform_aead_get_nonce() function + declared in . + The header is made available when this Kconfig option is enabled. + +endchoice # SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_PROVIDER + +config SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_SIZE + int "AEAD ITS transform module nonce size in bytes" + range 4 24 + default 12 + help + Make sure to update CONFIG_SECURE_STORAGE_ITS_TRANSFORM_OUTPUT_OVERHEAD + appropriately when changing the value of this option. + +endif # SECURE_STORAGE_ITS_TRANSFORM_IMPLEMENTATION_AEAD diff --git a/subsys/secure_storage/include/internal/zephyr/secure_storage/common.h b/subsys/secure_storage/include/internal/zephyr/secure_storage/common.h new file mode 100644 index 000000000000000..6c2c8922395fcc7 --- /dev/null +++ b/subsys/secure_storage/include/internal/zephyr/secure_storage/common.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef SECURE_STORAGE_COMMON_H +#define SECURE_STORAGE_COMMON_H + +/** @file zephyr/secure_storage/common.h Common definitions of the secure storage subsystem. */ +#include + +/* A size-optimized version of `psa_storage_create_flags_t`. Used for storing the `create_flags`. */ +typedef uint8_t secure_storage_packed_create_flags_t; + +#define SECURE_STORAGE_ALL_CREATE_FLAGS \ + (PSA_STORAGE_FLAG_NONE | \ + PSA_STORAGE_FLAG_WRITE_ONCE | \ + PSA_STORAGE_FLAG_NO_CONFIDENTIALITY | \ + PSA_STORAGE_FLAG_NO_REPLAY_PROTECTION) + +#endif diff --git a/subsys/secure_storage/include/internal/zephyr/secure_storage/its.h b/subsys/secure_storage/include/internal/zephyr/secure_storage/its.h new file mode 100644 index 000000000000000..009bb5e061af75f --- /dev/null +++ b/subsys/secure_storage/include/internal/zephyr/secure_storage/its.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef SECURE_STORAGE_ITS_H +#define SECURE_STORAGE_ITS_H + +/** @file zephyr/secure_storage/its.h The secure storage ITS implementation. + * + * The functions declared in this header implement the PSA ITS API + * when the secure storage subsystem is enabled. + * They must not be called directly, and this header must not be included other than when + * providing a custom implementation (@kconfig{CONFIG_SECURE_STORAGE_ITS_IMPLEMENTATION_CUSTOM}). + */ +#include "its/common.h" + +/** @brief See `psa_its_set()`, to which this function is analogous. */ +psa_status_t secure_storage_its_set(secure_storage_its_uid_t uid, size_t data_length, + const void *p_data, psa_storage_create_flags_t create_flags); + +/** @brief See `psa_its_get()`, to which this function is analogous. */ +psa_status_t secure_storage_its_get(secure_storage_its_uid_t uid, size_t data_offset, + size_t data_size, void *p_data, size_t *p_data_length); + +/** @brief See `psa_its_get_info()`, to which this function is analogous. */ +psa_status_t secure_storage_its_get_info(secure_storage_its_uid_t uid, + struct psa_storage_info_t *p_info); + +/** @brief See `psa_its_remove()`, to which this function is analogous. */ +psa_status_t secure_storage_its_remove(secure_storage_its_uid_t uid); + +#endif diff --git a/subsys/secure_storage/include/internal/zephyr/secure_storage/its/common.h b/subsys/secure_storage/include/internal/zephyr/secure_storage/its/common.h new file mode 100644 index 000000000000000..4b73eafcf3d8868 --- /dev/null +++ b/subsys/secure_storage/include/internal/zephyr/secure_storage/its/common.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef SECURE_STORAGE_ITS_COMMON_H +#define SECURE_STORAGE_ITS_COMMON_H + +/** @file zephyr/secure_storage/its/common.h + * @brief Common definitions of the secure storage subsystem's ITS APIs. + */ +#include "../common.h" +#include +#include + +/** @brief The ID of the caller from which the ITS API call originates. + * This is used to prevent ID collisions between different callers that are not aware + * of each other and so might use the same numerical IDs, e.g. PSA Crypto and PSA ITS. + */ +typedef enum { + SECURE_STORAGE_ITS_CALLER_PSA_ITS, + SECURE_STORAGE_ITS_CALLER_PSA_PS, + SECURE_STORAGE_ITS_CALLER_MBEDTLS, +} secure_storage_its_caller_id_t; + +/** The UID (caller + entry IDs) of an ITS entry. */ +typedef struct { + psa_storage_uid_t uid; + secure_storage_its_caller_id_t caller_id; +} __packed secure_storage_its_uid_t; + +#endif diff --git a/subsys/secure_storage/include/internal/zephyr/secure_storage/its/store.h b/subsys/secure_storage/include/internal/zephyr/secure_storage/its/store.h new file mode 100644 index 000000000000000..fec3448b21589e2 --- /dev/null +++ b/subsys/secure_storage/include/internal/zephyr/secure_storage/its/store.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef SECURE_STORAGE_ITS_STORE_H +#define SECURE_STORAGE_ITS_STORE_H + +/** @file zephyr/secure_storage/its/store.h The secure storage ITS store module. + * + * The functions declared in this header implement the ITS store module. + * They are meant to be called only by the ITS implementation. + * This header may be included when providing a custom implementation of the + * ITS store module (@kconfig{CONFIG_SECURE_STORAGE_ITS_STORE_IMPLEMENTATION_CUSTOM}). + */ +#include + +/** @brief Writes the data of an ITS entry to the storage medium. + * + * @param uid The entry's UID. + * @param data_length The number of bytes in `data`. + * @param data The data to store. + * + * @retval `PSA_SUCCESS` on success. + * @retval `PSA_ERROR_INSUFFICIENT_STORAGE` if there is insufficient storage space. + * @retval `PSA_ERROR_STORAGE_FAILURE` on any other failure. + */ +psa_status_t secure_storage_its_store_set(secure_storage_its_uid_t uid, + size_t data_length, const void *data); + +/** @brief Retrieves the data of an ITS entry from the storage medium. + * + * @param[in] uid The entry's UID. + * @param[in] data_size The size of `data` in bytes. + * @param[out] data The buffer to which the entry's stored data is written. + * @param[out] data_length On success, the number of bytes written to `data`. + * May be less than `data_size`. + * + * @retval `PSA_SUCCESS` on success. + * @retval `PSA_ERROR_DOES_NOT_EXIST` if no entry with the given UID exists. + * @retval `PSA_ERROR_STORAGE_FAILURE` on any other failure. + */ +psa_status_t secure_storage_its_store_get(secure_storage_its_uid_t uid, size_t data_size, + void *data, size_t *data_length); + +/** @brief Removes an ITS entry from the storage medium. + * + * @param uid The entry's UID. + * + * @return `PSA_SUCCESS` on success, anything else on failure. + */ +psa_status_t secure_storage_its_store_remove(secure_storage_its_uid_t uid); + +#endif diff --git a/subsys/secure_storage/include/internal/zephyr/secure_storage/its/transform.h b/subsys/secure_storage/include/internal/zephyr/secure_storage/its/transform.h new file mode 100644 index 000000000000000..ad8f3e81a3186f0 --- /dev/null +++ b/subsys/secure_storage/include/internal/zephyr/secure_storage/its/transform.h @@ -0,0 +1,62 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef SECURE_STORAGE_ITS_TRANSFORM_H +#define SECURE_STORAGE_ITS_TRANSFORM_H + +/** @file zephyr/secure_storage/its/transform.h The secure storage ITS transform module. + * + * The functions declared in this header implement the ITS transform module. + * They are meant to be called only by the ITS implementation. + * This header may be included when providing a custom implementation of the + * ITS transform module (@kconfig{CONFIG_SECURE_STORAGE_ITS_TRANSFORM_IMPLEMENTATION_CUSTOM}). + */ +#include + +/** The maximum size, in bytes, of an entry's data after it has been transformed for storage. */ +enum { SECURE_STORAGE_ITS_TRANSFORM_MAX_STORED_DATA_SIZE + = CONFIG_SECURE_STORAGE_ITS_MAX_DATA_SIZE + + sizeof(secure_storage_packed_create_flags_t) + + CONFIG_SECURE_STORAGE_ITS_TRANSFORM_OUTPUT_OVERHEAD }; + +#define SECURE_STORAGE_ITS_TRANSFORM_DATA_SIZE(stored_data_len) \ + (stored_data_len - (SECURE_STORAGE_ITS_TRANSFORM_MAX_STORED_DATA_SIZE \ + - CONFIG_SECURE_STORAGE_ITS_MAX_DATA_SIZE)) + +/** @brief Transforms the data of an ITS entry for storage. + * + * @param[in] uid The entry's UID. + * @param[in] data_len The number of bytes in `data`. + * @param[in] data The data to transform for storage. + * @param[in] create_flags The entry's create flags. It must contain only valid + * `PSA_STORAGE_FLAG_*` values. It gets stored as part of `stored_data`. + * @param[out] stored_data The buffer to which the transformed data is written. + * @param[out] stored_data_len On success, the number of bytes written to `stored_data`. + * + * @return `PSA_SUCCESS` on success, anything else on failure. + */ +psa_status_t secure_storage_its_transform_to_store( + secure_storage_its_uid_t uid, size_t data_len, const void *data, + secure_storage_packed_create_flags_t create_flags, + uint8_t stored_data[static SECURE_STORAGE_ITS_TRANSFORM_MAX_STORED_DATA_SIZE], + size_t *stored_data_len); + +/** @brief Transforms and validates the stored data of an ITS entry for use. + * + * @param[in] uid The entry's UID. + * @param[in] stored_data_len The number of bytes in `stored_data`. + * @param[in] stored_data The stored data to transform for use. + * @param[in] data_size The size of `data` in bytes. + * @param[out] data The buffer to which the transformed data is written. + * @param[out] data_len On success, the number of bytes written to `stored_data`. + * @param[out] create_flags On success, the entry's create flags. + * + * @return `PSA_SUCCESS` on success, anything else on failure. + */ +psa_status_t secure_storage_its_transform_from_store( + secure_storage_its_uid_t uid, size_t stored_data_len, + const uint8_t stored_data[static SECURE_STORAGE_ITS_TRANSFORM_MAX_STORED_DATA_SIZE], + size_t data_size, void *data, size_t *data_len, + psa_storage_create_flags_t *create_flags); + +#endif diff --git a/subsys/secure_storage/include/internal/zephyr/secure_storage/its/transform/aead_get.h b/subsys/secure_storage/include/internal/zephyr/secure_storage/its/transform/aead_get.h new file mode 100644 index 000000000000000..071c74c029e6257 --- /dev/null +++ b/subsys/secure_storage/include/internal/zephyr/secure_storage/its/transform/aead_get.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef SECURE_STORAGE_ITS_TRANSFORM_AEAD_GET_H +#define SECURE_STORAGE_ITS_TRANSFORM_AEAD_GET_H + +/** @file zephyr/secure_storage/its/transform/aead_get.h The AEAD ITS transform module API. + * + * The functions declared in this header allow customization + * of the AEAD implementation of the ITS transform module. + * They are not meant to be called directly other than by the AEAD ITS transform module. + * This header may be included when providing a custom implementation of one + * or more of these functions (@kconfig{CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_*_CUSTOM}). + */ +#include +#include + +/** @brief Returns the key type and algorithm to use for the AEAD operations. + * + * @param[out] key_type The key type to use. + * @param[out] alg The algorithm to use. + */ +void secure_storage_its_transform_aead_get_scheme(psa_key_type_t *key_type, psa_algorithm_t *alg); + +/** @brief Returns the encryption key to use for an ITS entry's AEAD operations. + * + * @param[in] uid The UID of the ITS entry for whom the returned key is used. + * @param[out] key The encryption key. + * + * @return `PSA_SUCCESS` on success, anything else on failure. + */ +psa_status_t secure_storage_its_transform_aead_get_key( + secure_storage_its_uid_t uid, + uint8_t key[static CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_SIZE]); + +/** @brief Generates a nonce for an AEAD operation. + * + * @param[out] nonce The generated nonce. + * + * @return `PSA_SUCCESS` on success, anything else on failure. + */ +psa_status_t secure_storage_its_transform_aead_get_nonce( + uint8_t nonce[static CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_SIZE]); + +#endif diff --git a/subsys/secure_storage/include/internal/zephyr/secure_storage/ps.h b/subsys/secure_storage/include/internal/zephyr/secure_storage/ps.h new file mode 100644 index 000000000000000..fa5344ded352204 --- /dev/null +++ b/subsys/secure_storage/include/internal/zephyr/secure_storage/ps.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef SECURE_STORAGE_PS_H +#define SECURE_STORAGE_PS_H + +/** @file zephyr/secure_storage/ps.h The secure storage PS implementation. + * + * The functions declared in this header implement the PSA PS API + * when the secure storage subsystem is enabled. + * They must not be called directly, and this header must not be included other than when + * providing a custom implementation (@kconfig{CONFIG_SECURE_STORAGE_PS_IMPLEMENTATION_CUSTOM}). + */ +#include + +/** @brief See `psa_ps_set()`, to which this function is analogous. */ +psa_status_t secure_storage_ps_set(const psa_storage_uid_t uid, size_t data_length, + const void *p_data, psa_storage_create_flags_t create_flags); + +/** @brief See `psa_ps_get()`, to which this function is analogous. */ +psa_status_t secure_storage_ps_get(const psa_storage_uid_t uid, size_t data_offset, + size_t data_length, void *p_data, size_t *p_data_length); + +/** @brief See `psa_ps_get_info()`, to which this function is analogous. */ +psa_status_t secure_storage_ps_get_info(const psa_storage_uid_t uid, + struct psa_storage_info_t *p_info); + +/** @brief See `psa_ps_remove()`, to which this function is analogous. */ +psa_status_t secure_storage_ps_remove(const psa_storage_uid_t uid); + +/** @brief See `psa_ps_create()`, to which this function is analogous. */ +psa_status_t secure_storage_ps_create(psa_storage_uid_t uid, size_t capacity, + psa_storage_create_flags_t create_flags); + +/** @brief See `psa_ps_set_extended()`, to which this function is analogous. */ +psa_status_t secure_storage_ps_set_extended(psa_storage_uid_t uid, size_t data_offset, + size_t data_length, const void *p_data); + +#endif diff --git a/subsys/secure_storage/include/psa/error.h b/subsys/secure_storage/include/psa/error.h new file mode 100644 index 000000000000000..439f4b5e4de512b --- /dev/null +++ b/subsys/secure_storage/include/psa/error.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef PSA_ERROR_H +#define PSA_ERROR_H +/** + * @file psa/error.h Return values of the PSA Secure Storage API. + * @ingroup psa_secure_storage + * @{ + */ +#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 diff --git a/subsys/secure_storage/include/psa/internal_trusted_storage.h b/subsys/secure_storage/include/psa/internal_trusted_storage.h new file mode 100644 index 000000000000000..a8c3b77c601a00d --- /dev/null +++ b/subsys/secure_storage/include/psa/internal_trusted_storage.h @@ -0,0 +1,126 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef PSA_INTERNAL_TRUSTED_STORAGE_H +#define PSA_INTERNAL_TRUSTED_STORAGE_H + +/** @file psa/internal_trusted_storage.h The PSA Internal Trusted Storage (ITS) API. + * @ingroup psa_secure_storage + * For more information on the ITS, see [The Internal Trusted Storage API](https://arm-software.github.io/psa-api/storage/1.0/overview/architecture.html#the-internal-trusted-storage-api). + */ + +/** @cond INTERNAL_HIDDEN */ +#include "../internal/zephyr/secure_storage/its.h" +#ifdef BUILDING_MBEDTLS_CRYPTO +#define ITS_CALLER_ID SECURE_STORAGE_ITS_CALLER_MBEDTLS +#else +#define ITS_CALLER_ID SECURE_STORAGE_ITS_CALLER_PSA_ITS +#endif +#define ITS_UID (secure_storage_its_uid_t){.uid = uid, .caller_id = ITS_CALLER_ID} +/** @endcond */ + +#include + +#define PSA_ITS_API_VERSION_MAJOR 1 +#define PSA_ITS_API_VERSION_MINOR 0 + +/** + * @brief Creates a new or modifies an existing entry. + * + * Stores data in the internal storage. + * + * @param uid The identifier of the data. Must be nonzero. + * @param data_length The size in bytes of the data in `p_data` to store. + * @param p_data A buffer containing the data to store. + * @param create_flags Flags indicating the properties of the entry. + * + * @retval PSA_SUCCESS The operation completed successfully. + * @retval PSA_ERROR_NOT_PERMITTED An entry associated with the provided `uid` already + * exists and was created with `PSA_STORAGE_FLAG_WRITE_ONCE`. + * @retval PSA_ERROR_NOT_SUPPORTED One or more of the flags provided in `create_flags` + * are not supported or invalid. + * @retval PSA_ERROR_INVALID_ARGUMENT One or more arguments other than `create_flags` are + * invalid. + * @retval PSA_ERROR_INSUFFICIENT_STORAGE There is insufficient space on the storage medium. + * @retval PSA_ERROR_STORAGE_FAILURE The physical storage has failed (fatal error). + */ +/** @cond INTERNAL_HIDDEN */ +static ALWAYS_INLINE +/** @endcond */ +psa_status_t psa_its_set(psa_storage_uid_t uid, size_t data_length, + const void *p_data, psa_storage_create_flags_t create_flags) +{ + return secure_storage_its_set(ITS_UID, data_length, p_data, create_flags); +} + +/** + * @brief Retrieves data associated with the provided `uid`. + * + * @param[in] uid The identifier of the data. + * @param[in] data_offset The offset, in bytes, from which to start reading the data. + * @param[in] data_size The number of bytes to read. + * @param[out] p_data The buffer where the data will be placed on success. + * Must be at least `data_size` bytes long. + * @param[out] p_data_length On success, the number of bytes placed in `p_data`. + * + * @retval PSA_SUCCESS The operation completed successfully. + * @retval PSA_ERROR_INVALID_ARGUMENT One or more of the arguments are invalid. This can also + * happen if `data_offset` is larger than the size of the data + * associated with `uid`. + * @retval PSA_ERROR_DOES_NOT_EXIST The provided `uid` was not found in the storage. + * @retval PSA_ERROR_STORAGE_FAILURE The physical storage has failed (fatal error). + */ +/** @cond INTERNAL_HIDDEN */ +static ALWAYS_INLINE +/** @endcond */ +psa_status_t psa_its_get(psa_storage_uid_t uid, size_t data_offset, + size_t data_size, void *p_data, size_t *p_data_length) +{ + return secure_storage_its_get(ITS_UID, data_offset, data_size, p_data, p_data_length); +} + +/** + * @brief Retrieves the metadata of a given entry. + * + * @param[in] uid The identifier of the entry. + * @param[out] p_info A pointer to a `psa_storage_info_t` struct that will + * be populated with the metadata on success. + * + * @retval PSA_SUCCESS The operation completed successfully. + * @retval PSA_ERROR_INVALID_ARGUMENT One or more of the arguments are invalid. + * @retval PSA_ERROR_DOES_NOT_EXIST The provided `uid` was not found in the storage. + * @retval PSA_ERROR_STORAGE_FAILURE The physical storage has failed (fatal error). + */ +/** @cond INTERNAL_HIDDEN */ +static ALWAYS_INLINE +/** @endcond */ +psa_status_t psa_its_get_info(psa_storage_uid_t uid, struct psa_storage_info_t *p_info) +{ + return secure_storage_its_get_info(ITS_UID, p_info); +} + +/** + * @brief Removes the provided `uid` and its associated data. + * + * Deletes all the data associated with the entry from internal storage. + * + * @param uid The identifier of the entry to remove. + * + * @retval PSA_SUCCESS The operation completed successfully. + * @retval PSA_ERROR_NOT_PERMITTED The entry was created with `PSA_STORAGE_FLAG_WRITE_ONCE`. + * @retval PSA_ERROR_INVALID_ARGUMENT `uid` is invalid. + * @retval PSA_ERROR_DOES_NOT_EXIST The provided `uid` was not found in the storage. + * @retval PSA_ERROR_STORAGE_FAILURE The physical storage has failed (fatal error). + */ +/** @cond INTERNAL_HIDDEN */ +static ALWAYS_INLINE +/** @endcond */ +psa_status_t psa_its_remove(psa_storage_uid_t uid) +{ + return secure_storage_its_remove(ITS_UID); +} + +#undef ITS_UID +#undef ITS_CALLER_ID + +#endif diff --git a/subsys/secure_storage/include/psa/protected_storage.h b/subsys/secure_storage/include/psa/protected_storage.h new file mode 100644 index 000000000000000..7c20d3097a2ad5d --- /dev/null +++ b/subsys/secure_storage/include/psa/protected_storage.h @@ -0,0 +1,241 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef PSA_PROTECTED_STORAGE_H +#define PSA_PROTECTED_STORAGE_H + +/** @file psa/protected_storage.h The PSA Protected Storage (PS) API. + * @ingroup psa_secure_storage + * For more information on the PS, see [The Protected Storage API](https://arm-software.github.io/psa-api/storage/1.0/overview/architecture.html#the-protected-storage-api). + */ + +/** @cond INTERNAL_HIDDEN */ +#ifdef CONFIG_SECURE_STORAGE_PS_IMPLEMENTATION_ITS +#include "../internal/zephyr/secure_storage/its.h" +#define ITS_UID (secure_storage_its_uid_t){.uid = uid, \ + .caller_id = SECURE_STORAGE_ITS_CALLER_PSA_PS} +#else +#include "../internal/zephyr/secure_storage/ps.h" +#endif +/** @endcond */ + +#include + +#define PSA_PS_API_VERSION_MAJOR 1 +#define PSA_PS_API_VERSION_MINOR 0 + +/** + * @brief Creates a new or modifies an existing entry. + * + * @param uid The identifier of the data. Must be nonzero. + * @param data_length The size in bytes of the data in `p_data` to store. + * @param p_data A buffer containing the data to store. + * @param create_flags Flags indicating the properties of the entry. + * + * @retval PSA_SUCCESS The operation completed successfully. + * @retval PSA_ERROR_GENERIC_ERROR An unspecified internal failure happened. + * @retval PSA_ERROR_NOT_PERMITTED An entry associated with the provided `uid` already + * exists and was created with `PSA_STORAGE_FLAG_WRITE_ONCE`. + * @retval PSA_ERROR_NOT_SUPPORTED One or more of the flags provided in `create_flags` + * are not supported or invalid. + * @retval PSA_ERROR_INVALID_ARGUMENT One or more arguments other than `create_flags` are + * invalid. + * @retval PSA_ERROR_INSUFFICIENT_STORAGE There is insufficient space on the storage medium. + * @retval PSA_ERROR_STORAGE_FAILURE The physical storage has failed (fatal error). + */ +/** @cond INTERNAL_HIDDEN */ +static ALWAYS_INLINE +/** @endcond */ +psa_status_t psa_ps_set(psa_storage_uid_t uid, size_t data_length, + const void *p_data, psa_storage_create_flags_t create_flags) +{ +#ifdef CONFIG_SECURE_STORAGE_PS_IMPLEMENTATION_ITS + return secure_storage_its_set(ITS_UID, data_length, p_data, create_flags); +#else + return secure_storage_ps_set(uid, data_length, p_data, create_flags); +#endif +} + +/** + * @brief Retrieves data associated with the provided `uid`. + * + * @param[in] uid The identifier of the data. + * @param[in] data_offset The offset, in bytes, from which to start reading the data. + * @param[in] data_size The number of bytes to read. + * @param[out] p_data The buffer where the data will be placed on success. + * Must be at least `data_size` bytes long. + * @param[out] p_data_length On success, the number of bytes placed in `p_data`. + * + * @retval PSA_SUCCESS The operation completed successfully. + * @retval PSA_ERROR_GENERIC_ERROR An unspecified internal failure happened. + * @retval PSA_ERROR_INVALID_ARGUMENT One or more of the arguments are invalid. This can also + * happen if `data_offset` is larger than the size of the data + * associated with `uid`. + * @retval PSA_ERROR_DOES_NOT_EXIST The provided `uid` was not found in the storage. + * @retval PSA_ERROR_STORAGE_FAILURE The physical storage has failed (fatal error). + * @retval PSA_ERROR_INVALID_SIGNATURE The data associated with `uid` failed authentication. + * @retval PSA_ERROR_DATA_CORRUPT The data associated with `uid` is corrupt. + */ +/** @cond INTERNAL_HIDDEN */ +static ALWAYS_INLINE +/** @endcond */ +psa_status_t psa_ps_get(psa_storage_uid_t uid, size_t data_offset, + size_t data_size, void *p_data, size_t *p_data_length) +{ +#ifdef CONFIG_SECURE_STORAGE_PS_IMPLEMENTATION_ITS + return secure_storage_its_get(ITS_UID, data_offset, data_size, p_data, p_data_length); +#else + return secure_storage_ps_get(uid, data_offset, data_size, p_data, p_data_length); +#endif +} + +/** + * @brief Retrieves the metadata of a given entry. + * + * @param[in] uid The identifier of the entry. + * @param[out] p_info A pointer to a `psa_storage_info_t` struct that will + * be populated with the metadata on success. + * + * @retval PSA_SUCCESS The operation completed successfully. + * @retval PSA_ERROR_GENERIC_ERROR An unspecified internal failure happened. + * @retval PSA_ERROR_INVALID_ARGUMENT One or more of the arguments are invalid. + * @retval PSA_ERROR_DOES_NOT_EXIST The provided `uid` was not found in the storage. + * @retval PSA_ERROR_STORAGE_FAILURE The physical storage has failed (fatal error). + * @retval PSA_ERROR_INVALID_SIGNATURE The data associated with `uid` failed authentication. + * @retval PSA_ERROR_DATA_CORRUPT The data associated with `uid` is corrupt. + */ +/** @cond INTERNAL_HIDDEN */ +static ALWAYS_INLINE +/** @endcond */ +psa_status_t psa_ps_get_info(psa_storage_uid_t uid, struct psa_storage_info_t *p_info) +{ +#ifdef CONFIG_SECURE_STORAGE_PS_IMPLEMENTATION_ITS + return secure_storage_its_get_info(ITS_UID, p_info); +#else + return secure_storage_ps_get_info(uid, p_info); +#endif +} + +/** + * @brief Removes the provided `uid` and its associated data. + * + * Deletes previously stored data and any associated metadata, including rollback protection data. + * + * @param uid The identifier of the entry to remove. + * + * @return A status indicating the success/failure of the operation + * + * @retval PSA_SUCCESS The operation completed successfully. + * @retval PSA_ERROR_GENERIC_ERROR An unspecified internal failure happened. + * @retval PSA_ERROR_NOT_PERMITTED The entry was created with `PSA_STORAGE_FLAG_WRITE_ONCE`. + * @retval PSA_ERROR_INVALID_ARGUMENT `uid` is invalid. + * @retval PSA_ERROR_DOES_NOT_EXIST The provided `uid` was not found in the storage. + * @retval PSA_ERROR_STORAGE_FAILURE The physical storage has failed (fatal error). + */ +/** @cond INTERNAL_HIDDEN */ +static ALWAYS_INLINE +/** @endcond */ +psa_status_t psa_ps_remove(psa_storage_uid_t uid) +{ +#ifdef CONFIG_SECURE_STORAGE_PS_IMPLEMENTATION_ITS + return secure_storage_its_remove(ITS_UID); +#else + return secure_storage_ps_remove(uid); +#endif +} + +/** + * @brief Reserves storage for the provided `uid`. + * + * Upon success, the capacity of the storage for `uid` will be `capacity`, and the size will be 0. + * It is only necessary to call this function for data that will be written with the + * @ref psa_ps_set_extended() function. If only the @ref psa_ps_set() function is used, calls to + * this function are redundant. This function cannot be used to replace or resize an existing entry. + * + * @param uid The identifier of the entry to reserve storage for. + * @param capacity The capacity, in bytes, to allocate. + * @param create_flags Flags indicating the properties of the entry. + * + * @retval PSA_SUCCESS The operation completed successfully. + * @retval PSA_ERROR_GENERIC_ERROR An unspecified internal failure happened. + * @retval PSA_ERROR_NOT_SUPPORTED The implementation doesn't support this function or one + * or more of the flags provided in `create_flags` are not + * supported or invalid. + * @retval PSA_ERROR_INVALID_ARGUMENT `uid` is invalid. + * @retval PSA_ERROR_ALREADY_EXISTS An entry with the provided `uid` already exists. + * @retval PSA_ERROR_INSUFFICIENT_STORAGE There is insufficient space on the storage medium. + * @retval PSA_ERROR_STORAGE_FAILURE The physical storage has failed (fatal error). + */ +/** @cond INTERNAL_HIDDEN */ +static ALWAYS_INLINE +/** @endcond */ +psa_status_t psa_ps_create(psa_storage_uid_t uid, size_t capacity, + psa_storage_create_flags_t create_flags) +{ +#ifdef CONFIG_SECURE_STORAGE_PS_SUPPORTS_SET_EXTENDED + return secure_storage_ps_create(uid, capacity, create_flags); +#else + return PSA_ERROR_NOT_SUPPORTED; +#endif +} + +/** + * @brief Writes part of the data associated with the provided `uid`. + * + * Before calling this function, storage must have been reserved with a call to + * @ref psa_ps_create(). This function can also be used to overwrite data that was + * written with @ref psa_ps_set(). This function can overwrite existing data and/or extend + * it up to the capacity of the entry specified in @ref psa_ps_create(), but cannot create gaps. + * + * @param uid The identifier of the entry to write. + * @param data_offset The offset, in bytes, from which to start writing the data. + * Can be at most the current size of the data. + * @param data_length The size in bytes of the data in `p_data` to write. `data_offset` + * + `data_length` can be at most the capacity of the entry. + * @param p_data A buffer containing the data to write. + * + * @retval PSA_SUCCESS The operation completed successfully. + * @retval PSA_ERROR_GENERIC_ERROR An unspecified internal failure happened. + * @retval PSA_ERROR_NOT_PERMITTED The entry was created with `PSA_STORAGE_FLAG_WRITE_ONCE`. + * @retval PSA_ERROR_NOT_SUPPORTED The implementation doesn't support this function. + * @retval PSA_ERROR_INVALID_ARGUMENT One or more of the arguments are invalid. + * @retval PSA_ERROR_DOES_NOT_EXIST The provided `uid` was not found in the storage. + * @retval PSA_ERROR_STORAGE_FAILURE The physical storage has failed (fatal error). + * @retval PSA_ERROR_INVALID_SIGNATURE The data associated with `uid` failed authentication. + * @retval PSA_ERROR_DATA_CORRUPT The data associated with `uid` is corrupt. + */ +/** @cond INTERNAL_HIDDEN */ +static ALWAYS_INLINE +/** @endcond */ +psa_status_t psa_ps_set_extended(psa_storage_uid_t uid, size_t data_offset, + size_t data_length, const void *p_data) +{ +#ifdef CONFIG_SECURE_STORAGE_PS_SUPPORTS_SET_EXTENDED + return secure_storage_ps_set_extended(uid, data_offset, data_length, p_data); +#else + return PSA_ERROR_NOT_SUPPORTED; +#endif +} + +/** + * @brief Lists optional features. + * + * @return A bitmask with flags set for the optional features supported by the implementation. + * Currently defined flags are limited to `PSA_STORAGE_SUPPORT_SET_EXTENDED`. + */ +/** @cond INTERNAL_HIDDEN */ +static ALWAYS_INLINE +/** @endcond */ +uint32_t psa_ps_get_support(void) +{ + uint32_t flags = 0; + +#ifdef CONFIG_SECURE_STORAGE_PS_SUPPORTS_SET_EXTENDED + flags |= PSA_STORAGE_SUPPORT_SET_EXTENDED; +#endif + return flags; +} + +#undef ITS_UID + +#endif diff --git a/subsys/secure_storage/include/psa/storage_common.h b/subsys/secure_storage/include/psa/storage_common.h new file mode 100644 index 000000000000000..d5bed2a692e7016 --- /dev/null +++ b/subsys/secure_storage/include/psa/storage_common.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef PSA_STORAGE_COMMON_H +#define PSA_STORAGE_COMMON_H +/** + * @defgroup psa_secure_storage PSA Secure Storage API + * @ingroup os_services + * @details For more information on the PSA Secure Storage API, see the + * [PSA Certified Secure Storage API](https://arm-software.github.io/psa-api/storage/1.0/) + * specification. + */ +/** + * @file psa/storage_common.h + * @ingroup psa_secure_storage + * @brief Common definitions of the PSA Secure Storage API. + * @{ + */ +#include +#include + +/** UID type for identifying entries. */ +typedef uint64_t psa_storage_uid_t; + +/** Flags used when creating an entry. */ +typedef uint32_t psa_storage_create_flags_t; + +/** No flag to pass. */ +#define PSA_STORAGE_FLAG_NONE 0u +/** The data associated with the UID will not be able to be modified or deleted. */ +#define PSA_STORAGE_FLAG_WRITE_ONCE (1u << 0) +/** The data associated with the UID is public, requiring only integrity. */ +#define PSA_STORAGE_FLAG_NO_CONFIDENTIALITY (1u << 1) +/** The data associated with the UID does not require replay protection. */ +#define PSA_STORAGE_FLAG_NO_REPLAY_PROTECTION (1u << 2) + +/** Metadata associated with a specific entry. */ +struct psa_storage_info_t { + /** The allocated capacity of the storage associated with an entry. */ + size_t capacity; + /** The size of an entry's data. */ + size_t size; + /** The flags used when the entry was created. */ + psa_storage_create_flags_t flags; +}; + +/** Flag indicating that @ref psa_ps_create() and @ref psa_ps_set_extended() are supported. */ +#define PSA_STORAGE_SUPPORT_SET_EXTENDED (1u << 0) + +/** @} */ +#endif diff --git a/subsys/secure_storage/src/CMakeLists.txt b/subsys/secure_storage/src/CMakeLists.txt new file mode 100644 index 000000000000000..50def1ab93fa050 --- /dev/null +++ b/subsys/secure_storage/src/CMakeLists.txt @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library_sources( + log.c +) +add_subdirectory(its) diff --git a/subsys/secure_storage/src/its/CMakeLists.txt b/subsys/secure_storage/src/its/CMakeLists.txt new file mode 100644 index 000000000000000..b9e2bb877d9c1a8 --- /dev/null +++ b/subsys/secure_storage/src/its/CMakeLists.txt @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library_sources_ifdef(CONFIG_SECURE_STORAGE_ITS_IMPLEMENTATION_ZEPHYR + implementation.c +) + +zephyr_library_sources_ifdef(CONFIG_SECURE_STORAGE_ITS_TRANSFORM_IMPLEMENTATION_AEAD + transform/aead.c + transform/aead_get.c +) +if (NOT CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_NO_INSECURE_KEY_WARNING) + if (CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER_DEVICE_ID_HASH) + message(WARNING " + The PSA ITS encryption key provider in use generates keys by hashing the device ID + retrieved through the HW info API. This is not necessarily secure as the device ID may be + easily readable by an attacker, not unique, and/or guessable, depending on the device. + This means that the data and keys stored via the PSA APIs may not be secure at rest.") + elseif(CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER_ENTRY_UID_HASH) + message(WARNING " + The PSA ITS encryption key provider in use is not secure. + It's only intended for functional support. + This means that the data and keys stored via the PSA APIs will not be secure at rest. + Use a secure key provider if possible.") + endif() +endif() + +zephyr_library_sources_ifdef(CONFIG_SECURE_STORAGE_ITS_STORE_IMPLEMENTATION_SETTINGS + store_settings.c +) diff --git a/subsys/secure_storage/src/its/implementation.c b/subsys/secure_storage/src/its/implementation.c new file mode 100644 index 000000000000000..2ad937d45d27b3c --- /dev/null +++ b/subsys/secure_storage/src/its/implementation.c @@ -0,0 +1,228 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(secure_storage, CONFIG_SECURE_STORAGE_LOG_LEVEL); + +static void log_failed_operation(const char *operation, const char *preposition, psa_status_t ret) +{ + LOG_ERR("Failed to %s data %s storage. (%d)", operation, preposition, ret); +} + +static psa_status_t get_stored_data( + secure_storage_its_uid_t uid, + uint8_t stored_data[static SECURE_STORAGE_ITS_TRANSFORM_MAX_STORED_DATA_SIZE], + size_t *stored_data_len) +{ + psa_status_t ret; + + ret = secure_storage_its_store_get(uid, SECURE_STORAGE_ITS_TRANSFORM_MAX_STORED_DATA_SIZE, + stored_data, stored_data_len); + if (ret != PSA_SUCCESS) { + if (ret != PSA_ERROR_DOES_NOT_EXIST) { + log_failed_operation("retrieve", "from", ret); + } + return ret; + } + return PSA_SUCCESS; +} + +static psa_status_t transform_stored_data( + secure_storage_its_uid_t uid, size_t stored_data_len, + const uint8_t stored_data[static SECURE_STORAGE_ITS_TRANSFORM_MAX_STORED_DATA_SIZE], + size_t data_size, void *data, size_t *data_len, + psa_storage_create_flags_t *create_flags) +{ + psa_status_t ret; + + ret = secure_storage_its_transform_from_store(uid, stored_data_len, stored_data, + data_size, data, data_len, create_flags); + if (ret != PSA_SUCCESS) { + log_failed_operation("transform", "from", ret); + return PSA_ERROR_STORAGE_FAILURE; + } + return PSA_SUCCESS; +} + +static psa_status_t get_entry(secure_storage_its_uid_t uid, size_t data_size, uint8_t *data, + size_t *data_len, psa_storage_create_flags_t *create_flags) +{ + psa_status_t ret; + uint8_t stored_data[SECURE_STORAGE_ITS_TRANSFORM_MAX_STORED_DATA_SIZE]; + size_t stored_data_len; + + ret = get_stored_data(uid, stored_data, &stored_data_len); + if (ret != PSA_SUCCESS) { + return ret; + } + + return transform_stored_data(uid, stored_data_len, stored_data, data_size, data, data_len, + create_flags); +} + +static bool keep_stored_entry(secure_storage_its_uid_t uid, size_t data_length, const void *p_data, + psa_storage_create_flags_t create_flags, psa_status_t *ret) +{ + psa_storage_create_flags_t existing_create_flags; + uint8_t existing_data[CONFIG_SECURE_STORAGE_ITS_MAX_DATA_SIZE]; + size_t existing_data_len; + + *ret = get_entry(uid, sizeof(existing_data), existing_data, &existing_data_len, + &existing_create_flags); + if (*ret != PSA_SUCCESS) { + /* The entry either doesn't exist or is corrupted. */ + /* Allow overwriting corrupted entries to not be stuck with them forever. */ + return false; + } + if (existing_create_flags & PSA_STORAGE_FLAG_WRITE_ONCE) { + *ret = PSA_ERROR_NOT_PERMITTED; + return true; + } + if (existing_data_len == data_length && + existing_create_flags == create_flags && + !memcmp(existing_data, p_data, data_length)) { + LOG_DBG("Not writing entry %u/%llu to storage because its stored data" + " (of length %zu) is identical.", uid.caller_id, uid.uid, data_length); + *ret = PSA_SUCCESS; + return true; + } + return false; +} + +static psa_status_t store_entry(secure_storage_its_uid_t uid, size_t data_length, + const void *p_data, psa_storage_create_flags_t create_flags) +{ + psa_status_t ret; + uint8_t stored_data[SECURE_STORAGE_ITS_TRANSFORM_MAX_STORED_DATA_SIZE]; + size_t stored_data_len; + + ret = secure_storage_its_transform_to_store(uid, data_length, p_data, create_flags, + stored_data, &stored_data_len); + if (ret != PSA_SUCCESS) { + log_failed_operation("transform", "for", ret); + return PSA_ERROR_STORAGE_FAILURE; + } + + ret = secure_storage_its_store_set(uid, stored_data_len, stored_data); + if (ret != PSA_SUCCESS) { + log_failed_operation("write", "to", ret); + } + return ret; +} + +psa_status_t secure_storage_its_set(secure_storage_its_uid_t uid, size_t data_length, + const void *p_data, psa_storage_create_flags_t create_flags) +{ + psa_status_t ret; + + if (uid.uid == 0 || (p_data == NULL && data_length != 0)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if (create_flags & ~SECURE_STORAGE_ALL_CREATE_FLAGS) { + return PSA_ERROR_NOT_SUPPORTED; + } + if (data_length > CONFIG_SECURE_STORAGE_ITS_MAX_DATA_SIZE) { + LOG_DBG("Passed data length (%zu) exceeds maximum allowed (%u).", + data_length, CONFIG_SECURE_STORAGE_ITS_MAX_DATA_SIZE); + return PSA_ERROR_INSUFFICIENT_STORAGE; + } + + if (keep_stored_entry(uid, data_length, p_data, create_flags, &ret)) { + return ret; + } + + ret = store_entry(uid, data_length, p_data, create_flags); + return ret; +} + +psa_status_t secure_storage_its_get(secure_storage_its_uid_t uid, size_t data_offset, + size_t data_size, void *p_data, size_t *p_data_length) +{ + if (uid.uid == 0 || (p_data == NULL && data_size != 0) || p_data_length == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if (data_size == 0) { + *p_data_length = 0; + return PSA_SUCCESS; + } + psa_status_t ret; + uint8_t stored_data[SECURE_STORAGE_ITS_TRANSFORM_MAX_STORED_DATA_SIZE]; + size_t stored_data_len; + psa_storage_create_flags_t create_flags; + + ret = get_stored_data(uid, stored_data, &stored_data_len); + if (ret != PSA_SUCCESS) { + return ret; + } + if (data_offset == 0 + && data_size >= SECURE_STORAGE_ITS_TRANSFORM_DATA_SIZE(stored_data_len)) { + /* All the data fits directly in the provided buffer. */ + return transform_stored_data(uid, stored_data_len, stored_data, data_size, p_data, + p_data_length, &create_flags); + } + uint8_t data[CONFIG_SECURE_STORAGE_ITS_MAX_DATA_SIZE]; + size_t data_len; + + ret = transform_stored_data(uid, stored_data_len, stored_data, sizeof(data), data, + &data_len, &create_flags); + if (ret == PSA_SUCCESS) { + if (data_offset > data_len) { + LOG_DBG("Passed data offset (%zu) exceeds existing data length (%zu).", + data_offset, data_len); + return PSA_ERROR_INVALID_ARGUMENT; + } + *p_data_length = MIN(data_size, data_len - data_offset); + memcpy(p_data, data + data_offset, *p_data_length); + } + return ret; +} + +psa_status_t secure_storage_its_get_info(secure_storage_its_uid_t uid, + struct psa_storage_info_t *p_info) +{ + psa_status_t ret; + uint8_t data[CONFIG_SECURE_STORAGE_ITS_MAX_DATA_SIZE]; + + if (uid.uid == 0 || p_info == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + ret = get_entry(uid, sizeof(data), data, &p_info->size, &p_info->flags); + if (ret == PSA_SUCCESS) { + p_info->capacity = p_info->size; + } + return ret; +} + +psa_status_t secure_storage_its_remove(secure_storage_its_uid_t uid) +{ + psa_status_t ret; + psa_storage_create_flags_t create_flags; + uint8_t data[CONFIG_SECURE_STORAGE_ITS_MAX_DATA_SIZE]; + size_t data_len; + + if (uid.uid == 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + ret = get_entry(uid, sizeof(data), data, &data_len, &create_flags); + if (ret == PSA_SUCCESS && (create_flags & PSA_STORAGE_FLAG_WRITE_ONCE)) { + return PSA_ERROR_NOT_PERMITTED; + } + /* Allow overwriting corrupted entries as well to not be stuck with them forever. */ + if (ret == PSA_SUCCESS || ret == PSA_ERROR_STORAGE_FAILURE) { + ret = secure_storage_its_store_remove(uid); + if (ret != PSA_SUCCESS) { + log_failed_operation("remove", "from", ret); + return PSA_ERROR_STORAGE_FAILURE; + } + } + return ret; +} diff --git a/subsys/secure_storage/src/its/store_settings.c b/subsys/secure_storage/src/its/store_settings.c new file mode 100644 index 000000000000000..d6942fbae69846b --- /dev/null +++ b/subsys/secure_storage/src/its/store_settings.c @@ -0,0 +1,116 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SECURE_STORAGE_ITS_IMPLEMENTATION_ZEPHYR +#include +BUILD_ASSERT(SECURE_STORAGE_ITS_TRANSFORM_MAX_STORED_DATA_SIZE <= SETTINGS_MAX_VAL_LEN); +#endif + +LOG_MODULE_DECLARE(secure_storage, CONFIG_SECURE_STORAGE_LOG_LEVEL); + +static int init_settings_subsys(void) +{ + const int ret = settings_subsys_init(); + + if (ret) { + LOG_DBG("Failed to initialize the settings subsystem. (%d)", ret); + } + return ret; +} +SYS_INIT(init_settings_subsys, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); + +enum { NAME_BUF_SIZE = sizeof(CONFIG_SECURE_STORAGE_ITS_STORE_SETTINGS_PREFIX) - 1 + + 2 * (sizeof(secure_storage_its_uid_t) + 1) }; +BUILD_ASSERT(NAME_BUF_SIZE <= SETTINGS_MAX_NAME_LEN + 1); + +static void make_name(secure_storage_its_uid_t uid, char name[static NAME_BUF_SIZE]) +{ + int ret; + + ret = snprintf(name, NAME_BUF_SIZE, CONFIG_SECURE_STORAGE_ITS_STORE_SETTINGS_PREFIX + "%x/%llx", uid.caller_id, (unsigned long long)uid.uid); + __ASSERT_NO_MSG(ret > 0 && ret < NAME_BUF_SIZE); +} + +psa_status_t secure_storage_its_store_set(secure_storage_its_uid_t uid, + size_t data_length, const void *data) +{ + int ret; + char name[NAME_BUF_SIZE]; + + make_name(uid, name); + ret = settings_save_one(name, data, data_length); + LOG_DBG("%s %s with %zu bytes. (%d)", + (ret == 0) ? "Saved" : "Failed to save", name, data_length, ret); + + switch (ret) { + case 0: + return PSA_SUCCESS; + case -ENOMEM: + case -ENOSPC: + return PSA_ERROR_INSUFFICIENT_STORAGE; + default: + return PSA_ERROR_STORAGE_FAILURE; + } +} + +struct load_params { + const size_t data_size; + uint8_t *const data; + ssize_t ret; +}; + +static int load_direct_setting(const char *key, size_t len, settings_read_cb read_cb, + void *cb_arg, void *param) +{ + (void)key; + struct load_params *load_params = param; + + load_params->ret = read_cb(cb_arg, load_params->data, MIN(load_params->data_size, len)); + return 0; +} + +psa_status_t secure_storage_its_store_get(secure_storage_its_uid_t uid, size_t data_size, + void *data, size_t *data_length) +{ + psa_status_t ret; + char name[NAME_BUF_SIZE]; + struct load_params load_params = {.data_size = data_size, .data = data, .ret = -ENOENT}; + + make_name(uid, name); + + settings_load_subtree_direct(name, load_direct_setting, &load_params); + if (load_params.ret > 0) { + *data_length = load_params.ret; + ret = PSA_SUCCESS; + } else if (load_params.ret == 0 || load_params.ret == -ENOENT) { + ret = PSA_ERROR_DOES_NOT_EXIST; + } else { + ret = PSA_ERROR_STORAGE_FAILURE; + } + LOG_DBG("%s %s for up to %zu bytes. (%zd)", (ret != PSA_ERROR_STORAGE_FAILURE) ? + "Loaded" : "Failed to load", name, data_size, load_params.ret); + return ret; +} + +psa_status_t secure_storage_its_store_remove(secure_storage_its_uid_t uid) +{ + int ret; + char name[NAME_BUF_SIZE]; + + make_name(uid, name); + ret = settings_delete(name); + if (ret) { + LOG_DBG("Failed to delete %s. (%d)", name, ret); + return PSA_ERROR_STORAGE_FAILURE; + } + return PSA_SUCCESS; +} diff --git a/subsys/secure_storage/src/its/transform/aead.c b/subsys/secure_storage/src/its/transform/aead.c new file mode 100644 index 000000000000000..e9f2d57d73abb5a --- /dev/null +++ b/subsys/secure_storage/src/its/transform/aead.c @@ -0,0 +1,131 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#include <../library/psa_crypto_driver_wrappers.h> +#include +#include +#include + +static psa_status_t psa_aead_crypt(psa_key_usage_t operation, secure_storage_its_uid_t uid, + const uint8_t nonce + [static CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_SIZE], + size_t add_data_len, const void *add_data, size_t input_len, + const void *input, size_t output_size, void *output, + size_t *output_len) +{ + psa_status_t ret; + psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT; + uint8_t key[CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_SIZE]; + psa_key_type_t key_type; + psa_algorithm_t alg; + psa_status_t (*aead_crypt)(const psa_key_attributes_t *attributes, const uint8_t *key, + size_t key_size, psa_algorithm_t alg, const uint8_t *nonce, + size_t nonce_length, const uint8_t *add_data, + size_t add_data_len, const uint8_t *input, size_t input_len, + uint8_t *output, size_t output_size, size_t *output_len); + + secure_storage_its_transform_aead_get_scheme(&key_type, &alg); + + psa_set_key_usage_flags(&key_attributes, operation); + psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE); + psa_set_key_type(&key_attributes, key_type); + psa_set_key_algorithm(&key_attributes, alg); + psa_set_key_bits(&key_attributes, sizeof(key) * 8); + + /* Avoid calling psa_aead_*crypt() because that would require importing keys into + * PSA Crypto. This gets called from PSA Crypto for storing persistent keys so, + * even if using PSA_KEY_LIFETIME_VOLATILE, it would corrupt the global key store + * which holds all the active keys in the PSA Crypto core. + */ + aead_crypt = (operation == PSA_KEY_USAGE_ENCRYPT) ? + psa_driver_wrapper_aead_encrypt : psa_driver_wrapper_aead_decrypt; + + ret = secure_storage_its_transform_aead_get_key(uid, key); + if (ret != PSA_SUCCESS) { + return ret; + } + + ret = aead_crypt(&key_attributes, key, sizeof(key), alg, nonce, + CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_SIZE, add_data, + add_data_len, input, input_len, output, output_size, output_len); + + mbedtls_platform_zeroize(key, sizeof(key)); + return ret; +} + +enum { CIPHERTEXT_MAX_SIZE + = PSA_AEAD_ENCRYPT_OUTPUT_MAX_SIZE(CONFIG_SECURE_STORAGE_ITS_MAX_DATA_SIZE) }; + +BUILD_ASSERT(CONFIG_SECURE_STORAGE_ITS_TRANSFORM_OUTPUT_OVERHEAD + == CIPHERTEXT_MAX_SIZE - CONFIG_SECURE_STORAGE_ITS_MAX_DATA_SIZE + + CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_SIZE); + +BUILD_ASSERT(SECURE_STORAGE_ALL_CREATE_FLAGS + <= (1 << (8 * sizeof(secure_storage_packed_create_flags_t))) - 1); + +struct stored_entry { + secure_storage_packed_create_flags_t create_flags; + uint8_t nonce[CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_SIZE]; + uint8_t ciphertext[CIPHERTEXT_MAX_SIZE]; /* Keep last as this is variable in size. */ +} __packed; +BUILD_ASSERT(sizeof(struct stored_entry) == SECURE_STORAGE_ITS_TRANSFORM_MAX_STORED_DATA_SIZE); + +/** @return The length of a `struct stored_entry` whose `ciphertext` is `len` bytes long. */ +#define STORED_ENTRY_LEN(len) (sizeof(struct stored_entry) - CIPHERTEXT_MAX_SIZE + len) + +struct additional_data { + secure_storage_its_uid_t uid; + secure_storage_packed_create_flags_t create_flags; +} __packed; + +psa_status_t secure_storage_its_transform_to_store( + secure_storage_its_uid_t uid, size_t data_len, const void *data, + secure_storage_packed_create_flags_t create_flags, + uint8_t stored_data[static SECURE_STORAGE_ITS_TRANSFORM_MAX_STORED_DATA_SIZE], + size_t *stored_data_len) +{ + psa_status_t ret; + struct stored_entry *stored_entry = (struct stored_entry *)stored_data; + const struct additional_data add_data = {.uid = uid, .create_flags = create_flags}; + size_t ciphertext_len; + + stored_entry->create_flags = create_flags; + + ret = secure_storage_its_transform_aead_get_nonce(stored_entry->nonce); + if (ret != PSA_SUCCESS) { + return ret; + } + + ret = psa_aead_crypt(PSA_KEY_USAGE_ENCRYPT, uid, stored_entry->nonce, sizeof(add_data), + &add_data, data_len, data, sizeof(stored_entry->ciphertext), + &stored_entry->ciphertext, &ciphertext_len); + if (ret == PSA_SUCCESS) { + *stored_data_len = STORED_ENTRY_LEN(ciphertext_len); + } + return ret; +} + +psa_status_t secure_storage_its_transform_from_store( + secure_storage_its_uid_t uid, size_t stored_data_len, + const uint8_t stored_data[static SECURE_STORAGE_ITS_TRANSFORM_MAX_STORED_DATA_SIZE], + size_t data_size, void *data, size_t *data_len, + psa_storage_create_flags_t *create_flags) +{ + if (stored_data_len < STORED_ENTRY_LEN(0)) { + return PSA_ERROR_STORAGE_FAILURE; + } + + psa_status_t ret; + struct stored_entry *stored_entry = (struct stored_entry *)stored_data; + const struct additional_data add_data = {.uid = uid, + .create_flags = stored_entry->create_flags}; + const size_t ciphertext_len = stored_data_len - STORED_ENTRY_LEN(0); + + ret = psa_aead_crypt(PSA_KEY_USAGE_DECRYPT, uid, stored_entry->nonce, sizeof(add_data), + &add_data, ciphertext_len, stored_entry->ciphertext, data_size, data, + data_len); + if (ret == PSA_SUCCESS) { + *create_flags = stored_entry->create_flags; + } + return ret; +} diff --git a/subsys/secure_storage/src/its/transform/aead_get.c b/subsys/secure_storage/src/its/transform/aead_get.c new file mode 100644 index 000000000000000..07492b47e3f4f1f --- /dev/null +++ b/subsys/secure_storage/src/its/transform/aead_get.c @@ -0,0 +1,144 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(secure_storage, CONFIG_SECURE_STORAGE_LOG_LEVEL); + +#ifdef CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_SCHEME_AES_GCM +#define PSA_KEY_TYPE PSA_KEY_TYPE_AES +#define PSA_ALG PSA_ALG_GCM +#elif defined(CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_SCHEME_CHACHA20_POLY1305) +#define PSA_KEY_TYPE PSA_KEY_TYPE_CHACHA20 +#define PSA_ALG PSA_ALG_CHACHA20_POLY1305 +#endif +#ifndef CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_SCHEME_CUSTOM +void secure_storage_its_transform_aead_get_scheme(psa_key_type_t *key_type, psa_algorithm_t *alg) +{ + *key_type = PSA_KEY_TYPE; + *alg = PSA_ALG; +} +#endif /* !CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_SCHEME_CUSTOM */ + +#ifndef CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER_CUSTOM + +#define SHA256_OUTPUT_SIZE 32 +BUILD_ASSERT(SHA256_OUTPUT_SIZE == PSA_HASH_LENGTH(PSA_ALG_SHA_256)); +BUILD_ASSERT(SHA256_OUTPUT_SIZE >= CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_SIZE); + +static psa_status_t hash_data_into_key( + size_t data_len, const void *data, + uint8_t key[static CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_SIZE]) +{ + size_t hash_len; + +#if CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_SIZE == SHA256_OUTPUT_SIZE + /* Save stack usage and avoid unnecessary memory operations.*/ + return psa_hash_compute(PSA_ALG_SHA_256, data, data_len, key, + CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_SIZE, &hash_len); +#else + uint8_t hash_output[SHA256_OUTPUT_SIZE]; + const psa_status_t ret = psa_hash_compute(PSA_ALG_SHA_256, data, data_len, hash_output, + sizeof(hash_output), &hash_len); + + if (ret == PSA_SUCCESS) { + memcpy(key, hash_output, CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_SIZE); + mbedtls_platform_zeroize(hash_output, sizeof(hash_output)); + } + return ret; +#endif +} + +#ifdef CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER_DEVICE_ID_HASH + +#define WARNING "Using a potentially insecure PSA ITS encryption key provider." + +psa_status_t secure_storage_its_transform_aead_get_key( + secure_storage_its_uid_t uid, + uint8_t key[static CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_SIZE]) +{ + psa_status_t ret; + ssize_t hwinfo_ret; + struct { + uint8_t device_id[8]; + secure_storage_its_uid_t uid; /* acts as a salt */ + } __packed data; + + hwinfo_ret = hwinfo_get_device_eui64(data.device_id); + if (hwinfo_ret != 0) { + hwinfo_ret = hwinfo_get_device_id(data.device_id, sizeof(data.device_id)); + if (hwinfo_ret <= 0) { + return PSA_ERROR_HARDWARE_FAILURE; + } + if (hwinfo_ret < sizeof(data.device_id)) { + memset(data.device_id + hwinfo_ret, 0, sizeof(data.device_id) - hwinfo_ret); + } + } + data.uid = uid; + ret = hash_data_into_key(sizeof(data), &data, key); + + mbedtls_platform_zeroize(data.device_id, sizeof(data.device_id)); + return ret; +} + +#elif defined(CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER_ENTRY_UID_HASH) + +#define WARNING "Using an insecure PSA ITS encryption key provider." + +psa_status_t secure_storage_its_transform_aead_get_key( + secure_storage_its_uid_t uid, + uint8_t key[static CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_SIZE]) +{ + return hash_data_into_key(sizeof(uid), &uid, key); +} + +#endif /* CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER */ + +#ifndef CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_NO_INSECURE_KEY_WARNING + +static int warn_insecure_key(void) +{ + printk("WARNING: %s\n", WARNING); + LOG_WRN("%s", WARNING); + return 0; +} +SYS_INIT(warn_insecure_key, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); + +#endif /* !CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_NO_INSECURE_KEY_WARNING */ + +#endif /* !CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_KEY_PROVIDER_CUSTOM */ + +#ifdef CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_PROVIDER_DEFAULT + +psa_status_t secure_storage_its_transform_aead_get_nonce( + uint8_t nonce[static CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_SIZE]) +{ + psa_status_t ret; + static uint8_t s_nonce[CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_SIZE]; + static bool s_nonce_initialized; + + if (!s_nonce_initialized) { + ret = psa_generate_random(s_nonce, sizeof(s_nonce)); + if (ret != PSA_SUCCESS) { + return ret; + } + s_nonce_initialized = true; + } else { + for (unsigned int i = 0; i != sizeof(s_nonce); ++i) { + ++s_nonce[i]; + if (s_nonce[i] != 0) { + break; + } + } + } + + memcpy(nonce, &s_nonce, sizeof(s_nonce)); + return PSA_SUCCESS; +} +#endif /* CONFIG_SECURE_STORAGE_ITS_TRANSFORM_AEAD_NONCE_PROVIDER_DEFAULT */ diff --git a/subsys/secure_storage/src/log.c b/subsys/secure_storage/src/log.c new file mode 100644 index 000000000000000..e07be28ee7b6671 --- /dev/null +++ b/subsys/secure_storage/src/log.c @@ -0,0 +1,6 @@ +/* Copyright (c) 2024 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +LOG_MODULE_REGISTER(secure_storage, CONFIG_SECURE_STORAGE_LOG_LEVEL);