/*
 * SubmitCredRequest, CSR form.
 */
void AppleTPSession::SubmitCsrRequest(
	const CSSM_TP_REQUEST_SET &RequestInput,
	const CSSM_TP_CALLERAUTH_CONTEXT *CallerAuthContext,
	sint32 &EstimatedTime,						// RETURNED
	CssmData &ReferenceIdentifier)				// RETURNED
{
	CSSM_DATA_PTR	csrPtr = NULL;
	CSSM_CC_HANDLE 	sigHand = 0;
	CSSM_APPLE_CL_CSR_REQUEST csrReq;
	
	memset(&csrReq, 0, sizeof(csrReq));

	/* for now we're using the same struct for input as the the normal
	 * X509 cert request. */
	CSSM_APPLE_TP_CERT_REQUEST *certReq =
		(CSSM_APPLE_TP_CERT_REQUEST *)RequestInput.Requests;
	if((certReq->cspHand == 0) || 
	   (certReq->clHand == 0) ||
	   (certReq->certPublicKey == NULL) ||
	   (certReq->issuerPrivateKey == NULL) ||
	   (certReq->signatureOid.Data == NULL)) {
		CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
	}
	
	/* convert ref public key to raw per CL requirements */
	const CSSM_KEY *subjectPubKey = certReq->certPublicKey;
	const CSSM_KEY *actPubKey = NULL;
	CSSM_BOOL freeRawKey = CSSM_FALSE;
	CSSM_KEY rawPubKey;
	
	switch(subjectPubKey->KeyHeader.BlobType) {
		case CSSM_KEYBLOB_RAW:
			actPubKey = subjectPubKey;
			break;
		case CSSM_KEYBLOB_REFERENCE:
			refKeyToRaw(certReq->cspHand, subjectPubKey, &rawPubKey);
			actPubKey = &rawPubKey;
			freeRawKey = CSSM_TRUE;
			break;
		default:
			tpCredDebug("SubmitCsrRequest: bad key blob type (%u)",
				(unsigned)subjectPubKey->KeyHeader.BlobType);
			CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
	}

	/* cook up a CL-passthrough-specific request */
	csrReq.subjectNameX509 	 = buildX509Name(certReq->subjectNames, 
											certReq->numSubjectNames);
	csrReq.signatureAlg 	 = certReq->signatureAlg;
	csrReq.signatureOid 	 = certReq->signatureOid;
	csrReq.cspHand 			 = certReq->cspHand;
	csrReq.subjectPublicKey  = actPubKey;
	csrReq.subjectPrivateKey = certReq->issuerPrivateKey;
	csrReq.challengeString   = certReq->challengeString;
	
	/* A crypto handle to pass to the CL */
	CSSM_RETURN crtn;
	crtn = CSSM_CSP_CreateSignatureContext(certReq->cspHand,
			certReq->signatureAlg,
			(CallerAuthContext ? CallerAuthContext->CallerCredentials : NULL),
			certReq->issuerPrivateKey,
			&sigHand);
	if(crtn) {
		tpCredDebug("CSSM_CSP_CreateSignatureContext returned %ld", (long)crtn);
		goto abort;
	}
	
	/* down to the CL to do the actual work */
	crtn = CSSM_CL_PassThrough(certReq->clHand,
		sigHand,
		CSSM_APPLEX509CL_OBTAIN_CSR,
		&csrReq,
		(void **)&csrPtr);
	if(crtn) {
		tpCredDebug("CSSM_CL_PassThrough returned %ld", (long)crtn);
		goto abort;
	}

	/* save it for retrieval by RetrieveCredResult */
	addCertToMap(csrPtr, &ReferenceIdentifier);
	EstimatedTime = 0;

abort:
	/* free local resources */
	if(csrReq.subjectNameX509) {
		freeX509Name(csrReq.subjectNameX509);
	}
	if(sigHand) {
		CSSM_DeleteContext(sigHand);
	}
	if(freeRawKey) {
		tpFreeCssmData(*this, &rawPubKey.KeyData, CSSM_FALSE);
	}
	if(crtn) {
		CssmError::throwMe(crtn);
	}
}
/*
 * 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;
}
/*
 * Cook up an unsigned cert.
 * This is just a wrapper for CSSM_CL_CertCreateTemplate().
 */
void AppleTPSession::makeCertTemplate(
	/* required */
	CSSM_CL_HANDLE			clHand,
	CSSM_CSP_HANDLE			cspHand,		// for converting ref to raw key
	uint32					serialNumber,
	const CSSM_X509_NAME	*issuerName,	
	const CSSM_X509_NAME	*subjectName,
	const CSSM_X509_TIME	*notBefore,	
	const CSSM_X509_TIME	*notAfter,	
	const CSSM_KEY			*subjectPubKey,
	const CSSM_OID			&sigOid,		// e.g., CSSMOID_SHA1WithRSA
	/* optional */
	const CSSM_DATA			*subjectUniqueId,
	const CSSM_DATA			*issuerUniqueId,
	CSSM_X509_EXTENSION		*extensions,
	unsigned				numExtensions,
	CSSM_DATA_PTR			&rawCert)
{
	CSSM_FIELD		*certTemp;		
	unsigned		fieldDex = 0;			// index into certTemp
	CSSM_DATA		serialDER = {0, NULL};	// serial number, DER format
	CSSM_DATA		versionDER = {0, NULL};
	unsigned		extNum;
	CSSM_X509_ALGORITHM_IDENTIFIER algId;
	const CSSM_KEY	*actPubKey;
	CSSM_KEY		rawPubKey;
	CSSM_BOOL		freeRawKey = CSSM_FALSE;
	
	rawCert = NULL;
	algId.algorithm = sigOid;
	algId.parameters.Data = NULL;
	algId.parameters.Length = 0;
	
	/*
	 * Convert possible ref public key to raw format as required by CL.
	 */
	switch(subjectPubKey->KeyHeader.BlobType) {
		case CSSM_KEYBLOB_RAW:
			actPubKey = subjectPubKey;
			break;
		case CSSM_KEYBLOB_REFERENCE:
			refKeyToRaw(cspHand, subjectPubKey, &rawPubKey);
			actPubKey = &rawPubKey;
			freeRawKey = CSSM_TRUE;
			break;
		default:
			tpCredDebug("CSSM_CL_CertCreateTemplate: bad key blob type (%u)",
				(unsigned)subjectPubKey->KeyHeader.BlobType);
			CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
	}
			

	/*
	 * version, always 2 (X509v3)
	 * serialNumber thru subjectPubKey
	 */
	unsigned numFields = 8 + numExtensions;
	if(subjectUniqueId) {
		numFields++;
	}
	if(issuerUniqueId) {
		numFields++;
	}

	certTemp = (CSSM_FIELD *)malloc(sizeof(CSSM_FIELD) * numFields);

	 
	/* version */
	intToDER(2, versionDER, *this);
	certTemp[fieldDex].FieldOid = CSSMOID_X509V1Version;
	certTemp[fieldDex++].FieldValue = versionDER;
	
	/* serial number */
	intToDER(serialNumber, serialDER, *this);
	certTemp[fieldDex].FieldOid = CSSMOID_X509V1SerialNumber;
	certTemp[fieldDex++].FieldValue = serialDER;

	/* subject and issuer name  */
	certTemp[fieldDex].FieldOid = CSSMOID_X509V1IssuerNameCStruct;
	certTemp[fieldDex].FieldValue.Data = (uint8 *)issuerName;
	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_NAME);
	
	certTemp[fieldDex].FieldOid = CSSMOID_X509V1SubjectNameCStruct;
	certTemp[fieldDex].FieldValue.Data = (uint8 *)subjectName;
	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_NAME);

	/* not before/after */
	certTemp[fieldDex].FieldOid = CSSMOID_X509V1ValidityNotBefore;
	certTemp[fieldDex].FieldValue.Data = (uint8 *)notBefore;
	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_TIME);

	certTemp[fieldDex].FieldOid = CSSMOID_X509V1ValidityNotAfter;
	certTemp[fieldDex].FieldValue.Data = (uint8 *)notAfter;
	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_TIME);

	/* the subject key */
	certTemp[fieldDex].FieldOid = CSSMOID_CSSMKeyStruct;
	certTemp[fieldDex].FieldValue.Data = (uint8 *)actPubKey;
	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_KEY);

	/* signature algorithm */
	certTemp[fieldDex].FieldOid = CSSMOID_X509V1SignatureAlgorithmTBS;
	certTemp[fieldDex].FieldValue.Data = (uint8 *)&algId;
	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_ALGORITHM_IDENTIFIER);
	
	/* subject/issuer unique IDs */
	if(subjectUniqueId != 0) {
		certTemp[fieldDex].FieldOid = CSSMOID_X509V1CertificateSubjectUniqueId;
		certTemp[fieldDex++].FieldValue = *subjectUniqueId;
	}
	if(issuerUniqueId != 0) {
		certTemp[fieldDex].FieldOid = CSSMOID_X509V1CertificateIssuerUniqueId;
		certTemp[fieldDex++].FieldValue = *issuerUniqueId;
	}

	for(extNum=0; extNum<numExtensions; extNum++) {
		CSSM_X509_EXTENSION_PTR ext = &extensions[extNum];
		if(ext->format == CSSM_X509_DATAFORMAT_PARSED) {
			certTemp[fieldDex].FieldOid = ext->extnId;
		}
		else {
			certTemp[fieldDex].FieldOid = CSSMOID_X509V3CertificateExtensionCStruct;
		}
		certTemp[fieldDex].FieldValue.Data = (uint8 *)ext;
		certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_EXTENSION);
	}
	assert(fieldDex == numFields);
	
	/*
	 * OK, here we go
	 */
	rawCert = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA));
	rawCert->Data = NULL;
	rawCert->Length = 0;
	CSSM_RETURN crtn = CSSM_CL_CertCreateTemplate(clHand,
		fieldDex,
		certTemp,
		rawCert);
	if(crtn) {
		tpCredDebug("CSSM_CL_CertCreateTemplate returned %ld", (long)crtn);
		free(rawCert->Data);
		free(rawCert);
		rawCert = NULL;
	}

	/* free the stuff we mallocd to get here */
	free(serialDER.Data);
	free(versionDER.Data);
	free(certTemp);
	if(freeRawKey) {
		tpFreeCssmData(*this, &rawPubKey.KeyData, CSSM_FALSE);
	}
	if(crtn) {
		CssmError::throwMe(crtn);
	}
}