--- /dev/null
+/* Copyright (c) 1998,2011-2012,2014 Apple 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, INC. AND THE
+ * ORIGINAL LICENSEE THAT OBTAINED THESE MATERIALS FROM APPLE,
+ * 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 at NeXT
+ * Major changes for IEEE P1363 compliance.
+ * 23 Mar 98 at Apple
+ * Added blob support.
+ * 21 Jan 98 at Apple
+ * Fixed feePubKeyBitsize bitlen bug for PT_GENERAL case.
+ * 05 Jan 98 at Apple
+ * ECDSA now uses SHA-1 hash. Imcompatible with old ECDSA signatures.
+ * 17 Jul 97 at Apple
+ * Added ECDSA signature routines.
+ * 12 Jun 97 at Apple
+ * Added feePubKeyInitGiants()
+ * Deleted obsolete code
+ * Changes for lesserX1OrderJustify (was curveOrderJustify)
+ * 31 Mar 97 at Apple
+ * Fixed leak in feePubKeyCreateKeyString()
+ * 15 Jan 97 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 at NeXT
+ * Added initFromEnc64KeyStr().
+ * 20 Aug 96 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
+
+#warning HLS
+/*
+ * 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 /* CRYPTKIT_HIGH_LEVEL_SIG */
+#endif /* ECDSA_VERIFY_ONLY */
+
+#if CRYPTKIT_HIGH_LEVEL_SIG
+
+#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,
+ (unsigned)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,
+ (unsigned)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, (unsigned)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, (unsigned)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, (unsigned)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;
+}
+