/* ***************************************************************** *
 * 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 "dhkey.hpp"
#include "ibmcylinkcsp.hpp"

/**********************************************************
 *
 *	DHParams class
 *
 **********************************************************/
DHParams::DHParams(CssmData &_base, CssmData &_modulus) 
:AlgorithmParams(CSSM_ALGID_DH)
{
	if ((_base.getLength() < DH_LENGTH_MIN) ||
		(_base.getLength() % sizeof(ord)) ||
		(_base.getLength() != _modulus.getLength()))
	{
		throw InputException(CSSM_CSP_PARAM_INVALID_VALUE);
		return;
	}

	base.setData(_base.getLength(), _base.getData());
	modulus.setData(_modulus.getLength(), _modulus.getData());
}	

/**************************************************************
 * Sees the data as:			----------------
 *		bytes 0:3				|  baseLength   |
 *								----------------
 *		bytes 4:3 + baseLength	|  base Bytes   |
 *								----------------
 *		bytes 4 + baseLength:   | modulusLength |
 *			  7 + baseLength	----------------
 *		bytes 8 + baseLength:	| modulus Bytes |
 *			  7 + baseLength	----------------
 *			  + modulusLength
 **************************************************************/
DHParams::DHParams(void* data, void** endOfData) 
:AlgorithmParams(CSSM_ALGID_DH)
{
	uint8	*dataPtr;
	uint32	baseLength, modulusLength;

	if (dataPtr == NULL)
		throw InputException(CSSM_CSP_INVALID_POINTER);

	dataPtr = (uint8*)data;
	baseLength = (uint32)(*dataPtr);
	dataPtr = dataPtr + 4;
	if ((baseLength < DH_LENGTH_MIN) ||
		(baseLength % sizeof(ord)))
	{
		throw InputException(CSSM_CSP_CONVERSION_ERROR);
		return;
	}

	base.setData(baseLength, dataPtr);
	dataPtr = dataPtr + base.getLength();

	modulusLength = (uint32)*dataPtr;
	dataPtr = dataPtr + 4;
	if (modulusLength != baseLength) {
		throw InputException(CSSM_CSP_CONVERSION_ERROR);
		return;
	}

	modulus.setData(modulusLength, dataPtr);
	if (endOfData != NULL)
		*endOfData = dataPtr + modulusLength;
}

DHParams::~DHParams() {
	base.setLength(0);
	modulus.setLength(0);
}

CssmData& DHParams::exportCssmData() {
	CssmData*	cssmDataPtr;
	uint8*		currPtr;
	uint32		length;

	cssmDataPtr = new CssmData(sizeof(uint32) + base.getLength() +
							   sizeof(uint32) + modulus.getLength());
	currPtr = cssmDataPtr->getData();
	
	memcpy(currPtr, (uint8*)&(length = base.getLength()), sizeof(uint32));
	currPtr = currPtr + sizeof(uint32);

	memcpy(currPtr, base.getData(), base.getLength());
	currPtr = currPtr + base.getLength();

	memcpy(currPtr, (uint8*)&(length = modulus.getLength()), sizeof(uint32));
	currPtr = currPtr + sizeof(uint32);

	memcpy(currPtr, modulus.getData(), modulus.getLength());

	return(*cssmDataPtr);
}

uint32 DHParams::getSizeInBits() {
	return((sizeof(uint32) + base.getLength() +
		sizeof(uint32) + modulus.getLength()) * 8);
}

uint32 DHParams::getLength() {
	return(base.getLength());
}

CssmData& DHParams::getBase() {
	return(base.clone());
}

CssmData& DHParams::getModulus() {
	return(modulus.clone());
}

void DHParams::destroy() {
	base.setLength(0);
	modulus.setLength(0);
}

/**********************************************************
 *
 *	DHPublicKey class
 *
 **********************************************************/
DHPublicKey::DHPublicKey() 
:KeyMaterial(CSSM_ALGID_DH, CSSM_KEYBLOB_RAW, CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYCLASS_PUBLIC_KEY)
{
	dhParams = NULL;
	y.setDestroy(CSSM_TRUE);
}

DHPublicKey::~DHPublicKey() {
	destroy();

	y.setLength(0);

	if (dhParams != NULL) {
		delete dhParams;
	}
}

void* DHPublicKey::importCssmKey(CSSM_KEY_PTR key) {
	uint32		length;
	CSSM_GUID	tmpGuid = IBMCYLINKCSP_GUID;

	if (key == NULL)
		throw InputException(CSSM_CSP_INVALID_KEY_POINTER);

	if ((memcmp(&key->KeyHeader.CspId, &tmpGuid, sizeof(CSSM_GUID)) != 0) ||
		(key->KeyHeader.AlgorithmId != CSSM_ALGID_DH) ||
		(key->KeyHeader.BlobType != CSSM_KEYBLOB_RAW) ||
		(key->KeyHeader.Format != CSSM_KEYBLOB_RAW_FORMAT_NONE) ||
		((key->KeyHeader.KeyClass != CSSM_KEYCLASS_PUBLIC_KEY) &&
		(key->KeyHeader.KeyClass != CSSM_KEYCLASS_PRIVATE_KEY)))
	{
		throw InputException(CSSM_CSP_INPUT_DATA_ERROR);
		return(NULL);
	}

	//keys are immutable objects
	if ((y.getLength() != 0) || (dhParams != NULL))
		throw InternalException(CSSM_CSP_KEY_PROTECTED);

	uint8* startOfPubKeyData;
	dhParams = new DHParams((void*)key->KeyData.Data, (void**)&startOfPubKeyData);
	
	length = (uint32)*startOfPubKeyData;
	if (length != dhParams->getLength())
		throw InputException(CSSM_CSP_CONVERSION_ERROR);

	startOfPubKeyData += sizeof(uint32);
	y.setData(length, startOfPubKeyData);
	startOfPubKeyData += length;

	return(startOfPubKeyData);
}

CssmData& DHPublicKey::exportCssmData() {
	CssmData	&tmpParams = dhParams->exportCssmData();
	CssmData	*tmpKey;
	uint8		*currPtr;
	uint32		length;

	tmpKey = new CssmData(tmpParams.getLength() + sizeof(uint32) + y.getLength());

	currPtr = tmpKey->getData();

	memcpy(currPtr, tmpParams.getData(), tmpParams.getLength());
	currPtr += tmpParams.getLength();
	delete &tmpParams;

	memcpy(currPtr, &(length = y.getLength()), sizeof(uint32));
	currPtr += sizeof(uint32);

	if (y.getLength() != 0)
		memcpy(currPtr, y.getData(), y.getLength());

	return(*tmpKey);
}

uint32 DHPublicKey::getSizeInBits() {
	if ((dhParams == NULL) || (y.getLength() == 0))
		return(0);

	return((sizeof(uint32) + y.getLength()) * 8 + dhParams->getSizeInBits());
}

DHParams* DHPublicKey::getParams() {
	DHParams*	tmpParams;

	if (dhParams == NULL) {
		return(NULL);
	}

	CssmData &base = this->dhParams->getBase();
	CssmData &modulus = this->dhParams->getModulus();
	tmpParams = new DHParams(base, modulus);
	delete &base;
	delete &modulus;

	return(tmpParams);
}

uint32 DHPublicKey::getEffectiveSizeInBits() {
	if (dhParams == NULL)
		return(0);

	//this is the effective length of the params. in bytes
	return (dhParams->getLength() * 8);
}

void DHPublicKey::destroy() {
	if (dhParams != NULL) {
		dhParams->destroy();
	}

	y.setLength(0);
}

KeyMaterial* DHPublicKey::clone() {
	DHPublicKey *material = new DHPublicKey();

	copy(material);

	return (material);
}

void DHPublicKey::copy(KeyMaterial *material) {
	DHPublicKey	*tmpKeyMat;

	if (material == NULL)
		throw InternalException(CSSM_CSP_INVALID_POINTER);

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

	tmpKeyMat = (DHPublicKey*)material;
	KeyMaterial::copy(tmpKeyMat);
	
	if (this->dhParams == NULL) {
		tmpKeyMat->dhParams = NULL;
	}
	else {
		CssmData &base = this->dhParams->getBase();
		CssmData &modulus = this->dhParams->getModulus();

		tmpKeyMat->dhParams = new DHParams(base, modulus);
		delete &base;
		delete &modulus;
	}

	if (this->y.getLength() == 0) {
		tmpKeyMat->y.setLength(0);
	}
	else {
		tmpKeyMat->y.setData(this->y.getLength(), this->y.getData());
	}
}

CssmData& DHPublicKey::getY() {
	CssmData *tmpY = new CssmData(y.getLength(), y.getData());
	return(*tmpY);
}

void DHPublicKey::setY(CssmData &_y) {
	//object is immutable
	if (y.getLength() != NULL)
		throw InternalException(CSSM_CSP_KEY_PROTECTED);

	if (_y.getLength() < DH_LENGTH_MIN)
		InputException(CSSM_CSP_KEY_BAD_LENGTH);

	y.setData(_y.getLength(), _y.getData());
}

void DHPublicKey::importAlgParams(AlgorithmParams* params) {
	if (params == NULL)
		throw InputException(CSSM_CSP_INVALID_POINTER);
	
	if (params->getAlgId() != CSSM_ALGID_DH)
		throw InputException(CSSM_CSP_INVALID_ALGORITHM);

	if (dhParams != NULL) {
		dhParams->destroy();
		delete dhParams;
	}

	DHParams *tmpParams = (DHParams*)params;
	CssmData &tmpBase = tmpParams->getBase();
	CssmData &tmpModulus = tmpParams->getModulus();

	dhParams = new DHParams(tmpBase, tmpModulus);
	delete &tmpBase;
	delete &tmpModulus;
}

/**********************************************************
 *
 *	DHPrivateKey class
 *
 **********************************************************/
DHPrivateKey::DHPrivateKey()
:DHPublicKey()
{
	this->keyClass = CSSM_KEYCLASS_PRIVATE_KEY;
	x.setDestroy(CSSM_TRUE);
}

DHPrivateKey::~DHPrivateKey() {
	destroy();
	x.setLength(0);
}

uint32 DHPrivateKey::getSizeInBits() {
	uint32 pubSize = DHPublicKey::getSizeInBits();

	if ((pubSize == 0) || (x.getLength() == 0))
		return(0);

	return((sizeof(uint32) + x.getLength()) * 8 + pubSize);
}

void* DHPrivateKey::importCssmKey(CSSM_KEY_PTR key) {
	//keys are immutable objects
	if (x.getLength() != 0)
		throw InternalException(CSSM_CSP_KEY_PROTECTED);

	if (key == NULL)
		throw InputException(CSSM_CSP_INVALID_KEY_POINTER);

	if (key->KeyHeader.KeyClass != CSSM_KEYCLASS_PRIVATE_KEY)
		throw InputException(CSSM_CSP_KEY_ALGID_NOTMATCH);

	uint8* startOfPrivKeyData = (uint8*)DHPublicKey::importCssmKey(key);

	x.setData((uint32)*startOfPrivKeyData, startOfPrivKeyData + sizeof(uint32));

	startOfPrivKeyData += sizeof(uint32);
	startOfPrivKeyData += x.getLength();

	return(startOfPrivKeyData);
}

CssmData& DHPrivateKey::exportPublicCssmData() {
	return(DHPublicKey::exportCssmData());
}

CssmData& DHPrivateKey::exportCssmData() {
	CssmData	&pubKey = DHPublicKey::exportCssmData();
	CssmData	*privKey = new CssmData(pubKey.getLength() + sizeof(uint32) + x.getLength());
	uint32		length;

	memcpy(privKey->getData(), pubKey.getData(), pubKey.getLength());
	memcpy(privKey->getData() + pubKey.getLength(), &(length = x.getLength()), sizeof(uint32));
	memcpy(privKey->getData() + pubKey.getLength() + sizeof(uint32), x.getData(), x.getLength());

	delete &pubKey;
	return(*privKey);
}

void DHPrivateKey::destroy() {
	DHPublicKey::destroy();
	x.destroy();
}

CssmData& DHPrivateKey::getX() {
	CssmData *tmpX = new CssmData(x.getLength(), x.getData());
	return(*tmpX);
}

void DHPrivateKey::setX(CssmData &_x) {
	//object is immutable
	if (x.getLength() != 0)
		throw InternalException(CSSM_CSP_PRIKEY_ALREADY_EXIST);

	if (_x.getLength() < DH_LENGTH_MIN)
		throw InputException(CSSM_CSP_INPUT_DATA_ERROR);

	x.setData(_x.getLength(), _x.getData());
}

KeyMaterial* DHPrivateKey::clone() {
	DHPrivateKey *material = new DHPrivateKey();
	
	copy(material);
	return (material);
}

void DHPrivateKey::copy(KeyMaterial *material) 
{
	DHPrivateKey *tmpKeyMat;

	if (material == NULL)
		throw InternalException(CSSM_CSP_INPUT_DATA_ERROR);

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

	if (material->getKeyClass() != this->keyClass)
		throw InternalException(CSSM_CSP_INVALID_KEYCLASS);

	DHPublicKey::copy(material);

	tmpKeyMat = (DHPrivateKey*)material;
	tmpKeyMat->x.setData(this->x.getLength(), this->x.getData());
}
