cgVerifyThr.cpp   [plain text]


/* cgVerifyThr.cpp - simple version CertGroupVerify test */

#include "testParams.h"
#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>
#include <Security/oidsalg.h>

/* for memory leak debug only, with only one thread running */
#define DO_PAUSE			0

/*** start of code directly copied from ../cgVerify/cgVerify.cpp ***/
#define NUM_CERTS_MIN		4
#define KEYGEN_ALG_DEF		CSSM_ALGID_RSA
#define SIG_ALG_DEF			CSSM_ALGID_SHA1WithRSA
#define LOOPS_DEF			10
#define CG_KEY_SIZE_DEFAULT	CSP_RSA_KEY_SIZE_DEFAULT
#define SECONDS_TO_LIVE		(60 * 60 * 24)		/* certs are valid for this long */

#define CERT_IN_DB		0

/*
 * How we define the "expected result".
 */
typedef enum {
	ER_InvalidAnchor,		// root in certGroup, not found in AnchorCerts
	ER_RootInCertGroup,		// root in certGroup, copy in AnchorCerts
	ER_AnchorVerify,		// end of chain verified by an anchor
	ER_NoRoot				// no root, no anchor verify
} ExpectResult;

static int testError()
{
	char resp;

	printf("Attach via debugger for more info.\n");
	printf("a to abort, c to continue: ");
	resp = getchar();
	return (resp == 'a');
}

static int doTest(
	CSSM_TP_HANDLE	tpHand,
	CSSM_CL_HANDLE	clHand,
	CSSM_CSP_HANDLE	cspHand,
	CSSM_DL_DB_HANDLE dlDb,
	CSSM_DATA_PTR	certs,
	unsigned		numCerts,
	CSSM_BOOL		useDb,
	ExpectResult	expectResult,	
	CSSM_BOOL		verbose)
{
	unsigned			cgEnd;				// last cert in certGroupFrag
	unsigned			anchorStart;		// first cert in anchorGroup
	unsigned			anchorEnd;			// last cert in anchorGroup
	CSSM_CERTGROUP		certGroupFrag;		// INPUT to CertGroupVerify
	CSSM_CERTGROUP		anchorCerts;		// ditto 
	unsigned			die;				// random number
	CSSM_DL_DB_LIST		dbList;
	CSSM_DL_DB_LIST_PTR	dbListPtr;
	CSSM_DL_DB_HANDLE_PTR	dlDbPtr;
	CSSM_RETURN			expErr;				// expected rtn from GroupVfy()
	int					rtn = 0;
	const char			*expResStr;
	uint32				expEvidenceSize;	// expected evidenceSize
	unsigned			evidenceSize;		// actual evidence size
	CSSM_TP_VERIFY_CONTEXT_RESULT	vfyResult;
	CSSM_CERTGROUP_PTR 	outGrp = NULL;
	CSSM_RETURN			crtn;
	
	memset(&vfyResult, 0, sizeof(CSSM_TP_VERIFY_CONTEXT_RESULT));
	
	if(useDb) {
		dlDbPtr = &dlDb;
		dbList.NumHandles = 1;
		dbList.DLDBHandle = &dlDb;
		dbListPtr = &dbList;
	}
	else {
		/* not yet */
		dlDbPtr = NULL;
		dbListPtr = NULL;
	}
 	
 	/* the four test cases */
 	switch(expectResult) {
		case ER_InvalidAnchor:		
			/* root in certGroup, not found in AnchorCerts */
			cgEnd = numCerts - 1;		// certGroupFrag is the whole pile
			anchorStart = 0;			// anchors = all except root
			anchorEnd = numCerts - 2;
			expErr = CSSMERR_TP_INVALID_ANCHOR_CERT;
			expEvidenceSize = numCerts;
			expResStr = "InvalidAnchor (root in certGroup but not in anchors)";
			break;
			
		case ER_RootInCertGroup:		
			/* root in certGroup, copy in AnchorCerts */
			cgEnd = numCerts - 1;		// certGroupFrag = the whole pile
			anchorStart = 0;			// anchors = the whole pile
			anchorEnd = numCerts - 1;
			expErr = CSSM_OK;
			expEvidenceSize = numCerts;
			expResStr = "Good (root in certGroup AND in anchors)";
			break;

		case ER_AnchorVerify:	
			/* non-root end of chain verified by an anchor */
			/* break chain at random place other than start and end-2 */
			die = genRand(1, numCerts-3);
			cgEnd = die;				// certGroupFrag up to break point
			anchorStart = 0;			// anchors = all
			anchorEnd = numCerts - 1;
			expErr = CSSM_OK;
			/* size = # certs in certGroupFrag, plus one anchor */ 
			expEvidenceSize = die + 2;
			expResStr = "Good (root ONLY in anchors)";
			break;
			
		case ER_NoRoot:			
			/* no root, no anchor verify */
			/* break chain at random place other than start and end-1 */
			die = genRand(1, numCerts-2);
			cgEnd = die;				// certGroupFrag up to break point
			/* and skip one cert */
			anchorStart = die + 2;		// anchors = n+1...numCerts-2
										// may be empty if n == numCerts-2
			anchorEnd = numCerts - 1;
			expErr = CSSMERR_TP_NOT_TRUSTED;
			expEvidenceSize = die + 1;
			expResStr = "Not Trusted (no root, no anchor verify)";
			break;
 	}
 	
 	if(verbose) {
 		printf("   ...expectResult = %s\n", expResStr);
 	}
 	
 	/* cook up two cert groups */
 	if(verbose) {
 		printf("   ...building certGroupFrag from certs[0..%d]\n",
 			cgEnd);
 	}
  	if(tpMakeRandCertGroup(clHand,
	 		dbListPtr,
	 		certs,				// certGroupFrag always starts at 0
	 		cgEnd+1,			// # of certs
	 		&certGroupFrag,
	 		CSSM_TRUE,			// firstCertIsSubject
	 		verbose,
	 		CSSM_FALSE,			// allInDbs
	 		CSSM_FALSE)) {		// skipFirstDb
	 	printf("\nError in tpMakeRandCertGroup\n");
	 	return 1;		
	}
	
	if(anchorStart > anchorEnd) {
		/* legal for ER_NoRoot */
		if((expectResult != ER_NoRoot) || (anchorStart != numCerts)) {
			printf("Try again, pal.\n");
			exit(1);
		}
	}
 	if(verbose) {
 		printf("   ...building anchorCerts from certs[%d..%d]\n",
 			anchorStart, anchorEnd);
 	}
 	if(anchorEnd > (numCerts - 1)) {
 		printf("anchorEnd overflow\n");
 		exit(1);
 	} 
 	/* anchors do not go in DB */
  	if(tpMakeRandCertGroup(clHand,
	 		NULL,
	 		certs + anchorStart,
	 		anchorEnd - anchorStart + 1,			// # of certs
	 		&anchorCerts,
	 		CSSM_FALSE,			// firstCertIsSubject
	 		verbose,
	 		CSSM_FALSE,			// allInDbs
	 		CSSM_FALSE)) {		// skipFirstDb
	 	printf("\nError in tpMakeRandCertGroup\n");
	 	return 1;		
	}
	
	crtn = tpCertGroupVerify(
		tpHand,
		clHand,
		cspHand,
		dbListPtr,
		&CSSMOID_APPLE_X509_BASIC, 	// Policy
		NULL,						// fieldOpts
		NULL,						// actionData
		NULL,						// policyOpts
		&certGroupFrag,
		anchorCerts.GroupList.CertList,	// passed as CSSM_DATA_PTR, not CERTGROUP....
		anchorCerts.NumCerts, 
		CSSM_TP_STOP_ON_POLICY,
		NULL,					// cssmTimeStr
		&vfyResult);
		
	/* first verify format of result */
	if( (vfyResult.NumberOfEvidences != 3) ||
	    (vfyResult.Evidence == NULL) ||
		(vfyResult.Evidence[0].EvidenceForm != CSSM_EVIDENCE_FORM_APPLE_HEADER) ||
		(vfyResult.Evidence[1].EvidenceForm != CSSM_EVIDENCE_FORM_APPLE_CERTGROUP) ||
		(vfyResult.Evidence[2].EvidenceForm != CSSM_EVIDENCE_FORM_APPLE_CERT_INFO) ||
		(vfyResult.Evidence[0].Evidence == NULL) ||
		(vfyResult.Evidence[1].Evidence == NULL) ||
		(vfyResult.Evidence[2].Evidence == NULL)) {
		printf("***Malformed VerifyContextResult\n");
		return 1;
	}
	if((vfyResult.Evidence != NULL) && (vfyResult.Evidence[1].Evidence != NULL)) {
		outGrp = (CSSM_CERTGROUP_PTR)vfyResult.Evidence[1].Evidence;
		evidenceSize = outGrp->NumCerts;
	}
	else {
		/* in case no evidence returned */
		evidenceSize = 0;
	}

	/* %%% since non-root anchors are permitted as of <rdar://5685316>,
	 * the test assumptions have become invalid: these tests generate
	 * an anchors list which always includes the full chain, so by
	 * definition, the evidence chain will never be longer than 2,
	 * since the leaf's issuer is always an anchor.
	 * %%% need to revisit and rewrite these tests. -kcm
	 */
	if ((evidenceSize > 1) && (evidenceSize < expEvidenceSize) &&
		(crtn == CSSM_OK || crtn == CSSMERR_TP_CERTIFICATE_CANT_OPERATE)) {
		/* ignore, for now */
		expErr = crtn;
		expEvidenceSize = evidenceSize;
	}
	
	if((crtn != expErr) || 
	   (evidenceSize != expEvidenceSize)) {
		printf("\n***cgVerify: Error on tpCertGroupVerify expectResult %s\n", 
			expResStr);
		printf("   err  %s   expErr  %s\n", 
			cssmErrToStr(crtn), cssmErrToStr(expErr));
		printf("   evidenceSize  %d   expEvidenceSize  %u\n", 
			evidenceSize, (unsigned)expEvidenceSize);
		printf("   numCerts %d  cgEnd %d  anchorStart %d  anchorEnd %d\n",
			numCerts, cgEnd, anchorStart, anchorEnd);
		rtn = testError();
	}
	else {
		rtn = 0;
	}

	/* free resources */
	tpFreeCertGroup(&certGroupFrag, 
		CSSM_FALSE,			// caller malloc'd the actual certs 
		CSSM_FALSE);		// struct is on stack
	tpFreeCertGroup(&anchorCerts, 
		CSSM_FALSE,			// caller malloc'd the actual certs 
		CSSM_FALSE);		// struct is on stack
	freeVfyResult(&vfyResult);
	if(useDb) {
		clDeleteAllCerts(dlDb);
	}
	return rtn;
}

/*** end of code directly copied from ../cgVerify/cgVerify.cpp ***/

/*
 * For debug only - ensure that the given array of public keys are all unique 
 * Only saw this when using FEE RNG (i.e., no SecurityServer running).
 */
int comparePubKeys(
	unsigned numKeys,
	const CSSM_KEY *pubKeys)	
{
	unsigned i,j;
	
	for(i=0; i<numKeys-1; i++) {
		for(j=i+1; j<numKeys; j++) {
			if(appCompareCssmData(&pubKeys[i].KeyData, &pubKeys[j].KeyData)) {
				printf("***HEY! DUPLICATE PUBLIC KEYS in cgVerify!\n");
				return testError();
			}
		}
	}
	return 0;
}

 
/*
 * key pairs - created in cgConstructInit, stored in testParams->perThread
 */
typedef struct {
	CSSM_KEY_PTR pubKeys;
	CSSM_KEY_PTR privKeys;
	unsigned numKeys;
	char *notBeforeStr;		// to use thread-safe tpGenCerts()
	char *notAfterStr;		// to use thread-safe tpGenCerts()
}	TT_KeyPairs;


int cgVerifyInit(TestParams *testParams)
{
	unsigned			numKeys = NUM_CERTS_MIN + testParams->threadNum;
	TT_KeyPairs			*keyPairs;
	
	if(testParams->verbose) {
		printf("cgVerify thread %d: generating keys...\n", 
			testParams->threadNum);
	}
	keyPairs = (TT_KeyPairs *)CSSM_MALLOC(sizeof(TT_KeyPairs));
	keyPairs->numKeys = numKeys;
	keyPairs->pubKeys  = (CSSM_KEY_PTR)CSSM_CALLOC(numKeys, sizeof(CSSM_KEY));
	keyPairs->privKeys = (CSSM_KEY_PTR)CSSM_CALLOC(numKeys, sizeof(CSSM_KEY));
	CSSM_DL_DB_HANDLE	dlDbHand = {0, 0};
	if(tpGenKeys(testParams->cspHand,
			dlDbHand,
			numKeys,
			KEYGEN_ALG_DEF, 
			CG_KEY_SIZE_DEFAULT,
			"cgVerify",			// keyLabelBase
			keyPairs->pubKeys,
			keyPairs->privKeys)) {
		goto abort;
	}
	if(comparePubKeys(numKeys, keyPairs->pubKeys)) {
		return 1;
	}
	keyPairs->notBeforeStr = genTimeAtNowPlus(0);
	keyPairs->notAfterStr = genTimeAtNowPlus(SECONDS_TO_LIVE);
	
	testParams->perThread = keyPairs;
	return 0;
	
abort:
	printf("Error generating keys; aborting\n");
	CSSM_FREE(keyPairs->pubKeys);
	CSSM_FREE(keyPairs->privKeys);
	CSSM_FREE(keyPairs);
	return 1;
}

int cgVerify(TestParams *testParams)
{
	unsigned 			loopNum;
	int					status = -1;		// exit status
	unsigned			dex;
	
	TT_KeyPairs			*keyPairs = (TT_KeyPairs *)testParams->perThread;

	/* all three of these are arrays with numCert elements */
	CSSM_KEY_PTR		pubKeys = keyPairs->pubKeys;
	CSSM_KEY_PTR		privKeys = keyPairs->privKeys;
	CSSM_DATA_PTR		certs = NULL;
	
	unsigned			numCerts = keyPairs->numKeys;
	uint32				sigAlg = SIG_ALG_DEF;
	ExpectResult		expectResult;
	#if	CERT_IN_DB
	CSSM_BOOL			useDb = CSSM_TRUE;
	#else
	CSSM_BOOL			useDb = CSSM_FALSE;
	#endif
	CSSM_DL_DB_HANDLE	dlDbHand = {0, 0};
	
	/* malloc empty certs */
	certs = (CSSM_DATA_PTR)CSSM_CALLOC(numCerts, sizeof(CSSM_DATA));
	if(certs == NULL) {
		printf("not enough memory for %u certs.\n", numCerts);
		goto abort;
	}
	memset(certs, 0, numCerts * sizeof(CSSM_DATA));

	for(loopNum=0; loopNum<testParams->numLoops; loopNum++) {
		/* generate certs */
		if(testParams->verbose) {
			printf("generating certs...\n");
		}
		else if(!testParams->quiet) {
			printChar(testParams->progressChar);
		}
		if(tpGenCerts(testParams->cspHand,
				testParams->clHand,
				numCerts,
				sigAlg, 
				"cgConstruct",	// nameBase
				pubKeys,
				privKeys,
				certs,
				keyPairs->notBeforeStr,
				keyPairs->notAfterStr)) {
			goto abort;
		}

		/* cycle thru test scenarios */
		switch(loopNum % 4) {
			case 0:
				expectResult = ER_InvalidAnchor;
				break;
			case 1:
				expectResult = ER_RootInCertGroup;
				break;
			case 2:
				expectResult = ER_AnchorVerify;
				break;
			case 3:
				expectResult = ER_NoRoot;
				break;
		}
		status = doTest(testParams->tpHand,
				testParams->clHand,
				testParams->cspHand,
				dlDbHand,
				certs,
				numCerts,
				useDb,
				expectResult,
				testParams->verbose);
		if(status) {
			break;
		}
		/* free certs */
		for(dex=0; dex<numCerts; dex++) {
			CSSM_FREE(certs[dex].Data);
		}
		memset(certs, 0, numCerts * sizeof(CSSM_DATA));
		#if DO_PAUSE
		fpurge(stdin);
		printf("Hit CR to proceed: ");
		getchar();
		#endif
	}
abort:
	/* free resources */
	for(dex=0; dex<numCerts; dex++) {
		if(certs[dex].Data) {
			CSSM_FREE(certs[dex].Data);
		}
	}
	CSSM_FREE(keyPairs->pubKeys);
	CSSM_FREE(keyPairs->privKeys);
	CSSM_FREE(keyPairs);
	return status;
}