/***
 *** Issuer Name, Subject Name (encoded, NON-normalized version)
 *** Format = CSSM_DATA containing the DER encoding of the name
 ***/
static bool getFieldSubjectStd(
	DecodedItem		 	&item,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue)	// RETURNED
{
	if(index != 0) {
		return false;
	}
	const DecodedCert &cert = dynamic_cast<const DecodedCert &>(item);
	fieldValue.copy(cert.mCert.tbs.derSubject);
	numFields = 1;
	return true;
}
/***
 *** Signature
 *** Format = raw bytes
 *** read-only
 ***/
static bool getField_Signature (
	DecodedItem		 	&item,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue)	// RETURNED
{
	const DecodedCert &cert = dynamic_cast<const DecodedCert &>(item);
	const CSSM_DATA &sigBits = cert.mCert.signature;
	if(!tbsGetCheck(sigBits.Data, index)) {
		return false;
	}
	fieldValue.copy(sigBits.Data, (sigBits.Length + 7) / 8);
	numFields = 1;
	return true;
}
/***
 *** Serial Number
 *** Format = DER-encoded int, variable length
 ***/
static bool getField_SerialNumber (
	DecodedItem		 	&item,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue)	// RETURNED
{
	const DecodedCert &cert = dynamic_cast<const DecodedCert &>(item);
	const CSSM_DATA &sn = cert.mCert.tbs.serialNumber;
	if(!tbsGetCheck(sn.Data, index)) {
		return false;
	}
	fieldValue.copy(sn.Data, sn.Length);
	numFields = 1;
	return true;
}
/***
 *** key info from CSSM_KEY
 *** Format = CSSM_KEY
 ***/
static bool getField_PublicKeyStruct (
	DecodedItem		 	&item,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue)	// RETURNED
{
	const DecodedCert &cert = dynamic_cast<const DecodedCert &>(item);
	if(!tbsGetCheck(cert.mCert.tbs.subjectPublicKeyInfo.subjectPublicKey.Data,
			index)) {
		return false;
	}
	CSSM_KEY_PTR cssmKey = cert.extractCSSMKey(fieldValue.allocator);
	fieldValue.set(reinterpret_cast<uint8 *>(cssmKey), sizeof(CSSM_KEY));
	numFields = 1;
	return true;
}
/***
 *** Version
 *** Format = DER-encoded int (max of four bytes in this case)
 ***/
static bool getField_Version (
	DecodedItem		 	&item,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue)	// RETURNED
{
	const DecodedCert &cert = dynamic_cast<const DecodedCert &>(item);
	const CSSM_DATA &vers = cert.mCert.tbs.version;
	if(!tbsGetCheck(vers.Data, index)) {
		/* not present, optional */
		return false;
	}
	fieldValue.copy(vers.Data, vers.Length);
	numFields = 1;
	return true;
}
static bool getField_IssuerUniqueId (
	DecodedItem		 	&item,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue)	// RETURNED
{
	const DecodedCert &cert = dynamic_cast<const DecodedCert &>(item);
	const CSSM_DATA &srcBits = cert.mCert.tbs.issuerID;
	if(!tbsGetCheck(srcBits.Data, index)) {
		return false;
	}

	/* That CSSM_DATA is a decoded BITSTRING; its length is in bits */
	CSSM_DATA tmp = srcBits;
	tmp.Length = (tmp.Length + 7) / 8;
	fieldValue.copy(tmp.Data, tmp.Length);
	numFields = 1;
	return true;
}
static void freeField_PublicKeyStruct (
	CssmOwnedData		&fieldValue)
{
	CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR)fieldValue.data();
	CL_freeCSSMKey(cssmKey, fieldValue.allocator, false);
}
/* 
 * Infer DescriptiveData (i.e., comment) from a SecKeyRef's PrintName
 * attribute.
 */
void impExpOpensshInferDescData(
	SecKeyRef keyRef,
	CssmOwnedData &descData)
{
	OSStatus ortn;
	SecKeychainAttributeInfo attrInfo;
	SecKeychainAttrType	attrType = kSecKeyPrintName;
	attrInfo.count = 1;
	attrInfo.tag = &attrType;
	attrInfo.format = NULL;	
	SecKeychainAttributeList *attrList = NULL;
	
	ortn = SecKeychainItemCopyAttributesAndData(
		(SecKeychainItemRef)keyRef, 
		&attrInfo,
		NULL,			// itemClass
		&attrList, 
		NULL,			// don't need the data
		NULL);
	if(ortn) {
		SecSSHDbg("SecKeychainItemCopyAttributesAndData returned %ld", (unsigned long)ortn);
		return;
	}
	/* subsequent errors to errOut: */
	SecKeychainAttribute *attr = attrList->attr;
		
	/*
	 * On a previous import, we would have set this to something like 
	 * "OpenSSHv2 Public Key: comment".
	 * We want to strip off everything up to the actual comment.
	 */
	unsigned toStrip = 0;	
	
	/* min length of attribute value for this code to be meaningful */
	unsigned len = strlen(SSHv2_PUB_KEY_NAME) + 1;	
	char *printNameStr = NULL;
	if(len < attr->length) {
		printNameStr = (char *)malloc(attr->length + 1);
		memmove(printNameStr, attr->data, attr->length);
		printNameStr[attr->length] = '\0';
		if(strstr(printNameStr, SSHv2_PUB_KEY_NAME) == printNameStr) {
			toStrip = strlen(SSHv2_PUB_KEY_NAME);
		}
		else if(strstr(printNameStr, SSHv1_PUB_KEY_NAME) == printNameStr) {
			toStrip = strlen(SSHv1_PUB_KEY_NAME);
		}
		else if(strstr(printNameStr, SSHv1_PRIV_KEY_NAME) == printNameStr) {
			toStrip = strlen(SSHv1_PRIV_KEY_NAME);
		}
		if(toStrip) {
			/* only strip if we have ": " after toStrip bytes */
			if((printNameStr[toStrip] == ':') && (printNameStr[toStrip+1] == ' ')) {
				toStrip += 2;
			}
		}
	}
	if(printNameStr) {
		free(printNameStr);
	}
	len = attr->length;

	unsigned char *attrVal;

	if(len < toStrip) {
		SecSSHDbg("impExpOpensshInferDescData: string parse screwup");
		goto errOut;
	}
	if(len > toStrip) {
		/* Normal case of stripping off leading header */
		len -= toStrip;
	}
	else {
		/* 
		 * If equal, then the attr value *is* "OpenSSHv2 Public Key: " with 
		 * no comment. Not sure how that could happen, but let's be careful.
		 */
		toStrip = 0;
	}
	
	attrVal = ((unsigned char *)attr->data) + toStrip;
	descData.copy(attrVal, len);
errOut:
	SecKeychainItemFreeAttributesAndData(attrList, NULL);
	return;
}