X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_cryptkit/lib/feePublicKey.c diff --git a/Security/libsecurity_cryptkit/lib/feePublicKey.c b/Security/libsecurity_cryptkit/lib/feePublicKey.c new file mode 100644 index 00000000..3105723a --- /dev/null +++ b/Security/libsecurity_cryptkit/lib/feePublicKey.c @@ -0,0 +1,1612 @@ +/* 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 + +/* + * 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 privLen), last digest will hash all + * remaining passwd data. + */ + cp = (unsigned char *)passwd; + moved = 0; + for(i=0; i 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; icp); + 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; +} +