blob: af9b01420ddb3299b594b698a7d68de9ff05c8fa [file] [log] [blame]
/*
* Copyright (c) 2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <stddef.h>
#include <stdint.h>
/* FixMe: Use PSA_ERROR_CONNECTION_REFUSED when performing parameter
* integrity checks but this will have to be revised
* when the full set of error codes mandated by PSA FF
* is available.
*/
#include "tfm_mbedcrypto_include.h"
/* Required for mbedtls_calloc in tfm_crypto_huk_derivation */
#include "mbedtls/platform.h"
#include "tfm_crypto_api.h"
#include "tfm_crypto_defs.h"
#include "tfm_memory_utils.h"
#include "platform/include/tfm_plat_crypto_keys.h"
/**
* \brief Perform a key derivation operation from the hardware unique key (HUK).
*
* \note This function allows key derivation from the HUK to be implemented in
* a platform-defined way by calling the TF-M platform function
* tfm_plat_get_huk_derived_key.
*
* \param[in,out] generator Generator object to set up
* \param[in] key_handle Handle to the secret key
* \param[in] salt Salt to use
* \param[in] salt_length Size of the salt buffer in bytes
* \param[in] label Label to use
* \param[in] label_length Size of the label buffer in bytes
* \param[in] capacity Maximum number of bytes that the generator will
* be able to provide
*
* \return Return values as described in \ref psa_status_t
*/
static psa_status_t tfm_crypto_huk_derivation(psa_crypto_generator_t *generator,
psa_key_handle_t key_handle,
const uint8_t *salt,
size_t salt_length,
const uint8_t *label,
size_t label_length,
size_t capacity)
{
psa_status_t status;
enum tfm_plat_err_t plat_err;
int32_t partition_id;
uint8_t *partition_label;
psa_key_policy_t key_policy;
status = psa_get_key_policy(key_handle, &key_policy);
if (status != PSA_SUCCESS) {
return status;
}
/* Check that the input key has the correct policy */
if (key_policy.usage != PSA_KEY_USAGE_DERIVE ||
key_policy.alg != TFM_CRYPTO_ALG_HUK_DERIVATION) {
return PSA_ERROR_INVALID_ARGUMENT;
}
/* Concatenate the caller's partition ID with the supplied label to prevent
* two different partitions from deriving the same key.
*/
status = tfm_crypto_get_caller_id(&partition_id);
if (status != PSA_SUCCESS) {
return status;
}
partition_label = mbedtls_calloc(1, sizeof(partition_id) + label_length);
if (partition_label == NULL) {
return PSA_ERROR_INSUFFICIENT_MEMORY;
}
(void)tfm_memcpy(partition_label, &partition_id, sizeof(partition_id));
(void)tfm_memcpy(partition_label + sizeof(partition_id), label,
label_length);
/* Set up the generator object to contain the raw derived key material, so
* that it can be directly extracted in psa_generator_import_key or
* psa_generator_read.
*/
generator->alg = PSA_ALG_SELECT_RAW;
generator->ctx.buffer.data = mbedtls_calloc(1, capacity);
if (generator->ctx.buffer.data == NULL) {
mbedtls_free(partition_label);
(void)psa_generator_abort(generator);
return PSA_ERROR_INSUFFICIENT_MEMORY;
}
generator->ctx.buffer.size = capacity;
generator->capacity = capacity;
/* Derive key material from the HUK and output it to the generator buffer */
plat_err = tfm_plat_get_huk_derived_key(partition_label,
sizeof(partition_id) + label_length,
salt, salt_length,
generator->ctx.buffer.data,
generator->ctx.buffer.size);
mbedtls_free(partition_label);
if (plat_err != TFM_PLAT_ERR_SUCCESS) {
(void)psa_generator_abort(generator);
return PSA_ERROR_HARDWARE_FAILURE;
}
return PSA_SUCCESS;
}
/*!
* \defgroup public_psa Public functions, PSA
*
*/
/*!@{*/
psa_status_t tfm_crypto_get_generator_capacity(psa_invec in_vec[],
size_t in_len,
psa_outvec out_vec[],
size_t out_len)
{
#if (TFM_CRYPTO_GENERATOR_MODULE_DISABLED != 0)
return PSA_ERROR_NOT_SUPPORTED;
#else
psa_status_t status;
if ((in_len != 1) || (out_len != 1)) {
return PSA_ERROR_CONNECTION_REFUSED;
}
if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) ||
(out_vec[0].len != sizeof(size_t))) {
return PSA_ERROR_CONNECTION_REFUSED;
}
const struct tfm_crypto_pack_iovec *iov = in_vec[0].base;
uint32_t handle = iov->op_handle;
size_t *capacity = out_vec[0].base;
psa_crypto_generator_t *generator = NULL;
/* Look up the corresponding operation context */
status = tfm_crypto_operation_lookup(TFM_CRYPTO_GENERATOR_OPERATION,
handle,
(void **)&generator);
if (status != PSA_SUCCESS) {
*capacity = 0;
return status;
}
return psa_get_generator_capacity(generator, capacity);
#endif /* TFM_CRYPTO_GENERATOR_MODULE_DISABLED */
}
psa_status_t tfm_crypto_generator_read(psa_invec in_vec[],
size_t in_len,
psa_outvec out_vec[],
size_t out_len)
{
#if (TFM_CRYPTO_GENERATOR_MODULE_DISABLED != 0)
return PSA_ERROR_NOT_SUPPORTED;
#else
psa_status_t status;
if ((in_len != 1) || (out_len != 1)) {
return PSA_ERROR_CONNECTION_REFUSED;
}
if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec))) {
return PSA_ERROR_CONNECTION_REFUSED;
}
const struct tfm_crypto_pack_iovec *iov = in_vec[0].base;
uint32_t handle = iov->op_handle;
uint8_t *output = out_vec[0].base;
size_t output_length = out_vec[0].len;
psa_crypto_generator_t *generator = NULL;
/* Look up the corresponding operation context */
status = tfm_crypto_operation_lookup(TFM_CRYPTO_GENERATOR_OPERATION,
handle,
(void **)&generator);
if (status != PSA_SUCCESS) {
return status;
}
return psa_generator_read(generator, output, output_length);
#endif /* TFM_CRYPTO_GENERATOR_MODULE_DISABLED */
}
psa_status_t tfm_crypto_generator_import_key(psa_invec in_vec[],
size_t in_len,
psa_outvec out_vec[],
size_t out_len)
{
#if (TFM_CRYPTO_GENERATOR_MODULE_DISABLED != 0)
return PSA_ERROR_NOT_SUPPORTED;
#else
psa_status_t status;
if ((in_len != 2) || (out_len != 0)) {
return PSA_ERROR_CONNECTION_REFUSED;
}
if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) ||
(in_vec[1].len != sizeof(size_t))) {
return PSA_ERROR_CONNECTION_REFUSED;
}
const struct tfm_crypto_pack_iovec *iov = in_vec[0].base;
uint32_t handle = iov->op_handle;
psa_key_handle_t key_handle = iov->key_handle;
psa_key_type_t type = iov->type;
size_t bits = *(size_t *)(in_vec[1].base);
psa_crypto_generator_t *generator = NULL;
status = tfm_crypto_check_handle_owner(key_handle, NULL);
if (status != PSA_SUCCESS) {
return status;
}
/* Look up the corresponding operation context */
status = tfm_crypto_operation_lookup(TFM_CRYPTO_GENERATOR_OPERATION,
handle,
(void **)&generator);
if (status != PSA_SUCCESS) {
return status;
}
return psa_generator_import_key(key_handle, type, bits, generator);
#endif /* TFM_CRYPTO_GENERATOR_MODULE_DISABLED */
}
psa_status_t tfm_crypto_generator_abort(psa_invec in_vec[],
size_t in_len,
psa_outvec out_vec[],
size_t out_len)
{
#if (TFM_CRYPTO_GENERATOR_MODULE_DISABLED != 0)
return PSA_ERROR_NOT_SUPPORTED;
#else
psa_status_t status;
if ((in_len != 1) || (out_len != 1)) {
return PSA_ERROR_CONNECTION_REFUSED;
}
if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) ||
(out_vec[0].len != sizeof(uint32_t))) {
return PSA_ERROR_CONNECTION_REFUSED;
}
const struct tfm_crypto_pack_iovec *iov = in_vec[0].base;
uint32_t handle = iov->op_handle;
uint32_t *handle_out = out_vec[0].base;
psa_crypto_generator_t *generator = NULL;
/* Init the handle in the operation with the one passed from the iov */
*handle_out = iov->op_handle;
/* Look up the corresponding operation context */
status = tfm_crypto_operation_lookup(TFM_CRYPTO_GENERATOR_OPERATION,
handle,
(void **)&generator);
if (status != PSA_SUCCESS) {
/* Operation does not exist, so abort has no effect */
return PSA_SUCCESS;
}
*handle_out = handle;
status = psa_generator_abort(generator);
if (status != PSA_SUCCESS) {
/* Release the operation context, ignore if the operation fails. */
(void)tfm_crypto_operation_release(handle_out);
return status;
}
status = tfm_crypto_operation_release(handle_out);
return status;
#endif /* TFM_CRYPTO_GENERATOR_MODULE_DISABLED */
}
psa_status_t tfm_crypto_key_derivation(psa_invec in_vec[],
size_t in_len,
psa_outvec out_vec[],
size_t out_len)
{
#if (TFM_CRYPTO_GENERATOR_MODULE_DISABLED != 0)
return PSA_ERROR_NOT_SUPPORTED;
#else
psa_status_t status;
if (!((in_len == 1) || (in_len == 2) || (in_len == 3)) || (out_len != 1)) {
return PSA_ERROR_CONNECTION_REFUSED;
}
if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) ||
(out_vec[0].len != sizeof(uint32_t))) {
return PSA_ERROR_CONNECTION_REFUSED;
}
const struct tfm_crypto_pack_iovec *iov = in_vec[0].base;
uint32_t handle = iov->op_handle;
uint32_t *handle_out = out_vec[0].base;
psa_key_handle_t key_handle = iov->key_handle;
psa_algorithm_t alg = iov->alg;
const uint8_t *salt = NULL;
size_t salt_length = 0;
const uint8_t *label = NULL;
size_t label_length = 0;
size_t capacity = iov->capacity;
psa_crypto_generator_t *generator = NULL;
if (in_len > 1) {
salt = in_vec[1].base;
salt_length = in_vec[1].len;
}
if (in_len > 2) {
label = in_vec[2].base;
label_length = in_vec[2].len;
}
status = tfm_crypto_check_handle_owner(key_handle, NULL);
if (status != PSA_SUCCESS) {
return status;
}
/* Allocate the generator context in the secure world */
status = tfm_crypto_operation_alloc(TFM_CRYPTO_GENERATOR_OPERATION,
&handle,
(void **)&generator);
if (status != PSA_SUCCESS) {
return status;
}
*handle_out = handle;
/* If the caller requests that the TFM_CRYPTO_ALG_HUK_DERIVATION algorithm
* is used for key derivation, then redirect the request to the TF-M HUK
* derivation function, so that key derivation from the HUK can be
* implemented in a platform-defined way.
* FIXME: In the future, this should be replaced by the Mbed Crypto driver
* model.
*/
if (alg == TFM_CRYPTO_ALG_HUK_DERIVATION) {
status = tfm_crypto_huk_derivation(generator, key_handle, salt,
salt_length, label, label_length,
capacity);
} else {
status = psa_key_derivation(generator, key_handle, alg, salt,
salt_length, label, label_length, capacity);
}
if (status != PSA_SUCCESS) {
/* Release the operation context, ignore if the operation fails. */
(void)tfm_crypto_operation_release(handle_out);
}
return status;
#endif /* TFM_CRYPTO_GENERATOR_MODULE_DISABLED */
}
psa_status_t tfm_crypto_key_agreement(psa_invec in_vec[],
size_t in_len,
psa_outvec out_vec[],
size_t out_len)
{
#if (TFM_CRYPTO_GENERATOR_MODULE_DISABLED != 0)
return PSA_ERROR_NOT_SUPPORTED;
#else
psa_status_t status;
if ((in_len != 2) || (out_len != 1)) {
return PSA_ERROR_CONNECTION_REFUSED;
}
if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) ||
(out_vec[0].len != sizeof(uint32_t))) {
return PSA_ERROR_CONNECTION_REFUSED;
}
const struct tfm_crypto_pack_iovec *iov = in_vec[0].base;
uint32_t handle = iov->op_handle;
uint32_t *handle_out = out_vec[0].base;
psa_key_handle_t private_key = iov->key_handle;
psa_algorithm_t alg = iov->alg;
const uint8_t *peer_key = in_vec[1].base;
size_t peer_key_length = in_vec[1].len;
psa_crypto_generator_t *generator = NULL;
status = tfm_crypto_check_handle_owner(private_key, NULL);
if (status != PSA_SUCCESS) {
return status;
}
/* Allocate the generator context in the secure world */
status = tfm_crypto_operation_alloc(TFM_CRYPTO_GENERATOR_OPERATION,
&handle,
(void **)&generator);
if (status != PSA_SUCCESS) {
return status;
}
*handle_out = handle;
status = psa_key_agreement(generator, private_key,
peer_key, peer_key_length, alg);
if (status != PSA_SUCCESS) {
/* Release the operation context, ignore if the operation fails. */
(void)tfm_crypto_operation_release(handle_out);
}
return status;
#endif /* TFM_CRYPTO_GENERATOR_MODULE_DISABLED */
}
psa_status_t tfm_crypto_generate_random(psa_invec in_vec[],
size_t in_len,
psa_outvec out_vec[],
size_t out_len)
{
#if (TFM_CRYPTO_GENERATOR_MODULE_DISABLED != 0)
return PSA_ERROR_NOT_SUPPORTED;
#else
if ((in_len != 1) || (out_len != 1)) {
return PSA_ERROR_CONNECTION_REFUSED;
}
if (in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) {
return PSA_ERROR_CONNECTION_REFUSED;
}
uint8_t *output = out_vec[0].base;
size_t output_size = out_vec[0].len;
return psa_generate_random(output, output_size);
#endif /* TFM_CRYPTO_GENERATOR_MODULE_DISABLED */
}
psa_status_t tfm_crypto_generate_key(psa_invec in_vec[],
size_t in_len,
psa_outvec out_vec[],
size_t out_len)
{
#if (TFM_CRYPTO_GENERATOR_MODULE_DISABLED != 0)
return PSA_ERROR_NOT_SUPPORTED;
#else
if (!((in_len == 2) || (in_len == 3)) || (out_len != 0)) {
return PSA_ERROR_CONNECTION_REFUSED;
}
if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) ||
(in_vec[1].len != sizeof(size_t))) {
return PSA_ERROR_CONNECTION_REFUSED;
}
const struct tfm_crypto_pack_iovec *iov = in_vec[0].base;
psa_key_handle_t key_handle = iov->key_handle;
psa_key_type_t type = iov->type;
size_t bits = *((size_t *)(in_vec[1].base));
const void *extra = NULL;
size_t extra_size = 0;
psa_status_t status;
status = tfm_crypto_check_handle_owner(key_handle, NULL);
if (status != PSA_SUCCESS) {
return status;
}
if (in_len == 3) {
extra = in_vec[2].base;
extra_size = in_vec[2].len;
}
return psa_generate_key(key_handle, type, bits, extra, extra_size);
#endif /* TFM_CRYPTO_GENERATOR_MODULE_DISABLED */
}
/*!@}*/