| // SPDX-License-Identifier: BSD-2-Clause |
| /* |
| * Copyright (c) 2014-2019, Linaro Limited |
| */ |
| |
| /* |
| * This is implemented here as being the plain text which is encoded with IV=0. |
| * Result of the CBC-MAC is the last 16-bytes cipher. |
| */ |
| |
| #include <assert.h> |
| #include <crypto/crypto.h> |
| #include <crypto/crypto_impl.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <types_ext.h> |
| #include <util.h> |
| |
| #define CBCMAC_MAX_BLOCK_LEN 16 |
| |
| struct crypto_cbc_mac_ctx { |
| struct crypto_mac_ctx ctx; |
| void *cbc_ctx; |
| uint32_t cbc_algo; |
| uint8_t block[CBCMAC_MAX_BLOCK_LEN]; |
| uint8_t digest[CBCMAC_MAX_BLOCK_LEN]; |
| unsigned char current_block_len; |
| unsigned char block_len; |
| bool is_computed; |
| bool pkcs5_pad; |
| }; |
| |
| static const struct crypto_mac_ops crypto_cbc_mac_ops; |
| |
| static struct crypto_cbc_mac_ctx *to_cbc_mac_ctx(struct crypto_mac_ctx *ctx) |
| { |
| assert(ctx && ctx->ops == &crypto_cbc_mac_ops); |
| |
| return container_of(ctx, struct crypto_cbc_mac_ctx, ctx); |
| } |
| |
| static TEE_Result crypto_cbc_mac_init(struct crypto_mac_ctx *ctx, |
| const uint8_t *key, size_t len) |
| { |
| struct crypto_cbc_mac_ctx *mc = to_cbc_mac_ctx(ctx); |
| |
| memset(mc->block, 0, sizeof(mc->block)); |
| memset(mc->digest, 0, sizeof(mc->digest)); |
| mc->current_block_len = 0; |
| mc->is_computed = false; |
| |
| /* IV should be zero and mc->block happens to be zero at this stage */ |
| return crypto_cipher_init(mc->cbc_ctx, TEE_MODE_ENCRYPT, key, len, |
| NULL, 0, mc->block, mc->block_len); |
| } |
| |
| static TEE_Result crypto_cbc_mac_update(struct crypto_mac_ctx *ctx, |
| const uint8_t *data, size_t len) |
| { |
| TEE_Result res = TEE_SUCCESS; |
| struct crypto_cbc_mac_ctx *mc = to_cbc_mac_ctx(ctx); |
| |
| if ((mc->current_block_len > 0) && |
| (len + mc->current_block_len >= mc->block_len)) { |
| size_t pad_len = mc->block_len - mc->current_block_len; |
| |
| memcpy(mc->block + mc->current_block_len, data, pad_len); |
| data += pad_len; |
| len -= pad_len; |
| res = crypto_cipher_update(mc->cbc_ctx, TEE_MODE_ENCRYPT, |
| false, mc->block, mc->block_len, |
| mc->digest); |
| if (res) |
| return res; |
| mc->is_computed = 1; |
| mc->current_block_len = 0; |
| } |
| |
| while (len >= mc->block_len) { |
| res = crypto_cipher_update(mc->cbc_ctx, TEE_MODE_ENCRYPT, |
| false, data, mc->block_len, |
| mc->digest); |
| if (res) |
| return res; |
| mc->is_computed = 1; |
| data += mc->block_len; |
| len -= mc->block_len; |
| } |
| |
| if (len > 0) { |
| assert(mc->current_block_len + len < mc->block_len); |
| memcpy(mc->block + mc->current_block_len, data, len); |
| mc->current_block_len += len; |
| } |
| |
| return TEE_SUCCESS; |
| } |
| |
| static TEE_Result crypto_cbc_mac_final(struct crypto_mac_ctx *ctx, |
| uint8_t *digest, size_t digest_len) |
| { |
| struct crypto_cbc_mac_ctx *mc = to_cbc_mac_ctx(ctx); |
| |
| if (mc->pkcs5_pad) { |
| /* |
| * Padding is in whole bytes. The value of each added |
| * byte is the number of bytes that are added, i.e. N |
| * bytes, each of value N are added |
| */ |
| size_t pad_len = mc->block_len - mc->current_block_len; |
| |
| memset(mc->block + mc->current_block_len, pad_len, pad_len); |
| mc->current_block_len = 0; |
| if (crypto_cbc_mac_update(ctx, mc->block, mc->block_len)) |
| return TEE_ERROR_BAD_STATE; |
| } |
| |
| if (!mc->is_computed || mc->current_block_len) |
| return TEE_ERROR_BAD_STATE; |
| |
| memcpy(digest, mc->digest, MIN(digest_len, mc->block_len)); |
| crypto_cipher_final(mc->cbc_ctx); |
| |
| return TEE_SUCCESS; |
| } |
| |
| static void crypto_cbc_mac_free_ctx(struct crypto_mac_ctx *ctx) |
| { |
| struct crypto_cbc_mac_ctx *mc = to_cbc_mac_ctx(ctx); |
| |
| crypto_cipher_free_ctx(mc->cbc_ctx); |
| free(mc); |
| } |
| |
| static void crypto_cbc_mac_copy_state(struct crypto_mac_ctx *dst_ctx, |
| struct crypto_mac_ctx *src_ctx) |
| { |
| struct crypto_cbc_mac_ctx *dst = to_cbc_mac_ctx(dst_ctx); |
| struct crypto_cbc_mac_ctx *src = to_cbc_mac_ctx(src_ctx); |
| |
| assert(dst->block_len == src->block_len); |
| assert(dst->pkcs5_pad == src->pkcs5_pad); |
| assert(dst->cbc_algo == src->cbc_algo); |
| |
| crypto_cipher_copy_state(dst->cbc_ctx, src->cbc_ctx); |
| memcpy(dst->block, src->block, sizeof(dst->block)); |
| memcpy(dst->digest, src->digest, sizeof(dst->digest)); |
| dst->current_block_len = src->current_block_len; |
| dst->is_computed = src->is_computed; |
| } |
| |
| static const struct crypto_mac_ops crypto_cbc_mac_ops = { |
| .init = crypto_cbc_mac_init, |
| .update = crypto_cbc_mac_update, |
| .final = crypto_cbc_mac_final, |
| .free_ctx = crypto_cbc_mac_free_ctx, |
| .copy_state = crypto_cbc_mac_copy_state, |
| }; |
| |
| static TEE_Result crypto_cbc_mac_alloc_ctx(struct crypto_mac_ctx **ctx_ret, |
| uint32_t cbc_algo, bool pkcs5_pad) |
| { |
| TEE_Result res; |
| void *cbc_ctx = NULL; |
| struct crypto_cbc_mac_ctx *ctx = NULL; |
| size_t block_size = 0; |
| |
| res = crypto_cipher_get_block_size(cbc_algo, &block_size); |
| if (res) |
| return res; |
| |
| res = crypto_cipher_alloc_ctx(&cbc_ctx, cbc_algo); |
| if (res) |
| return res; |
| |
| ctx = calloc(1, sizeof(*ctx)); |
| if (!ctx) { |
| crypto_cipher_free_ctx(cbc_ctx); |
| return TEE_ERROR_OUT_OF_MEMORY; |
| } |
| |
| ctx->cbc_ctx = cbc_ctx; |
| ctx->cbc_algo = cbc_algo; |
| ctx->pkcs5_pad = pkcs5_pad; |
| ctx->block_len = block_size; |
| ctx->ctx.ops = &crypto_cbc_mac_ops; |
| *ctx_ret = &ctx->ctx; |
| |
| return TEE_SUCCESS; |
| } |
| |
| TEE_Result crypto_aes_cbc_mac_nopad_alloc_ctx(struct crypto_mac_ctx **ctx) |
| { |
| return crypto_cbc_mac_alloc_ctx(ctx, TEE_ALG_AES_CBC_NOPAD, false); |
| } |
| |
| TEE_Result crypto_aes_cbc_mac_pkcs5_alloc_ctx(struct crypto_mac_ctx **ctx) |
| { |
| return crypto_cbc_mac_alloc_ctx(ctx, TEE_ALG_AES_CBC_NOPAD, true); |
| } |
| |
| TEE_Result crypto_des_cbc_mac_nopad_alloc_ctx(struct crypto_mac_ctx **ctx) |
| { |
| return crypto_cbc_mac_alloc_ctx(ctx, TEE_ALG_DES_CBC_NOPAD, false); |
| } |
| |
| TEE_Result crypto_des_cbc_mac_pkcs5_alloc_ctx(struct crypto_mac_ctx **ctx) |
| { |
| return crypto_cbc_mac_alloc_ctx(ctx, TEE_ALG_DES_CBC_NOPAD, true); |
| } |
| |
| TEE_Result crypto_des3_cbc_mac_nopad_alloc_ctx(struct crypto_mac_ctx **ctx) |
| { |
| return crypto_cbc_mac_alloc_ctx(ctx, TEE_ALG_DES3_CBC_NOPAD, false); |
| } |
| |
| TEE_Result crypto_des3_cbc_mac_pkcs5_alloc_ctx(struct crypto_mac_ctx **ctx) |
| { |
| return crypto_cbc_mac_alloc_ctx(ctx, TEE_ALG_DES3_CBC_NOPAD, true); |
| } |