/* min <= return <= max */
static unsigned 
genRand(unsigned min, unsigned max)
{
	unsigned i;
	if(min == max) {
		return min;
	}
	appGetRandomBytes(&i, 4);
	return (min + (i % (max - min + 1)));
}	
Пример #2
0
int main(int argc, char **argv)
{
	int					arg;
	char				*argp;
	unsigned			loop;
	uint8				*ptext;
	size_t				ptextLen;
	bool				stagedEncr = false;
	bool				stagedDecr = false;
	bool				doPadding;
	bool				doCbc = false;
	bool				nullIV;
	const char			*algStr;
	CCAlgorithm			encrAlg;	
	int					i;
	int					currAlg;		// ALG_xxx
	uint32				minKeySizeInBytes;
	uint32				maxKeySizeInBytes;
	uint32				keySizeInBytes = 0;
	int					rtn = 0;
	uint32				blockSize;		// for noPadding case
	size_t				ctxSize;		// always set per alg
	size_t				ctxSizeUsed;	// passed to doTest
	bool				askOutSize;		// inquire output size each op
	
	/*
	 * User-spec'd params
	 */
	bool		keySizeSpec = false;		// false: use rand key size
	SymAlg		minAlg = ALG_FIRST;
	SymAlg		maxAlg = ALG_LAST;
	unsigned	loops = LOOPS_DEF;
	bool		verbose = false;
	size_t		minPtextSize = MIN_DATA_SIZE;
	size_t		maxPtextSize = MAX_DATA_SIZE;
	bool		quiet = false;
	unsigned	pauseInterval = 0;
	bool		paddingSpec = false;		// true: user calls doPadding, const
	bool		cbcSpec = false;			// ditto for doCbc
	bool		stagedSpec = false;			// ditto for stagedEncr and stagedDecr
	bool		inPlace = false;			// en/decrypt in place for ECB
	bool		allocCtxSpec = false;		// use allocCtx
	bool		allocCtx = false;			// allocate context ourself
	
	for(arg=1; arg<argc; arg++) {
		argp = argv[arg];
		switch(argp[0]) {
			case 'a':
				if(argp[1] != '=') {
					usage(argv);
				}
				switch(argp[2]) {
					case 's':
						minAlg = maxAlg = ALG_ASC;
						break;
					case 'd':
						minAlg = maxAlg = ALG_DES;
						break;
					case '3':
						minAlg = maxAlg = ALG_3DES;
						break;
					case '2':
						minAlg = maxAlg = ALG_RC2;
						break;
					case '4':
						minAlg = maxAlg = ALG_RC4;
						break;
					case '5':
						minAlg = maxAlg = ALG_RC5;
						break;
					case 'a':
						minAlg = maxAlg = ALG_AES_128;
						break;
					case 'n':
						minAlg = maxAlg = ALG_AES_192;
						break;
					case 'A':
						minAlg = maxAlg = ALG_AES_256;
						break;
					case 'b':
						minAlg = maxAlg = ALG_BFISH;
						break;
					case 'c':
						minAlg = maxAlg = ALG_CAST;
						break;
					default:
						usage(argv);
				}
				if(maxAlg > ALG_LAST) {
					/* we left them in the switch but we can't use them */
					usage(argv);
				}
				break;
		    case 'l':
				loops = atoi(&argp[2]);
				break;
		    case 'n':
				minPtextSize = atoi(&argp[2]);
				break;
		    case 'm':
				maxPtextSize = atoi(&argp[2]);
				break;
		    case 'k':
		    	minKeySizeInBytes = maxKeySizeInBytes = atoi(&argp[2]);
		    	keySizeSpec = true;
				break;
			case 'x':
				allocCtxSpec = true;
				allocCtx = true;
				break;
			case 'X':
				allocCtxSpec = true;
				allocCtx = false;
				break;
		    case 'v':
		    	verbose = true;
				break;
		    case 'q':
		    	quiet = true;
				break;
		    case 'p':
		    	pauseInterval = atoi(&argp[2]);;
				break;
			case 'o':
				doPadding = false;
				paddingSpec = true;
				break;
			case 'e':
				doCbc = false;
				cbcSpec = true;
				break;
			case 'E':
				doCbc = true;
				cbcSpec = true;
				break;
		    case 'u':
		    	stagedEncr = false;
		    	stagedDecr = false;
				stagedSpec = true;
				break;
		    case 'U':
		    	stagedEncr = true;
		    	stagedDecr = true;
				stagedSpec = true;
				break;
		    case 'h':
		    default:
				usage(argv);
		}
	}
	ptext = (uint8 *)malloc(maxPtextSize);
	if(ptext == NULL) {
		printf("Insufficient heap space\n");
		exit(1);
	}
	/* ptext length set in test loop */
	
	printf("Starting ccSymTest; args: ");
	for(i=1; i<argc; i++) {
		printf("%s ", argv[i]);
	}
	printf("\n");
	
	if(pauseInterval) {
		fpurge(stdin);
		printf("Top of test; hit CR to proceed: ");
		getchar();
	}

	for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) {
		switch(currAlg) {
			case ALG_DES:
				encrAlg = kCCAlgorithmDES;
				blockSize = kCCBlockSizeDES;
				minKeySizeInBytes = kCCKeySizeDES;
				maxKeySizeInBytes = minKeySizeInBytes;
				ctxSize = kCCContextSizeDES;
				algStr = "DES";
				break;
			case ALG_3DES:
				encrAlg = kCCAlgorithm3DES;
				blockSize = kCCBlockSize3DES;
				minKeySizeInBytes = kCCKeySize3DES;
				maxKeySizeInBytes = minKeySizeInBytes;
				ctxSize = kCCContextSize3DES;
				
				algStr = "3DES";
				break;
			case ALG_AES_128:
				encrAlg = kCCAlgorithmAES128;
				blockSize = kCCBlockSizeAES128;
				minKeySizeInBytes = kCCKeySizeAES128;
				maxKeySizeInBytes = minKeySizeInBytes;
				ctxSize = kCCContextSizeAES128;
				algStr = "AES128";
				break;
			case ALG_AES_192:
				encrAlg = kCCAlgorithmAES128;
				blockSize = kCCBlockSizeAES128;
				minKeySizeInBytes = kCCKeySizeAES192;
				maxKeySizeInBytes = minKeySizeInBytes;
				ctxSize = kCCContextSizeAES128;
				algStr = "AES192";
				break;
			case ALG_AES_256:
				encrAlg = kCCAlgorithmAES128;
				blockSize = kCCBlockSizeAES128;
				minKeySizeInBytes = kCCKeySizeAES256;
				maxKeySizeInBytes = minKeySizeInBytes;
				ctxSize = kCCContextSizeAES128;
				algStr = "AES256";
				break;
			case ALG_CAST:
				encrAlg = kCCAlgorithmCAST;
				blockSize = kCCBlockSizeCAST;
				minKeySizeInBytes = kCCKeySizeMinCAST;
				maxKeySizeInBytes = kCCKeySizeMaxCAST;
				ctxSize = kCCContextSizeCAST;
				algStr = "CAST";
				break;
			case ALG_RC4:
				encrAlg = kCCAlgorithmRC4;
				blockSize = 0;
				minKeySizeInBytes = kCCKeySizeMinRC4;
				maxKeySizeInBytes = kCCKeySizeMaxRC4;
				ctxSize = kCCContextSizeRC4;
				algStr = "RC4";
				break;
			default:
				printf("***BRRZAP!\n");
				exit(1);
		}
		if(!quiet || verbose) {
			printf("Testing alg %s\n", algStr);
		}
		for(loop=1; ; loop++) {
			ptextLen = genRand(minPtextSize, maxPtextSize);
			appGetRandomBytes(ptext, ptextLen);
			
			/* per-loop settings */
			if(!keySizeSpec) {
				if(minKeySizeInBytes == maxKeySizeInBytes) {
					keySizeInBytes = minKeySizeInBytes;
				}
				else {
					keySizeInBytes = genRand(minKeySizeInBytes, maxKeySizeInBytes);
				}
			}
			if(blockSize == 0) {
				/* stream cipher */
				doCbc = false;
				doPadding = false;
			}
			else {
				if(!cbcSpec) {
					doCbc = isBitSet(0, loop);
				}
				if(!paddingSpec) {
					doPadding = isBitSet(1, loop);
				}
			}
			if(!doPadding && (blockSize != 0)) {
				/* align plaintext */
				ptextLen = (ptextLen / blockSize) * blockSize;
				if(ptextLen == 0) {
					ptextLen = blockSize;
				}
			}
			if(!stagedSpec) {
				stagedEncr = isBitSet(2, loop);
				stagedDecr = isBitSet(3, loop);
			}
			if(doCbc) {
				nullIV = isBitSet(4, loop);
			}
			else {
				nullIV = false;
			}
			inPlace = isBitSet(5, loop);
			if(allocCtxSpec) {
				ctxSizeUsed = allocCtx ? ctxSize : 0;
			}
			else if(isBitSet(6, loop)) {
				ctxSizeUsed = ctxSize;
			}
			else {
				ctxSizeUsed = 0;
			}
			askOutSize = isBitSet(7, loop);
			if(!quiet) {
			   	if(verbose || ((loop % LOOP_NOTIFY) == 0)) {
					printf("..loop %3d ptextLen %lu keyLen %d cbc=%d padding=%d stagedEncr=%d "
							"stagedDecr=%d\n",
						loop, (unsigned long)ptextLen, (int)keySizeInBytes, 
						(int)doCbc, (int)doPadding,
					 	(int)stagedEncr, (int)stagedDecr);
					printf("           nullIV %d inPlace %d ctxSize %d askOutSize %d\n",
						(int)nullIV, (int)inPlace, (int)ctxSizeUsed, (int)askOutSize);
				}
			}
			
			if(doTest(ptext, ptextLen,
					encrAlg, doCbc, doPadding, nullIV,
					keySizeInBytes,
					stagedEncr,	stagedDecr, inPlace, ctxSizeUsed, askOutSize,
					quiet)) {
				rtn = 1;
				break;
			}
			if(pauseInterval && ((loop % pauseInterval) == 0)) {
				char c;
				fpurge(stdin);
				printf("Hit CR to proceed, q to abort: ");
				c = getchar();
				if(c == 'q') {
					goto testDone;
				}
			}
			if(loops && (loop == loops)) {
				break;
			}
		}	/* main loop */
		if(rtn) {
			break;
		}
		
	}	/* for algs */
	
testDone:
	if(pauseInterval) {
		fpurge(stdin);
		printf("ModuleDetach/Unload complete; hit CR to exit: ");
		getchar();
	}
	if((rtn == 0) && !quiet) {
		printf("%s test complete\n", argv[0]);
	}
	free(ptext);
	return rtn;
}
Пример #3
0
static int doTest(const uint8_t *ptext,
	size_t ptextLen,
	CCAlgorithm encrAlg,			
	bool doCbc,
	bool doPadding,
	bool nullIV,			/* if CBC, use NULL IV */
	uint32 keySizeInBytes,
	bool stagedEncr,
	bool stagedDecr,
	bool inPlace,	
	size_t ctxSize,		
	bool askOutSize,
	bool quiet)
{
	uint8_t			keyBytes[MAX_KEY_SIZE];
	uint8_t			iv[MAX_BLOCK_SIZE];
	uint8_t			*ivPtrEncrypt;
	uint8_t			*ivPtrDecrypt;
	uint8_t			*ctext = NULL;		/* mallocd by doCCCrypt */
	size_t			ctextLen = 0;
	uint8_t			*rptext = NULL;		/* mallocd by doCCCrypt */
	size_t			rptextLen;
	CCCryptorStatus	crtn;
	int				rtn = 0;
	
	/* random key */
	appGetRandomBytes(keyBytes, keySizeInBytes);
	
	/* random IV if needed */
	if(doCbc) {
		if(nullIV) {
			memset(iv, 0, MAX_BLOCK_SIZE);
			
			/* flip a coin, give one side NULL, the other size zeroes */
			if(genRand(1,2) == 1) {
				ivPtrEncrypt = NULL;
				ivPtrDecrypt = iv;
			}
			else {
				ivPtrEncrypt = iv;
				ivPtrDecrypt = NULL;
			}
		}
		else {
			appGetRandomBytes(iv, MAX_BLOCK_SIZE);
			ivPtrEncrypt = iv;
			ivPtrDecrypt = iv;
		}
	}	
	else {
		ivPtrEncrypt = NULL;
		ivPtrDecrypt = NULL;
	}

	crtn = doCCCrypt(true, encrAlg, doCbc, doPadding,
		keyBytes, keySizeInBytes, ivPtrEncrypt,
		stagedEncr, inPlace, ctxSize, askOutSize,
		ptext, ptextLen,
		&ctext, &ctextLen);
	if(crtn) {
		rtn = testError(quiet);
		if(rtn) {
			goto abort;
		}
	}
	
	logSize(("###ctext len %lu\n", ctextLen)); 
	
	crtn = doCCCrypt(false, encrAlg, doCbc, doPadding,
		keyBytes, keySizeInBytes, ivPtrDecrypt,
		stagedDecr, inPlace, ctxSize, askOutSize,
		ctext, ctextLen,
		&rptext, &rptextLen);
	if(crtn) {
		rtn = testError(quiet);
		if(rtn) {
			goto abort;
		}
	}

	logSize(("###rptext len %lu\n", rptextLen)); 
	
	/* compare ptext, rptext */
	if(ptextLen != rptextLen) {
		printf("Ptext length mismatch: expect %lu, got %lu\n", ptextLen, rptextLen);
		rtn = testError(quiet);
		if(rtn) {
			goto abort;
		}
	}
	if(memcmp(ptext, rptext, ptextLen)) {
		printf("***data miscompare\n");
		rtn = testError(quiet);
	}
abort:
	if(ctext) {
		free(ctext);
	}
	if(rptext) {
		free(rptext);
	}
	return rtn;
}
int main(int argc, char **argv)
{
	RingBuffer		serverToClientRing;
	RingBuffer		clientToServerRing;
	unsigned		numBufs = DEFAULT_NUM_BUFS;
	unsigned		bufSize = DEFAULT_BUF_SIZE;
	unsigned		chunkSize = DEFAULT_CHUNK;
	unsigned char	clientBuf[DEFAULT_CHUNK];
	unsigned char	serverBuf[DEFAULT_CHUNK];
	RingBufferArgs	clientArgs;
	RingBufferArgs	serverArgs;
	bool			abortFlag = false;
	pthread_t		client_thread = NULL;
	int				result;
	OSStatus		ortn;
	unsigned char	sessionTicket[SESSION_TICKET_SIZE];
	int				ourRtn = 0;
	CFArrayRef		idArray = NULL;				/* for SSLSetCertificate */
	CFArrayRef		anchorArray = NULL;			/* trusted roots */
	char			*hostName = NULL;
	
	/* user-spec'd variables */
	char 			*kcName = NULL;
	unsigned 		xferSize = DEFAULT_XFER;
	bool			pauseOnError = false;
	bool			runForever = false;
	bool			skipPAC = false;

	extern int optind;
	extern char *optarg;
	int arg;
	optind = 1;
	while ((arg = getopt(argc, argv, "x:c:k:h:np")) != -1) {
		switch (arg) {
			case 'x':
			{
				unsigned xsize = atoi(optarg);
				if(xsize == 0) {
					runForever = true;
					/* and leave xferSize alone */
				}
				else {
					xferSize = xsize;
				}
				break;
			}
			case 'k':
				kcName = optarg;
				break;
			case 'n':
				skipPAC = true;
				break;
			case 'h':
				/* mainly to test EAP session ticket and ServerName simultaneously */
				hostName = optarg;
				break;
			case 'p':
				pauseOnError = true;
				break;
			default:
				usage(argv);
		}
	}
	if(optind != argc) {
		usage(argv);
	}
	
	/* set up ring buffers */
	ringBufSetup(&serverToClientRing, "serveToClient", numBufs, bufSize);
	ringBufSetup(&clientToServerRing, "clientToServe", numBufs, bufSize);

	/* get optional server SecIdentity */
	if(kcName) {
		SecKeychainRef	kcRef = NULL;
		SecCertificateRef anchorCert = NULL;
		SecIdentityRef	idRef = NULL;
		idArray = getSslCerts(kcName, 
			CSSM_FALSE,		/* encryptOnly */
			CSSM_FALSE,		/* completeCertChain */
			NULL,			/* anchorFile */
			&kcRef);
		if(idArray == NULL) {
			printf("***Can't get signing cert from %s\n", kcName);
			exit(1);
		}
		idRef = (SecIdentityRef)CFArrayGetValueAtIndex(idArray, 0);
		ortn = SecIdentityCopyCertificate(idRef, &anchorCert);
		if(ortn) {
			cssmPerror("SecIdentityCopyCertificate", ortn);
			exit(1);
		}
		anchorArray = CFArrayCreate(NULL, (const void **)&anchorCert,
				1, &kCFTypeArrayCallBacks);
		CFRelease(kcRef);
		CFRelease(anchorCert);
	}

	/* set up server side */
	memset(&serverArgs, 0, sizeof(serverArgs));
	serverArgs.xferSize = xferSize;
	serverArgs.xferBuf = serverBuf;
	serverArgs.chunkSize = chunkSize;
	serverArgs.ringWrite = &serverToClientRing;
	serverArgs.ringRead = &clientToServerRing;
	serverArgs.goFlag = &clientArgs.iAmReady;
	serverArgs.abortFlag = &abortFlag;
	serverArgs.pauseOnError = pauseOnError;
	appGetRandomBytes(serverArgs.sharedSecret, SHARED_SECRET_SIZE);
	if(!skipPAC) {
		serverArgs.setMasterSecret = true;
	}
	serverArgs.idArray = idArray;
	serverArgs.trustedRoots = anchorArray;

	/* set up client side */
	memset(&clientArgs, 0, sizeof(clientArgs));
	clientArgs.xferSize = xferSize;
	clientArgs.xferBuf = clientBuf;
	clientArgs.chunkSize = chunkSize;
	clientArgs.ringWrite = &clientToServerRing;
	clientArgs.ringRead = &serverToClientRing;
	clientArgs.goFlag = &serverArgs.iAmReady;
	clientArgs.abortFlag = &abortFlag;
	clientArgs.pauseOnError = pauseOnError;
	memmove(clientArgs.sharedSecret, serverArgs.sharedSecret, SHARED_SECRET_SIZE);
	clientArgs.hostName = hostName;
	
	/* for now set up an easily recognizable ticket */
	for(unsigned dex=0; dex<SESSION_TICKET_SIZE; dex++) {
		sessionTicket[dex] = dex;
	}
	clientArgs.sessionTicket = sessionTicket;
	clientArgs.sessionTicketLen = SESSION_TICKET_SIZE;
	/* client always tries setting the master secret in this test */
	clientArgs.setMasterSecret = true;
	clientArgs.trustedRoots = anchorArray;

	/* fire up client thread */
	result = pthread_create(&client_thread, NULL, 
			rbClientThread, &clientArgs);
	if(result) {
		printf("***pthread_create returned %d, aborting\n", result);
		exit(1);
	}
	
	/* 
	 * And the server pseudo thread. This returns when all data has been transferred. 
	 */
	ortn = rbServerThread(&serverArgs);
	
	if(abortFlag) {
		printf("***Test aborted.\n");
		exit(1);
	}
	
	printf("\n");
	
	printf("SSL Protocol Version : %s\n",
		sslGetProtocolVersionString(serverArgs.negotiatedProt));
	printf("SSL Cipher           : %s\n",
		sslGetCipherSuiteString(serverArgs.negotiatedCipher));
		
	if(skipPAC) {
		if(clientArgs.sessionWasResumed) {
			printf("***skipPAC true, but client reported sessionWasResumed\n");
			ourRtn = -1;
		}
		if(serverArgs.sessionWasResumed) {
			printf("***skipPAC true, but server reported sessionWasResumed\n");
			ourRtn = -1;
		}
		if(ourRtn == 0) {
			printf("...PAC session attempted by client; refused by server;\n");
			printf("   Normal session proceeded correctly.\n");
		}
	}
	else {
		if(!clientArgs.sessionWasResumed) {
			printf("***client reported !sessionWasResumed\n");
			ourRtn = -1;
		}
		if(!serverArgs.sessionWasResumed) {
			printf("***server reported !sessionWasResumed\n");
			ourRtn = -1;
		}
		if(memcmp(clientBuf, serverBuf, DEFAULT_CHUNK)) {
			printf("***Data miscompare***\n");
			ourRtn = -1;
		}
		if(ourRtn == 0) {
			printf("...PAC session resumed correctly.\n");
		}
	}
	/* FIXME other stuff? */

	return ourRtn;
}
int CommonCryptoSymRegression(int argc, char *const *argv)
{
	unsigned			loop;
	uint8_t				*ptext;
	size_t				ptextLen;
	bool				stagedEncr = false;
	bool				stagedDecr = false;
	bool				doPadding;
	bool				doCbc = false;
	bool				nullIV;
	const char			*algStr;
	CCAlgorithm			encrAlg;	
	int					i;
	int					currAlg;		// ALG_xxx
	uint32_t				minKeySizeInBytes;
	uint32_t				maxKeySizeInBytes;
	uint32_t				keySizeInBytes = 0;
	int					rtn = 0;
	uint32_t				blockSize;		// for noPadding case
	size_t				ctxSize;		// always set per alg
	size_t				ctxSizeUsed;	// passed to doTest
	bool				askOutSize;		// inquire output size each op
	
	/*
	 * User-spec'd params
	 */
	bool		keySizeSpec = false;		// false: use rand key size
	SymAlg		minAlg = ALG_FIRST;
	SymAlg		maxAlg = ALG_LAST;
	unsigned	loops = LOOPS_DEF;
	bool		verbose = false;
	size_t		minPtextSize = MIN_DATA_SIZE;
	size_t		maxPtextSize = MAX_DATA_SIZE;
	bool		quiet = true;
	unsigned	pauseInterval = 0;
	bool		paddingSpec = false;		// true: user calls doPadding, const
	bool		cbcSpec = false;			// ditto for doCbc
	bool		stagedSpec = false;			// ditto for stagedEncr and stagedDecr
	bool		inPlace = false;			// en/decrypt in place for ECB
	bool		allocCtxSpec = false;		// use allocCtx
	bool		allocCtx = false;			// allocate context ourself

	plan_tests(kTestTestCount);
	

	ptext = (uint8_t *)malloc(maxPtextSize);
	if(ptext == NULL) {
		diag("Insufficient heap space\n");
		exit(1);
	}
	/* ptext length set in test loop */
	
	if(!quiet) diag("Starting ccSymTest; args: ");
	for(i=1; i<argc; i++) {
		if(!quiet) diag("%s ", argv[i]);
	}
	if(!quiet) diag("\n");
	
	if(pauseInterval) {
		fpurge(stdin);
		diag("Top of test; hit CR to proceed: ");
		getchar();
	}

	for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) {
		switch(currAlg) {
			case ALG_DES:
				encrAlg = kCCAlgorithmDES;
				blockSize = kCCBlockSizeDES;
				minKeySizeInBytes = kCCKeySizeDES;
				maxKeySizeInBytes = minKeySizeInBytes;
				ctxSize = kCCContextSizeDES;
				algStr = "DES";
                diag("Running DES Tests");
				break;
			case ALG_3DES:
				encrAlg = kCCAlgorithm3DES;
				blockSize = kCCBlockSize3DES;
				minKeySizeInBytes = kCCKeySize3DES;
				maxKeySizeInBytes = minKeySizeInBytes;
				ctxSize = kCCContextSize3DES;
				
				algStr = "3DES";
                diag("Running 3DES Tests");
				break;
			case ALG_AES_128:
				encrAlg = kCCAlgorithmAES128;
				blockSize = kCCBlockSizeAES128;
				minKeySizeInBytes = kCCKeySizeAES128;
				maxKeySizeInBytes = minKeySizeInBytes;
				ctxSize = kCCContextSizeAES128;
				algStr = "AES128";
                diag("Running AES (128 bit key) Tests");
				break;
			case ALG_AES_192:
				encrAlg = kCCAlgorithmAES128;
				blockSize = kCCBlockSizeAES128;
				minKeySizeInBytes = kCCKeySizeAES192;
				maxKeySizeInBytes = minKeySizeInBytes;
				ctxSize = kCCContextSizeAES128;
				algStr = "AES192";
                diag("Running AES (192 bit key) Tests");
				break;
			case ALG_AES_256:
				encrAlg = kCCAlgorithmAES128;
				blockSize = kCCBlockSizeAES128;
				minKeySizeInBytes = kCCKeySizeAES256;
				maxKeySizeInBytes = minKeySizeInBytes;
				ctxSize = kCCContextSizeAES128;
				algStr = "AES256";
                diag("Running AES (256 bit key) Tests");
				break;
			case ALG_CAST:
				encrAlg = kCCAlgorithmCAST;
				blockSize = kCCBlockSizeCAST;
				minKeySizeInBytes = kCCKeySizeMinCAST;
				maxKeySizeInBytes = kCCKeySizeMaxCAST;
				ctxSize = kCCContextSizeCAST;
				algStr = "CAST";
                diag("Running CAST Tests");
				break;
			case ALG_RC4:
				encrAlg = kCCAlgorithmRC4;
				blockSize = 0;
				minKeySizeInBytes = kCCKeySizeMinRC4;
				maxKeySizeInBytes = kCCKeySizeMaxRC4;
				ctxSize = kCCContextSizeRC4;
				algStr = "RC4";
                diag("Running RC4 Tests");
				break;
			default:
				diag("***BRRZAP!\n");
				exit(1);
		}
		if(!quiet || verbose) {
			diag("Testing alg %s\n", algStr);
		}
		for(loop=1; ; loop++) {
			ptextLen = (size_t) genRand((unsigned int) minPtextSize, (unsigned int) maxPtextSize);
			appGetRandomBytes(ptext, ptextLen);
			
			/* per-loop settings */
			if(!keySizeSpec) {
				if(minKeySizeInBytes == maxKeySizeInBytes) {
					keySizeInBytes = minKeySizeInBytes;
				}
				else {
					keySizeInBytes = genRand(minKeySizeInBytes, maxKeySizeInBytes);
				}
			}
			if(blockSize == 0) {
				/* stream cipher */
				doCbc = false;
				doPadding = false;
			}
			else {
				if(!cbcSpec) {
					doCbc = isBitSet(0, loop);
				}
				if(!paddingSpec) {
					doPadding = isBitSet(1, loop);
				}
			}
			if(!doPadding && (blockSize != 0)) {
				/* align plaintext */
				ptextLen = (ptextLen / blockSize) * blockSize;
				if(ptextLen == 0) {
					ptextLen = blockSize;
				}
			}
			if(!stagedSpec) {
				stagedEncr = isBitSet(2, loop);
				stagedDecr = isBitSet(3, loop);
			}
			if(doCbc) {
				nullIV = isBitSet(4, loop);
			}
			else {
				nullIV = false;
			}
			inPlace = isBitSet(5, loop);
			if(allocCtxSpec) {
				ctxSizeUsed = allocCtx ? ctxSize : 0;
			}
			else if(isBitSet(6, loop)) {
				ctxSizeUsed = ctxSize;
			}
			else {
				ctxSizeUsed = 0;
			}
			askOutSize = isBitSet(7, loop);
			if(!quiet) {
			   	if(verbose || ((loop % LOOP_NOTIFY) == 0)) {
					diag("..loop %3d ptextLen %lu keyLen %d cbc=%d padding=%d stagedEncr=%d "
							"stagedDecr=%d\n",
						loop, (unsigned long)ptextLen, (int)keySizeInBytes, 
						(int)doCbc, (int)doPadding,
					 	(int)stagedEncr, (int)stagedDecr);
					diag("           nullIV %d inPlace %d ctxSize %d askOutSize %d\n",
						(int)nullIV, (int)inPlace, (int)ctxSizeUsed, (int)askOutSize);
				}
			}
			
			if(doTest(ptext, ptextLen,
					encrAlg, doCbc, doPadding, nullIV,
					keySizeInBytes,
					stagedEncr,	stagedDecr, inPlace, ctxSizeUsed, askOutSize,
					quiet)) {
				rtn = 1;
				break;
			}
			if(pauseInterval && ((loop % pauseInterval) == 0)) {
				char c;
				fpurge(stdin);
				diag("Hit CR to proceed, q to abort: ");
				c = getchar();
				if(c == 'q') {
					goto testDone;
				}
			}
			if(loops && (loop == loops)) {
				break;
			}
		}	/* main loop */
		if(rtn) {
			break;
		}
		
	}	/* for algs */
	
testDone:

    ok(rtn == 0, "ccSymTest");

	if(pauseInterval) {
		fpurge(stdin);
		diag("ModuleDetach/Unload complete; hit CR to exit: ");
		getchar();
	}
	if((rtn == 0) && !quiet) {
		diag("%s test complete\n", argv[0]);
	}
	free(ptext);
	return rtn;
}
int main(int argc, char **argv)
{
	unsigned loops = LOOPS_DEF;
	unsigned bufSize = BUFSIZE_DEF;
	unsigned algFirst = ALG_FIRST;
	unsigned algLast = ALG_LAST;
	bool ecbMode = false;
	extern char *optarg;
	int arg;
	while ((arg = getopt(argc, argv, "a:l:b:eh")) != -1) {
		switch (arg) {
			case 'a':
				switch(optarg[0]) {
					case 'a':
						algFirst = algLast = ALG_AES_128;
						break;
					case 'n':
						algFirst = algLast = ALG_AES_192;
						break;
					case 'A':
						algFirst = algLast = ALG_AES_256;
						break;
					case 'd':
						algFirst = algLast = ALG_DES;
						break;
					case '3':
						algFirst = algLast = ALG_3DES;
						break;
					case 'c':
						algFirst = algLast = ALG_CAST;
					case '4':
						algFirst = algLast = ALG_RC4;
				}
				break;
			case 'l':
				loops = atoi(optarg);
				break;
			case 'b':
				bufSize = atoi(optarg);
				break;
			case 'e':
				ecbMode = true;
				break;
			case 'h':
				usage(argv);
		}
	}
	if(optind != argc) {
		usage(argv);
	}

	/* 
	 * encrypt and decrypt on workBuf
	 * save original ptext in saveBuf, compare at end as sanity check 
	 *   for ECB only
	 */
	unsigned char *workBuf = (unsigned char *)malloc(bufSize);
	unsigned char *saveBuf = (unsigned char *)malloc(bufSize);
	if((workBuf == NULL) || (saveBuf == NULL)) {
		printf("***malloc failure\n");
		exit(1);
	}
	appGetRandomBytes(workBuf, bufSize);
	memmove(saveBuf, workBuf, bufSize);
	
	uint8_t keyBytes[MAX_KEY_SIZE];
	size_t keyLength;
	
	appGetRandomBytes(keyBytes, MAX_KEY_SIZE);
	
	CCCryptorRef cryptor;
	CCAlgorithm alg;
	CCOptions options = 0;
	OSStatus ortn;
	
	if(ecbMode) {
		options |= kCCOptionECBMode;
	}
	
	unsigned currAlg;
	for(currAlg=algFirst; currAlg<=algLast; currAlg++) {
		const char *algStr = NULL;
		
		switch(currAlg) {
			case ALG_DES:
				keyLength = kCCKeySizeDES;
				alg = kCCAlgorithmDES;
				algStr = "DES ";
				break;
			case ALG_3DES:
				keyLength = kCCKeySize3DES;
				alg = kCCAlgorithm3DES;
				algStr = "3DES";
				break;
			case ALG_AES_128:
				keyLength = kCCKeySizeAES128;
				alg = kCCAlgorithmAES128;
				algStr = "AES128";
				break;
			case ALG_AES_192:
				keyLength = kCCKeySizeAES192;
				alg = kCCAlgorithmAES128;
				algStr = "AES192";
				break;
			case ALG_AES_256:
				keyLength = kCCKeySizeAES256;
				alg = kCCAlgorithmAES128;
				algStr = "AES256";
				break;
			case ALG_CAST:
				keyLength = kCCKeySizeMaxCAST;
				alg = kCCAlgorithmCAST;
				algStr = "CAST";
				break;
			case ALG_RC4:
				keyLength = kCCKeySizeMaxRC4;
				alg = kCCAlgorithmRC4;
				algStr = "RC4";
				break;
		}
		
		printf("Algorithm: %s  keySize: %u  mode: %s  loops: %u  bufSize: %u\n",
			algStr, (unsigned)keyLength, ecbMode ? "ECB" : "CBC",
			(unsigned)loops, (unsigned)bufSize);
			
		CFAbsoluteTime start, end;
		unsigned loop;
		size_t thisMoved;
		
		/* encrypt: GO */
		start = CFAbsoluteTimeGetCurrent();
		
		ortn = CCCryptorCreate(kCCEncrypt, alg, options,
			keyBytes, keyLength, NULL, &cryptor);
		if(ortn) {
			printCCError("CCCryptorCreate", ortn);
			exit(1);
		}
		
		for(loop=0; loop<loops; loop++) {
			ortn = CCCryptorUpdate(cryptor, workBuf, bufSize,
				workBuf, bufSize, &thisMoved);
			if(ortn) {
				printCCError("CCCryptorUpdate", ortn);
				exit(1);
			}
		}
		/* no padding, CCCryptFinal not needed */
		end = CFAbsoluteTimeGetCurrent();
		
		printf("   encrypt %u * %u bytes took %gs: %g KBytes/s\n",
			(unsigned)loops, (unsigned)bufSize,
			end - start,
			(loops * bufSize) / (end - start) / 1024.0);
			
		/* dncrypt: GO */
		start = CFAbsoluteTimeGetCurrent();
		
		ortn = CCCryptorCreate(kCCDecrypt, alg, options,
			keyBytes, keyLength, NULL, &cryptor);
		if(ortn) {
			printCCError("CCCryptorCreate", ortn);
			exit(1);
		}
		
		for(loop=0; loop<loops; loop++) {
			ortn = CCCryptorUpdate(cryptor, workBuf, bufSize,
				workBuf, bufSize, &thisMoved);
			if(ortn) {
				printCCError("CCCryptorUpdate", ortn);
				exit(1);
			}
		}
		/* no padding, CCCryptFinal not needed */
		end = CFAbsoluteTimeGetCurrent();
		
		printf("   decrypt %u * %u bytes took %gs: %g KBytes/s\n",
			(unsigned)loops, (unsigned)bufSize,
			end - start,
			(loops * bufSize) / (end - start) / 1024.0);
			
		if(ecbMode) {
			if(memcmp(workBuf, saveBuf, bufSize)) {
				printf("***plaintext miscompare!\n");
			}
		}
	}
	return 0;
}