blob: 96980d2f3b83492075dece1cb0b3fed25a931e12 [file] [log] [blame]
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <limits.h>
#include "tfm_crypto_defs.h"
/* Pre include Mbed TLS headers */
#define LIB_PREFIX_NAME __tfm_crypto__
#include "mbedtls_global_symbols.h"
/* Include the Mbed TLS configuration file, the way Mbed TLS does it
* in each of its header files.
*/
#if !defined(MBEDTLS_CONFIG_FILE)
#include "platform/ext/common/tfm_mbedtls_config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif
#include "psa_crypto.h"
/* The file "psa_crypto_struct.h" contains definitions for
* implementation-specific structs that are declared in "psa_crypto.h".
*/
#include "psa_crypto_struct.h"
#include "tfm_crypto_api.h"
#include "crypto_utils.h"
/**
* \brief For a TFM_CRYPTO_CIPHER_OPERATION, define the possible
* modes of configuration.
*
*/
enum tfm_crypto_cipher_mode_t {
TFM_CRYPTO_CIPHER_MODE_DECRYPT = 0,
TFM_CRYPTO_CIPHER_MODE_ENCRYPT = 1,
};
static enum tfm_crypto_err_t tfm_crypto_cipher_setup(
psa_cipher_operation_t *handle,
psa_key_slot_t key,
psa_algorithm_t alg,
enum tfm_crypto_cipher_mode_t c_mode)
{
const mbedtls_cipher_info_t *info = NULL;
psa_algorithm_t padding_mode = PSA_ALG_BLOCK_CIPHER_PAD_NONE;
psa_key_type_t key_type;
size_t key_size;
enum tfm_crypto_err_t err;
uint8_t key_data[TFM_CRYPTO_MAX_KEY_LENGTH];
uint32_t ret;
mbedtls_cipher_type_t type = MBEDTLS_CIPHER_NONE;
mbedtls_cipher_padding_t mbedtls_padding_mode = MBEDTLS_PADDING_NONE;
struct psa_cipher_operation_s *operation = NULL;
/* Validate pointers */
err = tfm_crypto_memory_check(handle,
sizeof(psa_cipher_operation_t),
TFM_MEMORY_ACCESS_RW);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
if (!PSA_ALG_IS_CIPHER(alg)) {
return TFM_CRYPTO_ERR_PSA_ERROR_NOT_SUPPORTED;
}
/* FIXME: Check that key is compatible with alg */
err = tfm_crypto_get_key_information(key, &key_type, &key_size);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
err = tfm_crypto_export_key(key, &key_data[0], TFM_CRYPTO_MAX_KEY_LENGTH,
&key_size);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
/* Mbed TLS cipher setup */
if (PSA_BYTES_TO_BITS(key_size) == 128) {
if (alg == PSA_ALG_CBC_BASE) {
type = MBEDTLS_CIPHER_AES_128_CBC;
} else if (alg == PSA_ALG_CFB_BASE) {
if (c_mode == TFM_CRYPTO_CIPHER_MODE_ENCRYPT) {
type = MBEDTLS_CIPHER_AES_128_CFB128;
}
}
}
/* The requested alg/key/mode is not supported */
if (type == MBEDTLS_CIPHER_NONE) {
return TFM_CRYPTO_ERR_PSA_ERROR_NOT_SUPPORTED;
}
/* Allocate the operation context in the TFM space */
err = tfm_crypto_operation_alloc(TFM_CRYPTO_CIPHER_OPERATION, handle);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
/* Look up the corresponding operation context */
err = tfm_crypto_operation_lookup(TFM_CRYPTO_CIPHER_OPERATION,
handle,
(void **)&operation);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
/* Bind the algorithm to the cipher operation */
operation->alg = alg;
/* Mbed TLS cipher init */
mbedtls_cipher_init(&(operation->ctx.cipher));
info = mbedtls_cipher_info_from_type(type);
ret = mbedtls_cipher_setup(&(operation->ctx.cipher), info);
if (ret != 0) {
/* Release the operation context */
tfm_crypto_operation_release(handle);
return TFM_CRYPTO_ERR_PSA_ERROR_COMMUNICATION_FAILURE;
}
/* FIXME: Check based on the algorithm, if we need to have an IV */
operation->iv_required = 1;
/* Bind the key to the cipher operation */
operation->key = key;
operation->key_set = 1;
/* Mbed TLS cipher set key */
if (c_mode == TFM_CRYPTO_CIPHER_MODE_ENCRYPT) {
ret = mbedtls_cipher_setkey(&(operation->ctx.cipher),
&key_data[0],
PSA_BYTES_TO_BITS(key_size),
MBEDTLS_ENCRYPT);
} else if (c_mode == TFM_CRYPTO_CIPHER_MODE_DECRYPT) {
ret = mbedtls_cipher_setkey(&(operation->ctx.cipher),
&key_data[0],
PSA_BYTES_TO_BITS(key_size),
MBEDTLS_DECRYPT);
} else {
/* Release the operation context */
tfm_crypto_operation_release(handle);
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
if (ret != 0) {
/* Release the operation context */
tfm_crypto_operation_release(handle);
return TFM_CRYPTO_ERR_PSA_ERROR_COMMUNICATION_FAILURE;
}
/* Mbed TLS cipher set padding mode in case of CBC */
if ((alg & ~PSA_ALG_BLOCK_CIPHER_PADDING_MASK) == PSA_ALG_CBC_BASE) {
/* Check the value of padding field */
padding_mode = alg & PSA_ALG_BLOCK_CIPHER_PADDING_MASK;
switch (padding_mode) {
case PSA_ALG_BLOCK_CIPHER_PAD_PKCS7:
mbedtls_padding_mode = MBEDTLS_PADDING_PKCS7;
break;
case PSA_ALG_BLOCK_CIPHER_PAD_NONE:
mbedtls_padding_mode = MBEDTLS_PADDING_NONE;
break;
default:
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
ret = mbedtls_cipher_set_padding_mode(&(operation->ctx.cipher),
mbedtls_padding_mode);
if (ret != 0) {
/* Release the operation context */
tfm_crypto_operation_release(handle);
return TFM_CRYPTO_ERR_PSA_ERROR_COMMUNICATION_FAILURE;
}
}
return TFM_CRYPTO_ERR_PSA_SUCCESS;
}
/*!
* \defgroup public_psa Public functions, PSA
*
*/
/*!@{*/
enum tfm_crypto_err_t tfm_crypto_encrypt_set_iv(
psa_cipher_operation_t *handle,
const unsigned char *iv,
size_t iv_length)
{
int ret;
enum tfm_crypto_err_t err;
struct psa_cipher_operation_s *operation = NULL;
/* Validate pointers */
err = tfm_crypto_memory_check(handle,
sizeof(psa_cipher_operation_t),
TFM_MEMORY_ACCESS_RW);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
err = tfm_crypto_memory_check((void *)iv, iv_length, TFM_MEMORY_ACCESS_RO);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
/* Look up the corresponding operation context */
err = tfm_crypto_operation_lookup(TFM_CRYPTO_CIPHER_OPERATION,
handle,
(void **)&operation);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
if (operation->iv_required == 0) {
/* Release the operation context */
tfm_crypto_operation_release(handle);
return TFM_CRYPTO_ERR_PSA_ERROR_NOT_PERMITTED;
}
if (iv_length > PSA_CIPHER_IV_MAX_SIZE) {
/* Release the operation context */
tfm_crypto_operation_release(handle);
return TFM_CRYPTO_ERR_PSA_ERROR_NOT_SUPPORTED;
}
/* Bind the IV to the cipher operation */
ret = mbedtls_cipher_set_iv(&(operation->ctx.cipher), iv, iv_length);
if (ret != 0) {
/* Release the operation context */
tfm_crypto_operation_release(handle);
return TFM_CRYPTO_ERR_PSA_ERROR_COMMUNICATION_FAILURE;
}
operation->iv_set = 1;
operation->iv_size = iv_length;
/* Reset the context after IV is set */
ret = mbedtls_cipher_reset(&(operation->ctx.cipher));
if (ret != 0) {
/* Release the operation context */
tfm_crypto_operation_release(handle);
return TFM_CRYPTO_ERR_PSA_ERROR_COMMUNICATION_FAILURE;
}
return TFM_CRYPTO_ERR_PSA_SUCCESS;
}
enum tfm_crypto_err_t tfm_crypto_encrypt_setup(
psa_cipher_operation_t *handle,
psa_key_slot_t key,
psa_algorithm_t alg)
{
return tfm_crypto_cipher_setup(handle,
key,
alg,
TFM_CRYPTO_CIPHER_MODE_ENCRYPT);
}
enum tfm_crypto_err_t tfm_crypto_decrypt_setup(
psa_cipher_operation_t *handle,
psa_key_slot_t key,
psa_algorithm_t alg)
{
return tfm_crypto_cipher_setup(handle,
key,
alg,
TFM_CRYPTO_CIPHER_MODE_DECRYPT);
}
enum tfm_crypto_err_t tfm_crypto_cipher_update(
psa_cipher_operation_t *handle,
const uint8_t *input,
size_t input_length,
unsigned char *output,
size_t output_size,
size_t *output_length)
{
int ret;
enum tfm_crypto_err_t err;
size_t olen;
struct psa_cipher_operation_s *operation = NULL;
/* Validate pointers */
err = tfm_crypto_memory_check(handle,
sizeof(psa_cipher_operation_t),
TFM_MEMORY_ACCESS_RW);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
err = tfm_crypto_memory_check((void *)input,
input_length,
TFM_MEMORY_ACCESS_RO);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
err = tfm_crypto_memory_check(output,
output_size,
TFM_MEMORY_ACCESS_RW);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
err = tfm_crypto_memory_check(output_length,
sizeof(size_t),
TFM_MEMORY_ACCESS_RW);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
/* Look up the corresponding operation context */
err = tfm_crypto_operation_lookup(TFM_CRYPTO_CIPHER_OPERATION,
handle,
(void **)&operation);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
/* If the IV is required and it's not been set yet */
if ((operation->iv_required == 1) && (operation->iv_set == 0)) {
if (operation->ctx.cipher.operation != MBEDTLS_DECRYPT) {
/* Release the operation context */
tfm_crypto_operation_release(handle);
return TFM_CRYPTO_ERR_PSA_ERROR_BAD_STATE;
}
/* This call is used to set the IV on the object */
err = tfm_crypto_encrypt_set_iv(handle, input, input_length);
*output_length = 0;
return err;
}
/* If the key is not set, setup phase has not been completed */
if (operation->key_set == 0) {
/* Release the operation context */
tfm_crypto_operation_release(handle);
return TFM_CRYPTO_ERR_PSA_ERROR_BAD_STATE;
}
*output_length = 0;
ret = mbedtls_cipher_update(&(operation->ctx.cipher), input, input_length,
output, &olen);
if ((ret != 0) || (olen == 0)) {
/* Release the operation context */
tfm_crypto_operation_release(handle);
return TFM_CRYPTO_ERR_PSA_ERROR_COMMUNICATION_FAILURE;
}
/* Assign the output buffer length */
*output_length = olen;
return TFM_CRYPTO_ERR_PSA_SUCCESS;
}
enum tfm_crypto_err_t tfm_crypto_cipher_finish(
psa_cipher_operation_t *handle,
uint8_t *output,
size_t output_size,
size_t *output_length)
{
int ret;
enum tfm_crypto_err_t err;
size_t olen;
struct psa_cipher_operation_s *operation = NULL;
/* Validate pointers */
err = tfm_crypto_memory_check(handle,
sizeof(psa_cipher_operation_t),
TFM_MEMORY_ACCESS_RW);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
err = tfm_crypto_memory_check(output,
output_size,
TFM_MEMORY_ACCESS_RW);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
err = tfm_crypto_memory_check(output_length,
sizeof(size_t),
TFM_MEMORY_ACCESS_RW);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
*output_length = 0;
/* Look up the corresponding operation context */
err = tfm_crypto_operation_lookup(TFM_CRYPTO_CIPHER_OPERATION,
handle,
(void **)&operation);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
ret = mbedtls_cipher_finish(&(operation->ctx.cipher), output, &olen);
if (ret != 0) {
/* Release the operation context */
tfm_crypto_operation_release(handle);
return TFM_CRYPTO_ERR_PSA_ERROR_COMMUNICATION_FAILURE;
}
*output_length = olen;
/* Clear the Mbed TLS context */
mbedtls_cipher_free(&(operation->ctx.cipher));
/* Release the operation context */
err = tfm_crypto_operation_release(handle);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
return TFM_CRYPTO_ERR_PSA_SUCCESS;
}
enum tfm_crypto_err_t tfm_crypto_cipher_abort(psa_cipher_operation_t *handle)
{
enum tfm_crypto_err_t err;
struct psa_cipher_operation_s *operation = NULL;
/* Validate pointers */
err = tfm_crypto_memory_check(handle,
sizeof(psa_cipher_operation_t),
TFM_MEMORY_ACCESS_RW);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
/* Look up the corresponding operation context */
err = tfm_crypto_operation_lookup(TFM_CRYPTO_CIPHER_OPERATION,
handle,
(void **)&operation);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
/* Clear the Mbed TLS context */
mbedtls_cipher_free(&(operation->ctx.cipher));
/* Release the operation context */
err = tfm_crypto_operation_release(handle);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
return TFM_CRYPTO_ERR_PSA_SUCCESS;
}
/*!@}*/