1/* Copyright (c) 1998,2011,2014 Apple Inc.  All Rights Reserved.
2 *
3 * NOTICE: USE OF THE MATERIALS ACCOMPANYING THIS NOTICE IS SUBJECT
4 * TO THE TERMS OF THE SIGNED "FAST ELLIPTIC ENCRYPTION (FEE) REFERENCE
5 * SOURCE CODE EVALUATION AGREEMENT" BETWEEN APPLE, INC. AND THE
6 * ORIGINAL LICENSEE THAT OBTAINED THESE MATERIALS FROM APPLE,
7 * INC.  ANY USE OF THESE MATERIALS NOT PERMITTED BY SUCH AGREEMENT WILL
8 * EXPOSE YOU TO LIABILITY.
9 ***************************************************************************
10 *
11 * FeeDES.c - generic, portable DES encryption object
12 *
13 * Revision History
14 * ----------------
15 * 10/06/98		ap
16 *	Changed to compile with C++.
17 * 05 Jan 98 at Apple
18 *	Avoid a bcopy() on encrypt/decrypt of each block
19 * 31 Mar 97 at Apple
20 *	New per-instance API for DES.c
21 * 26 Aug 96 at NeXT
22 *	Created.
23 */
24
25#include "ckconfig.h"
26
27#if	CRYPTKIT_SYMMETRIC_ENABLE
28
29#include "feeDES.h"
30#include "feeTypes.h"
31#include "ckDES.h"
32#include "falloc.h"
33#include "feeDebug.h"
34#include "feeFunctions.h"
35#include "platform.h"
36#include <stdlib.h>
37
38#ifndef	NULL
39#define NULL	((void *)0)
40#endif	/* NULL */
41
42typedef struct {
43	int		blockMode;			/* default = 0 */
44	unsigned char 	lastBlock[DES_BLOCK_SIZE_BYTES];	/* for CBC */
45	struct _desInst	dinst;
46} fdesInst;
47
48static void feeDESInit(desInst dinst)
49{
50	desinit(dinst, DES_MODE_STD);		// detects redundant calls
51}
52
53/*
54 * Alloc and init a feeDES object with specified initial state.
55 * State must be at least 8 bytes; only 8 bytes are used, ignoring
56 * MSB of each bytes.
57 */
58feeDES feeDESNewWithState(const unsigned char *state,
59	unsigned stateLen)
60{
61	fdesInst *fdinst;
62
63	if(stateLen < FEE_DES_MIN_STATE_SIZE) {
64		return NULL;
65	}
66	fdinst = (fdesInst*) fmalloc(sizeof(fdesInst));
67	bzero(fdinst, sizeof(fdesInst));
68	feeDESInit(&fdinst->dinst);
69	feeDESSetState((feeDES)fdinst, state, stateLen);
70	return fdinst;
71}
72
73void feeDESFree(feeDES des)
74{
75	memset(des, 0, sizeof(fdesInst));
76	ffree(des);
77}
78
79/*
80 * Set new initial state.
81 */
82feeReturn feeDESSetState(feeDES des,
83	const unsigned char *state,
84	unsigned stateLen)
85{
86	fdesInst *fdinst = (fdesInst*) des;
87	char Key[DES_KEY_SIZE_BYTES_EXTERNAL];
88					// 'key' causes problems with
89					// some weird Unix header
90	unsigned byte;
91
92	if(stateLen < (DES_KEY_SIZE_BYTES_EXTERNAL)) {
93		return FR_IllegalArg;
94	}
95	bzero(fdinst->lastBlock, DES_BLOCK_SIZE_BYTES);
96	bcopy(state, Key, DES_KEY_SIZE_BYTES_EXTERNAL);
97
98	/*
99	 * Set up parity bits
100	 */
101	for(byte=0; byte<DES_KEY_SIZE_BYTES_EXTERNAL; byte++){
102	    int i;
103	    unsigned p;
104
105	    p = 0;
106	    for(i=0;i<7;i++) {
107		if(Key[byte] & (1 << i)) {
108		    p++;
109		}
110	    }
111	    if((p & 1) == 0) {
112		Key[byte] |= 0x80;
113	    }
114	    else {
115		Key[byte] &= ~0x80;
116	    }
117	}
118	dessetkey(&fdinst->dinst, Key);
119	return FR_Success;
120}
121
122void feeDESSetBlockMode(feeDES des)
123{
124	fdesInst *fdinst = (fdesInst*) des;
125
126	fdinst->blockMode = 1;
127}
128
129void feeDESSetChainMode(feeDES des)
130{
131	fdesInst *fdinst = (fdesInst*) des;
132
133	fdinst->blockMode = 0;
134}
135
136unsigned feeDESPlainBlockSize(feeDES des)
137{
138	return DES_BLOCK_SIZE_BYTES;
139}
140
141unsigned feeDESCipherBlockSize(feeDES des)
142{
143	return DES_BLOCK_SIZE_BYTES;
144}
145
146unsigned feeDESCipherBufSize(feeDES des)
147{
148	/*
149	 * Normally DES_BLOCK_SIZE, two blocks for finalBlock
150	 */
151	return 2 * DES_BLOCK_SIZE_BYTES;
152}
153
154/*
155
156 * Return the size of ciphertext to hold specified size of plaintext.
157
158 */
159
160unsigned feeDESCipherTextSize(feeDES des, unsigned plainTextSize)
161
162{
163
164	unsigned blocks = (plainTextSize + DES_BLOCK_SIZE_BYTES - 1) /
165	    DES_BLOCK_SIZE_BYTES;
166
167	if((plainTextSize % DES_BLOCK_SIZE_BYTES) == 0) {
168		/*
169		 * One more block for resid count
170		 */
171		blocks++;
172	}
173
174	return blocks * DES_BLOCK_SIZE_BYTES;
175
176}
177
178
179/*
180 * Key size in bits.
181 */
182unsigned feeDESKeySize(feeDES des)
183{
184	return DES_KEY_SIZE_BITS;
185}
186
187/*
188 * Encrypt a block or less of data. Caller malloc's cipherText.
189 */
190feeReturn feeDESEncryptBlock(feeDES des,
191	const unsigned char *plainText,
192	unsigned plainTextLen,
193	unsigned char *cipherText,
194	unsigned *cipherTextLen,		// RETURNED
195	int finalBlock)
196{
197	fdesInst *fdinst = (fdesInst*) des;
198	feeReturn frtn = FR_Success;
199	unsigned cipherLen;
200
201	if(plainTextLen > DES_BLOCK_SIZE_BYTES) {
202		return FR_IllegalArg;
203	}
204	if(plainTextLen) {
205		/*
206		 * We're called with plainTextLen = 0 and finalBlock
207		 * recursively to clean up last block.
208		 */
209		bcopy(plainText, cipherText, plainTextLen);
210	}
211	if(plainTextLen < DES_BLOCK_SIZE_BYTES) {
212		if(!finalBlock) {
213			/*
214			 * odd-size block only legal last time thru
215			 */
216			return FR_IllegalArg;
217		}
218
219		/*
220		 * Last block, final byte = residual length.
221		 */
222		cipherText[DES_BLOCK_SIZE_BYTES - 1] = plainTextLen;
223	}
224
225        if(!fdinst->blockMode) {
226		/*
227		 * CBC mode; chain in last cipher word
228		 */
229		unsigned char *cp = cipherText;
230		unsigned char *cp1 = fdinst->lastBlock;
231		int i;
232
233		for(i=0; i<DES_BLOCK_SIZE_BYTES; i++) {
234		    *cp++ ^= *cp1++;
235		}
236        }
237        endes(&fdinst->dinst, (char *)cipherText);	/* Encrypt block */
238        if(!fdinst->blockMode){
239		/*
240		 * Save outgoing ciphertext for chain
241		 */
242			bcopy(cipherText, fdinst->lastBlock, DES_BLOCK_SIZE_BYTES);
243        }
244	cipherLen = DES_BLOCK_SIZE_BYTES;
245
246	if(finalBlock) {
247	    if(plainTextLen == DES_BLOCK_SIZE_BYTES) {
248	       /*
249		* Special case: finalBlock true, plainTextLen == blockSize.
250		* In this case we generate one more block of ciphertext,
251		* with a resid length of zero.
252		*/
253		unsigned moreCipher;			// additional cipherLen
254
255		frtn = feeDESEncryptBlock(des,
256			NULL,				// plainText not used
257			0,				// resid
258			cipherText + DES_BLOCK_SIZE_BYTES,	// append...
259			&moreCipher,
260			1);
261		if(frtn == FR_Success) {
262			cipherLen += moreCipher;
263		}
264
265	    }
266	    if(plainTextLen != 0) {
267		/*
268		 * Reset internal state in prep for next encrypt/decrypt.
269		 * Note we avoid this in the recursive call (plainTextLen = 0).
270		 */
271		bzero(fdinst->lastBlock, DES_BLOCK_SIZE_BYTES);
272	    }
273	}
274
275	if(frtn == FR_Success) {
276		*cipherTextLen = cipherLen;
277	}
278	return frtn;
279}
280
281/*
282 * Decrypt a block of data. Caller malloc's plainText. Always
283 * generates DES_BLOCK_SIZE_BYTES bytes or less of plainText.
284 */
285feeReturn feeDESDecryptBlock(feeDES des,
286	const unsigned char *cipherText,
287	unsigned cipherTextLen,
288	unsigned char *plainText,
289	unsigned *plainTextLen,		// RETURNED
290	int finalBlock)
291{
292	fdesInst *fdinst = (fdesInst*) des;
293	unsigned char work[DES_BLOCK_SIZE_BYTES];
294	unsigned char ivtmp[DES_BLOCK_SIZE_BYTES];
295
296	if(cipherTextLen != DES_BLOCK_SIZE_BYTES) {
297		/*
298		 * We always generate ciphertext in multiples of block size.
299		 */
300		return FR_IllegalArg;
301	}
302
303        bcopy(cipherText, work, DES_BLOCK_SIZE_BYTES);
304        if(!fdinst->blockMode && !finalBlock) {
305		/*
306		 * Save incoming ciphertext for chain
307		 */
308            	bcopy(cipherText, ivtmp, DES_BLOCK_SIZE_BYTES);
309        }
310        dedes(&fdinst->dinst, (char *)work);
311        if(!fdinst->blockMode){
312		/*
313		 * Unchain block using previous block's ciphertext;
314		 * save current ciphertext for next
315		 */
316		char *cp = (char *)work;
317		char *cp1 = (char*)fdinst->lastBlock;
318		int i;
319
320		for(i=0; i<DES_BLOCK_SIZE_BYTES; i++) {
321		    *cp++ ^= *cp1++;
322		}
323		if(!finalBlock) {
324		    bcopy(ivtmp, fdinst->lastBlock, DES_BLOCK_SIZE_BYTES);
325		}
326        }
327	if(finalBlock) {
328		/*
329		 * deal with residual block; its size is in last byte of
330		 * work[]
331		 */
332		unsigned resid = work[DES_BLOCK_SIZE_BYTES-1];
333
334		if(resid > (DES_BLOCK_SIZE_BYTES-1)) {
335			return FR_BadCipherText;
336		}
337		if(resid > 0) {
338			bcopy(work, plainText, resid);
339		}
340		*plainTextLen = resid;
341
342		/*
343		 * Reset internal state in prep for next encrypt/decrypt.
344		 */
345		bzero(fdinst->lastBlock, DES_BLOCK_SIZE_BYTES);
346	}
347	else {
348		bcopy(work, plainText, DES_BLOCK_SIZE_BYTES);
349		*plainTextLen = DES_BLOCK_SIZE_BYTES;
350	}
351	return FR_Success;
352}
353
354/*
355 * Convenience routines to encrypt & decrypt multi-block data.
356 */
357feeReturn feeDESEncrypt(feeDES des,
358	const unsigned char *plainText,
359	unsigned plainTextLen,
360	unsigned char **cipherText,		// malloc'd and RETURNED
361	unsigned *cipherTextLen)		// RETURNED
362{
363	const unsigned char	*ptext;			// per block
364	unsigned		ptextLen;		// total to go
365	unsigned		thisPtextLen;		// per block
366	unsigned		ctextLen;		// per block
367	unsigned char		*ctextResult;		// to return
368	unsigned char		*ctextPtr;
369	unsigned 		ctextLenTotal;		// running total
370	feeReturn		frtn;
371	int			finalBlock;
372	unsigned		ctextMallocd;
373
374	if(plainTextLen == 0) {
375		dbgLog(("feeDESDecrypt: NULL plainText\n"));
376		return FR_IllegalArg;
377	}
378
379	ptext = plainText;
380	ptextLen = plainTextLen;
381	ctextMallocd = feeDESCipherTextSize(des, plainTextLen);
382	ctextResult = (unsigned char*) fmalloc(ctextMallocd);
383	ctextPtr = ctextResult;
384	ctextLenTotal = 0;
385
386	while(1) {
387		if(ptextLen <= DES_BLOCK_SIZE_BYTES) {
388			finalBlock = 1;
389			thisPtextLen = ptextLen;
390		}
391		else {
392			finalBlock = 0;
393			thisPtextLen = DES_BLOCK_SIZE_BYTES;
394		}
395		frtn = feeDESEncryptBlock(des,
396			ptext,
397			thisPtextLen,
398			ctextPtr,
399			&ctextLen,
400			finalBlock);
401		if(frtn) {
402			dbgLog(("feeDESEncrypt: encrypt error: %s\n",
403				feeReturnString(frtn)));
404			break;
405		}
406		if(ctextLen == 0) {
407			dbgLog(("feeDESEncrypt: null ciphertext\n"));
408			frtn = FR_Internal;
409			break;
410		}
411		ctextLenTotal += ctextLen;
412		if(ctextLenTotal > (plainTextLen + DES_BLOCK_SIZE_BYTES)) {
413			dbgLog(("feeDESEncrypt: ciphertext overflow\n"));
414			frtn = FR_Internal;
415			break;
416		}
417		if(finalBlock) {
418			break;
419		}
420		ctextPtr += ctextLen;
421		ptext += thisPtextLen;
422		ptextLen -= thisPtextLen;
423	}
424	if(frtn) {
425		ffree(ctextResult);
426		*cipherText = NULL;
427		*cipherTextLen = 0;
428	}
429	else {
430		#if	FEE_DEBUG
431		if(ctextLenTotal != ctextMallocd) {
432			dbgLog(("feeDESEncrypt: ctextLen error\n"));
433		}
434		#endif	/* FEE_DEBUG */
435		*cipherText = ctextResult;
436		*cipherTextLen = ctextLenTotal;
437	}
438	return frtn;
439
440}
441
442feeReturn feeDESDecrypt(feeDES des,
443	const unsigned char *cipherText,
444	unsigned cipherTextLen,
445	unsigned char **plainText,		// malloc'd and RETURNED
446	unsigned *plainTextLen)			// RETURNED
447{
448	const unsigned char	*ctext;
449	unsigned		ctextLen;		// total to go
450	unsigned		ptextLen;		// per block
451	unsigned char		*ptextResult;		// to return
452	unsigned char		*ptextPtr;
453	unsigned 		ptextLenTotal;		// running total
454	feeReturn		frtn = FR_Success;
455	int			finalBlock;
456
457	if(cipherTextLen % DES_BLOCK_SIZE_BYTES) {
458		dbgLog(("feeDESDecrypt: unaligned cipherText\n"));
459		return FR_BadCipherText;
460	}
461	if(cipherTextLen == 0) {
462		dbgLog(("feeDESDecrypt: NULL cipherText\n"));
463		return FR_BadCipherText;
464	}
465
466	ctext = cipherText;
467	ctextLen = cipherTextLen;
468
469	/*
470	 * Plaintext length always <= cipherTextLen
471	 */
472	ptextResult = (unsigned char*) fmalloc(cipherTextLen);
473	ptextPtr = ptextResult;
474	ptextLenTotal = 0;
475
476	while(ctextLen) {
477		if(ctextLen == DES_BLOCK_SIZE_BYTES) {
478		    finalBlock = 1;
479		}
480		else {
481		    finalBlock = 0;
482		}
483		frtn = feeDESDecryptBlock(des,
484			ctext,
485			DES_BLOCK_SIZE_BYTES,
486			ptextPtr,
487			&ptextLen,
488			finalBlock);
489		if(frtn) {
490			dbgLog(("feeDESDecrypt decrypt: %s\n",
491				feeReturnString(frtn)));
492			break;
493		}
494		if(ptextLen == 0) {
495			/*
496			 * Normal termination case for
497			 * plainTextLen % DES_BLOCK_SIZE_BYTES == 0
498			 */
499			if(!finalBlock) {
500				dbgLog(("feeDESDecrypt: decrypt sync"
501					" error!\n"));
502				frtn = FR_BadCipherText;
503				break;
504			}
505			else {
506				break;
507			}
508		}
509		else {
510			ptextPtr += ptextLen;
511			ptextLenTotal += ptextLen;
512		}
513		ctext += DES_BLOCK_SIZE_BYTES;
514		ctextLen -= DES_BLOCK_SIZE_BYTES;
515	}
516
517	if(frtn) {
518		ffree(ptextResult);
519		*plainText = NULL;
520		*plainTextLen = 0;
521	}
522	else {
523		*plainText = ptextResult;
524		*plainTextLen = ptextLenTotal;
525	}
526	return frtn;
527}
528
529#endif	/* CRYPTKIT_SYMMETRIC_ENABLE */
530