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