feePublicKey.c   [plain text]


/* Copyright (c) 1998 Apple Computer, Inc.  All rights reserved.
 *
 * NOTICE: USE OF THE MATERIALS ACCOMPANYING THIS NOTICE IS SUBJECT
 * TO THE TERMS OF THE SIGNED "FAST ELLIPTIC ENCRYPTION (FEE) REFERENCE
 * SOURCE CODE EVALUATION AGREEMENT" BETWEEN APPLE COMPUTER, INC. AND THE
 * ORIGINAL LICENSEE THAT OBTAINED THESE MATERIALS FROM APPLE COMPUTER,
 * INC.  ANY USE OF THESE MATERIALS NOT PERMITTED BY SUCH AGREEMENT WILL
 * EXPOSE YOU TO LIABILITY.
 ***************************************************************************
 *
 * feePublicKey.c - Portable FEE public key object.
 *
 * Revision History
 * ----------------
 * 11/27/98	dmitch
 *	Added ECDSA_VERIFY_ONLY dependencies.
 * 10/06/98	ap
 *	Changed to compile with C++.
 *  9 Sep 98	Doug Mitchell at NeXT
 * 	Major changes for IEEE P1363 compliance.
 * 23 Mar 98	Doug Mitchell at Apple
 *	Added blob support.
 * 21 Jan 98	Doug Mitchell at Apple
 * 	Fixed feePubKeyBitsize bitlen bug for PT_GENERAL case.
 * 05 Jan 98	Doug Mitchell at Apple
 *	ECDSA now uses SHA-1 hash. Imcompatible with old ECDSA signatures.
 * 17 Jul 97	Doug Mitchell at Apple
 *	Added ECDSA signature routines.
 * 12 Jun 97	Doug Mitchell at Apple
 *	Added feePubKeyInitGiants()
 *	Deleted obsolete code
 *	Changes for lesserX1OrderJustify (was curveOrderJustify)
 * 31 Mar 97	Doug Mitchell at Apple
 *	Fixed leak in feePubKeyCreateKeyString()
 * 15 Jan 97	Doug Mitchell at NeXT
 *	PUBLIC_KEY_STRING_VERSION = 3; broke compatibility with all older
 *		versions.
 *	Cleaned up which_curve/index code to use CURVE_MINUS/CURVE_PLUS.
 * 12 Dec 96	Doug Mitchell at NeXT
 *	Added initFromEnc64KeyStr().
 * 20 Aug 96	Doug Mitchell at NeXT
 *	Ported to C.
 *  ???? 1994	Blaine Garst at NeXT
 *	Created.
 */

#include "ckconfig.h"
#include "feePublicKey.h"
#include "feePublicKeyPrivate.h"
#include "ckutilities.h"
#include "giantIntegers.h"
#include "elliptic.h"
#include "curveParams.h"
#include "falloc.h"
#include "feeTypes.h"
#include "feeDebug.h"
#include "feeHash.h"
#include "ckSHA1.h"
#include "feeDigitalSignature.h"
#include "feeECDSA.h"
#include "platform.h"
#include "enc64.h"
#include "feeDES.h"
#include "byteRep.h"
#if	CRYPTKIT_DER_ENABLE
#include "CryptKitDER.h"
#endif
#include <stdio.h>

/*
 * 11/27/98 dmitch: The ECDSA_VERIFY_ONLY symbol, when #defined, disables all
 * of the code in this module except that which is necessary for ECDSA
 * siggnature verification.
 */
 
#ifndef	NULL
#define NULL ((void *)0)
#endif	// NULL

/*
 * Magic number for a portable key blobs. Must be in sync with static
 * final PUBLIC_KEY_STRING_MAGIC in JavaFee/PublicKey.java.
 */
#define PUBLIC_KEY_BLOB_MAGIC_PUB  		0xfeeddeef
#define PUBLIC_KEY_BLOB_MAGIC_PRIV  	0xfeeddeed
#define PUBLIC_KEY_BLOB_VERSION  		6
#define PUBLIC_KEY_BLOB_MINVERSION		6

#if	CRYPTKIT_DER_ENABLE
#define PUBLIC_DER_KEY_BLOB_VERSION		1
#endif

/*
 * Private data. All "instance" routines are passed a feePubKey (actually
 * a void *) which is actually a pointer to one of these.
 */
typedef struct {
	key			plus;
	key			minus;		// not needed for ECDSA
	curveParams	*cp;		// common params shared by minus, plus
	giant		privGiant;	// private key
} pubKeyInst;

static feeReturn feeGenPrivate(pubKeyInst *pkinst,
	const unsigned char *passwd,
	unsigned passwdLen,
	char hashPasswd);
static pubKeyInst *pubKeyInstAlloc(void);
static void pubKeyInstFree(pubKeyInst *pkinst);
#if		GIANTS_VIA_STACK
static void feePubKeyInitGiants(void);
#endif
static feeReturn createKeyBlob(pubKeyInst *pkinst,
	int isPrivate,			// 0 : public   1 : private
	unsigned char **keyBlob,	// mallocd and RETURNED
	unsigned *keyBlobLen);		// RETURNED
static feeReturn feePubKeyInitFromKeyBlob(feePubKey pubKey,
	unsigned char *keyBlob,
	unsigned keyBlobLen);

#pragma mark --- General public API function ---

/*
 * Obatin a newly allocated feePubKey.
 */
feePubKey feePubKeyAlloc(void)
{
	pubKeyInst *pkinst = pubKeyInstAlloc();

	#if		GIANTS_VIA_STACK
	feePubKeyInitGiants();
	#endif
	return pkinst;
}

void feePubKeyFree(feePubKey pubKey)
{
	pubKeyInstFree((pubKeyInst*) pubKey);
}

#ifndef	ECDSA_VERIFY_ONLY
/*
 * Init feePubKey from private key data.
 */
feeReturn feePubKeyInitFromPrivDataKeyBits(feePubKey pubKey,
	const unsigned char *privData,
	unsigned privDataLen,
	unsigned keyBits,			/* key size in bits */
	feePrimeType primeType,		/* FPT_Fefault means "best one" */
	feeCurveType curveType,		/* FCT_Default means "best one" */
	char hashPrivData)
{
	feeReturn frtn;
	feeDepth depth;
	
	frtn = feeKeyBitsToDepth(keyBits, primeType, curveType, &depth);
	if(frtn) {
		return frtn;
	}
	return feePubKeyInitFromPrivDataDepth(pubKey,
		privData, 
		privDataLen,
		depth,
		hashPrivData);
}

feeReturn feePubKeyInitFromPrivDataDepth(feePubKey pubKey,
	const unsigned char *privData,
	unsigned privDataLen,
	feeDepth depth,
	char hashPrivData)
{
	pubKeyInst *pkinst = (pubKeyInst *) pubKey;
	feeReturn  frtn;

	#if	ENGINE_127_BITS
	if(depth != FEE_DEPTH_127_1) {
		dbgLog(("Illegal Depth\n"));
		return FR_IllegalDepth;
	}
	#endif	// ENGINE_127_BITS
	if(depth > FEE_DEPTH_MAX) {
		dbgLog(("Illegal Depth\n"));
		return FR_IllegalDepth;
	}

	pkinst->cp = curveParamsForDepth(depth);
	pkinst->plus  = new_public(pkinst->cp, CURVE_PLUS);
	if(pkinst->cp->x1Minus != NULL) {
		pkinst->minus = new_public(pkinst->cp, CURVE_MINUS);
	}
	/* else only usable for ECDSA */
	
	frtn = feeGenPrivate(pkinst, privData, privDataLen, hashPrivData);
	if(frtn) {
		return frtn;
	}
	set_priv_key_giant(pkinst->plus, pkinst->privGiant);
	if(pkinst->cp->x1Minus != NULL) {
		set_priv_key_giant(pkinst->minus, pkinst->privGiant);
	}
	return FR_Success;
}

#endif	/* ECDSA_VERIFY_ONLY */

/*
 * Init feePubKey from curve parameters matching existing oldKey.
 */
feeReturn feePubKeyInitFromKey(feePubKey pubKey,
	const unsigned char *privData,
	unsigned privDataLen,
	feePubKey oldKey,
	char hashPrivData)
{
	pubKeyInst *pkinst = (pubKeyInst *) pubKey;
	pubKeyInst *oldInst = (pubKeyInst *) oldKey;
	feeReturn  frtn;

	if(oldKey == NULL) {
		dbgLog(("NULL existing key\n"));
		return FR_BadPubKey;
	}

	pkinst->cp = curveParamsCopy(oldInst->cp);
	if(pkinst->cp->x1Minus != NULL) {
		pkinst->minus = new_public(pkinst->cp, CURVE_MINUS);
		if(pkinst->minus == NULL) {
			goto abort;
		}
	}
	/* else this curve only usable for ECDSA */
	
	pkinst->plus = new_public(pkinst->cp, CURVE_PLUS);
	if(pkinst->plus == NULL) {
		goto abort;
	}
	frtn = feeGenPrivate(pkinst, privData, privDataLen, hashPrivData);
	if(frtn) {
		return frtn;
	}
	set_priv_key_giant(pkinst->plus, pkinst->privGiant);
	if(pkinst->cp->x1Minus != NULL) {
		set_priv_key_giant(pkinst->minus, pkinst->privGiant);
	}
	return FR_Success;

abort:
	dbgLog(("Bad Existing Public Key\n"));
	return FR_BadPubKey;
}

/***
 *** Public KeyString support. 
 ***/
/*
 * Init feePubKey from a public key string.
 *
 * See ByteRep.doc for info on the format of the public key string and blobs;
 * PLEASE UPDATE THIS DOCUMENT WHEN YOU MAKE CHANGES TO THE STRING FORMAT.
 */
feeReturn feePubKeyInitFromKeyString(feePubKey pubKey,
	const char *keyStr,
	unsigned keyStrLen)
{
	unsigned char 	*blob = NULL;
	unsigned		blobLen;
	feeReturn		frtn;

	blob = dec64((unsigned char *)keyStr, keyStrLen, &blobLen);
	if(blob == NULL) {
		dbgLog(("Bad Public Key String (not enc64)\n"));
		return FR_BadPubKeyString;
	}
	frtn = feePubKeyInitFromKeyBlob(pubKey, blob, blobLen);
	ffree(blob);
	return frtn;
}

/*
 * Create a public key in the form of a null-terminated C string.
 * This string contains an encoded version of all of our ivars except for
 * privGiant.
 *
 * See ByteRep.doc for info on the format of the public key string and blobs;
 * PLEASE UPDATE THIS DOCUMENT WHEN YOU MAKE CHANGES TO THE STRING FORMAT.
 */
feeReturn feePubKeyCreateKeyString(feePubKey pubKey,
	char **pubKeyString,		/* RETURNED */
	unsigned *pubKeyStringLen)	/* RETURNED */
{
	unsigned char 	*blob;
	unsigned 	blobLen;
	feeReturn 	frtn;
	pubKeyInst 	*pkinst = (pubKeyInst *)pubKey;

	/* get binary pub blob, encode the blob, free the blob */
	frtn = createKeyBlob(pkinst,
		0,		// isPrivate
		&blob,
		&blobLen);
	if(frtn) {
		return frtn;
	}

	*pubKeyString = (char *)enc64(blob, blobLen, pubKeyStringLen);
	ffree(blob);
	return FR_Success;
}

/*** 
 *** Native key blob support.
 ***/
 
#ifndef	ECDSA_VERIFY_ONLY

/*
 * Obtain portable public and private key blobs from a key.
 */
feeReturn feePubKeyCreatePubBlob(feePubKey pubKey,
	unsigned char **keyBlob,	// mallocd and RETURNED
	unsigned *keyBlobLen)		// RETURNED
{
	pubKeyInst *pkinst = (pubKeyInst *)pubKey;

	return createKeyBlob(pkinst,
		0,
		keyBlob,
		keyBlobLen);
}

feeReturn feePubKeyCreatePrivBlob(feePubKey pubKey,
	unsigned char **keyBlob,	// mallocd and RETURNED
	unsigned *keyBlobLen)		// RETURNED
{
	pubKeyInst *pkinst = (pubKeyInst *)pubKey;

	if(pkinst->privGiant == NULL) {
		return FR_IncompatibleKey;
	}
	return createKeyBlob(pkinst,
		1,
		keyBlob,
		keyBlobLen);
}

/* 
 * Given private-capable privKey, initialize pubKey to be its corresponding 
 * public key.
 */
feeReturn feePubKeyInitPubKeyFromPriv(feePubKey privKey,
	feePubKey pubKey)
{
	pubKeyInst *privInst = (pubKeyInst *)privKey;
	pubKeyInst *pubInst  = (pubKeyInst *)pubKey;

	if((privInst == NULL) || (pubInst == NULL)) {
		return FR_BadPubKey;
	}
	if(privInst->privGiant == NULL) {
		return FR_IncompatibleKey;
	}
	pubInst->cp = curveParamsCopy(privInst->cp);
	if(pubInst == NULL) {
		return FR_Memory;
	}
	pubInst->plus   = new_public_with_key(privInst->plus,  pubInst->cp);
	if(pubInst->plus == NULL) {
		return FR_Memory;
	} 
	if(pubInst->cp->x1Minus != NULL) {
		pubInst->minus  = new_public_with_key(privInst->minus, pubInst->cp);
		if(pubInst->minus == NULL) {
			return FR_Memory;
		} 
	}
	return FR_Success;
}

#endif	/* ECDSA_VERIFY_ONLY */

/*
 * Returns non-zero if two keys are equivalent.
 */
int feePubKeyIsEqual(feePubKey key1, feePubKey key2)
{
	pubKeyInst *pkinst1 = (pubKeyInst *) key1;
	pubKeyInst *pkinst2 = (pubKeyInst *) key2;

	if ((pkinst1 == NULL) || (pkinst2 == NULL)) {
		return 0;
	}
	if((pkinst1->minus != NULL) && (pkinst2->minus != NULL)) {
		if(key_equal(pkinst1->minus, pkinst2->minus) == 0) {
			return 0;
		}
	}
	if(key_equal(pkinst1->plus, pkinst2->plus) == 0) {
		return 0;
	}
	return 1;
}

/*
 * Returns non-zero if key is private-capable (i.e., capable of signing
 * and decrypting).
 */
int feePubKeyIsPrivate(feePubKey key)
{
	pubKeyInst *myPkinst = (pubKeyInst *)key;

	return ((myPkinst->privGiant != NULL) ? 1 : 0);
}

#ifndef	ECDSA_VERIFY_ONLY

#if	CRYPTKIT_KEY_EXCHANGE

feeReturn feePubKeyCreatePad(feePubKey myKey,
	feePubKey theirKey,
	unsigned char **padData,	/* RETURNED */
	unsigned *padDataLen)		/* RETURNED padData length in bytes */
{
	pubKeyInst *myPkinst = (pubKeyInst *) myKey;
	pubKeyInst *theirPkinst = (pubKeyInst *) theirKey;
	giant pad;
	unsigned char *result;
    	unsigned padLen;
	key pkey;

	/*
	 * Do some compatibility checking (myKey, theirKey) here...?
	 */
	if(DEFAULT_CURVE == CURVE_PLUS) {
		pkey = theirPkinst->plus;
	}
	else {
		pkey = theirPkinst->minus;
	}
	pad = make_pad(myPkinst->privGiant, pkey);
	result = mem_from_giant(pad, &padLen);
	freeGiant(pad);

	/*
	 * Ensure we have a the minimum necessary for DES. A bit of a hack,
	 * to be sure.
	 */
	if(padLen >= FEE_DES_MIN_STATE_SIZE) {
		*padData = result;
		*padDataLen = padLen;
	}
	else {
		*padData = (unsigned char*) fmalloc(FEE_DES_MIN_STATE_SIZE);
		*padDataLen = FEE_DES_MIN_STATE_SIZE;
		bzero(*padData, FEE_DES_MIN_STATE_SIZE);
		bcopy(result, *padData, padLen);
		ffree(result);
	}
	return FR_Success;
}

#endif	/* CRYPTKIT_KEY_EXCHANGE */

#if	CRYPTKIT_HIGH_LEVEL_SIG

/*
 * Generate digital signature, ElGamal style.
 */
feeReturn feePubKeyCreateSignature(feePubKey pubKey,
	const unsigned char *data,
	unsigned dataLen,
	unsigned char **signature,	/* fmalloc'd and RETURNED */
	unsigned *signatureLen)		/* RETURNED */
{
	pubKeyInst	*pkinst = (pubKeyInst *) pubKey;
	feeHash 	hash;
	feeSig 		sig;
	unsigned char 	*Pm = NULL;
	unsigned 	PmLen;
	feeReturn	frtn;

	if(pkinst->privGiant == NULL) {
		dbgLog(("feePubKeyCreateSignature: Attempt to Sign without"
			" private data\n"));
		return FR_BadPubKey;
	}
	hash = feeHashAlloc();
	sig = feeSigNewWithKey(pubKey, NULL, NULL);
	if(sig == NULL) {
		/*
		 * Shouldn't happen, but...
		 */
		feeHashFree(hash);
		return FR_BadPubKey;
	}

	/*
	 * Get Pm to salt hash object
	 */
	Pm = feeSigPm(sig, &PmLen);
	feeHashAddData(hash, Pm, PmLen);

	/*
	 * Now hash the data proper, then sign the hash
	 */
	feeHashAddData(hash, data, dataLen);
	frtn = feeSigSign(sig,
		feeHashDigest(hash),
		feeHashDigestLen(),
		pubKey);
	if(frtn == FR_Success) {
		frtn = feeSigData(sig, signature, signatureLen);
	}
	feeHashFree(hash);
	feeSigFree(sig);
	ffree(Pm);
	return frtn;
}

/*
 * Verify digital signature, ElGamal style. If the signature is ECDSA,
 * we'll use that format for compatibility.
 */
feeReturn feePubKeyVerifySignature(feePubKey pubKey,
	const unsigned char *data,
	unsigned dataLen,
	const unsigned char *signature,
	unsigned signatureLen)
{
	feeHash 		hash;
	feeSig 			sig;
	unsigned char 	*Pm = NULL;
	unsigned 		PmLen;
	feeReturn		frtn;

	hash = feeHashAlloc();
	frtn = feeSigParse(signature, signatureLen, &sig);
	if(frtn) {
		feeHashFree(hash);
		#if CRYPTKIT_ECDSA_ENABLE
		if(frtn == FR_WrongSignatureType) {
			return feePubKeyVerifyECDSASignature(pubKey,
				data,
				dataLen,
				signature,
				signatureLen);
		}
		#endif	/* CRYPTKIT_ECDSA_ENABLE */
		return frtn;
	}

	/*
	 * Get PM as salt; eat salt, then hash data
	 */
	Pm = feeSigPm(sig, &PmLen);
	feeHashAddData(hash, Pm, PmLen);
	feeHashAddData(hash, data, dataLen);
	frtn = feeSigVerify(sig,
		feeHashDigest(hash),
		feeHashDigestLen(),
		pubKey);

	feeHashFree(hash);
	feeSigFree(sig);
	ffree(Pm);
	return frtn;
}

#pragma mark --- ECDSA signature: high level routines ---

#if	CRYPTKIT_ECDSA_ENABLE
/*
 * Generate digital signature, ECDSA style.
 */
feeReturn feePubKeyCreateECDSASignature(feePubKey pubKey,
	const unsigned char *data,
	unsigned dataLen,
	unsigned char **signature,	/* fmalloc'd and RETURNED */
	unsigned *signatureLen)		/* RETURNED */
{
	pubKeyInst	*pkinst = (pubKeyInst *) pubKey;
	sha1Obj 	sha1;
	feeReturn	frtn;

	if(pkinst->privGiant == NULL) {
		dbgLog(("feePubKeyCreateECDSASignature: Attempt to Sign "
			"without private data\n"));
		return FR_BadPubKey;
	}
	sha1 = sha1Alloc();
	sha1AddData(sha1, data, dataLen);
	frtn = feeECDSASign(pubKey,
		sha1Digest(sha1),
		sha1DigestLen(),
		NULL,			// randFcn
		NULL,
		signature,
		signatureLen);
	sha1Free(sha1);
	return frtn;
}
#endif	/* CRYPTKIT_ECDSA_ENABLE */
#endif	/* ECDSA_VERIFY_ONLY */

#if	CRYPTKIT_ECDSA_ENABLE

/*
 * Verify digital signature, ECDSA style.
 */
feeReturn feePubKeyVerifyECDSASignature(feePubKey pubKey,
	const unsigned char *data,
	unsigned dataLen,
	const unsigned char *signature,
	unsigned signatureLen)
{
	sha1Obj 	sha1;
	feeReturn	frtn;

	sha1 = sha1Alloc();
	sha1AddData(sha1, data, dataLen);
	frtn = feeECDSAVerify(signature,
		signatureLen,
		sha1Digest(sha1),
		sha1DigestLen(),
		pubKey);
	sha1Free(sha1);
	return frtn;
}

#endif	/* CRYPTKIT_ECDSA_ENABLE */

#endif	/* CRYPTKIT_HIGH_LEVEL_SIG */

#pragma mark --- ECDH ---

/* 
 * Diffie-Hellman. Public key is specified either as a feePubKey or 
 * a ANSI X9.62 format public key string (0x04 | x | y). In either case
 * the caller must ensure that the two keys are on the same curve. 
 * Output data is fmalloc'd here; caller must free. Output data is 
 * exactly the size of the curve's modulus in bytes. 
 */
feeReturn feePubKeyECDH(
	feePubKey privKey,
	/* one of the following two is non-NULL */
	feePubKey pubKey,
	const unsigned char *pubKeyStr,
	unsigned pubKeyStrLen,
	/* output fmallocd and RETURNED here */
	unsigned char **output,
	unsigned *outputLen)
{
	feePubKey theirPub = pubKey;
	feeReturn frtn = FR_Success;
	pubKeyInst *privInst = (pubKeyInst *) privKey;
	
	if(privInst->privGiant == NULL) {
		dbgLog(("feePubKeyECDH: privKey not a private key\n"));
		return FR_IncompatibleKey;
	}
	
	if(theirPub == NULL) {
		if(pubKeyStr == NULL) {
			return FR_IllegalArg;
		}
		
		/* Cook up a public key with the same curveParams as the private key */
		feeDepth depth;
		frtn = curveParamsDepth(privInst->cp, &depth);
		if(frtn) {
			return frtn;
		}
		theirPub = feePubKeyAlloc();
		if(theirPub == NULL) {
			return FR_Memory;
		}
		frtn = feePubKeyInitFromECDSAPubBlob(theirPub, pubKeyStr, pubKeyStrLen, depth);
		if(frtn) {
			goto errOut;
		}
	}
	
	pubKeyInst *pubInst = (pubKeyInst *) theirPub;
	
	giant outputGiant = make_pad(privInst->privGiant, pubInst->plus);
	if(outputGiant == NULL) {
		dbgLog(("feePubKeyECDH: make_pad error\n"));
		frtn = FR_Internal;
	}
	else {
		*outputLen = (privInst->cp->q + 7) / 8;
		*output = (unsigned char *)fmalloc(*outputLen);
		if(*output == NULL) {
			frtn = FR_Memory;
			goto errOut;
		}
		serializeGiant(outputGiant, *output, *outputLen);
		freeGiant(outputGiant);
	}
errOut:
	if((pubKey == NULL) && (theirPub != NULL)) {
		feePubKeyFree(theirPub);
	}
	return frtn;
}

#pragma mark --- feePubKey data accessors ---

unsigned feePubKeyBitsize(feePubKey pubKey)
{
	pubKeyInst *pkinst = (pubKeyInst *) pubKey;
	switch(pkinst->cp->primeType) {
		case FPT_General:	/* cp->q is here for just this purpose */
		case FPT_Mersenne:
			return pkinst->cp->q;
		case FPT_FEE:		/* could be larger or smaller than 2^q-1 */
		default:
			return bitlen(pkinst->cp->basePrime);	
	}
	/* NOT REACHED */
	return 0;
}

/*
 * Accessor routines.
 */
/* private only...*/
key feePubKeyPlusCurve(feePubKey pubKey)
{
	pubKeyInst *pkinst = (pubKeyInst *) pubKey;

	return pkinst->plus;
}

key feePubKeyMinusCurve(feePubKey pubKey)
{
	pubKeyInst *pkinst = (pubKeyInst *) pubKey;

	return pkinst->minus;
}

curveParams *feePubKeyCurveParams(feePubKey pubKey)
{
	pubKeyInst *pkinst = (pubKeyInst *) pubKey;

	return pkinst->cp;
}

giant feePubKeyPrivData(feePubKey pubKey)
{
	pubKeyInst *pkinst = (pubKeyInst *) pubKey;

	return pkinst->privGiant;
}

const char *feePubKeyAlgorithmName(void)
{
	return "Elliptic Curve - FEE by Apple Computer";
}

#pragma mark --- Private functions ---

/*
 * alloc, free pubKeyInst
 */
static pubKeyInst *pubKeyInstAlloc(void)
{
	pubKeyInst *pkinst = (pubKeyInst *) fmalloc(sizeof(pubKeyInst));

	bzero(pkinst, sizeof(pubKeyInst));
	return pkinst;
}

static void pubKeyInstFree(pubKeyInst *pkinst)
{
	if(pkinst->minus) {
		free_key(pkinst->minus);
	}
	if(pkinst->plus) {
		free_key(pkinst->plus);
	}
	if(pkinst->cp) {
		freeCurveParams(pkinst->cp);
	}
	if(pkinst->privGiant) {
		/*
		 * Zero out the private data...
		 */
		clearGiant(pkinst->privGiant);
		freeGiant(pkinst->privGiant);
	}
	ffree(pkinst);
}

#ifndef	ECDSA_VERIFY_ONLY

/*
 * Create a pubKeyInst.privGiant given a password of
 * arbitrary length.
 * Currently, the only error is "private data too short" (FR_IllegalArg).
 */

#define NO_PRIV_MUNGE		0	/* skip this step */

static feeReturn feeGenPrivate(pubKeyInst *pkinst,
	const unsigned char *passwd,
	unsigned passwdLen,
	char hashPasswd)
{
	unsigned 		privLen;			// desired size of pkinst->privData
	feeHash 		*hash = NULL;		// a malloc'd array
	unsigned		digestLen;			// size of MD5 digest
	unsigned 		dataSize;			// min(privLen, passwdLen)
	unsigned		numDigests = 0;
	unsigned		i;
	unsigned char	*cp;
	unsigned		toMove;				// for this digest
	unsigned		moved;				// total digested
	unsigned char	*digest = NULL;
	unsigned char	*privData = NULL;	// temp, before modg(curveOrder)
	giant			corder;				// lesser of two curve orders
	
	/*
	 * generate privData which is just larger than the smaller
	 * curve order.
	 * We'll take the result mod the curve order when we're done.
	 * Note we do *not* have to free corder - it's a pointer to a giant
	 * in pkinst->cp.
	 */
	corder = lesserX1Order(pkinst->cp);
	CKASSERT(!isZero(corder));
	privLen = (bitlen(corder) / 8) + 1;

	if(!hashPasswd) {
		/* 
		 * Caller trusts the incoming entropy. Verify it's big enough and proceed. 
		 */
		if(passwdLen < privLen) {
			return FR_ShortPrivData;
		}
		privLen = passwdLen;
		privData = (unsigned char *)passwd;
		goto finishUp;
	}
	if(passwdLen < 2) {
		return FR_IllegalArg;
	}


	/*
	 * Calculate how many MD5 digests we'll generate.
	 */
	if(privLen > passwdLen) {
		dataSize = passwdLen;
	}
	else {
		dataSize = privLen;
	}
	digestLen = feeHashDigestLen();
	numDigests = (dataSize + digestLen - 1) / digestLen;

	hash = (void**) fmalloc(numDigests * sizeof(feeHash));
	for(i=0; i<numDigests; i++) {
		hash[i] = feeHashAlloc();
	}

	/*
	 * fill digests with passwd data, digestLen (or resid length)
	 * at a time. If (passwdLen > privLen), last digest will hash all
	 * remaining passwd data.
	 */
	cp = (unsigned char *)passwd;
	moved = 0;
	for(i=0; i<numDigests; i++) {
		if(i == (numDigests - 1)) {		    // last digest
		    toMove = passwdLen - moved;
		}
		else {
		    toMove = digestLen;
		}
		feeHashAddData(hash[i], cp, toMove);
		cp += toMove;
		moved += toMove;
	}

	/*
	 * copy digests to privData, up to privLen bytes. Pad with
	 * additional copies of digests if necessary.
	 */
	privData = (unsigned char*) fmalloc(privLen);
	cp = privData;
	moved = 0;
	i = 0;			// digest number
	for(moved=0; moved<privLen; ) {
		if((moved + digestLen) > privLen) {
		   toMove = privLen - moved;
		}
		else {
		   toMove = digestLen;
		}
		digest = feeHashDigest(hash[i++]);
		bcopy(digest, cp, toMove);
		cp += toMove;
		moved += toMove;
		if(i == numDigests) {
		    i = 0;		// wrap to 0, start padding
		}
	}
	
finishUp:
	/*
	 * Convert to giant, justify result to within [2, lesserX1Order]
	 */
	pkinst->privGiant = giant_with_data(privData, privLen);

	#if	FEE_DEBUG
	if(isZero(pkinst->privGiant)) {
		printf("feeGenPrivate: privData = 0!\n");
	}
	#endif	// FEE_DEBUG

	lesserX1OrderJustify(pkinst->privGiant, pkinst->cp);
	if(hashPasswd) {
		memset(privData, 0, privLen);
		ffree(privData);
		for(i=0; i<numDigests; i++) {
			feeHashFree(hash[i]);
		}
		ffree(hash);
	}
	return FR_Success;
}

#endif	/* ECDSA_VERIFY_ONLY */

#if	FEE_DEBUG

void printPubKey(feePubKey pubKey)
{
	pubKeyInst *pkinst = pubKey;

	printf("\ncurveParams:\n");
	printCurveParams(pkinst->cp);
	printf("plus:\n");
	printKey(pkinst->plus);
	printf("minus:\n");
	printKey(pkinst->minus);
	if(pkinst->privGiant != NULL) {
	    printf("privGiant : ");
	    printGiant(pkinst->privGiant);
	}
}

#else	// FEE_DEBUG
void printPubKey(feePubKey pubKey) {}
#endif	// FEE_DEBUG

/*
 * Prime the curveParams and giants modules for quick allocs of giants.
 */
#if		GIANTS_VIA_STACK

static int giantsInitd = 0;

static void feePubKeyInitGiants(void)
{
	if(giantsInitd) {
		return;
	}
	curveParamsInitGiants();
	giantsInitd = 1;
}
#endif

#pragma mark --- Native (custom) key blob formatting ---

/*
 * Exported key blob support. New, 23 Mar 1998.
 *
 * Convert to public or private key blob.
 */

#ifndef	ECDSA_VERIFY_ONLY

/***
 *** Common native blob support 
 ***/
static feeReturn createKeyBlob(pubKeyInst *pkinst,
	int 			isPrivate,		// 0 : public   1 : private
	unsigned char 	**keyBlob,		// mallocd and RETURNED
	unsigned 		*keyBlobLen)	// RETURNED
{
	unsigned char 	*s;		// running ptr into *origS
	unsigned		sLen;
	int				magic;

	/* common blob elements */
	sLen = (4 * sizeof(int)) +		// magic, version, minVersion,
									// spare
	    lengthOfByteRepCurveParams(pkinst->cp);
	if(isPrivate) {
	    /* private only */
	    sLen += lengthOfByteRepGiant(pkinst->privGiant);
	    magic = PUBLIC_KEY_BLOB_MAGIC_PRIV;
	}
	else {
	    /* public only */
	    sLen += (lengthOfByteRepKey(pkinst->plus) +
		     lengthOfByteRepKey(pkinst->minus));
	    magic = PUBLIC_KEY_BLOB_MAGIC_PUB;
	}
	*keyBlob = s = (unsigned char*) fmalloc(sLen);
	s += intToByteRep(magic, s);
	s += intToByteRep(PUBLIC_KEY_BLOB_VERSION, s);
	s += intToByteRep(PUBLIC_KEY_BLOB_MINVERSION, s);
	s += intToByteRep(0, s);			// spare
	s += curveParamsToByteRep(pkinst->cp, s);
	if(isPrivate) {
	    s += giantToByteRep(pkinst->privGiant, s);
	}
	else {
	    /* keyToByteRep writes y for plus curve only */
	    s += keyToByteRep(pkinst->plus, s);
		if(pkinst->minus != NULL) {
			s += keyToByteRep(pkinst->minus, s);
		}
		else {
			/* TBD */
			dbgLog(("work needed here for blobs with no minus key\n"));
		}
	}
	*keyBlobLen = sLen;
	return FR_Success;
}

#endif	/* ECDSA_VERIFY_ONLY */

/*
 * Init an empty feePubKey from a native blob (non-DER format).
 */
static feeReturn feePubKeyInitFromKeyBlob(feePubKey pubKey,
	unsigned char *keyBlob,
	unsigned keyBlobLen)
{
	pubKeyInst		*pkinst = (pubKeyInst *) pubKey;
	unsigned char	*s;		// running pointer
	unsigned		sLen;		// bytes remaining in *s
	int				magic;
	unsigned		len;		// for length of individual components
	int 			minVersion;
	int				version;
	int				isPrivate;

	s = keyBlob;
	sLen = keyBlobLen;
	if(sLen < (4 * sizeof(int))) {	// magic, version, minVersion, spare
		/*
		 * Too short for all the ints we need
		 */
		dbgLog(("feePublicKey: key blob (1)\n"));
		return FR_BadKeyBlob;
	}

	magic = byteRepToInt(s);
	s += sizeof(int);
	sLen -= sizeof(int);
	switch(magic) {
	    case PUBLIC_KEY_BLOB_MAGIC_PUB:
	    	isPrivate = 0;
		break;
	    case PUBLIC_KEY_BLOB_MAGIC_PRIV:
	    	isPrivate = 1;
		break;
	    default:
		dbgLog(("feePublicKey: Bad Public Key Magic Number\n"));
		return FR_BadKeyBlob;
	}

	/*
	 * Switch on this for version-specific cases
	 */
	version = byteRepToInt(s);
	s += sizeof(int);
	sLen -= sizeof(int);

	minVersion = byteRepToInt(s);
	s += sizeof(int);
	sLen -= sizeof(int);
	if(minVersion > PUBLIC_KEY_BLOB_VERSION) {
		/*
		 * old code, newer key blob - can't parse
		 */
		dbgLog(("feePublicKey: Incompatible Public Key (1)\n"));
		return FR_BadKeyBlob;
	}

	s += sizeof(int);			// skip spare
	sLen -= sizeof(int);

	pkinst->cp = byteRepToCurveParams(s, sLen, &len);
	if(pkinst->cp == NULL) {
		dbgLog(("feePublicKey: Bad Key Blob(2)\n"));
		return FR_BadKeyBlob;
	}
	s += len;
	sLen -= len;

	/*
	 * Private key blob: privGiant.
	 * Public Key blob:  plusX, minusX, plusY.
	 */
	if(isPrivate) {
		pkinst->privGiant = byteRepToGiant(s, sLen, &len);
		if(pkinst->privGiant == NULL) {
			dbgLog(("feePublicKey: Bad Key Blob(3)\n"));
			return FR_BadKeyBlob;
		}
		s += len;
		sLen -= len;
	}
	else {
		/* this writes x and y */
		pkinst->plus = byteRepToKey(s,
			sLen,
			CURVE_PLUS,		// twist
			pkinst->cp,
			&len);
		if(pkinst->plus == NULL) {
			dbgLog(("feePublicKey: Bad Key Blob(4)\n"));
			return FR_BadKeyBlob;
		}
		s += len;
		sLen -= len;

		/* this only writes x */
		pkinst->minus = byteRepToKey(s,
			sLen,
			CURVE_MINUS,		// twist
			pkinst->cp,
			&len);
		if(pkinst->minus == NULL) {
			dbgLog(("feePublicKey: Bad Key Blob(5)\n"));
			return FR_BadKeyBlob;
		}
		s += len;
		sLen -= len;
	}

	/*
	 * One more thing: cook up public plusX and minusX for private key
	 * blob case.
	 */
	if(isPrivate) {
		pkinst->plus  = new_public(pkinst->cp, CURVE_PLUS);
		pkinst->minus = new_public(pkinst->cp, CURVE_MINUS);
		set_priv_key_giant(pkinst->plus, pkinst->privGiant);
		set_priv_key_giant(pkinst->minus, pkinst->privGiant);
	}
	return FR_Success;

}

feeReturn feePubKeyInitFromPubBlob(feePubKey pubKey,
	unsigned char *keyBlob,
	unsigned keyBlobLen)
{
	return feePubKeyInitFromKeyBlob(pubKey, keyBlob, keyBlobLen);
}

#ifndef	ECDSA_VERIFY_ONLY

feeReturn feePubKeyInitFromPrivBlob(feePubKey pubKey,
	unsigned char *keyBlob,
	unsigned keyBlobLen)
{
	return feePubKeyInitFromKeyBlob(pubKey, keyBlob, keyBlobLen);	
}

#endif	/* ECDSA_VERIFY_ONLY */

#if	CRYPTKIT_DER_ENABLE
#ifndef	ECDSA_VERIFY_ONLY

/* 
 * DER format support. 
 * Obtain portable public and private DER-encoded key blobs from a key.
 */
feeReturn feePubKeyCreateDERPubBlob(feePubKey pubKey,
	unsigned char **keyBlob,	// mallocd and RETURNED
	unsigned *keyBlobLen)		// RETURNED
{
	pubKeyInst *pkinst = (pubKeyInst *)pubKey;

	if(pkinst == NULL) {
		return FR_BadPubKey;
	}
	if(pkinst->minus == NULL) {
		/* Only ECDSA key formats supported */
		return FR_IncompatibleKey;
	}
	return feeDEREncodePublicKey(PUBLIC_DER_KEY_BLOB_VERSION,
		pkinst->cp,
		pkinst->plus->x,
		pkinst->minus->x,
		isZero(pkinst->plus->y) ? NULL : pkinst->plus->y, 
		keyBlob,
		keyBlobLen);
}

feeReturn feePubKeyCreateDERPrivBlob(feePubKey pubKey,
	unsigned char **keyBlob,	// mallocd and RETURNED
	unsigned *keyBlobLen)		// RETURNED
{
	pubKeyInst *pkinst = (pubKeyInst *)pubKey;

	if(pkinst == NULL) {
		return FR_BadPubKey;
	}
	if(pkinst->privGiant == NULL) {
		return FR_IncompatibleKey;
	}
	if(pkinst->minus == NULL) {
		/* Only ECDSA key formats supported */
		return FR_IncompatibleKey;
	}
	return feeDEREncodePrivateKey(PUBLIC_DER_KEY_BLOB_VERSION,
		pkinst->cp,
		pkinst->privGiant,
		keyBlob,
		keyBlobLen);
}

#endif	/* ECDSA_VERIFY_ONLY */

/*
 * Init an empty feePubKey from a DER-encoded blob, public and private key versions. 
 */
feeReturn feePubKeyInitFromDERPubBlob(feePubKey pubKey,
	unsigned char *keyBlob,
	size_t keyBlobLen)
{
	pubKeyInst	*pkinst = (pubKeyInst *) pubKey;
	feeReturn	frtn;
	int			version;
	
	if(pkinst == NULL) {
		return FR_BadPubKey;
	}
	
	/* kind of messy, maybe we should clean this up. But new_public() does too
	 * much - e.g., it allocates the x and y which we really don't want */
	 memset(pkinst, 0, sizeof(pubKeyInst));
	 pkinst->plus = (key) fmalloc(sizeof(keystruct));
	 pkinst->minus = (key) fmalloc(sizeof(keystruct));
	 if((pkinst->plus == NULL) || (pkinst->minus == NULL)) {
		return FR_Memory;
	 }
	 memset(pkinst->plus, 0, sizeof(keystruct));
	 memset(pkinst->minus, 0, sizeof(keystruct));
	 pkinst->cp = NULL;
	 pkinst->privGiant = NULL;
	 pkinst->plus->twist  = CURVE_PLUS;
	 pkinst->minus->twist = CURVE_MINUS;
	 frtn = feeDERDecodePublicKey(keyBlob, 
		keyBlobLen,
		&version,			// currently unused
		&pkinst->cp,
		&pkinst->plus->x,
		&pkinst->minus->x,
		&pkinst->plus->y);
	if(frtn) {
		return frtn;
	}
	/* minus curve, y is not used */
	pkinst->minus->y = newGiant(1);
	int_to_giant(0, pkinst->minus->y);
	pkinst->plus->cp = pkinst->minus->cp = pkinst->cp;
	return FR_Success;
}

#ifndef	ECDSA_VERIFY_ONLY

feeReturn feePubKeyInitFromDERPrivBlob(feePubKey pubKey,
	unsigned char *keyBlob,
	size_t keyBlobLen)
{
	pubKeyInst	*pkinst = (pubKeyInst *) pubKey;
	int			version;
	feeReturn	frtn;
	
	if(pkinst == NULL) {
		return FR_BadPubKey;
	}
	memset(pkinst, 0, sizeof(pubKeyInst));
	frtn = feeDERDecodePrivateKey(keyBlob, 
		keyBlobLen, 
		&version,		// currently unused
		&pkinst->cp,
		&pkinst->privGiant);
	if(frtn) {
		return frtn;
	}
	
	/* since this blob only had the private data, infer the remaining fields */
	pkinst->plus  = new_public(pkinst->cp, CURVE_PLUS);
	pkinst->minus = new_public(pkinst->cp, CURVE_MINUS);
	set_priv_key_giant(pkinst->plus, pkinst->privGiant);
	set_priv_key_giant(pkinst->minus, pkinst->privGiant);
	return FR_Success;
}

#endif	/* ECDSA_VERIFY_ONLY */

#pragma mark --- X509 (public) and PKCS8 (private) key formatting ---

feeReturn feePubKeyCreateX509Blob(
	feePubKey pubKey,			// public key
	unsigned char **keyBlob,	// mallocd and RETURNED
	unsigned *keyBlobLen)		// RETURNED
{
	pubKeyInst *pkinst = (pubKeyInst *) pubKey;
	unsigned char *xyStr = NULL;
	unsigned xyStrLen = 0;
	feeReturn frtn = feeCreateECDSAPubBlob(pubKey, &xyStr, &xyStrLen);
	if(frtn) {
		return frtn;
	}
	frtn = feeDEREncodeX509PublicKey(xyStr, xyStrLen, pkinst->cp, keyBlob, keyBlobLen);
	ffree(xyStr);
	return frtn;
}

feeReturn feePubKeyCreatePKCS8Blob(
	feePubKey pubKey,			// private key
	unsigned char **keyBlob,	// mallocd and RETURNED
	unsigned *keyBlobLen)		// RETURNED
{
	pubKeyInst *pkinst = (pubKeyInst *) pubKey;
	unsigned char *privStr = NULL;
	unsigned privStrLen = 0;
	feeReturn frtn = feeCreateECDSAPrivBlob(pubKey, &privStr, &privStrLen);
	if(frtn) {
		return frtn;
	}
	unsigned char *pubStr = NULL;
	unsigned pubStrLen = 0;
	frtn = feeCreateECDSAPubBlob(pubKey, &pubStr, &pubStrLen);
	if(frtn) {
		goto errOut;
	}
	frtn = feeDEREncodePKCS8PrivateKey(privStr, privStrLen, 
		pubStr, pubStrLen,
		pkinst->cp, keyBlob, keyBlobLen);
errOut:
	if(privStr) {
		ffree(privStr);
	}
	if(pubStr) {
		ffree(pubStr);
	}
	return frtn;
}

feeReturn feePubKeyInitFromX509Blob(
	feePubKey pubKey,			// public key 
	unsigned char *keyBlob,
	size_t keyBlobLen)
{
	feeDepth depth;
	unsigned char *xyStr = NULL;
	unsigned xyStrLen = 0;
	
	/* obtain x/y and depth from X509 encoding */
	feeReturn frtn = feeDERDecodeX509PublicKey(keyBlob, keyBlobLen, &depth,
		&xyStr, &xyStrLen);
	if(frtn) {
		return frtn;
	}
	
	frtn = feePubKeyInitFromECDSAPubBlob(pubKey, xyStr, xyStrLen, depth);
	ffree(xyStr);
	return frtn;
}


feeReturn feePubKeyInitFromPKCS8Blob(
	feePubKey pubKey,			// private key 
	unsigned char *keyBlob,
	size_t keyBlobLen)
{
	feeDepth depth;
	unsigned char *privStr = NULL;
	unsigned privStrLen = 0;
	
	/* obtain x/y and depth from PKCS8 encoding */
	/* For now we ignore the possible public key string */
	feeReturn frtn = feeDERDecodePKCS8PrivateKey(keyBlob, keyBlobLen, &depth,
		&privStr, &privStrLen, NULL, NULL);
	if(frtn) {
		return frtn;
	}
	
	frtn = feePubKeyInitFromECDSAPrivBlob(pubKey, privStr, privStrLen, depth);
	ffree(privStr);
	return frtn;
}

#pragma mark --- OpenSSL key formatting ---

/*
 * The native OpenSSL ECDSA key format contains both the private and public
 * components in one blob. This throws a bit of a monkey wrench into the API
 * here, as we only have one encoder - which requires a private key - and one
 * decoder, which can result in the decoding of either a public or a private
 * key.
 */
feeReturn feePubKeyCreateOpenSSLBlob(
	feePubKey pubKey,			// private key
	unsigned char **keyBlob,	// mallocd and RETURNED
	unsigned *keyBlobLen)		// RETURNED
{
	pubKeyInst *pkinst = (pubKeyInst *) pubKey;
	unsigned char *privStr = NULL;
	unsigned privStrLen = 0;
	feeReturn frtn = feeCreateECDSAPrivBlob(pubKey, &privStr, &privStrLen);
	if(frtn) {
		return frtn;
	}
	unsigned char *pubStr = NULL;
	unsigned pubStrLen = 0;
	frtn = feeCreateECDSAPubBlob(pubKey, &pubStr, &pubStrLen);
	if(frtn) {
		goto errOut;
	}
	frtn = feeDEREncodeOpenSSLPrivateKey(privStr, privStrLen, 
		pubStr, pubStrLen,
		pkinst->cp, keyBlob, keyBlobLen);
errOut:
	if(privStr) {
		ffree(privStr);
	}
	if(pubStr) {
		ffree(pubStr);
	}
	return frtn;
}

feeReturn feePubKeyInitFromOpenSSLBlob(
	feePubKey pubKey,			// private or public key 
	int pubOnly,
	unsigned char *keyBlob,
	size_t keyBlobLen)
{
	feeDepth depth;
	unsigned char *privStr = NULL;
	unsigned privStrLen = 0;
	unsigned char *pubStr = NULL;
	unsigned pubStrLen = 0;
	
	/* obtain x/y, public bit string, and depth from PKCS8 encoding */
	feeReturn frtn = feeDERDecodeOpenSSLKey(keyBlob, keyBlobLen, &depth,
		&privStr, &privStrLen, &pubStr, &pubStrLen);
	if(frtn) {
		return frtn;
	}
	
	if(pubOnly) {
		frtn = feePubKeyInitFromECDSAPubBlob(pubKey, pubStr, pubStrLen, depth);
	}
	else {
		frtn = feePubKeyInitFromECDSAPrivBlob(pubKey, privStr, privStrLen, depth);
	}
	if(privStr) {
		ffree(privStr);
	}
	if(pubStr) {
		ffree(pubStr);
	}
	return frtn;
}

#endif	/* CRYPTKIT_DER_ENABLE */

/*
 * ANSI X9.62/Certicom key support.
 * Public key is 04 || x || y
 * Private key is privData per Certicom SEC1 C.4.
 */
feeReturn feeCreateECDSAPubBlob(feePubKey pubKey,
	unsigned char **keyBlob,
	unsigned *keyBlobLen)
{
	pubKeyInst *pkinst = (pubKeyInst *)pubKey;
	if(pkinst == NULL) {
		return FR_BadPubKey;
	}
	
	unsigned giantBytes = (pkinst->cp->q + 7) / 8;
	unsigned blobSize = 1 + (2 * giantBytes);
	unsigned char *blob = fmalloc(blobSize);
	if(blob == NULL) {
		return FR_Memory;
	}
	*blob = 0x04;
	serializeGiant(pkinst->plus->x, blob+1, giantBytes);
	serializeGiant(pkinst->plus->y, blob+1+giantBytes, giantBytes);
	*keyBlob = blob;
	*keyBlobLen = blobSize;
	return FR_Success;
}

feeReturn feeCreateECDSAPrivBlob(feePubKey pubKey,
	unsigned char **keyBlob,
	unsigned *keyBlobLen)
{
	pubKeyInst *pkinst = (pubKeyInst *)pubKey;
	if(pkinst == NULL) {
		return FR_BadPubKey;
	}
	if(pkinst->privGiant == NULL) {
		return FR_IncompatibleKey;
	}

	/* 
	 * Return the raw private key bytes padded with zeroes in
	 * the m.s. end to fill exactly one prime-size byte array.
	 */
	unsigned giantBytes = (pkinst->cp->q + 7) / 8;
	unsigned char *blob = fmalloc(giantBytes);
	if(blob == NULL) {
		return FR_Memory;
	}
	serializeGiant(pkinst->privGiant, blob, giantBytes);
	*keyBlob = blob;
	*keyBlobLen = giantBytes;
	return FR_Success;
}

/* Caller determines depth from other sources (e.g. AlgId.Params) */
feeReturn feePubKeyInitFromECDSAPubBlob(feePubKey pubKey,
	const unsigned char *keyBlob,
	unsigned keyBlobLen,
	feeDepth depth)
{
	pubKeyInst *pkinst = (pubKeyInst *)pubKey;
	if(pkinst == NULL) {
		return FR_BadPubKey;
	}
	curveParams *cp = curveParamsForDepth(depth);
	if(cp == NULL) {
		return FR_IllegalDepth;
	}
	unsigned giantBytes = (cp->q + 7) / 8;
	unsigned blobSize = 1 + (2 * giantBytes);
	if(keyBlobLen != blobSize) {
		dbgLog(("feePubKeyInitFromECDSAPubBlob: bad blobLen\n"));
		return FR_BadKeyBlob;
	}
	if(*keyBlob != 0x04) {
		dbgLog(("feePubKeyInitFromECDSAPubBlob: bad blob leader\n"));
		return FR_BadKeyBlob;
	}
	
	pkinst->cp = cp;
	pkinst->plus = new_public(cp, CURVE_PLUS);
	deserializeGiant(keyBlob+1, pkinst->plus->x, giantBytes);
	deserializeGiant(keyBlob+1+giantBytes, pkinst->plus->y, giantBytes);
	return FR_Success;
}

feeReturn feePubKeyInitFromECDSAPrivBlob(feePubKey pubKey,
	const unsigned char *keyBlob,
	unsigned keyBlobLen,
	feeDepth depth)
{
	pubKeyInst *pkinst = (pubKeyInst *)pubKey;
	if(pkinst == NULL) {
		return FR_BadPubKey;
	}
	curveParams *cp = curveParamsForDepth(depth);
	if(cp == NULL) {
		return FR_IllegalDepth;
	}
	unsigned giantDigits = cp->basePrime->sign;
	unsigned giantBytes = (cp->q + 7) / 8;

	/* 
	 * The specified private key can be one byte smaller than the modulus */
	if((keyBlobLen > giantBytes) || (keyBlobLen < (giantBytes - 1))) {
		dbgLog(("feePubKeyInitFromECDSAPrivBlob: bad blobLen\n"));
		return FR_BadKeyBlob;
	}
	
	pkinst->cp = cp;
	
	/* cook up a new private giant */
	pkinst->privGiant = newGiant(giantDigits);
	if(pkinst->privGiant == NULL) {
		return FR_Memory;
	}
	deserializeGiant(keyBlob, pkinst->privGiant, keyBlobLen);

	/* since this blob only had the private data, infer the remaining fields */
	pkinst->plus  = new_public(pkinst->cp, CURVE_PLUS);
	set_priv_key_giant(pkinst->plus, pkinst->privGiant);
	return FR_Success;
}