Crypto: Add support for HMAC functionalities
This patch introduces support for the HMAC functionalities.
The HMAC construction procedure is as described by RFC-2104.
It also adds the related Regression test suite to validate
the API implementation.
Change-Id: I622d866b34ba7e3a3e61e1a28d43fb80e49fd8ec
Signed-off-by: Louis Mayencourt <louis.mayencourt@arm.com>
diff --git a/secure_fw/services/crypto/crypto_mac.c b/secure_fw/services/crypto/crypto_mac.c
new file mode 100644
index 0000000..a068830
--- /dev/null
+++ b/secure_fw/services/crypto/crypto_mac.c
@@ -0,0 +1,559 @@
+/*
+ * 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;
+
+ /* 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;
+ }
+
+ /* Get the key data to start the HMAC */
+ 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;
+ }
+
+ /* 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;
+}
+/*!@}*/