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