X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_cryptkit/lib/feeFEEDExp.c diff --git a/Security/libsecurity_cryptkit/lib/feeFEEDExp.c b/Security/libsecurity_cryptkit/lib/feeFEEDExp.c new file mode 100644 index 00000000..4126c2bb --- /dev/null +++ b/Security/libsecurity_cryptkit/lib/feeFEEDExp.c @@ -0,0 +1,735 @@ +/* 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. + *************************************************************************** + * + * FeeFEEDExp.c - generic FEED encryption object, 2:1 expansion + * + * 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() + * 03 Mar 1997 at Apple + * Trimmed plainBlockSize by one byte if q mod 8 = 0 + * 03 Feb 97 at NeXT + * Renamed to feeFEEDExp.c + * Justified random xaux to [2, minimumX1Order] + * Added feeFEEDExpCipherTextSize() + * 15 Jan 97 at NeXT + * Cleaned up which_curve/index code to use CURVE_MINUS/CURVE_PLUS + * 28 Aug 96 at NeXT + * Created from Blaine Garst's NSFEECryptor.m. + */ + +#include "ckconfig.h" + +#if CRYPTKIT_ASYMMETRIC_ENABLE + +#include "feeTypes.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 "feeDebug.h" +#include + +#define FEED_DEBUG 0 + +#define PRINT_GIANT(g) printGiant(g) + +/* + * Format of clue byte. Currently just one bit. + */ +#define CLUE_ELL_ADD_SIGN 0x01 +#define CLUE_ELL_ADD_SIGN_PLUS 0x01 +#define CLUE_ELL_ADD_SIGN_MINUS 0x00 + +/* + * Private data. + */ +typedef struct { + key plus; + key minus; + unsigned plainBlockSize; /* plaintext block size */ + unsigned cipherBlockSize;/* ciphertext block size */ + curveParams *cp; + giant gPriv; /* private data, only for decrypt */ + /* one of the follow two is valid for encrypt */ + feeRand rand; /* only created for encrypt */ + feeRandFcn randFcn; + void *randRef; + + /* + * temporary variables used for encrypt/decrypt. The values in these + * is 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 xc; /* clue = r(P1?) */ + giant xq; /* r(pubB?) or priB?(xc) */ + giant xm; /* ciphertext */ + giant xaux; /* scratch */ + unsigned char *randData; /* only created for encrypt */ +} feedInst; + +/* + * "zero residue" indicator. + */ +#define RESID_ZERO 0xff + +/* + * Alloc and init a feeFEEDExp object associated with specified feePubKey. + */ +feeFEEDExp feeFEEDExpNewWithPubKey( + feePubKey pubKey, + feeRandFcn randFcn, // optional + void *randRef) +{ + feedInst *finst = (feedInst *) fmalloc(sizeof(feedInst)); + giant privGiant; + + finst->cp = curveParamsCopy(feePubKeyCurveParams(pubKey)); + finst->plus = new_public_with_key(feePubKeyPlusCurve(pubKey), + finst->cp); + finst->minus = new_public_with_key(feePubKeyMinusCurve(pubKey), + finst->cp); + + /* + * These might yield NULL data; we can only encrypt in that case. + */ + privGiant = feePubKeyPrivData(pubKey); + if(privGiant) { + finst->gPriv = newGiant(finst->cp->maxDigits); + gtog(privGiant, finst->gPriv); + } + else { + finst->gPriv = NULL; + } + + /* + * Conservative, rounding down, on plaintext blocks since we don't + * want to split bytes. + */ + if(finst->cp->primeType == FPT_General) { + unsigned blen = bitlen(finst->cp->basePrime); + + finst->plainBlockSize = blen / 8; + if((blen % 8) == 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--; + } + } + + /* + * One block of ciphertext - two giants (with implied sign) and a + * parity byte + */ + finst->cipherBlockSize = (2 * finst->cp->minBytes) + 1; + + finst->xp = newGiant(finst->cp->maxDigits); + finst->xc = newGiant(finst->cp->maxDigits); + finst->xq = newGiant(finst->cp->maxDigits); + finst->xm = newGiant(finst->cp->maxDigits); + finst->xaux = newGiant(finst->cp->maxDigits); + finst->rand = NULL; + finst->randData = NULL; + finst->randFcn = randFcn; + finst->randRef = randRef; + return finst; +} + +void feeFEEDExpFree(feeFEEDExp feed) +{ + feedInst *finst = (feedInst *) feed; + + free_key(finst->plus); + free_key(finst->minus); + freeGiant(finst->xc); + clearGiant(finst->xp); freeGiant(finst->xp); + clearGiant(finst->xq); freeGiant(finst->xq); + freeGiant(finst->xm); + clearGiant(finst->xaux); freeGiant(finst->xaux); + if(finst->gPriv) { + clearGiant(finst->gPriv); + freeGiant(finst->gPriv); + } + if(finst->rand) { + feeRandFree(finst->rand); + } + if(finst->randData) { + ffree(finst->randData); + } + if(finst->cp) { + freeCurveParams(finst->cp); + } + ffree(finst); +} + +unsigned feeFEEDExpPlainBlockSize(feeFEEDExp feed) +{ + feedInst *finst = (feedInst *) feed; + + return finst->plainBlockSize; +} + +unsigned feeFEEDExpCipherBlockSize(feeFEEDExp feed) +{ + feedInst *finst = (feedInst *) feed; + + return finst->cipherBlockSize; +} + +unsigned feeFEEDExpCipherBufSize(feeFEEDExp feed) +{ + feedInst *finst = (feedInst *) feed; + + return 2 * finst->cipherBlockSize; +} + +/* + * Return the size of ciphertext to hold specified size of plaintext. + */ +unsigned feeFEEDExpCipherTextSize(feeFEEDExp feed, unsigned plainTextSize) +{ + /* + * Normal case is one block of ciphertext for each block of + * plaintext. Add one cipherBlock if + * plainTextSize % plainBlockSize == 0. + */ + feedInst *finst = (feedInst *) feed; + unsigned blocks = (plainTextSize + finst->plainBlockSize - 1) / + finst->plainBlockSize; + + if((plainTextSize % finst->plainBlockSize) == 0) { + blocks++; + } + return blocks * finst->cipherBlockSize; +} + +/* + * Return the size of plaintext to hold specified size of decrypted ciphertext. + */ +unsigned feeFEEDExpPlainTextSize(feeFEEDExp feed, unsigned cipherTextSize) +{ + feedInst *finst = (feedInst *) feed; + unsigned blocks = (cipherTextSize + finst->cipherBlockSize - 1) / + finst->cipherBlockSize; + + return blocks * finst->plainBlockSize; +} + +/* + * Encrypt a block or less of data. Caller malloc's cipherText. + */ +feeReturn feeFEEDExpEncryptBlock(feeFEEDExp feed, + const unsigned char *plainText, + unsigned plainTextLen, + unsigned char *cipherText, + unsigned *cipherTextLen, // RETURNED + int finalBlock) +{ + feedInst *finst = (feedInst *) feed; + int index; /* which curve (+/- 1) */ + char g = 0; /* parity, which_curve bits in ciphertext */ + key B; + unsigned char *ptext; /* for final block */ + unsigned ctextLen; + feeReturn frtn = FR_Success; + giant x1; + unsigned randLen; + curveParams *cp = finst->cp; + + if(plainTextLen > finst->plainBlockSize) { + return FR_IllegalArg; + } + else if ((plainTextLen < finst->plainBlockSize) && !finalBlock) { + return FR_IllegalArg; + } + + /* + * Init only on first encrypt + */ + if((finst->randFcn == NULL) && (finst->rand == NULL)) { + finst->rand = feeRandAlloc(); + } + if(finst->randData == NULL) { + finst->randData = (unsigned char*) fmalloc(finst->cp->minBytes); + } + + /* + * plaintext as giant xp + */ + if(finalBlock) { + ptext = (unsigned char*) fmalloc(finst->plainBlockSize); + bzero(ptext, finst->plainBlockSize); + if(plainTextLen) { + /* + * 0 for empty block with resid length 0 + */ + bcopy(plainText, ptext, plainTextLen); + } + if(plainTextLen < finst->plainBlockSize) { + if(plainTextLen == 0) { + /* + * Special case - can't actually write zero here; + * it screws up deserializing the giant during + * decrypt + */ + ptext[finst->plainBlockSize - 1] = RESID_ZERO; + } + else { + ptext[finst->plainBlockSize - 1] = plainTextLen; + } + #if FEED_DEBUG + printf("encrypt: resid 0x%x\n", ptext[finst->plainBlockSize - 1]); + #endif + } + /* + * else handle evenly aligned case below... + */ + deserializeGiant(ptext, finst->xp, finst->plainBlockSize); + ffree(ptext); + } + else { + deserializeGiant(plainText, finst->xp, plainTextLen); + } + #if FEED_DEBUG + printf("encrypt:\n"); + printf(" xp : "); PRINT_GIANT(finst->xp); + #endif // FEED_DEBUG + + /* + * pick curve B? that data lies upon + */ + index = which_curve(finst->xp, finst->cp); + if(index == CURVE_PLUS) { + B = finst->plus; + x1 = finst->cp->x1Plus; + } + else { + B = finst->minus; + x1 = finst->cp->x1Minus; + } + #if FEED_DEBUG + printf(" which_curve: %s\n", + (index == CURVE_PLUS) ? "CURVE_PLUS" : "CURVE_MINUS"); + #endif + + /* + * random number as giant xaux + */ + randLen = cp->minBytes; + if(finst->randFcn != NULL) { + finst->randFcn(finst->randRef, finst->randData, randLen); + } + else { + feeRandBytes(finst->rand, finst->randData, randLen); + } + deserializeGiant(finst->randData, finst->xaux, randLen); + + #if FEE_DEBUG + if(isZero(finst->xaux)) { + printf("feeFEEDExpEncryptBlock: random xaux = 0!\n"); + } + #endif // FEE_DEBUG + /* + * Justify random # to be in [2, minimumX1Order]. + */ + lesserX1OrderJustify(finst->xaux, cp); + #if FEED_DEBUG + printf(" xaux: "); PRINT_GIANT(finst->xaux); + #endif // FEED_DEBUG + + gtog(B->x, finst->xq); // xq = pubB? + elliptic_simple(finst->xq, finst->xaux, cp); + // xq = r(pubB?) + #if FEED_DEBUG + printf(" r(pubB?): "); PRINT_GIANT(finst->xq); + #endif + elliptic_add(finst->xp, finst->xq, finst->xm, cp, SIGN_PLUS); + // xm = data + r(pubB?) + gtog(x1, finst->xc); + elliptic_simple(finst->xc, finst->xaux, cp); + // xc = r(P1?) + elliptic_add(finst->xm, finst->xq, finst->xaux, cp, SIGN_PLUS); + // xaux = xm + xq (for curve +1) + // = (data + r(pubB?)) + r(pubB?) + if(gcompg(finst->xaux, finst->xp) == 0) { + g |= CLUE_ELL_ADD_SIGN_PLUS; + } + else { + g |= CLUE_ELL_ADD_SIGN_MINUS; + #if FEED_DEBUG + /* this better be true.... */ + elliptic_add(finst->xm, finst->xq, finst->xaux, cp, SIGN_MINUS); + if(gcompg(finst->xaux, finst->xp)) { + printf("*******elliptic_add(xm, xq, -1) != xp! *************\n"); + printf(" xq : "); PRINT_GIANT(finst->xq); + printf(" ell_add(xm, xq, -1) : "); PRINT_GIANT(finst->xaux); + } + #endif + } // g = (xaux == data) ? add : subtract + + /* + * Ciphertext = (xm, xc, g) + */ + serializeGiant(finst->xm, cipherText, cp->minBytes); + cipherText += cp->minBytes; + serializeGiant(finst->xc, cipherText, cp->minBytes); + cipherText += cp->minBytes; + *cipherText++ = g; + ctextLen = finst->cipherBlockSize; + #if FEED_DEBUG + printf(" xm : "); PRINT_GIANT(finst->xm); + printf(" xc : "); PRINT_GIANT(finst->xc); + printf(" g : %d\n", g); + #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 + + #if FEED_DEBUG + printf("encrypt: one more empty block\n"); + #endif + frtn = feeFEEDExpEncryptBlock(feed, + NULL, // plainText not used + 0, // resid + cipherText, // append... + &moreCipher, + 1); + if(frtn == FR_Success) { + ctextLen += moreCipher; + } + } + + *cipherTextLen = ctextLen; + return frtn; +} + +/* + * Decrypt (exactly) a block of data. Caller malloc's plainText. Always + * generates feeFEEDExpPlainBlockSize of plaintext, unless finalBlock is + * non-zero (in which case feeFEEDExpPlainBlockSize or less bytes of + * plainText are generated). + */ +feeReturn feeFEEDExpDecryptBlock(feeFEEDExp feed, + const unsigned char *cipherText, + unsigned cipherTextLen, + unsigned char *plainText, + unsigned *plainTextLen, // RETURNED + int finalBlock) +{ + feedInst *finst = (feedInst *) feed; + char g; + int s; + feeReturn frtn = FR_Success; + curveParams *cp = finst->cp; + + if(finst->gPriv == NULL) { + /* + * Can't decrypt without private data + */ + return FR_BadPubKey; + } + + /* + * grab xm, xc, and g from cipherText + */ + deserializeGiant(cipherText, finst->xm, finst->cp->minBytes); + cipherText += finst->cp->minBytes; + deserializeGiant(cipherText, finst->xc, finst->cp->minBytes); + cipherText += finst->cp->minBytes; + g = *cipherText; + #if FEED_DEBUG + printf("decrypt g=%d\n", g); + printf(" privKey : "); PRINT_GIANT(finst->gPriv); + printf(" xm : "); PRINT_GIANT(finst->xm); + printf(" xc : "); PRINT_GIANT(finst->xc); + #endif // FEED_DEBUG + + if((g & CLUE_ELL_ADD_SIGN) == CLUE_ELL_ADD_SIGN_PLUS) { + s = SIGN_PLUS; + } + else { + s = SIGN_MINUS; + } + + /* + * xc = r(P1?) + * xc := r(P1?)(pri) = xq + * xp = data + r(priB+) +/- pri(rB?) + */ + elliptic_simple(finst->xc, finst->gPriv, cp); + #if FEED_DEBUG + printf(" xc1 : "); PRINT_GIANT(finst->xc); + #endif + elliptic_add(finst->xm, finst->xc, finst->xp, cp, s); + + /* + * plaintext in xp + */ + #if FEED_DEBUG + printf(" xp : "); PRINT_GIANT(finst->xp); + #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 FEED_DEBUG + printf("decrypt: resid 0x%x\n", *plainTextLen); + #endif + if(*plainTextLen == RESID_ZERO) { + *plainTextLen = 0; + } + else if(*plainTextLen > (finst->plainBlockSize - 1)) { + dbgLog(("feeFEEDExpDecryptBlock: ptext overflow!\n")); + frtn = FR_BadCipherText; + } + else { + bcopy(ptext, plainText, *plainTextLen); + } + ffree(ptext); + } + else { + *plainTextLen = finst->plainBlockSize; + serializeGiant(finst->xp, plainText, *plainTextLen); + } + return frtn; +} + +/* + * Convenience routines to encrypt & decrypt multi-block data. + */ +feeReturn feeFEEDExpEncrypt(feeFEEDExp 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; + unsigned char *ctextPtr; + unsigned ctextLenTotal; // running total + feeReturn frtn; + int finalBlock; + unsigned numBlocks; + unsigned plainBlockSize; + + if(plainTextLen == 0) { + dbgLog(("feeFEEDExpDecrypt: NULL plainText\n")); + return FR_IllegalArg; + } + + ptext = plainText; + ptextLen = plainTextLen; + ctext = (unsigned char*) fmalloc(feeFEEDExpCipherBufSize(feed)); + plainBlockSize = feeFEEDExpPlainBlockSize(feed); + numBlocks = (plainTextLen + plainBlockSize - 1)/plainBlockSize; + ctextResultLen = (numBlocks + 1) * feeFEEDExpCipherBlockSize(feed); + ctextResult = (unsigned char*) fmalloc(ctextResultLen); + ctextPtr = ctextResult; + ctextLenTotal = 0; + + while(1) { + if(ptextLen <= plainBlockSize) { + finalBlock = 1; + thisPtextLen = ptextLen; + } + else { + finalBlock = 0; + thisPtextLen = plainBlockSize; + } + frtn = feeFEEDExpEncryptBlock(feed, + ptext, + thisPtextLen, + ctext, + &ctextLen, + finalBlock); + if(frtn) { + dbgLog(("feeFEEDExpEncrypt: encrypt error: %s\n", + feeReturnString(frtn))); + break; + } + if(ctextLen == 0) { + dbgLog(("feeFEEDExpEncrypt: null ciphertext\n")); + frtn = FR_Internal; + break; + } + bcopy(ctext, ctextPtr, ctextLen); + ctextLenTotal += ctextLen; + if(ctextLenTotal > ctextResultLen) { + dbgLog(("feeFEEDExpEncrypt: 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(feeFEEDExpCipherTextSize(feed, plainTextLen) != + ctextLenTotal) { + printf("feeFEEDExpEncrypt: feeFEEDCipherTextSize " + "error!\n"); + printf("ptext %d exp ctext %d actual ctext %d\n", + plainTextLen, + feeFEEDExpCipherTextSize(feed, plainTextLen), + ctextLenTotal); + } + #endif // FEE_DEBUG + } + return frtn; + +} + +feeReturn feeFEEDExpDecrypt(feeFEEDExp 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 = + feeFEEDExpPlainBlockSize(feed); + unsigned cipherBlockSize = + feeFEEDExpCipherBlockSize(feed); + + if(cipherTextLen % cipherBlockSize) { + dbgLog(("feeFEEDExpDecrypt: unaligned cipherText\n")); + return FR_BadCipherText; + } + if(cipherTextLen == 0) { + dbgLog(("feeFEEDExpDecrypt: 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 = feeFEEDExpDecryptBlock(feed, + ctext, + cipherBlockSize, + ptext, + &ptextLen, + finalBlock); + if(frtn) { + dbgLog(("feeFEEDExpDecryptBlock: %s\n", + feeReturnString(frtn))); + break; + } + if(ptextLen == 0) { + /* + * Normal termination case for + * plainTextLen % plainBlockSize == 0 + */ + if(!finalBlock) { + dbgLog(("feeFEEDExpDecrypt: decrypt sync" + " error!\n")); + frtn = FR_BadCipherText; + } + break; + } + else if(ptextLen > plainBlockSize) { + dbgLog(("feeFEEDExpDecrypt: ptext overflow!\n")); + frtn = FR_Internal; + break; + } + else { + bcopy(ptext, ptextPtr, ptextLen); + ptextPtr += ptextLen; + ptextLenTotal += ptextLen; + } + 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 */