CertFields.cpp   [plain text]


/*
 * Copyright (c) 2000-2001 Apple Computer, 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.
 */


/*
 * CertFields.cpp - convert between NSS-based Certificate components and CDSA-style
 *                  fields. A major component of DecodedCert.
 *
 * Created 9/1/2000 by Doug Mitchell. 
 * Copyright (c) 2000 by Apple Computer. 
 */

#include "DecodedCert.h"
#include "cldebugging.h"
#include "CLCertExtensions.h"
#include "clNssUtils.h"
#include "clNameUtils.h"
#include "CLFieldsCommon.h"
#include <Security/utilities.h>
#include <Security/oidscert.h>
#include <Security/cssmerr.h>
#include <Security/x509defs.h>

/***
 *** 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 void setField_Version (
	DecodedItem			&item,
	const CssmData		&fieldValue)
{
	DecodedCert &cert = dynamic_cast<DecodedCert &>(item);
	CSSM_DATA &vers = cert.mCert.tbs.version;
	tbsSetCheck(vers.Data, fieldValue, 0, "version");
	cert.coder().allocCopyItem(fieldValue, vers);
}


#if	this_is_a_template
/***
 *** Version
 *** Format = DER-encoded int (always 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);
	tbsGetCheck(cert.certificateToSign->version, index);
}
static void setField_Version (
	DecodedItem			&item,
	const CssmData		&fieldValue)
{
	DecodedCert &cert = dynamic_cast<DecodedCert &>(item);
	tbsSetCheck(cert.certificateToSign->version, fieldValue, sizeof(uint32),
		"version");

}
static void freeField_Version (
	CssmOwnedData		&fieldValue)
{
}
#endif

/***
 *** 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;
}

static void setField_SerialNumber (
	DecodedItem			&item,
	const CssmData		&fieldValue)
{
	DecodedCert &cert = dynamic_cast<DecodedCert &>(item);
	CSSM_DATA &sn = cert.mCert.tbs.serialNumber;
	tbsSetCheck(sn.Data, fieldValue, 0, "SerialNumber");
	cert.coder().allocCopyItem(fieldValue, sn);
}

/*** issuer/subject
 *** Format = CSSM_X509_NAME
 *** class Name from sm_x501if
 ***/
static bool getField_Issuer (
	DecodedItem			&item,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue)	// RETURNED
{
	if(index != 0) {
		return false;
	}

	bool brtn;
	
	const DecodedCert &cert = dynamic_cast<const DecodedCert &>(item);
	try {
		brtn = getField_RDN_NSS(cert.mCert.tbs.issuer, fieldValue);
		if(brtn) {
			numFields = 1;
		}
	}
	catch (...) {
		freeField_RDN(fieldValue);
		throw;
	}
	return brtn;
}

static void setField_Issuer  (
	DecodedItem			&item,
	const CssmData		&fieldValue)
{
	DecodedCert &cert = dynamic_cast<DecodedCert &>(item);
	const CSSM_X509_NAME *cssmName = (const CSSM_X509_NAME *)fieldValue.Data;
	NSS_Name &nssName = cert.mCert.tbs.issuer;
	tbsSetCheck(nssName.rdns, fieldValue, sizeof(CSSM_X509_NAME),
		"IssuerName");
	CL_cssmNameToNss(*cssmName, nssName, cert.coder());
}

/*** subject ***/
static bool getField_Subject (
	DecodedItem		 	&item,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue)	// RETURNED
{
	if(index != 0) {
		return false;
	}

	bool brtn;
	
	const DecodedCert &cert = dynamic_cast<const DecodedCert &>(item);
	try {
		brtn = getField_RDN_NSS(cert.mCert.tbs.subject, fieldValue);
		if(brtn) {
			numFields = 1;
		}
	}
	catch (...) {
		freeField_RDN(fieldValue);
		throw;
	}
	return brtn;
}

static void setField_Subject  (
	DecodedItem			&item,
	const CssmData		&fieldValue)
{
	DecodedCert &cert = dynamic_cast<DecodedCert &>(item);
	const CSSM_X509_NAME *cssmName = (const CSSM_X509_NAME *)fieldValue.Data;
	NSS_Name &nssName = cert.mCert.tbs.subject;
	tbsSetCheck(nssName.rdns, fieldValue, sizeof(CSSM_X509_NAME),
		"SubjectName");
	CL_cssmNameToNss(*cssmName, nssName, cert.coder());
}

/***
 *** Issuer Name, Subject Name (normalized and encoded version)
 *** Format = CSSM_DATA containing the DER encoding of the normalized name
 ***/
static bool getFieldSubjectNorm(
	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);
	return getField_normRDN_NSS(cert.mCert.tbs.derSubject, numFields, 
		fieldValue);
}

static bool getFieldIssuerNorm(
	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);
	return getField_normRDN_NSS(cert.mCert.tbs.derIssuer, numFields, fieldValue);
}

/***
 *** 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;
}

static bool getFieldIssuerStd(
	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.derIssuer);
	numFields = 1;
	return true;
}

/***
 *** TBS AlgId, Signature AlgId
 *** Format = CSSM_X509_ALGORITHM_IDENTIFIER
 ***/
/* TBS AlgId */
static bool getField_TbsAlgId (
	DecodedItem		 	&item,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue)	// RETURNED
{
	const DecodedCert &cert = dynamic_cast<const DecodedCert &>(item);
	const CSSM_X509_ALGORITHM_IDENTIFIER &srcAlgId = cert.mCert.tbs.signature;
	if(!tbsGetCheck(srcAlgId.algorithm.Data, index)) {
		return false;
	}
	getField_AlgIdNSS(srcAlgId, fieldValue);
	numFields = 1;
	return true;
}

static void setField_TbsAlgId (
	DecodedItem			&item,
	const CssmData		&fieldValue)
{
	DecodedCert &cert = dynamic_cast<DecodedCert &>(item);
	CSSM_X509_ALGORITHM_IDENTIFIER &dstAlgId = cert.mCert.tbs.signature;
	tbsSetCheck(dstAlgId.algorithm.Data, fieldValue, 
		sizeof(CSSM_X509_ALGORITHM_IDENTIFIER), "TBS_AlgId");
	setField_AlgIdNSS(fieldValue, dstAlgId, cert.coder());
}

/* Cert AlgId - read only */
static bool getField_CertAlgId (
	DecodedItem		 	&item,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue)	// RETURNED
{
	const DecodedCert &cert = dynamic_cast<const DecodedCert &>(item);
	const CSSM_X509_ALGORITHM_IDENTIFIER &srcAlgId = cert.mCert.signatureAlgorithm;
	if(!tbsGetCheck(srcAlgId.algorithm.Data, index)) {
		return false;
	}
	getField_AlgIdNSS(srcAlgId, fieldValue);
	numFields = 1;
	return true;
}

/***
 *** Validity not before, not after
 *** Format: CSSM_X509_TIME
 ***/

/*** not before ***/
static bool getField_NotBefore (
	DecodedItem		 	&item,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue)	// RETURNED
{
	const DecodedCert &cert = dynamic_cast<const DecodedCert &>(item);
	const NSS_Time &srcTime = cert.mCert.tbs.validity.notBefore;
	return getField_TimeNSS(srcTime, index, numFields, fieldValue);
}

static void setField_NotBefore (
	DecodedItem			&item,
	const CssmData		&fieldValue)
{
	DecodedCert &cert = dynamic_cast<DecodedCert &>(item);
	NSS_Time &dstTime = cert.mCert.tbs.validity.notBefore;
	tbsSetCheck(dstTime.item.Data, fieldValue, 
		sizeof(CSSM_X509_TIME), "NotBefore");
	setField_TimeNSS(fieldValue, dstTime, cert.coder());
}

/*** not after ***/
static bool getField_NotAfter (
	DecodedItem		 	&item,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue)	// RETURNED
{
	const DecodedCert &cert = dynamic_cast<const DecodedCert &>(item);
	const NSS_Time &srcTime = cert.mCert.tbs.validity.notAfter;
	return getField_TimeNSS(srcTime, index, numFields, fieldValue);
}

static void setField_NotAfter (
	DecodedItem			&item,
	const CssmData		&fieldValue)
{
	DecodedCert &cert = dynamic_cast<DecodedCert &>(item);
	NSS_Time &dstTime = cert.mCert.tbs.validity.notAfter;
	tbsSetCheck(dstTime.item.Data, fieldValue, 
		sizeof(CSSM_X509_TIME), "NotAfter");
	setField_TimeNSS(fieldValue, dstTime, cert.coder());
}

/***
 *** Subject/issuer unique ID
 *** Format: Raw bytes. It's stored in the cert as an ASN bit string; the decoded
 *** bytes are present at this level (i.e., not tag and length in the bytes). 
 *** NOTE: this is not quite accurate in that we only provide byte-aligned size,
 *** not bit-aligned. This field is rarely if ever used so I think it's O, but
 *** beware.
 ***/
static bool getField_SubjectUniqueId (
	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.subjectID;
	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 setField_SubjectUniqueId (
	DecodedItem			&item,
	const CssmData		&fieldValue)
{
	DecodedCert &cert = dynamic_cast<DecodedCert &>(item);
	CSSM_DATA &dstBits = cert.mCert.tbs.subjectID;
	tbsSetCheck(dstBits.Data, fieldValue, 0, "SubjectUniqueID");
	cert.coder().allocCopyItem(fieldValue, dstBits);
	dstBits.Length *= 8;
}

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 setField_IssuerUniqueId (
	DecodedItem			&item,
	const CssmData		&fieldValue)
{
	DecodedCert &cert = dynamic_cast<DecodedCert &>(item);
	CSSM_DATA &dstBits = cert.mCert.tbs.issuerID;
	tbsSetCheck(dstBits.Data, fieldValue, 0, "IssuerUniqueID");
	cert.coder().allocCopyItem(fieldValue, dstBits);
	dstBits.Length *= 8;
}

/***
 *** Public key info
 *** Format = CSSM_X509_SUBJECT_PUBLIC_KEY_INFO
 ***/
static bool getField_PublicKeyInfo (
	DecodedItem		 	&item,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue)	// RETURNED
{
	const DecodedCert &cert = dynamic_cast<const DecodedCert &>(item);
	const CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &srcInfo = 
		cert.mCert.tbs.subjectPublicKeyInfo;
	if(!tbsGetCheck(srcInfo.subjectPublicKey.Data, index)) {
		return false;
	}

	CssmAllocator &alloc = fieldValue.allocator;
	fieldValue.malloc(sizeof(CSSM_X509_SUBJECT_PUBLIC_KEY_INFO));
	CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *dstInfo = 
		(CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)fieldValue.data();
		
	CL_copySubjPubKeyInfo(srcInfo, true,		// length in bits here
		*dstInfo, false,						// length in bytes
		alloc);

	numFields = 1;
	return true;
}

static void setField_PublicKeyInfo (
	DecodedItem			&item,
	const CssmData		&fieldValue)
{
	DecodedCert &cert = dynamic_cast<DecodedCert &>(item);
	CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &dstKeyInfo = 
		cert.mCert.tbs.subjectPublicKeyInfo;
	tbsSetCheck(dstKeyInfo.subjectPublicKey.Data, fieldValue, 
		sizeof(CSSM_X509_SUBJECT_PUBLIC_KEY_INFO), "PubKeyInfo");
		
	CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *srcKeyInfo = 
		(CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)fieldValue.Data;
	if((srcKeyInfo->subjectPublicKey.Data == NULL) ||
	   (srcKeyInfo->subjectPublicKey.Length == 0)) {
		CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER);
	}

	ArenaAllocator arenaAlloc(cert.coder());
	CL_copySubjPubKeyInfo(*srcKeyInfo, false,	// length in bytes here
		dstKeyInfo, true,						// length in bits
		arenaAlloc);
}

static void freeField_PublicKeyInfo (
	CssmOwnedData		&fieldValue)
{
	CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *cssmKeyInfo = 
		(CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)fieldValue.data();
	if(cssmKeyInfo == NULL) {
		return;
	}
	CssmAllocator &alloc = fieldValue.allocator;
	CL_freeCssmAlgId(&cssmKeyInfo->algorithm, alloc);
	alloc.free(cssmKeyInfo->subjectPublicKey.Data);
	memset(cssmKeyInfo, 0, sizeof(CSSM_X509_SUBJECT_PUBLIC_KEY_INFO));}

/***
 *** 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;
}

static void setField_PublicKeyStruct (
	DecodedItem			&item,
	const CssmData		&fieldValue)
{
	DecodedCert &cert = dynamic_cast<DecodedCert &>(item);
	CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &dstKeyInfo = 
		cert.mCert.tbs.subjectPublicKeyInfo;
	tbsSetCheck(dstKeyInfo.subjectPublicKey.Data, fieldValue, 
		sizeof(CSSM_KEY), "PubKeyStruct");

	CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR)fieldValue.data();
	if((cssmKey->KeyData.Data == NULL) ||
	   (cssmKey->KeyData.Data == 0)) {
		CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER);
	}
	CL_CSSMKeyToSubjPubKeyInfoNSS(*cssmKey, dstKeyInfo, cert.coder());
}

static void freeField_PublicKeyStruct (
	CssmOwnedData		&fieldValue)
{
	CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR)fieldValue.data();
	CL_freeCSSMKey(cssmKey, fieldValue.allocator, false);
}

/***
 *** 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;
}

/***
 *** end of field-specific triplets
 ***/
 
/*
 * Table to map OID to {get,set,free}field
 */
typedef struct {
	const CSSM_OID		*fieldId;
	getItemFieldFcn		*getFcn;
	setItemFieldFcn		*setFcn;
	freeFieldFcn		*freeFcn;		// OPTIONAL - NULL means just free the 
										// top-level data
} oidToFieldFuncs;

static const oidToFieldFuncs fieldFuncTable[] = {
	{ 	&CSSMOID_X509V1Version, 
		&getField_Version, &setField_Version, NULL },
	{ 	&CSSMOID_X509V1SerialNumber, 
		&getField_SerialNumber, &setField_SerialNumber, NULL 	},
	{ 	&CSSMOID_X509V1IssuerNameCStruct, 
		&getField_Issuer, &setField_Issuer, &freeField_RDN },
	{ 	&CSSMOID_X509V1SubjectNameCStruct, 
		&getField_Subject, &setField_Subject, &freeField_RDN },
	{	&CSSMOID_X509V1SignatureAlgorithmTBS,
		&getField_TbsAlgId, &setField_TbsAlgId, &freeField_AlgId },
	{	&CSSMOID_X509V1SignatureAlgorithm, 
		&getField_CertAlgId, &setField_ReadOnly, &freeField_AlgId	},
	{	&CSSMOID_X509V1ValidityNotBefore,
		&getField_NotBefore,	&setField_NotBefore,	&freeField_Time },
	{	&CSSMOID_X509V1ValidityNotAfter,
		&getField_NotAfter, &setField_NotAfter, &freeField_Time },
	{	&CSSMOID_X509V1CertificateIssuerUniqueId,
		&getField_IssuerUniqueId, &setField_IssuerUniqueId, NULL },
	{	&CSSMOID_X509V1CertificateSubjectUniqueId,
		&getField_SubjectUniqueId, &setField_SubjectUniqueId, NULL },
	{	&CSSMOID_X509V1SubjectPublicKeyCStruct,
		&getField_PublicKeyInfo, &setField_PublicKeyInfo, &freeField_PublicKeyInfo },
	{	&CSSMOID_CSSMKeyStruct,
		&getField_PublicKeyStruct, &setField_PublicKeyStruct, 
		&freeField_PublicKeyStruct },
	{	&CSSMOID_X509V1Signature,
		&getField_Signature, &setField_ReadOnly, NULL },
	{   &CSSMOID_X509V1IssuerName, 
		getFieldIssuerNorm, &setField_ReadOnly, NULL },
	{   &CSSMOID_X509V1SubjectName, 
		getFieldSubjectNorm, &setField_ReadOnly, NULL },
	{   &CSSMOID_X509V1IssuerNameStd, 
		getFieldIssuerStd, &setField_ReadOnly, NULL },
	{   &CSSMOID_X509V1SubjectNameStd, 
		getFieldSubjectStd, &setField_ReadOnly, NULL },
		
	/* 
	 * Extensions, implemented in CertExtensions.cpp 
	 * When adding new ones, also add to:
	 *   -- clOidToNssInfo() in CLFieldsCommon.cpp
	 *   -- get/set/free functions in CertExtensions.{cpp,h}
	 */
	{	&CSSMOID_KeyUsage, &getFieldKeyUsage, &setFieldKeyUsage, 
	    &freeFieldSimpleExtension },
	{   &CSSMOID_BasicConstraints, &getFieldBasicConstraints, 
	    &setFieldBasicConstraints, &freeFieldSimpleExtension },
	{	&CSSMOID_ExtendedKeyUsage, &getFieldExtKeyUsage,
		&setFieldExtKeyUsage, &freeFieldExtKeyUsage } ,
	{	&CSSMOID_SubjectKeyIdentifier, &getFieldSubjectKeyId,
		&setFieldSubjectKeyId, &freeFieldSubjectKeyId } ,
	{	&CSSMOID_AuthorityKeyIdentifier, &getFieldAuthorityKeyId,
		&setFieldAuthorityKeyId, &freeFieldAuthorityKeyId } ,
	{	&CSSMOID_SubjectAltName, &getFieldSubjAltName,
		&setFieldSubjIssuerAltName, &freeFieldSubjIssuerAltName } ,
	{	&CSSMOID_IssuerAltName, &getFieldIssuerAltName,
		&setFieldSubjIssuerAltName, &freeFieldSubjIssuerAltName } ,
	{	&CSSMOID_CertificatePolicies, &getFieldCertPolicies,
		&setFieldCertPolicies, &freeFieldCertPolicies } ,
	{	&CSSMOID_NetscapeCertType, &getFieldNetscapeCertType,
		&setFieldNetscapeCertType, &freeFieldSimpleExtension } ,
	{	&CSSMOID_CrlDistributionPoints, &getFieldCrlDistPoints,
		&setFieldCrlDistPoints, &freeFieldCrlDistPoints },
	{   &CSSMOID_X509V3CertificateExtensionCStruct, &getFieldUnknownExt,
		&setFieldUnknownExt, &freeFieldUnknownExt },
};

#define NUM_KNOWN_FIELDS		(sizeof(fieldFuncTable) / sizeof(oidToFieldFuncs))
#define NUM_STD_CERT_FIELDS		17		/* not including extensions */

/* map an OID to an oidToFieldFuncs */
static const oidToFieldFuncs *oidToFields(
	const CssmOid			&fieldId)
{
	const oidToFieldFuncs *fieldTable = fieldFuncTable;
	for(unsigned i=0; i<NUM_KNOWN_FIELDS; i++) {
		if(fieldId == CssmData::overlay(*fieldTable->fieldId)) {
			return fieldTable;
		}
		fieldTable++;
	}
	CssmError::throwMe(CSSMERR_CL_UNKNOWN_TAG);
}


/***
 *** Public functions
 ***/

/* 
 * Obtain the index'th occurrence of field specified by fieldId in specified cert.
 * Format of the returned field depends on fieldId.
 * Returns total number of fieldId fields in the cert if index is 0.
 * FieldValue assumed to be empty on entry. 
 * Returns true if specified field was found, else returns false. 
 */
bool DecodedCert::getCertFieldData(
	const CssmOid		&fieldId,		// which field
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue) 	// RETURNED
{ 
	switch(mState) {
		case IS_Empty:		
		case IS_Building:	
			clErrorLog("DecodedCert::getCertField: can't parse undecoded cert!");
			CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR);
		case IS_DecodedAll:
		case IS_DecodedTBS:
			break;
	}
	const oidToFieldFuncs *fieldFuncs = oidToFields(fieldId);
	return fieldFuncs->getFcn(*this, index, numFields, fieldValue);
}
 
/*
 * Set the field specified by fieldId in the specified Cert. 
 * Note no index - individual field routines either append (for extensions)
 * or if field already set ::throwMe(for all others) 
 */
void DecodedCert::setCertField(
	const CssmOid		&fieldId,		// which field
	const CssmData		&fieldValue) 
{
	switch(mState) {
		case IS_Empty:			// first time thru
			mState = IS_Building;
			break;
		case IS_Building:		// subsequent passes
			break;
		case IS_DecodedAll:
		case IS_DecodedTBS:
			clErrorLog("DecodedCert::setCertField: can't build on a decoded cert!");
			CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR);
	}
	if((fieldValue.data() == NULL) || (fieldValue.length() == 0)) {
		CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER);
	}
	const oidToFieldFuncs *fieldFuncs = oidToFields(fieldId);
	const CssmData &value = CssmData::overlay(fieldValue);
	fieldFuncs->setFcn(*this, value);
}

/*
 * Free the fieldId-specific data referred to by fieldValue->Data. 
 */
void DecodedCert::freeCertFieldData(
	const CssmOid		&fieldId,
	CssmOwnedData		&fieldValue)
{
	if((fieldValue.data() == NULL) || (fieldValue.length() == 0)) {
		CssmError::throwMe(CSSM_ERRCODE_INVALID_FIELD_POINTER);
	}
	const oidToFieldFuncs *fieldFuncs = oidToFields(fieldId);
	if(fieldFuncs->freeFcn != NULL) {
		/* optional - simple cases handled below */
		fieldFuncs->freeFcn(fieldValue);
	}
	fieldValue.reset();
	fieldValue.release();

}


/*
 * Common means to get all fields from a decoded cert. Used in 
 * CertGetAllTemplateFields and CertGetAllFields.
 */
void DecodedCert::getAllParsedCertFields(
	uint32 				&NumberOfFields,		// RETURNED
	CSSM_FIELD_PTR 		&CertFields)			// RETURNED
{
	/* this is the max - some might be missing */
	uint32 maxFields = NUM_STD_CERT_FIELDS + mDecodedExtensions.numExtensions();
	CSSM_FIELD_PTR outFields = (CSSM_FIELD_PTR)mAlloc.malloc(maxFields * sizeof(CSSM_FIELD));
	
	/*
	 * We'll be copying oids and values for fields we find into
	 * outFields; current number of valid fields found in numOutFields.
	 */
	memset(outFields, 0, maxFields * sizeof(CSSM_FIELD));
	uint32 			numOutFields = 0;
	CSSM_FIELD_PTR 	currOutField;
	uint32 			currOidDex;
	const CSSM_OID 	*currOid;
	CssmAutoData 	aData(mAlloc);		// for malloc/copy of outgoing data
	
	/* query for each OID we know about */
	for(currOidDex=0; currOidDex<NUM_KNOWN_FIELDS; currOidDex++) {
		const oidToFieldFuncs *fieldFuncs = &fieldFuncTable[currOidDex];
		currOid = fieldFuncs->fieldId;
		uint32 numFields;				// for THIS oid

		/* 
		 * Return false if field not there, which is not an error here. 
		 * Actual exceptions are fatal.
		 */
		if(!fieldFuncs->getFcn(*this, 
				0, 				// index - looking for first one
				numFields, 
				aData)) {
			continue;
		}
		
		/* got some data for this oid - copy it and oid to outgoing CertFields */
		assert(numOutFields < maxFields);
		currOutField = &outFields[numOutFields];
		currOutField->FieldValue = aData.release();
		aData.copy(*currOid);
		currOutField->FieldOid = aData.release();
		numOutFields++;
		
		/* if more fields are available for this OID, snag them too */
		for(uint32 fieldDex=1; fieldDex<numFields; fieldDex++) {
			/* note this should always succeed */
			bool brtn = fieldFuncs->getFcn(*this,
				fieldDex, 			
				numFields, 			// shouldn't change
				aData);
			if(!brtn) {
				clErrorLog("getAllParsedCertFields: index screwup");
				CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR);
			}
			assert(numOutFields < maxFields);
			currOutField = &outFields[numOutFields];
			currOutField->FieldValue = aData.release();
			aData.copy(*currOid);
			currOutField->FieldOid = aData.release();
			numOutFields++;
		}	/* multiple fields for currOid */
	}		/* for each known OID */
	
	NumberOfFields = numOutFields;
	CertFields = outFields;
}

void
DecodedCert::describeFormat(
	CssmAllocator &alloc,
	uint32 &NumberOfFields,
	CSSM_OID_PTR &OidList)
{
	/* malloc in app's space, do deep copy (including ->Data) */
	CSSM_OID_PTR oidList = (CSSM_OID_PTR)alloc.malloc(
		NUM_KNOWN_FIELDS * sizeof(CSSM_OID));
	memset(oidList, 0, NUM_KNOWN_FIELDS * sizeof(CSSM_OID));
	for(unsigned i=0; i<NUM_KNOWN_FIELDS; i++) {
		CssmAutoData oidCopy(alloc, *fieldFuncTable[i].fieldId);
		oidList[i] = oidCopy.release();
	}
	NumberOfFields = NUM_KNOWN_FIELDS;
	OidList = oidList;
}