xref: /onnv-gate/usr/src/uts/common/crypto/io/md4_mod.c (revision 5072:a2b6f90cc61e)
14002Sdarrenm /*
24002Sdarrenm  * CDDL HEADER START
34002Sdarrenm  *
44002Sdarrenm  * The contents of this file are subject to the terms of the
54002Sdarrenm  * Common Development and Distribution License (the "License").
64002Sdarrenm  * You may not use this file except in compliance with the License.
74002Sdarrenm  *
84002Sdarrenm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94002Sdarrenm  * or http://www.opensolaris.org/os/licensing.
104002Sdarrenm  * See the License for the specific language governing permissions
114002Sdarrenm  * and limitations under the License.
124002Sdarrenm  *
134002Sdarrenm  * When distributing Covered Code, include this CDDL HEADER in each
144002Sdarrenm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154002Sdarrenm  * If applicable, add the following below this CDDL HEADER, with the
164002Sdarrenm  * fields enclosed by brackets "[]" replaced with your own identifying
174002Sdarrenm  * information: Portions Copyright [yyyy] [name of copyright owner]
184002Sdarrenm  *
194002Sdarrenm  * CDDL HEADER END
204002Sdarrenm  */
214002Sdarrenm 
224002Sdarrenm /*
234002Sdarrenm  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
244002Sdarrenm  * Use is subject to license terms.
254002Sdarrenm  */
264002Sdarrenm #pragma ident	"%Z%%M%	%I%	%E% SMI"
274002Sdarrenm 
284002Sdarrenm /*
294002Sdarrenm  * In kernel module, the md4 module is created with one modlinkage,
304002Sdarrenm  * this is different to md5 and sha1 modules which have a legacy misc
314002Sdarrenm  * variant for direct calls to the Init/Update/Final routines.
324002Sdarrenm  *
334002Sdarrenm  * - a modlcrypto that allows the module to register with the Kernel
344002Sdarrenm  *   Cryptographic Framework (KCF) as a software provider for the MD4
354002Sdarrenm  *   mechanisms.
364002Sdarrenm  */
374002Sdarrenm 
384002Sdarrenm #include <sys/types.h>
394002Sdarrenm #include <sys/systm.h>
404002Sdarrenm #include <sys/modctl.h>
414002Sdarrenm #include <sys/cmn_err.h>
424002Sdarrenm #include <sys/ddi.h>
434002Sdarrenm #include <sys/crypto/common.h>
444002Sdarrenm #include <sys/crypto/spi.h>
454002Sdarrenm #include <sys/sysmacros.h>
464002Sdarrenm #include <sys/strsun.h>
474002Sdarrenm #include <sys/note.h>
484002Sdarrenm #include <sys/md4.h>
494002Sdarrenm 
504002Sdarrenm extern struct mod_ops mod_miscops;
514002Sdarrenm extern struct mod_ops mod_cryptoops;
524002Sdarrenm 
534002Sdarrenm /*
544002Sdarrenm  * Module linkage information for the kernel.
554002Sdarrenm  */
564002Sdarrenm 
574002Sdarrenm static struct modlcrypto modlcrypto = {
584002Sdarrenm 	&mod_cryptoops,
59*5072Smcpowers 	"MD4 Kernel SW Provider"
604002Sdarrenm };
614002Sdarrenm 
624002Sdarrenm static struct modlinkage modlinkage = {
634002Sdarrenm 	MODREV_1,
644002Sdarrenm 	(void *)&modlcrypto,
654002Sdarrenm 	NULL
664002Sdarrenm };
674002Sdarrenm 
684002Sdarrenm /*
694002Sdarrenm  * CSPI information (entry points, provider info, etc.)
704002Sdarrenm  */
714002Sdarrenm 
724002Sdarrenm typedef enum md4_mech_type {
734002Sdarrenm 	MD4_MECH_INFO_TYPE,		/* SUN_CKM_MD4 */
744002Sdarrenm } md4_mech_type_t;
754002Sdarrenm 
764002Sdarrenm #define	MD4_DIGEST_LENGTH	16	/* MD4 digest length in bytes */
774002Sdarrenm 
784002Sdarrenm /*
794002Sdarrenm  * Context for MD4 mechanism.
804002Sdarrenm  */
814002Sdarrenm typedef struct md4_ctx {
824002Sdarrenm 	md4_mech_type_t		mc_mech_type;	/* type of context */
834002Sdarrenm 	MD4_CTX			mc_md4_ctx;	/* MD4 context */
844002Sdarrenm } md4_ctx_t;
854002Sdarrenm 
864002Sdarrenm /*
874002Sdarrenm  * Macros to access the MD4 contexts from a context passed
884002Sdarrenm  * by KCF to one of the entry points.
894002Sdarrenm  */
904002Sdarrenm 
914002Sdarrenm #define	PROV_MD4_CTX(ctx)	((md4_ctx_t *)(ctx)->cc_provider_private)
924002Sdarrenm 
934002Sdarrenm /*
944002Sdarrenm  * Mechanism info structure passed to KCF during registration.
954002Sdarrenm  */
964002Sdarrenm static crypto_mech_info_t md4_mech_info_tab[] = {
974002Sdarrenm 	/* MD4 */
984002Sdarrenm 	{SUN_CKM_MD4, MD4_MECH_INFO_TYPE,
994002Sdarrenm 	    CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC,
1004002Sdarrenm 	    0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
1014002Sdarrenm };
1024002Sdarrenm 
1034002Sdarrenm static void md4_provider_status(crypto_provider_handle_t, uint_t *);
1044002Sdarrenm 
1054002Sdarrenm static crypto_control_ops_t md4_control_ops = {
1064002Sdarrenm 	md4_provider_status
1074002Sdarrenm };
1084002Sdarrenm 
1094002Sdarrenm static int md4_digest_init(crypto_ctx_t *, crypto_mechanism_t *,
1104002Sdarrenm     crypto_req_handle_t);
1114002Sdarrenm static int md4_digest(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
1124002Sdarrenm     crypto_req_handle_t);
1134002Sdarrenm static int md4_digest_update(crypto_ctx_t *, crypto_data_t *,
1144002Sdarrenm     crypto_req_handle_t);
1154002Sdarrenm static int md4_digest_final(crypto_ctx_t *, crypto_data_t *,
1164002Sdarrenm     crypto_req_handle_t);
1174002Sdarrenm static int md4_digest_atomic(crypto_provider_handle_t, crypto_session_id_t,
1184002Sdarrenm     crypto_mechanism_t *, crypto_data_t *, crypto_data_t *,
1194002Sdarrenm     crypto_req_handle_t);
1204002Sdarrenm 
1214002Sdarrenm static crypto_digest_ops_t md4_digest_ops = {
1224002Sdarrenm 	md4_digest_init,
1234002Sdarrenm 	md4_digest,
1244002Sdarrenm 	md4_digest_update,
1254002Sdarrenm 	NULL,
1264002Sdarrenm 	md4_digest_final,
1274002Sdarrenm 	md4_digest_atomic
1284002Sdarrenm };
1294002Sdarrenm 
1304002Sdarrenm static crypto_ops_t md4_crypto_ops = {
1314002Sdarrenm 	&md4_control_ops,
1324002Sdarrenm 	&md4_digest_ops,
1334002Sdarrenm 	NULL,
1344002Sdarrenm 	NULL,
1354002Sdarrenm 	NULL,
1364002Sdarrenm 	NULL,
1374002Sdarrenm 	NULL,
1384002Sdarrenm 	NULL,
1394002Sdarrenm 	NULL,
1404002Sdarrenm 	NULL,
1414002Sdarrenm 	NULL,
1424002Sdarrenm 	NULL,
1434002Sdarrenm 	NULL,
1444002Sdarrenm 	NULL,
1454002Sdarrenm };
1464002Sdarrenm 
1474002Sdarrenm static crypto_provider_info_t md4_prov_info = {
1484002Sdarrenm 	CRYPTO_SPI_VERSION_1,
1494002Sdarrenm 	"MD4 Software Provider",
1504002Sdarrenm 	CRYPTO_SW_PROVIDER,
1514002Sdarrenm 	{&modlinkage},
1524002Sdarrenm 	NULL,
1534002Sdarrenm 	&md4_crypto_ops,
1544002Sdarrenm 	sizeof (md4_mech_info_tab)/sizeof (crypto_mech_info_t),
1554002Sdarrenm 	md4_mech_info_tab
1564002Sdarrenm };
1574002Sdarrenm 
1584002Sdarrenm static crypto_kcf_provider_handle_t md4_prov_handle = NULL;
1594002Sdarrenm 
1604002Sdarrenm int
1614002Sdarrenm _init(void)
1624002Sdarrenm {
1634002Sdarrenm 	int ret;
1644002Sdarrenm 
1654002Sdarrenm 	if ((ret = mod_install(&modlinkage)) != 0)
1664002Sdarrenm 		return (ret);
1674002Sdarrenm 
1684002Sdarrenm 	/*
1694002Sdarrenm 	 * Register with KCF. If the registration fails, log an
1704002Sdarrenm 	 * error and uninstall the module.
1714002Sdarrenm 	 */
1724002Sdarrenm 	if ((ret = crypto_register_provider(&md4_prov_info,
1734002Sdarrenm 	    &md4_prov_handle)) != CRYPTO_SUCCESS) {
1744002Sdarrenm 		cmn_err(CE_WARN, "md4 _init: "
1754002Sdarrenm 		    "crypto_register_provider() failed (0x%x)", ret);
1764002Sdarrenm 		(void) mod_remove(&modlinkage);
1774002Sdarrenm 		return (ret);
1784002Sdarrenm 	}
1794002Sdarrenm 
1804002Sdarrenm 	return (0);
1814002Sdarrenm }
1824002Sdarrenm 
1834002Sdarrenm int
1844002Sdarrenm _fini(void)
1854002Sdarrenm {
1864002Sdarrenm 	int ret;
1874002Sdarrenm 
1884002Sdarrenm 	/*
1894002Sdarrenm 	 * Unregister from KCF if previous registration succeeded.
1904002Sdarrenm 	 */
1914002Sdarrenm 	if (md4_prov_handle != NULL) {
1924002Sdarrenm 		if ((ret = crypto_unregister_provider(md4_prov_handle)) !=
1934002Sdarrenm 		    CRYPTO_SUCCESS) {
1944002Sdarrenm 			cmn_err(CE_WARN, "md4 _fini: "
1954002Sdarrenm 			    "crypto_unregister_provider() failed (0x%x)", ret);
1964002Sdarrenm 			return (EBUSY);
1974002Sdarrenm 		}
1984002Sdarrenm 		md4_prov_handle = NULL;
1994002Sdarrenm 	}
2004002Sdarrenm 
2014002Sdarrenm 	return (mod_remove(&modlinkage));
2024002Sdarrenm }
2034002Sdarrenm 
2044002Sdarrenm int
2054002Sdarrenm _info(struct modinfo *modinfop)
2064002Sdarrenm {
2074002Sdarrenm 	return (mod_info(&modlinkage, modinfop));
2084002Sdarrenm }
2094002Sdarrenm 
2104002Sdarrenm /*
2114002Sdarrenm  * KCF software provider control entry points.
2124002Sdarrenm  */
2134002Sdarrenm /* ARGSUSED */
2144002Sdarrenm static void
2154002Sdarrenm md4_provider_status(crypto_provider_handle_t provider, uint_t *status)
2164002Sdarrenm {
2174002Sdarrenm 	*status = CRYPTO_PROVIDER_READY;
2184002Sdarrenm }
2194002Sdarrenm 
2204002Sdarrenm /*
2214002Sdarrenm  * KCF software provider digest entry points.
2224002Sdarrenm  */
2234002Sdarrenm 
2244002Sdarrenm static int
2254002Sdarrenm md4_digest_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
2264002Sdarrenm     crypto_req_handle_t req)
2274002Sdarrenm {
2284002Sdarrenm 	if (mechanism->cm_type != MD4_MECH_INFO_TYPE)
2294002Sdarrenm 		return (CRYPTO_MECHANISM_INVALID);
2304002Sdarrenm 
2314002Sdarrenm 	/*
2324002Sdarrenm 	 * Allocate and initialize MD4 context.
2334002Sdarrenm 	 */
2344002Sdarrenm 	ctx->cc_provider_private = kmem_alloc(sizeof (md4_ctx_t),
2354002Sdarrenm 	    crypto_kmflag(req));
2364002Sdarrenm 	if (ctx->cc_provider_private == NULL)
2374002Sdarrenm 		return (CRYPTO_HOST_MEMORY);
2384002Sdarrenm 
2394002Sdarrenm 	PROV_MD4_CTX(ctx)->mc_mech_type = MD4_MECH_INFO_TYPE;
2404002Sdarrenm 	MD4Init(&PROV_MD4_CTX(ctx)->mc_md4_ctx);
2414002Sdarrenm 
2424002Sdarrenm 	return (CRYPTO_SUCCESS);
2434002Sdarrenm }
2444002Sdarrenm 
2454002Sdarrenm /*
2464002Sdarrenm  * Helper MD4 digest update function for uio data.
2474002Sdarrenm  */
2484002Sdarrenm static int
2494002Sdarrenm md4_digest_update_uio(MD4_CTX *md4_ctx, crypto_data_t *data)
2504002Sdarrenm {
2514002Sdarrenm 	off_t offset = data->cd_offset;
2524002Sdarrenm 	size_t length = data->cd_length;
2534002Sdarrenm 	uint_t vec_idx;
2544002Sdarrenm 	size_t cur_len;
2554002Sdarrenm 
2564002Sdarrenm 	/* we support only kernel buffer */
2574002Sdarrenm 	if (data->cd_uio->uio_segflg != UIO_SYSSPACE)
2584002Sdarrenm 		return (CRYPTO_ARGUMENTS_BAD);
2594002Sdarrenm 
2604002Sdarrenm 	/*
2614002Sdarrenm 	 * Jump to the first iovec containing data to be
2624002Sdarrenm 	 * digested.
2634002Sdarrenm 	 */
2644002Sdarrenm 	for (vec_idx = 0; vec_idx < data->cd_uio->uio_iovcnt &&
2654002Sdarrenm 	    offset >= data->cd_uio->uio_iov[vec_idx].iov_len;
266*5072Smcpowers 	    offset -= data->cd_uio->uio_iov[vec_idx++].iov_len)
267*5072Smcpowers 		;
2684002Sdarrenm 	if (vec_idx == data->cd_uio->uio_iovcnt) {
2694002Sdarrenm 		/*
2704002Sdarrenm 		 * The caller specified an offset that is larger than the
2714002Sdarrenm 		 * total size of the buffers it provided.
2724002Sdarrenm 		 */
2734002Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
2744002Sdarrenm 	}
2754002Sdarrenm 
2764002Sdarrenm 	/*
2774002Sdarrenm 	 * Now do the digesting on the iovecs.
2784002Sdarrenm 	 */
2794002Sdarrenm 	while (vec_idx < data->cd_uio->uio_iovcnt && length > 0) {
2804002Sdarrenm 		cur_len = MIN(data->cd_uio->uio_iov[vec_idx].iov_len -
2814002Sdarrenm 		    offset, length);
2824002Sdarrenm 
2834002Sdarrenm 		MD4Update(md4_ctx, data->cd_uio->uio_iov[vec_idx].iov_base +
2844002Sdarrenm 		    offset, cur_len);
2854002Sdarrenm 
2864002Sdarrenm 		length -= cur_len;
2874002Sdarrenm 		vec_idx++;
2884002Sdarrenm 		offset = 0;
2894002Sdarrenm 	}
2904002Sdarrenm 
2914002Sdarrenm 	if (vec_idx == data->cd_uio->uio_iovcnt && length > 0) {
2924002Sdarrenm 		/*
2934002Sdarrenm 		 * The end of the specified iovec's was reached but
2944002Sdarrenm 		 * the length requested could not be processed, i.e.
2954002Sdarrenm 		 * The caller requested to digest more data than it provided.
2964002Sdarrenm 		 */
2974002Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
2984002Sdarrenm 	}
2994002Sdarrenm 
3004002Sdarrenm 	return (CRYPTO_SUCCESS);
3014002Sdarrenm }
3024002Sdarrenm 
3034002Sdarrenm /*
3044002Sdarrenm  * Helper MD4 digest final function for uio data.
3054002Sdarrenm  * digest_len is the length of the desired digest. If digest_len
3064002Sdarrenm  * is smaller than the default MD4 digest length, the caller
3074002Sdarrenm  * must pass a scratch buffer, digest_scratch, which must
3084002Sdarrenm  * be at least MD4_DIGEST_LENGTH bytes.
3094002Sdarrenm  */
3104002Sdarrenm static int
3114002Sdarrenm md4_digest_final_uio(MD4_CTX *md4_ctx, crypto_data_t *digest,
3124002Sdarrenm     ulong_t digest_len, uchar_t *digest_scratch)
3134002Sdarrenm {
3144002Sdarrenm 	off_t offset = digest->cd_offset;
3154002Sdarrenm 	uint_t vec_idx;
3164002Sdarrenm 
3174002Sdarrenm 	/* we support only kernel buffer */
3184002Sdarrenm 	if (digest->cd_uio->uio_segflg != UIO_SYSSPACE)
3194002Sdarrenm 		return (CRYPTO_ARGUMENTS_BAD);
3204002Sdarrenm 
3214002Sdarrenm 	/*
3224002Sdarrenm 	 * Jump to the first iovec containing ptr to the digest to
3234002Sdarrenm 	 * be returned.
3244002Sdarrenm 	 */
3254002Sdarrenm 	for (vec_idx = 0; offset >= digest->cd_uio->uio_iov[vec_idx].iov_len &&
3264002Sdarrenm 	    vec_idx < digest->cd_uio->uio_iovcnt;
327*5072Smcpowers 	    offset -= digest->cd_uio->uio_iov[vec_idx++].iov_len)
328*5072Smcpowers 		;
3294002Sdarrenm 	if (vec_idx == digest->cd_uio->uio_iovcnt) {
3304002Sdarrenm 		/*
3314002Sdarrenm 		 * The caller specified an offset that is
3324002Sdarrenm 		 * larger than the total size of the buffers
3334002Sdarrenm 		 * it provided.
3344002Sdarrenm 		 */
3354002Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
3364002Sdarrenm 	}
3374002Sdarrenm 
3384002Sdarrenm 	if (offset + digest_len <=
3394002Sdarrenm 	    digest->cd_uio->uio_iov[vec_idx].iov_len) {
3404002Sdarrenm 		/*
3414002Sdarrenm 		 * The computed MD4 digest will fit in the current
3424002Sdarrenm 		 * iovec.
3434002Sdarrenm 		 */
3444002Sdarrenm 		if (digest_len != MD4_DIGEST_LENGTH) {
3454002Sdarrenm 			/*
3464002Sdarrenm 			 * The caller requested a short digest. Digest
3474002Sdarrenm 			 * into a scratch buffer and return to
3484002Sdarrenm 			 * the user only what was requested.
3494002Sdarrenm 			 */
3504002Sdarrenm 			MD4Final(digest_scratch, md4_ctx);
3514002Sdarrenm 			bcopy(digest_scratch, (uchar_t *)digest->
3524002Sdarrenm 			    cd_uio->uio_iov[vec_idx].iov_base + offset,
3534002Sdarrenm 			    digest_len);
3544002Sdarrenm 		} else {
3554002Sdarrenm 			MD4Final((uchar_t *)digest->
3564002Sdarrenm 			    cd_uio->uio_iov[vec_idx].iov_base + offset,
3574002Sdarrenm 			    md4_ctx);
3584002Sdarrenm 		}
3594002Sdarrenm 	} else {
3604002Sdarrenm 		/*
3614002Sdarrenm 		 * The computed digest will be crossing one or more iovec's.
3624002Sdarrenm 		 * This is bad performance-wise but we need to support it.
3634002Sdarrenm 		 * Allocate a small scratch buffer on the stack and
3644002Sdarrenm 		 * copy it piece meal to the specified digest iovec's.
3654002Sdarrenm 		 */
3664002Sdarrenm 		uchar_t digest_tmp[MD4_DIGEST_LENGTH];
3674002Sdarrenm 		off_t scratch_offset = 0;
3684002Sdarrenm 		size_t length = digest_len;
3694002Sdarrenm 		size_t cur_len;
3704002Sdarrenm 
3714002Sdarrenm 		MD4Final(digest_tmp, md4_ctx);
3724002Sdarrenm 
3734002Sdarrenm 		while (vec_idx < digest->cd_uio->uio_iovcnt && length > 0) {
3744002Sdarrenm 			cur_len = MIN(digest->cd_uio->uio_iov[vec_idx].iov_len -
3754002Sdarrenm 			    offset, length);
3764002Sdarrenm 			bcopy(digest_tmp + scratch_offset,
3774002Sdarrenm 			    digest->cd_uio->uio_iov[vec_idx].iov_base + offset,
3784002Sdarrenm 			    cur_len);
3794002Sdarrenm 
3804002Sdarrenm 			length -= cur_len;
3814002Sdarrenm 			vec_idx++;
3824002Sdarrenm 			scratch_offset += cur_len;
3834002Sdarrenm 			offset = 0;
3844002Sdarrenm 		}
3854002Sdarrenm 
3864002Sdarrenm 		if (vec_idx == digest->cd_uio->uio_iovcnt && length > 0) {
3874002Sdarrenm 			/*
3884002Sdarrenm 			 * The end of the specified iovec's was reached but
3894002Sdarrenm 			 * the length requested could not be processed, i.e.
3904002Sdarrenm 			 * The caller requested to digest more data than it
3914002Sdarrenm 			 * provided.
3924002Sdarrenm 			 */
3934002Sdarrenm 			return (CRYPTO_DATA_LEN_RANGE);
3944002Sdarrenm 		}
3954002Sdarrenm 	}
3964002Sdarrenm 
3974002Sdarrenm 	return (CRYPTO_SUCCESS);
3984002Sdarrenm }
3994002Sdarrenm 
4004002Sdarrenm /*
4014002Sdarrenm  * Helper MD4 digest update for mblk's.
4024002Sdarrenm  */
4034002Sdarrenm static int
4044002Sdarrenm md4_digest_update_mblk(MD4_CTX *md4_ctx, crypto_data_t *data)
4054002Sdarrenm {
4064002Sdarrenm 	off_t offset = data->cd_offset;
4074002Sdarrenm 	size_t length = data->cd_length;
4084002Sdarrenm 	mblk_t *mp;
4094002Sdarrenm 	size_t cur_len;
4104002Sdarrenm 
4114002Sdarrenm 	/*
4124002Sdarrenm 	 * Jump to the first mblk_t containing data to be digested.
4134002Sdarrenm 	 */
4144002Sdarrenm 	for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp);
415*5072Smcpowers 	    offset -= MBLKL(mp), mp = mp->b_cont)
416*5072Smcpowers 		;
4174002Sdarrenm 	if (mp == NULL) {
4184002Sdarrenm 		/*
4194002Sdarrenm 		 * The caller specified an offset that is larger than the
4204002Sdarrenm 		 * total size of the buffers it provided.
4214002Sdarrenm 		 */
4224002Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
4234002Sdarrenm 	}
4244002Sdarrenm 
4254002Sdarrenm 	/*
4264002Sdarrenm 	 * Now do the digesting on the mblk chain.
4274002Sdarrenm 	 */
4284002Sdarrenm 	while (mp != NULL && length > 0) {
4294002Sdarrenm 		cur_len = MIN(MBLKL(mp) - offset, length);
4304002Sdarrenm 		MD4Update(md4_ctx, mp->b_rptr + offset, cur_len);
4314002Sdarrenm 		length -= cur_len;
4324002Sdarrenm 		offset = 0;
4334002Sdarrenm 		mp = mp->b_cont;
4344002Sdarrenm 	}
4354002Sdarrenm 
4364002Sdarrenm 	if (mp == NULL && length > 0) {
4374002Sdarrenm 		/*
4384002Sdarrenm 		 * The end of the mblk was reached but the length requested
4394002Sdarrenm 		 * could not be processed, i.e. The caller requested
4404002Sdarrenm 		 * to digest more data than it provided.
4414002Sdarrenm 		 */
4424002Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
4434002Sdarrenm 	}
4444002Sdarrenm 
4454002Sdarrenm 	return (CRYPTO_SUCCESS);
4464002Sdarrenm }
4474002Sdarrenm 
4484002Sdarrenm /*
4494002Sdarrenm  * Helper MD4 digest final for mblk's.
4504002Sdarrenm  * digest_len is the length of the desired digest. If digest_len
4514002Sdarrenm  * is smaller than the default MD4 digest length, the caller
4524002Sdarrenm  * must pass a scratch buffer, digest_scratch, which must
4534002Sdarrenm  * be at least MD4_DIGEST_LENGTH bytes.
4544002Sdarrenm  */
4554002Sdarrenm static int
4564002Sdarrenm md4_digest_final_mblk(MD4_CTX *md4_ctx, crypto_data_t *digest,
4574002Sdarrenm     ulong_t digest_len, uchar_t *digest_scratch)
4584002Sdarrenm {
4594002Sdarrenm 	off_t offset = digest->cd_offset;
4604002Sdarrenm 	mblk_t *mp;
4614002Sdarrenm 
4624002Sdarrenm 	/*
4634002Sdarrenm 	 * Jump to the first mblk_t that will be used to store the digest.
4644002Sdarrenm 	 */
4654002Sdarrenm 	for (mp = digest->cd_mp; mp != NULL && offset >= MBLKL(mp);
466*5072Smcpowers 	    offset -= MBLKL(mp), mp = mp->b_cont)
467*5072Smcpowers 		;
4684002Sdarrenm 	if (mp == NULL) {
4694002Sdarrenm 		/*
4704002Sdarrenm 		 * The caller specified an offset that is larger than the
4714002Sdarrenm 		 * total size of the buffers it provided.
4724002Sdarrenm 		 */
4734002Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
4744002Sdarrenm 	}
4754002Sdarrenm 
4764002Sdarrenm 	if (offset + digest_len <= MBLKL(mp)) {
4774002Sdarrenm 		/*
4784002Sdarrenm 		 * The computed MD4 digest will fit in the current mblk.
4794002Sdarrenm 		 * Do the MD4Final() in-place.
4804002Sdarrenm 		 */
4814002Sdarrenm 		if (digest_len != MD4_DIGEST_LENGTH) {
4824002Sdarrenm 			/*
4834002Sdarrenm 			 * The caller requested a short digest. Digest
4844002Sdarrenm 			 * into a scratch buffer and return to
4854002Sdarrenm 			 * the user only what was requested.
4864002Sdarrenm 			 */
4874002Sdarrenm 			MD4Final(digest_scratch, md4_ctx);
4884002Sdarrenm 			bcopy(digest_scratch, mp->b_rptr + offset, digest_len);
4894002Sdarrenm 		} else {
4904002Sdarrenm 			MD4Final(mp->b_rptr + offset, md4_ctx);
4914002Sdarrenm 		}
4924002Sdarrenm 	} else {
4934002Sdarrenm 		/*
4944002Sdarrenm 		 * The computed digest will be crossing one or more mblk's.
4954002Sdarrenm 		 * This is bad performance-wise but we need to support it.
4964002Sdarrenm 		 * Allocate a small scratch buffer on the stack and
4974002Sdarrenm 		 * copy it piece meal to the specified digest iovec's.
4984002Sdarrenm 		 */
4994002Sdarrenm 		uchar_t digest_tmp[MD4_DIGEST_LENGTH];
5004002Sdarrenm 		off_t scratch_offset = 0;
5014002Sdarrenm 		size_t length = digest_len;
5024002Sdarrenm 		size_t cur_len;
5034002Sdarrenm 
5044002Sdarrenm 		MD4Final(digest_tmp, md4_ctx);
5054002Sdarrenm 
5064002Sdarrenm 		while (mp != NULL && length > 0) {
5074002Sdarrenm 			cur_len = MIN(MBLKL(mp) - offset, length);
5084002Sdarrenm 			bcopy(digest_tmp + scratch_offset,
5094002Sdarrenm 			    mp->b_rptr + offset, cur_len);
5104002Sdarrenm 
5114002Sdarrenm 			length -= cur_len;
5124002Sdarrenm 			mp = mp->b_cont;
5134002Sdarrenm 			scratch_offset += cur_len;
5144002Sdarrenm 			offset = 0;
5154002Sdarrenm 		}
5164002Sdarrenm 
5174002Sdarrenm 		if (mp == NULL && length > 0) {
5184002Sdarrenm 			/*
5194002Sdarrenm 			 * The end of the specified mblk was reached but
5204002Sdarrenm 			 * the length requested could not be processed, i.e.
5214002Sdarrenm 			 * The caller requested to digest more data than it
5224002Sdarrenm 			 * provided.
5234002Sdarrenm 			 */
5244002Sdarrenm 			return (CRYPTO_DATA_LEN_RANGE);
5254002Sdarrenm 		}
5264002Sdarrenm 	}
5274002Sdarrenm 
5284002Sdarrenm 	return (CRYPTO_SUCCESS);
5294002Sdarrenm }
5304002Sdarrenm 
5314002Sdarrenm /* ARGSUSED */
5324002Sdarrenm static int
5334002Sdarrenm md4_digest(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *digest,
5344002Sdarrenm     crypto_req_handle_t req)
5354002Sdarrenm {
5364002Sdarrenm 	int ret = CRYPTO_SUCCESS;
5374002Sdarrenm 
5384002Sdarrenm 	ASSERT(ctx->cc_provider_private != NULL);
5394002Sdarrenm 
5404002Sdarrenm 	/*
5414002Sdarrenm 	 * We need to just return the length needed to store the output.
5424002Sdarrenm 	 * We should not destroy the context for the following cases.
5434002Sdarrenm 	 */
5444002Sdarrenm 	if ((digest->cd_length == 0) ||
5454002Sdarrenm 	    (digest->cd_length < MD4_DIGEST_LENGTH)) {
5464002Sdarrenm 		digest->cd_length = MD4_DIGEST_LENGTH;
5474002Sdarrenm 		return (CRYPTO_BUFFER_TOO_SMALL);
5484002Sdarrenm 	}
5494002Sdarrenm 
5504002Sdarrenm 	/*
5514002Sdarrenm 	 * Do the MD4 update on the specified input data.
5524002Sdarrenm 	 */
5534002Sdarrenm 	switch (data->cd_format) {
5544002Sdarrenm 	case CRYPTO_DATA_RAW:
5554002Sdarrenm 		MD4Update(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
5564002Sdarrenm 		    data->cd_raw.iov_base + data->cd_offset,
5574002Sdarrenm 		    data->cd_length);
5584002Sdarrenm 		break;
5594002Sdarrenm 	case CRYPTO_DATA_UIO:
5604002Sdarrenm 		ret = md4_digest_update_uio(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
5614002Sdarrenm 		    data);
5624002Sdarrenm 		break;
5634002Sdarrenm 	case CRYPTO_DATA_MBLK:
5644002Sdarrenm 		ret = md4_digest_update_mblk(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
5654002Sdarrenm 		    data);
5664002Sdarrenm 		break;
5674002Sdarrenm 	default:
5684002Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
5694002Sdarrenm 	}
5704002Sdarrenm 
5714002Sdarrenm 	if (ret != CRYPTO_SUCCESS) {
5724002Sdarrenm 		/* the update failed, free context and bail */
5734002Sdarrenm 		kmem_free(ctx->cc_provider_private, sizeof (md4_ctx_t));
5744002Sdarrenm 		ctx->cc_provider_private = NULL;
5754002Sdarrenm 		digest->cd_length = 0;
5764002Sdarrenm 		return (ret);
5774002Sdarrenm 	}
5784002Sdarrenm 
5794002Sdarrenm 	/*
5804002Sdarrenm 	 * Do an MD4 final, must be done separately since the digest
5814002Sdarrenm 	 * type can be different than the input data type.
5824002Sdarrenm 	 */
5834002Sdarrenm 	switch (digest->cd_format) {
5844002Sdarrenm 	case CRYPTO_DATA_RAW:
5854002Sdarrenm 		MD4Final((unsigned char *)digest->cd_raw.iov_base +
5864002Sdarrenm 		    digest->cd_offset, &PROV_MD4_CTX(ctx)->mc_md4_ctx);
5874002Sdarrenm 		break;
5884002Sdarrenm 	case CRYPTO_DATA_UIO:
5894002Sdarrenm 		ret = md4_digest_final_uio(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
5904002Sdarrenm 		    digest, MD4_DIGEST_LENGTH, NULL);
5914002Sdarrenm 		break;
5924002Sdarrenm 	case CRYPTO_DATA_MBLK:
5934002Sdarrenm 		ret = md4_digest_final_mblk(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
5944002Sdarrenm 		    digest, MD4_DIGEST_LENGTH, NULL);
5954002Sdarrenm 		break;
5964002Sdarrenm 	default:
5974002Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
5984002Sdarrenm 	}
5994002Sdarrenm 
6004002Sdarrenm 	/* all done, free context and return */
6014002Sdarrenm 
6024002Sdarrenm 	if (ret == CRYPTO_SUCCESS) {
6034002Sdarrenm 		digest->cd_length = MD4_DIGEST_LENGTH;
6044002Sdarrenm 	} else {
6054002Sdarrenm 		digest->cd_length = 0;
6064002Sdarrenm 	}
6074002Sdarrenm 
6084002Sdarrenm 	kmem_free(ctx->cc_provider_private, sizeof (md4_ctx_t));
6094002Sdarrenm 	ctx->cc_provider_private = NULL;
6104002Sdarrenm 	return (ret);
6114002Sdarrenm }
6124002Sdarrenm 
6134002Sdarrenm /* ARGSUSED */
6144002Sdarrenm static int
6154002Sdarrenm md4_digest_update(crypto_ctx_t *ctx, crypto_data_t *data,
6164002Sdarrenm     crypto_req_handle_t req)
6174002Sdarrenm {
6184002Sdarrenm 	int ret = CRYPTO_SUCCESS;
6194002Sdarrenm 
6204002Sdarrenm 	ASSERT(ctx->cc_provider_private != NULL);
6214002Sdarrenm 
6224002Sdarrenm 	/*
6234002Sdarrenm 	 * Do the MD4 update on the specified input data.
6244002Sdarrenm 	 */
6254002Sdarrenm 	switch (data->cd_format) {
6264002Sdarrenm 	case CRYPTO_DATA_RAW:
6274002Sdarrenm 		MD4Update(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
6284002Sdarrenm 		    data->cd_raw.iov_base + data->cd_offset,
6294002Sdarrenm 		    data->cd_length);
6304002Sdarrenm 		break;
6314002Sdarrenm 	case CRYPTO_DATA_UIO:
6324002Sdarrenm 		ret = md4_digest_update_uio(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
6334002Sdarrenm 		    data);
6344002Sdarrenm 		break;
6354002Sdarrenm 	case CRYPTO_DATA_MBLK:
6364002Sdarrenm 		ret = md4_digest_update_mblk(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
6374002Sdarrenm 		    data);
6384002Sdarrenm 		break;
6394002Sdarrenm 	default:
6404002Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
6414002Sdarrenm 	}
6424002Sdarrenm 
6434002Sdarrenm 	return (ret);
6444002Sdarrenm }
6454002Sdarrenm 
6464002Sdarrenm /* ARGSUSED */
6474002Sdarrenm static int
6484002Sdarrenm md4_digest_final(crypto_ctx_t *ctx, crypto_data_t *digest,
6494002Sdarrenm     crypto_req_handle_t req)
6504002Sdarrenm {
6514002Sdarrenm 	int ret = CRYPTO_SUCCESS;
6524002Sdarrenm 
6534002Sdarrenm 	ASSERT(ctx->cc_provider_private != NULL);
6544002Sdarrenm 
6554002Sdarrenm 	/*
6564002Sdarrenm 	 * We need to just return the length needed to store the output.
6574002Sdarrenm 	 * We should not destroy the context for the following cases.
6584002Sdarrenm 	 */
6594002Sdarrenm 	if ((digest->cd_length == 0) ||
6604002Sdarrenm 	    (digest->cd_length < MD4_DIGEST_LENGTH)) {
6614002Sdarrenm 		digest->cd_length = MD4_DIGEST_LENGTH;
6624002Sdarrenm 		return (CRYPTO_BUFFER_TOO_SMALL);
6634002Sdarrenm 	}
6644002Sdarrenm 
6654002Sdarrenm 	/*
6664002Sdarrenm 	 * Do an MD4 final.
6674002Sdarrenm 	 */
6684002Sdarrenm 	switch (digest->cd_format) {
6694002Sdarrenm 	case CRYPTO_DATA_RAW:
6704002Sdarrenm 		MD4Final((unsigned char *)digest->cd_raw.iov_base +
6714002Sdarrenm 		    digest->cd_offset, &PROV_MD4_CTX(ctx)->mc_md4_ctx);
6724002Sdarrenm 		break;
6734002Sdarrenm 	case CRYPTO_DATA_UIO:
6744002Sdarrenm 		ret = md4_digest_final_uio(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
6754002Sdarrenm 		    digest, MD4_DIGEST_LENGTH, NULL);
6764002Sdarrenm 		break;
6774002Sdarrenm 	case CRYPTO_DATA_MBLK:
6784002Sdarrenm 		ret = md4_digest_final_mblk(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
6794002Sdarrenm 		    digest, MD4_DIGEST_LENGTH, NULL);
6804002Sdarrenm 		break;
6814002Sdarrenm 	default:
6824002Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
6834002Sdarrenm 	}
6844002Sdarrenm 
6854002Sdarrenm 	/* all done, free context and return */
6864002Sdarrenm 
6874002Sdarrenm 	if (ret == CRYPTO_SUCCESS) {
6884002Sdarrenm 		digest->cd_length = MD4_DIGEST_LENGTH;
6894002Sdarrenm 	} else {
6904002Sdarrenm 		digest->cd_length = 0;
6914002Sdarrenm 	}
6924002Sdarrenm 
6934002Sdarrenm 	kmem_free(ctx->cc_provider_private, sizeof (md4_ctx_t));
6944002Sdarrenm 	ctx->cc_provider_private = NULL;
6954002Sdarrenm 
6964002Sdarrenm 	return (ret);
6974002Sdarrenm }
6984002Sdarrenm 
6994002Sdarrenm /* ARGSUSED */
7004002Sdarrenm static int
7014002Sdarrenm md4_digest_atomic(crypto_provider_handle_t provider,
7024002Sdarrenm     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
7034002Sdarrenm     crypto_data_t *data, crypto_data_t *digest,
7044002Sdarrenm     crypto_req_handle_t req)
7054002Sdarrenm {
7064002Sdarrenm 	int ret = CRYPTO_SUCCESS;
7074002Sdarrenm 	MD4_CTX md4_ctx;
7084002Sdarrenm 
7094002Sdarrenm 	if (mechanism->cm_type != MD4_MECH_INFO_TYPE)
7104002Sdarrenm 		return (CRYPTO_MECHANISM_INVALID);
7114002Sdarrenm 
7124002Sdarrenm 	/*
7134002Sdarrenm 	 * Do the MD4 init.
7144002Sdarrenm 	 */
7154002Sdarrenm 	MD4Init(&md4_ctx);
7164002Sdarrenm 
7174002Sdarrenm 	/*
7184002Sdarrenm 	 * Do the MD4 update on the specified input data.
7194002Sdarrenm 	 */
7204002Sdarrenm 	switch (data->cd_format) {
7214002Sdarrenm 	case CRYPTO_DATA_RAW:
7224002Sdarrenm 		MD4Update(&md4_ctx, data->cd_raw.iov_base + data->cd_offset,
7234002Sdarrenm 		    data->cd_length);
7244002Sdarrenm 		break;
7254002Sdarrenm 	case CRYPTO_DATA_UIO:
7264002Sdarrenm 		ret = md4_digest_update_uio(&md4_ctx, data);
7274002Sdarrenm 		break;
7284002Sdarrenm 	case CRYPTO_DATA_MBLK:
7294002Sdarrenm 		ret = md4_digest_update_mblk(&md4_ctx, data);
7304002Sdarrenm 		break;
7314002Sdarrenm 	default:
7324002Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
7334002Sdarrenm 	}
7344002Sdarrenm 
7354002Sdarrenm 	if (ret != CRYPTO_SUCCESS) {
7364002Sdarrenm 		/* the update failed, bail */
7374002Sdarrenm 		digest->cd_length = 0;
7384002Sdarrenm 		return (ret);
7394002Sdarrenm 	}
7404002Sdarrenm 
7414002Sdarrenm 	/*
7424002Sdarrenm 	 * Do an MD4 final, must be done separately since the digest
7434002Sdarrenm 	 * type can be different than the input data type.
7444002Sdarrenm 	 */
7454002Sdarrenm 	switch (digest->cd_format) {
7464002Sdarrenm 	case CRYPTO_DATA_RAW:
7474002Sdarrenm 		MD4Final((unsigned char *)digest->cd_raw.iov_base +
7484002Sdarrenm 		    digest->cd_offset, &md4_ctx);
7494002Sdarrenm 		break;
7504002Sdarrenm 	case CRYPTO_DATA_UIO:
7514002Sdarrenm 		ret = md4_digest_final_uio(&md4_ctx, digest,
7524002Sdarrenm 		    MD4_DIGEST_LENGTH, NULL);
7534002Sdarrenm 		break;
7544002Sdarrenm 	case CRYPTO_DATA_MBLK:
7554002Sdarrenm 		ret = md4_digest_final_mblk(&md4_ctx, digest,
7564002Sdarrenm 		    MD4_DIGEST_LENGTH, NULL);
7574002Sdarrenm 		break;
7584002Sdarrenm 	default:
7594002Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
7604002Sdarrenm 	}
7614002Sdarrenm 
7624002Sdarrenm 	if (ret == CRYPTO_SUCCESS) {
7634002Sdarrenm 		digest->cd_length = MD4_DIGEST_LENGTH;
7644002Sdarrenm 	} else {
7654002Sdarrenm 		digest->cd_length = 0;
7664002Sdarrenm 	}
7674002Sdarrenm 
7684002Sdarrenm 	return (ret);
7694002Sdarrenm }
770