#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 <sys/byteorder.h>
static void
ctr_new_keyblock(ctr_ctx_t *ctx,
int (*cipher)(const void *ks, const uint8_t *pt, uint8_t *ct))
{
uint64_t lower_counter, upper_counter;
lower_counter = ntohll(ctx->ctr_cb[1] & ctx->ctr_lower_mask);
lower_counter = htonll(lower_counter + 1);
lower_counter &= ctx->ctr_lower_mask;
ctx->ctr_cb[1] = (ctx->ctr_cb[1] & ~(ctx->ctr_lower_mask)) |
lower_counter;
if (lower_counter == 0) {
upper_counter = ntohll(ctx->ctr_cb[0] & ctx->ctr_upper_mask);
upper_counter = htonll(upper_counter + 1);
upper_counter &= ctx->ctr_upper_mask;
ctx->ctr_cb[0] = (ctx->ctr_cb[0] & ~(ctx->ctr_upper_mask)) |
upper_counter;
}
cipher(ctx->ctr_keysched, (uint8_t *)ctx->ctr_cb,
(uint8_t *)ctx->ctr_keystream);
ctx->ctr_offset = 0;
}
static void
ctr_xor(ctr_ctx_t *ctx, const uint8_t *in, uint8_t *out, size_t outlen,
size_t block_size,
int (*cipher)(const void *ks, const uint8_t *pt, uint8_t *ct))
{
const uint8_t *keyp;
size_t keyamt;
while (outlen > 0) {
if (ctx->ctr_offset == block_size) {
ctr_new_keyblock(ctx, cipher);
}
keyp = (uint8_t *)ctx->ctr_keystream + ctx->ctr_offset;
keyamt = block_size - ctx->ctr_offset;
while (keyamt > 0 && outlen > 0 &&
!IS_P2ALIGNED(in, sizeof (uint32_t)) &&
!IS_P2ALIGNED(out, sizeof (uint32_t)) &&
!IS_P2ALIGNED(keyp, sizeof (uint32_t))) {
*out++ = *in++ ^ *keyp++;
keyamt--;
outlen--;
}
if (keyamt > 3 && outlen > 3 &&
IS_P2ALIGNED(in, sizeof (uint32_t)) &&
IS_P2ALIGNED(out, sizeof (uint32_t)) &&
IS_P2ALIGNED(keyp, sizeof (uint32_t))) {
const uint32_t *key32 = (const uint32_t *)keyp;
const uint32_t *in32 = (const uint32_t *)in;
uint32_t *out32 = (uint32_t *)out;
do {
*out32++ = *in32++ ^ *key32++;
keyamt -= sizeof (uint32_t);
outlen -= sizeof (uint32_t);
} while (keyamt > 3 && outlen > 3);
keyp = (const uint8_t *)key32;
in = (const uint8_t *)in32;
out = (uint8_t *)out32;
}
while (keyamt > 0 && outlen > 0) {
*out++ = *in++ ^ *keyp++;
keyamt--;
outlen--;
}
ctx->ctr_offset = block_size - keyamt;
}
}
int
ctr_mode_contiguous_blocks(ctr_ctx_t *ctx, char *in, size_t in_length,
crypto_data_t *out, size_t block_size,
int (*cipher)(const void *ks, const uint8_t *pt, uint8_t *ct))
{
size_t in_remainder = in_length;
uint8_t *inp = (uint8_t *)in;
void *iov_or_mp;
offset_t offset;
uint8_t *out_data;
uint8_t *out_data_remainder;
size_t out_data_len;
if (block_size > sizeof (ctx->ctr_keystream))
return (CRYPTO_ARGUMENTS_BAD);
if (out == NULL)
return (CRYPTO_ARGUMENTS_BAD);
if (out->cd_offset < 0)
return (CRYPTO_DATA_LEN_RANGE);
if (SIZE_MAX - in_length < (size_t)out->cd_offset)
return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
if (out->cd_offset + in_length > out->cd_length)
return (CRYPTO_BUFFER_TOO_SMALL);
crypto_init_ptrs(out, &iov_or_mp, &offset);
while (in_remainder > 0) {
crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data,
&out_data_len, &out_data_remainder, in_remainder);
ASSERT3U(out_data_len, <=, in_remainder);
ASSERT3U(out_data_len, >, 0);
ctr_xor(ctx, inp, out_data, out_data_len, block_size, cipher);
inp += out_data_len;
in_remainder -= out_data_len;
}
out->cd_offset += in_length;
return (CRYPTO_SUCCESS);
}
int
ctr_init_ctx(ctr_ctx_t *ctr_ctx, ulong_t count, uint8_t *cb,
int (*cipher)(const void *ks, const uint8_t *pt, uint8_t *ct),
void (*copy_block)(uint8_t *, uint8_t *))
{
uint64_t upper_mask = 0;
uint64_t lower_mask = 0;
if (count == 0 || count > 128) {
return (CRYPTO_MECHANISM_PARAM_INVALID);
}
if (count >= 64) {
count -= 64;
upper_mask = (count == 64) ? UINT64_MAX : (1ULL << count) - 1;
lower_mask = UINT64_MAX;
} else {
lower_mask = (1ULL << count) - 1;
}
ctr_ctx->ctr_lower_mask = htonll(lower_mask);
ctr_ctx->ctr_upper_mask = htonll(upper_mask);
copy_block(cb, (uchar_t *)ctr_ctx->ctr_cb);
ctr_ctx->ctr_lastp = (uint8_t *)&ctr_ctx->ctr_cb[0];
cipher(ctr_ctx->ctr_keysched, (uint8_t *)ctr_ctx->ctr_cb,
(uint8_t *)ctr_ctx->ctr_keystream);
ctr_ctx->ctr_flags |= CTR_MODE;
return (CRYPTO_SUCCESS);
}
void *
ctr_alloc_ctx(int kmflag)
{
ctr_ctx_t *ctr_ctx;
#ifdef _KERNEL
if ((ctr_ctx = kmem_zalloc(sizeof (ctr_ctx_t), kmflag)) == NULL)
#else
if ((ctr_ctx = calloc(1, sizeof (ctr_ctx_t))) == NULL)
#endif
return (NULL);
ctr_ctx->ctr_flags = CTR_MODE;
return (ctr_ctx);
}