]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_cryptkit/lib/CryptKitDER.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_cryptkit / lib / CryptKitDER.cpp
diff --git a/Security/libsecurity_cryptkit/lib/CryptKitDER.cpp b/Security/libsecurity_cryptkit/lib/CryptKitDER.cpp
new file mode 100644 (file)
index 0000000..e6fe06a
--- /dev/null
@@ -0,0 +1,1150 @@
+/*
+ * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved.
+ * 
+ * The contents of this file constitute Original Code as defined in and are
+ * subject to the Apple Public Source License Version 1.2 (the 'License').
+ * You may not use this file except in compliance with the License. Please obtain
+ * a copy of the License at http://www.apple.com/publicsource and read it before
+ * using this file.
+ * 
+ * This Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
+ * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
+ * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
+ * specific language governing rights and limitations under the License.
+ */
+
+
+/*
+ * CryptKitDER.h - snacc-based routines to create and parse DER-encoded FEE 
+ *                                keys and signatures
+ *
+ */
+
+#include "ckconfig.h"
+
+#if    CRYPTKIT_DER_ENABLE
+
+#include <security_cryptkit/CryptKitDER.h>
+#include <security_cryptkit/falloc.h>
+#include <security_cryptkit/feeDebug.h>
+#include <security_cryptkit/feeFunctions.h>
+#include "CryptKitAsn1.h"
+#include <security_asn1/SecNssCoder.h>
+#include <security_asn1/nssUtils.h>
+#include <Security/keyTemplates.h>
+#include <Security/oidsalg.h>
+#include <Security/oidsattr.h>
+
+#define PRINT_SIG_GIANTS               0
+#define PRINT_CURVE_PARAMS             0
+#define PRINT_SIZES                            0
+#if            PRINT_SIZES
+#define szprint(s)                             printf s
+#else
+#define szprint(s)
+#endif
+
+/*
+ * Trivial exception class associated with a feeReturn.
+ */
+class feeException
+{
+protected:
+       feeException(feeReturn frtn, const char *op);   
+public:
+       ~feeException() throw() {}
+       feeReturn frtn() const throw() { return mFrtn; }
+    static void throwMe(feeReturn frtn, const char *op = NULL) __attribute__((noreturn));
+private:
+       feeReturn mFrtn;
+};
+
+feeException::feeException(
+       feeReturn frtn, 
+       const char *op)
+               : mFrtn(frtn)
+{ 
+       if(op) {
+               dbgLog(("%s: %s\n", op, feeReturnString(frtn)));
+       }
+}
+
+void feeException::throwMe(feeReturn frtn, const char *op /*= NULL*/) { throw feeException(frtn, op); }
+
+/*
+ * ASN1 encoding rules specify that an integer's sign is indicated by the MSB
+ * of the first (MS) content byte. For a non-negative number, if the MSB of 
+ * the MS byte (of the unencoded number) is one, then the encoding starts with
+ * a byte of zeroes to indicate positive sign. For a negative number, the first
+ * nine bits can not be all 1 - if they are (in the undecoded number), leading 
+ * bytes of 0xff are trimmed off until the first nine bits are something other
+ * than one. Also, the first nine bits of the encoded number can not all be 
+ * zero. 
+ *
+ * CryptKit giants express their sign as part of the giantstruct.sign field. 
+ * The giantDigit array (giantstruct.n[]) is stored l.s. digit first. 
+ *
+ * These routines are independent of platform, endianness, and giatn digit size. 
+ */
+
+/* routines to guess maximum size of DER-encoded objects */ 
+static unsigned feeSizeOfSnaccGiant(
+       giant g)
+{
+       unsigned rtn = abs(g->sign) * GIANT_BYTES_PER_DIGIT;
+       szprint(("feeSizeOfSnaccGiant: sign %d size %d\n", g->sign, rtn + 4));
+       return rtn + 4;
+}
+
+/* PUBLIC... */
+unsigned feeSizeOfDERSig(
+       giant g1,
+       giant g2)
+{
+       unsigned rtn = feeSizeOfSnaccGiant(g1);
+       rtn += feeSizeOfSnaccGiant(g2);
+       szprint(("feeSizeOfDERSig: size %d\n", rtn + 4));
+       return rtn + 4;
+}
+
+/* perform 2's complement of byte array, expressed MS byte first */
+static void twosComplement(
+       unsigned char *bytePtr,         // points to MS byte
+       unsigned numBytes)
+{
+       unsigned char *outp = bytePtr + numBytes - 1;
+       unsigned char carry = 1;        // first time thru, carry = 1 to add one to 1's comp
+       for(unsigned byteDex=0; byteDex<numBytes; byteDex++) {
+               /* first complement, then add carry */
+               *outp = ~*outp + carry;
+               if(carry && (*outp == 0)) {
+                       /* overflow/carry */
+                       carry = 1;
+               }
+               else {
+                       carry = 0;
+               }
+               outp--;
+       }
+}
+
+/*
+ * CSSM_DATA --> unsigned int
+ */
+static unsigned cssmDataToInt(
+       const CSSM_DATA &cdata)
+{
+       if((cdata.Length == 0) || (cdata.Data == NULL)) {
+               return 0;
+       }
+       unsigned len = (unsigned)cdata.Length;
+       if(len > sizeof(int)) {
+               feeException::throwMe(FR_BadKeyBlob, "cssmDataToInt");
+       }
+       
+       unsigned rtn = 0;
+       uint8 *cp = cdata.Data;
+       for(unsigned i=0; i<len; i++) {
+               rtn = (rtn << 8) | *cp++;
+       }
+       return rtn;
+}
+
+/*
+ * unsigned int --> CSSM_DATA, mallocing from an SecNssCoder 
+ */
+static void intToCssmData(
+       unsigned num,
+       CSSM_DATA &cdata,
+       SecNssCoder &coder)
+{
+       unsigned len = 0;
+       
+       if(num < 0x100) {
+               len = 1;
+       }
+       else if(num < 0x10000) {
+               len = 2;
+       }
+       else if(num < 0x1000000) {
+               len = 3;
+       }
+       else {
+               len = 4;
+       }
+       cdata.Data = (uint8 *)coder.malloc(len);
+       cdata.Length = len;
+       uint8 *cp = &cdata.Data[len - 1];
+       for(unsigned i=0; i<len; i++) {
+               *cp-- = num & 0xff;
+               num >>= 8;
+       }
+}
+
+/*
+ * Convert a decoded ASN integer, as a CSSM_DATA, to a (mallocd) giant. 
+ * Only known exception is a feeException.
+ */
+static giant cssmDataToGiant(
+       const CSSM_DATA         &cdata)
+{
+       char *rawOcts = (char *)cdata.Data;
+       unsigned numBytes = (unsigned)cdata.Length;
+       unsigned numGiantDigits;
+       int sign = 1;
+       giant grtn;
+       feeReturn frtn = FR_Success;
+       unsigned char *inp = NULL;
+       unsigned digitDex;                      // index into g->giantDigit[]
+       
+       /* handle degenerate case (value of zero) */
+       if((numBytes == 0) || ((numBytes == 1) && rawOcts[0] == 0)) {
+               grtn = newGiant(1);
+               if(grtn == NULL) {
+                       feeException::throwMe(FR_Memory, "newGiant(1)");
+               }
+               int_to_giant(0, grtn);
+               return grtn;
+       }
+       
+       /* make a copy of raw octets if we have to do two's complement */
+       unsigned char *byteArray = NULL;
+       bool didMalloc = false;
+       if(rawOcts[0] & 0x80) {
+               sign = -1;
+               numBytes++;
+               byteArray = (unsigned char *)fmalloc(numBytes);
+               didMalloc = true;
+               byteArray[0] = 0xff;
+               memmove(byteArray + 1, rawOcts, numBytes-1);
+               twosComplement(byteArray, numBytes);
+       }
+       else {
+               /* no copy */
+               char *foo = rawOcts;
+               byteArray = (unsigned char *)foo;
+       }
+       
+       /* cook up a new giant */
+       numGiantDigits = (numBytes + GIANT_BYTES_PER_DIGIT - 1) /
+                       GIANT_BYTES_PER_DIGIT;
+       grtn = newGiant(numGiantDigits);
+       if(grtn == NULL) {
+               frtn = FR_Memory;
+               goto abort;
+       }
+
+       /* 
+        * Convert byteArray to array of giantDigits
+        * inp - raw input bytes, LSB last
+        * grtn->n[] - output array of giantDigits, LSD first
+        * Start at LS byte and LD digit
+        */
+       digitDex = 0;                                   // index into g->giantDigit[]
+       giantDigit thisDigit;
+       inp = byteArray + numBytes - 1; 
+       unsigned dex;                                   // total byte counter
+       unsigned byteDex;                               // index into one giantDigit
+       unsigned shiftCount;
+       for(dex=0; dex<numBytes; ) {    // increment dex inside
+               thisDigit = 0;
+               shiftCount = 0;
+               for(byteDex=0; byteDex<GIANT_BYTES_PER_DIGIT; byteDex++) {
+                       thisDigit |= ((giantDigit)(*inp--) << shiftCount);
+                       shiftCount += 8;
+                       if(++dex == numBytes) {
+                               /* must be partial giantDigit */
+                               break;
+                       }
+               }
+               CKASSERT(digitDex < numGiantDigits);
+               grtn->n[digitDex++] = thisDigit;
+       }
+       grtn->sign = (int)numGiantDigits * sign;
+       
+       /* trim leading (MS) zeroes */
+       gtrimSign(grtn);
+abort:
+       if(didMalloc) {
+               ffree(byteArray);
+       }
+       if(frtn) {
+               feeException::throwMe(frtn, "bigIntStrToGiant");
+       }
+       return grtn;
+}
+
+/*
+ * Convert a giant to an CSSM_DATA, mallocing using specified coder. 
+ * Only known exception is a feeException.
+ */
+ static void giantToCssmData(
+       giant           g,
+       CSSM_DATA       &cdata,
+       SecNssCoder     &coder)
+{
+       unsigned char doPrepend = 0;    
+       unsigned numGiantDigits = abs(g->sign);
+       unsigned numBytes = numGiantDigits * GIANT_BYTES_PER_DIGIT;
+       giantDigit msGiantBit = 0;
+       if(isZero(g)) {
+               /* special degenerate case */
+               intToCssmData(0, cdata, coder);
+               return;
+       }
+       else {
+               msGiantBit = g->n[numGiantDigits - 1] >> (GIANT_BITS_PER_DIGIT - 1);
+       }
+       
+       /* prepend a byte of zero if necessary */
+       if((g->sign < 0) ||                                     // negative - to handle 2's complement 
+          ((g->sign > 0) && msGiantBit)) {     // ensure MS byte is zero
+                       doPrepend = 1;
+                       numBytes++;
+       }
+       
+       unsigned char *rawBytes = (unsigned char *)fmalloc(numBytes);
+       if(rawBytes == NULL) {
+               feeException::throwMe(FR_Memory, "giantToBigIntStr fmalloc(rawBytes)");
+       }
+       unsigned char *outp = rawBytes;
+       if(doPrepend) {
+               *outp++ = 0;
+       }
+       
+       /* 
+        * Convert array of giantDigits to bytes. 
+        * outp point to MS output byte.
+        */
+       int digitDex;                   // index into g->giantDigit[]
+       unsigned byteDex;               // byte index into a giantDigit
+       for(digitDex=numGiantDigits-1; digitDex>=0; digitDex--) {
+               /* one loop per giantDigit, starting at MS end */
+               giantDigit thisDigit = g->n[digitDex];
+               unsigned char *bp = outp + GIANT_BYTES_PER_DIGIT - 1;
+               for(byteDex=0; byteDex<GIANT_BYTES_PER_DIGIT; byteDex++) {
+                       /* one loop per byte within the digit, starting at LS end */
+                       *bp-- = (unsigned char)(thisDigit) & 0xff;
+                       thisDigit >>= 8;
+               }
+               outp += GIANT_BYTES_PER_DIGIT;
+       }
+       
+       /* do two's complement for negative giants */
+       if(g->sign < 0) {
+               twosComplement(rawBytes, numBytes);
+       }
+       
+       /* strip off redundant leading bits (nine zeroes or nine ones) */
+       outp = rawBytes;
+       unsigned char *endp = outp + numBytes - 1;
+       while((*outp == 0) &&                   // m.s. byte zero
+             (outp < endp) &&                  // more bytes exist
+                 (!(outp[1] & 0x80))) {        // 9th bit is 0
+               outp++;
+               numBytes--;
+       }
+       while((*outp == 0xff) &&                // m.s. byte all ones
+             (outp < endp) &&                  // more bytes exist
+                 (outp[1] & 0x80)) {           // 9th bit is 1
+               outp++;
+               numBytes--;
+       }
+       cdata.Data = (uint8 *)coder.malloc(numBytes);
+       memmove(cdata.Data, outp, numBytes);
+       cdata.Length = numBytes;
+       ffree(rawBytes);
+       return;
+}
+
+/* curveParams : CryptKit <--> FEECurveParametersASN1 */
+/* Only known exception is a feeException */
+static void feeCurveParamsToASN1(
+       const curveParams *cp,
+       FEECurveParametersASN1 &asnCp,
+       SecNssCoder &coder)
+{
+       #if     PRINT_CURVE_PARAMS
+       printf("===encoding curveParams; cp:\n"); printCurveParams(cp);
+       #endif
+       memset(&asnCp, 0, sizeof(asnCp));
+       try {
+               intToCssmData(cp->primeType, asnCp.primeType, coder);
+               intToCssmData(cp->curveType, asnCp.curveType, coder);
+               intToCssmData(cp->q, asnCp.q, coder);
+               intToCssmData(cp->k, asnCp.k, coder);
+               intToCssmData(cp->m, asnCp.m, coder);
+               giantToCssmData(cp->a, asnCp.a, coder);
+               giantToCssmData(cp->b, asnCp.b_, coder);
+               giantToCssmData(cp->c, asnCp.c, coder);
+               giantToCssmData(cp->x1Plus, asnCp.x1Plus, coder);
+               giantToCssmData(cp->x1Minus, asnCp.x1Minus, coder);
+               giantToCssmData(cp->cOrderPlus, asnCp.cOrderPlus, coder);
+               giantToCssmData(cp->cOrderMinus, asnCp.cOrderMinus, coder);
+               giantToCssmData(cp->x1OrderPlus, asnCp.x1OrderPlus, coder);
+               giantToCssmData(cp->x1OrderMinus, asnCp.x1OrderMinus, coder);
+               if(cp->primeType == FPT_General) {
+                       giantToCssmData(cp->basePrime, asnCp.basePrime, coder);
+               }
+       }
+       catch(const feeException &ferr) {
+               throw;
+       }
+       catch(...) {
+               feeException::throwMe(FR_Memory, "feeCurveParamsToSnacc catchall");     // ???
+       }
+}
+
+static curveParams *feeCurveParamsFromAsn1(
+       const FEECurveParametersASN1 &asnCp)
+{
+       curveParams *cp = newCurveParams();
+       if(cp == NULL) {
+               feeException::throwMe(FR_Memory, "feeCurveParamsFromSnacc alloc cp");
+       }
+       cp->primeType = (feePrimeType)cssmDataToInt(asnCp.primeType);
+       cp->curveType = (feeCurveType)cssmDataToInt(asnCp.curveType);
+       cp->q                      = cssmDataToInt(asnCp.q);
+       cp->k                      = cssmDataToInt(asnCp.k);
+       cp->m                      = cssmDataToInt(asnCp.m);
+       cp->a                      = cssmDataToGiant(asnCp.a);
+       cp->b                      = cssmDataToGiant(asnCp.b_);
+       cp->c              = cssmDataToGiant(asnCp.c);
+       cp->x1Plus         = cssmDataToGiant(asnCp.x1Plus);
+       cp->x1Minus        = cssmDataToGiant(asnCp.x1Minus);
+       cp->cOrderPlus     = cssmDataToGiant(asnCp.cOrderPlus);
+       cp->cOrderMinus    = cssmDataToGiant(asnCp.cOrderMinus);
+       cp->x1OrderPlus    = cssmDataToGiant(asnCp.x1OrderPlus);
+       cp->x1OrderMinus   = cssmDataToGiant(asnCp.x1OrderMinus);
+       if(asnCp.basePrime.Data != NULL) {
+               cp->basePrime  = cssmDataToGiant(asnCp.basePrime);
+       }
+       
+       /* remaining fields inferred */
+       curveParamsInferFields(cp);
+       allocRecipGiants(cp);
+       #if     PRINT_CURVE_PARAMS
+       printf("===decoding curveParams; cp:\n"); printCurveParams(cp);
+       #endif
+       return cp;
+}
+
+/***
+ *** Public routines. These are usable from C code; they never throw.
+ ***/
+/*
+ * Encode/decode the two FEE signature types. We malloc returned data via
+ * fmalloc(); caller must free via ffree().
+ */
+feeReturn feeDEREncodeElGamalSignature(
+       giant                   u,
+       giant                   PmX,
+       unsigned char   **encodedSig,           // fmallocd and RETURNED
+       unsigned                *encodedSigLen)         // RETURNED
+{
+       /* convert to FEEElGamalSignatureASN1 */
+       FEEElGamalSignatureASN1 asnSig;
+       SecNssCoder coder;
+       
+       try {
+               giantToCssmData(u, asnSig.u, coder);
+               giantToCssmData(PmX, asnSig.pmX, coder);
+       } 
+       catch(const feeException &ferr) {
+               return ferr.frtn();
+       }
+       
+       /* DER encode */
+       PRErrorCode perr;
+       CSSM_DATA encBlob;                      // mallocd by coder
+       perr = coder.encodeItem(&asnSig, FEEElGamalSignatureASN1Template, encBlob);
+       if(perr) {
+               return FR_Memory;
+       }
+
+       /* copy out  to caller */
+       *encodedSig = (unsigned char *)fmalloc((unsigned)encBlob.Length);
+       *encodedSigLen = (unsigned)encBlob.Length;
+       memmove(*encodedSig, encBlob.Data, encBlob.Length); 
+       
+       #if     PRINT_SIG_GIANTS
+       printf("feeEncodeElGamalSignature:\n");
+       printf("   u   : "); printGiantHex(u);
+       printf("   PmX : "); printGiantHex(PmX);
+       #endif
+       
+       return FR_Success;
+}
+
+feeReturn feeDEREncodeECDSASignature(
+       giant                   c,
+       giant                   d,
+       unsigned char   **encodedSig,           // fmallocd and RETURNED
+       unsigned                *encodedSigLen)         // RETURNED
+{
+       /* convert to FEEECDSASignatureASN1 */
+       FEEECDSASignatureASN1 asnSig;
+       SecNssCoder coder;
+       
+       try {
+               giantToCssmData(c, asnSig.c, coder);
+               giantToCssmData(d, asnSig.d, coder);
+       } 
+       catch(const feeException &ferr) {
+               return ferr.frtn();
+       }
+       
+       /* DER encode */
+       PRErrorCode perr;
+       CSSM_DATA encBlob;                      // mallocd by coder
+       perr = coder.encodeItem(&asnSig, FEEECDSASignatureASN1Template, encBlob);
+       if(perr) {
+               return FR_Memory;
+       }
+
+       /* copy out  to caller */
+       *encodedSig = (unsigned char *)fmalloc((unsigned)encBlob.Length);
+       *encodedSigLen = (unsigned)encBlob.Length;
+       memmove(*encodedSig, encBlob.Data, encBlob.Length); 
+       
+       #if     PRINT_SIG_GIANTS
+       printf("feeEncodeECDSASignature:\n");
+       printf("   c   : "); printGiantHex(*c);
+       printf("   d   : "); printGiantHex(*d);
+       #endif
+       return FR_Success;
+
+}
+
+feeReturn feeDERDecodeElGamalSignature(
+       const unsigned char     *encodedSig,
+       size_t                          encodedSigLen,
+       giant                           *u,                             // newGiant'd and RETURNED
+       giant                           *PmX)                   // newGiant'd and RETURNED
+{
+       FEEElGamalSignatureASN1 asnSig;
+       SecNssCoder coder;
+       
+       memset(&asnSig, 0, sizeof(asnSig));
+       PRErrorCode perr = coder.decode(encodedSig, encodedSigLen, 
+               FEEElGamalSignatureASN1Template, &asnSig);
+       if(perr) {
+               return FR_BadSignatureFormat;
+       }
+
+       try {
+               *u   = cssmDataToGiant(asnSig.u);
+               *PmX = cssmDataToGiant(asnSig.pmX);
+       }
+       catch(const feeException &ferr) {
+               return ferr.frtn();
+       }
+       catch(...) {
+               /* FIXME - bad sig? memory? */
+               return FR_Memory;
+       }
+       #if     PRINT_SIG_GIANTS
+       printf("feeDecodeElGamalSignature:\n");
+       printf("   u   : "); printGiantHex(*u);
+       printf("   PmX : "); printGiantHex(*PmX);
+       #endif
+       return FR_Success;
+}
+
+feeReturn feeDERDecodeECDSASignature(
+       const unsigned char     *encodedSig,
+       size_t                          encodedSigLen,
+       giant                           *c,                             // newGiant'd and RETURNED
+       giant                           *d)                             // newGiant'd and RETURNED
+{
+       FEEECDSASignatureASN1 asnSig;
+       SecNssCoder coder;
+       
+       memset(&asnSig, 0, sizeof(asnSig));
+       PRErrorCode perr = coder.decode(encodedSig, encodedSigLen, 
+               FEEECDSASignatureASN1Template, &asnSig);
+       if(perr) {
+               return FR_BadSignatureFormat;
+       }
+
+       try {
+               *c = cssmDataToGiant(asnSig.c);
+               *d = cssmDataToGiant(asnSig.d);
+       }
+       catch(const feeException &ferr) {
+               return ferr.frtn();
+       }
+       catch(...) {
+               /* FIXME - bad sig? memory? */
+               return FR_Memory;
+       }
+       #if     PRINT_SIG_GIANTS
+       printf("feeDERDecodeECDSASignature:\n");
+       printf("   u   : "); printGiantHex(*u);
+       printf("   PmX : "); printGiantHex(*PmX);
+       #endif
+       return FR_Success;
+}
+
+/*
+ * Encode/decode the FEE private and public keys. We malloc returned data via
+ * falloc(); caller must free via ffree(). Public C functions which never throw. 
+ */
+feeReturn feeDEREncodePublicKey(
+       int                                     version,
+       const curveParams       *cp,
+       giant                           plusX,
+       giant                           minusX,
+       giant                           plusY,                          // may be NULL
+       unsigned char           **keyBlob,                      // fmallocd and RETURNED
+       unsigned                        *keyBlobLen)            // RETURNED
+{
+       FEEPublicKeyASN1 asnKey;
+       SecNssCoder coder;
+       
+       memset(&asnKey, 0, sizeof(asnKey));
+       intToCssmData(version, asnKey.version, coder);
+       
+       try {
+               feeCurveParamsToASN1(cp, asnKey.curveParams, coder);
+               giantToCssmData(plusX, asnKey.plusX, coder);
+               giantToCssmData(minusX, asnKey.minusX, coder);
+               if(plusY != NULL) {
+                       giantToCssmData(plusY, asnKey.plusY, coder);
+               }
+       }
+       catch(const feeException &ferr) {
+               return ferr.frtn();
+       }
+       
+       /* DER encode */
+       PRErrorCode perr;
+       CSSM_DATA encBlob;                      // mallocd by coder
+       perr = coder.encodeItem(&asnKey, FEEPublicKeyASN1Template, encBlob);
+       if(perr) {
+               return FR_Memory;
+       }
+
+       /* copy out */
+       *keyBlob = (unsigned char *)fmalloc((unsigned)encBlob.Length);
+       *keyBlobLen = (unsigned)encBlob.Length;
+       memmove(*keyBlob, encBlob.Data, encBlob.Length); 
+       return FR_Success;
+}
+
+feeReturn feeDEREncodePrivateKey(
+       int                                     version,
+       const curveParams       *cp,
+       const giant                     privData,
+       unsigned char           **keyBlob,                      // fmallocd and RETURNED
+       unsigned                        *keyBlobLen)            // RETURNED
+{
+       FEEPrivateKeyASN1 asnKey;
+       SecNssCoder coder;
+       
+       memset(&asnKey, 0, sizeof(asnKey));
+       intToCssmData(version, asnKey.version, coder);
+       
+       try {
+               feeCurveParamsToASN1(cp, asnKey.curveParams, coder);
+               giantToCssmData(privData, asnKey.privData, coder);
+       }
+       catch(const feeException &ferr) {
+               return ferr.frtn();
+       }
+       
+       /* DER encode */
+       PRErrorCode perr;
+       CSSM_DATA encBlob;                      // mallocd by coder
+       perr = coder.encodeItem(&asnKey, FEEPrivateKeyASN1Template, encBlob);
+       if(perr) {
+               return FR_Memory;
+       }
+
+       /* copy out */
+       *keyBlob = (unsigned char *)fmalloc((unsigned)encBlob.Length);
+       *keyBlobLen = (unsigned)encBlob.Length;
+       memmove(*keyBlob, encBlob.Data, encBlob.Length); 
+       return FR_Success;
+}
+
+feeReturn feeDERDecodePublicKey(
+       const unsigned char     *keyBlob,
+       unsigned                        keyBlobLen,
+       int                                     *version,                       // this and remainder RETURNED
+       curveParams                     **cp,
+       giant                           *plusX,
+       giant                           *minusX,
+       giant                           *plusY)                         // may be NULL
+{
+       FEEPublicKeyASN1 asnKey;
+       SecNssCoder coder;
+       
+       memset(&asnKey, 0, sizeof(asnKey));
+       PRErrorCode perr = coder.decode(keyBlob, keyBlobLen, 
+               FEEPublicKeyASN1Template, &asnKey);
+       if(perr) {
+               return FR_BadKeyBlob;
+       }
+
+       try {
+               *version = cssmDataToInt(asnKey.version);
+               *cp     = feeCurveParamsFromAsn1(asnKey.curveParams);
+               *plusX  = cssmDataToGiant(asnKey.plusX);
+               *minusX = cssmDataToGiant(asnKey.minusX);
+               if(asnKey.plusY.Data != NULL) {
+                       /* optional */
+                       *plusY = cssmDataToGiant(asnKey.plusY);
+               }
+               else {
+                       *plusY = newGiant(1);
+                       int_to_giant(0, *plusY);
+               }
+       }
+       catch(const feeException &ferr) {
+               return ferr.frtn();
+       }
+       catch(...) {
+               /* FIXME - bad sig? memory? */
+               return FR_Memory;
+       }
+       return FR_Success;
+}
+       
+feeReturn feeDERDecodePrivateKey(
+       const unsigned char     *keyBlob,
+       unsigned                        keyBlobLen,
+       int                                     *version,                       // this and remainder RETURNED
+       curveParams                     **cp,
+       giant                           *privData)                      // RETURNED
+{
+       FEEPrivateKeyASN1 asnKey;
+       SecNssCoder coder;
+       
+       memset(&asnKey, 0, sizeof(asnKey));
+       PRErrorCode perr = coder.decode(keyBlob, keyBlobLen, 
+               FEEPrivateKeyASN1Template, &asnKey);
+       if(perr) {
+               return FR_BadKeyBlob;
+       }
+
+       try {
+               *version = cssmDataToInt(asnKey.version);
+               *cp     = feeCurveParamsFromAsn1(asnKey.curveParams);
+               *privData  = cssmDataToGiant(asnKey.privData);
+       }
+       catch(const feeException &ferr) {
+               return ferr.frtn();
+       }
+       catch(...) {
+               /* FIXME - bad sig? memory? */
+               return FR_Memory;
+       }
+       return FR_Success;
+}
+
+#pragma mark --- ECDSA support ---
+
+/* convert between feeDepth and curve OIDs */
+static const CSSM_OID *depthToOid(
+       feeDepth depth)
+{
+       switch(depth) {
+               case FEE_DEPTH_secp192r1:
+                       return &CSSMOID_secp192r1;
+               case FEE_DEPTH_secp256r1:
+                       return &CSSMOID_secp256r1;
+               case FEE_DEPTH_secp384r1:
+                       return &CSSMOID_secp384r1;
+               case FEE_DEPTH_secp521r1:
+                       return &CSSMOID_secp521r1;
+               default:
+                       dbgLog(("depthToOid needs work\n"));
+                       return NULL;
+       }
+}
+
+static feeReturn curveOidToFeeDepth(
+       const CSSM_OID *curveOid, 
+       feeDepth *depth)                        /* RETURNED */
+{
+       if(nssCompareCssmData(curveOid, &CSSMOID_secp192r1)) {
+               *depth = FEE_DEPTH_secp192r1;
+       }
+       else if(nssCompareCssmData(curveOid, &CSSMOID_secp256r1)) {
+               *depth = FEE_DEPTH_secp256r1;
+       }
+       else if(nssCompareCssmData(curveOid, &CSSMOID_secp384r1)) {
+               *depth = FEE_DEPTH_secp384r1;
+       }
+       else if(nssCompareCssmData(curveOid, &CSSMOID_secp521r1)) {
+               *depth = FEE_DEPTH_secp521r1;
+       }
+       else {
+               dbgLog(("curveOidToFeeDepth: unknown curve OID\n"));
+               return FR_BadKeyBlob;
+       }
+       return FR_Success;
+}
+
+
+/* 
+ * Validate a decoded CSSM_X509_ALGORITHM_IDENTIFIER and infer
+ * depth from its algorith.parameter
+ */
+static feeReturn feeAlgIdToDepth(
+       const CSSM_X509_ALGORITHM_IDENTIFIER *algId,
+       feeDepth *depth)
+{
+       const CSSM_OID *oid = &algId->algorithm;
+       /* FIXME what's the value here for a private key!? */
+       if(!nssCompareCssmData(oid, &CSSMOID_ecPublicKey)) {
+               dbgLog(("feeAlgIdToDepth: bad OID"));
+               return FR_BadKeyBlob;
+       }
+       
+       /* 
+        * AlgId.params is curve OID, still encoded since it's an ASN_ANY.
+        * First two bytes of encoded OID are (06, length) 
+        */
+       const CSSM_DATA *param = &algId->parameters;
+       if((param->Length <= 2) || (param->Data[0] != BER_TAG_OID)) {
+               dbgLog(("feeAlgIdToDepth: no curve params\n"));
+               return FR_BadKeyBlob;
+       }
+       
+       CSSM_OID decOid = {param->Length-2, algId->parameters.Data+2};
+       return curveOidToFeeDepth(&decOid, depth);
+}
+
+/*
+ * Prepare an CSSM_X509_ALGORITHM_IDENTIFIER for encoding.
+ */
+static feeReturn feeSetupAlgId(
+       feeDepth depth,
+       SecNssCoder &coder,
+       CSSM_X509_ALGORITHM_IDENTIFIER &algId)
+{
+       algId.algorithm = CSSMOID_ecPublicKey;
+       const CSSM_OID *curveOid = depthToOid(depth);
+       if(curveOid == NULL) {
+               return FR_IllegalDepth;
+       }
+       
+       /* quick & dirty encode of the parameter OID; it's an ASN_ANY in the template */
+       coder.allocItem(algId.parameters, curveOid->Length + 2);
+       algId.parameters.Data[0] = BER_TAG_OID;
+       algId.parameters.Data[1] = curveOid->Length;
+       memmove(algId.parameters.Data+2, curveOid->Data, curveOid->Length);
+       return FR_Success;
+}
+
+#pragma mark --- ECDSA public key, X.509 format ---
+
+/* 
+ * Encode/decode public key in X.509 format.
+ */
+feeReturn feeDEREncodeX509PublicKey(
+       const unsigned char     *pubBlob,               /* x and y octet string */
+       unsigned                        pubBlobLen,
+       curveParams                     *cp,
+       unsigned char           **x509Blob,             /* fmallocd and RETURNED */
+       unsigned                        *x509BlobLen)   /* RETURNED */
+{
+       SecNssCoder coder;
+       CSSM_X509_SUBJECT_PUBLIC_KEY_INFO nssPubKeyInfo;
+       
+       memset(&nssPubKeyInfo, 0, sizeof(nssPubKeyInfo));
+       
+       /* The x/y string, to be encoded in a bit string */
+       nssPubKeyInfo.subjectPublicKey.Data = (uint8 *)pubBlob;
+       nssPubKeyInfo.subjectPublicKey.Length = pubBlobLen * 8;
+       
+       feeDepth depth;
+       feeReturn frtn = curveParamsDepth(cp, &depth);
+       if(frtn) {
+               dbgLog(("feeDEREncodePKCS8PrivateKey: curveParamsDepth error\n"));
+               return frtn;
+       }
+
+       CSSM_X509_ALGORITHM_IDENTIFIER &algId = nssPubKeyInfo.algorithm;
+       frtn = feeSetupAlgId(depth, coder, algId);
+       if(frtn) {
+               return frtn;
+       }
+       
+       /* DER encode */
+       CSSM_DATA encBlob;                      // mallocd by coder
+       PRErrorCode perr = coder.encodeItem(&nssPubKeyInfo, kSecAsn1SubjectPublicKeyInfoTemplate, encBlob);
+       if(perr) {
+               return FR_Memory;
+       }
+
+       /* copy out */
+       *x509Blob = (unsigned char *)fmalloc((unsigned)encBlob.Length);
+       *x509BlobLen = (unsigned)encBlob.Length;
+       memmove(*x509Blob, encBlob.Data, encBlob.Length); 
+       return FR_Success;
+}
+
+feeReturn feeDERDecodeX509PublicKey(
+       const unsigned char     *x509Blob,
+       unsigned                        x509BlobLen,
+       feeDepth                        *depth,                 /* RETURNED */
+       unsigned char           **pubBlob,              /* x and y octet string RETURNED */
+       unsigned                        *pubBlobLen)    /* RETURNED */
+{
+       SecNssCoder coder;
+       CSSM_X509_SUBJECT_PUBLIC_KEY_INFO nssPubKeyInfo;
+       PRErrorCode perr;
+       
+       memset(&nssPubKeyInfo, 0, sizeof(nssPubKeyInfo));
+       perr = coder.decode(x509Blob, x509BlobLen, kSecAsn1SubjectPublicKeyInfoTemplate, 
+               &nssPubKeyInfo);
+       if(perr) {
+               dbgLog(("decode(SubjectPublicKeyInfo) error"));
+               return FR_BadKeyBlob;
+       }
+
+       /* verify alg identifier & depth */
+       feeReturn frtn = feeAlgIdToDepth(&nssPubKeyInfo.algorithm, depth);
+       if(frtn) {
+               return frtn;
+       }
+       
+       /* copy public key string - it's in bits here */
+       CSSM_DATA *pubKey = &nssPubKeyInfo.subjectPublicKey;
+       unsigned keyLen =(unsigned) (pubKey->Length + 7) / 8;
+       *pubBlob = (unsigned char *)fmalloc(keyLen);
+       if(*pubBlob == NULL) {
+               return FR_Memory;
+       }
+       memmove(*pubBlob, pubKey->Data, keyLen);
+       *pubBlobLen = keyLen;
+       return FR_Success;
+}
+
+#pragma mark --- ECDSA keys, OpenSSL format ---
+
+/* 
+ * Encode private, and decode private or public key, in unencrypted OpenSSL format.
+ */
+feeReturn feeDEREncodeOpenSSLPrivateKey(
+       const unsigned char     *privBlob,              /* private data octet string */
+       unsigned                        privBlobLen,
+       const unsigned char *pubBlob,           /* public key, optional */
+       unsigned                        pubBlobLen,
+       curveParams                     *cp,
+       unsigned char           **openBlob,             /* fmallocd and RETURNED */
+       unsigned                        *openBlobLen)   /* RETURNED */
+{
+       feeDepth depth;
+       const CSSM_OID *curveOid;
+       SecNssCoder coder;
+       
+       NSS_ECDSA_PrivateKey ecdsaPrivKey;
+       memset(&ecdsaPrivKey, 0, sizeof(ecdsaPrivKey));
+       uint8 vers = 1;
+       ecdsaPrivKey.version.Data = &vers;
+       ecdsaPrivKey.version.Length = 1;
+       ecdsaPrivKey.privateKey.Data = (uint8 *)privBlob;
+       ecdsaPrivKey.privateKey.Length = privBlobLen;
+       
+       /* Params - ASN_ANY - actually the curve OID */
+       if(curveParamsDepth(cp, &depth)) {
+               dbgLog(("feeDEREncodeOpenSSLPrivateKey: bad depth"));
+               return FR_BadKeyBlob;
+       }
+       curveOid = depthToOid(depth);
+       if(curveOid == NULL) {
+               return FR_BadKeyBlob;
+       }
+       
+       /* quickie DER-encode of the curve OID */
+       try {
+               coder.allocItem(ecdsaPrivKey.params, curveOid->Length + 2);
+       }
+       catch(...) {
+               return FR_Memory;
+       }
+       ecdsaPrivKey.params.Data[0] = BER_TAG_OID;
+       ecdsaPrivKey.params.Data[1] = curveOid->Length;
+       memmove(ecdsaPrivKey.params.Data+2, curveOid->Data, curveOid->Length);
+       
+       /* public key - optional - bit string, length in bits */
+       if(pubBlob) {
+               ecdsaPrivKey.pubKey.Data = (uint8 *)pubBlob;
+               ecdsaPrivKey.pubKey.Length = pubBlobLen * 8;
+       }
+       
+       CSSM_DATA encPriv = {0, NULL};
+       PRErrorCode perr = coder.encodeItem(&ecdsaPrivKey, kSecAsn1ECDSAPrivateKeyInfoTemplate, encPriv);
+       if(perr) {
+               return FR_Memory;
+       }
+
+       /* copy out */
+       *openBlob = (unsigned char *)fmalloc((unsigned)encPriv.Length);
+       *openBlobLen = (unsigned)encPriv.Length;
+       memmove(*openBlob, encPriv.Data, encPriv.Length); 
+       return FR_Success;
+}
+
+feeReturn feeDERDecodeOpenSSLKey(
+       const unsigned char     *osBlob,
+       unsigned                        osBlobLen,
+       feeDepth                        *depth,                 /* RETURNED */
+       unsigned char           **privBlob,             /* private data octet string RETURNED */
+       unsigned                        *privBlobLen,   /* RETURNED */
+       unsigned char           **pubBlob,              /* public data octet string optionally RETURNED */
+       unsigned                        *pubBlobLen)
+{
+       SecNssCoder coder;
+       NSS_ECDSA_PrivateKey ecdsaPrivKey;
+       memset(&ecdsaPrivKey, 0, sizeof(ecdsaPrivKey));
+       if(coder.decode(osBlob, osBlobLen,
+                       kSecAsn1ECDSAPrivateKeyInfoTemplate, &ecdsaPrivKey)) {
+               dbgLog(("Error decoding openssl priv key\n"));
+               return FR_BadKeyBlob;
+       }
+       
+       unsigned keyLen = (unsigned)ecdsaPrivKey.privateKey.Length;
+       if(keyLen == 0) {
+               dbgLog(("NULL priv key data in PKCS8\n"));
+       }
+       *privBlob = (unsigned char *)fmalloc(keyLen);
+       if(*privBlob == NULL) {
+               return FR_Memory;
+       }
+       *privBlobLen = keyLen;
+       memmove(*privBlob, ecdsaPrivKey.privateKey.Data, keyLen);
+       
+       /* curve OID --> depth */
+       if(ecdsaPrivKey.params.Data != NULL) {
+               /* quickie decode */
+               const CSSM_DATA *param = &ecdsaPrivKey.params;
+               if((param->Data[0] != BER_TAG_OID) || (param->Length <= 2)) {
+                       dbgLog(("feeDERDecodeOpenSSLKey: bad curve params\n"));
+                       return FR_BadKeyBlob;
+               }
+               CSSM_OID decOid = {param->Length-2, param->Data+2};
+               if(curveOidToFeeDepth(&decOid, depth)) {
+                       return FR_BadKeyBlob;
+               }
+       }
+
+       /* Public key, if it's there and caller wants it */
+       if((ecdsaPrivKey.pubKey.Length != 0) && (pubBlob != NULL)) {
+               *pubBlobLen = (unsigned)(ecdsaPrivKey.pubKey.Length + 7) / 8;
+               *pubBlob = (unsigned char *)fmalloc(*pubBlobLen);
+               memmove(*pubBlob, ecdsaPrivKey.pubKey.Data, *pubBlobLen);
+       }
+       return FR_Success;
+}
+
+#pragma mark --- ECDSA public key, PKCS8 format ---
+
+/* 
+ * Encode/decode private key in unencrypted PKCS8 format.
+ */
+feeReturn feeDEREncodePKCS8PrivateKey(
+       const unsigned char     *privBlob,              /* private data octet string */
+       unsigned                        privBlobLen,
+       const unsigned char     *pubBlob,               /* public blob, optional */
+       unsigned                        pubBlobLen,
+       curveParams                     *cp,
+       unsigned char           **pkcs8Blob,    /* fmallocd and RETURNED */
+       unsigned                        *pkcs8BlobLen)  /* RETURNED */
+{
+       /* First encode a NSS_ECDSA_PrivateKey */
+       unsigned char *encPriv = NULL;
+       unsigned encPrivLen = 0;
+       feeReturn frtn = feeDEREncodeOpenSSLPrivateKey(privBlob, privBlobLen,
+               pubBlob, pubBlobLen, cp, &encPriv, &encPrivLen);
+       if(frtn) {
+               return frtn;
+       }
+       
+       /* That encoding goes into NSS_PrivateKeyInfo.private key */
+       SecNssCoder coder;
+       NSS_PrivateKeyInfo nssPrivKeyInfo;
+       CSSM_X509_ALGORITHM_IDENTIFIER &algId = nssPrivKeyInfo.algorithm;
+       memset(&nssPrivKeyInfo, 0, sizeof(nssPrivKeyInfo));
+       nssPrivKeyInfo.privateKey.Data = (uint8 *)encPriv;
+       nssPrivKeyInfo.privateKey.Length = encPrivLen;
+       uint8 vers = 0;
+       
+       feeDepth depth;
+       frtn = curveParamsDepth(cp, &depth);
+       if(frtn) {
+               dbgLog(("feeDEREncodePKCS8PrivateKey: curveParamsDepth error\n"));
+               goto errOut;
+       }
+       frtn = feeSetupAlgId(depth, coder, algId);
+       if(frtn) {
+               goto errOut;
+       }
+       
+       nssPrivKeyInfo.version.Data = &vers;
+       nssPrivKeyInfo.version.Length = 1;
+       
+       /* DER encode */
+       CSSM_DATA encPrivInfo;                  // mallocd by coder
+       if(coder.encodeItem(&nssPrivKeyInfo, kSecAsn1PrivateKeyInfoTemplate, encPrivInfo)) {
+               frtn = FR_Memory;
+               goto errOut;
+       }
+
+       /* copy out */
+       *pkcs8Blob = (unsigned char *)fmalloc((unsigned)encPrivInfo.Length);
+       *pkcs8BlobLen = (unsigned)encPrivInfo.Length;
+       memmove(*pkcs8Blob, encPrivInfo.Data, encPrivInfo.Length); 
+errOut:
+       if(encPriv) {
+               ffree(encPriv);
+       }
+       return frtn;
+}
+
+feeReturn feeDERDecodePKCS8PrivateKey(
+       const unsigned char     *pkcs8Blob,
+       unsigned                        pkcs8BlobLen,
+       feeDepth                        *depth,                 /* RETURNED */
+       unsigned char           **privBlob,             /* private data octet string RETURNED */
+       unsigned                        *privBlobLen,   /* RETURNED */
+       unsigned char           **pubBlob,              /* optionally returned, if it's there */
+       unsigned                        *pubBlobLen)
+{
+       NSS_PrivateKeyInfo nssPrivKeyInfo;
+       PRErrorCode perr;
+       SecNssCoder coder;
+       
+       memset(&nssPrivKeyInfo, 0, sizeof(nssPrivKeyInfo));
+       perr = coder.decode(pkcs8Blob, pkcs8BlobLen, kSecAsn1PrivateKeyInfoTemplate, &nssPrivKeyInfo);
+       if(perr) {
+               dbgLog(("Error decoding top level PKCS8\n"));
+               return FR_BadKeyBlob;
+       }
+       
+       /* verify alg identifier & depth */
+       feeReturn frtn = feeAlgIdToDepth(&nssPrivKeyInfo.algorithm, depth);
+       if(frtn) {
+               return frtn;
+       }
+       
+       /* 
+        * nssPrivKeyInfo.privateKey is an octet string containing an encoded 
+        * NSS_ECDSA_PrivateKey. 
+        */
+       frtn = feeDERDecodeOpenSSLKey((const unsigned char *)nssPrivKeyInfo.privateKey.Data,
+               (unsigned)nssPrivKeyInfo.privateKey.Length, depth, 
+               privBlob, privBlobLen,
+               pubBlob, pubBlobLen);
+               
+       return frtn;
+}
+
+#endif /* CRYPTKIT_DER_ENABLE */