xref: /onnv-gate/usr/src/uts/common/crypto/io/md5_mod.c (revision 5072:a2b6f90cc61e)
11694Sdarrenm /*
21694Sdarrenm  * CDDL HEADER START
31694Sdarrenm  *
41694Sdarrenm  * The contents of this file are subject to the terms of the
51694Sdarrenm  * Common Development and Distribution License (the "License").
61694Sdarrenm  * You may not use this file except in compliance with the License.
71694Sdarrenm  *
81694Sdarrenm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91694Sdarrenm  * or http://www.opensolaris.org/os/licensing.
101694Sdarrenm  * See the License for the specific language governing permissions
111694Sdarrenm  * and limitations under the License.
121694Sdarrenm  *
131694Sdarrenm  * When distributing Covered Code, include this CDDL HEADER in each
141694Sdarrenm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151694Sdarrenm  * If applicable, add the following below this CDDL HEADER, with the
161694Sdarrenm  * fields enclosed by brackets "[]" replaced with your own identifying
171694Sdarrenm  * information: Portions Copyright [yyyy] [name of copyright owner]
181694Sdarrenm  *
191694Sdarrenm  * CDDL HEADER END
201694Sdarrenm  */
211694Sdarrenm 
221694Sdarrenm /*
23*5072Smcpowers  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
241694Sdarrenm  * Use is subject to license terms.
251694Sdarrenm  */
261694Sdarrenm #pragma ident	"%Z%%M%	%I%	%E% SMI"
271694Sdarrenm 
281694Sdarrenm /*
291694Sdarrenm  * In kernel module, the md5 module is created with two modlinkages:
301694Sdarrenm  * - a modlmisc that allows consumers to directly call the entry points
311694Sdarrenm  *   MD5Init, MD5Update, and MD5Final.
321694Sdarrenm  * - a modlcrypto that allows the module to register with the Kernel
331694Sdarrenm  *   Cryptographic Framework (KCF) as a software provider for the MD5
341694Sdarrenm  *   mechanisms.
351694Sdarrenm  */
361694Sdarrenm 
371694Sdarrenm #include <sys/types.h>
381694Sdarrenm #include <sys/systm.h>
391694Sdarrenm #include <sys/modctl.h>
401694Sdarrenm #include <sys/cmn_err.h>
411694Sdarrenm #include <sys/ddi.h>
421694Sdarrenm #include <sys/crypto/common.h>
431694Sdarrenm #include <sys/crypto/spi.h>
441694Sdarrenm #include <sys/sysmacros.h>
451694Sdarrenm #include <sys/strsun.h>
461694Sdarrenm #include <sys/note.h>
471694Sdarrenm #include <sys/md5.h>
481694Sdarrenm 
491694Sdarrenm extern struct mod_ops mod_miscops;
501694Sdarrenm extern struct mod_ops mod_cryptoops;
511694Sdarrenm 
521694Sdarrenm /*
531694Sdarrenm  * Module linkage information for the kernel.
541694Sdarrenm  */
551694Sdarrenm 
561694Sdarrenm static struct modlmisc modlmisc = {
571694Sdarrenm 	&mod_miscops,
581694Sdarrenm 	"MD5 Message-Digest Algorithm"
591694Sdarrenm };
601694Sdarrenm 
611694Sdarrenm static struct modlcrypto modlcrypto = {
621694Sdarrenm 	&mod_cryptoops,
63*5072Smcpowers 	"MD5 Kernel SW Provider"
641694Sdarrenm };
651694Sdarrenm 
661694Sdarrenm static struct modlinkage modlinkage = {
671694Sdarrenm 	MODREV_1,
681694Sdarrenm 	(void *)&modlmisc,
691694Sdarrenm 	(void *)&modlcrypto,
701694Sdarrenm 	NULL
711694Sdarrenm };
721694Sdarrenm 
731694Sdarrenm /*
741694Sdarrenm  * CSPI information (entry points, provider info, etc.)
751694Sdarrenm  */
761694Sdarrenm 
771694Sdarrenm typedef enum md5_mech_type {
781694Sdarrenm 	MD5_MECH_INFO_TYPE,		/* SUN_CKM_MD5 */
791694Sdarrenm 	MD5_HMAC_MECH_INFO_TYPE,	/* SUN_CKM_MD5_HMAC */
801694Sdarrenm 	MD5_HMAC_GEN_MECH_INFO_TYPE	/* SUN_CKM_MD5_HMAC_GENERAL */
811694Sdarrenm } md5_mech_type_t;
821694Sdarrenm 
831694Sdarrenm #define	MD5_DIGEST_LENGTH	16	/* MD5 digest length in bytes */
841694Sdarrenm #define	MD5_HMAC_BLOCK_SIZE	64	/* MD5 block size */
851694Sdarrenm #define	MD5_HMAC_MIN_KEY_LEN	8	/* MD5-HMAC min key length in bits */
861694Sdarrenm #define	MD5_HMAC_MAX_KEY_LEN	INT_MAX	/* MD5-HMAC max key length in bits */
871694Sdarrenm #define	MD5_HMAC_INTS_PER_BLOCK	(MD5_HMAC_BLOCK_SIZE/sizeof (uint32_t))
881694Sdarrenm 
891694Sdarrenm /*
901694Sdarrenm  * Context for MD5 mechanism.
911694Sdarrenm  */
921694Sdarrenm typedef struct md5_ctx {
931694Sdarrenm 	md5_mech_type_t		mc_mech_type;	/* type of context */
941694Sdarrenm 	MD5_CTX			mc_md5_ctx;	/* MD5 context */
951694Sdarrenm } md5_ctx_t;
961694Sdarrenm 
971694Sdarrenm /*
981694Sdarrenm  * Context for MD5-HMAC and MD5-HMAC-GENERAL mechanisms.
991694Sdarrenm  */
1001694Sdarrenm typedef struct md5_hmac_ctx {
1011694Sdarrenm 	md5_mech_type_t		hc_mech_type;	/* type of context */
1021694Sdarrenm 	uint32_t		hc_digest_len;	/* digest len in bytes */
1031694Sdarrenm 	MD5_CTX			hc_icontext;	/* inner MD5 context */
1041694Sdarrenm 	MD5_CTX			hc_ocontext;	/* outer MD5 context */
1051694Sdarrenm } md5_hmac_ctx_t;
1061694Sdarrenm 
1071694Sdarrenm /*
1081694Sdarrenm  * Macros to access the MD5 or MD5-HMAC contexts from a context passed
1091694Sdarrenm  * by KCF to one of the entry points.
1101694Sdarrenm  */
1111694Sdarrenm 
1121694Sdarrenm #define	PROV_MD5_CTX(ctx)	((md5_ctx_t *)(ctx)->cc_provider_private)
1131694Sdarrenm #define	PROV_MD5_HMAC_CTX(ctx)	((md5_hmac_ctx_t *)(ctx)->cc_provider_private)
1141694Sdarrenm /* to extract the digest length passed as mechanism parameter */
1151694Sdarrenm 
1161694Sdarrenm #define	PROV_MD5_GET_DIGEST_LEN(m, len) {				\
1171694Sdarrenm 	if (IS_P2ALIGNED((m)->cm_param, sizeof (ulong_t)))		\
1181694Sdarrenm 		(len) = (uint32_t)*((ulong_t *)mechanism->cm_param);	\
1191694Sdarrenm 	else {								\
1201694Sdarrenm 		ulong_t tmp_ulong;					\
1211694Sdarrenm 		bcopy((m)->cm_param, &tmp_ulong, sizeof (ulong_t));	\
1221694Sdarrenm 		(len) = (uint32_t)tmp_ulong;				\
1231694Sdarrenm 	}								\
1241694Sdarrenm }
1251694Sdarrenm 
1261694Sdarrenm #define	PROV_MD5_DIGEST_KEY(ctx, key, len, digest) {	\
1271694Sdarrenm 	MD5Init(ctx);					\
1281694Sdarrenm 	MD5Update(ctx, key, len);			\
1291694Sdarrenm 	MD5Final(digest, ctx);				\
1301694Sdarrenm }
1311694Sdarrenm 
1321694Sdarrenm /*
1331694Sdarrenm  * Mechanism info structure passed to KCF during registration.
1341694Sdarrenm  */
1351694Sdarrenm static crypto_mech_info_t md5_mech_info_tab[] = {
1361694Sdarrenm 	/* MD5 */
1371694Sdarrenm 	{SUN_CKM_MD5, MD5_MECH_INFO_TYPE,
1381694Sdarrenm 	    CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC,
1391694Sdarrenm 	    0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
1401694Sdarrenm 	/* MD5-HMAC */
1411694Sdarrenm 	{SUN_CKM_MD5_HMAC, MD5_HMAC_MECH_INFO_TYPE,
1421694Sdarrenm 	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC,
1431694Sdarrenm 	    MD5_HMAC_MIN_KEY_LEN, MD5_HMAC_MAX_KEY_LEN,
1441694Sdarrenm 	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
1451694Sdarrenm 	/* MD5-HMAC GENERAL */
1461694Sdarrenm 	{SUN_CKM_MD5_HMAC_GENERAL, MD5_HMAC_GEN_MECH_INFO_TYPE,
1471694Sdarrenm 	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC,
1481694Sdarrenm 	    MD5_HMAC_MIN_KEY_LEN, MD5_HMAC_MAX_KEY_LEN,
1491694Sdarrenm 	    CRYPTO_KEYSIZE_UNIT_IN_BITS}
1501694Sdarrenm };
1511694Sdarrenm 
1521694Sdarrenm static void md5_provider_status(crypto_provider_handle_t, uint_t *);
1531694Sdarrenm 
1541694Sdarrenm static crypto_control_ops_t md5_control_ops = {
1551694Sdarrenm 	md5_provider_status
1561694Sdarrenm };
1571694Sdarrenm 
1581694Sdarrenm static int md5_digest_init(crypto_ctx_t *, crypto_mechanism_t *,
1591694Sdarrenm     crypto_req_handle_t);
1601694Sdarrenm static int md5_digest(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
1611694Sdarrenm     crypto_req_handle_t);
1621694Sdarrenm static int md5_digest_update(crypto_ctx_t *, crypto_data_t *,
1631694Sdarrenm     crypto_req_handle_t);
1641694Sdarrenm static int md5_digest_final(crypto_ctx_t *, crypto_data_t *,
1651694Sdarrenm     crypto_req_handle_t);
1661694Sdarrenm static int md5_digest_atomic(crypto_provider_handle_t, crypto_session_id_t,
1671694Sdarrenm     crypto_mechanism_t *, crypto_data_t *, crypto_data_t *,
1681694Sdarrenm     crypto_req_handle_t);
1691694Sdarrenm 
1701694Sdarrenm static crypto_digest_ops_t md5_digest_ops = {
1711694Sdarrenm 	md5_digest_init,
1721694Sdarrenm 	md5_digest,
1731694Sdarrenm 	md5_digest_update,
1741694Sdarrenm 	NULL,
1751694Sdarrenm 	md5_digest_final,
1761694Sdarrenm 	md5_digest_atomic
1771694Sdarrenm };
1781694Sdarrenm 
1791694Sdarrenm static int md5_mac_init(crypto_ctx_t *, crypto_mechanism_t *, crypto_key_t *,
1801694Sdarrenm     crypto_spi_ctx_template_t, crypto_req_handle_t);
1811694Sdarrenm static int md5_mac_update(crypto_ctx_t *, crypto_data_t *, crypto_req_handle_t);
1821694Sdarrenm static int md5_mac_final(crypto_ctx_t *, crypto_data_t *, crypto_req_handle_t);
1831694Sdarrenm static int md5_mac_atomic(crypto_provider_handle_t, crypto_session_id_t,
1841694Sdarrenm     crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
1851694Sdarrenm     crypto_spi_ctx_template_t, crypto_req_handle_t);
1861694Sdarrenm static int md5_mac_verify_atomic(crypto_provider_handle_t, crypto_session_id_t,
1871694Sdarrenm     crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
1881694Sdarrenm     crypto_spi_ctx_template_t, crypto_req_handle_t);
1891694Sdarrenm 
1901694Sdarrenm static crypto_mac_ops_t md5_mac_ops = {
1911694Sdarrenm 	md5_mac_init,
1921694Sdarrenm 	NULL,
1931694Sdarrenm 	md5_mac_update,
1941694Sdarrenm 	md5_mac_final,
1951694Sdarrenm 	md5_mac_atomic,
1961694Sdarrenm 	md5_mac_verify_atomic
1971694Sdarrenm };
1981694Sdarrenm 
1991694Sdarrenm static int md5_create_ctx_template(crypto_provider_handle_t,
2001694Sdarrenm     crypto_mechanism_t *, crypto_key_t *, crypto_spi_ctx_template_t *,
2011694Sdarrenm     size_t *, crypto_req_handle_t);
2021694Sdarrenm static int md5_free_context(crypto_ctx_t *);
2031694Sdarrenm 
2041694Sdarrenm static crypto_ctx_ops_t md5_ctx_ops = {
2051694Sdarrenm 	md5_create_ctx_template,
2061694Sdarrenm 	md5_free_context
2071694Sdarrenm };
2081694Sdarrenm 
2091694Sdarrenm static crypto_ops_t md5_crypto_ops = {
2101694Sdarrenm 	&md5_control_ops,
2111694Sdarrenm 	&md5_digest_ops,
2121694Sdarrenm 	NULL,
2131694Sdarrenm 	&md5_mac_ops,
2141694Sdarrenm 	NULL,
2151694Sdarrenm 	NULL,
2161694Sdarrenm 	NULL,
2171694Sdarrenm 	NULL,
2181694Sdarrenm 	NULL,
2191694Sdarrenm 	NULL,
2201694Sdarrenm 	NULL,
2211694Sdarrenm 	NULL,
2221694Sdarrenm 	NULL,
2231694Sdarrenm 	&md5_ctx_ops
2241694Sdarrenm };
2251694Sdarrenm 
2261694Sdarrenm static crypto_provider_info_t md5_prov_info = {
2271694Sdarrenm 	CRYPTO_SPI_VERSION_1,
2281694Sdarrenm 	"MD5 Software Provider",
2291694Sdarrenm 	CRYPTO_SW_PROVIDER,
2301694Sdarrenm 	{&modlinkage},
2311694Sdarrenm 	NULL,
2321694Sdarrenm 	&md5_crypto_ops,
2331694Sdarrenm 	sizeof (md5_mech_info_tab)/sizeof (crypto_mech_info_t),
2341694Sdarrenm 	md5_mech_info_tab
2351694Sdarrenm };
2361694Sdarrenm 
2371694Sdarrenm static crypto_kcf_provider_handle_t md5_prov_handle = NULL;
2381694Sdarrenm 
2391694Sdarrenm int
2401694Sdarrenm _init(void)
2411694Sdarrenm {
2421694Sdarrenm 	int ret;
2431694Sdarrenm 
2441694Sdarrenm 	if ((ret = mod_install(&modlinkage)) != 0)
2451694Sdarrenm 		return (ret);
2461694Sdarrenm 
2471694Sdarrenm 	/*
2481694Sdarrenm 	 * Register with KCF. If the registration fails, log an
2491694Sdarrenm 	 * error but do not uninstall the module, since the functionality
2501694Sdarrenm 	 * provided by misc/md5 should still be available.
2511694Sdarrenm 	 */
2521694Sdarrenm 	if ((ret = crypto_register_provider(&md5_prov_info,
2531694Sdarrenm 	    &md5_prov_handle)) != CRYPTO_SUCCESS)
2541694Sdarrenm 		cmn_err(CE_WARN, "md5 _init: "
2551694Sdarrenm 		    "crypto_register_provider() failed (0x%x)", ret);
2561694Sdarrenm 
2571694Sdarrenm 	return (0);
2581694Sdarrenm }
2591694Sdarrenm 
2601694Sdarrenm int
2611694Sdarrenm _fini(void)
2621694Sdarrenm {
2631694Sdarrenm 	int ret;
2641694Sdarrenm 
2651694Sdarrenm 	/*
2661694Sdarrenm 	 * Unregister from KCF if previous registration succeeded.
2671694Sdarrenm 	 */
2681694Sdarrenm 	if (md5_prov_handle != NULL) {
2691694Sdarrenm 		if ((ret = crypto_unregister_provider(md5_prov_handle)) !=
2701694Sdarrenm 		    CRYPTO_SUCCESS) {
2711694Sdarrenm 			cmn_err(CE_WARN, "md5 _fini: "
2721694Sdarrenm 			    "crypto_unregister_provider() failed (0x%x)", ret);
2731694Sdarrenm 			return (EBUSY);
2741694Sdarrenm 		}
2751694Sdarrenm 		md5_prov_handle = NULL;
2761694Sdarrenm 	}
2771694Sdarrenm 
2781694Sdarrenm 	return (mod_remove(&modlinkage));
2791694Sdarrenm }
2801694Sdarrenm 
2811694Sdarrenm int
2821694Sdarrenm _info(struct modinfo *modinfop)
2831694Sdarrenm {
2841694Sdarrenm 	return (mod_info(&modlinkage, modinfop));
2851694Sdarrenm }
2861694Sdarrenm 
2871694Sdarrenm /*
2881694Sdarrenm  * KCF software provider control entry points.
2891694Sdarrenm  */
2901694Sdarrenm /* ARGSUSED */
2911694Sdarrenm static void
2921694Sdarrenm md5_provider_status(crypto_provider_handle_t provider, uint_t *status)
2931694Sdarrenm {
2941694Sdarrenm 	*status = CRYPTO_PROVIDER_READY;
2951694Sdarrenm }
2961694Sdarrenm 
2971694Sdarrenm /*
2981694Sdarrenm  * KCF software provider digest entry points.
2991694Sdarrenm  */
3001694Sdarrenm 
3011694Sdarrenm static int
3021694Sdarrenm md5_digest_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
3031694Sdarrenm     crypto_req_handle_t req)
3041694Sdarrenm {
3051694Sdarrenm 	if (mechanism->cm_type != MD5_MECH_INFO_TYPE)
3061694Sdarrenm 		return (CRYPTO_MECHANISM_INVALID);
3071694Sdarrenm 
3081694Sdarrenm 	/*
3091694Sdarrenm 	 * Allocate and initialize MD5 context.
3101694Sdarrenm 	 */
3111694Sdarrenm 	ctx->cc_provider_private = kmem_alloc(sizeof (md5_ctx_t),
3121694Sdarrenm 	    crypto_kmflag(req));
3131694Sdarrenm 	if (ctx->cc_provider_private == NULL)
3141694Sdarrenm 		return (CRYPTO_HOST_MEMORY);
3151694Sdarrenm 
3161694Sdarrenm 	PROV_MD5_CTX(ctx)->mc_mech_type = MD5_MECH_INFO_TYPE;
3171694Sdarrenm 	MD5Init(&PROV_MD5_CTX(ctx)->mc_md5_ctx);
3181694Sdarrenm 
3191694Sdarrenm 	return (CRYPTO_SUCCESS);
3201694Sdarrenm }
3211694Sdarrenm 
3221694Sdarrenm /*
3231694Sdarrenm  * Helper MD5 digest update function for uio data.
3241694Sdarrenm  */
3251694Sdarrenm static int
3261694Sdarrenm md5_digest_update_uio(MD5_CTX *md5_ctx, crypto_data_t *data)
3271694Sdarrenm {
3281694Sdarrenm 	off_t offset = data->cd_offset;
3291694Sdarrenm 	size_t length = data->cd_length;
3301694Sdarrenm 	uint_t vec_idx;
3311694Sdarrenm 	size_t cur_len;
3321694Sdarrenm 
3331694Sdarrenm 	/* we support only kernel buffer */
3341694Sdarrenm 	if (data->cd_uio->uio_segflg != UIO_SYSSPACE)
3351694Sdarrenm 		return (CRYPTO_ARGUMENTS_BAD);
3361694Sdarrenm 
3371694Sdarrenm 	/*
3381694Sdarrenm 	 * Jump to the first iovec containing data to be
3391694Sdarrenm 	 * digested.
3401694Sdarrenm 	 */
3411694Sdarrenm 	for (vec_idx = 0; vec_idx < data->cd_uio->uio_iovcnt &&
3421694Sdarrenm 	    offset >= data->cd_uio->uio_iov[vec_idx].iov_len;
343*5072Smcpowers 	    offset -= data->cd_uio->uio_iov[vec_idx++].iov_len)
344*5072Smcpowers 		;
3451694Sdarrenm 	if (vec_idx == data->cd_uio->uio_iovcnt) {
3461694Sdarrenm 		/*
3471694Sdarrenm 		 * The caller specified an offset that is larger than the
3481694Sdarrenm 		 * total size of the buffers it provided.
3491694Sdarrenm 		 */
3501694Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
3511694Sdarrenm 	}
3521694Sdarrenm 
3531694Sdarrenm 	/*
3541694Sdarrenm 	 * Now do the digesting on the iovecs.
3551694Sdarrenm 	 */
3561694Sdarrenm 	while (vec_idx < data->cd_uio->uio_iovcnt && length > 0) {
3571694Sdarrenm 		cur_len = MIN(data->cd_uio->uio_iov[vec_idx].iov_len -
3581694Sdarrenm 		    offset, length);
3591694Sdarrenm 
3601694Sdarrenm 		MD5Update(md5_ctx, data->cd_uio->uio_iov[vec_idx].iov_base +
3611694Sdarrenm 		    offset, cur_len);
3621694Sdarrenm 
3631694Sdarrenm 		length -= cur_len;
3641694Sdarrenm 		vec_idx++;
3651694Sdarrenm 		offset = 0;
3661694Sdarrenm 	}
3671694Sdarrenm 
3681694Sdarrenm 	if (vec_idx == data->cd_uio->uio_iovcnt && length > 0) {
3691694Sdarrenm 		/*
3701694Sdarrenm 		 * The end of the specified iovec's was reached but
3711694Sdarrenm 		 * the length requested could not be processed, i.e.
3721694Sdarrenm 		 * The caller requested to digest more data than it provided.
3731694Sdarrenm 		 */
3741694Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
3751694Sdarrenm 	}
3761694Sdarrenm 
3771694Sdarrenm 	return (CRYPTO_SUCCESS);
3781694Sdarrenm }
3791694Sdarrenm 
3801694Sdarrenm /*
3811694Sdarrenm  * Helper MD5 digest final function for uio data.
3821694Sdarrenm  * digest_len is the length of the desired digest. If digest_len
3831694Sdarrenm  * is smaller than the default MD5 digest length, the caller
3841694Sdarrenm  * must pass a scratch buffer, digest_scratch, which must
3851694Sdarrenm  * be at least MD5_DIGEST_LENGTH bytes.
3861694Sdarrenm  */
3871694Sdarrenm static int
3881694Sdarrenm md5_digest_final_uio(MD5_CTX *md5_ctx, crypto_data_t *digest,
3891694Sdarrenm     ulong_t digest_len, uchar_t *digest_scratch)
3901694Sdarrenm {
3911694Sdarrenm 	off_t offset = digest->cd_offset;
3921694Sdarrenm 	uint_t vec_idx;
3931694Sdarrenm 
3941694Sdarrenm 	/* we support only kernel buffer */
3951694Sdarrenm 	if (digest->cd_uio->uio_segflg != UIO_SYSSPACE)
3961694Sdarrenm 		return (CRYPTO_ARGUMENTS_BAD);
3971694Sdarrenm 
3981694Sdarrenm 	/*
3991694Sdarrenm 	 * Jump to the first iovec containing ptr to the digest to
4001694Sdarrenm 	 * be returned.
4011694Sdarrenm 	 */
4021694Sdarrenm 	for (vec_idx = 0; offset >= digest->cd_uio->uio_iov[vec_idx].iov_len &&
4031694Sdarrenm 	    vec_idx < digest->cd_uio->uio_iovcnt;
404*5072Smcpowers 	    offset -= digest->cd_uio->uio_iov[vec_idx++].iov_len)
405*5072Smcpowers 		;
4061694Sdarrenm 	if (vec_idx == digest->cd_uio->uio_iovcnt) {
4071694Sdarrenm 		/*
4081694Sdarrenm 		 * The caller specified an offset that is
4091694Sdarrenm 		 * larger than the total size of the buffers
4101694Sdarrenm 		 * it provided.
4111694Sdarrenm 		 */
4121694Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
4131694Sdarrenm 	}
4141694Sdarrenm 
4151694Sdarrenm 	if (offset + digest_len <=
4161694Sdarrenm 	    digest->cd_uio->uio_iov[vec_idx].iov_len) {
4171694Sdarrenm 		/*
4181694Sdarrenm 		 * The computed MD5 digest will fit in the current
4191694Sdarrenm 		 * iovec.
4201694Sdarrenm 		 */
4211694Sdarrenm 		if (digest_len != MD5_DIGEST_LENGTH) {
4221694Sdarrenm 			/*
4231694Sdarrenm 			 * The caller requested a short digest. Digest
4241694Sdarrenm 			 * into a scratch buffer and return to
4251694Sdarrenm 			 * the user only what was requested.
4261694Sdarrenm 			 */
4271694Sdarrenm 			MD5Final(digest_scratch, md5_ctx);
4281694Sdarrenm 			bcopy(digest_scratch, (uchar_t *)digest->
4291694Sdarrenm 			    cd_uio->uio_iov[vec_idx].iov_base + offset,
4301694Sdarrenm 			    digest_len);
4311694Sdarrenm 		} else {
4321694Sdarrenm 			MD5Final((uchar_t *)digest->
4331694Sdarrenm 			    cd_uio->uio_iov[vec_idx].iov_base + offset,
4341694Sdarrenm 			    md5_ctx);
4351694Sdarrenm 		}
4361694Sdarrenm 	} else {
4371694Sdarrenm 		/*
4381694Sdarrenm 		 * The computed digest will be crossing one or more iovec's.
4391694Sdarrenm 		 * This is bad performance-wise but we need to support it.
4401694Sdarrenm 		 * Allocate a small scratch buffer on the stack and
4411694Sdarrenm 		 * copy it piece meal to the specified digest iovec's.
4421694Sdarrenm 		 */
4431694Sdarrenm 		uchar_t digest_tmp[MD5_DIGEST_LENGTH];
4441694Sdarrenm 		off_t scratch_offset = 0;
4451694Sdarrenm 		size_t length = digest_len;
4461694Sdarrenm 		size_t cur_len;
4471694Sdarrenm 
4481694Sdarrenm 		MD5Final(digest_tmp, md5_ctx);
4491694Sdarrenm 
4501694Sdarrenm 		while (vec_idx < digest->cd_uio->uio_iovcnt && length > 0) {
4511694Sdarrenm 			cur_len = MIN(digest->cd_uio->uio_iov[vec_idx].iov_len -
4521694Sdarrenm 			    offset, length);
4531694Sdarrenm 			bcopy(digest_tmp + scratch_offset,
4541694Sdarrenm 			    digest->cd_uio->uio_iov[vec_idx].iov_base + offset,
4551694Sdarrenm 			    cur_len);
4561694Sdarrenm 
4571694Sdarrenm 			length -= cur_len;
4581694Sdarrenm 			vec_idx++;
4591694Sdarrenm 			scratch_offset += cur_len;
4601694Sdarrenm 			offset = 0;
4611694Sdarrenm 		}
4621694Sdarrenm 
4631694Sdarrenm 		if (vec_idx == digest->cd_uio->uio_iovcnt && length > 0) {
4641694Sdarrenm 			/*
4651694Sdarrenm 			 * The end of the specified iovec's was reached but
4661694Sdarrenm 			 * the length requested could not be processed, i.e.
4671694Sdarrenm 			 * The caller requested to digest more data than it
4681694Sdarrenm 			 * provided.
4691694Sdarrenm 			 */
4701694Sdarrenm 			return (CRYPTO_DATA_LEN_RANGE);
4711694Sdarrenm 		}
4721694Sdarrenm 	}
4731694Sdarrenm 
4741694Sdarrenm 	return (CRYPTO_SUCCESS);
4751694Sdarrenm }
4761694Sdarrenm 
4771694Sdarrenm /*
4781694Sdarrenm  * Helper MD5 digest update for mblk's.
4791694Sdarrenm  */
4801694Sdarrenm static int
4811694Sdarrenm md5_digest_update_mblk(MD5_CTX *md5_ctx, crypto_data_t *data)
4821694Sdarrenm {
4831694Sdarrenm 	off_t offset = data->cd_offset;
4841694Sdarrenm 	size_t length = data->cd_length;
4851694Sdarrenm 	mblk_t *mp;
4861694Sdarrenm 	size_t cur_len;
4871694Sdarrenm 
4881694Sdarrenm 	/*
4891694Sdarrenm 	 * Jump to the first mblk_t containing data to be digested.
4901694Sdarrenm 	 */
4911694Sdarrenm 	for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp);
492*5072Smcpowers 	    offset -= MBLKL(mp), mp = mp->b_cont)
493*5072Smcpowers 		;
4941694Sdarrenm 	if (mp == NULL) {
4951694Sdarrenm 		/*
4961694Sdarrenm 		 * The caller specified an offset that is larger than the
4971694Sdarrenm 		 * total size of the buffers it provided.
4981694Sdarrenm 		 */
4991694Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
5001694Sdarrenm 	}
5011694Sdarrenm 
5021694Sdarrenm 	/*
5031694Sdarrenm 	 * Now do the digesting on the mblk chain.
5041694Sdarrenm 	 */
5051694Sdarrenm 	while (mp != NULL && length > 0) {
5061694Sdarrenm 		cur_len = MIN(MBLKL(mp) - offset, length);
5071694Sdarrenm 		MD5Update(md5_ctx, mp->b_rptr + offset, cur_len);
5081694Sdarrenm 		length -= cur_len;
5091694Sdarrenm 		offset = 0;
5101694Sdarrenm 		mp = mp->b_cont;
5111694Sdarrenm 	}
5121694Sdarrenm 
5131694Sdarrenm 	if (mp == NULL && length > 0) {
5141694Sdarrenm 		/*
5151694Sdarrenm 		 * The end of the mblk was reached but the length requested
5161694Sdarrenm 		 * could not be processed, i.e. The caller requested
5171694Sdarrenm 		 * to digest more data than it provided.
5181694Sdarrenm 		 */
5191694Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
5201694Sdarrenm 	}
5211694Sdarrenm 
5221694Sdarrenm 	return (CRYPTO_SUCCESS);
5231694Sdarrenm }
5241694Sdarrenm 
5251694Sdarrenm /*
5261694Sdarrenm  * Helper MD5 digest final for mblk's.
5271694Sdarrenm  * digest_len is the length of the desired digest. If digest_len
5281694Sdarrenm  * is smaller than the default MD5 digest length, the caller
5291694Sdarrenm  * must pass a scratch buffer, digest_scratch, which must
5301694Sdarrenm  * be at least MD5_DIGEST_LENGTH bytes.
5311694Sdarrenm  */
5321694Sdarrenm static int
5331694Sdarrenm md5_digest_final_mblk(MD5_CTX *md5_ctx, crypto_data_t *digest,
5341694Sdarrenm     ulong_t digest_len, uchar_t *digest_scratch)
5351694Sdarrenm {
5361694Sdarrenm 	off_t offset = digest->cd_offset;
5371694Sdarrenm 	mblk_t *mp;
5381694Sdarrenm 
5391694Sdarrenm 	/*
5401694Sdarrenm 	 * Jump to the first mblk_t that will be used to store the digest.
5411694Sdarrenm 	 */
5421694Sdarrenm 	for (mp = digest->cd_mp; mp != NULL && offset >= MBLKL(mp);
543*5072Smcpowers 	    offset -= MBLKL(mp), mp = mp->b_cont)
544*5072Smcpowers 		;
5451694Sdarrenm 	if (mp == NULL) {
5461694Sdarrenm 		/*
5471694Sdarrenm 		 * The caller specified an offset that is larger than the
5481694Sdarrenm 		 * total size of the buffers it provided.
5491694Sdarrenm 		 */
5501694Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
5511694Sdarrenm 	}
5521694Sdarrenm 
5531694Sdarrenm 	if (offset + digest_len <= MBLKL(mp)) {
5541694Sdarrenm 		/*
5551694Sdarrenm 		 * The computed MD5 digest will fit in the current mblk.
5561694Sdarrenm 		 * Do the MD5Final() in-place.
5571694Sdarrenm 		 */
5581694Sdarrenm 		if (digest_len != MD5_DIGEST_LENGTH) {
5591694Sdarrenm 			/*
5601694Sdarrenm 			 * The caller requested a short digest. Digest
5611694Sdarrenm 			 * into a scratch buffer and return to
5621694Sdarrenm 			 * the user only what was requested.
5631694Sdarrenm 			 */
5641694Sdarrenm 			MD5Final(digest_scratch, md5_ctx);
5651694Sdarrenm 			bcopy(digest_scratch, mp->b_rptr + offset, digest_len);
5661694Sdarrenm 		} else {
5671694Sdarrenm 			MD5Final(mp->b_rptr + offset, md5_ctx);
5681694Sdarrenm 		}
5691694Sdarrenm 	} else {
5701694Sdarrenm 		/*
5711694Sdarrenm 		 * The computed digest will be crossing one or more mblk's.
5721694Sdarrenm 		 * This is bad performance-wise but we need to support it.
5731694Sdarrenm 		 * Allocate a small scratch buffer on the stack and
5741694Sdarrenm 		 * copy it piece meal to the specified digest iovec's.
5751694Sdarrenm 		 */
5761694Sdarrenm 		uchar_t digest_tmp[MD5_DIGEST_LENGTH];
5771694Sdarrenm 		off_t scratch_offset = 0;
5781694Sdarrenm 		size_t length = digest_len;
5791694Sdarrenm 		size_t cur_len;
5801694Sdarrenm 
5811694Sdarrenm 		MD5Final(digest_tmp, md5_ctx);
5821694Sdarrenm 
5831694Sdarrenm 		while (mp != NULL && length > 0) {
5841694Sdarrenm 			cur_len = MIN(MBLKL(mp) - offset, length);
5851694Sdarrenm 			bcopy(digest_tmp + scratch_offset,
5861694Sdarrenm 			    mp->b_rptr + offset, cur_len);
5871694Sdarrenm 
5881694Sdarrenm 			length -= cur_len;
5891694Sdarrenm 			mp = mp->b_cont;
5901694Sdarrenm 			scratch_offset += cur_len;
5911694Sdarrenm 			offset = 0;
5921694Sdarrenm 		}
5931694Sdarrenm 
5941694Sdarrenm 		if (mp == NULL && length > 0) {
5951694Sdarrenm 			/*
5961694Sdarrenm 			 * The end of the specified mblk was reached but
5971694Sdarrenm 			 * the length requested could not be processed, i.e.
5981694Sdarrenm 			 * The caller requested to digest more data than it
5991694Sdarrenm 			 * provided.
6001694Sdarrenm 			 */
6011694Sdarrenm 			return (CRYPTO_DATA_LEN_RANGE);
6021694Sdarrenm 		}
6031694Sdarrenm 	}
6041694Sdarrenm 
6051694Sdarrenm 	return (CRYPTO_SUCCESS);
6061694Sdarrenm }
6071694Sdarrenm 
6081694Sdarrenm /* ARGSUSED */
6091694Sdarrenm static int
6101694Sdarrenm md5_digest(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *digest,
6111694Sdarrenm     crypto_req_handle_t req)
6121694Sdarrenm {
6131694Sdarrenm 	int ret = CRYPTO_SUCCESS;
6141694Sdarrenm 
6151694Sdarrenm 	ASSERT(ctx->cc_provider_private != NULL);
6161694Sdarrenm 
6171694Sdarrenm 	/*
6181694Sdarrenm 	 * We need to just return the length needed to store the output.
6191694Sdarrenm 	 * We should not destroy the context for the following cases.
6201694Sdarrenm 	 */
6211694Sdarrenm 	if ((digest->cd_length == 0) ||
6221694Sdarrenm 	    (digest->cd_length < MD5_DIGEST_LENGTH)) {
6231694Sdarrenm 		digest->cd_length = MD5_DIGEST_LENGTH;
6241694Sdarrenm 		return (CRYPTO_BUFFER_TOO_SMALL);
6251694Sdarrenm 	}
6261694Sdarrenm 
6271694Sdarrenm 	/*
6281694Sdarrenm 	 * Do the MD5 update on the specified input data.
6291694Sdarrenm 	 */
6301694Sdarrenm 	switch (data->cd_format) {
6311694Sdarrenm 	case CRYPTO_DATA_RAW:
6321694Sdarrenm 		MD5Update(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
6331694Sdarrenm 		    data->cd_raw.iov_base + data->cd_offset,
6341694Sdarrenm 		    data->cd_length);
6351694Sdarrenm 		break;
6361694Sdarrenm 	case CRYPTO_DATA_UIO:
6371694Sdarrenm 		ret = md5_digest_update_uio(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
6381694Sdarrenm 		    data);
6391694Sdarrenm 		break;
6401694Sdarrenm 	case CRYPTO_DATA_MBLK:
6411694Sdarrenm 		ret = md5_digest_update_mblk(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
6421694Sdarrenm 		    data);
6431694Sdarrenm 		break;
6441694Sdarrenm 	default:
6451694Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
6461694Sdarrenm 	}
6471694Sdarrenm 
6481694Sdarrenm 	if (ret != CRYPTO_SUCCESS) {
6491694Sdarrenm 		/* the update failed, free context and bail */
6501694Sdarrenm 		kmem_free(ctx->cc_provider_private, sizeof (md5_ctx_t));
6511694Sdarrenm 		ctx->cc_provider_private = NULL;
6521694Sdarrenm 		digest->cd_length = 0;
6531694Sdarrenm 		return (ret);
6541694Sdarrenm 	}
6551694Sdarrenm 
6561694Sdarrenm 	/*
6571694Sdarrenm 	 * Do an MD5 final, must be done separately since the digest
6581694Sdarrenm 	 * type can be different than the input data type.
6591694Sdarrenm 	 */
6601694Sdarrenm 	switch (digest->cd_format) {
6611694Sdarrenm 	case CRYPTO_DATA_RAW:
6621694Sdarrenm 		MD5Final((unsigned char *)digest->cd_raw.iov_base +
6631694Sdarrenm 		    digest->cd_offset, &PROV_MD5_CTX(ctx)->mc_md5_ctx);
6641694Sdarrenm 		break;
6651694Sdarrenm 	case CRYPTO_DATA_UIO:
6661694Sdarrenm 		ret = md5_digest_final_uio(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
6671694Sdarrenm 		    digest, MD5_DIGEST_LENGTH, NULL);
6681694Sdarrenm 		break;
6691694Sdarrenm 	case CRYPTO_DATA_MBLK:
6701694Sdarrenm 		ret = md5_digest_final_mblk(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
6711694Sdarrenm 		    digest, MD5_DIGEST_LENGTH, NULL);
6721694Sdarrenm 		break;
6731694Sdarrenm 	default:
6741694Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
6751694Sdarrenm 	}
6761694Sdarrenm 
6771694Sdarrenm 	/* all done, free context and return */
6781694Sdarrenm 
6791694Sdarrenm 	if (ret == CRYPTO_SUCCESS) {
6801694Sdarrenm 		digest->cd_length = MD5_DIGEST_LENGTH;
6811694Sdarrenm 	} else {
6821694Sdarrenm 		digest->cd_length = 0;
6831694Sdarrenm 	}
6841694Sdarrenm 
6851694Sdarrenm 	kmem_free(ctx->cc_provider_private, sizeof (md5_ctx_t));
6861694Sdarrenm 	ctx->cc_provider_private = NULL;
6871694Sdarrenm 	return (ret);
6881694Sdarrenm }
6891694Sdarrenm 
6901694Sdarrenm /* ARGSUSED */
6911694Sdarrenm static int
6921694Sdarrenm md5_digest_update(crypto_ctx_t *ctx, crypto_data_t *data,
6931694Sdarrenm     crypto_req_handle_t req)
6941694Sdarrenm {
6951694Sdarrenm 	int ret = CRYPTO_SUCCESS;
6961694Sdarrenm 
6971694Sdarrenm 	ASSERT(ctx->cc_provider_private != NULL);
6981694Sdarrenm 
6991694Sdarrenm 	/*
7001694Sdarrenm 	 * Do the MD5 update on the specified input data.
7011694Sdarrenm 	 */
7021694Sdarrenm 	switch (data->cd_format) {
7031694Sdarrenm 	case CRYPTO_DATA_RAW:
7041694Sdarrenm 		MD5Update(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
7051694Sdarrenm 		    data->cd_raw.iov_base + data->cd_offset,
7061694Sdarrenm 		    data->cd_length);
7071694Sdarrenm 		break;
7081694Sdarrenm 	case CRYPTO_DATA_UIO:
7091694Sdarrenm 		ret = md5_digest_update_uio(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
7101694Sdarrenm 		    data);
7111694Sdarrenm 		break;
7121694Sdarrenm 	case CRYPTO_DATA_MBLK:
7131694Sdarrenm 		ret = md5_digest_update_mblk(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
7141694Sdarrenm 		    data);
7151694Sdarrenm 		break;
7161694Sdarrenm 	default:
7171694Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
7181694Sdarrenm 	}
7191694Sdarrenm 
7201694Sdarrenm 	return (ret);
7211694Sdarrenm }
7221694Sdarrenm 
7231694Sdarrenm /* ARGSUSED */
7241694Sdarrenm static int
7251694Sdarrenm md5_digest_final(crypto_ctx_t *ctx, crypto_data_t *digest,
7261694Sdarrenm     crypto_req_handle_t req)
7271694Sdarrenm {
7281694Sdarrenm 	int ret = CRYPTO_SUCCESS;
7291694Sdarrenm 
7301694Sdarrenm 	ASSERT(ctx->cc_provider_private != NULL);
7311694Sdarrenm 
7321694Sdarrenm 	/*
7331694Sdarrenm 	 * We need to just return the length needed to store the output.
7341694Sdarrenm 	 * We should not destroy the context for the following cases.
7351694Sdarrenm 	 */
7361694Sdarrenm 	if ((digest->cd_length == 0) ||
7371694Sdarrenm 	    (digest->cd_length < MD5_DIGEST_LENGTH)) {
7381694Sdarrenm 		digest->cd_length = MD5_DIGEST_LENGTH;
7391694Sdarrenm 		return (CRYPTO_BUFFER_TOO_SMALL);
7401694Sdarrenm 	}
7411694Sdarrenm 
7421694Sdarrenm 	/*
7431694Sdarrenm 	 * Do an MD5 final.
7441694Sdarrenm 	 */
7451694Sdarrenm 	switch (digest->cd_format) {
7461694Sdarrenm 	case CRYPTO_DATA_RAW:
7471694Sdarrenm 		MD5Final((unsigned char *)digest->cd_raw.iov_base +
7481694Sdarrenm 		    digest->cd_offset, &PROV_MD5_CTX(ctx)->mc_md5_ctx);
7491694Sdarrenm 		break;
7501694Sdarrenm 	case CRYPTO_DATA_UIO:
7511694Sdarrenm 		ret = md5_digest_final_uio(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
7521694Sdarrenm 		    digest, MD5_DIGEST_LENGTH, NULL);
7531694Sdarrenm 		break;
7541694Sdarrenm 	case CRYPTO_DATA_MBLK:
7551694Sdarrenm 		ret = md5_digest_final_mblk(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
7561694Sdarrenm 		    digest, MD5_DIGEST_LENGTH, NULL);
7571694Sdarrenm 		break;
7581694Sdarrenm 	default:
7591694Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
7601694Sdarrenm 	}
7611694Sdarrenm 
7621694Sdarrenm 	/* all done, free context and return */
7631694Sdarrenm 
7641694Sdarrenm 	if (ret == CRYPTO_SUCCESS) {
7651694Sdarrenm 		digest->cd_length = MD5_DIGEST_LENGTH;
7661694Sdarrenm 	} else {
7671694Sdarrenm 		digest->cd_length = 0;
7681694Sdarrenm 	}
7691694Sdarrenm 
7701694Sdarrenm 	kmem_free(ctx->cc_provider_private, sizeof (md5_ctx_t));
7711694Sdarrenm 	ctx->cc_provider_private = NULL;
7721694Sdarrenm 
7731694Sdarrenm 	return (ret);
7741694Sdarrenm }
7751694Sdarrenm 
7761694Sdarrenm /* ARGSUSED */
7771694Sdarrenm static int
7781694Sdarrenm md5_digest_atomic(crypto_provider_handle_t provider,
7791694Sdarrenm     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
7801694Sdarrenm     crypto_data_t *data, crypto_data_t *digest,
7811694Sdarrenm     crypto_req_handle_t req)
7821694Sdarrenm {
7831694Sdarrenm 	int ret = CRYPTO_SUCCESS;
7841694Sdarrenm 	MD5_CTX md5_ctx;
7851694Sdarrenm 
7861694Sdarrenm 	if (mechanism->cm_type != MD5_MECH_INFO_TYPE)
7871694Sdarrenm 		return (CRYPTO_MECHANISM_INVALID);
7881694Sdarrenm 
7891694Sdarrenm 	/*
7901694Sdarrenm 	 * Do the MD5 init.
7911694Sdarrenm 	 */
7921694Sdarrenm 	MD5Init(&md5_ctx);
7931694Sdarrenm 
7941694Sdarrenm 	/*
7951694Sdarrenm 	 * Do the MD5 update on the specified input data.
7961694Sdarrenm 	 */
7971694Sdarrenm 	switch (data->cd_format) {
7981694Sdarrenm 	case CRYPTO_DATA_RAW:
7991694Sdarrenm 		MD5Update(&md5_ctx, data->cd_raw.iov_base + data->cd_offset,
8001694Sdarrenm 		    data->cd_length);
8011694Sdarrenm 		break;
8021694Sdarrenm 	case CRYPTO_DATA_UIO:
8031694Sdarrenm 		ret = md5_digest_update_uio(&md5_ctx, data);
8041694Sdarrenm 		break;
8051694Sdarrenm 	case CRYPTO_DATA_MBLK:
8061694Sdarrenm 		ret = md5_digest_update_mblk(&md5_ctx, data);
8071694Sdarrenm 		break;
8081694Sdarrenm 	default:
8091694Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
8101694Sdarrenm 	}
8111694Sdarrenm 
8121694Sdarrenm 	if (ret != CRYPTO_SUCCESS) {
8131694Sdarrenm 		/* the update failed, bail */
8141694Sdarrenm 		digest->cd_length = 0;
8151694Sdarrenm 		return (ret);
8161694Sdarrenm 	}
8171694Sdarrenm 
8181694Sdarrenm 	/*
8191694Sdarrenm 	 * Do an MD5 final, must be done separately since the digest
8201694Sdarrenm 	 * type can be different than the input data type.
8211694Sdarrenm 	 */
8221694Sdarrenm 	switch (digest->cd_format) {
8231694Sdarrenm 	case CRYPTO_DATA_RAW:
8241694Sdarrenm 		MD5Final((unsigned char *)digest->cd_raw.iov_base +
8251694Sdarrenm 		    digest->cd_offset, &md5_ctx);
8261694Sdarrenm 		break;
8271694Sdarrenm 	case CRYPTO_DATA_UIO:
8281694Sdarrenm 		ret = md5_digest_final_uio(&md5_ctx, digest,
8291694Sdarrenm 		    MD5_DIGEST_LENGTH, NULL);
8301694Sdarrenm 		break;
8311694Sdarrenm 	case CRYPTO_DATA_MBLK:
8321694Sdarrenm 		ret = md5_digest_final_mblk(&md5_ctx, digest,
8331694Sdarrenm 		    MD5_DIGEST_LENGTH, NULL);
8341694Sdarrenm 		break;
8351694Sdarrenm 	default:
8361694Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
8371694Sdarrenm 	}
8381694Sdarrenm 
8391694Sdarrenm 	if (ret == CRYPTO_SUCCESS) {
8401694Sdarrenm 		digest->cd_length = MD5_DIGEST_LENGTH;
8411694Sdarrenm 	} else {
8421694Sdarrenm 		digest->cd_length = 0;
8431694Sdarrenm 	}
8441694Sdarrenm 
8451694Sdarrenm 	return (ret);
8461694Sdarrenm }
8471694Sdarrenm 
8481694Sdarrenm /*
8491694Sdarrenm  * KCF software provider mac entry points.
8501694Sdarrenm  *
8511694Sdarrenm  * MD5 HMAC is: MD5(key XOR opad, MD5(key XOR ipad, text))
8521694Sdarrenm  *
8531694Sdarrenm  * Init:
8541694Sdarrenm  * The initialization routine initializes what we denote
8551694Sdarrenm  * as the inner and outer contexts by doing
8561694Sdarrenm  * - for inner context: MD5(key XOR ipad)
8571694Sdarrenm  * - for outer context: MD5(key XOR opad)
8581694Sdarrenm  *
8591694Sdarrenm  * Update:
8601694Sdarrenm  * Each subsequent MD5 HMAC update will result in an
8611694Sdarrenm  * update of the inner context with the specified data.
8621694Sdarrenm  *
8631694Sdarrenm  * Final:
8641694Sdarrenm  * The MD5 HMAC final will do a MD5 final operation on the
8651694Sdarrenm  * inner context, and the resulting digest will be used
8661694Sdarrenm  * as the data for an update on the outer context. Last
8671694Sdarrenm  * but not least, an MD5 final on the outer context will
8681694Sdarrenm  * be performed to obtain the MD5 HMAC digest to return
8691694Sdarrenm  * to the user.
8701694Sdarrenm  */
8711694Sdarrenm 
8721694Sdarrenm /*
8731694Sdarrenm  * Initialize a MD5-HMAC context.
8741694Sdarrenm  */
8751694Sdarrenm static void
8761694Sdarrenm md5_mac_init_ctx(md5_hmac_ctx_t *ctx, void *keyval, uint_t length_in_bytes)
8771694Sdarrenm {
8781694Sdarrenm 	uint32_t ipad[MD5_HMAC_INTS_PER_BLOCK];
8791694Sdarrenm 	uint32_t opad[MD5_HMAC_INTS_PER_BLOCK];
8801694Sdarrenm 	uint_t i;
8811694Sdarrenm 
8821694Sdarrenm 	bzero(ipad, MD5_HMAC_BLOCK_SIZE);
8831694Sdarrenm 	bzero(opad, MD5_HMAC_BLOCK_SIZE);
8841694Sdarrenm 
8851694Sdarrenm 	bcopy(keyval, ipad, length_in_bytes);
8861694Sdarrenm 	bcopy(keyval, opad, length_in_bytes);
8871694Sdarrenm 
8881694Sdarrenm 	/* XOR key with ipad (0x36) and opad (0x5c) */
8891694Sdarrenm 	for (i = 0; i < MD5_HMAC_INTS_PER_BLOCK; i++) {
8901694Sdarrenm 		ipad[i] ^= 0x36363636;
8911694Sdarrenm 		opad[i] ^= 0x5c5c5c5c;
8921694Sdarrenm 	}
8931694Sdarrenm 
8941694Sdarrenm 	/* perform MD5 on ipad */
8951694Sdarrenm 	MD5Init(&ctx->hc_icontext);
8961694Sdarrenm 	MD5Update(&ctx->hc_icontext, ipad, MD5_HMAC_BLOCK_SIZE);
8971694Sdarrenm 
8981694Sdarrenm 	/* perform MD5 on opad */
8991694Sdarrenm 	MD5Init(&ctx->hc_ocontext);
9001694Sdarrenm 	MD5Update(&ctx->hc_ocontext, opad, MD5_HMAC_BLOCK_SIZE);
9011694Sdarrenm }
9021694Sdarrenm 
9031694Sdarrenm /*
9041694Sdarrenm  * Initializes a multi-part MAC operation.
9051694Sdarrenm  */
9061694Sdarrenm static int
9071694Sdarrenm md5_mac_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
9081694Sdarrenm     crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
9091694Sdarrenm     crypto_req_handle_t req)
9101694Sdarrenm {
9111694Sdarrenm 	int ret = CRYPTO_SUCCESS;
9121694Sdarrenm 	uint_t keylen_in_bytes = CRYPTO_BITS2BYTES(key->ck_length);
9131694Sdarrenm 
9141694Sdarrenm 	if (mechanism->cm_type != MD5_HMAC_MECH_INFO_TYPE &&
9151694Sdarrenm 	    mechanism->cm_type != MD5_HMAC_GEN_MECH_INFO_TYPE)
9161694Sdarrenm 		return (CRYPTO_MECHANISM_INVALID);
9171694Sdarrenm 
9181694Sdarrenm 	/* Add support for key by attributes (RFE 4706552) */
9191694Sdarrenm 	if (key->ck_format != CRYPTO_KEY_RAW)
9201694Sdarrenm 		return (CRYPTO_ARGUMENTS_BAD);
9211694Sdarrenm 
9221694Sdarrenm 	ctx->cc_provider_private = kmem_alloc(sizeof (md5_hmac_ctx_t),
9231694Sdarrenm 	    crypto_kmflag(req));
9241694Sdarrenm 	if (ctx->cc_provider_private == NULL)
9251694Sdarrenm 		return (CRYPTO_HOST_MEMORY);
9261694Sdarrenm 
9271694Sdarrenm 	if (ctx_template != NULL) {
9281694Sdarrenm 		/* reuse context template */
9291694Sdarrenm 		bcopy(ctx_template, PROV_MD5_HMAC_CTX(ctx),
9301694Sdarrenm 		    sizeof (md5_hmac_ctx_t));
9311694Sdarrenm 	} else {
9321694Sdarrenm 		/* no context template, compute context */
9331694Sdarrenm 		if (keylen_in_bytes > MD5_HMAC_BLOCK_SIZE) {
9341694Sdarrenm 			uchar_t digested_key[MD5_DIGEST_LENGTH];
9351694Sdarrenm 			md5_hmac_ctx_t *hmac_ctx = ctx->cc_provider_private;
9361694Sdarrenm 
9371694Sdarrenm 			/*
9381694Sdarrenm 			 * Hash the passed-in key to get a smaller key.
9391694Sdarrenm 			 * The inner context is used since it hasn't been
9401694Sdarrenm 			 * initialized yet.
9411694Sdarrenm 			 */
9421694Sdarrenm 			PROV_MD5_DIGEST_KEY(&hmac_ctx->hc_icontext,
9431694Sdarrenm 			    key->ck_data, keylen_in_bytes, digested_key);
9441694Sdarrenm 			md5_mac_init_ctx(PROV_MD5_HMAC_CTX(ctx),
9451694Sdarrenm 			    digested_key, MD5_DIGEST_LENGTH);
9461694Sdarrenm 		} else {
9471694Sdarrenm 			md5_mac_init_ctx(PROV_MD5_HMAC_CTX(ctx),
9481694Sdarrenm 			    key->ck_data, keylen_in_bytes);
9491694Sdarrenm 		}
9501694Sdarrenm 	}
9511694Sdarrenm 
9521694Sdarrenm 	/*
9531694Sdarrenm 	 * Get the mechanism parameters, if applicable.
9541694Sdarrenm 	 */
9551694Sdarrenm 	PROV_MD5_HMAC_CTX(ctx)->hc_mech_type = mechanism->cm_type;
9561694Sdarrenm 	if (mechanism->cm_type == MD5_HMAC_GEN_MECH_INFO_TYPE) {
9571694Sdarrenm 		if (mechanism->cm_param == NULL ||
9581694Sdarrenm 		    mechanism->cm_param_len != sizeof (ulong_t))
9591694Sdarrenm 			ret = CRYPTO_MECHANISM_PARAM_INVALID;
9601694Sdarrenm 		PROV_MD5_GET_DIGEST_LEN(mechanism,
9611694Sdarrenm 		    PROV_MD5_HMAC_CTX(ctx)->hc_digest_len);
9621694Sdarrenm 		if (PROV_MD5_HMAC_CTX(ctx)->hc_digest_len >
9631694Sdarrenm 		    MD5_DIGEST_LENGTH)
9641694Sdarrenm 			ret = CRYPTO_MECHANISM_PARAM_INVALID;
9651694Sdarrenm 	}
9661694Sdarrenm 
9671694Sdarrenm 	if (ret != CRYPTO_SUCCESS) {
9681694Sdarrenm 		bzero(ctx->cc_provider_private, sizeof (md5_hmac_ctx_t));
9691694Sdarrenm 		kmem_free(ctx->cc_provider_private, sizeof (md5_hmac_ctx_t));
9701694Sdarrenm 		ctx->cc_provider_private = NULL;
9711694Sdarrenm 	}
9721694Sdarrenm 
9731694Sdarrenm 	return (ret);
9741694Sdarrenm }
9751694Sdarrenm 
9761694Sdarrenm 
9771694Sdarrenm /* ARGSUSED */
9781694Sdarrenm static int
9791694Sdarrenm md5_mac_update(crypto_ctx_t *ctx, crypto_data_t *data, crypto_req_handle_t req)
9801694Sdarrenm {
9811694Sdarrenm 	int ret = CRYPTO_SUCCESS;
9821694Sdarrenm 
9831694Sdarrenm 	ASSERT(ctx->cc_provider_private != NULL);
9841694Sdarrenm 
9851694Sdarrenm 	/*
9861694Sdarrenm 	 * Do an MD5 update of the inner context using the specified
9871694Sdarrenm 	 * data.
9881694Sdarrenm 	 */
9891694Sdarrenm 	switch (data->cd_format) {
9901694Sdarrenm 	case CRYPTO_DATA_RAW:
9911694Sdarrenm 		MD5Update(&PROV_MD5_HMAC_CTX(ctx)->hc_icontext,
9921694Sdarrenm 		    data->cd_raw.iov_base + data->cd_offset,
9931694Sdarrenm 		    data->cd_length);
9941694Sdarrenm 		break;
9951694Sdarrenm 	case CRYPTO_DATA_UIO:
9961694Sdarrenm 		ret = md5_digest_update_uio(
9971694Sdarrenm 		    &PROV_MD5_HMAC_CTX(ctx)->hc_icontext, data);
9981694Sdarrenm 		break;
9991694Sdarrenm 	case CRYPTO_DATA_MBLK:
10001694Sdarrenm 		ret = md5_digest_update_mblk(
10011694Sdarrenm 		    &PROV_MD5_HMAC_CTX(ctx)->hc_icontext, data);
10021694Sdarrenm 		break;
10031694Sdarrenm 	default:
10041694Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
10051694Sdarrenm 	}
10061694Sdarrenm 
10071694Sdarrenm 	return (ret);
10081694Sdarrenm }
10091694Sdarrenm 
10101694Sdarrenm /* ARGSUSED */
10111694Sdarrenm static int
10121694Sdarrenm md5_mac_final(crypto_ctx_t *ctx, crypto_data_t *mac, crypto_req_handle_t req)
10131694Sdarrenm {
10141694Sdarrenm 	int ret = CRYPTO_SUCCESS;
10151694Sdarrenm 	uchar_t digest[MD5_DIGEST_LENGTH];
10161694Sdarrenm 	uint32_t digest_len = MD5_DIGEST_LENGTH;
10171694Sdarrenm 
10181694Sdarrenm 	ASSERT(ctx->cc_provider_private != NULL);
10191694Sdarrenm 
10201694Sdarrenm 	if (PROV_MD5_HMAC_CTX(ctx)->hc_mech_type == MD5_HMAC_GEN_MECH_INFO_TYPE)
1021*5072Smcpowers 		digest_len = PROV_MD5_HMAC_CTX(ctx)->hc_digest_len;
10221694Sdarrenm 
10231694Sdarrenm 	/*
10241694Sdarrenm 	 * We need to just return the length needed to store the output.
10251694Sdarrenm 	 * We should not destroy the context for the following cases.
10261694Sdarrenm 	 */
10271694Sdarrenm 	if ((mac->cd_length == 0) || (mac->cd_length < digest_len)) {
10281694Sdarrenm 		mac->cd_length = digest_len;
10291694Sdarrenm 		return (CRYPTO_BUFFER_TOO_SMALL);
10301694Sdarrenm 	}
10311694Sdarrenm 
10321694Sdarrenm 	/*
10331694Sdarrenm 	 * Do an MD5 final on the inner context.
10341694Sdarrenm 	 */
10351694Sdarrenm 	MD5Final(digest, &PROV_MD5_HMAC_CTX(ctx)->hc_icontext);
10361694Sdarrenm 
10371694Sdarrenm 	/*
10381694Sdarrenm 	 * Do an MD5 update on the outer context, feeding the inner
10391694Sdarrenm 	 * digest as data.
10401694Sdarrenm 	 */
10411694Sdarrenm 	MD5Update(&PROV_MD5_HMAC_CTX(ctx)->hc_ocontext, digest,
10421694Sdarrenm 	    MD5_DIGEST_LENGTH);
10431694Sdarrenm 
10441694Sdarrenm 	/*
10451694Sdarrenm 	 * Do an MD5 final on the outer context, storing the computing
10461694Sdarrenm 	 * digest in the users buffer.
10471694Sdarrenm 	 */
10481694Sdarrenm 	switch (mac->cd_format) {
10491694Sdarrenm 	case CRYPTO_DATA_RAW:
10501694Sdarrenm 		if (digest_len != MD5_DIGEST_LENGTH) {
10511694Sdarrenm 			/*
10521694Sdarrenm 			 * The caller requested a short digest. Digest
10531694Sdarrenm 			 * into a scratch buffer and return to
10541694Sdarrenm 			 * the user only what was requested.
10551694Sdarrenm 			 */
10561694Sdarrenm 			MD5Final(digest,
10571694Sdarrenm 			    &PROV_MD5_HMAC_CTX(ctx)->hc_ocontext);
10581694Sdarrenm 			bcopy(digest, (unsigned char *)mac->cd_raw.iov_base +
10591694Sdarrenm 			    mac->cd_offset, digest_len);
10601694Sdarrenm 		} else {
10611694Sdarrenm 			MD5Final((unsigned char *)mac->cd_raw.iov_base +
10621694Sdarrenm 			    mac->cd_offset,
10631694Sdarrenm 			    &PROV_MD5_HMAC_CTX(ctx)->hc_ocontext);
10641694Sdarrenm 		}
10651694Sdarrenm 		break;
10661694Sdarrenm 	case CRYPTO_DATA_UIO:
10671694Sdarrenm 		ret = md5_digest_final_uio(
10681694Sdarrenm 		    &PROV_MD5_HMAC_CTX(ctx)->hc_ocontext, mac,
10691694Sdarrenm 		    digest_len, digest);
10701694Sdarrenm 		break;
10711694Sdarrenm 	case CRYPTO_DATA_MBLK:
10721694Sdarrenm 		ret = md5_digest_final_mblk(
10731694Sdarrenm 		    &PROV_MD5_HMAC_CTX(ctx)->hc_ocontext, mac,
10741694Sdarrenm 		    digest_len, digest);
10751694Sdarrenm 		break;
10761694Sdarrenm 	default:
10771694Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
10781694Sdarrenm 	}
10791694Sdarrenm 
10801694Sdarrenm 	if (ret == CRYPTO_SUCCESS) {
10811694Sdarrenm 		mac->cd_length = digest_len;
10821694Sdarrenm 	} else {
10831694Sdarrenm 		mac->cd_length = 0;
10841694Sdarrenm 	}
10851694Sdarrenm 
10861694Sdarrenm 	bzero(ctx->cc_provider_private, sizeof (md5_hmac_ctx_t));
10871694Sdarrenm 	kmem_free(ctx->cc_provider_private, sizeof (md5_hmac_ctx_t));
10881694Sdarrenm 	ctx->cc_provider_private = NULL;
10891694Sdarrenm 
10901694Sdarrenm 	return (ret);
10911694Sdarrenm }
10921694Sdarrenm 
10931694Sdarrenm #define	MD5_MAC_UPDATE(data, ctx, ret) {				\
10941694Sdarrenm 	switch (data->cd_format) {					\
10951694Sdarrenm 	case CRYPTO_DATA_RAW:						\
10961694Sdarrenm 		MD5Update(&(ctx).hc_icontext,				\
10971694Sdarrenm 		    data->cd_raw.iov_base + data->cd_offset,		\
10981694Sdarrenm 		    data->cd_length);					\
10991694Sdarrenm 		break;							\
11001694Sdarrenm 	case CRYPTO_DATA_UIO:						\
11011694Sdarrenm 		ret = md5_digest_update_uio(&(ctx).hc_icontext,	data);	\
11021694Sdarrenm 		break;							\
11031694Sdarrenm 	case CRYPTO_DATA_MBLK:						\
11041694Sdarrenm 		ret = md5_digest_update_mblk(&(ctx).hc_icontext,	\
11051694Sdarrenm 		    data);						\
11061694Sdarrenm 		break;							\
11071694Sdarrenm 	default:							\
11081694Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;				\
11091694Sdarrenm 	}								\
11101694Sdarrenm }
11111694Sdarrenm 
11121694Sdarrenm 
11131694Sdarrenm /* ARGSUSED */
11141694Sdarrenm static int
11151694Sdarrenm md5_mac_atomic(crypto_provider_handle_t provider,
11161694Sdarrenm     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
11171694Sdarrenm     crypto_key_t *key, crypto_data_t *data, crypto_data_t *mac,
11181694Sdarrenm     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
11191694Sdarrenm {
11201694Sdarrenm 	int ret = CRYPTO_SUCCESS;
11211694Sdarrenm 	uchar_t digest[MD5_DIGEST_LENGTH];
11221694Sdarrenm 	md5_hmac_ctx_t md5_hmac_ctx;
11231694Sdarrenm 	uint32_t digest_len = MD5_DIGEST_LENGTH;
11241694Sdarrenm 	uint_t keylen_in_bytes = CRYPTO_BITS2BYTES(key->ck_length);
11251694Sdarrenm 
11261694Sdarrenm 	if (mechanism->cm_type != MD5_HMAC_MECH_INFO_TYPE &&
11271694Sdarrenm 	    mechanism->cm_type != MD5_HMAC_GEN_MECH_INFO_TYPE)
11281694Sdarrenm 		return (CRYPTO_MECHANISM_INVALID);
11291694Sdarrenm 
11301694Sdarrenm 	/* Add support for key by attributes (RFE 4706552) */
11311694Sdarrenm 	if (key->ck_format != CRYPTO_KEY_RAW)
11321694Sdarrenm 		return (CRYPTO_ARGUMENTS_BAD);
11331694Sdarrenm 
11341694Sdarrenm 	if (ctx_template != NULL) {
11351694Sdarrenm 		/* reuse context template */
11361694Sdarrenm 		bcopy(ctx_template, &md5_hmac_ctx, sizeof (md5_hmac_ctx_t));
11371694Sdarrenm 	} else {
11381694Sdarrenm 		/* no context template, compute context */
11391694Sdarrenm 		if (keylen_in_bytes > MD5_HMAC_BLOCK_SIZE) {
11401694Sdarrenm 			/*
11411694Sdarrenm 			 * Hash the passed-in key to get a smaller key.
11421694Sdarrenm 			 * The inner context is used since it hasn't been
11431694Sdarrenm 			 * initialized yet.
11441694Sdarrenm 			 */
11451694Sdarrenm 			PROV_MD5_DIGEST_KEY(&md5_hmac_ctx.hc_icontext,
11461694Sdarrenm 			    key->ck_data, keylen_in_bytes, digest);
11471694Sdarrenm 			md5_mac_init_ctx(&md5_hmac_ctx, digest,
11481694Sdarrenm 			    MD5_DIGEST_LENGTH);
11491694Sdarrenm 		} else {
11501694Sdarrenm 			md5_mac_init_ctx(&md5_hmac_ctx, key->ck_data,
11511694Sdarrenm 			    keylen_in_bytes);
11521694Sdarrenm 		}
11531694Sdarrenm 	}
11541694Sdarrenm 
11551694Sdarrenm 	/*
11561694Sdarrenm 	 * Get the mechanism parameters, if applicable.
11571694Sdarrenm 	 */
11581694Sdarrenm 	if (mechanism->cm_type == MD5_HMAC_GEN_MECH_INFO_TYPE) {
11591694Sdarrenm 		if (mechanism->cm_param == NULL ||
11601694Sdarrenm 		    mechanism->cm_param_len != sizeof (ulong_t)) {
11611694Sdarrenm 			ret = CRYPTO_MECHANISM_PARAM_INVALID;
11621694Sdarrenm 			goto bail;
11631694Sdarrenm 		}
11641694Sdarrenm 		PROV_MD5_GET_DIGEST_LEN(mechanism, digest_len);
11651694Sdarrenm 		if (digest_len > MD5_DIGEST_LENGTH) {
11661694Sdarrenm 			ret = CRYPTO_MECHANISM_PARAM_INVALID;
11671694Sdarrenm 			goto bail;
11681694Sdarrenm 		}
11691694Sdarrenm 	}
11701694Sdarrenm 
11711694Sdarrenm 	/* do an MD5 update of the inner context using the specified data */
11721694Sdarrenm 	MD5_MAC_UPDATE(data, md5_hmac_ctx, ret);
11731694Sdarrenm 	if (ret != CRYPTO_SUCCESS)
11741694Sdarrenm 		/* the update failed, free context and bail */
11751694Sdarrenm 		goto bail;
11761694Sdarrenm 
11771694Sdarrenm 	/* do an MD5 final on the inner context */
11781694Sdarrenm 	MD5Final(digest, &md5_hmac_ctx.hc_icontext);
11791694Sdarrenm 
11801694Sdarrenm 	/*
11811694Sdarrenm 	 * Do an MD5 update on the outer context, feeding the inner
11821694Sdarrenm 	 * digest as data.
11831694Sdarrenm 	 */
11841694Sdarrenm 	MD5Update(&md5_hmac_ctx.hc_ocontext, digest, MD5_DIGEST_LENGTH);
11851694Sdarrenm 
11861694Sdarrenm 	/*
11871694Sdarrenm 	 * Do an MD5 final on the outer context, storing the computed
11881694Sdarrenm 	 * digest in the users buffer.
11891694Sdarrenm 	 */
11901694Sdarrenm 	switch (mac->cd_format) {
11911694Sdarrenm 	case CRYPTO_DATA_RAW:
11921694Sdarrenm 		if (digest_len != MD5_DIGEST_LENGTH) {
11931694Sdarrenm 			/*
11941694Sdarrenm 			 * The caller requested a short digest. Digest
11951694Sdarrenm 			 * into a scratch buffer and return to
11961694Sdarrenm 			 * the user only what was requested.
11971694Sdarrenm 			 */
11981694Sdarrenm 			MD5Final(digest, &md5_hmac_ctx.hc_ocontext);
11991694Sdarrenm 			bcopy(digest, (unsigned char *)mac->cd_raw.iov_base +
12001694Sdarrenm 			    mac->cd_offset, digest_len);
12011694Sdarrenm 		} else {
12021694Sdarrenm 			MD5Final((unsigned char *)mac->cd_raw.iov_base +
12031694Sdarrenm 			    mac->cd_offset, &md5_hmac_ctx.hc_ocontext);
12041694Sdarrenm 		}
12051694Sdarrenm 		break;
12061694Sdarrenm 	case CRYPTO_DATA_UIO:
12071694Sdarrenm 		ret = md5_digest_final_uio(&md5_hmac_ctx.hc_ocontext, mac,
12081694Sdarrenm 		    digest_len, digest);
12091694Sdarrenm 		break;
12101694Sdarrenm 	case CRYPTO_DATA_MBLK:
12111694Sdarrenm 		ret = md5_digest_final_mblk(&md5_hmac_ctx.hc_ocontext, mac,
12121694Sdarrenm 		    digest_len, digest);
12131694Sdarrenm 		break;
12141694Sdarrenm 	default:
12151694Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
12161694Sdarrenm 	}
12171694Sdarrenm 
12181694Sdarrenm 	if (ret == CRYPTO_SUCCESS) {
12191694Sdarrenm 		mac->cd_length = digest_len;
12201694Sdarrenm 	} else {
12211694Sdarrenm 		mac->cd_length = 0;
12221694Sdarrenm 	}
12231694Sdarrenm 	/* Extra paranoia: zeroizing the local context on the stack */
12241694Sdarrenm 	bzero(&md5_hmac_ctx, sizeof (md5_hmac_ctx_t));
12251694Sdarrenm 
12261694Sdarrenm 	return (ret);
12271694Sdarrenm bail:
12281694Sdarrenm 	bzero(&md5_hmac_ctx, sizeof (md5_hmac_ctx_t));
12291694Sdarrenm 	mac->cd_length = 0;
12301694Sdarrenm 	return (ret);
12311694Sdarrenm }
12321694Sdarrenm 
12331694Sdarrenm /* ARGSUSED */
12341694Sdarrenm static int
12351694Sdarrenm md5_mac_verify_atomic(crypto_provider_handle_t provider,
12361694Sdarrenm     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
12371694Sdarrenm     crypto_key_t *key, crypto_data_t *data, crypto_data_t *mac,
12381694Sdarrenm     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
12391694Sdarrenm {
12401694Sdarrenm 	int ret = CRYPTO_SUCCESS;
12411694Sdarrenm 	uchar_t digest[MD5_DIGEST_LENGTH];
12421694Sdarrenm 	md5_hmac_ctx_t md5_hmac_ctx;
12431694Sdarrenm 	uint32_t digest_len = MD5_DIGEST_LENGTH;
12441694Sdarrenm 	uint_t keylen_in_bytes = CRYPTO_BITS2BYTES(key->ck_length);
12451694Sdarrenm 
12461694Sdarrenm 	if (mechanism->cm_type != MD5_HMAC_MECH_INFO_TYPE &&
12471694Sdarrenm 	    mechanism->cm_type != MD5_HMAC_GEN_MECH_INFO_TYPE)
12481694Sdarrenm 		return (CRYPTO_MECHANISM_INVALID);
12491694Sdarrenm 
12501694Sdarrenm 	/* Add support for key by attributes (RFE 4706552) */
12511694Sdarrenm 	if (key->ck_format != CRYPTO_KEY_RAW)
12521694Sdarrenm 		return (CRYPTO_ARGUMENTS_BAD);
12531694Sdarrenm 
12541694Sdarrenm 	if (ctx_template != NULL) {
12551694Sdarrenm 		/* reuse context template */
12561694Sdarrenm 		bcopy(ctx_template, &md5_hmac_ctx, sizeof (md5_hmac_ctx_t));
12571694Sdarrenm 	} else {
12581694Sdarrenm 		/* no context template, compute context */
12591694Sdarrenm 		if (keylen_in_bytes > MD5_HMAC_BLOCK_SIZE) {
12601694Sdarrenm 			/*
12611694Sdarrenm 			 * Hash the passed-in key to get a smaller key.
12621694Sdarrenm 			 * The inner context is used since it hasn't been
12631694Sdarrenm 			 * initialized yet.
12641694Sdarrenm 			 */
12651694Sdarrenm 			PROV_MD5_DIGEST_KEY(&md5_hmac_ctx.hc_icontext,
12661694Sdarrenm 			    key->ck_data, keylen_in_bytes, digest);
12671694Sdarrenm 			md5_mac_init_ctx(&md5_hmac_ctx, digest,
12681694Sdarrenm 			    MD5_DIGEST_LENGTH);
12691694Sdarrenm 		} else {
12701694Sdarrenm 			md5_mac_init_ctx(&md5_hmac_ctx, key->ck_data,
12711694Sdarrenm 			    keylen_in_bytes);
12721694Sdarrenm 		}
12731694Sdarrenm 	}
12741694Sdarrenm 
12751694Sdarrenm 	/*
12761694Sdarrenm 	 * Get the mechanism parameters, if applicable.
12771694Sdarrenm 	 */
12781694Sdarrenm 	if (mechanism->cm_type == MD5_HMAC_GEN_MECH_INFO_TYPE) {
12791694Sdarrenm 		if (mechanism->cm_param == NULL ||
12801694Sdarrenm 		    mechanism->cm_param_len != sizeof (ulong_t)) {
12811694Sdarrenm 			ret = CRYPTO_MECHANISM_PARAM_INVALID;
12821694Sdarrenm 			goto bail;
12831694Sdarrenm 		}
12841694Sdarrenm 		PROV_MD5_GET_DIGEST_LEN(mechanism, digest_len);
12851694Sdarrenm 		if (digest_len > MD5_DIGEST_LENGTH) {
12861694Sdarrenm 			ret = CRYPTO_MECHANISM_PARAM_INVALID;
12871694Sdarrenm 			goto bail;
12881694Sdarrenm 		}
12891694Sdarrenm 	}
12901694Sdarrenm 
12911694Sdarrenm 	if (mac->cd_length != digest_len) {
12921694Sdarrenm 		ret = CRYPTO_INVALID_MAC;
12931694Sdarrenm 		goto bail;
12941694Sdarrenm 	}
12951694Sdarrenm 
12961694Sdarrenm 	/* do an MD5 update of the inner context using the specified data */
12971694Sdarrenm 	MD5_MAC_UPDATE(data, md5_hmac_ctx, ret);
12981694Sdarrenm 	if (ret != CRYPTO_SUCCESS)
12991694Sdarrenm 		/* the update failed, free context and bail */
13001694Sdarrenm 		goto bail;
13011694Sdarrenm 
13021694Sdarrenm 	/* do an MD5 final on the inner context */
13031694Sdarrenm 	MD5Final(digest, &md5_hmac_ctx.hc_icontext);
13041694Sdarrenm 
13051694Sdarrenm 	/*
13061694Sdarrenm 	 * Do an MD5 update on the outer context, feeding the inner
13071694Sdarrenm 	 * digest as data.
13081694Sdarrenm 	 */
13091694Sdarrenm 	MD5Update(&md5_hmac_ctx.hc_ocontext, digest, MD5_DIGEST_LENGTH);
13101694Sdarrenm 
13111694Sdarrenm 	/*
13121694Sdarrenm 	 * Do an MD5 final on the outer context, storing the computed
13131694Sdarrenm 	 * digest in the local digest buffer.
13141694Sdarrenm 	 */
13151694Sdarrenm 	MD5Final(digest, &md5_hmac_ctx.hc_ocontext);
13161694Sdarrenm 
13171694Sdarrenm 	/*
13181694Sdarrenm 	 * Compare the computed digest against the expected digest passed
13191694Sdarrenm 	 * as argument.
13201694Sdarrenm 	 */
13211694Sdarrenm 	switch (mac->cd_format) {
13221694Sdarrenm 
13231694Sdarrenm 	case CRYPTO_DATA_RAW:
13241694Sdarrenm 		if (bcmp(digest, (unsigned char *)mac->cd_raw.iov_base +
13251694Sdarrenm 		    mac->cd_offset, digest_len) != 0)
13261694Sdarrenm 			ret = CRYPTO_INVALID_MAC;
13271694Sdarrenm 		break;
13281694Sdarrenm 
13291694Sdarrenm 	case CRYPTO_DATA_UIO: {
13301694Sdarrenm 		off_t offset = mac->cd_offset;
13311694Sdarrenm 		uint_t vec_idx;
13321694Sdarrenm 		off_t scratch_offset = 0;
13331694Sdarrenm 		size_t length = digest_len;
13341694Sdarrenm 		size_t cur_len;
13351694Sdarrenm 
13361694Sdarrenm 		/* we support only kernel buffer */
13371694Sdarrenm 		if (mac->cd_uio->uio_segflg != UIO_SYSSPACE)
13381694Sdarrenm 			return (CRYPTO_ARGUMENTS_BAD);
13391694Sdarrenm 
13401694Sdarrenm 		/* jump to the first iovec containing the expected digest */
13411694Sdarrenm 		for (vec_idx = 0;
13421694Sdarrenm 		    offset >= mac->cd_uio->uio_iov[vec_idx].iov_len &&
13431694Sdarrenm 		    vec_idx < mac->cd_uio->uio_iovcnt;
1344*5072Smcpowers 		    offset -= mac->cd_uio->uio_iov[vec_idx++].iov_len)
1345*5072Smcpowers 			;
13461694Sdarrenm 		if (vec_idx == mac->cd_uio->uio_iovcnt) {
13471694Sdarrenm 			/*
13481694Sdarrenm 			 * The caller specified an offset that is
13491694Sdarrenm 			 * larger than the total size of the buffers
13501694Sdarrenm 			 * it provided.
13511694Sdarrenm 			 */
13521694Sdarrenm 			ret = CRYPTO_DATA_LEN_RANGE;
13531694Sdarrenm 			break;
13541694Sdarrenm 		}
13551694Sdarrenm 
13561694Sdarrenm 		/* do the comparison of computed digest vs specified one */
13571694Sdarrenm 		while (vec_idx < mac->cd_uio->uio_iovcnt && length > 0) {
13581694Sdarrenm 			cur_len = MIN(mac->cd_uio->uio_iov[vec_idx].iov_len -
13591694Sdarrenm 			    offset, length);
13601694Sdarrenm 
13611694Sdarrenm 			if (bcmp(digest + scratch_offset,
13621694Sdarrenm 			    mac->cd_uio->uio_iov[vec_idx].iov_base + offset,
13631694Sdarrenm 			    cur_len) != 0) {
13641694Sdarrenm 				ret = CRYPTO_INVALID_MAC;
13651694Sdarrenm 				break;
13661694Sdarrenm 			}
13671694Sdarrenm 
13681694Sdarrenm 			length -= cur_len;
13691694Sdarrenm 			vec_idx++;
13701694Sdarrenm 			scratch_offset += cur_len;
13711694Sdarrenm 			offset = 0;
13721694Sdarrenm 		}
13731694Sdarrenm 		break;
13741694Sdarrenm 	}
13751694Sdarrenm 
13761694Sdarrenm 	case CRYPTO_DATA_MBLK: {
13771694Sdarrenm 		off_t offset = mac->cd_offset;
13781694Sdarrenm 		mblk_t *mp;
13791694Sdarrenm 		off_t scratch_offset = 0;
13801694Sdarrenm 		size_t length = digest_len;
13811694Sdarrenm 		size_t cur_len;
13821694Sdarrenm 
13831694Sdarrenm 		/* jump to the first mblk_t containing the expected digest */
13841694Sdarrenm 		for (mp = mac->cd_mp; mp != NULL && offset >= MBLKL(mp);
1385*5072Smcpowers 		    offset -= MBLKL(mp), mp = mp->b_cont)
1386*5072Smcpowers 			;
13871694Sdarrenm 		if (mp == NULL) {
13881694Sdarrenm 			/*
13891694Sdarrenm 			 * The caller specified an offset that is larger than
13901694Sdarrenm 			 * the total size of the buffers it provided.
13911694Sdarrenm 			 */
13921694Sdarrenm 			ret = CRYPTO_DATA_LEN_RANGE;
13931694Sdarrenm 			break;
13941694Sdarrenm 		}
13951694Sdarrenm 
13961694Sdarrenm 		while (mp != NULL && length > 0) {
13971694Sdarrenm 			cur_len = MIN(MBLKL(mp) - offset, length);
13981694Sdarrenm 			if (bcmp(digest + scratch_offset,
13991694Sdarrenm 			    mp->b_rptr + offset, cur_len) != 0) {
14001694Sdarrenm 				ret = CRYPTO_INVALID_MAC;
14011694Sdarrenm 				break;
14021694Sdarrenm 			}
14031694Sdarrenm 
14041694Sdarrenm 			length -= cur_len;
14051694Sdarrenm 			mp = mp->b_cont;
14061694Sdarrenm 			scratch_offset += cur_len;
14071694Sdarrenm 			offset = 0;
14081694Sdarrenm 		}
14091694Sdarrenm 		break;
14101694Sdarrenm 	}
14111694Sdarrenm 
14121694Sdarrenm 	default:
14131694Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
14141694Sdarrenm 	}
14151694Sdarrenm 
14161694Sdarrenm 	bzero(&md5_hmac_ctx, sizeof (md5_hmac_ctx_t));
14171694Sdarrenm 	return (ret);
14181694Sdarrenm bail:
14191694Sdarrenm 	bzero(&md5_hmac_ctx, sizeof (md5_hmac_ctx_t));
14201694Sdarrenm 	mac->cd_length = 0;
14211694Sdarrenm 	return (ret);
14221694Sdarrenm }
14231694Sdarrenm 
14241694Sdarrenm /*
14251694Sdarrenm  * KCF software provider context management entry points.
14261694Sdarrenm  */
14271694Sdarrenm 
14281694Sdarrenm /* ARGSUSED */
14291694Sdarrenm static int
14301694Sdarrenm md5_create_ctx_template(crypto_provider_handle_t provider,
14311694Sdarrenm     crypto_mechanism_t *mechanism, crypto_key_t *key,
14321694Sdarrenm     crypto_spi_ctx_template_t *ctx_template, size_t *ctx_template_size,
14331694Sdarrenm     crypto_req_handle_t req)
14341694Sdarrenm {
14351694Sdarrenm 	md5_hmac_ctx_t *md5_hmac_ctx_tmpl;
14361694Sdarrenm 	uint_t keylen_in_bytes = CRYPTO_BITS2BYTES(key->ck_length);
14371694Sdarrenm 
14381694Sdarrenm 	if ((mechanism->cm_type != MD5_HMAC_MECH_INFO_TYPE) &&
14391694Sdarrenm 	    (mechanism->cm_type != MD5_HMAC_GEN_MECH_INFO_TYPE))
14401694Sdarrenm 		return (CRYPTO_MECHANISM_INVALID);
14411694Sdarrenm 
14421694Sdarrenm 	/* Add support for key by attributes (RFE 4706552) */
14431694Sdarrenm 	if (key->ck_format != CRYPTO_KEY_RAW)
14441694Sdarrenm 		return (CRYPTO_ARGUMENTS_BAD);
14451694Sdarrenm 
14461694Sdarrenm 	/*
14471694Sdarrenm 	 * Allocate and initialize MD5 context.
14481694Sdarrenm 	 */
14491694Sdarrenm 	md5_hmac_ctx_tmpl = kmem_alloc(sizeof (md5_hmac_ctx_t),
14501694Sdarrenm 	    crypto_kmflag(req));
14511694Sdarrenm 	if (md5_hmac_ctx_tmpl == NULL)
14521694Sdarrenm 		return (CRYPTO_HOST_MEMORY);
14531694Sdarrenm 
14541694Sdarrenm 	if (keylen_in_bytes > MD5_HMAC_BLOCK_SIZE) {
14551694Sdarrenm 		uchar_t digested_key[MD5_DIGEST_LENGTH];
14561694Sdarrenm 
14571694Sdarrenm 		/*
14581694Sdarrenm 		 * Hash the passed-in key to get a smaller key.
14591694Sdarrenm 		 * The inner context is used since it hasn't been
14601694Sdarrenm 		 * initialized yet.
14611694Sdarrenm 		 */
14621694Sdarrenm 		PROV_MD5_DIGEST_KEY(&md5_hmac_ctx_tmpl->hc_icontext,
14631694Sdarrenm 		    key->ck_data, keylen_in_bytes, digested_key);
14641694Sdarrenm 		md5_mac_init_ctx(md5_hmac_ctx_tmpl, digested_key,
14651694Sdarrenm 		    MD5_DIGEST_LENGTH);
14661694Sdarrenm 	} else {
14671694Sdarrenm 		md5_mac_init_ctx(md5_hmac_ctx_tmpl, key->ck_data,
14681694Sdarrenm 		    keylen_in_bytes);
14691694Sdarrenm 	}
14701694Sdarrenm 
14711694Sdarrenm 	md5_hmac_ctx_tmpl->hc_mech_type = mechanism->cm_type;
14721694Sdarrenm 	*ctx_template = (crypto_spi_ctx_template_t)md5_hmac_ctx_tmpl;
14731694Sdarrenm 	*ctx_template_size = sizeof (md5_hmac_ctx_t);
14741694Sdarrenm 
14751694Sdarrenm 	return (CRYPTO_SUCCESS);
14761694Sdarrenm }
14771694Sdarrenm 
14781694Sdarrenm static int
14791694Sdarrenm md5_free_context(crypto_ctx_t *ctx)
14801694Sdarrenm {
14811694Sdarrenm 	uint_t ctx_len;
14821694Sdarrenm 	md5_mech_type_t mech_type;
14831694Sdarrenm 
14841694Sdarrenm 	if (ctx->cc_provider_private == NULL)
14851694Sdarrenm 		return (CRYPTO_SUCCESS);
14861694Sdarrenm 
14871694Sdarrenm 	/*
14881694Sdarrenm 	 * We have to free either MD5 or MD5-HMAC contexts, which
14891694Sdarrenm 	 * have different lengths.
14901694Sdarrenm 	 */
14911694Sdarrenm 
14921694Sdarrenm 	mech_type = PROV_MD5_CTX(ctx)->mc_mech_type;
14931694Sdarrenm 	if (mech_type == MD5_MECH_INFO_TYPE)
14941694Sdarrenm 		ctx_len = sizeof (md5_ctx_t);
14951694Sdarrenm 	else {
14961694Sdarrenm 		ASSERT(mech_type == MD5_HMAC_MECH_INFO_TYPE ||
14971694Sdarrenm 		    mech_type == MD5_HMAC_GEN_MECH_INFO_TYPE);
14981694Sdarrenm 		ctx_len = sizeof (md5_hmac_ctx_t);
14991694Sdarrenm 	}
15001694Sdarrenm 
15011694Sdarrenm 	bzero(ctx->cc_provider_private, ctx_len);
15021694Sdarrenm 	kmem_free(ctx->cc_provider_private, ctx_len);
15031694Sdarrenm 	ctx->cc_provider_private = NULL;
15041694Sdarrenm 
15051694Sdarrenm 	return (CRYPTO_SUCCESS);
15061694Sdarrenm }
1507