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