11694Sdarrenm /*
21694Sdarrenm * CDDL HEADER START
31694Sdarrenm *
41694Sdarrenm * The contents of this file are subject to the terms of the
51694Sdarrenm * Common Development and Distribution License (the "License").
61694Sdarrenm * You may not use this file except in compliance with the License.
71694Sdarrenm *
81694Sdarrenm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91694Sdarrenm * or http://www.opensolaris.org/os/licensing.
101694Sdarrenm * See the License for the specific language governing permissions
111694Sdarrenm * and limitations under the License.
121694Sdarrenm *
131694Sdarrenm * When distributing Covered Code, include this CDDL HEADER in each
141694Sdarrenm * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151694Sdarrenm * If applicable, add the following below this CDDL HEADER, with the
161694Sdarrenm * fields enclosed by brackets "[]" replaced with your own identifying
171694Sdarrenm * information: Portions Copyright [yyyy] [name of copyright owner]
181694Sdarrenm *
191694Sdarrenm * CDDL HEADER END
201694Sdarrenm */
211694Sdarrenm
221694Sdarrenm /*
23*11751SAnthony.Scarpino@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
241694Sdarrenm * Use is subject to license terms.
251694Sdarrenm */
261694Sdarrenm
271694Sdarrenm /*
281694Sdarrenm * In kernel module, the md5 module is created with two modlinkages:
291694Sdarrenm * - a modlmisc that allows consumers to directly call the entry points
301694Sdarrenm * MD5Init, MD5Update, and MD5Final.
311694Sdarrenm * - a modlcrypto that allows the module to register with the Kernel
321694Sdarrenm * Cryptographic Framework (KCF) as a software provider for the MD5
331694Sdarrenm * mechanisms.
341694Sdarrenm */
351694Sdarrenm
361694Sdarrenm #include <sys/types.h>
371694Sdarrenm #include <sys/systm.h>
381694Sdarrenm #include <sys/modctl.h>
391694Sdarrenm #include <sys/cmn_err.h>
401694Sdarrenm #include <sys/ddi.h>
411694Sdarrenm #include <sys/crypto/common.h>
421694Sdarrenm #include <sys/crypto/spi.h>
431694Sdarrenm #include <sys/sysmacros.h>
441694Sdarrenm #include <sys/strsun.h>
451694Sdarrenm #include <sys/note.h>
461694Sdarrenm #include <sys/md5.h>
471694Sdarrenm
481694Sdarrenm extern struct mod_ops mod_miscops;
491694Sdarrenm extern struct mod_ops mod_cryptoops;
501694Sdarrenm
511694Sdarrenm /*
521694Sdarrenm * Module linkage information for the kernel.
531694Sdarrenm */
541694Sdarrenm
551694Sdarrenm static struct modlmisc modlmisc = {
561694Sdarrenm &mod_miscops,
571694Sdarrenm "MD5 Message-Digest Algorithm"
581694Sdarrenm };
591694Sdarrenm
601694Sdarrenm static struct modlcrypto modlcrypto = {
611694Sdarrenm &mod_cryptoops,
625072Smcpowers "MD5 Kernel SW Provider"
631694Sdarrenm };
641694Sdarrenm
651694Sdarrenm static struct modlinkage modlinkage = {
661694Sdarrenm MODREV_1,
671694Sdarrenm (void *)&modlmisc,
681694Sdarrenm (void *)&modlcrypto,
691694Sdarrenm NULL
701694Sdarrenm };
711694Sdarrenm
721694Sdarrenm /*
731694Sdarrenm * CSPI information (entry points, provider info, etc.)
741694Sdarrenm */
751694Sdarrenm
761694Sdarrenm typedef enum md5_mech_type {
771694Sdarrenm MD5_MECH_INFO_TYPE, /* SUN_CKM_MD5 */
781694Sdarrenm MD5_HMAC_MECH_INFO_TYPE, /* SUN_CKM_MD5_HMAC */
791694Sdarrenm MD5_HMAC_GEN_MECH_INFO_TYPE /* SUN_CKM_MD5_HMAC_GENERAL */
801694Sdarrenm } md5_mech_type_t;
811694Sdarrenm
821694Sdarrenm #define MD5_DIGEST_LENGTH 16 /* MD5 digest length in bytes */
831694Sdarrenm #define MD5_HMAC_BLOCK_SIZE 64 /* MD5 block size */
849364SVladimir.Kotal@Sun.COM #define MD5_HMAC_MIN_KEY_LEN 1 /* MD5-HMAC min key length in bytes */
859364SVladimir.Kotal@Sun.COM #define MD5_HMAC_MAX_KEY_LEN INT_MAX /* MD5-HMAC max key length in bytes */
861694Sdarrenm #define MD5_HMAC_INTS_PER_BLOCK (MD5_HMAC_BLOCK_SIZE/sizeof (uint32_t))
871694Sdarrenm
881694Sdarrenm /*
891694Sdarrenm * Context for MD5 mechanism.
901694Sdarrenm */
911694Sdarrenm typedef struct md5_ctx {
921694Sdarrenm md5_mech_type_t mc_mech_type; /* type of context */
931694Sdarrenm MD5_CTX mc_md5_ctx; /* MD5 context */
941694Sdarrenm } md5_ctx_t;
951694Sdarrenm
961694Sdarrenm /*
971694Sdarrenm * Context for MD5-HMAC and MD5-HMAC-GENERAL mechanisms.
981694Sdarrenm */
991694Sdarrenm typedef struct md5_hmac_ctx {
1001694Sdarrenm md5_mech_type_t hc_mech_type; /* type of context */
1011694Sdarrenm uint32_t hc_digest_len; /* digest len in bytes */
1021694Sdarrenm MD5_CTX hc_icontext; /* inner MD5 context */
1031694Sdarrenm MD5_CTX hc_ocontext; /* outer MD5 context */
1041694Sdarrenm } md5_hmac_ctx_t;
1051694Sdarrenm
1061694Sdarrenm /*
1071694Sdarrenm * Macros to access the MD5 or MD5-HMAC contexts from a context passed
1081694Sdarrenm * by KCF to one of the entry points.
1091694Sdarrenm */
1101694Sdarrenm
1111694Sdarrenm #define PROV_MD5_CTX(ctx) ((md5_ctx_t *)(ctx)->cc_provider_private)
1121694Sdarrenm #define PROV_MD5_HMAC_CTX(ctx) ((md5_hmac_ctx_t *)(ctx)->cc_provider_private)
1131694Sdarrenm /* to extract the digest length passed as mechanism parameter */
1141694Sdarrenm
1151694Sdarrenm #define PROV_MD5_GET_DIGEST_LEN(m, len) { \
1161694Sdarrenm if (IS_P2ALIGNED((m)->cm_param, sizeof (ulong_t))) \
11711141Sopensolaris@drydog.com (len) = (uint32_t)*((ulong_t *)(void *)mechanism->cm_param); \
1181694Sdarrenm else { \
1191694Sdarrenm ulong_t tmp_ulong; \
1201694Sdarrenm bcopy((m)->cm_param, &tmp_ulong, sizeof (ulong_t)); \
1211694Sdarrenm (len) = (uint32_t)tmp_ulong; \
1221694Sdarrenm } \
1231694Sdarrenm }
1241694Sdarrenm
1251694Sdarrenm #define PROV_MD5_DIGEST_KEY(ctx, key, len, digest) { \
1261694Sdarrenm MD5Init(ctx); \
1271694Sdarrenm MD5Update(ctx, key, len); \
1281694Sdarrenm MD5Final(digest, ctx); \
1291694Sdarrenm }
1301694Sdarrenm
1311694Sdarrenm /*
1321694Sdarrenm * Mechanism info structure passed to KCF during registration.
1331694Sdarrenm */
1341694Sdarrenm static crypto_mech_info_t md5_mech_info_tab[] = {
1351694Sdarrenm /* MD5 */
1361694Sdarrenm {SUN_CKM_MD5, MD5_MECH_INFO_TYPE,
1371694Sdarrenm CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC,
1381694Sdarrenm 0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
1391694Sdarrenm /* MD5-HMAC */
1401694Sdarrenm {SUN_CKM_MD5_HMAC, MD5_HMAC_MECH_INFO_TYPE,
1411694Sdarrenm CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC,
1421694Sdarrenm MD5_HMAC_MIN_KEY_LEN, MD5_HMAC_MAX_KEY_LEN,
1439364SVladimir.Kotal@Sun.COM CRYPTO_KEYSIZE_UNIT_IN_BYTES},
1441694Sdarrenm /* MD5-HMAC GENERAL */
1451694Sdarrenm {SUN_CKM_MD5_HMAC_GENERAL, MD5_HMAC_GEN_MECH_INFO_TYPE,
1461694Sdarrenm CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC,
1471694Sdarrenm MD5_HMAC_MIN_KEY_LEN, MD5_HMAC_MAX_KEY_LEN,
1489364SVladimir.Kotal@Sun.COM CRYPTO_KEYSIZE_UNIT_IN_BYTES}
1491694Sdarrenm };
1501694Sdarrenm
1511694Sdarrenm static void md5_provider_status(crypto_provider_handle_t, uint_t *);
1521694Sdarrenm
1531694Sdarrenm static crypto_control_ops_t md5_control_ops = {
1541694Sdarrenm md5_provider_status
1551694Sdarrenm };
1561694Sdarrenm
1571694Sdarrenm static int md5_digest_init(crypto_ctx_t *, crypto_mechanism_t *,
1581694Sdarrenm crypto_req_handle_t);
1591694Sdarrenm static int md5_digest(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
1601694Sdarrenm crypto_req_handle_t);
1611694Sdarrenm static int md5_digest_update(crypto_ctx_t *, crypto_data_t *,
1621694Sdarrenm crypto_req_handle_t);
1631694Sdarrenm static int md5_digest_final(crypto_ctx_t *, crypto_data_t *,
1641694Sdarrenm crypto_req_handle_t);
1651694Sdarrenm static int md5_digest_atomic(crypto_provider_handle_t, crypto_session_id_t,
1661694Sdarrenm crypto_mechanism_t *, crypto_data_t *, crypto_data_t *,
1671694Sdarrenm crypto_req_handle_t);
1681694Sdarrenm
1691694Sdarrenm static crypto_digest_ops_t md5_digest_ops = {
1701694Sdarrenm md5_digest_init,
1711694Sdarrenm md5_digest,
1721694Sdarrenm md5_digest_update,
1731694Sdarrenm NULL,
1741694Sdarrenm md5_digest_final,
1751694Sdarrenm md5_digest_atomic
1761694Sdarrenm };
1771694Sdarrenm
1781694Sdarrenm static int md5_mac_init(crypto_ctx_t *, crypto_mechanism_t *, crypto_key_t *,
1791694Sdarrenm crypto_spi_ctx_template_t, crypto_req_handle_t);
1801694Sdarrenm static int md5_mac_update(crypto_ctx_t *, crypto_data_t *, crypto_req_handle_t);
1811694Sdarrenm static int md5_mac_final(crypto_ctx_t *, crypto_data_t *, crypto_req_handle_t);
1821694Sdarrenm static int md5_mac_atomic(crypto_provider_handle_t, crypto_session_id_t,
1831694Sdarrenm crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
1841694Sdarrenm crypto_spi_ctx_template_t, crypto_req_handle_t);
1851694Sdarrenm static int md5_mac_verify_atomic(crypto_provider_handle_t, crypto_session_id_t,
1861694Sdarrenm crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
1871694Sdarrenm crypto_spi_ctx_template_t, crypto_req_handle_t);
1881694Sdarrenm
1891694Sdarrenm static crypto_mac_ops_t md5_mac_ops = {
1901694Sdarrenm md5_mac_init,
1911694Sdarrenm NULL,
1921694Sdarrenm md5_mac_update,
1931694Sdarrenm md5_mac_final,
1941694Sdarrenm md5_mac_atomic,
1951694Sdarrenm md5_mac_verify_atomic
1961694Sdarrenm };
1971694Sdarrenm
1981694Sdarrenm static int md5_create_ctx_template(crypto_provider_handle_t,
1991694Sdarrenm crypto_mechanism_t *, crypto_key_t *, crypto_spi_ctx_template_t *,
2001694Sdarrenm size_t *, crypto_req_handle_t);
2011694Sdarrenm static int md5_free_context(crypto_ctx_t *);
2021694Sdarrenm
2031694Sdarrenm static crypto_ctx_ops_t md5_ctx_ops = {
2041694Sdarrenm md5_create_ctx_template,
2051694Sdarrenm md5_free_context
2061694Sdarrenm };
2071694Sdarrenm
2081694Sdarrenm static crypto_ops_t md5_crypto_ops = {
2091694Sdarrenm &md5_control_ops,
2101694Sdarrenm &md5_digest_ops,
2111694Sdarrenm NULL,
2121694Sdarrenm &md5_mac_ops,
2131694Sdarrenm NULL,
2141694Sdarrenm NULL,
2151694Sdarrenm NULL,
2161694Sdarrenm NULL,
2171694Sdarrenm NULL,
2181694Sdarrenm NULL,
2191694Sdarrenm NULL,
2201694Sdarrenm NULL,
2211694Sdarrenm NULL,
2221694Sdarrenm &md5_ctx_ops
2231694Sdarrenm };
2241694Sdarrenm
2251694Sdarrenm static crypto_provider_info_t md5_prov_info = {
2261694Sdarrenm CRYPTO_SPI_VERSION_1,
2271694Sdarrenm "MD5 Software Provider",
2281694Sdarrenm CRYPTO_SW_PROVIDER,
2291694Sdarrenm {&modlinkage},
2301694Sdarrenm NULL,
2311694Sdarrenm &md5_crypto_ops,
2321694Sdarrenm sizeof (md5_mech_info_tab)/sizeof (crypto_mech_info_t),
2331694Sdarrenm md5_mech_info_tab
2341694Sdarrenm };
2351694Sdarrenm
2361694Sdarrenm static crypto_kcf_provider_handle_t md5_prov_handle = NULL;
2371694Sdarrenm
2381694Sdarrenm int
_init(void)2391694Sdarrenm _init(void)
2401694Sdarrenm {
2411694Sdarrenm int ret;
2421694Sdarrenm
2431694Sdarrenm if ((ret = mod_install(&modlinkage)) != 0)
2441694Sdarrenm return (ret);
2451694Sdarrenm
2461694Sdarrenm /*
247*11751SAnthony.Scarpino@Sun.COM * Register with KCF. If the registration fails, do not uninstall the
248*11751SAnthony.Scarpino@Sun.COM * module, since the functionality provided by misc/md5 should still be
249*11751SAnthony.Scarpino@Sun.COM * available.
2501694Sdarrenm */
251*11751SAnthony.Scarpino@Sun.COM (void) crypto_register_provider(&md5_prov_info, &md5_prov_handle);
2521694Sdarrenm
2531694Sdarrenm return (0);
2541694Sdarrenm }
2551694Sdarrenm
2561694Sdarrenm int
_fini(void)2571694Sdarrenm _fini(void)
2581694Sdarrenm {
2591694Sdarrenm int ret;
2601694Sdarrenm
2611694Sdarrenm /*
2621694Sdarrenm * Unregister from KCF if previous registration succeeded.
2631694Sdarrenm */
2641694Sdarrenm if (md5_prov_handle != NULL) {
2651694Sdarrenm if ((ret = crypto_unregister_provider(md5_prov_handle)) !=
266*11751SAnthony.Scarpino@Sun.COM CRYPTO_SUCCESS)
267*11751SAnthony.Scarpino@Sun.COM return (ret);
268*11751SAnthony.Scarpino@Sun.COM
2691694Sdarrenm md5_prov_handle = NULL;
2701694Sdarrenm }
2711694Sdarrenm
2721694Sdarrenm return (mod_remove(&modlinkage));
2731694Sdarrenm }
2741694Sdarrenm
2751694Sdarrenm int
_info(struct modinfo * modinfop)2761694Sdarrenm _info(struct modinfo *modinfop)
2771694Sdarrenm {
2781694Sdarrenm return (mod_info(&modlinkage, modinfop));
2791694Sdarrenm }
2801694Sdarrenm
2811694Sdarrenm /*
2821694Sdarrenm * KCF software provider control entry points.
2831694Sdarrenm */
2841694Sdarrenm /* ARGSUSED */
2851694Sdarrenm static void
md5_provider_status(crypto_provider_handle_t provider,uint_t * status)2861694Sdarrenm md5_provider_status(crypto_provider_handle_t provider, uint_t *status)
2871694Sdarrenm {
2881694Sdarrenm *status = CRYPTO_PROVIDER_READY;
2891694Sdarrenm }
2901694Sdarrenm
2911694Sdarrenm /*
2921694Sdarrenm * KCF software provider digest entry points.
2931694Sdarrenm */
2941694Sdarrenm
2951694Sdarrenm static int
md5_digest_init(crypto_ctx_t * ctx,crypto_mechanism_t * mechanism,crypto_req_handle_t req)2961694Sdarrenm md5_digest_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
2971694Sdarrenm crypto_req_handle_t req)
2981694Sdarrenm {
2991694Sdarrenm if (mechanism->cm_type != MD5_MECH_INFO_TYPE)
3001694Sdarrenm return (CRYPTO_MECHANISM_INVALID);
3011694Sdarrenm
3021694Sdarrenm /*
3031694Sdarrenm * Allocate and initialize MD5 context.
3041694Sdarrenm */
3051694Sdarrenm ctx->cc_provider_private = kmem_alloc(sizeof (md5_ctx_t),
3061694Sdarrenm crypto_kmflag(req));
3071694Sdarrenm if (ctx->cc_provider_private == NULL)
3081694Sdarrenm return (CRYPTO_HOST_MEMORY);
3091694Sdarrenm
3101694Sdarrenm PROV_MD5_CTX(ctx)->mc_mech_type = MD5_MECH_INFO_TYPE;
3111694Sdarrenm MD5Init(&PROV_MD5_CTX(ctx)->mc_md5_ctx);
3121694Sdarrenm
3131694Sdarrenm return (CRYPTO_SUCCESS);
3141694Sdarrenm }
3151694Sdarrenm
3161694Sdarrenm /*
3171694Sdarrenm * Helper MD5 digest update function for uio data.
3181694Sdarrenm */
3191694Sdarrenm static int
md5_digest_update_uio(MD5_CTX * md5_ctx,crypto_data_t * data)3201694Sdarrenm md5_digest_update_uio(MD5_CTX *md5_ctx, crypto_data_t *data)
3211694Sdarrenm {
3221694Sdarrenm off_t offset = data->cd_offset;
3231694Sdarrenm size_t length = data->cd_length;
3241694Sdarrenm uint_t vec_idx;
3251694Sdarrenm size_t cur_len;
3261694Sdarrenm
3271694Sdarrenm /* we support only kernel buffer */
3281694Sdarrenm if (data->cd_uio->uio_segflg != UIO_SYSSPACE)
3291694Sdarrenm return (CRYPTO_ARGUMENTS_BAD);
3301694Sdarrenm
3311694Sdarrenm /*
3321694Sdarrenm * Jump to the first iovec containing data to be
3331694Sdarrenm * digested.
3341694Sdarrenm */
3351694Sdarrenm for (vec_idx = 0; vec_idx < data->cd_uio->uio_iovcnt &&
3361694Sdarrenm offset >= data->cd_uio->uio_iov[vec_idx].iov_len;
3375072Smcpowers offset -= data->cd_uio->uio_iov[vec_idx++].iov_len)
3385072Smcpowers ;
3391694Sdarrenm if (vec_idx == data->cd_uio->uio_iovcnt) {
3401694Sdarrenm /*
3411694Sdarrenm * The caller specified an offset that is larger than the
3421694Sdarrenm * total size of the buffers it provided.
3431694Sdarrenm */
3441694Sdarrenm return (CRYPTO_DATA_LEN_RANGE);
3451694Sdarrenm }
3461694Sdarrenm
3471694Sdarrenm /*
3481694Sdarrenm * Now do the digesting on the iovecs.
3491694Sdarrenm */
3501694Sdarrenm while (vec_idx < data->cd_uio->uio_iovcnt && length > 0) {
3511694Sdarrenm cur_len = MIN(data->cd_uio->uio_iov[vec_idx].iov_len -
3521694Sdarrenm offset, length);
3531694Sdarrenm
3541694Sdarrenm MD5Update(md5_ctx, data->cd_uio->uio_iov[vec_idx].iov_base +
3551694Sdarrenm offset, cur_len);
3561694Sdarrenm
3571694Sdarrenm length -= cur_len;
3581694Sdarrenm vec_idx++;
3591694Sdarrenm offset = 0;
3601694Sdarrenm }
3611694Sdarrenm
3621694Sdarrenm if (vec_idx == data->cd_uio->uio_iovcnt && length > 0) {
3631694Sdarrenm /*
3641694Sdarrenm * The end of the specified iovec's was reached but
3651694Sdarrenm * the length requested could not be processed, i.e.
3661694Sdarrenm * The caller requested to digest more data than it provided.
3671694Sdarrenm */
3681694Sdarrenm return (CRYPTO_DATA_LEN_RANGE);
3691694Sdarrenm }
3701694Sdarrenm
3711694Sdarrenm return (CRYPTO_SUCCESS);
3721694Sdarrenm }
3731694Sdarrenm
3741694Sdarrenm /*
3751694Sdarrenm * Helper MD5 digest final function for uio data.
3761694Sdarrenm * digest_len is the length of the desired digest. If digest_len
3771694Sdarrenm * is smaller than the default MD5 digest length, the caller
3781694Sdarrenm * must pass a scratch buffer, digest_scratch, which must
3791694Sdarrenm * be at least MD5_DIGEST_LENGTH bytes.
3801694Sdarrenm */
3811694Sdarrenm static int
md5_digest_final_uio(MD5_CTX * md5_ctx,crypto_data_t * digest,ulong_t digest_len,uchar_t * digest_scratch)3821694Sdarrenm md5_digest_final_uio(MD5_CTX *md5_ctx, crypto_data_t *digest,
3831694Sdarrenm ulong_t digest_len, uchar_t *digest_scratch)
3841694Sdarrenm {
3851694Sdarrenm off_t offset = digest->cd_offset;
3861694Sdarrenm uint_t vec_idx;
3871694Sdarrenm
3881694Sdarrenm /* we support only kernel buffer */
3891694Sdarrenm if (digest->cd_uio->uio_segflg != UIO_SYSSPACE)
3901694Sdarrenm return (CRYPTO_ARGUMENTS_BAD);
3911694Sdarrenm
3921694Sdarrenm /*
3931694Sdarrenm * Jump to the first iovec containing ptr to the digest to
3941694Sdarrenm * be returned.
3951694Sdarrenm */
3961694Sdarrenm for (vec_idx = 0; offset >= digest->cd_uio->uio_iov[vec_idx].iov_len &&
3971694Sdarrenm vec_idx < digest->cd_uio->uio_iovcnt;
3985072Smcpowers offset -= digest->cd_uio->uio_iov[vec_idx++].iov_len)
3995072Smcpowers ;
4001694Sdarrenm if (vec_idx == digest->cd_uio->uio_iovcnt) {
4011694Sdarrenm /*
4021694Sdarrenm * The caller specified an offset that is
4031694Sdarrenm * larger than the total size of the buffers
4041694Sdarrenm * it provided.
4051694Sdarrenm */
4061694Sdarrenm return (CRYPTO_DATA_LEN_RANGE);
4071694Sdarrenm }
4081694Sdarrenm
4091694Sdarrenm if (offset + digest_len <=
4101694Sdarrenm digest->cd_uio->uio_iov[vec_idx].iov_len) {
4111694Sdarrenm /*
4121694Sdarrenm * The computed MD5 digest will fit in the current
4131694Sdarrenm * iovec.
4141694Sdarrenm */
4151694Sdarrenm if (digest_len != MD5_DIGEST_LENGTH) {
4161694Sdarrenm /*
4171694Sdarrenm * The caller requested a short digest. Digest
4181694Sdarrenm * into a scratch buffer and return to
4191694Sdarrenm * the user only what was requested.
4201694Sdarrenm */
4211694Sdarrenm MD5Final(digest_scratch, md5_ctx);
4221694Sdarrenm bcopy(digest_scratch, (uchar_t *)digest->
4231694Sdarrenm cd_uio->uio_iov[vec_idx].iov_base + offset,
4241694Sdarrenm digest_len);
4251694Sdarrenm } else {
4261694Sdarrenm MD5Final((uchar_t *)digest->
4271694Sdarrenm cd_uio->uio_iov[vec_idx].iov_base + offset,
4281694Sdarrenm md5_ctx);
4291694Sdarrenm }
4301694Sdarrenm } else {
4311694Sdarrenm /*
4321694Sdarrenm * The computed digest will be crossing one or more iovec's.
4331694Sdarrenm * This is bad performance-wise but we need to support it.
4341694Sdarrenm * Allocate a small scratch buffer on the stack and
4351694Sdarrenm * copy it piece meal to the specified digest iovec's.
4361694Sdarrenm */
4371694Sdarrenm uchar_t digest_tmp[MD5_DIGEST_LENGTH];
4381694Sdarrenm off_t scratch_offset = 0;
4391694Sdarrenm size_t length = digest_len;
4401694Sdarrenm size_t cur_len;
4411694Sdarrenm
4421694Sdarrenm MD5Final(digest_tmp, md5_ctx);
4431694Sdarrenm
4441694Sdarrenm while (vec_idx < digest->cd_uio->uio_iovcnt && length > 0) {
4451694Sdarrenm cur_len = MIN(digest->cd_uio->uio_iov[vec_idx].iov_len -
4461694Sdarrenm offset, length);
4471694Sdarrenm bcopy(digest_tmp + scratch_offset,
4481694Sdarrenm digest->cd_uio->uio_iov[vec_idx].iov_base + offset,
4491694Sdarrenm cur_len);
4501694Sdarrenm
4511694Sdarrenm length -= cur_len;
4521694Sdarrenm vec_idx++;
4531694Sdarrenm scratch_offset += cur_len;
4541694Sdarrenm offset = 0;
4551694Sdarrenm }
4561694Sdarrenm
4571694Sdarrenm if (vec_idx == digest->cd_uio->uio_iovcnt && length > 0) {
4581694Sdarrenm /*
4591694Sdarrenm * The end of the specified iovec's was reached but
4601694Sdarrenm * the length requested could not be processed, i.e.
4611694Sdarrenm * The caller requested to digest more data than it
4621694Sdarrenm * provided.
4631694Sdarrenm */
4641694Sdarrenm return (CRYPTO_DATA_LEN_RANGE);
4651694Sdarrenm }
4661694Sdarrenm }
4671694Sdarrenm
4681694Sdarrenm return (CRYPTO_SUCCESS);
4691694Sdarrenm }
4701694Sdarrenm
4711694Sdarrenm /*
4721694Sdarrenm * Helper MD5 digest update for mblk's.
4731694Sdarrenm */
4741694Sdarrenm static int
md5_digest_update_mblk(MD5_CTX * md5_ctx,crypto_data_t * data)4751694Sdarrenm md5_digest_update_mblk(MD5_CTX *md5_ctx, crypto_data_t *data)
4761694Sdarrenm {
4771694Sdarrenm off_t offset = data->cd_offset;
4781694Sdarrenm size_t length = data->cd_length;
4791694Sdarrenm mblk_t *mp;
4801694Sdarrenm size_t cur_len;
4811694Sdarrenm
4821694Sdarrenm /*
4831694Sdarrenm * Jump to the first mblk_t containing data to be digested.
4841694Sdarrenm */
4851694Sdarrenm for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp);
4865072Smcpowers offset -= MBLKL(mp), mp = mp->b_cont)
4875072Smcpowers ;
4881694Sdarrenm if (mp == NULL) {
4891694Sdarrenm /*
4901694Sdarrenm * The caller specified an offset that is larger than the
4911694Sdarrenm * total size of the buffers it provided.
4921694Sdarrenm */
4931694Sdarrenm return (CRYPTO_DATA_LEN_RANGE);
4941694Sdarrenm }
4951694Sdarrenm
4961694Sdarrenm /*
4971694Sdarrenm * Now do the digesting on the mblk chain.
4981694Sdarrenm */
4991694Sdarrenm while (mp != NULL && length > 0) {
5001694Sdarrenm cur_len = MIN(MBLKL(mp) - offset, length);
5011694Sdarrenm MD5Update(md5_ctx, mp->b_rptr + offset, cur_len);
5021694Sdarrenm length -= cur_len;
5031694Sdarrenm offset = 0;
5041694Sdarrenm mp = mp->b_cont;
5051694Sdarrenm }
5061694Sdarrenm
5071694Sdarrenm if (mp == NULL && length > 0) {
5081694Sdarrenm /*
5091694Sdarrenm * The end of the mblk was reached but the length requested
5101694Sdarrenm * could not be processed, i.e. The caller requested
5111694Sdarrenm * to digest more data than it provided.
5121694Sdarrenm */
5131694Sdarrenm return (CRYPTO_DATA_LEN_RANGE);
5141694Sdarrenm }
5151694Sdarrenm
5161694Sdarrenm return (CRYPTO_SUCCESS);
5171694Sdarrenm }
5181694Sdarrenm
5191694Sdarrenm /*
5201694Sdarrenm * Helper MD5 digest final for mblk's.
5211694Sdarrenm * digest_len is the length of the desired digest. If digest_len
5221694Sdarrenm * is smaller than the default MD5 digest length, the caller
5231694Sdarrenm * must pass a scratch buffer, digest_scratch, which must
5241694Sdarrenm * be at least MD5_DIGEST_LENGTH bytes.
5251694Sdarrenm */
5261694Sdarrenm static int
md5_digest_final_mblk(MD5_CTX * md5_ctx,crypto_data_t * digest,ulong_t digest_len,uchar_t * digest_scratch)5271694Sdarrenm md5_digest_final_mblk(MD5_CTX *md5_ctx, crypto_data_t *digest,
5281694Sdarrenm ulong_t digest_len, uchar_t *digest_scratch)
5291694Sdarrenm {
5301694Sdarrenm off_t offset = digest->cd_offset;
5311694Sdarrenm mblk_t *mp;
5321694Sdarrenm
5331694Sdarrenm /*
5341694Sdarrenm * Jump to the first mblk_t that will be used to store the digest.
5351694Sdarrenm */
5361694Sdarrenm for (mp = digest->cd_mp; mp != NULL && offset >= MBLKL(mp);
5375072Smcpowers offset -= MBLKL(mp), mp = mp->b_cont)
5385072Smcpowers ;
5391694Sdarrenm if (mp == NULL) {
5401694Sdarrenm /*
5411694Sdarrenm * The caller specified an offset that is larger than the
5421694Sdarrenm * total size of the buffers it provided.
5431694Sdarrenm */
5441694Sdarrenm return (CRYPTO_DATA_LEN_RANGE);
5451694Sdarrenm }
5461694Sdarrenm
5471694Sdarrenm if (offset + digest_len <= MBLKL(mp)) {
5481694Sdarrenm /*
5491694Sdarrenm * The computed MD5 digest will fit in the current mblk.
5501694Sdarrenm * Do the MD5Final() in-place.
5511694Sdarrenm */
5521694Sdarrenm if (digest_len != MD5_DIGEST_LENGTH) {
5531694Sdarrenm /*
5541694Sdarrenm * The caller requested a short digest. Digest
5551694Sdarrenm * into a scratch buffer and return to
5561694Sdarrenm * the user only what was requested.
5571694Sdarrenm */
5581694Sdarrenm MD5Final(digest_scratch, md5_ctx);
5591694Sdarrenm bcopy(digest_scratch, mp->b_rptr + offset, digest_len);
5601694Sdarrenm } else {
5611694Sdarrenm MD5Final(mp->b_rptr + offset, md5_ctx);
5621694Sdarrenm }
5631694Sdarrenm } else {
5641694Sdarrenm /*
5651694Sdarrenm * The computed digest will be crossing one or more mblk's.
5661694Sdarrenm * This is bad performance-wise but we need to support it.
5671694Sdarrenm * Allocate a small scratch buffer on the stack and
5681694Sdarrenm * copy it piece meal to the specified digest iovec's.
5691694Sdarrenm */
5701694Sdarrenm uchar_t digest_tmp[MD5_DIGEST_LENGTH];
5711694Sdarrenm off_t scratch_offset = 0;
5721694Sdarrenm size_t length = digest_len;
5731694Sdarrenm size_t cur_len;
5741694Sdarrenm
5751694Sdarrenm MD5Final(digest_tmp, md5_ctx);
5761694Sdarrenm
5771694Sdarrenm while (mp != NULL && length > 0) {
5781694Sdarrenm cur_len = MIN(MBLKL(mp) - offset, length);
5791694Sdarrenm bcopy(digest_tmp + scratch_offset,
5801694Sdarrenm mp->b_rptr + offset, cur_len);
5811694Sdarrenm
5821694Sdarrenm length -= cur_len;
5831694Sdarrenm mp = mp->b_cont;
5841694Sdarrenm scratch_offset += cur_len;
5851694Sdarrenm offset = 0;
5861694Sdarrenm }
5871694Sdarrenm
5881694Sdarrenm if (mp == NULL && length > 0) {
5891694Sdarrenm /*
5901694Sdarrenm * The end of the specified mblk was reached but
5911694Sdarrenm * the length requested could not be processed, i.e.
5921694Sdarrenm * The caller requested to digest more data than it
5931694Sdarrenm * provided.
5941694Sdarrenm */
5951694Sdarrenm return (CRYPTO_DATA_LEN_RANGE);
5961694Sdarrenm }
5971694Sdarrenm }
5981694Sdarrenm
5991694Sdarrenm return (CRYPTO_SUCCESS);
6001694Sdarrenm }
6011694Sdarrenm
6021694Sdarrenm /* ARGSUSED */
6031694Sdarrenm static int
md5_digest(crypto_ctx_t * ctx,crypto_data_t * data,crypto_data_t * digest,crypto_req_handle_t req)6041694Sdarrenm md5_digest(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *digest,
6051694Sdarrenm crypto_req_handle_t req)
6061694Sdarrenm {
6071694Sdarrenm int ret = CRYPTO_SUCCESS;
6081694Sdarrenm
6091694Sdarrenm ASSERT(ctx->cc_provider_private != NULL);
6101694Sdarrenm
6111694Sdarrenm /*
6121694Sdarrenm * We need to just return the length needed to store the output.
6131694Sdarrenm * We should not destroy the context for the following cases.
6141694Sdarrenm */
6151694Sdarrenm if ((digest->cd_length == 0) ||
6161694Sdarrenm (digest->cd_length < MD5_DIGEST_LENGTH)) {
6171694Sdarrenm digest->cd_length = MD5_DIGEST_LENGTH;
6181694Sdarrenm return (CRYPTO_BUFFER_TOO_SMALL);
6191694Sdarrenm }
6201694Sdarrenm
6211694Sdarrenm /*
6221694Sdarrenm * Do the MD5 update on the specified input data.
6231694Sdarrenm */
6241694Sdarrenm switch (data->cd_format) {
6251694Sdarrenm case CRYPTO_DATA_RAW:
6261694Sdarrenm MD5Update(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
6271694Sdarrenm data->cd_raw.iov_base + data->cd_offset,
6281694Sdarrenm data->cd_length);
6291694Sdarrenm break;
6301694Sdarrenm case CRYPTO_DATA_UIO:
6311694Sdarrenm ret = md5_digest_update_uio(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
6321694Sdarrenm data);
6331694Sdarrenm break;
6341694Sdarrenm case CRYPTO_DATA_MBLK:
6351694Sdarrenm ret = md5_digest_update_mblk(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
6361694Sdarrenm data);
6371694Sdarrenm break;
6381694Sdarrenm default:
6391694Sdarrenm ret = CRYPTO_ARGUMENTS_BAD;
6401694Sdarrenm }
6411694Sdarrenm
6421694Sdarrenm if (ret != CRYPTO_SUCCESS) {
6431694Sdarrenm /* the update failed, free context and bail */
6441694Sdarrenm kmem_free(ctx->cc_provider_private, sizeof (md5_ctx_t));
6451694Sdarrenm ctx->cc_provider_private = NULL;
6461694Sdarrenm digest->cd_length = 0;
6471694Sdarrenm return (ret);
6481694Sdarrenm }
6491694Sdarrenm
6501694Sdarrenm /*
6511694Sdarrenm * Do an MD5 final, must be done separately since the digest
6521694Sdarrenm * type can be different than the input data type.
6531694Sdarrenm */
6541694Sdarrenm switch (digest->cd_format) {
6551694Sdarrenm case CRYPTO_DATA_RAW:
6561694Sdarrenm MD5Final((unsigned char *)digest->cd_raw.iov_base +
6571694Sdarrenm digest->cd_offset, &PROV_MD5_CTX(ctx)->mc_md5_ctx);
6581694Sdarrenm break;
6591694Sdarrenm case CRYPTO_DATA_UIO:
6601694Sdarrenm ret = md5_digest_final_uio(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
6611694Sdarrenm digest, MD5_DIGEST_LENGTH, NULL);
6621694Sdarrenm break;
6631694Sdarrenm case CRYPTO_DATA_MBLK:
6641694Sdarrenm ret = md5_digest_final_mblk(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
6651694Sdarrenm digest, MD5_DIGEST_LENGTH, NULL);
6661694Sdarrenm break;
6671694Sdarrenm default:
6681694Sdarrenm ret = CRYPTO_ARGUMENTS_BAD;
6691694Sdarrenm }
6701694Sdarrenm
6711694Sdarrenm /* all done, free context and return */
6721694Sdarrenm
6731694Sdarrenm if (ret == CRYPTO_SUCCESS) {
6741694Sdarrenm digest->cd_length = MD5_DIGEST_LENGTH;
6751694Sdarrenm } else {
6761694Sdarrenm digest->cd_length = 0;
6771694Sdarrenm }
6781694Sdarrenm
6791694Sdarrenm kmem_free(ctx->cc_provider_private, sizeof (md5_ctx_t));
6801694Sdarrenm ctx->cc_provider_private = NULL;
6811694Sdarrenm return (ret);
6821694Sdarrenm }
6831694Sdarrenm
6841694Sdarrenm /* ARGSUSED */
6851694Sdarrenm static int
md5_digest_update(crypto_ctx_t * ctx,crypto_data_t * data,crypto_req_handle_t req)6861694Sdarrenm md5_digest_update(crypto_ctx_t *ctx, crypto_data_t *data,
6871694Sdarrenm crypto_req_handle_t req)
6881694Sdarrenm {
6891694Sdarrenm int ret = CRYPTO_SUCCESS;
6901694Sdarrenm
6911694Sdarrenm ASSERT(ctx->cc_provider_private != NULL);
6921694Sdarrenm
6931694Sdarrenm /*
6941694Sdarrenm * Do the MD5 update on the specified input data.
6951694Sdarrenm */
6961694Sdarrenm switch (data->cd_format) {
6971694Sdarrenm case CRYPTO_DATA_RAW:
6981694Sdarrenm MD5Update(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
6991694Sdarrenm data->cd_raw.iov_base + data->cd_offset,
7001694Sdarrenm data->cd_length);
7011694Sdarrenm break;
7021694Sdarrenm case CRYPTO_DATA_UIO:
7031694Sdarrenm ret = md5_digest_update_uio(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
7041694Sdarrenm data);
7051694Sdarrenm break;
7061694Sdarrenm case CRYPTO_DATA_MBLK:
7071694Sdarrenm ret = md5_digest_update_mblk(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
7081694Sdarrenm data);
7091694Sdarrenm break;
7101694Sdarrenm default:
7111694Sdarrenm ret = CRYPTO_ARGUMENTS_BAD;
7121694Sdarrenm }
7131694Sdarrenm
7141694Sdarrenm return (ret);
7151694Sdarrenm }
7161694Sdarrenm
7171694Sdarrenm /* ARGSUSED */
7181694Sdarrenm static int
md5_digest_final(crypto_ctx_t * ctx,crypto_data_t * digest,crypto_req_handle_t req)7191694Sdarrenm md5_digest_final(crypto_ctx_t *ctx, crypto_data_t *digest,
7201694Sdarrenm crypto_req_handle_t req)
7211694Sdarrenm {
7221694Sdarrenm int ret = CRYPTO_SUCCESS;
7231694Sdarrenm
7241694Sdarrenm ASSERT(ctx->cc_provider_private != NULL);
7251694Sdarrenm
7261694Sdarrenm /*
7271694Sdarrenm * We need to just return the length needed to store the output.
7281694Sdarrenm * We should not destroy the context for the following cases.
7291694Sdarrenm */
7301694Sdarrenm if ((digest->cd_length == 0) ||
7311694Sdarrenm (digest->cd_length < MD5_DIGEST_LENGTH)) {
7321694Sdarrenm digest->cd_length = MD5_DIGEST_LENGTH;
7331694Sdarrenm return (CRYPTO_BUFFER_TOO_SMALL);
7341694Sdarrenm }
7351694Sdarrenm
7361694Sdarrenm /*
7371694Sdarrenm * Do an MD5 final.
7381694Sdarrenm */
7391694Sdarrenm switch (digest->cd_format) {
7401694Sdarrenm case CRYPTO_DATA_RAW:
7411694Sdarrenm MD5Final((unsigned char *)digest->cd_raw.iov_base +
7421694Sdarrenm digest->cd_offset, &PROV_MD5_CTX(ctx)->mc_md5_ctx);
7431694Sdarrenm break;
7441694Sdarrenm case CRYPTO_DATA_UIO:
7451694Sdarrenm ret = md5_digest_final_uio(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
7461694Sdarrenm digest, MD5_DIGEST_LENGTH, NULL);
7471694Sdarrenm break;
7481694Sdarrenm case CRYPTO_DATA_MBLK:
7491694Sdarrenm ret = md5_digest_final_mblk(&PROV_MD5_CTX(ctx)->mc_md5_ctx,
7501694Sdarrenm digest, MD5_DIGEST_LENGTH, NULL);
7511694Sdarrenm break;
7521694Sdarrenm default:
7531694Sdarrenm ret = CRYPTO_ARGUMENTS_BAD;
7541694Sdarrenm }
7551694Sdarrenm
7561694Sdarrenm /* all done, free context and return */
7571694Sdarrenm
7581694Sdarrenm if (ret == CRYPTO_SUCCESS) {
7591694Sdarrenm digest->cd_length = MD5_DIGEST_LENGTH;
7601694Sdarrenm } else {
7611694Sdarrenm digest->cd_length = 0;
7621694Sdarrenm }
7631694Sdarrenm
7641694Sdarrenm kmem_free(ctx->cc_provider_private, sizeof (md5_ctx_t));
7651694Sdarrenm ctx->cc_provider_private = NULL;
7661694Sdarrenm
7671694Sdarrenm return (ret);
7681694Sdarrenm }
7691694Sdarrenm
7701694Sdarrenm /* ARGSUSED */
7711694Sdarrenm static int
md5_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)7721694Sdarrenm md5_digest_atomic(crypto_provider_handle_t provider,
7731694Sdarrenm crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
7741694Sdarrenm crypto_data_t *data, crypto_data_t *digest,
7751694Sdarrenm crypto_req_handle_t req)
7761694Sdarrenm {
7771694Sdarrenm int ret = CRYPTO_SUCCESS;
7781694Sdarrenm MD5_CTX md5_ctx;
7791694Sdarrenm
7801694Sdarrenm if (mechanism->cm_type != MD5_MECH_INFO_TYPE)
7811694Sdarrenm return (CRYPTO_MECHANISM_INVALID);
7821694Sdarrenm
7831694Sdarrenm /*
7841694Sdarrenm * Do the MD5 init.
7851694Sdarrenm */
7861694Sdarrenm MD5Init(&md5_ctx);
7871694Sdarrenm
7881694Sdarrenm /*
7891694Sdarrenm * Do the MD5 update on the specified input data.
7901694Sdarrenm */
7911694Sdarrenm switch (data->cd_format) {
7921694Sdarrenm case CRYPTO_DATA_RAW:
7931694Sdarrenm MD5Update(&md5_ctx, data->cd_raw.iov_base + data->cd_offset,
7941694Sdarrenm data->cd_length);
7951694Sdarrenm break;
7961694Sdarrenm case CRYPTO_DATA_UIO:
7971694Sdarrenm ret = md5_digest_update_uio(&md5_ctx, data);
7981694Sdarrenm break;
7991694Sdarrenm case CRYPTO_DATA_MBLK:
8001694Sdarrenm ret = md5_digest_update_mblk(&md5_ctx, data);
8011694Sdarrenm break;
8021694Sdarrenm default:
8031694Sdarrenm ret = CRYPTO_ARGUMENTS_BAD;
8041694Sdarrenm }
8051694Sdarrenm
8061694Sdarrenm if (ret != CRYPTO_SUCCESS) {
8071694Sdarrenm /* the update failed, bail */
8081694Sdarrenm digest->cd_length = 0;
8091694Sdarrenm return (ret);
8101694Sdarrenm }
8111694Sdarrenm
8121694Sdarrenm /*
8131694Sdarrenm * Do an MD5 final, must be done separately since the digest
8141694Sdarrenm * type can be different than the input data type.
8151694Sdarrenm */
8161694Sdarrenm switch (digest->cd_format) {
8171694Sdarrenm case CRYPTO_DATA_RAW:
8181694Sdarrenm MD5Final((unsigned char *)digest->cd_raw.iov_base +
8191694Sdarrenm digest->cd_offset, &md5_ctx);
8201694Sdarrenm break;
8211694Sdarrenm case CRYPTO_DATA_UIO:
8221694Sdarrenm ret = md5_digest_final_uio(&md5_ctx, digest,
8231694Sdarrenm MD5_DIGEST_LENGTH, NULL);
8241694Sdarrenm break;
8251694Sdarrenm case CRYPTO_DATA_MBLK:
8261694Sdarrenm ret = md5_digest_final_mblk(&md5_ctx, digest,
8271694Sdarrenm MD5_DIGEST_LENGTH, NULL);
8281694Sdarrenm break;
8291694Sdarrenm default:
8301694Sdarrenm ret = CRYPTO_ARGUMENTS_BAD;
8311694Sdarrenm }
8321694Sdarrenm
8331694Sdarrenm if (ret == CRYPTO_SUCCESS) {
8341694Sdarrenm digest->cd_length = MD5_DIGEST_LENGTH;
8351694Sdarrenm } else {
8361694Sdarrenm digest->cd_length = 0;
8371694Sdarrenm }
8381694Sdarrenm
8391694Sdarrenm return (ret);
8401694Sdarrenm }
8411694Sdarrenm
8421694Sdarrenm /*
8431694Sdarrenm * KCF software provider mac entry points.
8441694Sdarrenm *
8451694Sdarrenm * MD5 HMAC is: MD5(key XOR opad, MD5(key XOR ipad, text))
8461694Sdarrenm *
8471694Sdarrenm * Init:
8481694Sdarrenm * The initialization routine initializes what we denote
8491694Sdarrenm * as the inner and outer contexts by doing
8501694Sdarrenm * - for inner context: MD5(key XOR ipad)
8511694Sdarrenm * - for outer context: MD5(key XOR opad)
8521694Sdarrenm *
8531694Sdarrenm * Update:
8541694Sdarrenm * Each subsequent MD5 HMAC update will result in an
8551694Sdarrenm * update of the inner context with the specified data.
8561694Sdarrenm *
8571694Sdarrenm * Final:
8581694Sdarrenm * The MD5 HMAC final will do a MD5 final operation on the
8591694Sdarrenm * inner context, and the resulting digest will be used
8601694Sdarrenm * as the data for an update on the outer context. Last
8611694Sdarrenm * but not least, an MD5 final on the outer context will
8621694Sdarrenm * be performed to obtain the MD5 HMAC digest to return
8631694Sdarrenm * to the user.
8641694Sdarrenm */
8651694Sdarrenm
8661694Sdarrenm /*
8671694Sdarrenm * Initialize a MD5-HMAC context.
8681694Sdarrenm */
8691694Sdarrenm static void
md5_mac_init_ctx(md5_hmac_ctx_t * ctx,void * keyval,uint_t length_in_bytes)8701694Sdarrenm md5_mac_init_ctx(md5_hmac_ctx_t *ctx, void *keyval, uint_t length_in_bytes)
8711694Sdarrenm {
8721694Sdarrenm uint32_t ipad[MD5_HMAC_INTS_PER_BLOCK];
8731694Sdarrenm uint32_t opad[MD5_HMAC_INTS_PER_BLOCK];
8741694Sdarrenm uint_t i;
8751694Sdarrenm
8761694Sdarrenm bzero(ipad, MD5_HMAC_BLOCK_SIZE);
8771694Sdarrenm bzero(opad, MD5_HMAC_BLOCK_SIZE);
8781694Sdarrenm
8791694Sdarrenm bcopy(keyval, ipad, length_in_bytes);
8801694Sdarrenm bcopy(keyval, opad, length_in_bytes);
8811694Sdarrenm
8821694Sdarrenm /* XOR key with ipad (0x36) and opad (0x5c) */
8831694Sdarrenm for (i = 0; i < MD5_HMAC_INTS_PER_BLOCK; i++) {
8841694Sdarrenm ipad[i] ^= 0x36363636;
8851694Sdarrenm opad[i] ^= 0x5c5c5c5c;
8861694Sdarrenm }
8871694Sdarrenm
8881694Sdarrenm /* perform MD5 on ipad */
8891694Sdarrenm MD5Init(&ctx->hc_icontext);
8901694Sdarrenm MD5Update(&ctx->hc_icontext, ipad, MD5_HMAC_BLOCK_SIZE);
8911694Sdarrenm
8921694Sdarrenm /* perform MD5 on opad */
8931694Sdarrenm MD5Init(&ctx->hc_ocontext);
8941694Sdarrenm MD5Update(&ctx->hc_ocontext, opad, MD5_HMAC_BLOCK_SIZE);
8951694Sdarrenm }
8961694Sdarrenm
8971694Sdarrenm /*
8981694Sdarrenm * Initializes a multi-part MAC operation.
8991694Sdarrenm */
9001694Sdarrenm static int
md5_mac_init(crypto_ctx_t * ctx,crypto_mechanism_t * mechanism,crypto_key_t * key,crypto_spi_ctx_template_t ctx_template,crypto_req_handle_t req)9011694Sdarrenm md5_mac_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
9021694Sdarrenm crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
9031694Sdarrenm crypto_req_handle_t req)
9041694Sdarrenm {
9051694Sdarrenm int ret = CRYPTO_SUCCESS;
9061694Sdarrenm uint_t keylen_in_bytes = CRYPTO_BITS2BYTES(key->ck_length);
9071694Sdarrenm
9081694Sdarrenm if (mechanism->cm_type != MD5_HMAC_MECH_INFO_TYPE &&
9091694Sdarrenm mechanism->cm_type != MD5_HMAC_GEN_MECH_INFO_TYPE)
9101694Sdarrenm return (CRYPTO_MECHANISM_INVALID);
9111694Sdarrenm
9121694Sdarrenm /* Add support for key by attributes (RFE 4706552) */
9131694Sdarrenm if (key->ck_format != CRYPTO_KEY_RAW)
9141694Sdarrenm return (CRYPTO_ARGUMENTS_BAD);
9151694Sdarrenm
9161694Sdarrenm ctx->cc_provider_private = kmem_alloc(sizeof (md5_hmac_ctx_t),
9171694Sdarrenm crypto_kmflag(req));
9181694Sdarrenm if (ctx->cc_provider_private == NULL)
9191694Sdarrenm return (CRYPTO_HOST_MEMORY);
9201694Sdarrenm
9211694Sdarrenm if (ctx_template != NULL) {
9221694Sdarrenm /* reuse context template */
9231694Sdarrenm bcopy(ctx_template, PROV_MD5_HMAC_CTX(ctx),
9241694Sdarrenm sizeof (md5_hmac_ctx_t));
9251694Sdarrenm } else {
9261694Sdarrenm /* no context template, compute context */
9271694Sdarrenm if (keylen_in_bytes > MD5_HMAC_BLOCK_SIZE) {
9281694Sdarrenm uchar_t digested_key[MD5_DIGEST_LENGTH];
9291694Sdarrenm md5_hmac_ctx_t *hmac_ctx = ctx->cc_provider_private;
9301694Sdarrenm
9311694Sdarrenm /*
9321694Sdarrenm * Hash the passed-in key to get a smaller key.
9331694Sdarrenm * The inner context is used since it hasn't been
9341694Sdarrenm * initialized yet.
9351694Sdarrenm */
9361694Sdarrenm PROV_MD5_DIGEST_KEY(&hmac_ctx->hc_icontext,
9371694Sdarrenm key->ck_data, keylen_in_bytes, digested_key);
9381694Sdarrenm md5_mac_init_ctx(PROV_MD5_HMAC_CTX(ctx),
9391694Sdarrenm digested_key, MD5_DIGEST_LENGTH);
9401694Sdarrenm } else {
9411694Sdarrenm md5_mac_init_ctx(PROV_MD5_HMAC_CTX(ctx),
9421694Sdarrenm key->ck_data, keylen_in_bytes);
9431694Sdarrenm }
9441694Sdarrenm }
9451694Sdarrenm
9461694Sdarrenm /*
9471694Sdarrenm * Get the mechanism parameters, if applicable.
9481694Sdarrenm */
9491694Sdarrenm PROV_MD5_HMAC_CTX(ctx)->hc_mech_type = mechanism->cm_type;
9501694Sdarrenm if (mechanism->cm_type == MD5_HMAC_GEN_MECH_INFO_TYPE) {
9511694Sdarrenm if (mechanism->cm_param == NULL ||
9521694Sdarrenm mechanism->cm_param_len != sizeof (ulong_t))
9531694Sdarrenm ret = CRYPTO_MECHANISM_PARAM_INVALID;
9541694Sdarrenm PROV_MD5_GET_DIGEST_LEN(mechanism,
9551694Sdarrenm PROV_MD5_HMAC_CTX(ctx)->hc_digest_len);
9561694Sdarrenm if (PROV_MD5_HMAC_CTX(ctx)->hc_digest_len >
9571694Sdarrenm MD5_DIGEST_LENGTH)
9581694Sdarrenm ret = CRYPTO_MECHANISM_PARAM_INVALID;
9591694Sdarrenm }
9601694Sdarrenm
9611694Sdarrenm if (ret != CRYPTO_SUCCESS) {
9621694Sdarrenm bzero(ctx->cc_provider_private, sizeof (md5_hmac_ctx_t));
9631694Sdarrenm kmem_free(ctx->cc_provider_private, sizeof (md5_hmac_ctx_t));
9641694Sdarrenm ctx->cc_provider_private = NULL;
9651694Sdarrenm }
9661694Sdarrenm
9671694Sdarrenm return (ret);
9681694Sdarrenm }
9691694Sdarrenm
9701694Sdarrenm
9711694Sdarrenm /* ARGSUSED */
9721694Sdarrenm static int
md5_mac_update(crypto_ctx_t * ctx,crypto_data_t * data,crypto_req_handle_t req)9731694Sdarrenm md5_mac_update(crypto_ctx_t *ctx, crypto_data_t *data, crypto_req_handle_t req)
9741694Sdarrenm {
9751694Sdarrenm int ret = CRYPTO_SUCCESS;
9761694Sdarrenm
9771694Sdarrenm ASSERT(ctx->cc_provider_private != NULL);
9781694Sdarrenm
9791694Sdarrenm /*
9801694Sdarrenm * Do an MD5 update of the inner context using the specified
9811694Sdarrenm * data.
9821694Sdarrenm */
9831694Sdarrenm switch (data->cd_format) {
9841694Sdarrenm case CRYPTO_DATA_RAW:
9851694Sdarrenm MD5Update(&PROV_MD5_HMAC_CTX(ctx)->hc_icontext,
9861694Sdarrenm data->cd_raw.iov_base + data->cd_offset,
9871694Sdarrenm data->cd_length);
9881694Sdarrenm break;
9891694Sdarrenm case CRYPTO_DATA_UIO:
9901694Sdarrenm ret = md5_digest_update_uio(
9911694Sdarrenm &PROV_MD5_HMAC_CTX(ctx)->hc_icontext, data);
9921694Sdarrenm break;
9931694Sdarrenm case CRYPTO_DATA_MBLK:
9941694Sdarrenm ret = md5_digest_update_mblk(
9951694Sdarrenm &PROV_MD5_HMAC_CTX(ctx)->hc_icontext, data);
9961694Sdarrenm break;
9971694Sdarrenm default:
9981694Sdarrenm ret = CRYPTO_ARGUMENTS_BAD;
9991694Sdarrenm }
10001694Sdarrenm
10011694Sdarrenm return (ret);
10021694Sdarrenm }
10031694Sdarrenm
10041694Sdarrenm /* ARGSUSED */
10051694Sdarrenm static int
md5_mac_final(crypto_ctx_t * ctx,crypto_data_t * mac,crypto_req_handle_t req)10061694Sdarrenm md5_mac_final(crypto_ctx_t *ctx, crypto_data_t *mac, crypto_req_handle_t req)
10071694Sdarrenm {
10081694Sdarrenm int ret = CRYPTO_SUCCESS;
10091694Sdarrenm uchar_t digest[MD5_DIGEST_LENGTH];
10101694Sdarrenm uint32_t digest_len = MD5_DIGEST_LENGTH;
10111694Sdarrenm
10121694Sdarrenm ASSERT(ctx->cc_provider_private != NULL);
10131694Sdarrenm
10141694Sdarrenm if (PROV_MD5_HMAC_CTX(ctx)->hc_mech_type == MD5_HMAC_GEN_MECH_INFO_TYPE)
10155072Smcpowers digest_len = PROV_MD5_HMAC_CTX(ctx)->hc_digest_len;
10161694Sdarrenm
10171694Sdarrenm /*
10181694Sdarrenm * We need to just return the length needed to store the output.
10191694Sdarrenm * We should not destroy the context for the following cases.
10201694Sdarrenm */
10211694Sdarrenm if ((mac->cd_length == 0) || (mac->cd_length < digest_len)) {
10221694Sdarrenm mac->cd_length = digest_len;
10231694Sdarrenm return (CRYPTO_BUFFER_TOO_SMALL);
10241694Sdarrenm }
10251694Sdarrenm
10261694Sdarrenm /*
10271694Sdarrenm * Do an MD5 final on the inner context.
10281694Sdarrenm */
10291694Sdarrenm MD5Final(digest, &PROV_MD5_HMAC_CTX(ctx)->hc_icontext);
10301694Sdarrenm
10311694Sdarrenm /*
10321694Sdarrenm * Do an MD5 update on the outer context, feeding the inner
10331694Sdarrenm * digest as data.
10341694Sdarrenm */
10351694Sdarrenm MD5Update(&PROV_MD5_HMAC_CTX(ctx)->hc_ocontext, digest,
10361694Sdarrenm MD5_DIGEST_LENGTH);
10371694Sdarrenm
10381694Sdarrenm /*
10391694Sdarrenm * Do an MD5 final on the outer context, storing the computing
10401694Sdarrenm * digest in the users buffer.
10411694Sdarrenm */
10421694Sdarrenm switch (mac->cd_format) {
10431694Sdarrenm case CRYPTO_DATA_RAW:
10441694Sdarrenm if (digest_len != MD5_DIGEST_LENGTH) {
10451694Sdarrenm /*
10461694Sdarrenm * The caller requested a short digest. Digest
10471694Sdarrenm * into a scratch buffer and return to
10481694Sdarrenm * the user only what was requested.
10491694Sdarrenm */
10501694Sdarrenm MD5Final(digest,
10511694Sdarrenm &PROV_MD5_HMAC_CTX(ctx)->hc_ocontext);
10521694Sdarrenm bcopy(digest, (unsigned char *)mac->cd_raw.iov_base +
10531694Sdarrenm mac->cd_offset, digest_len);
10541694Sdarrenm } else {
10551694Sdarrenm MD5Final((unsigned char *)mac->cd_raw.iov_base +
10561694Sdarrenm mac->cd_offset,
10571694Sdarrenm &PROV_MD5_HMAC_CTX(ctx)->hc_ocontext);
10581694Sdarrenm }
10591694Sdarrenm break;
10601694Sdarrenm case CRYPTO_DATA_UIO:
10611694Sdarrenm ret = md5_digest_final_uio(
10621694Sdarrenm &PROV_MD5_HMAC_CTX(ctx)->hc_ocontext, mac,
10631694Sdarrenm digest_len, digest);
10641694Sdarrenm break;
10651694Sdarrenm case CRYPTO_DATA_MBLK:
10661694Sdarrenm ret = md5_digest_final_mblk(
10671694Sdarrenm &PROV_MD5_HMAC_CTX(ctx)->hc_ocontext, mac,
10681694Sdarrenm digest_len, digest);
10691694Sdarrenm break;
10701694Sdarrenm default:
10711694Sdarrenm ret = CRYPTO_ARGUMENTS_BAD;
10721694Sdarrenm }
10731694Sdarrenm
10741694Sdarrenm if (ret == CRYPTO_SUCCESS) {
10751694Sdarrenm mac->cd_length = digest_len;
10761694Sdarrenm } else {
10771694Sdarrenm mac->cd_length = 0;
10781694Sdarrenm }
10791694Sdarrenm
10801694Sdarrenm bzero(ctx->cc_provider_private, sizeof (md5_hmac_ctx_t));
10811694Sdarrenm kmem_free(ctx->cc_provider_private, sizeof (md5_hmac_ctx_t));
10821694Sdarrenm ctx->cc_provider_private = NULL;
10831694Sdarrenm
10841694Sdarrenm return (ret);
10851694Sdarrenm }
10861694Sdarrenm
10871694Sdarrenm #define MD5_MAC_UPDATE(data, ctx, ret) { \
10881694Sdarrenm switch (data->cd_format) { \
10891694Sdarrenm case CRYPTO_DATA_RAW: \
10901694Sdarrenm MD5Update(&(ctx).hc_icontext, \
10911694Sdarrenm data->cd_raw.iov_base + data->cd_offset, \
10921694Sdarrenm data->cd_length); \
10931694Sdarrenm break; \
10941694Sdarrenm case CRYPTO_DATA_UIO: \
10951694Sdarrenm ret = md5_digest_update_uio(&(ctx).hc_icontext, data); \
10961694Sdarrenm break; \
10971694Sdarrenm case CRYPTO_DATA_MBLK: \
10981694Sdarrenm ret = md5_digest_update_mblk(&(ctx).hc_icontext, \
10991694Sdarrenm data); \
11001694Sdarrenm break; \
11011694Sdarrenm default: \
11021694Sdarrenm ret = CRYPTO_ARGUMENTS_BAD; \
11031694Sdarrenm } \
11041694Sdarrenm }
11051694Sdarrenm
11061694Sdarrenm
11071694Sdarrenm /* ARGSUSED */
11081694Sdarrenm static int
md5_mac_atomic(crypto_provider_handle_t provider,crypto_session_id_t session_id,crypto_mechanism_t * mechanism,crypto_key_t * key,crypto_data_t * data,crypto_data_t * mac,crypto_spi_ctx_template_t ctx_template,crypto_req_handle_t req)11091694Sdarrenm md5_mac_atomic(crypto_provider_handle_t provider,
11101694Sdarrenm crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
11111694Sdarrenm crypto_key_t *key, crypto_data_t *data, crypto_data_t *mac,
11121694Sdarrenm crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
11131694Sdarrenm {
11141694Sdarrenm int ret = CRYPTO_SUCCESS;
11151694Sdarrenm uchar_t digest[MD5_DIGEST_LENGTH];
11161694Sdarrenm md5_hmac_ctx_t md5_hmac_ctx;
11171694Sdarrenm uint32_t digest_len = MD5_DIGEST_LENGTH;
11181694Sdarrenm uint_t keylen_in_bytes = CRYPTO_BITS2BYTES(key->ck_length);
11191694Sdarrenm
11201694Sdarrenm if (mechanism->cm_type != MD5_HMAC_MECH_INFO_TYPE &&
11211694Sdarrenm mechanism->cm_type != MD5_HMAC_GEN_MECH_INFO_TYPE)
11221694Sdarrenm return (CRYPTO_MECHANISM_INVALID);
11231694Sdarrenm
11241694Sdarrenm /* Add support for key by attributes (RFE 4706552) */
11251694Sdarrenm if (key->ck_format != CRYPTO_KEY_RAW)
11261694Sdarrenm return (CRYPTO_ARGUMENTS_BAD);
11271694Sdarrenm
11281694Sdarrenm if (ctx_template != NULL) {
11291694Sdarrenm /* reuse context template */
11301694Sdarrenm bcopy(ctx_template, &md5_hmac_ctx, sizeof (md5_hmac_ctx_t));
11311694Sdarrenm } else {
11321694Sdarrenm /* no context template, compute context */
11331694Sdarrenm if (keylen_in_bytes > MD5_HMAC_BLOCK_SIZE) {
11341694Sdarrenm /*
11351694Sdarrenm * Hash the passed-in key to get a smaller key.
11361694Sdarrenm * The inner context is used since it hasn't been
11371694Sdarrenm * initialized yet.
11381694Sdarrenm */
11391694Sdarrenm PROV_MD5_DIGEST_KEY(&md5_hmac_ctx.hc_icontext,
11401694Sdarrenm key->ck_data, keylen_in_bytes, digest);
11411694Sdarrenm md5_mac_init_ctx(&md5_hmac_ctx, digest,
11421694Sdarrenm MD5_DIGEST_LENGTH);
11431694Sdarrenm } else {
11441694Sdarrenm md5_mac_init_ctx(&md5_hmac_ctx, key->ck_data,
11451694Sdarrenm keylen_in_bytes);
11461694Sdarrenm }
11471694Sdarrenm }
11481694Sdarrenm
11491694Sdarrenm /*
11501694Sdarrenm * Get the mechanism parameters, if applicable.
11511694Sdarrenm */
11521694Sdarrenm if (mechanism->cm_type == MD5_HMAC_GEN_MECH_INFO_TYPE) {
11531694Sdarrenm if (mechanism->cm_param == NULL ||
11541694Sdarrenm mechanism->cm_param_len != sizeof (ulong_t)) {
11551694Sdarrenm ret = CRYPTO_MECHANISM_PARAM_INVALID;
11561694Sdarrenm goto bail;
11571694Sdarrenm }
11581694Sdarrenm PROV_MD5_GET_DIGEST_LEN(mechanism, digest_len);
11591694Sdarrenm if (digest_len > MD5_DIGEST_LENGTH) {
11601694Sdarrenm ret = CRYPTO_MECHANISM_PARAM_INVALID;
11611694Sdarrenm goto bail;
11621694Sdarrenm }
11631694Sdarrenm }
11641694Sdarrenm
11651694Sdarrenm /* do an MD5 update of the inner context using the specified data */
11661694Sdarrenm MD5_MAC_UPDATE(data, md5_hmac_ctx, ret);
11671694Sdarrenm if (ret != CRYPTO_SUCCESS)
11681694Sdarrenm /* the update failed, free context and bail */
11691694Sdarrenm goto bail;
11701694Sdarrenm
11711694Sdarrenm /* do an MD5 final on the inner context */
11721694Sdarrenm MD5Final(digest, &md5_hmac_ctx.hc_icontext);
11731694Sdarrenm
11741694Sdarrenm /*
11751694Sdarrenm * Do an MD5 update on the outer context, feeding the inner
11761694Sdarrenm * digest as data.
11771694Sdarrenm */
11781694Sdarrenm MD5Update(&md5_hmac_ctx.hc_ocontext, digest, MD5_DIGEST_LENGTH);
11791694Sdarrenm
11801694Sdarrenm /*
11811694Sdarrenm * Do an MD5 final on the outer context, storing the computed
11821694Sdarrenm * digest in the users buffer.
11831694Sdarrenm */
11841694Sdarrenm switch (mac->cd_format) {
11851694Sdarrenm case CRYPTO_DATA_RAW:
11861694Sdarrenm if (digest_len != MD5_DIGEST_LENGTH) {
11871694Sdarrenm /*
11881694Sdarrenm * The caller requested a short digest. Digest
11891694Sdarrenm * into a scratch buffer and return to
11901694Sdarrenm * the user only what was requested.
11911694Sdarrenm */
11921694Sdarrenm MD5Final(digest, &md5_hmac_ctx.hc_ocontext);
11931694Sdarrenm bcopy(digest, (unsigned char *)mac->cd_raw.iov_base +
11941694Sdarrenm mac->cd_offset, digest_len);
11951694Sdarrenm } else {
11961694Sdarrenm MD5Final((unsigned char *)mac->cd_raw.iov_base +
11971694Sdarrenm mac->cd_offset, &md5_hmac_ctx.hc_ocontext);
11981694Sdarrenm }
11991694Sdarrenm break;
12001694Sdarrenm case CRYPTO_DATA_UIO:
12011694Sdarrenm ret = md5_digest_final_uio(&md5_hmac_ctx.hc_ocontext, mac,
12021694Sdarrenm digest_len, digest);
12031694Sdarrenm break;
12041694Sdarrenm case CRYPTO_DATA_MBLK:
12051694Sdarrenm ret = md5_digest_final_mblk(&md5_hmac_ctx.hc_ocontext, mac,
12061694Sdarrenm digest_len, digest);
12071694Sdarrenm break;
12081694Sdarrenm default:
12091694Sdarrenm ret = CRYPTO_ARGUMENTS_BAD;
12101694Sdarrenm }
12111694Sdarrenm
12121694Sdarrenm if (ret == CRYPTO_SUCCESS) {
12131694Sdarrenm mac->cd_length = digest_len;
12141694Sdarrenm } else {
12151694Sdarrenm mac->cd_length = 0;
12161694Sdarrenm }
12171694Sdarrenm /* Extra paranoia: zeroizing the local context on the stack */
12181694Sdarrenm bzero(&md5_hmac_ctx, sizeof (md5_hmac_ctx_t));
12191694Sdarrenm
12201694Sdarrenm return (ret);
12211694Sdarrenm bail:
12221694Sdarrenm bzero(&md5_hmac_ctx, sizeof (md5_hmac_ctx_t));
12231694Sdarrenm mac->cd_length = 0;
12241694Sdarrenm return (ret);
12251694Sdarrenm }
12261694Sdarrenm
12271694Sdarrenm /* ARGSUSED */
12281694Sdarrenm static int
md5_mac_verify_atomic(crypto_provider_handle_t provider,crypto_session_id_t session_id,crypto_mechanism_t * mechanism,crypto_key_t * key,crypto_data_t * data,crypto_data_t * mac,crypto_spi_ctx_template_t ctx_template,crypto_req_handle_t req)12291694Sdarrenm md5_mac_verify_atomic(crypto_provider_handle_t provider,
12301694Sdarrenm crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
12311694Sdarrenm crypto_key_t *key, crypto_data_t *data, crypto_data_t *mac,
12321694Sdarrenm crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
12331694Sdarrenm {
12341694Sdarrenm int ret = CRYPTO_SUCCESS;
12351694Sdarrenm uchar_t digest[MD5_DIGEST_LENGTH];
12361694Sdarrenm md5_hmac_ctx_t md5_hmac_ctx;
12371694Sdarrenm uint32_t digest_len = MD5_DIGEST_LENGTH;
12381694Sdarrenm uint_t keylen_in_bytes = CRYPTO_BITS2BYTES(key->ck_length);
12391694Sdarrenm
12401694Sdarrenm if (mechanism->cm_type != MD5_HMAC_MECH_INFO_TYPE &&
12411694Sdarrenm mechanism->cm_type != MD5_HMAC_GEN_MECH_INFO_TYPE)
12421694Sdarrenm return (CRYPTO_MECHANISM_INVALID);
12431694Sdarrenm
12441694Sdarrenm /* Add support for key by attributes (RFE 4706552) */
12451694Sdarrenm if (key->ck_format != CRYPTO_KEY_RAW)
12461694Sdarrenm return (CRYPTO_ARGUMENTS_BAD);
12471694Sdarrenm
12481694Sdarrenm if (ctx_template != NULL) {
12491694Sdarrenm /* reuse context template */
12501694Sdarrenm bcopy(ctx_template, &md5_hmac_ctx, sizeof (md5_hmac_ctx_t));
12511694Sdarrenm } else {
12521694Sdarrenm /* no context template, compute context */
12531694Sdarrenm if (keylen_in_bytes > MD5_HMAC_BLOCK_SIZE) {
12541694Sdarrenm /*
12551694Sdarrenm * Hash the passed-in key to get a smaller key.
12561694Sdarrenm * The inner context is used since it hasn't been
12571694Sdarrenm * initialized yet.
12581694Sdarrenm */
12591694Sdarrenm PROV_MD5_DIGEST_KEY(&md5_hmac_ctx.hc_icontext,
12601694Sdarrenm key->ck_data, keylen_in_bytes, digest);
12611694Sdarrenm md5_mac_init_ctx(&md5_hmac_ctx, digest,
12621694Sdarrenm MD5_DIGEST_LENGTH);
12631694Sdarrenm } else {
12641694Sdarrenm md5_mac_init_ctx(&md5_hmac_ctx, key->ck_data,
12651694Sdarrenm keylen_in_bytes);
12661694Sdarrenm }
12671694Sdarrenm }
12681694Sdarrenm
12691694Sdarrenm /*
12701694Sdarrenm * Get the mechanism parameters, if applicable.
12711694Sdarrenm */
12721694Sdarrenm if (mechanism->cm_type == MD5_HMAC_GEN_MECH_INFO_TYPE) {
12731694Sdarrenm if (mechanism->cm_param == NULL ||
12741694Sdarrenm mechanism->cm_param_len != sizeof (ulong_t)) {
12751694Sdarrenm ret = CRYPTO_MECHANISM_PARAM_INVALID;
12761694Sdarrenm goto bail;
12771694Sdarrenm }
12781694Sdarrenm PROV_MD5_GET_DIGEST_LEN(mechanism, digest_len);
12791694Sdarrenm if (digest_len > MD5_DIGEST_LENGTH) {
12801694Sdarrenm ret = CRYPTO_MECHANISM_PARAM_INVALID;
12811694Sdarrenm goto bail;
12821694Sdarrenm }
12831694Sdarrenm }
12841694Sdarrenm
12851694Sdarrenm if (mac->cd_length != digest_len) {
12861694Sdarrenm ret = CRYPTO_INVALID_MAC;
12871694Sdarrenm goto bail;
12881694Sdarrenm }
12891694Sdarrenm
12901694Sdarrenm /* do an MD5 update of the inner context using the specified data */
12911694Sdarrenm MD5_MAC_UPDATE(data, md5_hmac_ctx, ret);
12921694Sdarrenm if (ret != CRYPTO_SUCCESS)
12931694Sdarrenm /* the update failed, free context and bail */
12941694Sdarrenm goto bail;
12951694Sdarrenm
12961694Sdarrenm /* do an MD5 final on the inner context */
12971694Sdarrenm MD5Final(digest, &md5_hmac_ctx.hc_icontext);
12981694Sdarrenm
12991694Sdarrenm /*
13001694Sdarrenm * Do an MD5 update on the outer context, feeding the inner
13011694Sdarrenm * digest as data.
13021694Sdarrenm */
13031694Sdarrenm MD5Update(&md5_hmac_ctx.hc_ocontext, digest, MD5_DIGEST_LENGTH);
13041694Sdarrenm
13051694Sdarrenm /*
13061694Sdarrenm * Do an MD5 final on the outer context, storing the computed
13071694Sdarrenm * digest in the local digest buffer.
13081694Sdarrenm */
13091694Sdarrenm MD5Final(digest, &md5_hmac_ctx.hc_ocontext);
13101694Sdarrenm
13111694Sdarrenm /*
13121694Sdarrenm * Compare the computed digest against the expected digest passed
13131694Sdarrenm * as argument.
13141694Sdarrenm */
13151694Sdarrenm switch (mac->cd_format) {
13161694Sdarrenm
13171694Sdarrenm case CRYPTO_DATA_RAW:
13181694Sdarrenm if (bcmp(digest, (unsigned char *)mac->cd_raw.iov_base +
13191694Sdarrenm mac->cd_offset, digest_len) != 0)
13201694Sdarrenm ret = CRYPTO_INVALID_MAC;
13211694Sdarrenm break;
13221694Sdarrenm
13231694Sdarrenm case CRYPTO_DATA_UIO: {
13241694Sdarrenm off_t offset = mac->cd_offset;
13251694Sdarrenm uint_t vec_idx;
13261694Sdarrenm off_t scratch_offset = 0;
13271694Sdarrenm size_t length = digest_len;
13281694Sdarrenm size_t cur_len;
13291694Sdarrenm
13301694Sdarrenm /* we support only kernel buffer */
13311694Sdarrenm if (mac->cd_uio->uio_segflg != UIO_SYSSPACE)
13321694Sdarrenm return (CRYPTO_ARGUMENTS_BAD);
13331694Sdarrenm
13341694Sdarrenm /* jump to the first iovec containing the expected digest */
13351694Sdarrenm for (vec_idx = 0;
13361694Sdarrenm offset >= mac->cd_uio->uio_iov[vec_idx].iov_len &&
13371694Sdarrenm vec_idx < mac->cd_uio->uio_iovcnt;
13385072Smcpowers offset -= mac->cd_uio->uio_iov[vec_idx++].iov_len)
13395072Smcpowers ;
13401694Sdarrenm if (vec_idx == mac->cd_uio->uio_iovcnt) {
13411694Sdarrenm /*
13421694Sdarrenm * The caller specified an offset that is
13431694Sdarrenm * larger than the total size of the buffers
13441694Sdarrenm * it provided.
13451694Sdarrenm */
13461694Sdarrenm ret = CRYPTO_DATA_LEN_RANGE;
13471694Sdarrenm break;
13481694Sdarrenm }
13491694Sdarrenm
13501694Sdarrenm /* do the comparison of computed digest vs specified one */
13511694Sdarrenm while (vec_idx < mac->cd_uio->uio_iovcnt && length > 0) {
13521694Sdarrenm cur_len = MIN(mac->cd_uio->uio_iov[vec_idx].iov_len -
13531694Sdarrenm offset, length);
13541694Sdarrenm
13551694Sdarrenm if (bcmp(digest + scratch_offset,
13561694Sdarrenm mac->cd_uio->uio_iov[vec_idx].iov_base + offset,
13571694Sdarrenm cur_len) != 0) {
13581694Sdarrenm ret = CRYPTO_INVALID_MAC;
13591694Sdarrenm break;
13601694Sdarrenm }
13611694Sdarrenm
13621694Sdarrenm length -= cur_len;
13631694Sdarrenm vec_idx++;
13641694Sdarrenm scratch_offset += cur_len;
13651694Sdarrenm offset = 0;
13661694Sdarrenm }
13671694Sdarrenm break;
13681694Sdarrenm }
13691694Sdarrenm
13701694Sdarrenm case CRYPTO_DATA_MBLK: {
13711694Sdarrenm off_t offset = mac->cd_offset;
13721694Sdarrenm mblk_t *mp;
13731694Sdarrenm off_t scratch_offset = 0;
13741694Sdarrenm size_t length = digest_len;
13751694Sdarrenm size_t cur_len;
13761694Sdarrenm
13771694Sdarrenm /* jump to the first mblk_t containing the expected digest */
13781694Sdarrenm for (mp = mac->cd_mp; mp != NULL && offset >= MBLKL(mp);
13795072Smcpowers offset -= MBLKL(mp), mp = mp->b_cont)
13805072Smcpowers ;
13811694Sdarrenm if (mp == NULL) {
13821694Sdarrenm /*
13831694Sdarrenm * The caller specified an offset that is larger than
13841694Sdarrenm * the total size of the buffers it provided.
13851694Sdarrenm */
13861694Sdarrenm ret = CRYPTO_DATA_LEN_RANGE;
13871694Sdarrenm break;
13881694Sdarrenm }
13891694Sdarrenm
13901694Sdarrenm while (mp != NULL && length > 0) {
13911694Sdarrenm cur_len = MIN(MBLKL(mp) - offset, length);
13921694Sdarrenm if (bcmp(digest + scratch_offset,
13931694Sdarrenm mp->b_rptr + offset, cur_len) != 0) {
13941694Sdarrenm ret = CRYPTO_INVALID_MAC;
13951694Sdarrenm break;
13961694Sdarrenm }
13971694Sdarrenm
13981694Sdarrenm length -= cur_len;
13991694Sdarrenm mp = mp->b_cont;
14001694Sdarrenm scratch_offset += cur_len;
14011694Sdarrenm offset = 0;
14021694Sdarrenm }
14031694Sdarrenm break;
14041694Sdarrenm }
14051694Sdarrenm
14061694Sdarrenm default:
14071694Sdarrenm ret = CRYPTO_ARGUMENTS_BAD;
14081694Sdarrenm }
14091694Sdarrenm
14101694Sdarrenm bzero(&md5_hmac_ctx, sizeof (md5_hmac_ctx_t));
14111694Sdarrenm return (ret);
14121694Sdarrenm bail:
14131694Sdarrenm bzero(&md5_hmac_ctx, sizeof (md5_hmac_ctx_t));
14141694Sdarrenm mac->cd_length = 0;
14151694Sdarrenm return (ret);
14161694Sdarrenm }
14171694Sdarrenm
14181694Sdarrenm /*
14191694Sdarrenm * KCF software provider context management entry points.
14201694Sdarrenm */
14211694Sdarrenm
14221694Sdarrenm /* ARGSUSED */
14231694Sdarrenm static int
md5_create_ctx_template(crypto_provider_handle_t provider,crypto_mechanism_t * mechanism,crypto_key_t * key,crypto_spi_ctx_template_t * ctx_template,size_t * ctx_template_size,crypto_req_handle_t req)14241694Sdarrenm md5_create_ctx_template(crypto_provider_handle_t provider,
14251694Sdarrenm crypto_mechanism_t *mechanism, crypto_key_t *key,
14261694Sdarrenm crypto_spi_ctx_template_t *ctx_template, size_t *ctx_template_size,
14271694Sdarrenm crypto_req_handle_t req)
14281694Sdarrenm {
14291694Sdarrenm md5_hmac_ctx_t *md5_hmac_ctx_tmpl;
14301694Sdarrenm uint_t keylen_in_bytes = CRYPTO_BITS2BYTES(key->ck_length);
14311694Sdarrenm
14321694Sdarrenm if ((mechanism->cm_type != MD5_HMAC_MECH_INFO_TYPE) &&
14331694Sdarrenm (mechanism->cm_type != MD5_HMAC_GEN_MECH_INFO_TYPE))
14341694Sdarrenm return (CRYPTO_MECHANISM_INVALID);
14351694Sdarrenm
14361694Sdarrenm /* Add support for key by attributes (RFE 4706552) */
14371694Sdarrenm if (key->ck_format != CRYPTO_KEY_RAW)
14381694Sdarrenm return (CRYPTO_ARGUMENTS_BAD);
14391694Sdarrenm
14401694Sdarrenm /*
14411694Sdarrenm * Allocate and initialize MD5 context.
14421694Sdarrenm */
14431694Sdarrenm md5_hmac_ctx_tmpl = kmem_alloc(sizeof (md5_hmac_ctx_t),
14441694Sdarrenm crypto_kmflag(req));
14451694Sdarrenm if (md5_hmac_ctx_tmpl == NULL)
14461694Sdarrenm return (CRYPTO_HOST_MEMORY);
14471694Sdarrenm
14481694Sdarrenm if (keylen_in_bytes > MD5_HMAC_BLOCK_SIZE) {
14491694Sdarrenm uchar_t digested_key[MD5_DIGEST_LENGTH];
14501694Sdarrenm
14511694Sdarrenm /*
14521694Sdarrenm * Hash the passed-in key to get a smaller key.
14531694Sdarrenm * The inner context is used since it hasn't been
14541694Sdarrenm * initialized yet.
14551694Sdarrenm */
14561694Sdarrenm PROV_MD5_DIGEST_KEY(&md5_hmac_ctx_tmpl->hc_icontext,
14571694Sdarrenm key->ck_data, keylen_in_bytes, digested_key);
14581694Sdarrenm md5_mac_init_ctx(md5_hmac_ctx_tmpl, digested_key,
14591694Sdarrenm MD5_DIGEST_LENGTH);
14601694Sdarrenm } else {
14611694Sdarrenm md5_mac_init_ctx(md5_hmac_ctx_tmpl, key->ck_data,
14621694Sdarrenm keylen_in_bytes);
14631694Sdarrenm }
14641694Sdarrenm
14651694Sdarrenm md5_hmac_ctx_tmpl->hc_mech_type = mechanism->cm_type;
14661694Sdarrenm *ctx_template = (crypto_spi_ctx_template_t)md5_hmac_ctx_tmpl;
14671694Sdarrenm *ctx_template_size = sizeof (md5_hmac_ctx_t);
14681694Sdarrenm
14691694Sdarrenm return (CRYPTO_SUCCESS);
14701694Sdarrenm }
14711694Sdarrenm
14721694Sdarrenm static int
md5_free_context(crypto_ctx_t * ctx)14731694Sdarrenm md5_free_context(crypto_ctx_t *ctx)
14741694Sdarrenm {
14751694Sdarrenm uint_t ctx_len;
14761694Sdarrenm md5_mech_type_t mech_type;
14771694Sdarrenm
14781694Sdarrenm if (ctx->cc_provider_private == NULL)
14791694Sdarrenm return (CRYPTO_SUCCESS);
14801694Sdarrenm
14811694Sdarrenm /*
14821694Sdarrenm * We have to free either MD5 or MD5-HMAC contexts, which
14831694Sdarrenm * have different lengths.
14841694Sdarrenm */
14851694Sdarrenm
14861694Sdarrenm mech_type = PROV_MD5_CTX(ctx)->mc_mech_type;
14871694Sdarrenm if (mech_type == MD5_MECH_INFO_TYPE)
14881694Sdarrenm ctx_len = sizeof (md5_ctx_t);
14891694Sdarrenm else {
14901694Sdarrenm ASSERT(mech_type == MD5_HMAC_MECH_INFO_TYPE ||
14911694Sdarrenm mech_type == MD5_HMAC_GEN_MECH_INFO_TYPE);
14921694Sdarrenm ctx_len = sizeof (md5_hmac_ctx_t);
14931694Sdarrenm }
14941694Sdarrenm
14951694Sdarrenm bzero(ctx->cc_provider_private, ctx_len);
14961694Sdarrenm kmem_free(ctx->cc_provider_private, ctx_len);
14971694Sdarrenm ctx->cc_provider_private = NULL;
14981694Sdarrenm
14991694Sdarrenm return (CRYPTO_SUCCESS);
15001694Sdarrenm }
1501