view usr/src/uts/common/crypto/io/dprov.c @ 4002:d12f36b7c388

PSARC/2007/139 Kernel Crypto support for MD4 6354305 Kernel Crypto support for MD4 6533491 libmd's MD4 produces invalid message digests in 64-bit mode 6539262 MD4/MD5/SHA1/SHA2 Final functions not consistent
author darrenm
date Tue, 10 Apr 2007 11:12:59 -0700
parents 5517729c4306
children 3b30e48bc1a6
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * Dummy Cryptographic Provider:
 *
 * This file implements a "dummy" cryptographic provider. It is implemented
 * as a pseudo device driver.
 *
 */

/*
 * This driver implements a KEF provider with the following capabilities:
 *
 * - registration/unregistration with KEF
 * - digest entry points
 * - mac entry points
 * - ctx management
 * - support for async requests
 * - cipher entry points
 * - dual entry points
 * - sign entry points
 * - verify entry points
 * - dual operations entry points
 * - dual cipher/mac operation entry points
 * - session management
 * - object management
 * - key management
 * - provider management
 *
 * In order to avoid duplicating the implementation of algorithms
 * provided by software providers, this pseudo driver acts as
 * a consumer of the framework. When invoking one of the framework's
 * entry points, the driver specifies the software provider to
 * be used for the operation.
 *
 * User management: we implement a PKCS#11 style provider which supports:
 * - one normal user with a PIN, and
 * - one SO user with a PIN.
 * These values are kept in the per-instance structure, and are initialized
 * with the provider management entry points.
 *
 */


#include <sys/types.h>
#include <sys/modctl.h>
#include <sys/conf.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/kmem.h>
#include <sys/errno.h>
#include <sys/ksynch.h>
#include <sys/file.h>
#include <sys/open.h>
#include <sys/cred.h>
#include <sys/model.h>
#include <sys/note.h>
#include <sys/random.h>
#include <sys/byteorder.h>
#include <sys/crypto/common.h>
#include <sys/crypto/spi.h>

#include <sys/taskq.h>
#include <sys/disp.h>
#include <sys/sysmacros.h>
#include <sys/crypto/impl.h>
#include <sys/crypto/sched_impl.h>

#include <sys/sha2.h>
#include <aes/aes_cbc_crypt.h>
#include <des/des_impl.h>
#include <blowfish/blowfish_impl.h>

/*
 * Debugging macros.
 */
#ifdef DEBUG
#define	D_INIT		0x00000001	/* _init/_fini/_info */
#define	D_ATTACH	0x00000002	/* attach/detach */
#define	D_DIGEST	0x00000010	/* digest entry points */
#define	D_MAC		0x00000020	/* mac entry points */
#define	D_CONTEXT	0x00000040	/* context entry points */
#define	D_CIPHER	0x00000080	/* cipher entry points */
#define	D_SIGN		0x00000100	/* sign entry points */
#define	D_VERIFY	0x00000200	/* verify entry points */
#define	D_SESSION	0x00000400	/* session management entry points */
#define	D_MGMT		0x00000800	/* provider management entry points */
#define	D_DUAL		0x00001000	/* dual ops */
#define	D_CIPHER_MAC	0x00002000	/* cipher/mac dual ops */
#define	D_OBJECT	0x00004000	/* object management */
#define	D_RANDOM	0x00008000	/* random number generation */
#define	D_KEY		0x00010000	/* key management */

static uint32_t dprov_debug = 0;

#define	DPROV_DEBUG(f, x)	if (dprov_debug & (f)) { (void) printf x; }
#define	DPROV_CALL(f, r, x)	if (dprov_debug & (f)) { (void) r x; }
#else /* DEBUG */
#define	DPROV_DEBUG(f, x)
#define	DPROV_CALL(f, r, x)
#endif /* DEBUG */

/*
 * DDI entry points.
 */
static int dprov_attach(dev_info_t *, ddi_attach_cmd_t);
static int dprov_detach(dev_info_t *, ddi_detach_cmd_t);
static int dprov_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);

/*
 * Module linkage.
 */
static struct cb_ops cbops = {
	nodev,			/* cb_open */
	nodev,			/* cb_close */
	nodev,			/* cb_strategy */
	nodev,			/* cb_print */
	nodev,			/* cb_dump */
	nodev,			/* cb_read */
	nodev,			/* cb_write */
	nodev,			/* cb_ioctl */
	nodev,			/* cb_devmap */
	nodev,			/* cb_mmap */
	nodev,			/* cb_segmap */
	nochpoll,		/* cb_chpoll */
	ddi_prop_op,		/* cb_prop_op */
	NULL,			/* cb_streamtab */
	D_MP,			/* cb_flag */
	CB_REV,			/* cb_rev */
	nodev,			/* cb_aread */
	nodev,			/* cb_awrite */
};

static struct dev_ops devops = {
	DEVO_REV,		/* devo_rev */
	0,			/* devo_refcnt */
	dprov_getinfo,		/* devo_getinfo */
	nulldev,		/* devo_identify */
	nulldev,		/* devo_probe */
	dprov_attach,		/* devo_attach */
	dprov_detach,		/* devo_detach */
	nodev,			/* devo_reset */
	&cbops,			/* devo_cb_ops */
	NULL,			/* devo_bus_ops */
	NULL,			/* devo_power */
};

static struct modldrv modldrv = {
	&mod_driverops,
	"Pseudo KCF Prov (drv) %I%",
	&devops
};

static struct modlcrypto modlcrypto = {
	&mod_cryptoops,
	"Pseudo KCF Prov (crypto) %I%"
};

static struct modlinkage modlinkage = {
	MODREV_1,
	&modldrv,
	&modlcrypto,
	NULL
};

/*
 * CSPI information (entry points, provider info, etc.)
 */

typedef enum dprov_mech_type {
	MD4_MECH_INFO_TYPE,		/* SUN_CKM_MD4 */

	MD5_MECH_INFO_TYPE,		/* SUN_CKM_MD5 */
	MD5_HMAC_MECH_INFO_TYPE,	/* SUN_CKM_MD5_HMAC */
	MD5_HMAC_GEN_MECH_INFO_TYPE,	/* SUN_CKM_MD5_HMAC_GENERAL */

	SHA1_HMAC_MECH_INFO_TYPE,	/* SUN_CKM_SHA1_HMAC */
	SHA1_HMAC_GEN_MECH_INFO_TYPE,	/* SUN_CKM_SHA1_HMAC_GENERAL */
	SHA1_MECH_INFO_TYPE,		/* SUN_CKM_SHA1 */

	SHA256_HMAC_MECH_INFO_TYPE,	/* SUN_CKM_SHA256_HMAC */
	SHA256_HMAC_GEN_MECH_INFO_TYPE,	/* SUN_CKM_SHA256_HMAC_GENERAL */
	SHA256_MECH_INFO_TYPE,		/* SUN_CKM_SHA256 */
	SHA384_HMAC_MECH_INFO_TYPE,	/* SUN_CKM_SHA384_HMAC */
	SHA384_HMAC_GEN_MECH_INFO_TYPE,	/* SUN_CKM_SHA384_HMAC_GENERAL */
	SHA384_MECH_INFO_TYPE,		/* SUN_CKM_SHA384 */
	SHA512_HMAC_MECH_INFO_TYPE,	/* SUN_CKM_SHA512_HMAC */
	SHA512_HMAC_GEN_MECH_INFO_TYPE,	/* SUN_CKM_SHA512_HMAC_GENERAL */
	SHA512_MECH_INFO_TYPE,		/* SUN_CKM_SHA512 */

	DES_CBC_MECH_INFO_TYPE,		/* SUN_CKM_DES_CBC */
	DES3_CBC_MECH_INFO_TYPE,	/* SUN_CKM_DES3_CBC */
	DES_ECB_MECH_INFO_TYPE,		/* SUN_CKM_DES_ECB */
	DES3_ECB_MECH_INFO_TYPE,	/* SUN_CKM_DES3_ECB */

	BLOWFISH_CBC_MECH_INFO_TYPE,	/* SUN_CKM_BLOWFISH_CBC */
	BLOWFISH_ECB_MECH_INFO_TYPE,	/* SUN_CKM_BLOWFISH_ECB */
	AES_CBC_MECH_INFO_TYPE,		/* SUN_CKM_AES_CBC */
	AES_ECB_MECH_INFO_TYPE,		/* SUN_CKM_AES_ECB */
	AES_CTR_MECH_INFO_TYPE,		/* SUN_CKM_AES_CTR */
	RC4_MECH_INFO_TYPE,		/* SUN_CKM_RC4 */
	RSA_PKCS_MECH_INFO_TYPE,	/* SUN_CKM_RSA_PKCS */
	RSA_X_509_MECH_INFO_TYPE,	/* SUN_CKM_RSA_X_509 */
	MD5_RSA_PKCS_MECH_INFO_TYPE,	/* SUN_CKM_MD5_RSA_PKCS */
	SHA1_RSA_PKCS_MECH_INFO_TYPE,	/* SUN_CKM_SHA1_RSA_PKCS */
	SHA256_RSA_PKCS_MECH_INFO_TYPE,	/* SUN_CKM_SHA256_RSA_PKCS */
	SHA384_RSA_PKCS_MECH_INFO_TYPE,	/* SUN_CKM_SHA384_RSA_PKCS */
	SHA512_RSA_PKCS_MECH_INFO_TYPE,	/* SUN_CKM_SHA512_RSA_PKCS */
	MD5_KEY_DERIVATION_MECH_INFO_TYPE, /* SUN_CKM_MD5_KEY_DERIVATION */
	SHA1_KEY_DERIVATION_MECH_INFO_TYPE, /* SUN_CKM_SHA1_KEY_DERIVATION */
	/* SUN_CKM_SHA256_KEY_DERIVATION */
	SHA256_KEY_DERIVATION_MECH_INFO_TYPE,
	/* SUN_CKM_SHA384_KEY_DERIVATION */
	SHA384_KEY_DERIVATION_MECH_INFO_TYPE,
	/* SUN_CKM_SHA512_KEY_DERIVATION */
	SHA512_KEY_DERIVATION_MECH_INFO_TYPE,
	DES_KEY_GEN_MECH_INFO_TYPE,	/* SUN_CKM_DES_KEY_GEN */
	DES3_KEY_GEN_MECH_INFO_TYPE,	/* SUN_CKM_DES3_KEY_GEN */
	AES_KEY_GEN_MECH_INFO_TYPE,	/* SUN_CKM_AES_KEY_GEN */
	BLOWFISH_KEY_GEN_MECH_INFO_TYPE,	/* SUN_CKM_BLOWFISH_KEY_GEN */
	RC4_KEY_GEN_MECH_INFO_TYPE,	/* SUN_CKM_RC4_KEY_GEN */
	RSA_PKCS_KEY_PAIR_GEN_MECH_INFO_TYPE /* SUN_CKM_RSA_PKCS_KEY_PAIR_GEN */
} dprov_mech_type_t;

/*
 * Mechanism info structure passed to KCF during registration.
 */
#define	MD5_DIGEST_LEN		16	/* MD5 digest size */
#define	MD5_HMAC_BLOCK_SIZE	64	/* MD5-HMAC block size */
#define	MD5_HMAC_MIN_KEY_LEN	8	/* MD5-HMAC min key length in bits */
#define	MD5_HMAC_MAX_KEY_LEN	INT_MAX	/* MD5-HMAC max key length in bits */

#define	SHA1_DIGEST_LEN		20	/* SHA1 digest size */
#define	SHA1_HMAC_BLOCK_SIZE	64	/* SHA1-HMAC block size */
#define	SHA1_HMAC_MIN_KEY_LEN	8	/* SHA1-HMAC min key length in bits */
#define	SHA1_HMAC_MAX_KEY_LEN	INT_MAX /* SHA1-HMAC max key length in bits */

#define	DES_KEY_LEN		8	/* DES key length in bytes */
#define	DES3_KEY_LEN		24	/* DES3 key length in bytes */

#define	BLOWFISH_MIN_KEY_LEN	32	/* Blowfish min key length in bits */
#define	BLOWFISH_MAX_KEY_LEN	448	/* Blowfish max key length in bits */

#define	AES_MIN_KEY_LEN		16	/* AES min key length in bytes */
#define	AES_MAX_KEY_LEN		32	/* AES max key length in bytes */

#define	ARCFOUR_MIN_KEY_BITS	40	/* RC4 min supported key size */
#define	ARCFOUR_MAX_KEY_BITS	2048	/* RC4 max supported key size */

#define	RSA_MIN_KEY_LEN		256	/* RSA min key length in bits */
#define	RSA_MAX_KEY_LEN		4096	/* RSA max key length in bits */

#define	DPROV_CKM_MD5_KEY_DERIVATION	"CKM_MD5_KEY_DERIVATION"
#define	DPROV_CKM_SHA1_KEY_DERIVATION	"CKM_SHA1_KEY_DERIVATION"
#define	DPROV_CKM_SHA256_KEY_DERIVATION	"CKM_SHA256_KEY_DERIVATION"
#define	DPROV_CKM_SHA384_KEY_DERIVATION	"CKM_SHA384_KEY_DERIVATION"
#define	DPROV_CKM_SHA512_KEY_DERIVATION	"CKM_SHA512_KEY_DERIVATION"
#define	DPROV_CKM_DES_KEY_GEN		"CKM_DES_KEY_GEN"
#define	DPROV_CKM_DES3_KEY_GEN		"CKM_DES3_KEY_GEN"
#define	DPROV_CKM_AES_KEY_GEN		"CKM_AES_KEY_GEN"
#define	DPROV_CKM_BLOWFISH_KEY_GEN	"CKM_BLOWFISH_KEY_GEN"
#define	DPROV_CKM_RC4_KEY_GEN		"CKM_RC4_KEY_GEN"
#define	DPROV_CKM_RSA_PKCS_KEY_PAIR_GEN	"CKM_RSA_PKCS_KEY_PAIR_GEN"

static crypto_mech_info_t dprov_mech_info_tab[] = {
	/* MD4 */
	{SUN_CKM_MD4, MD4_MECH_INFO_TYPE,
	    CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC, 0, 0,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* MD5 */
	{SUN_CKM_MD5, MD5_MECH_INFO_TYPE,
	    CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC, 0, 0,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* MD5-HMAC */
	{SUN_CKM_MD5_HMAC, MD5_HMAC_MECH_INFO_TYPE,
	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC |
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC |
	    CRYPTO_FG_ENCRYPT_MAC | CRYPTO_FG_MAC_DECRYPT |
	    CRYPTO_FG_ENCRYPT_MAC_ATOMIC | CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    MD5_HMAC_MIN_KEY_LEN, MD5_HMAC_MAX_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* MD5-HMAC GENERAL */
	{SUN_CKM_MD5_HMAC_GENERAL, MD5_HMAC_GEN_MECH_INFO_TYPE,
	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC |
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC |
	    CRYPTO_FG_ENCRYPT_MAC | CRYPTO_FG_MAC_DECRYPT |
	    CRYPTO_FG_ENCRYPT_MAC_ATOMIC | CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    MD5_HMAC_MIN_KEY_LEN, MD5_HMAC_MAX_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA1 */
	{SUN_CKM_SHA1, SHA1_MECH_INFO_TYPE,
	    CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC, 0, 0,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA1-HMAC */
	{SUN_CKM_SHA1_HMAC, SHA1_HMAC_MECH_INFO_TYPE,
	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC |
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC |
	    CRYPTO_FG_ENCRYPT_MAC | CRYPTO_FG_MAC_DECRYPT |
	    CRYPTO_FG_ENCRYPT_MAC_ATOMIC | CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    SHA1_HMAC_MIN_KEY_LEN, SHA1_HMAC_MAX_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA1-HMAC GENERAL */
	{SUN_CKM_SHA1_HMAC_GENERAL, SHA1_HMAC_GEN_MECH_INFO_TYPE,
	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC |
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC |
	    CRYPTO_FG_ENCRYPT_MAC | CRYPTO_FG_MAC_DECRYPT |
	    CRYPTO_FG_ENCRYPT_MAC_ATOMIC | CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    SHA1_HMAC_MIN_KEY_LEN, SHA1_HMAC_MAX_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA256 */
	{SUN_CKM_SHA256, SHA256_MECH_INFO_TYPE,
	    CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC, 0, 0,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA256-HMAC */
	{SUN_CKM_SHA256_HMAC, SHA256_HMAC_MECH_INFO_TYPE,
	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC |
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC |
	    CRYPTO_FG_ENCRYPT_MAC | CRYPTO_FG_MAC_DECRYPT |
	    CRYPTO_FG_ENCRYPT_MAC_ATOMIC | CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    SHA2_HMAC_MIN_KEY_LEN, SHA2_HMAC_MAX_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA256-HMAC GENERAL */
	{SUN_CKM_SHA256_HMAC_GENERAL, SHA256_HMAC_GEN_MECH_INFO_TYPE,
	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC |
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC |
	    CRYPTO_FG_ENCRYPT_MAC | CRYPTO_FG_MAC_DECRYPT |
	    CRYPTO_FG_ENCRYPT_MAC_ATOMIC | CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    SHA2_HMAC_MIN_KEY_LEN, SHA2_HMAC_MAX_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA384 */
	{SUN_CKM_SHA384, SHA384_MECH_INFO_TYPE,
	    CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC, 0, 0,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA384-HMAC */
	{SUN_CKM_SHA384_HMAC, SHA384_HMAC_MECH_INFO_TYPE,
	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC |
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC |
	    CRYPTO_FG_ENCRYPT_MAC | CRYPTO_FG_MAC_DECRYPT |
	    CRYPTO_FG_ENCRYPT_MAC_ATOMIC | CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    SHA2_HMAC_MIN_KEY_LEN, SHA2_HMAC_MAX_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA384-HMAC GENERAL */
	{SUN_CKM_SHA384_HMAC_GENERAL, SHA384_HMAC_GEN_MECH_INFO_TYPE,
	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC |
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC |
	    CRYPTO_FG_ENCRYPT_MAC | CRYPTO_FG_MAC_DECRYPT |
	    CRYPTO_FG_ENCRYPT_MAC_ATOMIC | CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    SHA2_HMAC_MIN_KEY_LEN, SHA2_HMAC_MAX_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA512 */
	{SUN_CKM_SHA512, SHA512_MECH_INFO_TYPE,
	    CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC, 0, 0,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA512-HMAC */
	{SUN_CKM_SHA512_HMAC, SHA512_HMAC_MECH_INFO_TYPE,
	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC |
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC |
	    CRYPTO_FG_ENCRYPT_MAC | CRYPTO_FG_MAC_DECRYPT |
	    CRYPTO_FG_ENCRYPT_MAC_ATOMIC | CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    SHA2_HMAC_MIN_KEY_LEN, SHA2_HMAC_MAX_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA512-HMAC GENERAL */
	{SUN_CKM_SHA512_HMAC_GENERAL, SHA512_HMAC_GEN_MECH_INFO_TYPE,
	    CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC |
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC |
	    CRYPTO_FG_ENCRYPT_MAC | CRYPTO_FG_MAC_DECRYPT |
	    CRYPTO_FG_ENCRYPT_MAC_ATOMIC | CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    SHA2_HMAC_MIN_KEY_LEN, SHA2_HMAC_MAX_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* DES-CBC */
	{SUN_CKM_DES_CBC, DES_CBC_MECH_INFO_TYPE,
	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_ENCRYPT_MAC |
	    CRYPTO_FG_MAC_DECRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
	    CRYPTO_FG_DECRYPT_ATOMIC | CRYPTO_FG_ENCRYPT_MAC_ATOMIC |
	    CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    DES_KEY_LEN, DES_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BYTES},
	/* DES3-CBC */
	{SUN_CKM_DES3_CBC, DES3_CBC_MECH_INFO_TYPE,
	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_ENCRYPT_MAC |
	    CRYPTO_FG_MAC_DECRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
	    CRYPTO_FG_DECRYPT_ATOMIC | CRYPTO_FG_ENCRYPT_MAC_ATOMIC |
	    CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    DES3_KEY_LEN, DES3_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BYTES},
	/* DES-ECB */
	{SUN_CKM_DES_ECB, DES_ECB_MECH_INFO_TYPE,
	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_ENCRYPT_MAC |
	    CRYPTO_FG_MAC_DECRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
	    CRYPTO_FG_DECRYPT_ATOMIC | CRYPTO_FG_ENCRYPT_MAC_ATOMIC |
	    CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    DES_KEY_LEN, DES_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BYTES},
	/* DES3-ECB */
	{SUN_CKM_DES3_ECB, DES3_ECB_MECH_INFO_TYPE,
	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_ENCRYPT_MAC |
	    CRYPTO_FG_MAC_DECRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
	    CRYPTO_FG_DECRYPT_ATOMIC | CRYPTO_FG_ENCRYPT_MAC_ATOMIC |
	    CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    DES3_KEY_LEN, DES3_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BYTES},
	/* BLOWFISH-CBC */
	{SUN_CKM_BLOWFISH_CBC, BLOWFISH_CBC_MECH_INFO_TYPE,
	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_ENCRYPT_MAC |
	    CRYPTO_FG_MAC_DECRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
	    CRYPTO_FG_DECRYPT_ATOMIC | CRYPTO_FG_ENCRYPT_MAC_ATOMIC |
	    CRYPTO_FG_MAC_DECRYPT_ATOMIC, BLOWFISH_MIN_KEY_LEN,
	    BLOWFISH_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* BLOWFISH-ECB */
	{SUN_CKM_BLOWFISH_ECB, BLOWFISH_ECB_MECH_INFO_TYPE,
	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_ENCRYPT_MAC |
	    CRYPTO_FG_MAC_DECRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
	    CRYPTO_FG_DECRYPT_ATOMIC | CRYPTO_FG_ENCRYPT_MAC_ATOMIC |
	    CRYPTO_FG_MAC_DECRYPT_ATOMIC, BLOWFISH_MIN_KEY_LEN,
	    BLOWFISH_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* AES-CBC */
	{SUN_CKM_AES_CBC, AES_CBC_MECH_INFO_TYPE,
	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_ENCRYPT_MAC |
	    CRYPTO_FG_MAC_DECRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
	    CRYPTO_FG_DECRYPT_ATOMIC | CRYPTO_FG_ENCRYPT_MAC_ATOMIC |
	    CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    AES_MIN_KEY_LEN, AES_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BYTES},
	/* AES-ECB */
	{SUN_CKM_AES_ECB, AES_ECB_MECH_INFO_TYPE,
	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_ENCRYPT_MAC |
	    CRYPTO_FG_MAC_DECRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
	    CRYPTO_FG_DECRYPT_ATOMIC | CRYPTO_FG_ENCRYPT_MAC_ATOMIC |
	    CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    AES_MIN_KEY_LEN, AES_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BYTES},
	/* AES-CTR */
	{SUN_CKM_AES_CTR, AES_CTR_MECH_INFO_TYPE,
	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_ENCRYPT_MAC |
	    CRYPTO_FG_MAC_DECRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
	    CRYPTO_FG_DECRYPT_ATOMIC | CRYPTO_FG_ENCRYPT_MAC_ATOMIC |
	    CRYPTO_FG_MAC_DECRYPT_ATOMIC,
	    AES_MIN_KEY_LEN, AES_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BYTES},
	/* RC4 */
	{SUN_CKM_RC4, RC4_MECH_INFO_TYPE,
	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
	    CRYPTO_FG_DECRYPT | CRYPTO_FG_DECRYPT_ATOMIC,
	    ARCFOUR_MIN_KEY_BITS, ARCFOUR_MAX_KEY_BITS,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS | CRYPTO_CAN_SHARE_OPSTATE},
	/* RSA_PKCS */
	{SUN_CKM_RSA_PKCS, RSA_PKCS_MECH_INFO_TYPE,
	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
	    CRYPTO_FG_DECRYPT | CRYPTO_FG_DECRYPT_ATOMIC |
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC |
	    CRYPTO_FG_SIGN_RECOVER | CRYPTO_FG_SIGN_RECOVER_ATOMIC |
	    CRYPTO_FG_VERIFY_RECOVER | CRYPTO_FG_VERIFY_RECOVER_ATOMIC,
	    RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* RSA_X_509 */
	{SUN_CKM_RSA_X_509, RSA_X_509_MECH_INFO_TYPE,
	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
	    CRYPTO_FG_DECRYPT | CRYPTO_FG_DECRYPT_ATOMIC |
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC |
	    CRYPTO_FG_SIGN_RECOVER | CRYPTO_FG_SIGN_RECOVER_ATOMIC |
	    CRYPTO_FG_VERIFY_RECOVER | CRYPTO_FG_VERIFY_RECOVER_ATOMIC,
	    RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* MD5_RSA_PKCS */
	{SUN_CKM_MD5_RSA_PKCS, MD5_RSA_PKCS_MECH_INFO_TYPE,
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC,
	    RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA1_RSA_PKCS */
	{SUN_CKM_SHA1_RSA_PKCS, SHA1_RSA_PKCS_MECH_INFO_TYPE,
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC,
	    RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA256_RSA_PKCS */
	{SUN_CKM_SHA256_RSA_PKCS, SHA256_RSA_PKCS_MECH_INFO_TYPE,
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC,
	    RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA384_RSA_PKCS */
	{SUN_CKM_SHA384_RSA_PKCS, SHA384_RSA_PKCS_MECH_INFO_TYPE,
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC,
	    RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA512_RSA_PKCS */
	{SUN_CKM_SHA512_RSA_PKCS, SHA512_RSA_PKCS_MECH_INFO_TYPE,
	    CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
	    CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC,
	    RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* MD5_KEY_DERIVATION */
	{DPROV_CKM_MD5_KEY_DERIVATION, MD5_KEY_DERIVATION_MECH_INFO_TYPE,
	    CRYPTO_FG_DERIVE, 0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA1_KEY_DERIVATION */
	{DPROV_CKM_SHA1_KEY_DERIVATION, SHA1_KEY_DERIVATION_MECH_INFO_TYPE,
	    CRYPTO_FG_DERIVE, 0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA256_KEY_DERIVATION */
	{DPROV_CKM_SHA256_KEY_DERIVATION, SHA256_KEY_DERIVATION_MECH_INFO_TYPE,
	    CRYPTO_FG_DERIVE, 0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA384_KEY_DERIVATION */
	{DPROV_CKM_SHA384_KEY_DERIVATION, SHA384_KEY_DERIVATION_MECH_INFO_TYPE,
	    CRYPTO_FG_DERIVE, 0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* SHA512_KEY_DERIVATION */
	{DPROV_CKM_SHA512_KEY_DERIVATION, SHA512_KEY_DERIVATION_MECH_INFO_TYPE,
	    CRYPTO_FG_DERIVE, 0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* DES_KEY_GENERATION */
	{DPROV_CKM_DES_KEY_GEN, DES_KEY_GEN_MECH_INFO_TYPE,
	    CRYPTO_FG_GENERATE, DES_KEY_LEN, DES_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BYTES},
	/* DES3_KEY_GENERATION */
	{DPROV_CKM_DES3_KEY_GEN, DES3_KEY_GEN_MECH_INFO_TYPE,
	    CRYPTO_FG_GENERATE, DES3_KEY_LEN, DES3_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BYTES},
	/* AES_KEY_GENERATION */
	{DPROV_CKM_AES_KEY_GEN, AES_KEY_GEN_MECH_INFO_TYPE,
	    CRYPTO_FG_GENERATE, AES_MIN_KEY_LEN, AES_MAX_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BYTES},
	/* BLOWFISH_KEY_GENERATION */
	{DPROV_CKM_BLOWFISH_KEY_GEN, BLOWFISH_KEY_GEN_MECH_INFO_TYPE,
	    CRYPTO_FG_GENERATE, BLOWFISH_MIN_KEY_LEN, BLOWFISH_MAX_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BYTES},
	/* RC4_KEY_GENERATION */
	{DPROV_CKM_RC4_KEY_GEN, RC4_KEY_GEN_MECH_INFO_TYPE,
	    CRYPTO_FG_GENERATE, ARCFOUR_MIN_KEY_BITS, ARCFOUR_MAX_KEY_BITS,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
	/* RSA_PKCS_KEY_PAIR_GEN */
	{DPROV_CKM_RSA_PKCS_KEY_PAIR_GEN, RSA_PKCS_KEY_PAIR_GEN_MECH_INFO_TYPE,
	    CRYPTO_FG_GENERATE_KEY_PAIR, RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN,
	    CRYPTO_KEYSIZE_UNIT_IN_BITS}
};

static void dprov_provider_status(crypto_provider_handle_t, uint_t *);

static crypto_control_ops_t dprov_control_ops = {
	dprov_provider_status
};

#define	DPROV_MANUFACTURER	"SUNW                            "
#define	DPROV_MODEL		"dprov           "
#define	DPROV_ALLSPACES		"                "

static int dprov_digest_init(crypto_ctx_t *, crypto_mechanism_t *,
    crypto_req_handle_t);
static int dprov_digest(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_digest_update(crypto_ctx_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_digest_key(crypto_ctx_t *, crypto_key_t *,
    crypto_req_handle_t);
static int dprov_digest_final(crypto_ctx_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_digest_atomic(crypto_provider_handle_t, crypto_session_id_t,
    crypto_mechanism_t *, crypto_data_t *, crypto_data_t *,
    crypto_req_handle_t);

static crypto_digest_ops_t dprov_digest_ops = {
	dprov_digest_init,
	dprov_digest,
	dprov_digest_update,
	dprov_digest_key,
	dprov_digest_final,
	dprov_digest_atomic
};

static int dprov_mac_init(crypto_ctx_t *, crypto_mechanism_t *, crypto_key_t *,
    crypto_spi_ctx_template_t, crypto_req_handle_t);
static int dprov_mac(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_mac_update(crypto_ctx_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_mac_final(crypto_ctx_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_mac_atomic(crypto_provider_handle_t, crypto_session_id_t,
    crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
    crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
static int dprov_mac_verify_atomic(crypto_provider_handle_t,
    crypto_session_id_t, crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
    crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);

static crypto_mac_ops_t dprov_mac_ops = {
	dprov_mac_init,
	dprov_mac,
	dprov_mac_update,
	dprov_mac_final,
	dprov_mac_atomic,
	dprov_mac_verify_atomic
};

static int dprov_encrypt_init(crypto_ctx_t *, crypto_mechanism_t *,
    crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
static int dprov_encrypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_encrypt_update(crypto_ctx_t *, crypto_data_t *,
    crypto_data_t *, crypto_req_handle_t);
static int dprov_encrypt_final(crypto_ctx_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_encrypt_atomic(crypto_provider_handle_t, crypto_session_id_t,
    crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
    crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);

static int dprov_decrypt_init(crypto_ctx_t *, crypto_mechanism_t *,
    crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
static int dprov_decrypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_decrypt_update(crypto_ctx_t *, crypto_data_t *,
    crypto_data_t *, crypto_req_handle_t);
static int dprov_decrypt_final(crypto_ctx_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_decrypt_atomic(crypto_provider_handle_t, crypto_session_id_t,
    crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
    crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);

static crypto_cipher_ops_t dprov_cipher_ops = {
	dprov_encrypt_init,
	dprov_encrypt,
	dprov_encrypt_update,
	dprov_encrypt_final,
	dprov_encrypt_atomic,
	dprov_decrypt_init,
	dprov_decrypt,
	dprov_decrypt_update,
	dprov_decrypt_final,
	dprov_decrypt_atomic
};

static int dprov_sign_init(crypto_ctx_t *, crypto_mechanism_t *, crypto_key_t *,
    crypto_spi_ctx_template_t, crypto_req_handle_t);
static int dprov_sign(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_sign_update(crypto_ctx_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_sign_final(crypto_ctx_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_sign_atomic(crypto_provider_handle_t, crypto_session_id_t,
    crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
    crypto_spi_ctx_template_t, crypto_req_handle_t);
static int dprov_sign_recover_init(crypto_ctx_t *, crypto_mechanism_t *,
    crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
static int dprov_sign_recover(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_sign_recover_atomic(crypto_provider_handle_t,
    crypto_session_id_t, crypto_mechanism_t *, crypto_key_t *,
    crypto_data_t *, crypto_data_t *, crypto_spi_ctx_template_t,
    crypto_req_handle_t);

static crypto_sign_ops_t dprov_sign_ops = {
	dprov_sign_init,
	dprov_sign,
	dprov_sign_update,
	dprov_sign_final,
	dprov_sign_atomic,
	dprov_sign_recover_init,
	dprov_sign_recover,
	dprov_sign_recover_atomic
};

static int dprov_verify_init(crypto_ctx_t *, crypto_mechanism_t *,
    crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
static int dprov_verify(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_verify_update(crypto_ctx_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_verify_final(crypto_ctx_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_verify_atomic(crypto_provider_handle_t, crypto_session_id_t,
    crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
    crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
static int dprov_verify_recover_init(crypto_ctx_t *, crypto_mechanism_t *,
    crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
static int dprov_verify_recover(crypto_ctx_t *, crypto_data_t *,
    crypto_data_t *, crypto_req_handle_t);
static int dprov_verify_recover_atomic(crypto_provider_handle_t,
    crypto_session_id_t, crypto_mechanism_t *, crypto_key_t *,
    crypto_data_t *, crypto_data_t *, crypto_spi_ctx_template_t,
    crypto_req_handle_t);

static crypto_verify_ops_t dprov_verify_ops = {
	dprov_verify_init,
	dprov_verify,
	dprov_verify_update,
	dprov_verify_final,
	dprov_verify_atomic,
	dprov_verify_recover_init,
	dprov_verify_recover,
	dprov_verify_recover_atomic
};

static int dprov_digest_encrypt_update(crypto_ctx_t *, crypto_ctx_t *,
    crypto_data_t *, crypto_data_t *, crypto_req_handle_t);
static int dprov_decrypt_digest_update(crypto_ctx_t *, crypto_ctx_t *,
    crypto_data_t *, crypto_data_t *, crypto_req_handle_t);
static int dprov_sign_encrypt_update(crypto_ctx_t *, crypto_ctx_t *,
    crypto_data_t *, crypto_data_t *, crypto_req_handle_t);
static int dprov_decrypt_verify_update(crypto_ctx_t *, crypto_ctx_t *,
    crypto_data_t *, crypto_data_t *, crypto_req_handle_t);

static crypto_dual_ops_t dprov_dual_ops = {
	dprov_digest_encrypt_update,
	dprov_decrypt_digest_update,
	dprov_sign_encrypt_update,
	dprov_decrypt_verify_update
};

static int dprov_encrypt_mac_init(crypto_ctx_t *,
    crypto_mechanism_t *, crypto_key_t *, crypto_mechanism_t *,
    crypto_key_t *, crypto_spi_ctx_template_t,
    crypto_spi_ctx_template_t, crypto_req_handle_t);
static int dprov_encrypt_mac(crypto_ctx_t *,
    crypto_data_t *, crypto_dual_data_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_encrypt_mac_update(crypto_ctx_t *,
    crypto_data_t *, crypto_dual_data_t *, crypto_req_handle_t);
static int dprov_encrypt_mac_final(crypto_ctx_t *,
    crypto_dual_data_t *, crypto_data_t *, crypto_req_handle_t);
static int dprov_encrypt_mac_atomic(crypto_provider_handle_t,
    crypto_session_id_t, crypto_mechanism_t *, crypto_key_t *,
    crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
    crypto_dual_data_t *, crypto_data_t *, crypto_spi_ctx_template_t,
    crypto_spi_ctx_template_t, crypto_req_handle_t);

static int dprov_mac_decrypt_init(crypto_ctx_t *,
    crypto_mechanism_t *, crypto_key_t *, crypto_mechanism_t *,
    crypto_key_t *, crypto_spi_ctx_template_t,
    crypto_spi_ctx_template_t, crypto_req_handle_t);
static int dprov_mac_decrypt(crypto_ctx_t *,
    crypto_dual_data_t *, crypto_data_t *, crypto_data_t *,
    crypto_req_handle_t);
static int dprov_mac_decrypt_update(crypto_ctx_t *,
    crypto_dual_data_t *, crypto_data_t *, crypto_req_handle_t);
static int dprov_mac_decrypt_final(crypto_ctx_t *,
    crypto_data_t *, crypto_data_t *, crypto_req_handle_t);
static int dprov_mac_decrypt_atomic(crypto_provider_handle_t,
    crypto_session_id_t, crypto_mechanism_t *, crypto_key_t *,
    crypto_mechanism_t *, crypto_key_t *, crypto_dual_data_t *,
    crypto_data_t *, crypto_data_t *, crypto_spi_ctx_template_t,
    crypto_spi_ctx_template_t, crypto_req_handle_t);
static int dprov_mac_verify_decrypt_atomic(crypto_provider_handle_t,
    crypto_session_id_t, crypto_mechanism_t *, crypto_key_t *,
    crypto_mechanism_t *, crypto_key_t *, crypto_dual_data_t *,
    crypto_data_t *, crypto_data_t *, crypto_spi_ctx_template_t,
    crypto_spi_ctx_template_t, crypto_req_handle_t);

static crypto_dual_cipher_mac_ops_t dprov_cipher_mac_ops = {
	dprov_encrypt_mac_init,
	dprov_encrypt_mac,
	dprov_encrypt_mac_update,
	dprov_encrypt_mac_final,
	dprov_encrypt_mac_atomic,
	dprov_mac_decrypt_init,
	dprov_mac_decrypt,
	dprov_mac_decrypt_update,
	dprov_mac_decrypt_final,
	dprov_mac_decrypt_atomic,
	dprov_mac_verify_decrypt_atomic
};

static int dprov_seed_random(crypto_provider_handle_t, crypto_session_id_t,
    uchar_t *, size_t, uint_t, uint32_t, crypto_req_handle_t);
static int dprov_generate_random(crypto_provider_handle_t, crypto_session_id_t,
    uchar_t *, size_t, crypto_req_handle_t);

static crypto_random_number_ops_t dprov_random_number_ops = {
	dprov_seed_random,
	dprov_generate_random
};

static int dprov_session_open(crypto_provider_handle_t, crypto_session_id_t *,
    crypto_req_handle_t);
static int dprov_session_close(crypto_provider_handle_t, crypto_session_id_t,
    crypto_req_handle_t);
static int dprov_session_login(crypto_provider_handle_t, crypto_session_id_t,
    crypto_user_type_t, char *, size_t, crypto_req_handle_t);
static int dprov_session_logout(crypto_provider_handle_t, crypto_session_id_t,
    crypto_req_handle_t);

static crypto_session_ops_t dprov_session_ops = {
	dprov_session_open,
	dprov_session_close,
	dprov_session_login,
	dprov_session_logout
};

static int dprov_object_create(crypto_provider_handle_t, crypto_session_id_t,
    crypto_object_attribute_t *, uint_t, crypto_object_id_t *,
    crypto_req_handle_t);
static int dprov_object_copy(crypto_provider_handle_t, crypto_session_id_t,
    crypto_object_id_t, crypto_object_attribute_t *, uint_t,
    crypto_object_id_t *, crypto_req_handle_t);
static int dprov_object_destroy(crypto_provider_handle_t, crypto_session_id_t,
    crypto_object_id_t, crypto_req_handle_t);
static int dprov_object_get_size(crypto_provider_handle_t, crypto_session_id_t,
    crypto_object_id_t, size_t *, crypto_req_handle_t);
static int dprov_object_get_attribute_value(crypto_provider_handle_t,
    crypto_session_id_t, crypto_object_id_t,
    crypto_object_attribute_t *, uint_t, crypto_req_handle_t);
static int dprov_object_set_attribute_value(crypto_provider_handle_t,
    crypto_session_id_t, crypto_object_id_t,
    crypto_object_attribute_t *,  uint_t, crypto_req_handle_t);
static int dprov_object_find_init(crypto_provider_handle_t, crypto_session_id_t,
    crypto_object_attribute_t *, uint_t, void **,
    crypto_req_handle_t);
static int dprov_object_find(crypto_provider_handle_t, void *,
    crypto_object_id_t *, uint_t, uint_t *, crypto_req_handle_t);
static int dprov_object_find_final(crypto_provider_handle_t, void *,
    crypto_req_handle_t);

static crypto_object_ops_t dprov_object_ops = {
	dprov_object_create,
	dprov_object_copy,
	dprov_object_destroy,
	dprov_object_get_size,
	dprov_object_get_attribute_value,
	dprov_object_set_attribute_value,
	dprov_object_find_init,
	dprov_object_find,
	dprov_object_find_final
};

static int dprov_key_generate(crypto_provider_handle_t, crypto_session_id_t,
    crypto_mechanism_t *, crypto_object_attribute_t *, uint_t,
    crypto_object_id_t *, crypto_req_handle_t);
static int dprov_key_generate_pair(crypto_provider_handle_t,
    crypto_session_id_t, crypto_mechanism_t *, crypto_object_attribute_t *,
    uint_t, crypto_object_attribute_t *, uint_t, crypto_object_id_t *,
    crypto_object_id_t *, crypto_req_handle_t);
static int dprov_key_wrap(crypto_provider_handle_t, crypto_session_id_t,
    crypto_mechanism_t *, crypto_key_t *, crypto_object_id_t *,
    uchar_t *, size_t *, crypto_req_handle_t);
static int dprov_key_unwrap(crypto_provider_handle_t, crypto_session_id_t,
    crypto_mechanism_t *, crypto_key_t *, uchar_t *, size_t *,
    crypto_object_attribute_t *, uint_t,
    crypto_object_id_t *, crypto_req_handle_t);
static int dprov_key_derive(crypto_provider_handle_t, crypto_session_id_t,
    crypto_mechanism_t *, crypto_key_t *, crypto_object_attribute_t *,
    uint_t, crypto_object_id_t *, crypto_req_handle_t);

static crypto_key_ops_t dprov_key_ops = {
	dprov_key_generate,
	dprov_key_generate_pair,
	dprov_key_wrap,
	dprov_key_unwrap,
	dprov_key_derive
};

static int dprov_ext_info(crypto_provider_handle_t,
    crypto_provider_ext_info_t *, crypto_req_handle_t);
static int dprov_init_token(crypto_provider_handle_t, char *, size_t,
    char *, crypto_req_handle_t);
static int dprov_init_pin(crypto_provider_handle_t, crypto_session_id_t,
    char *, size_t, crypto_req_handle_t);
static int dprov_set_pin(crypto_provider_handle_t, crypto_session_id_t,
    char *, size_t, char *, size_t, crypto_req_handle_t);

static crypto_provider_management_ops_t dprov_management_ops = {
	dprov_ext_info,
	dprov_init_token,
	dprov_init_pin,
	dprov_set_pin
};

static int dprov_free_context(crypto_ctx_t *);
static int dprov_copyin_mechanism(crypto_provider_handle_t,
    crypto_mechanism_t *, crypto_mechanism_t *, int *error, int);
static int dprov_copyout_mechanism(crypto_provider_handle_t,
    crypto_mechanism_t *, crypto_mechanism_t *, int *error, int);
static int dprov_free_mechanism(crypto_provider_handle_t,
    crypto_mechanism_t *);

static crypto_ctx_ops_t dprov_ctx_ops = {
	NULL,
	dprov_free_context
};

static crypto_mech_ops_t dprov_mech_ops = {
	dprov_copyin_mechanism,
	dprov_copyout_mechanism,
	dprov_free_mechanism
};

static crypto_ops_t dprov_crypto_ops = {
	&dprov_control_ops,
	&dprov_digest_ops,
	&dprov_cipher_ops,
	&dprov_mac_ops,
	&dprov_sign_ops,
	&dprov_verify_ops,
	&dprov_dual_ops,
	&dprov_cipher_mac_ops,
	&dprov_random_number_ops,
	&dprov_session_ops,
	&dprov_object_ops,
	&dprov_key_ops,
	&dprov_management_ops,
	&dprov_ctx_ops,
	&dprov_mech_ops
};


/* maximum SO and user PIN lengths */
#define	DPROV_MAX_PIN_LEN	128

/*
 * Objects: each session is associated with an array of objects.
 * Unlike PKCS#11, the objects cannot be shared between sessions.
 * The ioctl driver multiplexes PKCS#11 sessions to providers
 * sessions in order to support this semantic. This simplifies
 * the CSPI greatly since the provider does not have to associate
 * sessions with a user space process.
 * There is also a per-instance array of objects, which correspond
 * to PKCS#11 token objects. These objects can be shared by multiple
 * sesions.
 *
 * Token objects are identified by having a CKA_TOKEN attribute B_TRUE.
 * Private objects are identified by having a CKA_PRIVATE attribute
 * set to B_TRUE.
 */

#define	DPROV_MAX_OBJECTS	128	/* max # of objects */
#define	DPROV_MAX_ATTR		64	/* max # of attributes per object */

/* object description */
typedef struct dprov_object {
	crypto_object_attribute_t do_attr[DPROV_MAX_ATTR]; /* attributes */
	uint_t do_token_idx;		/* index in per-instance table */
					/* for token objects. */
	boolean_t do_destroyed;		/* object has been destroyed. */
					/* keep object around until all */
					/* sessions that refer to it */
					/* are closed, but mark it */
					/* destroyed so that references */
					/* to the object fail. */
					/* used for token objects only */
	uint_t do_refcnt;
} dprov_object_t;

/*
 * If a session has a reference to a dprov_object_t,
 * it REFHOLD()s.
 */
#define	DPROV_OBJECT_REFHOLD(object) {		\
	atomic_add_32(&(object)->do_refcnt, 1);	\
	ASSERT((object)->do_refcnt != 0);		\
}

/*
 * Releases a reference to an object. When the last
 * reference is released, the object is freed.
 */
#define	DPROV_OBJECT_REFRELE(object) {				\
	ASSERT((object)->do_refcnt != 0);			\
	membar_exit();						\
	if (atomic_add_32_nv(&(object)->do_refcnt, -1) == 0)	\
		dprov_free_object(object);			\
}

/*
 * Object attributes are passed to the provider using crypto_object_attribute
 * structures, which contain the type of the attribute, a pointer to
 * it's value, and the length of its value. The attribute types values
 * are defined by the PKCS#11 specification. This provider only cares
 * about a subset of these attributes. In order to avoid having to
 * include the PKCS#11 header files, we define here the attributes values
 * which are used by the provider.
 */

#define	DPROV_CKA_CLASS			0x00000000
#define	DPROV_CKA_TOKEN			0x00000001
#define	DPROV_CKA_PRIVATE		0x00000002
#define	DPROV_CKA_VALUE			0x00000011
#define	DPROV_CKA_CERTIFICATE_TYPE	0x00000080
#define	DPROV_CKA_KEY_TYPE		0x00000100
#define	DPROV_CKA_ENCRYPT		0x00000104
#define	DPROV_CKA_DECRYPT		0x00000105
#define	DPROV_CKA_WRAP			0x00000106
#define	DPROV_CKA_UNWRAP		0x00000107
#define	DPROV_CKA_SIGN			0x00000108
#define	DPROV_CKA_SIGN_RECOVER		0x00000109
#define	DPROV_CKA_VERIFY		0x0000010A
#define	DPROV_CKA_VERIFY_RECOVER	0x0000010B
#define	DPROV_CKA_DERIVE		0x0000010C
#define	DPROV_CKA_MODULUS		0x00000120
#define	DPROV_CKA_MODULUS_BITS		0x00000121
#define	DPROV_CKA_PUBLIC_EXPONENT	0x00000122
#define	DPROV_CKA_PRIVATE_EXPONENT	0x00000123
#define	DPROV_CKA_VALUE_BITS		0x00000160
#define	DPROV_CKA_VALUE_LEN		0x00000161
#define	DPROV_CKA_EXTRACTABLE		0x00000162
#define	DPROV_HW_FEATURE_TYPE		0x00000300

/*
 * Object classes from PKCS#11
 */
#define	DPROV_CKO_DATA			0x00000000
#define	DPROV_CKO_CERTIFICATE		0x00000001
#define	DPROV_CKO_PUBLIC_KEY		0x00000002
#define	DPROV_CKO_PRIVATE_KEY		0x00000003
#define	DPROV_CKO_SECRET_KEY		0x00000004
#define	DPROV_CKO_HW_FEATURE		0x00000005
#define	DPROV_CKO_DOMAIN_PARAMETERS	0x00000006
#define	DPROV_CKO_VENDOR_DEFINED	0x80000000

/*
 * A few key types from PKCS#11
 */
#define	DPROV_CKK_RSA			0x00000000
#define	DPROV_CKK_GENERIC_SECRET	0x00000010
#define	DPROV_CKK_RC4			0x00000012
#define	DPROV_CKK_DES			0x00000013
#define	DPROV_CKK_DES3			0x00000015
#define	DPROV_CKK_AES			0x0000001F
#define	DPROV_CKK_BLOWFISH		0x00000020

/*
 * Find object context. Allows the find object init/find/final
 * to store data persistent across calls.
 */
typedef struct dprov_find_ctx {
	crypto_object_id_t fc_ids[DPROV_MAX_OBJECTS];	/* object ids */
	uint_t fc_nids;			/* number of ids in fc_ids */
	uint_t fc_next;			/* next id to return */
} dprov_find_ctx_t;

/*
 * Session management: each instance is associated with an array
 * of sessions. KEF providers sessions are always R/W the library and
 * the ioctl maintain the PKCS#11 R/W attributes for the session.
 */

#define	DPROV_MIN_SESSIONS	32	/* # of sessions to start with */

typedef enum dprov_session_state {
	DPROV_SESSION_STATE_PUBLIC,	/* public (default) */
	DPROV_SESSION_STATE_SO,		/* SO logged in */
	DPROV_SESSION_STATE_USER	/* user logged in */
} dprov_session_state_t;

/* session description */
typedef struct dprov_session {
	dprov_session_state_t ds_state;	/* session state */
	dprov_object_t *ds_objects[DPROV_MAX_OBJECTS];	/* session objects */
} dprov_session_t;


static crypto_provider_info_t dprov_prov_info = {
	CRYPTO_SPI_VERSION_2,
	"Dummy Pseudo HW Provider",
	CRYPTO_HW_PROVIDER,
	NULL,				/* pi_provider_dev */
	NULL,				/* pi_provider_handle */
	&dprov_crypto_ops,
	sizeof (dprov_mech_info_tab)/sizeof (crypto_mech_info_t),
	dprov_mech_info_tab,
	0,				/* pi_logical_provider_count */
	NULL				/* pi_logical_providers */
};

/*
 * Per-instance info.
 */
typedef struct dprov_state {
	kmutex_t ds_lock;		/* per-instance lock */
	dev_info_t *ds_dip;		/* device info */
	crypto_kcf_provider_handle_t ds_prov_handle;	/* framework handle */
	taskq_t *ds_taskq;		/* taskq for async behavior */
	char ds_user_pin[DPROV_MAX_PIN_LEN];	/* normal user PIN */
	uint_t ds_user_pin_len;
	char ds_so_pin[DPROV_MAX_PIN_LEN];	/* SO PIN */
	uint_t ds_so_pin_len;
	dprov_session_t **ds_sessions;	/* sessions for this instance */
	uint_t ds_sessions_slots;	/* number of session slots */
	uint_t ds_sessions_count;	/* number of open sessions */
	boolean_t ds_token_initialized;	/* provider initialized? */
	boolean_t ds_user_pin_set;	/* user pin set? */
	char ds_label[CRYPTO_EXT_SIZE_LABEL];		/* "token" label */
	dprov_object_t *ds_objects[DPROV_MAX_OBJECTS];	/* "token" objects */
} dprov_state_t;


/*
 * A taskq is associated with each instance of the pseudo driver in order
 * to simulate the asynchronous execution of requests.
 * The following defines the taskq request structures.
 */

/* request types */
typedef enum dprov_req_type {
	/* digest requests */
	DPROV_REQ_DIGEST_INIT = 1,
	DPROV_REQ_DIGEST,
	DPROV_REQ_DIGEST_UPDATE,
	DPROV_REQ_DIGEST_KEY,
	DPROV_REQ_DIGEST_FINAL,
	DPROV_REQ_DIGEST_ATOMIC,
	/* cipher requests */
	DPROV_REQ_ENCRYPT_INIT,
	DPROV_REQ_ENCRYPT,
	DPROV_REQ_ENCRYPT_UPDATE,
	DPROV_REQ_ENCRYPT_FINAL,
	DPROV_REQ_ENCRYPT_ATOMIC,
	DPROV_REQ_DECRYPT_INIT,
	DPROV_REQ_DECRYPT,
	DPROV_REQ_DECRYPT_UPDATE,
	DPROV_REQ_DECRYPT_FINAL,
	DPROV_REQ_DECRYPT_ATOMIC,
	/* mac requests */
	DPROV_REQ_MAC_INIT,
	DPROV_REQ_MAC,
	DPROV_REQ_MAC_UPDATE,
	DPROV_REQ_MAC_FINAL,
	DPROV_REQ_MAC_ATOMIC,
	DPROV_REQ_MAC_VERIFY_ATOMIC,
	/* sign requests */
	DPROV_REQ_SIGN_INIT,
	DPROV_REQ_SIGN,
	DPROV_REQ_SIGN_UPDATE,
	DPROV_REQ_SIGN_FINAL,
	DPROV_REQ_SIGN_ATOMIC,
	DPROV_REQ_SIGN_RECOVER_INIT,
	DPROV_REQ_SIGN_RECOVER,
	DPROV_REQ_SIGN_RECOVER_ATOMIC,
	/* verify requests */
	DPROV_REQ_VERIFY_INIT,
	DPROV_REQ_VERIFY,
	DPROV_REQ_VERIFY_UPDATE,
	DPROV_REQ_VERIFY_FINAL,
	DPROV_REQ_VERIFY_ATOMIC,
	DPROV_REQ_VERIFY_RECOVER_INIT,
	DPROV_REQ_VERIFY_RECOVER,
	DPROV_REQ_VERIFY_RECOVER_ATOMIC,
	/* dual ops requests */
	DPROV_REQ_DIGEST_ENCRYPT_UPDATE,
	DPROV_REQ_DECRYPT_DIGEST_UPDATE,
	DPROV_REQ_SIGN_ENCRYPT_UPDATE,
	DPROV_REQ_DECRYPT_VERIFY_UPDATE,
	/* dual cipher/mac requests */
	DPROV_REQ_ENCRYPT_MAC_INIT,
	DPROV_REQ_ENCRYPT_MAC,
	DPROV_REQ_ENCRYPT_MAC_UPDATE,
	DPROV_REQ_ENCRYPT_MAC_FINAL,
	DPROV_REQ_ENCRYPT_MAC_ATOMIC,
	DPROV_REQ_MAC_DECRYPT_INIT,
	DPROV_REQ_MAC_DECRYPT,
	DPROV_REQ_MAC_DECRYPT_UPDATE,
	DPROV_REQ_MAC_DECRYPT_FINAL,
	DPROV_REQ_MAC_DECRYPT_ATOMIC,
	DPROV_REQ_MAC_VERIFY_DECRYPT_ATOMIC,
	/* random number ops */
	DPROV_REQ_RANDOM_SEED,
	DPROV_REQ_RANDOM_GENERATE,
	/* session management requests */
	DPROV_REQ_SESSION_OPEN,
	DPROV_REQ_SESSION_CLOSE,
	DPROV_REQ_SESSION_LOGIN,
	DPROV_REQ_SESSION_LOGOUT,
	/* object management requests */
	DPROV_REQ_OBJECT_CREATE,
	DPROV_REQ_OBJECT_COPY,
	DPROV_REQ_OBJECT_DESTROY,
	DPROV_REQ_OBJECT_GET_SIZE,
	DPROV_REQ_OBJECT_GET_ATTRIBUTE_VALUE,
	DPROV_REQ_OBJECT_SET_ATTRIBUTE_VALUE,
	DPROV_REQ_OBJECT_FIND_INIT,
	DPROV_REQ_OBJECT_FIND,
	DPROV_REQ_OBJECT_FIND_FINAL,
	/* key management requests */
	DPROV_REQ_KEY_GENERATE,
	DPROV_REQ_KEY_GENERATE_PAIR,
	DPROV_REQ_KEY_WRAP,
	DPROV_REQ_KEY_UNWRAP,
	DPROV_REQ_KEY_DERIVE,
	/* provider management requests */
	DPROV_REQ_MGMT_EXTINFO,
	DPROV_REQ_MGMT_INITTOKEN,
	DPROV_REQ_MGMT_INITPIN,
	DPROV_REQ_MGMT_SETPIN
} dprov_req_type_t;

/* for DPROV_REQ_DIGEST requests */
typedef struct dprov_digest_req {
	crypto_mechanism_t *dr_mechanism;
	crypto_ctx_t *dr_ctx;
	crypto_data_t *dr_data;
	crypto_key_t *dr_key;
	crypto_data_t *dr_digest;
} dprov_digest_req_t;

/* for DPROV_REQ_MAC requests */
typedef struct dprov_mac_req {
	crypto_mechanism_t *dr_mechanism;
	crypto_ctx_t *dr_ctx;
	crypto_data_t *dr_data;
	crypto_key_t *dr_key;
	crypto_data_t *dr_mac;
	crypto_session_id_t dr_session_id;
} dprov_mac_req_t;

/* for DPROV_REQ_ENCRYPT and DPROV_REQ_DECRYPT requests */
typedef struct dprov_cipher_req {
	crypto_mechanism_t *dr_mechanism;
	crypto_ctx_t *dr_ctx;
	crypto_key_t *dr_key;
	crypto_data_t *dr_plaintext;
	crypto_data_t *dr_ciphertext;
	crypto_session_id_t dr_session_id;
} dprov_cipher_req_t;

/* for DPROV_REQ_SIGN requests */
typedef struct dprov_sign_req {
	crypto_mechanism_t *sr_mechanism;
	crypto_ctx_t *sr_ctx;
	crypto_key_t *sr_key;
	crypto_data_t *sr_data;
	crypto_data_t *sr_signature;
	crypto_session_id_t sr_session_id;
} dprov_sign_req_t;

/* for DPROV_REQ_VERIFY requests */
typedef struct dprov_verify_req {
	crypto_mechanism_t *vr_mechanism;
	crypto_ctx_t *vr_ctx;
	crypto_key_t *vr_key;
	crypto_data_t *vr_data;
	crypto_data_t *vr_signature;
	crypto_session_id_t vr_session_id;
} dprov_verify_req_t;

/* for dual ops requests */
typedef struct dprov_dual_req {
	crypto_ctx_t *dr_signverify_ctx;
	crypto_ctx_t *dr_cipher_ctx;
	crypto_data_t *dr_plaintext;
	crypto_data_t *dr_ciphertext;
} dprov_dual_req_t;

/* for cipher/mac dual ops requests */
typedef struct dprov_cipher_mac_req {
	crypto_session_id_t mr_session_id;
	crypto_ctx_t *mr_ctx;
	crypto_mechanism_t *mr_cipher_mech;
	crypto_key_t *mr_cipher_key;
	crypto_mechanism_t *mr_mac_mech;
	crypto_key_t *mr_mac_key;
	crypto_dual_data_t *mr_dual_data;
	crypto_data_t *mr_data;
	crypto_data_t *mr_mac;
} dprov_cipher_mac_req_t;

/* for DPROV_REQ_RANDOM requests */
typedef struct dprov_random_req {
	uchar_t *rr_buf;
	size_t rr_len;
	crypto_session_id_t rr_session_id;
	uint_t rr_entropy_est;
	uint32_t rr_flags;
} dprov_random_req_t;

/* for DPROV_REQ_SESSION requests */
typedef struct dprov_session_req {
	crypto_session_id_t *sr_session_id_ptr;
	crypto_session_id_t sr_session_id;
	crypto_user_type_t sr_user_type;
	char *sr_pin;
	size_t sr_pin_len;
} dprov_session_req_t;

/* for DPROV_REQ_OBJECT requests */
typedef struct dprov_object_req {
	crypto_session_id_t or_session_id;
	crypto_object_id_t or_object_id;
	crypto_object_attribute_t *or_template;
	uint_t or_attribute_count;
	crypto_object_id_t *or_object_id_ptr;
	size_t *or_object_size;
	void **or_find_pp;
	void *or_find_p;
	uint_t or_max_object_count;
	uint_t *or_object_count_ptr;
} dprov_object_req_t;

/* for DPROV_REQ_KEY requests */
typedef struct dprov_key_req {
	crypto_session_id_t kr_session_id;
	crypto_mechanism_t *kr_mechanism;
	crypto_object_attribute_t *kr_template;
	uint_t kr_attribute_count;
	crypto_object_id_t *kr_object_id_ptr;
	crypto_object_attribute_t *kr_private_key_template;
	uint_t kr_private_key_attribute_count;
	crypto_object_id_t *kr_private_key_object_id_ptr;
	crypto_key_t *kr_key;
	uchar_t *kr_wrapped_key;
	size_t *kr_wrapped_key_len_ptr;
} dprov_key_req_t;

/* for DPROV_REQ_MGMT requests */
typedef struct dprov_mgmt_req {
	crypto_session_id_t mr_session_id;
	char *mr_pin;
	size_t mr_pin_len;
	char *mr_old_pin;
	size_t mr_old_pin_len;
	char *mr_label;
	crypto_provider_ext_info_t *mr_ext_info;
} dprov_mgmt_req_t;

/* request, as queued on taskq */
typedef struct dprov_req {
	dprov_req_type_t dr_type;
	dprov_state_t *dr_softc;
	crypto_req_handle_t dr_kcf_req;
	union {
		dprov_digest_req_t dru_digest_req;
		dprov_mac_req_t dru_mac_req;
		dprov_cipher_req_t dru_cipher_req;
		dprov_sign_req_t dru_sign_req;
		dprov_verify_req_t dru_verify_req;
		dprov_dual_req_t dru_dual_req;
		dprov_cipher_mac_req_t dru_cipher_mac_req;
		dprov_random_req_t dru_random_req;
		dprov_session_req_t dru_session_req;
		dprov_object_req_t dru_object_req;
		dprov_key_req_t dru_key_req;
		dprov_mgmt_req_t dru_mgmt_req;
	} dr_req;
} dprov_req_t;

/* shortcuts for union fields */
#define	dr_digest_req		dr_req.dru_digest_req
#define	dr_mac_req		dr_req.dru_mac_req
#define	dr_cipher_req		dr_req.dru_cipher_req
#define	dr_sign_req		dr_req.dru_sign_req
#define	dr_verify_req		dr_req.dru_verify_req
#define	dr_dual_req		dr_req.dru_dual_req
#define	dr_cipher_mac_req	dr_req.dru_cipher_mac_req
#define	dr_random_req		dr_req.dru_random_req
#define	dr_session_req		dr_req.dru_session_req
#define	dr_object_req		dr_req.dru_object_req
#define	dr_key_req		dr_req.dru_key_req
#define	dr_mgmt_req		dr_req.dru_mgmt_req

/* prototypes for the tasq dispatcher functions */
static void dprov_digest_task(dprov_req_t *);
static void dprov_mac_task(dprov_req_t *);
static void dprov_sign_task(dprov_req_t *);
static void dprov_verify_task(dprov_req_t *);
static void dprov_dual_task(dprov_req_t *);
static void dprov_cipher_task(dprov_req_t *);
static void dprov_cipher_mac_task(dprov_req_t *);
static void dprov_random_task(dprov_req_t *);
static void dprov_session_task(dprov_req_t *);
static void dprov_object_task(dprov_req_t *);
static void dprov_key_task(dprov_req_t *);
static void dprov_mgmt_task(dprov_req_t *);

/* helper functions */
static int dprov_digest_submit_req(dprov_req_type_t, dprov_state_t *,
    crypto_req_handle_t, crypto_mechanism_t *, crypto_data_t *, crypto_key_t *,
    crypto_data_t *, crypto_ctx_t *, int);
static int dprov_cipher_submit_req(dprov_req_type_t, dprov_state_t *,
    crypto_req_handle_t, crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
    crypto_data_t *, crypto_ctx_t *, crypto_session_id_t, int);
static int dprov_mac_submit_req(dprov_req_type_t, dprov_state_t *,
    crypto_req_handle_t, crypto_mechanism_t *, crypto_data_t *,
    crypto_key_t *, crypto_data_t *, crypto_ctx_t *, crypto_session_id_t, int);
static int dprov_sign_submit_req(dprov_req_type_t, dprov_state_t *,
    crypto_req_handle_t, crypto_mechanism_t *, crypto_key_t *,
    crypto_data_t *, crypto_data_t *, crypto_ctx_t *, crypto_session_id_t, int);
static int dprov_verify_submit_req(dprov_req_type_t, dprov_state_t *,
    crypto_req_handle_t, crypto_mechanism_t *, crypto_key_t *,
    crypto_data_t *, crypto_data_t *, crypto_ctx_t *, crypto_session_id_t, int);
static int dprov_dual_submit_req(dprov_req_type_t, dprov_state_t *,
    crypto_req_handle_t, crypto_ctx_t *, crypto_ctx_t *, crypto_data_t *,
    crypto_data_t *);
static int dprov_cipher_mac_submit_req(dprov_req_type_t, dprov_state_t *,
    crypto_req_handle_t, crypto_ctx_t *, crypto_session_id_t,
    crypto_mechanism_t *, crypto_key_t *, crypto_mechanism_t *, crypto_key_t *,
    crypto_dual_data_t *, crypto_data_t *, crypto_data_t *, int);
static int dprov_random_submit_req(dprov_req_type_t, dprov_state_t *,
    crypto_req_handle_t, uchar_t *, size_t, crypto_session_id_t, uint_t,
    uint32_t);
static int dprov_session_submit_req(dprov_req_type_t, dprov_state_t *,
    crypto_req_handle_t, crypto_session_id_t *, crypto_session_id_t,
    crypto_user_type_t, char *, size_t);
static int dprov_object_submit_req(dprov_req_type_t, dprov_state_t *,
    crypto_req_handle_t, crypto_session_id_t, crypto_object_id_t,
    crypto_object_attribute_t *, uint_t, crypto_object_id_t *, size_t *,
    void **, void *, uint_t, uint_t *, int);
static int dprov_key_submit_req(dprov_req_type_t, dprov_state_t *,
    crypto_req_handle_t, crypto_session_id_t, crypto_mechanism_t *,
    crypto_object_attribute_t *, uint_t, crypto_object_id_t *,
    crypto_object_attribute_t *, uint_t, crypto_object_id_t *,
    crypto_key_t *, uchar_t *, size_t *);
static int dprov_mgmt_submit_req(dprov_req_type_t, dprov_state_t *,
    crypto_req_handle_t, crypto_session_id_t, char *, size_t, char *, size_t,
    char *, crypto_provider_ext_info_t *);
static int dprov_get_sw_prov(crypto_mechanism_t *, kcf_provider_desc_t **,
    crypto_mech_type_t *);

/* object management helper functions */
static void dprov_free_object(dprov_object_t *);
static void dprov_release_session_objects(dprov_session_t *);
static boolean_t dprov_object_is_private(dprov_object_t *);
static boolean_t dprov_object_is_token(dprov_object_t *);
static int dprov_key_value_secret(dprov_state_t *, crypto_session_id_t,
    dprov_req_type_t, crypto_key_t *, crypto_key_t *);
static int dprov_key_attr_asymmetric(dprov_state_t *, crypto_session_id_t,
    dprov_req_type_t, crypto_key_t *, crypto_key_t *);
static int dprov_get_object_attr_boolean(dprov_object_t *, uint64_t,
	boolean_t *);
static int dprov_get_object_attr_ulong(dprov_object_t *, uint64_t, ulong_t *);
static int dprov_get_object_attr_array(dprov_object_t *, uint64_t, void **,
    size_t *);
static int dprov_get_key_attr_ulong(crypto_key_t *, uint64_t, ulong_t *);
static int dprov_get_key_attr_array(crypto_key_t *, uint64_t, void **,
    size_t *);
static int dprov_create_object_from_template(dprov_state_t *, dprov_session_t *,
    crypto_object_attribute_t *, uint_t, crypto_object_id_t *, boolean_t,
    boolean_t);
static int dprov_get_template_attr_scalar_common(crypto_object_attribute_t *,
    uint_t, uint64_t, void *, size_t);
static int dprov_get_template_attr_boolean(crypto_object_attribute_t *,
    uint_t, uint64_t, boolean_t *);
static int dprov_get_template_attr_ulong(crypto_object_attribute_t *, uint_t,
    uint64_t, ulong_t *);
static int dprov_template_attr_present(crypto_object_attribute_t *, uint_t,
    uint64_t);
static int dprov_get_template_attr_array(crypto_object_attribute_t *, uint_t,
    uint64_t, void **, size_t *);
static int dprov_destroy_object(dprov_state_t *, dprov_session_t *,
    crypto_object_id_t);
static int dprov_object_set_attr(dprov_session_t *, crypto_object_id_t,
    crypto_object_attribute_t *, uint_t, boolean_t);
static int dprov_find_attr(crypto_object_attribute_t *, uint_t, uint64_t);
static boolean_t dprov_attributes_match(dprov_object_t *,
    crypto_object_attribute_t *, uint_t);

/* retrieve the softc and instance number from a SPI crypto context */
#define	DPROV_SOFTC_FROM_CTX(ctx, softc, instance) {	\
	(softc) = (dprov_state_t *)(ctx)->cc_provider;	\
	(instance) = ddi_get_instance((softc)->ds_dip);	\
}

/* retrieve the softc and instance number from a taskq request */
#define	DPROV_SOFTC_FROM_REQ(req, softc, instance) {	\
	(softc) = (req)->dr_softc;			\
	(instance) = ddi_get_instance((softc)->ds_dip);	\
}

/*
 * The dprov private context most of the time contains a pointer to the
 * crypto_context_t that was allocated when calling a KCF function.
 * Dual cipher/mac operations however require the dprov driver
 * to maintain the contexts associated with the separate cipher
 * and mac operations. These two types of dprov contexts are
 * defined below.
 */
typedef enum dprov_ctx_type {
	DPROV_CTX_SINGLE,
	DPROV_CTX_DUAL
} dprov_ctx_type_t;

/*
 * When the context refers to a single KCF context, the
 * cc_provider field of a crypto_ctx_t points to a structure of
 * type dprov_ctx_single.
 */
typedef struct dprov_ctx_single {
	dprov_ctx_type_t dc_type;
	crypto_context_t dc_ctx;
} dprov_ctx_single_t;

/*
 * When the context is used for cipher/mac operations, it contains
 * pointers to to KCF contexts, one for the cipher operation, the
 * other for the mac operation.
 */
typedef struct dprov_ctx_dual {
	dprov_ctx_type_t cd_type;
	crypto_context_t cd_cipher_ctx;
	crypto_context_t cd_mac_ctx;
} dprov_ctx_dual_t;

/*
 * Helper macros for context accessors. These macros return the
 * k-API context corresponding to the given SPI context for
 * single and dual cipher/mac operations.
 */

#define	DPROV_CTX_SINGLE(_ctx) \
	(((dprov_ctx_single_t *)(_ctx)->cc_provider_private)->dc_ctx)

#define	DPROV_CTX_DUAL_CIPHER(_ctx) \
	(((dprov_ctx_dual_t *)(_ctx)->cc_provider_private)->cd_cipher_ctx)

#define	DPROV_CTX_DUAL_MAC(_ctx) \
	(((dprov_ctx_dual_t *)(_ctx)->cc_provider_private)->cd_mac_ctx)

static int dprov_alloc_context(dprov_req_type_t, crypto_ctx_t *);



static void *statep;	/* state pointer */

/*
 * DDI entry points.
 */
int
_init(void)
{
	int error;

	DPROV_DEBUG(D_INIT, ("dprov: in _init\n"));

	if ((error = ddi_soft_state_init(&statep, sizeof (dprov_state_t),
	    0)) != 0)
		return (error);

	return (mod_install(&modlinkage));
}

int
_fini(void)
{
	int error;

	DPROV_DEBUG(D_INIT, ("dprov: in _fini\n"));

	if ((error = mod_remove(&modlinkage)) != 0)
		return (error);

	ddi_soft_state_fini(&statep);

	return (0);
}

int
_info(struct modinfo *modinfop)
{
	DPROV_DEBUG(D_INIT, ("dprov: in _info\n"));

	return (mod_info(&modlinkage, modinfop));
}

/* ARGSUSED */
static int
dprov_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
{
	int instance = getminor((dev_t)arg);
	dprov_state_t *softc;

	DPROV_DEBUG(D_ATTACH, ("dprov: in dprov_getinfo() for %d\n",
	    instance));

	switch (cmd) {
	case DDI_INFO_DEVT2DEVINFO:
		softc = ddi_get_soft_state(statep, instance);
		*result = softc->ds_dip;
		return (DDI_SUCCESS);

	case DDI_INFO_DEVT2INSTANCE:
		*result = (void *)(uintptr_t)instance;
		return (DDI_SUCCESS);
	}
	return (DDI_FAILURE);
}

static int
dprov_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
	int instance = ddi_get_instance(dip);
	dprov_state_t *softc;
	char devname[256];
	int ret;

	DPROV_DEBUG(D_ATTACH, ("dprov: in dprov_attach() for %d\n",
	    instance));

	if (cmd != DDI_ATTACH) {
		return (DDI_FAILURE);
	}

	/* get new softc and initialize it */
	if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS)
		return (DDI_FAILURE);

	softc = ddi_get_soft_state(statep, instance);
	mutex_init(&softc->ds_lock, NULL, MUTEX_DRIVER, NULL);
	softc->ds_dip = dip;
	softc->ds_prov_handle = NULL;

	/* create minor node */
	(void) sprintf(devname, "dprov%d", instance);
	if (ddi_create_minor_node(dip, devname, S_IFCHR, instance,
	    DDI_PSEUDO, 0) != DDI_SUCCESS) {
		cmn_err(CE_WARN, "attach: failed creating minor node");
		mutex_destroy(&softc->ds_lock);
		ddi_soft_state_free(statep, instance);
		return (DDI_FAILURE);
	}

	/* create taskq */
	softc->ds_taskq = taskq_create(devname, 1, minclsyspri,
	    crypto_taskq_minalloc, crypto_taskq_maxalloc, TASKQ_PREPOPULATE);

	/* initialize table of sessions */
	softc->ds_sessions = kmem_zalloc(DPROV_MIN_SESSIONS *
	    sizeof (dprov_session_t *), KM_SLEEP);
	softc->ds_sessions_slots = DPROV_MIN_SESSIONS;
	softc->ds_sessions_count = 0;

	/* initialized done by init_token entry point */
	softc->ds_token_initialized = B_TRUE;

	(void) memset(softc->ds_label, ' ', CRYPTO_EXT_SIZE_LABEL);
	bcopy("Dummy Pseudo HW Provider", softc->ds_label, 24);

	bcopy("changeme", softc->ds_user_pin, 8);
	softc->ds_user_pin_len = 8;
	softc->ds_user_pin_set = B_TRUE;

	/* register with the crypto framework */
	dprov_prov_info.pi_provider_dev.pd_hw = dip;
	dprov_prov_info.pi_provider_handle = softc;
	if ((ret = crypto_register_provider(&dprov_prov_info,
	    &softc->ds_prov_handle)) != CRYPTO_SUCCESS) {
		cmn_err(CE_WARN,
		    "dprov crypto_register_provider() failed (0x%x)", ret);
		taskq_destroy(softc->ds_taskq);
		kmem_free(softc->ds_sessions, softc->ds_sessions_slots *
		    sizeof (dprov_session_t *));
		mutex_destroy(&softc->ds_lock);
		ddi_soft_state_free(statep, instance);
		ddi_remove_minor_node(dip, NULL);
		return (DDI_FAILURE);
	}

	/*
	 * This call is for testing only; it is not required by the SPI.
	 */
	crypto_provider_notification(softc->ds_prov_handle,
	    CRYPTO_PROVIDER_READY);

	return (DDI_SUCCESS);
}

static int
dprov_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
	int instance = ddi_get_instance(dip);
	dprov_state_t *softc = ddi_get_soft_state(statep, instance);
	dprov_session_t *session;
	int i, ret;

	DPROV_DEBUG(D_ATTACH, ("dprov: in dprov_detach() for %d\n",
	    instance));

	if (cmd != DDI_DETACH)
		return (DDI_FAILURE);

	/* unregister from the crypto framework */
	if (softc->ds_prov_handle != NULL)
		if ((ret = crypto_unregister_provider(
		    softc->ds_prov_handle)) != CRYPTO_SUCCESS) {
			cmn_err(CE_WARN, "dprov_detach: "
			    "crypto_unregister_provider() "
			    "failed (0x%x)", ret);
			return (DDI_FAILURE);
		}


	taskq_destroy(softc->ds_taskq);

	for (i = 0; i < softc->ds_sessions_slots; i++) {
		if ((session = softc->ds_sessions[i]) == NULL)
			continue;

		dprov_release_session_objects(session);

		kmem_free(session, sizeof (dprov_session_t));
		softc->ds_sessions_count--;

	}

	kmem_free(softc->ds_sessions, softc->ds_sessions_slots *
	    sizeof (dprov_session_t *));
	/* free token objects */
	for (i = 0; i < DPROV_MAX_OBJECTS; i++)
		if (softc->ds_objects[i] != NULL)
			dprov_free_object(softc->ds_objects[i]);

	mutex_destroy(&softc->ds_lock);
	ddi_soft_state_free(statep, instance);

	ddi_remove_minor_node(dip, NULL);

	return (DDI_SUCCESS);
}

/*
 * Control entry points.
 */
static void
dprov_provider_status(crypto_provider_handle_t provider, uint_t *status)
{
	_NOTE(ARGUNUSED(provider))

	*status = CRYPTO_PROVIDER_READY;
}

/*
 * Digest entry points.
 */

static int
dprov_digest_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_digest_init: started\n", instance));

	/* check mechanism */
	if (mechanism->cm_type != MD4_MECH_INFO_TYPE &&
	    mechanism->cm_type != MD5_MECH_INFO_TYPE &&
	    mechanism->cm_type != SHA1_MECH_INFO_TYPE &&
	    mechanism->cm_type != SHA256_MECH_INFO_TYPE &&
	    mechanism->cm_type != SHA384_MECH_INFO_TYPE &&
	    mechanism->cm_type != SHA512_MECH_INFO_TYPE) {
		cmn_err(CE_WARN, "dprov_digest_init: unexpected mech type "
		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	/* submit request to the taskq */
	error = dprov_digest_submit_req(DPROV_REQ_DIGEST_INIT, softc, req,
	    mechanism, NULL, NULL, NULL, ctx, KM_SLEEP);

	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_digest_init: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_digest(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *digest,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_digest: started\n", instance));

	/* submit request to the taskq */
	error = dprov_digest_submit_req(DPROV_REQ_DIGEST, softc, req,
	    NULL, data, NULL, digest, ctx, KM_NOSLEEP);

	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_digest: done, err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_digest_update(crypto_ctx_t *ctx, crypto_data_t *data,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_digest_update: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_digest_submit_req(DPROV_REQ_DIGEST_UPDATE, softc,
	    req, NULL, data, NULL, NULL, ctx, KM_NOSLEEP);

	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_digest_update: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_digest_key(crypto_ctx_t *ctx, crypto_key_t *key, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_digest_key: started\n", instance));

	/* submit request to the taskq */
	error = dprov_digest_submit_req(DPROV_REQ_DIGEST_KEY, softc, req, NULL,
	    NULL, key, NULL, ctx, KM_NOSLEEP);

	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_digest_key: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_digest_final(crypto_ctx_t *ctx, crypto_data_t *digest,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_digest_final: started\n", instance));

	/* submit request to the taskq */
	error = dprov_digest_submit_req(DPROV_REQ_DIGEST_FINAL, softc, req,
	    NULL, NULL, NULL, digest, ctx, KM_NOSLEEP);

	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_digest_final: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

/* ARGSUSED */
static int
dprov_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)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_digest_atomic: started\n",
	    instance));

	/* check mechanism */
	if (mechanism->cm_type != MD4_MECH_INFO_TYPE &&
	    mechanism->cm_type != MD5_MECH_INFO_TYPE &&
	    mechanism->cm_type != SHA1_MECH_INFO_TYPE &&
	    mechanism->cm_type != SHA256_MECH_INFO_TYPE &&
	    mechanism->cm_type != SHA384_MECH_INFO_TYPE &&
	    mechanism->cm_type != SHA512_MECH_INFO_TYPE) {
		cmn_err(CE_WARN, "dprov_digest_atomic: unexpected mech type "
		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	/* submit request to the taskq */
	error = dprov_digest_submit_req(DPROV_REQ_DIGEST_ATOMIC, softc, req,
	    mechanism, data, NULL, digest, NULL, KM_SLEEP);

	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_digest_atomic: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

/*
 * MAC entry points.
 */

/*
 * Checks whether the specified mech_type is supported by mac
 * entry points.
 */
static boolean_t
dprov_valid_mac_mech(crypto_mech_type_t mech_type)
{
	return (mech_type == MD5_HMAC_MECH_INFO_TYPE ||
	    mech_type == MD5_HMAC_GEN_MECH_INFO_TYPE ||
	    mech_type == SHA1_HMAC_MECH_INFO_TYPE ||
	    mech_type == SHA1_HMAC_GEN_MECH_INFO_TYPE ||
	    mech_type == SHA256_HMAC_MECH_INFO_TYPE ||
	    mech_type == SHA256_HMAC_GEN_MECH_INFO_TYPE ||
	    mech_type == SHA384_HMAC_MECH_INFO_TYPE ||
	    mech_type == SHA384_HMAC_GEN_MECH_INFO_TYPE ||
	    mech_type == SHA512_HMAC_MECH_INFO_TYPE ||
	    mech_type == SHA512_HMAC_GEN_MECH_INFO_TYPE);
}

static int
dprov_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)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac_init: started\n", instance));

	/* check mechanism */
	if (!dprov_valid_mac_mech(mechanism->cm_type)) {
		cmn_err(CE_WARN, "dprov_mac_init: unexpected mech type "
		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	if (ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* submit request to the taskq */
	error = dprov_mac_submit_req(DPROV_REQ_MAC_INIT, softc, req,
	    mechanism, NULL, key, NULL, ctx, 0, KM_SLEEP);

	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac_init: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_mac(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *mac,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac: started\n", instance));

	/* submit request to the taskq */
	error = dprov_mac_submit_req(DPROV_REQ_MAC, softc, req,
	    NULL, data, NULL, mac, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac: done, err = 0x%x\n", instance,
	    error));

	return (error);
}

static int
dprov_mac_update(crypto_ctx_t *ctx, crypto_data_t *data,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac_update: started\n", instance));

	/* submit request to the taskq */
	error = dprov_mac_submit_req(DPROV_REQ_MAC_UPDATE, softc,
	    req, NULL, data, NULL, NULL, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac_update: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_mac_final(crypto_ctx_t *ctx, crypto_data_t *mac, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac_final: started\n", instance));

	/* submit request to the taskq */
	error = dprov_mac_submit_req(DPROV_REQ_MAC_FINAL, softc, req,
	    NULL, NULL, NULL, mac, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac_final: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_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)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac_atomic: started\n", instance));

	if (ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* check mechanism */
	if (!dprov_valid_mac_mech(mechanism->cm_type)) {
		cmn_err(CE_WARN, "dprov_mac_atomic: unexpected mech type "
		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	/* submit request to the taskq */
	error = dprov_mac_submit_req(DPROV_REQ_MAC_ATOMIC, softc, req,
	    mechanism, data, key, mac, NULL, session_id, KM_SLEEP);

	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac_atomic: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_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)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac_verify_atomic: started\n",
	    instance));

	if (ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* check mechanism */
	if (!dprov_valid_mac_mech(mechanism->cm_type)) {
		cmn_err(CE_WARN, "dprov_mac_verify_atomic: unexpected mech "
		    "type 0x%llx\n", (unsigned long long)mechanism->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	/* submit request to the taskq */
	error = dprov_mac_submit_req(DPROV_REQ_MAC_VERIFY_ATOMIC, softc, req,
	    mechanism, data, key, mac, NULL, session_id, KM_SLEEP);

	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac_verify_atomic: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

/*
 * Cipher (encrypt/decrypt) entry points.
 */

/*
 * Checks whether the specified mech_type is supported by cipher entry
 * points.
 */
static boolean_t
dprov_valid_cipher_mech(crypto_mech_type_t mech_type)
{
	return (mech_type == DES_CBC_MECH_INFO_TYPE ||
		mech_type == DES3_CBC_MECH_INFO_TYPE ||
		mech_type == DES_ECB_MECH_INFO_TYPE ||
		mech_type == DES3_ECB_MECH_INFO_TYPE ||
		mech_type == BLOWFISH_CBC_MECH_INFO_TYPE ||
		mech_type == BLOWFISH_ECB_MECH_INFO_TYPE ||
		mech_type == AES_CBC_MECH_INFO_TYPE ||
		mech_type == AES_ECB_MECH_INFO_TYPE ||
		mech_type == AES_CTR_MECH_INFO_TYPE ||
		mech_type == RC4_MECH_INFO_TYPE ||
		mech_type == RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == RSA_X_509_MECH_INFO_TYPE ||
		mech_type == MD5_RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == SHA1_RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == SHA256_RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == SHA384_RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == SHA512_RSA_PKCS_MECH_INFO_TYPE);
}

static boolean_t
is_publickey_mech(crypto_mech_type_t mech_type)
{
	return (mech_type == RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == RSA_X_509_MECH_INFO_TYPE ||
		mech_type == MD5_RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == SHA1_RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == SHA256_RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == SHA384_RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == SHA512_RSA_PKCS_MECH_INFO_TYPE);
}


/* ARGSUSED */
static int
dprov_encrypt_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)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_encrypt_init: started\n",
	    instance));

	/* check mechanism */
	if (!dprov_valid_cipher_mech(mechanism->cm_type)) {
		cmn_err(CE_WARN, "dprov_encrypt_init: unexpected mech type "
		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	/* submit request to the taskq */
	error = dprov_cipher_submit_req(DPROV_REQ_ENCRYPT_INIT, softc,
	    req, mechanism, key, NULL, NULL, ctx, 0, KM_SLEEP);

	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_encrypt_init: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

/* ARGSUSED */
static int
dprov_encrypt(crypto_ctx_t *ctx, crypto_data_t *plaintext,
    crypto_data_t *ciphertext, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_encrypt: started\n", instance));

	/* submit request to the taskq */
	error = dprov_cipher_submit_req(DPROV_REQ_ENCRYPT, softc,
	    req, NULL, NULL, plaintext, ciphertext, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_encrypt: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

/* ARGSUSED */
static int
dprov_encrypt_update(crypto_ctx_t *ctx, crypto_data_t *plaintext,
    crypto_data_t *ciphertext, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_encrypt_update: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_cipher_submit_req(DPROV_REQ_ENCRYPT_UPDATE, softc,
	    req, NULL, NULL, plaintext, ciphertext, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_encrypt_update: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

/* ARGSUSED */
static int
dprov_encrypt_final(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_encrypt_final: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_cipher_submit_req(DPROV_REQ_ENCRYPT_FINAL, softc,
	    req, NULL, NULL, NULL, ciphertext, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_encrypt_final: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_encrypt_atomic(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
    crypto_key_t *key, crypto_data_t *plaintext, crypto_data_t *ciphertext,
    crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_MAC, ("(%d) dprov_encrypt_atomic: started\n", instance));

	if (ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* check mechanism */
	if (!dprov_valid_cipher_mech(mechanism->cm_type)) {
		cmn_err(CE_WARN, "dprov_encrypt_atomic: unexpected mech type "
		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	error = dprov_cipher_submit_req(DPROV_REQ_ENCRYPT_ATOMIC, softc,
	    req, mechanism, key, plaintext, ciphertext, NULL, session_id,
	    KM_SLEEP);

	DPROV_DEBUG(D_MAC, ("(%d) dprov_encrypt_atomic: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

/* ARGSUSED */
static int
dprov_decrypt_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)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_decrypt_init: started\n",
	    instance));

	/* check mechanism */
	if (!dprov_valid_cipher_mech(mechanism->cm_type)) {
		cmn_err(CE_WARN, "dprov_decrypt_init: unexpected mech type "
		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	/* submit request to the taskq */
	error = dprov_cipher_submit_req(DPROV_REQ_DECRYPT_INIT, softc,
	    req, mechanism, key, NULL, NULL, ctx, 0, KM_SLEEP);

	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_decrypt_init: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

/* ARGSUSED */
static int
dprov_decrypt(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
    crypto_data_t *plaintext, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;

	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_decrypt: started\n", instance));

	/* submit request to the taskq */
	error = dprov_cipher_submit_req(DPROV_REQ_DECRYPT, softc,
	    req, NULL, NULL, plaintext, ciphertext, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_decrypt: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

/* ARGSUSED */
static int
dprov_decrypt_update(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
    crypto_data_t *plaintext, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_decrypt_update: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_cipher_submit_req(DPROV_REQ_DECRYPT_UPDATE, softc,
	    req, NULL, NULL, plaintext, ciphertext, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_decrypt_update: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

/* ARGSUSED */
static int
dprov_decrypt_final(crypto_ctx_t *ctx, crypto_data_t *plaintext,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_decrypt_final: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_cipher_submit_req(DPROV_REQ_DECRYPT_FINAL, softc,
	    req, NULL, NULL, plaintext, NULL, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_decrypt_final: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_decrypt_atomic(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
    crypto_key_t *key, crypto_data_t *ciphertext, crypto_data_t *plaintext,
    crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_MAC, ("(%d) dprov_decrypt_atomic: started\n", instance));

	if (ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* check mechanism */
	if (!dprov_valid_cipher_mech(mechanism->cm_type)) {
		cmn_err(CE_WARN, "dprov_atomic_init: unexpected mech type "
		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	error = dprov_cipher_submit_req(DPROV_REQ_DECRYPT_ATOMIC, softc,
	    req, mechanism, key, plaintext, ciphertext, NULL, session_id,
	    KM_SLEEP);

	DPROV_DEBUG(D_MAC, ("(%d) dprov_decrypt_atomic: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

/*
 * Sign entry points.
 */

/*
 * Checks whether the specified mech_type is supported by sign/verify
 * entry points.
 */
static boolean_t
dprov_valid_sign_verif_mech(crypto_mech_type_t mech_type)
{
	return (mech_type == MD5_HMAC_MECH_INFO_TYPE ||
		mech_type == MD5_HMAC_GEN_MECH_INFO_TYPE ||
		mech_type == RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == RSA_X_509_MECH_INFO_TYPE ||
		mech_type == MD5_RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == SHA1_RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == SHA256_RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == SHA384_RSA_PKCS_MECH_INFO_TYPE ||
		mech_type == SHA512_RSA_PKCS_MECH_INFO_TYPE);
}

static int
dprov_sign_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)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_init: started\n", instance));

	/* check mechanism */
	if (!dprov_valid_sign_verif_mech(mechanism->cm_type)) {
		cmn_err(CE_WARN, "dprov_sign_init: unexpected mech type "
		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	if (ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* submit request to the taskq */
	error = dprov_sign_submit_req(DPROV_REQ_SIGN_INIT, softc, req,
	    mechanism, key, NULL, NULL, ctx, 0, KM_SLEEP);

	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_init: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_sign(crypto_ctx_t *ctx, crypto_data_t *data,
    crypto_data_t *signature, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign: started\n", instance));

	/* submit request to the taskq */
	error = dprov_sign_submit_req(DPROV_REQ_SIGN, softc, req,
	    NULL, NULL, data, signature, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_sign_update(crypto_ctx_t *ctx, crypto_data_t *data,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_update: started\n", instance));

	/* submit request to the taskq */
	error = dprov_sign_submit_req(DPROV_REQ_SIGN_UPDATE, softc, req,
	    NULL, NULL, data, NULL, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_update: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_sign_final(crypto_ctx_t *ctx, crypto_data_t *signature,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_final: started\n", instance));

	/* submit request to the taskq */
	error = dprov_sign_submit_req(DPROV_REQ_SIGN_FINAL, softc, req,
	    NULL, NULL, NULL, signature, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_final: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_sign_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 *signature,
    crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_atomic: started\n", instance));

	/* check mechanism */
	if (!dprov_valid_sign_verif_mech(mechanism->cm_type)) {
		cmn_err(CE_WARN, "dprov_sign_atomic: unexpected mech type "
		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	if (ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* submit request to the taskq */
	error = dprov_sign_submit_req(DPROV_REQ_SIGN_ATOMIC, softc, req,
	    mechanism, key, data, signature, NULL, session_id, KM_SLEEP);

	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_atomic: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_sign_recover_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)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_recover_init: started\n",
	    instance));

	if (ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* submit request to the taskq */
	error = dprov_sign_submit_req(DPROV_REQ_SIGN_RECOVER_INIT, softc, req,
	    mechanism, key, NULL, NULL, ctx, 0, KM_SLEEP);

	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_recover_init: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_sign_recover(crypto_ctx_t *ctx, crypto_data_t *data,
    crypto_data_t *signature, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_recover: started\n", instance));

	/* submit request to the taskq */
	error = dprov_sign_submit_req(DPROV_REQ_SIGN_RECOVER, softc, req,
	    NULL, NULL, data, signature, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_recover: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_sign_recover_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 *signature,
    crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_recover_atomic: started\n",
	    instance));

	if (ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* submit request to the taskq */
	error = dprov_sign_submit_req(DPROV_REQ_SIGN_RECOVER_ATOMIC, softc, req,
	    mechanism, key, data, signature, NULL, session_id, KM_SLEEP);

	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_recover_atomic: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

/*
 * Verify entry points.
 */

static int
dprov_verify_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)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_init: started\n", instance));

	/* check mechanism */
	if (!dprov_valid_sign_verif_mech(mechanism->cm_type)) {
		cmn_err(CE_WARN, "dprov_verify_init: unexpected mech type "
		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	if (ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	error = dprov_verify_submit_req(DPROV_REQ_VERIFY_INIT, softc, req,
	    mechanism, key, NULL, NULL, ctx, 0, KM_SLEEP);

	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_init: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_verify(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *signature,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify: started\n", instance));

	/* submit request to the taskq */
	error = dprov_verify_submit_req(DPROV_REQ_VERIFY, softc, req,
	    NULL, NULL, data, signature, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_verify_update(crypto_ctx_t *ctx, crypto_data_t *data,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_update: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_verify_submit_req(DPROV_REQ_VERIFY_UPDATE, softc, req,
	    NULL, NULL, data, NULL, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_update: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_verify_final(crypto_ctx_t *ctx, crypto_data_t *signature,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_final: started\n", instance));

	/* submit request to the taskq */
	error = dprov_verify_submit_req(DPROV_REQ_VERIFY_FINAL, softc, req,
	    NULL, NULL, NULL, signature, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_final: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_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 *signature,
    crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_atomic: started\n",
	    instance));

	/* check mechanism */
	if (!dprov_valid_sign_verif_mech(mechanism->cm_type)) {
		cmn_err(CE_WARN, "dprov_verify_atomic: unexpected mech type "
		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	if (ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* submit request to the taskq */
	error = dprov_verify_submit_req(DPROV_REQ_VERIFY_ATOMIC, softc, req,
	    mechanism, key, data, signature, NULL, session_id, KM_SLEEP);

	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_atomic: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_verify_recover_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)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_recover_init: started\n",
	    instance));

	if (ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* submit request to the taskq */
	error = dprov_verify_submit_req(DPROV_REQ_VERIFY_RECOVER_INIT, softc,
	    req, mechanism, key, NULL, NULL, ctx, 0, KM_SLEEP);

	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_recover_init: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

static int
dprov_verify_recover(crypto_ctx_t *ctx, crypto_data_t *signature,
    crypto_data_t *data, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_recover: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_verify_submit_req(DPROV_REQ_VERIFY_RECOVER, softc, req,
	    NULL, NULL, data, signature, ctx, 0, KM_NOSLEEP);

	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_recover: done err = 0x%x\n",
	    instance, error));

	return (error);
}

static int
dprov_verify_recover_atomic(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
    crypto_key_t *key, crypto_data_t *signature, crypto_data_t *data,
    crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_recover_atomic: started\n",
	    instance));

	if (ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* submit request to the taskq */
	error = dprov_verify_submit_req(DPROV_REQ_VERIFY_RECOVER_ATOMIC, softc,
	    req, mechanism, key, data, signature, NULL, session_id, KM_SLEEP);

	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_recover_atomic: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

/*
 * Dual operations entry points.
 */

static int
dprov_digest_encrypt_update(crypto_ctx_t *digest_ctx,
    crypto_ctx_t *encrypt_ctx, crypto_data_t *plaintext,
    crypto_data_t *ciphertext, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(digest_ctx, softc, instance);
	DPROV_DEBUG(D_DUAL, ("(%d) dprov_digest_encrypt_update: started\n",
	    instance));

	if (digest_ctx->cc_provider != encrypt_ctx->cc_provider)
		return (CRYPTO_INVALID_CONTEXT);

	/* submit request to the taskq */
	error = dprov_dual_submit_req(DPROV_REQ_DIGEST_ENCRYPT_UPDATE,
	    softc, req, digest_ctx, encrypt_ctx, plaintext, ciphertext);

	DPROV_DEBUG(D_DUAL, ("(%d) dprov_digest_encrypt_update: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

static int
dprov_decrypt_digest_update(crypto_ctx_t *decrypt_ctx, crypto_ctx_t *digest_ctx,
    crypto_data_t *ciphertext, crypto_data_t *plaintext,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(decrypt_ctx, softc, instance);
	DPROV_DEBUG(D_DUAL, ("(%d) dprov_decrypt_digest_update: started\n",
	    instance));

	if (decrypt_ctx->cc_provider != digest_ctx->cc_provider)
		return (CRYPTO_INVALID_CONTEXT);

	/* submit request to the taskq */
	error = dprov_dual_submit_req(DPROV_REQ_DECRYPT_DIGEST_UPDATE,
	    softc, req, digest_ctx, decrypt_ctx, plaintext, ciphertext);

	DPROV_DEBUG(D_DUAL, ("(%d) dprov_decrypt_digest_update: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

static int
dprov_sign_encrypt_update(crypto_ctx_t *sign_ctx, crypto_ctx_t *encrypt_ctx,
    crypto_data_t *plaintext, crypto_data_t *ciphertext,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(sign_ctx, softc, instance);
	DPROV_DEBUG(D_DUAL, ("(%d) dprov_sign_encrypt_update: started\n",
	    instance));

	if (sign_ctx->cc_provider != encrypt_ctx->cc_provider)
		return (CRYPTO_INVALID_CONTEXT);

	/* submit request to the taskq */
	error = dprov_dual_submit_req(DPROV_REQ_SIGN_ENCRYPT_UPDATE,
	    softc, req, sign_ctx, encrypt_ctx, plaintext, ciphertext);

	DPROV_DEBUG(D_DUAL, ("(%d) dprov_sign_encrypt_update: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

static int
dprov_decrypt_verify_update(crypto_ctx_t *decrypt_ctx, crypto_ctx_t *verify_ctx,
    crypto_data_t *ciphertext, crypto_data_t *plaintext,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(decrypt_ctx, softc, instance);
	DPROV_DEBUG(D_DUAL, ("(%d) dprov_decrypt_verify_update: started\n",
	    instance));

	if (decrypt_ctx->cc_provider != verify_ctx->cc_provider)
		return (CRYPTO_INVALID_CONTEXT);

	/* submit request to the taskq */
	error = dprov_dual_submit_req(DPROV_REQ_DECRYPT_VERIFY_UPDATE,
	    softc, req, verify_ctx, decrypt_ctx, plaintext, ciphertext);

	DPROV_DEBUG(D_DUAL, ("(%d) dprov_decrypt_verify_update: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

/*
 * Dual cipher-mac entry points.
 */

static int
dprov_encrypt_mac_init(crypto_ctx_t *ctx, crypto_mechanism_t *encrypt_mech,
    crypto_key_t *encrypt_key, crypto_mechanism_t *mac_mech,
    crypto_key_t *mac_key, crypto_spi_ctx_template_t encr_ctx_template,
    crypto_spi_ctx_template_t mac_ctx_template,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_encrypt_mac_init: started\n",
	    instance));

	/* check mechanisms */
	if (!dprov_valid_cipher_mech(encrypt_mech->cm_type)) {
		cmn_err(CE_WARN, "dprov_encrypt_mac_init: unexpected encrypt "
		    "mech type 0x%llx\n",
		    (unsigned long long)encrypt_mech->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}
	if (!dprov_valid_mac_mech(mac_mech->cm_type)) {
		cmn_err(CE_WARN, "dprov_encrypt_mac_init: unexpected mac "
		    "mech type 0x%llx\n",
		    (unsigned long long)mac_mech->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	if (encr_ctx_template != NULL || mac_ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* submit request to the taskq */
	error = dprov_cipher_mac_submit_req(DPROV_REQ_ENCRYPT_MAC_INIT,
	    softc, req, ctx, 0, encrypt_mech, encrypt_key, mac_mech, mac_key,
	    NULL, NULL, NULL, KM_SLEEP);

	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_encrypt_mac_init: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

static int
dprov_encrypt_mac(crypto_ctx_t *ctx, crypto_data_t *plaintext,
    crypto_dual_data_t *ciphertext, crypto_data_t *mac, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_encrypt_mac: started\n",
	    instance));

	/*
	 * submit request to the taskq
	 * Careful! cihertext/plaintext order inversion
	 */
	error = dprov_cipher_mac_submit_req(DPROV_REQ_ENCRYPT_MAC,
	    softc, req, ctx, 0, NULL, NULL, NULL, NULL,
	    ciphertext, plaintext, mac, KM_NOSLEEP);

	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_encrypt_mac: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

static int
dprov_encrypt_mac_update(crypto_ctx_t *ctx, crypto_data_t *plaintext,
    crypto_dual_data_t *ciphertext, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_encrypt_mac_update: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_cipher_mac_submit_req(DPROV_REQ_ENCRYPT_MAC_UPDATE,
	    softc, req, ctx, 0, NULL, NULL, NULL, NULL,
	    ciphertext, plaintext, NULL, KM_NOSLEEP);

	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_encrypt_mac_update: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

static int
dprov_encrypt_mac_final(crypto_ctx_t *ctx,
    crypto_dual_data_t *ciphertext, crypto_data_t *mac,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_encrypt_mac_final: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_cipher_mac_submit_req(DPROV_REQ_ENCRYPT_MAC_FINAL,
	    softc, req, ctx, 0, NULL, NULL, NULL, NULL,
	    ciphertext, NULL, mac, KM_NOSLEEP);

	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_encrypt_mac_final: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

static int
dprov_encrypt_mac_atomic(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_mechanism_t *encrypt_mech,
    crypto_key_t *encrypt_key, crypto_mechanism_t *mac_mech,
    crypto_key_t *mac_key, crypto_data_t *plaintext,
    crypto_dual_data_t *ciphertext, crypto_data_t *mac,
    crypto_spi_ctx_template_t encr_ctx_template,
    crypto_spi_ctx_template_t mac_ctx_template,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_encrypt_mac_atomic: started\n",
	    instance));

	/* check mechanisms */
	if (!dprov_valid_cipher_mech(encrypt_mech->cm_type)) {
		cmn_err(CE_WARN, "dprov_encrypt_mac_atomic: unexpected encrypt "
		    "mech type 0x%llx\n",
		    (unsigned long long)encrypt_mech->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}
	if (!dprov_valid_mac_mech(mac_mech->cm_type)) {
		cmn_err(CE_WARN, "dprov_encrypt_mac_atomic: unexpected mac "
		    "mech type 0x%llx\n",
		    (unsigned long long)mac_mech->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	if (encr_ctx_template != NULL || mac_ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* submit request to the taskq */
	error = dprov_cipher_mac_submit_req(DPROV_REQ_ENCRYPT_MAC_ATOMIC,
	    softc, req, NULL, session_id, encrypt_mech, encrypt_key, mac_mech,
	    mac_key, ciphertext, plaintext, mac, KM_SLEEP);

	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_encrypt_mac_atomic: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

static int
dprov_mac_decrypt_init(crypto_ctx_t *ctx, crypto_mechanism_t *mac_mech,
    crypto_key_t *mac_key, crypto_mechanism_t *decrypt_mech,
    crypto_key_t *decrypt_key, crypto_spi_ctx_template_t mac_ctx_template,
    crypto_spi_ctx_template_t decr_ctx_template,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_mac_decrypt_init: started\n",
	    instance));

	/* check mechanisms */
	if (!dprov_valid_cipher_mech(decrypt_mech->cm_type)) {
		cmn_err(CE_WARN, "dprov_mac_decrypt_init: unexpected decrypt "
		    "mech type 0x%llx\n",
		    (unsigned long long)decrypt_mech->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}
	if (!dprov_valid_mac_mech(mac_mech->cm_type)) {
		cmn_err(CE_WARN, "dprov_mac_decrypt_init: unexpected mac "
		    "mech type 0x%llx\n",
		    (unsigned long long)mac_mech->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	if (decr_ctx_template != NULL || mac_ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* submit request to the taskq */
	error = dprov_cipher_mac_submit_req(DPROV_REQ_MAC_DECRYPT_INIT,
	    softc, req, ctx, 0, decrypt_mech, decrypt_key, mac_mech, mac_key,
	    NULL, NULL, NULL, KM_SLEEP);

	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_mac_decrypt_init: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

static int
dprov_mac_decrypt(crypto_ctx_t *ctx, crypto_dual_data_t *ciphertext,
    crypto_data_t *mac, crypto_data_t *plaintext, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_mac_decrypt: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_cipher_mac_submit_req(DPROV_REQ_MAC_DECRYPT,
	    softc, req, ctx, 0, NULL, NULL, NULL, NULL,
	    ciphertext, plaintext, mac, KM_NOSLEEP);

	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_mac_decrypt: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

static int
dprov_mac_decrypt_update(crypto_ctx_t *ctx, crypto_dual_data_t *ciphertext,
    crypto_data_t *plaintext, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_mac_decrypt_update: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_cipher_mac_submit_req(DPROV_REQ_MAC_DECRYPT_UPDATE,
	    softc, req, ctx, 0, NULL, NULL, NULL, NULL,
	    ciphertext, plaintext, NULL, KM_NOSLEEP);

	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_mac_decrypt_update: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

static int
dprov_mac_decrypt_final(crypto_ctx_t *ctx, crypto_data_t *mac,
    crypto_data_t *plaintext, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	/* extract softc and instance number from context */
	DPROV_SOFTC_FROM_CTX(ctx, softc, instance);
	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_mac_decrypt_final: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_cipher_mac_submit_req(DPROV_REQ_MAC_DECRYPT_FINAL,
	    softc, req, ctx, 0, NULL, NULL, NULL, NULL,
	    NULL, plaintext, mac, KM_NOSLEEP);

	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_mac_decrypt_final: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

static int
dprov_mac_decrypt_atomic(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_mechanism_t *mac_mech,
    crypto_key_t *mac_key, crypto_mechanism_t *decrypt_mech,
    crypto_key_t *decrypt_key, crypto_dual_data_t *ciphertext,
    crypto_data_t *mac, crypto_data_t *plaintext,
    crypto_spi_ctx_template_t mac_ctx_template,
    crypto_spi_ctx_template_t decr_ctx_template,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_mac_decrypt_atomic: started\n",
	    instance));

	/* check mechanisms */
	if (!dprov_valid_cipher_mech(decrypt_mech->cm_type)) {
		cmn_err(CE_WARN, "dprov_mac_decrypt_atomic: unexpected encrypt "
		    "mech type 0x%llx\n",
		    (unsigned long long)decrypt_mech->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}
	if (!dprov_valid_mac_mech(mac_mech->cm_type)) {
		cmn_err(CE_WARN, "dprov_mac_decrypt_atomic: unexpected mac "
		    "mech type 0x%llx\n",
		    (unsigned long long)mac_mech->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	if (decr_ctx_template != NULL || mac_ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* submit request to the taskq */
	error = dprov_cipher_mac_submit_req(DPROV_REQ_MAC_DECRYPT_ATOMIC,
	    softc, req, NULL, session_id, decrypt_mech, decrypt_key, mac_mech,
	    mac_key, ciphertext, plaintext, mac, KM_SLEEP);

	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_mac_decrypt_atomic: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

static int
dprov_mac_verify_decrypt_atomic(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_mechanism_t *mac_mech,
    crypto_key_t *mac_key, crypto_mechanism_t *decrypt_mech,
    crypto_key_t *decrypt_key, crypto_dual_data_t *ciphertext,
    crypto_data_t *mac, crypto_data_t *plaintext,
    crypto_spi_ctx_template_t mac_ctx_template,
    crypto_spi_ctx_template_t decr_ctx_template,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_mac_verify_decrypt_atomic:"
	    "started\n", instance));

	/* check mechanisms */
	if (!dprov_valid_cipher_mech(decrypt_mech->cm_type)) {
		cmn_err(CE_WARN, "dprov_mac_verify_decrypt_atomic: "
		    "unexpected encrypt mech type 0x%llx\n",
		    (unsigned long long)decrypt_mech->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}
	if (!dprov_valid_mac_mech(mac_mech->cm_type)) {
		cmn_err(CE_WARN, "dprov_mac_verify_decrypt_atomic: "
		    "unexpected mac mech type 0x%llx\n",
		    (unsigned long long)mac_mech->cm_type);
		return (CRYPTO_MECHANISM_INVALID);
	}

	if (decr_ctx_template != NULL || mac_ctx_template != NULL)
		return (CRYPTO_ARGUMENTS_BAD);

	/* submit request to the taskq */
	error = dprov_cipher_mac_submit_req(DPROV_REQ_MAC_VERIFY_DECRYPT_ATOMIC,
	    softc, req, NULL, session_id, decrypt_mech, decrypt_key, mac_mech,
	    mac_key, ciphertext, plaintext, mac, KM_SLEEP);

	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_mac_verify_decrypt_atomic: done "
	    "err = 0x%x\n", instance, error));

	return (error);
}

/*
 * Random number entry points.
 */

static int
dprov_seed_random(crypto_provider_handle_t provider,  crypto_session_id_t sid,
    uchar_t *buf, size_t len, uint_t entropy_est, uint32_t flags,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_RANDOM, ("(%d) dprov_seed_random: started\n",
	    instance));

	error = dprov_random_submit_req(DPROV_REQ_RANDOM_SEED, softc,
	    req, buf, len, sid, entropy_est, flags);

	DPROV_DEBUG(D_RANDOM, ("(%d) dprov_seed_random: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_generate_random(crypto_provider_handle_t provider,
    crypto_session_id_t sid, uchar_t *buf, size_t len, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_RANDOM, ("(%d) dprov_generate_random: started\n",
	    instance));

	error = dprov_random_submit_req(DPROV_REQ_RANDOM_GENERATE, softc,
	    req, buf, len, sid, 0, 0);

	DPROV_DEBUG(D_RANDOM, ("(%d) dprov_generate_random: done "
	    "err = 0x0%x\n", instance, error));

	return (error);
}

/*
 * Session Management entry points.
 */

static int
dprov_session_open(crypto_provider_handle_t provider,
    crypto_session_id_t *session_id, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_SESSION, ("(%d) dprov_session_open: started\n",
	    instance));

	error = dprov_session_submit_req(DPROV_REQ_SESSION_OPEN, softc,
	    req, session_id, 0, 0, NULL, 0);

	DPROV_DEBUG(D_SESSION, ("(%d) dprov_session_open: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_session_close(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_SESSION, ("(%d) dprov_session_close: started\n",
	    instance));

	error = dprov_session_submit_req(DPROV_REQ_SESSION_CLOSE, softc,
	    req, 0, session_id, 0, NULL, 0);

	DPROV_DEBUG(D_SESSION, ("(%d) dprov_session_close: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_session_login(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_user_type_t user_type,
    char *pin, size_t pin_len, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_SESSION, ("(%d) dprov_session_login: started\n",
	    instance));

	error = dprov_session_submit_req(DPROV_REQ_SESSION_LOGIN, softc,
	    req, 0, session_id, user_type, pin, pin_len);

	DPROV_DEBUG(D_SESSION, ("(%d) dprov_session_login: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_session_logout(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_SESSION, ("(%d) dprov_session_logout: started\n",
	    instance));

	error = dprov_session_submit_req(DPROV_REQ_SESSION_LOGOUT, softc,
	    req, 0, session_id, 0, NULL, 0);

	DPROV_DEBUG(D_SESSION, ("(%d) dprov_session_logout: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

/*
 * Object management entry points.
 */

static int
dprov_object_create(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_object_attribute_t *template,
    uint_t attribute_count, crypto_object_id_t *object,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_create: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_object_submit_req(DPROV_REQ_OBJECT_CREATE, softc, req,
	    session_id, 0, template, attribute_count, object, NULL, NULL,
	    NULL, 0, NULL, KM_NOSLEEP);

	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_create: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_object_copy(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_object_id_t object,
    crypto_object_attribute_t *template, uint_t attribute_count,
    crypto_object_id_t *new_object, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_copy: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_object_submit_req(DPROV_REQ_OBJECT_COPY, softc, req,
	    session_id, object, template, attribute_count, new_object,
	    NULL, NULL, NULL, 0, NULL, KM_NOSLEEP);

	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_copy: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_object_destroy(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_object_id_t object,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_destroy: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_object_submit_req(DPROV_REQ_OBJECT_DESTROY, softc, req,
	    session_id, object, NULL, 0, NULL, NULL, NULL, NULL, 0, NULL,
	    KM_NOSLEEP);

	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_destroy: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_object_get_size(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_object_id_t object,
    size_t *size, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_get_size: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_object_submit_req(DPROV_REQ_OBJECT_GET_SIZE, softc, req,
	    session_id, object, NULL, 0, NULL, size, NULL, NULL, 0, NULL,
	    KM_NOSLEEP);

	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_get_size: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_object_get_attribute_value(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_object_id_t object,
    crypto_object_attribute_t *template, uint_t attribute_count,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_get_attribute_value: "
	    "started\n", instance));

	/* submit request to the taskq */
	error = dprov_object_submit_req(DPROV_REQ_OBJECT_GET_ATTRIBUTE_VALUE,
	    softc, req, session_id, object, template, attribute_count,
	    NULL, NULL, NULL, NULL, 0, NULL, KM_NOSLEEP);

	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_get_attribute_value: "
	    "done err = 0x0%x\n", instance, error));

	return (error);
}

static int
dprov_object_set_attribute_value(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_object_id_t object,
    crypto_object_attribute_t *template, uint_t attribute_count,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_set_attribute_value: "
	    "started\n", instance));

	/* submit request to the taskq */
	error = dprov_object_submit_req(DPROV_REQ_OBJECT_SET_ATTRIBUTE_VALUE,
	    softc, req, session_id, object, template, attribute_count,
	    NULL, NULL, NULL, NULL, 0, NULL, KM_NOSLEEP);

	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_set_attribute_value: "
	    "done err = 0x0%x\n", instance, error));

	return (error);
}

static int
dprov_object_find_init(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_object_attribute_t *template,
    uint_t attribute_count, void **provider_private,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_find_init: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_object_submit_req(DPROV_REQ_OBJECT_FIND_INIT, softc, req,
	    session_id, 0, template, attribute_count, NULL, NULL,
	    provider_private, NULL, 0, NULL, KM_SLEEP);

	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_find_init: done "
	    "err = 0x0%x\n", instance, error));

	return (error);
}

static int
dprov_object_find(crypto_provider_handle_t provider, void *provider_private,
    crypto_object_id_t *objects, uint_t max_object_count,
    uint_t *object_count, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_find: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_object_submit_req(DPROV_REQ_OBJECT_FIND, softc, req,
	    0, 0, NULL, 0, objects, NULL, NULL, provider_private,
	    max_object_count, object_count, KM_NOSLEEP);


	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_find: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_object_find_final(crypto_provider_handle_t provider,
    void *provider_private, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_find_final: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_object_submit_req(DPROV_REQ_OBJECT_FIND_FINAL, softc, req,
	    0, 0, NULL, 0, NULL, NULL, NULL, provider_private,
	    0, NULL, KM_NOSLEEP);

	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_find_final: done "
	    "err = 0x0%x\n", instance, error));

	return (error);
}

/*
 * Key management entry points.
 */

static int
dprov_key_generate(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
    crypto_object_attribute_t *template, uint_t attribute_count,
    crypto_object_id_t *object, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_KEY, ("(%d) dprov_key_generate: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_key_submit_req(DPROV_REQ_KEY_GENERATE, softc, req,
	    session_id, mechanism, template, attribute_count, object, NULL,
	    0, NULL, NULL, NULL, 0);

	DPROV_DEBUG(D_KEY, ("(%d) dprov_key_generate: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_key_generate_pair(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
    crypto_object_attribute_t *public_key_template,
    uint_t public_key_attribute_count,
    crypto_object_attribute_t *private_key_template,
    uint_t private_key_attribute_count,
    crypto_object_id_t *public_key, crypto_object_id_t *private_key,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_KEY, ("(%d) dprov_key_generate_pair: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_key_submit_req(DPROV_REQ_KEY_GENERATE_PAIR, softc, req,
	    session_id, mechanism, public_key_template,
	    public_key_attribute_count, public_key, private_key_template,
	    private_key_attribute_count, private_key, NULL, NULL, 0);

	DPROV_DEBUG(D_KEY, ("(%d) dprov_key_generate_pair: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_key_wrap(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
    crypto_key_t *wrapping_key, crypto_object_id_t *key,
    uchar_t *wrapped_key, size_t *wrapped_key_len_ptr, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_KEY, ("(%d) dprov_key_wrap: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_key_submit_req(DPROV_REQ_KEY_WRAP, softc, req,
	    session_id, mechanism, NULL, 0, key, NULL,
	    0, NULL, wrapping_key, wrapped_key, wrapped_key_len_ptr);

	DPROV_DEBUG(D_KEY, ("(%d) dprov_key_wrap: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_key_unwrap(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
    crypto_key_t *unwrapping_key, uchar_t *wrapped_key,
    size_t *wrapped_key_len_ptr, crypto_object_attribute_t *template,
    uint_t attribute_count, crypto_object_id_t *key, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_KEY, ("(%d) dprov_key_unwrap: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_key_submit_req(DPROV_REQ_KEY_UNWRAP, softc, req,
	    session_id, mechanism, template, attribute_count, key, NULL,
	    0, NULL, unwrapping_key, wrapped_key, wrapped_key_len_ptr);

	DPROV_DEBUG(D_KEY, ("(%d) dprov_key_unwrap: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_key_derive(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
    crypto_key_t *base_key, crypto_object_attribute_t *template,
    uint_t attribute_count, crypto_object_id_t *key, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_KEY, ("(%d) dprov_key_derive: started\n",
	    instance));

	/* submit request to the taskq */
	error = dprov_key_submit_req(DPROV_REQ_KEY_DERIVE, softc, req,
	    session_id, mechanism, template, attribute_count, key, NULL,
	    0, NULL, base_key, NULL, 0);

	DPROV_DEBUG(D_KEY, ("(%d) dprov_key_derive: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

/*
 * Provider management entry points.
 */

static int
dprov_ext_info(crypto_provider_handle_t provider,
    crypto_provider_ext_info_t *ext_info, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_MGMT, ("(%d) dprov_ext_info: started\n",
	    instance));

	error = dprov_mgmt_submit_req(DPROV_REQ_MGMT_EXTINFO, softc, req,
	    0, NULL, 0, NULL, 0, NULL, ext_info);

	DPROV_DEBUG(D_MGMT, ("(%d) dprov_ext_info: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_init_token(crypto_provider_handle_t provider, char *pin, size_t pin_len,
    char *label, crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_MGMT, ("(%d) dprov_init_token: started\n",
	    instance));

	error = dprov_mgmt_submit_req(DPROV_REQ_MGMT_INITTOKEN, softc, req,
	    0, pin, pin_len, NULL, 0, label, NULL);

	DPROV_DEBUG(D_MGMT, ("(%d) dprov_init_token: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_init_pin(crypto_provider_handle_t provider,
    crypto_session_id_t session_id, char *pin, size_t pin_len,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_MGMT, ("(%d) dprov_init_pin: started\n",
	    instance));

	error = dprov_mgmt_submit_req(DPROV_REQ_MGMT_INITPIN, softc, req,
	    session_id, pin, pin_len, NULL, 0, NULL, NULL);

	DPROV_DEBUG(D_MGMT, ("(%d) dprov_init_pin: done err = 0x0%x\n",
	    instance, error));

	return (error);
}

static int
dprov_set_pin(crypto_provider_handle_t provider, crypto_session_id_t session_id,
    char *old_pin, size_t old_pin_len, char *new_pin, size_t new_pin_len,
    crypto_req_handle_t req)
{
	int error = CRYPTO_FAILED;
	dprov_state_t *softc = (dprov_state_t *)provider;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;

	instance = ddi_get_instance(softc->ds_dip);
	DPROV_DEBUG(D_MGMT, ("(%d) dprov_set_pin: started\n",
	    instance));

	error = dprov_mgmt_submit_req(DPROV_REQ_MGMT_SETPIN, softc, req,
	    session_id, new_pin, new_pin_len, old_pin, old_pin_len, NULL, NULL);

	DPROV_DEBUG(D_MGMT, ("(%d) dprov_set_pin: done err = 0x0%x\n",
	    instance, error));

	return (error);
}


/*
 * Context management entry points.
 */

/*
 * Allocate a dprov-private context based on the specified dprov request.
 * For dual cipher/mac requests, the allocated context will
 * contain a structure dprov_ctx_dual_t, for other request types,
 * it will contain a dprov_ctx_single.
 * Returns one of the CRYPTO_ status codes.
 */
static int
dprov_alloc_context(dprov_req_type_t req_type, crypto_ctx_t *spi_ctx)
{
	dprov_ctx_single_t *dprov_private;

	switch (req_type) {
	case DPROV_REQ_ENCRYPT_MAC_INIT:
	case DPROV_REQ_MAC_DECRYPT_INIT:
		dprov_private = kmem_zalloc(sizeof (dprov_ctx_dual_t),
		    KM_NOSLEEP);
		if (dprov_private == NULL)
			return (CRYPTO_HOST_MEMORY);
		dprov_private->dc_type = DPROV_CTX_DUAL;
		break;
	default:
		dprov_private = kmem_zalloc(sizeof (dprov_ctx_single_t),
		    KM_NOSLEEP);
		if (dprov_private == NULL)
			return (CRYPTO_HOST_MEMORY);
		dprov_private->dc_type = DPROV_CTX_SINGLE;
		break;
	}

	spi_ctx->cc_provider_private = (void *)dprov_private;

	return (CRYPTO_SUCCESS);
}

static int
dprov_free_context(crypto_ctx_t *ctx)
{
	if (ctx->cc_provider_private == NULL)
		return (CRYPTO_SUCCESS);

	DPROV_DEBUG(D_CONTEXT, ("dprov_free_context\n"));

	{
		/*
		 * The dprov private context could contain either
		 * a dprov_ctx_single_t or a dprov_ctx_dual_t. Free
		 * the context based on its type. The k-API contexts
		 * that were attached to the dprov private context
		 * are freed by the framework.
		 */
		dprov_ctx_single_t *ctx_single =
		    (dprov_ctx_single_t *)(ctx->cc_provider_private);

		if (ctx_single->dc_type == DPROV_CTX_SINGLE) {
			crypto_context_t context = DPROV_CTX_SINGLE(ctx);

			/*
			 * This case happens for the crypto_cancel_ctx() case.
			 * We have to cancel the SW provider context also.
			 */
			if (context != NULL)
				crypto_cancel_ctx(context);

			kmem_free(ctx_single, sizeof (dprov_ctx_single_t));
		} else {
			crypto_context_t cipher_context =
			    DPROV_CTX_DUAL_CIPHER(ctx);
			crypto_context_t mac_context = DPROV_CTX_DUAL_MAC(ctx);

			/* See comments above. */
			if (cipher_context != NULL)
				crypto_cancel_ctx(cipher_context);
			if (mac_context != NULL)
				crypto_cancel_ctx(mac_context);

			ASSERT(ctx_single->dc_type == DPROV_CTX_DUAL);
			kmem_free(ctx_single, sizeof (dprov_ctx_dual_t));
		}
		ctx->cc_provider_private = NULL;
	}

	return (CRYPTO_SUCCESS);
}

/*
 * Resource control checks don't need to be done. Why? Because this routine
 * knows the size of the structure, and it can't be overridden by a user.
 * This is different from the crypto module, which has no knowledge of
 * specific mechanisms, and therefore has to trust specified size of the
 * parameter.  This trust, or lack of trust, is why the size of the
 * parameter has to be charged against the project resource control.
 */
static int
copyin_aes_ctr_mech(crypto_mechanism_t *in_mech, crypto_mechanism_t *out_mech,
    int *out_error, int mode)
{
	STRUCT_DECL(crypto_mechanism, mech);
	STRUCT_DECL(CK_AES_CTR_PARAMS, params);
	CK_AES_CTR_PARAMS *aes_ctr_params;
	caddr_t pp;
	size_t param_len;
	int error = 0;
	int rv = 0;

	STRUCT_INIT(mech, mode);
	STRUCT_INIT(params, mode);
	bcopy(in_mech, STRUCT_BUF(mech), STRUCT_SIZE(mech));
	pp = STRUCT_FGETP(mech, cm_param);
	param_len = STRUCT_FGET(mech, cm_param_len);

	if (param_len != STRUCT_SIZE(params)) {
		rv = CRYPTO_ARGUMENTS_BAD;
		goto out;
	}

	out_mech->cm_type = STRUCT_FGET(mech, cm_type);
	out_mech->cm_param = NULL;
	out_mech->cm_param_len = 0;
	if (pp != NULL) {
		if (copyin((char *)pp, STRUCT_BUF(params), param_len) != 0) {
			out_mech->cm_param = NULL;
			error = EFAULT;
			goto out;
		}
		/* allocate param structure and counter block */
		aes_ctr_params = kmem_alloc(sizeof (CK_AES_CTR_PARAMS) + 16,
		    KM_NOSLEEP);
		if (aes_ctr_params == NULL) {
			rv = CRYPTO_HOST_MEMORY;
			goto out;
		}
		aes_ctr_params->cb = (uchar_t *)aes_ctr_params +
		    sizeof (CK_AES_CTR_PARAMS);
		aes_ctr_params->ulCounterBits = STRUCT_FGET(params,
		    ulCounterBits);
		if (copyin((char *)STRUCT_FGETP(params, cb),
		    &aes_ctr_params->cb[0], 16) != 0) {
			kmem_free(aes_ctr_params,
			    sizeof (CK_AES_CTR_PARAMS) + 16);
			out_mech->cm_param = NULL;
			error = EFAULT;
			goto out;
		}
		out_mech->cm_param = (char *)aes_ctr_params;
		out_mech->cm_param_len = sizeof (CK_AES_CTR_PARAMS);
	}
out:
	*out_error = error;
	return (rv);
}

/* ARGSUSED */
static int
copyout_aes_ctr_mech(crypto_mechanism_t *in_mech, crypto_mechanism_t *out_mech,
    int *out_error, int mode)
{
	STRUCT_DECL(crypto_mechanism, mech);
	STRUCT_DECL(CK_AES_CTR_PARAMS, params);
	uint8_t cb[16];
	caddr_t pp;
	size_t param_len;
	int error = 0;
	int rv = 0;

	STRUCT_INIT(mech, mode);
	STRUCT_INIT(params, mode);
	bcopy(out_mech, STRUCT_BUF(mech), STRUCT_SIZE(mech));
	pp = STRUCT_FGETP(mech, cm_param);
	param_len = STRUCT_FGET(mech, cm_param_len);
	if (param_len != STRUCT_SIZE(params)) {
		rv = CRYPTO_ARGUMENTS_BAD;
		goto out;
	}

	if (copyin((char *)pp, STRUCT_BUF(params), param_len) != 0) {
		error = EFAULT;
		goto out;
	}

	/* for testing, overwrite the iv with 16 X 'A' */
	if (pp != NULL) {
		(void) memset(cb, 'A', 16);
		if (copyout(cb, STRUCT_FGETP(params, cb),  16) != 0) {
			error = EFAULT;
			goto out;
		}
	}
out:
	*out_error = error;
	return (rv);
}

/* ARGSUSED */
static int
dprov_copyin_mechanism(crypto_provider_handle_t provider,
    crypto_mechanism_t *umech, crypto_mechanism_t *kmech,
    int *out_error, int mode)
{
	STRUCT_DECL(crypto_mechanism, mech);
	size_t param_len, expected_param_len;
	caddr_t pp;
	char *param;
	int rv;
	int error = 0;

	ASSERT(!servicing_interrupt());

	STRUCT_INIT(mech, mode);
	bcopy(umech, STRUCT_BUF(mech), STRUCT_SIZE(mech));
	pp = STRUCT_FGETP(mech, cm_param);
	param_len = STRUCT_FGET(mech, cm_param_len);

	kmech->cm_param = NULL;
	kmech->cm_param_len = 0;

	switch (kmech->cm_type) {
	case DES_CBC_MECH_INFO_TYPE:
	case DES3_CBC_MECH_INFO_TYPE:
		expected_param_len = DES_BLOCK_LEN;
		break;

	case BLOWFISH_CBC_MECH_INFO_TYPE:
		expected_param_len = BLOWFISH_BLOCK_LEN;
		break;

	case AES_CBC_MECH_INFO_TYPE:
		expected_param_len = AES_BLOCK_LEN;
		break;

	case AES_CTR_MECH_INFO_TYPE:
	case SHA1_KEY_DERIVATION_MECH_INFO_TYPE:	/* for testing only */
		rv = copyin_aes_ctr_mech(umech, kmech, &error, mode);
		goto out;

	default:
		/* nothing to do - mechanism has no parameters */
		rv = CRYPTO_SUCCESS;
		goto out;
	}

	if (param_len != expected_param_len) {
		rv = CRYPTO_MECHANISM_PARAM_INVALID;
		goto out;
	}
	if (pp == NULL) {
		rv = CRYPTO_MECHANISM_PARAM_INVALID;
		goto out;
	}
	if ((param = kmem_alloc(param_len, KM_NOSLEEP)) == NULL) {
		rv = CRYPTO_HOST_MEMORY;
		goto out;
	}
	if (copyin((char *)pp, param, param_len) != 0) {
		kmem_free(param, param_len);
		error = EFAULT;
		rv = CRYPTO_FAILED;
		goto out;
	}
	kmech->cm_param = (char *)param;
	kmech->cm_param_len = param_len;
	rv = CRYPTO_SUCCESS;
out:
	*out_error = error;
	return (rv);
}

/* ARGSUSED */
static int
dprov_copyout_mechanism(crypto_provider_handle_t provider,
    crypto_mechanism_t *kmech, crypto_mechanism_t *umech,
    int *out_error, int mode)
{
	ASSERT(!servicing_interrupt());

	switch (kmech->cm_type) {
	case AES_CTR_MECH_INFO_TYPE:
	case SHA1_KEY_DERIVATION_MECH_INFO_TYPE:	/* for testing only */
		return (copyout_aes_ctr_mech(kmech, umech, out_error, mode));
	default:
		return (CRYPTO_MECHANISM_INVALID);
	}
}

/*
 * Free mechanism parameter that was allocated by the provider.
 */
/* ARGSUSED */
static int
dprov_free_mechanism(crypto_provider_handle_t provider,
    crypto_mechanism_t *mech)
{
	size_t len;

	if (mech->cm_param == NULL || mech->cm_param_len == 0)
		return (CRYPTO_SUCCESS);

	if (mech->cm_type == AES_CTR_MECH_INFO_TYPE ||
	    mech->cm_type == SHA1_KEY_DERIVATION_MECH_INFO_TYPE) {
		len = sizeof (CK_AES_CTR_PARAMS) + 16;
	} else {
		len = mech->cm_param_len;
	}
	kmem_free(mech->cm_param, len);
	return (CRYPTO_SUCCESS);
}

/*
 * Allocate a dprov taskq request and initialize the common fields.
 * Return NULL if the memory allocation failed.
 */
static dprov_req_t *
dprov_alloc_req(dprov_req_type_t req_type, dprov_state_t *softc,
    crypto_req_handle_t kcf_req, int kmflag)
{
	dprov_req_t *taskq_req;

	if ((taskq_req = kmem_alloc(sizeof (dprov_req_t), kmflag)) == NULL)
		return (NULL);

	taskq_req->dr_type = req_type;
	taskq_req->dr_softc = softc;
	taskq_req->dr_kcf_req = kcf_req;

	return (taskq_req);
}

/*
 * Dispatch a dprov request on the taskq associated with a softc.
 * Returns CRYPTO_HOST_MEMORY if the request cannot be queued,
 * CRYPTO_QUEUED on success.
 */
static int
dprov_taskq_dispatch(dprov_state_t *softc, dprov_req_t *taskq_req,
    task_func_t *func, int kmflag)
{
	if (taskq_dispatch(softc->ds_taskq, func, taskq_req,
	    kmflag == KM_NOSLEEP ? TQ_NOSLEEP : TQ_SLEEP) == (taskqid_t)0) {
		kmem_free(taskq_req, sizeof (dprov_req_t));
		return (CRYPTO_HOST_MEMORY);
	} else
		return (CRYPTO_QUEUED);
}

/*
 * Helper function to submit digest operations to the taskq.
 * Returns one of the CRYPTO_ errors.
 */
static int
dprov_digest_submit_req(dprov_req_type_t req_type,
    dprov_state_t *softc, crypto_req_handle_t req,
    crypto_mechanism_t *mechanism, crypto_data_t *data, crypto_key_t *key,
    crypto_data_t *digest, crypto_ctx_t *ctx, int kmflag)
{
	dprov_req_t *taskq_req;

	if ((taskq_req = dprov_alloc_req(req_type, softc, req, kmflag)) == NULL)
		return (CRYPTO_HOST_MEMORY);

	taskq_req->dr_digest_req.dr_mechanism = mechanism;
	taskq_req->dr_digest_req.dr_ctx = ctx;
	taskq_req->dr_digest_req.dr_data = data;
	taskq_req->dr_digest_req.dr_key = key;
	taskq_req->dr_digest_req.dr_digest = digest;

	return (dprov_taskq_dispatch(softc, taskq_req,
	    (task_func_t *)dprov_digest_task, kmflag));
}

/*
 * Helper function to submit mac operations to the taskq.
 * Returns one of the CRYPTO_ errors.
 */
static int
dprov_mac_submit_req(dprov_req_type_t req_type,
    dprov_state_t *softc, crypto_req_handle_t req,
    crypto_mechanism_t *mechanism, crypto_data_t *data, crypto_key_t *key,
    crypto_data_t *mac, crypto_ctx_t *ctx, crypto_session_id_t sid, int kmflag)
{
	dprov_req_t *taskq_req;

	if ((taskq_req = dprov_alloc_req(req_type, softc, req, kmflag)) == NULL)
		return (CRYPTO_HOST_MEMORY);

	taskq_req->dr_mac_req.dr_mechanism = mechanism;
	taskq_req->dr_mac_req.dr_ctx = ctx;
	taskq_req->dr_mac_req.dr_data = data;
	taskq_req->dr_mac_req.dr_key = key;
	taskq_req->dr_mac_req.dr_mac = mac;
	taskq_req->dr_mac_req.dr_session_id = sid;

	return (dprov_taskq_dispatch(softc, taskq_req,
	    (task_func_t *)dprov_mac_task, kmflag));
}

/*
 * Helper function to submit sign operations to the taskq.
 * Returns one of the CRYPTO_ errors.
 */
static int
dprov_sign_submit_req(dprov_req_type_t req_type,
    dprov_state_t *softc, crypto_req_handle_t req,
    crypto_mechanism_t *mechanism, crypto_key_t *key, crypto_data_t *data,
    crypto_data_t *signature, crypto_ctx_t *ctx, crypto_session_id_t sid,
    int kmflag)
{
	dprov_req_t *taskq_req;

	if ((taskq_req = dprov_alloc_req(req_type, softc, req, kmflag)) == NULL)
		return (CRYPTO_HOST_MEMORY);

	taskq_req->dr_sign_req.sr_mechanism = mechanism;
	taskq_req->dr_sign_req.sr_ctx = ctx;
	taskq_req->dr_sign_req.sr_key = key;
	taskq_req->dr_sign_req.sr_data = data;
	taskq_req->dr_sign_req.sr_signature = signature;
	taskq_req->dr_sign_req.sr_session_id = sid;

	return (dprov_taskq_dispatch(softc, taskq_req,
	    (task_func_t *)dprov_sign_task, kmflag));
}

/*
 * Helper function to submit verify operations to the taskq.
 * Returns one of the CRYPTO_ errors.
 */
static int
dprov_verify_submit_req(dprov_req_type_t req_type,
    dprov_state_t *softc, crypto_req_handle_t req,
    crypto_mechanism_t *mechanism, crypto_key_t *key, crypto_data_t *data,
    crypto_data_t *signature, crypto_ctx_t *ctx, crypto_session_id_t sid,
    int kmflag)
{
	dprov_req_t *taskq_req;

	if ((taskq_req = dprov_alloc_req(req_type, softc, req, kmflag)) == NULL)
		return (CRYPTO_HOST_MEMORY);

	taskq_req->dr_verify_req.vr_mechanism = mechanism;
	taskq_req->dr_verify_req.vr_ctx = ctx;
	taskq_req->dr_verify_req.vr_key = key;
	taskq_req->dr_verify_req.vr_data = data;
	taskq_req->dr_verify_req.vr_signature = signature;
	taskq_req->dr_verify_req.vr_session_id = sid;

	return (dprov_taskq_dispatch(softc, taskq_req,
	    (task_func_t *)dprov_verify_task, kmflag));
}

/*
 * Helper function to submit dual operations to the taskq.
 * Returns one of the CRYPTO_ errors.
 */
static int
dprov_dual_submit_req(dprov_req_type_t req_type, dprov_state_t *softc,
    crypto_req_handle_t req, crypto_ctx_t *signverify_ctx,
    crypto_ctx_t *cipher_ctx, crypto_data_t *plaintext,
    crypto_data_t *ciphertext)
{
	dprov_req_t *taskq_req;

	if ((taskq_req = dprov_alloc_req(req_type, softc, req,
	    KM_NOSLEEP)) == NULL)
		return (CRYPTO_HOST_MEMORY);

	taskq_req->dr_dual_req.dr_signverify_ctx = signverify_ctx;
	taskq_req->dr_dual_req.dr_cipher_ctx = cipher_ctx;
	taskq_req->dr_dual_req.dr_plaintext = plaintext;
	taskq_req->dr_dual_req.dr_ciphertext = ciphertext;

	return (dprov_taskq_dispatch(softc, taskq_req,
	    (task_func_t *)dprov_dual_task, KM_NOSLEEP));
}

/*
 * Helper function to submit dual cipher/mac operations to the taskq.
 * Returns one of the CRYPTO_ errors.
 */
static int
dprov_cipher_mac_submit_req(dprov_req_type_t req_type,
    dprov_state_t *softc, crypto_req_handle_t req, crypto_ctx_t *ctx,
    crypto_session_id_t sid, crypto_mechanism_t *cipher_mech,
    crypto_key_t *cipher_key, crypto_mechanism_t *mac_mech,
    crypto_key_t *mac_key, crypto_dual_data_t *dual_data,
    crypto_data_t *data, crypto_data_t *mac, int kmflag)
{
	dprov_req_t *taskq_req;

	if ((taskq_req = dprov_alloc_req(req_type, softc, req, kmflag)) == NULL)
		return (CRYPTO_HOST_MEMORY);

	taskq_req->dr_cipher_mac_req.mr_session_id = sid;
	taskq_req->dr_cipher_mac_req.mr_ctx = ctx;
	taskq_req->dr_cipher_mac_req.mr_cipher_mech = cipher_mech;
	taskq_req->dr_cipher_mac_req.mr_cipher_key = cipher_key;
	taskq_req->dr_cipher_mac_req.mr_mac_mech = mac_mech;
	taskq_req->dr_cipher_mac_req.mr_mac_key = mac_key;
	taskq_req->dr_cipher_mac_req.mr_dual_data = dual_data;
	taskq_req->dr_cipher_mac_req.mr_data = data;
	taskq_req->dr_cipher_mac_req.mr_mac = mac;

	return (dprov_taskq_dispatch(softc, taskq_req,
	    (task_func_t *)dprov_cipher_mac_task, kmflag));
}

/*
 * Helper function to submit cipher operations to the taskq.
 * Returns one of the CRYPTO_ errors.
 */
static int
dprov_cipher_submit_req(dprov_req_type_t req_type,
    dprov_state_t *softc, crypto_req_handle_t req,
    crypto_mechanism_t *mechanism, crypto_key_t *key, crypto_data_t *plaintext,
    crypto_data_t *ciphertext, crypto_ctx_t *ctx, crypto_session_id_t sid,
    int kmflag)
{
	dprov_req_t *taskq_req;

	if ((taskq_req = dprov_alloc_req(req_type, softc, req, kmflag)) == NULL)
		return (CRYPTO_HOST_MEMORY);

	taskq_req->dr_cipher_req.dr_mechanism = mechanism;
	taskq_req->dr_cipher_req.dr_ctx = ctx;
	taskq_req->dr_cipher_req.dr_key = key;
	taskq_req->dr_cipher_req.dr_plaintext = plaintext;
	taskq_req->dr_cipher_req.dr_ciphertext = ciphertext;
	taskq_req->dr_cipher_req.dr_session_id = sid;

	return (dprov_taskq_dispatch(softc, taskq_req,
	    (task_func_t *)dprov_cipher_task, kmflag));
}

/*
 * Helper function to submit random number operations to the taskq.
 * Returns one of the CRYPTO_ errors.
 */
static int
dprov_random_submit_req(dprov_req_type_t req_type,
    dprov_state_t *softc, crypto_req_handle_t req, uchar_t *buf, size_t len,
    crypto_session_id_t sid, uint_t entropy_est, uint32_t flags)
{
	dprov_req_t *taskq_req;

	if ((taskq_req = dprov_alloc_req(req_type, softc, req,
	    KM_NOSLEEP)) == NULL)
		return (CRYPTO_HOST_MEMORY);

	taskq_req->dr_random_req.rr_buf = buf;
	taskq_req->dr_random_req.rr_len = len;
	taskq_req->dr_random_req.rr_session_id = sid;
	taskq_req->dr_random_req.rr_entropy_est = entropy_est;
	taskq_req->dr_random_req.rr_flags = flags;

	return (dprov_taskq_dispatch(softc, taskq_req,
	    (task_func_t *)dprov_random_task, KM_NOSLEEP));
}


/*
 * Helper function to submit session management operations to the taskq.
 * Returns one of the CRYPTO_ errors.
 */
static int
dprov_session_submit_req(dprov_req_type_t req_type,
    dprov_state_t *softc, crypto_req_handle_t req,
    crypto_session_id_t *session_id_ptr, crypto_session_id_t session_id,
    crypto_user_type_t user_type, char *pin, size_t pin_len)
{
	dprov_req_t *taskq_req;

	if ((taskq_req = dprov_alloc_req(req_type, softc, req,
	    KM_NOSLEEP)) == NULL)
		return (CRYPTO_HOST_MEMORY);

	taskq_req->dr_session_req.sr_session_id_ptr = session_id_ptr;
	taskq_req->dr_session_req.sr_session_id = session_id;
	taskq_req->dr_session_req.sr_user_type = user_type;
	taskq_req->dr_session_req.sr_pin = pin;
	taskq_req->dr_session_req.sr_pin_len = pin_len;

	return (dprov_taskq_dispatch(softc, taskq_req,
	    (task_func_t *)dprov_session_task, KM_NOSLEEP));
}

/*
 * Helper function to submit object management operations to the taskq.
 * Returns one of the CRYPTO_ errors.
 */
static int
dprov_object_submit_req(dprov_req_type_t req_type,
    dprov_state_t *softc, crypto_req_handle_t req,
    crypto_session_id_t session_id, crypto_object_id_t object_id,
    crypto_object_attribute_t *template, uint_t attribute_count,
    crypto_object_id_t *object_id_ptr, size_t *object_size,
    void **find_pp, void *find_p, uint_t max_object_count,
    uint_t *object_count_ptr, int kmflag)
{
	dprov_req_t *taskq_req;

	if ((taskq_req = dprov_alloc_req(req_type, softc, req,
	    kmflag)) == NULL)
		return (CRYPTO_HOST_MEMORY);

	taskq_req->dr_object_req.or_session_id = session_id;
	taskq_req->dr_object_req.or_object_id = object_id;
	taskq_req->dr_object_req.or_template = template;
	taskq_req->dr_object_req.or_attribute_count = attribute_count;
	taskq_req->dr_object_req.or_object_id_ptr = object_id_ptr;
	taskq_req->dr_object_req.or_object_size = object_size;
	taskq_req->dr_object_req.or_find_pp = find_pp;
	taskq_req->dr_object_req.or_find_p = find_p;
	taskq_req->dr_object_req.or_max_object_count = max_object_count;
	taskq_req->dr_object_req.or_object_count_ptr = object_count_ptr;

	return (dprov_taskq_dispatch(softc, taskq_req,
	    (task_func_t *)dprov_object_task, KM_NOSLEEP));
}

/*
 * Helper function to submit key management operations to the taskq.
 * Returns one of the CRYPTO_ errors.
 */
static int
dprov_key_submit_req(dprov_req_type_t req_type,
    dprov_state_t *softc, crypto_req_handle_t req,
    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
    crypto_object_attribute_t *template, uint_t attribute_count,
    crypto_object_id_t *object_id_ptr,
    crypto_object_attribute_t *private_key_template,
    uint_t private_key_attribute_count,
    crypto_object_id_t *private_key_object_id_ptr, crypto_key_t *key,
    uchar_t *wrapped_key, size_t *wrapped_key_len_ptr)
{
	dprov_req_t *taskq_req;

	if ((taskq_req = dprov_alloc_req(req_type, softc, req,
	    KM_NOSLEEP)) == NULL)
		return (CRYPTO_HOST_MEMORY);

	taskq_req->dr_key_req.kr_session_id = session_id;
	taskq_req->dr_key_req.kr_mechanism = mechanism;
	taskq_req->dr_key_req.kr_template = template;
	taskq_req->dr_key_req.kr_attribute_count = attribute_count;
	taskq_req->dr_key_req.kr_object_id_ptr = object_id_ptr;
	taskq_req->dr_key_req.kr_private_key_template = private_key_template;
	taskq_req->dr_key_req.kr_private_key_attribute_count =
	    private_key_attribute_count;
	taskq_req->dr_key_req.kr_private_key_object_id_ptr =
	    private_key_object_id_ptr;
	taskq_req->dr_key_req.kr_key = key;
	taskq_req->dr_key_req.kr_wrapped_key = wrapped_key;
	taskq_req->dr_key_req.kr_wrapped_key_len_ptr = wrapped_key_len_ptr;

	return (dprov_taskq_dispatch(softc, taskq_req,
	    (task_func_t *)dprov_key_task, KM_NOSLEEP));
}

/*
 * Helper function to submit provider management operations to the taskq.
 * Returns one of the CRYPTO_ errors.
 */
static int
dprov_mgmt_submit_req(dprov_req_type_t req_type,
    dprov_state_t *softc, crypto_req_handle_t req,
    crypto_session_id_t session_id, char *pin, size_t pin_len,
    char *old_pin, size_t old_pin_len, char *label,
    crypto_provider_ext_info_t *ext_info)
{
	dprov_req_t *taskq_req;

	if ((taskq_req = dprov_alloc_req(req_type, softc, req,
	    KM_NOSLEEP)) == NULL)
		return (CRYPTO_HOST_MEMORY);

	taskq_req->dr_mgmt_req.mr_session_id = session_id;
	taskq_req->dr_mgmt_req.mr_pin = pin;
	taskq_req->dr_mgmt_req.mr_pin_len = pin_len;
	taskq_req->dr_mgmt_req.mr_old_pin = old_pin;
	taskq_req->dr_mgmt_req.mr_old_pin_len = old_pin_len;
	taskq_req->dr_mgmt_req.mr_label = label;
	taskq_req->dr_mgmt_req.mr_ext_info = ext_info;

	return (dprov_taskq_dispatch(softc, taskq_req,
	    (task_func_t *)dprov_mgmt_task, KM_NOSLEEP));
}

/*
 * Helper function for taskq dispatcher routines. Notify the framework
 * that the operation corresponding to the specified request is done,
 * and pass it the error code. Finally, free the taskq_req.
 */
static void
dprov_op_done(dprov_req_t *taskq_req, int error)
{
	/* notify framework that request is completed */
	crypto_op_notification(taskq_req->dr_kcf_req, error);

	/* free taskq request structure */
	kmem_free(taskq_req, sizeof (dprov_req_t));
}

/*
 * taskq dispatcher function for digest operations.
 */
static void
dprov_digest_task(dprov_req_t *taskq_req)
{
	kcf_provider_desc_t *pd;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;
	int error = CRYPTO_NOT_SUPPORTED;
	crypto_ctx_t *ctx = taskq_req->dr_digest_req.dr_ctx;
	crypto_mechanism_t mech;

	DPROV_SOFTC_FROM_REQ(taskq_req, softc, instance);
	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_digest_task: started\n", instance));

	switch (taskq_req->dr_type) {

	case DPROV_REQ_DIGEST_INIT:
		/* allocate a dprov-private context */
		if ((error = dprov_alloc_context(taskq_req->dr_type, ctx)) !=
		    CRYPTO_SUCCESS)
			break;

		/* structure assignment */
		mech = *taskq_req->dr_digest_req.dr_mechanism;

		/* get the software provider for this mechanism */
		if ((error = dprov_get_sw_prov(
		    taskq_req->dr_digest_req.dr_mechanism, &pd,
		    &mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		/* Use a session id of zero since we use a software provider */
		error = crypto_digest_init_prov(pd, 0, &mech,
		    &DPROV_CTX_SINGLE(ctx), NULL);

		/* release provider reference */
		KCF_PROV_REFRELE(pd);
		break;

	case DPROV_REQ_DIGEST:
		error = crypto_digest_single(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_digest_req.dr_data,
		    taskq_req->dr_digest_req.dr_digest, NULL);

		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_SINGLE(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;

	case DPROV_REQ_DIGEST_UPDATE:
		error = crypto_digest_update(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_digest_req.dr_data, NULL);
		break;

	case DPROV_REQ_DIGEST_KEY: {
		crypto_data_t data;
		crypto_key_t key;
		size_t len;

		mutex_enter(&softc->ds_lock);
		error = dprov_key_value_secret(softc, ctx->cc_session,
		    taskq_req->dr_type, taskq_req->dr_digest_req.dr_key, &key);
		mutex_exit(&softc->ds_lock);
		if (error != CRYPTO_SUCCESS)
			break;

		/* key lengths are specified in bits */
		len = CRYPTO_BITS2BYTES(key.ck_length);
		data.cd_format = CRYPTO_DATA_RAW;
		data.cd_offset = 0;
		data.cd_length = len;
		data.cd_raw.iov_base = key.ck_data;
		data.cd_raw.iov_len = len;
		error = crypto_digest_update(DPROV_CTX_SINGLE(ctx),
		    &data, NULL);
		break;
	}

	case DPROV_REQ_DIGEST_FINAL:
		error = crypto_digest_final(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_digest_req.dr_digest, NULL);
		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_SINGLE(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;

	case DPROV_REQ_DIGEST_ATOMIC:
		/* structure assignment */
		mech = *taskq_req->dr_digest_req.dr_mechanism;

		/* get the software provider for this mechanism */
		if ((error = dprov_get_sw_prov(
		    taskq_req->dr_digest_req.dr_mechanism, &pd,
		    &mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		/* use a session id of zero since we use a software provider */
		error = crypto_digest_prov(pd, 0, &mech,
		    taskq_req->dr_digest_req.dr_data,
		    taskq_req->dr_digest_req.dr_digest, NULL);

		/* release provider reference */
		KCF_PROV_REFRELE(pd);

		break;
	}

	dprov_op_done(taskq_req, error);
	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_digest_task: end\n", instance));
}

/*
 * taskq dispatcher function for mac operations.
 */
static void
dprov_mac_task(dprov_req_t *taskq_req)
{
	kcf_provider_desc_t *pd;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;
	int error = CRYPTO_NOT_SUPPORTED;
	crypto_ctx_t *ctx = taskq_req->dr_mac_req.dr_ctx;
	crypto_key_t key;
	crypto_mechanism_t mech;

	DPROV_SOFTC_FROM_REQ(taskq_req, softc, instance);
	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac_task: started\n", instance));

	switch (taskq_req->dr_type) {

	case DPROV_REQ_MAC_INIT:
		/* allocate a dprov-private context */
		if ((error = dprov_alloc_context(taskq_req->dr_type, ctx)) !=
		    CRYPTO_SUCCESS)
			break;

		/* get key value */
		mutex_enter(&softc->ds_lock);
		error = dprov_key_value_secret(softc, ctx->cc_session,
		    taskq_req->dr_type, taskq_req->dr_mac_req.dr_key, &key);
		mutex_exit(&softc->ds_lock);
		if (error != CRYPTO_SUCCESS)
			break;

		/* structure assignment */
		mech = *taskq_req->dr_mac_req.dr_mechanism;

		/* get the software provider for this mechanism */
		if ((error = dprov_get_sw_prov(
		    taskq_req->dr_mac_req.dr_mechanism, &pd,
		    &mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		/* Use a session id of zero since we use a software provider */
		error = crypto_mac_init_prov(pd, 0, &mech, &key, NULL,
		    &DPROV_CTX_SINGLE(ctx), NULL);

		/* release provider reference */
		KCF_PROV_REFRELE(pd);
		break;

	case DPROV_REQ_MAC:
		error = crypto_mac_single(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_mac_req.dr_data,
		    taskq_req->dr_mac_req.dr_mac, NULL);

		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_SINGLE(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;

	case DPROV_REQ_MAC_UPDATE:
		error = crypto_mac_update(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_mac_req.dr_data, NULL);
		break;

	case DPROV_REQ_MAC_FINAL:
		error = crypto_mac_final(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_mac_req.dr_mac, NULL);
		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_SINGLE(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;

	case DPROV_REQ_MAC_ATOMIC:
	case DPROV_REQ_MAC_VERIFY_ATOMIC:
		/* get key value */
		mutex_enter(&softc->ds_lock);
		error = dprov_key_value_secret(softc,
		    taskq_req->dr_mac_req.dr_session_id,
		    taskq_req->dr_type, taskq_req->dr_mac_req.dr_key, &key);
		mutex_exit(&softc->ds_lock);
		if (error != CRYPTO_SUCCESS)
			break;

		/* structure assignment */
		mech = *taskq_req->dr_mac_req.dr_mechanism;

		/* get the software provider for this mechanism */
		if ((error = dprov_get_sw_prov(
		    taskq_req->dr_mac_req.dr_mechanism, &pd,
		    &mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		/* use a session id of zero since we use a software provider */
		if (taskq_req->dr_type == DPROV_REQ_MAC_ATOMIC)
			error = crypto_mac_prov(pd, 0, &mech,
			    taskq_req->dr_mac_req.dr_data,
			    &key, NULL, taskq_req->dr_mac_req.dr_mac, NULL);
		else
			error = crypto_mac_verify_prov(pd, 0, &mech,
			    taskq_req->dr_mac_req.dr_data,
			    &key, NULL, taskq_req->dr_mac_req.dr_mac, NULL);

		/* release provider reference */
		KCF_PROV_REFRELE(pd);

		break;
	}

	dprov_op_done(taskq_req, error);
	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac_task: end\n", instance));
}

/*
 * taskq dispatcher function for sign operations.
 */
static void
dprov_sign_task(dprov_req_t *taskq_req)
{
	kcf_provider_desc_t *pd;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;
	int error = CRYPTO_NOT_SUPPORTED;
	crypto_ctx_t *ctx = taskq_req->dr_mac_req.dr_ctx;
	crypto_key_t key, *keyp;
	crypto_mechanism_t mech;

	DPROV_SOFTC_FROM_REQ(taskq_req, softc, instance);
	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_task: started\n", instance));

	switch (taskq_req->dr_type) {

	case DPROV_REQ_SIGN_INIT:
	case DPROV_REQ_SIGN_RECOVER_INIT:
		/* allocate a dprov-private context */
		if ((error = dprov_alloc_context(taskq_req->dr_type, ctx)) !=
		    CRYPTO_SUCCESS)
			break;

		/* structure assignment */
		mech = *taskq_req->dr_sign_req.sr_mechanism;

		mutex_enter(&softc->ds_lock);
		/* get key value for secret key algorithms */
		if (is_publickey_mech(mech.cm_type)) {
			if ((error = dprov_key_attr_asymmetric(softc,
			    ctx->cc_session, taskq_req->dr_type,
			    taskq_req->dr_sign_req.sr_key, &key))
			    != CRYPTO_SUCCESS) {
				mutex_exit(&softc->ds_lock);
				break;
			}
			keyp = &key;
		} else {
			if ((error = dprov_key_value_secret(softc,
			    ctx->cc_session, taskq_req->dr_type,
			    taskq_req->dr_sign_req.sr_key, &key))
			    != CRYPTO_SUCCESS) {
				mutex_exit(&softc->ds_lock);
				break;
			}
			keyp = &key;
		}
		mutex_exit(&softc->ds_lock);

		/* get the software provider for this mechanism */
		if ((error = dprov_get_sw_prov(
		    taskq_req->dr_sign_req.sr_mechanism, &pd,
		    &mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		/* Use a session id of zero since we use a software provider */
		if (taskq_req->dr_type == DPROV_REQ_SIGN_INIT)
			error = crypto_sign_init_prov(pd, 0, &mech, keyp,
			    NULL, &DPROV_CTX_SINGLE(ctx), NULL);
		else
			error = crypto_sign_recover_init_prov(pd, 0, &mech,
			    keyp, NULL, &DPROV_CTX_SINGLE(ctx), NULL);

		/* release provider reference */
		KCF_PROV_REFRELE(pd);

		break;

	case DPROV_REQ_SIGN:
		error = crypto_sign_single(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_sign_req.sr_data,
		    taskq_req->dr_sign_req.sr_signature, NULL);

		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_SINGLE(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;

	case DPROV_REQ_SIGN_UPDATE:
		error = crypto_sign_update(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_sign_req.sr_data, NULL);
		break;

	case DPROV_REQ_SIGN_FINAL:
		error = crypto_sign_final(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_sign_req.sr_signature, NULL);

		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_SINGLE(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;

	case DPROV_REQ_SIGN_ATOMIC:
	case DPROV_REQ_SIGN_RECOVER_ATOMIC:
		/* structure assignment */
		mech = *taskq_req->dr_sign_req.sr_mechanism;

		mutex_enter(&softc->ds_lock);
		/* get key value for secret key algorithms */
		if (is_publickey_mech(mech.cm_type)) {
			if ((error = dprov_key_attr_asymmetric(softc,
			    taskq_req->dr_sign_req.sr_session_id,
			    taskq_req->dr_type,
			    taskq_req->dr_sign_req.sr_key, &key))
			    != CRYPTO_SUCCESS) {
				mutex_exit(&softc->ds_lock);
				break;
			}
			keyp = &key;
		} else {
			if ((error = dprov_key_value_secret(softc,
			    taskq_req->dr_sign_req.sr_session_id,
			    taskq_req->dr_type,
			    taskq_req->dr_sign_req.sr_key, &key))
			    != CRYPTO_SUCCESS) {
				mutex_exit(&softc->ds_lock);
				break;
			}
			keyp = &key;
		}
		mutex_exit(&softc->ds_lock);

		/* get the software provider for this mechanism */
		if ((error = dprov_get_sw_prov(
		    taskq_req->dr_sign_req.sr_mechanism, &pd,
		    &mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		/* Use a session id of zero since we use a software provider */
		if (taskq_req->dr_type == DPROV_REQ_SIGN_ATOMIC)
			error = crypto_sign_prov(pd, 0, &mech, keyp,
			    taskq_req->dr_sign_req.sr_data,
			    NULL, taskq_req->dr_sign_req.sr_signature, NULL);
		else
			error = crypto_sign_recover_prov(pd, 0, &mech, keyp,
			    taskq_req->dr_sign_req.sr_data,
			    NULL, taskq_req->dr_sign_req.sr_signature, NULL);

		/* release provider reference */
		KCF_PROV_REFRELE(pd);
		break;

	case DPROV_REQ_SIGN_RECOVER:
		error = crypto_sign_recover_single(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_sign_req.sr_data,
		    taskq_req->dr_sign_req.sr_signature, NULL);

		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_SINGLE(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;
	}

	dprov_op_done(taskq_req, error);
	DPROV_DEBUG(D_SIGN, ("(%d) dprov_sign_task: end\n", instance));
}

/*
 * taskq dispatcher function for verify operations.
 */
static void
dprov_verify_task(dprov_req_t *taskq_req)
{
	kcf_provider_desc_t *pd;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;
	int error = CRYPTO_NOT_SUPPORTED;
	crypto_ctx_t *ctx = taskq_req->dr_verify_req.vr_ctx;
	crypto_key_t key, *keyp;
	crypto_mechanism_t mech;

	DPROV_SOFTC_FROM_REQ(taskq_req, softc, instance);
	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_task: started\n", instance));

	switch (taskq_req->dr_type) {

	case DPROV_REQ_VERIFY_INIT:
	case DPROV_REQ_VERIFY_RECOVER_INIT:
		/* allocate a dprov-private context */
		if ((error = dprov_alloc_context(taskq_req->dr_type, ctx)) !=
		    CRYPTO_SUCCESS)
			break;

		/* structure assignment */
		mech = *taskq_req->dr_verify_req.vr_mechanism;

		mutex_enter(&softc->ds_lock);
		/* get key value for secret key algorithms */
		if (is_publickey_mech(mech.cm_type)) {
			if ((error = dprov_key_attr_asymmetric(softc,
			    ctx->cc_session, taskq_req->dr_type,
			    taskq_req->dr_verify_req.vr_key, &key))
			    != CRYPTO_SUCCESS) {
				mutex_exit(&softc->ds_lock);
				break;
			}
			keyp = &key;
		} else {
			if ((error = dprov_key_value_secret(softc,
			    ctx->cc_session, taskq_req->dr_type,
			    taskq_req->dr_verify_req.vr_key, &key))
			    != CRYPTO_SUCCESS) {
				mutex_exit(&softc->ds_lock);
				break;
			}
			keyp = &key;
		}
		mutex_exit(&softc->ds_lock);

		/* get the software provider for this mechanism */
		if ((error = dprov_get_sw_prov(
		    taskq_req->dr_verify_req.vr_mechanism, &pd,
		    &mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		/* Use a session id of zero since we use a software provider */
		if (taskq_req->dr_type == DPROV_REQ_VERIFY_INIT)
			error = crypto_verify_init_prov(pd, 0, &mech, keyp,
			    NULL, &DPROV_CTX_SINGLE(ctx), NULL);
		else
			error = crypto_verify_recover_init_prov(pd, 0, &mech,
			    keyp, NULL, &DPROV_CTX_SINGLE(ctx), NULL);

		/* release provider reference */
		KCF_PROV_REFRELE(pd);

		break;

	case DPROV_REQ_VERIFY:
		error = crypto_verify_single(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_verify_req.vr_data,
		    taskq_req->dr_verify_req.vr_signature, NULL);

		ASSERT(error != CRYPTO_BUFFER_TOO_SMALL);
		DPROV_CTX_SINGLE(ctx) = NULL;
		(void) dprov_free_context(ctx);
		break;

	case DPROV_REQ_VERIFY_UPDATE:
		error = crypto_verify_update(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_verify_req.vr_data, NULL);
		break;

	case DPROV_REQ_VERIFY_FINAL:
		error = crypto_verify_final(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_verify_req.vr_signature, NULL);

		ASSERT(error != CRYPTO_BUFFER_TOO_SMALL);
		DPROV_CTX_SINGLE(ctx) = NULL;
		(void) dprov_free_context(ctx);
		break;

	case DPROV_REQ_VERIFY_ATOMIC:
	case DPROV_REQ_VERIFY_RECOVER_ATOMIC:
		/* structure assignment */
		mech = *taskq_req->dr_verify_req.vr_mechanism;

		mutex_enter(&softc->ds_lock);
		/* get key value for secret key algorithms */
		if (is_publickey_mech(mech.cm_type)) {
			if ((error = dprov_key_attr_asymmetric(softc,
			    taskq_req->dr_verify_req.vr_session_id,
			    taskq_req->dr_type,
			    taskq_req->dr_verify_req.vr_key, &key))
			    != CRYPTO_SUCCESS) {
				mutex_exit(&softc->ds_lock);
				break;
			}
			keyp = &key;
		} else {
			if ((error = dprov_key_value_secret(softc,
			    taskq_req->dr_verify_req.vr_session_id,
			    taskq_req->dr_type,
			    taskq_req->dr_verify_req.vr_key, &key))
			    != CRYPTO_SUCCESS) {
				mutex_exit(&softc->ds_lock);
				break;
			}
			keyp = &key;
		}
		mutex_exit(&softc->ds_lock);

		/* get the software provider for this mechanism */
		if ((error = dprov_get_sw_prov(
		    taskq_req->dr_verify_req.vr_mechanism, &pd,
		    &mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		/* Use a session id of zero since we use a software provider */
		if (taskq_req->dr_type == DPROV_REQ_VERIFY_ATOMIC)
			error = crypto_verify_prov(pd, 0, &mech, keyp,
			    taskq_req->dr_verify_req.vr_data,
			    NULL, taskq_req->dr_verify_req.vr_signature, NULL);
		else
			/*
			 * crypto_verify_recover_prov() has different argument
			 * order than crypto_verify_prov().
			 */
			error = crypto_verify_recover_prov(pd, 0, &mech, keyp,
			    taskq_req->dr_verify_req.vr_signature,
			    NULL, taskq_req->dr_verify_req.vr_data, NULL);

		/* release provider reference */
		KCF_PROV_REFRELE(pd);
		break;

	case DPROV_REQ_VERIFY_RECOVER:
		/*
		 * crypto_verify_recover_single() has different argument
		 * order than crypto_verify_single().
		 */
		error = crypto_verify_recover_single(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_verify_req.vr_signature,
		    taskq_req->dr_verify_req.vr_data, NULL);

		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_SINGLE(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;
	}

	dprov_op_done(taskq_req, error);
	DPROV_DEBUG(D_VERIFY, ("(%d) dprov_verify_task: end\n", instance));
}

/*
 * taskq dispatcher function for dual operations.
 */
static void
dprov_dual_task(dprov_req_t *taskq_req)
{
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;
	int error = CRYPTO_NOT_SUPPORTED;
	crypto_ctx_t *signverify_ctx = taskq_req->dr_dual_req.dr_signverify_ctx;
	crypto_ctx_t *cipher_ctx = taskq_req->dr_dual_req.dr_cipher_ctx;

	DPROV_SOFTC_FROM_REQ(taskq_req, softc, instance);
	DPROV_DEBUG(D_DUAL, ("(%d) dprov_dual_task: started\n", instance));

	switch (taskq_req->dr_type) {

	case DPROV_REQ_DIGEST_ENCRYPT_UPDATE:
		error = crypto_digest_encrypt_update(
		    DPROV_CTX_SINGLE(signverify_ctx),
		    DPROV_CTX_SINGLE(cipher_ctx),
		    taskq_req->dr_dual_req.dr_plaintext,
		    taskq_req->dr_dual_req.dr_ciphertext, NULL);
		break;

	case DPROV_REQ_DECRYPT_DIGEST_UPDATE:
		error = crypto_decrypt_digest_update(
		    DPROV_CTX_SINGLE(cipher_ctx),
		    DPROV_CTX_SINGLE(signverify_ctx),
		    taskq_req->dr_dual_req.dr_ciphertext,
		    taskq_req->dr_dual_req.dr_plaintext, NULL);
		break;

	case DPROV_REQ_SIGN_ENCRYPT_UPDATE:
		error = crypto_sign_encrypt_update(
		    DPROV_CTX_SINGLE(signverify_ctx),
		    DPROV_CTX_SINGLE(cipher_ctx),
		    taskq_req->dr_dual_req.dr_plaintext,
		    taskq_req->dr_dual_req.dr_ciphertext, NULL);
		break;

	case DPROV_REQ_DECRYPT_VERIFY_UPDATE:
		error = crypto_decrypt_verify_update(
		    DPROV_CTX_SINGLE(cipher_ctx),
		    DPROV_CTX_SINGLE(signverify_ctx),
		    taskq_req->dr_dual_req.dr_ciphertext,
		    taskq_req->dr_dual_req.dr_plaintext, NULL);
		break;
	}

	dprov_op_done(taskq_req, error);
	DPROV_DEBUG(D_DUAL, ("(%d) dprov_dual_task: end\n", instance));
}

/*
 * taskq dispatcher function for cipher operations.
 */
static void
dprov_cipher_task(dprov_req_t *taskq_req)
{
	kcf_provider_desc_t *pd;
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;
	int error = CRYPTO_NOT_SUPPORTED;
	crypto_ctx_t *ctx = taskq_req->dr_cipher_req.dr_ctx;
	crypto_key_t key, *keyp;
	crypto_mechanism_t mech;

	DPROV_SOFTC_FROM_REQ(taskq_req, softc, instance);
	DPROV_DEBUG(D_CIPHER, ("(%d) dprov_cipher_task: started\n", instance));

	switch (taskq_req->dr_type) {

	case DPROV_REQ_ENCRYPT_INIT:
	case DPROV_REQ_DECRYPT_INIT:
		/* allocate a dprov-private context */
		if ((error = dprov_alloc_context(taskq_req->dr_type, ctx)) !=
		    CRYPTO_SUCCESS)
			break;

		/* structure assignment */
		mech = *taskq_req->dr_cipher_req.dr_mechanism;

		mutex_enter(&softc->ds_lock);
		/* get key value for secret key algorithms */
		if (is_publickey_mech(mech.cm_type)) {
			if ((error = dprov_key_attr_asymmetric(softc,
			    ctx->cc_session, taskq_req->dr_type,
			    taskq_req->dr_cipher_req.dr_key, &key))
			    != CRYPTO_SUCCESS) {
				mutex_exit(&softc->ds_lock);
				break;
			}
			keyp = &key;
		} else {
			if ((error = dprov_key_value_secret(softc,
			    ctx->cc_session, taskq_req->dr_type,
			    taskq_req->dr_cipher_req.dr_key, &key))
			    != CRYPTO_SUCCESS) {
				mutex_exit(&softc->ds_lock);
				break;
			}
			keyp = &key;
		}
		mutex_exit(&softc->ds_lock);

		/* get the software provider for this mechanism */
		if ((error = dprov_get_sw_prov(
		    taskq_req->dr_cipher_req.dr_mechanism, &pd,
		    &mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		/* Use a session id of zero since we use a software provider */
		if (taskq_req->dr_type == DPROV_REQ_ENCRYPT_INIT)
			error = crypto_encrypt_init_prov(pd, 0, &mech, keyp,
			    NULL, &DPROV_CTX_SINGLE(ctx), NULL);
		else
			error = crypto_decrypt_init_prov(pd, 0, &mech, keyp,
			    NULL, &DPROV_CTX_SINGLE(ctx), NULL);

		if (ctx->cc_flags & CRYPTO_INIT_OPSTATE) {
			crypto_ctx_t *lctx =
			    (crypto_ctx_t *)(DPROV_CTX_SINGLE(ctx));

			ctx->cc_opstate = lctx->cc_provider_private;
			ctx->cc_flags |= CRYPTO_USE_OPSTATE;
		}

		/* release provider reference */
		KCF_PROV_REFRELE(pd);
		break;

	case DPROV_REQ_ENCRYPT:
		error = crypto_encrypt_single(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_cipher_req.dr_plaintext,
		    taskq_req->dr_cipher_req.dr_ciphertext, NULL);

		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_SINGLE(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;

	case DPROV_REQ_DECRYPT:
		error = crypto_decrypt_single(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_cipher_req.dr_ciphertext,
		    taskq_req->dr_cipher_req.dr_plaintext, NULL);

		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_SINGLE(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;

	case DPROV_REQ_ENCRYPT_UPDATE:
		ASSERT(!(ctx->cc_flags & CRYPTO_INIT_OPSTATE) ||
		    (ctx->cc_flags & CRYPTO_USE_OPSTATE));
		error = crypto_encrypt_update(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_cipher_req.dr_plaintext,
		    taskq_req->dr_cipher_req.dr_ciphertext, NULL);
		break;

	case DPROV_REQ_DECRYPT_UPDATE:
		ASSERT(!(ctx->cc_flags & CRYPTO_INIT_OPSTATE) ||
		    (ctx->cc_flags & CRYPTO_USE_OPSTATE));
		error = crypto_decrypt_update(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_cipher_req.dr_ciphertext,
		    taskq_req->dr_cipher_req.dr_plaintext, NULL);
		break;

	case DPROV_REQ_ENCRYPT_FINAL:
		error = crypto_encrypt_final(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_cipher_req.dr_ciphertext, NULL);
		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_SINGLE(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;

	case DPROV_REQ_DECRYPT_FINAL:
		error = crypto_decrypt_final(DPROV_CTX_SINGLE(ctx),
		    taskq_req->dr_cipher_req.dr_plaintext, NULL);
		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_SINGLE(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;

	case DPROV_REQ_ENCRYPT_ATOMIC:
	case DPROV_REQ_DECRYPT_ATOMIC:
		/* structure assignment */
		mech = *taskq_req->dr_cipher_req.dr_mechanism;

		mutex_enter(&softc->ds_lock);
		/* get key value for secret key algorithms */
		if (is_publickey_mech(mech.cm_type)) {
			if ((error = dprov_key_attr_asymmetric(softc,
			    taskq_req->dr_cipher_req.dr_session_id,
			    taskq_req->dr_type,
			    taskq_req->dr_cipher_req.dr_key,
			    &key)) != CRYPTO_SUCCESS) {
				mutex_exit(&softc->ds_lock);
				break;
			}
			keyp = &key;
		} else {
			if ((error = dprov_key_value_secret(softc,
			    taskq_req->dr_cipher_req.dr_session_id,
			    taskq_req->dr_type, taskq_req->dr_cipher_req.dr_key,
			    &key))
			    != CRYPTO_SUCCESS) {
				mutex_exit(&softc->ds_lock);
				break;
			}
			keyp = &key;
		}
		mutex_exit(&softc->ds_lock);

		/* get the software provider for this mechanism */
		if ((error = dprov_get_sw_prov(
		    taskq_req->dr_cipher_req.dr_mechanism, &pd,
		    &mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		/* use a session id of zero since we use a software provider */
		if (taskq_req->dr_type == DPROV_REQ_ENCRYPT_ATOMIC)
			error = crypto_encrypt_prov(pd, 0, &mech,
			    taskq_req->dr_cipher_req.dr_plaintext,
			    keyp, NULL,
			    taskq_req->dr_cipher_req.dr_ciphertext, NULL);
		else
			error = crypto_decrypt_prov(pd, 0, &mech,
			    taskq_req->dr_cipher_req.dr_ciphertext,
			    keyp, NULL,
			    taskq_req->dr_cipher_req.dr_plaintext, NULL);

		/* release provider reference */
		KCF_PROV_REFRELE(pd);

		break;
	}

	dprov_op_done(taskq_req, error);
	DPROV_DEBUG(D_MAC, ("(%d) dprov_mac_task: end\n", instance));
}

/*
 * Helper function for the cipher/mac dual operation taskq dispatch
 * function. Initialize the cipher and mac key values and find the
 * providers that can process the request for the specified mechanisms.
 */
static int
dprov_cipher_mac_key_pd(dprov_state_t *softc, crypto_session_id_t sid,
    dprov_req_t *taskq_req, crypto_key_t *cipher_key, crypto_key_t *mac_key,
    kcf_provider_desc_t **cipher_pd, kcf_provider_desc_t **mac_pd,
    crypto_mech_type_t *cipher_mech_type, crypto_mech_type_t *mac_mech_type)
{
	int error;

	/* get the cipher key value */
	mutex_enter(&softc->ds_lock);
	error = dprov_key_value_secret(softc, sid, DPROV_REQ_ENCRYPT_ATOMIC,
	    taskq_req->dr_cipher_mac_req.mr_cipher_key, cipher_key);
	if (error != CRYPTO_SUCCESS) {
		mutex_exit(&softc->ds_lock);
		return (error);
	}

	/* get the mac key value */
	error = dprov_key_value_secret(softc, sid, DPROV_REQ_MAC_ATOMIC,
	    taskq_req->dr_cipher_mac_req.mr_mac_key, mac_key);
	mutex_exit(&softc->ds_lock);
	if (error != CRYPTO_SUCCESS)
		return (error);

	/* get the SW provider to perform the cipher operation */
	if ((error = dprov_get_sw_prov(
	    taskq_req->dr_cipher_mac_req.mr_cipher_mech, cipher_pd,
	    cipher_mech_type)) != CRYPTO_SUCCESS)
		return (error);

	/* get the SW provider to perform the mac operation */
	error = dprov_get_sw_prov(taskq_req->dr_cipher_mac_req.mr_mac_mech,
	    mac_pd, mac_mech_type);

	return (error);
}

/*
 * taskq dispatcher function for cipher/mac dual operations.
 */
static void
dprov_cipher_mac_task(dprov_req_t *taskq_req)
{
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;
	int error = CRYPTO_NOT_SUPPORTED;
	crypto_ctx_t *ctx = taskq_req->dr_cipher_mac_req.mr_ctx;
	kcf_provider_desc_t *cipher_pd;
	kcf_provider_desc_t *mac_pd;
	crypto_key_t cipher_key;
	crypto_key_t mac_key;
	crypto_dual_data_t *dual_data =
	    taskq_req->dr_cipher_mac_req.mr_dual_data;
	crypto_data_t cipher_data;
	crypto_data_t mac_data;
	crypto_mechanism_t cipher_mech, mac_mech;
	crypto_session_id_t session_id;

	DPROV_SOFTC_FROM_REQ(taskq_req, softc, instance);
	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_cipher_mac_task: started\n",
	    instance));

	switch (taskq_req->dr_type) {
	case DPROV_REQ_ENCRYPT_MAC_INIT:
	case DPROV_REQ_MAC_DECRYPT_INIT:
		/* structure assignment */
		cipher_mech = *taskq_req->dr_cipher_mac_req.mr_cipher_mech;
		mac_mech = *taskq_req->dr_cipher_mac_req.mr_mac_mech;

		/* get the keys values and providers to use for operations */
		if ((error = dprov_cipher_mac_key_pd(softc, ctx->cc_session,
		    taskq_req, &cipher_key, &mac_key, &cipher_pd, &mac_pd,
		    &cipher_mech.cm_type, &mac_mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		/* allocate a dprov-private context */
		if ((error = dprov_alloc_context(taskq_req->dr_type, ctx)) !=
		    CRYPTO_SUCCESS)
			break;

		if (taskq_req->dr_type == DPROV_REQ_ENCRYPT_MAC_INIT)
			/* do the encryption initialization */
			error = crypto_encrypt_init_prov(cipher_pd, 0,
			    &cipher_mech, &cipher_key, NULL,
			    &DPROV_CTX_DUAL_CIPHER(ctx), NULL);
		else
			/* do the decryption initialization */
			error = crypto_decrypt_init_prov(cipher_pd, 0,
			    &cipher_mech, &cipher_key, NULL,
			    &DPROV_CTX_DUAL_CIPHER(ctx), NULL);
		if (error != CRYPTO_SUCCESS)
			break;

		/* do the mac initialization */
		if ((error = crypto_mac_init_prov(mac_pd, 0,
		    &mac_mech, &mac_key, NULL, &DPROV_CTX_DUAL_MAC(ctx),
		    NULL)) != CRYPTO_SUCCESS)
			break;

		/* release references to providers */
		KCF_PROV_REFRELE(cipher_pd);
		KCF_PROV_REFRELE(mac_pd);

		break;

	case DPROV_REQ_ENCRYPT_MAC: {
		size_t encrypted;
		boolean_t inplace;

		crypto_data_t *plaintext_tmp, *ciphertext_tmp;

		cipher_data = *((crypto_data_t *)dual_data);

		/* do an encrypt update */
		inplace = (taskq_req->dr_cipher_mac_req.mr_data == NULL);
		if (inplace) {
			plaintext_tmp = &cipher_data;
			ciphertext_tmp = NULL;
		} else {
			plaintext_tmp = taskq_req->dr_cipher_mac_req.mr_data;
			ciphertext_tmp = &cipher_data;
		}
		if ((error = crypto_encrypt_update(DPROV_CTX_DUAL_CIPHER(ctx),
		    plaintext_tmp, ciphertext_tmp, NULL)) != CRYPTO_SUCCESS)
			break;

		/* do an encrypt final */
		encrypted = cipher_data.cd_length;

		cipher_data.cd_offset += encrypted;
		cipher_data.cd_length = dual_data->dd_len1 - encrypted;

		if ((error = crypto_encrypt_final(DPROV_CTX_DUAL_CIPHER(ctx),
		    &cipher_data, NULL)) != CRYPTO_SUCCESS)
			break;

		/*
		 * Do a mac update on the resulting ciphertext, but with no
		 * more bytes than specified by dual_data, and starting at
		 * offset specified by dual_data. For in-place operations,
		 * we use the length specified by the dual_data.
		 */
		mac_data = cipher_data;
		mac_data.cd_offset = dual_data->dd_offset2;
		mac_data.cd_length = dual_data->dd_len2;
		if ((error = crypto_mac_update(DPROV_CTX_DUAL_MAC(ctx),
		    &mac_data, NULL)) != CRYPTO_SUCCESS)
			break;

		/* do a mac final */
		error = crypto_mac_final(DPROV_CTX_DUAL_MAC(ctx),
		    taskq_req->dr_cipher_mac_req.mr_mac, NULL);

		/* Set the total size of the ciphertext, when successful */
		if (error == CRYPTO_SUCCESS)
			dual_data->dd_len1 = encrypted + cipher_data.cd_length;

		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_DUAL_CIPHER(ctx) = NULL;
			DPROV_CTX_DUAL_MAC(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;
	}

	case DPROV_REQ_ENCRYPT_MAC_UPDATE: {
		crypto_data_t *plaintext_tmp, *ciphertext_tmp;
		size_t encrypted;
		ssize_t maclen;
		boolean_t inplace;

		cipher_data = *((crypto_data_t *)dual_data);

		/* do an encrypt update */
		inplace = (taskq_req->dr_cipher_mac_req.mr_data == NULL);
		if (inplace) {
			plaintext_tmp = &cipher_data;
			ciphertext_tmp = NULL;
		} else {
			plaintext_tmp = taskq_req->dr_cipher_mac_req.mr_data;
			ciphertext_tmp = &cipher_data;
		}
		if ((error = crypto_encrypt_update(DPROV_CTX_DUAL_CIPHER(ctx),
		    plaintext_tmp, ciphertext_tmp, NULL)) != CRYPTO_SUCCESS)
			break;

		encrypted = cipher_data.cd_length;

		/*
		 * Do a mac update on the resulting ciphertext, but with no
		 * more bytes than specified by dual_data, and starting at
		 * offset specified by dual_data. For in-place operations,
		 * we use the length specified by the dual_data.
		 * There is an edge case, when the encryption step produced
		 * zero bytes in the ciphertext. Only the portion between
		 * offset2 and offset1 is then thrown in the MAC mix.
		 */
		maclen = dual_data->dd_offset1 - dual_data->dd_offset2 +
		    encrypted;
		if (maclen > 0) {
			mac_data = cipher_data;
			mac_data.cd_offset = dual_data->dd_offset2;
			mac_data.cd_length = min(dual_data->dd_len2, maclen);
			if ((error = crypto_mac_update(DPROV_CTX_DUAL_MAC(ctx),
			    &mac_data, NULL)) != CRYPTO_SUCCESS)
				break;
		}
		/* Set the total size of the ciphertext, when successful */
		if (error == CRYPTO_SUCCESS)
			dual_data->dd_len1 = encrypted;

		break;
	}

	case DPROV_REQ_ENCRYPT_MAC_FINAL:
		cipher_data = *((crypto_data_t *)dual_data);

		/* do an encrypt final */
		if ((error = crypto_encrypt_final(DPROV_CTX_DUAL_CIPHER(ctx),
		    taskq_req->dr_cipher_mac_req.mr_data == NULL ?
		    &cipher_data : taskq_req->dr_cipher_mac_req.mr_data,
		    NULL)) != CRYPTO_SUCCESS)
			break;

		/*
		 * If ciphertext length is different from zero, do a mac
		 * update on it. This does not apply to in-place since we
		 * do not allow partial updates, hence no final residual.
		 */
		if (taskq_req->dr_cipher_mac_req.mr_data != NULL &&
		    taskq_req->dr_cipher_mac_req.mr_data->cd_length > 0)
			if ((error = crypto_mac_update(DPROV_CTX_DUAL_MAC(ctx),
			    taskq_req->dr_cipher_mac_req.mr_data, NULL)) !=
			    CRYPTO_SUCCESS)
				break;

		/* do a mac final */
		error = crypto_mac_final(DPROV_CTX_DUAL_MAC(ctx),
		    taskq_req->dr_cipher_mac_req.mr_mac, NULL);

		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_DUAL_CIPHER(ctx) = NULL;
			DPROV_CTX_DUAL_MAC(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;

	case DPROV_REQ_ENCRYPT_MAC_ATOMIC: {
		crypto_data_t *plaintext_tmp, *ciphertext_tmp;
		boolean_t inplace;

		cipher_data = *((crypto_data_t *)dual_data);

		/* do an encrypt atomic */
		inplace = (taskq_req->dr_cipher_mac_req.mr_data == NULL);
		if (inplace) {
			plaintext_tmp = &cipher_data;
			ciphertext_tmp = NULL;
		} else {
			plaintext_tmp = taskq_req->dr_cipher_mac_req.mr_data;
			ciphertext_tmp = &cipher_data;
		}

		/* structure assignment */
		cipher_mech = *taskq_req->dr_cipher_mac_req.mr_cipher_mech;
		mac_mech = *taskq_req->dr_cipher_mac_req.mr_mac_mech;
		session_id = taskq_req->dr_cipher_mac_req.mr_session_id;

		/* get the keys values and providers to use for operations */
		if ((error = dprov_cipher_mac_key_pd(softc, session_id,
		    taskq_req, &cipher_key, &mac_key, &cipher_pd, &mac_pd,
		    &cipher_mech.cm_type, &mac_mech.cm_type)) !=
		    CRYPTO_SUCCESS)
			break;

		/* do the atomic encrypt */
		if ((error = crypto_encrypt_prov(cipher_pd, 0,
		    &cipher_mech, plaintext_tmp, &cipher_key, NULL,
		    ciphertext_tmp, NULL)) != CRYPTO_SUCCESS)
			break;

		/* do the atomic mac */
		mac_data = cipher_data;
		mac_data.cd_length = dual_data->dd_len2;
		mac_data.cd_offset = dual_data->dd_offset2;
		error = crypto_mac_prov(mac_pd, 0, &mac_mech, &mac_data,
		    &mac_key, NULL, taskq_req->dr_cipher_mac_req.mr_mac, NULL);

		dual_data->dd_len1 = cipher_data.cd_length;

		break;
	}

	case DPROV_REQ_MAC_DECRYPT: {
		uint_t decrypted;
		crypto_data_t plaintext_tmp;

		cipher_data = *((crypto_data_t *)dual_data);

		/* do a mac update and final on the ciphertext */
		if ((error = crypto_mac_update(DPROV_CTX_DUAL_MAC(ctx),
		    &mac_data, NULL)) != CRYPTO_SUCCESS)
			break;

		/* do a mac final */
		if ((error = crypto_mac_final(DPROV_CTX_DUAL_MAC(ctx),
		    taskq_req->dr_cipher_mac_req.mr_mac, NULL)) !=
		    CRYPTO_SUCCESS)
			break;

		/* do an decrypt update */
		cipher_data = mac_data;
		cipher_data.cd_length = dual_data->dd_len2;
		cipher_data.cd_offset = dual_data->dd_offset2;
		if (taskq_req->dr_cipher_mac_req.mr_data == NULL)
			/* in-place */
			plaintext_tmp = cipher_data;
		else
			plaintext_tmp = *taskq_req->dr_cipher_mac_req.mr_data;

		if ((error = crypto_decrypt_update(DPROV_CTX_DUAL_CIPHER(ctx),
		    &cipher_data, taskq_req->dr_cipher_mac_req.mr_data,
		    NULL)) != CRYPTO_SUCCESS)
			break;

		/* do an decrypt final */
		if (taskq_req->dr_cipher_mac_req.mr_data == NULL)
			/* in-place, everything must have been decrypted */
			decrypted = cipher_data.cd_length;
		else
			decrypted =
			    taskq_req->dr_cipher_mac_req.mr_data->cd_length;
		plaintext_tmp.cd_offset += decrypted;
		plaintext_tmp.cd_length -= decrypted;

		error = crypto_decrypt_final(DPROV_CTX_DUAL_CIPHER(ctx),
		    &plaintext_tmp, NULL);
		if (taskq_req->dr_cipher_mac_req.mr_data != NULL)
			taskq_req->dr_cipher_mac_req.mr_data->cd_length +=
			    plaintext_tmp.cd_length;

		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_DUAL_MAC(ctx) = NULL;
			DPROV_CTX_DUAL_CIPHER(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;
	}

	case DPROV_REQ_MAC_DECRYPT_UPDATE:
		cipher_data = *((crypto_data_t *)dual_data);

		/* do mac update */
		if ((error = crypto_mac_update(DPROV_CTX_DUAL_MAC(ctx),
		    &cipher_data, NULL)) != CRYPTO_SUCCESS)
			break;

		/* do a decrypt update */
		cipher_data.cd_length = dual_data->dd_len2;
		cipher_data.cd_offset = dual_data->dd_offset2;
		error = crypto_decrypt_update(DPROV_CTX_DUAL_CIPHER(ctx),
		    &cipher_data, taskq_req->dr_cipher_mac_req.mr_data, NULL);

		break;

	case DPROV_REQ_MAC_DECRYPT_FINAL:
		/* do a mac final */
		if ((error = crypto_mac_final(DPROV_CTX_DUAL_MAC(ctx),
		    taskq_req->dr_cipher_mac_req.mr_mac, NULL)) !=
		    CRYPTO_SUCCESS)
			break;

		/* do a decrypt final */
		error = crypto_decrypt_final(DPROV_CTX_DUAL_CIPHER(ctx),
		    taskq_req->dr_cipher_mac_req.mr_data, NULL);

		if (error != CRYPTO_BUFFER_TOO_SMALL) {
			DPROV_CTX_DUAL_MAC(ctx) = NULL;
			DPROV_CTX_DUAL_CIPHER(ctx) = NULL;
			(void) dprov_free_context(ctx);
		}
		break;

	case DPROV_REQ_MAC_DECRYPT_ATOMIC:
	case DPROV_REQ_MAC_VERIFY_DECRYPT_ATOMIC:
		cipher_data = *((crypto_data_t *)dual_data);

		/* structure assignment */
		cipher_mech = *taskq_req->dr_cipher_mac_req.mr_cipher_mech;
		mac_mech = *taskq_req->dr_cipher_mac_req.mr_mac_mech;
		session_id = taskq_req->dr_cipher_mac_req.mr_session_id;

		/* get the keys values and providers to use for operations */
		if ((error = dprov_cipher_mac_key_pd(softc, session_id,
		    taskq_req, &cipher_key, &mac_key, &cipher_pd, &mac_pd,
		    &cipher_mech.cm_type, &mac_mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		/* do the atomic mac */
		if (taskq_req->dr_type == DPROV_REQ_MAC_DECRYPT_ATOMIC)
			error = crypto_mac_prov(mac_pd, 0, &mac_mech,
			    &cipher_data, &mac_key, NULL,
			    taskq_req->dr_cipher_mac_req.mr_mac, NULL);
		else
			/* DPROV_REQ_MAC_VERIFY_DECRYPT_ATOMIC */
			error = crypto_mac_verify_prov(mac_pd, 0, &mac_mech,
			    &cipher_data, &mac_key, NULL,
			    taskq_req->dr_cipher_mac_req.mr_mac, NULL);

		if (error != CRYPTO_SUCCESS)
			break;

		/* do the atomic decrypt */
		cipher_data.cd_length = dual_data->dd_len2;
		cipher_data.cd_offset = dual_data->dd_offset2;
		error = crypto_decrypt_prov(cipher_pd, 0, &cipher_mech,
		    &cipher_data, &cipher_key, NULL,
		    taskq_req->dr_cipher_mac_req.mr_data, NULL);

		break;
	}

	dprov_op_done(taskq_req, error);
	DPROV_DEBUG(D_CIPHER_MAC, ("(%d) dprov_cipher_mac_task: end\n",
	    instance));
}

/*
 * taskq dispatcher function for random number generation.
 */
static void
dprov_random_task(dprov_req_t *taskq_req)
{
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;
	int error = CRYPTO_SUCCESS;

	DPROV_SOFTC_FROM_REQ(taskq_req, softc, instance);
	DPROV_DEBUG(D_RANDOM, ("(%d) dprov_random_task: started\n", instance));

	mutex_enter(&softc->ds_lock);

	switch (taskq_req->dr_type) {

	DPROV_REQ_RANDOM_SEED:
		/*
		 * Since we don't really generate random numbers
		 * nothing to do.
		 */
		break;

	case DPROV_REQ_RANDOM_GENERATE: {
		uint_t i;
		uchar_t c = 0;

		/*
		 * We don't generate random numbers so that the result
		 * of the operation can be checked during testing.
		 */

		for (i = 0; i < taskq_req->dr_random_req.rr_len; i++)
			taskq_req->dr_random_req.rr_buf[i] = c++;

		break;
	}
	}

	mutex_exit(&softc->ds_lock);
	dprov_op_done(taskq_req, error);
	DPROV_DEBUG(D_RANDOM, ("(%d) dprov_random_task: end\n", instance));
}


/*
 * taskq dispatcher function for session management operations.
 */
static void
dprov_session_task(dprov_req_t *taskq_req)
{
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;
	int error = CRYPTO_NOT_SUPPORTED;
	crypto_session_id_t session_id =
	    taskq_req->dr_session_req.sr_session_id;
	dprov_session_t *session;
	dprov_object_t *object;
	int i;

	DPROV_SOFTC_FROM_REQ(taskq_req, softc, instance);
	DPROV_DEBUG(D_SESSION, ("(%d) dprov_session_task: started\n",
	    instance));

	mutex_enter(&softc->ds_lock);

	if (taskq_req->dr_type != DPROV_REQ_SESSION_OPEN)
		/* validate session id and get ptr to session */
		if ((session = softc->ds_sessions[session_id]) == NULL) {
			mutex_exit(&softc->ds_lock);
			dprov_op_done(taskq_req, CRYPTO_SESSION_HANDLE_INVALID);
			return;
		}

	switch (taskq_req->dr_type) {

	case DPROV_REQ_SESSION_OPEN: {
		dprov_session_t **new_sessions;

		if (softc->ds_token_initialized == B_FALSE) {
			error = CRYPTO_OPERATION_NOT_INITIALIZED;
			break;
		}

		/* search for available session slot */
		for (i = 0; i < softc->ds_sessions_slots; i++)
			if (softc->ds_sessions[i] == NULL)
				break;

		if (i == softc->ds_sessions_slots) {
			/* ran out of slots, grow sessions array */
			new_sessions = kmem_zalloc(2 * softc->ds_sessions_slots,
			    KM_NOSLEEP);
			if (new_sessions == NULL) {
				error = CRYPTO_SESSION_COUNT;
				break;
			}
			bcopy(softc->ds_sessions, new_sessions,
			    softc->ds_sessions_slots);
			kmem_free(softc->ds_sessions, softc->ds_sessions_slots *
			    sizeof (dprov_session_t *));
			softc->ds_sessions = new_sessions;
			softc->ds_sessions_slots *= 2;
		}

		/* allocate and initialize new session */
		softc->ds_sessions[i] = kmem_zalloc(
		    sizeof (dprov_session_t), KM_NOSLEEP);
		if (softc->ds_sessions[i] == NULL) {
			error = CRYPTO_HOST_MEMORY;
			break;
		}
		softc->ds_sessions_count++;

		/* initialize session state */
		softc->ds_sessions[i]->ds_state = DPROV_SESSION_STATE_PUBLIC;

		/* return new session id to caller */
		*(taskq_req->dr_session_req.sr_session_id_ptr) = i;

		error = CRYPTO_SUCCESS;
		break;
	}

	case DPROV_REQ_SESSION_CLOSE:
		softc->ds_sessions[session_id] = NULL;

		if (softc->ds_token_initialized == B_FALSE) {
			error = CRYPTO_OPERATION_NOT_INITIALIZED;
			break;
		}

		dprov_release_session_objects(session);

		/* free session state and corresponding slot */
		kmem_free(session, sizeof (dprov_session_t));
		softc->ds_sessions_count--;

		error = CRYPTO_SUCCESS;
		break;

	case DPROV_REQ_SESSION_LOGIN: {
		char *pin = taskq_req->dr_session_req.sr_pin;
		size_t pin_len = taskq_req->dr_session_req.sr_pin_len;
		crypto_user_type_t user_type =
		    taskq_req->dr_session_req.sr_user_type;

		/* check user type */
		if (user_type != CRYPTO_SO && user_type != CRYPTO_USER) {
			error = CRYPTO_USER_TYPE_INVALID;
			break;
		}

		/* check pin length */
		if (pin_len > DPROV_MAX_PIN_LEN) {
			error = CRYPTO_PIN_LEN_RANGE;
			break;
		}

		/* check pin */
		if (pin == NULL) {
			error = CRYPTO_PIN_INVALID;
			break;
		}

		/* validate PIN state */
		if ((user_type == CRYPTO_SO) && !softc->ds_token_initialized ||
		    (user_type == CRYPTO_USER) && !softc->ds_user_pin_set) {
			error = CRYPTO_USER_PIN_NOT_INITIALIZED;
			break;
		}

		if ((user_type == CRYPTO_SO &&
		    softc->ds_sessions[session_id]->ds_state ==
		    DPROV_SESSION_STATE_SO) ||
		    (user_type == CRYPTO_USER &&
		    softc->ds_sessions[session_id]->ds_state ==
		    DPROV_SESSION_STATE_USER)) {
			/* SO or user already logged in */
			error = CRYPTO_USER_ALREADY_LOGGED_IN;
			break;
		}

		if (softc->ds_sessions[session_id]->ds_state !=
		    DPROV_SESSION_STATE_PUBLIC) {
			/* another user already logged in */
			error = CRYPTO_USER_ANOTHER_ALREADY_LOGGED_IN;
			break;
		}

		/* everything's fine, update session */
		softc->ds_sessions[session_id]->ds_state =
		    user_type == CRYPTO_SO ?
		    DPROV_SESSION_STATE_SO : DPROV_SESSION_STATE_USER;

		error = CRYPTO_SUCCESS;
		break;
	}

	case DPROV_REQ_SESSION_LOGOUT:
		/* fail if not logged in */
		if (softc->ds_sessions[session_id]->ds_state ==
		    DPROV_SESSION_STATE_PUBLIC) {
			error = CRYPTO_USER_NOT_LOGGED_IN;
			break;
		}

		/*
		 * Destroy all private session objects.
		 * Invalidate handles to all private objects.
		 */
		for (i = 0; i < DPROV_MAX_OBJECTS; i++) {
			object = softc->ds_sessions[session_id]->ds_objects[i];
			if (object != NULL && dprov_object_is_private(object)) {
				if (!dprov_object_is_token(object))
					/* It's a session object, free it */
					DPROV_OBJECT_REFRELE(object);
				softc->ds_sessions[session_id]->ds_objects[i] =
				    NULL;
			}
		}

		/* update session state */
		softc->ds_sessions[session_id]->ds_state =
		    DPROV_SESSION_STATE_PUBLIC;

		error = CRYPTO_SUCCESS;
		break;
	}

	mutex_exit(&softc->ds_lock);
	dprov_op_done(taskq_req, error);
	DPROV_DEBUG(D_SESSION, ("(%d) dprov_session_task: end\n", instance));
}

/* return true if attribute is defined to be a PKCS#11 long */
static boolean_t
fixed_size_attribute(crypto_attr_type_t type)
{
	return (type == DPROV_CKA_CLASS ||
	    type == DPROV_CKA_CERTIFICATE_TYPE ||
	    type == DPROV_CKA_KEY_TYPE ||
	    type == DPROV_HW_FEATURE_TYPE);
}

/*
 * Attributes defined to be a PKCS#11 long causes problems for dprov
 * because 32-bit applications set the size to 4 and 64-bit applications
 * set the size to 8. dprov always stores these fixed-size attributes
 * as uint32_t.
 */
static ssize_t
attribute_size(crypto_attr_type_t type, ssize_t len)
{
	if (fixed_size_attribute(type))
		return (sizeof (uint32_t));

	return (len);
}

/*
 * taskq dispatcher function for object management operations.
 */
static void
dprov_object_task(dprov_req_t *taskq_req)
{
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;
	int error = CRYPTO_NOT_SUPPORTED;
	crypto_object_id_t object_id = taskq_req->dr_object_req.or_object_id;
	crypto_session_id_t session_id = taskq_req->dr_object_req.or_session_id;
	crypto_object_attribute_t *template =
	    taskq_req->dr_object_req.or_template;
	uint_t attr_count = taskq_req->dr_object_req.or_attribute_count;
	dprov_object_t *object;
	dprov_session_t *session;

	DPROV_SOFTC_FROM_REQ(taskq_req, softc, instance);
	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_task: started\n", instance));

	mutex_enter(&softc->ds_lock);

	/* validate session id and get ptr to session */
	if ((session = softc->ds_sessions[session_id]) == NULL) {
		mutex_exit(&softc->ds_lock);
		dprov_op_done(taskq_req, CRYPTO_SESSION_HANDLE_INVALID);
		return;
	}

	switch (taskq_req->dr_type) {

	case DPROV_REQ_OBJECT_CREATE:
		/* create the object from the specified template */
		if ((error = dprov_create_object_from_template(softc, session,
		    template, attr_count,
		    taskq_req->dr_object_req.or_object_id_ptr, B_TRUE,
		    B_FALSE)) != CRYPTO_SUCCESS)
			break;

		break;

	case DPROV_REQ_OBJECT_COPY:
		/* check object id */
		if (object_id >= DPROV_MAX_OBJECTS ||
		    (object = session->ds_objects[object_id]) == NULL) {
			error = CRYPTO_OBJECT_HANDLE_INVALID;
			break;
		}

		/*
		 * Create a new object from the object passed as
		 * argument.
		 */
		if ((error = dprov_create_object_from_template(softc, session,
		    object->do_attr, DPROV_MAX_ATTR,
		    taskq_req->dr_object_req.or_object_id_ptr, B_TRUE,
		    B_FALSE)) != CRYPTO_SUCCESS)
			break;

		/*
		 * Add the attributes specified by the template to the
		 * newly created object, replacing existing ones if needed.
		 */
		error = dprov_object_set_attr(session,
		    *taskq_req->dr_object_req.or_object_id_ptr,
		    taskq_req->dr_object_req.or_template,
		    taskq_req->dr_object_req.or_attribute_count, B_TRUE);

		break;

	case DPROV_REQ_OBJECT_DESTROY:
		/* destroy the object */
		error = dprov_destroy_object(softc, session,
		    taskq_req->dr_object_req.or_object_id);

		break;

	case DPROV_REQ_OBJECT_GET_SIZE:
		/* get ptr to object */
		if (object_id >= DPROV_MAX_OBJECTS ||
		    session->ds_objects[object_id] == NULL) {
			error = CRYPTO_OBJECT_HANDLE_INVALID;
			break;
		}

		/*
		 * The PKCS11 specification does not specifies what
		 * the object size really is, here we just return
		 * the number of possible attributes of the object.
		 */
		*taskq_req->dr_object_req.or_object_size = DPROV_MAX_ATTR;

		error = CRYPTO_SUCCESS;
		break;

	case DPROV_REQ_OBJECT_GET_ATTRIBUTE_VALUE: {
		crypto_attr_type_t type;
		size_t olen, tlen;
		offset_t offset;
		int tmpl_idx;
		int object_idx;
		ulong_t class = DPROV_CKO_DATA;
		boolean_t extractable = B_TRUE;

		error = CRYPTO_SUCCESS;

		/* get ptr to object */
		if (object_id >= DPROV_MAX_OBJECTS ||
		    (object = session->ds_objects[object_id]) == NULL) {
			error = CRYPTO_OBJECT_HANDLE_INVALID;
			break;
		}

		(void) dprov_get_object_attr_boolean(object,
		    DPROV_CKA_EXTRACTABLE, &extractable);

		(void) dprov_get_object_attr_ulong(object,
		    DPROV_CKA_CLASS, &class);

		/* return the specified attributes, when possible */
		for (tmpl_idx = 0; tmpl_idx < attr_count; tmpl_idx++) {
			/*
			 * Attribute can't be revealed if the CKA_EXTRACTABLE
			 * attribute is set to false.
			 */
			type = template[tmpl_idx].oa_type;
			if (!extractable && class == DPROV_CKO_SECRET_KEY) {
				if (type == DPROV_CKA_VALUE) {
					template[tmpl_idx].oa_value_len = -1;
					error = CRYPTO_ATTRIBUTE_SENSITIVE;
					continue;
				}
			}
			if (!extractable && class == DPROV_CKO_PRIVATE_KEY) {
				if (type == DPROV_CKA_PRIVATE_EXPONENT) {
					template[tmpl_idx].oa_value_len = -1;
					error = CRYPTO_ATTRIBUTE_SENSITIVE;
					continue;
				}
			}

			object_idx = dprov_find_attr(object->do_attr,
			    DPROV_MAX_ATTR, type);
			if (object_idx == -1) {
				/* attribute not found in object */
				template[tmpl_idx].oa_value_len = -1;
				error = CRYPTO_ATTRIBUTE_TYPE_INVALID;
				continue;
			}

			tlen = template[tmpl_idx].oa_value_len;
			olen = object->do_attr[object_idx].oa_value_len;
			/* return attribute length */
			if (template[tmpl_idx].oa_value == NULL) {
				/*
				 * The size of the attribute is set by the
				 * library according to the data model of the
				 * application, so don't overwrite it with
				 * dprov's size.
				 */
				if (!fixed_size_attribute(type))
					template[tmpl_idx].oa_value_len = olen;
				continue;
			}

			if (tlen < olen) {
				template[tmpl_idx].oa_value_len = -1;
				error = CRYPTO_BUFFER_TOO_SMALL;
				continue;
			}

			/* copy attribute value */
			bzero(template[tmpl_idx].oa_value, tlen);

			offset = 0;
#ifdef _BIG_ENDIAN
			if (fixed_size_attribute(type)) {
				offset = tlen - olen;
			}
#endif
			bcopy(object->do_attr[object_idx].oa_value,
			    &template[tmpl_idx].oa_value[offset], olen);

			/* don't update length for fixed-size attributes */
			if (!fixed_size_attribute(type))
				template[tmpl_idx].oa_value_len = olen;
		}

		break;
	}

	case DPROV_REQ_OBJECT_SET_ATTRIBUTE_VALUE:
		/*
		 * Add the attributes specified by the template to the
		 * newly created object, replacing existing ones if needed.
		 */
		error = dprov_object_set_attr(session,
		    taskq_req->dr_object_req.or_object_id,
		    taskq_req->dr_object_req.or_template,
		    taskq_req->dr_object_req.or_attribute_count, B_TRUE);

		break;

	case DPROV_REQ_OBJECT_FIND_INIT: {
		dprov_find_ctx_t *find_ctx;
		int so_idx;		/* session object index */
		int to_idx;		/* token object index */

		error = CRYPTO_SUCCESS;
		/* allocate find context */
		find_ctx = kmem_zalloc(sizeof (dprov_find_ctx_t), KM_SLEEP);
		*taskq_req->dr_object_req.or_find_pp = find_ctx;

		/* first go through the existing session objects */
		for (so_idx = 0; so_idx < DPROV_MAX_OBJECTS; so_idx++) {
			if ((object = session->ds_objects[so_idx]) == NULL)
				continue;

			/* setting count to zero means find all objects */
			if (attr_count > 0) {
				if (!dprov_attributes_match(object, template,
				    attr_count))
					continue;
			}

			/* session object attributes matches template */
			find_ctx->fc_ids[find_ctx->fc_nids] = so_idx;
			find_ctx->fc_nids++;
		}

		/*
		 * Go through the token object. For each token object
		 * that can be accessed:
		 * If there was already an session object id assigned
		 * to that token object, skip it, since it was returned
		 * during the check of session objects, else,
		 * assign a new object id for that token object and
		 * add it to the array of matching objects.
		 */
		for (to_idx = 0; to_idx < DPROV_MAX_OBJECTS &&
		    error == CRYPTO_SUCCESS; to_idx++) {
			if ((object = softc->ds_objects[to_idx]) == NULL)
				continue;

			/* setting count to zero means find all objects */
			if (attr_count > 0) {
				if (!dprov_attributes_match(object, template,
				    attr_count))
					continue;
			}

			/* if the the object has been destroyed, skip it */
			if (object->do_destroyed)
				continue;

			/* skip object if it cannot be accessed from session */
			if (dprov_object_is_private(object) &&
			    session->ds_state != DPROV_SESSION_STATE_USER)
				continue;

			/*
			 * Is there already a session object id for this
			 * token object?
			 */
			for (so_idx = 0; so_idx < DPROV_MAX_OBJECTS; so_idx++)
				if (session->ds_objects[so_idx] != NULL &&
				    session->ds_objects[so_idx]->do_token_idx ==
				    to_idx)
					break;
			if (so_idx < DPROV_MAX_OBJECTS)
				/* object found in session table, skip it */
				continue;

			/* find free session slot for this object */
			for (so_idx = 0; so_idx < DPROV_MAX_OBJECTS; so_idx++)
				if (session->ds_objects[so_idx] == NULL)
					break;
			if (so_idx == DPROV_MAX_OBJECTS) {
				/* ran out of session objects slots */
				kmem_free(find_ctx, sizeof (dprov_find_ctx_t));
				error = CRYPTO_HOST_MEMORY;
				break;
			}

			/* add object to session objects table */
			session->ds_objects[so_idx] = object;
			DPROV_OBJECT_REFHOLD(object);

			/* add object to list of objects to return */
			find_ctx->fc_ids[find_ctx->fc_nids] = so_idx;
			find_ctx->fc_nids++;
		}

		break;
	}

	case DPROV_REQ_OBJECT_FIND: {
		crypto_object_id_t *object_ids =
			taskq_req->dr_object_req.or_object_id_ptr;
		uint_t max_object_count =
			taskq_req->dr_object_req.or_max_object_count;
		dprov_find_ctx_t *find_ctx =
			taskq_req->dr_object_req.or_find_p;
		uint_t ret_oid_idx;

		/* return the desired number of object ids */
		for (ret_oid_idx = 0; ret_oid_idx < max_object_count &&
		    find_ctx->fc_next < find_ctx->fc_nids; ret_oid_idx++)
			object_ids[ret_oid_idx] =
			    find_ctx->fc_ids[find_ctx->fc_next++];

		*taskq_req->dr_object_req.or_object_count_ptr = ret_oid_idx;

		error = CRYPTO_SUCCESS;
		break;
	}

	case DPROV_REQ_OBJECT_FIND_FINAL:
		kmem_free(taskq_req->dr_object_req.or_find_p,
		    sizeof (dprov_find_ctx_t));

		error = CRYPTO_SUCCESS;
		break;
	}

	mutex_exit(&softc->ds_lock);
	dprov_op_done(taskq_req, error);
	DPROV_DEBUG(D_OBJECT, ("(%d) dprov_object_task: end\n", instance));
}

/*
 * taskq dispatcher function for key management operations.
 */
static void
dprov_key_task(dprov_req_t *taskq_req)
{
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;
	int error = CRYPTO_NOT_SUPPORTED;
	kcf_provider_desc_t *pd;
	crypto_session_id_t session_id = taskq_req->dr_key_req.kr_session_id;
	dprov_session_t *session;

	DPROV_SOFTC_FROM_REQ(taskq_req, softc, instance);
	DPROV_DEBUG(D_KEY, ("(%d) dprov_key_task: started\n", instance));

	mutex_enter(&softc->ds_lock);

	/* validate session id and get ptr to session */
	if ((session = softc->ds_sessions[session_id]) == NULL) {
		mutex_exit(&softc->ds_lock);
		dprov_op_done(taskq_req, CRYPTO_SESSION_HANDLE_INVALID);
		return;
	}

	switch (taskq_req->dr_type) {
	case DPROV_REQ_KEY_GENERATE: {
		crypto_mechanism_t *mechp;
		crypto_object_id_t *object_id_ptr;
		crypto_object_attribute_t *template;
		crypto_object_attribute_t attribute;
		uint_t attribute_count;
		ulong_t key_type = ~0UL, class = ~0UL;
		ulong_t value_len;
		size_t key_len = 0;

		error = CRYPTO_SUCCESS;

		template = taskq_req->dr_key_req.kr_template;
		attribute_count = taskq_req->dr_key_req.kr_attribute_count;
		object_id_ptr = taskq_req->dr_key_req.kr_object_id_ptr;
		mechp = taskq_req->dr_key_req.kr_mechanism;

		/* optional */
		(void) dprov_get_template_attr_ulong(template, attribute_count,
		    DPROV_CKA_CLASS, &class);

		/* optional */
		(void) dprov_get_template_attr_ulong(template, attribute_count,
		    DPROV_CKA_KEY_TYPE, &key_type);

		if (class != ~0UL && class != DPROV_CKO_SECRET_KEY) {
			error = CRYPTO_TEMPLATE_INCONSISTENT;
			break;
		}

		switch (mechp->cm_type) {
		case DES_KEY_GEN_MECH_INFO_TYPE:
			if (key_type != ~0UL && key_type != DPROV_CKK_DES) {
				error = CRYPTO_TEMPLATE_INCONSISTENT;
				break;
			}
			key_len = DES_KEY_LEN;
			key_type = DPROV_CKK_DES;
			break;

		case DES3_KEY_GEN_MECH_INFO_TYPE:
			if (key_type != ~0UL && key_type != DPROV_CKK_DES3) {
				error = CRYPTO_TEMPLATE_INCONSISTENT;
				break;
			}
			key_len = DES3_KEY_LEN;
			key_type = DPROV_CKK_DES3;
			break;

		case AES_KEY_GEN_MECH_INFO_TYPE:
			if (key_type != ~0UL && key_type != DPROV_CKK_AES) {
				error = CRYPTO_TEMPLATE_INCONSISTENT;
				break;
			}
			if (dprov_get_template_attr_ulong(template,
			    attribute_count, DPROV_CKA_VALUE_LEN,
			    &value_len) != CRYPTO_SUCCESS) {
				error = CRYPTO_TEMPLATE_INCOMPLETE;
				break;
			}
			if (value_len >= AES_MAX_KEY_LEN) {
				error = CRYPTO_ATTRIBUTE_VALUE_INVALID;
				break;
			}
			key_len = value_len;
			key_type = DPROV_CKK_AES;
			break;

		case BLOWFISH_KEY_GEN_MECH_INFO_TYPE:
			if (key_type != ~0UL &&
			    key_type != DPROV_CKK_BLOWFISH) {
				error = CRYPTO_TEMPLATE_INCONSISTENT;
				break;
			}
			if (dprov_get_template_attr_ulong(template,
			    attribute_count, DPROV_CKA_VALUE_LEN,
			    &value_len) != CRYPTO_SUCCESS) {
				error = CRYPTO_TEMPLATE_INCOMPLETE;
				break;
			}
			if (value_len >= BLOWFISH_MAX_KEY_LEN) {
				error = CRYPTO_ATTRIBUTE_VALUE_INVALID;
				break;
			}
			key_len = value_len;
			key_type = DPROV_CKK_BLOWFISH;
			break;

		case RC4_KEY_GEN_MECH_INFO_TYPE:
			if (key_type != ~0UL && key_type != DPROV_CKK_RC4) {
				error = CRYPTO_TEMPLATE_INCONSISTENT;
				break;
			}
			if (dprov_get_template_attr_ulong(template,
			    attribute_count, DPROV_CKA_VALUE_LEN,
			    &value_len) != CRYPTO_SUCCESS) {
				error = CRYPTO_TEMPLATE_INCOMPLETE;
				break;
			}
			if (value_len >=
			    CRYPTO_BITS2BYTES(ARCFOUR_MAX_KEY_BITS)) {
				error = CRYPTO_ATTRIBUTE_VALUE_INVALID;
				break;
			}
			key_len = value_len;
			key_type = DPROV_CKK_RC4;
			break;

		default:
			error = CRYPTO_MECHANISM_INVALID;
		}

		if (error != CRYPTO_SUCCESS)
			break;

		error = dprov_create_object_from_template(softc, session,
		    template, attribute_count, object_id_ptr, B_FALSE, B_TRUE);

		if (error != CRYPTO_SUCCESS)
			break;

		/* make sure class is set */
		attribute.oa_type = DPROV_CKA_CLASS;
		attribute.oa_value = (char *)&class;
		attribute.oa_value_len = sizeof (ulong_t);
		error = dprov_object_set_attr(session, *object_id_ptr,
		    &attribute, 1, B_FALSE);

		if (error != CRYPTO_SUCCESS) {
			goto destroy_object;
		}

		/* make sure key_type is set */
		attribute.oa_type = DPROV_CKA_KEY_TYPE;
		attribute.oa_value = (char *)&key_type;
		attribute.oa_value_len = sizeof (ulong_t);
		error = dprov_object_set_attr(session, *object_id_ptr,
		    &attribute, 1, B_FALSE);

		if (error != CRYPTO_SUCCESS) {
			goto destroy_object;
		}

		attribute.oa_type = DPROV_CKA_VALUE;
		attribute.oa_value = kmem_alloc(key_len, KM_SLEEP);
		attribute.oa_value_len = key_len;

		if (random_get_pseudo_bytes((uchar_t *)attribute.oa_value,
		    key_len) != 0) {
			bzero(attribute.oa_value, key_len);
			kmem_free(attribute.oa_value, key_len);
			goto destroy_object;
		}
		error = dprov_object_set_attr(session, *object_id_ptr,
		    &attribute, 1, B_FALSE);

		bzero(attribute.oa_value, key_len);
		kmem_free(attribute.oa_value, key_len);

		if (error != CRYPTO_SUCCESS) {
			goto destroy_object;
		}
		break;

destroy_object:
		(void) dprov_destroy_object(softc, session, *object_id_ptr);
		break;
	}

	case DPROV_REQ_KEY_GENERATE_PAIR: {
		crypto_mechanism_t *mechp;
		crypto_object_id_t *pub_object_id_ptr;
		crypto_object_id_t *pri_object_id_ptr;
		crypto_object_attribute_t *pub_template;
		crypto_object_attribute_t *pri_template;
		crypto_object_attribute_t attribute;
		uint_t pub_attribute_count;
		uint_t pri_attribute_count;
		ulong_t pub_key_type = ~0UL, pub_class = ~0UL;
		ulong_t pri_key_type = ~0UL, pri_class = ~0UL;
		char public_exponent[3] = {
		    0x01, 0x00, 0x01
		};
		static uchar_t private_exponent[128] = {
		    0x4a, 0xef, 0xb9, 0x30, 0xa1, 0x44, 0x0a, 0xe0,
		    0x30, 0xdd, 0xd3, 0x57, 0xc2, 0xb6, 0x6a, 0xba,
		    0x5f, 0x81, 0xb0, 0x72, 0x55, 0x5b, 0x5b, 0xf3,
		    0x07, 0xd2, 0x5f, 0x15, 0x87, 0xc0, 0x78, 0x7d,
		    0x4f, 0x95, 0x09, 0x59, 0xd2, 0x9a, 0x95, 0xc3,
		    0xcc, 0xdc, 0xf1, 0xa4, 0x60, 0x76, 0xd7, 0xa7,
		    0xf8, 0x01, 0x13, 0xf8, 0x54, 0xd4, 0xf8, 0x2f,
		    0xcf, 0x0c, 0x8b, 0x6e, 0xee, 0x34, 0x53, 0x19,
		    0x14, 0xa6, 0x15, 0x14, 0xaf, 0xb2, 0x28, 0xa1,
		    0x78, 0x1b, 0x2d, 0x9b, 0x28, 0x52, 0x14, 0xfa,
		    0x72, 0x2b, 0x81, 0x11, 0xd0, 0x59, 0xde, 0xc6,
		    0x52, 0x90, 0xa4, 0xae, 0xc9, 0xf1, 0x97, 0xd4,
		    0x57, 0xbc, 0xe3, 0x90, 0x30, 0xc7, 0xff, 0xe8,
		    0xd7, 0x2d, 0xd8, 0xef, 0x14, 0x2d, 0x2d, 0x60,
		    0x54, 0x22, 0x9d, 0x32, 0x36, 0xcf, 0x4f, 0xf7,
		    0x37, 0xcc, 0x8e, 0x00, 0x85, 0xb1, 0x00, 0x01
		};

		static uchar_t modulus[128] = {
		    0x87, 0xb2, 0x2a, 0x54, 0x63, 0x83, 0x5e, 0x30,
		    0xdc, 0x73, 0x03, 0xc6, 0x35, 0xf8, 0xa0, 0x25,
		    0xa5, 0x6e, 0xcc, 0x14, 0xdd, 0x77, 0x2e, 0x80,
		    0xd1, 0xa3, 0x05, 0x09, 0x1a, 0x2f, 0x88, 0xec,
		    0xd1, 0x46, 0x92, 0x19, 0x8c, 0x7a, 0xf7, 0x63,
		    0x28, 0xbf, 0x7a, 0xea, 0x9f, 0xfe, 0x96, 0x0f,
		    0x80, 0xa1, 0x3e, 0xa6, 0xe1, 0x89, 0x7c, 0x4c,
		    0x8e, 0x92, 0xdc, 0x6e, 0x69, 0xba, 0x4b, 0x3f,
		    0x93, 0xc0, 0xaf, 0xbd, 0xbc, 0x81, 0xf2, 0x1e,
		    0xc0, 0x7d, 0xc3, 0x4c, 0x8f, 0x5f, 0xfe, 0xdc,
		    0xdb, 0xdb, 0x52, 0x03, 0x94, 0x78, 0x04, 0xa6,
		    0x78, 0x1f, 0xdd, 0x5e, 0xd8, 0x85, 0x3e, 0x19,
		    0x54, 0x7c, 0xd5, 0x81, 0xfb, 0x70, 0x69, 0x5d,
		    0xbf, 0x23, 0x7a, 0xb1, 0xf9, 0x85, 0x6e, 0xc2,
		    0x0a, 0x4d, 0x81, 0x21, 0xf8, 0xad, 0x71, 0x65,
		    0xaf, 0xb6, 0x8f, 0xa1, 0xac, 0x46, 0x8a, 0x4d
		};

		pub_template = taskq_req->dr_key_req.kr_template;
		pub_attribute_count = taskq_req->dr_key_req.kr_attribute_count;
		pub_object_id_ptr = taskq_req->dr_key_req.kr_object_id_ptr;
		pri_template = taskq_req->dr_key_req.kr_private_key_template;
		pri_attribute_count =
		    taskq_req->dr_key_req.kr_private_key_attribute_count;
		pri_object_id_ptr =
		    taskq_req->dr_key_req.kr_private_key_object_id_ptr;
		mechp = taskq_req->dr_key_req.kr_mechanism;

		error = CRYPTO_SUCCESS;

		/* optional */
		(void) dprov_get_template_attr_ulong(pub_template,
		    pub_attribute_count, DPROV_CKA_CLASS, &pub_class);

		/* optional */
		(void) dprov_get_template_attr_ulong(pri_template,
		    pri_attribute_count, DPROV_CKA_CLASS, &pri_class);

		/* optional */
		(void) dprov_get_template_attr_ulong(pub_template,
		    pub_attribute_count, DPROV_CKA_KEY_TYPE, &pub_key_type);

		/* optional */
		(void) dprov_get_template_attr_ulong(pri_template,
		    pri_attribute_count, DPROV_CKA_KEY_TYPE, &pri_key_type);

		if (pub_class != ~0UL && pub_class != DPROV_CKO_PUBLIC_KEY) {
			error = CRYPTO_TEMPLATE_INCONSISTENT;
			break;
		}

		if (pri_class != ~0UL && pri_class != DPROV_CKO_PRIVATE_KEY) {
			error = CRYPTO_TEMPLATE_INCONSISTENT;
			break;
		}

		switch (mechp->cm_type) {
		case RSA_PKCS_KEY_PAIR_GEN_MECH_INFO_TYPE:
			if (pub_key_type != ~0UL &&
			    pub_key_type != DPROV_CKK_RSA) {
				error = CRYPTO_TEMPLATE_INCONSISTENT;
				break;
			}
			pri_key_type = DPROV_CKK_RSA;

			if (pri_key_type != ~0UL &&
			    pri_key_type != DPROV_CKK_RSA) {
				error = CRYPTO_TEMPLATE_INCONSISTENT;
				break;
			}
			pri_key_type = DPROV_CKK_RSA;

			if (pub_class != ~0UL &&
			    pub_class != DPROV_CKO_PUBLIC_KEY) {
				error = CRYPTO_TEMPLATE_INCONSISTENT;
				break;
			}
			pub_class = DPROV_CKO_PUBLIC_KEY;

			if (pri_class != ~0UL &&
			    pri_class != DPROV_CKO_PRIVATE_KEY) {
				error = CRYPTO_TEMPLATE_INCONSISTENT;
				break;
			}
			pri_class = DPROV_CKO_PRIVATE_KEY;
			break;

		default:
			error = CRYPTO_MECHANISM_INVALID;
		}

		if (error != CRYPTO_SUCCESS)
			break;

		error = dprov_create_object_from_template(softc, session,
		    pub_template, pub_attribute_count, pub_object_id_ptr,
		    B_FALSE, B_TRUE);

		if (error != CRYPTO_SUCCESS)
			break;

		/* make sure class is set */
		attribute.oa_type = DPROV_CKA_CLASS;
		attribute.oa_value = (char *)&pub_class;
		attribute.oa_value_len = sizeof (ulong_t);
		error = dprov_object_set_attr(session, *pub_object_id_ptr,
		    &attribute, 1, B_FALSE);

		if (error != CRYPTO_SUCCESS) {
			goto destroy_public_object;
		}

		/* make sure key_type is set */
		attribute.oa_type = DPROV_CKA_KEY_TYPE;
		attribute.oa_value = (char *)&pub_key_type;
		attribute.oa_value_len = sizeof (ulong_t);
		error = dprov_object_set_attr(session, *pub_object_id_ptr,
		    &attribute, 1, B_FALSE);

		if (error != CRYPTO_SUCCESS) {
			goto destroy_public_object;
		}

		attribute.oa_type = DPROV_CKA_MODULUS;
		attribute.oa_value = (char *)modulus;
		attribute.oa_value_len = sizeof (modulus);
		error = dprov_object_set_attr(session, *pub_object_id_ptr,
		    &attribute, 1, B_FALSE);

		if (error != CRYPTO_SUCCESS) {
			goto destroy_public_object;
		}

		attribute.oa_type = DPROV_CKA_PUBLIC_EXPONENT;
		attribute.oa_value = public_exponent;
		attribute.oa_value_len = sizeof (public_exponent);
		error = dprov_object_set_attr(session, *pub_object_id_ptr,
		    &attribute, 1, B_FALSE);

		if (error != CRYPTO_SUCCESS) {
			goto destroy_public_object;
		}

		error = dprov_create_object_from_template(softc, session,
		    pri_template, pri_attribute_count, pri_object_id_ptr,
		    B_FALSE, B_TRUE);

		if (error != CRYPTO_SUCCESS)
			break;

		/* make sure class is set */
		attribute.oa_type = DPROV_CKA_CLASS;
		attribute.oa_value = (char *)&pri_class;
		attribute.oa_value_len = sizeof (ulong_t);
		error = dprov_object_set_attr(session, *pri_object_id_ptr,
		    &attribute, 1, B_FALSE);

		if (error != CRYPTO_SUCCESS) {
			goto destroy_private_object;
		}

		/* make sure key_type is set */
		attribute.oa_type = DPROV_CKA_KEY_TYPE;
		attribute.oa_value = (char *)&pri_key_type;
		attribute.oa_value_len = sizeof (ulong_t);
		error = dprov_object_set_attr(session, *pri_object_id_ptr,
		    &attribute, 1, B_FALSE);

		if (error != CRYPTO_SUCCESS) {
			goto destroy_private_object;
		}

		attribute.oa_type = DPROV_CKA_MODULUS;
		attribute.oa_value = (char *)modulus;
		attribute.oa_value_len = sizeof (modulus);
		error = dprov_object_set_attr(session, *pri_object_id_ptr,
		    &attribute, 1, B_FALSE);

		if (error != CRYPTO_SUCCESS) {
			goto destroy_private_object;
		}

		attribute.oa_type = DPROV_CKA_PRIVATE_EXPONENT;
		attribute.oa_value = (char *)private_exponent;
		attribute.oa_value_len = sizeof (private_exponent);
		error = dprov_object_set_attr(session, *pri_object_id_ptr,
		    &attribute, 1, B_FALSE);

		if (error != CRYPTO_SUCCESS) {
			goto destroy_private_object;
		}
		break;

destroy_private_object:
		(void) dprov_destroy_object(softc, session,
		    *pri_object_id_ptr);
destroy_public_object:
		(void) dprov_destroy_object(softc, session,
		    *pub_object_id_ptr);

		break;
	}

	case DPROV_REQ_KEY_WRAP: {
		crypto_mechanism_t mech, *mechp;
		crypto_key_t key, *keyp;
		crypto_object_id_t object_id;
		ulong_t class = DPROV_CKO_DATA;
		boolean_t extractable = B_TRUE;
		dprov_object_t *object;
		int object_idx;
		char *plaintext_key;
		size_t plaintext_key_len;
		crypto_data_t plaintext;
		crypto_data_t ciphertext;
		size_t *lenp;

		mechp = taskq_req->dr_key_req.kr_mechanism;
		/* structure assignment */
		mech = *mechp;

		/* get wrapping key value */
		if (is_publickey_mech(mech.cm_type)) {
			if ((error = dprov_key_attr_asymmetric(softc,
			    session_id, taskq_req->dr_type,
			    taskq_req->dr_key_req.kr_key,
			    &key)) != CRYPTO_SUCCESS)
				break;
			keyp = &key;
		} else {
			if ((error = dprov_key_value_secret(softc,
			    session_id, taskq_req->dr_type,
			    taskq_req->dr_key_req.kr_key,
			    &key)) != CRYPTO_SUCCESS)
				break;
			keyp = &key;
		}

		/* get the software provider for this mechanism */
		if ((error = dprov_get_sw_prov(mechp, &pd,
		    &mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		object_id = *taskq_req->dr_key_req.kr_object_id_ptr;
		if (object_id >= DPROV_MAX_OBJECTS) {
			error = CRYPTO_KEY_HANDLE_INVALID;
			break;
		}

		/* get ptr to object */
		if ((object = session->ds_objects[object_id]) == NULL) {
			error = CRYPTO_OBJECT_HANDLE_INVALID;
			break;
		}

		(void) dprov_get_object_attr_boolean(object,
		    DPROV_CKA_EXTRACTABLE, &extractable);

		if (!extractable) {
			error = CRYPTO_ATTRIBUTE_SENSITIVE;
			break;
		}

		(void) dprov_get_object_attr_ulong(object,
		    DPROV_CKA_CLASS, &class);

		switch (class) {
		case DPROV_CKO_SECRET_KEY:
			object_idx = dprov_find_attr(object->do_attr,
			    DPROV_MAX_ATTR, DPROV_CKA_VALUE);
			if (object_idx == -1) {
				error = CRYPTO_ATTRIBUTE_TYPE_INVALID;
				break;
			}
			break;

		case DPROV_CKO_PRIVATE_KEY:
			/*
			 * PKCS#11 says that ASN.1 should be used to encode
			 * specific attributes before encrypting the blob.
			 * We only encrypt the private exponent for the
			 * purpose of testing.
			 */
			object_idx = dprov_find_attr(object->do_attr,
			    DPROV_MAX_ATTR, DPROV_CKA_PRIVATE_EXPONENT);
			if (object_idx == -1) {
				error = CRYPTO_ATTRIBUTE_TYPE_INVALID;
				break;
			}
			break;
		default:
			error = CRYPTO_KEY_NOT_WRAPPABLE;
			break;
		}
		if (error != CRYPTO_SUCCESS)
			break;

		plaintext_key = object->do_attr[object_idx].oa_value;
		plaintext_key_len = object->do_attr[object_idx].oa_value_len;
		lenp = taskq_req->dr_key_req.kr_wrapped_key_len_ptr;

		/* session id is 0 for software provider */
		plaintext.cd_format = CRYPTO_DATA_RAW;
		plaintext.cd_offset = 0;
		plaintext.cd_length = plaintext_key_len;
		plaintext.cd_raw.iov_base = plaintext_key;
		plaintext.cd_raw.iov_len = plaintext_key_len;
		plaintext.cd_miscdata = NULL;

		ciphertext.cd_format = CRYPTO_DATA_RAW;
		ciphertext.cd_offset = 0;
		ciphertext.cd_length = *lenp;
		ciphertext.cd_raw.iov_base =
		    (char *)taskq_req->dr_key_req.kr_wrapped_key;
		ciphertext.cd_raw.iov_len = ciphertext.cd_length;
		ciphertext.cd_miscdata = NULL;

		error = crypto_encrypt_prov(pd, 0, &mech, &plaintext, keyp,
		    NULL, &ciphertext, NULL);

		KCF_PROV_REFRELE(pd);
		if (error == CRYPTO_SUCCESS ||
		    error == CRYPTO_BUFFER_TOO_SMALL) {
			*lenp = ciphertext.cd_length;
		}
		break;
	}

	case DPROV_REQ_KEY_UNWRAP: {
		crypto_mechanism_t mech, *mechp;
		crypto_key_t key, *keyp;
		crypto_object_id_t *object_id_ptr;
		ulong_t class = DPROV_CKO_DATA;
		uchar_t *wrapped_key;
		char *plaintext_buf;
		size_t wrapped_key_len;
		crypto_data_t plaintext;
		crypto_data_t ciphertext;
		crypto_object_attribute_t unwrapped_key;
		crypto_object_attribute_t *template;
		uint_t attribute_count;

		template = taskq_req->dr_key_req.kr_template;
		attribute_count = taskq_req->dr_key_req.kr_attribute_count;
		object_id_ptr = taskq_req->dr_key_req.kr_object_id_ptr;

		/* all objects must have an object class attribute */
		if (dprov_get_template_attr_ulong(template, attribute_count,
		    DPROV_CKA_CLASS, &class) != CRYPTO_SUCCESS) {
			error = CRYPTO_TEMPLATE_INCOMPLETE;
			break;
		}

		mechp = taskq_req->dr_key_req.kr_mechanism;
		/* structure assignment */
		mech = *mechp;

		/* get unwrapping key value */
		if (is_publickey_mech(mech.cm_type)) {
			if ((error = dprov_key_attr_asymmetric(softc,
			    session_id, taskq_req->dr_type,
			    taskq_req->dr_key_req.kr_key,
			    &key)) != CRYPTO_SUCCESS)
				break;
			keyp = &key;
		} else {
			if ((error = dprov_key_value_secret(softc,
			    session_id, taskq_req->dr_type,
			    taskq_req->dr_key_req.kr_key,
			    &key)) != CRYPTO_SUCCESS)
				break;
			keyp = &key;
		}

		/* get the software provider for this mechanism */
		if ((error = dprov_get_sw_prov(mechp, &pd,
		    &mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		wrapped_key = taskq_req->dr_key_req.kr_wrapped_key;
		wrapped_key_len = *taskq_req->dr_key_req.kr_wrapped_key_len_ptr;
		ciphertext.cd_format = CRYPTO_DATA_RAW;
		ciphertext.cd_offset = 0;
		ciphertext.cd_length = wrapped_key_len;
		ciphertext.cd_raw.iov_base = (char *)wrapped_key;
		ciphertext.cd_raw.iov_len = wrapped_key_len;
		ciphertext.cd_miscdata = NULL;

		/*
		 * Plaintext length is less than or equal to
		 * the length of the ciphertext.
		 */
		plaintext_buf = kmem_alloc(wrapped_key_len, KM_SLEEP);
		plaintext.cd_format = CRYPTO_DATA_RAW;
		plaintext.cd_offset = 0;
		plaintext.cd_length = wrapped_key_len;
		plaintext.cd_raw.iov_base = plaintext_buf;
		plaintext.cd_raw.iov_len = wrapped_key_len;
		plaintext.cd_miscdata = NULL;

		error = crypto_decrypt_prov(pd, 0, &mech, &ciphertext, keyp,
		    NULL, &plaintext, NULL);

		KCF_PROV_REFRELE(pd);

		if (error != CRYPTO_SUCCESS)
			goto free_unwrapped_key;

		error = dprov_create_object_from_template(softc, session,
		    template, attribute_count, object_id_ptr, B_FALSE, B_FALSE);

		if (error != CRYPTO_SUCCESS)
			goto free_unwrapped_key;

		switch (class) {
		case DPROV_CKO_SECRET_KEY:
			unwrapped_key.oa_type = DPROV_CKA_VALUE;
			unwrapped_key.oa_value_len = plaintext.cd_length;
			unwrapped_key.oa_value = plaintext_buf;
			break;
		case DPROV_CKO_PRIVATE_KEY:
			/*
			 * PKCS#11 says that ASN.1 should be used to encode
			 * specific attributes before encrypting the blob.
			 * We only encrypt the private exponent for the
			 * purpose of testing.
			 */
			unwrapped_key.oa_type = DPROV_CKA_PRIVATE_EXPONENT;
			unwrapped_key.oa_value_len = plaintext.cd_length;
			unwrapped_key.oa_value = plaintext_buf;
			break;
		default:
			error = CRYPTO_TEMPLATE_INCONSISTENT;
			goto free_unwrapped_key;
		}

		if ((error = dprov_object_set_attr(session, *object_id_ptr,
		    &unwrapped_key, 1, B_FALSE)) == CRYPTO_SUCCESS)
			break;	/* don't free the unwrapped key */

		/* failure */
		(void) dprov_destroy_object(softc, session, *object_id_ptr);
		break;

free_unwrapped_key:
		bzero(plaintext_buf, wrapped_key_len);
		kmem_free(plaintext_buf, wrapped_key_len);
		break;
	}

	case DPROV_REQ_KEY_DERIVE: {
		crypto_mechanism_t digest_mech, *mechp;
		crypto_key_t key, *base_keyp;
		crypto_object_id_t *object_id_ptr;
		crypto_data_t data;
		crypto_data_t digest;
		size_t hash_size;
		char *digest_buf;
		crypto_object_attribute_t derived_key;
		crypto_object_attribute_t *template;
		uint_t attribute_count;
		ulong_t key_type;
		void *value;
		size_t value_len = 0;

		error = CRYPTO_SUCCESS;

		template = taskq_req->dr_key_req.kr_template;
		attribute_count = taskq_req->dr_key_req.kr_attribute_count;
		object_id_ptr = taskq_req->dr_key_req.kr_object_id_ptr;

		/* required */
		if (dprov_get_template_attr_ulong(template, attribute_count,
		    DPROV_CKA_KEY_TYPE, &key_type) != CRYPTO_SUCCESS) {
			error = CRYPTO_TEMPLATE_INCOMPLETE;
			break;
		}

		mechp = taskq_req->dr_key_req.kr_mechanism;
		/* structure assignment */
		digest_mech = *mechp;

		switch (digest_mech.cm_type) {
		case SHA1_KEY_DERIVATION_MECH_INFO_TYPE:
			hash_size = SHA1_DIGEST_LEN;
			digest_mech.cm_type = SHA1_MECH_INFO_TYPE;
			break;

		case SHA256_KEY_DERIVATION_MECH_INFO_TYPE:
			hash_size = SHA256_DIGEST_LENGTH;
			digest_mech.cm_type = SHA256_MECH_INFO_TYPE;
			break;

		case SHA384_KEY_DERIVATION_MECH_INFO_TYPE:
			hash_size = SHA384_DIGEST_LENGTH;
			digest_mech.cm_type = SHA384_MECH_INFO_TYPE;
			break;

		case SHA512_KEY_DERIVATION_MECH_INFO_TYPE:
			hash_size = SHA512_DIGEST_LENGTH;
			digest_mech.cm_type = SHA512_MECH_INFO_TYPE;
			break;

		case MD5_KEY_DERIVATION_MECH_INFO_TYPE:
			hash_size = MD5_DIGEST_LEN;
			digest_mech.cm_type = MD5_MECH_INFO_TYPE;
			break;

		default:
			error = CRYPTO_MECHANISM_INVALID;
		}

		if (error != CRYPTO_SUCCESS)
			break;

		/* CKA_VALUE is optional */
		(void) dprov_get_template_attr_array(template, attribute_count,
		    DPROV_CKA_VALUE, &value, &value_len);

		/* check for inconsistent value length */
		switch (key_type) {
		case DPROV_CKK_GENERIC_SECRET:
			if (value_len > 0) {
				if (value_len > hash_size)
					error = CRYPTO_ATTRIBUTE_VALUE_INVALID;
			} else {
				value_len = hash_size;
			}
			break;

		case DPROV_CKK_RC4:
		case DPROV_CKK_AES:
			if (value_len == 0 ||
			    value_len > hash_size) {
				error = CRYPTO_ATTRIBUTE_VALUE_INVALID;
			}
			break;

		case DPROV_CKK_DES:
			if (value_len > 0 &&
			    value_len != DES_KEY_LEN) {
				error = CRYPTO_ATTRIBUTE_VALUE_INVALID;
			}
			value_len = DES_KEY_LEN;
			break;

		case DPROV_CKK_DES3:
			if (value_len > 0 &&
			    value_len != DES3_KEY_LEN) {
				error = CRYPTO_ATTRIBUTE_VALUE_INVALID;
			}
			value_len = DES3_KEY_LEN;
			break;

		default:
			error = CRYPTO_ATTRIBUTE_VALUE_INVALID;
			break;
		}

		if (error != CRYPTO_SUCCESS)
			break;

		/* get the software provider for this mechanism */
		if ((error = dprov_get_sw_prov(&digest_mech, &pd,
		    &digest_mech.cm_type)) != CRYPTO_SUCCESS)
			break;

		/* get the base key */
		error = dprov_key_value_secret(softc, session_id,
		    taskq_req->dr_type, taskq_req->dr_key_req.kr_key, &key);
		if (error != CRYPTO_SUCCESS)
			break;

		base_keyp = &key;

		data.cd_format = CRYPTO_DATA_RAW;
		data.cd_offset = 0;
		data.cd_length = CRYPTO_BITS2BYTES(base_keyp->ck_length);
		data.cd_raw.iov_base = base_keyp->ck_data;
		data.cd_raw.iov_len = data.cd_length;

		digest_buf = kmem_alloc(hash_size, KM_SLEEP);
		digest.cd_format = CRYPTO_DATA_RAW;
		digest.cd_offset = 0;
		digest.cd_length = hash_size;
		digest.cd_raw.iov_base = digest_buf;
		digest.cd_raw.iov_len = hash_size;

		error = crypto_digest_prov(pd, 0, &digest_mech, &data,
		    &digest, NULL);

		KCF_PROV_REFRELE(pd);

		if (error != CRYPTO_SUCCESS)
			goto free_derived_key;

		error = dprov_create_object_from_template(softc, session,
		    template, attribute_count, object_id_ptr, B_FALSE, B_FALSE);

		if (error != CRYPTO_SUCCESS)
			goto free_derived_key;

		derived_key.oa_type = DPROV_CKA_VALUE;
		derived_key.oa_value = digest_buf;
		derived_key.oa_value_len = value_len;

		error = dprov_object_set_attr(session, *object_id_ptr,
		    &derived_key, 1, B_FALSE);

		if (error != CRYPTO_SUCCESS) {
			(void) dprov_destroy_object(softc, session,
			    *object_id_ptr);
		}

free_derived_key:
		bzero(digest_buf, hash_size);
		kmem_free(digest_buf, hash_size);
	}
	}

	mutex_exit(&softc->ds_lock);
	dprov_op_done(taskq_req, error);
	DPROV_DEBUG(D_KEY, ("(%d) dprov_key_task: end\n", instance));
}


/*
 * taskq dispatcher function for provider management operations.
 */
static void
dprov_mgmt_task(dprov_req_t *taskq_req)
{
	dprov_state_t *softc;
	/* LINTED E_FUNC_SET_NOT_USED */
	int instance;
	int error = CRYPTO_NOT_SUPPORTED;

	DPROV_SOFTC_FROM_REQ(taskq_req, softc, instance);
	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_mgmt_task: started\n", instance));

	mutex_enter(&softc->ds_lock);

	switch (taskq_req->dr_type) {
	case DPROV_REQ_MGMT_EXTINFO: {
		crypto_provider_ext_info_t *ext_info =
		    taskq_req->dr_mgmt_req.mr_ext_info;

		(void) memset(ext_info->ei_label, ' ', CRYPTO_EXT_SIZE_LABEL);
		if (!softc->ds_token_initialized) {
			bcopy("(not initialized)", ext_info->ei_label,
			    strlen("(not initialized)"));
		} else {
			bcopy(softc->ds_label, ext_info->ei_label,
			    CRYPTO_EXT_SIZE_LABEL);
		}

		bcopy(DPROV_MANUFACTURER, ext_info->ei_manufacturerID,
		    CRYPTO_EXT_SIZE_MANUF);
		bcopy(DPROV_MODEL, ext_info->ei_model, CRYPTO_EXT_SIZE_MODEL);

		(void) snprintf((char *)ext_info->ei_serial_number, 16, "%d%s",
		    instance, DPROV_ALLSPACES);
		/* PKCS#11 blank padding */
		ext_info->ei_serial_number[15] = ' ';
		ext_info->ei_max_session_count = CRYPTO_EFFECTIVELY_INFINITE;
		ext_info->ei_max_pin_len = (ulong_t)DPROV_MAX_PIN_LEN;
		ext_info->ei_min_pin_len = 1;
		ext_info->ei_total_public_memory = CRYPTO_EFFECTIVELY_INFINITE;
		ext_info->ei_free_public_memory = CRYPTO_EFFECTIVELY_INFINITE;
		ext_info->ei_total_private_memory = CRYPTO_EFFECTIVELY_INFINITE;
		ext_info->ei_free_private_memory = CRYPTO_EFFECTIVELY_INFINITE;
		ext_info->ei_hardware_version.cv_major = 1;
		ext_info->ei_hardware_version.cv_minor = 0;
		ext_info->ei_firmware_version.cv_major = 1;
		ext_info->ei_firmware_version.cv_minor = 0;

		ext_info->ei_flags = CRYPTO_EXTF_RNG |
		    CRYPTO_EXTF_LOGIN_REQUIRED |
		    CRYPTO_EXTF_DUAL_CRYPTO_OPERATIONS;
		if (softc->ds_user_pin_set)
			ext_info->ei_flags |= CRYPTO_EXTF_USER_PIN_INITIALIZED;
		if (softc->ds_token_initialized)
			ext_info->ei_flags |= CRYPTO_EXTF_TOKEN_INITIALIZED;

		error = CRYPTO_SUCCESS;
		break;
	}
	case DPROV_REQ_MGMT_INITTOKEN: {
		char *pin = taskq_req->dr_mgmt_req.mr_pin;
		size_t pin_len = taskq_req->dr_mgmt_req.mr_pin_len;
		char *label = taskq_req->dr_mgmt_req.mr_label;

		/* cannot initialize token when a session is open */
		if (softc->ds_sessions_count > 0) {
			error = CRYPTO_SESSION_EXISTS;
			break;
		}

		/* check PIN length */
		if (pin_len > DPROV_MAX_PIN_LEN) {
			error = CRYPTO_PIN_LEN_RANGE;
			break;
		}

		/* check PIN */
		if (pin == NULL) {
			error = CRYPTO_PIN_INVALID;
			break;
		}

		/*
		 * If the token has already been initialized, need
		 * to validate supplied PIN.
		 */
		if (softc->ds_token_initialized &&
		    (softc->ds_so_pin_len != pin_len ||
		    strncmp(softc->ds_so_pin, pin, pin_len) != 0)) {
			/* invalid SO PIN */
			error = CRYPTO_PIN_INCORRECT;
			break;
		}

		/* set label */
		bcopy(label, softc->ds_label, CRYPTO_EXT_SIZE_LABEL);

		/* set new SO PIN, update state */
		bcopy(pin, softc->ds_so_pin, pin_len);
		softc->ds_so_pin_len = pin_len;
		softc->ds_token_initialized = B_TRUE;
		softc->ds_user_pin_set = B_FALSE;

		error = CRYPTO_SUCCESS;
		break;
	}
	case DPROV_REQ_MGMT_INITPIN: {
		char *pin = taskq_req->dr_mgmt_req.mr_pin;
		size_t pin_len = taskq_req->dr_mgmt_req.mr_pin_len;
		crypto_session_id_t session_id =
		    taskq_req->dr_mgmt_req.mr_session_id;

		/* check session id */
		if (softc->ds_sessions[session_id] == NULL) {
			error = CRYPTO_SESSION_HANDLE_INVALID;
			break;
		}

		/* fail if not logged in as SO */
		if (softc->ds_sessions[session_id]->ds_state !=
		    DPROV_SESSION_STATE_SO) {
			error = CRYPTO_USER_NOT_LOGGED_IN;
			break;
		}

		/* check PIN length */
		if (pin_len > DPROV_MAX_PIN_LEN) {
			error = CRYPTO_PIN_LEN_RANGE;
			break;
		}

		/* check PIN */
		if (pin == NULL) {
			error = CRYPTO_PIN_INVALID;
			break;
		}

		/* set new normal user PIN */
		bcopy(pin, softc->ds_user_pin, pin_len);
		softc->ds_user_pin_len = pin_len;
		softc->ds_user_pin_set = B_TRUE;

		error = CRYPTO_SUCCESS;
		break;
	}
	case DPROV_REQ_MGMT_SETPIN: {
		char *new_pin = taskq_req->dr_mgmt_req.mr_pin;
		size_t new_pin_len = taskq_req->dr_mgmt_req.mr_pin_len;
		char *old_pin = taskq_req->dr_mgmt_req.mr_old_pin;
		size_t old_pin_len = taskq_req->dr_mgmt_req.mr_old_pin_len;
		crypto_session_id_t session_id =
		    taskq_req->dr_mgmt_req.mr_session_id;

		/* check session id */
		if (softc->ds_sessions[session_id] == NULL) {
			error = CRYPTO_SESSION_HANDLE_INVALID;
			break;
		}

		/* check PIN length */
		if (old_pin_len > DPROV_MAX_PIN_LEN ||
		    new_pin_len > DPROV_MAX_PIN_LEN) {
			error = CRYPTO_PIN_LEN_RANGE;
			break;
		}

		/* check PIN */
		if (old_pin == NULL || new_pin == NULL) {
			error = CRYPTO_PIN_INVALID;
			break;
		}

		/* check user PIN state */
		if (!softc->ds_user_pin_set) {
			error = CRYPTO_USER_PIN_NOT_INITIALIZED;
			break;
		}

		/*
		 * If the token has already been initialized, need
		 * to validate supplied PIN.
		 */
		if (softc->ds_user_pin_len != old_pin_len ||
		    strncmp(softc->ds_user_pin, old_pin, old_pin_len) != 0) {
			/* invalid SO PIN */
			error = CRYPTO_PIN_INCORRECT;
			break;
		}

		/* set new PIN */
		bcopy(new_pin, softc->ds_user_pin, new_pin_len);
		softc->ds_user_pin_len = new_pin_len;

		error = CRYPTO_SUCCESS;
		break;
	}
	}

	mutex_exit(&softc->ds_lock);
	dprov_op_done(taskq_req, error);
	DPROV_DEBUG(D_DIGEST, ("(%d) dprov_mgmt_task: end\n", instance));
}

/*
 * Returns in the location pointed to by pd a pointer to the descriptor
 * for the software provider for the specified mechanism.
 * The provider descriptor is returned held. Returns one of the CRYPTO_
 * error codes on failure, CRYPTO_SUCCESS on success.
 */
static int
dprov_get_sw_prov(crypto_mechanism_t *mech, kcf_provider_desc_t **pd,
    crypto_mech_type_t *provider_mech_type)
{
	crypto_mech_type_t kcf_mech_type = CRYPTO_MECH_INVALID;
	int i, rv;

	/* lookup the KCF mech type associated with our mech type */
	for (i = 0; i < sizeof (dprov_mech_info_tab)/
	    sizeof (crypto_mech_info_t); i++) {
		if (mech->cm_type == dprov_mech_info_tab[i].cm_mech_number) {
			kcf_mech_type = crypto_mech2id_common(
				dprov_mech_info_tab[i].cm_mech_name, B_TRUE);
		}
	}

	rv = kcf_get_sw_prov(kcf_mech_type, pd, NULL, B_TRUE);
	if (rv == CRYPTO_SUCCESS)
		*provider_mech_type = kcf_mech_type;

	return (rv);
}

/*
 * Object management helper functions.
 */

/*
 * Given a crypto_key_t, return whether the key can be used or not
 * for the specified request. The attributes used here are defined
 * in table 42 of the PKCS#11 spec (Common secret key attributes).
 */
static int
dprov_key_can_use(dprov_object_t *object, dprov_req_type_t req_type)
{
	boolean_t ret = 0;
	int rv = CRYPTO_SUCCESS;

	/* check if object is allowed for specified operation */
	switch (req_type) {
	case DPROV_REQ_ENCRYPT_INIT:
	case DPROV_REQ_ENCRYPT_ATOMIC:
		rv = dprov_get_object_attr_boolean(object,
		    DPROV_CKA_ENCRYPT, &ret);
		break;
	case DPROV_REQ_DECRYPT_INIT:
	case DPROV_REQ_DECRYPT_ATOMIC:
		rv = dprov_get_object_attr_boolean(object,
		    DPROV_CKA_DECRYPT, &ret);
		break;
	case DPROV_REQ_SIGN_INIT:
	case DPROV_REQ_SIGN_ATOMIC:
	case DPROV_REQ_MAC_INIT:
	case DPROV_REQ_MAC_ATOMIC:
	case DPROV_REQ_MAC_VERIFY_ATOMIC:
		rv = dprov_get_object_attr_boolean(object,
		    DPROV_CKA_SIGN, &ret);
		break;
	case DPROV_REQ_SIGN_RECOVER_INIT:
	case DPROV_REQ_SIGN_RECOVER_ATOMIC:
		rv = dprov_get_object_attr_boolean(object,
		    DPROV_CKA_SIGN_RECOVER, &ret);
		break;
	case DPROV_REQ_VERIFY_INIT:
	case DPROV_REQ_VERIFY_ATOMIC:
		rv = dprov_get_object_attr_boolean(object,
		    DPROV_CKA_VERIFY, &ret);
		break;
	case DPROV_REQ_VERIFY_RECOVER_INIT:
	case DPROV_REQ_VERIFY_RECOVER_ATOMIC:
		rv = dprov_get_object_attr_boolean(object,
		    DPROV_CKA_VERIFY_RECOVER, &ret);
		break;
	case DPROV_REQ_KEY_WRAP:
		rv = dprov_get_object_attr_boolean(object,
		    DPROV_CKA_WRAP, &ret);
		break;
	case DPROV_REQ_KEY_UNWRAP:
		rv = dprov_get_object_attr_boolean(object,
		    DPROV_CKA_UNWRAP, &ret);
		break;
	case DPROV_REQ_DIGEST_KEY:
		/*
		 * There is no attribute to check for; therefore,
		 * any secret key can be used.
		 */
		ret = B_TRUE;
		rv = CRYPTO_SUCCESS;
		break;
	case DPROV_REQ_KEY_DERIVE:
		rv = dprov_get_object_attr_boolean(object,
		    DPROV_CKA_DERIVE, &ret);
		break;
	}

	if (rv != CRYPTO_SUCCESS || !ret)
		return (CRYPTO_KEY_FUNCTION_NOT_PERMITTED);

	return (CRYPTO_SUCCESS);
}

/*
 * Given a crypto_key_t corresponding to a secret key (i.e. for
 * use with symmetric crypto algorithms) specified in raw format, by
 * attribute, or by reference, initialize the ck_data and ck_length
 * fields of the ret_key argument so that they specify the key value
 * and length.
 *
 * For a key by value, this function uess the ck_data and ck_length,
 * for a key by reference, it looks up the corresponding object and
 * returns the appropriate attribute. For a key by attribute, it returns
 * the appropriate attribute. The attributes used are CKA_VALUE to retrieve
 * the value of the key, and CKA_VALUE_LEN to retrieve its length in bytes.
 */
static int
dprov_key_value_secret(dprov_state_t *softc, crypto_session_id_t session_id,
    dprov_req_type_t req_type, crypto_key_t *key, crypto_key_t *ret_key)
{
	ulong_t key_type;
	int ret = CRYPTO_SUCCESS;

	ret_key->ck_format = CRYPTO_KEY_RAW;

	switch (key->ck_format) {

	case CRYPTO_KEY_RAW:
		ret_key->ck_data = key->ck_data;
		ret_key->ck_length = key->ck_length;
		break;

	case CRYPTO_KEY_ATTR_LIST: {
		void *value;
		size_t len, value_len;

		if ((ret = dprov_get_key_attr_ulong(key, DPROV_CKA_KEY_TYPE,
		    &key_type)) != CRYPTO_SUCCESS)
			break;

		if ((ret = dprov_get_key_attr_array(key, DPROV_CKA_VALUE,
		    &value, &len)) != CRYPTO_SUCCESS)
			break;

		/*
		 * The length of the array is expressed in bytes.
		 * Convert to bits now since that's how keys are measured.
		 */
		len  = len << 3;

		/* optional */
		if ((dprov_get_key_attr_ulong(key, DPROV_CKA_VALUE_LEN,
		    &value_len)) == CRYPTO_SUCCESS) {
			len = value_len;
		}

		ret_key->ck_data = value;
		ret_key->ck_length = (uint_t)len;

		break;
	}

	case CRYPTO_KEY_REFERENCE: {
		dprov_object_t *object;
		void *value;
		size_t len, value_len;

		/* check session id */
		if (softc->ds_sessions[session_id] == NULL) {
			ret = CRYPTO_SESSION_HANDLE_INVALID;
			break;
		}

		if (key->ck_obj_id >= DPROV_MAX_OBJECTS) {
			ret = CRYPTO_KEY_HANDLE_INVALID;
			goto bail;
		}

		/* check if object id specified by key is valid */
		object = softc->ds_sessions[session_id]->
		    ds_objects[key->ck_obj_id];
		if (object == NULL) {
			ret = CRYPTO_KEY_HANDLE_INVALID;
			goto bail;
		}

		/* check if object can be used for operation */
		if ((ret = dprov_key_can_use(object, req_type)) !=
		    CRYPTO_SUCCESS)
			goto bail;

		if ((ret = dprov_get_object_attr_ulong(object,
		    DPROV_CKA_KEY_TYPE, &key_type)) != CRYPTO_SUCCESS)
			goto bail;

		if ((ret = dprov_get_object_attr_array(object,
		    DPROV_CKA_VALUE, &value, &len)) != CRYPTO_SUCCESS)
			goto bail;

		/* optional */
		if ((dprov_get_object_attr_ulong(object, DPROV_CKA_VALUE_LEN,
		    &value_len)) == CRYPTO_SUCCESS) {
			len = value_len;
		}

		/*
		 * The length of attributes are in bytes.
		 * Convert to bits now since that's how keys are measured.
		 */
		len  = len << 3;

		ret_key->ck_data = value;
		ret_key->ck_length = (uint_t)len;
bail:
		break;
	}

	default:
		ret = CRYPTO_ARGUMENTS_BAD;
		break;
	}

	return (ret);
}

/*
 * Get the attribute list for the specified asymmetric key.
 */
static int
dprov_key_attr_asymmetric(dprov_state_t *softc, crypto_session_id_t session_id,
    dprov_req_type_t req_type, crypto_key_t *key, crypto_key_t *ret_key)
{
	int ret = CRYPTO_SUCCESS;

	ret_key->ck_format = CRYPTO_KEY_ATTR_LIST;

	switch (key->ck_format) {

	case CRYPTO_KEY_ATTR_LIST:
		ret_key->ck_attrs = key->ck_attrs;
		ret_key->ck_count = key->ck_count;
		break;

	case CRYPTO_KEY_REFERENCE: {
		dprov_object_t *object;

		/* check session id */
		if (softc->ds_sessions[session_id] == NULL) {
			ret = CRYPTO_SESSION_HANDLE_INVALID;
			break;
		}

		/* check if object id specified by key is valid */
		object = softc->ds_sessions[session_id]->
		    ds_objects[key->ck_obj_id];
		if (object == NULL) {
			ret = CRYPTO_KEY_HANDLE_INVALID;
			break;
		}

		/* check if object can be used for operation */
		if ((ret = dprov_key_can_use(object, req_type)) !=
		    CRYPTO_SUCCESS)
			break;

		ret_key->ck_attrs = object->do_attr;
		ret_key->ck_count = DPROV_MAX_ATTR;
		break;
	}

	default:
		ret = CRYPTO_ARGUMENTS_BAD;
	}

	return (ret);
}

/*
 * Return the index of an attribute of specified type found in
 * the specified array of attributes. If the attribute cannot
 * found, return -1.
 */
static int
dprov_find_attr(crypto_object_attribute_t *attr, uint_t nattr,
    uint64_t attr_type)
{
	int i;

	for (i = 0; i < nattr; i++)
		if (attr[i].oa_value != NULL &&
		    attr[i].oa_type == attr_type)
			return (i);

	return (-1);
}

/*
 * Given the given object template and session, return whether
 * an object can be created from that template according to the
 * following rules:
 * - private objects can be created only by a logged-in user
 */
static int
dprov_template_can_create(dprov_session_t *session,
    crypto_object_attribute_t *template, uint_t nattr,
    boolean_t check_for_secret)
{
	boolean_t is_private = B_FALSE;
	ulong_t key_type, class;
	int error;

	/* check CKA_PRIVATE attribute value */
	error = dprov_get_template_attr_boolean(template, nattr,
	    DPROV_CKA_PRIVATE, &is_private);
	if (error == CRYPTO_SUCCESS && is_private) {
		/* it's a private object */
		if (session->ds_state != DPROV_SESSION_STATE_USER) {
			/*
			 * Cannot create private object with SO or public
			 * sessions.
			 */
			return (CRYPTO_ATTRIBUTE_VALUE_INVALID);
		}
	}

	/* all objects must have an object class attribute */
	if (dprov_get_template_attr_ulong(template, nattr, DPROV_CKA_CLASS,
	    &class) != CRYPTO_SUCCESS) {
		return (CRYPTO_TEMPLATE_INCOMPLETE);
	}

	/* key objects must have a key type attribute */
	if (class == DPROV_CKO_SECRET_KEY ||
	    class == DPROV_CKO_PUBLIC_KEY ||
	    class == DPROV_CKO_PRIVATE_KEY) {
		if (!dprov_template_attr_present(template, nattr,
		    DPROV_CKA_KEY_TYPE)) {
			return (CRYPTO_TEMPLATE_INCOMPLETE);
		}
	}

	/* check for RSA public key attributes that must be present */
	if (class == DPROV_CKO_PUBLIC_KEY) {
		if (dprov_get_template_attr_ulong(template, nattr,
		    DPROV_CKA_KEY_TYPE, &key_type) == CRYPTO_SUCCESS) {
			if (key_type == DPROV_CKK_RSA) {
				if (!dprov_template_attr_present(template,
				    nattr, DPROV_CKA_MODULUS) ||
				    !dprov_template_attr_present(template,
				    nattr, DPROV_CKA_PUBLIC_EXPONENT)) {
					return (CRYPTO_TEMPLATE_INCOMPLETE);
				}

				/* these attributes should not be present */
				if (dprov_template_attr_present(template, nattr,
				    DPROV_CKA_MODULUS_BITS)) {
					return (CRYPTO_TEMPLATE_INCONSISTENT);
				}
			}
		}
	}

	/* check for RSA private key attributes that must be present */
	if (class == DPROV_CKO_PRIVATE_KEY) {
		if (dprov_get_template_attr_ulong(template, nattr,
		    DPROV_CKA_KEY_TYPE, &key_type) == CRYPTO_SUCCESS) {
			if (key_type == DPROV_CKK_RSA) {
				if (!dprov_template_attr_present(template,
				    nattr, DPROV_CKA_MODULUS))
					return (CRYPTO_TEMPLATE_INCOMPLETE);

				if (check_for_secret) {
					if (!dprov_template_attr_present(
					    template, nattr,
					    DPROV_CKA_PRIVATE_EXPONENT))
						return (
						    CRYPTO_TEMPLATE_INCOMPLETE);
				}
			}
		}
	}

	/* check for secret key attributes that must be present */
	if (class == DPROV_CKO_SECRET_KEY) {
		if (check_for_secret) {
			if (!dprov_template_attr_present(template, nattr,
			    DPROV_CKA_VALUE)) {
				return (CRYPTO_TEMPLATE_INCOMPLETE);
			}
		}

		/* these attributes should not be present */
		if (dprov_template_attr_present(template, nattr,
		    DPROV_CKA_VALUE_LEN)) {
			return (CRYPTO_TEMPLATE_INCONSISTENT);
		}
	}

	return (CRYPTO_SUCCESS);
}

/*
 * Create an object from the specified template. Checks whether the
 * object can be created according to its attributes and the state
 * of the session. The new session object id is returned. If the
 * object is a token object, it is added to the per-instance object
 * table as well.
 */
static int
dprov_create_object_from_template(dprov_state_t *softc,
    dprov_session_t *session, crypto_object_attribute_t *template,
    uint_t nattr, crypto_object_id_t *object_id, boolean_t check_for_secret,
    boolean_t force)
{
	dprov_object_t *object;
	boolean_t is_token = B_FALSE;
	boolean_t extractable_attribute_present = B_FALSE;
	boolean_t private_attribute_present = B_FALSE;
	uint_t i;
	int error;
	uint_t attr;
	uint_t oattr;
	crypto_attr_type_t type;
	size_t old_len, new_len;
	offset_t offset;

	if (nattr > DPROV_MAX_ATTR)
		return (CRYPTO_HOST_MEMORY);

	if (!force) {
		/* verify that object can be created */
		if ((error = dprov_template_can_create(session, template,
		    nattr, check_for_secret)) != CRYPTO_SUCCESS)
			return (error);
	}

	/* allocate new object */
	object = kmem_zalloc(sizeof (dprov_object_t), KM_SLEEP);
	if (object == NULL)
		return (CRYPTO_HOST_MEMORY);

	/* is it a token object? */
	/* check CKA_TOKEN attribute value */
	error = dprov_get_template_attr_boolean(template, nattr,
	    DPROV_CKA_TOKEN, &is_token);
	if (error == CRYPTO_SUCCESS && is_token) {
		/* token object, add it to the per-instance object table */
		for (i = 0; i < DPROV_MAX_OBJECTS; i++)
			if (softc->ds_objects[i] == NULL)
				break;
		if (i == DPROV_MAX_OBJECTS)
			/* no free slot */
			return (CRYPTO_HOST_MEMORY);
		softc->ds_objects[i] = object;
		object->do_token_idx = i;
		DPROV_OBJECT_REFHOLD(object);
	}

	/* add object to session object table */
	for (i = 0; i < DPROV_MAX_OBJECTS; i++)
		if (session->ds_objects[i] == NULL)
			break;
	if (i == DPROV_MAX_OBJECTS) {
		/* no more session object slots */
		DPROV_OBJECT_REFRELE(object);
		return (CRYPTO_HOST_MEMORY);
	}
	session->ds_objects[i] = object;
	DPROV_OBJECT_REFHOLD(object);
	*object_id = i;

	/* initialize object from template */
	for (attr = 0, oattr = 0; attr < nattr; attr++) {
		if (template[attr].oa_value == NULL)
			continue;
		type = template[attr].oa_type;
		old_len = template[attr].oa_value_len;
		new_len = attribute_size(type, old_len);

		if (type == DPROV_CKA_EXTRACTABLE) {
			extractable_attribute_present = B_TRUE;
		} else if (type == DPROV_CKA_PRIVATE) {
			private_attribute_present = B_TRUE;
		}
		object->do_attr[oattr].oa_type = type;
		object->do_attr[oattr].oa_value_len = new_len;

		object->do_attr[oattr].oa_value = kmem_zalloc(new_len,
		    KM_SLEEP);

		offset = 0;
#ifdef _BIG_ENDIAN
		if (fixed_size_attribute(type)) {
			offset = old_len - new_len;
		}
#endif
		bcopy(&template[attr].oa_value[offset],
		    object->do_attr[oattr].oa_value, new_len);
		oattr++;
	}

	/* add boolean attributes that must be present */
	if (extractable_attribute_present == B_FALSE) {
		object->do_attr[oattr].oa_type = DPROV_CKA_EXTRACTABLE;
		object->do_attr[oattr].oa_value_len = 1;
		object->do_attr[oattr].oa_value = kmem_alloc(1, KM_SLEEP);
		object->do_attr[oattr].oa_value[0] = B_TRUE;
		oattr++;
	}

	if (private_attribute_present == B_FALSE) {
		object->do_attr[oattr].oa_type = DPROV_CKA_PRIVATE;
		object->do_attr[oattr].oa_value_len = 1;
		object->do_attr[oattr].oa_value = kmem_alloc(1, KM_SLEEP);
		object->do_attr[oattr].oa_value[0] = B_FALSE;
		oattr++;
	}

	return (CRYPTO_SUCCESS);
}

/*
 * Checks whether or not the object matches the specified attributes.
 *
 * PKCS#11 attributes which are longs are stored in uint32_t containers
 * so they can be matched by both 32 and 64-bit applications.
 */
static boolean_t
dprov_attributes_match(dprov_object_t *object,
    crypto_object_attribute_t *template, uint_t nattr)
{
	crypto_attr_type_t type;
	size_t tlen, olen, diff;
	int ta_idx;	/* template attribute index */
	int oa_idx;	/* object attribute index */

	for (ta_idx = 0; ta_idx < nattr; ta_idx++) {
		/* no value for template attribute */
		if (template[ta_idx].oa_value == NULL)
			continue;

		/* find attribute in object */
		type = template[ta_idx].oa_type;
		oa_idx = dprov_find_attr(object->do_attr, DPROV_MAX_ATTR, type);

		if (oa_idx == -1)
			/* attribute not found in object */
			return (B_FALSE);

		tlen = template[ta_idx].oa_value_len;
		olen = object->do_attr[oa_idx].oa_value_len;
		if (tlen < olen)
			return (B_FALSE);

		diff = 0;
#ifdef _BIG_ENDIAN
		/* application may think attribute is 8 bytes */
		if (fixed_size_attribute(type))
			diff = tlen - olen;
#endif

		if (bcmp(&template[ta_idx].oa_value[diff],
		    object->do_attr[oa_idx].oa_value, olen) != 0)
			/* value mismatch */
			return (B_FALSE);
	}

	return (B_TRUE);
}

/*
 * Destroy the object specified by its session and object id.
 */
static int
dprov_destroy_object(dprov_state_t *softc, dprov_session_t *session,
    crypto_object_id_t object_id)
{
	dprov_object_t *object;

	if ((object = session->ds_objects[object_id]) == NULL)
		return (CRYPTO_OBJECT_HANDLE_INVALID);

	/* remove from session table */
	session->ds_objects[object_id] = NULL;

	if (dprov_object_is_token(object)) {
		if (!object->do_destroyed) {
			object->do_destroyed = B_TRUE;
			/* remove from per-instance token table */
			softc->ds_objects[object->do_token_idx] = NULL;
			DPROV_OBJECT_REFRELE(object);
		} else {
			DPROV_DEBUG(D_OBJECT, ("dprov_destroy_object: "
			    "object %p already destroyed\n", (void *)object));
		}
	}

	DPROV_OBJECT_REFRELE(object);
	return (CRYPTO_SUCCESS);
}

static int
dprov_object_can_modify(dprov_object_t *object,
    crypto_object_attribute_t *template, uint_t nattr)
{
	ulong_t object_class;

	/* all objects should have an object class attribute */
	if (dprov_get_object_attr_ulong(object, DPROV_CKA_CLASS,
	    &object_class) != CRYPTO_SUCCESS) {
		return (CRYPTO_SUCCESS);
	}

	if (object_class == DPROV_CKO_SECRET_KEY ||
	    object_class == DPROV_CKO_PUBLIC_KEY ||
	    object_class == DPROV_CKO_PRIVATE_KEY) {
		if (dprov_template_attr_present(template, nattr,
		    DPROV_CKA_CLASS) ||
		    dprov_template_attr_present(template, nattr,
		    DPROV_CKA_KEY_TYPE))
			return (CRYPTO_TEMPLATE_INCONSISTENT);
	}

	switch (object_class) {
	case DPROV_CKO_SECRET_KEY:
		if (dprov_template_attr_present(template, nattr,
		    DPROV_CKA_VALUE))
			return (CRYPTO_TEMPLATE_INCONSISTENT);
		break;

	case DPROV_CKO_PUBLIC_KEY:
		if (dprov_template_attr_present(template, nattr,
		    DPROV_CKA_MODULUS) ||
		    dprov_template_attr_present(template, nattr,
		    DPROV_CKA_PUBLIC_EXPONENT))
			return (CRYPTO_TEMPLATE_INCONSISTENT);
		break;

	case DPROV_CKO_PRIVATE_KEY:
		if (dprov_template_attr_present(template, nattr,
		    DPROV_CKA_MODULUS) ||
		    dprov_template_attr_present(template, nattr,
		    DPROV_CKA_PRIVATE_EXPONENT))
			return (CRYPTO_TEMPLATE_INCONSISTENT);
		break;

	default:
		return (CRYPTO_SUCCESS);
	}

	return (CRYPTO_SUCCESS);
}

/*
 * Set the attributes specified by the template in the specified object,
 * replacing existing ones if needed.
 */
static int
dprov_object_set_attr(dprov_session_t *session, crypto_object_id_t object_id,
    crypto_object_attribute_t *template, uint_t nattr,
    boolean_t check_attributes)
{
	crypto_attr_type_t type;
	dprov_object_t *object;
	size_t old_len, new_len;
	uint_t i, j;
	int error;

	if ((object = session->ds_objects[object_id]) == NULL)
		return (CRYPTO_OBJECT_HANDLE_INVALID);

	if (check_attributes) {
		/* verify that attributes in the template can be modified */
		if ((error = dprov_object_can_modify(object, template, nattr))
		    != CRYPTO_SUCCESS)
			return (error);
	}

	/* go through the attributes specified in the template */
	for (i = 0; i < nattr; i++) {
		if (template[i].oa_value == NULL)
			continue;

		/* find attribute in object */
		type = template[i].oa_type;
		j = dprov_find_attr(object->do_attr, DPROV_MAX_ATTR, type);

		if (j != -1) {
			/* attribute already exists, free old value */
			kmem_free(object->do_attr[j].oa_value,
			    object->do_attr[j].oa_value_len);
		} else {
			/* attribute does not exist, create it */
			for (j = 0; j < DPROV_MAX_ATTR; j++)
				if (object->do_attr[j].oa_value == NULL)
					break;
			if (j == DPROV_MAX_ATTR)
				/* ran out of attribute slots */
				return (CRYPTO_HOST_MEMORY);
		}

		old_len = template[i].oa_value_len;
		new_len = attribute_size(type, old_len);

		/* set object attribute value */
		object->do_attr[j].oa_value = kmem_alloc(new_len, KM_SLEEP);
		bcopy(&template[i].oa_value[old_len - new_len],
		    object->do_attr[j].oa_value, new_len);
		object->do_attr[j].oa_value_len = new_len;

		/* and the type */
		object->do_attr[j].oa_type = type;
	}

	return (CRYPTO_SUCCESS);
}


/*
 * Free the specified object.
 */
static void
dprov_free_object(dprov_object_t *object)
{
	int i;

	/* free the object attributes values */
	for (i = 0; i < DPROV_MAX_ATTR; i++)
		if (object->do_attr[i].oa_value != NULL)
			kmem_free(object->do_attr[i].oa_value,
			    object->do_attr[i].oa_value_len);

	/* free the object */
	kmem_free(object, sizeof (dprov_object_t));
}

/*
 * Checks whether the specified object is a private or public object.
 */
static boolean_t
dprov_object_is_private(dprov_object_t *object)
{
	boolean_t ret;
	int err;

	err = dprov_get_object_attr_boolean(object, DPROV_CKA_PRIVATE, &ret);

	if (err != CRYPTO_SUCCESS)
		/* by default, CKA_PRIVATE is false */
		ret = B_FALSE;

	return (ret);
}

/*
 * Checks whether the specified object is a token or session object.
 */
static boolean_t
dprov_object_is_token(dprov_object_t *object)
{
	boolean_t ret;
	int err;

	err = dprov_get_object_attr_boolean(object, DPROV_CKA_TOKEN, &ret);

	if (err != CRYPTO_SUCCESS)
		/* by default, CKA_TOKEN is false */
		ret = B_FALSE;

	return (ret);
}

/*
 * Common function used by the dprov_get_object_attr_*() family of
 * functions. Returns the value of the specified attribute of specified
 * length. Returns CRYPTO_SUCCESS on success, CRYPTO_ATTRIBUTE_VALUE_INVALID
 * if the length of the attribute does not match the specified length,
 * or CRYPTO_ARGUMENTS_BAD if the attribute cannot be found.
 */
static int
dprov_get_object_attr_scalar_common(dprov_object_t *object, uint64_t attr_type,
				    void *value, size_t value_len)
{
	int attr_idx;
	size_t oa_value_len;
	size_t offset = 0;

	if ((attr_idx = dprov_find_attr(object->do_attr, DPROV_MAX_ATTR,
	    attr_type)) == -1)
		return (CRYPTO_ARGUMENTS_BAD);

	oa_value_len = object->do_attr[attr_idx].oa_value_len;
	if (oa_value_len != value_len) {
		/*
		 * For some attributes, it's okay to copy the value
		 * into a larger container, e.g. copy an unsigned
		 * 32-bit integer into a 64-bit container.
		 */
		if (attr_type == DPROV_CKA_VALUE_LEN ||
		    attr_type == DPROV_CKA_KEY_TYPE ||
		    attr_type == DPROV_CKA_CLASS) {
			if (oa_value_len < value_len) {
#ifdef _BIG_ENDIAN
				offset = value_len - oa_value_len;
#endif
				bzero(value, value_len);
				goto do_copy;
			}
		}
		/* incorrect attribute value length */
		return (CRYPTO_ATTRIBUTE_VALUE_INVALID);
	}

do_copy:
	bcopy(object->do_attr[attr_idx].oa_value, (uchar_t *)value + offset,
	    oa_value_len);

	return (CRYPTO_SUCCESS);
}

/*
 * Get the value of the a boolean attribute from the specified object.
 */
static int
dprov_get_object_attr_boolean(dprov_object_t *object, uint64_t attr_type,
    boolean_t *attr_value)
{
	uchar_t val;
	int ret;

	/* PKCS#11 defines a boolean as one byte */
	ret = dprov_get_object_attr_scalar_common(object, attr_type, &val, 1);
	if (ret == CRYPTO_SUCCESS) {
		*attr_value = (val == '\0') ? B_FALSE : B_TRUE;
	}
	return (ret);
}

/*
 * Get the value of a ulong_t attribute from the specified object.
 */
static int
dprov_get_object_attr_ulong(dprov_object_t *object, uint64_t attr_type,
    ulong_t *attr_value)
{
	return (dprov_get_object_attr_scalar_common(object, attr_type,
	    attr_value, sizeof (ulong_t)));
}

/*
 * Find the specified byte array attribute of specified type in
 * the specified object. Returns CRYPTO_SUCCESS
 * on success or CRYPTO_ARGUMENTS_BAD if the specified
 * attribute cannot be found.
 */
static int
dprov_get_object_attr_array(dprov_object_t *object, uint64_t attr_type,
    void **array, size_t *len)
{
	int attr_idx;

	if ((attr_idx = dprov_find_attr(object->do_attr, DPROV_MAX_ATTR,
	    attr_type)) == -1)
		return (CRYPTO_ARGUMENTS_BAD);

	*array = object->do_attr[attr_idx].oa_value;
	*len = object->do_attr[attr_idx].oa_value_len;

	return (CRYPTO_SUCCESS);
}

/*
 * Common function used by the dprov_get_template_attr_*() family of
 * functions. Returns the value of the specified attribute of specified
 * length. Returns CRYPTO_SUCCESS on success, CRYPTO_ATTRIBUTE_VALUE_INVALID
 * if the length of the attribute does not match the specified length,
 * or CRYPTO_ARGUMENTS_BAD if the attribute cannot be found.
 */
static int
dprov_get_template_attr_scalar_common(crypto_object_attribute_t *template,
    uint_t nattr, uint64_t attr_type, void *value, size_t value_len)
{
	size_t oa_value_len;
	size_t offset = 0;
	int attr_idx;

	if ((attr_idx = dprov_find_attr(template, nattr, attr_type)) == -1)
		return (CRYPTO_ARGUMENTS_BAD);

	oa_value_len = template[attr_idx].oa_value_len;
	if (oa_value_len != value_len) {
		/*
		 * For some attributes, it's okay to copy the value
		 * into a larger container, e.g. copy an unsigned
		 * 32-bit integer into a 64-bit container.
		 */
		if (attr_type == DPROV_CKA_VALUE_LEN ||
		    attr_type == DPROV_CKA_KEY_TYPE ||
		    attr_type == DPROV_CKA_CLASS) {
			if (oa_value_len < value_len) {
#ifdef _BIG_ENDIAN
				offset = value_len - oa_value_len;
#endif
				bzero(value, value_len);
				goto do_copy;
			}
		}
		/* incorrect attribute value length */
		return (CRYPTO_ATTRIBUTE_VALUE_INVALID);
	}

do_copy:
	bcopy(template[attr_idx].oa_value, (uchar_t *)value + offset,
	    oa_value_len);

	return (CRYPTO_SUCCESS);
}

/*
 * Get the value of the a boolean attribute from the specified template
 */
static int
dprov_get_template_attr_boolean(crypto_object_attribute_t *template,
    uint_t nattr, uint64_t attr_type, boolean_t *attr_value)
{
	uchar_t val;
	int ret;

	/* PKCS#11 defines a boolean as one byte */
	ret = dprov_get_template_attr_scalar_common(template, nattr,
	    attr_type, &val, 1);
	if (ret == CRYPTO_SUCCESS) {
		*attr_value = (val == '\0') ? B_FALSE : B_TRUE;
	}
	return (ret);
}

/*
 * Get the value of a ulong_t attribute from the specified template.
 */
static int
dprov_get_template_attr_ulong(crypto_object_attribute_t *template,
    uint_t nattr, uint64_t attr_type, ulong_t *attr_value)
{
	return (dprov_get_template_attr_scalar_common(template, nattr,
	    attr_type, attr_value, sizeof (ulong_t)));
}

static int
dprov_template_attr_present(crypto_object_attribute_t *template,
    uint_t nattr, uint64_t attr_type)
{
	return (dprov_find_attr(template, nattr,
	    attr_type) == -1 ? B_FALSE : B_TRUE);
}

/*
 * Find the specified byte array attribute of specified type in
 * the specified template. Returns CRYPTO_SUCCESS on success or
 * CRYPTO_ARGUMENTS_BAD if the specified attribute cannot be found.
 */
static int
dprov_get_template_attr_array(crypto_object_attribute_t *template,
    uint_t nattr, uint64_t attr_type, void **array, size_t *len)
{
	int attr_idx;

	if ((attr_idx = dprov_find_attr(template, nattr, attr_type)) == -1)
		return (CRYPTO_ARGUMENTS_BAD);

	*array = template[attr_idx].oa_value;
	*len = template[attr_idx].oa_value_len;

	return (CRYPTO_SUCCESS);
}

/*
 * Common function used by the dprov_get_key_attr_*() family of
 * functions. Returns the value of the specified attribute of specified
 * length. Returns CRYPTO_SUCCESS on success, CRYPTO_ATTRIBUTE_VALUE_INVALID
 * if the length of the attribute does not match the specified length,
 * or CRYPTO_ARGUMENTS_BAD if the attribute cannot be found.
 */
static int
dprov_get_key_attr_scalar_common(crypto_key_t *key, uint64_t attr_type,
    void *value, size_t value_len)
{
	int attr_idx;

	ASSERT(key->ck_format == CRYPTO_KEY_ATTR_LIST);

	if ((attr_idx = dprov_find_attr(key->ck_attrs, key->ck_count,
	    attr_type)) == -1)
		return (CRYPTO_ARGUMENTS_BAD);

	if (key->ck_attrs[attr_idx].oa_value_len != value_len)
		/* incorrect attribute value length */
		return (CRYPTO_ATTRIBUTE_VALUE_INVALID);

	bcopy(key->ck_attrs[attr_idx].oa_value, value, value_len);

	return (CRYPTO_SUCCESS);
}

/*
 * Get the value of a ulong_t attribute from the specified key.
 */
static int
dprov_get_key_attr_ulong(crypto_key_t *key, uint64_t attr_type,
    ulong_t *attr_value)
{
	return (dprov_get_key_attr_scalar_common(key, attr_type,
	    attr_value, sizeof (ulong_t)));
}

/*
 * Find the specified byte array attribute of specified type in
 * the specified key by attributes. Returns CRYPTO_SUCCESS
 * on success or CRYPTO_ARGUMENTS_BAD if the specified
 * attribute cannot be found.
 */
static int
dprov_get_key_attr_array(crypto_key_t *key, uint64_t attr_type,
    void **array, size_t *len)
{
	int attr_idx;

	ASSERT(key->ck_format == CRYPTO_KEY_ATTR_LIST);

	if ((attr_idx = dprov_find_attr(key->ck_attrs, key->ck_count,
	    attr_type)) == -1)
		return (CRYPTO_ARGUMENTS_BAD);

	*array = key->ck_attrs[attr_idx].oa_value;
	*len = key->ck_attrs[attr_idx].oa_value_len;

	return (CRYPTO_SUCCESS);
}

static void
dprov_release_session_objects(dprov_session_t *session)
{
	dprov_object_t *object;
	int i;

	for (i = 0; i < DPROV_MAX_OBJECTS; i++) {
		object = session->ds_objects[i];
		if (object != NULL) {
			DPROV_OBJECT_REFRELE(object);
		}
	}
}