| /* |
| * Copyright (c) 2018-2022, Arm Limited. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| * |
| */ |
| #include <stdbool.h> |
| |
| #include "tfm_mbedcrypto_include.h" |
| |
| #include "tfm_crypto_api.h" |
| #include "tfm_crypto_defs.h" |
| #include "tfm_sp_log.h" |
| |
| /* |
| * \brief This Mbed TLS include is needed to initialise the memory allocator |
| * of the library used for internal allocations |
| */ |
| #include "mbedtls/memory_buffer_alloc.h" |
| |
| #ifdef CRYPTO_NV_SEED |
| #include "tfm_plat_crypto_nv_seed.h" |
| #endif /* CRYPTO_NV_SEED */ |
| |
| #ifndef TFM_PSA_API |
| #include "tfm_secure_api.h" |
| #endif |
| |
| #ifdef CRYPTO_HW_ACCELERATOR |
| #include "crypto_hw.h" |
| #endif /* CRYPTO_HW_ACCELERATOR */ |
| |
| #ifndef MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER |
| #error "MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER must be selected in Mbed TLS config file" |
| #endif |
| |
| /** |
| * \brief Type describing the properties of each function identifier, i.e. the |
| * group ID and the function type |
| */ |
| struct tfm_crypto_api_descriptor { |
| uint8_t group_id : 6; /*!< Value from \ref tfm_crypto_group_id */ |
| uint8_t function_type: 2; /*!< Value from \ref tfm_crypto_function_type */ |
| }; |
| |
| /** |
| * \brief This table contains the description of each of the function IDs |
| * defined by \ref tfm_crypto_function_id |
| */ |
| #define X(_function_id, _group_id, _function_type) \ |
| [_function_id] = {.group_id = _group_id, .function_type = _function_type}, |
| static const struct tfm_crypto_api_descriptor tfm_crypto_api_descriptor[] = { |
| TFM_CRYPTO_SERVICE_API_DESCRIPTION |
| }; |
| #undef X |
| |
| enum tfm_crypto_function_type get_function_type_from_descriptor(enum tfm_crypto_function_id func) |
| { |
| return tfm_crypto_api_descriptor[func].function_type; |
| } |
| |
| enum tfm_crypto_group_id get_group_id_from_descriptor(enum tfm_crypto_function_id func) |
| { |
| return tfm_crypto_api_descriptor[func].group_id; |
| } |
| |
| #ifdef TFM_PSA_API |
| #include <string.h> |
| #include "psa/framework_feature.h" |
| #include "psa/service.h" |
| #include "psa_manifest/tfm_crypto.h" |
| |
| /** |
| * \brief Aligns a value x up to an alignment a. |
| */ |
| #define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) |
| |
| /** |
| * \brief Maximum alignment required by any iovec parameters to the TF-M Crypto |
| * partition. |
| */ |
| #define TFM_CRYPTO_IOVEC_ALIGNMENT (4u) |
| |
| #if PSA_FRAMEWORK_HAS_MM_IOVEC == 1 |
| static int32_t g_client_id; |
| |
| static void tfm_crypto_set_caller_id(int32_t id) |
| { |
| g_client_id = id; |
| } |
| |
| psa_status_t tfm_crypto_get_caller_id(int32_t *id) |
| { |
| *id = g_client_id; |
| return PSA_SUCCESS; |
| } |
| |
| static psa_status_t tfm_crypto_init_iovecs(const psa_msg_t *msg, |
| psa_invec in_vec[], |
| size_t in_len, |
| psa_outvec out_vec[], |
| size_t out_len) |
| { |
| uint32_t i; |
| |
| /* Map from the second element as the first is read when parsing */ |
| for (i = 1; i < in_len; i++) { |
| in_vec[i].len = msg->in_size[i]; |
| if (in_vec[i].len != 0) { |
| in_vec[i].base = psa_map_invec(msg->handle, i); |
| } else { |
| in_vec[i].base = NULL; |
| } |
| } |
| |
| for (i = 0; i < out_len; i++) { |
| out_vec[i].len = msg->out_size[i]; |
| if (out_vec[i].len != 0) { |
| out_vec[i].base = psa_map_outvec(msg->handle, i); |
| } else { |
| out_vec[i].base = NULL; |
| } |
| } |
| |
| return PSA_SUCCESS; |
| } |
| #else /* PSA_FRAMEWORK_HAS_MM_IOVEC == 1 */ |
| /** |
| * \brief Default size of the internal scratch buffer used for IOVec allocations |
| * in bytes |
| */ |
| #ifndef TFM_CRYPTO_IOVEC_BUFFER_SIZE |
| #error TFM_CRYPTO_IOVEC_BUFFER_SIZE is not defined |
| #endif |
| |
| /** |
| * \brief Internal scratch used for IOVec allocations |
| * |
| */ |
| static struct tfm_crypto_scratch { |
| __attribute__((__aligned__(TFM_CRYPTO_IOVEC_ALIGNMENT))) |
| uint8_t buf[TFM_CRYPTO_IOVEC_BUFFER_SIZE]; |
| uint32_t alloc_index; |
| int32_t owner; |
| } scratch = {.buf = {0}, .alloc_index = 0}; |
| |
| static psa_status_t tfm_crypto_set_scratch_owner(int32_t id) |
| { |
| scratch.owner = id; |
| return PSA_SUCCESS; |
| } |
| |
| static psa_status_t tfm_crypto_get_scratch_owner(int32_t *id) |
| { |
| *id = scratch.owner; |
| return PSA_SUCCESS; |
| } |
| |
| static psa_status_t tfm_crypto_alloc_scratch(size_t requested_size, void **buf) |
| { |
| /* Ensure alloc_index remains aligned to the required iovec alignment */ |
| requested_size = ALIGN(requested_size, TFM_CRYPTO_IOVEC_ALIGNMENT); |
| |
| if (requested_size > (sizeof(scratch.buf) - scratch.alloc_index)) { |
| return PSA_ERROR_INSUFFICIENT_MEMORY; |
| } |
| |
| /* Compute the pointer to the allocated space */ |
| *buf = (void *)&scratch.buf[scratch.alloc_index]; |
| |
| /* Increase the allocated size */ |
| scratch.alloc_index += requested_size; |
| |
| return PSA_SUCCESS; |
| } |
| |
| static void tfm_crypto_clear_scratch(void) |
| { |
| scratch.owner = 0; |
| (void)memset(scratch.buf, 0, scratch.alloc_index); |
| scratch.alloc_index = 0; |
| } |
| |
| static void tfm_crypto_set_caller_id(int32_t id) |
| { |
| /* Set the owner of the data in the scratch */ |
| (void)tfm_crypto_set_scratch_owner(id); |
| } |
| |
| psa_status_t tfm_crypto_get_caller_id(int32_t *id) |
| { |
| return tfm_crypto_get_scratch_owner(id); |
| } |
| |
| static psa_status_t tfm_crypto_init_iovecs(const psa_msg_t *msg, |
| psa_invec in_vec[], |
| size_t in_len, |
| psa_outvec out_vec[], |
| size_t out_len) |
| { |
| uint32_t i; |
| void *alloc_buf_ptr = NULL; |
| psa_status_t status; |
| |
| /* Alloc/read from the second element as the first is read when parsing */ |
| for (i = 1; i < in_len; i++) { |
| /* Allocate necessary space in the internal scratch */ |
| status = tfm_crypto_alloc_scratch(msg->in_size[i], &alloc_buf_ptr); |
| if (status != PSA_SUCCESS) { |
| tfm_crypto_clear_scratch(); |
| return status; |
| } |
| /* Read from the IPC framework inputs into the scratch */ |
| in_vec[i].len = |
| psa_read(msg->handle, i, alloc_buf_ptr, msg->in_size[i]); |
| /* Populate the fields of the input to the secure function */ |
| in_vec[i].base = alloc_buf_ptr; |
| } |
| |
| for (i = 0; i < out_len; i++) { |
| /* Allocate necessary space for the output in the internal scratch */ |
| status = tfm_crypto_alloc_scratch(msg->out_size[i], &alloc_buf_ptr); |
| if (status != PSA_SUCCESS) { |
| tfm_crypto_clear_scratch(); |
| return status; |
| } |
| /* Populate the fields of the output to the secure function */ |
| out_vec[i].base = alloc_buf_ptr; |
| out_vec[i].len = msg->out_size[i]; |
| } |
| |
| return PSA_SUCCESS; |
| } |
| #endif /* PSA_FRAMEWORK_HAS_MM_IOVEC == 1 */ |
| |
| static psa_status_t tfm_crypto_call_srv(const psa_msg_t *msg) |
| { |
| psa_status_t status = PSA_SUCCESS; |
| size_t in_len = PSA_MAX_IOVEC, out_len = PSA_MAX_IOVEC, i; |
| psa_invec in_vec[PSA_MAX_IOVEC] = { {NULL, 0} }; |
| psa_outvec out_vec[PSA_MAX_IOVEC] = { {NULL, 0} }; |
| struct tfm_crypto_pack_iovec iov = {0}; |
| |
| /* Check the number of in_vec filled */ |
| while ((in_len > 0) && (msg->in_size[in_len - 1] == 0)) { |
| in_len--; |
| } |
| |
| /* Check the number of out_vec filled */ |
| while ((out_len > 0) && (msg->out_size[out_len - 1] == 0)) { |
| out_len--; |
| } |
| |
| /* There will always be a tfm_crypto_pack_iovec in the first iovec */ |
| if (in_len < 1) { |
| return PSA_ERROR_GENERIC_ERROR; |
| } |
| |
| if (psa_read(msg->handle, 0, &iov, sizeof(iov)) != sizeof(iov)) { |
| return PSA_ERROR_GENERIC_ERROR; |
| } |
| |
| /* Initialise the first iovec with the IOV read when parsing */ |
| in_vec[0].base = &iov; |
| in_vec[0].len = sizeof(struct tfm_crypto_pack_iovec); |
| |
| status = tfm_crypto_init_iovecs(msg, in_vec, in_len, out_vec, out_len); |
| if (status != PSA_SUCCESS) { |
| return status; |
| } |
| |
| tfm_crypto_set_caller_id(msg->client_id); |
| |
| /* Call the dispatcher to the functions that implement the PSA Crypto API */ |
| status = tfm_crypto_api_dispatcher(in_vec, in_len, out_vec, out_len); |
| |
| #if PSA_FRAMEWORK_HAS_MM_IOVEC == 1 |
| for (i = 0; i < out_len; i++) { |
| if (out_vec[i].base != NULL) { |
| psa_unmap_outvec(msg->handle, i, out_vec[i].len); |
| } |
| } |
| #else |
| /* Write into the IPC framework outputs from the scratch */ |
| for (i = 0; i < out_len; i++) { |
| psa_write(msg->handle, i, out_vec[i].base, out_vec[i].len); |
| } |
| |
| /* Clear the allocated internal scratch before returning */ |
| tfm_crypto_clear_scratch(); |
| #endif |
| |
| return status; |
| } |
| #else /* TFM_PSA_API */ |
| psa_status_t tfm_crypto_get_caller_id(int32_t *id) |
| { |
| int32_t res; |
| |
| res = tfm_core_get_caller_client_id(id); |
| if (res != TFM_SUCCESS) { |
| return PSA_ERROR_NOT_PERMITTED; |
| } else { |
| return PSA_SUCCESS; |
| } |
| } |
| #endif /* TFM_PSA_API */ |
| |
| /** |
| * \brief Default value for the size of the static buffer used by Mbed |
| * Crypto for its dynamic allocations |
| */ |
| #ifndef TFM_CRYPTO_ENGINE_BUF_SIZE |
| #error TFM_CRYPTO_ENGINE_BUF_SIZE is not defined |
| #endif |
| |
| /** |
| * \brief Static buffer to be used by Mbed Crypto for memory allocations |
| * |
| */ |
| static uint8_t mbedtls_mem_buf[TFM_CRYPTO_ENGINE_BUF_SIZE] = {0}; |
| |
| static psa_status_t tfm_crypto_engine_init(void) |
| { |
| #ifdef CRYPTO_NV_SEED |
| LOG_INFFMT("[INF][Crypto] "); |
| #ifdef TFM_PSA_API |
| LOG_INFFMT("Provisioning entropy seed... "); |
| if (tfm_plat_crypto_provision_entropy_seed() != TFM_CRYPTO_NV_SEED_SUCCESS) { |
| return PSA_ERROR_GENERIC_ERROR; |
| } |
| LOG_INFFMT("\033[0;32mcomplete.\033[0m\r\n"); |
| #else |
| LOG_INFFMT("TF-M in library mode uses a dummy NV seed. "); |
| LOG_INFFMT("This is not suitable for production! "); |
| LOG_INFFMT("This device is \033[1;31mNOT SECURE\033[0m\r\n"); |
| #endif /* TFM_PSA_API */ |
| #endif /* CRYPTO_NV_SEED */ |
| |
| /* Initialise the Mbed Crypto memory allocator to use static memory |
| * allocation from the provided buffer instead of using the heap |
| */ |
| mbedtls_memory_buffer_alloc_init(mbedtls_mem_buf, |
| TFM_CRYPTO_ENGINE_BUF_SIZE); |
| |
| /* Initialise the crypto accelerator if one is enabled */ |
| #ifdef CRYPTO_HW_ACCELERATOR |
| LOG_INFFMT("[INF][Crypto] Initialising HW accelerator... "); |
| if (crypto_hw_accelerator_init() != 0) { |
| return PSA_ERROR_HARDWARE_FAILURE; |
| } |
| LOG_INFFMT("\033[0;32mcomplete.\033[0m\r\n"); |
| #endif /* CRYPTO_HW_ACCELERATOR */ |
| |
| /* Previous function does not return any value, so just call the |
| * initialisation function of the Mbed Crypto layer |
| */ |
| return psa_crypto_init(); |
| } |
| |
| static psa_status_t tfm_crypto_module_init(void) |
| { |
| /* Init the Alloc module */ |
| return tfm_crypto_init_alloc(); |
| } |
| |
| psa_status_t tfm_crypto_init(void) |
| { |
| psa_status_t status; |
| |
| /* Initialise other modules of the service */ |
| status = tfm_crypto_module_init(); |
| if (status != PSA_SUCCESS) { |
| return status; |
| } |
| |
| /* Initialise the engine layer */ |
| return tfm_crypto_engine_init(); |
| } |
| |
| #ifdef TFM_PSA_API |
| psa_status_t tfm_crypto_sfn(const psa_msg_t *msg) |
| { |
| /* Process the message type */ |
| switch (msg->type) { |
| case PSA_IPC_CALL: |
| return tfm_crypto_call_srv(msg); |
| default: |
| return PSA_ERROR_NOT_SUPPORTED; |
| } |
| |
| return PSA_ERROR_GENERIC_ERROR; |
| } |
| #endif |
| |
| psa_status_t tfm_crypto_api_dispatcher(psa_invec in_vec[], |
| size_t in_len, |
| psa_outvec out_vec[], |
| size_t out_len) |
| { |
| psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; |
| const struct tfm_crypto_pack_iovec *iov = in_vec[0].base; |
| int32_t caller_id = 0; |
| mbedtls_svc_key_id_t encoded_key = MBEDTLS_SVC_KEY_ID_INIT; |
| bool is_key_required = false; |
| |
| if (in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) { |
| return PSA_ERROR_PROGRAMMER_ERROR; |
| } |
| |
| is_key_required = !(TFM_CRYPTO_IS_GROUP_ID( |
| iov->function_id, TFM_CRYPTO_GROUP_ID_HASH) || |
| (iov->function_id == TFM_CRYPTO_GENERATE_RANDOM_SID)); |
| |
| if (is_key_required) { |
| status = tfm_crypto_get_caller_id(&caller_id); |
| if (status != PSA_SUCCESS) { |
| return status; |
| } |
| /* The caller_id being set in the owner field is the partition ID |
| * of the calling partition |
| */ |
| encoded_key = mbedtls_svc_key_id_make(caller_id, iov->key_id); |
| } |
| |
| /* Dispatch to each sub-module based on the Group ID */ |
| if (TFM_CRYPTO_IS_GROUP_ID( |
| iov->function_id, TFM_CRYPTO_GROUP_ID_KEY_MANAGEMENT)) { |
| status = tfm_crypto_key_management_interface(in_vec, |
| out_vec, |
| &encoded_key); |
| } else if (TFM_CRYPTO_IS_GROUP_ID( |
| iov->function_id, TFM_CRYPTO_GROUP_ID_HASH)) { |
| status = tfm_crypto_hash_interface(in_vec, out_vec); |
| } else if (TFM_CRYPTO_IS_GROUP_ID( |
| iov->function_id, TFM_CRYPTO_GROUP_ID_MAC)) { |
| status = tfm_crypto_mac_interface(in_vec, |
| out_vec, |
| &encoded_key); |
| } else if (TFM_CRYPTO_IS_GROUP_ID( |
| iov->function_id, TFM_CRYPTO_GROUP_ID_CIPHER)) { |
| status = tfm_crypto_cipher_interface(in_vec, |
| out_vec, |
| &encoded_key); |
| } else if (TFM_CRYPTO_IS_GROUP_ID( |
| iov->function_id, TFM_CRYPTO_GROUP_ID_AEAD)) { |
| status = tfm_crypto_aead_interface(in_vec, |
| out_vec, |
| &encoded_key); |
| } else if (TFM_CRYPTO_IS_GROUP_ID( |
| iov->function_id, TFM_CRYPTO_GROUP_ID_ASYM_SIGN) || |
| TFM_CRYPTO_IS_GROUP_ID( |
| iov->function_id, TFM_CRYPTO_GROUP_ID_ASYM_ENCRYPT)) { |
| status = tfm_crypto_asymmetric_interface(in_vec, |
| out_vec, |
| &encoded_key); |
| } else if (TFM_CRYPTO_IS_GROUP_ID( |
| iov->function_id, TFM_CRYPTO_GROUP_ID_KEY_DERIVATION)) { |
| status = tfm_crypto_key_derivation_interface(in_vec, |
| out_vec, |
| &encoded_key); |
| } else if (iov->function_id == TFM_CRYPTO_GENERATE_RANDOM_SID) { |
| status = tfm_crypto_random_interface(in_vec, out_vec); |
| } else { |
| LOG_ERRFMT("[ERR][Crypto] Unsupported request!\r\n"); |
| status = PSA_ERROR_NOT_SUPPORTED; |
| } |
| |
| return status; |
| } |