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