/* ***************************************************************** *
 * Copyright 1998 International Business Machines Corporation. All   *
 * Rights Reserved.                                                  *
 *                                                                   *
 * Please read this carefully.  Your use of this reference           *
 * implementation of certain of the IETF public-key infrastructure   *
 * specifications ("Software") indicates your acceptance of the      *
 * following.  If you do not agree to the following, do not install  *
 * or use any of the Software.                                       *
 *                                                                   *
 * Permission to use, reproduce, distribute and create derivative    *
 * works from the Software ("Software Derivative Works"), and to     *
 * distribute such Software Derivative Works is hereby granted to    *
 * you by International Business Machines Corporation ("IBM").  This *
 * permission includes a license under the patents of IBM that are   *
 * necessarily infringed by your use of the Software as provided by  *
 * IBM.                                                              *
 *                                                                   *
 * IBM licenses the Software to you on an "AS IS" basis, without     *
 * warranty of any kind.  IBM HEREBY EXPRESSLY DISCLAIMS ALL         *
 * WARRANTIES OR CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING,   *
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS OF       *
 * MERCHANTABILITY, NON INFRINGEMENT AND FITNESS FOR A PARTICULAR    *
 * PURPOSE.  You are solely responsible for determining the          *
 * appropriateness of using this Software and assume all risks       *
 * associated with the use of this Software, including but not       *
 * limited to the risks of program errors, damage to or loss of      *
 * data, programs or equipment, and unavailability or interruption   *
 * of operations.                                                    *
 *                                                                   *
 * IBM WILL NOT BE LIABLE FOR ANY DIRECT DAMAGES OR FOR ANY SPECIAL, *
 * INCIDENTAL, OR  INDIRECT DAMAGES OR FOR ANY ECONOMIC              *
 * CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), EVEN   *
 * IF IBM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  IBM  *
 * will not be liable for the loss of, or damage to, your records or *
 * data, or any damages claimed by you based on a third party claim. *
 *                                                                   *
 * IBM wishes to obtain your feedback to assist in improving the     *
 * Software.  You grant IBM a world-wide, royalty-free right to use, *
 * copy, distribute, sublicense and prepare derivative works based   *
 * upon any feedback, including materials, error corrections,        *
 * Software Derivatives, enhancements, suggestions and the like that *
 * you provide to IBM relating to the Software (this does not        *
 * include products for which you charge a royalty and distribute to *
 * IBM under other terms and conditions).                            *
 *                                                                   *
 * You agree to distribute the Software and any Software Derivatives *
 * under a license agreement that: 1) is sufficient to notify all    *
 * licensees of the Software and Software Derivatives that IBM       *
 * assumes no liability for any claim that may arise regarding the   *
 * Software or Software Derivatives, and 2) that disclaims all       *
 * warranties, both express and implied, from IBM regarding the      *
 * Software and Software Derivatives.  (If you include this          *
 * Agreement with any distribution of the Software or Software       *
 * Derivatives you will have met this requirement.)  You agree that  *
 * you will not delete any copyright notices in the Software.        *
 *                                                                   *
 * This Agreement is the exclusive statement of your rights in the   *
 * Software as provided by IBM.   Except for the rights granted to   *
 * you in the second paragraph above, You are not granted any other  *
 * patent rights, including but not limited to the right to make     *
 * combinations of the Software with products that infringe IBM      *
 * patents. You agree to comply with all applicable laws and         *
 * regulations, including all export and import laws and regulation. *
 * This Agreement is governed by the laws of the State of New York.  *
 * This Agreement supersedes all other communications,               *
 * understandings or agreements we may have had prior to this        *
 * Agreement.                                                        *
 * ***************************************************************** */

#include "exception.hpp"
#include "cssmerr.h"
#include "misc.hpp"
#include "csp.hpp"
#include "context.hpp"

#include "cipher.hpp"
#include "sign.hpp"
#include "digest.hpp"
#include "random.hpp"
#include "keygen.hpp"
#include "dhderive.hpp"
#include "key.hpp"

#include "cssmtype.h"
#include "cssmapi.h"
#include "cssmerr.h"



extern CSP	*csp;
CSSM_GUID	guid = IBMCYLINKCSP_GUID;

CSSM_RETURN CSSMAPI QuerySize (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR Context,
	CSSM_BOOL Encrypt, uint32 QuerySizeCount,
	CSSM_QUERY_SIZE_DATA_PTR DataBlock) 
{
	CSSM_SetError(&guid, CSSM_CSP_QUERY_SIZE_UNKNOWN);
	return(CSSM_FAIL);
}


    /* Crypto Functions */
int CSSMAPI SignOrVerifyData (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR ContextPtr,
	const CSSM_DATA_PTR DataBufs, uint32 DataBufCount,
	CSSM_DATA_PTR Signature, CSSM_BOOL signOperation)
{
	CSSM_CONTEXT_ATTRIBUTE_PTR	keyAttr;
	CSSM_KEY_PTR				cssmKey;
	int							rc = CSSM_OK;
	Session						*session;
	uint32						signLength;

	if (ContextPtr == NULL) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT_POINTER);
		return(CSSM_FAIL);
	}

	//need algId and key
	if (ContextPtr->ContextType != CSSM_ALGCLASS_SIGNATURE) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT);
		return(CSSM_FAIL);
	}

	keyAttr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_KEY);
	if ((keyAttr == NULL) || (keyAttr->Attribute.Key == NULL)) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_KEY_POINTER);
		return(CSSM_FAIL);
	}
	cssmKey = keyAttr->Attribute.Key;

	try {
		csp->GetContext(CSPHandle, CCHandle);
		session = csp->GetSession(CSPHandle);
		Key	key = Key(cssmKey);
		Sign sign = Sign(CCHandle, ContextPtr->AlgorithmType);
		signLength = sign.getSignatureLength();
		if (signOperation == CSSM_TRUE) {
			if ((Signature != NULL) && (Signature->Data == NULL)) {
				Signature->Data = (uint8*)session->malloc(signLength);
				Signature->Length = signLength;
			}
			sign.sign(&key, NULL, DataBufs, DataBufCount, Signature);
			Signature->Length = signLength;
		}
		else {
			rc = sign.verify(&key, NULL, DataBufs, DataBufCount, Signature);
			if (rc == CSSM_FALSE) {
				CSSM_SetError(&guid, CSSM_CSP_INVALID_SIGNATURE);
			}
		}
	} catch (Exception &excp) {
		CSSM_SetError(&guid, excp.errCode);
		rc = CSSM_FAIL;
	} 

	return(rc);
}

CSSM_RETURN CSSMAPI SignData (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR ContextPtr,
	const CSSM_DATA_PTR DataBufs, uint32 DataBufCount,
	CSSM_DATA_PTR Signature)
{
	return((CSSM_RETURN)SignOrVerifyData(CSPHandle, CCHandle, ContextPtr,
			DataBufs, DataBufCount, Signature, CSSM_TRUE));
}

CSSM_BOOL CSSMAPI VerifyData (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR ContextPtr,
	const CSSM_DATA_PTR DataBufs, uint32 DataBufCount,
	const CSSM_DATA_PTR Signature)
{
	return((CSSM_BOOL)SignOrVerifyData(CSPHandle, CCHandle, ContextPtr,
			DataBufs, DataBufCount, Signature, CSSM_FALSE));
}

CSSM_RETURN CSSMAPI SignOrVerifyDataInit (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR ContextPtr,
	CSSM_BOOL signOperation)
{
	CSSM_CONTEXT_ATTRIBUTE_PTR	keyAttr;
	CSSM_KEY_PTR				cssmKey;
	Key							*key = NULL;
	Sign						*sign = NULL;
	CSSM_RETURN					rc = CSSM_OK;
	Session						*session;

	if (ContextPtr == NULL) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT_POINTER);
		return(CSSM_FAIL);
	}

	if (ContextPtr->ContextType != CSSM_ALGCLASS_SIGNATURE) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT);
		return(CSSM_FAIL);
	}

	keyAttr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_KEY);
	if ((keyAttr == NULL) || (keyAttr->Attribute.Key == NULL)) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_KEY_POINTER);
		return(CSSM_FAIL);
	}
	cssmKey = keyAttr->Attribute.Key;

	try {
		csp->GetContext(CSPHandle, CCHandle);
		session = csp->GetSession(CSPHandle);
		key = new Key(cssmKey);
		sign = new Sign(CCHandle, ContextPtr->AlgorithmType);

		//saves the context for subsequent calls to update and final
		session->AddContext(sign);

		if (signOperation == CSSM_TRUE) {
			sign->signInit(key, NULL);
		}
		else {
			sign->verifyInit(key, NULL);
		}
	} catch (Exception &excp) {
		CSSM_SetError(&guid, excp.errCode);
		rc = CSSM_FAIL;
	} 

	if (key != NULL)
		delete key;

	return(rc);
}

CSSM_RETURN CSSMAPI SignDataInit (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR Context)
{
	return(SignOrVerifyDataInit(CSPHandle, CCHandle, Context, CSSM_TRUE));
}

CSSM_RETURN CSSMAPI VerifyDataInit (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR Context) 
{
	return(SignOrVerifyDataInit(CSPHandle, CCHandle, Context, CSSM_FALSE));
}



CSSM_RETURN CSSMAPI SignOrVerifyDataUpdate (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_DATA_PTR DataBufs,
	uint32 DataBufCount, CSSM_BOOL signOperation)
{
	Sign						*sign = NULL;
	CSSM_RETURN					rc = CSSM_OK;
	Session						*session;

	try {
		session = csp->GetSession(CSPHandle);
		sign = (Sign*)session->GetContext(CCHandle);
		
		if (sign->getType() != CSSM_ALGCLASS_SIGNATURE)
			throw InternalException(CSSM_CSP_INVALID_CONTEXT);

		if (signOperation == CSSM_TRUE) {
			sign->signUpdate(DataBufs, DataBufCount);
		}
		else {
			sign->verifyUpdate(DataBufs, DataBufCount);
		}
	} catch (Exception &excp) {
		CSSM_SetError(&guid, excp.errCode);
		rc = CSSM_FAIL;
	} 

	return(rc);
}

CSSM_RETURN CSSMAPI SignDataUpdate (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_DATA_PTR DataBufs,
	uint32 DataBufCount)
{
	return(SignOrVerifyDataUpdate(CSPHandle, CCHandle, DataBufs,
		DataBufCount, CSSM_TRUE));
}

CSSM_RETURN CSSMAPI VerifyDataUpdate (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_DATA_PTR DataBufs,
	uint32 DataBufCount)
{
	return(SignOrVerifyDataUpdate(CSPHandle, CCHandle, DataBufs,
		DataBufCount, CSSM_FALSE));
}

int CSSMAPI SignOrVerifyDataFinal (CSSM_CSP_HANDLE CSPHandle,
  CSSM_CC_HANDLE CCHandle, CSSM_DATA_PTR Signature,
  CSSM_BOOL signOperation)
{
	Sign						*sign = NULL;
	int							rc = CSSM_OK;
	Session						*session;
	uint32						signLength;

	try {
		session = csp->GetSession(CSPHandle);
		sign = (Sign*)session->GetContext(CCHandle);

		if (sign->getType() != CSSM_ALGCLASS_SIGNATURE)
			throw InternalException(CSSM_CSP_INVALID_CONTEXT);

		signLength = sign->getSignatureLength();
		if (signOperation == CSSM_TRUE) {
			if ((Signature != NULL) && (Signature->Data == NULL)) {
				Signature->Data = (uint8*)session->malloc(signLength);
				Signature->Length = signLength;
			}
			sign->signFinal(Signature);
			Signature->Length = signLength;
		}
		else {
			rc = sign->verifyFinal(Signature);
			if (rc == CSSM_FALSE) {
				CSSM_SetError(&guid, CSSM_CSP_INVALID_SIGNATURE);
			}
		}
	} catch (Exception &excp) {
		CSSM_SetError(&guid, excp.errCode);
		rc = CSSM_FAIL;
	} 

	return(rc);
}

CSSM_RETURN CSSMAPI SignDataFinal (CSSM_CSP_HANDLE CSPHandle,
  CSSM_CC_HANDLE CCHandle, CSSM_DATA_PTR Signature)
{
	return((CSSM_RETURN)SignOrVerifyDataFinal(CSPHandle, CCHandle, Signature, CSSM_TRUE));
}

CSSM_BOOL CSSMAPI VerifyDataFinal (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_DATA_PTR Signature)
{
	return((CSSM_BOOL)SignOrVerifyDataFinal(CSPHandle, CCHandle, Signature, CSSM_FALSE));
}

CSSM_RETURN CSSMAPI DigestData (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR ContextPtr,
	const CSSM_DATA_PTR DataBufs, uint32 DataBufCount,
	CSSM_DATA_PTR DigestPtr)
{
	Session	*session = NULL;
	Digest	*digest = NULL;

	if (ContextPtr == NULL) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT_POINTER);
		return(CSSM_FAIL);
	}

	if (ContextPtr->ContextType != CSSM_ALGCLASS_DIGEST) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT_HANDLE);
		return(CSSM_FAIL);
	}

	try {
		//this only makes sure CSPHandle and CCHandle are good
		csp->GetContext(CSPHandle, CCHandle);
		session = csp->GetSession(CSPHandle);
		digest = new Digest(CCHandle, ContextPtr->AlgorithmType);

		//allocate memory if necessary
		if ((DigestPtr != NULL) && (DigestPtr->Data == NULL)) {
			DigestPtr->Length = digest->getDigestLength();
			DigestPtr->Data = (uint8*)session->malloc(DigestPtr->Length);
		}

		digest->digest(DataBufs, DataBufCount, DigestPtr);
		delete digest;
	} catch (Exception e) {
		CSSM_SetError(&guid, e.errCode);
		if (digest != NULL) delete digest;
		return(CSSM_FAIL);
	}

	return(CSSM_OK);
}

CSSM_RETURN CSSMAPI DigestDataInit (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR Context)
{
	Session	*session = NULL;
	Digest	*digest = NULL;

	if (Context == NULL) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT_POINTER);
		return(CSSM_FAIL);
	}

	if (Context->ContextType != CSSM_ALGCLASS_DIGEST) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT_HANDLE);
		return(CSSM_FAIL);
	}

	try {
		//this only makes sure CSPHandle and CCHandle are good
		csp->GetContext(CSPHandle, CCHandle);
		session = csp->GetSession(CSPHandle);
		digest = new Digest(CCHandle, Context->AlgorithmType);
		if (session->GetContext(CCHandle) != NULL)
			session->RemoveContext(CCHandle);

		//saves the context for subsequent calls to update and final
		session->AddContext(digest);

		digest->digestInit();
	} catch (Exception e) {
		CSSM_SetError(&guid, e.errCode);
		if (digest != NULL) delete digest;
		return(CSSM_FAIL);
	}

	return(CSSM_OK);
}

CSSM_RETURN CSSMAPI DigestDataUpdate (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_DATA_PTR DataBufs,
	uint32 DataBufCount)
{
	Digest	*digest = NULL;

	try {
		digest = (Digest*)csp->GetContext(CSPHandle, CCHandle);
		if (digest->getType() != CSSM_ALGCLASS_DIGEST)
			throw InternalException(CSSM_CSP_INVALID_CONTEXT);

		digest->digestUpdate(DataBufs, DataBufCount);
	} catch (Exception e) {
		CSSM_SetError(&guid, e.errCode);
		return(CSSM_FAIL);
	}

	return(CSSM_OK);
}

CSSM_RETURN CSSMAPI DigestDataFinal (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, CSSM_DATA_PTR DigestPtr)
{
	Digest	*digest = NULL;
	Session	*session = NULL;

	try {
		session = csp->GetSession(CSPHandle);
		digest = (Digest*)session->GetContext(CCHandle);
		if (digest->getType() != CSSM_ALGCLASS_DIGEST)
			throw InternalException(CSSM_CSP_INVALID_CONTEXT);

		//allocate memory if necessary
		if ((DigestPtr != NULL) && (DigestPtr->Data == NULL)) {
			DigestPtr->Length = digest->getDigestLength();
			DigestPtr->Data = (uint8*)session->malloc(DigestPtr->Length);
		}

		digest->digestFinal(DigestPtr);
	} catch (Exception e) {
		CSSM_SetError(&guid, e.errCode);
		return(CSSM_FAIL);
	}

	return(CSSM_OK);
}

CSSM_RETURN CSSMAPI EncryptOrDecryptData (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR ContextPtr,
	const CSSM_DATA_PTR InBufs, uint32 InBufCount,
	CSSM_DATA_PTR OutBufs, uint32 OutBufCount,
	uint32 *bytesProcessed,	CSSM_DATA_PTR RemData,
	CSSM_BOOL encrypt)
{
	CSSM_CONTEXT_ATTRIBUTE_PTR	attr;
	CSSM_KEY_PTR				cssmKey;
	Key							*key = NULL;
	Cipher						*cipher = NULL;
	CSSM_RETURN					rc = CSSM_OK;
	Session						*session;
	CSSM_DATA					*IVPtr = NULL;
	uint32						mode = 0;

	if (ContextPtr == NULL) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT_POINTER);
		return(CSSM_FAIL);
	}

	if (ContextPtr->ContextType == CSSM_ALGCLASS_SYMMETRIC) {
		attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_INIT_VECTOR);
		if ((attr != NULL) || (attr->Attribute.Data != NULL)) {
			if ((attr->Attribute.Data->Length == 0) || (attr->Attribute.Data->Data == NULL)) {
				CSSM_SetError(&guid, (encrypt == CSSM_TRUE) ? CSSM_CSP_ENC_IV_ERROR : CSSM_CSP_DEC_IV_ERROR);
				return(CSSM_FAIL);
			}
			IVPtr = attr->Attribute.Data;
		}

		attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_MODE);
		if (attr != NULL) {
			mode = attr->Attribute.Uint32;
		}
	}
	else {
		if (ContextPtr->ContextType != CSSM_ALGCLASS_ASYMMETRIC) {
			CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT);
			return(CSSM_FAIL);
		}
	}

	attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_KEY);
	if ((attr == NULL) || (attr->Attribute.Key == NULL)) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_KEY_POINTER);
		return(CSSM_FAIL);
	}
	cssmKey = attr->Attribute.Key;

	try {
		csp->GetContext(CSPHandle, CCHandle);
		session = csp->GetSession(CSPHandle);
		cipher = new Cipher(CCHandle, ContextPtr->AlgorithmType, mode);
		key = new Key(cssmKey);

		//Allocate memory on behalf of the application, if necesary
		if (OutBufs->Data == NULL) {
			if (encrypt == CSSM_TRUE)
				OutBufs->Length = cipher->getCipherTextLength(InBufs->Length);
			else
				OutBufs->Length = cipher->getClearTextLength(InBufs->Length);

			OutBufs->Data = (uint8*)session->malloc(OutBufs->Length);
		}
		if (RemData->Data == NULL) {
			if (encrypt == CSSM_TRUE)
				RemData->Length = cipher->getCipherRemLength(InBufs->Length);
			else
				RemData->Length = cipher->getClearRemLength(InBufs->Length);

			RemData->Data = (uint8*)session->malloc(RemData->Length);
		}

		if (encrypt == CSSM_TRUE)
			cipher->encrypt(key, IVPtr, InBufs, InBufCount, OutBufs, OutBufCount, bytesProcessed, RemData);
		else
			cipher->decrypt(key, IVPtr, InBufs, InBufCount, OutBufs, OutBufCount, bytesProcessed, RemData);
	} catch (Exception &excp) {
		CSSM_SetError(&guid, excp.errCode);
		rc = CSSM_FAIL;
	} 

	if (key != NULL) delete key;
	if (cipher != NULL) delete cipher;
	return(rc);
}

CSSM_RETURN CSSMAPI EncryptData (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR ContextPtr,
	const CSSM_DATA_PTR ClearBufs, uint32 ClearBufCount,
	CSSM_DATA_PTR CipherBufs, uint32 CipherBufCount,
	uint32 *bytesEncrypted,	CSSM_DATA_PTR RemData)
{
	return(EncryptOrDecryptData(CSPHandle, CCHandle, ContextPtr,
		ClearBufs, ClearBufCount, CipherBufs, CipherBufCount, 
		bytesEncrypted, RemData, CSSM_TRUE));
}

CSSM_RETURN CSSMAPI DecryptData (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR ContextPtr,
	const CSSM_DATA_PTR CipherBufs, uint32 CipherBufCount,
	CSSM_DATA_PTR ClearBufs, uint32 ClearBufCount,
	uint32 *bytesDecrypted,	CSSM_DATA_PTR RemData)
{
	return(EncryptOrDecryptData(CSPHandle, CCHandle, ContextPtr,
		CipherBufs, CipherBufCount, ClearBufs, ClearBufCount, 
		bytesDecrypted, RemData, CSSM_FALSE));
}

CSSM_RETURN CSSMAPI EncryptOrDecryptDataInit (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR ContextPtr,
	CSSM_BOOL encrypt)
{
	CSSM_CONTEXT_ATTRIBUTE_PTR	attr;
	CSSM_KEY_PTR				cssmKey;
	Key							*key = NULL;
	Cipher						*cipher = NULL;
	CSSM_RETURN					rc = CSSM_OK;
	Session						*session;
	CSSM_DATA					*IVPtr = NULL;
	uint32						mode = 0;

	if (ContextPtr == NULL) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT_POINTER);
		return(CSSM_FAIL);
	}

	if (ContextPtr->ContextType == CSSM_ALGCLASS_SYMMETRIC) {
		attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_INIT_VECTOR);
		if ((attr != NULL) || (attr->Attribute.Data != NULL)) {
			if ((attr->Attribute.Data->Length == 0) || (attr->Attribute.Data->Data == NULL)) {
				CSSM_SetError(&guid, CSSM_CSP_ENC_IV_ERROR);
				return(CSSM_FAIL);
			}
			IVPtr = attr->Attribute.Data;
		}

		attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_MODE);
		if (attr != NULL) {
			mode = attr->Attribute.Uint32;
		}
	}
	else {
		if (ContextPtr->ContextType != CSSM_ALGCLASS_ASYMMETRIC) {
			CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT);
			return(CSSM_FAIL);
		}
	}

	attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_KEY);
	if ((attr == NULL) || (attr->Attribute.Key == NULL)) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_KEY_POINTER);
		return(CSSM_FAIL);
	}
	cssmKey = attr->Attribute.Key;

	try {
		csp->GetContext(CSPHandle, CCHandle);
		session = csp->GetSession(CSPHandle);
		cipher = new Cipher(CCHandle, ContextPtr->AlgorithmType, mode);
		key = new Key(cssmKey);

		//saves the context for subsequent calls to update and final
		session->AddContext(cipher);

		if (encrypt == CSSM_TRUE)
			cipher->encryptInit(key, IVPtr);
		else
			cipher->decryptInit(key, IVPtr);
	} catch (Exception &excp) {
		CSSM_SetError(&guid, excp.errCode);
		rc = CSSM_FAIL;
	} 

	if (key != NULL) delete key;
	return(rc);
}

CSSM_RETURN CSSMAPI EncryptDataInit (CSSM_CSP_HANDLE CSPHandle,
CSSM_CC_HANDLE CCHandle,
const CSSM_CONTEXT_PTR ContextPtr)
{
	return(EncryptOrDecryptDataInit(CSPHandle, CCHandle, ContextPtr, CSSM_TRUE));
}

CSSM_RETURN CSSMAPI DecryptDataInit (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle,
	const CSSM_CONTEXT_PTR ContextPtr)
{
	return(EncryptOrDecryptDataInit(CSPHandle, CCHandle, ContextPtr, CSSM_FALSE));
}

CSSM_RETURN CSSMAPI EncryptOrDecryptDataUpdate (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_DATA_PTR InBufs,
	uint32 InBufCount, CSSM_DATA_PTR OutBufs,
	uint32 OutBufCount, uint32 *bytesProcessed,
	CSSM_BOOL encrypt)
{	
	Cipher			*cipher = NULL;
	CSSM_RETURN		rc = CSSM_OK;
	Session			*session;

	try {
		session = csp->GetSession(CSPHandle);
		cipher = (Cipher*)session->GetContext(CCHandle);
		if ((cipher->getType() != CSSM_ALGCLASS_SYMMETRIC) &&
			(cipher->getType() != CSSM_ALGCLASS_ASYMMETRIC))
			throw InternalException(CSSM_CSP_INVALID_CONTEXT);

		//Allocate memory on behalf of the application, if necesary
		if (OutBufs->Data == NULL) {
			if (encrypt == CSSM_TRUE)
				OutBufs->Length = cipher->getCipherTextLength(InBufs->Length);
			else
				OutBufs->Length = cipher->getClearTextLength(InBufs->Length);
			OutBufs->Data = (uint8*)session->malloc(OutBufs->Length);
		}

		if (encrypt == CSSM_TRUE)
			cipher->encryptUpdate(InBufs, InBufCount, OutBufs, OutBufCount, bytesProcessed);
		else
			cipher->decryptUpdate(InBufs, InBufCount, OutBufs, OutBufCount, bytesProcessed);
	} catch (Exception &excp) {
		CSSM_SetError(&guid, excp.errCode);
		rc = CSSM_FAIL;
	} 

	return(rc);
}

CSSM_RETURN CSSMAPI EncryptDataUpdate (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_DATA_PTR ClearBufs,
	uint32 ClearBufCount, CSSM_DATA_PTR CipherBufs,
	uint32 CipherBufCount, uint32 *bytesEncrypted)
{	
	return(EncryptOrDecryptDataUpdate(CSPHandle, CCHandle,
		ClearBufs, ClearBufCount, CipherBufs, CipherBufCount,
		bytesEncrypted, CSSM_TRUE));
}

CSSM_RETURN CSSMAPI DecryptDataUpdate (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_DATA_PTR CipherBufs,
	uint32 CipherBufCount, CSSM_DATA_PTR ClearBufs,
	uint32 ClearBufCount, uint32 *bytesDecrypted)
{
	return(EncryptOrDecryptDataUpdate(CSPHandle, CCHandle,
		CipherBufs, CipherBufCount, ClearBufs, ClearBufCount,
		bytesDecrypted, CSSM_FALSE));
}

CSSM_RETURN CSSMAPI EncryptOrDecryptDataFinal (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, CSSM_DATA_PTR RemData,
	CSSM_BOOL encrypt)
{
	Cipher			*cipher = NULL;
	CSSM_RETURN		rc = CSSM_OK;
	Session			*session;

	try {
		session = csp->GetSession(CSPHandle);
		cipher = (Cipher*)session->GetContext(CCHandle);
		if ((cipher->getType() != CSSM_ALGCLASS_SYMMETRIC) &&
			(cipher->getType() != CSSM_ALGCLASS_ASYMMETRIC))
			throw InternalException(CSSM_CSP_INVALID_CONTEXT);

		//Allocate memory on behalf of the application, if necesary
		if (RemData->Data == NULL) {
			if (encrypt == CSSM_TRUE)
				RemData->Length = cipher->getCipherMaxRemLength();
			else
				RemData->Length = cipher->getClearMaxRemLength();
			RemData->Data = (uint8*)session->malloc(RemData->Length);
		}

		if (encrypt == CSSM_TRUE)
			cipher->encryptFinal(RemData);
		else
			cipher->decryptFinal(RemData);
	} catch (Exception &excp) {
		CSSM_SetError(&guid, excp.errCode);
		rc = CSSM_FAIL;
	} 

	return(rc);
}

CSSM_RETURN CSSMAPI EncryptDataFinal (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, CSSM_DATA_PTR RemData)
{
	return(EncryptOrDecryptDataFinal(CSPHandle, CCHandle, RemData, CSSM_TRUE));
}

CSSM_RETURN CSSMAPI DecryptDataFinal (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, CSSM_DATA_PTR RemData)
{
	return(EncryptOrDecryptDataFinal(CSPHandle, CCHandle, RemData, CSSM_FALSE));
}

CSSM_RETURN CSSMAPI QueryKeySizeInBits (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, CSSM_KEY_SIZE_PTR KeySize)
{
	CSSM_CONTEXT			*ContextPtr = NULL;
	CSSM_CONTEXT_ATTRIBUTE	*attr = NULL;
	CSSM_KEY				*cssmKey = NULL;
	Key						*key = NULL;
	CSSM_RETURN				rc = CSSM_OK;
	
	if (KeySize == NULL) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_POINTER);
		return(CSSM_FAIL);
	}

	if ((ContextPtr = CSSM_GetContext(CCHandle)) == NULL) {
		rc = CSSM_SetError(&guid, CSSM_INVALID_CONTEXT_HANDLE);
		return(CSSM_FAIL);
	}

	attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_KEY);
	if ((attr == NULL) || (attr->Attribute.Key == NULL)) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_KEY_POINTER);
		return(CSSM_FAIL);
	}
	cssmKey = attr->Attribute.Key;

	try {
		Session *session = csp->GetSession(CSPHandle);

		key = new Key(cssmKey);
		KeySize->KeySizeInBits = key->getSizeInBits();
		KeySize->EffectiveKeySizeInBits = key->getEffectiveSizeInBits();
	} catch (Exception e) {
		CSSM_SetError(&guid, e.errCode);
		rc = CSSM_FAIL;
	}

	if (key != NULL)
		delete key;
	if (ContextPtr != NULL)
		CSSM_FreeContext(ContextPtr);

	return(rc);
}

CSSM_RETURN CSSMAPI GenerateKey (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR ContextPtr,
	uint32 KeyUsage, uint32 KeyAttr, const CSSM_DATA_PTR KeyLabel,
	CSSM_KEY_PTR KeyPtr)
{
	Session					*session = NULL;
	uint32					keyLength = 0;
	CSSM_CONTEXT_ATTRIBUTE	*attr = NULL;
	CssmData				seed;
	Key						*key = NULL;
	KeyGen					*keyGen = NULL;
	CSSM_RETURN				rc = CSSM_OK;
	CSSM_KEY				*cssmKey = NULL;
	Random					*random = NULL;
	CSSM_GUID				tmpGuid = IBMCYLINKCSP_GUID;

	if (ContextPtr == NULL) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT_POINTER);
		return(CSSM_FAIL);
	}

	if (ContextPtr->ContextType != CSSM_ALGCLASS_KEYGEN) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT);
		return(CSSM_FAIL);
	}

	if (KeyPtr == NULL) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_KEY_POINTER);
		return(CSSM_FAIL);
	}

	attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_KEY_LENGTH);
	if (attr == NULL) {
		rc =  CSSM_SetError(&guid, CSSM_CSP_PARAM_NO_KEY_LENGTH);
		return(CSSM_FAIL);
	}
	keyLength = attr->Attribute.Uint32 / 8; //it is given in bits

	attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_SEED);
	if ((attr != NULL) && (attr->Attribute.Crypto)) {
		seed.setData(attr->Attribute.Crypto->Param);
		random = new Random(CCHandle, CSSM_ALGID_SHARandom);
		random->init(seed);
	}

	try {
		//this only makes sure CSPHandle and CCHandle are good
		csp->GetContext(CSPHandle, CCHandle);
		session = csp->GetSession(CSPHandle);

		//Don't enforce key usage at key generation time
		KeyPtr->KeyHeader.CspId = tmpGuid;
		KeyPtr->KeyHeader.AlgorithmId = ContextPtr->AlgorithmType;
		KeyPtr->KeyHeader.KeyClass = CSSM_KEYCLASS_SESSION_KEY;
		KeyPtr->KeyHeader.KeyUsage = KeyUsage;
		//this CSP doesn't care about Key Attribute
		KeyPtr->KeyHeader.KeyAttr = KeyAttr;

		key = new Key(KeyPtr);

		keyGen = new KeyGen(CCHandle, ContextPtr->AlgorithmType, random);
		keyGen->generateKeyMat(key, keyLength);
		delete keyGen;
		keyGen = NULL;

		KeyPtr->KeyHeader.KeySizeInBits = key->getEffectiveSizeInBits();
		cssmKey = key->exportCssmKey();
		if (KeyPtr->KeyData.Data != NULL) {
			if (KeyPtr->KeyData.Length != cssmKey->KeyData.Length) {
				CSSM_SetError(&guid, CSSM_CSP_ERR_OUTBUF_LENGTH);
				rc = CSSM_FAIL;
				goto CLEAN_UP;
			}
		} else {
			KeyPtr->KeyData.Length = cssmKey->KeyData.Length;
			KeyPtr->KeyData.Data = (uint8*)session->malloc(cssmKey->KeyData.Length);
			memcpy(KeyPtr->KeyData.Data, cssmKey->KeyData.Data, cssmKey->KeyData.Length);
		}
	} catch (Exception e) {
		CSSM_SetError(&guid, e.errCode);
		rc = CSSM_FAIL;
	}

CLEAN_UP:
	if (key != NULL) delete key;
	if (keyGen != NULL) delete keyGen;
	if (random != NULL) delete random;
	if (cssmKey != NULL) {
		if (cssmKey->KeyData.Data != NULL) {
			memset(cssmKey->KeyData.Data, '\0', cssmKey->KeyData.Length);
			cssmKey->KeyData.Length = 0;
			delete cssmKey->KeyData.Data;
		}
		delete cssmKey;
	}
	return(rc);
}

CSSM_RETURN CSSMAPI GenerateKeyPair (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR ContextPtr,
	uint32 PublicKeyUsage, uint32 PublicKeyAttr,
	const CSSM_DATA_PTR PublicKeyLabel, CSSM_KEY_PTR PublicKey,
	uint32 PrivateKeyUsage, uint32 PrivateKeyAttr,
	const CSSM_DATA_PTR PrivateKeyLabel, CSSM_KEY_PTR PrivateKey)
{
	Session					*session = NULL;
	uint32					keyLength = 0;
	CSSM_CONTEXT_ATTRIBUTE	*attr = NULL;
	CssmData				seed;
	Key						*privKey = NULL;
	KeyGen					*keyGen = NULL;
	CSSM_RETURN				rc = CSSM_OK;
	CSSM_KEY				*cssmPrivKey = NULL, *cssmPubKey = NULL;
	Random					*random = NULL;
	CSSM_DATA				*paramsPtr = NULL;
	AlgorithmParams			*params = NULL;
	CSSM_GUID				tmpGuid	= IBMCYLINKCSP_GUID;

	if (ContextPtr == NULL) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT_POINTER);
		return(CSSM_FAIL);
	}

	if (ContextPtr->ContextType != CSSM_ALGCLASS_KEYGEN) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT);
		return(CSSM_FAIL);
	}

	if ((PublicKey == NULL) || (PrivateKey == NULL)) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_KEY_POINTER);
		return(CSSM_FAIL);
	}

	attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_KEY_LENGTH);
	if (attr == NULL) {
		rc =  CSSM_SetError(&guid, CSSM_CSP_PARAM_NO_KEY_LENGTH);
		return(CSSM_FAIL);
	}
	keyLength = attr->Attribute.Uint32 / 8; //it is given in bits

	attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_SEED);
	if ((attr != NULL) && (attr->Attribute.Crypto)) {
		seed.setData(attr->Attribute.Crypto->Param);
		random = new Random(CCHandle, CSSM_ALGID_SHARandom);
		random->init(seed);
	}

	attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_ALG_PARAMS);
	if ((attr != NULL) && (attr->Attribute.Crypto)) {
		paramsPtr = attr->Attribute.Data;
	}

	try {
		//this only makes sure CSPHandle and CCHandle are good
		csp->GetContext(CSPHandle, CCHandle);
		session = csp->GetSession(CSPHandle);

		//Don't enforce key usage at key generation time
		PublicKey->KeyHeader.CspId = tmpGuid;
		PrivateKey->KeyHeader.CspId = tmpGuid;

		PublicKey->KeyHeader.AlgorithmId = ContextPtr->AlgorithmType;
		PrivateKey->KeyHeader.AlgorithmId = ContextPtr->AlgorithmType;

		PublicKey->KeyHeader.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY;
		PrivateKey->KeyHeader.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY;

		PublicKey->KeyHeader.KeyUsage = PublicKeyUsage;
		PrivateKey->KeyHeader.KeyUsage = PrivateKeyUsage;

		//this CSP doesn't care about Key Attribute
		PublicKey->KeyHeader.KeyAttr = PublicKeyAttr;
		PrivateKey->KeyHeader.KeyAttr = PrivateKeyAttr;
		
		privKey = new Key(PrivateKey);

		keyGen = new KeyGen(CCHandle, ContextPtr->AlgorithmType, random);

		//set the algorithm parameters
		if (paramsPtr == NULL) {
			params = keyGen->generateParams(&keyLength);
			//keyGen->generateParams(privKey, &keyLength);
		}
		else {
			if (ContextPtr->AlgorithmType == CSSM_ALGID_DSA) {
				params = new DSAParams(paramsPtr->Data, NULL);
				//privKey->importAlgParams(params);
			} else if (ContextPtr->AlgorithmType == CSSM_ALGID_DH) {
				params = new DHParams(paramsPtr->Data, NULL);
				//privKey->importAlgParams(params);
			}
		}

		keyGen->generateKeyMat(privKey, params);
		cssmPrivKey = privKey->exportCssmKey();
		cssmPubKey = privKey->exportCssmPublicKey();

		PublicKey->KeyHeader.KeySizeInBits = privKey->getEffectiveSizeInBits();
		PrivateKey->KeyHeader.KeySizeInBits = privKey->getEffectiveSizeInBits();

		//copy out the private key
		if (PrivateKey->KeyData.Data != NULL) {
			if (PrivateKey->KeyData.Length != cssmPrivKey->KeyData.Length) {
				CSSM_SetError(&guid, CSSM_CSP_ERR_OUTBUF_LENGTH);
				rc = CSSM_FAIL;
				goto CLEAN_UP;
			}
		} else {
			PrivateKey->KeyData.Length = cssmPrivKey->KeyData.Length;
			PrivateKey->KeyData.Data = (uint8*)session->malloc(cssmPrivKey->KeyData.Length);
			memcpy(PrivateKey->KeyData.Data, cssmPrivKey->KeyData.Data, cssmPrivKey->KeyData.Length);
		}

		//copy out the public key
		if (PublicKey->KeyData.Data != NULL) {
			if (PublicKey->KeyData.Length != cssmPubKey->KeyData.Length) {
				CSSM_SetError(&guid, CSSM_CSP_ERR_OUTBUF_LENGTH);
				rc = CSSM_FAIL;
				goto CLEAN_UP;
			}
		} else {
			PublicKey->KeyData.Length = cssmPubKey->KeyData.Length;
			PublicKey->KeyData.Data = (uint8*)session->malloc(cssmPubKey->KeyData.Length);
			memcpy(PublicKey->KeyData.Data, cssmPubKey->KeyData.Data, cssmPubKey->KeyData.Length);
		}

	} catch (Exception e) {
		CSSM_SetError(&guid, e.errCode);
		rc = CSSM_FAIL;
	}

CLEAN_UP:
	if (privKey != NULL) delete privKey;
	if (keyGen != NULL) delete keyGen;
	if (cssmPrivKey != NULL) {
		if (cssmPrivKey->KeyData.Data != NULL) {
			memset(cssmPrivKey->KeyData.Data, '\0', cssmPrivKey->KeyData.Length);
			cssmPrivKey->KeyData.Length = 0;
			delete cssmPrivKey->KeyData.Data;
		}
		delete cssmPrivKey;
	}
	if (cssmPubKey != NULL) {
		if (cssmPubKey->KeyData.Data != NULL) {
			memset(cssmPubKey->KeyData.Data, '\0', cssmPubKey->KeyData.Length);
			cssmPubKey->KeyData.Length = 0;
			delete cssmPubKey->KeyData.Data;
		}
		delete cssmPubKey;
	}
	if (random != NULL) delete random;
	if (params != NULL) delete params;

	return(rc);
}


CSSM_RETURN CSSMAPI GenerateRandom (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR Context,
	CSSM_DATA_PTR RandomNumber)
{
	CSSM_CONTEXT_ATTRIBUTE	*attrib;
	CssmData				seed;
	CssmData				randomData;
	CSSM_DATA_PTR			randomPtr = NULL;
	uint32					length;
	Random					*random = NULL;

	if (Context == NULL) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT_POINTER);
		return(CSSM_FAIL);
	}

	if (Context->ContextType != CSSM_ALGCLASS_RANDOMGEN) {
			CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT_HANDLE);
			return(CSSM_FAIL);
	}

	if (RandomNumber == NULL) {
			CSSM_SetError(&guid, CSSM_CSP_INVALID_DATA_POINTER);
			return(CSSM_FAIL);
	}

	attrib = CSSM_GetContextAttribute(Context, CSSM_ATTRIBUTE_OUTPUT_SIZE);
	if (attrib == NULL) {
		CSSM_SetError(&guid, CSSM_CSP_PARAM_NO_OUTPUT_SIZE);
		return(CSSM_FAIL);
	}
	length = attrib->Attribute.Uint32;

	try {
		//check the validity of CSPHandle and CCHandle
		csp->GetContext(CSPHandle, CCHandle);

		randomData.setLength(length);
		if (RandomNumber->Data == NULL) {
			Session* session = csp->GetSession(CSPHandle);
			RandomNumber->Data = (uint8*)session->calloc(1, length);
			RandomNumber->Length = length;
		}
		else if (RandomNumber->Length < length) {
			CSSM_SetError(&guid, CSSM_CSP_ERR_OUTBUF_LENGTH);
			return(CSSM_FAIL);
		}

		attrib = CSSM_GetContextAttribute(Context, CSSM_ATTRIBUTE_SEED);
		if ((attrib != NULL) && (attrib->Attribute.Crypto)) {
			seed.setData(attrib->Attribute.Crypto->Param);
		}

		random = new Random(CCHandle, Context->AlgorithmType);
		random->generateRandom(randomData, seed);
		memcpy(RandomNumber->Data, randomData.getData(), length);

		delete random;
		delete randomPtr;
	} catch (Exception e) {
		CSSM_SetError(&guid, e.errCode);
		if (random != NULL) delete random;
		if (randomPtr != NULL) delete randomPtr;
		return(CSSM_FAIL);
	}

	return(CSSM_OK);
}

CSSM_RETURN CSSMAPI GenerateAlgorithmParams (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR ContextPtr,
	uint32 ParamBits, CSSM_DATA_PTR ParamPtr)
{
	Session					*session = NULL;
	CssmData				seed;
	CSSM_CONTEXT_ATTRIBUTE	*attr;
	KeyGen					*keyGen = NULL; 
	Random					*random = NULL;
	AlgorithmParams			*params = NULL;
	CSSM_RETURN				rc = CSSM_OK;
	uint32					paramLength = 0;

	if (ContextPtr == NULL) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT_POINTER);
		return(CSSM_FAIL);
	}

	if (ContextPtr->ContextType != CSSM_ALGCLASS_KEYGEN) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT);
		return(CSSM_FAIL);
	}

	if (ParamPtr == NULL) {
			CSSM_SetError(&guid, CSSM_CSP_INVALID_DATA_POINTER);
			return(CSSM_FAIL);
	}

	attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_SEED);
	if ((attr != NULL) && (attr->Attribute.Crypto)) {
		seed.setData(attr->Attribute.Crypto->Param);
		random = new Random(CCHandle, CSSM_ALGID_SHARandom);
		random->init(seed);
	}

	try {
		csp->GetContext(CSPHandle, CCHandle);
		session = csp->GetSession(CSPHandle);
		keyGen = new KeyGen(CCHandle, ContextPtr->AlgorithmType, random);
		paramLength = ParamBits / 8;
		params = keyGen->generateParams(&paramLength);
		delete keyGen;
		keyGen = NULL;
		CssmData &p = params->exportCssmData();
		delete params;
		params = NULL;
		if (ParamPtr->Data == NULL) {
			ParamPtr->Length = p.getLength();
			ParamPtr->Data = (uint8*)session->malloc(ParamPtr->Length);
		}
		else if (ParamPtr->Length < p.getLength()) {
			CSSM_SetError(&guid, CSSM_CSP_ERR_OUTBUF_LENGTH);
			rc = CSSM_FAIL;
			goto CLEAN_UP;
		}
		memcpy(ParamPtr->Data, p.getData(), ParamPtr->Length);
		delete &p;
	} catch (Exception e) {
		CSSM_SetError(&guid, e.errCode);
		rc = CSSM_FAIL;
	}

CLEAN_UP:
	if (keyGen != NULL) delete keyGen; 
	if (random != NULL) delete random;
	if (params != NULL) delete params;

	return(rc);
}

CSSM_RETURN CSSMAPI DeriveKey (CSSM_CSP_HANDLE CSPHandle,
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR ContextPtr,
	const CSSM_KEY_PTR BaseKey, void *Param, uint32 KeyUsage,
	uint32 KeyAttr, const CSSM_DATA_PTR KeyLabel, CSSM_KEY_PTR DerivedKey)
{
	CSSM_CONTEXT_ATTRIBUTE	*attr;
	uint32					keyLength, keyAlgId;
	CSSM_RETURN				rc = CSSM_OK;
	CSSM_GUID				tmpGuid = IBMCYLINKCSP_GUID;
	Session					*session;

	if (ContextPtr == NULL) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT_POINTER);
		return(CSSM_FAIL);
	}

	if (ContextPtr->ContextType != CSSM_ALGCLASS_DERIVEKEY) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_CONTEXT);
		return(CSSM_FAIL);
	}

	if ((BaseKey == NULL) || (DerivedKey == NULL) || (Param == NULL)) {
		CSSM_SetError(&guid, CSSM_CSP_INVALID_KEY_POINTER);
		return(CSSM_FAIL);
	}

	attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_KEY_LENGTH);
	if (attr == NULL) {
		rc =  CSSM_SetError(&guid, CSSM_CSP_PARAM_NO_KEY_LENGTH);
		return(CSSM_FAIL);
	}
	keyLength = attr->Attribute.Uint32 / 8; //it is given in bits

	attr = CSSM_GetContextAttribute(ContextPtr, CSSM_ATTRIBUTE_KEY_TYPE);
	if (attr == NULL) {
		rc =  CSSM_SetError(&guid, CSSM_CSP_PARAM_NO_KEY_TYPE);
		return(CSSM_FAIL);
	}
	keyAlgId = attr->Attribute.Uint32;

	try {
		csp->GetContext(CSPHandle, CCHandle);
		session = csp->GetSession(CSPHandle);

		DHDeriveKey	deriveKey = DHDeriveKey(CCHandle, keyAlgId);
		Key			key1 = Key(BaseKey);
		Key			key2 = Key((CSSM_KEY*)Param);
		CssmData	sharedSecret = CssmData(keyLength);

		uint32 length;
		deriveKey.init(key1, key2);
		deriveKey.derive(sharedSecret, (void*)&(length = sharedSecret.getLength()));

		//setup the header of the derived key
		DerivedKey->KeyHeader.CspId = tmpGuid;
		DerivedKey->KeyHeader.BlobType = CSSM_KEYBLOB_RAW;
		DerivedKey->KeyHeader.Format = CSSM_KEYBLOB_RAW_FORMAT_NONE;
		DerivedKey->KeyHeader.AlgorithmId = keyAlgId;
		DerivedKey->KeyHeader.KeyClass = CSSM_KEYCLASS_SESSION_KEY;
		DerivedKey->KeyHeader.KeyUsage = KeyUsage;
		DerivedKey->KeyHeader.KeyAttr = KeyAttr;
		DerivedKey->KeyHeader.KeySizeInBits = keyLength; //??? DES
		DerivedKey->KeyHeader.WrapAlgorithmId = CSSM_ALGID_NONE;
		DerivedKey->KeyHeader.WrapMode = 0;
		
		DerivedKey->KeyData.Length = keyLength;
		if (DerivedKey->KeyData.Data == NULL) {
			DerivedKey->KeyData.Data = session->malloc(keyLength);
		}
		else if (DerivedKey->KeyData.Length < keyLength) {
			CSSM_SetError(&guid, CSSM_CSP_ERR_OUTBUF_LENGTH);
			rc = CSSM_FAIL;
			goto CLEAN_UP;
		}

		memcpy(DerivedKey->KeyData.Data, sharedSecret.getData(), keyLength);
	} catch (Exception e) {
		CSSM_SetError(&guid, e.errCode);
		rc = CSSM_FAIL;
	}

CLEAN_UP:
	return(rc);	
}


/* Expandability Functions */
void * CSSMAPI PassThrough (CSSM_CSP_HANDLE CSPHandle, 
	CSSM_CC_HANDLE CCHandle, const CSSM_CONTEXT_PTR Context,
	uint32 PassThroughId, const void * InData)
{
	return(NULL);
}

/* User Login Functions */

CSSM_RETURN CSSMAPI Login (CSSM_CSP_HANDLE CSPHandle,
	const CSSM_CRYPTO_DATA_PTR Password, const CSSM_DATA_PTR Reserved)
{
	return(CSSM_FAIL);
}

CSSM_RETURN CSSMAPI Logout (CSSM_CSP_HANDLE CSPHandle)
{
	return(CSSM_FAIL);
}

CSSM_RETURN CSSMAPI ChangeLoginPassword (CSSM_CSP_HANDLE CSPHandle,
	const CSSM_CRYPTO_DATA_PTR OldPassword, const CSSM_CRYPTO_DATA_PTR NewPassword)
{
	return(CSSM_FAIL);
}

/* Loading, Unloading and Event Notifications */
CSSM_RETURN CSSMAPI Initialize(CSSM_MODULE_HANDLE Handle,
	uint32 VerMajor, uint32 VerMinor) 
{
	CSSM_GUID guid = IBMCYLINKCSP_GUID;

	if ((VerMajor != IBMCYLINKCSP_MAJOR_VERSION) || (VerMinor != IBMCYLINKCSP_MINOR_VERSION)) {
		CSSM_SetError(&guid, CSSM_INCOMPATIBLE_VERSION);
		return(CSSM_FAIL);
	}
	return(CSSM_OK);
}

CSSM_RETURN CSSMAPI Terminate(CSSM_MODULE_HANDLE Handle) {
	return(CSSM_OK);
}

CSSM_RETURN CSSMAPI EventNotify(CSSM_MODULE_HANDLE Handle,
	const CSSM_EVENT_TYPE Event, const uint32 Param) 
{
	CSSM_RETURN	rc = CSSM_OK;


	try {
		switch (Event) {
			case CSSM_EVENT_ATTACH:
				//If a session with this handle already exists, and exception
				//is thrown
				csp->OpenSession(Handle);
				break;
			case CSSM_EVENT_DETACH:
				csp->CloseSession(Handle);
				break;
			case CSSM_EVENT_INFOATTACH:
				break;
			case CSSM_EVENT_INFODETACH:
				break;
			case CSSM_EVENT_CREATE_CONTEXT: {
				try {
					Context *context = csp->GetContext(Handle, Param);
					//If no exception is thrown, it means there already exists a
					//context wih the same context handle
				} catch (InputException e) {
					if (e.errCode != CSSM_CSP_INVALID_CONTEXT_HANDLE) {
						//some other error ocurred
						throw e;
					}

					//It is going to be created by one of the cryptographic functions, or I could create it here
					CSSM_CONTEXT	*cssmContext = CSSM_GetContext(Param);
					uint32			mode = 0;
					Session			*session;

					try {
						if (cssmContext == NULL)
							throw InternalException(CSSM_CSP_INVALID_CONTEXT_HANDLE);

						if (cssmContext->ContextType == CSSM_ALGCLASS_SYMMETRIC) {
							CSSM_CONTEXT_ATTRIBUTE *attrib;
							attrib = CSSM_GetContextAttribute(cssmContext, CSSM_ATTRIBUTE_MODE);
							if (attrib == NULL)
								throw InternalException(CSSM_CSP_INVALID_CONTEXT_HANDLE);
							mode = attrib->Attribute.Uint32;
						}

						session = csp->GetSession(Handle);
						session->AddContext(session->CreateContext(Param, cssmContext->ContextType, cssmContext->AlgorithmType, mode));
						rc =  CSSM_FreeContext(cssmContext);
						cssmContext = NULL;
					} catch (...) {
						if (cssmContext != NULL)
							CSSM_FreeContext(cssmContext);
						throw;
					}

					if (cssmContext != NULL)
						CSSM_FreeContext(cssmContext);
					break;
				}

				throw InputException(CSSM_CSP_INVALID_CONTEXT_HANDLE);
				break;
			}
			case CSSM_EVENT_DELETE_CONTEXT:
				csp->GetSession(Handle)->RemoveContext(Param);
				break;
		}
	} catch (Exception e) {
		CSSM_SetError(&guid, e.errCode);
		rc = CSSM_FAIL;
	} catch (...) {
		CSSM_SetError(&guid, CSSM_CSP_UNKNOWN_ERROR);
		rc = CSSM_FAIL;
	}

	return(rc);
}
