asymTest.c   [plain text]


/* Copyright (c) 1998,2003-2006,2008 Apple Inc.
 *
 * asymTest.c - test CSP asymmetric encrypt/decrypt.
 *
 * Revision History
 * ----------------
 *  10 May 2000 Doug Mitchell
 *		Ported to X/CDSA2. 
 *  14 May 1998	Doug Mitchell at Apple
 *		Created.
 */
 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <Security/cssm.h>
#include "cspwrap.h"
#include "common.h"
#include "cspdlTesting.h"

#define USAGE_NAME			"noUsage"
#define USAGE_NAME_LEN		(strlen(USAGE_NAME))
#define USAGE2_NAME			"noUsage2"
#define USAGE2_NAME_LEN		(strlen(USAGE2_NAME))
#define LOOPS_DEF			10
#define MIN_EXP				2		/* for data size 10**exp */
#define DEFAULT_MAX_EXP		2
#define MAX_EXP				4

/*
 * Enumerate algs our own way to allow iteration.
 */
#define ALG_RSA				1
#define ALG_FEED			2
#define ALG_FEEDEXP			3
#define ALG_FEE_CFILE		4
#define ALG_FIRST			ALG_RSA
#define ALG_LAST			ALG_FEEDEXP
#define MAX_DATA_SIZE		(10000 + 100)	/* bytes */
#define FEE_PASSWD_LEN		32		/* private data length in bytes, FEE only */

#define DUMP_KEY_DATA		0

/* 
 * RSA encryption now allows arbitrary plaintext size. BSAFE was limited to 
 * primeSize - 11.
 */
#define RSA_PLAINTEXT_LIMIT		0

static void usage(char **argv)
{
	printf("usage: %s [options]\n", argv[0]);
	printf("   Options:\n");
	printf("   a=algorithm (f=FEED; x=FEEDExp; c=FEE_Cfile; r=RSA; default=all)\n");
	printf("   l=loops (default=%d; 0=forever)\n", LOOPS_DEF);
	printf("   n=minExp (default=%d)\n", MIN_EXP);
	printf("   x=maxExp (default=%d, max=%d)\n", DEFAULT_MAX_EXP, MAX_EXP);
	printf("   k=keySize\n");
	printf("   P=primeType (m=Mersenne, f=FEE, g=general; FEE only)\n");
	printf("   C=curveType (m=Montgomery, w=Weierstrass, g=general; FEE only)\n");
	printf("   e(xport)\n");
	printf("   r(eference keys only)\n");
	printf("   K (skip decrypt)\n");
	printf("   N(o padding, RSA only)\n");
	printf("   S (no staging)\n");
	printf("   D (CSP/DL; default = bare CSP)\n");
	printf("   p (pause on each loop)\n");
	printf("   u (quick; small keys)\n");
	printf("   t=plainTextSize; default=random\n");
	printf("   z(ero data)\n");
	printf("   v(erbose)\n");
	printf("   q(uiet)\n");
	printf("   h(elp)\n");
	exit(1);
}

static int doTest(CSSM_CSP_HANDLE cspHand,
	CSSM_ALGORITHMS	alg,					// CSSM_ALGID_xxx
	CSSM_PADDING	padding,
	CSSM_DATA_PTR 	ptext,
	CSSM_BOOL 		verbose,
	CSSM_BOOL 		quiet,
	uint32 			keySizeInBits,			// may be 0, i.e., default per alg
	uint32			primeType,				// FEE only
	uint32			curveType,				// ditto
	CSSM_BOOL 		pubIsRef,
	CSSM_KEYBLOB_FORMAT rawPubForm,
	CSSM_BOOL		privIsRef,
	CSSM_KEYBLOB_FORMAT rawPrivForm,
	CSSM_BOOL		secondPubIsRaw,			// FEED via CSPDL: 2nd key must be raw
	CSSM_BOOL 		stagedEncr,
	CSSM_BOOL 		stagedDecr,
	CSSM_BOOL 		mallocPtext,			// only meaningful if !stagedDecr
	CSSM_BOOL 		mallocCtext,			// only meaningful if !stagedEncr
	CSSM_BOOL		skipDecrypt,
	CSSM_BOOL		genSeed)				// FEE keys only
{
	// these two are always generated
	CSSM_KEY		recvPubKey;
	CSSM_KEY		recvPrivKey;
	// these two are for two-key FEE algorithms only
	CSSM_KEY		sendPubKey;		
	CSSM_KEY		sendPrivKey;	
	// these two are optionally created by cspRefKeyToRaw if (FEED && secondPubIsRaw)
	CSSM_KEY		sendPubKeyRaw;	
	CSSM_KEY		recvPubKeyRaw;	
	CSSM_BOOL		rawPubKeysCreated = CSSM_FALSE;
	
	/* two-key FEE, CSP  :  &{send,recv}PubKey
	 * two-key FEE, CSPDL:  &{send,recv}PubKeyRaw
	 * else              :  NULL, &recvPubKey
	 */
	CSSM_KEY_PTR	sendPubKeyPtr = NULL;	
	CSSM_KEY_PTR	recvPubKeyPtr = NULL;
	
	CSSM_DATA 		ctext = {0, NULL};
	CSSM_DATA		rptext = {0, NULL};
	CSSM_RETURN		crtn;
	int				rtn = 0;
	uint32			keyGenAlg;
	uint32			mode = CSSM_ALGMODE_NONE;	// FIXME - does this need testing?
	CSSM_BOOL		twoKeys = CSSM_FALSE;
	
	switch(alg) {
		case CSSM_ALGID_FEED:
		case CSSM_ALGID_FEECFILE:
			twoKeys = CSSM_TRUE;
			/* drop thru */
		case CSSM_ALGID_FEEDEXP:
			keyGenAlg = CSSM_ALGID_FEE;
			break;
		case CSSM_ALGID_RSA:
			keyGenAlg = CSSM_ALGID_RSA;
			break;
		default:
			printf("bogus algorithm\n");
			return 1;
	}
	
	/* one key pair for all algs except CFILE and FEED, which need two */
	if(keyGenAlg == CSSM_ALGID_FEE) {
		uint8 			passwd[FEE_PASSWD_LEN];
		CSSM_DATA		pwdData = {FEE_PASSWD_LEN, passwd};
		CSSM_DATA_PTR	pwdDataPtr;
		if(genSeed) {
			simpleGenData(&pwdData, FEE_PASSWD_LEN, FEE_PASSWD_LEN);
			pwdDataPtr = &pwdData;
		}
		else {
			pwdDataPtr = NULL;
		}
		/*
		 * Note we always generate public keys per the pubIsRef argument, even if 
		 * secondPubIsRaw is true, 'cause the CSPDL can't generate raw keys.
		 */
		rtn = cspGenFEEKeyPair(cspHand,
				USAGE_NAME,
				USAGE_NAME_LEN,
				keySizeInBits,
				primeType,
				curveType,
				&recvPubKey,
				pubIsRef,
				CSSM_KEYUSE_ANY,
				CSSM_KEYBLOB_RAW_FORMAT_NONE,
				&recvPrivKey,
				privIsRef,
				CSSM_KEYUSE_ANY,
				CSSM_KEYBLOB_RAW_FORMAT_NONE,
				pwdDataPtr);
		if(rtn) {
			/* leak */
			return testError(quiet);
		}
		if(twoKeys) {
			rtn = cspGenFEEKeyPair(cspHand,
					USAGE2_NAME,
					USAGE2_NAME_LEN,
					keySizeInBits,
					primeType,
					curveType,
					&sendPubKey,
					pubIsRef,
					CSSM_KEYUSE_ANY,
					CSSM_KEYBLOB_RAW_FORMAT_NONE,
					&sendPrivKey,
					privIsRef,
					CSSM_KEYUSE_ANY,
					CSSM_KEYBLOB_RAW_FORMAT_NONE,
					pwdDataPtr);
			if(rtn) {
				/* leak recv*Key */
				return testError(quiet);
			}
		}
		if(twoKeys) {
			if(secondPubIsRaw && pubIsRef) {
				/* 
				* Convert ref public keys to raw - they're going into a Context; the
				* SecurityServer doesn't deal with ref keys there.
				* Leak all sorts of stuff on any error here. 
				*/
				crtn = cspRefKeyToRaw(cspHand, &sendPubKey, &sendPubKeyRaw);
				if(crtn) {
					return testError(quiet);
				}
				crtn = cspRefKeyToRaw(cspHand, &recvPubKey, &recvPubKeyRaw);
				if(crtn) {
					return testError(quiet);
				}
				/* two keys, CSPDL */
				sendPubKeyPtr = &sendPubKeyRaw;
				recvPubKeyPtr = &recvPubKeyRaw;
				rawPubKeysCreated = CSSM_TRUE;
			}
			else {
				/* two keys, CSP */
				sendPubKeyPtr = &sendPubKey;
				recvPubKeyPtr = &recvPubKey;
			}
		}
		else {
			/* one key pair, standard config */
			sendPubKeyPtr = NULL;
			recvPubKeyPtr =  &recvPubKey;
		}
	}
	else {
		CSSM_KEYBLOB_FORMAT expectPubForm = rawPubForm;
		CSSM_KEYBLOB_FORMAT expectPrivForm = rawPrivForm;
		
		rtn = cspGenKeyPair(cspHand,
				keyGenAlg,
				USAGE_NAME,
				USAGE_NAME_LEN,
				keySizeInBits,
				&recvPubKey,
				pubIsRef,
				twoKeys ? CSSM_KEYUSE_ANY : CSSM_KEYUSE_ENCRYPT,
				rawPubForm,
				&recvPrivKey,
				privIsRef,
				twoKeys ? CSSM_KEYUSE_ANY : CSSM_KEYUSE_DECRYPT,
				rawPrivForm,
				genSeed);
		if(rtn) {
			return testError(quiet);
		}
		/* one key pair, standard config */
		sendPubKeyPtr = NULL;
		recvPubKeyPtr =  &recvPubKey;
		
		/* verify defaults - only for RSA */
		if(!pubIsRef) {
			if(rawPubForm == CSSM_KEYBLOB_RAW_FORMAT_NONE) {
				expectPubForm = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
			}
			if(recvPubKey.KeyHeader.Format != expectPubForm) {
				printf("***Bad raw RSA pub key format - exp %u got %u\n",
					(unsigned)expectPubForm, 
					(unsigned)recvPubKey.KeyHeader.Format);
				if(testError(quiet)) {
					return 1;
				}
			}
		}
		if(!privIsRef) {
			if(rawPrivForm == CSSM_KEYBLOB_RAW_FORMAT_NONE) {
				expectPrivForm = CSSM_KEYBLOB_RAW_FORMAT_PKCS8;
			}
			if(recvPrivKey.KeyHeader.Format != expectPrivForm) {
				printf("***Bad raw RSA priv key format - exp %u got %u\n",
					(unsigned)expectPrivForm, 
					(unsigned)recvPrivKey.KeyHeader.Format);
				if(testError(quiet)) {
					return 1;
				}
			}
		}
	}
	#if	DUMP_KEY_DATA
	dumpBuffer("Pub  Key Data", recvPubKey.KeyData.Data, recvPubKey.KeyData.Length);
	dumpBuffer("Priv Key Data", recvPrivKey.KeyData.Data, recvPrivKey.KeyData.Length);
	#endif
	if(stagedEncr) {
		crtn = cspStagedEncrypt(cspHand,
			alg,
			mode,
			padding,
			/* Two keys: second must be pub */
			twoKeys ? &sendPrivKey  : &recvPubKey,
			twoKeys ? recvPubKeyPtr : NULL,
			0,			// effectiveKeySize
			0,			// cipherBlockSize
			0,			// rounds
			NULL,		// initVector
			ptext,
			&ctext,
			CSSM_TRUE);	// multi
	}
	else {
		crtn = cspEncrypt(cspHand,
			alg,
			mode,
			padding,
			/* Two keys: second must be pub */
			twoKeys ? &sendPrivKey  : &recvPubKey,
			twoKeys ? recvPubKeyPtr : NULL,
			0,			// effectiveKeySize
			0,			// rounds
			NULL,		// initVector
			ptext,
			&ctext,
			mallocCtext);
	}
	if(crtn) {
		rtn = testError(quiet);
		goto abort;
	}
	if(verbose) {
		printf("  ..ptext size %lu  ctext size %lu\n",
			(unsigned long)ptext->Length, (unsigned long)ctext.Length);
	}
	if(skipDecrypt) {
		goto abort;
	}
	if(stagedDecr) {
		crtn = cspStagedDecrypt(cspHand,
			alg,
			mode,
			padding,
			/* Two keys: second must be pub */
			&recvPrivKey,
			sendPubKeyPtr,
			0,			// effectiveKeySize
			0,			// cipherBlockSize
			0,			// rounds
			NULL,		// initVector
			&ctext,
			&rptext,
			CSSM_TRUE);	// multi
	}
	else {
		crtn = cspDecrypt(cspHand,
			alg,
			mode,
			padding,
			&recvPrivKey,
			sendPubKeyPtr,
			0,			// effectiveKeySize
			0,			// rounds
			NULL,		// initVector
			&ctext,
			&rptext,
			mallocPtext);
	}
	if(crtn) {
		rtn = testError(quiet);
		goto abort;
	}
	/* compare ptext, rptext */
	if(ptext->Length != rptext.Length) {
		printf("Ptext length mismatch: expect %lu, got %lu\n", 
			(unsigned long)ptext->Length, (unsigned long)rptext.Length);
		rtn = testError(quiet);
		if(rtn) {
			goto abort;
		}
	}
	if(memcmp(ptext->Data, rptext.Data, ptext->Length)) {
		printf("***data miscompare\n");
		rtn = testError(quiet);
	}
abort:
	/* free keys */
	if(cspFreeKey(cspHand, &recvPubKey)) {
		printf("Error freeing recvPubKey\n");
		rtn = 1;
	}
	if(cspFreeKey(cspHand, &recvPrivKey)) {
		printf("Error freeing recvPrivKey\n");
		rtn = 1;
	}
	if(twoKeys) {
		if(cspFreeKey(cspHand, &sendPubKey)) {
			printf("Error freeing sendPubKey\n");
			rtn = 1;
		}
		if(cspFreeKey(cspHand, &sendPrivKey)) {
			printf("Error freeing sendPrivKey\n");
			rtn = 1;
		}
		if(rawPubKeysCreated) {
			if(cspFreeKey(cspHand, &sendPubKeyRaw)) {
				printf("Error freeing sendPubKeyRaw\n");
				rtn = 1;
			}
			if(cspFreeKey(cspHand, &recvPubKeyRaw)) {
				printf("Error freeing recvPubKeyRaw\n");
				rtn = 1;
			}
		}
	}
	/* free rptext, ctext */
	appFreeCssmData(&rptext, CSSM_FALSE);
	appFreeCssmData(&ctext, CSSM_FALSE);
	return rtn;
}

static const char *formStr(
	CSSM_KEYBLOB_FORMAT form)
{
	switch(form) {
		case CSSM_KEYBLOB_RAW_FORMAT_NONE:  return "NONE";
		case CSSM_KEYBLOB_RAW_FORMAT_PKCS1: return "PKCS1";
		case CSSM_KEYBLOB_RAW_FORMAT_PKCS8: return "PKCS8";
		case CSSM_KEYBLOB_RAW_FORMAT_X509:  return "X509";
		case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:  return "SSH1";
		case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2:  return "SSH2";
		default:
			printf("***BRRRZAP! formStr needs work\n");
			exit(1);
	}
}

int main(int argc, char **argv)
{
	int					arg;
	char				*argp;
	unsigned			loop;
	CSSM_DATA			ptext;
	CSSM_CSP_HANDLE 	cspHand;
	CSSM_BOOL			pubIsRef = CSSM_TRUE;
	CSSM_BOOL			privIsRef = CSSM_TRUE;
	CSSM_BOOL			stagedEncr;
	CSSM_BOOL 			stagedDecr;
	const char 			*algStr;
	uint32				encAlg;			// CSSM_ALGID_xxx
	unsigned			currAlg;		// ALG_xxx
	int					i;
	CSSM_BOOL			mallocCtext;
	CSSM_BOOL			mallocPtext;
	int					rtn = 0;
	CSSM_BOOL			genSeed;		// for FEE key gen
	CSSM_PADDING		padding;
	CSSM_KEYBLOB_FORMAT	rawPubFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE;
	CSSM_KEYBLOB_FORMAT	rawPrivFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE;
	
	/*
	 * User-spec'd params
	 */
	unsigned			loops = LOOPS_DEF;
	CSSM_BOOL			verbose = CSSM_FALSE;
	unsigned			minExp = MIN_EXP;
	unsigned			maxExp = DEFAULT_MAX_EXP;
	CSSM_BOOL			quiet = CSSM_FALSE;
	uint32				keySizeInBits = CSP_KEY_SIZE_DEFAULT;
	CSSM_BOOL			keySizeSpec = CSSM_FALSE;
	unsigned			minAlg = ALG_FIRST;
	uint32				maxAlg = ALG_LAST;
	CSSM_BOOL			skipDecrypt = CSSM_FALSE;
	CSSM_BOOL			bareCsp = CSSM_TRUE;
	CSSM_BOOL			doPause = CSSM_FALSE;
	CSSM_BOOL			smallKeys = CSSM_FALSE;
	uint32				primeType = CSSM_FEE_PRIME_TYPE_DEFAULT;	// FEE only
	uint32				curveType = CSSM_FEE_CURVE_TYPE_DEFAULT;	// FEE only
	uint32				ptextSize = 0;			// 0 means random
	dataType			dtype = DT_Random;
	CSSM_BOOL			refKeysOnly = CSSM_FALSE;
	CSSM_BOOL			noPadding = CSSM_FALSE;
	CSSM_BOOL			stagingEnabled = CSSM_TRUE;
	
	for(arg=1; arg<argc; arg++) {
		argp = argv[arg];
		switch(argp[0]) {
			case 'a':
				if(argp[1] != '=') {
					usage(argv);
				}
				switch(argp[2]) {
					case 'f':
						minAlg = maxAlg = ALG_FEED;
						break;
					case 'x':
						minAlg = maxAlg = ALG_FEEDEXP;
						break;
					case 'c':
						minAlg = maxAlg = ALG_FEE_CFILE;
						break;
					case 'r':
						minAlg = maxAlg = ALG_RSA;
						break;
					case 'a':
						minAlg = ALG_FIRST;
						maxAlg = ALG_LAST;
						break;
					default:
						usage(argv);
				}
				break;
		    case 'l':
				loops = atoi(&argp[2]);
				break;
		    case 'n':
				minExp = atoi(&argp[2]);
				break;
		    case 'x':
				maxExp = atoi(&argp[2]);
				if(maxExp > MAX_EXP) {
					usage(argv);
				}
				break;
			case 'k':
				keySizeInBits = atoi(&argv[arg][2]);
				keySizeSpec = CSSM_TRUE;
				break;
			case 'K':
				skipDecrypt = CSSM_TRUE;
				break;
		    case 't':
				ptextSize = atoi(&argp[2]);
				break;
			case 'D':
				bareCsp = CSSM_FALSE;
				#if CSPDL_ALL_KEYS_ARE_REF
		    	refKeysOnly = CSSM_TRUE;
				#endif
				break;
			case 'u':
				smallKeys = CSSM_TRUE;
				break;
		    case 'N':
		    	noPadding = CSSM_TRUE;
				break;
			case 'z':
				dtype = DT_Zero;
				break;
		    case 'v':
		    	verbose = CSSM_TRUE;
				break;
		    case 'r':
		    	refKeysOnly = CSSM_TRUE;
				break;
			case 'S':
				stagingEnabled = CSSM_FALSE;
				break;
		    case 'p':
		    	doPause = CSSM_TRUE;
				break;
		    case 'q':
		    	quiet = CSSM_TRUE;
				break;
			case 'C':
				switch(argp[2]) {
					case 'm':
						curveType = CSSM_FEE_CURVE_TYPE_MONTGOMERY;
						break;
					case 'w':
						curveType = CSSM_FEE_CURVE_TYPE_WEIERSTRASS;
						break;
					default:
						usage(argv);
				}
				break;
			case 'P':
				switch(argp[2]) {
					case 'm':
						primeType = CSSM_FEE_PRIME_TYPE_MERSENNE;
						break;
					case 'f':
						primeType = CSSM_FEE_PRIME_TYPE_FEE;
						break;
					case 'g':
						primeType = CSSM_FEE_PRIME_TYPE_GENERAL;
						break;
					default:
						usage(argv);
				}
				break;
		    case 'h':
		    default:
				usage(argv);
		}
	}
	ptext.Data = (uint8 *)CSSM_MALLOC(MAX_DATA_SIZE);
	
	/* length set in test loop */
	if(ptext.Data == NULL) {
		printf("Insufficient heap\n");
		exit(1);
	}
	if(noPadding) {
		if(ptextSize == 0) {
			printf("**WARNING NoPad mode will fail with random plaintext size\n");
		}
		if(!keySizeSpec) {
			printf("**WARNING NoPad mode will fail with random key size\n");
		}
		else {
			uint32 keyBytes = keySizeInBits / 8;
			if(ptextSize != keyBytes) {
				/*
				 * FIXME: I actually do not understand why this fails, but
				 * doing raw RSA encryption with ptextSize != keySize results
				 * in random-looking failures, probably based on the plaintext
				 * itself (it doesn't fail with zero data).
				 */
				printf("***WARNING NoPad mode requires plaintext size = key size\n");
			}
		}
	}
	printf("Starting asymTest; args: ");
	for(i=1; i<argc; i++) {
		printf("%s ", argv[i]);
	}
	printf("\n");
	cspHand = cspDlDbStartup(bareCsp, NULL);
	if(cspHand == 0) {
		exit(1);
	}
	for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) {
		switch(currAlg) {
			case ALG_FEED:
				encAlg = CSSM_ALGID_FEED;
				algStr = "FEED";
				padding = CSSM_PADDING_NONE;
				break;
			case ALG_FEEDEXP:
				encAlg = CSSM_ALGID_FEEDEXP;
				algStr = "FEEDExp";
				padding = CSSM_PADDING_NONE;
				break;
			case ALG_FEE_CFILE:
				encAlg = CSSM_ALGID_FEECFILE;
				algStr = "FEE_CFILE";
				padding = CSSM_PADDING_NONE;
				break;
			case ALG_RSA:
				encAlg = CSSM_ALGID_RSA;
				algStr = "RSA";
				if(noPadding) {
					padding = CSSM_PADDING_NONE;
				}
				else {
					padding = CSSM_PADDING_PKCS1;
				}
				break;
		}
		if(!quiet) {
			printf("Testing alg %s\n", algStr);
		}
		for(loop=1; ; loop++) {
			if(doPause) {
				fpurge(stdin);
				printf("Top of loop; hit CR to proceed: ");
				getchar();
			}
			if(ptextSize) {
				if(dtype == DT_Zero) {
					memset(ptext.Data, 0, ptextSize);
					ptext.Length = ptextSize;
				}
				else {
					simpleGenData(&ptext, ptextSize, ptextSize);
				}
			}
			else {
				ptext.Length = genData(ptext.Data, minExp, maxExp, dtype);
			}
			
			/* raw RSA, no padding, ensure top two bits are zero */
			if((encAlg == CSSM_ALGID_RSA) && (padding == CSSM_PADDING_NONE)) {
				ptext.Data[0] &= 0x3f;
			}

			if(!keySizeSpec) {
				/* random per alg unless user overrides */
				if(encAlg == CSSM_ALGID_RSA) { 
					if(smallKeys) {
						keySizeInBits = CSP_RSA_KEY_SIZE_DEFAULT;
					}
					else {
						keySizeInBits = randKeySizeBits(CSSM_ALGID_RSA, OT_Encrypt);
					}
				}
				else {
					/* FEED, FEEDExp */
					if(smallKeys) {
						keySizeInBits = 127;
						/* default curveType = Weierstrass */
					}
					else {
						randFeeKeyParams(encAlg,
							&keySizeInBits,
							&primeType,
							&curveType);
					}
				}
			}
			#if		RSA_PLAINTEXT_LIMIT
			if(encAlg == CSSM_ALGID_RSA) {
				/* total ptext size can't exceed (modulus size - 11) */
				/* we should probably get this from the CSP, but this
				 * whole thing is such a kludge. What's the point?
				 * Only RSA encrypt/decrypt has a max total size.
				 */
			 	unsigned modSize;
			 	unsigned maxSize;
				if(keySizeInBits == CSP_KEY_SIZE_DEFAULT) {
					modSize = CSP_RSA_KEY_SIZE_DEFAULT / 8;
				}
				else {
					modSize = keySizeInBits / 8;
				}
				maxSize = modSize - 11;
				ptext.Length = genRand(1, maxSize);
			}
			#endif
			if(!quiet) {
				if(encAlg == CSSM_ALGID_RSA) { 
					printf("..loop %d text size %lu keySize %u\n",
						loop, (unsigned long)ptext.Length, (unsigned)keySizeInBits);
				}
				else {
					printf("..loop %d text size %lu keySize %u primeType %s "
						"curveType %s\n",
						loop, (unsigned long)ptext.Length, (unsigned)keySizeInBits,
						primeTypeStr(primeType), curveTypeStr(curveType));
				}
			}
			
			/* mix up some ref and data keys, as well as staging and mallocing */
			if(!refKeysOnly) {
				pubIsRef   = (loop & 1) ? CSSM_TRUE : CSSM_FALSE;
				privIsRef   = (loop & 8) ? CSSM_TRUE : CSSM_FALSE;
			}
			if((currAlg == ALG_FEE_CFILE) || !stagingEnabled) {
				/* staged ops unsupported */
				stagedEncr = CSSM_FALSE;
				stagedDecr = CSSM_FALSE;
			}
			else {
				stagedEncr = (loop & 2) ? CSSM_TRUE : CSSM_FALSE;
				stagedDecr  = (loop & 4) ? CSSM_TRUE : CSSM_FALSE;
			}
			if(!stagedEncr) {
				mallocCtext = (ptext.Data[0] & 1) ? CSSM_TRUE : CSSM_FALSE;
			}
			else {
				mallocCtext = CSSM_FALSE;
			}
			if(!stagedDecr) {
				mallocPtext = (ptext.Data[0] & 2) ? CSSM_TRUE : CSSM_FALSE;
			}
			else {
				mallocPtext = CSSM_FALSE;
			}
			switch(currAlg) {
				case ALG_FEED:
				case ALG_FEEDEXP:
					genSeed = (ptext.Data[0] & 4) ? CSSM_TRUE : CSSM_FALSE;
					break;
				default:
					genSeed = CSSM_FALSE;
			}
			rawPubFormat = rawPrivFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE;
			if(currAlg == ALG_RSA) {
				/* mix up raw key formats */
				unsigned die;
				if(!pubIsRef) {
					/* five formats */
					die = ptext.Data[1] % 5;
					switch(die) {
						case 0:
							rawPubFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE;
							break;
						case 1:
							rawPubFormat = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
							break;
						case 2:
							rawPubFormat = CSSM_KEYBLOB_RAW_FORMAT_OPENSSH;
							break;
						case 3:
							rawPubFormat = CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2;
							break;
						default:
							rawPubFormat = CSSM_KEYBLOB_RAW_FORMAT_X509;
							break;
					}
				}
				if(!privIsRef) {
					/* four formats */
					die = ptext.Data[2] % 4;
					switch(die) {
						case 0:
							rawPrivFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE;
							break;
						case 1:
							rawPrivFormat = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
							break;
						case 2:
							rawPrivFormat = CSSM_KEYBLOB_RAW_FORMAT_OPENSSH;
							break;
						default:
							rawPrivFormat = CSSM_KEYBLOB_RAW_FORMAT_PKCS8;
							break;
					}
				}
			}
			#if 0
			if(encAlg == CSSM_ALGID_RSA) {
				/* FIXME - another restriction - the MS bits of each block
				 * of plaintext must be zero, to make the numerical value of the
				 * block less than the modulus!
				 * Aug 7 1998: Now that we're using AI_PKCS_RSA{Public,Private}, the
				 * numerical value of the data no longer has to be less than
				 * the modulus.
				 */
				stagedEncr = stagedDecr = CSSM_TRUE;
			}
			#endif
			
			if(!quiet) {
				printf("  pubRef %d  privRef %d stgEncr %d  stgdDecr %d "
					"malPtext %d malCtext %d genSeed %d pubForm %s privForm %s\n",
					 (int)pubIsRef, (int)privIsRef, (int)stagedEncr, (int)stagedDecr, 
						(int)mallocPtext, (int)mallocCtext, (int)genSeed, 
						formStr(rawPubFormat), formStr(rawPrivFormat));
			}
			if(doTest(cspHand,
					encAlg,
					padding,
					&ptext,
					verbose,
					quiet,
					keySizeInBits,
					primeType,
					curveType,
					pubIsRef,
					rawPubFormat,
					privIsRef,
					rawPrivFormat,
					#if CSPDL_2ND_PUB_KEY_IS_RAW
					/* secondPubIsRaw */
					bareCsp ? CSSM_FALSE : CSSM_TRUE,
					#else
					CSSM_FALSE,
					#endif
					stagedEncr,
					stagedDecr,
					mallocCtext,
					mallocPtext,
					skipDecrypt,
					genSeed)) {
				rtn = 1;
				goto testDone;
			}
			if(loops && (loop == loops)) {
				break;
			}
		}	/* for loop */
	}		/* for alg */
testDone:
	CSSM_ModuleDetach(cspHand);
	if((rtn == 0) && !quiet) {
		printf("%s test complete\n", argv[0]);
	}
	return rtn;
}