cgConstruct.cpp   [plain text]


/*
 * cgConstruct.c - basic test of TP's CertGroupConstruct
 *
 * cook up array of n key pairs;
 * cook up cert chain to go with them;
 * main test loop {
 * 	pick a random spot to break the cert chain - half the time use the
 *		whole chain, half the time break it;
 * 	cook up CertGroup frag big enough for the unbroken part of the chain;
 * 	put cert[0] in certGroup[0];
 * 	for each cert from cert[1] to break point {
 *		roll the  dice and put the cert in either a random place
 *			in certGroup or in DB;
 * 	}
 *  resultGroup = certGroupConstruct();
 *  vfy result Grp is identical to unbroken part of chain;
 *  delete certs from DB;
 * }
 */
 
#include <Security/cssm.h>
#include <utilLib/common.h>
#include <utilLib/cspwrap.h>
#include <clAppUtils/clutils.h>
#include <clAppUtils/tpUtils.h>
#include <clAppUtils/timeStr.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

#define NUM_CERTS_DEF		10
#define NUM_DBS_DEF			3
#define KEYGEN_ALG_DEF		CSSM_ALGID_RSA
#define SIG_ALG_DEF			CSSM_ALGID_SHA1WithRSA 
#define LOOPS_DEF			10
#define DB_NAME_BASE		"cgConstruct"	/* default */
#define SECONDS_TO_LIVE		(60 * 60 * 24)	/* certs are valid for this long */

/* Read-only access not supported */ 
#define PUBLIC_READ_ENABLE	0

static void usage(char **argv)
{
	printf("Usage: %s [options]\n", argv[0]);
	printf("   Options:\n");
	printf("   n=numCerts; default = %d\n", NUM_CERTS_DEF);
	printf("   l=loops; default=%d; 0=forever\n", LOOPS_DEF);
	printf("   a=alg (f=FEE/MD5, f=FEE/SHA1, e=FEE/ECDSA, r=RSA, E=ANSI ECDSA; default = RSA\n");
	printf("   K=keySizeInBits\n");
	#if	TP_DB_ENABLE
	printf("   d=numDBs, default = %d\n", NUM_DBS_DEF);
	printf("   A(ll certs to DBs)\n");
	printf("   k (skip first DB when storing)\n");
	#if PUBLIC_READ_ENABLE
	printf("   p(ublic access open on read)\n");
	#endif
	#endif
	printf("   f=fileNameBase (default = %s)\n", DB_NAME_BASE);
	printf("   P(ause on each loop)\n");
	printf("   v(erbose)\n");
	printf("   q(uiet)\n");
	printf("   h(elp)\n");
	exit(1);
}

#if TP_DB_ENABLE
static int doOpenDbs(
	CSSM_DL_HANDLE			dlHand,
	const char 				*dbNameBase,
	CSSM_DL_DB_HANDLE_PTR 	dlDbPtr,
	unsigned 				numDbs,
	CSSM_BOOL 				publicReadOnly,		// ignored if !PUBLIC_READ_ENABLE
	CSSM_BOOL				quiet)
{
	unsigned i;
	char dbName[20];
	CSSM_BOOL doCreate = (publicReadOnly ? CSSM_FALSE : CSSM_TRUE);
	
	for(i=0; i<numDbs; i++) {
		dlDbPtr[i].DLHandle = dlHand;
		sprintf(dbName, "%s%d", dbNameBase, i);
		CSSM_RETURN crtn = tpKcOpen(dlHand, dbName, 
			dbName,			// file name as pwd
			doCreate,
			&dlDbPtr[i].DBHandle);
		if(crtn) {
			printf("Can't create %d DBs\n", numDbs);
			return testError(quiet);
		}
	}
	return 0;
}
#endif

static int doTest(
	CSSM_TP_HANDLE		tpHand,
	CSSM_CL_HANDLE		clHand,
	CSSM_CSP_HANDLE		cspHand,
	CSSM_DL_HANDLE		dlHand,
	CSSM_DL_DB_LIST_PTR	dbList,
	CSSM_DATA_PTR		certs,
	unsigned			numCerts,
	CSSM_BOOL			verbose,
	CSSM_BOOL			allInDbs,
	CSSM_BOOL			skipFirstDb,
	CSSM_BOOL			publicRead,		// close/open with public access
	const char			*fileBaseName,
	CSSM_BOOL			quiet)
{
	unsigned				certsToUse;		// # of certs we actually use
	CSSM_CERTGROUP			certGroupFrag;	// INPUT to CertGroupConstruct
	CSSM_CERTGROUP_PTR		resultGroup;	// OUTPUT from "
	unsigned				certDex;
	int						rtn = 0;
	CSSM_RETURN				crtn;
	
	#if	TP_DB_ENABLE
	if(publicRead && (dbList != NULL)) {
		/* DBs are closed on entry, open r/w */
		if(doOpenDbs(dlHand,
				fileBaseName,
				dbList->DLDBHandle,
				dbList->NumHandles,
				CSSM_FALSE,
				quiet)) {		// publicReadOnly: this is create/write
			return 1;
		}	
	}
	/* else DBs are already open and stay that way */
	#endif
	
	/*
	 * Pick a random spot to break the cert chain - half the time use the
	 * whole chain, half the time break it.
	 */
	certsToUse = genRand(1, numCerts * 2);
	if(certsToUse > numCerts) {
		/* use the whole chain */
		certsToUse = numCerts;
	}
 	if(verbose) {
 		printf("   ...numCerts %d  certsToUse %d\n", numCerts, certsToUse);   
 	}

 	if(tpMakeRandCertGroup(clHand,
			#if		TP_DB_ENABLE
	 		dbList,
			#else
			NULL,
			#endif
	 		certs,
	 		certsToUse,
	 		&certGroupFrag,
	 		CSSM_TRUE,			// firstCertIsSubject
	 		verbose,
	 		allInDbs,
	 		skipFirstDb)) {
	 	printf("Error in tpMakeRandCertGroup\n");
	 	return testError(quiet);
	}
 		
	if(certGroupFrag.NumCerts > certsToUse) {
		printf("Error NOMAD sterlize\n");
		exit(1);
	}
	
	#if	TP_DB_ENABLE
	if(publicRead) {
		/* close existing DBs and open again read-only */
		
		unsigned i;
		CSSM_RETURN crtn;
		
		if(verbose) {
			printf("   ...closing DBs\n");
		}
		for(i=0; i<dbList->NumHandles; i++) {
			crtn = CSSM_DL_DbClose(dbList->DLDBHandle[i]);
			if(crtn) {
				printError("CSSM_DL_DbClose", crtn);
				if(testError(quiet)) {
					return 1;
				}
			}
		}
		if(verbose) {
			printf("   ...opening DBs read-only\n");
		}
		if(doOpenDbs(dlHand,
				fileBaseName,
				dbList->DLDBHandle,
				dbList->NumHandles,
				CSSM_TRUE, 		// publicReadOnly: this is read only
				quiet)) {
			return 1;
		}
	}
	#endif
	
	/* 
	 * Okay, some of the certs we were given are in the DB, some are in 
	 * random places in certGroupFrag, some are nowhere (if certsToUse is 
	 * less than numCerts). Have the TP construct us an ordered verified
	 * group.
	 */
	crtn = CSSM_TP_CertGroupConstruct(
		tpHand,
		clHand,
		cspHand,
		dbList,
		NULL,			// ConstructParams
		&certGroupFrag,
		&resultGroup);
	if(crtn) {
		printError("CSSM_TP_CertGroupConstruct", crtn);
		return testError(quiet);
	}
	
 	/* vfy resultGroup is identical to unbroken part of chain */
 	if(verbose) {
 		printf("   ...CSSM_TP_CertGroupConstruct returned %u certs\n",
 			(unsigned)resultGroup->NumCerts);
 	}
	if(resultGroup->NumCerts != certsToUse) {
		printf("***resultGroup->NumCerts was %u, expected %u\n",
			(unsigned)resultGroup->NumCerts, (unsigned)certsToUse);
		rtn = testError(quiet);
		goto abort;
	}
	for(certDex=0; certDex<certsToUse; certDex++) {
		if(!appCompareCssmData(&certs[certDex], 
					&resultGroup->GroupList.CertList[certDex])) {
			printf("***certs[%d] miscompare\n", certDex);
			rtn = testError(quiet);
			goto abort;
		}
	}
abort:
	/* free resurces */
	tpFreeCertGroup(&certGroupFrag, 
		CSSM_FALSE,			// caller malloc'd the actual certs 
		CSSM_FALSE);		// struct is on stack
	tpFreeCertGroup(resultGroup, 
		CSSM_TRUE,			// mallocd by TP 
		CSSM_TRUE);			// ditto 
	#if	TP_DB_ENABLE
	if(dbList != NULL) {
		unsigned i;
		CSSM_RETURN crtn;
		
		if(verbose) {
			printf("   ...deleting all certs from DBs\n");
		}	
		for(i=0; i<dbList->NumHandles; i++) {		
			clDeleteAllCerts(dbList->DLDBHandle[i]);
		}
		if(publicRead) {
			if(verbose) {
				printf("   ...closing DBs\n");
			}
			for(i=0; i<dbList->NumHandles; i++) {
				crtn = CSSM_DL_DbClose(dbList->DLDBHandle[i]);
				if(crtn) {
					printError("CSSM_DL_DbClose", crtn);
					if(testError(quiet)) {
						return 1;
					}
				}
			}
		}
	}
	#endif
	return rtn;
}

int main(int argc, char **argv)
{
	int					arg;
	char				*argp;
	unsigned			loop;
	CSSM_TP_HANDLE		tpHand = 0;
	CSSM_CL_HANDLE		clHand = 0;
	CSSM_CSP_HANDLE		cspHand = 0;
	CSSM_DL_DB_LIST		dbList = {0, NULL};	/* for storing certs */
	CSSM_DL_DB_LIST_PTR	dbListPtr;			/* pts to dbList or NULL */
	unsigned			i;
	char				*notAfterStr;
	char 				*notBeforeStr;
	CSSM_DL_HANDLE 		dlHand;
	
	/* all three of these are arrays with numCert elements */
	CSSM_KEY_PTR		pubKeys = NULL;
	CSSM_KEY_PTR		privKeys = NULL;
	CSSM_DATA_PTR		certs = NULL;
	
	/* Keys do NOT go in the cert DB */
	CSSM_DL_DB_HANDLE 	keyDb = {0, 0};

	/*
	 * User-spec'd params
	 */
	unsigned	loops = LOOPS_DEF;
	CSSM_BOOL	verbose = CSSM_FALSE;
	CSSM_BOOL	quiet = CSSM_FALSE;
	unsigned	numCerts = NUM_CERTS_DEF;
	uint32		keyGenAlg = KEYGEN_ALG_DEF;
	uint32		sigAlg = SIG_ALG_DEF;
	unsigned	numDBs = NUM_DBS_DEF;
	CSSM_BOOL	allInDbs = CSSM_FALSE;
	CSSM_BOOL	skipFirstDb = CSSM_FALSE;
	CSSM_BOOL	publicRead = CSSM_FALSE;
	CSSM_BOOL	doPause = CSSM_FALSE;
	uint32		keySizeInBits = CSP_KEY_SIZE_DEFAULT;
	const char	*fileBaseName = DB_NAME_BASE;
	
	for(arg=1; arg<argc; arg++) {
		argp = argv[arg];
		switch(argp[0]) {
		    case 'l':
				loops = atoi(&argp[2]);
				break;
		    case 'n':
				numCerts = atoi(&argp[2]);
				break;
		    case 'K':
				keySizeInBits = atoi(&argp[2]);
				break;
		    case 'v':
		    	verbose = CSSM_TRUE;
				break;
		    case 'q':
		    	quiet = CSSM_TRUE;
				break;
			case 'a':
				switch(argp[2]) {
					case 'f':
						keyGenAlg = CSSM_ALGID_FEE;
						sigAlg = CSSM_ALGID_FEE_MD5;
						break;
					case 'F':
						keyGenAlg = CSSM_ALGID_FEE;
						sigAlg = CSSM_ALGID_FEE_SHA1;
						break;
					case 'e':
						keyGenAlg = CSSM_ALGID_FEE;
						sigAlg = CSSM_ALGID_SHA1WithECDSA;
						break;
					case 'E':
						keyGenAlg = CSSM_ALGID_ECDSA;
						sigAlg = CSSM_ALGID_SHA1WithECDSA;
						break;
					case 'r':
						break;
					default:
						usage(argv);
				}
				break;
			case 'd':
				numDBs = atoi(&argp[2]);
				break;
			case 'A':
				allInDbs = CSSM_TRUE;
				break;
			case 'k':
				skipFirstDb = CSSM_TRUE;
				break;
			#if PUBLIC_READ_ENABLE
			case 'p':
				publicRead = CSSM_TRUE;
				break;
			#endif
			case 'f':
				fileBaseName = &argp[2];
				break;
			case 'P':
				doPause = CSSM_TRUE;
				break;
		    case 'h':
		    default:
				usage(argv);
		}
	}

	/* attach to all the modules we need */
	cspHand = cspStartup();
	if(cspHand == 0) {
		exit(1);
	}
	if(cspHand == 0) {
		exit(1);
	}
	clHand = clStartup();
	if(clHand == 0) {
		goto abort;
	}
	tpHand = tpStartup();
	if(tpHand == 0) {
		goto abort;
	}
	
	/* malloc empty keys and certs */
	pubKeys  = (CSSM_KEY_PTR)CSSM_CALLOC(numCerts, sizeof(CSSM_KEY));
	privKeys = (CSSM_KEY_PTR)CSSM_CALLOC(numCerts, sizeof(CSSM_KEY));
	certs    = (CSSM_DATA_PTR)CSSM_CALLOC(numCerts, sizeof(CSSM_DATA));
	if((pubKeys == NULL) || (privKeys == NULL) || (certs == NULL)) {
		printf("not enough memory for %u keys pairs and certs.\n",
			numCerts);
		goto abort;
	}
	
	printf("Starting cgConstruct; args: ");
	for(i=1; i<(unsigned)argc; i++) {
		printf("%s ", argv[i]);
	}
	printf("\n");
	
	/* generate key pairs */
	if(!quiet) {
		printf("generating keys...\n");
	}
	if(tpGenKeys(cspHand,
			keyDb,
			numCerts,
			keyGenAlg, 
			keySizeInBits,
			"cgConstruct",		// keyLabelBase
			pubKeys,
			privKeys)) {
		goto abort;
	}
	notBeforeStr = genTimeAtNowPlus(0);
	notAfterStr = genTimeAtNowPlus(SECONDS_TO_LIVE);

	#if	TP_DB_ENABLE
	/* create numDbs new DBs */
	if(numDBs != 0) {
		dlHand = dlStartup();
		if(dlHand == 0) {
			exit(1);
		}
		dbList.NumHandles = numDBs;
		dbList.DLDBHandle = 
			(CSSM_DL_DB_HANDLE_PTR)CSSM_CALLOC(numDBs, sizeof(CSSM_DL_DB_HANDLE));
		if(!publicRead) {
			/*
			 * In this case, this is the only time we open these DBs - they
			 * stay open for the duration of the test
			 */
			if(verbose) {
				printf("   ...opening DBs read/write\n");
			}
			if(doOpenDbs(dlHand,
					fileBaseName,
					dbList.DLDBHandle,
					numDBs,
					CSSM_FALSE,		// publicReadOnly: this is create/write
					quiet)) {
				goto abort;
			}
		}
		dbListPtr = &dbList;
	}		
	else {
		/* it's required anyway... */
		dbList.NumHandles = 0;
		dbList.DLDBHandle = NULL;
		dbListPtr = &dbList;
	}
	#else
	/* it's required anyway... */
	dbList.NumHandles = 0;
	dbList.DLDBHandle = NULL;
	dbListPtr = &dbList;
	#endif
	
	for(loop=1; ; loop++) {
		if(!quiet) {
			printf("...loop %d\n", loop);
		}
		if(tpGenCerts(cspHand,
				clHand,
				numCerts,
				sigAlg, 
				"cgConstruct",	// nameBase
				pubKeys,
				privKeys,
				certs,
				notBeforeStr,
				notAfterStr)) {
			break;
		}
		if(doTest(tpHand,
				clHand,
				cspHand,
				dlHand,
				dbListPtr,
				certs,
				numCerts,
				verbose,
				allInDbs,
				skipFirstDb,
				publicRead,
				fileBaseName,
				quiet)) {
			break;			
		}
		for(i=0; i<numCerts; i++) {
			appFreeCssmData(&certs[i], CSSM_FALSE);
		}
		memset(certs, 0, numCerts * sizeof(CSSM_DATA));
		
		if(loops && (loop == loops)) {
			break;
		}
		if(doPause) {
			printf("Hit CR to continue: ");
			fpurge(stdin);
			getchar();
		}
	}

abort:
	/* free keys and certs */
	if(privKeys != NULL) {
		for(i=0; i<numCerts; i++) {
			if(privKeys[i].KeyData.Data != NULL) {
				cspFreeKey(cspHand, &privKeys[i]);
			}
		}
		CSSM_FREE(privKeys);
	}
	if(pubKeys != NULL) {
		for(i=0; i<numCerts; i++) {
			if(pubKeys[i].KeyData.Data != NULL) {
				cspFreeKey(cspHand, &pubKeys[i]);
			}
		}
		CSSM_FREE(pubKeys);
	}
	if(certs != NULL) {
		for(i=0; i<numCerts; i++) {
			appFreeCssmData(&certs[i], CSSM_FALSE);
		}
		CSSM_FREE(certs);
	}
	if(dbList.DLDBHandle != NULL) {
		/* don't have to close, detach should do that */
		CSSM_FREE(dbList.DLDBHandle);
	}
	if(cspHand != 0) {
		CSSM_ModuleDetach(cspHand);
	}
	if(clHand != 0) {
		CSSM_ModuleDetach(clHand);
	}
	if(tpHand != 0) {
		CSSM_ModuleDetach(tpHand);
	}
	
	if(!quiet) {
		printf("%s test complete\n", argv[0]);
	}
	return 0;
}