text: /*
   * Copyright (c) 2002,2011,2014 Apple Inc. All Rights Reserved.
   * 
   * The contents of this file constitute Original Code as defined in and are
   * subject to the Apple Public Source License Version 1.2 (the 'License').
   * You may not use this file except in compliance with the License. Please obtain
   * a copy of the License at http://www.apple.com/publicsource and read it before
   * using this file.
   * 
   * This Original Code and all software distributed under the License are
   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
   * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
   * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
   * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
   * specific language governing rights and limitations under the License.
   */
  
  
  /*
   * tpCredRequest.cpp - credential request functions SubmitCredRequest, 
   *                     RetrieveCredResult 
   *
   */
   
  #include "AppleTPSession.h"
  #include "certGroupUtils.h"
  #include "tpdebugging.h"
  #include "tpTime.h"
  #include <Security/oidsalg.h>
  #include <Security/oidsattr.h>
  #include <Security/oidscert.h>
  #include <Security/cssmapple.h>
  #include <security_utilities/debugging.h>
  #include <Security/cssmapple.h>
  #include <assert.h>
  
  #define tpCredDebug(args...)	secdebug("tpCred", ## args)
  
  /*
   * Build up a CSSM_X509_NAME from an arbitrary list of name/OID pairs. 
   * We do one a/v pair per RDN. 
   */
  CSSM_X509_NAME * AppleTPSession::buildX509Name(
  	const CSSM_APPLE_TP_NAME_OID *nameArray,
  	unsigned numNames)
  {
  	CSSM_X509_NAME *top = (CSSM_X509_NAME *)malloc(sizeof(CSSM_X509_NAME));
  	top->numberOfRDNs = numNames;
  	if(numNames == 0) {
  		/* legal! */
  		top->RelativeDistinguishedName = NULL;
  		return top;
  	}
  	top->RelativeDistinguishedName = 
  		(CSSM_X509_RDN_PTR)malloc(sizeof(CSSM_X509_RDN) * numNames);
  	CSSM_X509_RDN_PTR rdn;
  	const CSSM_APPLE_TP_NAME_OID *nameOid;
  	unsigned nameDex;
  	for(nameDex=0; nameDex<numNames; nameDex++) {
  		rdn = &top->RelativeDistinguishedName[nameDex];
  		nameOid = &nameArray[nameDex];
  		rdn->numberOfPairs = 1;
  		rdn->AttributeTypeAndValue = (CSSM_X509_TYPE_VALUE_PAIR_PTR)
  			malloc(sizeof(CSSM_X509_TYPE_VALUE_PAIR));
  		CSSM_X509_TYPE_VALUE_PAIR_PTR atvp = rdn->AttributeTypeAndValue;
  		tpCopyCssmData(*this, nameOid->oid, &atvp->type);
  		atvp->value.Length = strlen(nameOid->string);
  		if(tpCompareOids(&CSSMOID_CountryName, nameOid->oid)) {
  			/* 
  			 * Country handled differently per RFC 3280 - must be printable,
  			 * max of two characters in length
  			 */
  			if(atvp->value.Length > 2) {
  				CssmError::throwMe(CSSMERR_TP_INVALID_DATA);
  			}
  			for(unsigned dex=0; dex<atvp->value.Length; dex++) {
  				int c = nameOid->string[dex];
  				if(!isprint(c) || (c == EOF)) {
  					CssmError::throwMe(CSSMERR_TP_INVALID_DATA);
  				}
  			}
  			atvp->valueType = BER_TAG_PRINTABLE_STRING;
  		}
  		/* other special cases per RFC 3280 */
  		else if(tpCompareOids(&CSSMOID_DNQualifier, nameOid->oid)) {
  			atvp->valueType = BER_TAG_PRINTABLE_STRING;
  		}
  		else if(tpCompareOids(&CSSMOID_SerialNumber, nameOid->oid)) {
  			atvp->valueType = BER_TAG_PRINTABLE_STRING;
  		}
  		else if(tpCompareOids(&CSSMOID_EmailAddress, nameOid->oid)) {
  			atvp->valueType = BER_TAG_IA5_STRING;
  		}
  		else {
  			/* Default type */
  			atvp->valueType = BER_TAG_PKIX_UTF8_STRING;
  		}
  		atvp->value.Data = (uint8 *)malloc(atvp->value.Length);
  		memmove(atvp->value.Data, nameOid->string, atvp->value.Length);
  	}
  	return top;
  }
  
  /* free the CSSM_X509_NAME obtained from buildX509Name */
  void AppleTPSession::freeX509Name(
  	CSSM_X509_NAME *top)
  {
  	if(top == NULL) {
  		return;
  	}
  	unsigned nameDex;
  	CSSM_X509_RDN_PTR rdn;
  	for(nameDex=0; nameDex<top->numberOfRDNs; nameDex++) {
  		rdn = &top->RelativeDistinguishedName[nameDex];
  		if(rdn->AttributeTypeAndValue) {
  			for(unsigned aDex=0; aDex<rdn->numberOfPairs; aDex++) {
  				CSSM_X509_TYPE_VALUE_PAIR_PTR atvp = 
  					&rdn->AttributeTypeAndValue[aDex];
  				free(atvp->type.Data);
  				free(atvp->value.Data);
  			}
  			free(rdn->AttributeTypeAndValue);
  		}
  	}
  	free(top->RelativeDistinguishedName);
  	free(top);
  }
  
  /* Obtain a CSSM_X509_TIME representing "now" plus specified seconds */
  
  /* 
   * Although RFC 2459, *the* spec for X509 certs, allows for not before/after
   * times to be expressed in ther generalized (4-digit year) or UTC (2-digit year
   * with implied century rollover), IE 5 on Mac will not accept the generalized
   * format.
   */
  #define TP_FOUR_DIGIT_YEAR		0
  #if		TP_FOUR_DIGIT_YEAR
  #define TP_TIME_FORMAT 	TIME_GEN
  #define TP_TIME_TAG		BER_TAG_GENERALIZED_TIME
  #else
  #define TP_TIME_FORMAT 	TIME_UTC
  #define TP_TIME_TAG		BER_TAG_UTC_TIME
  #endif	/* TP_FOUR_DIGIT_YEAR */
  
  CSSM_X509_TIME * AppleTPSession::buildX509Time(
  	unsigned secondsFromNow)
  {
  	CSSM_X509_TIME *xtime = (CSSM_X509_TIME *)malloc(sizeof(CSSM_X509_TIME));
  	xtime->timeType = TP_TIME_TAG;
  	char *ts = (char *)malloc(GENERALIZED_TIME_STRLEN + 1);
  	{
  		StLock<Mutex> _(tpTimeLock());
  		timeAtNowPlus(secondsFromNow, TP_TIME_FORMAT, ts);
  	}
  	xtime->time.Data = (uint8 *)ts;
  	xtime->time.Length = strlen(ts);
  	return xtime;
  }
  
  /* Free CSSM_X509_TIME obtained in buildX509Time */
  void AppleTPSession::freeX509Time(
  	CSSM_X509_TIME	*xtime)
  {
  	if(xtime == NULL) {
  		return;
  	}
  	free((char *)xtime->time.Data);
  	free(xtime);
  }
  
  /*
   * Cook up a CSSM_DATA with specified integer, DER style (minimum number of
   * bytes, big-endian).
   */
  static void intToDER(
  	CSSM_INTPTR theInt,
  	CSSM_DATA &DER_Data,
  	Allocator &alloc)
  {
  	/*
   	 * Calculate length in bytes of encoded integer, minimum length of 1. 
  	 */
  	DER_Data.Length = 1;
  	uintptr_t unsignedInt = (uintptr_t)theInt;
  	while(unsignedInt > 0xff) {
  		DER_Data.Length++;
  		unsignedInt >>= 8;
  	}
  
  	/*
  	 * DER encoding requires top bit to be zero, else it's a negative number. 
  	 * Even though we're passing around integers as CSSM_INTPTR, they really are
   	 * always unsigned. 
  	 * unsignedInt contains the m.s. byte of theInt in its l.s. byte. 
  	 */
  	if(unsignedInt & 0x80) {
  		DER_Data.Length++;
  	}
  	
  	DER_Data.Data = (uint8 *)alloc.malloc(DER_Data.Length);
  	uint8 *dst = DER_Data.Data + DER_Data.Length - 1;
  	unsignedInt = (uintptr_t)theInt;
  	for(unsigned dex=0; dex<DER_Data.Length; dex++) {
  		*dst-- = unsignedInt & 0xff;
  		/* this shifts off to zero if we're adding a zero at the top */
  		unsignedInt >>= 8;
  	}
  }
  
  /* The reverse of the above. */
  static CSSM_INTPTR DERToInt(
  	const CSSM_DATA &DER_Data)
  {
  	CSSM_INTPTR rtn = 0;
  	uint8 *bp = DER_Data.Data;
  	for(unsigned dex=0; dex<DER_Data.Length; dex++) {
  		rtn <<= 8;
  		rtn |= *bp++;
  	}
  	return rtn;
  }
  
  /* Convert a reference key to a raw key. */
  void AppleTPSession::refKeyToRaw(
  	CSSM_CSP_HANDLE	cspHand,
  	const CSSM_KEY	*refKey,	
  	CSSM_KEY_PTR	rawKey)			// RETURNED
  {
  	CSSM_CC_HANDLE		ccHand;
  	CSSM_RETURN			crtn;
  	CSSM_ACCESS_CREDENTIALS	creds;
  	
  	memset(rawKey, 0, sizeof(CSSM_KEY));
  	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
  	crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
  			CSSM_ALGID_NONE,
  			CSSM_ALGMODE_NONE,
  			&creds,				// passPhrase
  			NULL,				// wrapping key
  			NULL,				// init vector
  			CSSM_PADDING_NONE,	// Padding
  			0,					// Params
  			&ccHand);
  	if(crtn) {
  		tpCredDebug("AppleTPSession::refKeyToRaw: context err");
  		CssmError::throwMe(crtn);
  	}
  	
  	crtn = CSSM_WrapKey(ccHand,
  		&creds,
  		refKey,
  		NULL,			// DescriptiveData
  		rawKey);
  	if(crtn != CSSM_OK) {
  		tpCredDebug("AppleTPSession::refKeyToRaw: wrapKey err");
  		CssmError::throwMe(crtn);
  	}
  	CSSM_DeleteContext(ccHand);
  }
  
  
  /*
   * 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;
      
      /* 
       * Set Signature Algorithm OID and parameters
       */
  	algId.algorithm = sigOid;
      
      /* NULL params - skip for ECDSA */
      CSSM_ALGORITHMS algorithmType = 0;
      cssmOidToAlg(&sigOid, &algorithmType);
      switch(algorithmType) {
          case CSSM_ALGID_SHA1WithECDSA:
          case CSSM_ALGID_SHA224WithECDSA:
          case CSSM_ALGID_SHA256WithECDSA:
          case CSSM_ALGID_SHA384WithECDSA:
          case CSSM_ALGID_SHA512WithECDSA:
          case CSSM_ALGID_ECDSA_SPECIFIED:
              algId.parameters.Data = NULL;
              algId.parameters.Length = 0;
              break;
          default:
              static const uint8 encNull[2] = { SEC_ASN1_NULL, 0 };
              CSSM_DATA encNullData;
              encNullData.Data = (uint8 *)encNull;
              encNullData.Length = 2;
              
              algId.parameters = encNullData;
              break;
      }
  
  	
  	/*
  	 * 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);
  	}
  }
  
  /* given a cert and a ReferenceIdentifier, fill in ReferenceIdentifier and 
   * add it and the cert to tpCredMap. */
  void AppleTPSession::addCertToMap(
  	const CSSM_DATA		*cert,
  	CSSM_DATA_PTR		refId)
  {
  	StLock<Mutex> _(tpCredMapLock);
  
  	TpCredHandle hand = reinterpret_cast<TpCredHandle>(cert);
  	intToDER(hand, *refId, *this);
  	tpCredMap[hand] = cert;
  }
  	
  /* given a ReferenceIdentifier, obtain associated cert and remove from the map */
  CSSM_DATA_PTR AppleTPSession::getCertFromMap(
  	const CSSM_DATA *refId)
  {
  	StLock<Mutex> _(tpCredMapLock);
  	CSSM_DATA_PTR rtn = NULL;
  	
  	if((refId == NULL) || (refId->Data == NULL)) {
  		return NULL;
  	}
  	TpCredHandle hand = DERToInt(*refId);
  	credMap::iterator it = tpCredMap.find(hand);
  	if(it == tpCredMap.end()) {
  		return NULL;
  	}
  	rtn = const_cast<CSSM_DATA *>(it->second);
  	tpCredMap.erase(hand);
  	return rtn;
  }
  
  /*
   * 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);
  	}
  }
  
  /*
   * Submit cred (cert) request. Currently the only form of request we
   * handle is the basis "sign this cert with key right now", with policy OI
   * CSSMOID_APPLE_TP_LOCAL_CERT_GEN.
   */
  void AppleTPSession::SubmitCredRequest(
  	const CSSM_TP_AUTHORITY_ID *PreferredAuthority,
  	CSSM_TP_AUTHORITY_REQUEST_TYPE RequestType,
  	const CSSM_TP_REQUEST_SET &RequestInput,
  	const CSSM_TP_CALLERAUTH_CONTEXT *CallerAuthContext,
  	sint32 &EstimatedTime,
  	CssmData &ReferenceIdentifier)
  {
  	/* free all of these on return if non-NULL */
  	CSSM_DATA_PTR certTemplate = NULL;
  	CSSM_X509_TIME_PTR notBeforeX509 = NULL;
  	CSSM_X509_TIME_PTR notAfterX509 = NULL;
  	CSSM_X509_NAME_PTR subjectX509 = NULL;
  	CSSM_X509_NAME_PTR issuerX509 = NULL;
  	CSSM_X509_EXTENSION_PTR extens509 = NULL;
  	CSSM_CC_HANDLE sigContext = 0;
  	
  	/* this gets saved on success */
  	CSSM_DATA_PTR signedCert = NULL;
  	
  	/* validate rather limited set of input args */
  	if(PreferredAuthority != NULL) {
  		CssmError::throwMe(CSSMERR_TP_INVALID_AUTHORITY);
  	}
  	if(RequestType != CSSM_TP_AUTHORITY_REQUEST_CERTISSUE) {
  		CssmError::throwMe(CSSMERR_TP_UNSUPPORTED_SERVICE);
  	}
  	if(CallerAuthContext == NULL) {
  		CssmError::throwMe(CSSMERR_TP_INVALID_CALLERAUTH_CONTEXT_POINTER);
  	}
  	if((RequestInput.NumberOfRequests != 1) ||
  	   (RequestInput.Requests == NULL)) {
  		CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
  	}
  	
  	/* Apple-specific args */
  	const CSSM_TP_POLICYINFO *tpPolicy = &CallerAuthContext->Policy;
  	if((tpPolicy->NumberOfPolicyIds != 1) ||
  	   (tpPolicy->PolicyIds == NULL)) {
  		CssmError::throwMe(CSSMERR_TP_INVALID_CALLERAUTH_CONTEXT_POINTER);
  	}
  	if(tpCompareCssmData(&tpPolicy->PolicyIds->FieldOid,
  		&CSSMOID_APPLE_TP_CSR_GEN)) {
  		/* break out to CSR-specific code */
  		SubmitCsrRequest(RequestInput, CallerAuthContext, EstimatedTime, ReferenceIdentifier);
  		return;
  	}
  	else if(!tpCompareCssmData(&tpPolicy->PolicyIds->FieldOid,
  		&CSSMOID_APPLE_TP_LOCAL_CERT_GEN)) {
  		CssmError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS);
  	}
  
  	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)) {
  		CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
  	}
  	if((certReq->numExtensions != 0) & (certReq->extensions == NULL)) {
  		CssmError::throwMe(CSSMERR_TP_INVALID_POINTER);
  	}
  	
  	CSSM_RETURN ourRtn = CSSM_OK;
  	
  	try {
  		/* convert caller's friendly names and times to CDSA style */
  		subjectX509 = buildX509Name(certReq->subjectNames, certReq->numSubjectNames);
  		if(certReq->issuerNames != NULL) {
  			issuerX509 = buildX509Name(certReq->issuerNames, certReq->numIssuerNames);
  		}
  		else if(certReq->issuerNameX509) {
  			/* caller obtained this from an existing signer's cert */
  			issuerX509 = certReq->issuerNameX509;
  		}
  		else {
  			/* self-signed */
  			issuerX509 = subjectX509;
  		}
  		notBeforeX509 = buildX509Time(certReq->notBefore);
  		notAfterX509 = buildX509Time(certReq->notAfter);
  		
  		if(certReq->numExtensions != 0) { 
  			/* convert extensions array from CE_DataAndType to CSSM_X509_EXTENSION */
  			extens509 = (CSSM_X509_EXTENSION *)malloc(sizeof(CSSM_X509_EXTENSION) * 
  					certReq->numExtensions);
  			memset(extens509, 0, sizeof(CSSM_X509_EXTENSION) * 
  					certReq->numExtensions);
  			for(unsigned dex=0; dex<certReq->numExtensions; dex++) {
  				CSSM_X509_EXTENSION *extn = &extens509[dex];
  				CE_DataAndType *cdt = &certReq->extensions[dex];
  				void *parsedValue;
  				CSSM_OID extnId;
  				
  				switch(cdt->type) {
  					case DT_AuthorityKeyID:	
  						parsedValue = &cdt->extension.authorityKeyID;
  						extnId = CSSMOID_AuthorityKeyIdentifier;
  						break;
  					case DT_SubjectKeyID:		
  						parsedValue = &cdt->extension.subjectKeyID;
  						extnId = CSSMOID_SubjectKeyIdentifier;
  						break;
  					case DT_KeyUsage:				 
  						parsedValue = &cdt->extension.keyUsage;
  						extnId = CSSMOID_KeyUsage;
  						break;
  					case DT_SubjectAltName:			
  						parsedValue = &cdt->extension.subjectAltName;
  						extnId = CSSMOID_SubjectAltName;
  						break;
  					case DT_IssuerAltName:			
  						parsedValue = &cdt->extension.issuerAltName;
  						extnId = CSSMOID_IssuerAltName;
  						break;
  					case DT_ExtendedKeyUsage:		
  						parsedValue = &cdt->extension.extendedKeyUsage;
  						extnId = CSSMOID_ExtendedKeyUsage;
  						break;
  					case DT_BasicConstraints:		
  						parsedValue = &cdt->extension.basicConstraints;
  						extnId = CSSMOID_BasicConstraints;
  						break;
  					case DT_CertPolicies:			
  						parsedValue = &cdt->extension.certPolicies;
  						extnId = CSSMOID_CertificatePolicies;
  						break;
  					case DT_NetscapeCertType:		
  						parsedValue = &cdt->extension.netscapeCertType;
  						extnId = CSSMOID_NetscapeCertType;
  						break;
  					case DT_CrlDistributionPoints:		
  						parsedValue = &cdt->extension.crlDistPoints;
  						extnId = CSSMOID_CrlDistributionPoints;
  						break;
  					case DT_AuthorityInfoAccess:		
  						parsedValue = &cdt->extension.authorityInfoAccess;
  						extnId = CSSMOID_AuthorityInfoAccess;
  						break;
  					case DT_Other:		
  					default:
  						tpCredDebug("SubmitCredRequest: DT_Other not supported");
  						CssmError::throwMe(CSSMERR_TP_UNKNOWN_TAG);
  						// NOT REACHED
  				}
  				extn->extnId   			= extnId;
  				extn->critical 			= cdt->critical;
  				extn->format   			= CSSM_X509_DATAFORMAT_PARSED;
  				extn->value.parsedValue 	= parsedValue;
  				extn->BERvalue.Data = NULL;
  				extn->BERvalue.Length = 0;
  			}	/* for each extension */
  		} 		/* converting extensions */
  			
  		/* cook up the unsigned template */
  		makeCertTemplate(certReq->clHand,
  			certReq->cspHand,
  			certReq->serialNumber,
  			issuerX509,
  			subjectX509,
  			notBeforeX509,
  			notAfterX509,
  			certReq->certPublicKey,
  			certReq->signatureOid,
  			NULL,				// subjectUniqueID, not used here (yet)
  			NULL,				// issuerUniqueId
  			extens509,
  			certReq->numExtensions,
  			certTemplate);
  			
  		/* create signature context */		
  		ourRtn = CSSM_CSP_CreateSignatureContext(certReq->cspHand,
  				certReq->signatureAlg,
  				(CallerAuthContext ? CallerAuthContext->CallerCredentials : NULL),
  				certReq->issuerPrivateKey,
  				&sigContext);
  		if(ourRtn) {
  			tpCredDebug("CSSM_CSP_CreateSignatureContext returned %ld", (long)ourRtn);
  			CssmError::throwMe(ourRtn);
  		}
  		
  		signedCert = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA));
  		signedCert->Data = NULL;
  		signedCert->Length = 0;
  		ourRtn = CSSM_CL_CertSign(certReq->clHand,
  			sigContext,
  			certTemplate,		// CertToBeSigned
  			NULL,				// SignScope
  			0,					// ScopeSize,
  			signedCert);
  		if(ourRtn) {
  			tpCredDebug("CSSM_CL_CertSign returned %ld", (long)ourRtn);
  			CssmError::throwMe(ourRtn);
  		}
  		
  		/* save it for retrieval by RetrieveCredResult */
  		addCertToMap(signedCert, &ReferenceIdentifier);
  		EstimatedTime = 0;
  	}
  	catch (const CssmError &cerr) {
  		tpCredDebug("SubmitCredRequest: CSSM error %ld", (long)cerr.error);
  		ourRtn = cerr.error;
  	}
  	catch(...) {
  		tpCredDebug("SubmitCredRequest: unknown exception");
  		ourRtn = CSSMERR_TP_INTERNAL_ERROR;	// ??
  	}
  	
  	/* free reources */
  	tpFreeCssmData(*this, certTemplate, CSSM_TRUE);
  	freeX509Name(subjectX509);
  	if(certReq->issuerNames) {
  		freeX509Name(issuerX509);
  	}
  	/* else same as subject */
  	freeX509Time(notBeforeX509);
  	freeX509Time(notAfterX509);
  	if(extens509) {
  		free(extens509);
  	}
  	if(sigContext != 0) {
  		CSSM_DeleteContext(sigContext);
  	}
  	if(ourRtn) {
  		CssmError::throwMe(ourRtn);
  	}
  }
  
  void AppleTPSession::RetrieveCredResult(
  	const CssmData &ReferenceIdentifier,
  	const CSSM_TP_CALLERAUTH_CONTEXT *CallerAuthCredentials,
  	sint32 &EstimatedTime,
  	CSSM_BOOL &ConfirmationRequired,
  	CSSM_TP_RESULT_SET_PTR &RetrieveOutput)
  {
  	CSSM_DATA *cert = getCertFromMap(&ReferenceIdentifier);
  	
  	if(cert == NULL) {
  		tpCredDebug("RetrieveCredResult: refId not found");
  		CssmError::throwMe(CSSMERR_TP_INVALID_IDENTIFIER);
  	}
  	
  	/* CSSM_TP_RESULT_SET.Results points to a CSSM_ENCODED_CERT */
  	CSSM_ENCODED_CERT *encCert = (CSSM_ENCODED_CERT *)malloc(sizeof(CSSM_ENCODED_CERT));
  	encCert->CertType = CSSM_CERT_X_509v3;
  	encCert->CertEncoding = CSSM_CERT_ENCODING_DER;
  	
  	/* 
  	 * caller must free all three:
  	 *   CSSM_TP_RESULT_SET_PTR RetrieveOutput
  	 *   RetrieveOutput->Results (CSSM_ENCODED_CERT *encCert)
  	 *   encCert->CertBlob.Data (the actual cert)
  	 * We free:
  	 * 	 cert 					-- mallocd in SubmitCredRequest
  	 */
  	encCert->CertBlob = *cert;
  	RetrieveOutput = (CSSM_TP_RESULT_SET_PTR)malloc(
  		sizeof(CSSM_TP_RESULT_SET));
  	RetrieveOutput->Results = encCert;
  	RetrieveOutput->NumberOfResults = 1;
  	ConfirmationRequired = CSSM_FALSE;
  	free(cert);	
  	EstimatedTime = 0;
  }
  