1/*
2	File:		comcryptPriv.c
3
4	Contains:	private routines for comcryption library.
5
6	Written by:	Doug Mitchell
7
8	Copyright:	(c) 1997 by Apple Computer, Inc., all rights reserved.
9
10	Change History (most recent first):
11
12		05/28/98	dpm		Added platform-dependent ascMalloc and ascFree
13		12/23/97	dpm		Added keyHash(), used result to initialize
14								nybbleDex and queue.
15		12/18/97	dpm		Improved queue initialization.
16		12/03/97	dpm		Added queue lookahead; various optimizations.
17		11/13/97	dpm		Created; broke out from comcryption.c
18
19	To Do:
20*/
21
22#include "comcryptPriv.h"
23#include <stdio.h>
24#include <string.h>
25#include <stdlib.h>
26#ifdef	macintosh
27#include <MacMemory.h>
28#endif
29
30/* if NULL, use our own */
31comMallocExternFcn *comMallocExt = NULL;
32comFreeExternFcn *comFreeExt = NULL;
33
34#if		COM_STATS
35comStats _comStats;
36
37void resetComStats()
38{
39	memset(&_comStats, 0, sizeof(comStats));
40}
41
42void getComStats(comStats *stats)
43{
44	*stats = _comStats;
45}
46
47#else	/*COM_STATS*/
48
49#define	incrComStat(stat, num)
50
51#endif	/*COM_STATS*/
52
53/*
54 * Generate a symbol permutation from the key.
55 */
56void key_perm(
57	const unsigned char *key,
58	int 				keybytes,
59	unsigned char 		*map,
60	unsigned char 		*invmap)
61{
62    int i, j, tmp, sum;
63
64    for(sum = 0, j = 0; j < keybytes; j++) {
65		sum += key[j];
66	}
67	for(j=0; j < 256; j++) {
68		map[j] = j;
69	}
70	for(j=0; j < 255; j++) {
71		i = (key[j % keybytes] + j*sum) & 0xff;
72		tmp = map[i];
73		map[i] = map[j];
74		map[j] = tmp;
75	}
76	for(j=0; j<256; j++) {
77		invmap[map[j]] = j;
78	}
79}
80
81int keybyte(
82	const unsigned char *key,
83	int 				keybytes,
84	int 				index)
85{
86    return((int) key[index % keybytes]);
87}
88
89int keynybble(
90	const unsigned char *key,
91	int 				keybytes,
92	int 				index)
93{
94	int i = index % (2*keybytes);
95	int j;
96
97    j = key[i>>1]; 		/* Which byte. */
98    if(i & 1) j >>= 4; 	/* Which nybble. */
99    return(j & 0xf);
100}
101
102/*
103 * Hash a key array.
104 */
105
106#define HASH_SEED	3
107#define HASH_REDUCE	1023
108
109static unsigned keyHash(const unsigned char *key, unsigned keylen)
110{
111	unsigned x = HASH_SEED;  /* Any seed in [1,p-1].  Like SEED = 3. */
112	unsigned ctr;
113
114	for(ctr=0; ctr<keylen; ctr++) {
115		x = (x * (key[ctr] + (ctr & HASH_REDUCE) + 1)) % HASH_PRIME;
116	}
117	return x;
118}
119
120void mallocCodeBufs(comcryptBuf *cbuf)
121{
122	/*
123	 * calculate required buffer sizes.
124	 *
125	 * Assume max required codeBuf size is the max size of ciphertext needed
126	 * to decrypt one block of plaintext.
127	 */
128	cbuf->codeBufSize = comcryptMaxOutBufSize(NULL,
129		CC_BLOCK_SIZE,
130		CCOP_COMCRYPT,
131		1);
132	cbuf->codeBuf = (unsigned char *)ascMalloc(cbuf->codeBufSize);
133
134	/*
135	 * max size needed for level2Buf is the MaxOutBufSize of comcrypting
136	 * a whole block of byte code. Note we assume that MaxOutBufSize(n) >= n.
137	 */
138	cbuf->level2BufSize = comcryptMaxOutBufSize(NULL,
139		MAX_TOKENS,				// one byte per token
140		CCOP_COMCRYPT,
141		1);
142	cbuf->level2Buf = (unsigned char *)ascMalloc(cbuf->level2BufSize);
143
144	cbuf->queue = (queueElt *)ascMalloc(sizeof(queueElt) * QLEN);
145
146	#if		QUEUE_LOOKAHEAD
147	/*
148	 * Might want to do this dynamically, though that requires the malloc
149	 * of the lookAhead buffer to be done in initCodeBufs(), not here (at
150	 * comcryptAlloc() time).
151	 *
152	 * FIXME : should do the malloc of lookAhead buffer lazily for
153	 * non-Mac platforms.
154	 */
155	cbuf->lookAhead = (unsigned char *)ascMalloc(LOOKAHEAD_SIZE);
156	#else	/* QUEUE_LOOKAHEAD */
157	cbuf->lookAhead = NULL;
158	#endif	/* QUEUE_LOOKAHEAD */
159
160	/*
161	 * This maybe should also be done dynamically, lazily...
162	 */
163	cbuf->sigArray = (unsigned *)ascMalloc((MAX_TOKENS + 1) * sizeof(unsigned));
164}
165
166void initCodeBufs(
167	comcryptBuf *cbuf,
168	const unsigned char *key,
169	unsigned keyLen,
170	unsigned char laEnable,
171	unsigned char sigSeqEnable)
172{
173	unsigned ct;
174	unsigned qval;
175	unsigned char khash = (unsigned char)keyHash(key, keyLen);
176
177	cbuf->nybbleDex = khash;
178
179	if(laEnable) {
180		memset(cbuf->lookAhead, 0, LOOKAHEAD_SIZE);
181	}
182
183	laprintf(("initing queue and lookahead\n"));
184
185	for(ct=0; ct<QLEN; ct++) {
186		/*
187		 * New queue init 23 Dec - init from khash
188		 */
189		unsigned short sbyte = ct ^ khash;
190		qval = (sbyte << 8) | ct;
191		cbuf->queue[ct] = qval;
192		if(laEnable) {
193			markInQueue(cbuf, qval, 1);
194		}
195	}
196	// note cbuf->nybbleDex = khash on return...
197
198	cbuf->f1 = F1_DEFAULT;
199	cbuf->f2 = F2_DEFAULT;
200	cbuf->jmatchThresh = THRESH_2LEVEL_JMATCH_DEF;
201	cbuf->minByteCode  = THRESH_2LEVEL_NUMBYTECODES_DEF;
202	if(sigSeqEnable) {
203		initSigSequence(cbuf, key, keyLen);
204	}
205}
206
207void freeCodeBufs(comcryptBuf *cbuf)
208{
209	if(cbuf->queue != NULL) {
210		ascFree(cbuf->queue);
211	}
212	if(cbuf->codeBuf != NULL) {
213		ascFree(cbuf->codeBuf);
214	}
215	if(cbuf->level2Buf != NULL) {
216		ascFree(cbuf->level2Buf);
217	}
218	if(cbuf->nextBuf != NULL) {
219		freeCodeBufs(cbuf->nextBuf);
220		ascFree(cbuf->nextBuf);
221		cbuf->nextBuf = NULL;
222	}
223	if(cbuf->lookAhead != NULL) {
224		ascFree(cbuf->lookAhead);
225	}
226	if(cbuf->sigArray != NULL) {
227		ascFree(cbuf->sigArray);
228	}
229}
230
231void serializeInt(
232	unsigned i,
233	unsigned char *buf)
234{
235	buf[0] = (unsigned char)(i >> 24);
236	buf[1] = (unsigned char)(i >> 16);
237	buf[2] = (unsigned char)(i >> 8);
238	buf[3] = (unsigned char)(i & 0xff);
239}
240
241unsigned deserializeInt(unsigned char *buf)
242{
243	unsigned i;
244
245	i  = ((unsigned)buf[0]) << 24;
246	i |= ((unsigned)buf[1]) << 16;
247	i |= ((unsigned)buf[2]) << 8;
248	i |= buf[3];
249	return i;
250}
251
252#if		COM_PARAM_ENABLE
253
254unsigned getF1(comcryptObj cobj)
255{
256	comcryptPriv *cpriv = (comcryptPriv *)cobj;
257
258	return cpriv->cbuf.f1;
259}
260
261void setF1(comcryptObj cobj, unsigned f1)
262{
263	comcryptPriv *cpriv = (comcryptPriv *)cobj;
264
265	cpriv->cbuf.f1 = f1;
266	if(cpriv->cbuf.nextBuf != NULL) {
267		cpriv->cbuf.nextBuf->f1 = f1;
268	}
269}
270
271unsigned getF2(comcryptObj cobj)
272{
273	comcryptPriv *cpriv = (comcryptPriv *)cobj;
274
275	return cpriv->cbuf.f2;
276}
277
278void setF2(comcryptObj cobj, unsigned f2)
279{
280	comcryptPriv *cpriv = (comcryptPriv *)cobj;
281
282	cpriv->cbuf.f2 = f2;
283	if(cpriv->cbuf.nextBuf != NULL) {
284		cpriv->cbuf.nextBuf->f2 = f2;
285	}
286}
287
288unsigned getJmatchThresh(comcryptObj cobj)
289{
290	comcryptPriv *cpriv = (comcryptPriv *)cobj;
291
292	return cpriv->cbuf.jmatchThresh;
293}
294
295void setJmatchThresh(comcryptObj cobj, unsigned jmatchThresh)
296{
297	comcryptPriv *cpriv = (comcryptPriv *)cobj;
298
299	cpriv->cbuf.jmatchThresh = jmatchThresh;
300	if(cpriv->cbuf.nextBuf != NULL) {
301		cpriv->cbuf.nextBuf->jmatchThresh = jmatchThresh;
302	}
303}
304
305unsigned getMinByteCode(comcryptObj cobj)
306{
307	comcryptPriv *cpriv = (comcryptPriv *)cobj;
308
309	return cpriv->cbuf.minByteCode;
310}
311
312void setMinByteCode(comcryptObj cobj, unsigned minByteCode)
313{
314	comcryptPriv *cpriv = (comcryptPriv *)cobj;
315
316	cpriv->cbuf.minByteCode = minByteCode;
317	if(cpriv->cbuf.nextBuf != NULL) {
318		cpriv->cbuf.nextBuf->minByteCode = minByteCode;
319	}
320}
321
322#endif	/*COM_PARAM_ENABLE*/
323
324
325#if		COM_LA_DEBUG
326
327/*
328 * Verify integrity of lookahead w.r.t. queue.
329 */
330int testLookAhead(comcryptBuf *cbuf, int i1, int i2)
331{
332	unsigned i;
333
334	if(!cbuf->laEnable) {
335		return 0;
336	}
337	for(i=0; i<QLEN; i++) {
338		if(!inQueue(cbuf, cbuf->queue[i])) {
339			printf("aaagh, corrupted lookahead - in queue[], !inQueue()\n");
340			printf("i=0x%x   i1=0x%x   i2=0x%x\n",
341				i, i1, i2);
342			printf("\n");
343			exit(1);
344		}
345	}
346	//return initTestLookAhead(cbuf);
347	return 0;
348}
349
350int initTestLookAhead(comcryptBuf *cbuf)
351{
352	#if		QUEUE_LOOKAHEAD_BIT
353
354	unsigned codeWord = 0;
355	unsigned char bit;
356	unsigned short byte;
357	unsigned char *la = cbuf->lookAhead;
358
359	for(byte=0; byte<LOOKAHEAD_SIZE; byte++) {
360		for(bit=1; bit!=0; bit<<=1) {
361			if(la[byte] & bit) {
362				/*
363				 * in lookahead, make sure it's in queue[]
364				 */
365				int i;
366				int found = 0;
367
368				for(i=0; i<QLEN; i++) {
369					if(cbuf->queue[i] == codeWord) {
370						found = 1;
371						break;
372					}
373				}
374				if(!found) {
375					printf("***corrupted init lookahead - in l.a., "
376						"not in queue[]\n");
377					printf("codeWord 0x%x\n", codeWord);
378					printf("\n");
379					exit(1);
380				}
381			}
382			codeWord++;
383		}
384	}
385
386	#endif	/* QUEUE_LOOKAHEAD_BIT */
387	return 0;
388}
389
390#endif	/* COM_LA_DEBUG */
391
392void initSigSequence(comcryptBuf *cbuf,
393	const unsigned char *key,
394	unsigned keyLen)
395{
396    unsigned seed = IN_OFFSET;
397	unsigned j;
398
399    for(j=0; j<keyLen; j++) {
400		seed += key[j];
401    }
402    seed %= HASH_PRIME;
403    if(seed == 0) {
404		seed = IN_OFFSET;
405	}
406	cbuf->sigArray[0] = (unsigned short)seed;
407}
408
409#if	0
410/*
411 * Called once per token bit, after processing the token.
412 */
413void nextSigWord(comcryptBuf *cbuf,
414	unsigned sigDex,			// same as tokenDex
415	unsigned match,
416	unsigned above)				// jabove, keyabove
417{
418	unsigned offset;
419	unsigned short *sigArray = cbuf->sigArray;
420
421	#if		COM_DEBUG
422	if(sigDex == 0) {
423		printf("nextSigWord underflow\n");
424		exit(1);
425	}
426	if(sigDex > MAX_TOKENS) {
427		printf("nextSigWord overflow\n");
428		exit(1);
429	}
430	#endif
431
432	if(match) {
433		offset = IN_OFFSET;
434	}
435	else {
436		offset = OUT_OFFSET;
437	}
438#if		1
439	sigArray[sigDex] = (sigArray[sigDex-1] * (above + offset)) % HASH_PRIME;
440#endif
441}
442#endif
443
444/*
445 * Obfuscate a block of ciphertext.
446 */
447void sigMunge(comcryptBuf *cbuf,
448	const unsigned char *tokenPtr,
449	unsigned numTokens,
450	unsigned char *byteCodePtr,
451	unsigned char *longCodePtr)
452{
453	unsigned char tokenBit = 0x01;
454	unsigned token;
455	unsigned short sig;
456
457	for(token=0; token<numTokens; token++) {
458		sig = cbuf->sigArray[token];
459		if(*tokenPtr & tokenBit) {
460			/* no match - munge longCode - written MSB first */
461			*longCodePtr++ ^= (unsigned char)(sig >> 8);
462			*longCodePtr++ ^= (unsigned char)sig;
463		}
464		else {
465			/* match - munge byteCode */
466			*byteCodePtr++ ^= (unsigned char)sig;
467		}
468		tokenBit <<= 1;
469		if(tokenBit == 0) {
470			tokenBit = 0x01;
471			tokenPtr++;
472		}
473	}
474}
475
476
477/*
478 * All this can be optimized and tailored to specific platforms, of course...
479 */
480
481void *ascMalloc(unsigned size)
482{
483	#ifdef	macintosh
484
485	Handle h;
486	OSErr err;
487	Ptr p;
488
489	#endif	/* mac */
490
491	if(comMallocExt != NULL) {
492		return (comMallocExt)(size);
493	}
494
495	#ifdef	macintosh
496
497	h = nil;
498	err = errSecSuccess;
499
500	h = NewHandleSys(size);		// system heap is not paged
501	do{
502		HLockHi(h);			// will move low in system heap
503		err = MemError();
504		if( err != errSecSuccess ) break;
505		p = *h;
506	}while(0);
507	if( err != errSecSuccess ){
508	    return NULL;
509	}
510	return p;
511
512	#else	/* others...*/
513	return malloc(size);
514	#endif
515}
516
517void ascFree(void *data)
518{
519	#ifdef macintosh
520	Handle h;
521	#endif
522
523	if(comFreeExt != NULL) {
524		(comFreeExt)(data);
525		return;
526	}
527
528	#ifdef macintosh
529	if( data != nil ){
530		h = RecoverHandle((Ptr) data);
531		DisposeHandle(h);
532	}
533
534	#else	/* others */
535	free(data);
536	#endif
537}
538