Skip to content

File syn_aes_cmac.c

File List > crypto > syn_aes_cmac.c

Go to the documentation of this file

#if __has_include("syn_config.h")
#include "syn_config.h"
#endif

#if !defined(SYN_USE_AES_CMAC) || SYN_USE_AES_CMAC

#include "syn_aes.h"
#include "syn_aes_cmac.h"

#include <string.h>

static void shl_128(const uint8_t in[16], uint8_t out[16])
{
    uint8_t carry = 0U;
    for (int i = 15; i >= 0; i--) {
        out[i] = (uint8_t)((in[i] << 1) | carry);
        carry = (uint8_t)((in[i] >> 7) & 1U);
    }
}

static void generate_subkeys(const SYN_AES_Context *ctx, uint8_t k1[16], uint8_t k2[16])
{
    const uint8_t zero_block[16] = {0};
    uint8_t l[16];

    /* Step 1: L := AES(K, const_Zero) */
    syn_aes_encrypt_block(ctx, zero_block, l);

    /* Step 2: Generate K1 */
    shl_128(l, k1);
    if ((l[0] & 0x80U) != 0U) {
        k1[15] ^= 0x87U;
    }

    /* Step 3: Generate K2 */
    shl_128(k1, k2);
    if ((k1[0] & 0x80U) != 0U) {
        k2[15] ^= 0x87U;
    }
}

SYN_Status syn_aes_cmac(const uint8_t key[SYN_AES_CMAC_KEY_SIZE], const uint8_t *msg,
                        size_t msg_len, uint8_t mac[SYN_AES_CMAC_TAG_SIZE])
{
    if (key == NULL || mac == NULL || (msg == NULL && msg_len > 0U)) {
        return SYN_INVALID_PARAM;
    }

    SYN_AES_Context ctx;
    (void)syn_aes_init(&ctx, key, SYN_AES_CMAC_KEY_SIZE);

    uint8_t k1[16];
    uint8_t k2[16];
    generate_subkeys(&ctx, k1, k2);

    /* Step 2 & 3: Compute number of blocks and complete-block flag */
    size_t n = (msg_len + 15U) / 16U;
    bool is_complete = false;

    if (n == 0U) {
        n = 1U;
        is_complete = false;
    } else if ((msg_len % 16U) == 0U) {
        is_complete = true;
    } else {
        is_complete = false;
    }

    /* Step 4: Calculate M_last */
    uint8_t m_last[16];
    if (is_complete) {
        for (size_t i = 0U; i < 16U; i++) {
            m_last[i] = (uint8_t)(msg[(n - 1U) * 16U + i] ^ k1[i]);
        }
    } else {
        size_t rem = msg_len % 16U;
        for (size_t i = 0U; i < rem; i++) {
            m_last[i] = msg[(n - 1U) * 16U + i];
        }
        m_last[rem] = 0x80U;
        for (size_t i = rem + 1U; i < 16U; i++) {
            m_last[i] = 0x00U;
        }
        for (size_t i = 0U; i < 16U; i++) {
            m_last[i] ^= k2[i];
        }
    }

    /* Step 5 & 6: CBC-MAC iteration */
    uint8_t x[16] = {0};
    uint8_t y[16];

    for (size_t i = 0U; i + 1U < n; i++) {
        for (size_t j = 0U; j < 16U; j++) {
            y[j] = (uint8_t)(x[j] ^ msg[i * 16U + j]);
        }
        syn_aes_encrypt_block(&ctx, y, x);
    }

    for (size_t j = 0U; j < 16U; j++) {
        y[j] = (uint8_t)(m_last[j] ^ x[j]);
    }
    syn_aes_encrypt_block(&ctx, y, mac);

    return SYN_OK;
}

bool syn_aes_cmac_verify(const uint8_t key[SYN_AES_CMAC_KEY_SIZE], const uint8_t *msg,
                         size_t msg_len, const uint8_t mac[SYN_AES_CMAC_TAG_SIZE])
{
    if (key == NULL || mac == NULL || (msg == NULL && msg_len > 0U)) {
        return false;
    }

    uint8_t computed[SYN_AES_CMAC_TAG_SIZE];
    (void)syn_aes_cmac(key, msg, msg_len, computed);

    /* Constant-time comparison */
    uint8_t diff = 0U;
    for (size_t i = 0U; i < SYN_AES_CMAC_TAG_SIZE; i++) {
        diff |= (uint8_t)(computed[i] ^ mac[i]);
    }

    return (diff == 0U);
}

#endif /* SYN_USE_AES_CMAC */