/* ***************************************************************** *
 * 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 "ibmcylinkcsp.hpp"
#include "key.hpp"
#include "deskey.hpp"
#include "tdeskey.hpp"
#include "saferkey.hpp"
#include "dsakey.hpp"
#include "dhkey.hpp"

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

/**********************************************************
 *
 *	Key class
 *
 **********************************************************/
Key::Key(CSSM_GUID *cspGuid, uint32 algId, uint32 keyClass, uint32 usage, uint32 attr, CSSM_DATE *start, CSSM_DATE *end) {
	KeySetup(cspGuid, algId, keyClass, usage, attr, start, end);
}

Key::Key(const CSSM_KEY_PTR key)
{
	if (key == NULL)
		throw InputException(CSSM_CSP_INVALID_KEY_POINTER);

	KeySetup(&key->KeyHeader.CspId,
		key->KeyHeader.AlgorithmId,
		key->KeyHeader.KeyClass, 
		key->KeyHeader.KeyUsage, 
		key->KeyHeader.KeyAttr,
		&key->KeyHeader.StartDate,
		&key->KeyHeader.EndDate);

	//*** There will be some memory leaks here!
	if (key->KeyData.Data != NULL) {
		keyMat = instantiateKeyMat();
		keyMat->importCssmKey(key);
	/*	key->KeyData.Length = getSizeInBits() / 8;
		key->KeyData.Data = new uint8[key->KeyData.Length];
		*/
	}
}

Key::Key(const Key& key) {
	cspGuid = key.cspGuid;
	algId = key.algId;
	keyClass = key.keyClass;
	usage = key.usage;
	attr = key.attr;
	startDate = key.startDate;
	endDate = key.endDate;
	if (key.keyMat != NULL)
		keyMat = key.keyMat->clone();
	else 
		keyMat = NULL;
}

Key::~Key() {
	if (keyMat != NULL) {
		delete keyMat;
	}
}

void Key::KeySetup(CSSM_GUID *_cspGuid, uint32 _algId, uint32 _keyClass,
				   uint32 _usage, uint32 _attr, CSSM_DATE *_start, CSSM_DATE *_end)
{
	//set up header
	memcpy(&cspGuid, _cspGuid, sizeof(CSSM_GUID));
	algId = _algId;
	keyClass = _keyClass;
	usage = _usage;
	attr = _attr;
	memcpy(&startDate, _start, sizeof(CSSM_DATE));
	memcpy(&endDate, _end, sizeof(CSSM_DATE));

	//create blob -> do it now ? What about doing it after generation
	//instantiateKeyMat()
	keyMat = NULL;
}

KeyMaterial* Key::instantiateKeyMat() {
	KeyMaterial*	keyMat = NULL;

	switch (algId) {
	case CSSM_ALGID_DES:
		if ((keyClass == CSSM_KEYCLASS_SESSION_KEY) || 
			(keyClass == CSSM_KEYCLASS_SECRET_PART)) 
			keyMat = new DESKey();
		break;
	case CSSM_ALGID_3DES_3KEY:
	case CSSM_ALGID_3DES:
	case CSSM_ALGID_3DES_2KEY:
	case CSSM_ALGID_3DES_1KEY:
		if ((keyClass == CSSM_KEYCLASS_SESSION_KEY) || 
			(keyClass == CSSM_KEYCLASS_SECRET_PART))
			keyMat = new TDESKey(algId);
		break;
	case CSSM_ALGID_SAFER:
		if ((keyClass == CSSM_KEYCLASS_SESSION_KEY) || 
			(keyClass == CSSM_KEYCLASS_SECRET_PART))
			keyMat = new SAFERKey();
		break;
	case CSSM_ALGID_DSA:
		if (keyClass == CSSM_KEYCLASS_PRIVATE_KEY) {
			keyMat = new DSAPrivateKey();
		}
		else if (keyClass == CSSM_KEYCLASS_PUBLIC_KEY) {
			keyMat = new DSAPublicKey();
		}
		break;
	case CSSM_ALGID_DH:
		if (keyClass == CSSM_KEYCLASS_PRIVATE_KEY) {
			keyMat = new DHPrivateKey();
		}
		else if (keyClass == CSSM_KEYCLASS_PUBLIC_KEY) {
			keyMat = new DHPublicKey();
		}
		break;
	default:
		throw InputException(CSSM_CSP_UNKNOWN_ALGORITHM);
		break;
	}

	if (keyMat == NULL)
		throw InputException(CSSM_CSP_INVALID_KEYCLASS);

	return(keyMat);
}

CSSM_GUID*	Key::getCspId() {
	CSSM_GUID	*tmpGuid = new CSSM_GUID;

	memcpy(tmpGuid, &cspGuid, sizeof(CSSM_GUID));

	return(tmpGuid);
}

uint32 Key::getAlgId() {
	return algId;
}

void Key::setKeyClass(uint32 _keyClass) {
	keyClass = _keyClass;
}
uint32 Key::getKeyClass() {
	return keyClass;
}

void Key::setUsage(uint32 _usage) {
	usage = _usage;
}
uint32 Key::getUsage() {
	return usage;
}

void Key::setAttr(uint32 _attr) {
	attr = _attr;
}
uint32 Key::getAttr() {
	return attr;
}

void Key::setStartDate(CSSM_DATE *_startDate) {
	memcpy(&startDate, _startDate, sizeof(CSSM_DATE));
}

CSSM_DATE_PTR Key::getStartDate() {
	CSSM_DATE_PTR	tmp;

	tmp = new CSSM_DATE;
	memcpy(tmp, &startDate, sizeof(CSSM_DATE));
	return (tmp);
}

void Key::setEndDate(CSSM_DATE *_endDate) {
	memcpy(&endDate, _endDate, sizeof(CSSM_DATE));
}

CSSM_DATE_PTR Key::getEndDate() {
	CSSM_DATE_PTR	tmp;

	tmp = new CSSM_DATE;
	memcpy(tmp, &endDate, sizeof(CSSM_DATE));
	return (tmp);
}

uint32 Key::getBlobType() {
	if (keyMat == NULL)
		//return(CSSM_KEYBLOB_RAW);
		throw ObjInitException(CSSM_CSP_KEY_BAD_KEY);

	return keyMat->getBlobType();
}

uint32 Key::getFormat() {
	if (keyMat == NULL)
		//return(CSSM_KEYBLOB_RAW_FORMAT_NONE);
		throw ObjInitException(CSSM_CSP_KEY_BAD_KEY);

	return keyMat->getFormat();
}

uint32 Key::getWrapAlgId() {
	if (keyMat == NULL)
		//return(CSSM_ALGID_NONE);
		throw ObjInitException(CSSM_CSP_KEY_BAD_KEY);

	return keyMat->getWrapAlgId();
}

uint32 Key::getWrapMode() {
	if (keyMat == NULL)
		//return(0);
		throw ObjInitException(CSSM_CSP_KEY_BAD_KEY);

	return keyMat->getWrapMode();
}

uint32 Key::getSizeInBits() {
	if (keyMat == NULL)
		//return(0);
		throw ObjInitException(CSSM_CSP_KEY_BAD_KEY);

	return (keyMat->getSizeInBits());
}

uint32 Key::getEffectiveSizeInBits() {
	if (keyMat == NULL)
		//return(0);
		throw ObjInitException(CSSM_CSP_KEY_BAD_KEY);

	return (keyMat->getEffectiveSizeInBits());
}

void Key::importKeyMat(KeyMaterial *material) {
	//check if this operation is permitted
	if (keyMat != NULL) {
		delete keyMat;
	}

	if (material != NULL)
		keyMat = material->clone();
	else 
		material = NULL;
}

//it is going to be part of keyGen
/*void Key::importAlgParams(AlgorithmParams *params) {
	//????? -> when do I instantiate the keyMat ?
	if (keyMat == NULL)
		throw ObjInitException(CSSM_CSP_KEY_BAD_KEY);
	
	keyMat->importAlgParams(params);
}*/

CSSM_KEY* Key::exportCssmPublicKey() {
	CSSM_KEY	*tmpKey = new CSSM_KEY;
	CSSM_GUID	tmpGuid = IBMCYLINKCSP_GUID;

	//it is exported in IbmCylinkCsp's internal format, so it is the same thing
	//as if it were generated by this CSP
	tmpKey->KeyHeader.CspId = tmpGuid;
	tmpKey->KeyHeader.AlgorithmId = algId;
	tmpKey->KeyHeader.KeyClass = keyClass;
	tmpKey->KeyHeader.KeyUsage = usage;
	tmpKey->KeyHeader.KeyAttr = attr;
	memcpy(&(tmpKey->KeyHeader.StartDate), &startDate, sizeof(CSSM_DATE));
	memcpy(&(tmpKey->KeyHeader.EndDate), &endDate, sizeof(CSSM_DATE));

	tmpKey->KeyHeader.BlobType = getBlobType();
	tmpKey->KeyHeader.Format = getFormat();
	tmpKey->KeyHeader.WrapAlgorithmId  = getWrapAlgId();
	tmpKey->KeyHeader.WrapMode = getWrapMode();

	if (keyMat == NULL) {
		tmpKey->KeyData.Length = 0;
		tmpKey->KeyData.Data = NULL;
	}
	else {
		CssmData	&tmpKeyMat = keyMat->exportPublicCssmData();

		tmpKey->KeyData.Length = tmpKeyMat.getLength();
		tmpKey->KeyData.Data = new uint8[tmpKey->KeyData.Length];
		memcpy(tmpKey->KeyData.Data, tmpKeyMat.getData(), tmpKey->KeyData.Length);
		delete &tmpKeyMat;
	}

	return(tmpKey);
}

CSSM_KEY* Key::exportCssmKey() {
	CSSM_KEY	*tmpKey = new CSSM_KEY;
	CSSM_GUID	tmpGuid = IBMCYLINKCSP_GUID;

	//it is exported in IbmCylinkCsp's internal format, so it is the same thing
	//as if it were generated by this CSP
	tmpKey->KeyHeader.CspId = tmpGuid;
	tmpKey->KeyHeader.AlgorithmId = algId;
	tmpKey->KeyHeader.KeyClass = keyClass;
	tmpKey->KeyHeader.KeyUsage = usage;
	tmpKey->KeyHeader.KeyAttr = attr;
	memcpy(&(tmpKey->KeyHeader.StartDate), &startDate, sizeof(CSSM_DATE));
	memcpy(&(tmpKey->KeyHeader.EndDate), &endDate, sizeof(CSSM_DATE));

	tmpKey->KeyHeader.BlobType = getBlobType();
	tmpKey->KeyHeader.Format = getFormat();
	tmpKey->KeyHeader.WrapAlgorithmId  = getWrapAlgId();
	tmpKey->KeyHeader.WrapMode = getWrapMode();

	if (keyMat == NULL) {
		tmpKey->KeyHeader.KeySizeInBits = 0;
		tmpKey->KeyData.Length = 0;
		tmpKey->KeyData.Data = NULL;
	}
	else {
		tmpKey->KeyHeader.KeySizeInBits = keyMat->getSizeInBits();
		CssmData	&tmpKeyMat = keyMat->exportCssmData();

		tmpKey->KeyData.Length = tmpKeyMat.getLength();
		tmpKey->KeyData.Data = new uint8[tmpKey->KeyData.Length];
		memcpy(tmpKey->KeyData.Data, tmpKeyMat.getData(), tmpKey->KeyData.Length);
		//free memory
		//tmpKeyMat.setLength(0);
		delete &tmpKeyMat;
	}

	return(tmpKey);
}

KeyMaterial* Key::exportKeyMat() {
	KeyMaterial	*tmp;
	if (keyMat == NULL)
		//return(NULL);
		throw ObjInitException(CSSM_CSP_KEY_BAD_KEY);

	tmp = keyMat->clone();
	if (tmp == NULL)
		//return (NULL);
		throw ObjInitException(CSSM_CSP_KEY_BAD_KEY);

	return(tmp);
}

void Key::destroy() {
	if (keyMat == NULL)
		return;

	keyMat->destroy();
	delete keyMat;
}

/**********************************************************
 *
 *	KeyMaterial class
 *
 **********************************************************/
KeyMaterial::KeyMaterial(uint32 _algId, uint32 _blobType, uint32 _format, uint32 _keyClass) {
	CSSM_GUID	tmpGuid	= IBMCYLINKCSP_GUID;

	cspGuid = tmpGuid;
	algId = _algId;
	blobType = _blobType;
	format = _format;
	keyClass = _keyClass;
	wrapAlgId = CSSM_ALGID_NONE;
	wrapMode = 0;
}

KeyMaterial::KeyMaterial(CSSM_GUID *_cspGuid, uint32 _algId, uint32 _blobType, uint32 _format, uint32 _keyClass) {
	cspGuid = *_cspGuid;
	algId = _algId;
	blobType = _blobType;
	format = _format;
	keyClass = _keyClass;
	wrapAlgId = CSSM_ALGID_NONE;
	wrapMode = 0;
}

KeyMaterial::~KeyMaterial() {
}

void KeyMaterial::importAlgParams(AlgorithmParams *params) {
	return;
}

uint32 KeyMaterial::getAlgId() {
	return (algId);
}

uint32 KeyMaterial::getBlobType() {
	return (blobType);
}

uint32 KeyMaterial::getFormat() {
	return (format);
}

uint32 KeyMaterial::getWrapAlgId() {
	return (wrapAlgId);
}

uint32 KeyMaterial::getWrapMode() {
	return (wrapMode);
}

uint32 KeyMaterial::getKeyClass() {
	return (keyClass);
}

void KeyMaterial::copy(KeyMaterial *material) {
	if (material == NULL)
		throw InputException(CSSM_CSP_KEY_BAD_KEY);

	if (material->getAlgId() != this->algId)
		throw InputException(CSSM_CSP_KEY_ALGID_NOTMATCH);

	material->blobType = blobType;
	material->format = format;
	material->wrapAlgId = wrapAlgId;
	material->wrapMode = wrapMode;
}

CssmData& KeyMaterial::exportPublicCssmData() {
	throw Exception(CSSM_CSP_INVALID_KEYCLASS);

	return(CssmData((uint32)0));
}


/**********************************************************
 *
 *	AlgorithmParams class
 *
 **********************************************************/
AlgorithmParams::AlgorithmParams(uint32 _algId) {
	algId = _algId;
}

AlgorithmParams::~AlgorithmParams() {
}

uint32	AlgorithmParams::getAlgId() {
	return(algId);
}

	
