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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)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
md4_provider_status(crypto_provider_handle_t provider,uint_t * status)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
md4_digest_init(crypto_ctx_t * ctx,crypto_mechanism_t * mechanism,crypto_req_handle_t req)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
md4_digest_update_uio(MD4_CTX * md4_ctx,crypto_data_t * data)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
md4_digest_final_uio(MD4_CTX * md4_ctx,crypto_data_t * digest,ulong_t digest_len,uchar_t * digest_scratch)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
md4_digest_update_mblk(MD4_CTX * md4_ctx,crypto_data_t * data)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
md4_digest_final_mblk(MD4_CTX * md4_ctx,crypto_data_t * digest,ulong_t digest_len,uchar_t * digest_scratch)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
md4_digest(crypto_ctx_t * ctx,crypto_data_t * data,crypto_data_t * digest,crypto_req_handle_t req)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
md4_digest_update(crypto_ctx_t * ctx,crypto_data_t * data,crypto_req_handle_t req)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
md4_digest_final(crypto_ctx_t * ctx,crypto_data_t * digest,crypto_req_handle_t req)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
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)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