diff options
Diffstat (limited to 'usr/src/common/crypto/modes/modes.c')
-rw-r--r-- | usr/src/common/crypto/modes/modes.c | 218 |
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); +} |