#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
void comMallocRegister(comMallocExternFcn *mallocExtern,
comFreeExternFcn *freeExtern)
{
comMallocExt = mallocExtern;
comFreeExt = freeExtern;
}
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
cpriv->key = (unsigned char *)ascMalloc(COMCRYPT_MAX_KEYLENGTH);
#endif
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
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;
}
comcryptReturn comcryptInit(
comcryptObj cobj,
const unsigned char *key,
unsigned keyLen,
comcryptOptimize optimize) {
comcryptPriv *cpriv = (comcryptPriv *)cobj;
unsigned maxKeySize;
#if COMCRYPT_EXPORT_ONLY
maxKeySize = EXPORT_KEY_SIZE;
#else
maxKeySize = COMCRYPT_MAX_KEYLENGTH;
#endif
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;
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
cpriv->laEnable = 0;
#endif
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;
}
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);
}
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:
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);
rtn &= 0xfffffffe;
rtn--;
if(rtn <= 0) {
return 0;
}
resid = outBufSize % MAX_CBLOCK_SIZE;
if(resid) {
rtn += resid;
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) {
rtn &= ~(CC_BLOCK_SIZE - 1);
}
return rtn;
case CCOP_DECOMCRYPT:
ptextFromCodeBuf = cpriv->cbuf.codeBufLength * 4;
if(outBufSize < ptextFromCodeBuf) {
rtn = 0;
}
else {
rtn = (outBufSize - ptextFromCodeBuf) / 4;
}
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;
}
}
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) {
unsigned tokenBytes = TOKEN_BYTES_FROM_PTEXT(resid);
rtn += MIN_CBLOCK_SIZE;
rtn += tokenBytes;
rtn += resid; if(resid & 1) {
rtn++; }
}
if((cpriv == NULL) || (cpriv->versionBytes == 0)) {
rtn += CTEXT_HDR_SIZE; }
return rtn;
case CCOP_DECOMCRYPT:
inBufSize += cpriv->cbuf.codeBufLength;
if(inBufSize) {
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;
}
}
#define QUEUE_MEMMOVE_THRESH 3
#define QUEUE_PEEK 0
static comcryptReturn comcryptBlock(
comcryptPriv *cpriv,
comcryptBuf *cbuf, const unsigned char *plainText,
unsigned plainTextLen,
unsigned char *cipherText,
unsigned *cipherTextLen, 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; unsigned short codeWord;
unsigned oddByte = 0;
unsigned match;
unsigned jmatch=0;
unsigned tokenDex = 0; 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];
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));
tokenPtr = cipherText + CTBO_NUM_TOKENS + 1;
if(plainTextLen >= (CC_BLOCK_SIZE - 1)) {
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);
while(plainTextLen != 0) {
if(plainTextLen == 1) {
codeWord = ((unsigned short)(cpriv->map[*plainText]) << 8) |
cpriv->map[0]; oddByte = 1;
blockDesc |= CBD_ODD;
plainTextLen--;
}
else {
codeWord = ((unsigned short)(cpriv->map[*plainText]) << 8) |
(unsigned short)(cpriv->map[plainText[1]]);
plainText += 2;
plainTextLen -= 2;
}
COMPROF_START;
COMPROF_END(cmcPerWordOhead);
COMPROF_START;
match = 0;
do {
if(oddByte) {
break; }
#if QUEUE_PEEK
if(cbufq[0] == codeWord) {
match = 1;
jmatch = 0;
break;
}
#endif
if(cpriv->laEnable && !inQueue(cbuf, codeWord)) {
break;
}
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;
}
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
} while(0);
COMPROF_END(cmcQueSearch);
#if !SKIP_NIBBLE_ON_QUEUE_0
nibble = keynybble(cpriv->key, cpriv->keybytes,
(cbuf->nybbleDex)++);
#endif
COMPROF_START;
if(match) {
if(jmatch == 0) {
above = 0;
laprintf(("...queue hit at queue[0]\n"));
#if SKIP_NIBBLE_ON_QUEUE_0
nibble = (cbuf->nybbleDex)++;
#endif
}
else {
#if SKIP_NIBBLE_ON_QUEUE_0
nibble = keynybble(cpriv->key, cpriv->keybytes,
(cbuf->nybbleDex)++);
#endif
above = (cbuf->f1 * jmatch * (16 + nibble)) >> 9;
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
}
COMPROF_END(cmcQueMatchMove);
codeWord = jmatch;
incr1byteFrags(recursLevel);
jmatchTotal += jmatch;
}
else if(oddByte == 0) {
#if SKIP_NIBBLE_ON_QUEUE_0
nibble = keynybble(cpriv->key, cpriv->keybytes,
(cbuf->nybbleDex)++);
#endif
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); markInQueue(cbuf, cbufq[QLEN-1], 0); }
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
COMPROF_END(cmcQueMissMove);
incr2byteFrags(recursLevel);
}
else {
incr2byteFrags(recursLevel);
above = 0;
#if SKIP_NIBBLE_ON_QUEUE_0
nibble = (cbuf->nybbleDex)++;
#endif
}
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
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
destByteCodePtr = startLongCodePtr + (numLongCodes * 2);
if(numByteCodes > 0) {
jmatchAvg = jmatchTotal / numByteCodes;
}
else {
jmatchAvg = cbuf->jmatchThresh + 1;
}
if((recursLevel == 0) && (cpriv->level2enable) && (numByteCodes >= cbuf->minByteCode) && (jmatchAvg <= cbuf->jmatchThresh)) {
unsigned thisCtext = cbuf->level2BufSize;
COMPROF_START;
crtn = comcryptBlock(cpriv,
cbuf->nextBuf,
cbuf->codeBuf,
numByteCodes,
cbuf->level2Buf,
&thisCtext,
recursLevel + 1);
if(crtn) {
return crtn;
}
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 {
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);
}
if(cpriv->sigSeqEnable) {
sigMunge(cbuf, startTokenPtr, tokenDex,
destByteCodePtr, startLongCodePtr);
cbuf->sigArray[0] = cbuf->sigArray[tokenDex];
}
}
cipherText[CTBO_BLOCK_DESC] = blockDesc;
*cipherTextLen = totalCipherTextLen;
return CCR_SUCCESS;
}
comcryptReturn comcryptData(
comcryptObj cobj,
unsigned char *plainText,
unsigned plainTextLen,
unsigned char *cipherText, unsigned *cipherTextLen, comcryptEos endOfStream) {
comcryptPriv *cpriv = (comcryptPriv *)cobj;
unsigned ctextLen = *cipherTextLen;
comcryptReturn crtn;
unsigned thisPtext;
unsigned thisCtext;
COMPROF_LOCALS;
COMPROF_START;
incrComStat(plaintextBytes, plainTextLen);
if(cpriv->versionBytes == 0) {
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); cipherText += SPARE_BYTES;
ctextLen -= CTEXT_HDR_SIZE;
}
while (plainTextLen != 0) {
thisPtext = CC_BLOCK_SIZE;
if(thisPtext > plainTextLen) {
thisPtext = plainTextLen;
}
thisCtext = ctextLen;
crtn = comcryptBlock(cpriv,
&cpriv->cbuf,
plainText,
thisPtext,
cipherText,
&thisCtext,
0); 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;
}
typedef enum {
DCB_SUCCESS, DCB_SHORT, DCB_PARSE_ERROR, DCB_OUTBUFFER_TOO_SMALL
} dcbReturn;
static dcbReturn deComcryptBlock(
comcryptPriv *cpriv,
comcryptBuf *cbuf, unsigned char *cipherText,
unsigned cipherTextLen,
unsigned char *plainText,
unsigned *plainTextLen, comcryptEos endOfStream, unsigned *blockSize) {
unsigned char *tokenPtr;
unsigned numTokenBits; 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; 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; unsigned match;
unsigned char sigSeq; 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;
}
if(cipherTextLen < 5) {
return DCB_SHORT;
}
if((blockDesc & CBD_FULL_BLOCK_MASK) == CBD_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); if((blockDesc & CBD_BLOCK_TYPE_MASK) == CBD_SINGLE) {
numByteCodes = numTokenBits - numLongCodes;
}
else {
if((unsigned)(byteCodePtr - cipherText) > cipherTextLen) {
return DCB_SHORT;
}
numByteCodes = *byteCodePtr++;
level2 = 1;
}
*blockSize = (unsigned)(byteCodePtr - cipherText) + numByteCodes;
if(*blockSize > cipherTextLen) {
return DCB_SHORT;
}
if(level2) {
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: case DCB_PARSE_ERROR:
default:
ddprintf(("2nd-level decomcrypt error (%d)\n", drtn));
return drtn;
case DCB_SUCCESS:
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;
}
sigSeq = cpriv->sigSeqEnable && !level2;
for(tokenDex=0; tokenDex<numTokenBits; tokenDex++) {
match = !getToken(tokenPtr, tokenDex);
nibble = keynybble(cpriv->key, cpriv->keybytes,
(cbuf->nybbleDex)++);
if(match) {
codeByte = *byteCodePtr++;
if(sigSeq) {
codeByte ^= (unsigned char)(cbuf->sigArray[tokenDex]);
}
codeWord = cbufq[codeByte];
above = (cbuf->f1 * codeByte * (16 + nibble)) >> 9;
#if SKIP_NIBBLE_ON_QUEUE_0
if(codeByte == 0) {
nibble = cbuf->nybbleDex - 1;
}
#endif
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 {
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
}
else {
longCodePtr += 2;
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) {
nextSigWord(cbuf, tokenDex+1, match, (above + nibble));
}
if(ptextLen < 1) {
ddprintf(("decryptBlock: ptext overflow (1)\n"));
return DCB_OUTBUFFER_TOO_SMALL;
}
*plainText++ = cpriv->invmap[(codeWord >> 8) & 0xff];
ptextLen--;
if(lastWord) {
tokenDex++; break; }
else {
if(ptextLen < 1) {
ddprintf(("decryptBlock: ptext overflow (2)\n"));
return DCB_OUTBUFFER_TOO_SMALL;
}
*plainText++ = cpriv->invmap[(codeWord) & 0xff];
ptextLen--;
}
}
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, comcryptEos endOfStream)
{
comcryptPriv *cpriv = (comcryptPriv *)cobj;
unsigned char *outorigin = plainText;
unsigned ptextLen = *plainTextLen;
unsigned thisPtext; unsigned blockSize;
dcbReturn drtn;
unsigned ctextUsed;
while((cpriv->versionBytes < VERSION_BYTES) && cipherTextLen) {
cpriv->version <<= 8;
cpriv->version |= *cipherText;
cpriv->versionBytes++;
cipherText++;
cipherTextLen--;
}
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) {
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:
if(endOfStream == CCE_END_OF_STREAM) {
ddprintf(("deComcryptData(): CCE_END_OF_STREAM, "
"not end of block\n"));
return CCR_BAD_CIPHERTEXT;
}
cipherTextLen -= toCopy;
if(cipherTextLen != 0) {
ddprintf(("deComcryptData: full codeBuf, incomplete "
"block\n"));
return CCR_BAD_CIPHERTEXT;
}
else {
scprintf(("====incomplete codeBuf, codeBufLength %d, "
"cipherTextLen %d\n",
cpriv->cbuf.codeBufLength, toCopy));
break; }
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 = 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;
}
if(cipherTextLen == 0) {
break; }
}
thisPtext = ptextLen;
drtn = deComcryptBlock(cpriv,
&cpriv->cbuf,
cipherText,
cipherTextLen,
plainText,
&thisPtext,
endOfStream,
&blockSize);
switch(drtn) {
case DCB_SHORT:
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;
case DCB_PARSE_ERROR:
case DCB_OUTBUFFER_TOO_SMALL:
default:
return CCR_BAD_CIPHERTEXT;
case DCB_SUCCESS:
if(ptextLen < thisPtext) {
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;
}
}
*plainTextLen = (unsigned)(plainText - outorigin);
return CCR_SUCCESS;
}