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 * FeeFEEDExp.c - generic FEED encryption object, 2:1 expansion
12 *
13 * Revision History
14 * ----------------
15 * 10/06/98		ap
16 *	Changed to compile with C++.
17 * 20 Jan 1998 at Apple
18 * 	Mods for primeType == PT_GENERAL case.
19 * 12 Jun 1997 at Apple
20 *	Was curveOrderJustify(), is lesserX1OrderJustify()
21 * 03 Mar 1997 at Apple
22 *	Trimmed plainBlockSize by one byte if q mod 8 = 0
23 * 03 Feb 97 at NeXT
24 *	Renamed to feeFEEDExp.c
25 *	Justified random xaux to [2, minimumX1Order]
26 *	Added feeFEEDExpCipherTextSize()
27 * 15 Jan 97 at NeXT
28 *	Cleaned up which_curve/index code to use CURVE_MINUS/CURVE_PLUS
29 * 28 Aug 96 at NeXT
30 *	Created from Blaine Garst's NSFEECryptor.m.
31 */
32
33#include "ckconfig.h"
34
35#if	CRYPTKIT_ASYMMETRIC_ENABLE
36
37#include "feeTypes.h"
38#include "feeFEEDExp.h"
39#include "feePublicKey.h"
40#include "feePublicKeyPrivate.h"
41#include "elliptic.h"
42#include "falloc.h"
43#include "feeRandom.h"
44#include "ckutilities.h"
45#include "feeFunctions.h"
46#include "platform.h"
47#include "feeDebug.h"
48#include <stdlib.h>
49
50#define	FEED_DEBUG				0
51
52#define PRINT_GIANT(g)	printGiant(g)
53
54/*
55 * Format of clue byte. Currently just one bit.
56 */
57#define CLUE_ELL_ADD_SIGN		0x01
58#define CLUE_ELL_ADD_SIGN_PLUS	0x01
59#define CLUE_ELL_ADD_SIGN_MINUS	0x00
60
61/*
62 * Private data.
63 */
64typedef struct {
65	key			plus;
66	key			minus;
67	unsigned	plainBlockSize;	/* plaintext block size */
68	unsigned	cipherBlockSize;/* ciphertext block size */
69	curveParams	*cp;
70	giant		gPriv;		/* private data, only for decrypt */
71	/* one of the follow two is valid for encrypt */
72	feeRand		rand;		/* only created for encrypt */
73	feeRandFcn	randFcn;
74	void		*randRef;
75
76	/*
77	 * temporary variables used for encrypt/decrypt. The values in these
78	 * is not needed to be kept from block to block; we just
79	 * alloc them once per lifetime of a feeFEED object as an optimization.
80	 */
81	giant 		xp;		/* plaintext */
82	giant		xc;		/* clue = r(P1?) */
83	giant		xq;		/* r(pubB?) or priB?(xc) */
84	giant		xm;		/* ciphertext */
85	giant		xaux;		/* scratch */
86	unsigned char	*randData;	/* only created for encrypt */
87} feedInst;
88
89/*
90 * "zero residue" indicator.
91 */
92#define RESID_ZERO	0xff
93
94/*
95 * Alloc and init a feeFEEDExp object associated with specified feePubKey.
96 */
97feeFEEDExp feeFEEDExpNewWithPubKey(
98	feePubKey pubKey,
99	feeRandFcn randFcn,		// optional
100	void *randRef)
101{
102	feedInst 		*finst = (feedInst *) fmalloc(sizeof(feedInst));
103	giant 			privGiant;
104
105	finst->cp = curveParamsCopy(feePubKeyCurveParams(pubKey));
106	finst->plus = new_public_with_key(feePubKeyPlusCurve(pubKey),
107		finst->cp);
108	finst->minus = new_public_with_key(feePubKeyMinusCurve(pubKey),
109		finst->cp);
110
111	/*
112	 * These might yield NULL data; we can only encrypt in that case.
113	 */
114	privGiant = feePubKeyPrivData(pubKey);
115	if(privGiant) {
116		finst->gPriv = newGiant(finst->cp->maxDigits);
117		gtog(privGiant, finst->gPriv);
118	}
119	else {
120		finst->gPriv = NULL;
121	}
122
123	/*
124	 * Conservative, rounding down, on plaintext blocks since we don't
125	 * want to split bytes.
126	 */
127	if(finst->cp->primeType == FPT_General) {
128	    unsigned blen = bitlen(finst->cp->basePrime);
129
130	    finst->plainBlockSize = blen / 8;
131	    if((blen % 8) == 0) {
132	    	/*
133		 * round down some more...
134		 */
135		finst->plainBlockSize--;
136	    }
137	}
138	else {
139	    finst->plainBlockSize = finst->cp->q / 8;
140	    if(((finst->cp->q & 0x7) == 0) && (finst->cp->k > 0)) {
141		/*
142		 * Special case, with q mod 8 == 0. Here we have to trim back
143		 * the plainBlockSize by one byte.
144		 */
145		finst->plainBlockSize--;
146	    }
147	}
148
149	/*
150	 * One block of ciphertext - two giants (with implied sign) and a
151	 * parity byte
152	 */
153	finst->cipherBlockSize = (2 * finst->cp->minBytes) + 1;
154
155	finst->xp = newGiant(finst->cp->maxDigits);
156	finst->xc = newGiant(finst->cp->maxDigits);
157	finst->xq = newGiant(finst->cp->maxDigits);
158	finst->xm = newGiant(finst->cp->maxDigits);
159	finst->xaux = newGiant(finst->cp->maxDigits);
160	finst->rand = NULL;
161	finst->randData = NULL;
162	finst->randFcn = randFcn;
163	finst->randRef = randRef;
164	return finst;
165}
166
167void feeFEEDExpFree(feeFEEDExp feed)
168{
169	feedInst *finst = (feedInst *) feed;
170
171	free_key(finst->plus);
172	free_key(finst->minus);
173	freeGiant(finst->xc);
174	clearGiant(finst->xp); freeGiant(finst->xp);
175	clearGiant(finst->xq); freeGiant(finst->xq);
176	freeGiant(finst->xm);
177	clearGiant(finst->xaux); freeGiant(finst->xaux);
178	if(finst->gPriv) {
179		clearGiant(finst->gPriv);
180		freeGiant(finst->gPriv);
181	}
182	if(finst->rand) {
183		feeRandFree(finst->rand);
184	}
185	if(finst->randData) {
186		ffree(finst->randData);
187	}
188	if(finst->cp) {
189		freeCurveParams(finst->cp);
190	}
191	ffree(finst);
192}
193
194unsigned feeFEEDExpPlainBlockSize(feeFEEDExp feed)
195{
196	feedInst *finst = (feedInst *) feed;
197
198	return finst->plainBlockSize;
199}
200
201unsigned feeFEEDExpCipherBlockSize(feeFEEDExp feed)
202{
203	feedInst *finst = (feedInst *) feed;
204
205	return finst->cipherBlockSize;
206}
207
208unsigned feeFEEDExpCipherBufSize(feeFEEDExp feed)
209{
210	feedInst *finst = (feedInst *) feed;
211
212	return 2 * finst->cipherBlockSize;
213}
214
215/*
216 * Return the size of ciphertext to hold specified size of plaintext.
217 */
218unsigned feeFEEDExpCipherTextSize(feeFEEDExp feed, unsigned plainTextSize)
219{
220	/*
221	 * Normal case is one block of ciphertext for each block of
222	 * plaintext. Add one cipherBlock if
223	 * plainTextSize % plainBlockSize == 0.
224	 */
225	feedInst *finst = (feedInst *) feed;
226	unsigned blocks = (plainTextSize + finst->plainBlockSize - 1) /
227		finst->plainBlockSize;
228
229	if((plainTextSize % finst->plainBlockSize) == 0) {
230		blocks++;
231	}
232	return blocks * finst->cipherBlockSize;
233}
234
235/*
236 * Return the size of plaintext to hold specified size of decrypted ciphertext.
237 */
238unsigned feeFEEDExpPlainTextSize(feeFEEDExp feed, unsigned cipherTextSize)
239{
240	feedInst *finst = (feedInst *) feed;
241	unsigned blocks = (cipherTextSize + finst->cipherBlockSize - 1) /
242		finst->cipherBlockSize;
243
244	return blocks * finst->plainBlockSize;
245}
246
247/*
248 * Encrypt a block or less of data. Caller malloc's cipherText.
249 */
250feeReturn feeFEEDExpEncryptBlock(feeFEEDExp feed,
251	const unsigned char *plainText,
252	unsigned plainTextLen,
253	unsigned char *cipherText,
254	unsigned *cipherTextLen,		// RETURNED
255	int finalBlock)
256{
257	feedInst 		*finst = (feedInst *) feed;
258	int 			index;				/* which curve (+/- 1) */
259	char 			g = 0;				/* parity, which_curve bits in ciphertext */
260	key 			B;
261	unsigned char	*ptext;				/* for final block */
262	unsigned		ctextLen;
263	feeReturn		frtn = FR_Success;
264	giant			x1;
265	unsigned		randLen;
266	curveParams		*cp = finst->cp;
267
268	if(plainTextLen > finst->plainBlockSize) {
269		return FR_IllegalArg;
270	}
271	else if ((plainTextLen < finst->plainBlockSize) && !finalBlock) {
272		return FR_IllegalArg;
273	}
274
275	/*
276	 * Init only on first encrypt
277	 */
278	if((finst->randFcn == NULL) && (finst->rand == NULL)) {
279		finst->rand = feeRandAlloc();
280	}
281	if(finst->randData == NULL) {
282		finst->randData = (unsigned char*) fmalloc(finst->cp->minBytes);
283	}
284
285	/*
286	 * plaintext as giant xp
287	 */
288	if(finalBlock) {
289		ptext = (unsigned char*) fmalloc(finst->plainBlockSize);
290		bzero(ptext, finst->plainBlockSize);
291		if(plainTextLen) {
292			/*
293			 * 0 for empty block with resid length 0
294			 */
295			bcopy(plainText, ptext, plainTextLen);
296		}
297		if(plainTextLen < finst->plainBlockSize) {
298		    if(plainTextLen == 0) {
299		    	/*
300				 * Special case - can't actually write zero here;
301				 * it screws up deserializing the giant during
302				 * decrypt
303				 */
304		        ptext[finst->plainBlockSize - 1] = RESID_ZERO;
305		    }
306		    else {
307				ptext[finst->plainBlockSize - 1] = plainTextLen;
308		    }
309			#if FEED_DEBUG
310			printf("encrypt: resid 0x%x\n", ptext[finst->plainBlockSize - 1]);
311			#endif
312		}
313		/*
314		 * else handle evenly aligned case below...
315		 */
316		deserializeGiant(ptext, finst->xp, finst->plainBlockSize);
317		ffree(ptext);
318	}
319	else {
320		deserializeGiant(plainText, finst->xp, plainTextLen);
321	}
322	#if	FEED_DEBUG
323	printf("encrypt:\n");
324	printf("  xp : "); PRINT_GIANT(finst->xp);
325	#endif	// FEED_DEBUG
326
327	/*
328	 * pick curve B? that data lies upon
329	 */
330	index = which_curve(finst->xp, finst->cp);
331	if(index == CURVE_PLUS) {
332		B = finst->plus;
333		x1 = finst->cp->x1Plus;
334	}
335	else {
336		B = finst->minus;
337		x1 = finst->cp->x1Minus;
338	}
339	#if	FEED_DEBUG
340	printf("  which_curve: %s\n",
341		(index == CURVE_PLUS) ? "CURVE_PLUS" : "CURVE_MINUS");
342	#endif
343
344	/*
345	 * random number as giant xaux
346	 */
347	randLen = cp->minBytes;
348	if(finst->randFcn != NULL) {
349		finst->randFcn(finst->randRef, finst->randData, randLen);
350	}
351	else {
352		feeRandBytes(finst->rand, finst->randData, randLen);
353	}
354	deserializeGiant(finst->randData, finst->xaux, randLen);
355
356	#if	FEE_DEBUG
357	if(isZero(finst->xaux)) {
358		printf("feeFEEDExpEncryptBlock: random xaux = 0!\n");
359	}
360	#endif	// FEE_DEBUG
361	/*
362	 * Justify random # to be in [2, minimumX1Order].
363	 */
364	lesserX1OrderJustify(finst->xaux, cp);
365	#if		FEED_DEBUG
366	printf(" xaux: "); PRINT_GIANT(finst->xaux);
367	#endif	// FEED_DEBUG
368
369	gtog(B->x, finst->xq);				// xq = pubB?
370    elliptic_simple(finst->xq, finst->xaux, cp);
371										// xq = r(pubB?)
372	#if		FEED_DEBUG
373	printf(" r(pubB?): "); PRINT_GIANT(finst->xq);
374	#endif
375	elliptic_add(finst->xp, finst->xq, finst->xm, cp, SIGN_PLUS);
376										// xm = data + r(pubB?)
377	gtog(x1, finst->xc);
378    elliptic_simple(finst->xc, finst->xaux, cp);
379										// xc = r(P1?)
380	elliptic_add(finst->xm, finst->xq, finst->xaux, cp, SIGN_PLUS);
381										// xaux = xm + xq (for curve +1)
382										//      = (data + r(pubB?)) + r(pubB?)
383	if(gcompg(finst->xaux, finst->xp) == 0) {
384		g |= CLUE_ELL_ADD_SIGN_PLUS;
385	}
386	else {
387		g |= CLUE_ELL_ADD_SIGN_MINUS;
388		#if	FEED_DEBUG
389		/* this better be true.... */
390		elliptic_add(finst->xm, finst->xq, finst->xaux, cp, SIGN_MINUS);
391		if(gcompg(finst->xaux, finst->xp)) {
392			printf("*******elliptic_add(xm, xq, -1) != xp! *************\n");
393			printf("  xq : "); PRINT_GIANT(finst->xq);
394			printf("  ell_add(xm, xq, -1) : "); PRINT_GIANT(finst->xaux);
395		}
396		#endif
397	}									// g = (xaux == data) ? add : subtract
398
399	/*
400	 * Ciphertext = (xm, xc, g)
401	 */
402	serializeGiant(finst->xm, cipherText, cp->minBytes);
403	cipherText += cp->minBytes;
404	serializeGiant(finst->xc, cipherText, cp->minBytes);
405	cipherText += cp->minBytes;
406	*cipherText++ = g;
407	ctextLen = finst->cipherBlockSize;
408	#if	FEED_DEBUG
409	printf("  xm : "); PRINT_GIANT(finst->xm);
410	printf("  xc : "); PRINT_GIANT(finst->xc);
411	printf("   g : %d\n", g);
412	#endif	// FEED_DEBUG
413	if(finalBlock && (plainTextLen == finst->plainBlockSize)) {
414		/*
415		 * Special case: finalBlock true, plainTextLen == blockSize.
416		 * In this case we generate one more block of ciphertext,
417		 * with a resid length of zero.
418		 */
419		unsigned moreCipher;			// additional cipherLen
420
421		#if FEED_DEBUG
422		printf("encrypt: one more empty block\n");
423		#endif
424		frtn = feeFEEDExpEncryptBlock(feed,
425			NULL,				// plainText not used
426			0,				// resid
427			cipherText,			// append...
428			&moreCipher,
429			1);
430		if(frtn == FR_Success) {
431			ctextLen += moreCipher;
432		}
433	}
434
435	*cipherTextLen = ctextLen;
436	return frtn;
437}
438
439/*
440 * Decrypt (exactly) a block of data. Caller malloc's plainText. Always
441 * generates feeFEEDExpPlainBlockSize of plaintext, unless finalBlock is
442 * non-zero (in which case feeFEEDExpPlainBlockSize or less bytes of
443 * plainText are generated).
444 */
445feeReturn feeFEEDExpDecryptBlock(feeFEEDExp feed,
446	const unsigned char *cipherText,
447	unsigned cipherTextLen,
448	unsigned char *plainText,
449	unsigned *plainTextLen,			// RETURNED
450	int finalBlock)
451{
452	feedInst 	*finst = (feedInst *) feed;
453	char 		g;
454	int 		s;
455	feeReturn	frtn = FR_Success;
456	curveParams	*cp = finst->cp;
457
458	if(finst->gPriv == NULL) {
459		/*
460		 * Can't decrypt without private data
461		 */
462		return FR_BadPubKey;
463	}
464
465	/*
466	 * grab xm, xc, and g from cipherText
467	 */
468	deserializeGiant(cipherText, finst->xm, finst->cp->minBytes);
469	cipherText += finst->cp->minBytes;
470	deserializeGiant(cipherText, finst->xc, finst->cp->minBytes);
471	cipherText += finst->cp->minBytes;
472	g = *cipherText;
473	#if	FEED_DEBUG
474	printf("decrypt g=%d\n", g);
475	printf("  privKey : "); PRINT_GIANT(finst->gPriv);
476	printf("  xm : "); PRINT_GIANT(finst->xm);
477	printf("  xc : "); PRINT_GIANT(finst->xc);
478	#endif	// FEED_DEBUG
479
480	if((g & CLUE_ELL_ADD_SIGN) == CLUE_ELL_ADD_SIGN_PLUS) {
481		s = SIGN_PLUS;
482	}
483	else {
484		s = SIGN_MINUS;
485	}
486
487	/*
488	 * xc = r(P1?)
489	 * xc := r(P1?)(pri) = xq
490	 * xp = data + r(priB+) +/- pri(rB?)
491	 */
492	elliptic_simple(finst->xc, finst->gPriv, cp);
493	#if	FEED_DEBUG
494	printf(" xc1 : "); PRINT_GIANT(finst->xc);
495	#endif
496	elliptic_add(finst->xm, finst->xc, finst->xp, cp, s);
497
498	/*
499	 * plaintext in xp
500	 */
501	#if	FEED_DEBUG
502	printf("  xp : "); PRINT_GIANT(finst->xp);
503	#endif	// FEED_DEBUG
504
505	if(finalBlock) {
506		/*
507		 * Snag data from xp in order to find out how much to move to
508		 * *plainText
509		 */
510		unsigned char *ptext = (unsigned char*) fmalloc(finst->plainBlockSize);
511
512		serializeGiant(finst->xp, ptext, finst->plainBlockSize);
513		*plainTextLen = ptext[finst->plainBlockSize - 1];
514		#if FEED_DEBUG
515		printf("decrypt: resid 0x%x\n", *plainTextLen);
516		#endif
517		if(*plainTextLen == RESID_ZERO) {
518			*plainTextLen = 0;
519		}
520		else if(*plainTextLen > (finst->plainBlockSize - 1)) {
521		    dbgLog(("feeFEEDExpDecryptBlock: ptext overflow!\n"));
522		    frtn = FR_BadCipherText;
523		}
524		else {
525			bcopy(ptext, plainText, *plainTextLen);
526		}
527		ffree(ptext);
528	}
529	else {
530		*plainTextLen = finst->plainBlockSize;
531		serializeGiant(finst->xp, plainText, *plainTextLen);
532	}
533	return frtn;
534}
535
536/*
537 * Convenience routines to encrypt & decrypt multi-block data.
538 */
539feeReturn feeFEEDExpEncrypt(feeFEEDExp feed,
540	const unsigned char *plainText,
541	unsigned plainTextLen,
542	unsigned char **cipherText,		// malloc'd and RETURNED
543	unsigned *cipherTextLen)		// RETURNED
544{
545	const unsigned char	*ptext;			// per block
546	unsigned		ptextLen;		// total to go
547	unsigned		thisPtextLen;		// per block
548	unsigned char		*ctext;			// per block
549	unsigned		ctextLen;		// per block
550	unsigned char		*ctextResult;		// to return
551	unsigned		ctextResultLen;
552	unsigned char		*ctextPtr;
553	unsigned 		ctextLenTotal;		// running total
554	feeReturn		frtn;
555	int			finalBlock;
556	unsigned		numBlocks;
557	unsigned		plainBlockSize;
558
559	if(plainTextLen == 0) {
560		dbgLog(("feeFEEDExpDecrypt: NULL plainText\n"));
561		return FR_IllegalArg;
562	}
563
564	ptext = plainText;
565	ptextLen = plainTextLen;
566	ctext = (unsigned char*) fmalloc(feeFEEDExpCipherBufSize(feed));
567	plainBlockSize = feeFEEDExpPlainBlockSize(feed);
568	numBlocks = (plainTextLen + plainBlockSize - 1)/plainBlockSize;
569	ctextResultLen = (numBlocks + 1) * feeFEEDExpCipherBlockSize(feed);
570	ctextResult = (unsigned char*) fmalloc(ctextResultLen);
571	ctextPtr = ctextResult;
572	ctextLenTotal = 0;
573
574	while(1) {
575		if(ptextLen <= plainBlockSize) {
576			finalBlock = 1;
577			thisPtextLen = ptextLen;
578		}
579		else {
580			finalBlock = 0;
581			thisPtextLen = plainBlockSize;
582		}
583		frtn = feeFEEDExpEncryptBlock(feed,
584			ptext,
585			thisPtextLen,
586			ctext,
587			&ctextLen,
588			finalBlock);
589		if(frtn) {
590			dbgLog(("feeFEEDExpEncrypt: encrypt error: %s\n",
591				feeReturnString(frtn)));
592			break;
593		}
594		if(ctextLen == 0) {
595			dbgLog(("feeFEEDExpEncrypt: null ciphertext\n"));
596			frtn = FR_Internal;
597			break;
598		}
599		bcopy(ctext, ctextPtr, ctextLen);
600		ctextLenTotal += ctextLen;
601		if(ctextLenTotal > ctextResultLen) {
602			dbgLog(("feeFEEDExpEncrypt: ciphertext overflow\n"));
603			frtn = FR_Internal;
604			break;
605		}
606		if(finalBlock) {
607			break;
608		}
609		ctextPtr += ctextLen;
610		ptext += thisPtextLen;
611		ptextLen -= thisPtextLen;
612	}
613
614	ffree(ctext);
615	if(frtn) {
616		ffree(ctextResult);
617		*cipherText = NULL;
618		*cipherTextLen = 0;
619	}
620	else {
621		*cipherText = ctextResult;
622		*cipherTextLen = ctextLenTotal;
623		#if	FEE_DEBUG
624		if(feeFEEDExpCipherTextSize(feed, plainTextLen) !=
625			    ctextLenTotal) {
626		    printf("feeFEEDExpEncrypt: feeFEEDCipherTextSize "
627		    	"error!\n");
628		    printf("ptext %d  exp ctext %d  actual ctext %d\n",
629		    	plainTextLen,
630			feeFEEDExpCipherTextSize(feed, plainTextLen),
631			ctextLenTotal);
632		}
633		#endif	// FEE_DEBUG
634	}
635	return frtn;
636
637}
638
639feeReturn feeFEEDExpDecrypt(feeFEEDExp feed,
640	const unsigned char *cipherText,
641	unsigned cipherTextLen,
642	unsigned char **plainText,		// malloc'd and RETURNED
643	unsigned *plainTextLen)			// RETURNED
644{
645	const unsigned char	*ctext;
646	unsigned		ctextLen;		// total to go
647	unsigned char		*ptext;			// per block
648	unsigned		ptextLen;		// per block
649	unsigned char		*ptextResult;		// to return
650	unsigned char		*ptextPtr;
651	unsigned 		ptextLenTotal;		// running total
652	feeReturn		frtn = FR_Success;
653	int			finalBlock;
654	unsigned		numBlocks;
655	unsigned		plainBlockSize =
656					feeFEEDExpPlainBlockSize(feed);
657	unsigned		cipherBlockSize =
658					feeFEEDExpCipherBlockSize(feed);
659
660	if(cipherTextLen % cipherBlockSize) {
661		dbgLog(("feeFEEDExpDecrypt: unaligned cipherText\n"));
662		return FR_BadCipherText;
663	}
664	if(cipherTextLen == 0) {
665		dbgLog(("feeFEEDExpDecrypt: NULL cipherText\n"));
666		return FR_BadCipherText;
667	}
668
669	ptext = (unsigned char*) fmalloc(plainBlockSize);
670	ctext = cipherText;
671	ctextLen = cipherTextLen;
672	numBlocks = cipherTextLen / cipherBlockSize;
673	ptextResult = (unsigned char*) fmalloc(plainBlockSize * numBlocks);
674	ptextPtr = ptextResult;
675	ptextLenTotal = 0;
676
677	while(ctextLen) {
678		if(ctextLen == cipherBlockSize) {
679		    finalBlock = 1;
680		}
681		else {
682		    finalBlock = 0;
683		}
684		frtn = feeFEEDExpDecryptBlock(feed,
685			ctext,
686			cipherBlockSize,
687			ptext,
688			&ptextLen,
689			finalBlock);
690		if(frtn) {
691			dbgLog(("feeFEEDExpDecryptBlock: %s\n",
692				feeReturnString(frtn)));
693			break;
694		}
695		if(ptextLen == 0) {
696			/*
697			 * Normal termination case for
698			 * plainTextLen % plainBlockSize == 0
699			 */
700			if(!finalBlock) {
701			    dbgLog(("feeFEEDExpDecrypt: decrypt sync"
702			    	" error!\n"));
703			    frtn = FR_BadCipherText;
704			}
705			break;
706		}
707		else if(ptextLen > plainBlockSize) {
708			dbgLog(("feeFEEDExpDecrypt: ptext overflow!\n"));
709			frtn = FR_Internal;
710			break;
711		}
712		else {
713			bcopy(ptext, ptextPtr, ptextLen);
714			ptextPtr += ptextLen;
715			ptextLenTotal += ptextLen;
716		}
717		ctext += cipherBlockSize;
718		ctextLen -= cipherBlockSize;
719	}
720
721	ffree(ptext);
722	if(frtn) {
723		ffree(ptextResult);
724		*plainText = NULL;
725		*plainTextLen = 0;
726	}
727	else {
728		*plainText = ptextResult;
729		*plainTextLen = ptextLenTotal;
730	}
731	return frtn;
732
733}
734
735#endif	/* CRYPTKIT_ASYMMETRIC_ENABLE */
736