#include "atca_basic.h"
#ifdef _WIN32
#include <stdlib.h>
#endif
static const uint8_t g_aes_zero_block[AES_DATA_SIZE] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
ATCA_STATUS atcab_aes_cmac_init(atca_aes_cmac_ctx_t* ctx, uint16_t key_id, uint8_t key_block)
{
if (ctx == NULL)
{
return ATCA_BAD_PARAM;
}
memset(ctx, 0, sizeof(*ctx));
return atcab_aes_cbc_init(&ctx->cbc_ctx, key_id, key_block, g_aes_zero_block);
}
ATCA_STATUS atcab_aes_cmac_update(atca_aes_cmac_ctx_t* ctx, const uint8_t* data, uint32_t data_size)
{
uint32_t rem_size = AES_DATA_SIZE - ctx->block_size;
uint32_t copy_size = data_size > rem_size ? rem_size : data_size;
ATCA_STATUS status = ATCA_SUCCESS;
uint8_t ciphertext[AES_DATA_SIZE];
uint32_t block_count;
uint32_t i;
if (ctx == NULL || (data == NULL && data_size > 0))
{
return ATCA_BAD_PARAM;
}
memcpy(&ctx->block[ctx->block_size], data, copy_size);
if (ctx->block_size + data_size < AES_DATA_SIZE + 1)
{
ctx->block_size += data_size;
return ATCA_SUCCESS;
}
status = atcab_aes_cbc_encrypt_block(&ctx->cbc_ctx, ctx->block, ciphertext);
if (status != ATCA_SUCCESS)
{
return status;
}
data_size -= copy_size; block_count = data_size / AES_DATA_SIZE;
if (block_count > 0 && data_size % AES_DATA_SIZE == 0)
{
block_count--; }
for (i = 0; i < block_count; i++)
{
status = atcab_aes_cbc_encrypt_block(&ctx->cbc_ctx, &data[copy_size + i * AES_DATA_SIZE], ciphertext);
if (status != ATCA_SUCCESS)
{
return status;
}
data_size -= AES_DATA_SIZE;
}
ctx->block_size = data_size;
memcpy(ctx->block, &data[copy_size + block_count * AES_DATA_SIZE], ctx->block_size);
return ATCA_SUCCESS;
}
static void left_shift_one(uint8_t* data, size_t data_size)
{
size_t i;
for (i = 0; i < data_size; i++)
{
data[i] = data[i] << 1;
if (i + 1 < data_size && data[i + 1] & 0x80)
{
data[i] |= 0x01; }
}
}
ATCA_STATUS atcab_aes_cmac_finish(atca_aes_cmac_ctx_t* ctx, uint8_t* cmac, uint32_t cmac_size)
{
uint32_t i;
uint8_t subkey[AES_DATA_SIZE];
ATCA_STATUS status = ATCA_SUCCESS;
bool is_msb_one;
uint8_t cmac_full[AES_DATA_SIZE];
if (ctx == NULL || cmac == NULL || cmac_size > AES_DATA_SIZE)
{
return ATCA_BAD_PARAM;
}
status = atcab_aes_encrypt(ctx->cbc_ctx.key_id, ctx->cbc_ctx.key_block, g_aes_zero_block, subkey);
if (status != ATCA_SUCCESS)
{
return status;
}
is_msb_one = (subkey[0] & 0x80);
left_shift_one(subkey, sizeof(subkey)); if (is_msb_one)
{
subkey[AES_DATA_SIZE - 1] ^= 0x87; }
if (ctx->block_size != AES_DATA_SIZE)
{
is_msb_one = (subkey[0] & 0x80);
left_shift_one(subkey, sizeof(subkey)); if (is_msb_one)
{
subkey[AES_DATA_SIZE - 1] ^= 0x87; }
for (i = 0; i < AES_DATA_SIZE - ctx->block_size; i++)
{
ctx->block[ctx->block_size + i] = (i == 0 ? 0x80 : 0x00);
}
}
for (i = 0; i < AES_DATA_SIZE; i++)
{
ctx->block[i] ^= subkey[i];
}
status = atcab_aes_cbc_encrypt_block(&ctx->cbc_ctx, ctx->block, cmac_full);
if (status != ATCA_SUCCESS)
{
return status;
}
memcpy(cmac, cmac_full, cmac_size);
return ATCA_SUCCESS;
}