]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_comcryption/lib/comcryption.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_comcryption / lib / comcryption.c
diff --git a/Security/libsecurity_comcryption/lib/comcryption.c b/Security/libsecurity_comcryption/lib/comcryption.c
new file mode 100644 (file)
index 0000000..a7d23e9
--- /dev/null
@@ -0,0 +1,1438 @@
+/*
+ * 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;
+}