+++ /dev/null
-/*
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#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; j<QLEN; j++) {
- if(cbufq[j] == codeWord) {
- printf("***Huh! Dup queue entry codeWord 0x%x jmatch "
- "0x%x 2nd j 0x%x\n",
- codeWord, jmatch, j);
- return CCR_INTERNAL;
- }
- }
- }
-#endif /*COM_LA_DEBUG*/
- } while(0);
-
- COMPROF_END(cmcQueSearch);
-
- /*
- * Note we measure the overhead on a per-codeword basis. Here,
- * we ensure that there is exactly one pair of start/end
- * timestamps per queue move per code word.
- *
- * New 17 Dec 1997 - always calculate keynibble for use in signature
- * sequence update
- */
-#if !SKIP_NIBBLE_ON_QUEUE_0
- nibble = keynybble(cpriv->key, 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; tokenDex<numTokenBits; tokenDex++) {
- match = !getToken(tokenPtr, tokenDex);
-
- /*
- * 17 Dec 1997 - Always calculate this regardless of match
- */
- nibble = keynybble(cpriv->key, 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;
-}