diff options
Diffstat (limited to 'core/lib/libtomcrypt/ccm.c')
-rw-r--r-- | core/lib/libtomcrypt/ccm.c | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/core/lib/libtomcrypt/ccm.c b/core/lib/libtomcrypt/ccm.c new file mode 100644 index 00000000..a7c06b9c --- /dev/null +++ b/core/lib/libtomcrypt/ccm.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2014-2019, Linaro Limited + */ + +#include <assert.h> +#include <crypto/crypto.h> +#include <crypto/crypto_impl.h> +#include <stdlib.h> +#include <string.h> +#include <tee_api_types.h> +#include <tomcrypt.h> +#include <util.h> + +#define TEE_CCM_KEY_MAX_LENGTH 32 +#define TEE_CCM_NONCE_MAX_LENGTH 13 +#define TEE_CCM_TAG_MAX_LENGTH 16 + +struct tee_ccm_state { + struct crypto_authenc_ctx aectx; + ccm_state ctx; /* the ccm state as defined by LTC */ + size_t tag_len; /* tag length */ +}; + +static const struct crypto_authenc_ops aes_ccm_ops; + +TEE_Result crypto_aes_ccm_alloc_ctx(struct crypto_authenc_ctx **ctx_ret) +{ + struct tee_ccm_state *ctx = calloc(1, sizeof(*ctx)); + + if (!ctx) + return TEE_ERROR_OUT_OF_MEMORY; + ctx->aectx.ops = &aes_ccm_ops; + + *ctx_ret = &ctx->aectx; + return TEE_SUCCESS; +} + +static struct tee_ccm_state *to_tee_ccm_state(struct crypto_authenc_ctx *aectx) +{ + assert(aectx && aectx->ops == &aes_ccm_ops); + + return container_of(aectx, struct tee_ccm_state, aectx); +} + +static void crypto_aes_ccm_free_ctx(struct crypto_authenc_ctx *aectx) +{ + free(to_tee_ccm_state(aectx)); +} + +static void crypto_aes_ccm_copy_state(struct crypto_authenc_ctx *dst_aectx, + struct crypto_authenc_ctx *src_aectx) +{ + struct tee_ccm_state *dst_ctx = to_tee_ccm_state(dst_aectx); + struct tee_ccm_state *src_ctx = to_tee_ccm_state(src_aectx); + + dst_ctx->ctx = src_ctx->ctx; + dst_ctx->tag_len = src_ctx->tag_len; +} + +static TEE_Result crypto_aes_ccm_init(struct crypto_authenc_ctx *aectx, + TEE_OperationMode mode __unused, + const uint8_t *key, size_t key_len, + const uint8_t *nonce, size_t nonce_len, + size_t tag_len, size_t aad_len, + size_t payload_len) +{ + int ltc_res = 0; + int ltc_cipherindex = find_cipher("aes"); + struct tee_ccm_state *ccm = to_tee_ccm_state(aectx); + + if (ltc_cipherindex < 0) + return TEE_ERROR_NOT_SUPPORTED; + + /* reset the state */ + memset(&ccm->ctx, 0, sizeof(ccm->ctx)); + ccm->tag_len = tag_len; + + /* Check the key length */ + if ((!key) || (key_len > TEE_CCM_KEY_MAX_LENGTH)) + return TEE_ERROR_BAD_PARAMETERS; + + /* check the nonce */ + if (nonce_len > TEE_CCM_NONCE_MAX_LENGTH) + return TEE_ERROR_BAD_PARAMETERS; + + /* check the tag len */ + if ((tag_len < 4) || (tag_len > TEE_CCM_TAG_MAX_LENGTH) || + (tag_len % 2 != 0)) + return TEE_ERROR_NOT_SUPPORTED; + + ltc_res = ccm_init(&ccm->ctx, ltc_cipherindex, key, key_len, + payload_len, tag_len, aad_len); + if (ltc_res != CRYPT_OK) + return TEE_ERROR_BAD_STATE; + + /* Add the IV */ + ltc_res = ccm_add_nonce(&ccm->ctx, nonce, nonce_len); + if (ltc_res != CRYPT_OK) + return TEE_ERROR_BAD_STATE; + + return TEE_SUCCESS; +} + +static TEE_Result crypto_aes_ccm_update_aad(struct crypto_authenc_ctx *aectx, + const uint8_t *data, size_t len) +{ + struct tee_ccm_state *ccm = to_tee_ccm_state(aectx); + int ltc_res = 0; + + /* Add the AAD (note: aad can be NULL if aadlen == 0) */ + ltc_res = ccm_add_aad(&ccm->ctx, data, len); + if (ltc_res != CRYPT_OK) + return TEE_ERROR_BAD_STATE; + + return TEE_SUCCESS; +} + +static TEE_Result +crypto_aes_ccm_update_payload(struct crypto_authenc_ctx *aectx, + TEE_OperationMode mode, const uint8_t *src_data, + size_t len, uint8_t *dst_data) +{ + int ltc_res = 0; + int dir = 0; + struct tee_ccm_state *ccm = to_tee_ccm_state(aectx); + unsigned char *pt = NULL; + unsigned char *ct = NULL; + + if (mode == TEE_MODE_ENCRYPT) { + pt = (unsigned char *)src_data; + ct = dst_data; + dir = CCM_ENCRYPT; + } else { + pt = dst_data; + ct = (unsigned char *)src_data; + dir = CCM_DECRYPT; + } + ltc_res = ccm_process(&ccm->ctx, pt, len, ct, dir); + if (ltc_res != CRYPT_OK) + return TEE_ERROR_BAD_STATE; + + return TEE_SUCCESS; +} + +static TEE_Result crypto_aes_ccm_enc_final(struct crypto_authenc_ctx *aectx, + const uint8_t *src_data, + size_t len, uint8_t *dst_data, + uint8_t *dst_tag, + size_t *dst_tag_len) +{ + TEE_Result res = TEE_SUCCESS; + struct tee_ccm_state *ccm = to_tee_ccm_state(aectx); + int ltc_res = 0; + + /* Finalize the remaining buffer */ + res = crypto_aes_ccm_update_payload(aectx, TEE_MODE_ENCRYPT, src_data, + len, dst_data); + if (res != TEE_SUCCESS) + return res; + + /* Check the tag length */ + if (*dst_tag_len < ccm->tag_len) { + *dst_tag_len = ccm->tag_len; + return TEE_ERROR_SHORT_BUFFER; + } + *dst_tag_len = ccm->tag_len; + + /* Compute the tag */ + ltc_res = ccm_done(&ccm->ctx, dst_tag, + (unsigned long *)dst_tag_len); + if (ltc_res != CRYPT_OK) + return TEE_ERROR_BAD_STATE; + + return TEE_SUCCESS; +} + +static TEE_Result crypto_aes_ccm_dec_final(struct crypto_authenc_ctx *aectx, + const uint8_t *src_data, size_t len, + uint8_t *dst_data, + const uint8_t *tag, size_t tag_len) +{ + TEE_Result res = TEE_ERROR_BAD_STATE; + struct tee_ccm_state *ccm = to_tee_ccm_state(aectx); + int ltc_res = 0; + uint8_t dst_tag[TEE_CCM_TAG_MAX_LENGTH] = { 0 }; + unsigned long ltc_tag_len = tag_len; + + if (tag_len == 0) + return TEE_ERROR_SHORT_BUFFER; + if (tag_len > TEE_CCM_TAG_MAX_LENGTH) + return TEE_ERROR_BAD_STATE; + + /* Process the last buffer, if any */ + res = crypto_aes_ccm_update_payload(aectx, TEE_MODE_DECRYPT, src_data, + len, dst_data); + if (res != TEE_SUCCESS) + return res; + + /* Finalize the authentication */ + ltc_res = ccm_done(&ccm->ctx, dst_tag, <c_tag_len); + if (ltc_res != CRYPT_OK) + return TEE_ERROR_BAD_STATE; + + if (consttime_memcmp(dst_tag, tag, tag_len) != 0) + res = TEE_ERROR_MAC_INVALID; + else + res = TEE_SUCCESS; + return res; +} + +static void crypto_aes_ccm_final(struct crypto_authenc_ctx *aectx) +{ + ccm_reset(&to_tee_ccm_state(aectx)->ctx); +} + +static const struct crypto_authenc_ops aes_ccm_ops = { + .init = crypto_aes_ccm_init, + .update_aad = crypto_aes_ccm_update_aad, + .update_payload = crypto_aes_ccm_update_payload, + .enc_final = crypto_aes_ccm_enc_final, + .dec_final = crypto_aes_ccm_dec_final, + .final = crypto_aes_ccm_final, + .free_ctx = crypto_aes_ccm_free_ctx, + .copy_state = crypto_aes_ccm_copy_state, +}; |