blob: 25a98da8e59d332218a2156872b6c66dbf890b7a [file] [log] [blame]
/*
* Copyright (c) 2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "secure_fw/core/secure_utilities.h"
#include "tfm_crypto_defs.h"
#include "psa_crypto.h"
#include "tfm_crypto_struct.h"
#include "tfm_crypto_api.h"
#include "crypto_utils.h"
static void mac_zeroize(void *data, size_t size)
{
tfm_memset(data, 0, size);
}
static size_t get_hash_block_size(psa_algorithm_t alg)
{
switch (alg) {
case PSA_ALG_MD2:
return 16;
case PSA_ALG_MD4:
return 64;
case PSA_ALG_MD5:
return 64;
case PSA_ALG_RIPEMD160:
return 64;
case PSA_ALG_SHA_1:
return 64;
case PSA_ALG_SHA_224:
return 64;
case PSA_ALG_SHA_256:
return 64;
case PSA_ALG_SHA_384:
return 128;
case PSA_ALG_SHA_512:
return 128;
default:
return 0;
}
}
static enum tfm_crypto_err_t tfm_crypto_hmac_setup(
struct tfm_mac_operation_s *ctx,
psa_key_slot_t key,
psa_algorithm_t alg)
{
enum tfm_crypto_err_t err;
psa_key_type_t key_type;
size_t key_size;
uint8_t key_data[TFM_CRYPTO_MAX_KEY_LENGTH];
uint8_t hashed_key[PSA_HMAC_MAX_HASH_BLOCK_SIZE];
size_t block_size;
uint8_t ipad[PSA_HMAC_MAX_HASH_BLOCK_SIZE];
uint8_t *opad = ctx->ctx.hmac.opad;
size_t i;
psa_key_usage_t usage;
/* Check provided key */
err = tfm_crypto_get_key_information(key, &key_type, &key_size);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
if (key_type != PSA_KEY_TYPE_HMAC){
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
/* Set the key usage based on whether this is a sign or verify operation */
if ((ctx->key_usage_sign == 1) && (ctx->key_usage_verify == 0)) {
usage = PSA_KEY_USAGE_SIGN;
} else if ((ctx->key_usage_sign == 0) && (ctx->key_usage_verify == 1)) {
usage = PSA_KEY_USAGE_VERIFY;
} else {
return TFM_CRYPTO_ERR_PSA_ERROR_BAD_STATE;
}
/* Get the key data to start the HMAC */
err = tfm_crypto_get_key(key,
usage,
alg,
key_data,
TFM_CRYPTO_MAX_KEY_LENGTH,
&key_size);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
/* Bind the digest size to the MAC operation */
ctx->mac_size = PSA_HASH_SIZE(PSA_ALG_HMAC_HASH(alg));
block_size = get_hash_block_size(PSA_ALG_HMAC_HASH(alg));
/* The HMAC algorithm is the standard procedure as described in
* RFC-2104 (https://tools.ietf.org/html/rfc2104)
*/
if (key_size > block_size) {
/* Hash the key to reduce it to block size */
err = tfm_crypto_hash_setup(&(ctx->ctx.hmac.hash_operation),
PSA_ALG_HMAC_HASH(alg));
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
err = tfm_crypto_hash_update(&(ctx->ctx.hmac.hash_operation),
&key_data[0],
key_size);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
tfm_crypto_hash_abort(&(ctx->ctx.hmac.hash_operation));
return err;
}
/* Replace the key with the hashed key */
err = tfm_crypto_hash_finish(&(ctx->ctx.hmac.hash_operation),
hashed_key, sizeof(hashed_key),
&key_size);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
tfm_crypto_hash_abort(&(ctx->ctx.hmac.hash_operation));
return err;
}
} else {
/* Copy the key inside the hashed_key buffer */
for (i=0; i<key_size; i++) {
hashed_key[i] = key_data[i];
}
}
/* Create ipad = hashed_key XOR 0x36 and opad = hashed_key XOR 0x5C */
for (i=0; i<key_size; i++) {
ipad[i] = hashed_key[i] ^ 0x36;
opad[i] = hashed_key[i] ^ 0x5C;
}
/* Fill ipad and opad to match block size */
for (i=key_size; i<block_size; i++) {
ipad[i] = 0x36;
opad[i] = 0x5C;
}
/* Start hash1 = H(i_key_pad || message) */
err = tfm_crypto_hash_setup(&(ctx->ctx.hmac.hash_operation),
PSA_ALG_HMAC_HASH(alg));
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
/* Clear key information on stack */
for (i=0; i<key_size; i++) {
hashed_key[i] = 0;
ipad[i] = 0;
}
return err;
}
err = tfm_crypto_hash_update(&(ctx->ctx.hmac.hash_operation),
ipad,
block_size);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
tfm_crypto_hash_abort(&(ctx->ctx.hmac.hash_operation));
return err;
}
return TFM_CRYPTO_ERR_PSA_SUCCESS;
}
static enum tfm_crypto_err_t tfm_crypto_mac_setup(psa_mac_operation_t *operation,
psa_key_slot_t key,
psa_algorithm_t alg,
uint8_t sign_operation)
{
enum tfm_crypto_err_t err;
struct tfm_mac_operation_s *ctx = NULL;
if (!PSA_ALG_IS_MAC(alg)) {
return TFM_CRYPTO_ERR_PSA_ERROR_NOT_SUPPORTED;
}
/* Validate pointers */
err = tfm_crypto_memory_check(operation,
sizeof(psa_mac_operation_t),
TFM_MEMORY_ACCESS_RW);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
/* Allocate the operation context in the secure world */
err = tfm_crypto_operation_alloc(TFM_CRYPTO_MAC_OPERATION,
&(operation->handle));
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
/* Look up the corresponding operation context */
err = tfm_crypto_operation_lookup(TFM_CRYPTO_MAC_OPERATION,
operation->handle,
(void **)&ctx);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
/* Release the operation context */
tfm_crypto_operation_release(&(operation->handle));
return err;
}
/* Bind the algorithm to the mac operation */
ctx->alg = alg;
/* Specify if this will be used for a sign or verify operation */
if (sign_operation) {
ctx->key_usage_verify = 0;
ctx->key_usage_sign = 1;
} else {
ctx->key_usage_verify = 1;
ctx->key_usage_sign = 0;
}
if (PSA_ALG_IS_HMAC(alg)) {
err = tfm_crypto_hmac_setup(ctx, key, alg);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
/* Release the operation context */
tfm_crypto_operation_release(&(operation->handle));
return err;
}
ctx->key_set = 1;
} else {
/* Other MAC types constructions are not supported */
/* Release the operation context */
tfm_crypto_operation_release(&(operation->handle));
return TFM_CRYPTO_ERR_PSA_ERROR_NOT_SUPPORTED;
}
return TFM_CRYPTO_ERR_PSA_SUCCESS;
}
static enum tfm_crypto_err_t tfm_crypto_mac_finish(
struct tfm_mac_operation_s *ctx,
uint8_t *mac,
size_t mac_size,
size_t *mac_length)
{
enum tfm_crypto_err_t err;
uint8_t hash1[PSA_HASH_MAX_SIZE];
size_t hash_size;
uint8_t *opad;
size_t block_size;
/* Sanity checks */
if (mac_size < ctx->mac_size) {
return TFM_CRYPTO_ERR_PSA_ERROR_BUFFER_TOO_SMALL;
}
if (!(ctx->has_input)) {
return TFM_CRYPTO_ERR_PSA_ERROR_BAD_STATE;
}
if (PSA_ALG_IS_HMAC(ctx->alg)) {
opad = ctx->ctx.hmac.opad;
block_size = get_hash_block_size(PSA_ALG_HMAC_HASH(ctx->alg));
/* finish the hash1 = H(ipad || message) */
err = tfm_crypto_hash_finish(&(ctx->ctx.hmac.hash_operation),
hash1,
sizeof(hash1),
&hash_size);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
/* compute the final mac value = H(opad || hash1) */
err = tfm_crypto_hash_setup(&(ctx->ctx.hmac.hash_operation),
PSA_ALG_HMAC_HASH(ctx->alg));
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
mac_zeroize(hash1, sizeof(hash1));
return err;
}
err = tfm_crypto_hash_update(&(ctx->ctx.hmac.hash_operation),
opad,
block_size);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
mac_zeroize(hash1, sizeof(hash1));
return err;
}
err = tfm_crypto_hash_update(&(ctx->ctx.hmac.hash_operation),
hash1,
hash_size);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
mac_zeroize(hash1, sizeof(hash1));
return err;
}
err = tfm_crypto_hash_finish(&(ctx->ctx.hmac.hash_operation),
mac,
mac_size,
mac_length);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
mac_zeroize(hash1, sizeof(hash1));
return err;
}
/* Clear intermediate hash value */
mac_zeroize(hash1, sizeof(hash1));
} else {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
/* Clear the mac context */
mac_zeroize(ctx, sizeof(struct tfm_mac_operation_s));
return TFM_CRYPTO_ERR_PSA_SUCCESS;
}
/*!
* \defgroup public_psa Public functions, PSA
*
*/
/*!@{*/
enum tfm_crypto_err_t tfm_crypto_mac_sign_setup(psa_mac_operation_t *operation,
psa_key_slot_t key,
psa_algorithm_t alg)
{
return tfm_crypto_mac_setup(operation, key, alg, 1);
}
enum tfm_crypto_err_t tfm_crypto_mac_verify_setup(
psa_mac_operation_t *operation,
psa_key_slot_t key,
psa_algorithm_t alg)
{
return tfm_crypto_mac_setup(operation, key, alg, 0);
}
enum tfm_crypto_err_t tfm_crypto_mac_update(psa_mac_operation_t *operation,
const uint8_t *input,
size_t input_length)
{
enum tfm_crypto_err_t err;
struct tfm_mac_operation_s *ctx = NULL;
if (input_length == 0) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
/* Validate pointers */
err = tfm_crypto_memory_check(operation,
sizeof(psa_mac_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;
}
/* Look up the corresponding operation context */
err = tfm_crypto_operation_lookup(TFM_CRYPTO_MAC_OPERATION,
operation->handle,
(void **)&ctx);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
/* Sanity check */
if (!(ctx->key_set)) {
return TFM_CRYPTO_ERR_PSA_ERROR_BAD_STATE;
}
/* Process the input chunk */
if (PSA_ALG_IS_HMAC(ctx->alg)) {
err = tfm_crypto_hash_update(&(ctx->ctx.hmac.hash_operation),
input,
input_length);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
/* Set this flag to avoid HMAC without data */
ctx->has_input = 1;
} else {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
return TFM_CRYPTO_ERR_PSA_SUCCESS;
}
enum tfm_crypto_err_t tfm_crypto_mac_sign_finish(psa_mac_operation_t *operation,
uint8_t *mac,
size_t mac_size,
size_t *mac_length)
{
enum tfm_crypto_err_t err;
struct tfm_mac_operation_s *ctx = NULL;
if (mac_size == 0) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
/* Validate pointers */
err = tfm_crypto_memory_check(operation,
sizeof(psa_mac_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 *)mac,
mac_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(mac_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_MAC_OPERATION,
operation->handle,
(void **)&ctx);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
if ((ctx->key_usage_sign == 1) && (ctx->key_usage_verify == 0)) {
/* Finalise the mac operation */
err = tfm_crypto_mac_finish(ctx, mac, mac_size, mac_length);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
/* Release the operation context */
err = tfm_crypto_operation_release(&(operation->handle));
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
} else {
return TFM_CRYPTO_ERR_PSA_ERROR_BAD_STATE;
}
return TFM_CRYPTO_ERR_PSA_SUCCESS;
}
enum tfm_crypto_err_t tfm_crypto_mac_verify_finish(
psa_mac_operation_t *operation,
const uint8_t *mac,
size_t mac_length)
{
enum tfm_crypto_err_t err;
struct tfm_mac_operation_s *ctx = NULL;
uint8_t computed_mac[PSA_HMAC_MAX_HASH_BLOCK_SIZE];
size_t computed_mac_length;
size_t i;
uint32_t comp_mismatch = 0;
if (mac_length == 0) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_ARGUMENT;
}
/* Validate pointers */
err = tfm_crypto_memory_check(operation,
sizeof(psa_mac_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 *)mac,
mac_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_MAC_OPERATION,
operation->handle,
(void **)&ctx);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
if ((ctx->key_usage_sign == 0) && (ctx->key_usage_verify == 1)) {
/* Finalise the mac operation */
err = tfm_crypto_mac_finish(ctx,
computed_mac,
sizeof(computed_mac),
&computed_mac_length);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
/* Release the operation context */
err = tfm_crypto_operation_release(&(operation->handle));
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
/* Check that the computed mac match the expected one */
if (computed_mac_length != mac_length) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_SIGNATURE;
}
for (i=0; i<computed_mac_length ; i++) {
if (computed_mac[i] != mac[i]) {
comp_mismatch = 1;
}
}
if (comp_mismatch == 1) {
return TFM_CRYPTO_ERR_PSA_ERROR_INVALID_SIGNATURE;
}
} else {
return TFM_CRYPTO_ERR_PSA_ERROR_BAD_STATE;
}
return TFM_CRYPTO_ERR_PSA_SUCCESS;
}
enum tfm_crypto_err_t tfm_crypto_mac_abort(psa_mac_operation_t *operation)
{
enum tfm_crypto_err_t err;
struct tfm_mac_operation_s *ctx = NULL;
/* Validate pointers */
err = tfm_crypto_memory_check(operation,
sizeof(psa_mac_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_MAC_OPERATION,
operation->handle,
(void **)&ctx);
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
if (PSA_ALG_IS_HMAC(ctx->alg)){
/* Check if the HMAC internal context needs to be deallocated */
if (ctx->ctx.hmac.hash_operation.handle != TFM_CRYPTO_INVALID_HANDLE) {
/* Clear hash context */
err = tfm_crypto_hash_abort(&(ctx->ctx.hmac.hash_operation));
if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
return err;
}
}
/* Release the operation context */
tfm_crypto_operation_release(&(operation->handle));
} else {
/* MACs other than HMACs not currently supported */
return TFM_CRYPTO_ERR_PSA_ERROR_NOT_SUPPORTED;
}
return TFM_CRYPTO_ERR_PSA_SUCCESS;
}
/*!@}*/