/*
 * Find private key by specified label, delete it.
 */
CSSM_RETURN p12DeleteKey(
	CSSM_DL_DB_HANDLE dlDbHand, 
	const CSSM_DATA	&keyLabel)
{
	CSSM_QUERY						query;
	CSSM_SELECTION_PREDICATE		predicate;
	CSSM_DB_UNIQUE_RECORD_PTR		record = NULL;
	CSSM_RETURN						crtn;
	CSSM_HANDLE						resultHand = 0;
	
	query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
	query.Conjunctive = CSSM_DB_NONE;
	query.NumSelectionPredicates = 1;
	predicate.DbOperator = CSSM_DB_EQUAL;
	
	predicate.Attribute.Info.AttributeNameFormat = 
		CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
	predicate.Attribute.Info.Label.AttributeName = 
		(char*) P12_KEY_ATTR_LABEL_AND_HASH;
	predicate.Attribute.Info.AttributeFormat = 
		CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
	predicate.Attribute.Value = const_cast<CSSM_DATA_PTR>(&keyLabel);
	
	query.SelectionPredicate = &predicate;
	query.QueryLimits.TimeLimit = 0;
	query.QueryLimits.SizeLimit = 1;
	query.QueryFlags = 0;

	crtn = CSSM_DL_DataGetFirst(dlDbHand,
		&query,
		&resultHand,
		NULL,			// attrs - don't need 'em
		NULL, 			// theData - don't need it
		&record);
	/* abort only on success */
	if(crtn) {
		p12LogCssmError("CSSM_DL_DataGetFirst", crtn);
		p12ErrorLog("***p12DeleteKey: can't find private key\n");
		return crtn;
	}

	crtn = CSSM_DL_DataDelete(dlDbHand, record);
	if(crtn) {
		p12LogCssmError("CSSM_DL_DataDelete", crtn);
		p12ErrorLog("***p12DeleteKey: can't delete private key\n");
	}
	
	CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
	CSSM_DL_FreeUniqueRecord(dlDbHand, record);
	return crtn;
}
/*
 * Find private key by label, modify its Label attr to be the
 * hash of the associated public key. 
 * Also optionally re-sets the key's PrintName attribute; used to reset
 * this attr from the random label we create when first unwrap it 
 * to the friendly name we find later after parsing attributes.
 * Detection of a duplicate key when updating the key's attributes
 * results in a lookup of the original key and returning it in
 * foundKey.
 */
CSSM_RETURN p12SetPubKeyHash(
	CSSM_CSP_HANDLE 	cspHand,		// where the key lives
	CSSM_DL_DB_HANDLE 	dlDbHand,		// ditto
	CSSM_DATA			&keyLabel,		// for DB lookup
	CSSM_DATA_PTR		newPrintName,	// optional
	SecNssCoder			&coder,			// for mallocing newLabel
	CSSM_DATA			&newLabel,		// RETURNED with label as hash
	CSSM_KEY_PTR		&foundKey)		// RETURNED
{
	CSSM_QUERY						query;
	CSSM_SELECTION_PREDICATE		predicate;
	CSSM_DB_UNIQUE_RECORD_PTR		record = NULL;
	CSSM_RETURN						crtn;
	CSSM_HANDLE						resultHand = 0;
	CSSM_DATA						keyData = {0, NULL};
	CSSM_CC_HANDLE					ccHand = 0;
	CSSM_KEY_PTR					privKey = NULL;
	CSSM_DATA_PTR					keyDigest = NULL;
	
	assert(cspHand != 0);
	query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
	query.Conjunctive = CSSM_DB_NONE;
	query.NumSelectionPredicates = 1;
	predicate.DbOperator = CSSM_DB_EQUAL;
	
	predicate.Attribute.Info.AttributeNameFormat = 
		CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
	predicate.Attribute.Info.Label.AttributeName = 
		(char*) P12_KEY_ATTR_LABEL_AND_HASH;
	predicate.Attribute.Info.AttributeFormat = 
		CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
	/* hope this cast is OK */
	predicate.Attribute.Value = &keyLabel;
	query.SelectionPredicate = &predicate;
	
	query.QueryLimits.TimeLimit = 0;	// FIXME - meaningful?
	query.QueryLimits.SizeLimit = 1;	// FIXME - meaningful?
	query.QueryFlags = CSSM_QUERY_RETURN_DATA;	

	/* build Record attribute with one or two attrs */
	CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
	CSSM_DB_ATTRIBUTE_DATA attr[2];
	attr[0].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
	attr[0].Info.Label.AttributeName = (char*) P12_KEY_ATTR_LABEL_AND_HASH;
	attr[0].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
	if(newPrintName) {
		attr[1].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
		attr[1].Info.Label.AttributeName = (char*) P12_KEY_ATTR_PRINT_NAME;
		attr[1].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
	}
	recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
	recordAttrs.NumberOfAttributes = newPrintName ? 2 : 1;
	recordAttrs.AttributeData = attr;
	
	crtn = CSSM_DL_DataGetFirst(dlDbHand,
		&query,
		&resultHand,
		&recordAttrs,
		&keyData,			// theData
		&record);
	/* abort only on success */
	if(crtn != CSSM_OK) {
		p12LogCssmError("CSSM_DL_DataGetFirst", crtn);
		p12ErrorLog("***p12SetPubKeyHash: can't find private key\n");
		return crtn;
	}
	/* subsequent errors to errOut: */
	if(keyData.Data == NULL) {
		p12ErrorLog("***p12SetPubKeyHash: private key lookup failure\n");
		crtn = CSSMERR_CSSM_INTERNAL_ERROR;
		goto errOut;
	}
	privKey = (CSSM_KEY_PTR)keyData.Data;
	
	/* public key hash via passthrough - works on any key, any CSP/CSPDL.... */
	/*
	 * Warning! This relies on the current default ACL meaning "allow this
	 * current app to access this private key" since we created the key. 
	 */
	crtn = CSSM_CSP_CreatePassThroughContext(cspHand, privKey, &ccHand);
	if(crtn) {
		p12LogCssmError("CSSM_CSP_CreatePassThroughContext", crtn);
		goto errOut;
	}
	crtn = CSSM_CSP_PassThrough(ccHand,
		CSSM_APPLECSP_KEYDIGEST,
		NULL,
		(void **)&keyDigest);
	if(crtn) {
		p12LogCssmError("CSSM_CSP_PassThrough", crtn);
		goto errOut;
	}

	/* 
	 * Replace Label attr data with hash.
	 * NOTE: the module which allocated this attribute data - a DL -
	 * was loaded and attached by out client layer, not by us. Thus 
	 * we can't use the memory allocator functions *we* used when 
	 * attaching to the CSP - we have to use the ones
	 * which the client registered with the DL.
	 */
	freeCssmMemory(dlDbHand.DLHandle, attr[0].Value->Data);
	freeCssmMemory(dlDbHand.DLHandle, attr[0].Value);
	if(newPrintName) {
		freeCssmMemory(dlDbHand.DLHandle, attr[1].Value->Data);
		freeCssmMemory(dlDbHand.DLHandle, attr[1].Value);
	}
	/* modify key attributes */
	attr[0].Value = keyDigest;
	if(newPrintName) {
		attr[1].Value = newPrintName;
	}
	crtn = CSSM_DL_DataModify(dlDbHand,
			CSSM_DL_DB_RECORD_PRIVATE_KEY,
			record,
			&recordAttrs,
            NULL,				// DataToBeModified
			CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
	switch(crtn) {
		case CSSM_OK:
			/* give caller the key's new label */
			coder.allocCopyItem(*keyDigest, newLabel);
			break;
		default:
			p12LogCssmError("CSSM_DL_DataModify", crtn);
			break;
		case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA:
		{
			/* 
			 * Special case: dup private key. The label we just tried to modify is 
			 * the public key hash so we can be confident that this really is a dup. 
			 * Delete it, look up the original, and return the original to caller. 
			 */ 
			CSSM_RETURN drtn = CSSM_DL_DataDelete(dlDbHand, record);
			if(drtn) {
				p12LogCssmError("CSSM_DL_DataDelete on dup key", drtn);
				crtn = drtn;
				break;
			}

			/* Free items created in last search */
			CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
			resultHand = 0;
			CSSM_DL_FreeUniqueRecord(dlDbHand, record);
			record = NULL;
			
			/* lookup by label as public key hash this time */
			predicate.Attribute.Value = keyDigest;
			drtn = CSSM_DL_DataGetFirst(dlDbHand,
				&query,
				&resultHand,
				NULL,				// no attrs this time
				&keyData,		
				&record);
			if(drtn) {
				p12LogCssmError("CSSM_DL_DataGetFirst on original key", crtn);
				crtn = drtn;
				break;
			}
			foundKey = (CSSM_KEY_PTR)keyData.Data;
			/* give caller the key's actual label */
			coder.allocCopyItem(*keyDigest, newLabel);
			break;
		}
	}
	
errOut:
	/* free resources */
	if(resultHand) {
		CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
	}
	if(record) {
		CSSM_DL_FreeUniqueRecord(dlDbHand, record);
	}
	if(ccHand) {
		CSSM_DeleteContext(ccHand);
	}
	if(privKey) {
		/* key created by the CSPDL */
		CSSM_FreeKey(cspHand, NULL, privKey, CSSM_FALSE);
		freeCssmMemory(dlDbHand.DLHandle, privKey);
	}
	if(keyDigest)  {
		/* mallocd by someone else's CSP */
		freeCssmMemory(cspHand, keyDigest->Data);
		freeCssmMemory(cspHand, keyDigest);
	}
	return crtn;
}
/*
 * Search a list of DBs for a CRL from the specified issuer and (optional)
 * TPVerifyContext.verifyTime.
 * Just a boolean return - we found it, or not. If we did, we return a
 * TPCrlInfo which has been verified with the specified TPVerifyContext.
 */
TPCrlInfo *tpDbFindIssuerCrl(
	TPVerifyContext		&vfyCtx,
	const CSSM_DATA		&issuer,
	TPCertInfo			&forCert)
{
	StLock<Mutex> _(SecTrustKeychainsGetMutex());

	uint32						dbDex;
	CSSM_HANDLE					resultHand;
	CSSM_DATA					crl;
	CSSM_DL_DB_HANDLE			dlDb;
	CSSM_DB_UNIQUE_RECORD_PTR	record;
	TPCrlInfo 					*issuerCrl = NULL;
	CSSM_DL_DB_LIST_PTR 		dbList = vfyCtx.dbList;
	CSSM_RETURN					crtn;

	if(dbList == NULL) {
		return NULL;
	}
	for(dbDex=0; dbDex<dbList->NumHandles; dbDex++) {
		dlDb = dbList->DLDBHandle[dbDex];
		crl.Data = NULL;
		crl.Length = 0;
		record = tpCrlLookup(dlDb,
			&issuer,
			vfyCtx.verifyTime,
			&resultHand,
			&crl);
		/* remember we have to:
		 * -- abort this query regardless, and
		 * -- free the CSSM_DATA crl regardless, and
		 * -- free the unique record if we don't use it
		 *    (by placing it in issuerCert)...
		 */
		if(record != NULL) {
			/* Found one */
			assert(crl.Data != NULL);
			issuerCrl = new TPCrlInfo(vfyCtx.clHand,
				vfyCtx.cspHand,
				&crl,
				TIC_CopyData,
				vfyCtx.verifyTime);
			/* we're done with raw CRL data */
			/* FIXME this assumes that vfyCtx.alloc is the same as the
			 * allocator associated with DlDB...OK? */
			tpFreeCssmData(vfyCtx.alloc, &crl, CSSM_FALSE);
			crl.Data = NULL;
			crl.Length = 0;

			/* and we're done with the record */
			CSSM_DL_FreeUniqueRecord(dlDb, record);

			/* Does it verify with specified context? */
			crtn = issuerCrl->verifyWithContextNow(vfyCtx, &forCert);
			if(crtn) {

				delete issuerCrl;
				issuerCrl = NULL;

				/*
				 * Verify fail. Continue searching this DB. Break on
				 * finding the holy grail or no more records found.
				 */
				for(;;) {
					crl.Data = NULL;
					crl.Length = 0;
					crtn = CSSM_DL_DataGetNext(dlDb,
						resultHand,
						NULL,		// no attrs
						&crl,
						&record);
					if(crtn) {
						/* no more, done with this DB */
						assert(crl.Data == NULL);
						break;
					}
					assert(crl.Data != NULL);

					/* found one - is it any good? */
					issuerCrl = new TPCrlInfo(vfyCtx.clHand,
						vfyCtx.cspHand,
						&crl,
						TIC_CopyData,
						vfyCtx.verifyTime);
					/* we're done with raw CRL data */
					/* FIXME this assumes that vfyCtx.alloc is the same as the
					* allocator associated with DlDB...OK? */
					tpFreeCssmData(vfyCtx.alloc, &crl, CSSM_FALSE);
					crl.Data = NULL;
					crl.Length = 0;

					CSSM_DL_FreeUniqueRecord(dlDb, record);

					crtn = issuerCrl->verifyWithContextNow(vfyCtx, &forCert);
					if(crtn == CSSM_OK) {
						/* yes! */
						break;
					}
					delete issuerCrl;
					issuerCrl = NULL;
				} /* searching subsequent records */
			}	/* verify fail */
			/* else success! */

			if(issuerCrl != NULL) {
				/* successful return */
				CSSM_DL_DataAbortQuery(dlDb, resultHand);
				tpDebug("tpDbFindIssuerCrl: found CRL record %p", record);
				return issuerCrl;
			}
		}	/* tpCrlLookup, i.e., CSSM_DL_DataGetFirst, succeeded */
		else {
			assert(crl.Data == NULL);
		}
		/* in any case, abort the query for this db */
		CSSM_DL_DataAbortQuery(dlDb, resultHand);

	}	/* main loop searching dbList */

	/* issuer not found */
	return NULL;
}
/*
 * Search a list of DBs for a cert which verifies specified subject item.
 * Just a boolean return - we found it, or not. If we did, we return
 * TPCertInfo associated with the raw cert.
 * A true partialIssuerKey on return indicates that caller must deal
 * with partial public key processing later.
 * If verifyCurrent is true, we will not return a cert which is not
 * temporally valid; else we may well do so.
 */
TPCertInfo *tpDbFindIssuerCert(
	Allocator				&alloc,
	CSSM_CL_HANDLE			clHand,
	CSSM_CSP_HANDLE			cspHand,
	const TPClItemInfo		*subjectItem,
	const CSSM_DL_DB_LIST	*dbList,
	const char 				*verifyTime,		// may be NULL
	bool					&partialIssuerKey)	// RETURNED
{
	StLock<Mutex> _(SecTrustKeychainsGetMutex());

	uint32						dbDex;
	CSSM_HANDLE					resultHand;
	CSSM_DATA					cert;
	CSSM_DL_DB_HANDLE			dlDb;
	CSSM_DB_UNIQUE_RECORD_PTR	record;
	TPCertInfo 					*issuerCert = NULL;
	bool 						foundIt;
	TPCertInfo					*expiredIssuer = NULL;
	TPCertInfo					*nonRootIssuer = NULL;

	partialIssuerKey = false;
	if(dbList == NULL) {
		return NULL;
	}
	for(dbDex=0; dbDex<dbList->NumHandles; dbDex++) {
		dlDb = dbList->DLDBHandle[dbDex];
		cert.Data = NULL;
		cert.Length = 0;
		resultHand = 0;
		record = tpCertLookup(dlDb,
			subjectItem->issuerName(),
			&resultHand,
			&cert);
		/* remember we have to:
		 * -- abort this query regardless, and
		 * -- free the CSSM_DATA cert regardless, and
		 * -- free the unique record if we don't use it
		 *    (by placing it in issuerCert)...
		 */
		if(record != NULL) {
			/* Found one */
			assert(cert.Data != NULL);
			tpDbDebug("tpDbFindIssuerCert: found cert record (1) %p", record);
			issuerCert = NULL;
			CSSM_RETURN crtn = CSSM_OK;
			try {
				issuerCert = new TPCertInfo(clHand, cspHand, &cert, TIC_CopyData, verifyTime);
			}
			catch(...) {
				crtn = CSSMERR_TP_INVALID_CERTIFICATE;
			}

			/* we're done with raw cert data */
			tpFreePluginMemory(dlDb.DLHandle, cert.Data);
			cert.Data = NULL;
			cert.Length = 0;

			/* Does it verify the subject cert? */
			if(crtn == CSSM_OK) {
				crtn = subjectItem->verifyWithIssuer(issuerCert);
			}

			/*
			 * Handle temporal invalidity - if so and this is the first one
			 * we've seen, hold on to it while we search for better one.
			 */
			if((crtn == CSSM_OK) && (expiredIssuer == NULL)) {
				if(issuerCert->isExpired() || issuerCert->isNotValidYet()) {
					/*
					 * Exact value not important here, this just uniquely identifies
					 * this situation in the switch below.
					 */
					tpDbDebug("tpDbFindIssuerCert: holding expired cert (1)");
					crtn = CSSM_CERT_STATUS_EXPIRED;
					expiredIssuer = issuerCert;
					expiredIssuer->dlDbHandle(dlDb);
					expiredIssuer->uniqueRecord(record);
				}
			}
			/*
			 * Prefer a root over an intermediate issuer if we can get one
			 * (in case a cross-signed intermediate and root are both available)
			 */
			if((crtn == CSSM_OK) && (nonRootIssuer == NULL)) {
				if(!issuerCert->isSelfSigned()) {
					/*
					 * Exact value not important here, this just uniquely identifies
					 * this situation in the switch below.
					 */
					tpDbDebug("tpDbFindIssuerCert: holding non-root cert (1)");
					crtn = CSSM_CERT_STATUS_IS_ROOT;
					nonRootIssuer = issuerCert;
					nonRootIssuer->dlDbHandle(dlDb);
					nonRootIssuer->uniqueRecord(record);
				}
			}
			switch(crtn) {
				case CSSM_OK:
					break;
				case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
					partialIssuerKey = true;
					break;
				default:
					if(issuerCert != NULL) {
						/* either holding onto this cert, or done with it. */
						if(crtn != CSSM_CERT_STATUS_EXPIRED &&
						   crtn != CSSM_CERT_STATUS_IS_ROOT) {
							delete issuerCert;
							CSSM_DL_FreeUniqueRecord(dlDb, record);
						}
						issuerCert = NULL;
					}

					/*
					 * Continue searching this DB. Break on finding the holy
					 * grail or no more records found.
					 */
					for(;;) {
						cert.Data = NULL;
						cert.Length = 0;
						record = NULL;
						CSSM_RETURN crtn = CSSM_DL_DataGetNext(dlDb,
							resultHand,
							NULL,		// no attrs
							&cert,
							&record);
						if(crtn) {
							/* no more, done with this DB */
							assert(cert.Data == NULL);
							break;
						}
						assert(cert.Data != NULL);
						tpDbDebug("tpDbFindIssuerCert: found cert record (2) %p", record);

						/* found one - does it verify subject? */
						try {
							issuerCert = new TPCertInfo(clHand, cspHand, &cert, TIC_CopyData,
									verifyTime);
						}
						catch(...) {
							crtn = CSSMERR_TP_INVALID_CERTIFICATE;
						}
						/* we're done with raw cert data */
						tpFreePluginMemory(dlDb.DLHandle, cert.Data);
						cert.Data = NULL;
						cert.Length = 0;

						if(crtn == CSSM_OK) {
							crtn = subjectItem->verifyWithIssuer(issuerCert);
						}

						/* temporal validity check, again */
						if((crtn == CSSM_OK) && (expiredIssuer == NULL)) {
							if(issuerCert->isExpired() || issuerCert->isNotValidYet()) {
								tpDbDebug("tpDbFindIssuerCert: holding expired cert (2)");
								crtn = CSSM_CERT_STATUS_EXPIRED;
								expiredIssuer = issuerCert;
								expiredIssuer->dlDbHandle(dlDb);
								expiredIssuer->uniqueRecord(record);
							}
						}
						/* self-signed check, again */
						if((crtn == CSSM_OK) && (nonRootIssuer == NULL)) {
							if(!issuerCert->isSelfSigned()) {
								tpDbDebug("tpDbFindIssuerCert: holding non-root cert (2)");
								crtn = CSSM_CERT_STATUS_IS_ROOT;
								nonRootIssuer = issuerCert;
								nonRootIssuer->dlDbHandle(dlDb);
								nonRootIssuer->uniqueRecord(record);
							}
						}

						foundIt = false;
						switch(crtn) {
							case CSSM_OK:
								foundIt = true;
								break;
							case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
								partialIssuerKey = true;
								foundIt = true;
								break;
							default:
								break;
						}
						if(foundIt) {
							/* yes! */
							break;
						}
						if(issuerCert != NULL) {
							/* either holding onto this cert, or done with it. */
							if(crtn != CSSM_CERT_STATUS_EXPIRED &&
							   crtn != CSSM_CERT_STATUS_IS_ROOT) {
								delete issuerCert;
								CSSM_DL_FreeUniqueRecord(dlDb, record);
							}
							issuerCert = NULL;
						}
					} /* searching subsequent records */
			}	/* switch verify */

			if(record != NULL) {
				/* NULL record --> end of search --> DB auto-aborted */
				crtn = CSSM_DL_DataAbortQuery(dlDb, resultHand);
				assert(crtn == CSSM_OK);
			}
			if(issuerCert != NULL) {
				/* successful return */
				tpDbDebug("tpDbFindIssuer: returning record %p", record);
				issuerCert->dlDbHandle(dlDb);
				issuerCert->uniqueRecord(record);
				if(expiredIssuer != NULL) {
					/* We found a replacement */
					tpDbDebug("tpDbFindIssuer: discarding expired cert");
					expiredIssuer->freeUniqueRecord();
					delete expiredIssuer;
				}
				/* Avoid deleting the non-root cert if same as expired cert */
				if(nonRootIssuer != NULL && nonRootIssuer != expiredIssuer) {
					/* We found a replacement */
					tpDbDebug("tpDbFindIssuer: discarding non-root cert");
					nonRootIssuer->freeUniqueRecord();
					delete nonRootIssuer;
				}
				return issuerCert;
			}
		}	/* tpCertLookup, i.e., CSSM_DL_DataGetFirst, succeeded */
		else {
			assert(cert.Data == NULL);
			assert(resultHand == 0);
		}
	}	/* main loop searching dbList */

	if(nonRootIssuer != NULL) {
		/* didn't find root issuer, so use this one */
		tpDbDebug("tpDbFindIssuer: taking non-root issuer cert, record %p",
			nonRootIssuer->uniqueRecord());
		if(expiredIssuer != NULL && expiredIssuer != nonRootIssuer) {
			expiredIssuer->freeUniqueRecord();
			delete expiredIssuer;
		}
		return nonRootIssuer;
	}

	if(expiredIssuer != NULL) {
		/* OK, we'll take this one */
		tpDbDebug("tpDbFindIssuer: taking expired cert after all, record %p",
			expiredIssuer->uniqueRecord());
		return expiredIssuer;
	}
	/* issuer not found */
	return NULL;
}
/*
 * Find private key by label, modify its Label attr to be the
 * hash of the associated public key.
 */
static CSSM_RETURN setPubKeyHash(
	CSSM_CSP_HANDLE 	cspHand,
	CSSM_DL_DB_HANDLE 	dlDbHand,
	const CSSM_KEY		*pubOrPrivKey,	// to get hash; raw or ref/CSPDL
	const char			*keyLabel)		// look up by this
{
	CSSM_QUERY						query;
	CSSM_SELECTION_PREDICATE		predicate;
	CSSM_DB_UNIQUE_RECORD_PTR		record = NULL;
	CSSM_RETURN						crtn;
	CSSM_DATA						labelData;
	CSSM_HANDLE						resultHand;

	labelData.Data = (uint8 *)keyLabel;
	labelData.Length = strlen(keyLabel) + 1;	// incl. NULL
	query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
	query.Conjunctive = CSSM_DB_NONE;
	query.NumSelectionPredicates = 1;
	predicate.DbOperator = CSSM_DB_EQUAL;

	predicate.Attribute.Info.AttributeNameFormat =
		CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
	predicate.Attribute.Info.Label.AttributeName = "Label";
	predicate.Attribute.Info.AttributeFormat =
		CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
	predicate.Attribute.Value = &labelData;
	query.SelectionPredicate = &predicate;

	query.QueryLimits.TimeLimit = 0;
	query.QueryLimits.SizeLimit = 1;
	query.QueryFlags = 0;

	/* build Record attribute with one attr */
	CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
	CSSM_DB_ATTRIBUTE_DATA attr;
	attr.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
	attr.Info.Label.AttributeName = "Label";
	attr.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;

	recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
	recordAttrs.NumberOfAttributes = 1;
	recordAttrs.AttributeData = &attr;

	crtn = CSSM_DL_DataGetFirst(dlDbHand,
		&query,
		&resultHand,
		&recordAttrs,
		NULL,			// hopefully optional ...theData,
		&record);
	/* abort only on success */
	if(crtn != CSSM_OK) {
		sec_error("CSSM_DL_DataGetFirst: setPubKeyHash: can't find private key: %s", sec_errstr(crtn));
		return crtn;
	}

	/*
	 * If specified key is a ref key, do NULL unwrap for use with raw CSP.
	 * If the CSPDL and SecurityServer support the key digest passthrough
	 * this is unnecessary.
	 */
	CSSM_KEY rawKeyToDigest;
	if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) {
		crtn = refKeyToRaw(cspHand, pubOrPrivKey, &rawKeyToDigest);
		if(crtn) {
            sec_error("setPubKeyHash: Error converting public key to raw format: %s", sec_errstr(crtn));
			return crtn;
		}
	}
	else {
		/* use as is */
		rawKeyToDigest = *pubOrPrivKey;
	}

	/* connect to raw CSP */
	CSSM_CSP_HANDLE rawCspHand = srCspStartup(CSSM_TRUE);
	if(rawCspHand == 0) {
		printf("***Error connecting to raw CSP; aborting.\n");
		return -1;
	}

	/* calculate hash of pub key from private or public part */
	CSSM_DATA_PTR keyDigest = NULL;
	CSSM_CC_HANDLE ccHand;
	crtn = CSSM_CSP_CreatePassThroughContext(rawCspHand,
	 	&rawKeyToDigest,
		&ccHand);
	if(ccHand == 0) {
        sec_error("CSSM_CSP_CreatePassThroughContext: Error calculating public key hash. Aborting: %s", sec_errstr(crtn));
		return -1;
	}
	crtn = CSSM_CSP_PassThrough(ccHand,
		CSSM_APPLECSP_KEYDIGEST,
		NULL,
		(void **)&keyDigest);
	if(crtn) {
        sec_error("CSSM_CSP_PassThrough(PUBKEYHASH): Error calculating public key hash. Aborting: %s", sec_errstr(crtn));
		return crtn;
	}
	if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) {
		/* created in refKeyToRaw().... */
		CSSM_FreeKey(cspHand, NULL, &rawKeyToDigest, CSSM_FALSE);
	}
	CSSM_DeleteContext(ccHand);
	CSSM_ModuleDetach(rawCspHand);

	/*
	 * Replace Label attr data with hash.
	 * NOTE: the module which allocated this attribute data - a DL -
	 * was loaded and attached by the Sec layer, not by us. Thus
	 * we can't use the memory allocator functions *we* used when
	 * attaching to the CSPDL - we have to use the ones
	 * which the Sec layer registered with the DL.
	 */
	CSSM_API_MEMORY_FUNCS memFuncs;
	crtn = CSSM_GetAPIMemoryFunctions(dlDbHand.DLHandle, &memFuncs);
	if(crtn) {
        sec_error("CSSM_GetAPIMemoryFunctions(DLHandle): Error: %s", sec_errstr(crtn));
		/* oh well, leak and continue */
	}
	else {
		memFuncs.free_func(attr.Value->Data, memFuncs.AllocRef);
		memFuncs.free_func(attr.Value, memFuncs.AllocRef);
	}
	attr.Value = keyDigest;

	/* modify key attributes */
	crtn = CSSM_DL_DataModify(dlDbHand,
			CSSM_DL_DB_RECORD_PRIVATE_KEY,
			record,
			&recordAttrs,
            NULL,				// DataToBeModified
			CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
	if(crtn) {
        sec_error("CSSM_DL_DataModify(PUBKEYHASH): Error setting public key hash. Aborting: %s", sec_errstr(crtn));
		return crtn;
	}
	crtn = CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
	if(crtn) {
        sec_error("CSSM_DL_DataAbortQuery: Error while stopping query: %s", sec_errstr(crtn));
		/* let's keep going in this case */
	}
	crtn = CSSM_DL_FreeUniqueRecord(dlDbHand, record);
	if(crtn) {
        sec_error("CSSM_DL_FreeUniqueRecord: Error while freeing record: %s", sec_errstr(crtn));
		/* let's keep going in this case */
		crtn = CSSM_OK;
	}

	/* free resources */
    if (keyDigest)
    {
        srAppFree(keyDigest->Data, NULL);
        srAppFree(keyDigest, NULL);
    }
	return CSSM_OK;
}