/*
 * 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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

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

#include <strings.h>
#include <cryptoutil.h>
#include <security/cryptoki.h>
#include <arcfour.h>
#include "softGlobal.h"
#include "softSession.h"
#include <des_impl.h>
#include <aes_impl.h>
#include <blowfish_impl.h>
#include "softDH.h"
#include "softObject.h"
#include "softKeystore.h"
#include "softKeystoreUtil.h"


static CK_MECHANISM_TYPE soft_mechanisms[] = {
	CKM_DES_CBC,
	CKM_DES_CBC_PAD,
	CKM_DES_ECB,
	CKM_DES_KEY_GEN,
	CKM_DES_MAC_GENERAL,
	CKM_DES_MAC,
	CKM_DES3_CBC,
	CKM_DES3_CBC_PAD,
	CKM_DES3_ECB,
	CKM_DES3_KEY_GEN,
	CKM_AES_CBC,
	CKM_AES_CBC_PAD,
	CKM_AES_ECB,
	CKM_AES_KEY_GEN,
	CKM_BLOWFISH_CBC,
	CKM_BLOWFISH_KEY_GEN,
	CKM_SHA_1,
	CKM_SHA_1_HMAC,
	CKM_SHA_1_HMAC_GENERAL,
	CKM_SHA256,
	CKM_SHA256_HMAC,
	CKM_SHA256_HMAC_GENERAL,
	CKM_SHA384,
	CKM_SHA384_HMAC,
	CKM_SHA384_HMAC_GENERAL,
	CKM_SHA512,
	CKM_SHA512_HMAC,
	CKM_SHA512_HMAC_GENERAL,
	CKM_SSL3_SHA1_MAC,
	CKM_MD5,
	CKM_MD5_HMAC,
	CKM_MD5_HMAC_GENERAL,
	CKM_SSL3_MD5_MAC,
	CKM_RC4,
	CKM_RC4_KEY_GEN,
	CKM_DSA,
	CKM_DSA_SHA1,
	CKM_DSA_KEY_PAIR_GEN,
	CKM_RSA_PKCS,
	CKM_RSA_PKCS_KEY_PAIR_GEN,
	CKM_RSA_X_509,
	CKM_MD5_RSA_PKCS,
	CKM_SHA1_RSA_PKCS,
	CKM_SHA256_RSA_PKCS,
	CKM_SHA384_RSA_PKCS,
	CKM_SHA512_RSA_PKCS,
	CKM_DH_PKCS_KEY_PAIR_GEN,
	CKM_DH_PKCS_DERIVE,
	CKM_MD5_KEY_DERIVATION,
	CKM_SHA1_KEY_DERIVATION,
	CKM_SHA256_KEY_DERIVATION,
	CKM_SHA384_KEY_DERIVATION,
	CKM_SHA512_KEY_DERIVATION,
	CKM_PBE_SHA1_RC4_128,
	CKM_PKCS5_PBKD2,
	CKM_SSL3_PRE_MASTER_KEY_GEN,
	CKM_TLS_PRE_MASTER_KEY_GEN,
	CKM_SSL3_MASTER_KEY_DERIVE,
	CKM_TLS_MASTER_KEY_DERIVE,
	CKM_SSL3_MASTER_KEY_DERIVE_DH,
	CKM_TLS_MASTER_KEY_DERIVE_DH,
	CKM_SSL3_KEY_AND_MAC_DERIVE,
	CKM_TLS_KEY_AND_MAC_DERIVE,
	CKM_TLS_PRF
};

/*
 * This is the table of CK_MECHANISM_INFO structs for the supported mechanisms.
 * The index for this table is the same as the one above for the same
 * mechanism.
 * The minimum and maximum sizes of the key for the mechanism can be measured
 * in bits or in bytes (i.e. mechanism-dependent). This table specifies the
 * supported range of key sizes in bytes; unless noted as in bits.
 */
static CK_MECHANISM_INFO soft_mechanism_info[] = {
	{DES_MINBYTES, DES_MAXBYTES,
		CKF_ENCRYPT|CKF_DECRYPT|
		CKF_WRAP|CKF_UNWRAP},		/* CKM_DES_CBC */
	{DES_MINBYTES, DES_MAXBYTES,
		CKF_ENCRYPT|CKF_DECRYPT|
		CKF_WRAP|CKF_UNWRAP},		/* CKM_DES_CBC_PAD */
	{DES_MINBYTES, DES_MAXBYTES,
		CKF_ENCRYPT|CKF_DECRYPT|
		CKF_WRAP|CKF_UNWRAP},		/* CKM_DES_ECB */
	{DES_MINBYTES, DES_MAXBYTES,
		CKF_GENERATE},			/* CKM_DES_KEY_GEN */
	{DES_MINBYTES, DES_MAXBYTES,
		CKF_SIGN|CKF_VERIFY},		/* CKM_DES_MAC_GENERAL */
	{DES_MINBYTES, DES_MAXBYTES,
		CKF_SIGN|CKF_VERIFY},		/* CKM_DES_MAC */
/*
 * Note that DES3 is allowed even if CRYPTO_UNLIMITED is not specified.
 * This is because 3DES has an exception to the Solaris PAC enforced
 * no crypto greater than 128 bit in core Solaris rule.
 * The actual key length of 3DES is 192, but most cryptographers regard
 * it to be effectively 112.
 */
	{DES3_MINBYTES, DES3_MAXBYTES,
		CKF_ENCRYPT|CKF_DECRYPT|
		CKF_WRAP|CKF_UNWRAP},		/* CKM_DES3_CBC */
	{DES3_MINBYTES, DES3_MAXBYTES,
		CKF_ENCRYPT|CKF_DECRYPT|
		CKF_WRAP|CKF_UNWRAP},		/* CKM_DES3_CBC_PAD */
	{DES3_MINBYTES, DES3_MAXBYTES,
		CKF_ENCRYPT|CKF_DECRYPT|
		CKF_WRAP|CKF_UNWRAP},		/* CKM_DES3_ECB */
	{DES3_MINBYTES, DES3_MAXBYTES,
		CKF_GENERATE},			/* CKM_DES3_KEY_GEN */
	{AES_MINBYTES, AES_MAXBYTES,
		CKF_ENCRYPT|CKF_DECRYPT|
		CKF_WRAP|CKF_UNWRAP},		/* CKM_AES_CBC */
	{AES_MINBYTES, AES_MAXBYTES,
		CKF_ENCRYPT|CKF_DECRYPT|
		CKF_WRAP|CKF_UNWRAP},		/* CKM_AES_CBC_PAD */
	{AES_MINBYTES, AES_MAXBYTES,
		CKF_ENCRYPT|CKF_DECRYPT|
		CKF_WRAP|CKF_UNWRAP},		/* CKM_AES_ECB */
	{AES_MINBYTES, AES_MAXBYTES,
		CKF_GENERATE},			/* CKM_AES_KEY_GEN */
	{BLOWFISH_MINBYTES, BLOWFISH_MAXBYTES,
		CKF_ENCRYPT|CKF_DECRYPT|
		CKF_WRAP|CKF_UNWRAP},		/* CKM_BLOWFISH_ECB */
	{BLOWFISH_MINBYTES, BLOWFISH_MAXBYTES,
		CKF_GENERATE},			/* CKM_BLOWFISH_KEY_GEN */
	{0, 0, CKF_DIGEST},			/* CKM_SHA_1 */
	{1, 64, CKF_SIGN|CKF_VERIFY},		/* CKM_SHA_1_HMAC */
	{1, 64, CKF_SIGN|CKF_VERIFY},		/* CKM_SHA_1_HMAC_GENERAL */
	{0, 0, CKF_DIGEST},			/* CKM_SHA256 */
	{1, 64, CKF_SIGN|CKF_VERIFY},		/* CKM_SHA256_HMAC */
	{1, 64, CKF_SIGN|CKF_VERIFY},		/* CKM_SHA256_HMAC_GENERAL */
	{0, 0, CKF_DIGEST},			/* CKM_SHA384 */
	{1, 128, CKF_SIGN|CKF_VERIFY},		/* CKM_SHA384_HMAC */
	{1, 128, CKF_SIGN|CKF_VERIFY},		/* CKM_SHA384_HMAC_GENERAL */
	{0, 0, CKF_DIGEST},			/* CKM_SHA512 */
	{1, 128, CKF_SIGN|CKF_VERIFY},		/* CKM_SHA512_HMAC */
	{1, 128, CKF_SIGN|CKF_VERIFY},		/* CKM_SHA512_HMAC_GENERAL */
	{1, 512, CKF_SIGN|CKF_VERIFY},		/* CKM_SSL3_SHA1_MAC */
	{0, 0, CKF_DIGEST},			/* CKM_MD5 */
	{1, 64, CKF_SIGN|CKF_VERIFY},		/* CKM_MD5_HMAC */
	{1, 64, CKF_SIGN|CKF_VERIFY},		/* CKM_MD5_HMAC_GENERAL */
	{1, 512, CKF_SIGN|CKF_VERIFY},		/* CKM_SSL3_MD5_MAC */
	{8, ARCFOUR_MAX_KEY_BITS, CKF_ENCRYPT|CKF_DECRYPT}, /* CKM_RC4; */
							    /* in bits  */
	{8, ARCFOUR_MAX_KEY_BITS, CKF_GENERATE }, /* CKM_RC4_KEY_GEN; in bits */
	{512, 1024, CKF_SIGN|CKF_VERIFY},	/* CKM_DSA; in bits */
	{512, 1024, CKF_SIGN|CKF_VERIFY},	/* CKM_DSA_SHA1; in bits */
	{512, 1024, CKF_GENERATE_KEY_PAIR},	/* CKM_DSA_KEY_PAIR_GEN; */
						/* in bits */
	{256, 4096, CKF_ENCRYPT|CKF_DECRYPT|
		CKF_SIGN|CKF_SIGN_RECOVER|
		CKF_WRAP|CKF_UNWRAP|
		CKF_VERIFY|CKF_VERIFY_RECOVER},	/* CKM_RSA_PKCS; in bits */
	{256, 4096, CKF_GENERATE_KEY_PAIR},	/* CKM_RSA_PKCS_KEY_PAIR_GEN; */
						/* in bits */
	{256, 4096, CKF_ENCRYPT|CKF_DECRYPT|
		CKF_SIGN|CKF_SIGN_RECOVER|
		CKF_WRAP|CKF_UNWRAP|
		CKF_VERIFY|CKF_VERIFY_RECOVER},	/* CKM_RSA_X_509 in bits */
	{256, 4096, CKF_SIGN|CKF_VERIFY},	/* CKM_MD5_RSA_PKCS in bits */
	{256, 4096, CKF_SIGN|CKF_VERIFY},	/* CKM_SHA1_RSA_PKCS in bits */
	{256, 4096, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA256_RSA_PKCS in bits */
	{256, 4096, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA384_RSA_PKCS in bits */
	{256, 4096, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA512_RSA_PKCS in bits */
	{MIN_DH_KEYLENGTH, MAX_DH_KEYLENGTH, CKF_GENERATE_KEY_PAIR},
						/* CKM_DH_PKCS_KEY_PAIR_GEN */
						/* in bits */
	{MIN_DH_KEYLENGTH, MAX_DH_KEYLENGTH, CKF_DERIVE},
						/* CKM_DH_PKCS_DERIVE; */
						/* in bits */
	{1, 16, CKF_DERIVE},			/* CKM_MD5_KEY_DERIVATION */
	{1, 20, CKF_DERIVE},			/* CKM_SHA1_KEY_DERIVATION */
	{1, 32, CKF_DERIVE},			/* CKM_SHA256_KEY_DERIVATION */
	{1, 48, CKF_DERIVE},			/* CKM_SHA384_KEY_DERIVATION */
	{1, 64, CKF_DERIVE},			/* CKM_SHA512_KEY_DERIVATION */
	{0, 0, CKF_GENERATE},			/* CKM_PBE_SHA1_RC4_128 */
	{0, 0, CKF_GENERATE},			/* CKM_PKCS5_PBKD2 */
	{48, 48, CKF_GENERATE},		/* CKM_SSL3_PRE_MASTER_KEY_GEN */
	{48, 48, CKF_GENERATE},		/* CKM_TLS_PRE_MASTER_KEY_GEN */
	{48, 48, CKF_DERIVE},		/* CKM_SSL3_MASTER_KEY_DERIVE */
	{48, 48, CKF_DERIVE},		/* CKM_TLS_MASTER_KEY_DERIVE */
	{48, 48, CKF_DERIVE},		/* CKM_SSL3_MASTER_KEY_DERIVE_DH */
	{48, 48, CKF_DERIVE},		/* CKM_TLS_MASTER_KEY_DERIVE_DH */
	{0, 0, CKF_DERIVE},		/* CKM_SSL3_KEY_AND_MAC_DERIVE */
	{0, 0, CKF_DERIVE},		/* CKM_TLS_KEY_AND_MAC_DERIVE */
	{0, 0, CKF_DERIVE}		/* CKM_TLS_PRF */
};

/*
 * Slot ID for softtoken is always 1. tokenPresent is ignored.
 * Also, only one slot is used.
 */
/*ARGSUSED*/
CK_RV
C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList,
    CK_ULONG_PTR pulCount)
{

	CK_RV rv;

	if (!softtoken_initialized)
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	if (pulCount == NULL) {
		return (CKR_ARGUMENTS_BAD);
	}

	if (pSlotList == NULL) {
		/*
		 * Application only wants to know the number of slots.
		 */
		*pulCount = 1;
		return (CKR_OK);
	}

	if ((*pulCount < 1) && (pSlotList != NULL)) {
		rv = CKR_BUFFER_TOO_SMALL;
	} else {
		pSlotList[0] = SOFTTOKEN_SLOTID;
		rv = CKR_OK;
	}

	*pulCount = 1;
	return (rv);
}


CK_RV
C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo)
{

	if (!softtoken_initialized)
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	if (pInfo == NULL)
		return (CKR_ARGUMENTS_BAD);

	/* Make sure the slot ID is valid */
	if (slotID != SOFTTOKEN_SLOTID)
		return (CKR_SLOT_ID_INVALID);

	/* Provide information about the slot in the provided buffer */
	(void) strncpy((char *)pInfo->slotDescription, SOFT_SLOT_DESCRIPTION,
	    64);
	(void) strncpy((char *)pInfo->manufacturerID, SOFT_MANUFACTURER_ID, 32);
	pInfo->flags = 0;
	if (soft_keystore_status(KEYSTORE_PRESENT)) {
		pInfo->flags |= CKF_TOKEN_PRESENT;
	}
	pInfo->hardwareVersion.major = HARDWARE_VERSION_MAJOR;
	pInfo->hardwareVersion.minor = HARDWARE_VERSION_MINOR;
	pInfo->firmwareVersion.major = FIRMWARE_VERSION_MAJOR;
	pInfo->firmwareVersion.minor = FIRMWARE_VERSION_MINOR;

	return (CKR_OK);
}


CK_RV
C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo)
{

	ulong_t	token_flag = 0;
	boolean_t pin_initialized = B_FALSE;
	char	*ks_cryptpin = NULL;
	CK_RV rv = CKR_OK;

	if (!softtoken_initialized)
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	/* Make sure the slot ID is valid */
	if (slotID != SOFTTOKEN_SLOTID)
		return (CKR_SLOT_ID_INVALID);

	if (pInfo == NULL)
		return (CKR_ARGUMENTS_BAD);

	if (!soft_keystore_status(KEYSTORE_VERSION_OK))
		return (CKR_DEVICE_REMOVED);

	/* Provide information about a token in the provided buffer */
	(void) strncpy((char *)pInfo->label, SOFT_TOKEN_LABEL, 32);
	(void) strncpy((char *)pInfo->manufacturerID, SOFT_MANUFACTURER_ID, 32);
	(void) strncpy((char *)pInfo->model, TOKEN_MODEL, 16);
	(void) strncpy((char *)pInfo->serialNumber, SOFT_TOKEN_SERIAL, 16);

	rv = soft_keystore_pin_initialized(&pin_initialized, &ks_cryptpin,
	    B_FALSE);
	if (rv != CKR_OK)
		return (rv);
	if (!pin_initialized)
		token_flag = CKF_USER_PIN_TO_BE_CHANGED;
	if (ks_cryptpin)
		free(ks_cryptpin);

	pInfo->flags = SOFT_TOKEN_FLAGS | token_flag;
	pInfo->ulMaxSessionCount = CK_EFFECTIVELY_INFINITE;
	pInfo->ulSessionCount = soft_session_cnt;
	pInfo->ulMaxRwSessionCount = CK_EFFECTIVELY_INFINITE;
	pInfo->ulRwSessionCount = soft_session_rw_cnt;
	pInfo->ulMaxPinLen = MAX_PIN_LEN;
	pInfo->ulMinPinLen = MIN_PIN_LEN;
	pInfo->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION;
	pInfo->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION;
	pInfo->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION;
	pInfo->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION;
	pInfo->hardwareVersion.major = HARDWARE_VERSION_MAJOR;
	pInfo->hardwareVersion.minor = HARDWARE_VERSION_MINOR;
	pInfo->firmwareVersion.major = FIRMWARE_VERSION_MAJOR;
	pInfo->firmwareVersion.minor = FIRMWARE_VERSION_MINOR;
	(void) memset(pInfo->utcTime, ' ', 16);

	return (CKR_OK);
}

/*ARGSUSED*/
CK_RV
C_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pReserved)
{
	if (!softtoken_initialized)
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	/*
	 * This is currently not implemented, however we could cause this
	 * to wait for the token files to appear if soft_token_present is
	 * false.
	 * However there is currently no polite and portable way to do that
	 * because we might not even be able to get to an fd to the
	 * parent directory, so instead we don't support any slot events.
	 */
	return (CKR_FUNCTION_NOT_SUPPORTED);
}


CK_RV
C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList,
    CK_ULONG_PTR pulCount)
{

	ulong_t i;
	ulong_t mechnum;

	if (!softtoken_initialized)
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	if (slotID != SOFTTOKEN_SLOTID)
		return (CKR_SLOT_ID_INVALID);

	mechnum = sizeof (soft_mechanisms) / sizeof (CK_MECHANISM_TYPE);

	if (pMechanismList == NULL) {
		/*
		 * Application only wants to know the number of
		 * supported mechanism types.
		 */
		*pulCount = mechnum;
		return (CKR_OK);
	}

	if (*pulCount < mechnum) {
		*pulCount = mechnum;
		return (CKR_BUFFER_TOO_SMALL);
	}

	for (i = 0; i < mechnum; i++) {
		pMechanismList[i] = soft_mechanisms[i];
	}

	*pulCount = mechnum;

	return (CKR_OK);
}


CK_RV
C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type,
    CK_MECHANISM_INFO_PTR pInfo)
{

	ulong_t i;
	ulong_t mechnum;

	if (!softtoken_initialized)
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	if (slotID != SOFTTOKEN_SLOTID)
		return (CKR_SLOT_ID_INVALID);

	if (pInfo == NULL) {
		return (CKR_ARGUMENTS_BAD);
	}

	mechnum = sizeof (soft_mechanisms) / sizeof (CK_MECHANISM_TYPE);
	for (i = 0; i < mechnum; i++) {
		if (soft_mechanisms[i] == type)
			break;
	}

	if (i == mechnum)
		/* unsupported mechanism */
		return (CKR_MECHANISM_INVALID);

	pInfo->ulMinKeySize = soft_mechanism_info[i].ulMinKeySize;
	pInfo->ulMaxKeySize = soft_mechanism_info[i].ulMaxKeySize;
	pInfo->flags = soft_mechanism_info[i].flags;

	return (CKR_OK);
}


/*ARGSUSED*/
CK_RV
C_InitToken(CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen,
    CK_UTF8CHAR_PTR pLabel)
{
	if (!softtoken_initialized)
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	return (CKR_FUNCTION_NOT_SUPPORTED);
}

/*ARGSUSED*/
CK_RV
C_InitPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
{
	if (!softtoken_initialized)
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	return (CKR_FUNCTION_NOT_SUPPORTED);
}


CK_RV
C_SetPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pOldPin,
    CK_ULONG ulOldPinLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewPinLen)
{

	soft_session_t *session_p;
	CK_RV rv;
	boolean_t lock_held = B_FALSE;

	if (!softtoken_initialized)
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	/*
	 * Obtain the session pointer. Also, increment the session
	 * reference count.
	 */
	rv = handle2session(hSession, &session_p);
	if (rv != CKR_OK)
		return (rv);

	if (!soft_keystore_status(KEYSTORE_VERSION_OK)) {
		SES_REFRELE(session_p, lock_held);
		return (CKR_DEVICE_REMOVED);
	}

	if ((ulOldPinLen < MIN_PIN_LEN) || (ulOldPinLen > MAX_PIN_LEN) ||
	    (ulNewPinLen < MIN_PIN_LEN) ||(ulNewPinLen > MAX_PIN_LEN)) {
		SES_REFRELE(session_p, lock_held);
		return (CKR_PIN_LEN_RANGE);
	}

	if ((pOldPin == NULL_PTR) || (pNewPin == NULL_PTR)) {
		/*
		 * We don't support CKF_PROTECTED_AUTHENTICATION_PATH
		 */
		SES_REFRELE(session_p, lock_held);
		return (CKR_ARGUMENTS_BAD);
	}

	/* check the state of the session */
	if ((session_p->state != CKS_RW_PUBLIC_SESSION) &&
	    (session_p->state != CKS_RW_USER_FUNCTIONS)) {
		SES_REFRELE(session_p, lock_held);
		return (CKR_SESSION_READ_ONLY);
	}

	rv = soft_setpin(pOldPin, ulOldPinLen, pNewPin, ulNewPinLen);

	SES_REFRELE(session_p, lock_held);
	return (rv);
}