/* Copyright (c) 1998 Apple Computer, Inc. All rights reserved. * * NOTICE: USE OF THE MATERIALS ACCOMPANYING THIS NOTICE IS SUBJECT * TO THE TERMS OF THE SIGNED "FAST ELLIPTIC ENCRYPTION (FEE) REFERENCE * SOURCE CODE EVALUATION AGREEMENT" BETWEEN APPLE COMPUTER, INC. AND THE * ORIGINAL LICENSEE THAT OBTAINED THESE MATERIALS FROM APPLE COMPUTER, * INC. ANY USE OF THESE MATERIALS NOT PERMITTED BY SUCH AGREEMENT WILL * EXPOSE YOU TO LIABILITY. *************************************************************************** * * FeeFEED.c - generic, portable FEED encryption object, expanionless version * * Revision History * ---------------- * 10/06/98 ap * Changed to compile with C++. * 20 Jan 1998 Doug Mitchell at Apple * Mods for primeType == PT_GENERAL case. * 12 Jun 1997 Doug Mitchell at Apple * Was curveOrderJustify(), is lesserX1OrderJustify() * 31 Mar 1997 Doug Mitchell at Apple * Fixed initialRS leak * 3 Mar 1997 Doug Mitchell at Apple * Trimmed plainBlockSize by one byte if q mod 8 = 0 * 30 Jan 1997 Doug Mitchell 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 #include #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 */