summaryrefslogtreecommitdiff
path: root/usr/src/common/crypto/modes/modes.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/common/crypto/modes/modes.c')
-rw-r--r--usr/src/common/crypto/modes/modes.c218
1 files changed, 216 insertions, 2 deletions
diff --git a/usr/src/common/crypto/modes/modes.c b/usr/src/common/crypto/modes/modes.c
index 19bf8cc16c..fbf66c0531 100644
--- a/usr/src/common/crypto/modes/modes.c
+++ b/usr/src/common/crypto/modes/modes.c
@@ -21,10 +21,14 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _KERNEL
#include <stdlib.h>
+#include <assert.h>
+#include <strings.h>
#endif
#include <sys/strsun.h>
@@ -170,8 +174,8 @@ crypto_free_mode_ctx(void *ctx)
{
common_ctx_t *common_ctx = (common_ctx_t *)ctx;
- switch (common_ctx->cc_flags &
- (ECB_MODE|CBC_MODE|CTR_MODE|CCM_MODE|GCM_MODE|GMAC_MODE)) {
+ 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));
@@ -181,6 +185,7 @@ crypto_free_mode_ctx(void *ctx)
break;
case CBC_MODE:
+ case CMAC_MODE:
#ifdef _KERNEL
kmem_free(common_ctx, sizeof (cbc_ctx_t));
#else
@@ -225,3 +230,212 @@ crypto_free_mode_ctx(void *ctx)
#endif
}
}
+
+/*
+ * Utility routine to apply the command, 'cmd', to the
+ * data in the uio structure.
+ */
+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);
+ }
+
+ /*
+ * Jump to the first iovec containing data to be
+ * processed.
+ */
+ 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) {
+ /*
+ * The caller specified an offset that is larger than
+ * the total size of the buffers it provided.
+ */
+ 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) {
+ /*
+ * The end of the specified iovec's was reached but
+ * the length requested could not be processed.
+ */
+ switch (cmd) {
+ case COPY_TO_DATA:
+ data->cd_length = len;
+ return (CRYPTO_BUFFER_TOO_SMALL);
+ default:
+ return (CRYPTO_DATA_LEN_RANGE);
+ }
+ }
+
+ return (CRYPTO_SUCCESS);
+}
+
+/*
+ * Utility routine to apply the command, 'cmd', to the
+ * data in the mblk structure.
+ */
+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
+ /*
+ * Jump to the first mblk_t containing data to be processed.
+ */
+ for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp);
+ offset -= MBLKL(mp), mp = mp->b_cont)
+ ;
+ if (mp == NULL) {
+ /*
+ * The caller specified an offset that is larger
+ * than the total size of the buffers it provided.
+ */
+ return (CRYPTO_DATA_LEN_RANGE);
+ }
+
+ /*
+ * Now do the processing on the mblk chain.
+ */
+ 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) {
+ /*
+ * The end of the mblk was reached but the length
+ * requested could not be processed.
+ */
+ switch (cmd) {
+ case COPY_TO_DATA:
+ data->cd_length = len;
+ return (CRYPTO_BUFFER_TOO_SMALL);
+ default:
+ return (CRYPTO_DATA_LEN_RANGE);
+ }
+ }
+
+ return (CRYPTO_SUCCESS);
+}
+
+/*
+ * Utility routine to copy a buffer to a crypto_data structure.
+ */
+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);
+}