#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/cmac.h>
#include "evp_local.h"
struct CMAC_CTX_st {
EVP_CIPHER_CTX *cipher_ctx;
unsigned char k1[EVP_MAX_BLOCK_LENGTH];
unsigned char k2[EVP_MAX_BLOCK_LENGTH];
unsigned char tbl[EVP_MAX_BLOCK_LENGTH];
unsigned char last_block[EVP_MAX_BLOCK_LENGTH];
int nlast_block;
};
static void
make_kn(unsigned char *kn, const unsigned char *l, int block_size)
{
unsigned char mask, Rb;
int i;
Rb = block_size == 16 ? 0x87 : 0x1b;
for (i = 0; i < block_size - 1; i++)
kn[i] = (l[i] << 1) | (l[i + 1] >> 7);
mask = 0 - (l[0] >> 7);
kn[block_size - 1] = (l[block_size - 1] << 1) ^ (Rb & mask);
}
CMAC_CTX *
CMAC_CTX_new(void)
{
CMAC_CTX *ctx;
if ((ctx = calloc(1, sizeof(CMAC_CTX))) == NULL)
goto err;
if ((ctx->cipher_ctx = EVP_CIPHER_CTX_new()) == NULL)
goto err;
ctx->nlast_block = -1;
return ctx;
err:
CMAC_CTX_free(ctx);
return NULL;
}
LCRYPTO_ALIAS(CMAC_CTX_new);
void
CMAC_CTX_cleanup(CMAC_CTX *ctx)
{
(void)EVP_CIPHER_CTX_reset(ctx->cipher_ctx);
explicit_bzero(ctx->tbl, EVP_MAX_BLOCK_LENGTH);
explicit_bzero(ctx->k1, EVP_MAX_BLOCK_LENGTH);
explicit_bzero(ctx->k2, EVP_MAX_BLOCK_LENGTH);
explicit_bzero(ctx->last_block, EVP_MAX_BLOCK_LENGTH);
ctx->nlast_block = -1;
}
LCRYPTO_ALIAS(CMAC_CTX_cleanup);
EVP_CIPHER_CTX *
CMAC_CTX_get0_cipher_ctx(CMAC_CTX *ctx)
{
return ctx->cipher_ctx;
}
LCRYPTO_ALIAS(CMAC_CTX_get0_cipher_ctx);
void
CMAC_CTX_free(CMAC_CTX *ctx)
{
if (ctx == NULL)
return;
CMAC_CTX_cleanup(ctx);
EVP_CIPHER_CTX_free(ctx->cipher_ctx);
freezero(ctx, sizeof(CMAC_CTX));
}
LCRYPTO_ALIAS(CMAC_CTX_free);
int
CMAC_CTX_copy(CMAC_CTX *out, const CMAC_CTX *in)
{
int block_size;
if (in->nlast_block == -1)
return 0;
if (!EVP_CIPHER_CTX_copy(out->cipher_ctx, in->cipher_ctx))
return 0;
block_size = EVP_CIPHER_CTX_block_size(in->cipher_ctx);
memcpy(out->k1, in->k1, block_size);
memcpy(out->k2, in->k2, block_size);
memcpy(out->tbl, in->tbl, block_size);
memcpy(out->last_block, in->last_block, block_size);
out->nlast_block = in->nlast_block;
return 1;
}
LCRYPTO_ALIAS(CMAC_CTX_copy);
int
CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen,
const EVP_CIPHER *cipher, ENGINE *impl)
{
static const unsigned char zero_iv[EVP_MAX_BLOCK_LENGTH];
int block_size;
if (key == NULL && cipher == NULL && keylen == 0) {
if (ctx->nlast_block == -1)
return 0;
if (!EVP_EncryptInit_ex(ctx->cipher_ctx, NULL, NULL, NULL, zero_iv))
return 0;
explicit_bzero(ctx->tbl, sizeof(ctx->tbl));
ctx->nlast_block = 0;
return 1;
}
if (cipher != NULL) {
if ((cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) != 0)
return 0;
if (!EVP_EncryptInit_ex(ctx->cipher_ctx, cipher, NULL, NULL, NULL))
return 0;
}
if (key != NULL) {
if (EVP_CIPHER_CTX_cipher(ctx->cipher_ctx) == NULL)
return 0;
block_size = EVP_CIPHER_CTX_block_size(ctx->cipher_ctx);
if (block_size != 8 && block_size != 16)
return 0;
if (!EVP_CIPHER_CTX_set_key_length(ctx->cipher_ctx, keylen))
return 0;
if (!EVP_EncryptInit_ex(ctx->cipher_ctx, NULL, NULL, key, zero_iv))
return 0;
if (!EVP_Cipher(ctx->cipher_ctx, ctx->tbl, zero_iv, block_size))
return 0;
make_kn(ctx->k1, ctx->tbl, block_size);
make_kn(ctx->k2, ctx->k1, block_size);
explicit_bzero(ctx->tbl, sizeof(ctx->tbl));
ctx->nlast_block = 0;
if (!EVP_EncryptInit_ex(ctx->cipher_ctx, NULL, NULL, NULL, zero_iv))
return 0;
}
return 1;
}
LCRYPTO_ALIAS(CMAC_Init);
int
CMAC_Update(CMAC_CTX *ctx, const void *in, size_t dlen)
{
const unsigned char *data = in;
size_t block_size;
if (ctx->nlast_block == -1)
return 0;
if (dlen == 0)
return 1;
block_size = EVP_CIPHER_CTX_block_size(ctx->cipher_ctx);
if (ctx->nlast_block > 0) {
size_t nleft;
nleft = block_size - ctx->nlast_block;
if (dlen < nleft)
nleft = dlen;
memcpy(ctx->last_block + ctx->nlast_block, data, nleft);
dlen -= nleft;
ctx->nlast_block += nleft;
if (dlen == 0)
return 1;
data += nleft;
if (!EVP_Cipher(ctx->cipher_ctx, ctx->tbl, ctx->last_block,
block_size))
return 0;
}
while (dlen > block_size) {
if (!EVP_Cipher(ctx->cipher_ctx, ctx->tbl, data, block_size))
return 0;
dlen -= block_size;
data += block_size;
}
memcpy(ctx->last_block, data, dlen);
ctx->nlast_block = dlen;
return 1;
}
LCRYPTO_ALIAS(CMAC_Update);
int
CMAC_Final(CMAC_CTX *ctx, unsigned char *out, size_t *poutlen)
{
int i, block_size, lb;
if (ctx->nlast_block == -1)
return 0;
block_size = EVP_CIPHER_CTX_block_size(ctx->cipher_ctx);
*poutlen = (size_t)block_size;
if (!out)
return 1;
lb = ctx->nlast_block;
if (lb == block_size) {
for (i = 0; i < block_size; i++)
out[i] = ctx->last_block[i] ^ ctx->k1[i];
} else {
ctx->last_block[lb] = 0x80;
if (block_size - lb > 1)
memset(ctx->last_block + lb + 1, 0, block_size - lb - 1);
for (i = 0; i < block_size; i++)
out[i] = ctx->last_block[i] ^ ctx->k2[i];
}
if (!EVP_Cipher(ctx->cipher_ctx, out, out, block_size)) {
explicit_bzero(out, block_size);
return 0;
}
return 1;
}
LCRYPTO_ALIAS(CMAC_Final);