145818ee1SMatthew Ahrens /*
245818ee1SMatthew Ahrens  * CDDL HEADER START
345818ee1SMatthew Ahrens  *
445818ee1SMatthew Ahrens  * The contents of this file are subject to the terms of the
545818ee1SMatthew Ahrens  * Common Development and Distribution License (the "License").
645818ee1SMatthew Ahrens  * You may not use this file except in compliance with the License.
745818ee1SMatthew Ahrens  *
845818ee1SMatthew Ahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
945818ee1SMatthew Ahrens  * or http://opensource.org/licenses/CDDL-1.0.
1045818ee1SMatthew Ahrens  * See the License for the specific language governing permissions
1145818ee1SMatthew Ahrens  * and limitations under the License.
1245818ee1SMatthew Ahrens  *
1345818ee1SMatthew Ahrens  * When distributing Covered Code, include this CDDL HEADER in each
1445818ee1SMatthew Ahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1545818ee1SMatthew Ahrens  * If applicable, add the following below this CDDL HEADER, with the
1645818ee1SMatthew Ahrens  * fields enclosed by brackets "[]" replaced with your own identifying
1745818ee1SMatthew Ahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
1845818ee1SMatthew Ahrens  *
1945818ee1SMatthew Ahrens  * CDDL HEADER END
2045818ee1SMatthew Ahrens  */
2145818ee1SMatthew Ahrens 
2245818ee1SMatthew Ahrens /*
2345818ee1SMatthew Ahrens  * Copyright 2013 Saso Kiselkov. All rights reserved.
2445818ee1SMatthew Ahrens  */
2545818ee1SMatthew Ahrens 
2645818ee1SMatthew Ahrens #include <sys/modctl.h>
2745818ee1SMatthew Ahrens #include <sys/crypto/common.h>
2845818ee1SMatthew Ahrens #include <sys/crypto/spi.h>
2945818ee1SMatthew Ahrens #include <sys/strsun.h>
3045818ee1SMatthew Ahrens #include <sys/sysmacros.h>
3145818ee1SMatthew Ahrens #include <sys/systm.h>
3245818ee1SMatthew Ahrens #define	SKEIN_MODULE_IMPL
3345818ee1SMatthew Ahrens #include <sys/skein.h>
3445818ee1SMatthew Ahrens 
3545818ee1SMatthew Ahrens /*
3645818ee1SMatthew Ahrens  * Like the sha2 module, we create the skein module with two modlinkages:
3745818ee1SMatthew Ahrens  * - modlmisc to allow direct calls to Skein_* API functions.
3845818ee1SMatthew Ahrens  * - modlcrypto to integrate well into the Kernel Crypto Framework (KCF).
3945818ee1SMatthew Ahrens  */
4045818ee1SMatthew Ahrens static struct modlmisc modlmisc = {
4145818ee1SMatthew Ahrens 	&mod_miscops,
4245818ee1SMatthew Ahrens 	"Skein Message-Digest Algorithm"
4345818ee1SMatthew Ahrens };
4445818ee1SMatthew Ahrens 
4545818ee1SMatthew Ahrens static struct modlcrypto modlcrypto = {
4645818ee1SMatthew Ahrens 	&mod_cryptoops,
4745818ee1SMatthew Ahrens 	"Skein Kernel SW Provider"
4845818ee1SMatthew Ahrens };
4945818ee1SMatthew Ahrens 
5045818ee1SMatthew Ahrens static struct modlinkage modlinkage = {
5145818ee1SMatthew Ahrens 	MODREV_1, &modlmisc, &modlcrypto, NULL
5245818ee1SMatthew Ahrens };
5345818ee1SMatthew Ahrens 
5445818ee1SMatthew Ahrens static crypto_mech_info_t skein_mech_info_tab[] = {
5545818ee1SMatthew Ahrens 	{CKM_SKEIN_256, SKEIN_256_MECH_INFO_TYPE,
5645818ee1SMatthew Ahrens 	    CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC,
5745818ee1SMatthew Ahrens 	    0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
5845818ee1SMatthew Ahrens 	{CKM_SKEIN_256_MAC, SKEIN_256_MAC_MECH_INFO_TYPE,
5945818ee1SMatthew Ahrens 	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC, 1, INT_MAX,
6045818ee1SMatthew Ahrens 	    CRYPTO_KEYSIZE_UNIT_IN_BYTES},
6145818ee1SMatthew Ahrens 	{CKM_SKEIN_512, SKEIN_512_MECH_INFO_TYPE,
6245818ee1SMatthew Ahrens 	    CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC,
6345818ee1SMatthew Ahrens 	    0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
6445818ee1SMatthew Ahrens 	{CKM_SKEIN_512_MAC, SKEIN_512_MAC_MECH_INFO_TYPE,
6545818ee1SMatthew Ahrens 	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC, 1, INT_MAX,
6645818ee1SMatthew Ahrens 	    CRYPTO_KEYSIZE_UNIT_IN_BYTES},
6745818ee1SMatthew Ahrens 	{CKM_SKEIN1024, SKEIN1024_MECH_INFO_TYPE,
6845818ee1SMatthew Ahrens 	    CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC,
6945818ee1SMatthew Ahrens 	    0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
7045818ee1SMatthew Ahrens 	{CKM_SKEIN1024_MAC, SKEIN1024_MAC_MECH_INFO_TYPE,
7145818ee1SMatthew Ahrens 	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC, 1, INT_MAX,
7245818ee1SMatthew Ahrens 	    CRYPTO_KEYSIZE_UNIT_IN_BYTES}
7345818ee1SMatthew Ahrens };
7445818ee1SMatthew Ahrens 
7545818ee1SMatthew Ahrens static void skein_provider_status(crypto_provider_handle_t, uint_t *);
7645818ee1SMatthew Ahrens 
7745818ee1SMatthew Ahrens static crypto_control_ops_t skein_control_ops = {
7845818ee1SMatthew Ahrens 	skein_provider_status
7945818ee1SMatthew Ahrens };
8045818ee1SMatthew Ahrens 
8145818ee1SMatthew Ahrens static int skein_digest_init(crypto_ctx_t *, crypto_mechanism_t *,
8245818ee1SMatthew Ahrens     crypto_req_handle_t);
8345818ee1SMatthew Ahrens static int skein_digest(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
8445818ee1SMatthew Ahrens     crypto_req_handle_t);
8545818ee1SMatthew Ahrens static int skein_update(crypto_ctx_t *, crypto_data_t *, crypto_req_handle_t);
8645818ee1SMatthew Ahrens static int skein_final(crypto_ctx_t *, crypto_data_t *, crypto_req_handle_t);
8745818ee1SMatthew Ahrens static int skein_digest_atomic(crypto_provider_handle_t, crypto_session_id_t,
8845818ee1SMatthew Ahrens     crypto_mechanism_t *, crypto_data_t *, crypto_data_t *,
8945818ee1SMatthew Ahrens     crypto_req_handle_t);
9045818ee1SMatthew Ahrens 
9145818ee1SMatthew Ahrens static crypto_digest_ops_t skein_digest_ops = {
9245818ee1SMatthew Ahrens 	skein_digest_init,
9345818ee1SMatthew Ahrens 	skein_digest,
9445818ee1SMatthew Ahrens 	skein_update,
9545818ee1SMatthew Ahrens 	NULL,
9645818ee1SMatthew Ahrens 	skein_final,
9745818ee1SMatthew Ahrens 	skein_digest_atomic
9845818ee1SMatthew Ahrens };
9945818ee1SMatthew Ahrens 
10045818ee1SMatthew Ahrens static int skein_mac_init(crypto_ctx_t *, crypto_mechanism_t *, crypto_key_t *,
10145818ee1SMatthew Ahrens     crypto_spi_ctx_template_t, crypto_req_handle_t);
10245818ee1SMatthew Ahrens static int skein_mac_atomic(crypto_provider_handle_t, crypto_session_id_t,
10345818ee1SMatthew Ahrens     crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
10445818ee1SMatthew Ahrens     crypto_spi_ctx_template_t, crypto_req_handle_t);
10545818ee1SMatthew Ahrens 
10645818ee1SMatthew Ahrens static crypto_mac_ops_t skein_mac_ops = {
10745818ee1SMatthew Ahrens 	skein_mac_init,
10845818ee1SMatthew Ahrens 	NULL,
10945818ee1SMatthew Ahrens 	skein_update,	/* using regular digest update is OK here */
11045818ee1SMatthew Ahrens 	skein_final,	/* using regular digest final is OK here */
11145818ee1SMatthew Ahrens 	skein_mac_atomic,
11245818ee1SMatthew Ahrens 	NULL
11345818ee1SMatthew Ahrens };
11445818ee1SMatthew Ahrens 
11545818ee1SMatthew Ahrens static int skein_create_ctx_template(crypto_provider_handle_t,
11645818ee1SMatthew Ahrens     crypto_mechanism_t *, crypto_key_t *, crypto_spi_ctx_template_t *,
11745818ee1SMatthew Ahrens     size_t *, crypto_req_handle_t);
11845818ee1SMatthew Ahrens static int skein_free_context(crypto_ctx_t *);
11945818ee1SMatthew Ahrens 
12045818ee1SMatthew Ahrens static crypto_ctx_ops_t skein_ctx_ops = {
12145818ee1SMatthew Ahrens 	skein_create_ctx_template,
12245818ee1SMatthew Ahrens 	skein_free_context
12345818ee1SMatthew Ahrens };
12445818ee1SMatthew Ahrens 
12545818ee1SMatthew Ahrens static crypto_ops_t skein_crypto_ops = {
12645818ee1SMatthew Ahrens 	&skein_control_ops,
12745818ee1SMatthew Ahrens 	&skein_digest_ops,
12845818ee1SMatthew Ahrens 	NULL,
12945818ee1SMatthew Ahrens 	&skein_mac_ops,
13045818ee1SMatthew Ahrens 	NULL,
13145818ee1SMatthew Ahrens 	NULL,
13245818ee1SMatthew Ahrens 	NULL,
13345818ee1SMatthew Ahrens 	NULL,
13445818ee1SMatthew Ahrens 	NULL,
13545818ee1SMatthew Ahrens 	NULL,
13645818ee1SMatthew Ahrens 	NULL,
13745818ee1SMatthew Ahrens 	NULL,
13845818ee1SMatthew Ahrens 	NULL,
13945818ee1SMatthew Ahrens 	&skein_ctx_ops,
14045818ee1SMatthew Ahrens 	NULL,
14145818ee1SMatthew Ahrens 	NULL,
14245818ee1SMatthew Ahrens 	NULL
14345818ee1SMatthew Ahrens };
14445818ee1SMatthew Ahrens 
14545818ee1SMatthew Ahrens static crypto_provider_info_t skein_prov_info = {
14645818ee1SMatthew Ahrens 	CRYPTO_SPI_VERSION_4,
14745818ee1SMatthew Ahrens 	"Skein Software Provider",
14845818ee1SMatthew Ahrens 	CRYPTO_SW_PROVIDER,
14945818ee1SMatthew Ahrens 	{&modlinkage},
15045818ee1SMatthew Ahrens 	NULL,
15145818ee1SMatthew Ahrens 	&skein_crypto_ops,
15245818ee1SMatthew Ahrens 	sizeof (skein_mech_info_tab) / sizeof (crypto_mech_info_t),
15345818ee1SMatthew Ahrens 	skein_mech_info_tab
15445818ee1SMatthew Ahrens };
15545818ee1SMatthew Ahrens 
156*0c3cd038SMatthew Ahrens static crypto_kcf_provider_handle_t skein_prov_handle = 0;
15745818ee1SMatthew Ahrens 
15845818ee1SMatthew Ahrens typedef struct skein_ctx {
15945818ee1SMatthew Ahrens 	skein_mech_type_t		sc_mech_type;
16045818ee1SMatthew Ahrens 	size_t				sc_digest_bitlen;
16145818ee1SMatthew Ahrens 	union {
16245818ee1SMatthew Ahrens 		Skein_256_Ctxt_t	sc_256;
16345818ee1SMatthew Ahrens 		Skein_512_Ctxt_t	sc_512;
16445818ee1SMatthew Ahrens 		Skein1024_Ctxt_t	sc_1024;
165be322840SRobert Mustacchi 	} sc_u;
16645818ee1SMatthew Ahrens } skein_ctx_t;
16745818ee1SMatthew Ahrens #define	SKEIN_CTX(_ctx_)	((skein_ctx_t *)((_ctx_)->cc_provider_private))
16845818ee1SMatthew Ahrens #define	SKEIN_CTX_LVALUE(_ctx_)	(_ctx_)->cc_provider_private
16945818ee1SMatthew Ahrens #define	SKEIN_OP(_skein_ctx, _op, ...)					\
17045818ee1SMatthew Ahrens 	do {								\
17145818ee1SMatthew Ahrens 		skein_ctx_t	*sc = (_skein_ctx);			\
17245818ee1SMatthew Ahrens 		switch (sc->sc_mech_type) {				\
17345818ee1SMatthew Ahrens 		case SKEIN_256_MECH_INFO_TYPE:				\
17445818ee1SMatthew Ahrens 		case SKEIN_256_MAC_MECH_INFO_TYPE:			\
175be322840SRobert Mustacchi 			(void) Skein_256_ ## _op(&sc->sc_u.sc_256,	\
176be322840SRobert Mustacchi 			    __VA_ARGS__);				\
17745818ee1SMatthew Ahrens 			break;						\
17845818ee1SMatthew Ahrens 		case SKEIN_512_MECH_INFO_TYPE:				\
17945818ee1SMatthew Ahrens 		case SKEIN_512_MAC_MECH_INFO_TYPE:			\
180be322840SRobert Mustacchi 			(void) Skein_512_ ## _op(&sc->sc_u.sc_512,	\
181be322840SRobert Mustacchi 			    __VA_ARGS__);				\
18245818ee1SMatthew Ahrens 			break;						\
18345818ee1SMatthew Ahrens 		case SKEIN1024_MECH_INFO_TYPE:				\
18445818ee1SMatthew Ahrens 		case SKEIN1024_MAC_MECH_INFO_TYPE:			\
185be322840SRobert Mustacchi 			(void) Skein1024_ ## _op(&sc->sc_u.sc_1024,	\
186be322840SRobert Mustacchi 			    __VA_ARGS__);				\
18745818ee1SMatthew Ahrens 			break;						\
18845818ee1SMatthew Ahrens 		}							\
18945818ee1SMatthew Ahrens 		_NOTE(CONSTCOND)					\
19045818ee1SMatthew Ahrens 	} while (0)
19145818ee1SMatthew Ahrens 
19245818ee1SMatthew Ahrens static int
skein_get_digest_bitlen(const crypto_mechanism_t * mechanism,size_t * result)19345818ee1SMatthew Ahrens skein_get_digest_bitlen(const crypto_mechanism_t *mechanism, size_t *result)
19445818ee1SMatthew Ahrens {
19545818ee1SMatthew Ahrens 	if (mechanism->cm_param != NULL) {
19645818ee1SMatthew Ahrens 		/*LINTED(E_BAD_PTR_CAST_ALIGN)*/
19745818ee1SMatthew Ahrens 		skein_param_t	*param = (skein_param_t *)mechanism->cm_param;
19845818ee1SMatthew Ahrens 
19945818ee1SMatthew Ahrens 		if (mechanism->cm_param_len != sizeof (*param) ||
20045818ee1SMatthew Ahrens 		    param->sp_digest_bitlen == 0) {
20145818ee1SMatthew Ahrens 			return (CRYPTO_MECHANISM_PARAM_INVALID);
20245818ee1SMatthew Ahrens 		}
20345818ee1SMatthew Ahrens 		*result = param->sp_digest_bitlen;
20445818ee1SMatthew Ahrens 	} else {
20545818ee1SMatthew Ahrens 		switch (mechanism->cm_type) {
20645818ee1SMatthew Ahrens 		case SKEIN_256_MECH_INFO_TYPE:
20745818ee1SMatthew Ahrens 			*result = 256;
20845818ee1SMatthew Ahrens 			break;
20945818ee1SMatthew Ahrens 		case SKEIN_512_MECH_INFO_TYPE:
21045818ee1SMatthew Ahrens 			*result = 512;
21145818ee1SMatthew Ahrens 			break;
21245818ee1SMatthew Ahrens 		case SKEIN1024_MECH_INFO_TYPE:
21345818ee1SMatthew Ahrens 			*result = 1024;
21445818ee1SMatthew Ahrens 			break;
21545818ee1SMatthew Ahrens 		default:
21645818ee1SMatthew Ahrens 			return (CRYPTO_MECHANISM_INVALID);
21745818ee1SMatthew Ahrens 		}
21845818ee1SMatthew Ahrens 	}
21945818ee1SMatthew Ahrens 	return (CRYPTO_SUCCESS);
22045818ee1SMatthew Ahrens }
22145818ee1SMatthew Ahrens 
22245818ee1SMatthew Ahrens int
_init(void)22345818ee1SMatthew Ahrens _init(void)
22445818ee1SMatthew Ahrens {
22545818ee1SMatthew Ahrens 	int error;
22645818ee1SMatthew Ahrens 
22745818ee1SMatthew Ahrens 	if ((error = mod_install(&modlinkage)) != 0)
22845818ee1SMatthew Ahrens 		return (error);
22945818ee1SMatthew Ahrens 
23045818ee1SMatthew Ahrens 	/*
23145818ee1SMatthew Ahrens 	 * Try to register with KCF - failure shouldn't unload us, since we
23245818ee1SMatthew Ahrens 	 * still may want to continue providing misc/skein functionality.
23345818ee1SMatthew Ahrens 	 */
23445818ee1SMatthew Ahrens 	(void) crypto_register_provider(&skein_prov_info, &skein_prov_handle);
23545818ee1SMatthew Ahrens 
23645818ee1SMatthew Ahrens 	return (0);
23745818ee1SMatthew Ahrens }
23845818ee1SMatthew Ahrens 
23945818ee1SMatthew Ahrens int
_info(struct modinfo * modinfop)24045818ee1SMatthew Ahrens _info(struct modinfo *modinfop)
24145818ee1SMatthew Ahrens {
24245818ee1SMatthew Ahrens 	return (mod_info(&modlinkage, modinfop));
24345818ee1SMatthew Ahrens }
24445818ee1SMatthew Ahrens 
24545818ee1SMatthew Ahrens /*
24645818ee1SMatthew Ahrens  * KCF software provider control entry points.
24745818ee1SMatthew Ahrens  */
24845818ee1SMatthew Ahrens /* ARGSUSED */
24945818ee1SMatthew Ahrens static void
skein_provider_status(crypto_provider_handle_t provider,uint_t * status)25045818ee1SMatthew Ahrens skein_provider_status(crypto_provider_handle_t provider, uint_t *status)
25145818ee1SMatthew Ahrens {
25245818ee1SMatthew Ahrens 	*status = CRYPTO_PROVIDER_READY;
25345818ee1SMatthew Ahrens }
25445818ee1SMatthew Ahrens 
25545818ee1SMatthew Ahrens /*
25645818ee1SMatthew Ahrens  * General Skein hashing helper functions.
25745818ee1SMatthew Ahrens  */
25845818ee1SMatthew Ahrens 
25945818ee1SMatthew Ahrens /*
26045818ee1SMatthew Ahrens  * Performs an Update on a context with uio input data.
26145818ee1SMatthew Ahrens  */
26245818ee1SMatthew Ahrens static int
skein_digest_update_uio(skein_ctx_t * ctx,const crypto_data_t * data)26345818ee1SMatthew Ahrens skein_digest_update_uio(skein_ctx_t *ctx, const crypto_data_t *data)
26445818ee1SMatthew Ahrens {
26545818ee1SMatthew Ahrens 	off_t		offset = data->cd_offset;
26645818ee1SMatthew Ahrens 	size_t		length = data->cd_length;
26745818ee1SMatthew Ahrens 	uint_t		vec_idx;
26845818ee1SMatthew Ahrens 	size_t		cur_len;
26945818ee1SMatthew Ahrens 	const uio_t	*uio = data->cd_uio;
27045818ee1SMatthew Ahrens 
27145818ee1SMatthew Ahrens 	/* we support only kernel buffer */
27245818ee1SMatthew Ahrens 	if (uio->uio_segflg != UIO_SYSSPACE)
27345818ee1SMatthew Ahrens 		return (CRYPTO_ARGUMENTS_BAD);
27445818ee1SMatthew Ahrens 
27545818ee1SMatthew Ahrens 	/*
27645818ee1SMatthew Ahrens 	 * Jump to the first iovec containing data to be
27745818ee1SMatthew Ahrens 	 * digested.
27845818ee1SMatthew Ahrens 	 */
27945818ee1SMatthew Ahrens 	for (vec_idx = 0; vec_idx < uio->uio_iovcnt &&
28045818ee1SMatthew Ahrens 	    offset >= uio->uio_iov[vec_idx].iov_len;
28145818ee1SMatthew Ahrens 	    offset -= uio->uio_iov[vec_idx++].iov_len)
28245818ee1SMatthew Ahrens 		;
28345818ee1SMatthew Ahrens 	if (vec_idx == uio->uio_iovcnt) {
28445818ee1SMatthew Ahrens 		/*
28545818ee1SMatthew Ahrens 		 * The caller specified an offset that is larger than the
28645818ee1SMatthew Ahrens 		 * total size of the buffers it provided.
28745818ee1SMatthew Ahrens 		 */
28845818ee1SMatthew Ahrens 		return (CRYPTO_DATA_LEN_RANGE);
28945818ee1SMatthew Ahrens 	}
29045818ee1SMatthew Ahrens 
29145818ee1SMatthew Ahrens 	/*
29245818ee1SMatthew Ahrens 	 * Now do the digesting on the iovecs.
29345818ee1SMatthew Ahrens 	 */
29445818ee1SMatthew Ahrens 	while (vec_idx < uio->uio_iovcnt && length > 0) {
29545818ee1SMatthew Ahrens 		cur_len = MIN(uio->uio_iov[vec_idx].iov_len - offset, length);
29645818ee1SMatthew Ahrens 		SKEIN_OP(ctx, Update, (uint8_t *)uio->uio_iov[vec_idx].iov_base
29745818ee1SMatthew Ahrens 		    + offset, cur_len);
29845818ee1SMatthew Ahrens 		length -= cur_len;
29945818ee1SMatthew Ahrens 		vec_idx++;
30045818ee1SMatthew Ahrens 		offset = 0;
30145818ee1SMatthew Ahrens 	}
30245818ee1SMatthew Ahrens 
30345818ee1SMatthew Ahrens 	if (vec_idx == uio->uio_iovcnt && length > 0) {
30445818ee1SMatthew Ahrens 		/*
30545818ee1SMatthew Ahrens 		 * The end of the specified iovec's was reached but
30645818ee1SMatthew Ahrens 		 * the length requested could not be processed, i.e.
30745818ee1SMatthew Ahrens 		 * The caller requested to digest more data than it provided.
30845818ee1SMatthew Ahrens 		 */
30945818ee1SMatthew Ahrens 		return (CRYPTO_DATA_LEN_RANGE);
31045818ee1SMatthew Ahrens 	}
31145818ee1SMatthew Ahrens 
31245818ee1SMatthew Ahrens 	return (CRYPTO_SUCCESS);
31345818ee1SMatthew Ahrens }
31445818ee1SMatthew Ahrens 
31545818ee1SMatthew Ahrens /*
31645818ee1SMatthew Ahrens  * Performs a Final on a context and writes to a uio digest output.
31745818ee1SMatthew Ahrens  */
31845818ee1SMatthew Ahrens static int
skein_digest_final_uio(skein_ctx_t * ctx,crypto_data_t * digest,crypto_req_handle_t req)31945818ee1SMatthew Ahrens skein_digest_final_uio(skein_ctx_t *ctx, crypto_data_t *digest,
32045818ee1SMatthew Ahrens     crypto_req_handle_t req)
32145818ee1SMatthew Ahrens {
32245818ee1SMatthew Ahrens 	off_t	offset = digest->cd_offset;
32345818ee1SMatthew Ahrens 	uint_t	vec_idx;
32445818ee1SMatthew Ahrens 	uio_t	*uio = digest->cd_uio;
32545818ee1SMatthew Ahrens 
32645818ee1SMatthew Ahrens 	/* we support only kernel buffer */
32745818ee1SMatthew Ahrens 	if (uio->uio_segflg != UIO_SYSSPACE)
32845818ee1SMatthew Ahrens 		return (CRYPTO_ARGUMENTS_BAD);
32945818ee1SMatthew Ahrens 
33045818ee1SMatthew Ahrens 	/*
33145818ee1SMatthew Ahrens 	 * Jump to the first iovec containing ptr to the digest to be returned.
33245818ee1SMatthew Ahrens 	 */
33345818ee1SMatthew Ahrens 	for (vec_idx = 0; offset >= uio->uio_iov[vec_idx].iov_len &&
33445818ee1SMatthew Ahrens 	    vec_idx < uio->uio_iovcnt;
33545818ee1SMatthew Ahrens 	    offset -= uio->uio_iov[vec_idx++].iov_len)
33645818ee1SMatthew Ahrens 		;
33745818ee1SMatthew Ahrens 	if (vec_idx == uio->uio_iovcnt) {
33845818ee1SMatthew Ahrens 		/*
33945818ee1SMatthew Ahrens 		 * The caller specified an offset that is larger than the
34045818ee1SMatthew Ahrens 		 * total size of the buffers it provided.
34145818ee1SMatthew Ahrens 		 */
34245818ee1SMatthew Ahrens 		return (CRYPTO_DATA_LEN_RANGE);
34345818ee1SMatthew Ahrens 	}
34445818ee1SMatthew Ahrens 	if (offset + CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen) <=
34545818ee1SMatthew Ahrens 	    uio->uio_iov[vec_idx].iov_len) {
34645818ee1SMatthew Ahrens 		/* The computed digest will fit in the current iovec. */
34745818ee1SMatthew Ahrens 		SKEIN_OP(ctx, Final,
34845818ee1SMatthew Ahrens 		    (uchar_t *)uio->uio_iov[vec_idx].iov_base + offset);
34945818ee1SMatthew Ahrens 	} else {
35045818ee1SMatthew Ahrens 		uint8_t *digest_tmp;
35145818ee1SMatthew Ahrens 		off_t scratch_offset = 0;
35245818ee1SMatthew Ahrens 		size_t length = CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen);
35345818ee1SMatthew Ahrens 		size_t cur_len;
35445818ee1SMatthew Ahrens 
35545818ee1SMatthew Ahrens 		digest_tmp = kmem_alloc(CRYPTO_BITS2BYTES(
35645818ee1SMatthew Ahrens 		    ctx->sc_digest_bitlen), crypto_kmflag(req));
35745818ee1SMatthew Ahrens 		if (digest_tmp == NULL)
35845818ee1SMatthew Ahrens 			return (CRYPTO_HOST_MEMORY);
35945818ee1SMatthew Ahrens 		SKEIN_OP(ctx, Final, digest_tmp);
36045818ee1SMatthew Ahrens 		while (vec_idx < uio->uio_iovcnt && length > 0) {
36145818ee1SMatthew Ahrens 			cur_len = MIN(uio->uio_iov[vec_idx].iov_len - offset,
36245818ee1SMatthew Ahrens 			    length);
36345818ee1SMatthew Ahrens 			bcopy(digest_tmp + scratch_offset,
36445818ee1SMatthew Ahrens 			    uio->uio_iov[vec_idx].iov_base + offset, cur_len);
36545818ee1SMatthew Ahrens 
36645818ee1SMatthew Ahrens 			length -= cur_len;
36745818ee1SMatthew Ahrens 			vec_idx++;
36845818ee1SMatthew Ahrens 			scratch_offset += cur_len;
36945818ee1SMatthew Ahrens 			offset = 0;
37045818ee1SMatthew Ahrens 		}
37145818ee1SMatthew Ahrens 		kmem_free(digest_tmp, CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen));
37245818ee1SMatthew Ahrens 
37345818ee1SMatthew Ahrens 		if (vec_idx == uio->uio_iovcnt && length > 0) {
37445818ee1SMatthew Ahrens 			/*
37545818ee1SMatthew Ahrens 			 * The end of the specified iovec's was reached but
37645818ee1SMatthew Ahrens 			 * the length requested could not be processed, i.e.
37745818ee1SMatthew Ahrens 			 * The caller requested to digest more data than it
37845818ee1SMatthew Ahrens 			 * provided.
37945818ee1SMatthew Ahrens 			 */
38045818ee1SMatthew Ahrens 			return (CRYPTO_DATA_LEN_RANGE);
38145818ee1SMatthew Ahrens 		}
38245818ee1SMatthew Ahrens 	}
38345818ee1SMatthew Ahrens 
38445818ee1SMatthew Ahrens 	return (CRYPTO_SUCCESS);
38545818ee1SMatthew Ahrens }
38645818ee1SMatthew Ahrens 
38745818ee1SMatthew Ahrens /*
38845818ee1SMatthew Ahrens  * Performs an Update on a context with mblk input data.
38945818ee1SMatthew Ahrens  */
39045818ee1SMatthew Ahrens static int
skein_digest_update_mblk(skein_ctx_t * ctx,crypto_data_t * data)39145818ee1SMatthew Ahrens skein_digest_update_mblk(skein_ctx_t *ctx, crypto_data_t *data)
39245818ee1SMatthew Ahrens {
39345818ee1SMatthew Ahrens 	off_t offset = data->cd_offset;
39445818ee1SMatthew Ahrens 	size_t length = data->cd_length;
39545818ee1SMatthew Ahrens 	mblk_t *mp;
39645818ee1SMatthew Ahrens 	size_t cur_len;
39745818ee1SMatthew Ahrens 
39845818ee1SMatthew Ahrens 	/* Jump to the first mblk_t containing data to be digested. */
39945818ee1SMatthew Ahrens 	for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp);
40045818ee1SMatthew Ahrens 	    offset -= MBLKL(mp), mp = mp->b_cont)
40145818ee1SMatthew Ahrens 		;
40245818ee1SMatthew Ahrens 	if (mp == NULL) {
40345818ee1SMatthew Ahrens 		/*
40445818ee1SMatthew Ahrens 		 * The caller specified an offset that is larger than the
40545818ee1SMatthew Ahrens 		 * total size of the buffers it provided.
40645818ee1SMatthew Ahrens 		 */
40745818ee1SMatthew Ahrens 		return (CRYPTO_DATA_LEN_RANGE);
40845818ee1SMatthew Ahrens 	}
40945818ee1SMatthew Ahrens 
41045818ee1SMatthew Ahrens 	/* Now do the digesting on the mblk chain. */
41145818ee1SMatthew Ahrens 	while (mp != NULL && length > 0) {
41245818ee1SMatthew Ahrens 		cur_len = MIN(MBLKL(mp) - offset, length);
41345818ee1SMatthew Ahrens 		SKEIN_OP(ctx, Update, mp->b_rptr + offset, cur_len);
41445818ee1SMatthew Ahrens 		length -= cur_len;
41545818ee1SMatthew Ahrens 		offset = 0;
41645818ee1SMatthew Ahrens 		mp = mp->b_cont;
41745818ee1SMatthew Ahrens 	}
41845818ee1SMatthew Ahrens 
41945818ee1SMatthew Ahrens 	if (mp == NULL && length > 0) {
42045818ee1SMatthew Ahrens 		/*
42145818ee1SMatthew Ahrens 		 * The end of the mblk was reached but the length requested
42245818ee1SMatthew Ahrens 		 * could not be processed, i.e. The caller requested
42345818ee1SMatthew Ahrens 		 * to digest more data than it provided.
42445818ee1SMatthew Ahrens 		 */
42545818ee1SMatthew Ahrens 		return (CRYPTO_DATA_LEN_RANGE);
42645818ee1SMatthew Ahrens 	}
42745818ee1SMatthew Ahrens 
42845818ee1SMatthew Ahrens 	return (CRYPTO_SUCCESS);
42945818ee1SMatthew Ahrens }
43045818ee1SMatthew Ahrens 
43145818ee1SMatthew Ahrens /*
43245818ee1SMatthew Ahrens  * Performs a Final on a context and writes to an mblk digest output.
43345818ee1SMatthew Ahrens  */
43445818ee1SMatthew Ahrens static int
skein_digest_final_mblk(skein_ctx_t * ctx,crypto_data_t * digest,crypto_req_handle_t req)43545818ee1SMatthew Ahrens skein_digest_final_mblk(skein_ctx_t *ctx, crypto_data_t *digest,
43645818ee1SMatthew Ahrens     crypto_req_handle_t req)
43745818ee1SMatthew Ahrens {
43845818ee1SMatthew Ahrens 	off_t	offset = digest->cd_offset;
43945818ee1SMatthew Ahrens 	mblk_t	*mp;
44045818ee1SMatthew Ahrens 
44145818ee1SMatthew Ahrens 	/* Jump to the first mblk_t that will be used to store the digest. */
44245818ee1SMatthew Ahrens 	for (mp = digest->cd_mp; mp != NULL && offset >= MBLKL(mp);
44345818ee1SMatthew Ahrens 	    offset -= MBLKL(mp), mp = mp->b_cont)
44445818ee1SMatthew Ahrens 		;
44545818ee1SMatthew Ahrens 	if (mp == NULL) {
44645818ee1SMatthew Ahrens 		/* caller specified offset is too large */
44745818ee1SMatthew Ahrens 		return (CRYPTO_DATA_LEN_RANGE);
44845818ee1SMatthew Ahrens 	}
44945818ee1SMatthew Ahrens 
45045818ee1SMatthew Ahrens 	if (offset + CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen) <= MBLKL(mp)) {
45145818ee1SMatthew Ahrens 		/* The digest will fit in the current mblk. */
45245818ee1SMatthew Ahrens 		SKEIN_OP(ctx, Final, mp->b_rptr + offset);
45345818ee1SMatthew Ahrens 	} else {
45445818ee1SMatthew Ahrens 		/* Split the digest up between the individual buffers. */
45545818ee1SMatthew Ahrens 		uint8_t *digest_tmp;
45645818ee1SMatthew Ahrens 		off_t scratch_offset = 0;
45745818ee1SMatthew Ahrens 		size_t length = CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen);
45845818ee1SMatthew Ahrens 		size_t cur_len;
45945818ee1SMatthew Ahrens 
46045818ee1SMatthew Ahrens 		digest_tmp = kmem_alloc(CRYPTO_BITS2BYTES(
46145818ee1SMatthew Ahrens 		    ctx->sc_digest_bitlen), crypto_kmflag(req));
46245818ee1SMatthew Ahrens 		if (digest_tmp == NULL)
46345818ee1SMatthew Ahrens 			return (CRYPTO_HOST_MEMORY);
46445818ee1SMatthew Ahrens 		SKEIN_OP(ctx, Final, digest_tmp);
46545818ee1SMatthew Ahrens 		while (mp != NULL && length > 0) {
46645818ee1SMatthew Ahrens 			cur_len = MIN(MBLKL(mp) - offset, length);
46745818ee1SMatthew Ahrens 			bcopy(digest_tmp + scratch_offset,
46845818ee1SMatthew Ahrens 			    mp->b_rptr + offset, cur_len);
46945818ee1SMatthew Ahrens 			length -= cur_len;
47045818ee1SMatthew Ahrens 			mp = mp->b_cont;
47145818ee1SMatthew Ahrens 			scratch_offset += cur_len;
47245818ee1SMatthew Ahrens 			offset = 0;
47345818ee1SMatthew Ahrens 		}
47445818ee1SMatthew Ahrens 		kmem_free(digest_tmp, CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen));
47545818ee1SMatthew Ahrens 		if (mp == NULL && length > 0) {
47645818ee1SMatthew Ahrens 			/* digest too long to fit in the mblk buffers */
47745818ee1SMatthew Ahrens 			return (CRYPTO_DATA_LEN_RANGE);
47845818ee1SMatthew Ahrens 		}
47945818ee1SMatthew Ahrens 	}
48045818ee1SMatthew Ahrens 
48145818ee1SMatthew Ahrens 	return (CRYPTO_SUCCESS);
48245818ee1SMatthew Ahrens }
48345818ee1SMatthew Ahrens 
48445818ee1SMatthew Ahrens /*
48545818ee1SMatthew Ahrens  * KCF software provider digest entry points.
48645818ee1SMatthew Ahrens  */
48745818ee1SMatthew Ahrens 
48845818ee1SMatthew Ahrens /*
48945818ee1SMatthew Ahrens  * Initializes a skein digest context to the configuration in `mechanism'.
49045818ee1SMatthew Ahrens  * The mechanism cm_type must be one of SKEIN_*_MECH_INFO_TYPE. The cm_param
49145818ee1SMatthew Ahrens  * field may contain a skein_param_t structure indicating the length of the
49245818ee1SMatthew Ahrens  * digest the algorithm should produce. Otherwise the default output lengths
49345818ee1SMatthew Ahrens  * are applied (32 bytes for Skein-256, 64 bytes for Skein-512 and 128 bytes
49445818ee1SMatthew Ahrens  * for Skein-1024).
49545818ee1SMatthew Ahrens  */
49645818ee1SMatthew Ahrens static int
skein_digest_init(crypto_ctx_t * ctx,crypto_mechanism_t * mechanism,crypto_req_handle_t req)49745818ee1SMatthew Ahrens skein_digest_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
49845818ee1SMatthew Ahrens     crypto_req_handle_t req)
49945818ee1SMatthew Ahrens {
50045818ee1SMatthew Ahrens 	int	error = CRYPTO_SUCCESS;
50145818ee1SMatthew Ahrens 
50245818ee1SMatthew Ahrens 	if (!VALID_SKEIN_DIGEST_MECH(mechanism->cm_type))
50345818ee1SMatthew Ahrens 		return (CRYPTO_MECHANISM_INVALID);
50445818ee1SMatthew Ahrens 
50545818ee1SMatthew Ahrens 	SKEIN_CTX_LVALUE(ctx) = kmem_alloc(sizeof (*SKEIN_CTX(ctx)),
50645818ee1SMatthew Ahrens 	    crypto_kmflag(req));
50745818ee1SMatthew Ahrens 	if (SKEIN_CTX(ctx) == NULL)
50845818ee1SMatthew Ahrens 		return (CRYPTO_HOST_MEMORY);
50945818ee1SMatthew Ahrens 
51045818ee1SMatthew Ahrens 	SKEIN_CTX(ctx)->sc_mech_type = mechanism->cm_type;
51145818ee1SMatthew Ahrens 	error = skein_get_digest_bitlen(mechanism,
51245818ee1SMatthew Ahrens 	    &SKEIN_CTX(ctx)->sc_digest_bitlen);
51345818ee1SMatthew Ahrens 	if (error != CRYPTO_SUCCESS)
51445818ee1SMatthew Ahrens 		goto errout;
51545818ee1SMatthew Ahrens 	SKEIN_OP(SKEIN_CTX(ctx), Init, SKEIN_CTX(ctx)->sc_digest_bitlen);
51645818ee1SMatthew Ahrens 
51745818ee1SMatthew Ahrens 	return (CRYPTO_SUCCESS);
51845818ee1SMatthew Ahrens errout:
51945818ee1SMatthew Ahrens 	bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
52045818ee1SMatthew Ahrens 	kmem_free(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
52145818ee1SMatthew Ahrens 	SKEIN_CTX_LVALUE(ctx) = NULL;
52245818ee1SMatthew Ahrens 	return (error);
52345818ee1SMatthew Ahrens }
52445818ee1SMatthew Ahrens 
52545818ee1SMatthew Ahrens /*
52645818ee1SMatthew Ahrens  * Executes a skein_update and skein_digest on a pre-initialized crypto
52745818ee1SMatthew Ahrens  * context in a single step. See the documentation to these functions to
52845818ee1SMatthew Ahrens  * see what to pass here.
52945818ee1SMatthew Ahrens  */
53045818ee1SMatthew Ahrens static int
skein_digest(crypto_ctx_t * ctx,crypto_data_t * data,crypto_data_t * digest,crypto_req_handle_t req)53145818ee1SMatthew Ahrens skein_digest(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *digest,
53245818ee1SMatthew Ahrens     crypto_req_handle_t req)
53345818ee1SMatthew Ahrens {
53445818ee1SMatthew Ahrens 	int error = CRYPTO_SUCCESS;
53545818ee1SMatthew Ahrens 
53645818ee1SMatthew Ahrens 	ASSERT(SKEIN_CTX(ctx) != NULL);
53745818ee1SMatthew Ahrens 
53845818ee1SMatthew Ahrens 	if (digest->cd_length <
53945818ee1SMatthew Ahrens 	    CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen)) {
54045818ee1SMatthew Ahrens 		digest->cd_length =
54145818ee1SMatthew Ahrens 		    CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen);
54245818ee1SMatthew Ahrens 		return (CRYPTO_BUFFER_TOO_SMALL);
54345818ee1SMatthew Ahrens 	}
54445818ee1SMatthew Ahrens 
54545818ee1SMatthew Ahrens 	error = skein_update(ctx, data, req);
54645818ee1SMatthew Ahrens 	if (error != CRYPTO_SUCCESS) {
54745818ee1SMatthew Ahrens 		bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
54845818ee1SMatthew Ahrens 		kmem_free(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
54945818ee1SMatthew Ahrens 		SKEIN_CTX_LVALUE(ctx) = NULL;
55045818ee1SMatthew Ahrens 		digest->cd_length = 0;
55145818ee1SMatthew Ahrens 		return (error);
55245818ee1SMatthew Ahrens 	}
55345818ee1SMatthew Ahrens 	error = skein_final(ctx, digest, req);
55445818ee1SMatthew Ahrens 
55545818ee1SMatthew Ahrens 	return (error);
55645818ee1SMatthew Ahrens }
55745818ee1SMatthew Ahrens 
55845818ee1SMatthew Ahrens /*
55945818ee1SMatthew Ahrens  * Performs a skein Update with the input message in `data' (successive calls
56045818ee1SMatthew Ahrens  * can push more data). This is used both for digest and MAC operation.
56145818ee1SMatthew Ahrens  * Supported input data formats are raw, uio and mblk.
56245818ee1SMatthew Ahrens  */
56345818ee1SMatthew Ahrens /*ARGSUSED*/
56445818ee1SMatthew Ahrens static int
skein_update(crypto_ctx_t * ctx,crypto_data_t * data,crypto_req_handle_t req)56545818ee1SMatthew Ahrens skein_update(crypto_ctx_t *ctx, crypto_data_t *data, crypto_req_handle_t req)
56645818ee1SMatthew Ahrens {
56745818ee1SMatthew Ahrens 	int error = CRYPTO_SUCCESS;
56845818ee1SMatthew Ahrens 
56945818ee1SMatthew Ahrens 	ASSERT(SKEIN_CTX(ctx) != NULL);
57045818ee1SMatthew Ahrens 
57145818ee1SMatthew Ahrens 	switch (data->cd_format) {
57245818ee1SMatthew Ahrens 	case CRYPTO_DATA_RAW:
57345818ee1SMatthew Ahrens 		SKEIN_OP(SKEIN_CTX(ctx), Update,
57445818ee1SMatthew Ahrens 		    (uint8_t *)data->cd_raw.iov_base + data->cd_offset,
57545818ee1SMatthew Ahrens 		    data->cd_length);
57645818ee1SMatthew Ahrens 		break;
57745818ee1SMatthew Ahrens 	case CRYPTO_DATA_UIO:
57845818ee1SMatthew Ahrens 		error = skein_digest_update_uio(SKEIN_CTX(ctx), data);
57945818ee1SMatthew Ahrens 		break;
58045818ee1SMatthew Ahrens 	case CRYPTO_DATA_MBLK:
58145818ee1SMatthew Ahrens 		error = skein_digest_update_mblk(SKEIN_CTX(ctx), data);
58245818ee1SMatthew Ahrens 		break;
58345818ee1SMatthew Ahrens 	default:
58445818ee1SMatthew Ahrens 		error = CRYPTO_ARGUMENTS_BAD;
58545818ee1SMatthew Ahrens 	}
58645818ee1SMatthew Ahrens 
58745818ee1SMatthew Ahrens 	return (error);
58845818ee1SMatthew Ahrens }
58945818ee1SMatthew Ahrens 
59045818ee1SMatthew Ahrens /*
59145818ee1SMatthew Ahrens  * Performs a skein Final, writing the output to `digest'. This is used both
59245818ee1SMatthew Ahrens  * for digest and MAC operation.
59345818ee1SMatthew Ahrens  * Supported output digest formats are raw, uio and mblk.
59445818ee1SMatthew Ahrens  */
59545818ee1SMatthew Ahrens /*ARGSUSED*/
59645818ee1SMatthew Ahrens static int
skein_final(crypto_ctx_t * ctx,crypto_data_t * digest,crypto_req_handle_t req)59745818ee1SMatthew Ahrens skein_final(crypto_ctx_t *ctx, crypto_data_t *digest, crypto_req_handle_t req)
59845818ee1SMatthew Ahrens {
59945818ee1SMatthew Ahrens 	int error = CRYPTO_SUCCESS;
60045818ee1SMatthew Ahrens 
60145818ee1SMatthew Ahrens 	ASSERT(SKEIN_CTX(ctx) != NULL);
60245818ee1SMatthew Ahrens 
60345818ee1SMatthew Ahrens 	if (digest->cd_length <
60445818ee1SMatthew Ahrens 	    CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen)) {
60545818ee1SMatthew Ahrens 		digest->cd_length =
60645818ee1SMatthew Ahrens 		    CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen);
60745818ee1SMatthew Ahrens 		return (CRYPTO_BUFFER_TOO_SMALL);
60845818ee1SMatthew Ahrens 	}
60945818ee1SMatthew Ahrens 
61045818ee1SMatthew Ahrens 	switch (digest->cd_format) {
61145818ee1SMatthew Ahrens 	case CRYPTO_DATA_RAW:
61245818ee1SMatthew Ahrens 		SKEIN_OP(SKEIN_CTX(ctx), Final,
61345818ee1SMatthew Ahrens 		    (uint8_t *)digest->cd_raw.iov_base + digest->cd_offset);
61445818ee1SMatthew Ahrens 		break;
61545818ee1SMatthew Ahrens 	case CRYPTO_DATA_UIO:
61645818ee1SMatthew Ahrens 		error = skein_digest_final_uio(SKEIN_CTX(ctx), digest, req);
61745818ee1SMatthew Ahrens 		break;
61845818ee1SMatthew Ahrens 	case CRYPTO_DATA_MBLK:
61945818ee1SMatthew Ahrens 		error = skein_digest_final_mblk(SKEIN_CTX(ctx), digest, req);
62045818ee1SMatthew Ahrens 		break;
62145818ee1SMatthew Ahrens 	default:
62245818ee1SMatthew Ahrens 		error = CRYPTO_ARGUMENTS_BAD;
62345818ee1SMatthew Ahrens 	}
62445818ee1SMatthew Ahrens 
62545818ee1SMatthew Ahrens 	if (error == CRYPTO_SUCCESS)
62645818ee1SMatthew Ahrens 		digest->cd_length =
62745818ee1SMatthew Ahrens 		    CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen);
62845818ee1SMatthew Ahrens 	else
62945818ee1SMatthew Ahrens 		digest->cd_length = 0;
63045818ee1SMatthew Ahrens 
63145818ee1SMatthew Ahrens 	bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
63245818ee1SMatthew Ahrens 	kmem_free(SKEIN_CTX(ctx), sizeof (*(SKEIN_CTX(ctx))));
63345818ee1SMatthew Ahrens 	SKEIN_CTX_LVALUE(ctx) = NULL;
63445818ee1SMatthew Ahrens 
63545818ee1SMatthew Ahrens 	return (error);
63645818ee1SMatthew Ahrens }
63745818ee1SMatthew Ahrens 
63845818ee1SMatthew Ahrens /*
63945818ee1SMatthew Ahrens  * Performs a full skein digest computation in a single call, configuring the
64045818ee1SMatthew Ahrens  * algorithm according to `mechanism', reading the input to be digested from
64145818ee1SMatthew Ahrens  * `data' and writing the output to `digest'.
64245818ee1SMatthew Ahrens  * Supported input/output formats are raw, uio and mblk.
64345818ee1SMatthew Ahrens  */
64445818ee1SMatthew Ahrens /*ARGSUSED*/
64545818ee1SMatthew Ahrens static int
skein_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)64645818ee1SMatthew Ahrens skein_digest_atomic(crypto_provider_handle_t provider,
64745818ee1SMatthew Ahrens     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
64845818ee1SMatthew Ahrens     crypto_data_t *data, crypto_data_t *digest, crypto_req_handle_t req)
64945818ee1SMatthew Ahrens {
65045818ee1SMatthew Ahrens 	int		error;
65145818ee1SMatthew Ahrens 	skein_ctx_t	skein_ctx;
65245818ee1SMatthew Ahrens 	crypto_ctx_t	ctx;
65345818ee1SMatthew Ahrens 	SKEIN_CTX_LVALUE(&ctx) = &skein_ctx;
65445818ee1SMatthew Ahrens 
65545818ee1SMatthew Ahrens 	/* Init */
65645818ee1SMatthew Ahrens 	if (!VALID_SKEIN_DIGEST_MECH(mechanism->cm_type))
65745818ee1SMatthew Ahrens 		return (CRYPTO_MECHANISM_INVALID);
65845818ee1SMatthew Ahrens 	skein_ctx.sc_mech_type = mechanism->cm_type;
65945818ee1SMatthew Ahrens 	error = skein_get_digest_bitlen(mechanism, &skein_ctx.sc_digest_bitlen);
66045818ee1SMatthew Ahrens 	if (error != CRYPTO_SUCCESS)
66145818ee1SMatthew Ahrens 		goto out;
66245818ee1SMatthew Ahrens 	SKEIN_OP(&skein_ctx, Init, skein_ctx.sc_digest_bitlen);
66345818ee1SMatthew Ahrens 
66445818ee1SMatthew Ahrens 	if ((error = skein_update(&ctx, data, digest)) != CRYPTO_SUCCESS)
66545818ee1SMatthew Ahrens 		goto out;
66645818ee1SMatthew Ahrens 	if ((error = skein_final(&ctx, data, digest)) != CRYPTO_SUCCESS)
66745818ee1SMatthew Ahrens 		goto out;
66845818ee1SMatthew Ahrens 
66945818ee1SMatthew Ahrens out:
67045818ee1SMatthew Ahrens 	if (error == CRYPTO_SUCCESS)
67145818ee1SMatthew Ahrens 		digest->cd_length =
67245818ee1SMatthew Ahrens 		    CRYPTO_BITS2BYTES(skein_ctx.sc_digest_bitlen);
67345818ee1SMatthew Ahrens 	else
67445818ee1SMatthew Ahrens 		digest->cd_length = 0;
67545818ee1SMatthew Ahrens 	bzero(&skein_ctx, sizeof (skein_ctx));
67645818ee1SMatthew Ahrens 
67745818ee1SMatthew Ahrens 	return (error);
67845818ee1SMatthew Ahrens }
67945818ee1SMatthew Ahrens 
68045818ee1SMatthew Ahrens /*
68145818ee1SMatthew Ahrens  * Helper function that builds a Skein MAC context from the provided
68245818ee1SMatthew Ahrens  * mechanism and key.
68345818ee1SMatthew Ahrens  */
68445818ee1SMatthew Ahrens static int
skein_mac_ctx_build(skein_ctx_t * ctx,crypto_mechanism_t * mechanism,crypto_key_t * key)68545818ee1SMatthew Ahrens skein_mac_ctx_build(skein_ctx_t *ctx, crypto_mechanism_t *mechanism,
68645818ee1SMatthew Ahrens     crypto_key_t *key)
68745818ee1SMatthew Ahrens {
68845818ee1SMatthew Ahrens 	int error;
68945818ee1SMatthew Ahrens 
69045818ee1SMatthew Ahrens 	if (!VALID_SKEIN_MAC_MECH(mechanism->cm_type))
69145818ee1SMatthew Ahrens 		return (CRYPTO_MECHANISM_INVALID);
69245818ee1SMatthew Ahrens 	if (key->ck_format != CRYPTO_KEY_RAW)
69345818ee1SMatthew Ahrens 		return (CRYPTO_ARGUMENTS_BAD);
69445818ee1SMatthew Ahrens 	ctx->sc_mech_type = mechanism->cm_type;
69545818ee1SMatthew Ahrens 	error = skein_get_digest_bitlen(mechanism, &ctx->sc_digest_bitlen);
69645818ee1SMatthew Ahrens 	if (error != CRYPTO_SUCCESS)
69745818ee1SMatthew Ahrens 		return (error);
69845818ee1SMatthew Ahrens 	SKEIN_OP(ctx, InitExt, ctx->sc_digest_bitlen, 0, key->ck_data,
69945818ee1SMatthew Ahrens 	    CRYPTO_BITS2BYTES(key->ck_length));
70045818ee1SMatthew Ahrens 
70145818ee1SMatthew Ahrens 	return (CRYPTO_SUCCESS);
70245818ee1SMatthew Ahrens }
70345818ee1SMatthew Ahrens 
70445818ee1SMatthew Ahrens /*
70545818ee1SMatthew Ahrens  * KCF software provide mac entry points.
70645818ee1SMatthew Ahrens  */
70745818ee1SMatthew Ahrens /*
70845818ee1SMatthew Ahrens  * Initializes a skein MAC context. You may pass a ctx_template, in which
70945818ee1SMatthew Ahrens  * case the template will be reused to make initialization more efficient.
71045818ee1SMatthew Ahrens  * Otherwise a new context will be constructed. The mechanism cm_type must
71145818ee1SMatthew Ahrens  * be one of SKEIN_*_MAC_MECH_INFO_TYPE. Same as in skein_digest_init, you
71245818ee1SMatthew Ahrens  * may pass a skein_param_t in cm_param to configure the length of the
71345818ee1SMatthew Ahrens  * digest. The key must be in raw format.
71445818ee1SMatthew Ahrens  */
71545818ee1SMatthew Ahrens static int
skein_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)71645818ee1SMatthew Ahrens skein_mac_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
71745818ee1SMatthew Ahrens     crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
71845818ee1SMatthew Ahrens     crypto_req_handle_t req)
71945818ee1SMatthew Ahrens {
72045818ee1SMatthew Ahrens 	int	error;
72145818ee1SMatthew Ahrens 
72245818ee1SMatthew Ahrens 	SKEIN_CTX_LVALUE(ctx) = kmem_alloc(sizeof (*SKEIN_CTX(ctx)),
72345818ee1SMatthew Ahrens 	    crypto_kmflag(req));
72445818ee1SMatthew Ahrens 	if (SKEIN_CTX(ctx) == NULL)
72545818ee1SMatthew Ahrens 		return (CRYPTO_HOST_MEMORY);
72645818ee1SMatthew Ahrens 
72745818ee1SMatthew Ahrens 	if (ctx_template != NULL) {
72845818ee1SMatthew Ahrens 		bcopy(ctx_template, SKEIN_CTX(ctx),
72945818ee1SMatthew Ahrens 		    sizeof (*SKEIN_CTX(ctx)));
73045818ee1SMatthew Ahrens 	} else {
73145818ee1SMatthew Ahrens 		error = skein_mac_ctx_build(SKEIN_CTX(ctx), mechanism, key);
73245818ee1SMatthew Ahrens 		if (error != CRYPTO_SUCCESS)
73345818ee1SMatthew Ahrens 			goto errout;
73445818ee1SMatthew Ahrens 	}
73545818ee1SMatthew Ahrens 
73645818ee1SMatthew Ahrens 	return (CRYPTO_SUCCESS);
73745818ee1SMatthew Ahrens errout:
73845818ee1SMatthew Ahrens 	bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
73945818ee1SMatthew Ahrens 	kmem_free(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
74045818ee1SMatthew Ahrens 	return (error);
74145818ee1SMatthew Ahrens }
74245818ee1SMatthew Ahrens 
74345818ee1SMatthew Ahrens /*
74445818ee1SMatthew Ahrens  * The MAC update and final calls are reused from the regular digest code.
74545818ee1SMatthew Ahrens  */
74645818ee1SMatthew Ahrens 
74745818ee1SMatthew Ahrens /*ARGSUSED*/
74845818ee1SMatthew Ahrens /*
74945818ee1SMatthew Ahrens  * Same as skein_digest_atomic, performs an atomic Skein MAC operation in
75045818ee1SMatthew Ahrens  * one step. All the same properties apply to the arguments of this
75145818ee1SMatthew Ahrens  * function as to those of the partial operations above.
75245818ee1SMatthew Ahrens  */
75345818ee1SMatthew Ahrens static int
skein_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)75445818ee1SMatthew Ahrens skein_mac_atomic(crypto_provider_handle_t provider,
75545818ee1SMatthew Ahrens     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
75645818ee1SMatthew Ahrens     crypto_key_t *key, crypto_data_t *data, crypto_data_t *mac,
75745818ee1SMatthew Ahrens     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
75845818ee1SMatthew Ahrens {
75945818ee1SMatthew Ahrens 	/* faux crypto context just for skein_digest_{update,final} */
76045818ee1SMatthew Ahrens 	int		error;
76145818ee1SMatthew Ahrens 	crypto_ctx_t	ctx;
76245818ee1SMatthew Ahrens 	skein_ctx_t	skein_ctx;
76345818ee1SMatthew Ahrens 	SKEIN_CTX_LVALUE(&ctx) = &skein_ctx;
76445818ee1SMatthew Ahrens 
76545818ee1SMatthew Ahrens 	if (ctx_template != NULL) {
76645818ee1SMatthew Ahrens 		bcopy(ctx_template, &skein_ctx, sizeof (skein_ctx));
76745818ee1SMatthew Ahrens 	} else {
76845818ee1SMatthew Ahrens 		error = skein_mac_ctx_build(&skein_ctx, mechanism, key);
76945818ee1SMatthew Ahrens 		if (error != CRYPTO_SUCCESS)
77045818ee1SMatthew Ahrens 			goto errout;
77145818ee1SMatthew Ahrens 	}
77245818ee1SMatthew Ahrens 
77345818ee1SMatthew Ahrens 	if ((error = skein_update(&ctx, data, req)) != CRYPTO_SUCCESS)
77445818ee1SMatthew Ahrens 		goto errout;
77545818ee1SMatthew Ahrens 	if ((error = skein_final(&ctx, mac, req)) != CRYPTO_SUCCESS)
77645818ee1SMatthew Ahrens 		goto errout;
77745818ee1SMatthew Ahrens 
77845818ee1SMatthew Ahrens 	return (CRYPTO_SUCCESS);
77945818ee1SMatthew Ahrens errout:
78045818ee1SMatthew Ahrens 	bzero(&skein_ctx, sizeof (skein_ctx));
78145818ee1SMatthew Ahrens 	return (error);
78245818ee1SMatthew Ahrens }
78345818ee1SMatthew Ahrens 
78445818ee1SMatthew Ahrens /*
78545818ee1SMatthew Ahrens  * KCF software provider context management entry points.
78645818ee1SMatthew Ahrens  */
78745818ee1SMatthew Ahrens 
78845818ee1SMatthew Ahrens /*
78945818ee1SMatthew Ahrens  * Constructs a context template for the Skein MAC algorithm. The same
79045818ee1SMatthew Ahrens  * properties apply to the arguments of this function as to those of
79145818ee1SMatthew Ahrens  * skein_mac_init.
79245818ee1SMatthew Ahrens  */
79345818ee1SMatthew Ahrens /*ARGSUSED*/
79445818ee1SMatthew Ahrens static int
skein_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)79545818ee1SMatthew Ahrens skein_create_ctx_template(crypto_provider_handle_t provider,
79645818ee1SMatthew Ahrens     crypto_mechanism_t *mechanism, crypto_key_t *key,
79745818ee1SMatthew Ahrens     crypto_spi_ctx_template_t *ctx_template, size_t *ctx_template_size,
79845818ee1SMatthew Ahrens     crypto_req_handle_t req)
79945818ee1SMatthew Ahrens {
80045818ee1SMatthew Ahrens 	int		error;
80145818ee1SMatthew Ahrens 	skein_ctx_t	*ctx_tmpl;
80245818ee1SMatthew Ahrens 
80345818ee1SMatthew Ahrens 	ctx_tmpl = kmem_alloc(sizeof (*ctx_tmpl), crypto_kmflag(req));
80445818ee1SMatthew Ahrens 	if (ctx_tmpl == NULL)
80545818ee1SMatthew Ahrens 		return (CRYPTO_HOST_MEMORY);
80645818ee1SMatthew Ahrens 	error = skein_mac_ctx_build(ctx_tmpl, mechanism, key);
80745818ee1SMatthew Ahrens 	if (error != CRYPTO_SUCCESS)
80845818ee1SMatthew Ahrens 		goto errout;
80945818ee1SMatthew Ahrens 	*ctx_template = ctx_tmpl;
81045818ee1SMatthew Ahrens 	*ctx_template_size = sizeof (*ctx_tmpl);
81145818ee1SMatthew Ahrens 
81245818ee1SMatthew Ahrens 	return (CRYPTO_SUCCESS);
81345818ee1SMatthew Ahrens errout:
81445818ee1SMatthew Ahrens 	bzero(ctx_tmpl, sizeof (*ctx_tmpl));
81545818ee1SMatthew Ahrens 	kmem_free(ctx_tmpl, sizeof (*ctx_tmpl));
81645818ee1SMatthew Ahrens 	return (error);
81745818ee1SMatthew Ahrens }
81845818ee1SMatthew Ahrens 
81945818ee1SMatthew Ahrens /*
82045818ee1SMatthew Ahrens  * Frees a skein context in a parent crypto context.
82145818ee1SMatthew Ahrens  */
82245818ee1SMatthew Ahrens static int
skein_free_context(crypto_ctx_t * ctx)82345818ee1SMatthew Ahrens skein_free_context(crypto_ctx_t *ctx)
82445818ee1SMatthew Ahrens {
82545818ee1SMatthew Ahrens 	if (SKEIN_CTX(ctx) != NULL) {
82645818ee1SMatthew Ahrens 		bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
82745818ee1SMatthew Ahrens 		kmem_free(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
82845818ee1SMatthew Ahrens 		SKEIN_CTX_LVALUE(ctx) = NULL;
82945818ee1SMatthew Ahrens 	}
83045818ee1SMatthew Ahrens 
83145818ee1SMatthew Ahrens 	return (CRYPTO_SUCCESS);
83245818ee1SMatthew Ahrens }
833