zephyr/subsys/secure_storage/src/its/implementation.c
Tomi Fontanilles bf0e6d7c83 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 <tomi.fontanilles@nordicsemi.no>
2024-10-07 13:38:43 +02:00

229 lines
7.1 KiB
C

/* Copyright (c) 2024 Nordic Semiconductor
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/secure_storage/its.h>
#include <zephyr/secure_storage/its/store.h>
#include <zephyr/secure_storage/its/transform.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>
#include <zephyr/toolchain.h>
#include <string.h>
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;
}