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

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

/**********************************************************
 *
 *	DSAParams class
 *
 **********************************************************/
DSAParams::DSAParams(CssmData &_p, CssmData &_q, CssmData &_g)
:AlgorithmParams(CSSM_ALGID_DSA)
{
	if ((_q.getLength() != SHA_LENGTH) ||
		(_p.getLength() < DSS_LENGTH_MIN) || (_p.getLength() > DSS_LENGTH_MAX) ||
		(_p.getLength() != _g.getLength()))
	{
		throw InputException(CSSM_CSP_INPUT_DATA_ERROR);
	}

	p.setData(_p.getLength(), _p.getData());
	q.setData(_q.getLength(), _q.getData());
	g.setData(_g.getLength(), _g.getData());
}	

DSAParams::DSAParams(void* data, void** endOfData) 
:AlgorithmParams(CSSM_ALGID_DSA)
{
	uint8	*dataPtr = (uint8*)data;
	uint32	length;
	
	if (dataPtr == NULL)
		throw InputException(CSSM_CSP_INVALID_POINTER);
	
	length = (uint32)*dataPtr;
	if ((length < DSS_LENGTH_MIN) || (length > DSS_LENGTH_MAX)) 
		throw InputException(CSSM_CSP_INPUT_DATA_ERROR);

	dataPtr += sizeof(uint32);
	p.setData(length, dataPtr);
	dataPtr += p.getLength();

	length = (uint32)*dataPtr;
	if (length != SHA_LENGTH)
		throw InputException(CSSM_CSP_INPUT_DATA_ERROR);

	dataPtr += sizeof(uint32);
	q.setData(length, dataPtr);
	dataPtr +=q.getLength();

	length = (uint32)*dataPtr;
	if (length != p.getLength())
		throw InputException(CSSM_CSP_INPUT_DATA_ERROR);
	
	dataPtr += sizeof(uint32);
	g.setData(length, dataPtr);
	dataPtr += g.getLength();

	if (endOfData != NULL)
		*endOfData = dataPtr;
}

DSAParams::~DSAParams() {
	p.setLength(0);
	q.setLength(0);
	g.setLength(0);
}

CssmData& DSAParams::exportCssmData() {
	uint8	*dataPtr;
	uint32	length;
	CssmData *tmpParams =
		new CssmData(3 * sizeof(uint32) + p.getLength() + q.getLength() + g.getLength());

	dataPtr = tmpParams->getData();
	memcpy(dataPtr, &(length = p.getLength()), sizeof(uint32));
	dataPtr += sizeof(uint32);

	memcpy(dataPtr, p.getData(), p.getLength());
	dataPtr += p.getLength();

	memcpy(dataPtr, &(length = q.getLength()), sizeof(uint32));
	dataPtr += sizeof(uint32);

	memcpy(dataPtr, q.getData(), q.getLength());
	dataPtr += q.getLength();

	memcpy(dataPtr, &(length = g.getLength()), sizeof(uint32));
	dataPtr += sizeof(uint32);

	memcpy(dataPtr, g.getData(), g.getLength());

	return(*tmpParams);
}

uint32 DSAParams::getSizeInBits() {
	return((3 * sizeof(uint32) + p.getLength() + q.getLength() + g.getLength()) * 8);
}
uint32 DSAParams::getLength() {
	return(p.getLength());
}

CssmData& DSAParams::getP() {
	return(p.clone());
}

CssmData& DSAParams::getQ() {
	return(q.clone());
}

CssmData& DSAParams::getG() {
	return(g.clone());
}

void DSAParams::destroy() {
	p.setLength(0);
	q.setLength(0);
	g.setLength(0);
}

/**********************************************************
 *
 *	DSAPublicKey class
 *
 **********************************************************/
DSAPublicKey::DSAPublicKey()
:KeyMaterial(CSSM_ALGID_DSA, CSSM_KEYBLOB_RAW, CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYCLASS_PUBLIC_KEY)
{
	y.setDestroy(CSSM_TRUE);
	dsaParams = NULL;
}

DSAPublicKey::~DSAPublicKey() {
	destroy();
	y.setLength(0);
	if (dsaParams != NULL) {
		delete dsaParams;
	}
}

void* DSAPublicKey::importCssmKey(CSSM_KEY_PTR key) {
	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_DSA) ||
		(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);
	}

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

	uint8*	startOfPubKeyData;
	uint32	length;

	dsaParams = new DSAParams((void*)key->KeyData.Data, (void**)&startOfPubKeyData);
	
	length = (uint32)*startOfPubKeyData;
	startOfPubKeyData += sizeof(uint32);

	y.setData(length, startOfPubKeyData);
	startOfPubKeyData += y.getLength();

	return(startOfPubKeyData);
}

CssmData& DSAPublicKey::exportCssmData() {
	CssmData	&tmpParams = dsaParams->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);

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

	return(*tmpKey);
}

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

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

uint32 DSAPublicKey::getEffectiveSizeInBits() {
	return(y.getLength() * 8);
}

void DSAPublicKey::destroy() {
	if (dsaParams != NULL) {
		dsaParams->destroy();
	}

	y.destroy();
}

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

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

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

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

	tmpKeyMat = (DSAPublicKey*)material;
	KeyMaterial::copy(tmpKeyMat);
	
	if (this->dsaParams == NULL) {
		tmpKeyMat->dsaParams = NULL;
	}
	else {
		CssmData &p = this->dsaParams->getP();
		CssmData &q = this->dsaParams->getQ();
		CssmData &g = this->dsaParams->getG();

		tmpKeyMat->dsaParams = new DSAParams(p, q, g);
		delete &p;
		delete &q;
		delete &g;
	}

	tmpKeyMat->y.setData(this->y.getLength(), this->y.getData());
}

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

	if ((_y.getLength() < DSS_LENGTH_MIN) ||
		(_y.getLength() > DSS_LENGTH_MAX))
	{
		throw InputException(CSSM_CSP_INPUT_DATA_ERROR);
	}

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

CssmData& DSAPublicKey::getY() {
	return(y.clone());
}

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

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

	DSAParams *tmpParams = (DSAParams*)params;
	CssmData &tmpP = tmpParams->getP();
	CssmData &tmpQ = tmpParams->getQ();
	CssmData &tmpG = tmpParams->getG();
	dsaParams = new DSAParams(tmpP, tmpQ, tmpG);
	delete &tmpP;
	delete &tmpQ;
	delete &tmpG;
}

DSAParams* DSAPublicKey::getParams() {
	if (dsaParams == NULL) {
		return(NULL);
	}

	CssmData &p = this->dsaParams->getP();
	CssmData &q = this->dsaParams->getQ();
	CssmData &g = this->dsaParams->getG();
	
	DSAParams *tmpParams = new DSAParams(p, q, g);
	delete &p;
	delete &q;
	delete &g;
	return(tmpParams);
}

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

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

uint32 DSAPrivateKey::getSizeInBits() {
	uint32	pubSize = DSAPublicKey::getSizeInBits();

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

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

void* DSAPrivateKey::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_INVALID_KEYCLASS);

	uint8*	startOfPrivKeyData = (uint8*)DSAPublicKey::importCssmKey(key);
	uint32	length;

	length = (uint32)*startOfPrivKeyData;
	startOfPrivKeyData += sizeof(uint32);

	x.setData(length, startOfPrivKeyData);
	startOfPrivKeyData += x.getLength();

	return(startOfPrivKeyData);
}

CssmData& DSAPrivateKey::exportPublicCssmData() {
	return(DSAPublicKey::exportCssmData());
}

CssmData& DSAPrivateKey::exportCssmData() {
	CssmData	&pubKey = DSAPublicKey::exportCssmData();
	CssmData	*privKey;
	uint32		length;

	privKey = new CssmData(pubKey.getLength() + sizeof(uint32) + x.getLength());

	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 DSAPrivateKey::destroy() {
	DSAPublicKey::destroy();

	x.destroy();
}

void DSAPrivateKey::setX(CssmData &_x) {
	//object is immutable
	if (this->x.getLength() != 0)
		throw InternalException(0);

	if (_x.getLength() != SHA_LENGTH) {
		throw InputException(CSSM_CSP_INPUT_DATA_ERROR);
	}

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

CssmData &DSAPrivateKey::getX() {
	return(x.clone());
}

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

	copy(material);
	return (material);
}

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

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

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

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

	DSAPublicKey::copy(material);

	tmpKeyMat = (DSAPrivateKey*)material;

	tmpKeyMat->x.setData(this->x.getLength(), this->x.getData());
}
