xref: /illumos-gate/usr/src/uts/common/crypto/io/md4_mod.c (revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * In kernel module, the md4 module is created with one modlinkage,
30  * this is different to md5 and sha1 modules which have a legacy misc
31  * variant for direct calls to the Init/Update/Final routines.
32  *
33  * - a modlcrypto that allows the module to register with the Kernel
34  *   Cryptographic Framework (KCF) as a software provider for the MD4
35  *   mechanisms.
36  */
37 
38 #include <sys/types.h>
39 #include <sys/systm.h>
40 #include <sys/modctl.h>
41 #include <sys/cmn_err.h>
42 #include <sys/ddi.h>
43 #include <sys/crypto/common.h>
44 #include <sys/crypto/spi.h>
45 #include <sys/sysmacros.h>
46 #include <sys/strsun.h>
47 #include <sys/note.h>
48 #include <sys/md4.h>
49 
50 extern struct mod_ops mod_miscops;
51 extern struct mod_ops mod_cryptoops;
52 
53 /*
54  * Module linkage information for the kernel.
55  */
56 
57 static struct modlcrypto modlcrypto = {
58 	&mod_cryptoops,
59 	"MD4 Kernel SW Provider"
60 };
61 
62 static struct modlinkage modlinkage = {
63 	MODREV_1,
64 	(void *)&modlcrypto,
65 	NULL
66 };
67 
68 /*
69  * CSPI information (entry points, provider info, etc.)
70  */
71 
72 typedef enum md4_mech_type {
73 	MD4_MECH_INFO_TYPE,		/* SUN_CKM_MD4 */
74 } md4_mech_type_t;
75 
76 #define	MD4_DIGEST_LENGTH	16	/* MD4 digest length in bytes */
77 
78 /*
79  * Context for MD4 mechanism.
80  */
81 typedef struct md4_ctx {
82 	md4_mech_type_t		mc_mech_type;	/* type of context */
83 	MD4_CTX			mc_md4_ctx;	/* MD4 context */
84 } md4_ctx_t;
85 
86 /*
87  * Macros to access the MD4 contexts from a context passed
88  * by KCF to one of the entry points.
89  */
90 
91 #define	PROV_MD4_CTX(ctx)	((md4_ctx_t *)(ctx)->cc_provider_private)
92 
93 /*
94  * Mechanism info structure passed to KCF during registration.
95  */
96 static crypto_mech_info_t md4_mech_info_tab[] = {
97 	/* MD4 */
98 	{SUN_CKM_MD4, MD4_MECH_INFO_TYPE,
99 	    CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC,
100 	    0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
101 };
102 
103 static void md4_provider_status(crypto_provider_handle_t, uint_t *);
104 
105 static crypto_control_ops_t md4_control_ops = {
106 	md4_provider_status
107 };
108 
109 static int md4_digest_init(crypto_ctx_t *, crypto_mechanism_t *,
110     crypto_req_handle_t);
111 static int md4_digest(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
112     crypto_req_handle_t);
113 static int md4_digest_update(crypto_ctx_t *, crypto_data_t *,
114     crypto_req_handle_t);
115 static int md4_digest_final(crypto_ctx_t *, crypto_data_t *,
116     crypto_req_handle_t);
117 static int md4_digest_atomic(crypto_provider_handle_t, crypto_session_id_t,
118     crypto_mechanism_t *, crypto_data_t *, crypto_data_t *,
119     crypto_req_handle_t);
120 
121 static crypto_digest_ops_t md4_digest_ops = {
122 	md4_digest_init,
123 	md4_digest,
124 	md4_digest_update,
125 	NULL,
126 	md4_digest_final,
127 	md4_digest_atomic
128 };
129 
130 static crypto_ops_t md4_crypto_ops = {
131 	&md4_control_ops,
132 	&md4_digest_ops,
133 	NULL,
134 	NULL,
135 	NULL,
136 	NULL,
137 	NULL,
138 	NULL,
139 	NULL,
140 	NULL,
141 	NULL,
142 	NULL,
143 	NULL,
144 	NULL,
145 };
146 
147 static crypto_provider_info_t md4_prov_info = {
148 	CRYPTO_SPI_VERSION_1,
149 	"MD4 Software Provider",
150 	CRYPTO_SW_PROVIDER,
151 	{&modlinkage},
152 	NULL,
153 	&md4_crypto_ops,
154 	sizeof (md4_mech_info_tab)/sizeof (crypto_mech_info_t),
155 	md4_mech_info_tab
156 };
157 
158 static crypto_kcf_provider_handle_t md4_prov_handle = NULL;
159 
160 int
161 _init(void)
162 {
163 	int ret;
164 
165 	if ((ret = mod_install(&modlinkage)) != 0)
166 		return (ret);
167 
168 	/*
169 	 * Register with KCF. If the registration fails, log an
170 	 * error and uninstall the module.
171 	 */
172 	if ((ret = crypto_register_provider(&md4_prov_info,
173 	    &md4_prov_handle)) != CRYPTO_SUCCESS) {
174 		cmn_err(CE_WARN, "md4 _init: "
175 		    "crypto_register_provider() failed (0x%x)", ret);
176 		(void) mod_remove(&modlinkage);
177 		return (ret);
178 	}
179 
180 	return (0);
181 }
182 
183 int
184 _fini(void)
185 {
186 	int ret;
187 
188 	/*
189 	 * Unregister from KCF if previous registration succeeded.
190 	 */
191 	if (md4_prov_handle != NULL) {
192 		if ((ret = crypto_unregister_provider(md4_prov_handle)) !=
193 		    CRYPTO_SUCCESS) {
194 			cmn_err(CE_WARN, "md4 _fini: "
195 			    "crypto_unregister_provider() failed (0x%x)", ret);
196 			return (EBUSY);
197 		}
198 		md4_prov_handle = NULL;
199 	}
200 
201 	return (mod_remove(&modlinkage));
202 }
203 
204 int
205 _info(struct modinfo *modinfop)
206 {
207 	return (mod_info(&modlinkage, modinfop));
208 }
209 
210 /*
211  * KCF software provider control entry points.
212  */
213 /* ARGSUSED */
214 static void
215 md4_provider_status(crypto_provider_handle_t provider, uint_t *status)
216 {
217 	*status = CRYPTO_PROVIDER_READY;
218 }
219 
220 /*
221  * KCF software provider digest entry points.
222  */
223 
224 static int
225 md4_digest_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
226     crypto_req_handle_t req)
227 {
228 	if (mechanism->cm_type != MD4_MECH_INFO_TYPE)
229 		return (CRYPTO_MECHANISM_INVALID);
230 
231 	/*
232 	 * Allocate and initialize MD4 context.
233 	 */
234 	ctx->cc_provider_private = kmem_alloc(sizeof (md4_ctx_t),
235 	    crypto_kmflag(req));
236 	if (ctx->cc_provider_private == NULL)
237 		return (CRYPTO_HOST_MEMORY);
238 
239 	PROV_MD4_CTX(ctx)->mc_mech_type = MD4_MECH_INFO_TYPE;
240 	MD4Init(&PROV_MD4_CTX(ctx)->mc_md4_ctx);
241 
242 	return (CRYPTO_SUCCESS);
243 }
244 
245 /*
246  * Helper MD4 digest update function for uio data.
247  */
248 static int
249 md4_digest_update_uio(MD4_CTX *md4_ctx, crypto_data_t *data)
250 {
251 	off_t offset = data->cd_offset;
252 	size_t length = data->cd_length;
253 	uint_t vec_idx;
254 	size_t cur_len;
255 
256 	/* we support only kernel buffer */
257 	if (data->cd_uio->uio_segflg != UIO_SYSSPACE)
258 		return (CRYPTO_ARGUMENTS_BAD);
259 
260 	/*
261 	 * Jump to the first iovec containing data to be
262 	 * digested.
263 	 */
264 	for (vec_idx = 0; vec_idx < data->cd_uio->uio_iovcnt &&
265 	    offset >= data->cd_uio->uio_iov[vec_idx].iov_len;
266 	    offset -= data->cd_uio->uio_iov[vec_idx++].iov_len)
267 		;
268 	if (vec_idx == data->cd_uio->uio_iovcnt) {
269 		/*
270 		 * The caller specified an offset that is larger than the
271 		 * total size of the buffers it provided.
272 		 */
273 		return (CRYPTO_DATA_LEN_RANGE);
274 	}
275 
276 	/*
277 	 * Now do the digesting on the iovecs.
278 	 */
279 	while (vec_idx < data->cd_uio->uio_iovcnt && length > 0) {
280 		cur_len = MIN(data->cd_uio->uio_iov[vec_idx].iov_len -
281 		    offset, length);
282 
283 		MD4Update(md4_ctx, data->cd_uio->uio_iov[vec_idx].iov_base +
284 		    offset, cur_len);
285 
286 		length -= cur_len;
287 		vec_idx++;
288 		offset = 0;
289 	}
290 
291 	if (vec_idx == data->cd_uio->uio_iovcnt && length > 0) {
292 		/*
293 		 * The end of the specified iovec's was reached but
294 		 * the length requested could not be processed, i.e.
295 		 * The caller requested to digest more data than it provided.
296 		 */
297 		return (CRYPTO_DATA_LEN_RANGE);
298 	}
299 
300 	return (CRYPTO_SUCCESS);
301 }
302 
303 /*
304  * Helper MD4 digest final function for uio data.
305  * digest_len is the length of the desired digest. If digest_len
306  * is smaller than the default MD4 digest length, the caller
307  * must pass a scratch buffer, digest_scratch, which must
308  * be at least MD4_DIGEST_LENGTH bytes.
309  */
310 static int
311 md4_digest_final_uio(MD4_CTX *md4_ctx, crypto_data_t *digest,
312     ulong_t digest_len, uchar_t *digest_scratch)
313 {
314 	off_t offset = digest->cd_offset;
315 	uint_t vec_idx;
316 
317 	/* we support only kernel buffer */
318 	if (digest->cd_uio->uio_segflg != UIO_SYSSPACE)
319 		return (CRYPTO_ARGUMENTS_BAD);
320 
321 	/*
322 	 * Jump to the first iovec containing ptr to the digest to
323 	 * be returned.
324 	 */
325 	for (vec_idx = 0; offset >= digest->cd_uio->uio_iov[vec_idx].iov_len &&
326 	    vec_idx < digest->cd_uio->uio_iovcnt;
327 	    offset -= digest->cd_uio->uio_iov[vec_idx++].iov_len)
328 		;
329 	if (vec_idx == digest->cd_uio->uio_iovcnt) {
330 		/*
331 		 * The caller specified an offset that is
332 		 * larger than the total size of the buffers
333 		 * it provided.
334 		 */
335 		return (CRYPTO_DATA_LEN_RANGE);
336 	}
337 
338 	if (offset + digest_len <=
339 	    digest->cd_uio->uio_iov[vec_idx].iov_len) {
340 		/*
341 		 * The computed MD4 digest will fit in the current
342 		 * iovec.
343 		 */
344 		if (digest_len != MD4_DIGEST_LENGTH) {
345 			/*
346 			 * The caller requested a short digest. Digest
347 			 * into a scratch buffer and return to
348 			 * the user only what was requested.
349 			 */
350 			MD4Final(digest_scratch, md4_ctx);
351 			bcopy(digest_scratch, (uchar_t *)digest->
352 			    cd_uio->uio_iov[vec_idx].iov_base + offset,
353 			    digest_len);
354 		} else {
355 			MD4Final((uchar_t *)digest->
356 			    cd_uio->uio_iov[vec_idx].iov_base + offset,
357 			    md4_ctx);
358 		}
359 	} else {
360 		/*
361 		 * The computed digest will be crossing one or more iovec's.
362 		 * This is bad performance-wise but we need to support it.
363 		 * Allocate a small scratch buffer on the stack and
364 		 * copy it piece meal to the specified digest iovec's.
365 		 */
366 		uchar_t digest_tmp[MD4_DIGEST_LENGTH];
367 		off_t scratch_offset = 0;
368 		size_t length = digest_len;
369 		size_t cur_len;
370 
371 		MD4Final(digest_tmp, md4_ctx);
372 
373 		while (vec_idx < digest->cd_uio->uio_iovcnt && length > 0) {
374 			cur_len = MIN(digest->cd_uio->uio_iov[vec_idx].iov_len -
375 			    offset, length);
376 			bcopy(digest_tmp + scratch_offset,
377 			    digest->cd_uio->uio_iov[vec_idx].iov_base + offset,
378 			    cur_len);
379 
380 			length -= cur_len;
381 			vec_idx++;
382 			scratch_offset += cur_len;
383 			offset = 0;
384 		}
385 
386 		if (vec_idx == digest->cd_uio->uio_iovcnt && length > 0) {
387 			/*
388 			 * The end of the specified iovec's was reached but
389 			 * the length requested could not be processed, i.e.
390 			 * The caller requested to digest more data than it
391 			 * provided.
392 			 */
393 			return (CRYPTO_DATA_LEN_RANGE);
394 		}
395 	}
396 
397 	return (CRYPTO_SUCCESS);
398 }
399 
400 /*
401  * Helper MD4 digest update for mblk's.
402  */
403 static int
404 md4_digest_update_mblk(MD4_CTX *md4_ctx, crypto_data_t *data)
405 {
406 	off_t offset = data->cd_offset;
407 	size_t length = data->cd_length;
408 	mblk_t *mp;
409 	size_t cur_len;
410 
411 	/*
412 	 * Jump to the first mblk_t containing data to be digested.
413 	 */
414 	for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp);
415 	    offset -= MBLKL(mp), mp = mp->b_cont)
416 		;
417 	if (mp == NULL) {
418 		/*
419 		 * The caller specified an offset that is larger than the
420 		 * total size of the buffers it provided.
421 		 */
422 		return (CRYPTO_DATA_LEN_RANGE);
423 	}
424 
425 	/*
426 	 * Now do the digesting on the mblk chain.
427 	 */
428 	while (mp != NULL && length > 0) {
429 		cur_len = MIN(MBLKL(mp) - offset, length);
430 		MD4Update(md4_ctx, mp->b_rptr + offset, cur_len);
431 		length -= cur_len;
432 		offset = 0;
433 		mp = mp->b_cont;
434 	}
435 
436 	if (mp == NULL && length > 0) {
437 		/*
438 		 * The end of the mblk was reached but the length requested
439 		 * could not be processed, i.e. The caller requested
440 		 * to digest more data than it provided.
441 		 */
442 		return (CRYPTO_DATA_LEN_RANGE);
443 	}
444 
445 	return (CRYPTO_SUCCESS);
446 }
447 
448 /*
449  * Helper MD4 digest final for mblk's.
450  * digest_len is the length of the desired digest. If digest_len
451  * is smaller than the default MD4 digest length, the caller
452  * must pass a scratch buffer, digest_scratch, which must
453  * be at least MD4_DIGEST_LENGTH bytes.
454  */
455 static int
456 md4_digest_final_mblk(MD4_CTX *md4_ctx, crypto_data_t *digest,
457     ulong_t digest_len, uchar_t *digest_scratch)
458 {
459 	off_t offset = digest->cd_offset;
460 	mblk_t *mp;
461 
462 	/*
463 	 * Jump to the first mblk_t that will be used to store the digest.
464 	 */
465 	for (mp = digest->cd_mp; mp != NULL && offset >= MBLKL(mp);
466 	    offset -= MBLKL(mp), mp = mp->b_cont)
467 		;
468 	if (mp == NULL) {
469 		/*
470 		 * The caller specified an offset that is larger than the
471 		 * total size of the buffers it provided.
472 		 */
473 		return (CRYPTO_DATA_LEN_RANGE);
474 	}
475 
476 	if (offset + digest_len <= MBLKL(mp)) {
477 		/*
478 		 * The computed MD4 digest will fit in the current mblk.
479 		 * Do the MD4Final() in-place.
480 		 */
481 		if (digest_len != MD4_DIGEST_LENGTH) {
482 			/*
483 			 * The caller requested a short digest. Digest
484 			 * into a scratch buffer and return to
485 			 * the user only what was requested.
486 			 */
487 			MD4Final(digest_scratch, md4_ctx);
488 			bcopy(digest_scratch, mp->b_rptr + offset, digest_len);
489 		} else {
490 			MD4Final(mp->b_rptr + offset, md4_ctx);
491 		}
492 	} else {
493 		/*
494 		 * The computed digest will be crossing one or more mblk's.
495 		 * This is bad performance-wise but we need to support it.
496 		 * Allocate a small scratch buffer on the stack and
497 		 * copy it piece meal to the specified digest iovec's.
498 		 */
499 		uchar_t digest_tmp[MD4_DIGEST_LENGTH];
500 		off_t scratch_offset = 0;
501 		size_t length = digest_len;
502 		size_t cur_len;
503 
504 		MD4Final(digest_tmp, md4_ctx);
505 
506 		while (mp != NULL && length > 0) {
507 			cur_len = MIN(MBLKL(mp) - offset, length);
508 			bcopy(digest_tmp + scratch_offset,
509 			    mp->b_rptr + offset, cur_len);
510 
511 			length -= cur_len;
512 			mp = mp->b_cont;
513 			scratch_offset += cur_len;
514 			offset = 0;
515 		}
516 
517 		if (mp == NULL && length > 0) {
518 			/*
519 			 * The end of the specified mblk was reached but
520 			 * the length requested could not be processed, i.e.
521 			 * The caller requested to digest more data than it
522 			 * provided.
523 			 */
524 			return (CRYPTO_DATA_LEN_RANGE);
525 		}
526 	}
527 
528 	return (CRYPTO_SUCCESS);
529 }
530 
531 /* ARGSUSED */
532 static int
533 md4_digest(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *digest,
534     crypto_req_handle_t req)
535 {
536 	int ret = CRYPTO_SUCCESS;
537 
538 	ASSERT(ctx->cc_provider_private != NULL);
539 
540 	/*
541 	 * We need to just return the length needed to store the output.
542 	 * We should not destroy the context for the following cases.
543 	 */
544 	if ((digest->cd_length == 0) ||
545 	    (digest->cd_length < MD4_DIGEST_LENGTH)) {
546 		digest->cd_length = MD4_DIGEST_LENGTH;
547 		return (CRYPTO_BUFFER_TOO_SMALL);
548 	}
549 
550 	/*
551 	 * Do the MD4 update on the specified input data.
552 	 */
553 	switch (data->cd_format) {
554 	case CRYPTO_DATA_RAW:
555 		MD4Update(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
556 		    data->cd_raw.iov_base + data->cd_offset,
557 		    data->cd_length);
558 		break;
559 	case CRYPTO_DATA_UIO:
560 		ret = md4_digest_update_uio(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
561 		    data);
562 		break;
563 	case CRYPTO_DATA_MBLK:
564 		ret = md4_digest_update_mblk(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
565 		    data);
566 		break;
567 	default:
568 		ret = CRYPTO_ARGUMENTS_BAD;
569 	}
570 
571 	if (ret != CRYPTO_SUCCESS) {
572 		/* the update failed, free context and bail */
573 		kmem_free(ctx->cc_provider_private, sizeof (md4_ctx_t));
574 		ctx->cc_provider_private = NULL;
575 		digest->cd_length = 0;
576 		return (ret);
577 	}
578 
579 	/*
580 	 * Do an MD4 final, must be done separately since the digest
581 	 * type can be different than the input data type.
582 	 */
583 	switch (digest->cd_format) {
584 	case CRYPTO_DATA_RAW:
585 		MD4Final((unsigned char *)digest->cd_raw.iov_base +
586 		    digest->cd_offset, &PROV_MD4_CTX(ctx)->mc_md4_ctx);
587 		break;
588 	case CRYPTO_DATA_UIO:
589 		ret = md4_digest_final_uio(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
590 		    digest, MD4_DIGEST_LENGTH, NULL);
591 		break;
592 	case CRYPTO_DATA_MBLK:
593 		ret = md4_digest_final_mblk(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
594 		    digest, MD4_DIGEST_LENGTH, NULL);
595 		break;
596 	default:
597 		ret = CRYPTO_ARGUMENTS_BAD;
598 	}
599 
600 	/* all done, free context and return */
601 
602 	if (ret == CRYPTO_SUCCESS) {
603 		digest->cd_length = MD4_DIGEST_LENGTH;
604 	} else {
605 		digest->cd_length = 0;
606 	}
607 
608 	kmem_free(ctx->cc_provider_private, sizeof (md4_ctx_t));
609 	ctx->cc_provider_private = NULL;
610 	return (ret);
611 }
612 
613 /* ARGSUSED */
614 static int
615 md4_digest_update(crypto_ctx_t *ctx, crypto_data_t *data,
616     crypto_req_handle_t req)
617 {
618 	int ret = CRYPTO_SUCCESS;
619 
620 	ASSERT(ctx->cc_provider_private != NULL);
621 
622 	/*
623 	 * Do the MD4 update on the specified input data.
624 	 */
625 	switch (data->cd_format) {
626 	case CRYPTO_DATA_RAW:
627 		MD4Update(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
628 		    data->cd_raw.iov_base + data->cd_offset,
629 		    data->cd_length);
630 		break;
631 	case CRYPTO_DATA_UIO:
632 		ret = md4_digest_update_uio(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
633 		    data);
634 		break;
635 	case CRYPTO_DATA_MBLK:
636 		ret = md4_digest_update_mblk(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
637 		    data);
638 		break;
639 	default:
640 		ret = CRYPTO_ARGUMENTS_BAD;
641 	}
642 
643 	return (ret);
644 }
645 
646 /* ARGSUSED */
647 static int
648 md4_digest_final(crypto_ctx_t *ctx, crypto_data_t *digest,
649     crypto_req_handle_t req)
650 {
651 	int ret = CRYPTO_SUCCESS;
652 
653 	ASSERT(ctx->cc_provider_private != NULL);
654 
655 	/*
656 	 * We need to just return the length needed to store the output.
657 	 * We should not destroy the context for the following cases.
658 	 */
659 	if ((digest->cd_length == 0) ||
660 	    (digest->cd_length < MD4_DIGEST_LENGTH)) {
661 		digest->cd_length = MD4_DIGEST_LENGTH;
662 		return (CRYPTO_BUFFER_TOO_SMALL);
663 	}
664 
665 	/*
666 	 * Do an MD4 final.
667 	 */
668 	switch (digest->cd_format) {
669 	case CRYPTO_DATA_RAW:
670 		MD4Final((unsigned char *)digest->cd_raw.iov_base +
671 		    digest->cd_offset, &PROV_MD4_CTX(ctx)->mc_md4_ctx);
672 		break;
673 	case CRYPTO_DATA_UIO:
674 		ret = md4_digest_final_uio(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
675 		    digest, MD4_DIGEST_LENGTH, NULL);
676 		break;
677 	case CRYPTO_DATA_MBLK:
678 		ret = md4_digest_final_mblk(&PROV_MD4_CTX(ctx)->mc_md4_ctx,
679 		    digest, MD4_DIGEST_LENGTH, NULL);
680 		break;
681 	default:
682 		ret = CRYPTO_ARGUMENTS_BAD;
683 	}
684 
685 	/* all done, free context and return */
686 
687 	if (ret == CRYPTO_SUCCESS) {
688 		digest->cd_length = MD4_DIGEST_LENGTH;
689 	} else {
690 		digest->cd_length = 0;
691 	}
692 
693 	kmem_free(ctx->cc_provider_private, sizeof (md4_ctx_t));
694 	ctx->cc_provider_private = NULL;
695 
696 	return (ret);
697 }
698 
699 /* ARGSUSED */
700 static int
701 md4_digest_atomic(crypto_provider_handle_t provider,
702     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
703     crypto_data_t *data, crypto_data_t *digest,
704     crypto_req_handle_t req)
705 {
706 	int ret = CRYPTO_SUCCESS;
707 	MD4_CTX md4_ctx;
708 
709 	if (mechanism->cm_type != MD4_MECH_INFO_TYPE)
710 		return (CRYPTO_MECHANISM_INVALID);
711 
712 	/*
713 	 * Do the MD4 init.
714 	 */
715 	MD4Init(&md4_ctx);
716 
717 	/*
718 	 * Do the MD4 update on the specified input data.
719 	 */
720 	switch (data->cd_format) {
721 	case CRYPTO_DATA_RAW:
722 		MD4Update(&md4_ctx, data->cd_raw.iov_base + data->cd_offset,
723 		    data->cd_length);
724 		break;
725 	case CRYPTO_DATA_UIO:
726 		ret = md4_digest_update_uio(&md4_ctx, data);
727 		break;
728 	case CRYPTO_DATA_MBLK:
729 		ret = md4_digest_update_mblk(&md4_ctx, data);
730 		break;
731 	default:
732 		ret = CRYPTO_ARGUMENTS_BAD;
733 	}
734 
735 	if (ret != CRYPTO_SUCCESS) {
736 		/* the update failed, bail */
737 		digest->cd_length = 0;
738 		return (ret);
739 	}
740 
741 	/*
742 	 * Do an MD4 final, must be done separately since the digest
743 	 * type can be different than the input data type.
744 	 */
745 	switch (digest->cd_format) {
746 	case CRYPTO_DATA_RAW:
747 		MD4Final((unsigned char *)digest->cd_raw.iov_base +
748 		    digest->cd_offset, &md4_ctx);
749 		break;
750 	case CRYPTO_DATA_UIO:
751 		ret = md4_digest_final_uio(&md4_ctx, digest,
752 		    MD4_DIGEST_LENGTH, NULL);
753 		break;
754 	case CRYPTO_DATA_MBLK:
755 		ret = md4_digest_final_mblk(&md4_ctx, digest,
756 		    MD4_DIGEST_LENGTH, NULL);
757 		break;
758 	default:
759 		ret = CRYPTO_ARGUMENTS_BAD;
760 	}
761 
762 	if (ret == CRYPTO_SUCCESS) {
763 		digest->cd_length = MD4_DIGEST_LENGTH;
764 	} else {
765 		digest->cd_length = 0;
766 	}
767 
768 	return (ret);
769 }
770