/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

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

#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include <des_cbc_crypt.h>
#include <aes_cbc_crypt.h>
#include <blowfish_cbc_crypt.h>
#include <arcfour.h>
#include "softSession.h"
#include "softObject.h"
#include "softOps.h"
#include "softCrypt.h"
#include "softRSA.h"

/*
 * Add padding bytes with the value of length of padding.
 */
void
soft_add_pkcs7_padding(CK_BYTE *buf, int block_size, CK_ULONG data_len)
{

	ulong_t i, pad_len;
	CK_BYTE pad_value;

	pad_len = block_size - (data_len % block_size);
	pad_value = (CK_BYTE)pad_len;

	for (i = 0; i < pad_len; i++)
		buf[i] = pad_value;
}

/*
 * Perform encrypt init operation internally for the support of
 * CKM_DES_MAC and CKM_DES_MAC_GENERAL
 *
 * This function is called with the session being held, and without
 * its mutex taken.
 */
CK_RV
soft_encrypt_init_internal(soft_session_t *session_p, CK_MECHANISM_PTR
	pMechanism, soft_object_t *key_p)
{
	CK_RV rv;

	(void) pthread_mutex_lock(&session_p->session_mutex);

	/* Check to see if encrypt operation is already active */
	if (session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE) {
		(void) pthread_mutex_unlock(&session_p->session_mutex);
		return (CKR_OPERATION_ACTIVE);
	}

	session_p->encrypt.flags = CRYPTO_OPERATION_ACTIVE;

	(void) pthread_mutex_unlock(&session_p->session_mutex);

	rv = soft_encrypt_init(session_p, pMechanism, key_p);

	if (rv != CKR_OK) {
		(void) pthread_mutex_lock(&session_p->session_mutex);
		session_p->encrypt.flags &= ~CRYPTO_OPERATION_ACTIVE;
		(void) pthread_mutex_unlock(&session_p->session_mutex);
	}

	return (rv);
}

/*
 * soft_encrypt_init()
 *
 * Arguments:
 *	session_p:	pointer to soft_session_t struct
 *	pMechanism:	pointer to CK_MECHANISM struct provided by application
 *	key_p:		pointer to key soft_object_t struct
 *
 * Description:
 *	called by C_EncryptInit(). This function calls the corresponding
 *	encrypt init routine based on the mechanism.
 *
 * Returns:
 *	CKR_OK: success
 *	CKR_HOST_MEMORY: run out of system memory
 *	CKR_MECHANISM_PARAM_INVALID: invalid parameters in mechanism
 *	CKR_MECHANISM_INVALID: invalid mechanism type
 *	CKR_KEY_TYPE_INCONSISTENT: incorrect type of key to use
 *		with the specified mechanism
 */
CK_RV
soft_encrypt_init(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism,
    soft_object_t *key_p)
{

	CK_RV rv;

	switch (pMechanism->mechanism) {

	case CKM_DES_ECB:

		if (key_p->key_type != CKK_DES) {
			return (CKR_KEY_TYPE_INCONSISTENT);
		}
		goto ecb_common;

	case CKM_DES3_ECB:

		if ((key_p->key_type != CKK_DES2) &&
		    (key_p->key_type != CKK_DES3)) {
			return (CKR_KEY_TYPE_INCONSISTENT);
		}

ecb_common:
		return (soft_des_crypt_init_common(session_p, pMechanism,
		    key_p, B_TRUE));

	case CKM_DES_CBC:
	case CKM_DES_CBC_PAD:

		if (key_p->key_type != CKK_DES) {
			return (CKR_KEY_TYPE_INCONSISTENT);
		}

		goto cbc_common;

	case CKM_DES3_CBC:
	case CKM_DES3_CBC_PAD:
	{

		soft_des_ctx_t *soft_des_ctx;

		if ((key_p->key_type != CKK_DES2) &&
		    (key_p->key_type != CKK_DES3)) {
			return (CKR_KEY_TYPE_INCONSISTENT);
		}

cbc_common:
		if ((pMechanism->pParameter == NULL) ||
		    (pMechanism->ulParameterLen != DES_BLOCK_LEN)) {
			return (CKR_MECHANISM_PARAM_INVALID);
		}

		rv = soft_des_crypt_init_common(session_p, pMechanism,
		    key_p, B_TRUE);

		if (rv != CKR_OK)
			return (rv);

		(void) pthread_mutex_lock(&session_p->session_mutex);

		soft_des_ctx = (soft_des_ctx_t *)session_p->encrypt.context;
		/* Copy Initialization Vector (IV) into the context. */
		(void) memcpy(soft_des_ctx->ivec, pMechanism->pParameter,
		    DES_BLOCK_LEN);

		/* Allocate a context for DES cipher-block chaining. */
		soft_des_ctx->des_cbc = (void *)des_cbc_ctx_init(
		    soft_des_ctx->key_sched, soft_des_ctx->keysched_len,
		    soft_des_ctx->ivec, key_p->key_type);

		if (soft_des_ctx->des_cbc == NULL) {
			bzero(soft_des_ctx->key_sched,
			    soft_des_ctx->keysched_len);
			free(soft_des_ctx->key_sched);
			free(session_p->encrypt.context);
			session_p->encrypt.context = NULL;
			rv = CKR_HOST_MEMORY;
		}

		(void) pthread_mutex_unlock(&session_p->session_mutex);

		return (rv);
	}
	case CKM_AES_ECB:

		if (key_p->key_type != CKK_AES) {
			return (CKR_KEY_TYPE_INCONSISTENT);
		}

		return (soft_aes_crypt_init_common(session_p, pMechanism,
		    key_p, B_TRUE));

	case CKM_AES_CBC:
	case CKM_AES_CBC_PAD:
	{
		soft_aes_ctx_t *soft_aes_ctx;

		if (key_p->key_type != CKK_AES) {
			return (CKR_KEY_TYPE_INCONSISTENT);
		}

		if ((pMechanism->pParameter == NULL) ||
		    (pMechanism->ulParameterLen != AES_BLOCK_LEN)) {
			return (CKR_MECHANISM_PARAM_INVALID);
		}

		rv = soft_aes_crypt_init_common(session_p, pMechanism,
		    key_p, B_TRUE);

		if (rv != CKR_OK)
			return (rv);

		(void) pthread_mutex_lock(&session_p->session_mutex);

		soft_aes_ctx = (soft_aes_ctx_t *)session_p->encrypt.context;
		/* Copy Initialization Vector (IV) into the context. */
		(void) memcpy(soft_aes_ctx->ivec, pMechanism->pParameter,
		    AES_BLOCK_LEN);

		/* Allocate a context for AES cipher-block chaining. */
		soft_aes_ctx->aes_cbc = (void *)aes_cbc_ctx_init(
		    soft_aes_ctx->key_sched, soft_aes_ctx->keysched_len,
		    soft_aes_ctx->ivec);

		if (soft_aes_ctx->aes_cbc == NULL) {
			bzero(soft_aes_ctx->key_sched,
			    soft_aes_ctx->keysched_len);
			free(soft_aes_ctx->key_sched);
			free(session_p->encrypt.context);
			session_p->encrypt.context = NULL;
			rv = CKR_HOST_MEMORY;
		}

		(void) pthread_mutex_unlock(&session_p->session_mutex);

		return (rv);
	}
	case CKM_RC4:

		if (key_p->key_type != CKK_RC4) {
			return (CKR_KEY_TYPE_INCONSISTENT);
		}

		return (soft_arcfour_crypt_init(session_p, pMechanism, key_p,
		    B_TRUE));

	case CKM_RSA_X_509:
	case CKM_RSA_PKCS:

		if (key_p->key_type != CKK_RSA) {
			return (CKR_KEY_TYPE_INCONSISTENT);
		}

		return (soft_rsa_crypt_init_common(session_p, pMechanism,
		    key_p, B_TRUE));

	case CKM_BLOWFISH_CBC:
	{
		soft_blowfish_ctx_t *soft_blowfish_ctx;

		if (key_p->key_type != CKK_BLOWFISH)
			return (CKR_KEY_TYPE_INCONSISTENT);

		if ((pMechanism->pParameter == NULL) ||
		    (pMechanism->ulParameterLen != BLOWFISH_BLOCK_LEN))
			return (CKR_MECHANISM_PARAM_INVALID);

		rv = soft_blowfish_crypt_init_common(session_p, pMechanism,
		    key_p, B_TRUE);

		if (rv != CKR_OK)
			return (rv);

		(void) pthread_mutex_lock(&session_p->session_mutex);

		soft_blowfish_ctx =
		    (soft_blowfish_ctx_t *)session_p->encrypt.context;
		/* Copy Initialization Vector (IV) into the context. */
		(void) memcpy(soft_blowfish_ctx->ivec, pMechanism->pParameter,
		    BLOWFISH_BLOCK_LEN);

		/* Allocate a context for Blowfish cipher-block chaining */
		soft_blowfish_ctx->blowfish_cbc =
		    (void *)blowfish_cbc_ctx_init(soft_blowfish_ctx->key_sched,
			soft_blowfish_ctx->keysched_len,
			soft_blowfish_ctx->ivec);

		if (soft_blowfish_ctx->blowfish_cbc == NULL) {
			bzero(soft_blowfish_ctx->key_sched,
			    soft_blowfish_ctx->keysched_len);
			free(soft_blowfish_ctx->key_sched);
			free(session_p->encrypt.context);
			session_p->encrypt.context = NULL;
			rv = CKR_HOST_MEMORY;
		}

		(void) pthread_mutex_unlock(&session_p->session_mutex);

		return (rv);
	}
	default:
		return (CKR_MECHANISM_INVALID);
	}
}


/*
 * soft_encrypt_common()
 *
 * Arguments:
 *      session_p:	pointer to soft_session_t struct
 *	pData:		pointer to the input data to be encrypted
 *	ulDataLen:	length of the input data
 *	pEncrypted:	pointer to the output data after encryption
 *	pulEncryptedLen: pointer to the length of the output data
 *	update:		boolean flag indicates caller is soft_encrypt
 *			or soft_encrypt_update
 *
 * Description:
 *      This function calls the corresponding encrypt routine based
 *	on the mechanism.
 *
 * Returns:
 *	see corresponding encrypt routine.
 */
CK_RV
soft_encrypt_common(soft_session_t *session_p, CK_BYTE_PTR pData,
    CK_ULONG ulDataLen, CK_BYTE_PTR pEncrypted,
    CK_ULONG_PTR pulEncryptedLen, boolean_t update)
{

	CK_MECHANISM_TYPE mechanism = session_p->encrypt.mech.mechanism;

	switch (mechanism) {

	case CKM_DES_ECB:
	case CKM_DES_CBC:
	case CKM_DES_CBC_PAD:
	case CKM_DES3_ECB:
	case CKM_DES3_CBC:
	case CKM_DES3_CBC_PAD:

		return (soft_des_encrypt_common(session_p, pData,
		    ulDataLen, pEncrypted, pulEncryptedLen, update));

	case CKM_AES_ECB:
	case CKM_AES_CBC:
	case CKM_AES_CBC_PAD:

		return (soft_aes_encrypt_common(session_p, pData,
		    ulDataLen, pEncrypted, pulEncryptedLen, update));

	case CKM_BLOWFISH_CBC:

		return (soft_blowfish_encrypt_common(session_p, pData,
		    ulDataLen, pEncrypted, pulEncryptedLen, update));

	case CKM_RC4:
	{
		ARCFour_key *keystream = session_p->encrypt.context;
		CK_RV rv;

		rv = soft_arcfour_crypt(&(session_p->encrypt), pData,
		    ulDataLen, pEncrypted, pulEncryptedLen);
		if ((rv == CKR_OK) && (pEncrypted != NULL)) {
			bzero(keystream, sizeof (*keystream));
			free(keystream);
			session_p->encrypt.context = NULL;
		}
		return (rv);
	}

	case CKM_RSA_X_509:
	case CKM_RSA_PKCS:

		return (soft_rsa_encrypt_common(session_p, pData,
		    ulDataLen, pEncrypted, pulEncryptedLen, mechanism));

	default:
		return (CKR_MECHANISM_INVALID);
	}
}


/*
 * soft_encrypt()
 *
 * Arguments:
 *      session_p:	pointer to soft_session_t struct
 *	pData:		pointer to the input data to be encrypted
 *	ulDataLen:	length of the input data
 *	pEncryptedData:	pointer to the output data after encryption
 *	pulEncryptedDataLen: pointer to the length of the output data
 *
 * Description:
 *      called by C_Encrypt(). This function calls the soft_encrypt_common
 *	routine.
 *
 * Returns:
 *	see soft_encrypt_common().
 */
CK_RV
soft_encrypt(soft_session_t *session_p, CK_BYTE_PTR pData,
    CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData,
    CK_ULONG_PTR pulEncryptedDataLen)
{

	return (soft_encrypt_common(session_p, pData, ulDataLen,
	    pEncryptedData, pulEncryptedDataLen, B_FALSE));
}


/*
 * soft_encrypt_update()
 *
 * Arguments:
 *      session_p:	pointer to soft_session_t struct
 *      pPart:		pointer to the input data to be digested
 *      ulPartLen:	length of the input data
 *	pEncryptedPart:	pointer to the ciphertext
 *	pulEncryptedPartLen: pointer to the length of the ciphertext
 *
 * Description:
 *      called by C_EncryptUpdate(). This function calls the
 *	soft_encrypt_common routine (with update flag on).
 *
 * Returns:
 *	see soft_encrypt_common().
 */
CK_RV
soft_encrypt_update(soft_session_t *session_p, CK_BYTE_PTR pPart,
	CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart,
	CK_ULONG_PTR pulEncryptedPartLen)
{

	CK_MECHANISM_TYPE mechanism = session_p->encrypt.mech.mechanism;

	switch (mechanism) {

	case CKM_DES_ECB:
	case CKM_DES_CBC:
	case CKM_DES_CBC_PAD:
	case CKM_DES3_ECB:
	case CKM_DES3_CBC:
	case CKM_DES3_CBC_PAD:
	case CKM_AES_ECB:
	case CKM_AES_CBC:
	case CKM_AES_CBC_PAD:
	case CKM_BLOWFISH_CBC:

		return (soft_encrypt_common(session_p, pPart, ulPartLen,
		    pEncryptedPart, pulEncryptedPartLen, B_TRUE));

	case CKM_RC4:

		return (soft_arcfour_crypt(&(session_p->encrypt), pPart,
		    ulPartLen, pEncryptedPart, pulEncryptedPartLen));

	default:
		/* PKCS11: The mechanism only supports single-part operation. */
		return (CKR_MECHANISM_INVALID);
	}
}


/*
 * soft_encrypt_final()
 *
 * Arguments:
 *      session_p:		pointer to soft_session_t struct
 *      pLastEncryptedPart:	pointer to the last encrypted data part
 *      pulLastEncryptedPartLen: pointer to the length of the last
 *				encrypted data part
 *
 * Description:
 *      called by C_EncryptFinal().
 *
 * Returns:
 *	CKR_OK: success
 *	CKR_FUNCTION_FAILED: encrypt final function failed
 *	CKR_DATA_LEN_RANGE: remaining buffer contains bad length
 */
CK_RV
soft_encrypt_final(soft_session_t *session_p, CK_BYTE_PTR pLastEncryptedPart,
	CK_ULONG_PTR pulLastEncryptedPartLen)
{

	CK_MECHANISM_TYPE mechanism = session_p->encrypt.mech.mechanism;
	CK_ULONG out_len;
	CK_RV rv = CKR_OK;
	int rc;

	(void) pthread_mutex_lock(&session_p->session_mutex);

	if (session_p->encrypt.context == NULL) {
		rv = CKR_OPERATION_NOT_INITIALIZED;
		*pulLastEncryptedPartLen = 0;
		goto clean1;
	}
	switch (mechanism) {

	case CKM_DES_CBC_PAD:
	case CKM_DES3_CBC_PAD:
	{
		soft_des_ctx_t *soft_des_ctx;

		soft_des_ctx = (soft_des_ctx_t *)session_p->encrypt.context;
		/*
		 * For CKM_DES_CBC_PAD, compute output length with
		 * padding. If the remaining buffer has one block
		 * of data, then output length will be two blocksize of
		 * ciphertext. If the remaining buffer has less than
		 * one block of data, then output length will be
		 * one blocksize.
		 */
		if (soft_des_ctx->remain_len == DES_BLOCK_LEN)
			out_len = 2 * DES_BLOCK_LEN;
		else
			out_len = DES_BLOCK_LEN;

		if (pLastEncryptedPart == NULL) {
			/*
			 * Application asks for the length of the output
			 * buffer to hold the ciphertext.
			 */
			*pulLastEncryptedPartLen = out_len;
			goto clean1;
		} else {
			crypto_data_t out;

			/* Copy remaining data to the output buffer. */
			(void) memcpy(pLastEncryptedPart, soft_des_ctx->data,
			    soft_des_ctx->remain_len);

			/*
			 * Add padding bytes prior to encrypt final.
			 */
			soft_add_pkcs7_padding(pLastEncryptedPart +
			    soft_des_ctx->remain_len, DES_BLOCK_LEN,
			    soft_des_ctx->remain_len);

			out.cd_format = CRYPTO_DATA_RAW;
			out.cd_offset = 0;
			out.cd_length = out_len;
			out.cd_raw.iov_base = (char *)pLastEncryptedPart;
			out.cd_raw.iov_len = out_len;

			/* Encrypt multiple blocks of data. */
			rc = des_encrypt_contiguous_blocks(
			    (des_ctx_t *)soft_des_ctx->des_cbc,
			    (char *)pLastEncryptedPart, out_len, &out);

			if (rc == 0) {
				*pulLastEncryptedPartLen = out_len;
			} else {
				*pulLastEncryptedPartLen = 0;
				rv = CKR_FUNCTION_FAILED;
			}

			/* Cleanup memory space. */
			free(soft_des_ctx->des_cbc);
			bzero(soft_des_ctx->key_sched,
			    soft_des_ctx->keysched_len);
			free(soft_des_ctx->key_sched);
		}

		break;
	}
	case CKM_DES_CBC:
	case CKM_DES_ECB:
	case CKM_DES3_CBC:
	case CKM_DES3_ECB:
	{

		soft_des_ctx_t *soft_des_ctx;

		soft_des_ctx = (soft_des_ctx_t *)session_p->encrypt.context;
		/*
		 * CKM_DES_CBC and CKM_DES_ECB does not do any padding,
		 * so when the final is called, the remaining buffer
		 * should not contain any more data.
		 */
		*pulLastEncryptedPartLen = 0;
		if (soft_des_ctx->remain_len != 0) {
			rv = CKR_DATA_LEN_RANGE;
		} else {
			if (pLastEncryptedPart == NULL)
				goto clean1;
		}

		/* Cleanup memory space. */
		free(soft_des_ctx->des_cbc);
		bzero(soft_des_ctx->key_sched, soft_des_ctx->keysched_len);
		free(soft_des_ctx->key_sched);

		break;
	}
	case CKM_AES_CBC_PAD:
	{
		soft_aes_ctx_t *soft_aes_ctx;

		soft_aes_ctx = (soft_aes_ctx_t *)session_p->encrypt.context;
		/*
		 * For CKM_AES_CBC_PAD, compute output length with
		 * padding. If the remaining buffer has one block
		 * of data, then output length will be two blocksize of
		 * ciphertext. If the remaining buffer has less than
		 * one block of data, then output length will be
		 * one blocksize.
		 */
		if (soft_aes_ctx->remain_len == AES_BLOCK_LEN)
			out_len = 2 * AES_BLOCK_LEN;
		else
			out_len = AES_BLOCK_LEN;

		if (pLastEncryptedPart == NULL) {
			/*
			 * Application asks for the length of the output
			 * buffer to hold the ciphertext.
			 */
			*pulLastEncryptedPartLen = out_len;
			goto clean1;
		} else {
			crypto_data_t out;

			/* Copy remaining data to the output buffer. */
			(void) memcpy(pLastEncryptedPart, soft_aes_ctx->data,
			    soft_aes_ctx->remain_len);

			/*
			 * Add padding bytes prior to encrypt final.
			 */
			soft_add_pkcs7_padding(pLastEncryptedPart +
			    soft_aes_ctx->remain_len, AES_BLOCK_LEN,
			    soft_aes_ctx->remain_len);

			out.cd_format = CRYPTO_DATA_RAW;
			out.cd_offset = 0;
			out.cd_length = out_len;
			out.cd_raw.iov_base = (char *)pLastEncryptedPart;
			out.cd_raw.iov_len = out_len;

			/* Encrypt multiple blocks of data. */
			rc = aes_encrypt_contiguous_blocks(
			    (aes_ctx_t *)soft_aes_ctx->aes_cbc,
			    (char *)pLastEncryptedPart, out_len, &out);

			if (rc == 0) {
				*pulLastEncryptedPartLen = out_len;
			} else {
				*pulLastEncryptedPartLen = 0;
				rv = CKR_FUNCTION_FAILED;
			}

			/* Cleanup memory space. */
			free(soft_aes_ctx->aes_cbc);
			bzero(soft_aes_ctx->key_sched,
			    soft_aes_ctx->keysched_len);
			free(soft_aes_ctx->key_sched);
		}

		break;
	}
	case CKM_AES_CBC:
	case CKM_AES_ECB:
	{

		soft_aes_ctx_t *soft_aes_ctx;

		soft_aes_ctx = (soft_aes_ctx_t *)session_p->encrypt.context;
		/*
		 * CKM_AES_CBC and CKM_AES_ECB does not do any padding,
		 * so when the final is called, the remaining buffer
		 * should not contain any more data.
		 */
		*pulLastEncryptedPartLen = 0;
		if (soft_aes_ctx->remain_len != 0) {
			rv = CKR_DATA_LEN_RANGE;
		} else {
			if (pLastEncryptedPart == NULL)
				goto clean1;
		}

		/* Cleanup memory space. */
		free(soft_aes_ctx->aes_cbc);
		bzero(soft_aes_ctx->key_sched, soft_aes_ctx->keysched_len);
		free(soft_aes_ctx->key_sched);

		break;
	}

	case CKM_BLOWFISH_CBC:
	{
		soft_blowfish_ctx_t *soft_blowfish_ctx;

		soft_blowfish_ctx =
		    (soft_blowfish_ctx_t *)session_p->encrypt.context;
		/*
		 * CKM_BLOWFISH_CBC does not do any padding, so when the
		 * final is called, the remaining buffer should not contain
		 * any more data
		 */
		*pulLastEncryptedPartLen = 0;
		if (soft_blowfish_ctx->remain_len != 0)
			rv = CKR_DATA_LEN_RANGE;
		else {
			if (pLastEncryptedPart == NULL)
				goto clean1;
		}

		free(soft_blowfish_ctx->blowfish_cbc);
		bzero(soft_blowfish_ctx->key_sched,
		    soft_blowfish_ctx->keysched_len);
		free(soft_blowfish_ctx->key_sched);
		break;
	}

	case CKM_RC4:
	{
		ARCFour_key *key = (ARCFour_key *)session_p->encrypt.context;
		bzero(key, sizeof (*key));
		*pulLastEncryptedPartLen = 0;
		break;
	}
	default:
		/* PKCS11: The mechanism only supports single-part operation. */
		rv = CKR_MECHANISM_INVALID;
		break;
	}

	free(session_p->encrypt.context);
	session_p->encrypt.context = NULL;
clean1:
	(void) pthread_mutex_unlock(&session_p->session_mutex);

	return (rv);
}

/*
 * This function frees the allocated active crypto context and the
 * lower level of allocated struct as needed.
 * This function is called by the 1st tier of encrypt/decrypt routines
 * or by the 2nd tier of session close routine. Since the 1st tier
 * caller will always call this function without locking the session
 * mutex and the 2nd tier caller will call with the lock, we add the
 * third parameter "lock_held" to distiguish this case.
 */
void
soft_crypt_cleanup(soft_session_t *session_p, boolean_t encrypt,
	boolean_t lock_held)
{

	crypto_active_op_t *active_op;
	boolean_t lock_true = B_TRUE;

	if (!lock_held)
		(void) pthread_mutex_lock(&session_p->session_mutex);

	active_op = (encrypt) ? &(session_p->encrypt) : &(session_p->decrypt);

	switch (active_op->mech.mechanism) {

	case CKM_DES_CBC_PAD:
	case CKM_DES3_CBC_PAD:
	case CKM_DES_CBC:
	case CKM_DES_ECB:
	case CKM_DES3_CBC:
	case CKM_DES3_ECB:
	{

		soft_des_ctx_t *soft_des_ctx =
		    (soft_des_ctx_t *)active_op->context;
		des_ctx_t *des_ctx;

		if (soft_des_ctx != NULL) {
			des_ctx = (des_ctx_t *)soft_des_ctx->des_cbc;
			if (des_ctx != NULL) {
				bzero(des_ctx->dc_keysched,
				    des_ctx->dc_keysched_len);
				free(soft_des_ctx->des_cbc);
			}
			bzero(soft_des_ctx->key_sched,
			    soft_des_ctx->keysched_len);
			free(soft_des_ctx->key_sched);
		}
		break;
	}

	case CKM_AES_CBC_PAD:
	case CKM_AES_CBC:
	case CKM_AES_ECB:
	{
		soft_aes_ctx_t *soft_aes_ctx =
		    (soft_aes_ctx_t *)active_op->context;
		aes_ctx_t *aes_ctx;

		if (soft_aes_ctx != NULL) {
			aes_ctx = (aes_ctx_t *)soft_aes_ctx->aes_cbc;
			if (aes_ctx != NULL) {
				bzero(aes_ctx->ac_keysched,
				    aes_ctx->ac_keysched_len);
				free(soft_aes_ctx->aes_cbc);
			}
			bzero(soft_aes_ctx->key_sched,
			    soft_aes_ctx->keysched_len);
			free(soft_aes_ctx->key_sched);
		}
		break;
	}

	case CKM_BLOWFISH_CBC:
	{
		soft_blowfish_ctx_t *soft_blowfish_ctx =
		    (soft_blowfish_ctx_t *)active_op->context;
		blowfish_ctx_t *blowfish_ctx;

		if (soft_blowfish_ctx != NULL) {
			blowfish_ctx =
			    (blowfish_ctx_t *)soft_blowfish_ctx->blowfish_cbc;
			if (blowfish_ctx != NULL) {
				bzero(blowfish_ctx->bc_keysched,
				    blowfish_ctx->bc_keysched_len);
				free(soft_blowfish_ctx->blowfish_cbc);
			}

			bzero(soft_blowfish_ctx->key_sched,
			    soft_blowfish_ctx->keysched_len);
			free(soft_blowfish_ctx->key_sched);
		}
		break;
	}

	case CKM_RC4:
	{
		ARCFour_key *key = (ARCFour_key *)active_op->context;

		if (key != NULL)
			bzero(key, sizeof (*key));
		break;
	}

	case CKM_RSA_X_509:
	case CKM_RSA_PKCS:
		break;

	} /* switch */

	if (active_op->context != NULL) {
		free(active_op->context);
		active_op->context = NULL;
	}

	active_op->flags = 0;

	if (!lock_held)
		SES_REFRELE(session_p, lock_true);
}