#ifndef _KERNEL
#include <stdlib.h>
#include <assert.h>
#include <strings.h>
#endif
#include <sys/strsun.h>
#include <sys/types.h>
#include <modes/modes.h>
#include <sys/crypto/common.h>
#include <sys/crypto/impl.h>
void
crypto_init_ptrs(crypto_data_t *out, void **iov_or_mp, offset_t *current_offset)
{
offset_t offset;
switch (out->cd_format) {
case CRYPTO_DATA_RAW:
*current_offset = out->cd_offset;
break;
case CRYPTO_DATA_UIO: {
uio_t *uiop = out->cd_uio;
uintptr_t vec_idx;
offset = out->cd_offset;
for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
offset >= uiop->uio_iov[vec_idx].iov_len;
offset -= uiop->uio_iov[vec_idx++].iov_len)
;
*current_offset = offset;
*iov_or_mp = (void *)vec_idx;
break;
}
case CRYPTO_DATA_MBLK: {
mblk_t *mp;
offset = out->cd_offset;
for (mp = out->cd_mp; mp != NULL && offset >= MBLKL(mp);
offset -= MBLKL(mp), mp = mp->b_cont)
;
*current_offset = offset;
*iov_or_mp = mp;
break;
}
}
}
void
crypto_get_ptrs(crypto_data_t *out, void **iov_or_mp, offset_t *current_offset,
uint8_t **out_data_1, size_t *out_data_1_len, uint8_t **out_data_2,
size_t amt)
{
offset_t offset;
switch (out->cd_format) {
case CRYPTO_DATA_RAW: {
iovec_t *iov;
offset = *current_offset;
iov = &out->cd_raw;
if ((offset + amt) <= iov->iov_len) {
*out_data_1 = (uint8_t *)iov->iov_base + offset;
*out_data_1_len = amt;
*out_data_2 = NULL;
*current_offset = offset + amt;
}
break;
}
case CRYPTO_DATA_UIO: {
uio_t *uio = out->cd_uio;
iovec_t *iov;
offset_t offset;
uintptr_t vec_idx;
uint8_t *p;
offset = *current_offset;
vec_idx = (uintptr_t)(*iov_or_mp);
iov = &uio->uio_iov[vec_idx];
p = (uint8_t *)iov->iov_base + offset;
*out_data_1 = p;
if (offset + amt <= iov->iov_len) {
*out_data_1_len = amt;
*out_data_2 = NULL;
*current_offset = offset + amt;
} else {
*out_data_1_len = iov->iov_len - offset;
if (vec_idx == uio->uio_iovcnt)
return;
vec_idx++;
iov = &uio->uio_iov[vec_idx];
*out_data_2 = (uint8_t *)iov->iov_base;
*current_offset = amt - *out_data_1_len;
}
*iov_or_mp = (void *)vec_idx;
break;
}
case CRYPTO_DATA_MBLK: {
mblk_t *mp;
uint8_t *p;
offset = *current_offset;
mp = (mblk_t *)*iov_or_mp;
p = mp->b_rptr + offset;
*out_data_1 = p;
if ((p + amt) <= mp->b_wptr) {
*out_data_1_len = amt;
*out_data_2 = NULL;
*current_offset = offset + amt;
} else {
*out_data_1_len = _PTRDIFF(mp->b_wptr, p);
if ((mp = mp->b_cont) == NULL)
return;
*out_data_2 = mp->b_rptr;
*current_offset = (amt - *out_data_1_len);
}
*iov_or_mp = mp;
break;
}
}
}
void
crypto_free_mode_ctx(void *ctx)
{
common_ctx_t *common_ctx = (common_ctx_t *)ctx;
switch (common_ctx->cc_flags & (ECB_MODE|CBC_MODE|CMAC_MODE|CTR_MODE|
CCM_MODE|GCM_MODE|GMAC_MODE)) {
case ECB_MODE:
#ifdef _KERNEL
kmem_free(common_ctx, sizeof (ecb_ctx_t));
#else
free(common_ctx);
#endif
break;
case CBC_MODE:
case CMAC_MODE:
#ifdef _KERNEL
kmem_free(common_ctx, sizeof (cbc_ctx_t));
#else
free(common_ctx);
#endif
break;
case CTR_MODE:
#ifdef _KERNEL
kmem_free(common_ctx, sizeof (ctr_ctx_t));
#else
free(common_ctx);
#endif
break;
case CCM_MODE:
#ifdef _KERNEL
if (((ccm_ctx_t *)ctx)->ccm_pt_buf != NULL)
kmem_free(((ccm_ctx_t *)ctx)->ccm_pt_buf,
((ccm_ctx_t *)ctx)->ccm_data_len);
kmem_free(ctx, sizeof (ccm_ctx_t));
#else
if (((ccm_ctx_t *)ctx)->ccm_pt_buf != NULL)
free(((ccm_ctx_t *)ctx)->ccm_pt_buf);
free(ctx);
#endif
break;
case GCM_MODE:
case GMAC_MODE:
#ifdef _KERNEL
if (((gcm_ctx_t *)ctx)->gcm_pt_buf != NULL)
kmem_free(((gcm_ctx_t *)ctx)->gcm_pt_buf,
((gcm_ctx_t *)ctx)->gcm_pt_buf_len);
kmem_free(ctx, sizeof (gcm_ctx_t));
#else
if (((gcm_ctx_t *)ctx)->gcm_pt_buf != NULL)
free(((gcm_ctx_t *)ctx)->gcm_pt_buf);
free(ctx);
#endif
}
}
int
crypto_uio_data(crypto_data_t *data, uchar_t *buf, int len, cmd_type_t cmd,
void *digest_ctx, void (*update)())
{
uio_t *uiop = data->cd_uio;
off_t offset = data->cd_offset;
size_t length = len;
uint_t vec_idx;
size_t cur_len;
uchar_t *datap;
#ifdef _KERNEL
ASSERT3U(data->cd_format, ==, CRYPTO_DATA_UIO);
#else
assert(data->cd_format == CRYPTO_DATA_UIO);
#endif
if (uiop->uio_segflg != UIO_SYSSPACE) {
return (CRYPTO_ARGUMENTS_BAD);
}
for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
offset >= uiop->uio_iov[vec_idx].iov_len;
offset -= uiop->uio_iov[vec_idx++].iov_len)
;
if (vec_idx == uiop->uio_iovcnt && length > 0) {
return (CRYPTO_DATA_LEN_RANGE);
}
while (vec_idx < uiop->uio_iovcnt && length > 0) {
cur_len = MIN(uiop->uio_iov[vec_idx].iov_len -
offset, length);
datap = (uchar_t *)(uiop->uio_iov[vec_idx].iov_base +
offset);
switch (cmd) {
case COPY_FROM_DATA:
bcopy(datap, buf, cur_len);
buf += cur_len;
break;
case COPY_TO_DATA:
bcopy(buf, datap, cur_len);
buf += cur_len;
break;
case COMPARE_TO_DATA:
if (bcmp(datap, buf, cur_len))
return (CRYPTO_SIGNATURE_INVALID);
buf += cur_len;
break;
case MD5_DIGEST_DATA:
case SHA1_DIGEST_DATA:
case SHA2_DIGEST_DATA:
case GHASH_DATA:
update(digest_ctx, datap, cur_len);
break;
}
length -= cur_len;
vec_idx++;
offset = 0;
}
if (vec_idx == uiop->uio_iovcnt && length > 0) {
switch (cmd) {
case COPY_TO_DATA:
data->cd_length = len;
return (CRYPTO_BUFFER_TOO_SMALL);
default:
return (CRYPTO_DATA_LEN_RANGE);
}
}
return (CRYPTO_SUCCESS);
}
int
crypto_mblk_data(crypto_data_t *data, uchar_t *buf, int len, cmd_type_t cmd,
void *digest_ctx, void (*update)())
{
off_t offset = data->cd_offset;
size_t length = len;
mblk_t *mp;
size_t cur_len;
uchar_t *datap;
#ifdef _KERNEL
ASSERT3U(data->cd_format, ==, CRYPTO_DATA_MBLK);
#else
assert(data->cd_format == CRYPTO_DATA_MBLK);
#endif
for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp);
offset -= MBLKL(mp), mp = mp->b_cont)
;
if (mp == NULL) {
return (CRYPTO_DATA_LEN_RANGE);
}
while (mp != NULL && length > 0) {
cur_len = MIN(MBLKL(mp) - offset, length);
datap = (uchar_t *)(mp->b_rptr + offset);
switch (cmd) {
case COPY_FROM_DATA:
bcopy(datap, buf, cur_len);
buf += cur_len;
break;
case COPY_TO_DATA:
bcopy(buf, datap, cur_len);
buf += cur_len;
break;
case COMPARE_TO_DATA:
if (bcmp(datap, buf, cur_len))
return (CRYPTO_SIGNATURE_INVALID);
buf += cur_len;
break;
case MD5_DIGEST_DATA:
case SHA1_DIGEST_DATA:
case SHA2_DIGEST_DATA:
case GHASH_DATA:
update(digest_ctx, datap, cur_len);
break;
}
length -= cur_len;
offset = 0;
mp = mp->b_cont;
}
if (mp == NULL && length > 0) {
switch (cmd) {
case COPY_TO_DATA:
data->cd_length = len;
return (CRYPTO_BUFFER_TOO_SMALL);
default:
return (CRYPTO_DATA_LEN_RANGE);
}
}
return (CRYPTO_SUCCESS);
}
int
crypto_put_output_data(uchar_t *buf, crypto_data_t *output, int len)
{
switch (output->cd_format) {
case CRYPTO_DATA_RAW:
if (MAXOFF_T - output->cd_offset < (off_t)len) {
return (CRYPTO_ARGUMENTS_BAD);
}
if (output->cd_raw.iov_len < len + output->cd_offset) {
output->cd_length = len;
return (CRYPTO_BUFFER_TOO_SMALL);
}
bcopy(buf, (uchar_t *)(output->cd_raw.iov_base +
output->cd_offset), len);
break;
case CRYPTO_DATA_UIO:
return (crypto_uio_data(output, buf, len,
COPY_TO_DATA, NULL, NULL));
case CRYPTO_DATA_MBLK:
return (crypto_mblk_data(output, buf, len,
COPY_TO_DATA, NULL, NULL));
default:
return (CRYPTO_ARGUMENTS_BAD);
}
return (CRYPTO_SUCCESS);
}