/*
 * Verify proper asymmetric key wrap !EXTRACTABLE handling.
 *
 * Gen unwrapped ref key, !CSSM_KEYATTR_EXTRACTABLE;
 * Gen wrapping key - a simple DES key;
 * vfy you can't wrap unwrappedKey with wrappingKey;
 */
int asymBadWrapTest(
	CSSM_CSP_HANDLE	cspHand,
	CSSM_ALGORITHMS	alg,
	const char		*keyAlgStr,
	uint32			keySizeInBits,
	CSSM_BOOL 		quiet)
{
	CSSM_KEY pubKey;
	CSSM_KEY privKey;
	
	if(!quiet) {
		printf("      ...testing access to !EXTRACTABLE key bits via CUSTOM wrap\n");
	}
	
	/* gen ref key, CSSM_KEYATTR_SENSITIVE, !EXTRACTABLE */
	if(genKeyPair(cspHand, alg, keyAlgStr, keySizeInBits, 
			&pubKey, CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE, 
				CSSM_KEYUSE_ANY, 
			&privKey, CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE, 
				CSSM_KEYUSE_ANY, 
			CSSM_OK, quiet, CSSM_FALSE, "RETURN_REF | SENSITIVE")) {
		return 1;
	}	
	if(badWrapTest(cspHand, 
		&privKey, 
		CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM,
		quiet, keyAlgStr, 
		"!EXTRACTABLE wrap")) {
		return 1;
	}
	cspFreeKey(cspHand, &privKey);
	cspFreeKey(cspHand, &pubKey);
	return 0;
}
/*
 * Generate public and private ref keys, munge various fields in the header, 
 * verify that attempts to use the munged key result in 
 * CSSMERR_CSP_INVALID_KEY_REFERENCE.
 */
int asymHeaderTest(
	CSSM_CSP_HANDLE	cspHand,
	CSSM_ALGORITHMS	keyAlg,
	const char		*keyAlgStr,
	CSSM_ALGORITHMS	encrAlg,
	CSSM_ALGORITHMS	signAlg,
	uint32			keySizeInBits,
	CSSM_BOOL 		quiet)
{
	CSSM_KEY privKey;
	CSSM_KEY pubKey;

	if(!quiet) {
		printf("      ...testing munged ref key header\n");
	}
	if(genKeyPair(cspHand, keyAlg, keyAlgStr, keySizeInBits, 
			&pubKey, CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE, 
				CSSM_KEYUSE_ANY, 
			&privKey, CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE, 
				CSSM_KEYUSE_ANY, 
			CSSM_OK, quiet, CSSM_FALSE, "RETURN_REF")) {
		return 1;
	}	
	if(badHdrTest(cspHand, &privKey, quiet, keyAlgStr)) {
		return 1;
	}
	if(badHdrTest(cspHand, &pubKey, quiet, keyAlgStr)) {
		return 1;
	}
	cspFreeKey(cspHand, &privKey);
	cspFreeKey(cspHand, &pubKey);
	return 0;
}
/* 
 * Verify asymmetric key null wrap behavior:
 *	gen ref key, CSSM_KEYATTR_SENSITIVE, vfy you can't do null wrap;
 *	gen ref key, !CSSM_KEYATTR_EXTRACTABLE, vfy you can't do null wrap;
 */
static int asymNullWrapTest(
	CSSM_CSP_HANDLE	cspHand,
	CSSM_ALGORITHMS	alg,
	const char		*keyAlgStr,
	uint32			keySizeInBits,
	CSSM_BOOL 		quiet)
{
	CSSM_KEY pubKey;
	CSSM_KEY privKey;
	
	if(!quiet) {
		printf("      ...testing access to inaccessible key bits via NULL wrap\n");
	}
	/* gen priv ref key, CSSM_KEYATTR_SENSITIVE, vfy you can't do null wrap */
	if(genKeyPair(cspHand, alg, keyAlgStr, keySizeInBits, 
			&pubKey, CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE, 
				CSSM_KEYUSE_ANY, 
			&privKey, CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE, 
				CSSM_KEYUSE_ANY, 
			CSSM_OK, quiet, CSSM_FALSE, "RETURN_REF | SENSITIVE")) {
		return 1;
	}	
	if(nullWrapTest(cspHand, &privKey, quiet, CSSMERR_CSP_INVALID_KEYATTR_MASK,
			keyAlgStr, "SENSITIVE")) {
		return 1;
	}
	cspFreeKey(cspHand, &privKey);
	cspFreeKey(cspHand, &pubKey);
	
	/* gen priv ref key, !CSSM_KEYATTR_EXTRACTABLE, vfy you can't do null wrap */
	if(genKeyPair(cspHand, alg, keyAlgStr, keySizeInBits, 
			&pubKey, CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE, 
				CSSM_KEYUSE_ANY, 
			&privKey, CSSM_KEYATTR_RETURN_REF /* | !EXTRACTABLE */, CSSM_KEYUSE_ANY, 
			CSSM_OK, quiet, CSSM_FALSE, "RETURN_REF | !EXTRACTABLE")) {
		return 1;
	}	
	if(nullWrapTest(cspHand, &privKey, quiet, CSSMERR_CSP_INVALID_KEYATTR_MASK,
			keyAlgStr, "!EXTRACTABLE")) {
		return 1;
	}
	cspFreeKey(cspHand, &privKey);
	cspFreeKey(cspHand, &pubKey);
	return 0;
}
BOOL genKeyPair( PROV_CTX* pProvCtx, KEY_INFO* pKey ){
	if ( pKey->algId != CALG_GOST_SIGN ){
		SetLastError( NTE_BAD_ALGID );
		return FALSE;
	}
	if ( pKey->hKeyInformation == NULL ){
		SetLastError( NTE_BAD_KEY_STATE );
		return FALSE;
	}

	CONTAINER_IRZ *pContainerIRZ = (CONTAINER_IRZ*) pProvCtx->pContainer->hServiceInformation;
	KEY_SIGN_INFO *pKeyInfo = (KEY_SIGN_INFO*) pKey->hKeyInformation;


	// Generate key pair in proper.
	if ( pKeyInfo->pPrKey != NULL || pKeyInfo->pPubKey != NULL ){
		DEBUG( 1, "While generating key pair: Key already exists." );
	}
	if ( !genKeyPair( &pKeyInfo->pPubKey, &(pKeyInfo->pPrKey), &pKeyInfo->params, &pContainerIRZ->rand ) ){
		SetLastError( NTE_FAIL );
		return FALSE;
	}
	
	// Write the key to the token.
	LPSTR szPrKey = new CHAR[PRIVATEKEY_CHAR_LEN+1];
	LPSTR szPubKey = new CHAR[PUBLICKEY_CHAR_LEN+1];
	
	if ( !privateKeyToString( pKeyInfo, szPrKey ) ){
		/* \todo release resources.*/
		SetLastError( NTE_FAIL );
		return FALSE;
	}
	
	if ( !pubKeyToString( pKeyInfo, szPubKey ) ){
		/* \todo release resources.*/
		SetLastError( NTE_FAIL );
		return FALSE;
	}

	WritePrivateProfileStringA( 
		"PRIVATEKEY",
		"PRIVATEKEY",
		szPrKey,
		pContainerIRZ->szToken);

	WritePrivateProfileStringA( 
		"PUBLICKEY",
		"PUBLICKEY",
		szPubKey,
		pContainerIRZ->szToken);

	delete[] szPrKey;
	delete[] szPubKey;

	return TRUE;

}
/*
 * Generate key pair, specified pub key attr and expected result, standard
 * "good" priv key attr. Used by asymAttrTest().
 */
static int pubKeyAttrTest(
	CSSM_CSP_HANDLE		cspHand,
	CSSM_ALGORITHMS		alg,
	const char			*keyAlgStr,
	uint32				keySizeInBits,
	CSSM_KEYATTR_FLAGS 	pubKeyAttr,
	CSSM_RETURN			expectRtn,
	CSSM_BOOL 			quiet,
	const char			*testStr)
{
	CSSM_KEY pubKey;
	CSSM_KEY privKey;
	
	return genKeyPair(cspHand, alg, keyAlgStr, keySizeInBits, 
			&pubKey, pubKeyAttr, CSSM_KEYUSE_ANY, 
			&privKey, CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE, CSSM_KEYUSE_ANY, 
			expectRtn, quiet, CSSM_TRUE, testStr);
}
int main(int argc, char **argv)
{
	bool verbose = false;
	
	int arg;
	while ((arg = getopt(argc, argv, "vh")) != -1) {
		switch (arg) {
			case 'v':
				verbose = true;
				break;
			case 'h':
				usage(argv);
		}
	}
	if(optind != argc) {
		usage(argv);
	}
	
	printNoDialog();
	
	/* initial setup */
	verboseDisp(verbose, "deleting keychain");
	unlink(KEYCHAIN_NAME);
	
	verboseDisp(verbose, "creating keychain");
	SecKeychainRef kcRef = NULL;
	OSStatus ortn = SecKeychainCreate(KEYCHAIN_NAME, 
		strlen(KEYCHAIN_PWD), KEYCHAIN_PWD,
		false, NULL, &kcRef);
	if(ortn) {
		cssmPerror("SecKeychainCreate", ortn);
		exit(1);
	}

	/* 
	 * 1. Generate key pair with cleartext public key.
	 *    Ensure we can use the public key when keychain is locked with no
	 *    user interaction.  
	 */
	 
	/* generate key pair, cleartext public key */
	verboseDisp(verbose, "creating key pair, cleartext public key");
	SecKeyRef pubKeyRef = NULL;
	SecKeyRef privKeyRef = NULL;
	if(genKeyPair(false, kcRef, &pubKeyRef, &privKeyRef)) {
		exit(1);
	}
	
	/* Use generated cleartext public key with locked keychain */
	verboseDisp(verbose, "locking keychain, exporting public key");
	SecKeychainLock(kcRef);
	CFDataRef exportData = NULL;
	ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData);
	if(ortn) {
		cssmPerror("SecKeychainCreate", ortn);
		exit(1);
	}
	CFRelease(exportData);
	
	verboseDisp(verbose, "locking keychain, encrypting with public key");
	SecKeychainLock(kcRef);
	if(pubKeyEncrypt(pubKeyRef)) {
		exit(1);
	}

	/* reset */
	verboseDisp(verbose, "deleting keys");
	ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef);
	if(ortn) {
		cssmPerror("SecKeychainItemDelete", ortn);
		exit(1);
	}
	ortn = SecKeychainItemDelete((SecKeychainItemRef)privKeyRef);
	if(ortn) {
		cssmPerror("SecKeychainItemDelete", ortn);
		exit(1);
	}
	CFRelease(pubKeyRef);
	CFRelease(privKeyRef);

	/* 
	 * 2. Generate key pair with encrypted public key.
	 *    Ensure that user interaction is required when we use the public key 
	 *    when keychain is locked.
	 */

	verboseDisp(verbose, "programmatically unlocking keychain");
	ortn = SecKeychainUnlock(kcRef, strlen(KEYCHAIN_PWD), KEYCHAIN_PWD, TRUE);
	if(ortn) {
		cssmPerror("SecKeychainItemDelete", ortn);
		exit(1);
	}
	
	/* generate key pair, encrypted public key */
	verboseDisp(verbose, "creating key pair, encrypted public key");
	if(genKeyPair(true, kcRef, &pubKeyRef, &privKeyRef)) {
		exit(1);
	}

	/* Use generated encrypted public key with locked keychain */
	verboseDisp(verbose, "locking keychain, exporting public key");
	SecKeychainLock(kcRef);
	printExpectDialog();
	ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData);
	if(ortn) {
		cssmPerror("SecKeychainCreate", ortn);
		exit(1);
	}
	/* we'll use that exported blob later to test import */
	if(!didGetDialog()) {
		exit(1);
	}
	
	verboseDisp(verbose, "locking keychain, encrypting with public key");
	SecKeychainLock(kcRef);
	printExpectDialog();
	if(pubKeyEncrypt(pubKeyRef)) {
		exit(1);
	}
	if(!didGetDialog()) {
		exit(1);
	}

	/* reset */
	printNoDialog();
	verboseDisp(verbose, "locking keychain");
	SecKeychainLock(kcRef);
	verboseDisp(verbose, "deleting keys");
	ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef);
	if(ortn) {
		cssmPerror("SecKeychainItemDelete", ortn);
		exit(1);
	}
	ortn = SecKeychainItemDelete((SecKeychainItemRef)privKeyRef);
	if(ortn) {
		cssmPerror("SecKeychainItemDelete", ortn);
		exit(1);
	}
	CFRelease(pubKeyRef);
	CFRelease(privKeyRef);

	/* 
	 * 3. Import public key, storing in cleartext. Ensure that the import
	 *    doesn't require unlock, and ensure we can use the public key 
	 *    when keychain is locked with no user interaction.  
	 */

	printNoDialog();
	verboseDisp(verbose, "locking keychain");
	SecKeychainLock(kcRef);

	/* import public key - default is in the clear */
	verboseDisp(verbose, "importing public key, store in the clear (default)");
	CFArrayRef outArray = NULL;
	SecExternalFormat format = kSecFormatOpenSSL;
	SecExternalItemType type = kSecItemTypePublicKey;
	ortn = SecKeychainItemImport(exportData, 
		NULL, &format, &type,
		0, NULL,
		kcRef, &outArray);
	if(ortn) {
		cssmPerror("SecKeychainItemImport", ortn);
		exit(1);
	}
	CFRelease(exportData);
	if(CFArrayGetCount(outArray) != 1) {
		printf("***Unexpected outArray size (%ld) after import\n",
			(long)CFArrayGetCount(outArray));
		exit(1);
	}
	pubKeyRef = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0);
	if(CFGetTypeID(pubKeyRef) != SecKeyGetTypeID()) {
		printf("***Unexpected item type after import\n");
		exit(1);
	}
	
	/* Use imported cleartext public key with locked keychain */
	verboseDisp(verbose, "locking keychain, exporting public key");
	SecKeychainLock(kcRef);
	exportData = NULL;
	ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData);
	if(ortn) {
		cssmPerror("SecKeychainItemExport", ortn);
		exit(1);
	}
	/* we'll use exportData again */
	
	verboseDisp(verbose, "locking keychain, encrypting with public key");
	SecKeychainLock(kcRef);
	if(pubKeyEncrypt(pubKeyRef)) {
		exit(1);
	}

	/* reset */
	verboseDisp(verbose, "deleting key");
	ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef);
	if(ortn) {
		cssmPerror("SecKeychainItemDelete", ortn);
		exit(1);
	}
	CFRelease(pubKeyRef);
	
	/* 
	 * Import public key, storing in encrypted form.
	 * Ensure that user interaction is required when we use the public key 
	 * when keychain is locked.
	 */

	/* import public key, encrypted in the keychain */
	SecKeyImportExportParameters impExpParams;
	memset(&impExpParams, 0, sizeof(impExpParams));
	impExpParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
	impExpParams.keyAttributes = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE | 
								 CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT;
	verboseDisp(verbose, "importing public key, store encrypted");
	printExpectDialog();
	outArray = NULL;
	format = kSecFormatOpenSSL;
	type = kSecItemTypePublicKey;
	ortn = SecKeychainItemImport(exportData, 
		NULL, &format, &type,
		0, &impExpParams,
		kcRef, &outArray);
	if(ortn) {
		cssmPerror("SecKeychainItemImport", ortn);
		exit(1);
	}
	if(!didGetDialog()) {
		exit(1);
	}
	CFRelease(exportData);
	if(CFArrayGetCount(outArray) != 1) {
		printf("***Unexpected outArray size (%ld) after import\n",
			(long)CFArrayGetCount(outArray));
		exit(1);
	}
	pubKeyRef = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0);
	if(CFGetTypeID(pubKeyRef) != SecKeyGetTypeID()) {
		printf("***Unexpected item type after import\n");
		exit(1);
	}
					
	/* Use imported encrypted public key with locked keychain */
	verboseDisp(verbose, "locking keychain, exporting public key");
	SecKeychainLock(kcRef);
	printExpectDialog();
	ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData);
	if(ortn) {
		cssmPerror("SecKeychainItemExport", ortn);
		exit(1);
	}
	if(!didGetDialog()) {
		exit(1);
	}
	CFRelease(exportData);
	
	verboseDisp(verbose, "locking keychain, encrypting with public key");
	SecKeychainLock(kcRef);
	printExpectDialog();
	if(pubKeyEncrypt(pubKeyRef)) {
		exit(1);
	}
	if(!didGetDialog()) {
		exit(1);
	}

	SecKeychainDelete(kcRef);
	printf("...test succeeded.\n");
	return 0;
}
/*
 * Given a KeyHashTest:
 *	-- cook up key pair, raw, specified formats
 *  -- NULL unwrap each raw to ref;
 *  -- obtain four key digests;
 *  -- ensure all digests match;
 */
static int doTest(
	CSSM_CSP_HANDLE		rawCspHand,		// generate keys here
	CSSM_CSP_HANDLE		refCspHand,		// null unwrap here
	KeyHashTest			*testParam,
	CSSM_BOOL			verbose,
	CSSM_BOOL			quiet)
{
	CSSM_RETURN crtn;
	CSSM_KEY pubKey;
	CSSM_KEY privKey;
	CSSM_KEY pubKeyRef;			
	CSSM_KEY privKeyRef;
	CSSM_DATA_PTR rawPubHash;
	CSSM_DATA_PTR rawPrivHash;
	CSSM_DATA_PTR refPubHash;
	CSSM_DATA_PTR refPrivHash;
	int rtn = 0;
	
	/* generate key pair, specified raw form */
	crtn = genKeyPair(rawCspHand,
		testParam->keyAlg,
		testParam->keySizeInBits,
		&pubKey,
		testParam->pubKeyForm,
		&privKey,
		testParam->privKeyForm,
		testParam->algParams);
	if(crtn) {
		return testError(quiet);
	}
	
	/* null unwrap both raw keys to ref form */
	crtn = cspRawKeyToRef(refCspHand, &pubKey, &pubKeyRef);
	if(crtn) {
		return testError(quiet);
	}
	crtn = cspRawKeyToRef(refCspHand, &privKey, &privKeyRef);
	if(crtn) {
		return testError(quiet);
	}
	
	/* calculate four key digests */
	crtn = cspKeyHash(rawCspHand, &pubKey, &rawPubHash);
	if(crtn) {
		return testError(quiet);
	}
	crtn = cspKeyHash(rawCspHand, &privKey, &rawPrivHash);
	if(crtn) {
		return testError(quiet);
	}
	crtn = cspKeyHash(refCspHand, &pubKeyRef, &refPubHash);
	if(crtn) {
		return testError(quiet);
	}
	crtn = cspKeyHash(refCspHand, &privKeyRef, &refPrivHash);
	if(crtn) {
		return testError(quiet);
	}

	if(verbose) {
		printf("...raw pub key hash:\n");
		dumpBuf(rawPubHash->Data, rawPubHash->Length);
		printf("...ref pub key hash:\n");
		dumpBuf(refPubHash->Data, refPubHash->Length);
		printf("...raw priv key hash:\n");
		dumpBuf(rawPrivHash->Data, rawPrivHash->Length);
		printf("...ref priv key hash:\n");
		dumpBuf(refPrivHash->Data, refPrivHash->Length);
	}

	/* compare */
	rtn += compareKeyHashes(rawPubHash, "Raw public",
		refPubHash, "Ref public", verbose);
	rtn += compareKeyHashes(rawPrivHash, "Raw private",
		refPrivHash, "Ref private", verbose);
	rtn += compareKeyHashes(refPubHash, "Ref public",
		refPrivHash, "Ref private", verbose);
	if(rtn) {
		rtn = testError(quiet);
	}
	cspFreeKey(rawCspHand, &pubKey);
	cspFreeKey(rawCspHand, &privKey);
	cspFreeKey(refCspHand, &pubKeyRef);
	cspFreeKey(refCspHand, &privKeyRef);
	appFreeCssmData(rawPubHash, CSSM_TRUE);
	appFreeCssmData(rawPrivHash, CSSM_TRUE);
	appFreeCssmData(refPubHash, CSSM_TRUE);
	appFreeCssmData(refPrivHash, CSSM_TRUE);
	return rtn;
}
int doAsymTests(
	CSSM_CSP_HANDLE cspHand, 
	privAlg palg,
	CSSM_BOOL refKeys,
	CSSM_BOOL quiet)
{
	CSSM_ALGORITHMS		keyAlg;
	CSSM_ALGORITHMS 	sigAlg;
	CSSM_ALGORITHMS		encrAlg;
	CSSM_ENCRYPT_MODE	encrMode;
	CSSM_PADDING		encrPad;
	uint32				keySizeInBits;
	const char			*keyAlgStr;

	privAlgToCssm(palg, &keyAlg, &sigAlg, &encrAlg, &encrMode, 
		&encrPad, &keySizeInBits, &keyAlgStr);

	CSSM_KEY pubKey;
	CSSM_KEY privKey;
	int irtn;
	CSSM_KEYATTR_FLAGS pubKeyAttr  = CSSM_KEYATTR_EXTRACTABLE;
	CSSM_KEYATTR_FLAGS privKeyAttr = CSSM_KEYATTR_EXTRACTABLE;
	if(refKeys) {
		pubKeyAttr  |= CSSM_KEYATTR_RETURN_REF;
		privKeyAttr |= CSSM_KEYATTR_RETURN_REF;
	}
	else {
		pubKeyAttr  |= CSSM_KEYATTR_RETURN_DATA;
		privKeyAttr |= CSSM_KEYATTR_RETURN_DATA;
	}

	if(!quiet) {
		printf("...testing %s with %s keys\n", keyAlgStr,
			refKeys ? "Ref" : "Raw");
		printf("   ...verifying empty Dates\n");
	}
	irtn = genKeyPair(cspHand, keyAlg, keyAlgStr, keySizeInBits,
		&pubKey,  pubKeyAttr, CSSM_KEYUSE_ANY,
		&privKey, privKeyAttr, CSSM_KEYUSE_ANY,
		quiet,
		CSSM_FALSE, 0,		// no StartDate
		CSSM_FALSE, 0);		// no EndDate
	if(irtn) {
		return irtn;
	}
	irtn = doEncrypt(cspHand, keyAlgStr, &pubKey, encrAlg, encrMode,
		encrPad, CSSM_OK, quiet);
	if(irtn) {
		printf("***Failure on encrypting with empty Key Dates\n");
		return irtn;
	}
	irtn = doDecrypt(cspHand, keyAlgStr, &privKey, encrAlg, encrMode,
		encrPad, DR_BadData, quiet);
	if(irtn) {
		printf("***Failure on decrypting with empty Key Dates\n");
		return irtn;
	}
	irtn = doSign(cspHand, keyAlgStr, &privKey, sigAlg,
		CSSM_OK, quiet);
	if(irtn) {
		printf("***Failure on signing with empty Key Dates\n");
		return irtn;
	}
	irtn = doVerify(cspHand, keyAlgStr, &pubKey, sigAlg,
		KD_VERIFY_FAIL_ERR, quiet);
	if(irtn) {
		printf("***Failure on verifying with empty Key Dates\n");
		return irtn;
	}
	cspFreeKey(cspHand, &pubKey);
	cspFreeKey(cspHand, &privKey);
	
	if(!quiet) {
		printf("   ...verifying Good Dates\n");
	}
	irtn = genKeyPair(cspHand, keyAlg, keyAlgStr, keySizeInBits,
		&pubKey,  pubKeyAttr, CSSM_KEYUSE_ANY,
		&privKey, privKeyAttr, CSSM_KEYUSE_ANY,
		quiet,
		CSSM_TRUE, 0,		// StartDate = today
		CSSM_TRUE, 1);		// EndDate = tomorrow
	if(irtn) {
		return irtn;
	}
	irtn = doEncrypt(cspHand, keyAlgStr, &pubKey, encrAlg, encrMode,
		encrPad, CSSM_OK, quiet);
	if(irtn) {
		printf("***Failure on encrypting with good Key Dates\n");
		return irtn;
	}
	irtn = doDecrypt(cspHand, keyAlgStr, &privKey, encrAlg, encrMode,
		encrPad, DR_BadData, quiet);
	if(irtn) {
		printf("***Failure on decrypting with Good Key Dates\n");
		return irtn;
	}
	irtn = doSign(cspHand, keyAlgStr, &privKey, sigAlg,
		CSSM_OK, quiet);
	if(irtn) {
		printf("***Failure on signing with Good Key Dates\n");
		return irtn;
	}
	irtn = doVerify(cspHand, keyAlgStr, &pubKey, sigAlg,
		KD_VERIFY_FAIL_ERR, quiet);
	if(irtn) {
		printf("***Failure on verifying with Good Key Dates\n");
		return irtn;
	}
	cspFreeKey(cspHand, &pubKey);
	cspFreeKey(cspHand, &privKey);
	
	if(!quiet) {
		printf("   ...verifying Bad StartDate\n");
	}
	irtn = genKeyPair(cspHand, keyAlg, keyAlgStr, keySizeInBits,
		&pubKey,  pubKeyAttr, CSSM_KEYUSE_ANY,
		&privKey, privKeyAttr, CSSM_KEYUSE_ANY,
		quiet,
		CSSM_TRUE, 1,		// StartDate = tomorrow
		CSSM_TRUE, 1);		// EndDate = tomorrow
	if(irtn) {
		return irtn;
	}
	irtn = doEncrypt(cspHand, keyAlgStr, &pubKey, encrAlg, encrMode,
		encrPad, CSSMERR_CSP_APPLE_INVALID_KEY_START_DATE, quiet);
	if(irtn) {
		printf("***Failure on encrypting with bad StartDate\n");
		return irtn;
	}
	irtn = doDecrypt(cspHand, keyAlgStr, &privKey, encrAlg, encrMode,
		encrPad, DR_BadStartDate, quiet);
	if(irtn) {
		printf("***Failure on decrypting with bad StartDate\n");
		return irtn;
	}
	irtn = doSign(cspHand, keyAlgStr, &privKey, sigAlg,
		CSSMERR_CSP_APPLE_INVALID_KEY_START_DATE, quiet);
	if(irtn) {
		printf("***Failure on signing with bad StartDate\n");
		return irtn;
	}
	irtn = doVerify(cspHand, keyAlgStr, &pubKey, sigAlg,
		CSSMERR_CSP_APPLE_INVALID_KEY_START_DATE, quiet);
	if(irtn) {
		printf("***Failure on verifying with bad StartDate\n");
		return irtn;
	}
	cspFreeKey(cspHand, &pubKey);
	cspFreeKey(cspHand, &privKey);

	if(!quiet) {
		printf("   ...verifying Bad EndDate\n");
	}
	irtn = genKeyPair(cspHand, keyAlg, keyAlgStr, keySizeInBits,
		&pubKey,  pubKeyAttr, CSSM_KEYUSE_ANY,
		&privKey, privKeyAttr, CSSM_KEYUSE_ANY,
		quiet,
		CSSM_TRUE, 0,		// StartDate = today
		CSSM_TRUE, -1);		// EndDate = yesterday
	if(irtn) {
		return irtn;
	}
	irtn = doEncrypt(cspHand, keyAlgStr, &pubKey, encrAlg, encrMode,
		encrPad, CSSMERR_CSP_APPLE_INVALID_KEY_END_DATE, quiet);
	if(irtn) {
		printf("***Failure on encrypting with bad EndDate\n");
		return irtn;
	}
	irtn = doDecrypt(cspHand, keyAlgStr, &privKey, encrAlg, encrMode,
		encrPad, DR_BadEndDate, quiet);
	if(irtn) {
		printf("***Failure on decrypting with bad EndDate\n");
		return irtn;
	}
	irtn = doSign(cspHand, keyAlgStr, &privKey, sigAlg,
		CSSMERR_CSP_APPLE_INVALID_KEY_END_DATE, quiet);
	if(irtn) {
		printf("***Failure on signing with bad EndDate\n");
		return irtn;
	}
	irtn = doVerify(cspHand, keyAlgStr, &pubKey, sigAlg,
		CSSMERR_CSP_APPLE_INVALID_KEY_END_DATE, quiet);
	if(irtn) {
		printf("***Failure on verifying with bad EndDate\n");
		return irtn;
	}
	cspFreeKey(cspHand, &pubKey);
	cspFreeKey(cspHand, &privKey);

	return 0;
}
int doStoreTests(
	CSSM_CSP_HANDLE cspHand, 		// must be CSPDL
	CSSM_DL_DB_HANDLE dlDbHand,
	privAlg palg,
	CSSM_BOOL isAsym,
	CSSM_BOOL quiet)
{	
	CSSM_ALGORITHMS		keyAlg;
	CSSM_ALGORITHMS 	signAlg;
	CSSM_ALGORITHMS		encrAlg;
	CSSM_ENCRYPT_MODE	encrMode;
	CSSM_PADDING		encrPad;
	uint32				keySizeInBits;
	const char			*keyAlgStr;

	privAlgToCssm(palg, &keyAlg, &signAlg, &encrAlg, &encrMode, 
		&encrPad, &keySizeInBits, &keyAlgStr);

	CSSM_KEY symKey;
	CSSM_KEY privKey;
	CSSM_KEY pubKey;
	int irtn;
	CSSM_KEY_PTR lookupKey = NULL;		// obtained from DB
	CSSM_KEY_PTR compareKey;			// &symKey or &pubKey
	CT_KeyType lookupType;
	CSSM_KEYATTR_FLAGS pubKeyAttr  = 
		CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE |
		CSSM_KEYATTR_PERMANENT;
	CSSM_KEYATTR_FLAGS privKeyAttr = 
		CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT;
		
	if(!quiet) {
		printf("...testing %s key storage\n", keyAlgStr);
		printf("   ...verifying empty Dates\n");
	}
	if(isAsym) {
		lookupType = CKT_Public;
		compareKey = &pubKey;
		irtn = genKeyPair(cspHand, keyAlg, keyAlgStr, keySizeInBits,
			&pubKey,  pubKeyAttr, CSSM_KEYUSE_ANY,
			&privKey, privKeyAttr, CSSM_KEYUSE_ANY,
			quiet,
			CSSM_FALSE, 0,		// no StartDate
			CSSM_FALSE, 0,		// no EndDate
			&dlDbHand);
	}
	else {
		lookupType = CKT_Session;
		compareKey = &symKey;
		irtn = genSymKey(cspHand, &symKey, keyAlg, keyAlgStr, 
			keySizeInBits,
			CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT,
			CSSM_KEYUSE_ANY, quiet,
			CSSM_FALSE, 0,		// no StartDate
			CSSM_FALSE, 0,		// no EndDate
			&dlDbHand);
	}
	if(irtn) {
		return irtn;
	}
	
	/* 
	 * fetch stored key from DB, ensure it has same start/end date 
	 */
	if(fetchStoredKey(dlDbHand, lookupType,
			compareKey, "Store key with empty Dates", quiet, 
			&lookupKey)) {
		return 1;
	}
	
	/* quickie test, use it for encrypt */
	irtn = doEncrypt(cspHand, keyAlgStr, lookupKey, encrAlg, encrMode,
		encrPad, CSSM_OK, quiet);
	if(irtn) {
		printf("***Failure on encrypt, lookup with empty Key Dates\n");
		return irtn;
	}
	
	/* free and delete everything */
	if(isAsym) {
		cspDeleteKey(cspHand, dlDbHand.DLHandle, dlDbHand.DBHandle,
			&keyLabelData, &pubKey);
		cspDeleteKey(cspHand, dlDbHand.DLHandle, dlDbHand.DBHandle,
			&keyLabelData, &privKey);
	}
	else {
		cspDeleteKey(cspHand, dlDbHand.DLHandle, dlDbHand.DBHandle,
			&keyLabelData, &symKey);
	}
	cspFreeKey(cspHand, lookupKey);
	
	/*********************/
	
	if(!quiet) {
		printf("   ...verifying Good Dates\n");
	}
	if(isAsym) {
		lookupType = CKT_Public;
		compareKey = &pubKey;
		irtn = genKeyPair(cspHand, keyAlg, keyAlgStr, keySizeInBits,
			&pubKey,  pubKeyAttr, CSSM_KEYUSE_ANY,
			&privKey, privKeyAttr, CSSM_KEYUSE_ANY,
			quiet,
			CSSM_TRUE, 0,		// StartDate = today
			CSSM_TRUE, 1,		// EndDate = tomorrow
			&dlDbHand);
	}
	else {
		lookupType = CKT_Session;
		compareKey = &symKey;
		irtn = genSymKey(cspHand, &symKey, keyAlg, keyAlgStr, 
			keySizeInBits,
			CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT,
			CSSM_KEYUSE_ANY, quiet,
			CSSM_TRUE, 0,		// StartDate = today
			CSSM_TRUE, 1,		// EndDate = tomorrow
			&dlDbHand);
	}
	if(irtn) {
		return irtn;
	}
	
	/* 
	 * fetch stored key from DB, ensure it has same start/end date 
	 */
	if(fetchStoredKey(dlDbHand, lookupType,
			compareKey, "Store key with Good Dates", quiet, 
			&lookupKey)) {
		return 1;
	}
	
	/* quickie test, use it for encrypt */
	irtn = doEncrypt(cspHand, keyAlgStr, lookupKey, encrAlg, encrMode,
		encrPad, CSSM_OK, quiet);
	if(irtn) {
		printf("***Failure on encrypt, lookup with Good Key Dates\n");
		return irtn;
	}
	
	/* free and delete everything */
	if(isAsym) {
		cspDeleteKey(cspHand, dlDbHand.DLHandle, dlDbHand.DBHandle,
			&keyLabelData, &pubKey);
		cspDeleteKey(cspHand, dlDbHand.DLHandle, dlDbHand.DBHandle,
			&keyLabelData, &privKey);
	}
	else {
		cspDeleteKey(cspHand, dlDbHand.DLHandle, dlDbHand.DBHandle,
			&keyLabelData, &symKey);
	}
	cspFreeKey(cspHand, lookupKey);

	/*********************/

	if(!quiet) {
		printf("   ...verifying Bad StartDate\n");
	}
	if(isAsym) {
		lookupType = CKT_Public;
		compareKey = &pubKey;
		irtn = genKeyPair(cspHand, keyAlg, keyAlgStr, keySizeInBits,
			&pubKey,  pubKeyAttr, CSSM_KEYUSE_ANY,
			&privKey, privKeyAttr, CSSM_KEYUSE_ANY,
			quiet,
			CSSM_TRUE, 1,		// StartDate = tomorrow
			CSSM_TRUE, 1,		// EndDate = tomorrow
			&dlDbHand);
	}
	else {
		lookupType = CKT_Session;
		compareKey = &symKey;
		irtn = genSymKey(cspHand, &symKey, keyAlg, keyAlgStr, 
			keySizeInBits,
			CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT,
			CSSM_KEYUSE_ANY, quiet,
			CSSM_TRUE, 1,		// StartDate = tomorrow
			CSSM_TRUE, 1,		// EndDate = tomorrow
			&dlDbHand);
	}
	if(irtn) {
		return irtn;
	}
	
	/* 
	 * fetch stored key from DB, ensure it has same start/end date 
	 */
	if(fetchStoredKey(dlDbHand, lookupType,
			compareKey, "Store key with Bad StartDate", quiet, 
			&lookupKey)) {
		return 1;
	}
	
	/* quickie test, use it for encrypt */
	irtn = doEncrypt(cspHand, keyAlgStr, lookupKey, encrAlg, encrMode,
		encrPad, CSSMERR_CSP_APPLE_INVALID_KEY_START_DATE, quiet);
	if(irtn) {
		printf("***Failure on encrypt, lookup with Bad Start Dates\n");
		return irtn;
	}
	
	/* free and delete everything */
	if(isAsym) {
		cspDeleteKey(cspHand, dlDbHand.DLHandle, dlDbHand.DBHandle,
			&keyLabelData, &pubKey);
		cspDeleteKey(cspHand, dlDbHand.DLHandle, dlDbHand.DBHandle,
			&keyLabelData, &privKey);
	}
	else {
		cspDeleteKey(cspHand, dlDbHand.DLHandle, dlDbHand.DBHandle,
			&keyLabelData, &symKey);
	}
	cspFreeKey(cspHand, lookupKey);

	/*********************/
	
	if(!quiet) {
		printf("   ...verifying Bad EndDate\n");
	}
	if(isAsym) {
		lookupType = CKT_Public;
		compareKey = &pubKey;
		irtn = genKeyPair(cspHand, keyAlg, keyAlgStr, keySizeInBits,
			&pubKey,  pubKeyAttr, CSSM_KEYUSE_ANY,
			&privKey, privKeyAttr, CSSM_KEYUSE_ANY,
			quiet,
			CSSM_TRUE, 0,		// StartDate = today
			CSSM_TRUE, -1,		// EndDate = yesterday
			&dlDbHand);
	}
	else {
		lookupType = CKT_Session;
		compareKey = &symKey;
		irtn = genSymKey(cspHand, &symKey, keyAlg, keyAlgStr, 
			keySizeInBits,
			CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT,
			CSSM_KEYUSE_ANY, quiet,
			CSSM_TRUE, 0,		// StartDate = today
			CSSM_TRUE, -1,		// EndDate = yesterday
			&dlDbHand);
	}
	if(irtn) {
		return irtn;
	}
	
	/* 
	 * fetch stored key from DB, ensure it has same start/end date 
	 */
	if(fetchStoredKey(dlDbHand, lookupType,
			compareKey, "Store key with Bad EndDate", quiet, 
			&lookupKey)) {
		return 1;
	}
	
	/* quickie test, use it for encrypt */
	irtn = doEncrypt(cspHand, keyAlgStr, lookupKey, encrAlg, encrMode,
		encrPad, CSSMERR_CSP_APPLE_INVALID_KEY_END_DATE, quiet);
	if(irtn) {
		printf("***Failure on encrypt, lookup with Bad End Dates\n");
		return irtn;
	}
	
	/* free and delete everything */
	if(isAsym) {
		cspDeleteKey(cspHand, dlDbHand.DLHandle, dlDbHand.DBHandle,
			&keyLabelData, &pubKey);
		cspDeleteKey(cspHand, dlDbHand.DLHandle, dlDbHand.DBHandle,
			&keyLabelData, &privKey);
	}
	else {
		cspDeleteKey(cspHand, dlDbHand.DLHandle, dlDbHand.DBHandle,
			&keyLabelData, &symKey);
	}
	cspFreeKey(cspHand, lookupKey);

	return 0;
}