4 Contains: implementation of low-level comcryption engine
6 Written by: Richard Crandall and Doug Mitchell
8 Copyright: (c) 1997 by Apple Computer, Inc., all rights reserved.
10 Change History (most recent first):
12 05/28/98 dpm Added platform-dependent ascMalloc and ascFree
13 12/08/97 dpm Added Signature Sequence mechanism.
14 12/03/97 dpm Added queue lookahead; various optimizations.
15 11/13/97 dpm Rewrote for block-oriented ciphertext with
16 two-level comcryption.
17 11/11/97 gab Modified for MacOS (fixed implicit type
19 10/29/97 dpm Extensive mods for stream-oriented API.
20 10/01/97 rc original by Richard Crandall
25 Library for holistic (one-pass) compress-encrypt.
26 This is an explicit embodiment of the Comcryption^(TM)
34 #include "comcryption.h"
36 #include "comcryptPriv.h"
40 unsigned comProfEnable
;
42 comprof_t cmcQueSearch
;
43 comprof_t cmcQueMatchMove
;
44 comprof_t cmcQueMissMove
;
46 comprof_t cmcPerWordOhead
;
48 #endif /*COM_PROFILE*/
50 void comMallocRegister(comMallocExternFcn
*mallocExtern
,
51 comFreeExternFcn
*freeExtern
)
53 comMallocExt
= mallocExtern
;
54 comFreeExt
= freeExtern
;
58 * Call once at startup. The resulting comcryptObj can be reused multiple
61 comcryptObj
comcryptAlloc()
63 comcryptPriv
*cpriv
= (comcryptPriv
*) ascMalloc(sizeof(comcryptPriv
));
68 memset(cpriv
, 0, sizeof(comcryptPriv
));
70 #if COMCRYPT_EXPORT_ONLY
71 cpriv
->key
= (unsigned char *)ascMalloc(EXPORT_KEY_SIZE
);
72 #else /*COMCRYPT_EXPORT_ONLY*/
73 cpriv
->key
= (unsigned char *)ascMalloc(COMCRYPT_MAX_KEYLENGTH
);
74 #endif /*COMCRYPT_EXPORT_ONLY*/
76 if(cpriv
->key
== NULL
) {
79 cpriv
->map
= (unsigned char *)ascMalloc(256);
80 cpriv
->invmap
= (unsigned char *)ascMalloc(256);
81 if((cpriv
->map
== NULL
) || (cpriv
->invmap
== NULL
)) {
84 mallocCodeBufs(&cpriv
->cbuf
);
85 if((cpriv
->cbuf
.codeBuf
== NULL
) ||
86 (cpriv
->cbuf
.level2Buf
== NULL
)) {
90 if(cpriv
->cbuf
.lookAhead
== NULL
) {
96 * Hard coded limit of two levels of comcryption
98 cpriv
->cbuf
.nextBuf
= (comcryptBuf
*)ascMalloc(sizeof(comcryptBuf
));
99 if(cpriv
->cbuf
.nextBuf
== NULL
) {
102 mallocCodeBufs(cpriv
->cbuf
.nextBuf
);
103 if((cpriv
->cbuf
.nextBuf
->codeBuf
== NULL
) ||
104 (cpriv
->cbuf
.nextBuf
->level2Buf
== NULL
)) {
108 if(cpriv
->cbuf
.nextBuf
->lookAhead
== NULL
) {
113 cpriv
->cbuf
.nextBuf
->nextBuf
= NULL
;
118 * Call this before starting every stream process
120 comcryptReturn
comcryptInit(
122 const unsigned char *key
,
124 comcryptOptimize optimize
) // CCO_SIZE, etc.
126 comcryptPriv
*cpriv
= (comcryptPriv
*)cobj
;
129 #if COMCRYPT_EXPORT_ONLY
131 * FIXME - NSA might not be satisfied with this, may have to enforce
134 maxKeySize
= EXPORT_KEY_SIZE
;
135 #else /*COMCRYPT_EXPORT_ONLY*/
136 maxKeySize
= COMCRYPT_MAX_KEYLENGTH
;
137 #endif /*COMCRYPT_EXPORT_ONLY*/
139 if(keyLen
> maxKeySize
) {
142 memmove(cpriv
->key
, key
, keyLen
);
143 cpriv
->keybytes
= keyLen
;
144 cpriv
->cbuf
.codeBufLength
= 0;
145 cpriv
->cbuf
.nextBuf
->codeBufLength
= 0;
147 cpriv
->versionBytes
= 0;
148 cpriv
->spareBytes
= 0;
149 cpriv
->optimize
= optimize
;
152 * Derive feature enable bits from optimize arg. This is highly likely
155 cpriv
->level2enable
= 1;
156 cpriv
->sigSeqEnable
= 1;
159 cpriv
->level2enable
= 0;
162 cpriv
->sigSeqEnable
= 0;
169 #else /* QUEUE_LOOKAHEAD */
171 #endif /* QUEUE_LOOKAHEAD */
174 * init queue and maps
176 initCodeBufs(&cpriv
->cbuf
, key
, keyLen
, cpriv
->laEnable
,
177 cpriv
->sigSeqEnable
);
178 initCodeBufs(cpriv
->cbuf
.nextBuf
, key
, keyLen
, cpriv
->laEnable
,
179 cpriv
->sigSeqEnable
);
180 key_perm(key
, keyLen
, cpriv
->map
, cpriv
->invmap
);
185 * Free a comcryptObj object obtained via comcryptAlloc()
187 void comcryptObjFree(comcryptObj cobj
)
189 comcryptPriv
*cpriv
= (comcryptPriv
*)cobj
;
191 if(cpriv
->key
!= NULL
) {
194 if(cpriv
->map
!= NULL
) {
197 if(cpriv
->invmap
!= NULL
) {
198 ascFree(cpriv
->invmap
);
200 freeCodeBufs(&cpriv
->cbuf
);
205 * Return the maximum input buffer size allowed for for specified
206 * output buffer size. Note that for both comcrypt and decomcrypt,
207 * to cover the worst case, the output buffer always has to be
208 * larger than the input buffer.
210 unsigned comcryptMaxInBufSize(comcryptObj cobj
,
215 unsigned minCblockSize
;
219 comcryptPriv
*cpriv
= (comcryptPriv
*)cobj
;
220 unsigned ptextFromCodeBuf
;
225 * Worst case: no compression. Also, establish a minimum
226 * ciphertext size to accomodate header and one block.
228 minCblockSize
= MIN_CBLOCK_SIZE
;
229 if(cpriv
->versionBytes
== 0) {
230 minCblockSize
+= CTEXT_HDR_SIZE
;
232 if(outBufSize
< (minCblockSize
)) {
235 if(cpriv
->versionBytes
== 0) {
236 outBufSize
-= CTEXT_HDR_SIZE
;
238 fullBlocks
= outBufSize
/ MAX_CBLOCK_SIZE
;
239 rtn
= (fullBlocks
* CC_BLOCK_SIZE
); // bytes of ptext
242 * code must be even aligned, then chop off one for odd ptext
249 resid
= outBufSize
% MAX_CBLOCK_SIZE
;
254 * Account for resid block overhead
256 if(rtn
< MIN_CBLOCK_SIZE
) {
259 rtn
-= MIN_CBLOCK_SIZE
;
261 tokenBytes
= TOKEN_BYTES_FROM_PTEXT(resid
);
262 if(rtn
<= tokenBytes
) {
267 if(rtn
> INBUF_TRUNC_THRESH
) {
269 * Truncate to even block size to minimize partial cipherblocks
271 rtn
&= ~(CC_BLOCK_SIZE
- 1);
275 case CCOP_DECOMCRYPT
:
277 * Worst case - 4:1 compression and an almost full block in
278 * codeBuf. Note 4:1 is a super-conservative, easy arithmetic
279 * version of (9/16) squared...
281 ptextFromCodeBuf
= cpriv
->cbuf
.codeBufLength
* 4;
282 if(outBufSize
< ptextFromCodeBuf
) {
283 /* decrypting codeBuf might overflow output (plaintext)
284 * buffer - won't be able to move anything */
288 /* can decrypt (this much plainText - ptextFromCodeBuf) / 4 */
289 rtn
= (outBufSize
- ptextFromCodeBuf
) / 4;
292 /* may be able to handle a bit extra for initial decrypt... */
293 if(cpriv
->versionBytes
< VERSION_BYTES
) {
294 rtn
+= (VERSION_BYTES
- cpriv
->versionBytes
);
296 if(cpriv
->spareBytes
< SPARE_BYTES
) {
297 rtn
+= (SPARE_BYTES
- cpriv
->spareBytes
);
302 ddprintf(("bogus op (%d) in comcryptMaxInBufSize()\n", op
));
308 * Return the maximum output buffer size for specified input buffer size.
309 * Output buffer size will always be larger than input buffer size.
311 unsigned comcryptMaxOutBufSize(comcryptObj cobj
,
319 comcryptPriv
*cpriv
= (comcryptPriv
*)cobj
;
323 fullBlocks
= inBufSize
/ CC_BLOCK_SIZE
;
324 rtn
= fullBlocks
* MAX_CBLOCK_SIZE
;
325 resid
= inBufSize
% CC_BLOCK_SIZE
;
330 unsigned tokenBytes
= TOKEN_BYTES_FROM_PTEXT(resid
);
332 rtn
+= MIN_CBLOCK_SIZE
;
334 rtn
+= resid
; // no compression
336 rtn
++; // oddByte uses extra
339 if((cpriv
== NULL
) || // i.e., we're being called from mallocCodeBufs
340 (cpriv
->versionBytes
== 0)) {
341 rtn
+= CTEXT_HDR_SIZE
; // first of a stream
345 case CCOP_DECOMCRYPT
:
347 * Here assume max compression, including resid block in codeBuf
349 inBufSize
+= cpriv
->cbuf
.codeBufLength
;
351 /* may be able to handle a bit extra for initial decrypt... */
353 if(cpriv
->versionBytes
< VERSION_BYTES
) {
354 delta
= VERSION_BYTES
- cpriv
->versionBytes
;
355 if(inBufSize
> delta
) {
362 if(cpriv
->spareBytes
< SPARE_BYTES
) {
363 delta
= SPARE_BYTES
- cpriv
->spareBytes
;
364 if(inBufSize
> delta
) {
376 ddprintf(("bogus op (%d) in comcryptMaxOutBufSize()\n", op
));
382 * Threshold for using memmove() rather than hard-coded loop for
383 * moving queue segment. This was derived empirically on a Pentium;
384 * we should do similar measurements on PPC.
386 #define QUEUE_MEMMOVE_THRESH 3
389 * peek at queue[0] before search. This appears to only be a win for
390 * constant plaintext, i.e., the codeword is almost always at queue[0].
395 * Comcrypt one block.
397 static comcryptReturn
comcryptBlock(
399 comcryptBuf
*cbuf
, // not necessarily cpriv->cbuf
400 const unsigned char *plainText
,
401 unsigned plainTextLen
,
402 unsigned char *cipherText
,
403 unsigned *cipherTextLen
, // IN/OUT
404 unsigned recursLevel
)
406 unsigned char *byteCodePtr
;
407 unsigned char *destByteCodePtr
;
408 unsigned char *longCodePtr
;
409 unsigned char *startLongCodePtr
;
410 unsigned char *tokenPtr
;
411 unsigned char *startTokenPtr
;
412 unsigned char *startCtextPtr
= cipherText
;
413 unsigned numTokenBytes
; // in bytes, constant
414 unsigned short codeWord
;
415 unsigned oddByte
= 0;
418 unsigned tokenDex
= 0; // index into array of token bits
420 unsigned numLongCodes
= 0;
421 unsigned numByteCodes
= 0;
422 unsigned totalCipherTextLen
;
424 unsigned jmatchTotal
= 0;
427 unsigned char blockDesc
= CBD_MAGIC
;
428 unsigned fullBlock
= 0;
432 queueElt
*cbufq
= &cbuf
->queue
[0];
435 * 'nibble' is added to 'above' in the call to nextSigWord() for
436 * additional security.
438 * Normal case : nibble = keynybble()
439 * last word on odd byte : nibble = nibbleDex
440 * hit on queue q : nibble = nibbleDex (optimize to avoid keynybble()
443 unsigned char nibble
;
448 if(testLookAhead(cbuf
, 0, 0)) {
453 laprintf(("comcryptBlock recurs level %d\n", recursLevel
));
456 * Set up ptrs for the three arrays we'll be writing
458 tokenPtr
= cipherText
+ CTBO_NUM_TOKENS
+ 1;
459 if(plainTextLen
>= (CC_BLOCK_SIZE
- 1)) {
461 * Optimized for full block - no token count in block. Note
462 * that plainTextLen == (CC_BLOCK_SIZE - 1) is also a full block
463 * in that it uses up a full block's worth of tokens!
465 numTokenBytes
= CC_BLOCK_SIZE
>> 4;
467 blockDesc
|= CBD_FULL_BLOCK
;
471 numTokenBytes
= (plainTextLen
+ 15) >> 4;
473 longCodePtr
= tokenPtr
+ numTokenBytes
;
474 startLongCodePtr
= longCodePtr
;
475 byteCodePtr
= cbuf
->codeBuf
;
476 startTokenPtr
= tokenPtr
;
478 if((unsigned)(longCodePtr
- cipherText
) > *cipherTextLen
) {
479 ddprintf(("comcryptBlock: short block (1)\n"));
480 return CCR_OUTBUFFER_TOO_SMALL
;
482 memset(tokenPtr
, 0, numTokenBytes
);
485 * Entering time-critical region. This loop executes once for every
486 * 2 bytes of plaintext. Make every attempt to streamline the code
487 * here; avoid function calls in favor of macros; etc.
489 while(plainTextLen
!= 0) {
492 * assemble a 16-bit word from two bytes if possible
494 if(plainTextLen
== 1) {
498 codeWord
= ((unsigned short)(cpriv
->map
[*plainText
]) << 8) |
499 cpriv
->map
[0]; // a bit of obfuscation - mapped zero
501 blockDesc
|= CBD_ODD
;
505 codeWord
= ((unsigned short)(cpriv
->map
[*plainText
]) << 8) |
506 (unsigned short)(cpriv
->map
[plainText
[1]]);
512 * Calibrate how much profiling is costing us.
515 COMPROF_END(cmcPerWordOhead
);
518 * See if this word is in queue[]. Skip if oddByte; we'll force
519 * a 16-bit word in that case. Also skip the search if we know
520 * via lookahead that a search would be fruitless.
522 COMPROF_START
; /* cmcQueSearch */
524 do { /* while 0 - for easy breaks w/o goto */
527 * First handle some optimizations and special cases
530 break; // force longcode
534 if(cbufq
[0] == codeWord
) {
540 #endif /*QUEUE_PEEK*/
542 if(cpriv
->laEnable
&& !inQueue(cbuf
, codeWord
)) {
547 * OK, do the gruntwork search
549 for(j
=0; j
< QLEN
; j
++) {
550 if(cbufq
[j
] == codeWord
) {
558 if(cpriv
->laEnable
&& !match
) {
559 printf("inQueue, not found in queue!\n");
564 * Search for duplicates.
567 for(j
=jmatch
+1; j
<QLEN
; j
++) {
568 if(cbufq
[j
] == codeWord
) {
569 printf("***Huh! Dup queue entry codeWord 0x%x jmatch "
571 codeWord
, jmatch
, j
);
576 #endif /*COM_LA_DEBUG*/
579 COMPROF_END(cmcQueSearch
);
582 * Note we measure the overhead on a per-codeword basis. Here,
583 * we ensure that there is exactly one pair of start/end
584 * timestamps per queue move per code word.
586 * New 17 Dec 1997 - always calculate keynibble for use in signature
589 #if !SKIP_NIBBLE_ON_QUEUE_0
590 nibble
= keynybble(cpriv
->key
, cpriv
->keybytes
,
591 (cbuf
->nybbleDex
)++);
592 #endif /*SKIP_NIBBLE_ON_QUEUE_0*/
597 * 16-bit symbol is in queue. 8 bits of ciphertext, token bit is 0.
601 * Optimization: jmatch = 0. Keep state machine in sync,
602 * but skip queue update.
605 laprintf(("...queue hit at queue[0]\n"));
606 #if SKIP_NIBBLE_ON_QUEUE_0
607 nibble
= (cbuf
->nybbleDex
)++;
608 #endif /*SKIP_NIBBLE_ON_QUEUE_0*/
611 #if SKIP_NIBBLE_ON_QUEUE_0
612 nibble
= keynybble(cpriv
->key
, cpriv
->keybytes
,
613 (cbuf
->nybbleDex
)++);
614 #endif /*SKIP_NIBBLE_ON_QUEUE_0*/
616 above
= (cbuf
->f1
* jmatch
* (16 + nibble
)) >> 9;
619 * queue[above..(jmatch-1)] move one element towards end
620 * queue[above] = this codeWord
622 laprintf(("...queue hit, moving 0x%x from 0x%x to 0x%x\n",
623 codeWord
, jmatch
, above
));
625 len
= (int)jmatch
- (int)above
;
626 if(len
> QUEUE_MEMMOVE_THRESH
) {
629 len
*= sizeof(queueElt
);
630 memmove(dst
, src
, len
);
633 for(j
= jmatch
; j
>above
; j
--) {
634 cbufq
[j
] = cbufq
[j
-1];
638 cbufq
[above
] = codeWord
;
640 if(testLookAhead(cbuf
, above
, jmatch
)) {
643 #endif /*COM_LA_DEBUG*/
645 COMPROF_END(cmcQueMatchMove
);
648 incr1byteFrags(recursLevel
);
649 jmatchTotal
+= jmatch
;
651 else if(oddByte
== 0) {
653 * 16-bit symbol is not in queue. 16 bits of ciphertext.
656 * queue[above...QLEN-1] move one element toward end
657 * queue[QLEN-1] discarded
658 * queue[above] = new codeword
660 * Note we skip this queue manipulation in the oddbyte case, since
661 * we don't really know (or care) if the current code word is in
664 #if SKIP_NIBBLE_ON_QUEUE_0
665 nibble
= keynybble(cpriv
->key
, cpriv
->keybytes
,
666 (cbuf
->nybbleDex
)++);
667 #endif /*SKIP_NIBBLE_ON_QUEUE_0*/
669 above
= ABOVE(cbuf
->f2
) + nibble
;
673 printf("Hey Doug! above %d QLEN %d\n", above
, QLEN
);
678 laprintf(("...queue miss, adding 0x%x at 0x%x, deleting 0x%x\n",
679 codeWord
, above
, cbufq
[QLEN
-1]));
681 if(cpriv
->laEnable
) {
682 markInQueue(cbuf
, codeWord
, 1); // new entry
683 markInQueue(cbuf
, cbufq
[QLEN
-1], 0); // bumped out
686 len
= QLEN
- 1 - (int)above
;
687 if(len
> QUEUE_MEMMOVE_THRESH
) {
690 len
*= sizeof(queueElt
);
691 memmove(dst
, src
, len
);
694 for(j
=QLEN
-1; j
> above
; j
--) {
695 cbufq
[j
] = cbufq
[j
-1];
699 cbufq
[above
] = codeWord
;
702 if(testLookAhead(cbuf
, above
, 0)) {
705 #endif /*COM_LA_DEBUG*/
707 COMPROF_END(cmcQueMissMove
);
708 incr2byteFrags(recursLevel
);
712 * Odd byte case, at least gather stats.
714 incr2byteFrags(recursLevel
);
717 * ...and keep this in sync for signature sequence
720 #if SKIP_NIBBLE_ON_QUEUE_0
721 nibble
= (cbuf
->nybbleDex
)++;
722 #endif /*SKIP_NIBBLE_ON_QUEUE_0*/
725 updateToken(tokenPtr
, tokenDex
, !match
);
729 *byteCodePtr
++ = codeWord
& 0xff;
733 serializeShort(codeWord
, longCodePtr
);
737 if(cpriv
->sigSeqEnable
) {
738 nextSigWord(cbuf
, tokenDex
, match
, (above
+ nibble
));
743 if(numTokenBytes
!= ((tokenDex
+ 7) >> 3)) {
744 ddprintf(("comcryptBlock: numTokenBytes (%d), tokenDex (%d)\n",
745 numTokenBytes
, tokenDex
));
750 * We already wrote tokens and longcode to cipherText; verify we
753 totalCipherTextLen
= (longCodePtr
- startCtextPtr
);
754 if(*cipherTextLen
< totalCipherTextLen
) {
755 ddprintf(("comcryptBlock: short block (2)\n"));
756 return CCR_OUTBUFFER_TOO_SMALL
;
759 cipherText
[CTBO_NUM_TOKENS
] = tokenDex
;
761 cipherText
[CTBO_NUM_LONG_CODES
] = numLongCodes
;
764 if(tokenDex
> MAX_TOKENS
) {
765 ddprintf(("comcryptBlock: counter overflow!\n"));
768 if((numByteCodes
+ numLongCodes
) != tokenDex
) {
769 ddprintf(("comcryptBlock: counter mismatch!\n"));
775 * See if doing a second level comcryption makes sense.
777 destByteCodePtr
= startLongCodePtr
+ (numLongCodes
* 2);
778 if(numByteCodes
> 0) {
779 jmatchAvg
= jmatchTotal
/ numByteCodes
;
782 jmatchAvg
= cbuf
->jmatchThresh
+ 1;
784 if((recursLevel
== 0) && // hard coded recursion limit
785 (cpriv
->level2enable
) && // enabled by caller
786 (numByteCodes
>= cbuf
->minByteCode
) && // meaningful # of bytecodes
787 (jmatchAvg
<= cbuf
->jmatchThresh
)) { // reasonable compression
790 unsigned thisCtext
= cbuf
->level2BufSize
;
793 crtn
= comcryptBlock(cpriv
,
805 * Write level2Buf to cipherText (as byteCodeArray).
806 * Size of 2nd level comcrypted byte code follows longcode array,
807 * then the bytecode itself.
808 * First bump totalCipherTextLen by the size of the comcrypted array
809 * plus one (for the size byte itself), and verify no overflow
811 totalCipherTextLen
+= (thisCtext
+ 1);
812 if(*cipherTextLen
< totalCipherTextLen
) {
813 ddprintf(("comcryptBlock: short block (3)\n"));
814 return CCR_OUTBUFFER_TOO_SMALL
;
816 *destByteCodePtr
++ = thisCtext
;
817 COMPROF_END(cmcLevel2
);
818 memmove(destByteCodePtr
, cbuf
->level2Buf
, thisCtext
);
819 blockDesc
|= CBD_DOUBLE
;
821 l2printf(("***2nd-level comcrypt: numByteCodes %d encrypted "
822 "size %d\n", numByteCodes
, thisCtext
));
823 incrComStat(level2byteCode
, numByteCodes
);
824 incrComStat(level2cipherText
, thisCtext
);
825 incrComStat(level2jmatch
, jmatchTotal
);
826 incrComStat(level2blocks
, 1);
830 * Normal one-level comcryption. Write byteCodes to ciphertext.
831 * numByteCodes is inferred.
833 totalCipherTextLen
+= numByteCodes
;
834 if(*cipherTextLen
< totalCipherTextLen
) {
835 ddprintf(("comcryptBlock: short block (3)\n"));
836 return CCR_OUTBUFFER_TOO_SMALL
;
838 memmove(destByteCodePtr
, cbuf
->codeBuf
, numByteCodes
);
839 blockDesc
|= CBD_SINGLE
;
840 if(recursLevel
== 0) {
841 incrComStat(level1blocks
, 1);
843 /* else this is a 2nd-level, our caller will count */
846 * obfuscate via sigArray (only when we're NOT doing 2nd level
849 if(cpriv
->sigSeqEnable
) {
850 sigMunge(cbuf
, startTokenPtr
, tokenDex
,
851 destByteCodePtr
, startLongCodePtr
);
854 * Prime sigArray state machine for next block. Note in the case
855 * of 2nd level, we skip this step, so the next block starts from
856 * the same state as this one did.
858 cbuf
->sigArray
[0] = cbuf
->sigArray
[tokenDex
];
861 cipherText
[CTBO_BLOCK_DESC
] = blockDesc
;
862 *cipherTextLen
= totalCipherTextLen
;
867 * Main public encrypt function.
869 comcryptReturn
comcryptData(
871 unsigned char *plainText
,
872 unsigned plainTextLen
,
873 unsigned char *cipherText
, // malloc'd by caller
874 unsigned *cipherTextLen
, // IN/OUT
875 comcryptEos endOfStream
) // CCE_END_OF_STREAM, etc.
877 comcryptPriv
*cpriv
= (comcryptPriv
*)cobj
;
878 unsigned ctextLen
= *cipherTextLen
;
885 incrComStat(plaintextBytes
, plainTextLen
);
886 if(cpriv
->versionBytes
== 0) {
888 * First, put header (version, spare) into head of ciphertext.
890 if(ctextLen
< CTEXT_HDR_SIZE
) {
891 ddprintf(("comcryptData: overflow (0)\n"));
892 return CCR_OUTBUFFER_TOO_SMALL
;
894 serializeInt(VERSION_3_Dec_97
, cipherText
);
895 cipherText
+= VERSION_BYTES
;
896 cpriv
->versionBytes
= VERSION_BYTES
;
897 serializeInt(0, cipherText
); // spares
898 cipherText
+= SPARE_BYTES
;
899 ctextLen
-= CTEXT_HDR_SIZE
;
903 * OK, grind it out, one block at a time.
905 while (plainTextLen
!= 0) {
906 thisPtext
= CC_BLOCK_SIZE
;
907 if(thisPtext
> plainTextLen
) {
908 thisPtext
= plainTextLen
;
910 thisCtext
= ctextLen
;
911 crtn
= comcryptBlock(cpriv
,
921 plainText
+= thisPtext
;
922 plainTextLen
-= thisPtext
;
923 if(thisCtext
> ctextLen
) {
924 ddprintf(("comcryptData: undetected ciphertext overlow\n"));
925 return CCR_OUTBUFFER_TOO_SMALL
;
927 cipherText
+= thisCtext
;
928 ctextLen
-= thisCtext
;
930 *cipherTextLen
= *cipherTextLen
- ctextLen
;
931 incrComStat(ciphertextBytes
, *cipherTextLen
);
932 COMPROF_END(cmcTotal
);
937 * Return values from deComcryptBlock().
941 DCB_SHORT
, // incomplete block, try again with more ciphertext
942 DCB_PARSE_ERROR
, // bad block
943 DCB_OUTBUFFER_TOO_SMALL
947 * Assumes exactly one block of ciphertext, error otherwise.
949 static dcbReturn
deComcryptBlock(
951 comcryptBuf
*cbuf
, // not necessarily cpriv->cbuf
952 unsigned char *cipherText
,
953 unsigned cipherTextLen
,
954 unsigned char *plainText
,
955 unsigned *plainTextLen
, // IN/OUT
956 comcryptEos endOfStream
, // CCE_END_OF_STREAM, etc.
957 unsigned *blockSize
) // RETURNED on DCB_SUCCESS
959 unsigned char *tokenPtr
;
960 unsigned numTokenBits
; // constant, from ciphertext
961 unsigned numTokenBytes
;
962 unsigned char *longCodePtr
;
963 unsigned numLongCodes
;
964 unsigned char *byteCodePtr
;
965 unsigned numByteCodes
;
967 unsigned oddByte
= 0;
968 unsigned short codeWord
;
969 unsigned char codeByte
;
970 unsigned ptextLen
= *plainTextLen
; // bytes REMAINING
973 unsigned char blockDesc
;
979 queueElt
*cbufq
= &cbuf
->queue
[0];
980 int level2
= 0; // 2nd level comcrypted block
982 unsigned char sigSeq
; // signature sequence enable
983 unsigned char nibble
;
985 blockDesc
= cipherText
[CTBO_BLOCK_DESC
];
986 if((blockDesc
& CBD_MAGIC_MASK
) != CBD_MAGIC
) {
987 ddprintf(("deComcryptBlock: bad CBD_MAGIC\n"));
988 return DCB_PARSE_ERROR
;
992 * Min block size - blockDesc, numLongCodes, numTokens, one token byte,
995 if(cipherTextLen
< 5) {
998 if((blockDesc
& CBD_FULL_BLOCK_MASK
) == CBD_FULL_BLOCK
) {
1000 * # of token bits implied for full block
1002 numTokenBits
= TOKEN_BITS_FROM_PTEXT(CC_BLOCK_SIZE
);
1003 numTokenBytes
= TOKEN_BYTES_FROM_PTEXT(CC_BLOCK_SIZE
);
1004 tokenPtr
= cipherText
+ CTBO_NUM_TOKENS
;
1007 numTokenBits
= cipherText
[CTBO_NUM_TOKENS
];
1008 numTokenBytes
= TOKEN_BYTES_FROM_TOKEN_BITS(numTokenBits
);
1009 tokenPtr
= cipherText
+ CTBO_NUM_TOKENS
+ 1;
1011 longCodePtr
= tokenPtr
+ numTokenBytes
;
1012 numLongCodes
= cipherText
[CTBO_NUM_LONG_CODES
];
1014 byteCodePtr
= longCodePtr
+ (numLongCodes
* 2); // may increment...
1015 if((blockDesc
& CBD_BLOCK_TYPE_MASK
) == CBD_SINGLE
) {
1017 * # of bytecodes implied from numTokenBits and numLongCodes
1019 numByteCodes
= numTokenBits
- numLongCodes
;
1023 * size of 2nd level comcrypted bytecode specified after longCode
1024 * array (and before the bytecode itself).
1025 * Careful, verify that we can read numByteCodes first...
1027 if((unsigned)(byteCodePtr
- cipherText
) > cipherTextLen
) {
1030 numByteCodes
= *byteCodePtr
++;
1033 *blockSize
= (byteCodePtr
- cipherText
) + numByteCodes
;
1034 if(*blockSize
> cipherTextLen
) {
1039 * We now know that we have a complete cipherblock. Go for it.
1043 * this block's bytecode array contains 2nd level comcrypted bytecodes.
1045 unsigned thisPtext
= cbuf
->level2BufSize
;
1046 unsigned level1CodeSize
;
1048 if(cbuf
->nextBuf
== NULL
) {
1049 ddprintf(("2-level comcypt, no nextBuf available!\n"));
1050 return DCB_PARSE_ERROR
;
1052 drtn
= deComcryptBlock(cpriv
,
1062 ddprintf(("CBT_DOUBLE block, incomplete cipherblock in "
1063 "2nd level code\n"));
1064 return DCB_PARSE_ERROR
;
1066 case DCB_OUTBUFFER_TOO_SMALL
: // not our fault!
1067 case DCB_PARSE_ERROR
:
1069 ddprintf(("2nd-level decomcrypt error (%d)\n", drtn
));
1074 * Supposedly we passed in exactly one cipherblock...
1076 if(numByteCodes
!= level1CodeSize
) {
1077 ddprintf(("2nd-level decomcrypt: "
1078 "numByteCodes != level1CodeSize\n"));
1079 return DCB_PARSE_ERROR
;
1081 l2printf(("2nd-level decomcrypt: ciphertext %d "
1082 "numByteCodes %d\n", numByteCodes
, thisPtext
));
1085 byteCodePtr
= cbuf
->level2Buf
;
1086 numByteCodes
= thisPtext
;
1089 if((blockDesc
& CBD_ODD_MASK
) == CBD_ODD
) {
1094 * Skip signature sequence if this was a 2nd level comcrypted block
1096 sigSeq
= cpriv
->sigSeqEnable
&& !level2
;
1098 for(tokenDex
=0; tokenDex
<numTokenBits
; tokenDex
++) {
1099 match
= !getToken(tokenPtr
, tokenDex
);
1102 * 17 Dec 1997 - Always calculate this regardless of match
1104 nibble
= keynybble(cpriv
->key
, cpriv
->keybytes
,
1105 (cbuf
->nybbleDex
)++);
1108 codeByte
= *byteCodePtr
++;
1111 codeByte
^= (unsigned char)(cbuf
->sigArray
[tokenDex
]);
1115 * dynamically process the queue for match - 8 bits
1116 * of ciphercode, 16 bits of plaintext
1118 codeWord
= cbufq
[codeByte
];
1119 above
= (cbuf
->f1
* codeByte
* (16 + nibble
)) >> 9;
1121 #if SKIP_NIBBLE_ON_QUEUE_0
1124 * Special case for top of queue optimization during
1127 nibble
= cbuf
->nybbleDex
- 1;
1129 #endif /*SKIP_NIBBLE_ON_QUEUE_0*/
1132 * queue[above..codeByte] move one element towards end
1133 * queue[above] = this codeWord
1135 len
= (int)codeByte
- (int)above
;
1136 if(len
> QUEUE_MEMMOVE_THRESH
) {
1137 src
= &cbufq
[above
];
1139 len
*= sizeof(queueElt
);
1140 memmove(dst
, src
, len
);
1143 for(j
= codeByte
; j
> above
; j
--) {
1144 cbufq
[j
] = cbufq
[j
-1];
1147 cbufq
[above
] = codeWord
;
1151 * !match, 16 bits of code
1153 deserializeShort(codeWord
, longCodePtr
);
1155 codeWord
^= cbuf
->sigArray
[tokenDex
];
1158 if(oddByte
&& (tokenDex
== (numTokenBits
- 1))) {
1161 #if SKIP_NIBBLE_ON_QUEUE_0
1162 nibble
= cbuf
->nybbleDex
- 1;
1163 #endif /*SKIP_NIBBLE_ON_QUEUE_0*/
1169 * dynamically process the queue for unmatch; skip if this
1170 * is an oddByte codeword.
1171 * queue[above...QLEN-1] move one element toward end
1172 * queue[above] = new codeWord
1174 above
= ABOVE(cbuf
->f2
) + nibble
;
1175 len
= QLEN
- 1 - (int)above
;
1176 if(len
> QUEUE_MEMMOVE_THRESH
) {
1177 src
= &cbufq
[above
];
1179 len
*= sizeof(queueElt
);
1180 memmove(dst
, src
, len
);
1183 for(j
=QLEN
-1; j
> above
; j
--) {
1184 cbufq
[j
] = cbufq
[j
-1];
1187 cbufq
[above
] = codeWord
;
1193 * Advance signature sequence state machine.
1195 nextSigWord(cbuf
, tokenDex
+1, match
, (above
+ nibble
));
1199 * cook up a byte or two of plainText from code word and invmap[]
1202 ddprintf(("decryptBlock: ptext overflow (1)\n"));
1203 return DCB_OUTBUFFER_TOO_SMALL
;
1205 *plainText
++ = cpriv
->invmap
[(codeWord
>> 8) & 0xff];
1209 * end of oddByte block.
1211 tokenDex
++; // for sigArray maintenance
1212 break; // out of main loop
1216 ddprintf(("decryptBlock: ptext overflow (2)\n"));
1217 return DCB_OUTBUFFER_TOO_SMALL
;
1219 *plainText
++ = cpriv
->invmap
[(codeWord
) & 0xff];
1225 * Prime sigArray state machine for next block.
1228 cbuf
->sigArray
[0] = cbuf
->sigArray
[tokenDex
];
1230 *plainTextLen
= *plainTextLen
- ptextLen
;
1234 comcryptReturn
deComcryptData(
1236 unsigned char *cipherText
,
1237 unsigned cipherTextLen
,
1238 unsigned char *plainText
,
1239 unsigned *plainTextLen
, // IN/OUT
1240 comcryptEos endOfStream
) // CCE_END_OF_STREAM, etc.
1243 comcryptPriv
*cpriv
= (comcryptPriv
*)cobj
;
1244 unsigned char *outorigin
= plainText
;
1245 unsigned ptextLen
= *plainTextLen
;
1246 unsigned thisPtext
; // per block
1252 * Snag version from ciphertext, or as much as we can get
1254 while((cpriv
->versionBytes
< VERSION_BYTES
) && cipherTextLen
) {
1255 cpriv
->version
<<= 8;
1256 cpriv
->version
|= *cipherText
;
1257 cpriv
->versionBytes
++;
1263 * Then skip over the remainder of the header (currently spares)
1265 if((cpriv
->spareBytes
< SPARE_BYTES
) && cipherTextLen
) {
1266 unsigned toSkip
= SPARE_BYTES
- cpriv
->spareBytes
;
1268 if(toSkip
> cipherTextLen
) {
1269 toSkip
= cipherTextLen
;
1271 cpriv
->spareBytes
+= toSkip
;
1272 cipherText
+= toSkip
;
1273 cipherTextLen
-= toSkip
;
1276 if(cipherTextLen
== 0) {
1281 if(cpriv
->version
!= VERSION_3_Dec_97
) {
1282 ddprintf(("Incompatible version.\n"));
1283 return CCR_BAD_CIPHERTEXT
;
1286 while(cipherTextLen
!= 0) {
1289 * Main loop. First deal with possible existing partial block.
1291 if(cpriv
->cbuf
.codeBufLength
!= 0) {
1293 cpriv
->cbuf
.codeBufSize
- cpriv
->cbuf
.codeBufLength
;
1294 unsigned origBufSize
= cpriv
->cbuf
.codeBufLength
;
1296 if(toCopy
> cipherTextLen
) {
1297 toCopy
= cipherTextLen
;
1299 memmove(cpriv
->cbuf
.codeBuf
+ cpriv
->cbuf
.codeBufLength
,
1300 cipherText
, toCopy
);
1301 cpriv
->cbuf
.codeBufLength
+= toCopy
;
1303 thisPtext
= ptextLen
;
1304 drtn
= deComcryptBlock(cpriv
,
1306 cpriv
->cbuf
.codeBuf
,
1307 cpriv
->cbuf
.codeBufLength
,
1315 * Incomplete block in codeBuf
1317 if(endOfStream
== CCE_END_OF_STREAM
) {
1319 * Caller thinks this is the end, but we need more
1321 ddprintf(("deComcryptData(): CCE_END_OF_STREAM, "
1322 "not end of block\n"));
1323 return CCR_BAD_CIPHERTEXT
;
1325 cipherTextLen
-= toCopy
;
1326 if(cipherTextLen
!= 0) {
1328 * i.e., codeBuf overflow - could be s/w error? Do
1329 * we need a bigger buffer?
1331 ddprintf(("deComcryptData: full codeBuf, incomplete "
1333 return CCR_BAD_CIPHERTEXT
;
1337 * OK, stash it and try again
1339 scprintf(("====incomplete codeBuf, codeBufLength %d, "
1340 "cipherTextLen %d\n",
1341 cpriv
->cbuf
.codeBufLength
, toCopy
));
1342 break; // out of main loop (after this switch)
1345 case DCB_OUTBUFFER_TOO_SMALL
:
1346 ddprintf(("codeBuf decomcrypt error short buf\n"));
1347 return CCR_OUTBUFFER_TOO_SMALL
;
1349 case DCB_PARSE_ERROR
:
1351 ddprintf(("codeBuf decomcrypt error (%d)\n", drtn
));
1352 return CCR_BAD_CIPHERTEXT
;
1356 * ctextUsed is how much of caller's ciphertext we used
1357 * in this buffered block
1359 ctextUsed
= blockSize
- origBufSize
;
1360 scprintf(("====decrypted block in codeBuf, blockSize %d, "
1361 "ctextUsed %d, thisPtext %d\n",
1362 blockSize
, ctextUsed
, thisPtext
));
1363 cipherText
+= ctextUsed
;
1364 cipherTextLen
-= ctextUsed
;
1365 plainText
+= thisPtext
;
1366 ptextLen
-= thisPtext
;
1367 cpriv
->cbuf
.codeBufLength
= 0;
1372 * We might have used up all of caller's cipherText processing
1375 if(cipherTextLen
== 0) {
1376 break; // out of main loop
1379 } /* buffered ciphertext in codeBuf */
1382 * Snarf ciphertext, one block at a time.
1385 thisPtext
= ptextLen
;
1386 drtn
= deComcryptBlock(cpriv
,
1399 if(endOfStream
== CCE_END_OF_STREAM
) {
1400 ddprintf(("deComcryptData(): CCE_END_OF_STREAM, not end of "
1402 return CCR_BAD_CIPHERTEXT
;
1405 (cpriv
->cbuf
.codeBufSize
- cpriv
->cbuf
.codeBufLength
)) {
1406 ddprintf(("deComcryptData(): codeBuf overflow!\n"));
1407 return CCR_BAD_CIPHERTEXT
;
1409 memmove(cpriv
->cbuf
.codeBuf
+ cpriv
->cbuf
.codeBufLength
,
1410 cipherText
, cipherTextLen
);
1411 cpriv
->cbuf
.codeBufLength
+= cipherTextLen
;
1413 scprintf(("====Incomplete block, cipherTextLen %d "
1414 "codeBufLength %d\n", cipherTextLen
,
1415 cpriv
->cbuf
.codeBufLength
));
1416 break; // actually out of main loop
1418 case DCB_PARSE_ERROR
:
1419 case DCB_OUTBUFFER_TOO_SMALL
:
1421 return CCR_BAD_CIPHERTEXT
;
1424 if(ptextLen
< thisPtext
) {
1428 ddprintf(("deComcryptData: undetected ptext "
1430 return CCR_BAD_CIPHERTEXT
;
1432 plainText
+= thisPtext
;
1433 ptextLen
-= thisPtext
;
1434 cipherText
+= blockSize
;
1435 cipherTextLen
-= blockSize
;
1436 scprintf(("====decrypted one block, blockSize %d "
1437 "thisPtext %d\n", blockSize
, thisPtext
));
1442 *plainTextLen
= plainText
- outorigin
;