1/* Copyright 2006 Apple Computer, Inc.
2 *
3 * ccSymTest.c - test CommonCrypto symmetric encrypt/decrypt.
4 */
5#include "testmore.h"
6#include "capabilities.h"
7#if (CCSYMREGRESSION == 0)
8entryPoint(CommonCryptoSymRegression,"CommonCrypto Base Behavior Regression Tests")
9#else
10
11
12
13#include <string.h>
14#include <stdlib.h>
15#include <stdio.h>
16#include <sys/types.h>
17#include <CommonCrypto/CommonCryptor.h>
18#include <fcntl.h>
19#include <sys/types.h>
20#include <sys/uio.h>
21#include <unistd.h>
22
23// #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
24
25/*
26 * Defaults.
27 */
28#define LOOPS_DEF		500
29#define MIN_DATA_SIZE	8
30#define MAX_DATA_SIZE	10000						/* bytes */
31#define MAX_KEY_SIZE	kCCKeySizeMaxRC4			/* bytes */
32#define MAX_BLOCK_SIZE	kCCBlockSizeAES128			/* bytes */
33#define LOOP_NOTIFY		250
34
35/*
36 * Enumerate algs our own way to allow iteration.
37 */
38typedef enum {
39	ALG_AES_128 = 1,	/* 128 bit block, 128 bit key */
40	ALG_AES_192,		/* 128 bit block, 192 bit key */
41	ALG_AES_256,		/* 128 bit block, 256 bit key */
42	ALG_DES,
43	ALG_3DES,
44	ALG_CAST,
45	ALG_RC4,
46	/* these aren't in CommonCrypto (yet?) */
47	ALG_RC2,
48	ALG_RC5,
49	ALG_BFISH,
50	ALG_ASC,
51	ALG_NULL					/* normally not used */
52} SymAlg;
53#define ALG_FIRST			ALG_AES_128
54#define ALG_LAST			ALG_RC4
55
56
57#define LOG_SIZE			0
58#if		LOG_SIZE
59#define logSize(s)	diag s
60#else
61#define logSize(s)
62#endif
63
64
65
66
67static void
68appGetRandomBytes(void *keyBytes, size_t keySizeInBytes)
69{
70	int fd;
71
72    if((fd = open("/dev/random", O_RDONLY)) < 0) {
73    	diag("Can't open Random\n");
74        exit(0);
75    }
76    if(read(fd, keyBytes, keySizeInBytes) != (int) keySizeInBytes) {
77		diag("Can't read Random\n");
78        exit(0);
79    }
80    close(fd);
81}
82
83/* min <= return <= max */
84static unsigned
85genRand(unsigned min, unsigned max)
86{
87	unsigned i;
88	if(min == max) {
89		return min;
90	}
91	appGetRandomBytes(&i, 4);
92	return (min + (i % (max - min + 1)));
93}
94
95
96static void printCCError(const char *str, CCCryptorStatus crtn)
97{
98	const char *errStr;
99	char unknownStr[200];
100
101	switch(crtn) {
102		case kCCSuccess: errStr = "kCCSuccess"; break;
103		case kCCParamError: errStr = "kCCParamError"; break;
104		case kCCBufferTooSmall: errStr = "kCCBufferTooSmall"; break;
105		case kCCMemoryFailure: errStr = "kCCMemoryFailure"; break;
106		case kCCAlignmentError: errStr = "kCCAlignmentError"; break;
107		case kCCDecodeError: errStr = "kCCDecodeError"; break;
108		case kCCUnimplemented: errStr = "kCCUnimplemented"; break;
109		default:
110			sprintf(unknownStr, "Unknown(%ld)\n", (long)crtn);
111			errStr = unknownStr;
112			break;
113	}
114	diag("***%s returned %s\n", str, errStr);
115}
116
117/* max context size */
118#define CC_MAX_CTX_SIZE	kCCContextSizeRC4
119
120/*
121 * We write a marker at end of expected output and at end of caller-allocated
122 * CCCryptorRef, and check at the end to make sure they weren't written
123 */
124#define MARKER_LENGTH	8
125#define MARKER_BYTE		0x7e
126
127/*
128 * Test harness for CCCryptor with lots of options.
129 */
130static CCCryptorStatus doCCCrypt(
131	bool forEncrypt,
132	CCAlgorithm encrAlg,
133	bool doCbc,
134	bool doPadding,
135	const void *keyBytes, size_t keyLen,
136	const void *iv,
137	bool randUpdates,
138	bool inPlace,								/* !doPadding only */
139	size_t ctxSize,								/* if nonzero, we allocate ctx */
140	bool askOutSize,
141	const uint8_t *inText, size_t inTextLen,
142	uint8_t **outText, size_t *outTextLen)		/* both returned, WE malloc */
143{
144	CCCryptorRef	cryptor = NULL;
145	CCCryptorStatus crtn;
146	CCOperation		op = forEncrypt ? kCCEncrypt : kCCDecrypt;
147	CCOptions		options = 0;
148	uint8_t			*outBuf = NULL;			/* mallocd output buffer */
149	uint8_t			*outp;					/* running ptr into outBuf */
150	const uint8_t		*inp;					/* running ptr into inText */
151	size_t			outLen = 0;					/* bytes remaining in outBuf */
152	size_t			toMove;					/* bytes remaining in inText */
153	size_t			thisMoveOut;			/* output from CCCryptUpdate()/CCCryptFinal() */
154	size_t			outBytes;				/* total bytes actually produced in outBuf */
155	uint8_t			ctx[CC_MAX_CTX_SIZE];	/* for CCCryptorCreateFromData() */
156	uint8_t			*textMarker = NULL;		/* 8 bytes of marker here after expected end of
157											 * output */
158	uint8_t			*ctxMarker = NULL;		/* ditto for caller-provided context */
159	unsigned		dex;
160	size_t			askedOutSize;			/* from the lib */
161	size_t			thisOutLen;				/* dataOutAvailable we use */
162
163
164	if(0) diag("%s %s %s keylen %d %s %s %s %s %s input length %ld\n",
165    	forEncrypt ? "Encrypting": "Decrypting",
166    	doCbc ? "CBC": "ECB",
167    	doPadding ? "Padding ON": "Padding OFF",
168        (int) keyLen,
169        iv ? "IV Provided": "No IV Provided",
170		randUpdates ? "Random Updates": "Non-Random Updates",
171		inPlace ? "In Place": "Separate Buffers",
172    	ctxSize ? "We Allocate": "CC Allocated",
173    	askOutSize ? "Ask OutSize": "Don't Ask OutSize",
174		inTextLen);
175
176	if(ctxSize > CC_MAX_CTX_SIZE) {
177		diag("***HEY! Adjust CC_MAX_CTX_SIZE!\n");
178		exit(1);
179	}
180	if(!doCbc) {
181		options |= kCCOptionECBMode;
182	}
183	if(doPadding) {
184		options |= kCCOptionPKCS7Padding;
185	}
186
187	/* just hack this one */
188	outLen = inTextLen;
189	if(forEncrypt) {
190		outLen += MAX_BLOCK_SIZE;
191	}
192
193	outBuf = (uint8_t *)malloc(outLen + MARKER_LENGTH);
194	memset(outBuf, 0xEE, outLen + MARKER_LENGTH);
195
196	/* library should not touch this memory */
197	textMarker = outBuf + outLen;
198	memset(textMarker, MARKER_BYTE, MARKER_LENGTH);
199
200	/* subsequent errors to errOut: */
201
202	if(inPlace) {
203		memmove(outBuf, inText, inTextLen);
204		inp = outBuf;
205	}
206	else {
207		inp = inText;
208	}
209
210	if(!randUpdates) {
211		/* one shot */
212		if(askOutSize) {
213			crtn = CCCrypt(op, encrAlg, options,
214				keyBytes, keyLen, iv,
215				inp, inTextLen,
216				outBuf, 0, &askedOutSize);
217			if(crtn != kCCBufferTooSmall) {
218				diag("***Did not get kCCBufferTooSmall as expected\n");
219				diag("   alg %d inTextLen %lu cbc %d padding %d keyLen %lu\n",
220					(int)encrAlg, (unsigned long)inTextLen, (int)doCbc, (int)doPadding,
221					(unsigned long)keyLen);
222				printCCError("CCCrypt", crtn);
223				crtn = -1;
224				goto errOut;
225			}
226			outLen = askedOutSize;
227		}
228		crtn = CCCrypt(op, encrAlg, options,
229			keyBytes, keyLen, iv,
230			inp, inTextLen,
231			outBuf, outLen, &outLen);
232		if(crtn) {
233			printCCError("CCCrypt", crtn);
234			goto errOut;
235		}
236		*outText = outBuf;
237		*outTextLen = outLen;
238		goto errOut;
239	}
240
241	/* random multi updates */
242	if(ctxSize) {
243		size_t ctxSizeCreated;
244
245		if(askOutSize) {
246			crtn = CCCryptorCreateFromData(op, encrAlg, options,
247				keyBytes, keyLen, iv,
248				ctx, 0 /* ctxSize */,
249				&cryptor, &askedOutSize);
250			if(crtn != kCCBufferTooSmall) {
251				diag("***Did not get kCCBufferTooSmall as expected\n");
252				printCCError("CCCryptorCreateFromData", crtn);
253				crtn = -1;
254				goto errOut;
255			}
256			ctxSize = askedOutSize;
257		}
258		crtn = CCCryptorCreateFromData(op, encrAlg, options,
259			keyBytes, keyLen, iv,
260			ctx, ctxSize, &cryptor, &ctxSizeCreated);
261		if(crtn) {
262			printCCError("CCCryptorCreateFromData", crtn);
263			return crtn;
264		}
265		ctxMarker = ctx + ctxSizeCreated;
266		memset(ctxMarker, MARKER_BYTE, MARKER_LENGTH);
267	}
268	else {
269		crtn = CCCryptorCreate(op, encrAlg, options,
270			keyBytes, keyLen, iv,
271			&cryptor);
272		if(crtn) {
273			printCCError("CCCryptorCreate", crtn);
274			return crtn;
275		}
276	}
277
278	toMove = inTextLen;		/* total to go */
279	outp = outBuf;
280	outBytes = 0;			/* bytes actually produced in outBuf */
281
282	while(toMove) {
283		size_t thisMoveIn;			/* input to CCryptUpdate() */
284
285		thisMoveIn = (size_t) genRand(1, (unsigned int) toMove);
286		logSize(("###ptext segment len %lu\n", (unsigned long)thisMoveIn));
287		if(askOutSize) {
288			thisOutLen = CCCryptorGetOutputLength(cryptor, thisMoveIn, false);
289		}
290		else {
291			thisOutLen = outLen;
292		}
293		crtn = CCCryptorUpdate(cryptor, inp, thisMoveIn,
294			outp, thisOutLen, &thisMoveOut);
295		if(crtn) {
296			printCCError("CCCryptorUpdate", crtn);
297			goto errOut;
298		}
299		inp			+= thisMoveIn;
300		toMove		-= thisMoveIn;
301		outp		+= thisMoveOut;
302		outLen   	-= thisMoveOut;
303		outBytes	+= thisMoveOut;
304	}
305
306	if(doPadding) {
307		/* Final is not needed if padding is disabled */
308		if(askOutSize) {
309			thisOutLen = CCCryptorGetOutputLength(cryptor, 0, true);
310		}
311		else {
312			thisOutLen = outLen;
313		}
314		crtn = CCCryptorFinal(cryptor, outp, thisOutLen, &thisMoveOut);
315	}
316	else {
317		thisMoveOut = 0;
318		crtn = kCCSuccess;
319	}
320
321	if(crtn) {
322		printCCError("CCCryptorFinal", crtn);
323		goto errOut;
324	}
325
326	outBytes += thisMoveOut;
327	*outText = outBuf;
328	*outTextLen = outBytes;
329	crtn = kCCSuccess;
330
331	for(dex=0; dex<MARKER_LENGTH; dex++) {
332		if(textMarker[dex] != MARKER_BYTE) {
333			diag("***lib scribbled on our textMarker memory (op=%s)!\n",
334				forEncrypt ? "encrypt" : "decrypt");
335			crtn = (CCCryptorStatus)-1;
336		}
337	}
338	if(ctxSize) {
339		for(dex=0; dex<MARKER_LENGTH; dex++) {
340			if(ctxMarker[dex] != MARKER_BYTE) {
341				diag("***lib scribbled on our ctxMarker memory (op=%s)!\n",
342					forEncrypt ? "encrypt" : "decrypt");
343				crtn = (CCCryptorStatus)-1;
344			}
345		}
346	}
347
348errOut:
349	if(crtn) {
350		if(outBuf) {
351			free(outBuf);
352		}
353	}
354	if(cryptor) {
355		CCCryptorRelease(cryptor);
356	}
357	return crtn;
358}
359
360static int doTest(const uint8_t *ptext,
361	size_t ptextLen,
362	CCAlgorithm encrAlg,
363	bool doCbc,
364	bool doPadding,
365	bool nullIV,			/* if CBC, use NULL IV */
366	uint32_t keySizeInBytes,
367	bool stagedEncr,
368	bool stagedDecr,
369	bool inPlace,
370	size_t ctxSize,
371	bool askOutSize,
372	bool quiet)
373{
374	uint8_t			keyBytes[MAX_KEY_SIZE];
375	uint8_t			iv[MAX_BLOCK_SIZE];
376	uint8_t			*ivPtrEncrypt;
377	uint8_t			*ivPtrDecrypt;
378	uint8_t			*ctext = NULL;		/* mallocd by doCCCrypt */
379	size_t			ctextLen = 0;
380	uint8_t			*rptext = NULL;		/* mallocd by doCCCrypt */
381	size_t			rptextLen = 0;
382	CCCryptorStatus	crtn;
383	int				rtn = 0;
384
385	/* random key */
386	appGetRandomBytes(keyBytes, keySizeInBytes);
387
388	/* random IV if needed */
389	if(doCbc) {
390		if(nullIV) {
391			memset(iv, 0, MAX_BLOCK_SIZE);
392
393			/* flip a coin, give one side NULL, the other size zeroes */
394			if(genRand(1,2) == 1) {
395				ivPtrEncrypt = NULL;
396				ivPtrDecrypt = iv;
397			}
398			else {
399				ivPtrEncrypt = iv;
400				ivPtrDecrypt = NULL;
401			}
402		}
403		else {
404			appGetRandomBytes(iv, MAX_BLOCK_SIZE);
405			ivPtrEncrypt = iv;
406			ivPtrDecrypt = iv;
407		}
408	}
409	else {
410		ivPtrEncrypt = NULL;
411		ivPtrDecrypt = NULL;
412	}
413
414	crtn = doCCCrypt(true, encrAlg, doCbc, doPadding,
415		keyBytes, keySizeInBytes, ivPtrEncrypt,
416		stagedEncr, inPlace, ctxSize, askOutSize,
417		ptext, ptextLen,
418		&ctext, &ctextLen);
419
420    ok(crtn == 0, "doCCCrypt");
421	if(crtn) {
422        diag("Test Failure Encrypt encrAlg = %d dodCbc = %d doPadding %d\n", encrAlg, doCbc, doPadding);
423	}
424
425	logSize(("###ctext len %lu\n", ctextLen));
426	crtn = doCCCrypt(false, encrAlg, doCbc, doPadding,
427		keyBytes, keySizeInBytes, ivPtrDecrypt,
428		stagedDecr, inPlace, ctxSize, askOutSize,
429		ctext, ctextLen,
430		&rptext, &rptextLen);
431    ok(crtn == 0, "doCCCrypt");
432	if(crtn) {
433        diag("Test Failure Encrypt encrAlg = %d dodCbc = %d doPadding %d\n", encrAlg, doCbc, doPadding);
434	}
435
436	logSize(("###rptext len %lu\n", rptextLen));
437
438	/* compare ptext, rptext */
439	if(ptextLen != rptextLen) {
440		diag("Ptext length mismatch: expect %lu, got %lu\n", ptextLen, rptextLen);
441	} else  if(memcmp(ptext, rptext, ptextLen)) {
442		diag("***data miscompare\n");
443	}
444
445	if(ctext) {
446		free(ctext);
447	}
448	if(rptext) {
449		free(rptext);
450	}
451	return rtn;
452}
453
454static bool isBitSet(unsigned bit, unsigned word)
455{
456	if(bit > 31) {
457		diag("We don't have that many bits\n");
458		exit(1);
459	}
460	unsigned mask = 1 << bit;
461	return (word & mask) ? true : false;
462}
463
464
465static int kTestTestCount = 7001;
466
467
468int CommonCryptoSymRegression(int argc, char *const *argv)
469{
470	unsigned			loop;
471	uint8_t				*ptext;
472	size_t				ptextLen;
473	bool				stagedEncr = false;
474	bool				stagedDecr = false;
475	bool				doPadding;
476	bool				doCbc = false;
477	bool				nullIV;
478	const char			*algStr;
479	CCAlgorithm			encrAlg;
480	int					i;
481	SymAlg					currAlg;		// ALG_xxx
482	uint32_t				minKeySizeInBytes;
483	uint32_t				maxKeySizeInBytes;
484	uint32_t				keySizeInBytes = 0;
485	int					rtn = 0;
486	uint32_t				blockSize;		// for noPadding case
487	size_t				ctxSize;		// always set per alg
488	size_t				ctxSizeUsed;	// passed to doTest
489	bool				askOutSize;		// inquire output size each op
490
491	/*
492	 * User-spec'd params
493	 */
494	bool		keySizeSpec = false;		// false: use rand key size
495	SymAlg		minAlg = ALG_FIRST;
496	SymAlg		maxAlg = ALG_LAST;
497	unsigned	loops = LOOPS_DEF;
498	bool		verbose = false;
499	size_t		minPtextSize = MIN_DATA_SIZE;
500	size_t		maxPtextSize = MAX_DATA_SIZE;
501	bool		quiet = true;
502	unsigned	pauseInterval = 0;
503	bool		paddingSpec = false;		// true: user calls doPadding, const
504	bool		cbcSpec = false;			// ditto for doCbc
505	bool		stagedSpec = false;			// ditto for stagedEncr and stagedDecr
506	bool		inPlace = false;			// en/decrypt in place for ECB
507	bool		allocCtxSpec = false;		// use allocCtx
508	bool		allocCtx = false;			// allocate context ourself
509
510	plan_tests(kTestTestCount);
511
512
513	ptext = (uint8_t *)malloc(maxPtextSize);
514	if(ptext == NULL) {
515		diag("Insufficient heap space\n");
516		exit(1);
517	}
518	/* ptext length set in test loop */
519
520	if(!quiet) diag("Starting ccSymTest; args: ");
521	for(i=1; i<argc; i++) {
522		if(!quiet) diag("%s ", argv[i]);
523	}
524	if(!quiet) diag("\n");
525
526	if(pauseInterval) {
527		fpurge(stdin);
528		diag("Top of test; hit CR to proceed: ");
529		getchar();
530	}
531
532	for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) {
533		switch(currAlg) {
534			case ALG_DES:
535				encrAlg = kCCAlgorithmDES;
536				blockSize = kCCBlockSizeDES;
537				minKeySizeInBytes = kCCKeySizeDES;
538				maxKeySizeInBytes = minKeySizeInBytes;
539				ctxSize = kCCContextSizeDES;
540				algStr = "DES";
541                if(verbose) diag("Running DES Tests");
542				break;
543			case ALG_3DES:
544				encrAlg = kCCAlgorithm3DES;
545				blockSize = kCCBlockSize3DES;
546				minKeySizeInBytes = kCCKeySize3DES;
547				maxKeySizeInBytes = minKeySizeInBytes;
548				ctxSize = kCCContextSize3DES;
549
550				algStr = "3DES";
551                if(verbose) diag("Running 3DES Tests");
552				break;
553			case ALG_AES_128:
554				encrAlg = kCCAlgorithmAES128;
555				blockSize = kCCBlockSizeAES128;
556				minKeySizeInBytes = kCCKeySizeAES128;
557				maxKeySizeInBytes = minKeySizeInBytes;
558				ctxSize = kCCContextSizeAES128;
559				algStr = "AES128";
560                if(verbose) diag("Running AES (128 bit key) Tests");
561				break;
562			case ALG_AES_192:
563				encrAlg = kCCAlgorithmAES128;
564				blockSize = kCCBlockSizeAES128;
565				minKeySizeInBytes = kCCKeySizeAES192;
566				maxKeySizeInBytes = minKeySizeInBytes;
567				ctxSize = kCCContextSizeAES128;
568				algStr = "AES192";
569                if(verbose) diag("Running AES (192 bit key) Tests");
570				break;
571			case ALG_AES_256:
572				encrAlg = kCCAlgorithmAES128;
573				blockSize = kCCBlockSizeAES128;
574				minKeySizeInBytes = kCCKeySizeAES256;
575				maxKeySizeInBytes = minKeySizeInBytes;
576				ctxSize = kCCContextSizeAES128;
577				algStr = "AES256";
578                if(verbose) diag("Running AES (256 bit key) Tests");
579				break;
580			case ALG_CAST:
581				encrAlg = kCCAlgorithmCAST;
582				blockSize = kCCBlockSizeCAST;
583				minKeySizeInBytes = kCCKeySizeMinCAST;
584				maxKeySizeInBytes = kCCKeySizeMaxCAST;
585				ctxSize = kCCContextSizeCAST;
586				algStr = "CAST";
587                if(verbose) diag("Running CAST Tests");
588				break;
589			case ALG_RC4:
590				encrAlg = kCCAlgorithmRC4;
591				blockSize = 0;
592				minKeySizeInBytes = kCCKeySizeMinRC4;
593				maxKeySizeInBytes = kCCKeySizeMaxRC4;
594				ctxSize = kCCContextSizeRC4;
595				algStr = "RC4";
596                if(verbose) diag("Running RC4 Tests");
597				break;
598			default:
599				diag("***BRRZAP!\n");
600				exit(1);
601		}
602		if(!quiet || verbose) {
603			diag("Testing alg %s\n", algStr);
604		}
605		for(loop=1; ; loop++) {
606			ptextLen = (size_t) genRand((unsigned int) minPtextSize, (unsigned int) maxPtextSize);
607			appGetRandomBytes(ptext, ptextLen);
608
609			/* per-loop settings */
610			if(!keySizeSpec) {
611				if(minKeySizeInBytes == maxKeySizeInBytes) {
612					keySizeInBytes = minKeySizeInBytes;
613				}
614				else {
615					keySizeInBytes = genRand(minKeySizeInBytes, maxKeySizeInBytes);
616				}
617			}
618			if(blockSize == 0) {
619				/* stream cipher */
620				doCbc = false;
621				doPadding = false;
622			}
623			else {
624				if(!cbcSpec) {
625					doCbc = isBitSet(0, loop);
626				}
627				if(!paddingSpec) {
628					doPadding = isBitSet(1, loop);
629				}
630			}
631			if(!doPadding && (blockSize != 0)) {
632				/* align plaintext */
633				ptextLen = (ptextLen / blockSize) * blockSize;
634				if(ptextLen == 0) {
635					ptextLen = blockSize;
636				}
637			}
638			if(!stagedSpec) {
639				stagedEncr = isBitSet(2, loop);
640				stagedDecr = isBitSet(3, loop);
641			}
642			if(doCbc) {
643				nullIV = isBitSet(4, loop);
644			}
645			else {
646				nullIV = false;
647			}
648			inPlace = isBitSet(5, loop);
649			if(allocCtxSpec) {
650				ctxSizeUsed = allocCtx ? ctxSize : 0;
651			}
652			else if(isBitSet(6, loop)) {
653				ctxSizeUsed = ctxSize;
654			}
655			else {
656				ctxSizeUsed = 0;
657			}
658			askOutSize = isBitSet(7, loop);
659			if(!quiet) {
660			   	if(verbose || ((loop % LOOP_NOTIFY) == 0)) {
661					diag("..loop %3d ptextLen %lu keyLen %d cbc=%d padding=%d stagedEncr=%d "
662							"stagedDecr=%d\n",
663						loop, (unsigned long)ptextLen, (int)keySizeInBytes,
664						(int)doCbc, (int)doPadding,
665					 	(int)stagedEncr, (int)stagedDecr);
666					diag("           nullIV %d inPlace %d ctxSize %d askOutSize %d\n",
667						(int)nullIV, (int)inPlace, (int)ctxSizeUsed, (int)askOutSize);
668				}
669			}
670
671			if(doTest(ptext, ptextLen,
672					encrAlg, doCbc, doPadding, nullIV,
673					keySizeInBytes,
674					stagedEncr,	stagedDecr, inPlace, ctxSizeUsed, askOutSize,
675					quiet)) {
676				rtn = 1;
677				break;
678			}
679			if(pauseInterval && ((loop % pauseInterval) == 0)) {
680				char c;
681				fpurge(stdin);
682				diag("Hit CR to proceed, q to abort: ");
683				c = getchar();
684				if(c == 'q') {
685					goto testDone;
686				}
687			}
688			if(loops && (loop == loops)) {
689				break;
690			}
691		}	/* main loop */
692		if(rtn) {
693			break;
694		}
695
696	}	/* for algs */
697
698testDone:
699
700    ok(rtn == 0, "ccSymTest");
701
702	if(pauseInterval) {
703		fpurge(stdin);
704		diag("ModuleDetach/Unload complete; hit CR to exit: ");
705		getchar();
706	}
707	if((rtn == 0) && !quiet) {
708		diag("%s test complete\n", argv[0]);
709	}
710	free(ptext);
711	return rtn;
712}
713#endif
714