xref: /titanic_52/usr/src/uts/common/crypto/io/md4_mod.c (revision d3b2efc749bec3b757d5f018cf78c9a09fa29cb3)
15151fb12Sdarrenm /*
25151fb12Sdarrenm  * CDDL HEADER START
35151fb12Sdarrenm  *
45151fb12Sdarrenm  * The contents of this file are subject to the terms of the
55151fb12Sdarrenm  * Common Development and Distribution License (the "License").
65151fb12Sdarrenm  * You may not use this file except in compliance with the License.
75151fb12Sdarrenm  *
85151fb12Sdarrenm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95151fb12Sdarrenm  * or http://www.opensolaris.org/os/licensing.
105151fb12Sdarrenm  * See the License for the specific language governing permissions
115151fb12Sdarrenm  * and limitations under the License.
125151fb12Sdarrenm  *
135151fb12Sdarrenm  * When distributing Covered Code, include this CDDL HEADER in each
145151fb12Sdarrenm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155151fb12Sdarrenm  * If applicable, add the following below this CDDL HEADER, with the
165151fb12Sdarrenm  * fields enclosed by brackets "[]" replaced with your own identifying
175151fb12Sdarrenm  * information: Portions Copyright [yyyy] [name of copyright owner]
185151fb12Sdarrenm  *
195151fb12Sdarrenm  * CDDL HEADER END
205151fb12Sdarrenm  */
215151fb12Sdarrenm 
225151fb12Sdarrenm /*
23*d3b2efc7SAnthony Scarpino  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
245151fb12Sdarrenm  * Use is subject to license terms.
255151fb12Sdarrenm  */
265151fb12Sdarrenm 
275151fb12Sdarrenm /*
285151fb12Sdarrenm  * In kernel module, the md4 module is created with one modlinkage,
295151fb12Sdarrenm  * this is different to md5 and sha1 modules which have a legacy misc
305151fb12Sdarrenm  * variant for direct calls to the Init/Update/Final routines.
315151fb12Sdarrenm  *
325151fb12Sdarrenm  * - a modlcrypto that allows the module to register with the Kernel
335151fb12Sdarrenm  *   Cryptographic Framework (KCF) as a software provider for the MD4
345151fb12Sdarrenm  *   mechanisms.
355151fb12Sdarrenm  */
365151fb12Sdarrenm 
375151fb12Sdarrenm #include <sys/types.h>
385151fb12Sdarrenm #include <sys/systm.h>
395151fb12Sdarrenm #include <sys/modctl.h>
405151fb12Sdarrenm #include <sys/cmn_err.h>
415151fb12Sdarrenm #include <sys/ddi.h>
425151fb12Sdarrenm #include <sys/crypto/common.h>
435151fb12Sdarrenm #include <sys/crypto/spi.h>
445151fb12Sdarrenm #include <sys/sysmacros.h>
455151fb12Sdarrenm #include <sys/strsun.h>
465151fb12Sdarrenm #include <sys/note.h>
475151fb12Sdarrenm #include <sys/md4.h>
485151fb12Sdarrenm 
495151fb12Sdarrenm extern struct mod_ops mod_miscops;
505151fb12Sdarrenm extern struct mod_ops mod_cryptoops;
515151fb12Sdarrenm 
525151fb12Sdarrenm /*
535151fb12Sdarrenm  * Module linkage information for the kernel.
545151fb12Sdarrenm  */
555151fb12Sdarrenm 
565151fb12Sdarrenm static struct modlcrypto modlcrypto = {
575151fb12Sdarrenm 	&mod_cryptoops,
58d2b32306Smcpowers 	"MD4 Kernel SW Provider"
595151fb12Sdarrenm };
605151fb12Sdarrenm 
615151fb12Sdarrenm static struct modlinkage modlinkage = {
625151fb12Sdarrenm 	MODREV_1,
635151fb12Sdarrenm 	(void *)&modlcrypto,
645151fb12Sdarrenm 	NULL
655151fb12Sdarrenm };
665151fb12Sdarrenm 
675151fb12Sdarrenm /*
685151fb12Sdarrenm  * CSPI information (entry points, provider info, etc.)
695151fb12Sdarrenm  */
705151fb12Sdarrenm 
715151fb12Sdarrenm typedef enum md4_mech_type {
725151fb12Sdarrenm 	MD4_MECH_INFO_TYPE,		/* SUN_CKM_MD4 */
735151fb12Sdarrenm } md4_mech_type_t;
745151fb12Sdarrenm 
755151fb12Sdarrenm #define	MD4_DIGEST_LENGTH	16	/* MD4 digest length in bytes */
765151fb12Sdarrenm 
775151fb12Sdarrenm /*
785151fb12Sdarrenm  * Context for MD4 mechanism.
795151fb12Sdarrenm  */
805151fb12Sdarrenm typedef struct md4_ctx {
815151fb12Sdarrenm 	md4_mech_type_t		mc_mech_type;	/* type of context */
825151fb12Sdarrenm 	MD4_CTX			mc_md4_ctx;	/* MD4 context */
835151fb12Sdarrenm } md4_ctx_t;
845151fb12Sdarrenm 
855151fb12Sdarrenm /*
865151fb12Sdarrenm  * Macros to access the MD4 contexts from a context passed
875151fb12Sdarrenm  * by KCF to one of the entry points.
885151fb12Sdarrenm  */
895151fb12Sdarrenm 
905151fb12Sdarrenm #define	PROV_MD4_CTX(ctx)	((md4_ctx_t *)(ctx)->cc_provider_private)
915151fb12Sdarrenm 
925151fb12Sdarrenm /*
935151fb12Sdarrenm  * Mechanism info structure passed to KCF during registration.
945151fb12Sdarrenm  */
955151fb12Sdarrenm static crypto_mech_info_t md4_mech_info_tab[] = {
965151fb12Sdarrenm 	/* MD4 */
975151fb12Sdarrenm 	{SUN_CKM_MD4, MD4_MECH_INFO_TYPE,
985151fb12Sdarrenm 	    CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC,
995151fb12Sdarrenm 	    0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
1005151fb12Sdarrenm };
1015151fb12Sdarrenm 
1025151fb12Sdarrenm static void md4_provider_status(crypto_provider_handle_t, uint_t *);
1035151fb12Sdarrenm 
1045151fb12Sdarrenm static crypto_control_ops_t md4_control_ops = {
1055151fb12Sdarrenm 	md4_provider_status
1065151fb12Sdarrenm };
1075151fb12Sdarrenm 
1085151fb12Sdarrenm static int md4_digest_init(crypto_ctx_t *, crypto_mechanism_t *,
1095151fb12Sdarrenm     crypto_req_handle_t);
1105151fb12Sdarrenm static int md4_digest(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
1115151fb12Sdarrenm     crypto_req_handle_t);
1125151fb12Sdarrenm static int md4_digest_update(crypto_ctx_t *, crypto_data_t *,
1135151fb12Sdarrenm     crypto_req_handle_t);
1145151fb12Sdarrenm static int md4_digest_final(crypto_ctx_t *, crypto_data_t *,
1155151fb12Sdarrenm     crypto_req_handle_t);
1165151fb12Sdarrenm static int md4_digest_atomic(crypto_provider_handle_t, crypto_session_id_t,
1175151fb12Sdarrenm     crypto_mechanism_t *, crypto_data_t *, crypto_data_t *,
1185151fb12Sdarrenm     crypto_req_handle_t);
1195151fb12Sdarrenm 
1205151fb12Sdarrenm static crypto_digest_ops_t md4_digest_ops = {
1215151fb12Sdarrenm 	md4_digest_init,
1225151fb12Sdarrenm 	md4_digest,
1235151fb12Sdarrenm 	md4_digest_update,
1245151fb12Sdarrenm 	NULL,
1255151fb12Sdarrenm 	md4_digest_final,
1265151fb12Sdarrenm 	md4_digest_atomic
1275151fb12Sdarrenm };
1285151fb12Sdarrenm 
1295151fb12Sdarrenm static crypto_ops_t md4_crypto_ops = {
1305151fb12Sdarrenm 	&md4_control_ops,
1315151fb12Sdarrenm 	&md4_digest_ops,
1325151fb12Sdarrenm 	NULL,
1335151fb12Sdarrenm 	NULL,
1345151fb12Sdarrenm 	NULL,
1355151fb12Sdarrenm 	NULL,
1365151fb12Sdarrenm 	NULL,
1375151fb12Sdarrenm 	NULL,
1385151fb12Sdarrenm 	NULL,
1395151fb12Sdarrenm 	NULL,
1405151fb12Sdarrenm 	NULL,
1415151fb12Sdarrenm 	NULL,
1425151fb12Sdarrenm 	NULL,
1435151fb12Sdarrenm 	NULL,
1445151fb12Sdarrenm };
1455151fb12Sdarrenm 
1465151fb12Sdarrenm static crypto_provider_info_t md4_prov_info = {
1475151fb12Sdarrenm 	CRYPTO_SPI_VERSION_1,
1485151fb12Sdarrenm 	"MD4 Software Provider",
1495151fb12Sdarrenm 	CRYPTO_SW_PROVIDER,
1505151fb12Sdarrenm 	{&modlinkage},
1515151fb12Sdarrenm 	NULL,
1525151fb12Sdarrenm 	&md4_crypto_ops,
1535151fb12Sdarrenm 	sizeof (md4_mech_info_tab)/sizeof (crypto_mech_info_t),
1545151fb12Sdarrenm 	md4_mech_info_tab
1555151fb12Sdarrenm };
1565151fb12Sdarrenm 
1575151fb12Sdarrenm static crypto_kcf_provider_handle_t md4_prov_handle = NULL;
1585151fb12Sdarrenm 
1595151fb12Sdarrenm int
1605151fb12Sdarrenm _init(void)
1615151fb12Sdarrenm {
1625151fb12Sdarrenm 	int ret;
1635151fb12Sdarrenm 
1645151fb12Sdarrenm 	if ((ret = mod_install(&modlinkage)) != 0)
1655151fb12Sdarrenm 		return (ret);
1665151fb12Sdarrenm 
167*d3b2efc7SAnthony Scarpino 	/* Register with KCF.  If the registration fails, remove the module. */
168*d3b2efc7SAnthony Scarpino 	if (crypto_register_provider(&md4_prov_info, &md4_prov_handle)) {
1695151fb12Sdarrenm 		(void) mod_remove(&modlinkage);
170*d3b2efc7SAnthony Scarpino 		return (EACCES);
1715151fb12Sdarrenm 	}
1725151fb12Sdarrenm 
1735151fb12Sdarrenm 	return (0);
1745151fb12Sdarrenm }
1755151fb12Sdarrenm 
1765151fb12Sdarrenm int
1775151fb12Sdarrenm _fini(void)
1785151fb12Sdarrenm {
179*d3b2efc7SAnthony Scarpino 	/* Unregister from KCF if module is registered */
1805151fb12Sdarrenm 	if (md4_prov_handle != NULL) {
181*d3b2efc7SAnthony Scarpino 		if (crypto_unregister_provider(md4_prov_handle))
1825151fb12Sdarrenm 			return (EBUSY);
183*d3b2efc7SAnthony Scarpino 
1845151fb12Sdarrenm 		md4_prov_handle = NULL;
1855151fb12Sdarrenm 	}
1865151fb12Sdarrenm 
1875151fb12Sdarrenm 	return (mod_remove(&modlinkage));
1885151fb12Sdarrenm }
1895151fb12Sdarrenm 
1905151fb12Sdarrenm int
1915151fb12Sdarrenm _info(struct modinfo *modinfop)
1925151fb12Sdarrenm {
1935151fb12Sdarrenm 	return (mod_info(&modlinkage, modinfop));
1945151fb12Sdarrenm }
1955151fb12Sdarrenm 
1965151fb12Sdarrenm /*
1975151fb12Sdarrenm  * KCF software provider control entry points.
1985151fb12Sdarrenm  */
1995151fb12Sdarrenm /* ARGSUSED */
2005151fb12Sdarrenm static void
2015151fb12Sdarrenm md4_provider_status(crypto_provider_handle_t provider, uint_t *status)
2025151fb12Sdarrenm {
2035151fb12Sdarrenm 	*status = CRYPTO_PROVIDER_READY;
2045151fb12Sdarrenm }
2055151fb12Sdarrenm 
2065151fb12Sdarrenm /*
2075151fb12Sdarrenm  * KCF software provider digest entry points.
2085151fb12Sdarrenm  */
2095151fb12Sdarrenm 
2105151fb12Sdarrenm static int
2115151fb12Sdarrenm md4_digest_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
2125151fb12Sdarrenm     crypto_req_handle_t req)
2135151fb12Sdarrenm {
2145151fb12Sdarrenm 	if (mechanism->cm_type != MD4_MECH_INFO_TYPE)
2155151fb12Sdarrenm 		return (CRYPTO_MECHANISM_INVALID);
2165151fb12Sdarrenm 
2175151fb12Sdarrenm 	/*
2185151fb12Sdarrenm 	 * Allocate and initialize MD4 context.
2195151fb12Sdarrenm 	 */
2205151fb12Sdarrenm 	ctx->cc_provider_private = kmem_alloc(sizeof (md4_ctx_t),
2215151fb12Sdarrenm 	    crypto_kmflag(req));
2225151fb12Sdarrenm 	if (ctx->cc_provider_private == NULL)
2235151fb12Sdarrenm 		return (CRYPTO_HOST_MEMORY);
2245151fb12Sdarrenm 
2255151fb12Sdarrenm 	PROV_MD4_CTX(ctx)->mc_mech_type = MD4_MECH_INFO_TYPE;
2265151fb12Sdarrenm 	MD4Init(&PROV_MD4_CTX(ctx)->mc_md4_ctx);
2275151fb12Sdarrenm 
2285151fb12Sdarrenm 	return (CRYPTO_SUCCESS);
2295151fb12Sdarrenm }
2305151fb12Sdarrenm 
2315151fb12Sdarrenm /*
2325151fb12Sdarrenm  * Helper MD4 digest update function for uio data.
2335151fb12Sdarrenm  */
2345151fb12Sdarrenm static int
2355151fb12Sdarrenm md4_digest_update_uio(MD4_CTX *md4_ctx, crypto_data_t *data)
2365151fb12Sdarrenm {
2375151fb12Sdarrenm 	off_t offset = data->cd_offset;
2385151fb12Sdarrenm 	size_t length = data->cd_length;
2395151fb12Sdarrenm 	uint_t vec_idx;
2405151fb12Sdarrenm 	size_t cur_len;
2415151fb12Sdarrenm 
2425151fb12Sdarrenm 	/* we support only kernel buffer */
2435151fb12Sdarrenm 	if (data->cd_uio->uio_segflg != UIO_SYSSPACE)
2445151fb12Sdarrenm 		return (CRYPTO_ARGUMENTS_BAD);
2455151fb12Sdarrenm 
2465151fb12Sdarrenm 	/*
2475151fb12Sdarrenm 	 * Jump to the first iovec containing data to be
2485151fb12Sdarrenm 	 * digested.
2495151fb12Sdarrenm 	 */
2505151fb12Sdarrenm 	for (vec_idx = 0; vec_idx < data->cd_uio->uio_iovcnt &&
2515151fb12Sdarrenm 	    offset >= data->cd_uio->uio_iov[vec_idx].iov_len;
252d2b32306Smcpowers 	    offset -= data->cd_uio->uio_iov[vec_idx++].iov_len)
253d2b32306Smcpowers 		;
2545151fb12Sdarrenm 	if (vec_idx == data->cd_uio->uio_iovcnt) {
2555151fb12Sdarrenm 		/*
2565151fb12Sdarrenm 		 * The caller specified an offset that is larger than the
2575151fb12Sdarrenm 		 * total size of the buffers it provided.
2585151fb12Sdarrenm 		 */
2595151fb12Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
2605151fb12Sdarrenm 	}
2615151fb12Sdarrenm 
2625151fb12Sdarrenm 	/*
2635151fb12Sdarrenm 	 * Now do the digesting on the iovecs.
2645151fb12Sdarrenm 	 */
2655151fb12Sdarrenm 	while (vec_idx < data->cd_uio->uio_iovcnt && length > 0) {
2665151fb12Sdarrenm 		cur_len = MIN(data->cd_uio->uio_iov[vec_idx].iov_len -
2675151fb12Sdarrenm 		    offset, length);
2685151fb12Sdarrenm 
2695151fb12Sdarrenm 		MD4Update(md4_ctx, data->cd_uio->uio_iov[vec_idx].iov_base +
2705151fb12Sdarrenm 		    offset, cur_len);
2715151fb12Sdarrenm 
2725151fb12Sdarrenm 		length -= cur_len;
2735151fb12Sdarrenm 		vec_idx++;
2745151fb12Sdarrenm 		offset = 0;
2755151fb12Sdarrenm 	}
2765151fb12Sdarrenm 
2775151fb12Sdarrenm 	if (vec_idx == data->cd_uio->uio_iovcnt && length > 0) {
2785151fb12Sdarrenm 		/*
2795151fb12Sdarrenm 		 * The end of the specified iovec's was reached but
2805151fb12Sdarrenm 		 * the length requested could not be processed, i.e.
2815151fb12Sdarrenm 		 * The caller requested to digest more data than it provided.
2825151fb12Sdarrenm 		 */
2835151fb12Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
2845151fb12Sdarrenm 	}
2855151fb12Sdarrenm 
2865151fb12Sdarrenm 	return (CRYPTO_SUCCESS);
2875151fb12Sdarrenm }
2885151fb12Sdarrenm 
2895151fb12Sdarrenm /*
2905151fb12Sdarrenm  * Helper MD4 digest final function for uio data.
2915151fb12Sdarrenm  * digest_len is the length of the desired digest. If digest_len
2925151fb12Sdarrenm  * is smaller than the default MD4 digest length, the caller
2935151fb12Sdarrenm  * must pass a scratch buffer, digest_scratch, which must
2945151fb12Sdarrenm  * be at least MD4_DIGEST_LENGTH bytes.
2955151fb12Sdarrenm  */
2965151fb12Sdarrenm static int
2975151fb12Sdarrenm md4_digest_final_uio(MD4_CTX *md4_ctx, crypto_data_t *digest,
2985151fb12Sdarrenm     ulong_t digest_len, uchar_t *digest_scratch)
2995151fb12Sdarrenm {
3005151fb12Sdarrenm 	off_t offset = digest->cd_offset;
3015151fb12Sdarrenm 	uint_t vec_idx;
3025151fb12Sdarrenm 
3035151fb12Sdarrenm 	/* we support only kernel buffer */
3045151fb12Sdarrenm 	if (digest->cd_uio->uio_segflg != UIO_SYSSPACE)
3055151fb12Sdarrenm 		return (CRYPTO_ARGUMENTS_BAD);
3065151fb12Sdarrenm 
3075151fb12Sdarrenm 	/*
3085151fb12Sdarrenm 	 * Jump to the first iovec containing ptr to the digest to
3095151fb12Sdarrenm 	 * be returned.
3105151fb12Sdarrenm 	 */
3115151fb12Sdarrenm 	for (vec_idx = 0; offset >= digest->cd_uio->uio_iov[vec_idx].iov_len &&
3125151fb12Sdarrenm 	    vec_idx < digest->cd_uio->uio_iovcnt;
313d2b32306Smcpowers 	    offset -= digest->cd_uio->uio_iov[vec_idx++].iov_len)
314d2b32306Smcpowers 		;
3155151fb12Sdarrenm 	if (vec_idx == digest->cd_uio->uio_iovcnt) {
3165151fb12Sdarrenm 		/*
3175151fb12Sdarrenm 		 * The caller specified an offset that is
3185151fb12Sdarrenm 		 * larger than the total size of the buffers
3195151fb12Sdarrenm 		 * it provided.
3205151fb12Sdarrenm 		 */
3215151fb12Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
3225151fb12Sdarrenm 	}
3235151fb12Sdarrenm 
3245151fb12Sdarrenm 	if (offset + digest_len <=
3255151fb12Sdarrenm 	    digest->cd_uio->uio_iov[vec_idx].iov_len) {
3265151fb12Sdarrenm 		/*
3275151fb12Sdarrenm 		 * The computed MD4 digest will fit in the current
3285151fb12Sdarrenm 		 * iovec.
3295151fb12Sdarrenm 		 */
3305151fb12Sdarrenm 		if (digest_len != MD4_DIGEST_LENGTH) {
3315151fb12Sdarrenm 			/*
3325151fb12Sdarrenm 			 * The caller requested a short digest. Digest
3335151fb12Sdarrenm 			 * into a scratch buffer and return to
3345151fb12Sdarrenm 			 * the user only what was requested.
3355151fb12Sdarrenm 			 */
3365151fb12Sdarrenm 			MD4Final(digest_scratch, md4_ctx);
3375151fb12Sdarrenm 			bcopy(digest_scratch, (uchar_t *)digest->
3385151fb12Sdarrenm 			    cd_uio->uio_iov[vec_idx].iov_base + offset,
3395151fb12Sdarrenm 			    digest_len);
3405151fb12Sdarrenm 		} else {
3415151fb12Sdarrenm 			MD4Final((uchar_t *)digest->
3425151fb12Sdarrenm 			    cd_uio->uio_iov[vec_idx].iov_base + offset,
3435151fb12Sdarrenm 			    md4_ctx);
3445151fb12Sdarrenm 		}
3455151fb12Sdarrenm 	} else {
3465151fb12Sdarrenm 		/*
3475151fb12Sdarrenm 		 * The computed digest will be crossing one or more iovec's.
3485151fb12Sdarrenm 		 * This is bad performance-wise but we need to support it.
3495151fb12Sdarrenm 		 * Allocate a small scratch buffer on the stack and
3505151fb12Sdarrenm 		 * copy it piece meal to the specified digest iovec's.
3515151fb12Sdarrenm 		 */
3525151fb12Sdarrenm 		uchar_t digest_tmp[MD4_DIGEST_LENGTH];
3535151fb12Sdarrenm 		off_t scratch_offset = 0;
3545151fb12Sdarrenm 		size_t length = digest_len;
3555151fb12Sdarrenm 		size_t cur_len;
3565151fb12Sdarrenm 
3575151fb12Sdarrenm 		MD4Final(digest_tmp, md4_ctx);
3585151fb12Sdarrenm 
3595151fb12Sdarrenm 		while (vec_idx < digest->cd_uio->uio_iovcnt && length > 0) {
3605151fb12Sdarrenm 			cur_len = MIN(digest->cd_uio->uio_iov[vec_idx].iov_len -
3615151fb12Sdarrenm 			    offset, length);
3625151fb12Sdarrenm 			bcopy(digest_tmp + scratch_offset,
3635151fb12Sdarrenm 			    digest->cd_uio->uio_iov[vec_idx].iov_base + offset,
3645151fb12Sdarrenm 			    cur_len);
3655151fb12Sdarrenm 
3665151fb12Sdarrenm 			length -= cur_len;
3675151fb12Sdarrenm 			vec_idx++;
3685151fb12Sdarrenm 			scratch_offset += cur_len;
3695151fb12Sdarrenm 			offset = 0;
3705151fb12Sdarrenm 		}
3715151fb12Sdarrenm 
3725151fb12Sdarrenm 		if (vec_idx == digest->cd_uio->uio_iovcnt && length > 0) {
3735151fb12Sdarrenm 			/*
3745151fb12Sdarrenm 			 * The end of the specified iovec's was reached but
3755151fb12Sdarrenm 			 * the length requested could not be processed, i.e.
3765151fb12Sdarrenm 			 * The caller requested to digest more data than it
3775151fb12Sdarrenm 			 * provided.
3785151fb12Sdarrenm 			 */
3795151fb12Sdarrenm 			return (CRYPTO_DATA_LEN_RANGE);
3805151fb12Sdarrenm 		}
3815151fb12Sdarrenm 	}
3825151fb12Sdarrenm 
3835151fb12Sdarrenm 	return (CRYPTO_SUCCESS);
3845151fb12Sdarrenm }
3855151fb12Sdarrenm 
3865151fb12Sdarrenm /*
3875151fb12Sdarrenm  * Helper MD4 digest update for mblk's.
3885151fb12Sdarrenm  */
3895151fb12Sdarrenm static int
3905151fb12Sdarrenm md4_digest_update_mblk(MD4_CTX *md4_ctx, crypto_data_t *data)
3915151fb12Sdarrenm {
3925151fb12Sdarrenm 	off_t offset = data->cd_offset;
3935151fb12Sdarrenm 	size_t length = data->cd_length;
3945151fb12Sdarrenm 	mblk_t *mp;
3955151fb12Sdarrenm 	size_t cur_len;
3965151fb12Sdarrenm 
3975151fb12Sdarrenm 	/*
3985151fb12Sdarrenm 	 * Jump to the first mblk_t containing data to be digested.
3995151fb12Sdarrenm 	 */
4005151fb12Sdarrenm 	for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp);
401d2b32306Smcpowers 	    offset -= MBLKL(mp), mp = mp->b_cont)
402d2b32306Smcpowers 		;
4035151fb12Sdarrenm 	if (mp == NULL) {
4045151fb12Sdarrenm 		/*
4055151fb12Sdarrenm 		 * The caller specified an offset that is larger than the
4065151fb12Sdarrenm 		 * total size of the buffers it provided.
4075151fb12Sdarrenm 		 */
4085151fb12Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
4095151fb12Sdarrenm 	}
4105151fb12Sdarrenm 
4115151fb12Sdarrenm 	/*
4125151fb12Sdarrenm 	 * Now do the digesting on the mblk chain.
4135151fb12Sdarrenm 	 */
4145151fb12Sdarrenm 	while (mp != NULL && length > 0) {
4155151fb12Sdarrenm 		cur_len = MIN(MBLKL(mp) - offset, length);
4165151fb12Sdarrenm 		MD4Update(md4_ctx, mp->b_rptr + offset, cur_len);
4175151fb12Sdarrenm 		length -= cur_len;
4185151fb12Sdarrenm 		offset = 0;
4195151fb12Sdarrenm 		mp = mp->b_cont;
4205151fb12Sdarrenm 	}
4215151fb12Sdarrenm 
4225151fb12Sdarrenm 	if (mp == NULL && length > 0) {
4235151fb12Sdarrenm 		/*
4245151fb12Sdarrenm 		 * The end of the mblk was reached but the length requested
4255151fb12Sdarrenm 		 * could not be processed, i.e. The caller requested
4265151fb12Sdarrenm 		 * to digest more data than it provided.
4275151fb12Sdarrenm 		 */
4285151fb12Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
4295151fb12Sdarrenm 	}
4305151fb12Sdarrenm 
4315151fb12Sdarrenm 	return (CRYPTO_SUCCESS);
4325151fb12Sdarrenm }
4335151fb12Sdarrenm 
4345151fb12Sdarrenm /*
4355151fb12Sdarrenm  * Helper MD4 digest final for mblk's.
4365151fb12Sdarrenm  * digest_len is the length of the desired digest. If digest_len
4375151fb12Sdarrenm  * is smaller than the default MD4 digest length, the caller
4385151fb12Sdarrenm  * must pass a scratch buffer, digest_scratch, which must
4395151fb12Sdarrenm  * be at least MD4_DIGEST_LENGTH bytes.
4405151fb12Sdarrenm  */
4415151fb12Sdarrenm static int
4425151fb12Sdarrenm md4_digest_final_mblk(MD4_CTX *md4_ctx, crypto_data_t *digest,
4435151fb12Sdarrenm     ulong_t digest_len, uchar_t *digest_scratch)
4445151fb12Sdarrenm {
4455151fb12Sdarrenm 	off_t offset = digest->cd_offset;
4465151fb12Sdarrenm 	mblk_t *mp;
4475151fb12Sdarrenm 
4485151fb12Sdarrenm 	/*
4495151fb12Sdarrenm 	 * Jump to the first mblk_t that will be used to store the digest.
4505151fb12Sdarrenm 	 */
4515151fb12Sdarrenm 	for (mp = digest->cd_mp; mp != NULL && offset >= MBLKL(mp);
452d2b32306Smcpowers 	    offset -= MBLKL(mp), mp = mp->b_cont)
453d2b32306Smcpowers 		;
4545151fb12Sdarrenm 	if (mp == NULL) {
4555151fb12Sdarrenm 		/*
4565151fb12Sdarrenm 		 * The caller specified an offset that is larger than the
4575151fb12Sdarrenm 		 * total size of the buffers it provided.
4585151fb12Sdarrenm 		 */
4595151fb12Sdarrenm 		return (CRYPTO_DATA_LEN_RANGE);
4605151fb12Sdarrenm 	}
4615151fb12Sdarrenm 
4625151fb12Sdarrenm 	if (offset + digest_len <= MBLKL(mp)) {
4635151fb12Sdarrenm 		/*
4645151fb12Sdarrenm 		 * The computed MD4 digest will fit in the current mblk.
4655151fb12Sdarrenm 		 * Do the MD4Final() in-place.
4665151fb12Sdarrenm 		 */
4675151fb12Sdarrenm 		if (digest_len != MD4_DIGEST_LENGTH) {
4685151fb12Sdarrenm 			/*
4695151fb12Sdarrenm 			 * The caller requested a short digest. Digest
4705151fb12Sdarrenm 			 * into a scratch buffer and return to
4715151fb12Sdarrenm 			 * the user only what was requested.
4725151fb12Sdarrenm 			 */
4735151fb12Sdarrenm 			MD4Final(digest_scratch, md4_ctx);
4745151fb12Sdarrenm 			bcopy(digest_scratch, mp->b_rptr + offset, digest_len);
4755151fb12Sdarrenm 		} else {
4765151fb12Sdarrenm 			MD4Final(mp->b_rptr + offset, md4_ctx);
4775151fb12Sdarrenm 		}
4785151fb12Sdarrenm 	} else {
4795151fb12Sdarrenm 		/*
4805151fb12Sdarrenm 		 * The computed digest will be crossing one or more mblk's.
4815151fb12Sdarrenm 		 * This is bad performance-wise but we need to support it.
4825151fb12Sdarrenm 		 * Allocate a small scratch buffer on the stack and
4835151fb12Sdarrenm 		 * copy it piece meal to the specified digest iovec's.
4845151fb12Sdarrenm 		 */
4855151fb12Sdarrenm 		uchar_t digest_tmp[MD4_DIGEST_LENGTH];
4865151fb12Sdarrenm 		off_t scratch_offset = 0;
4875151fb12Sdarrenm 		size_t length = digest_len;
4885151fb12Sdarrenm 		size_t cur_len;
4895151fb12Sdarrenm 
4905151fb12Sdarrenm 		MD4Final(digest_tmp, md4_ctx);
4915151fb12Sdarrenm 
4925151fb12Sdarrenm 		while (mp != NULL && length > 0) {
4935151fb12Sdarrenm 			cur_len = MIN(MBLKL(mp) - offset, length);
4945151fb12Sdarrenm 			bcopy(digest_tmp + scratch_offset,
4955151fb12Sdarrenm 			    mp->b_rptr + offset, cur_len);
4965151fb12Sdarrenm 
4975151fb12Sdarrenm 			length -= cur_len;
4985151fb12Sdarrenm 			mp = mp->b_cont;
4995151fb12Sdarrenm 			scratch_offset += cur_len;
5005151fb12Sdarrenm 			offset = 0;
5015151fb12Sdarrenm 		}
5025151fb12Sdarrenm 
5035151fb12Sdarrenm 		if (mp == NULL && length > 0) {
5045151fb12Sdarrenm 			/*
5055151fb12Sdarrenm 			 * The end of the specified mblk was reached but
5065151fb12Sdarrenm 			 * the length requested could not be processed, i.e.
5075151fb12Sdarrenm 			 * The caller requested to digest more data than it
5085151fb12Sdarrenm 			 * provided.
5095151fb12Sdarrenm 			 */
5105151fb12Sdarrenm 			return (CRYPTO_DATA_LEN_RANGE);
5115151fb12Sdarrenm 		}
5125151fb12Sdarrenm 	}
5135151fb12Sdarrenm 
5145151fb12Sdarrenm 	return (CRYPTO_SUCCESS);
5155151fb12Sdarrenm }
5165151fb12Sdarrenm 
5175151fb12Sdarrenm /* ARGSUSED */
5185151fb12Sdarrenm static int
5195151fb12Sdarrenm md4_digest(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *digest,
5205151fb12Sdarrenm     crypto_req_handle_t req)
5215151fb12Sdarrenm {
5225151fb12Sdarrenm 	int ret = CRYPTO_SUCCESS;
5235151fb12Sdarrenm 
5245151fb12Sdarrenm 	ASSERT(ctx->cc_provider_private != NULL);
5255151fb12Sdarrenm 
5265151fb12Sdarrenm 	/*
5275151fb12Sdarrenm 	 * We need to just return the length needed to store the output.
5285151fb12Sdarrenm 	 * We should not destroy the context for the following cases.
5295151fb12Sdarrenm 	 */
5305151fb12Sdarrenm 	if ((digest->cd_length == 0) ||
5315151fb12Sdarrenm 	    (digest->cd_length < MD4_DIGEST_LENGTH)) {
5325151fb12Sdarrenm 		digest->cd_length = MD4_DIGEST_LENGTH;
5335151fb12Sdarrenm 		return (CRYPTO_BUFFER_TOO_SMALL);
5345151fb12Sdarrenm 	}
5355151fb12Sdarrenm 
5365151fb12Sdarrenm 	/*
5375151fb12Sdarrenm 	 * Do the MD4 update on the specified input data.
5385151fb12Sdarrenm 	 */
5395151fb12Sdarrenm 	switch (data->cd_format) {
5405151fb12Sdarrenm 	case CRYPTO_DATA_RAW:
5415151fb12Sdarrenm 		MD4Update(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
5425151fb12Sdarrenm 		    data->cd_raw.iov_base + data->cd_offset,
5435151fb12Sdarrenm 		    data->cd_length);
5445151fb12Sdarrenm 		break;
5455151fb12Sdarrenm 	case CRYPTO_DATA_UIO:
5465151fb12Sdarrenm 		ret = md4_digest_update_uio(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
5475151fb12Sdarrenm 		    data);
5485151fb12Sdarrenm 		break;
5495151fb12Sdarrenm 	case CRYPTO_DATA_MBLK:
5505151fb12Sdarrenm 		ret = md4_digest_update_mblk(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
5515151fb12Sdarrenm 		    data);
5525151fb12Sdarrenm 		break;
5535151fb12Sdarrenm 	default:
5545151fb12Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
5555151fb12Sdarrenm 	}
5565151fb12Sdarrenm 
5575151fb12Sdarrenm 	if (ret != CRYPTO_SUCCESS) {
5585151fb12Sdarrenm 		/* the update failed, free context and bail */
5595151fb12Sdarrenm 		kmem_free(ctx->cc_provider_private, sizeof (md4_ctx_t));
5605151fb12Sdarrenm 		ctx->cc_provider_private = NULL;
5615151fb12Sdarrenm 		digest->cd_length = 0;
5625151fb12Sdarrenm 		return (ret);
5635151fb12Sdarrenm 	}
5645151fb12Sdarrenm 
5655151fb12Sdarrenm 	/*
5665151fb12Sdarrenm 	 * Do an MD4 final, must be done separately since the digest
5675151fb12Sdarrenm 	 * type can be different than the input data type.
5685151fb12Sdarrenm 	 */
5695151fb12Sdarrenm 	switch (digest->cd_format) {
5705151fb12Sdarrenm 	case CRYPTO_DATA_RAW:
5715151fb12Sdarrenm 		MD4Final((unsigned char *)digest->cd_raw.iov_base +
5725151fb12Sdarrenm 		    digest->cd_offset, &PROV_MD4_CTX(ctx)->mc_md4_ctx);
5735151fb12Sdarrenm 		break;
5745151fb12Sdarrenm 	case CRYPTO_DATA_UIO:
5755151fb12Sdarrenm 		ret = md4_digest_final_uio(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
5765151fb12Sdarrenm 		    digest, MD4_DIGEST_LENGTH, NULL);
5775151fb12Sdarrenm 		break;
5785151fb12Sdarrenm 	case CRYPTO_DATA_MBLK:
5795151fb12Sdarrenm 		ret = md4_digest_final_mblk(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
5805151fb12Sdarrenm 		    digest, MD4_DIGEST_LENGTH, NULL);
5815151fb12Sdarrenm 		break;
5825151fb12Sdarrenm 	default:
5835151fb12Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
5845151fb12Sdarrenm 	}
5855151fb12Sdarrenm 
5865151fb12Sdarrenm 	/* all done, free context and return */
5875151fb12Sdarrenm 
5885151fb12Sdarrenm 	if (ret == CRYPTO_SUCCESS) {
5895151fb12Sdarrenm 		digest->cd_length = MD4_DIGEST_LENGTH;
5905151fb12Sdarrenm 	} else {
5915151fb12Sdarrenm 		digest->cd_length = 0;
5925151fb12Sdarrenm 	}
5935151fb12Sdarrenm 
5945151fb12Sdarrenm 	kmem_free(ctx->cc_provider_private, sizeof (md4_ctx_t));
5955151fb12Sdarrenm 	ctx->cc_provider_private = NULL;
5965151fb12Sdarrenm 	return (ret);
5975151fb12Sdarrenm }
5985151fb12Sdarrenm 
5995151fb12Sdarrenm /* ARGSUSED */
6005151fb12Sdarrenm static int
6015151fb12Sdarrenm md4_digest_update(crypto_ctx_t *ctx, crypto_data_t *data,
6025151fb12Sdarrenm     crypto_req_handle_t req)
6035151fb12Sdarrenm {
6045151fb12Sdarrenm 	int ret = CRYPTO_SUCCESS;
6055151fb12Sdarrenm 
6065151fb12Sdarrenm 	ASSERT(ctx->cc_provider_private != NULL);
6075151fb12Sdarrenm 
6085151fb12Sdarrenm 	/*
6095151fb12Sdarrenm 	 * Do the MD4 update on the specified input data.
6105151fb12Sdarrenm 	 */
6115151fb12Sdarrenm 	switch (data->cd_format) {
6125151fb12Sdarrenm 	case CRYPTO_DATA_RAW:
6135151fb12Sdarrenm 		MD4Update(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
6145151fb12Sdarrenm 		    data->cd_raw.iov_base + data->cd_offset,
6155151fb12Sdarrenm 		    data->cd_length);
6165151fb12Sdarrenm 		break;
6175151fb12Sdarrenm 	case CRYPTO_DATA_UIO:
6185151fb12Sdarrenm 		ret = md4_digest_update_uio(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
6195151fb12Sdarrenm 		    data);
6205151fb12Sdarrenm 		break;
6215151fb12Sdarrenm 	case CRYPTO_DATA_MBLK:
6225151fb12Sdarrenm 		ret = md4_digest_update_mblk(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
6235151fb12Sdarrenm 		    data);
6245151fb12Sdarrenm 		break;
6255151fb12Sdarrenm 	default:
6265151fb12Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
6275151fb12Sdarrenm 	}
6285151fb12Sdarrenm 
6295151fb12Sdarrenm 	return (ret);
6305151fb12Sdarrenm }
6315151fb12Sdarrenm 
6325151fb12Sdarrenm /* ARGSUSED */
6335151fb12Sdarrenm static int
6345151fb12Sdarrenm md4_digest_final(crypto_ctx_t *ctx, crypto_data_t *digest,
6355151fb12Sdarrenm     crypto_req_handle_t req)
6365151fb12Sdarrenm {
6375151fb12Sdarrenm 	int ret = CRYPTO_SUCCESS;
6385151fb12Sdarrenm 
6395151fb12Sdarrenm 	ASSERT(ctx->cc_provider_private != NULL);
6405151fb12Sdarrenm 
6415151fb12Sdarrenm 	/*
6425151fb12Sdarrenm 	 * We need to just return the length needed to store the output.
6435151fb12Sdarrenm 	 * We should not destroy the context for the following cases.
6445151fb12Sdarrenm 	 */
6455151fb12Sdarrenm 	if ((digest->cd_length == 0) ||
6465151fb12Sdarrenm 	    (digest->cd_length < MD4_DIGEST_LENGTH)) {
6475151fb12Sdarrenm 		digest->cd_length = MD4_DIGEST_LENGTH;
6485151fb12Sdarrenm 		return (CRYPTO_BUFFER_TOO_SMALL);
6495151fb12Sdarrenm 	}
6505151fb12Sdarrenm 
6515151fb12Sdarrenm 	/*
6525151fb12Sdarrenm 	 * Do an MD4 final.
6535151fb12Sdarrenm 	 */
6545151fb12Sdarrenm 	switch (digest->cd_format) {
6555151fb12Sdarrenm 	case CRYPTO_DATA_RAW:
6565151fb12Sdarrenm 		MD4Final((unsigned char *)digest->cd_raw.iov_base +
6575151fb12Sdarrenm 		    digest->cd_offset, &PROV_MD4_CTX(ctx)->mc_md4_ctx);
6585151fb12Sdarrenm 		break;
6595151fb12Sdarrenm 	case CRYPTO_DATA_UIO:
6605151fb12Sdarrenm 		ret = md4_digest_final_uio(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
6615151fb12Sdarrenm 		    digest, MD4_DIGEST_LENGTH, NULL);
6625151fb12Sdarrenm 		break;
6635151fb12Sdarrenm 	case CRYPTO_DATA_MBLK:
6645151fb12Sdarrenm 		ret = md4_digest_final_mblk(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
6655151fb12Sdarrenm 		    digest, MD4_DIGEST_LENGTH, NULL);
6665151fb12Sdarrenm 		break;
6675151fb12Sdarrenm 	default:
6685151fb12Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
6695151fb12Sdarrenm 	}
6705151fb12Sdarrenm 
6715151fb12Sdarrenm 	/* all done, free context and return */
6725151fb12Sdarrenm 
6735151fb12Sdarrenm 	if (ret == CRYPTO_SUCCESS) {
6745151fb12Sdarrenm 		digest->cd_length = MD4_DIGEST_LENGTH;
6755151fb12Sdarrenm 	} else {
6765151fb12Sdarrenm 		digest->cd_length = 0;
6775151fb12Sdarrenm 	}
6785151fb12Sdarrenm 
6795151fb12Sdarrenm 	kmem_free(ctx->cc_provider_private, sizeof (md4_ctx_t));
6805151fb12Sdarrenm 	ctx->cc_provider_private = NULL;
6815151fb12Sdarrenm 
6825151fb12Sdarrenm 	return (ret);
6835151fb12Sdarrenm }
6845151fb12Sdarrenm 
6855151fb12Sdarrenm /* ARGSUSED */
6865151fb12Sdarrenm static int
6875151fb12Sdarrenm md4_digest_atomic(crypto_provider_handle_t provider,
6885151fb12Sdarrenm     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
6895151fb12Sdarrenm     crypto_data_t *data, crypto_data_t *digest,
6905151fb12Sdarrenm     crypto_req_handle_t req)
6915151fb12Sdarrenm {
6925151fb12Sdarrenm 	int ret = CRYPTO_SUCCESS;
6935151fb12Sdarrenm 	MD4_CTX md4_ctx;
6945151fb12Sdarrenm 
6955151fb12Sdarrenm 	if (mechanism->cm_type != MD4_MECH_INFO_TYPE)
6965151fb12Sdarrenm 		return (CRYPTO_MECHANISM_INVALID);
6975151fb12Sdarrenm 
6985151fb12Sdarrenm 	/*
6995151fb12Sdarrenm 	 * Do the MD4 init.
7005151fb12Sdarrenm 	 */
7015151fb12Sdarrenm 	MD4Init(&md4_ctx);
7025151fb12Sdarrenm 
7035151fb12Sdarrenm 	/*
7045151fb12Sdarrenm 	 * Do the MD4 update on the specified input data.
7055151fb12Sdarrenm 	 */
7065151fb12Sdarrenm 	switch (data->cd_format) {
7075151fb12Sdarrenm 	case CRYPTO_DATA_RAW:
7085151fb12Sdarrenm 		MD4Update(&md4_ctx, data->cd_raw.iov_base + data->cd_offset,
7095151fb12Sdarrenm 		    data->cd_length);
7105151fb12Sdarrenm 		break;
7115151fb12Sdarrenm 	case CRYPTO_DATA_UIO:
7125151fb12Sdarrenm 		ret = md4_digest_update_uio(&md4_ctx, data);
7135151fb12Sdarrenm 		break;
7145151fb12Sdarrenm 	case CRYPTO_DATA_MBLK:
7155151fb12Sdarrenm 		ret = md4_digest_update_mblk(&md4_ctx, data);
7165151fb12Sdarrenm 		break;
7175151fb12Sdarrenm 	default:
7185151fb12Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
7195151fb12Sdarrenm 	}
7205151fb12Sdarrenm 
7215151fb12Sdarrenm 	if (ret != CRYPTO_SUCCESS) {
7225151fb12Sdarrenm 		/* the update failed, bail */
7235151fb12Sdarrenm 		digest->cd_length = 0;
7245151fb12Sdarrenm 		return (ret);
7255151fb12Sdarrenm 	}
7265151fb12Sdarrenm 
7275151fb12Sdarrenm 	/*
7285151fb12Sdarrenm 	 * Do an MD4 final, must be done separately since the digest
7295151fb12Sdarrenm 	 * type can be different than the input data type.
7305151fb12Sdarrenm 	 */
7315151fb12Sdarrenm 	switch (digest->cd_format) {
7325151fb12Sdarrenm 	case CRYPTO_DATA_RAW:
7335151fb12Sdarrenm 		MD4Final((unsigned char *)digest->cd_raw.iov_base +
7345151fb12Sdarrenm 		    digest->cd_offset, &md4_ctx);
7355151fb12Sdarrenm 		break;
7365151fb12Sdarrenm 	case CRYPTO_DATA_UIO:
7375151fb12Sdarrenm 		ret = md4_digest_final_uio(&md4_ctx, digest,
7385151fb12Sdarrenm 		    MD4_DIGEST_LENGTH, NULL);
7395151fb12Sdarrenm 		break;
7405151fb12Sdarrenm 	case CRYPTO_DATA_MBLK:
7415151fb12Sdarrenm 		ret = md4_digest_final_mblk(&md4_ctx, digest,
7425151fb12Sdarrenm 		    MD4_DIGEST_LENGTH, NULL);
7435151fb12Sdarrenm 		break;
7445151fb12Sdarrenm 	default:
7455151fb12Sdarrenm 		ret = CRYPTO_ARGUMENTS_BAD;
7465151fb12Sdarrenm 	}
7475151fb12Sdarrenm 
7485151fb12Sdarrenm 	if (ret == CRYPTO_SUCCESS) {
7495151fb12Sdarrenm 		digest->cd_length = MD4_DIGEST_LENGTH;
7505151fb12Sdarrenm 	} else {
7515151fb12Sdarrenm 		digest->cd_length = 0;
7525151fb12Sdarrenm 	}
7535151fb12Sdarrenm 
7545151fb12Sdarrenm 	return (ret);
7555151fb12Sdarrenm }
756