#ifndef _KERNEL
#include <strings.h>
#include <limits.h>
#include <assert.h>
#include <security/cryptoki.h>
#endif
#include <sys/debug.h>
#include <sys/types.h>
#include <modes/modes.h>
#include <sys/crypto/common.h>
#include <sys/crypto/impl.h>
#include <aes/aes_impl.h>
#define CONST_RB_128 0x87
#define CONST_RB_64 0x1B
int
cbc_encrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length,
crypto_data_t *out, size_t block_size,
int (*encrypt)(const void *, const uint8_t *, uint8_t *),
void (*copy_block)(uint8_t *, uint8_t *),
void (*xor_block)(uint8_t *, uint8_t *))
{
size_t remainder = length;
size_t need;
uint8_t *datap = (uint8_t *)data;
uint8_t *blockp;
uint8_t *lastp;
void *iov_or_mp;
offset_t offset;
uint8_t *out_data_1;
uint8_t *out_data_2;
size_t out_data_1_len;
if (length + ctx->cbc_remainder_len < ctx->max_remain) {
bcopy(datap,
(uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len,
length);
ctx->cbc_remainder_len += length;
ctx->cbc_copy_to = datap;
return (CRYPTO_SUCCESS);
}
lastp = (uint8_t *)ctx->cbc_iv;
if (out != NULL)
crypto_init_ptrs(out, &iov_or_mp, &offset);
do {
if (ctx->cbc_remainder_len > 0) {
need = block_size - ctx->cbc_remainder_len;
if (need > remainder)
return (CRYPTO_DATA_LEN_RANGE);
bcopy(datap, &((uint8_t *)ctx->cbc_remainder)
[ctx->cbc_remainder_len], need);
blockp = (uint8_t *)ctx->cbc_remainder;
} else {
blockp = datap;
}
if (out == NULL) {
xor_block(lastp, blockp);
encrypt(ctx->cbc_keysched, blockp, blockp);
ctx->cbc_lastp = blockp;
lastp = blockp;
if ((ctx->cbc_flags & CMAC_MODE) == 0 &&
ctx->cbc_remainder_len > 0) {
bcopy(blockp, ctx->cbc_copy_to,
ctx->cbc_remainder_len);
bcopy(blockp + ctx->cbc_remainder_len, datap,
need);
}
} else {
xor_block(blockp, lastp);
encrypt(ctx->cbc_keysched, lastp, lastp);
if ((ctx->cbc_flags & CMAC_MODE) == 0) {
crypto_get_ptrs(out, &iov_or_mp, &offset,
&out_data_1, &out_data_1_len,
&out_data_2, block_size);
if (out_data_1_len == block_size) {
copy_block(lastp, out_data_1);
} else {
bcopy(lastp, out_data_1,
out_data_1_len);
if (out_data_2 != NULL) {
bcopy(lastp + out_data_1_len,
out_data_2,
block_size -
out_data_1_len);
}
}
out->cd_offset += block_size;
}
}
if (ctx->cbc_remainder_len != 0) {
datap += need;
ctx->cbc_remainder_len = 0;
} else {
datap += block_size;
}
remainder = (size_t)&data[length] - (size_t)datap;
if (remainder > 0 && remainder < ctx->max_remain) {
bcopy(datap, ctx->cbc_remainder, remainder);
ctx->cbc_remainder_len = remainder;
ctx->cbc_copy_to = datap;
goto out;
}
ctx->cbc_copy_to = NULL;
} while (remainder > 0);
out:
if (ctx->cbc_lastp != NULL) {
copy_block((uint8_t *)ctx->cbc_lastp, (uint8_t *)ctx->cbc_iv);
ctx->cbc_lastp = (uint8_t *)ctx->cbc_iv;
}
return (CRYPTO_SUCCESS);
}
#define OTHER(a, ctx) \
(((a) == (ctx)->cbc_lastblock) ? (ctx)->cbc_iv : (ctx)->cbc_lastblock)
int
cbc_decrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length,
crypto_data_t *out, size_t block_size,
int (*decrypt)(const void *, const uint8_t *, uint8_t *),
void (*copy_block)(uint8_t *, uint8_t *),
void (*xor_block)(uint8_t *, uint8_t *))
{
size_t remainder = length;
size_t need;
uint8_t *datap = (uint8_t *)data;
uint8_t *blockp;
uint8_t *lastp;
void *iov_or_mp;
offset_t offset;
uint8_t *out_data_1;
uint8_t *out_data_2;
size_t out_data_1_len;
if (length + ctx->cbc_remainder_len < block_size) {
bcopy(datap,
(uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len,
length);
ctx->cbc_remainder_len += length;
ctx->cbc_copy_to = datap;
return (CRYPTO_SUCCESS);
}
lastp = ctx->cbc_lastp;
if (out != NULL)
crypto_init_ptrs(out, &iov_or_mp, &offset);
do {
if (ctx->cbc_remainder_len > 0) {
need = block_size - ctx->cbc_remainder_len;
if (need > remainder)
return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
bcopy(datap, &((uint8_t *)ctx->cbc_remainder)
[ctx->cbc_remainder_len], need);
blockp = (uint8_t *)ctx->cbc_remainder;
} else {
blockp = datap;
}
copy_block(blockp, (uint8_t *)OTHER((uint64_t *)lastp, ctx));
if (out != NULL) {
decrypt(ctx->cbc_keysched, blockp,
(uint8_t *)ctx->cbc_remainder);
blockp = (uint8_t *)ctx->cbc_remainder;
} else {
decrypt(ctx->cbc_keysched, blockp, blockp);
}
xor_block(lastp, blockp);
lastp = (uint8_t *)OTHER((uint64_t *)lastp, ctx);
if (out != NULL) {
crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
&out_data_1_len, &out_data_2, block_size);
bcopy(blockp, out_data_1, out_data_1_len);
if (out_data_2 != NULL) {
bcopy(blockp + out_data_1_len, out_data_2,
block_size - out_data_1_len);
}
out->cd_offset += block_size;
} else if (ctx->cbc_remainder_len > 0) {
bcopy(blockp, ctx->cbc_copy_to, ctx->cbc_remainder_len);
bcopy(blockp + ctx->cbc_remainder_len, datap, need);
}
if (ctx->cbc_remainder_len != 0) {
datap += need;
ctx->cbc_remainder_len = 0;
} else {
datap += block_size;
}
remainder = (size_t)&data[length] - (size_t)datap;
if (remainder > 0 && remainder < block_size) {
bcopy(datap, ctx->cbc_remainder, remainder);
ctx->cbc_remainder_len = remainder;
ctx->cbc_lastp = lastp;
ctx->cbc_copy_to = datap;
return (CRYPTO_SUCCESS);
}
ctx->cbc_copy_to = NULL;
} while (remainder > 0);
ctx->cbc_lastp = lastp;
return (CRYPTO_SUCCESS);
}
int
cbc_init_ctx(cbc_ctx_t *cbc_ctx, char *param, size_t param_len,
size_t block_size, void (*copy_block)(uint8_t *, uint64_t *))
{
if (param != NULL) {
#ifdef _KERNEL
ASSERT(param_len == block_size);
#else
assert(param_len == block_size);
#endif
copy_block((uchar_t *)param, cbc_ctx->cbc_iv);
}
cbc_ctx->cbc_lastp = (uint8_t *)&cbc_ctx->cbc_iv[0];
cbc_ctx->cbc_flags |= CBC_MODE;
cbc_ctx->max_remain = block_size;
return (CRYPTO_SUCCESS);
}
static void *
cbc_cmac_alloc_ctx(int kmflag, uint32_t mode)
{
cbc_ctx_t *cbc_ctx;
uint32_t modeval = mode & (CBC_MODE|CMAC_MODE);
VERIFY(modeval == CBC_MODE || modeval == CMAC_MODE);
#ifdef _KERNEL
if ((cbc_ctx = kmem_zalloc(sizeof (cbc_ctx_t), kmflag)) == NULL)
#else
if ((cbc_ctx = calloc(1, sizeof (cbc_ctx_t))) == NULL)
#endif
return (NULL);
cbc_ctx->cbc_flags = mode;
return (cbc_ctx);
}
void *
cbc_alloc_ctx(int kmflag)
{
return (cbc_cmac_alloc_ctx(kmflag, CBC_MODE));
}
void *
cmac_alloc_ctx(int kmflag)
{
return (cbc_cmac_alloc_ctx(kmflag, CMAC_MODE));
}
int
cmac_init_ctx(cbc_ctx_t *cbc_ctx, size_t block_size)
{
if (block_size != 16 && block_size != 8)
return (CRYPTO_INVALID_CONTEXT);
cbc_ctx->cbc_iv[0] = 0;
cbc_ctx->cbc_iv[1] = 0;
cbc_ctx->cbc_lastp = (uint8_t *)&cbc_ctx->cbc_iv[0];
cbc_ctx->cbc_flags |= CMAC_MODE;
cbc_ctx->max_remain = block_size + 1;
return (CRYPTO_SUCCESS);
}
static uint8_t
cmac_left_shift_block_by1(uint8_t *block, size_t block_size)
{
uint8_t carry = 0, old;
size_t i;
for (i = block_size; i > 0; i--) {
old = carry;
carry = (block[i - 1] & 0x80) ? 1 : 0;
block[i - 1] = (block[i - 1] << 1) | old;
}
return (carry);
}
int
cmac_mode_final(cbc_ctx_t *cbc_ctx, crypto_data_t *out,
int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
void (*xor_block)(uint8_t *, uint8_t *))
{
uint8_t buf[AES_BLOCK_LEN] = {0};
uint8_t *M_last = (uint8_t *)cbc_ctx->cbc_remainder;
size_t length = cbc_ctx->cbc_remainder_len;
size_t block_size = cbc_ctx->max_remain - 1;
uint8_t const_rb;
if (length > block_size)
return (CRYPTO_INVALID_CONTEXT);
if (out->cd_length < block_size)
return (CRYPTO_DATA_LEN_RANGE);
if (block_size == 16)
const_rb = CONST_RB_128;
else if (block_size == 8)
const_rb = CONST_RB_64;
else
return (CRYPTO_INVALID_CONTEXT);
encrypt_block(cbc_ctx->cbc_keysched, buf, buf);
if (cmac_left_shift_block_by1(buf, block_size))
buf[block_size - 1] ^= const_rb;
if (length == block_size) {
xor_block(buf, M_last);
xor_block(cbc_ctx->cbc_lastp, M_last);
encrypt_block(cbc_ctx->cbc_keysched, M_last, M_last);
} else {
if (cmac_left_shift_block_by1(buf, block_size))
buf[block_size - 1] ^= const_rb;
M_last[length] = 0x80;
bzero(M_last + length + 1, block_size - length - 1);
xor_block(buf, M_last);
xor_block(cbc_ctx->cbc_lastp, M_last);
encrypt_block(cbc_ctx->cbc_keysched, M_last, M_last);
}
#ifndef _KERNEL
explicit_bzero(&buf, sizeof (buf));
#else
bzero(&buf, sizeof (buf));
#endif
return (crypto_put_output_data(M_last, out, block_size));
}