]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_cryptkit/lib/feeFEED.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_cryptkit / lib / feeFEED.c
diff --git a/Security/libsecurity_cryptkit/lib/feeFEED.c b/Security/libsecurity_cryptkit/lib/feeFEED.c
new file mode 100644 (file)
index 0000000..144f734
--- /dev/null
@@ -0,0 +1,1233 @@
+/* Copyright (c) 1998,2011,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.
+ ***************************************************************************
+ *
+ * FeeFEED.c - generic, portable FEED encryption object, expanionless version
+ *
+ * Revision History
+ * ----------------
+ * 10/06/98            ap
+ *     Changed to compile with C++.
+ * 20 Jan 1998 at Apple
+ *     Mods for primeType == PT_GENERAL case.
+ * 12 Jun 1997 at Apple
+ *     Was curveOrderJustify(), is lesserX1OrderJustify()
+ * 31 Mar 1997 at Apple
+ *     Fixed initialRS leak
+ *  3 Mar 1997 at Apple
+ *     Trimmed plainBlockSize by one byte if q mod 8 = 0
+ * 30 Jan 1997 at NeXT
+ *     Created.
+ */
+
+/*
+ * FIXME - a reusable init function would be nice (i.e., free up
+ * session-dependent state and re-init it)...
+ */
+#include "ckconfig.h"
+
+#if    CRYPTKIT_ASYMMETRIC_ENABLE
+
+#include "feeTypes.h"
+#include "feeFEED.h"
+#include "feeFEEDExp.h"
+#include "feePublicKey.h"
+#include "feePublicKeyPrivate.h"
+#include "elliptic.h"
+#include "falloc.h"
+#include "feeRandom.h"
+#include "ckutilities.h"
+#include "feeFunctions.h"
+#include "platform.h"
+#include "curveParams.h"
+#include "feeDebug.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+#define FEED_DEBUG     0
+#define BUFFER_DEBUG   0
+#if            BUFFER_DEBUG
+#define bprintf(s)     printf s
+#else
+#define bprintf(s)
+#endif
+
+/*
+ * Minimum combined size of random r and s, in bytes. For small q sizes,
+ * r and s may be even smaller, but we never truncate them to smaller than
+ * this.
+ * This must be kept in sync with constant of same name in FEED.java.
+ */
+#define RS_MIN_SIZE    16
+
+/*
+ * Private data.
+ */
+typedef struct {
+       curveParams             *cp;
+
+       /*
+        * the clues are initially (r * ourPriv * theirPub(+/-)).
+        */
+       giant                   cluePlus;
+       giant                   clueMinus;
+
+       /*
+        * sPlus and sMinus are based on the random s generated at encrypt
+        * time. Values are s * x1{Plus,Minus}.
+        */
+       giant                   sPlus;
+       giant                   sMinus;
+       giant                   r;                                      /* random, generated at encrypt time */
+       unsigned                plainBlockSize;         /* plaintext block size */
+       unsigned                cipherBlockSize;        /* ciphertext block size */
+       unsigned char   *initialRS;                     /* initial random R,S as bytes */
+       unsigned                initialRSSize;          /* in bytes */
+       feeFEEDExp              feedExp;                        /* for encr/decr r+s params */
+
+       /*
+        * The first few blocks of ciphertext in a stream are the 2:1-FEED
+        * encrypted r and s parameters. While decrypting, we stash incoming
+        * ciphertext in rsCtext until we get enough ciphertext to decrypt
+        * initialRS. RsBlockCount keeps a running count of the
+        * cipherBlocks received. When rsBlockCount == rsSizeCipherBlocks, we
+        * FEEDExp-decrypt rsCtext to get r and s (actually, to get
+        * initialRS; r and s are extraced later in initFromRS()).
+        *
+        * During encrypt, if rsBlockCount is zero, the first thing we send as
+        * ciphertext is the FEED-encrypted initialRS.
+        */
+       unsigned char   *rsCtext;                       /* buffer for encrypted initialRS */
+       unsigned                rsBlockCount;           /* running total of incoming rs
+                                                                                *   cipherblocks */
+
+       int                     forEncrypt;                     /* added for feeFEED*TextSize() */
+       
+       /*
+        * These are calculated at init time - for encrypt and
+        * decrypt - as an optimization.
+        */
+       unsigned                rsCtextSize;            /* number of meaningful bytes in
+                                                                                *   rsCtext */
+       unsigned                rsSizeCipherBlocks;     /* # of our cipherblocks holding
+                                                                                *   rsCtext */
+       
+       /*
+        * temporary variables used for encrypt/decrypt. The values in these
+        * are not needed to be kept from block to block; we just
+        * alloc them once per lifetime of a feeFEED object as an optimization.
+        */
+       giant                   xp;                                     /* plaintext */
+       giant                   xm;                                     /* ciphertext */
+       giant                   tmp1;                           /* scratch */
+       giant                   tmp2;                           /* scratch */
+} feedInst;
+
+/*
+ * "zero residue" indicator.
+ */
+#define RESID_ZERO     0xff
+
+/*
+ * cons up:
+ *     cluePlus(0)
+ *     clueMinus(0)
+ *     sPlus
+ *     sMinus
+ *     r
+ * Assumes:
+ *     cluePlus = clueMinus = ourPriv * theirPub
+ *     initialRS
+ *     initialRSSize
+ *     cp
+ *
+ * Called at feeFEEDNewWithPubKey while encrypting, or upon decrypting
+ * first block of data.
+ */
+static feeReturn initFromRS(feedInst *finst)
+{
+       giant s;
+       unsigned rSize = finst->initialRSSize / 2;
+
+       #if     FEED_DEBUG
+       if((finst->initialRS == NULL) ||
+          (finst->cp == NULL) ||
+          (finst->cluePlus == NULL) ||
+          (finst->clueMinus == NULL) ||
+          (finst->initialRSSize == 0)) {
+               dbgLog(("initFromRS: resource shortage\n"));
+               return FR_Internal;
+       }
+       #endif  // FEED_DEBUG
+
+       finst->r = giant_with_data(finst->initialRS, rSize);
+       s = giant_with_data(finst->initialRS+rSize, rSize);
+
+       #if     FEED_DEBUG
+       if(isZero(finst->r)) {
+               printf("initFromRS: r = 0! initialRSSize = %d; encr = %s\n",
+                       finst->initialRSSize,
+                       (finst->rsCtext == NULL) ? "TRUE" : "FALSE");
+       }
+       if(isZero(s)) {
+               printf("initFromRS: s = 0! initialRSSize = %d; encr = %s\n",
+                       finst->initialRSSize,
+                       (finst->rsCtext == NULL) ? "TRUE" : "FALSE");
+       }
+       #endif  // FEE_DEBUG
+       /*
+        * Justify r and s to be in [2, minimumX1Order].
+        */
+       lesserX1OrderJustify(finst->r, finst->cp);
+       lesserX1OrderJustify(s, finst->cp);
+
+       /*
+        * sPlus  = s * x1Plus
+        * sMinus = s * x1Minus
+        */
+       finst->sPlus = newGiant(finst->cp->maxDigits);
+       finst->sMinus = newGiant(finst->cp->maxDigits);
+       gtog(finst->cp->x1Plus, finst->sPlus);
+       elliptic_simple(finst->sPlus, s, finst->cp);
+       gtog(finst->cp->x1Minus, finst->sMinus);
+       elliptic_simple(finst->sMinus, s, finst->cp);
+
+       /*
+        * And finally, the initial clues. They are currently set to
+        * ourPriv * theirPub.
+        */
+       #if     FEED_DEBUG
+       printf("cluePlus : "); printGiant(finst->cluePlus);
+       printf("clueMinus: "); printGiant(finst->clueMinus);
+       #endif  // FEED_EEBUG
+
+       elliptic_simple(finst->cluePlus, finst->r, finst->cp);
+       elliptic_simple(finst->clueMinus, finst->r, finst->cp);
+
+       #if     FEED_DEBUG
+       printf("r        : "); printGiant(finst->r);
+       printf("s        : "); printGiant(s);
+       printf("sPlus    : "); printGiant(finst->sPlus);
+       printf("sMinus   : "); printGiant(finst->sMinus);
+       printf("cluePlus : "); printGiant(finst->cluePlus);
+       printf("clueMinus: "); printGiant(finst->clueMinus);
+       #endif  // FEED_DEBUG
+
+       freeGiant(s);
+       return FR_Success;
+}
+
+/*
+ * Alloc and init a feeFEED object associated with specified public and
+ * private keys.
+ */
+feeFEED feeFEEDNewWithPubKey(feePubKey myPrivKey,
+       feePubKey theirPubKey,
+       int forEncrypt,                 // 0 ==> decrypt   1 ==> encrypt
+       feeRandFcn randFcn,             // optional 
+       void *randRef)
+{
+       feedInst                *finst;
+       giant                   privGiant;
+       key                             k;
+       unsigned                expPlainSize;
+       unsigned                expCipherSize;
+       unsigned                expBlocks;
+
+       if(!curveParamsEquivalent(feePubKeyCurveParams(theirPubKey),
+                   feePubKeyCurveParams(myPrivKey))) {
+               dbgLog(("feeFEEDNewWithPubKey: Incompatible Keys\n"));
+               return NULL;
+       }
+       finst = (feedInst*) fmalloc(sizeof(feedInst));
+       bzero(finst, sizeof(feedInst));
+       finst->forEncrypt = forEncrypt;
+       finst->cp = curveParamsCopy(feePubKeyCurveParams(theirPubKey));
+       finst->rsBlockCount = 0;
+       finst->xp = newGiant(finst->cp->maxDigits);
+       finst->xm = newGiant(finst->cp->maxDigits);
+       finst->tmp1 = newGiant(finst->cp->maxDigits);
+       if(forEncrypt) {
+           finst->tmp2 = newGiant(finst->cp->maxDigits);
+       }
+
+       /*
+        * cluePlus  = ourPriv * theirPub+
+        * clueMinus = ourPriv * theirPub-
+        */
+       finst->cluePlus  = newGiant(finst->cp->maxDigits);
+       finst->clueMinus = newGiant(finst->cp->maxDigits);
+       privGiant = feePubKeyPrivData(myPrivKey);
+       if(privGiant == NULL) {
+               dbgLog(("feeFEEDNewWithPubKey: no private key\n"));
+               goto abort;
+       }
+       k = feePubKeyPlusCurve(theirPubKey);
+       gtog(k->x, finst->cluePlus);                    // cluePlus = theirPub+
+       elliptic_simple(finst->cluePlus, privGiant, finst->cp);
+       k = feePubKeyMinusCurve(theirPubKey);
+       gtog(k->x, finst->clueMinus);                   // theirPub-
+       elliptic_simple(finst->clueMinus, privGiant, finst->cp);
+
+       /*
+        * Set up block sizes.
+        */
+       if(finst->cp->primeType == FPT_General) {
+           unsigned blen = bitlen(finst->cp->basePrime);
+
+           finst->plainBlockSize = blen / 8;
+           if((blen & 0x7) == 0) {
+               /*
+                * round down some more...
+                */
+               finst->plainBlockSize--;
+           }
+       }
+       else {
+           finst->plainBlockSize = finst->cp->q / 8;
+           if(((finst->cp->q & 0x7) == 0) && (finst->cp->k > 0)) {
+               /*
+                * Special case, with q mod 8 == 0. Here we have to
+                * trim back the plainBlockSize by one byte.
+                */
+               finst->plainBlockSize--;
+           }
+       }
+       finst->cipherBlockSize = finst->cp->minBytes + 1;
+
+       /*
+        * the size of initialRS is subject to tweaking - if we make it
+        * not a multiple of plainBlockSize, we save one FEEDExp cipherBlock
+        * in our ciphertext.
+        */
+       finst->initialRSSize = finst->plainBlockSize * 2;
+       if(finst->initialRSSize > RS_MIN_SIZE) {
+           unsigned minPlainBlocks;
+           unsigned maxSize;
+
+           /*
+            * How many plainblocks to hold RS_MIN_SIZE?
+            */
+           minPlainBlocks = (RS_MIN_SIZE + finst->plainBlockSize - 1) /
+               finst->plainBlockSize;
+
+           /*
+            * Max size = that many plainblocks, less 2 bytes (to avoid
+            * extra residue block).
+            */
+           maxSize = minPlainBlocks * finst->plainBlockSize - 2;
+
+           /*
+            * But don't bother with more than 2 plainblocks worth
+            */
+           if(finst->initialRSSize > maxSize) {
+               finst->initialRSSize = maxSize;
+           }
+       }
+       /* else leave it alone, that's small enough */
+
+       if(forEncrypt) {
+               feeRand frand = NULL;
+
+               /*
+                * Encrypt-capable FEEDExp object
+                */
+               finst->feedExp = feeFEEDExpNewWithPubKey(theirPubKey,
+                       randFcn,
+                       randRef);
+               if(finst->feedExp == NULL) {
+                       goto abort;
+               }
+
+               /*
+                * Generate initial r and s data.
+                */
+               finst->initialRS = (unsigned char*) fmalloc(finst->initialRSSize);
+               if(randFcn != NULL) {
+                       randFcn(randRef, finst->initialRS, finst->initialRSSize);
+               }
+               else {
+                       frand = feeRandAlloc();
+                       feeRandBytes(frand, finst->initialRS, finst->initialRSSize);
+                       feeRandFree(frand);
+               }
+               if(initFromRS(finst)) {
+                       goto abort;
+               }
+       }
+       else {
+               /*
+                * Decrypt-capable FEEDExp object
+                */
+               finst->feedExp = feeFEEDExpNewWithPubKey(myPrivKey,
+                       randFcn,
+                       randRef);
+               if(finst->feedExp == NULL) {
+                       goto abort;
+               }
+
+       }
+
+       /*
+        * Figure out how many of our cipherblocks it takes to hold
+        * a FEEDExp-encrypted initialRS. If initialRSSize is an exact
+        * multiple of expPlainSize, we get an additional feedExp
+        * residue block.
+        */
+       expPlainSize = feeFEEDExpPlainBlockSize(finst->feedExp);
+       expCipherSize = feeFEEDExpCipherBlockSize(finst->feedExp);
+       expBlocks = (finst->initialRSSize + expPlainSize - 1) /
+               expPlainSize;
+       if((finst->initialRSSize % expPlainSize) == 0) {
+               expBlocks++;
+       }
+
+       /*
+        * Total meaningful bytes of encrypted initialRS
+        */
+       finst->rsCtextSize = expBlocks * expCipherSize;
+
+       /*
+        * Number of our cipherblocks it takes to hold rsCtextSize
+        */
+       finst->rsSizeCipherBlocks = (finst->rsCtextSize +
+               finst->cipherBlockSize - 1) / finst->cipherBlockSize;
+       if(!forEncrypt) {
+           finst->rsCtext = (unsigned char*) fmalloc(finst->rsSizeCipherBlocks *
+               finst->cipherBlockSize);
+       }
+
+       /*
+        * Sanity check...
+        */
+       #if     FEED_DEBUG
+       {
+           unsigned fexpBlockSize = feeFEEDExpCipherBlockSize(finst->feedExp);
+
+           /*
+            * FEEDExp has one more giant in ciphertext, plaintext is
+            * same size
+            */
+           if((finst->cipherBlockSize + finst->cp->minBytes) !=
+                       fexpBlockSize) {
+               dbgLog(("feeFEEDNewWithPubKey: FEEDExp CBlock Size "
+                       "screwup\n"));
+               goto abort;
+           }
+           fexpBlockSize = feeFEEDExpPlainBlockSize(finst->feedExp);
+           if(fexpBlockSize != finst->plainBlockSize) {
+               dbgLog(("feeFEEDNewWithPubKey: FEEDExp PBlock Size "
+                       "screwup\n"));
+               goto abort;
+           }
+       }
+       #endif  // FEED_DEBUG
+
+       return finst;
+
+abort:
+       feeFEEDFree(finst);
+       return NULL;
+}
+
+void feeFEEDFree(feeFEED feed)
+{
+       feedInst *finst = (feedInst*) feed;
+
+       if(finst->cp) {
+               freeCurveParams(finst->cp);
+       }
+       if(finst->initialRS) {
+               ffree(finst->initialRS);
+       }
+       if(finst->cluePlus) {
+               freeGiant(finst->cluePlus);
+       }
+       if(finst->clueMinus) {
+               freeGiant(finst->clueMinus);
+       }
+       if(finst->sPlus) {
+               freeGiant(finst->sPlus);
+       }
+       if(finst->sMinus) {
+               freeGiant(finst->sMinus);
+       }
+       if(finst->r) {
+               freeGiant(finst->r);
+       }
+       if(finst->feedExp) {
+               feeFEEDExpFree(finst->feedExp);
+       }
+       if(finst->rsCtext) {
+               ffree(finst->rsCtext);
+       }
+       if(finst->xp) {
+               freeGiant(finst->xp);
+       }
+       if(finst->xm) {
+               freeGiant(finst->xm);
+       }
+       if(finst->tmp1) {
+               freeGiant(finst->tmp1);
+       }
+       if(finst->tmp2) {
+               freeGiant(finst->tmp2);
+       }
+       ffree(finst);
+}
+
+unsigned feeFEEDPlainBlockSize(feeFEED feed)
+{
+       feedInst *finst = (feedInst *) feed;
+
+       return finst->plainBlockSize;
+}
+
+unsigned feeFEEDCipherBlockSize(feeFEED feed)
+{
+       feedInst *finst = (feedInst *) feed;
+
+       return finst->cipherBlockSize;
+}
+
+/*
+ * Calculate size of buffer currently needed to encrypt one block of
+ * plaintext. Also used to calculate required input during decrypt
+ * to get any output.
+ */
+unsigned feeFEEDCipherBufSize(feeFEED feed,
+        int finalBlock)
+{
+       feedInst *finst = (feedInst *) feed;
+       unsigned blocks = 1;                    // always at least one block of ciphertext
+       
+       if(finst->rsBlockCount == 0) {
+               /* haven't sent/seen encrypted RS yet */
+               blocks += finst->rsSizeCipherBlocks;
+       }
+       
+       if(finalBlock) {
+               /* only needed if ptext is aligned, but tell caller to malloc */
+               blocks++;
+       }
+       bprintf(("$$$ feeFEEDCipherBufSize( %s, %s): rtn 0x%x\n",
+               finst->forEncrypt ? "encrypt" : "decrypt",
+               finalBlock ? " final" : "!final", 
+               blocks * finst->cipherBlockSize));
+       return blocks * finst->cipherBlockSize;
+}
+
+/*
+ * Return the size of ciphertext currently needed to encrypt specified 
+ * size of plaintext. Also can be used to calculate size of ciphertext 
+ * which can be decrypted into specified size of plaintext. 
+ */
+unsigned feeFEEDCipherTextSize(feeFEED feed, 
+       unsigned        plainTextSize,
+       int             finalBlock)
+{
+       feedInst *finst = (feedInst *) feed;
+       
+       /* how many blocks of plaintext? */
+       unsigned blocks = (plainTextSize + finst->plainBlockSize - 1) /
+               finst->plainBlockSize;
+
+       if(finst->forEncrypt) {
+               /* have we generated RS? */
+               if(finst->rsBlockCount == 0) {
+                       /* haven't sent encrypted RS yet */
+                       blocks += finst->rsSizeCipherBlocks;
+               }
+       
+               /* final? residue? */
+               if(finalBlock) {
+                       if((plainTextSize % finst->plainBlockSize) == 0) {
+                               blocks++;
+                       }
+               }
+       }       /* encrypting */
+       else {
+               /* 
+                * Decrypting - how much ciphertext can we decrypt safely into
+                * specified plaintext? Add in RS if we haven't seen it all
+                * yet.
+                */
+               #if BUFFER_DEBUG
+               if(finst->rsBlockCount > finst->rsSizeCipherBlocks) {
+                       printf("******HEY! rsBlockCount overflow! (blockCount %d rsSize %d)\n",
+                               finst->rsBlockCount, finst->rsSizeCipherBlocks);
+               }
+               #endif
+               blocks += (finst->rsSizeCipherBlocks - finst->rsBlockCount);
+       }
+       bprintf(("$$$ feeFEEDCipherTextSize(%s, %s, 0x%x): rtn 0x%x\n",
+               finst->forEncrypt ? "encrypt" : "decrypt",
+               finalBlock ? " final" : "!final", 
+               plainTextSize, blocks * finst->cipherBlockSize));
+       return blocks * finst->cipherBlockSize;
+}
+
+/*
+ * Return the size of plaintext currently needed to decrypt specified size 
+ * of ciphertext. Also can be used to calculate size of plaintext 
+ * which can be encrypted into specified size of ciphertext.
+ */
+unsigned feeFEEDPlainTextSize(feeFEED feed, 
+       unsigned        cipherTextSize,
+       int             finalBlock)                     // ignored if !forEncrypt
+{
+       feedInst *finst = (feedInst *) feed;
+       
+       /* start with basic cipher block count */
+       unsigned cipherBlocks = (cipherTextSize + finst->cipherBlockSize - 1) /
+               finst->cipherBlockSize;
+               
+       /* where are we in the RS stream? */
+       unsigned rsBlocksToGo = finst->rsSizeCipherBlocks - finst->rsBlockCount;
+       if(finst->forEncrypt) {
+               /* 
+                * Encrypting, seeking plaintext size we can encrypt given
+                * a specified size of ciphertext.
+                */
+               if(rsBlocksToGo >= cipherBlocks) {
+                       /* no room! next encrypt would overflow ctext buffer! */
+                       return 0;
+               }
+               cipherBlocks -= rsBlocksToGo;
+               
+               /* another constraint - residue */
+               if(finalBlock) {
+                       if(cipherBlocks) {
+                               /* skip if already zero... */
+                               cipherBlocks--;
+                       }
+               }
+       }       /* encrypting */
+       else {
+               /* decrypting */
+               if(rsBlocksToGo >= cipherBlocks) {
+                       /* still processing RS, no plaintext will be generated. Play it real
+                        * safe and just tell caller one block. */
+                       cipherBlocks = 1;
+               }
+               else {
+                       /* diminish by size of RS to be gobbled with no output */
+                       cipherBlocks -= rsBlocksToGo;
+               }
+       }
+       bprintf(("$$$ feeFEEDPlainTextSize( %s, %s, 0x%x): rtn 0x%x\n",
+               finst->forEncrypt ? "encrypt" : "decrypt",
+               finalBlock ? " final" : "!final", 
+               cipherTextSize, cipherBlocks * finst->plainBlockSize));
+       return cipherBlocks * finst->plainBlockSize;
+}
+
+/*
+ * Bits in last byte of cipherblock
+ */
+#define CLUE_BIT               0x01    /* 1 ==> plus curve */
+#define CLUE_PLUS              0x01
+#define CLUE_MINUS             0x00
+#define PARITY_BIT             0x02    /* 1 ==> plus 's' arg to elliptic_add() */
+#define PARITY_PLUS            0x02
+#define PARITY_MINUS   0x00
+
+/*
+ * Encrypt a block or less of data. Caller malloc's cipherText.
+ * Generates up to feeFEEDCipherBufSize() bytes of ciphertext.
+ */
+feeReturn feeFEEDEncryptBlock(feeFEED feed,
+       const unsigned char *plainText,
+       unsigned plainTextLen,
+       unsigned char *cipherText,
+       unsigned *cipherTextLen,                // RETURNED
+       int finalBlock)
+{
+       feedInst                *finst = (feedInst *) feed;
+       unsigned                ctextLen = 0;
+       feeReturn               frtn = FR_Success;
+       int                             whichCurve;
+       giant                   thisClue;               // not alloc'd or freed
+       giant                   thisS;                  // ditto
+       unsigned char   clueByte;
+
+       if(plainTextLen > finst->plainBlockSize) {
+               return FR_IllegalArg;
+       }
+       if((plainTextLen < finst->plainBlockSize) && !finalBlock) {
+               return FR_IllegalArg;
+       }
+       if(finst->initialRS == NULL) {
+               /*
+                * Init'd for decrypt?
+                */
+               return FR_IllegalArg;
+       }
+
+       /*
+        * First block - encrypt initialRS via FEEDExp
+        */
+       if(finst->rsBlockCount == 0) {
+           unsigned char *thisCtext;   // malloc's by FEEDExp
+           unsigned padLen;
+
+           if(finst->initialRS == NULL) {
+               /*
+                * init'd for decrypt or reused
+                */
+               dbgLog(("feeFEEDEncryptBlock: NULL initialRS!\n"));
+               return FR_IllegalArg;
+           }
+
+           frtn = feeFEEDExpEncrypt(finst->feedExp,
+                   finst->initialRS,
+                   finst->initialRSSize,
+                   &thisCtext,
+                   &ctextLen);
+           if(frtn) {
+                   /*
+                    * Should never happen...
+                    */
+                   dbgLog(("feeFEEDEncryptBlock: error writing encrypted"
+                           " initialRS (%s)\n", feeReturnString(frtn)));
+                   return FR_Internal;
+           }
+           bcopy(thisCtext, cipherText, ctextLen);
+           cipherText += ctextLen;
+           ffree(thisCtext);
+
+           finst->rsBlockCount = finst->rsSizeCipherBlocks;
+           padLen = finst->cipherBlockSize -
+               (ctextLen % finst->cipherBlockSize);    // zeros to write
+
+           #if         0       /* FEED_DEBUG */
+
+           /*
+            * Hard-coded assumptions and tests about initRSSize...
+            * Currently we assume that initRSSize % expBlockSize = 0
+            */
+           if((ctextLen / finst->cipherBlockSize) != 5) {
+               dbgLog(("feeFEEDEncryptBlock: cipherblock size screwup (1)\n"));
+               return FR_Internal;
+           }
+           if(padLen != 3) {
+               dbgLog(("feeFEEDEncryptBlock: cipherblock size screwup (2)\n"));
+               return FR_Internal;
+           }
+           #endif      // FEED_DEBUG
+
+           /*
+            * pad to multiple of (our) cipherblock size.
+            */
+           while(padLen) {
+               *cipherText++ = 0;
+               ctextLen++;
+               padLen--;
+           }
+       }
+
+       /*
+        * plaintext to giant xp
+        */
+       if(finalBlock) {
+               unsigned char *ptext = (unsigned char*) fmalloc(finst->plainBlockSize);
+               bzero(ptext, finst->plainBlockSize);
+               if(plainTextLen) {
+                       /*
+                        * skip for empty block with resid length 0
+                        */
+                       bcopy(plainText, ptext, plainTextLen);
+               }
+               if(plainTextLen < finst->plainBlockSize) {
+                   if(plainTextLen == 0) {
+                       /*
+                        * Special case - resid block with no actual plaintext.
+                        * Can't actually write zero here; it screws up
+                        * deserializing the giant during decrypt
+                        */
+                       ptext[finst->plainBlockSize - 1] = RESID_ZERO;
+                               bprintf(("=== FEED encrypt: RESID_ZERO\n"));
+                   }
+                   else {
+                               ptext[finst->plainBlockSize - 1] = plainTextLen;
+                               bprintf(("=== FEED encrypt: resid len 0x%x\n", plainTextLen));
+                   }
+               }
+               /*
+                * else handle evenly aligned case (i.e., finalBlock true
+                * and (plainTextLen ==  plainBlockSize)) below...
+                */
+               deserializeGiant(ptext, finst->xp, finst->plainBlockSize);
+               ffree(ptext);
+       }
+       else {
+               deserializeGiant(plainText, finst->xp, plainTextLen);
+       }
+
+       /*
+        * encrypt xp
+        *     xm = xp + clue(+/-)
+        * determine parity needed to restore xp
+        *     parity = ((xm + clue(+/-) == xp) ? 1 : -1
+        * and adjust clue
+        *     clue[n+1] = r * clue[n] + (s * P1)
+        */
+       whichCurve = which_curve(finst->xp, finst->cp);
+       if(whichCurve == CURVE_PLUS) {
+               thisClue = finst->cluePlus;
+               thisS    = finst->sPlus;
+               clueByte = CLUE_PLUS;
+       }
+       else {
+               thisClue = finst->clueMinus;
+               thisS    = finst->sMinus;
+               clueByte = CLUE_MINUS;
+       }
+       // calculate xm
+       elliptic_add(thisClue, finst->xp, finst->xm, finst->cp, SIGN_PLUS);
+       // save xm + clue in tmp1
+       elliptic_add(finst->xm, thisClue, finst->tmp1, finst->cp, SIGN_PLUS);
+       // Adjust clue
+       elliptic_simple(thisClue, finst->r, finst->cp);
+       gtog(thisClue, finst->tmp2);
+       elliptic_add(finst->tmp2, thisS, thisClue, finst->cp, SIGN_PLUS);
+
+       /*
+        * Calculate parity
+        */
+       if(gcompg(finst->tmp1, finst->xp) == 0) {
+               clueByte |= PARITY_PLUS;
+       }
+
+       /*
+        * Ciphertext = (xm, clueByte)
+        */
+       serializeGiant(finst->xm, cipherText, finst->cp->minBytes);
+       cipherText += finst->cp->minBytes;
+       ctextLen += finst->cp->minBytes;
+       *cipherText++ = clueByte;
+       ctextLen++;
+
+       #if     FEED_DEBUG
+       printf("encrypt  clue %d\n", clueByte);
+       printf("  xp : "); printGiant(finst->xp);
+       printf("  xm : "); printGiant(finst->xm);
+       printf("  cluePlus  :"); printGiant(finst->cluePlus);
+       printf("  clueMinus :"); printGiant(finst->clueMinus);
+       #endif  // FEED_DEBUG
+
+       if(finalBlock && (plainTextLen == finst->plainBlockSize)) {
+              /*
+               * Special case: finalBlock true, plainTextLen == blockSize.
+               * In this case we generate one more block of ciphertext,
+               * with a resid length of zero.
+               */
+               unsigned moreCipher;                    // additional cipherLen
+
+               frtn = feeFEEDEncryptBlock(feed,
+                       NULL,                           // plainText not used
+                       0,                              // resid
+                       cipherText,                     // append...
+                       &moreCipher,
+                       1);
+               if(frtn == FR_Success) {
+                       ctextLen += moreCipher;
+               }
+       }
+       bprintf(("=== FEED encryptBlock ptextLen 0x%x  ctextLen 0x%x\n",
+               plainTextLen, ctextLen));
+               
+       *cipherTextLen = ctextLen;
+       return frtn;
+}
+
+/*
+ * Decrypt (exactly) a block of data. Caller malloc's plainText. Always
+ * generates feeFEEDPlainBlockSize of plaintext, unless finalBlock is
+ * non-zero (in which case feeFEEDPlainBlockSize or less bytes of plainText are
+ * generated).
+ */
+feeReturn feeFEEDDecryptBlock(feeFEED feed,
+       const unsigned char *cipherText,
+       unsigned cipherTextLen,
+       unsigned char *plainText,
+       unsigned *plainTextLen,                 // RETURNED
+       int finalBlock)
+{
+       feedInst        *finst = (feedInst *) feed;
+       feeReturn       frtn = FR_Success;
+       unsigned char   clueByte;
+       giant           thisClue;               // not alloc'd
+       giant           thisS;                  // ditto
+       int             parity;
+
+       if(finst->rsCtext == NULL) {
+               /*
+                * Init'd for encrypt?
+                */
+               return FR_IllegalArg;
+       }
+       if(cipherTextLen != finst->cipherBlockSize) {
+               dbgLog(("feeFEEDDecryptBlock: bad cipherTextLen\n"));
+               return FR_IllegalArg;
+       }
+       if(finst->rsBlockCount < finst->rsSizeCipherBlocks) {
+               /*
+                * Processing initialRS, FEEDExp-encrypted
+                */
+               unsigned char *rsPtr = finst->rsCtext +
+                       (finst->rsBlockCount * finst->cipherBlockSize);
+               unsigned feedExpCipherSize;
+
+               if(finalBlock) {
+                   dbgLog(("feeFEEDDecryptBlock: incomplete initialRS\n"));
+                   return FR_BadCipherText;
+               }
+               bcopy(cipherText, rsPtr, finst->cipherBlockSize);
+               finst->rsBlockCount++;
+               if(finst->rsBlockCount < finst->rsSizeCipherBlocks) {
+                   /*
+                    * Not done with this yet...
+                    */
+                       bprintf(("=== FEED Decrypt: gobbled 0x%x bytes ctext, no ptext (1)\n", 
+                               cipherTextLen));
+                   *plainTextLen = 0;
+                   return FR_Success;
+               }
+
+               #if     FEED_DEBUG
+               if((finst->rsBlockCount * finst->cipherBlockSize) <
+                               finst->rsCtextSize) {
+                   dbgLog(("feeFEEDDecryptBlock: rsCtextSize underflow!\n"));
+                   return FR_Internal;
+               }
+               #endif  // FEED_DEBUG
+
+               /*
+                * OK, we should have the FEEDExp ciphertext for initialRS
+                * in rsCtext. Note the last few bytes are extra; we don't
+                * pass them to FEEDExp.
+                */
+               feedExpCipherSize = feeFEEDCipherBlockSize(finst->feedExp);
+               frtn = feeFEEDExpDecrypt(finst->feedExp,
+                       finst->rsCtext,
+                       finst->rsCtextSize,
+                       &finst->initialRS,
+                       &finst->initialRSSize);
+               if(frtn) {
+                   dbgLog(("feeFEEDDecryptBlock: error decrypting "
+                       "initialRS (%s)\n", feeReturnString(frtn)));
+                   return FR_BadCipherText;
+               }
+
+               /*
+                * we already know how long this should be...
+                */
+               if(finst->initialRSSize != finst->initialRSSize) {
+                   dbgLog(("feeFEEDDecryptBlock: initialRS sync error\n"));
+                   return FR_BadCipherText;
+               }
+
+               /*
+                * Set up clues
+                */
+               if(initFromRS(finst)) {
+                   dbgLog(("feeFEEDDecryptBlock: bad initialRS\n"));
+                   return FR_BadCipherText;
+               }
+               else {
+                   /*
+                    * Normal completion of last cipherblock containing
+                    * initialRS.
+                    */
+                       bprintf(("=== FEED Decrypt: gobbled 0x%x bytes ctext, no ptext (2)\n", 
+                               cipherTextLen));
+                   *plainTextLen = 0;
+                   return FR_Success;
+               }
+       }
+
+       /*
+        * grab xm and clueByte from cipherText
+        */
+       deserializeGiant(cipherText, finst->xm, finst->cp->minBytes);
+       cipherText += finst->cp->minBytes;
+       clueByte = *cipherText;
+
+       if((clueByte & CLUE_BIT) == CLUE_PLUS) {
+               thisClue = finst->cluePlus;
+               thisS = finst->sPlus;
+       }
+       else {
+               thisClue = finst->clueMinus;
+               thisS = finst->sMinus;
+       }
+       if((clueByte & PARITY_BIT) == PARITY_PLUS) {
+               parity = SIGN_PLUS;
+       }
+       else {
+               parity = SIGN_MINUS;
+       }
+
+       /*
+        * recover xp
+        *     xp = xm + clue(+/-) w/parity
+        * adjust clue
+        *     clue[n+1] = r * clue[n] + (s * P1)
+        */
+       elliptic_add(thisClue, finst->xm, finst->xp, finst->cp, parity);
+
+       elliptic_simple(thisClue, finst->r, finst->cp);
+       gtog(thisClue, finst->tmp1);
+       elliptic_add(finst->tmp1, thisS, thisClue, finst->cp, SIGN_PLUS);
+
+       /*
+        * plaintext in xp
+        */
+       #if     FEED_DEBUG
+       printf("decrypt  clue %d\n", clueByte);
+       printf("  xp : "); printGiant(finst->xp);
+       printf("  xm : "); printGiant(finst->xm);
+       printf("  cluePlus  :"); printGiant(finst->cluePlus);
+       printf("  clueMinus :"); printGiant(finst->clueMinus);
+       #endif  // FEED_DEBUG
+
+       if(finalBlock) {
+               /*
+                * Snag data from xp in order to find out how much to move to
+                * *plainText
+                */
+               unsigned char *ptext = (unsigned char*) fmalloc(finst->plainBlockSize);
+
+               serializeGiant(finst->xp, ptext, finst->plainBlockSize);
+               *plainTextLen = ptext[finst->plainBlockSize - 1];
+               if(*plainTextLen == RESID_ZERO) {
+                       bprintf(("=== FEED Decrypt: RESID_ZERO\n"));
+                       *plainTextLen = 0;
+               }
+               else if(*plainTextLen > (finst->plainBlockSize - 1)) {
+                       dbgLog(("feeFEEDDecryptBlock: ptext overflow!\n"));
+                       bprintf(("feeFEEDDecryptBlock: ptext overflow!\n"));
+                       frtn = FR_BadCipherText;
+               }
+               else {
+                       bprintf(("=== FEED Decrypt: resid len 0x%x\n", *plainTextLen));
+                       bcopy(ptext, plainText, *plainTextLen);
+               }
+               ffree(ptext);
+       }
+       else {
+               *plainTextLen = finst->plainBlockSize;
+               serializeGiant(finst->xp, plainText, *plainTextLen);
+       }
+       bprintf(("=== FEED decryptBlock ptextLen 0x%x  ctextLen 0x%x\n",
+               *plainTextLen, cipherTextLen));
+
+       return frtn;
+}
+
+/*
+ * Convenience routines to encrypt & decrypt multi-block data.
+ */
+feeReturn feeFEEDEncrypt(feeFEED feed,
+       const unsigned char *plainText,
+       unsigned plainTextLen,
+       unsigned char **cipherText,             // malloc'd and RETURNED
+       unsigned *cipherTextLen)                // RETURNED
+{
+       const unsigned char     *ptext;                 // per block
+       unsigned                        ptextLen;               // total to go
+       unsigned                        thisPtextLen;           // per block
+       unsigned char           *ctext;                 // per block
+       unsigned                        ctextLen;               // per block
+       unsigned char           *ctextResult;           // to return
+       unsigned                        ctextResultLen;         // size of ctextResult
+       unsigned char           *ctextPtr;
+       unsigned                        ctextLenTotal;          // running total
+       feeReturn                       frtn;
+       int                                     finalBlock;
+       unsigned                        numBlocks;
+       unsigned                        plainBlockSize;
+       #if     FEE_DEBUG
+       unsigned                expectedCtextSize;
+
+       expectedCtextSize = feeFEEDCipherTextSize(feed, plainTextLen, 1);
+       #endif
+       
+       if(plainTextLen == 0) {
+               dbgLog(("feeFEEDDecrypt: NULL plainText\n"));
+               return FR_IllegalArg;
+       }
+
+       ptext = plainText;
+       ptextLen = plainTextLen;
+       ctext = (unsigned char*) fmalloc(feeFEEDCipherBufSize(feed, 1));
+       plainBlockSize = feeFEEDPlainBlockSize(feed);
+       numBlocks = (plainTextLen + plainBlockSize - 1)/plainBlockSize;
+
+       /*
+        * Calculate the worst-case size needed to hold all of the ciphertext
+        */
+       ctextResultLen = feeFEEDCipherTextSize(feed, plainTextLen, 1);
+       ctextResult = (unsigned char*) fmalloc(ctextResultLen);
+       ctextPtr = ctextResult;
+       ctextLenTotal = 0;
+
+       while(1) {
+               if(ptextLen <= plainBlockSize) {
+                       finalBlock = 1;
+                       thisPtextLen = ptextLen;
+               }
+               else {
+                       finalBlock = 0;
+                       thisPtextLen = plainBlockSize;
+               }
+               frtn = feeFEEDEncryptBlock(feed,
+                       ptext,
+                       thisPtextLen,
+                       ctext,
+                       &ctextLen,
+                       finalBlock);
+               if(frtn) {
+                       dbgLog(("feeFEEDEncrypt: encrypt error: %s\n",
+                               feeReturnString(frtn)));
+                       break;
+               }
+               if(ctextLen == 0) {
+                       dbgLog(("feeFEEDEncrypt: null ciphertext\n"));
+                       frtn = FR_Internal;
+                       break;
+               }
+               bcopy(ctext, ctextPtr, ctextLen);
+               ctextLenTotal += ctextLen;
+               if(ctextLenTotal > ctextResultLen) {
+                       dbgLog(("feeFEEDEncrypt: ciphertext overflow\n"));
+                       frtn = FR_Internal;
+                       break;
+               }
+               if(finalBlock) {
+                       break;
+               }
+               ctextPtr += ctextLen;
+               ptext += thisPtextLen;
+               ptextLen -= thisPtextLen;
+       }
+
+       ffree(ctext);
+       if(frtn) {
+               ffree(ctextResult);
+               *cipherText = NULL;
+               *cipherTextLen = 0;
+       }
+       else {
+               *cipherText = ctextResult;
+               *cipherTextLen = ctextLenTotal;
+               #if     FEE_DEBUG
+               if(expectedCtextSize != ctextLenTotal) {
+                   printf("feeFEEDEncrypt: feeFEEDCipherTextSize error!\n");
+                   printf("ptext %d  exp ctext %d  actual ctext %d\n",
+                       plainTextLen,
+                       expectedCtextSize,
+                       ctextLenTotal);
+               }
+               #endif  // FEE_DEBUG
+       }
+       return frtn;
+
+}
+
+feeReturn feeFEEDDecrypt(feeFEED feed,
+       const unsigned char *cipherText,
+       unsigned cipherTextLen,
+       unsigned char **plainText,              // malloc'd and RETURNED
+       unsigned *plainTextLen)                 // RETURNED
+{
+       const unsigned char     *ctext;
+       unsigned                ctextLen;               // total to go
+       unsigned char           *ptext;                 // per block
+       unsigned                ptextLen;               // per block
+       unsigned char           *ptextResult;           // to return
+       unsigned char           *ptextPtr;
+       unsigned                ptextLenTotal;          // running total
+       feeReturn               frtn = FR_Success;
+       int                     finalBlock;
+       unsigned                numBlocks;
+       unsigned                plainBlockSize = feeFEEDPlainBlockSize(feed);
+       unsigned                cipherBlockSize = feeFEEDCipherBlockSize(feed);
+
+       if(cipherTextLen % cipherBlockSize) {
+               dbgLog(("feeFEEDDecrypt: unaligned cipherText\n"));
+               return FR_BadCipherText;
+       }
+       if(cipherTextLen == 0) {
+               dbgLog(("feeFEEDDecrypt: NULL cipherText\n"));
+               return FR_BadCipherText;
+       }
+
+       ptext = (unsigned char*) fmalloc(plainBlockSize);
+       ctext = cipherText;
+       ctextLen = cipherTextLen;
+       numBlocks = cipherTextLen / cipherBlockSize;
+       ptextResult = (unsigned char*) fmalloc(plainBlockSize * numBlocks);
+       ptextPtr = ptextResult;
+       ptextLenTotal = 0;
+
+       while(ctextLen) {
+               if(ctextLen == cipherBlockSize) {
+                   finalBlock = 1;
+               }
+               else {
+                   finalBlock = 0;
+               }
+               frtn = feeFEEDDecryptBlock(feed,
+                       ctext,
+                       cipherBlockSize,
+                       ptext,
+                       &ptextLen,
+                       finalBlock);
+               if(frtn) {
+                       dbgLog(("feeFEEDDecryptBlock: %s\n",
+                               feeReturnString(frtn)));
+                       break;
+               }
+               if(ptextLen) {
+                       if(ptextLen > plainBlockSize) {
+                           dbgLog(("feeFEEDDecrypt: ptext overflow!\n"));
+                           frtn = FR_Internal;
+                           break;
+                       }
+                       bcopy(ptext, ptextPtr, ptextLen);
+                       ptextPtr += ptextLen;
+                       ptextLenTotal += ptextLen;
+               }
+               /*
+                * note ptextLen == 0 is normal termination case for
+                * plainTextLen % plainBlockSize == 0.
+                * Also expected for first 4 blocks of ciphertext;
+                * proceed (we break when ctextLen is exhausted).
+                */
+               ctext += cipherBlockSize;
+               ctextLen -= cipherBlockSize;
+       }
+
+       ffree(ptext);
+       if(frtn) {
+               ffree(ptextResult);
+               *plainText = NULL;
+               *plainTextLen = 0;
+       }
+       else {
+               *plainText = ptextResult;
+               *plainTextLen = ptextLenTotal;
+       }
+       return frtn;
+
+}
+
+#endif /* CRYPTKIT_ASYMMETRIC_ENABLE */