blob: 4926b28867c087d8fa42c9d81bc203551a65893f [file] [log] [blame]
/*
* Copyright (c) 2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <limits.h>
#include "tfm_crypto_api.h"
#include "crypto_engine.h"
#include "tfm_crypto_struct.h"
/* FixMe: Use PSA_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.
*/
/**
* \def CRYPTO_AEAD_MAX_KEY_LENGTH
*
* \brief Specifies the maximum key length supported by the
* AEAD operations in this implementation
*/
#ifndef CRYPTO_AEAD_MAX_KEY_LENGTH
#define CRYPTO_AEAD_MAX_KEY_LENGTH (32)
#endif
static psa_status_t _psa_get_key_information(psa_key_slot_t key,
psa_key_type_t *type,
size_t *bits)
{
psa_status_t status;
struct tfm_crypto_pack_iovec iov = {
.sfn_id = TFM_CRYPTO_GET_KEY_INFORMATION_SFID,
.key = key,
};
psa_invec in_vec[] = {
{.base = &iov, .len = sizeof(struct tfm_crypto_pack_iovec)},
};
psa_outvec out_vec[] = {
{.base = type, .len = sizeof(psa_key_type_t)},
{.base = bits, .len = sizeof(size_t)}
};
status = tfm_crypto_get_key_information(
in_vec, sizeof(in_vec)/sizeof(in_vec[0]),
out_vec, sizeof(out_vec)/sizeof(out_vec[0]));
return status;
}
/*!
* \defgroup public_psa Public functions, PSA
*
*/
/*!@{*/
psa_status_t tfm_crypto_aead_encrypt(psa_invec in_vec[],
size_t in_len,
psa_outvec out_vec[],
size_t out_len)
{
psa_status_t status = PSA_SUCCESS;
uint8_t key_data[CRYPTO_AEAD_MAX_KEY_LENGTH];
uint32_t key_size;
psa_key_type_t key_type;
if ( !((in_len == 2) || (in_len == 3)) || (out_len != 1)) {
return PSA_CONNECTION_REFUSED;
}
if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec))) {
return PSA_CONNECTION_REFUSED;
}
const struct tfm_crypto_pack_iovec *iov = in_vec[0].base;
const struct tfm_crypto_aead_pack_input *aead_pack_input = &iov->aead_in;
psa_key_slot_t key = iov->key;
psa_algorithm_t alg = iov->alg;
const uint8_t *nonce = aead_pack_input->nonce;
size_t nonce_length = aead_pack_input->nonce_length;
const uint8_t *plaintext = in_vec[1].base;
size_t plaintext_length = in_vec[1].len;
uint8_t *ciphertext = out_vec[0].base;
size_t ciphertext_size = out_vec[0].len;
const uint8_t *additional_data = NULL;
size_t additional_data_length = 0;
/* Check if additional data has been passed and initialise it */
if (in_len == 3) {
additional_data = in_vec[2].base;
additional_data_length = in_vec[2].len;
}
/* Initialise ciphertext_length to zero */
out_vec[0].len = 0;
if (PSA_ALG_IS_AEAD(alg) == 0) {
return PSA_ERROR_NOT_SUPPORTED;
}
if (PSA_AEAD_TAG_SIZE(alg) == 0) {
return PSA_ERROR_INVALID_ARGUMENT;
}
if (PSA_AEAD_ENCRYPT_OUTPUT_SIZE(alg, plaintext_length) > ciphertext_size) {
return PSA_ERROR_BUFFER_TOO_SMALL;
}
if ((nonce_length == 0) ||
((additional_data_length == 0) && (additional_data != NULL))) {
return PSA_ERROR_INVALID_ARGUMENT;
}
/* Access the key data */
status = _psa_get_key_information(key, &key_type, (size_t *)&key_size);
if (status != PSA_SUCCESS) {
return status;
}
/* Support only AES based AEAD */
if (key_type != PSA_KEY_TYPE_AES) {
return PSA_ERROR_NOT_SUPPORTED;
}
/* Access the crypto service key module to retrieve key data */
status = tfm_crypto_get_key(key,
PSA_KEY_USAGE_ENCRYPT,
alg,
key_data,
CRYPTO_AEAD_MAX_KEY_LENGTH,
(size_t *)&key_size);
if (status != PSA_SUCCESS) {
return status;
}
/* Request AEAD encryption on the crypto engine */
status = tfm_crypto_engine_aead_encrypt(key_type,
alg,
key_data,
key_size,
nonce,
nonce_length,
additional_data,
additional_data_length,
plaintext,
plaintext_length,
ciphertext,
ciphertext_size,
(uint32_t *)&(out_vec[0].len));
if (status == PSA_SUCCESS) {
/* The ciphertext_length needs to take into account the tag length */
out_vec[0].len += PSA_AEAD_TAG_SIZE(alg);
} else {
/* In case of failure set the ciphertext_length to zero */
out_vec[0].len = 0;
}
return status;
}
psa_status_t tfm_crypto_aead_decrypt(psa_invec in_vec[],
size_t in_len,
psa_outvec out_vec[],
size_t out_len)
{
psa_status_t status = PSA_SUCCESS;
uint8_t key_data[CRYPTO_AEAD_MAX_KEY_LENGTH];
uint32_t key_size;
psa_key_type_t key_type;
if ( !((in_len == 2) || (in_len == 3)) || (out_len != 1)) {
return PSA_CONNECTION_REFUSED;
}
if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec))) {
return PSA_CONNECTION_REFUSED;
}
const struct tfm_crypto_pack_iovec *iov = in_vec[0].base;
const struct tfm_crypto_aead_pack_input *aead_pack_input = &iov->aead_in;
psa_key_slot_t key = iov->key;
psa_algorithm_t alg = iov->alg;
const uint8_t *nonce = aead_pack_input->nonce;
size_t nonce_length = aead_pack_input->nonce_length;
const uint8_t *ciphertext = in_vec[1].base;
size_t ciphertext_length = in_vec[1].len;
uint8_t *plaintext = out_vec[0].base;
size_t plaintext_size = out_vec[0].len;
const uint8_t *additional_data = NULL;
size_t additional_data_length = 0;
/* Check if additional data has been passed and initialise it */
if (in_len == 3) {
additional_data = in_vec[2].base;
additional_data_length = in_vec[2].len;
}
/* Initialise plaintext_length to zero */
out_vec[0].len = 0;
if (PSA_ALG_IS_AEAD(alg) == 0) {
return PSA_ERROR_NOT_SUPPORTED;
}
if ((PSA_AEAD_TAG_SIZE(alg) == 0) ||
(ciphertext_length < PSA_AEAD_TAG_SIZE(alg))) {
return PSA_ERROR_INVALID_ARGUMENT;
}
if (PSA_AEAD_DECRYPT_OUTPUT_SIZE(alg,ciphertext_length) > plaintext_size) {
return PSA_ERROR_BUFFER_TOO_SMALL;
}
if ((nonce_length == 0) ||
((additional_data_length == 0) && (additional_data != NULL))) {
return PSA_ERROR_INVALID_ARGUMENT;
}
/* Access the key data */
status = _psa_get_key_information(key, &key_type, (size_t *)&key_size);
if (status != PSA_SUCCESS) {
return status;
}
/* Support only AES based AEAD */
if (key_type != PSA_KEY_TYPE_AES) {
return PSA_ERROR_NOT_SUPPORTED;
}
/* Access the crypto service key module to retrieve key data */
status = tfm_crypto_get_key(key,
PSA_KEY_USAGE_DECRYPT,
alg,
key_data,
CRYPTO_AEAD_MAX_KEY_LENGTH,
(size_t *)&key_size);
if (status != PSA_SUCCESS) {
return status;
}
/* Request AEAD decryption on the crypto engine */
status = tfm_crypto_engine_aead_decrypt(key_type,
alg,
key_data,
key_size,
nonce,
nonce_length,
additional_data,
additional_data_length,
ciphertext,
ciphertext_length,
plaintext,
plaintext_size,
(uint32_t *)&(out_vec[0].len));
if (status != PSA_SUCCESS) {
out_vec[0].len = 0;
}
return status;
}
/*!@}*/