X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/Security/libsecurity_comcryption/lib/comcryption.c diff --git a/Security/libsecurity_comcryption/lib/comcryption.c b/Security/libsecurity_comcryption/lib/comcryption.c deleted file mode 100644 index a7d23e95..00000000 --- a/Security/libsecurity_comcryption/lib/comcryption.c +++ /dev/null @@ -1,1438 +0,0 @@ -/* - * Copyright (c) 1997,2011-2012,2014 Apple Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -#include -#include -#include -#include "comcryption.h" -#include "comDebug.h" -#include "comcryptPriv.h" - -#if COM_PROFILE - -unsigned comProfEnable; -comprof_t cmcTotal; -comprof_t cmcQueSearch; -comprof_t cmcQueMatchMove; -comprof_t cmcQueMissMove; -comprof_t cmcLevel2; -comprof_t cmcPerWordOhead; - -#endif /*COM_PROFILE*/ - -void comMallocRegister(comMallocExternFcn *mallocExtern, - comFreeExternFcn *freeExtern) -{ - comMallocExt = mallocExtern; - comFreeExt = freeExtern; -} - -/* - * Call once at startup. The resulting comcryptObj can be reused multiple - * times. - */ -comcryptObj comcryptAlloc(void) -{ - comcryptPriv *cpriv = (comcryptPriv *) ascMalloc(sizeof(comcryptPriv)); - - if(cpriv == NULL) { - return NULL; - } - memset(cpriv, 0, sizeof(comcryptPriv)); - -#if COMCRYPT_EXPORT_ONLY - cpriv->key = (unsigned char *)ascMalloc(EXPORT_KEY_SIZE); -#else /*COMCRYPT_EXPORT_ONLY*/ - cpriv->key = (unsigned char *)ascMalloc(COMCRYPT_MAX_KEYLENGTH); -#endif /*COMCRYPT_EXPORT_ONLY*/ - - if(cpriv->key == NULL) { - return NULL; - } - cpriv->map = (unsigned char *)ascMalloc(256); - cpriv->invmap = (unsigned char *)ascMalloc(256); - if((cpriv->map == NULL) || (cpriv->invmap == NULL)) { - return NULL; - } - mallocCodeBufs(&cpriv->cbuf); - if((cpriv->cbuf.codeBuf == NULL) || - (cpriv->cbuf.level2Buf == NULL)) { - return NULL; - } - #if QUEUE_LOOKAHEAD - if(cpriv->cbuf.lookAhead == NULL) { - return NULL; - } - #endif - - /* - * Hard coded limit of two levels of comcryption - */ - cpriv->cbuf.nextBuf = (comcryptBuf *)ascMalloc(sizeof(comcryptBuf)); - if(cpriv->cbuf.nextBuf == NULL) { - return NULL; - } - mallocCodeBufs(cpriv->cbuf.nextBuf); - if((cpriv->cbuf.nextBuf->codeBuf == NULL) || - (cpriv->cbuf.nextBuf->level2Buf == NULL)) { - return NULL; - } - #if QUEUE_LOOKAHEAD - if(cpriv->cbuf.nextBuf->lookAhead == NULL) { - return NULL; - } - #endif - - cpriv->cbuf.nextBuf->nextBuf = NULL; - return cpriv; -} - -/* - * Call this before starting every stream process - */ -comcryptReturn comcryptInit( - comcryptObj cobj, - const unsigned char *key, - unsigned keyLen, - comcryptOptimize optimize) // CCO_SIZE, etc. -{ - comcryptPriv *cpriv = (comcryptPriv *)cobj; - unsigned maxKeySize; - -#if COMCRYPT_EXPORT_ONLY - /* - * FIXME - NSA might not be satisfied with this, may have to enforce - * elsewhere - */ - maxKeySize = EXPORT_KEY_SIZE; -#else /*COMCRYPT_EXPORT_ONLY*/ - maxKeySize = COMCRYPT_MAX_KEYLENGTH; -#endif /*COMCRYPT_EXPORT_ONLY*/ - - if(keyLen > maxKeySize) { - keyLen = maxKeySize; - } - memmove(cpriv->key, key, keyLen); - cpriv->keybytes = keyLen; - cpriv->cbuf.codeBufLength = 0; - cpriv->cbuf.nextBuf->codeBufLength = 0; - cpriv->version = 0; - cpriv->versionBytes = 0; - cpriv->spareBytes = 0; - cpriv->optimize = optimize; - - /* - * Derive feature enable bits from optimize arg. This is highly likely - * to change.... - */ - cpriv->level2enable = 1; - cpriv->sigSeqEnable = 1; - switch(optimize) { - case CCO_TIME: - cpriv->level2enable = 0; - break; - case CCO_TIME_SIZE: - cpriv->sigSeqEnable = 0; - break; - default: - break; - } -#if QUEUE_LOOKAHEAD - cpriv->laEnable = 1; -#else /* QUEUE_LOOKAHEAD */ - cpriv->laEnable = 0; -#endif /* QUEUE_LOOKAHEAD */ - - /* - * init queue and maps - */ - initCodeBufs(&cpriv->cbuf, key, keyLen, cpriv->laEnable, - cpriv->sigSeqEnable); - initCodeBufs(cpriv->cbuf.nextBuf, key, keyLen, cpriv->laEnable, - cpriv->sigSeqEnable); - key_perm(key, keyLen, cpriv->map, cpriv->invmap); - return CCR_SUCCESS; -} - -/* - * Free a comcryptObj object obtained via comcryptAlloc() - */ -void comcryptObjFree(comcryptObj cobj) -{ - comcryptPriv *cpriv = (comcryptPriv *)cobj; - - if(cpriv->key != NULL) { - ascFree(cpriv->key); - } - if(cpriv->map != NULL) { - ascFree(cpriv->map); - } - if(cpriv->invmap != NULL) { - ascFree(cpriv->invmap); - } - freeCodeBufs(&cpriv->cbuf); - ascFree(cpriv); -} - -/* - * Return the maximum input buffer size allowed for for specified - * output buffer size. Note that for both comcrypt and decomcrypt, - * to cover the worst case, the output buffer always has to be - * larger than the input buffer. - */ -unsigned comcryptMaxInBufSize(comcryptObj cobj, - unsigned outBufSize, - comcryptOp op) -{ - unsigned fullBlocks; - unsigned minCblockSize; - unsigned resid; - unsigned rtn; - unsigned tokenBytes; - comcryptPriv *cpriv = (comcryptPriv *)cobj; - unsigned ptextFromCodeBuf; - - switch(op) { - case CCOP_COMCRYPT: - /* - * Worst case: no compression. Also, establish a minimum - * ciphertext size to accomodate header and one block. - */ - minCblockSize = MIN_CBLOCK_SIZE; - if(cpriv->versionBytes == 0) { - minCblockSize += CTEXT_HDR_SIZE; - } - if(outBufSize < (minCblockSize)) { - return 0; - } - if(cpriv->versionBytes == 0) { - outBufSize -= CTEXT_HDR_SIZE; - } - fullBlocks = outBufSize / MAX_CBLOCK_SIZE; - rtn = (fullBlocks * CC_BLOCK_SIZE); // bytes of ptext - - /* - * code must be even aligned, then chop off one for odd ptext - */ - rtn &= 0xfffffffe; - rtn--; - if(rtn <= 0) { - return 0; - } - resid = outBufSize % MAX_CBLOCK_SIZE; - if(resid) { - rtn += resid; - - /* - * Account for resid block overhead - */ - if(rtn < MIN_CBLOCK_SIZE) { - return 0; - } - rtn -= MIN_CBLOCK_SIZE; - - tokenBytes = TOKEN_BYTES_FROM_PTEXT(resid); - if(rtn <= tokenBytes) { - return 0; - } - rtn -= tokenBytes; - } - if(rtn > INBUF_TRUNC_THRESH) { - /* - * Truncate to even block size to minimize partial cipherblocks - */ - rtn &= ~(CC_BLOCK_SIZE - 1); - } - return rtn; - - case CCOP_DECOMCRYPT: - /* - * Worst case - 4:1 compression and an almost full block in - * codeBuf. Note 4:1 is a super-conservative, easy arithmetic - * version of (9/16) squared... - */ - ptextFromCodeBuf = cpriv->cbuf.codeBufLength * 4; - if(outBufSize < ptextFromCodeBuf) { - /* decrypting codeBuf might overflow output (plaintext) - * buffer - won't be able to move anything */ - rtn = 0; - } - else { - /* can decrypt (this much plainText - ptextFromCodeBuf) / 4 */ - rtn = (outBufSize - ptextFromCodeBuf) / 4; - } - - /* may be able to handle a bit extra for initial decrypt... */ - if(cpriv->versionBytes < VERSION_BYTES) { - rtn += (VERSION_BYTES - cpriv->versionBytes); - } - if(cpriv->spareBytes < SPARE_BYTES) { - rtn += (SPARE_BYTES - cpriv->spareBytes); - } - return rtn; - - default: - ddprintf(("bogus op (%d) in comcryptMaxInBufSize()\n", op)); - return 0; - } -} - -/* - * Return the maximum output buffer size for specified input buffer size. - * Output buffer size will always be larger than input buffer size. - */ -unsigned comcryptMaxOutBufSize(comcryptObj cobj, - unsigned inBufSize, - comcryptOp op, - char final) -{ - unsigned fullBlocks; - unsigned resid; - unsigned rtn; - comcryptPriv *cpriv = (comcryptPriv *)cobj; - - switch(op) { - case CCOP_COMCRYPT: - fullBlocks = inBufSize / CC_BLOCK_SIZE; - rtn = fullBlocks * MAX_CBLOCK_SIZE; - resid = inBufSize % CC_BLOCK_SIZE; - if(resid != 0) { - /* - * partial block - */ - unsigned tokenBytes = TOKEN_BYTES_FROM_PTEXT(resid); - - rtn += MIN_CBLOCK_SIZE; - rtn += tokenBytes; - rtn += resid; // no compression - if(resid & 1) { - rtn++; // oddByte uses extra - } - } - if((cpriv == NULL) || // i.e., we're being called from mallocCodeBufs - (cpriv->versionBytes == 0)) { - rtn += CTEXT_HDR_SIZE; // first of a stream - } - return rtn; - - case CCOP_DECOMCRYPT: - /* - * Here assume max compression, including resid block in codeBuf - */ - inBufSize += cpriv->cbuf.codeBufLength; - if(inBufSize) { - /* may be able to handle a bit extra for initial decrypt... */ - unsigned delta; - if(cpriv->versionBytes < VERSION_BYTES) { - delta = VERSION_BYTES - cpriv->versionBytes; - if(inBufSize > delta) { - inBufSize -= delta; - } - else { - inBufSize = 0; - } - } - if(cpriv->spareBytes < SPARE_BYTES) { - delta = SPARE_BYTES - cpriv->spareBytes; - if(inBufSize > delta) { - inBufSize -= delta; - } - else { - inBufSize = 0; - } - } - } - rtn = 4 * inBufSize; - return rtn; - - default: - ddprintf(("bogus op (%d) in comcryptMaxOutBufSize()\n", op)); - return 0; - } -} - -/* - * Threshold for using memmove() rather than hard-coded loop for - * moving queue segment. This was derived empirically on a Pentium; - * we should do similar measurements on PPC. - */ -#define QUEUE_MEMMOVE_THRESH 3 - -/* - * peek at queue[0] before search. This appears to only be a win for - * constant plaintext, i.e., the codeword is almost always at queue[0]. - */ -#define QUEUE_PEEK 0 - -/* - * Comcrypt one block. - */ -static comcryptReturn comcryptBlock( - comcryptPriv *cpriv, - comcryptBuf *cbuf, // not necessarily cpriv->cbuf - const unsigned char *plainText, - unsigned plainTextLen, - unsigned char *cipherText, - unsigned *cipherTextLen, // IN/OUT - unsigned recursLevel) -{ - unsigned char *byteCodePtr; - unsigned char *destByteCodePtr; - unsigned char *longCodePtr; - unsigned char *startLongCodePtr; - unsigned char *tokenPtr; - unsigned char *startTokenPtr; - unsigned char *startCtextPtr = cipherText; - unsigned numTokenBytes; // in bytes, constant - unsigned short codeWord; - unsigned oddByte = 0; - unsigned match; - unsigned jmatch=0; - unsigned tokenDex = 0; // index into array of token bits - unsigned j; - unsigned numLongCodes = 0; - unsigned numByteCodes = 0; - unsigned totalCipherTextLen; - unsigned above; - unsigned jmatchTotal = 0; - unsigned jmatchAvg; - comcryptReturn crtn; - unsigned char blockDesc = CBD_MAGIC; - unsigned fullBlock = 0; - int len; - queueElt *src; - queueElt *dst; - queueElt *cbufq = &cbuf->queue[0]; - - /* - * 'nibble' is added to 'above' in the call to nextSigWord() for - * additional security. - * - * Normal case : nibble = keynybble() - * last word on odd byte : nibble = nibbleDex - * hit on queue q : nibble = nibbleDex (optimize to avoid keynybble() - * call) - */ - unsigned char nibble; - - COMPROF_LOCALS; - - #if COM_LA_DEBUG - if(testLookAhead(cbuf, 0, 0)) { - return CCR_INTERNAL; - } - #endif - - laprintf(("comcryptBlock recurs level %d\n", recursLevel)); - - /* - * Set up ptrs for the three arrays we'll be writing - */ - tokenPtr = cipherText + CTBO_NUM_TOKENS + 1; - if(plainTextLen >= (CC_BLOCK_SIZE - 1)) { - /* - * Optimized for full block - no token count in block. Note - * that plainTextLen == (CC_BLOCK_SIZE - 1) is also a full block - * in that it uses up a full block's worth of tokens! - */ - numTokenBytes = CC_BLOCK_SIZE >> 4; - tokenPtr--; - blockDesc |= CBD_FULL_BLOCK; - fullBlock = 1; - } - else { - numTokenBytes = (plainTextLen + 15) >> 4; - } - longCodePtr = tokenPtr + numTokenBytes; - startLongCodePtr = longCodePtr; - byteCodePtr = cbuf->codeBuf; - startTokenPtr = tokenPtr; - - if((unsigned)(longCodePtr - cipherText) > *cipherTextLen) { - ddprintf(("comcryptBlock: short block (1)\n")); - return CCR_OUTBUFFER_TOO_SMALL; - } - memset(tokenPtr, 0, numTokenBytes); - - /* - * Entering time-critical region. This loop executes once for every - * 2 bytes of plaintext. Make every attempt to streamline the code - * here; avoid function calls in favor of macros; etc. - */ - while(plainTextLen != 0) { - - /* - * assemble a 16-bit word from two bytes if possible - */ - if(plainTextLen == 1) { - /* - * Odd byte case - */ - codeWord = ((unsigned short)(cpriv->map[*plainText]) << 8) | - cpriv->map[0]; // a bit of obfuscation - mapped zero - oddByte = 1; - blockDesc |= CBD_ODD; - plainTextLen--; - } - else { - codeWord = ((unsigned short)(cpriv->map[*plainText]) << 8) | - (unsigned short)(cpriv->map[plainText[1]]); - plainText += 2; - plainTextLen -= 2; - } - - /* - * Calibrate how much profiling is costing us. - */ - COMPROF_START; - COMPROF_END(cmcPerWordOhead); - - /* - * See if this word is in queue[]. Skip if oddByte; we'll force - * a 16-bit word in that case. Also skip the search if we know - * via lookahead that a search would be fruitless. - */ - COMPROF_START; /* cmcQueSearch */ - match = 0; - do { /* while 0 - for easy breaks w/o goto */ - - /* - * First handle some optimizations and special cases - */ - if(oddByte) { - break; // force longcode - } - -#if QUEUE_PEEK - if(cbufq[0] == codeWord) { - match = 1; - jmatch = 0; - break; - - } -#endif /*QUEUE_PEEK*/ - - if(cpriv->laEnable && !inQueue(cbuf, codeWord)) { - break; - } - - /* - * OK, do the gruntwork search - */ - for(j=0; j < QLEN; j++) { - if(cbufq[j] == codeWord) { - match = 1; - jmatch = j; - break; - } - } - -#if COM_LA_DEBUG - if(cpriv->laEnable && !match) { - printf("inQueue, not found in queue!\n"); - return CCR_INTERNAL; - } - - /* - * Search for duplicates. - */ - if(match) { - for(j=jmatch+1; jkey, cpriv->keybytes, - (cbuf->nybbleDex)++); -#endif /*SKIP_NIBBLE_ON_QUEUE_0*/ - - COMPROF_START; - if(match) { - /* - * 16-bit symbol is in queue. 8 bits of ciphertext, token bit is 0. - */ - if(jmatch == 0) { - /* - * Optimization: jmatch = 0. Keep state machine in sync, - * but skip queue update. - */ - above = 0; - laprintf(("...queue hit at queue[0]\n")); -#if SKIP_NIBBLE_ON_QUEUE_0 - nibble = (cbuf->nybbleDex)++; -#endif /*SKIP_NIBBLE_ON_QUEUE_0*/ - } - else { -#if SKIP_NIBBLE_ON_QUEUE_0 - nibble = keynybble(cpriv->key, cpriv->keybytes, - (cbuf->nybbleDex)++); -#endif /*SKIP_NIBBLE_ON_QUEUE_0*/ - - above = (cbuf->f1 * jmatch * (16 + nibble)) >> 9; - - /* - * queue[above..(jmatch-1)] move one element towards end - * queue[above] = this codeWord - */ - laprintf(("...queue hit, moving 0x%x from 0x%x to 0x%x\n", - codeWord, jmatch, above)); - - len = (int)jmatch - (int)above; - if(len > QUEUE_MEMMOVE_THRESH) { - src = &cbufq[above]; - dst = src + 1; - len *= sizeof(queueElt); - memmove(dst, src, len); - } - else { - for(j = jmatch; j>above; j--) { - cbufq[j] = cbufq[j-1]; - } - } - - cbufq[above] = codeWord; -#if COM_LA_DEBUG - if(testLookAhead(cbuf, above, jmatch)) { - return CCR_INTERNAL; - } -#endif /*COM_LA_DEBUG*/ - } - COMPROF_END(cmcQueMatchMove); - - codeWord = jmatch; - incr1byteFrags(recursLevel); - jmatchTotal += jmatch; - } - else if(oddByte == 0) { - /* - * 16-bit symbol is not in queue. 16 bits of ciphertext. - * Token bit is 1. - * - * queue[above...QLEN-1] move one element toward end - * queue[QLEN-1] discarded - * queue[above] = new codeword - * - * Note we skip this queue manipulation in the oddbyte case, since - * we don't really know (or care) if the current code word is in - * the queue or not. - */ -#if SKIP_NIBBLE_ON_QUEUE_0 - nibble = keynybble(cpriv->key, cpriv->keybytes, - (cbuf->nybbleDex)++); -#endif /*SKIP_NIBBLE_ON_QUEUE_0*/ - - above = ABOVE(cbuf->f2) + nibble; - -#if COM_DEBUG - if(above > QLEN) { - printf("Hey Doug! above %d QLEN %d\n", above, QLEN); - return CCR_INTERNAL; - } -#endif - - laprintf(("...queue miss, adding 0x%x at 0x%x, deleting 0x%x\n", - codeWord, above, cbufq[QLEN-1])); - - if(cpriv->laEnable) { - markInQueue(cbuf, codeWord, 1); // new entry - markInQueue(cbuf, cbufq[QLEN-1], 0); // bumped out - } - - len = QLEN - 1 - (int)above; - if(len > QUEUE_MEMMOVE_THRESH) { - src = &cbufq[above]; - dst = src + 1; - len *= sizeof(queueElt); - memmove(dst, src, len); - } - else { - for(j=QLEN-1; j > above; j--) { - cbufq[j] = cbufq[j-1]; - } - } - - cbufq[above] = codeWord; - -#if COM_LA_DEBUG - if(testLookAhead(cbuf, above, 0)) { - return CCR_INTERNAL; - } -#endif /*COM_LA_DEBUG*/ - - COMPROF_END(cmcQueMissMove); - incr2byteFrags(recursLevel); - } - else { - /* - * Odd byte case, at least gather stats. - */ - incr2byteFrags(recursLevel); - - /* - * ...and keep this in sync for signature sequence - */ - above = 0; -#if SKIP_NIBBLE_ON_QUEUE_0 - nibble = (cbuf->nybbleDex)++; -#endif /*SKIP_NIBBLE_ON_QUEUE_0*/ - } - - updateToken(tokenPtr, tokenDex, !match); - tokenDex++; - - if(match) { - *byteCodePtr++ = codeWord & 0xff; - numByteCodes++; - } - else { - serializeShort(codeWord, longCodePtr); - longCodePtr += 2; - numLongCodes++; - } - if(cpriv->sigSeqEnable) { - nextSigWord(cbuf, tokenDex, match, (above + nibble)); - } - } - -#if COM_DEBUG - if(numTokenBytes != ((tokenDex + 7) >> 3)) { - ddprintf(("comcryptBlock: numTokenBytes (%d), tokenDex (%d)\n", - numTokenBytes, tokenDex)); - } -#endif /*COM_DEBUG*/ - - /* - * We already wrote tokens and longcode to cipherText; verify we - * didn't overrun - */ - totalCipherTextLen = (unsigned)(longCodePtr - startCtextPtr); - if(*cipherTextLen < totalCipherTextLen) { - ddprintf(("comcryptBlock: short block (2)\n")); - return CCR_OUTBUFFER_TOO_SMALL; - } - if(!fullBlock) { - cipherText[CTBO_NUM_TOKENS] = tokenDex; - } - cipherText[CTBO_NUM_LONG_CODES] = numLongCodes; - -#if COM_DEBUG - if(tokenDex > MAX_TOKENS) { - ddprintf(("comcryptBlock: counter overflow!\n")); - return CCR_INTERNAL; - } - if((numByteCodes + numLongCodes) != tokenDex) { - ddprintf(("comcryptBlock: counter mismatch!\n")); - return CCR_INTERNAL; - } -#endif /*COM_DEBUG*/ - - /* - * See if doing a second level comcryption makes sense. - */ - destByteCodePtr = startLongCodePtr + (numLongCodes * 2); - if(numByteCodes > 0) { - jmatchAvg = jmatchTotal / numByteCodes; - } - else { - jmatchAvg = cbuf->jmatchThresh + 1; - } - if((recursLevel == 0) && // hard coded recursion limit - (cpriv->level2enable) && // enabled by caller - (numByteCodes >= cbuf->minByteCode) && // meaningful # of bytecodes - (jmatchAvg <= cbuf->jmatchThresh)) { // reasonable compression - // already achieved - - unsigned thisCtext = cbuf->level2BufSize; - - COMPROF_START; - crtn = comcryptBlock(cpriv, - cbuf->nextBuf, - cbuf->codeBuf, - numByteCodes, - cbuf->level2Buf, - &thisCtext, - recursLevel + 1); - if(crtn) { - return crtn; - } - - /* - * Write level2Buf to cipherText (as byteCodeArray). - * Size of 2nd level comcrypted byte code follows longcode array, - * then the bytecode itself. - * First bump totalCipherTextLen by the size of the comcrypted array - * plus one (for the size byte itself), and verify no overflow - */ - totalCipherTextLen += (thisCtext + 1); - if(*cipherTextLen < totalCipherTextLen) { - ddprintf(("comcryptBlock: short block (3)\n")); - return CCR_OUTBUFFER_TOO_SMALL; - } - *destByteCodePtr++ = thisCtext; - COMPROF_END(cmcLevel2); - memmove(destByteCodePtr, cbuf->level2Buf, thisCtext); - blockDesc |= CBD_DOUBLE; - - l2printf(("***2nd-level comcrypt: numByteCodes %d encrypted " - "size %d\n", numByteCodes, thisCtext)); - incrComStat(level2byteCode, numByteCodes); - incrComStat(level2cipherText, thisCtext); - incrComStat(level2jmatch, jmatchTotal); - incrComStat(level2blocks, 1); - } - else { - /* - * Normal one-level comcryption. Write byteCodes to ciphertext. - * numByteCodes is inferred. - */ - totalCipherTextLen += numByteCodes; - if(*cipherTextLen < totalCipherTextLen) { - ddprintf(("comcryptBlock: short block (3)\n")); - return CCR_OUTBUFFER_TOO_SMALL; - } - memmove(destByteCodePtr, cbuf->codeBuf, numByteCodes); - blockDesc |= CBD_SINGLE; - if(recursLevel == 0) { - incrComStat(level1blocks, 1); - } - /* else this is a 2nd-level, our caller will count */ - - /* - * obfuscate via sigArray (only when we're NOT doing 2nd level - * comcrypt) - */ - if(cpriv->sigSeqEnable) { - sigMunge(cbuf, startTokenPtr, tokenDex, - destByteCodePtr, startLongCodePtr); - - /* - * Prime sigArray state machine for next block. Note in the case - * of 2nd level, we skip this step, so the next block starts from - * the same state as this one did. - */ - cbuf->sigArray[0] = cbuf->sigArray[tokenDex]; - } - } - cipherText[CTBO_BLOCK_DESC] = blockDesc; - *cipherTextLen = totalCipherTextLen; - return CCR_SUCCESS; -} - -/* - * Main public encrypt function. - */ -comcryptReturn comcryptData( - comcryptObj cobj, - unsigned char *plainText, - unsigned plainTextLen, - unsigned char *cipherText, // malloc'd by caller - unsigned *cipherTextLen, // IN/OUT - comcryptEos endOfStream) // CCE_END_OF_STREAM, etc. -{ - comcryptPriv *cpriv = (comcryptPriv *)cobj; - unsigned ctextLen = *cipherTextLen; - comcryptReturn crtn; - unsigned thisPtext; - unsigned thisCtext; - COMPROF_LOCALS; - - COMPROF_START; - incrComStat(plaintextBytes, plainTextLen); - if(cpriv->versionBytes == 0) { - /* - * First, put header (version, spare) into head of ciphertext. - */ - if(ctextLen < CTEXT_HDR_SIZE) { - ddprintf(("comcryptData: overflow (0)\n")); - return CCR_OUTBUFFER_TOO_SMALL; - } - serializeInt(VERSION_3_Dec_97, cipherText); - cipherText += VERSION_BYTES; - cpriv->versionBytes = VERSION_BYTES; - serializeInt(0, cipherText); // spares - cipherText += SPARE_BYTES; - ctextLen -= CTEXT_HDR_SIZE; - } - - /* - * OK, grind it out, one block at a time. - */ - while (plainTextLen != 0) { - thisPtext = CC_BLOCK_SIZE; - if(thisPtext > plainTextLen) { - thisPtext = plainTextLen; - } - thisCtext = ctextLen; - crtn = comcryptBlock(cpriv, - &cpriv->cbuf, - plainText, - thisPtext, - cipherText, - &thisCtext, - 0); // recurs level - if(crtn) { - return crtn; - } - plainText += thisPtext; - plainTextLen -= thisPtext; - if(thisCtext > ctextLen) { - ddprintf(("comcryptData: undetected ciphertext overlow\n")); - return CCR_OUTBUFFER_TOO_SMALL; - } - cipherText += thisCtext; - ctextLen -= thisCtext; - } - *cipherTextLen = *cipherTextLen - ctextLen; - incrComStat(ciphertextBytes, *cipherTextLen); - COMPROF_END(cmcTotal); - return CCR_SUCCESS; -} - -/* - * Return values from deComcryptBlock(). - */ -typedef enum { - DCB_SUCCESS, // OK - DCB_SHORT, // incomplete block, try again with more ciphertext - DCB_PARSE_ERROR, // bad block - DCB_OUTBUFFER_TOO_SMALL -} dcbReturn; - -/* - * Assumes exactly one block of ciphertext, error otherwise. - */ -static dcbReturn deComcryptBlock( - comcryptPriv *cpriv, - comcryptBuf *cbuf, // not necessarily cpriv->cbuf - unsigned char *cipherText, - unsigned cipherTextLen, - unsigned char *plainText, - unsigned *plainTextLen, // IN/OUT - comcryptEos endOfStream, // CCE_END_OF_STREAM, etc. - unsigned *blockSize) // RETURNED on DCB_SUCCESS -{ - unsigned char *tokenPtr; - unsigned numTokenBits; // constant, from ciphertext - unsigned numTokenBytes; - unsigned char *longCodePtr; - unsigned numLongCodes; - unsigned char *byteCodePtr; - unsigned numByteCodes; - unsigned tokenDex; - unsigned oddByte = 0; - unsigned short codeWord; - unsigned char codeByte; - unsigned ptextLen = *plainTextLen; // bytes REMAINING - unsigned above; - unsigned j; - unsigned char blockDesc; - dcbReturn drtn; - int len; - queueElt *src; - queueElt *dst; - int lastWord = 0; - queueElt *cbufq = &cbuf->queue[0]; - int level2 = 0; // 2nd level comcrypted block - unsigned match; - unsigned char sigSeq; // signature sequence enable - unsigned char nibble; - - blockDesc = cipherText[CTBO_BLOCK_DESC]; - if((blockDesc & CBD_MAGIC_MASK) != CBD_MAGIC) { - ddprintf(("deComcryptBlock: bad CBD_MAGIC\n")); - return DCB_PARSE_ERROR; - } - - /* - * Min block size - blockDesc, numLongCodes, numTokens, one token byte, - * one bytecode - */ - if(cipherTextLen < 5) { - return DCB_SHORT; - } - if((blockDesc & CBD_FULL_BLOCK_MASK) == CBD_FULL_BLOCK) { - /* - * # of token bits implied for full block - */ - numTokenBits = TOKEN_BITS_FROM_PTEXT(CC_BLOCK_SIZE); - numTokenBytes = TOKEN_BYTES_FROM_PTEXT(CC_BLOCK_SIZE); - tokenPtr = cipherText + CTBO_NUM_TOKENS; - } - else { - numTokenBits = cipherText[CTBO_NUM_TOKENS]; - numTokenBytes = TOKEN_BYTES_FROM_TOKEN_BITS(numTokenBits); - tokenPtr = cipherText + CTBO_NUM_TOKENS + 1; - } - longCodePtr = tokenPtr + numTokenBytes; - numLongCodes = cipherText[CTBO_NUM_LONG_CODES]; - - byteCodePtr = longCodePtr + (numLongCodes * 2); // may increment... - if((blockDesc & CBD_BLOCK_TYPE_MASK) == CBD_SINGLE) { - /* - * # of bytecodes implied from numTokenBits and numLongCodes - */ - numByteCodes = numTokenBits - numLongCodes; - } - else { - /* - * size of 2nd level comcrypted bytecode specified after longCode - * array (and before the bytecode itself). - * Careful, verify that we can read numByteCodes first... - */ - if((unsigned)(byteCodePtr - cipherText) > cipherTextLen) { - return DCB_SHORT; - } - numByteCodes = *byteCodePtr++; - level2 = 1; - } - *blockSize = (unsigned)(byteCodePtr - cipherText) + numByteCodes; - if(*blockSize > cipherTextLen) { - return DCB_SHORT; - } - - /* - * We now know that we have a complete cipherblock. Go for it. - */ - if(level2) { - /* - * this block's bytecode array contains 2nd level comcrypted bytecodes. - */ - unsigned thisPtext = cbuf->level2BufSize; - unsigned level1CodeSize; - - if(cbuf->nextBuf == NULL) { - ddprintf(("2-level comcypt, no nextBuf available!\n")); - return DCB_PARSE_ERROR; - } - drtn = deComcryptBlock(cpriv, - cbuf->nextBuf, - byteCodePtr, - numByteCodes, - cbuf->level2Buf, - &thisPtext, - CCE_END_OF_STREAM, - &level1CodeSize); - switch(drtn) { - case DCB_SHORT: - ddprintf(("CBT_DOUBLE block, incomplete cipherblock in " - "2nd level code\n")); - return DCB_PARSE_ERROR; - - case DCB_OUTBUFFER_TOO_SMALL: // not our fault! - case DCB_PARSE_ERROR: - default: - ddprintf(("2nd-level decomcrypt error (%d)\n", drtn)); - return drtn; - - case DCB_SUCCESS: - /* - * Supposedly we passed in exactly one cipherblock... - */ - if(numByteCodes != level1CodeSize) { - ddprintf(("2nd-level decomcrypt: " - "numByteCodes != level1CodeSize\n")); - return DCB_PARSE_ERROR; - } - l2printf(("2nd-level decomcrypt: ciphertext %d " - "numByteCodes %d\n", numByteCodes, thisPtext)); - break; - } - byteCodePtr = cbuf->level2Buf; - numByteCodes = thisPtext; - } - - if((blockDesc & CBD_ODD_MASK) == CBD_ODD) { - oddByte = 1; - } - - /* - * Skip signature sequence if this was a 2nd level comcrypted block - */ - sigSeq = cpriv->sigSeqEnable && !level2; - - for(tokenDex=0; tokenDexkey, cpriv->keybytes, - (cbuf->nybbleDex)++); - - if(match) { - codeByte = *byteCodePtr++; - - if(sigSeq) { - codeByte ^= (unsigned char)(cbuf->sigArray[tokenDex]); - } - - /* - * dynamically process the queue for match - 8 bits - * of ciphercode, 16 bits of plaintext - */ - codeWord = cbufq[codeByte]; - above = (cbuf->f1 * codeByte * (16 + nibble)) >> 9; - -#if SKIP_NIBBLE_ON_QUEUE_0 - if(codeByte == 0) { - /* - * Special case for top of queue optimization during - * comcrypt - */ - nibble = cbuf->nybbleDex - 1; - } -#endif /*SKIP_NIBBLE_ON_QUEUE_0*/ - - /* - * queue[above..codeByte] move one element towards end - * queue[above] = this codeWord - */ - len = (int)codeByte - (int)above; - if(len > QUEUE_MEMMOVE_THRESH) { - src = &cbufq[above]; - dst = src + 1; - len *= sizeof(queueElt); - memmove(dst, src, len); - } - else { - for(j = codeByte; j > above; j--) { - cbufq[j] = cbufq[j-1]; - } - } - cbufq[above] = codeWord; - } - else { - /* - * !match, 16 bits of code - */ - deserializeShort(codeWord, longCodePtr); - if(sigSeq) { - codeWord ^= cbuf->sigArray[tokenDex]; - } - - if(oddByte && (tokenDex == (numTokenBits - 1))) { - lastWord = 1; - above = 0; -#if SKIP_NIBBLE_ON_QUEUE_0 - nibble = cbuf->nybbleDex - 1; -#endif /*SKIP_NIBBLE_ON_QUEUE_0*/ - } - else { - longCodePtr += 2; - - /* - * dynamically process the queue for unmatch; skip if this - * is an oddByte codeword. - * queue[above...QLEN-1] move one element toward end - * queue[above] = new codeWord - */ - above = ABOVE(cbuf->f2) + nibble; - len = QLEN - 1 - (int)above; - if(len > QUEUE_MEMMOVE_THRESH) { - src = &cbufq[above]; - dst = src + 1; - len *= sizeof(queueElt); - memmove(dst, src, len); - } - else { - for(j=QLEN-1; j > above; j--) { - cbufq[j] = cbufq[j-1]; - } - } - cbufq[above] = codeWord; - } - } - - if(sigSeq) { - /* - * Advance signature sequence state machine. - */ - nextSigWord(cbuf, tokenDex+1, match, (above + nibble)); - } - - /* - * cook up a byte or two of plainText from code word and invmap[] - */ - if(ptextLen < 1) { - ddprintf(("decryptBlock: ptext overflow (1)\n")); - return DCB_OUTBUFFER_TOO_SMALL; - } - *plainText++ = cpriv->invmap[(codeWord >> 8) & 0xff]; - ptextLen--; - if(lastWord) { - /* - * end of oddByte block. - */ - tokenDex++; // for sigArray maintenance - break; // out of main loop - } - else { - if(ptextLen < 1) { - ddprintf(("decryptBlock: ptext overflow (2)\n")); - return DCB_OUTBUFFER_TOO_SMALL; - } - *plainText++ = cpriv->invmap[(codeWord) & 0xff]; - ptextLen--; - } - } - - /* - * Prime sigArray state machine for next block. - */ - if(sigSeq) { - cbuf->sigArray[0] = cbuf->sigArray[tokenDex]; - } - *plainTextLen = *plainTextLen - ptextLen; - return DCB_SUCCESS; -} - -comcryptReturn deComcryptData( - comcryptObj cobj, - unsigned char *cipherText, - unsigned cipherTextLen, - unsigned char *plainText, - unsigned *plainTextLen, // IN/OUT - comcryptEos endOfStream) // CCE_END_OF_STREAM, etc. - -{ - comcryptPriv *cpriv = (comcryptPriv *)cobj; - unsigned char *outorigin = plainText; - unsigned ptextLen = *plainTextLen; - unsigned thisPtext; // per block - unsigned blockSize; - dcbReturn drtn; - unsigned ctextUsed; - - /* - * Snag version from ciphertext, or as much as we can get - */ - while((cpriv->versionBytes < VERSION_BYTES) && cipherTextLen) { - cpriv->version <<= 8; - cpriv->version |= *cipherText; - cpriv->versionBytes++; - cipherText++; - cipherTextLen--; - } - - /* - * Then skip over the remainder of the header (currently spares) - */ - if((cpriv->spareBytes < SPARE_BYTES) && cipherTextLen) { - unsigned toSkip = SPARE_BYTES - cpriv->spareBytes; - - if(toSkip > cipherTextLen) { - toSkip = cipherTextLen; - } - cpriv->spareBytes += toSkip; - cipherText += toSkip; - cipherTextLen -= toSkip; - } - - if(cipherTextLen == 0) { - *plainTextLen = 0; - return CCR_SUCCESS; - } - - if(cpriv->version != VERSION_3_Dec_97) { - ddprintf(("Incompatible version.\n")); - return CCR_BAD_CIPHERTEXT; - } - - while(cipherTextLen != 0) { - - /* - * Main loop. First deal with possible existing partial block. - */ - if(cpriv->cbuf.codeBufLength != 0) { - unsigned toCopy = - cpriv->cbuf.codeBufSize - cpriv->cbuf.codeBufLength; - unsigned origBufSize = cpriv->cbuf.codeBufLength; - - if(toCopy > cipherTextLen) { - toCopy = cipherTextLen; - } - memmove(cpriv->cbuf.codeBuf + cpriv->cbuf.codeBufLength, - cipherText, toCopy); - cpriv->cbuf.codeBufLength += toCopy; - - thisPtext = ptextLen; - drtn = deComcryptBlock(cpriv, - &cpriv->cbuf, - cpriv->cbuf.codeBuf, - cpriv->cbuf.codeBufLength, - plainText, - &thisPtext, - endOfStream, - &blockSize); - switch(drtn) { - case DCB_SHORT: - /* - * Incomplete block in codeBuf - */ - if(endOfStream == CCE_END_OF_STREAM) { - /* - * Caller thinks this is the end, but we need more - */ - ddprintf(("deComcryptData(): CCE_END_OF_STREAM, " - "not end of block\n")); - return CCR_BAD_CIPHERTEXT; - } - cipherTextLen -= toCopy; - if(cipherTextLen != 0) { - /* - * i.e., codeBuf overflow - could be s/w error? Do - * we need a bigger buffer? - */ - ddprintf(("deComcryptData: full codeBuf, incomplete " - "block\n")); - return CCR_BAD_CIPHERTEXT; - } - else { - /* - * OK, stash it and try again - */ - scprintf(("====incomplete codeBuf, codeBufLength %d, " - "cipherTextLen %d\n", - cpriv->cbuf.codeBufLength, toCopy)); - break; // out of main loop (after this switch) - } - - case DCB_OUTBUFFER_TOO_SMALL: - ddprintf(("codeBuf decomcrypt error short buf\n")); - return CCR_OUTBUFFER_TOO_SMALL; - - case DCB_PARSE_ERROR: - default: - ddprintf(("codeBuf decomcrypt error (%d)\n", drtn)); - return CCR_BAD_CIPHERTEXT; - - case DCB_SUCCESS: - /* - * ctextUsed is how much of caller's ciphertext we used - * in this buffered block - */ - ctextUsed = blockSize - origBufSize; - scprintf(("====decrypted block in codeBuf, blockSize %d, " - "ctextUsed %d, thisPtext %d\n", - blockSize, ctextUsed, thisPtext)); - cipherText += ctextUsed; - cipherTextLen -= ctextUsed; - plainText += thisPtext; - ptextLen -= thisPtext; - cpriv->cbuf.codeBufLength = 0; - break; - } - - /* - * We might have used up all of caller's cipherText processing - * codeBuf... - */ - if(cipherTextLen == 0) { - break; // out of main loop - } - - } /* buffered ciphertext in codeBuf */ - - /* - * Snarf ciphertext, one block at a time. - */ - - thisPtext = ptextLen; - drtn = deComcryptBlock(cpriv, - &cpriv->cbuf, - cipherText, - cipherTextLen, - plainText, - &thisPtext, - endOfStream, - &blockSize); - switch(drtn) { - case DCB_SHORT: - /* - * Incomplete block - */ - if(endOfStream == CCE_END_OF_STREAM) { - ddprintf(("deComcryptData(): CCE_END_OF_STREAM, not end of " - "block (2)\n")); - return CCR_BAD_CIPHERTEXT; - } - if(cipherTextLen > - (cpriv->cbuf.codeBufSize - cpriv->cbuf.codeBufLength)) { - ddprintf(("deComcryptData(): codeBuf overflow!\n")); - return CCR_BAD_CIPHERTEXT; - } - memmove(cpriv->cbuf.codeBuf + cpriv->cbuf.codeBufLength, - cipherText, cipherTextLen); - cpriv->cbuf.codeBufLength += cipherTextLen; - cipherTextLen = 0; - scprintf(("====Incomplete block, cipherTextLen %d " - "codeBufLength %d\n", cipherTextLen, - cpriv->cbuf.codeBufLength)); - break; // actually out of main loop - - case DCB_PARSE_ERROR: - case DCB_OUTBUFFER_TOO_SMALL: - default: - return CCR_BAD_CIPHERTEXT; - - case DCB_SUCCESS: - if(ptextLen < thisPtext) { - /* - * Software error - */ - ddprintf(("deComcryptData: undetected ptext " - "overflow (2)\n")); - return CCR_BAD_CIPHERTEXT; - } - plainText += thisPtext; - ptextLen -= thisPtext; - cipherText += blockSize; - cipherTextLen -= blockSize; - scprintf(("====decrypted one block, blockSize %d " - "thisPtext %d\n", blockSize, thisPtext)); - break; - } - } /* main loop */ - - *plainTextLen = (unsigned)(plainText - outorigin); - return CCR_SUCCESS; -}