clNssUtils.cpp   [plain text]


/*
 * Copyright (c) 2003 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.
 */

/*
 * clNssUtils.cpp - support for libnssasn1-based ASN1 encode/decode
 */

#include "clNssUtils.h"
#include "clNameUtils.h"
#include "CSPAttacher.h"
#include <security_asn1/secasn1.h>  
#include <security_asn1/SecNssCoder.h>
#include <security_asn1/nssUtils.h>
#include <Security/keyTemplates.h>
#include <Security/certExtensionTemplates.h>
#include <Security/oidsalg.h>
#include <Security/oidsattr.h>
#include <Security/cssmapple.h>
#include <string.h>

#pragma mark ----- ArenaAllocator -----

/* 
 * Avoid inlining this for debuggability 
 */
void *ArenaAllocator::malloc(size_t len) throw(std::bad_alloc)
{
	try {
		return mCoder.malloc(len);
	}
	catch (...) {
		throw std::bad_alloc();
	}
}

/* intentionally not implemented, should never be called */
void ArenaAllocator::free(void *p) throw()
{
	throw std::bad_alloc();
}
	
void *ArenaAllocator::realloc(void *p, size_t len) throw(std::bad_alloc)
{
	throw std::bad_alloc();
}

#pragma mark ----- Malloc/Copy/Compare CSSM_DATA -----

/* 
 * Misc. alloc/copy with arbitrary Allocator 
 */
/* malloc d.Data, set d.Length */
void clAllocData(
	Allocator	&alloc,
	CSSM_DATA		&dst,
	size_t			len)
{
	if(len == 0) {
		dst.Data = NULL;
	}
	else {
		dst.Data = (uint8 *)alloc.malloc(len);
	}
	dst.Length = len;
}

/* malloc and copy */
void clAllocCopyData(
	Allocator	&alloc,
	const CSSM_DATA	&src,
	CSSM_DATA		&dst)
{
	clAllocData(alloc, dst, src.Length);
	if(dst.Length != 0) {
		memmove(dst.Data, src.Data, src.Length);
	}
}

/*
 * Compare two CSSM_DATAs (or two CSSM_OIDs), return true if identical.
 */
bool clCompareCssmData(
	const CSSM_DATA *data1,
	const CSSM_DATA *data2)
{	
	if((data1 == NULL) || (data1->Data == NULL) || 
	   (data2 == NULL) || (data2->Data == NULL) ||
	   (data1->Length != data2->Length)) {
		return false;
	}
	if(data1->Length != data2->Length) {
		return false;
	}
	if(memcmp(data1->Data, data2->Data, data1->Length) == 0) {
		return true;
	}
	else {
		return false;
	}
}

#pragma mark ----- CSSM_DATA <--> uint32 -----

uint32 clDataToInt(
	const CSSM_DATA &cdata, 
	CSSM_RETURN toThrow)	/* = CSSMERR_CL_INVALID_CERT_POINTER */
{
	if((cdata.Length == 0) || (cdata.Data == NULL)) {
		return 0;
	}
	uint32 len = cdata.Length;
	if(len > sizeof(uint32)) {
		if(toThrow == 0) {
			/* tolerate this */
			len = sizeof(uint32);
		}
		else {
			CssmError::throwMe(toThrow);
		}
	}
	
	uint32 rtn = 0;
	uint8 *cp = cdata.Data;
	for(uint32 i=0; i<len; i++) {
		rtn = (rtn << 8) | *cp++;
	}
	return rtn;
}

void clIntToData(
	uint32 num,
	CSSM_DATA &cdata,
	Allocator &alloc)
{
	uint32 len = 0;
	
	if(num < 0x100) {
		len = 1;
	}
	else if(num < 0x10000) {
		len = 2;
	}
	else if(num < 0x1000000) {
		len = 3;
	}
	else {
		len = 4;
	}
	clAllocData(alloc, cdata, len);
	uint8 *cp = &cdata.Data[len - 1];
	for(unsigned i=0; i<len; i++) {
		*cp-- = num & 0xff;
		num >>= 8;
	}
}

#pragma mark ----- CSSM_BOOL <--> CSSM_DATA -----
/*
 * A Bool is encoded as one byte of either 0 or 0xff
 * Default of NSS boolean not present is false
 */
CSSM_BOOL clNssBoolToCssm(
	const CSSM_DATA	&nssBool)
{
	if((nssBool.Data != NULL) && (nssBool.Data[0] == 0xff)) {
		return CSSM_TRUE;
	}
	else {
		return CSSM_FALSE;
	}
}

void clCssmBoolToNss(
	CSSM_BOOL cBool,
	CSSM_DATA &nssBool,
	Allocator &alloc)
{
	uint32 num = cBool ? 0xff : 0;
	clIntToData(num, nssBool, alloc);
}

#pragma mark ----- Bit String manipulation -----

/*
 * Adjust the length of a CSSM_DATA representing a pre-encoded 
 * bit string. On entry the length field is the number of bytes
 * of data; en exit, the number if bits. Trailing zero bits 
 * are counted as unused (which is how KeyUsage and NetscapeCertType
 * extensions are encoded).
 */
void clCssmBitStringToNss(
	CSSM_DATA &b)
{
	int numBits = b.Length * 8;
	
	/* start at end of bit array, scanning backwards looking
	 * for the first set bit */
	bool foundSet = false;
	for(int dex=b.Length-1; dex>=0; dex--) {
		unsigned bitMask = 0x01;
		uint8 byte = b.Data[dex];
		for(unsigned bdex=0; bdex<8; bdex++) {
			if(byte & bitMask) {
				foundSet = true;
				break;
			}
			else {
				bitMask <<= 1;
				numBits--;
			}
		}
		if(foundSet) {
			break;
		}
	}
	/* !foundSet --> numBits = 0 */
	assert(((numBits > 0) & foundSet) || ((numBits == 0) && !foundSet));
	b.Length = (uint32)numBits;
}

/*
 * On entry, Length is bit count; on exit, a byte count.
 * The job here is to ensure that bits marked as "unused" in the 
 * BER encoding are cleared. Encoding rules say they are undefined in
 * the actual encoding.
 */
void clNssBitStringToCssm(
	CSSM_DATA &b)
{
	uint32 byteCount = (b.Length + 7) / 8;
	unsigned partialBits = b.Length & 0x7;
	b.Length = byteCount;
	if(partialBits == 0) {
		return;
	}
	
	/* mask off unused bits */
	unsigned unusedBits = 8 - partialBits;
	uint8 *bp = b.Data + b.Length - 1;
	/* mask = (2 ** unusedBits) - 1 */
	unsigned mask = (1 << unusedBits) - 1;
	*bp &= ~mask;
}

#pragma mark ----- NSS array manipulation -----
/*
 * How many items in a NULL-terminated array of pointers?
 */
unsigned clNssArraySize(
	const void **array)
{
    unsigned count = 0;
    if (array) {
		while (*array++) {
			count++;
		}
    }
    return count;
}

/* malloc a NULL-ed array of pointers of size num+1 */
void **clNssNullArray(
	uint32 num,
	SecNssCoder &coder)
{
	unsigned len = (num + 1) * sizeof(void *);
	void **p = (void **)coder.malloc(len);
	memset(p, 0, len);
	return p;
}

/*
 * GIven a CSSM_DATA containing a decoded BIT_STRING, 
 * convert to a KeyUsage.
 */
CE_KeyUsage clBitStringToKeyUsage(
	const CSSM_DATA &cdata)
{
	unsigned toCopy = (cdata.Length + 7) / 8;
	if(toCopy > 2) {
		/* I hope I never see this... */
		clErrorLog("clBitStringToKeyUsage: KeyUsage larger than 2 bytes!");
		toCopy = 2;
	}
	unsigned char bits[2] = {0, 0};
	memmove(bits, cdata.Data, toCopy);
	CE_KeyUsage usage = (((unsigned)bits[0]) << 8) | bits[1];
	return usage;
}

CSSM_ALGORITHMS CL_oidToAlg(
	const CSSM_OID &oid)
{
	CSSM_ALGORITHMS alg;
	bool found = cssmOidToAlg(&oid, &alg);
	if(!found) {
		clErrorLog("CL_oidToAlg: unknown alg\n");
		CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
	}
	return alg;
}

#pragma mark ----- copy CSSM_X509_ALGORITHM_IDENTIFIER -----

/*
 * Copy CSSM_X509_ALGORITHM_IDENTIFIER, same format (NSS and CSSM).
 */
void CL_copyAlgId(
	const CSSM_X509_ALGORITHM_IDENTIFIER &srcAlgId, 
	CSSM_X509_ALGORITHM_IDENTIFIER &dstAlgId, 
	Allocator &alloc)
{
	clAllocCopyData(alloc, srcAlgId.algorithm, dstAlgId.algorithm);
	clAllocCopyData(alloc, srcAlgId.parameters, dstAlgId.parameters);
}

void CL_freeCssmAlgId(
	CSSM_X509_ALGORITHM_IDENTIFIER	*cdsaObj,		// optional
	Allocator 					&alloc)
{
	if(cdsaObj == NULL) {
		return;
	}
	alloc.free(cdsaObj->algorithm.Data);
	alloc.free(cdsaObj->parameters.Data);
	memset(cdsaObj, 0, sizeof(CSSM_X509_ALGORITHM_IDENTIFIER));
}


#pragma mark ----- CSSM_X509_TIME <--> NSS format -----

/*
 * Map the tag associated with a choice of DirectoryString elements to 
 * a template array for encoding/decoding that string type.
 * Contrary to RFC2459, we allow the IA5String type, which is actually 
 * used in the real world (cf. the email address in Thawte's serverbasic
 * cert).
 */

/* The template chooser does the work here */

bool CL_nssTimeToCssm(
	const NSS_TaggedItem 	&nssTime,
	CSSM_X509_TIME			&cssmObj,
	Allocator 			&alloc)	
{
	cssmObj.timeType = nssTime.tag;
	clAllocCopyData(alloc, nssTime.item, cssmObj.time);
	return true;
}

/* 
 * CSSM time to NSS time. 
 */
void CL_cssmTimeToNss(
	const CSSM_X509_TIME &cssmTime, 
	NSS_TaggedItem &nssTime, 
	SecNssCoder &coder)
{
	nssTime.tag = cssmTime.timeType;
	coder.allocCopyItem(cssmTime.time, nssTime.item);
}

void CL_freeCssmTime(
	CSSM_X509_TIME	*cssmTime,
	Allocator	&alloc)
{
	if(cssmTime == NULL) {
		return;
	}
	if(cssmTime->time.Data) {
		alloc.free(cssmTime->time.Data);
	}
	memset(cssmTime, 0, sizeof(CSSM_X509_TIME));
}


#pragma mark ----- CSSM_X509_SUBJECT_PUBLIC_KEY_INFO <--> CSSM_KEY  -----

/*
 * Copy a CSSM_X509_SUBJECT_PUBLIC_KEY_INFO.
 *
 * Same format (NSS and CSSM), EXCEPT:
 *
 *   Objects which have just been NSS decoded or are about to be
 *   NSS encoded have the subjectPublicKey.Length field in BITS
 *   since this field is wrapped in a BIT STRING upon encoding. 
 * 
 *   Caller tells us which format (bits or bytes)
 *   to use for each of {src, dst}.
 */
void CL_copySubjPubKeyInfo(
	const CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &srcInfo, 
	bool srcInBits,
	CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &dstInfo, 
	bool dstInBits,
	Allocator &alloc)
{
	CL_copyAlgId(srcInfo.algorithm, dstInfo.algorithm, alloc);
	
	CSSM_DATA srcKey = srcInfo.subjectPublicKey;
	if(srcInBits) {
		srcKey.Length = (srcKey.Length + 7) / 8;
	}
	clAllocCopyData(alloc, srcKey, dstInfo.subjectPublicKey);
	if(dstInBits) {
		dstInfo.subjectPublicKey.Length *= 8;
	}
}

/*
 * Obtain a CSSM_KEY from a CSSM_X509_SUBJECT_PUBLIC_KEY_INFO, 
 * inferring as much as we can from required fields 
 * (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO) and extensions (for 
 * KeyUse, obtained from the optional DecodedCert).
 */
CSSM_KEY_PTR CL_extractCSSMKeyNSS(
	const CSSM_X509_SUBJECT_PUBLIC_KEY_INFO	&keyInfo,
	Allocator			&alloc,
	const DecodedCert		*decodedCert)			// optional
{
	CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR) alloc.malloc(sizeof(CSSM_KEY));
	memset(cssmKey, 0, sizeof(CSSM_KEY));
	CSSM_KEYHEADER &hdr = cssmKey->KeyHeader;
	CssmRemoteData keyData(alloc, cssmKey->KeyData);

	hdr.HeaderVersion = CSSM_KEYHEADER_VERSION;
	/* CspId blank */
	hdr.BlobType = CSSM_KEYBLOB_RAW;
	hdr.AlgorithmId = CL_oidToAlg(keyInfo.algorithm.algorithm);
	hdr.KeyAttr = CSSM_KEYATTR_MODIFIABLE | CSSM_KEYATTR_EXTRACTABLE;
	
	/* 
	 * Format inferred from AlgorithmId. I have never seen these defined
	 * anywhere, e.g., what's the format of an RSA public key in a cert?
	 * X509 certainly doesn't say. However. the following two cases are 
	 * known to be correct. 
	 */
	switch(hdr.AlgorithmId) {
		case CSSM_ALGID_RSA:
			hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
			break;
		case CSSM_ALGID_DSA:
		case CSSM_ALGID_ECDSA:
		case CSSM_ALGID_DH:
		case CSSM_ALGMODE_PKCS1_EME_OAEP:
			hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_X509;
			break;
		case CSSM_ALGID_FEE:
			/* CSSM_KEYBLOB_RAW_FORMAT_NONE --> DER encoded */
			hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_NONE;
			break;
		default:
			/* punt */
			hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_NONE;
	}
	hdr.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY;
	
	/* KeyUsage inferred from extensions */
	if(decodedCert) {
		hdr.KeyUsage = decodedCert->inferKeyUsage();
	}
	else {
		hdr.KeyUsage = CSSM_KEYUSE_ANY;
	}
	
	/* start/end date unknown, leave zero */
	hdr.WrapAlgorithmId = CSSM_ALGID_NONE;
	hdr.WrapMode = CSSM_ALGMODE_NONE;
	
	switch(hdr.AlgorithmId) {
		case CSSM_ALGID_DSA:
		case CSSM_ALGID_ECDSA:
		case CSSM_ALGID_DH:
		case CSSM_ALGMODE_PKCS1_EME_OAEP:
		{
			/* 
			 * Just encode the whole subject public key info blob.
			 * NOTE we're assuming that the keyInfo.subjectPublicKey
			 * field is in the NSS_native BITSTRING format, i.e., 
			 * its Length field is in bits and we don't have to adjust.
			 */
			PRErrorCode prtn = SecNssEncodeItemOdata(&keyInfo, 
				kSecAsn1SubjectPublicKeyInfoTemplate, keyData);
			if(prtn) {
				clErrorLog("extractCSSMKey: error on reencode\n");
				CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR);
			}
			break;
		}
		default:
			/*
			 * RSA, FEE for now.
			 * keyInfo.subjectPublicKey (in BITS) ==> KeyData
			 */
			keyData.copy(keyInfo.subjectPublicKey.Data,
				(keyInfo.subjectPublicKey.Length + 7) / 8);
	}

	/*
	 * LogicalKeySizeInBits - ask the CSP
	 */
	CSSM_CSP_HANDLE cspHand = getGlobalCspHand(true);
	CSSM_KEY_SIZE keySize;
	CSSM_RETURN crtn;
	crtn = CSSM_QueryKeySizeInBits(cspHand, CSSM_INVALID_HANDLE, cssmKey,
		&keySize);
	switch(crtn) {
		default:
			CssmError::throwMe(crtn);
		case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
			/*
			 * This is how the CSP indicates a "partial" public key,
			 * with a valid public key value but no alg-specific
			 * parameters (currently, DSA only). 
			 */
			hdr.KeyAttr |= CSSM_KEYATTR_PARTIAL;
			/* and drop thru */
		case CSSM_OK:
			cssmKey->KeyHeader.LogicalKeySizeInBits = 
				keySize.LogicalKeySizeInBits;
			break;
	}

	keyData.release();
	return cssmKey;
}

/* 
 * Set up a encoded NULL for CSSM_X509_ALGORITHM_IDENTIFIER.parameters.
 */
void CL_nullAlgParams(
	CSSM_X509_ALGORITHM_IDENTIFIER	&algId)
{
	static const uint8 encNull[2] = { SEC_ASN1_NULL, 0 };
	CSSM_DATA encNullData;
	encNullData.Data = (uint8 *)encNull;
	encNullData.Length = 2;

	algId.parameters = encNullData;
}

/* 
 * Convert a CSSM_KEY to a CSSM_X509_SUBJECT_PUBLIC_KEY_INFO. The
 * CSSM key must be in raw format and with a specific blob format.
 *  	-- RSA keys have to be CSSM_KEYBLOB_RAW_FORMAT_PKCS1
 * 		-- DSA keys have to be CSSM_KEYBLOB_RAW_FORMAT_X509
 * 		-- ECDSA keys have to be CSSM_KEYBLOB_RAW_FORMAT_X509
 */
void CL_CSSMKeyToSubjPubKeyInfoNSS(
	const CSSM_KEY 						&cssmKey,
	CSSM_X509_SUBJECT_PUBLIC_KEY_INFO	&nssKeyInfo,
	SecNssCoder							&coder)
{
	const CSSM_KEYHEADER &hdr = cssmKey.KeyHeader;
	if(hdr.BlobType != CSSM_KEYBLOB_RAW) {
		clErrorLog("CL SetField: must specify RAW key blob\n");
		CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
	}
	memset(&nssKeyInfo, 0, sizeof(nssKeyInfo));
	
	/* algorithm and format dependent from here... */
	switch(hdr.AlgorithmId) {
		case CSSM_ALGID_RSA:
			if(hdr.Format != CSSM_KEYBLOB_RAW_FORMAT_PKCS1) {
				clErrorLog("CL SetField: RSA key must be in PKCS1 format\n");
				CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
			}
			/* and fall thru */
		default:
		{
			/* Key header's algorithm --> OID */
			const CSSM_OID *oid = cssmAlgToOid(hdr.AlgorithmId);
			if(oid == NULL) {
				clErrorLog("CL SetField: Unknown key algorithm\n");
				CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
			}
			CSSM_X509_ALGORITHM_IDENTIFIER &algId = nssKeyInfo.algorithm;
			coder.allocCopyItem(*oid, algId.algorithm);

			/* NULL algorithm parameters, always in this case */
			CL_nullAlgParams(algId);
			
			/* Copy key bits, destination is a BIT STRING */
			coder.allocCopyItem(cssmKey.KeyData, nssKeyInfo.subjectPublicKey);
			nssKeyInfo.subjectPublicKey.Length *= 8;
			break;
		}	
		case CSSM_ALGID_DSA:
		case CSSM_ALGID_ECDSA:
			if(hdr.Format != CSSM_KEYBLOB_RAW_FORMAT_X509) {
				clErrorLog("CL SetField: DSA/ECDSA key must be in X509 format\n");
				CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
			}
			
			/* 
			 * All we do is decode the whole key blob into the 
			 * SubjectPublicKeyInfo.
			 */
			if(coder.decodeItem(cssmKey.KeyData, 
					kSecAsn1SubjectPublicKeyInfoTemplate, 
					&nssKeyInfo)) {
				clErrorLog("CL SetField: Error decoding DSA public key\n");
				CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
			}
			break;
	}
}

void CL_freeCSSMKey(
	CSSM_KEY_PTR		cssmKey,
	Allocator		&alloc,
	bool				freeTop)
{
	if(cssmKey == NULL) {
		return;
	}
	alloc.free(cssmKey->KeyData.Data);
	memset(cssmKey, 0, sizeof(CSSM_KEY));
	if(freeTop) {
		alloc.free(cssmKey);
	}
}

#pragma mark ----- CE_AuthorityKeyID <--> NSS_AuthorityKeyId -----

void CL_cssmAuthorityKeyIdToNss(
	const CE_AuthorityKeyID 	&cdsaObj,
	NSS_AuthorityKeyId 			&nssObj,
	SecNssCoder 				&coder) 
{
	memset(&nssObj, 0, sizeof(nssObj));
	if(cdsaObj.keyIdentifierPresent) {
		nssObj.keyIdentifier = (CSSM_DATA_PTR)coder.malloc(sizeof(CSSM_DATA));
		coder.allocCopyItem(cdsaObj.keyIdentifier, *nssObj.keyIdentifier);
	}
	if(cdsaObj.generalNamesPresent ) {
		/* GeneralNames, the hard one */
		CL_cssmGeneralNamesToNss(*cdsaObj.generalNames,
			nssObj.genNames, coder);
	}
	if(cdsaObj.serialNumberPresent) {
		coder.allocCopyItem(cdsaObj.serialNumber,nssObj.serialNumber);
	}
}

void CL_nssAuthorityKeyIdToCssm(
	const NSS_AuthorityKeyId 		&nssObj,
	CE_AuthorityKeyID 				&cdsaObj,
	SecNssCoder 					&coder,	// for temp decoding
	Allocator					&alloc)
{
	if(nssObj.keyIdentifier != NULL) {
		cdsaObj.keyIdentifierPresent = CSSM_TRUE;
		clAllocCopyData(alloc, *nssObj.keyIdentifier, cdsaObj.keyIdentifier);
	}
	if(nssObj.genNames.names != NULL) {
		/* GeneralNames, the hard one */
		cdsaObj.generalNamesPresent = CSSM_TRUE;
		cdsaObj.generalNames = 
			(CE_GeneralNames *)alloc.malloc(sizeof(CE_GeneralNames));
		CL_nssGeneralNamesToCssm(nssObj.genNames, 
			*cdsaObj.generalNames,
			coder,
			alloc);
	}
	if(nssObj.serialNumber.Data != NULL) {
		cdsaObj.serialNumberPresent = CSSM_TRUE;
		clAllocCopyData(alloc, nssObj.serialNumber, cdsaObj.serialNumber);
	}
}

#pragma mark ----- CE_AuthorityInfoAccess <--> NSS_AuthorityInfoAccess -----

void CL_cssmInfoAccessToNss(
	const CE_AuthorityInfoAccess	&cdsaObj,
	NSS_AuthorityInfoAccess			&nssObj,
	SecNssCoder						&coder)  
{
	memset(&nssObj, 0, sizeof(nssObj));
	uint32 numDescs = cdsaObj.numAccessDescriptions;
	nssObj.accessDescriptions = (NSS_AccessDescription **)clNssNullArray(numDescs, coder);
	
	for(unsigned dex=0; dex<numDescs; dex++) {
		nssObj.accessDescriptions[dex] = coder.mallocn<NSS_AccessDescription>();
		CE_AccessDescription *src = &cdsaObj.accessDescriptions[dex];
		NSS_AccessDescription *dst = nssObj.accessDescriptions[dex];
		coder.allocCopyItem(src->accessMethod, dst->accessMethod);
		
		/* Convert general name, then encode it into destination */
		NSS_GeneralName nssGenName;
		CL_cssmGeneralNameToNss(src->accessLocation, nssGenName, coder);
		PRErrorCode prtn = coder.encodeItem(&nssGenName, kSecAsn1GeneralNameTemplate, 
			dst->encodedAccessLocation);
		if(prtn) {
			clErrorLog("CL_cssmInfoAccessToNss: encode error\n");
			CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR);
		}
	}
}

void CL_infoAccessToCssm(
	const NSS_AuthorityInfoAccess 	&nssObj,
	CE_AuthorityInfoAccess			&cdsaObj,
	SecNssCoder						&coder,	// for temp decoding
	Allocator						&alloc)
{
	memset(&cdsaObj, 0, sizeof(cdsaObj));
	unsigned numDescs = clNssArraySize((const void **)nssObj.accessDescriptions);
	if(numDescs == 0) {
		return;
	}
	cdsaObj.accessDescriptions = (CE_AccessDescription *)alloc.malloc(
		numDescs * sizeof(CE_AccessDescription));
	cdsaObj.numAccessDescriptions = numDescs;
	for(unsigned dex=0; dex<numDescs; dex++) {
		CE_AccessDescription *dst = &cdsaObj.accessDescriptions[dex];
		NSS_AccessDescription *src = nssObj.accessDescriptions[dex];
		clAllocCopyData(alloc, src->accessMethod, dst->accessMethod);
		
		/* decode the general name */
		NSS_GeneralName nssGenName;
		memset(&nssGenName, 0, sizeof(nssGenName));
		PRErrorCode prtn = coder.decodeItem(src->encodedAccessLocation,
			kSecAsn1GeneralNameTemplate, &nssGenName);
		if(prtn) {
			clErrorLog("***Error decoding NSS_AuthorityInfoAccess.accessLocation\n");
			CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
		}
		
		/* then convert the result to CSSM */
		CL_nssGeneralNameToCssm(nssGenName, dst->accessLocation, coder, alloc);
	}
}

void CL_freeInfoAccess(
	CE_AuthorityInfoAccess	&cssmInfo,
	Allocator				&alloc)
{
	uint32 numDescs = cssmInfo.numAccessDescriptions;
	for(unsigned dex=0; dex<numDescs; dex++) {
		CE_AccessDescription *dst = &cssmInfo.accessDescriptions[dex];
		alloc.free(dst->accessMethod.Data);
		CL_freeCssmGeneralName(dst->accessLocation, alloc);
	}
	alloc.free(cssmInfo.accessDescriptions);
}


#pragma mark ----- CE_QC_Statements <--> NSS_QC_Statements -----

void CL_cssmQualCertStatementsToNss(
	const CE_QC_Statements	 	&cdsaObj,
	NSS_QC_Statements 			&nssObj,
	SecNssCoder 				&coder) 
{
	memset(&nssObj, 0, sizeof(nssObj));
	uint32 numQcs = cdsaObj.numQCStatements;
	nssObj.qcStatements = 
		(NSS_QC_Statement **)clNssNullArray(numQcs, coder);
	for(uint32 dex=0; dex<numQcs; dex++) {
		nssObj.qcStatements[dex] = (NSS_QC_Statement *)
			coder.malloc(sizeof(NSS_QC_Statement));
		NSS_QC_Statement *dst = nssObj.qcStatements[dex];
		CE_QC_Statement *src = &cdsaObj.qcStatements[dex];
		memset(dst, 0, sizeof(*dst));
		coder.allocCopyItem(src->statementId, dst->statementId);
		if(src->semanticsInfo) {
			if(src->otherInfo) {
				/* this is either/or, not both */
				CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER);
			}

			/* encode this CE_SemanticsInformation */
			CE_SemanticsInformation *srcSI = src->semanticsInfo;
			NSS_SemanticsInformation dstSI;
			memset(&dstSI, 0, sizeof(dstSI));
			if(srcSI->semanticsIdentifier) {
				dstSI.semanticsIdentifier = (CSSM_DATA_PTR)coder.malloc(sizeof(CSSM_DATA));
				coder.allocCopyItem(*srcSI->semanticsIdentifier, 
					*dstSI.semanticsIdentifier);
			}
			if(srcSI->nameRegistrationAuthorities) {
				dstSI.nameRegistrationAuthorities = 
					(NSS_GeneralNames *)coder.malloc(sizeof(NSS_GeneralNames));
				CL_cssmGeneralNamesToNss(*srcSI->nameRegistrationAuthorities,
					*dstSI.nameRegistrationAuthorities, coder);
			}
			PRErrorCode prtn = coder.encodeItem(&dstSI, kSecAsn1SemanticsInformationTemplate, 
				dst->info);
			if(prtn) {
				clErrorLog("CL_cssmQualCertStatementsToNss: encode error\n");
				CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR);
			}
			
		}
		if(src->otherInfo) {
			/* drop in as ASN_ANY */
			coder.allocCopyItem(*src->otherInfo, dst->info);
		}
	}
}

void CL_qualCertStatementsToCssm(
	const NSS_QC_Statements 		&nssObj,
	CE_QC_Statements 				&cdsaObj,
	SecNssCoder 					&coder,	// for temp decoding
	Allocator						&alloc)
{
	memset(&cdsaObj, 0, sizeof(cdsaObj));
	unsigned numQcs = clNssArraySize((const void **)nssObj.qcStatements);
	if(numQcs == 0) {
		return;
	}
	cdsaObj.qcStatements = (CE_QC_Statement *)alloc.malloc(
		numQcs * sizeof(CE_AccessDescription));
	cdsaObj.numQCStatements = numQcs;
	for(unsigned dex=0; dex<numQcs; dex++) {
		CE_QC_Statement *dst = &cdsaObj.qcStatements[dex];
		NSS_QC_Statement *src = nssObj.qcStatements[dex];

		memset(dst, 0, sizeof(*dst));
		clAllocCopyData(alloc, src->statementId, dst->statementId);

		/* 
		 * Whether the optional info is a SemanticsInformation or is uninterpreted
 		 * DER data depends on statementId.
		 */
		if(src->info.Data) {
			if(clCompareCssmData(&src->statementId, &CSSMOID_OID_QCS_SYNTAX_V2)) {
				NSS_SemanticsInformation srcSI;
				memset(&srcSI, 0, sizeof(srcSI));

				/* decode info as a NSS_SemanticsInformation */
				PRErrorCode prtn = coder.decodeItem(src->info,
					kSecAsn1SemanticsInformationTemplate, &srcSI);
				if(prtn) {
					clErrorLog("***Error decoding CE_SemanticsInformation\n");
					CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
				}

				/* NSS_SemanticsInformation --> CE_SemanticsInformation */
				dst->semanticsInfo = 
					(CE_SemanticsInformation *)alloc.malloc(sizeof(CE_SemanticsInformation));
				CE_SemanticsInformation *dstSI = dst->semanticsInfo;
				memset(dstSI, 0, sizeof(*dstSI));
				if(srcSI.semanticsIdentifier) {
					dstSI->semanticsIdentifier = (CSSM_OID *)alloc.malloc(sizeof(CSSM_OID));
					clAllocCopyData(alloc, *srcSI.semanticsIdentifier, *dstSI->semanticsIdentifier);
				}
				if(srcSI.nameRegistrationAuthorities) {
					dstSI->nameRegistrationAuthorities = 
						(CE_NameRegistrationAuthorities *)alloc.malloc(
							sizeof(CE_NameRegistrationAuthorities));
					CL_nssGeneralNamesToCssm(*srcSI.nameRegistrationAuthorities, 
						*dstSI->nameRegistrationAuthorities,
						coder,
						alloc);
				}
			}
			else {
				dst->otherInfo = (CSSM_DATA_PTR)alloc.malloc(sizeof(CSSM_DATA));
				clAllocCopyData(alloc, src->info, *dst->otherInfo);
			}
		}
	}
}

void CL_freeQualCertStatements(
	CE_QC_Statements	&cssmQCs,
	Allocator			&alloc)
{
	uint32 numQCs = cssmQCs.numQCStatements;
	for(unsigned dex=0; dex<numQCs; dex++) {
		CE_QC_Statement *dst = &cssmQCs.qcStatements[dex];
		alloc.free(dst->statementId.Data);
		if(dst->semanticsInfo) {
			CE_SemanticsInformation *si = dst->semanticsInfo;
			if(si->semanticsIdentifier) {
				alloc.free(si->semanticsIdentifier->Data);
				alloc.free(si->semanticsIdentifier);
			}
			if(si->nameRegistrationAuthorities) {
				CL_freeCssmGeneralNames(si->nameRegistrationAuthorities, alloc);
				alloc.free(si->nameRegistrationAuthorities);
			}
			alloc.free(si);
		}
		if(dst->otherInfo) {
			alloc.free(dst->otherInfo->Data);
			alloc.free(dst->otherInfo);
		}
	}
	alloc.free(cssmQCs.qcStatements);
}

#pragma mark ----- decode/encode CE_DistributionPointName -----

/* This is always a DER-encoded blob at the NSS level */
void CL_decodeDistributionPointName(
	const CSSM_DATA				&nssBlob,
	CE_DistributionPointName	&cssmDpn,
	SecNssCoder					&coder,
	Allocator				&alloc)
{
	memset(&cssmDpn, 0, sizeof(CE_DistributionPointName));
	if(nssBlob.Length == 0) {
		clErrorLog("***CL_decodeDistributionPointName: bad PointName\n");
		CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
	}
	unsigned char tag = nssBlob.Data[0] & SEC_ASN1_TAGNUM_MASK;
	switch(tag) {
		case NSS_DIST_POINT_FULL_NAME_TAG:
		{
			/* decode to temp coder memory */
			NSS_GeneralNames gnames;
			gnames.names = NULL;
			if(coder.decodeItem(nssBlob, kSecAsn1DistPointFullNameTemplate,
					&gnames)) {
				clErrorLog("***Error decoding DistPointFullName\n");
				CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
			}
			
			cssmDpn.nameType = CE_CDNT_FullName;
			cssmDpn.dpn.fullName = (CE_GeneralNames *)alloc.malloc(
				sizeof(CE_GeneralNames));
				
			/* copy out to caller */
			CL_nssGeneralNamesToCssm(gnames, 
				*cssmDpn.dpn.fullName, coder, alloc);
			break;
		}
		case NSS_DIST_POINT_RDN_TAG:
		{
			/* decode to temp coder memory */
			NSS_RDN rdn;
			memset(&rdn, 0, sizeof(rdn));
			if(coder.decodeItem(nssBlob, kSecAsn1DistPointRDNTemplate,
					&rdn)) {
				clErrorLog("***Error decoding DistPointRDN\n");
				CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
			}
			
			cssmDpn.nameType = CE_CDNT_NameRelativeToCrlIssuer;
			cssmDpn.dpn.rdn = (CSSM_X509_RDN_PTR)alloc.malloc(
				sizeof(CSSM_X509_RDN));
			
			/* copy out to caller */
			CL_nssRdnToCssm(rdn, *cssmDpn.dpn.rdn, alloc, coder);
			break;
		}
		default:
			clErrorLog("***Bad CE_DistributionPointName tag\n");
			CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
	}
}

void CL_encodeDistributionPointName(
	CE_DistributionPointName &cpoint,
	CSSM_DATA &npoint,
	SecNssCoder &coder)
{
	const SecAsn1Template *templ = NULL;
	NSS_GeneralNames gnames;
	NSS_RDN rdn;
	void *encodeSrc = NULL;
	
	/* 
	 * Our job is to convert one of two incoming aggregate types
	 * into NSS format, then encode the result into npoint.
	 */
	switch(cpoint.nameType) {
		case CE_CDNT_FullName:
			CL_cssmGeneralNamesToNss(*cpoint.dpn.fullName,
				gnames, coder);
			encodeSrc = &gnames;
			templ = kSecAsn1DistPointFullNameTemplate;
			break;
			
		case CE_CDNT_NameRelativeToCrlIssuer:
			CL_cssmRdnToNss(*cpoint.dpn.rdn, rdn, coder);
			encodeSrc = &rdn;
			templ = kSecAsn1DistPointRDNTemplate;
			break;
		default:
			clErrorLog("CL_encodeDistributionPointName: bad nameType\n");
			CssmError::throwMe(CSSMERR_CL_UNKNOWN_TAG);
	}
	if(coder.encodeItem(encodeSrc, templ, npoint)) {
		clErrorLog("CL_encodeDistributionPointName: encode error\n");
		CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR);
	}
}


#pragma mark --- CE_CRLDistPointsSyntax <--> NSS_CRLDistributionPoints ---

void CL_cssmDistPointsToNss(
	const CE_CRLDistPointsSyntax 	&cdsaObj,
	NSS_CRLDistributionPoints		&nssObj,
	SecNssCoder 					&coder)
{
	memset(&nssObj, 0, sizeof(nssObj));
	unsigned numPoints = cdsaObj.numDistPoints;
	if(numPoints == 0) {
		return;
	}
	nssObj.distPoints = 
		(NSS_DistributionPoint **)clNssNullArray(numPoints, coder);
	for(unsigned dex=0; dex<numPoints; dex++) {
		nssObj.distPoints[dex] = (NSS_DistributionPoint *)
			coder.malloc(sizeof(NSS_DistributionPoint));
		NSS_DistributionPoint *npoint = nssObj.distPoints[dex];
		memset(npoint, 0, sizeof(NSS_DistributionPoint));
		CE_CRLDistributionPoint *cpoint = &cdsaObj.distPoints[dex];
		
		/* all fields are optional */
		if(cpoint->distPointName) {
			/* encode and drop into ASN_ANY slot */
			npoint->distPointName = (CSSM_DATA *)
				coder.malloc(sizeof(CSSM_DATA));
			CL_encodeDistributionPointName(*cpoint->distPointName,
				*npoint->distPointName, coder);
			
		}
		
		if(cpoint->reasonsPresent) {
			/* bit string, presumed max length 8 bits */
			coder.allocItem(npoint->reasons, 1);
			npoint->reasons.Data[0] = cpoint->reasons;
			/* adjust for bit string length */
			npoint->reasons.Length = 8;
		}
		
		if(cpoint->crlIssuer) {
			CL_cssmGeneralNamesToNss(*cpoint->crlIssuer,
				npoint->crlIssuer, coder);
		}
	}
}

void CL_nssDistPointsToCssm(
	const NSS_CRLDistributionPoints	&nssObj,
	CE_CRLDistPointsSyntax			&cdsaObj,
	SecNssCoder 					&coder,	// for temp decoding
	Allocator						&alloc)
{
	memset(&cdsaObj, 0, sizeof(cdsaObj));
	unsigned numPoints = clNssArraySize((const void **)nssObj.distPoints);
	if(numPoints == 0) {
		return;
	}
	
	unsigned len = sizeof(CE_CRLDistributionPoint) * numPoints;
	cdsaObj.distPoints = (CE_CRLDistributionPoint *)alloc.malloc(len);
	memset(cdsaObj.distPoints, 0, len);
	cdsaObj.numDistPoints = numPoints;

	for(unsigned dex=0; dex<numPoints; dex++) {
		CE_CRLDistributionPoint &cpoint = cdsaObj.distPoints[dex];
		NSS_DistributionPoint &npoint = *(nssObj.distPoints[dex]);
	
		/* All three fields are optional */
		if(npoint.distPointName != NULL) {
			/* Drop in a CE_DistributionPointName */
			CE_DistributionPointName *cname = 
				(CE_DistributionPointName *)alloc.malloc(
					sizeof(CE_DistributionPointName));
			memset(cname, 0, sizeof(*cname));
			cpoint.distPointName = cname;
			
			/*
			 * This one is currently still encoded; we have to peek
			 * at its tag and decode accordingly.
			 */
			CL_decodeDistributionPointName(*npoint.distPointName,
				*cname, coder, alloc);
		}

		if(npoint.reasons.Data != NULL) {
			/* careful, it's a bit string */
			if(npoint.reasons.Length > 8) {
				clErrorLog("***CL_nssDistPointsToCssm: Malformed reasons\n");
				CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
			}
			cpoint.reasonsPresent = CSSM_TRUE;
			if(npoint.reasons.Length != 0) {
				cpoint.reasons = npoint.reasons.Data[0];
			}
		}
		
		if(npoint.crlIssuer.names != NULL) {
			/* Cook up a new CE_GeneralNames */
			cpoint.crlIssuer = 
				(CE_GeneralNames *)alloc.malloc(sizeof(CE_GeneralNames));
			CL_nssGeneralNamesToCssm(npoint.crlIssuer, *cpoint.crlIssuer,
				coder, alloc);
		}
	}
}

#pragma mark ----- IssuingDistributionPoint -----

void CL_nssIssuingDistPointToCssm(
	NSS_IssuingDistributionPoint *nssIdp,
	CE_IssuingDistributionPoint	*cssmIdp,
	SecNssCoder					&coder,
	Allocator					&alloc)
{
	/* All fields optional */
	memset(cssmIdp, 0, sizeof(*cssmIdp));
	if(nssIdp->distPointName) {
		CE_DistributionPointName *cssmDp = (CE_DistributionPointName *)
			alloc.malloc(sizeof(CE_DistributionPointName));
			
		/*
		 * This one is currently still encoded; we have to peek
		 * at its tag and decode accordingly.
		 */
		CL_decodeDistributionPointName(*nssIdp->distPointName,
			*cssmDp, coder, alloc);
		cssmIdp->distPointName = cssmDp;
	}
	if(nssIdp->onlyUserCerts) {
		cssmIdp->onlyUserCertsPresent = CSSM_TRUE;
		cssmIdp->onlyUserCerts = clNssBoolToCssm(*nssIdp->onlyUserCerts);
	}
	if(nssIdp->onlyCACerts) {
		cssmIdp->onlyCACertsPresent = CSSM_TRUE;
		cssmIdp->onlyCACerts = clNssBoolToCssm(*nssIdp->onlyCACerts);
	}
	if(nssIdp->onlySomeReasons) {
		cssmIdp->onlySomeReasonsPresent = CSSM_TRUE;
		if(nssIdp->onlySomeReasons->Length > 0) {
			cssmIdp->onlySomeReasons = *nssIdp->onlySomeReasons->Data;
		}
		else {
			cssmIdp->onlySomeReasons = 0;
		}
	}
	if(nssIdp->indirectCRL) {
		cssmIdp->indirectCrlPresent = CSSM_TRUE;
		cssmIdp->indirectCrl = clNssBoolToCssm(*nssIdp->indirectCRL);
	}
}

#pragma mark --- CE_NameConstraints <--> NSS_NameConstraints ---

void CL_cssmNameConstraintsToNss(
	const CE_NameConstraints		&cdsaObj,
	NSS_NameConstraints				&nssObj,
	SecNssCoder						&coder)
{
	//%%%FIXME tba
}

void CL_nssNameConstraintsToCssm(
	const NSS_NameConstraints		&nssObj,
	CE_NameConstraints				&cdsaObj,
	SecNssCoder 					&coder,	// for temp decoding
	Allocator						&alloc)
{
	//%%%FIXME tba
}

void CL_freeCssmNameConstraints(
	CE_NameConstraints				*cssmNcs,
	Allocator						&alloc)
{
	if(cssmNcs == NULL) {
		return;
	}
//%%%FIXME need to add a CL_freeCssmGeneralSubtrees function to clNameUtils{.h,.cpp}
#if 0
	switch(cssmDpn->nameType) {
		case CE_CDNT_FullName:
			CL_freeCssmGeneralNames(cssmDpn->dpn.fullName, alloc);
			alloc.free(cssmDpn->dpn.fullName);
			break;
		case CE_CDNT_NameRelativeToCrlIssuer:
			CL_freeX509Rdn(cssmDpn->dpn.rdn, alloc);
			alloc.free(cssmDpn->dpn.rdn);
			break;
	}
#endif
	memset(cssmNcs, 0, sizeof(*cssmNcs));
}

#pragma mark --- CE_PolicyMappings <--> NSS_PolicyMappings ---

void CL_cssmPolicyMappingsToNss(
	const CE_PolicyMappings			&cdsaObj,
	NSS_PolicyMappings				&nssObj,
	SecNssCoder						&coder)
{
	//%%%FIXME tba
}

void CL_nssPolicyMappingsToCssm(
	const NSS_PolicyMappings		&nssObj,
	CE_PolicyMappings				&cdsaObj,
	SecNssCoder 					&coder,	// for temp decoding
	Allocator						&alloc)
{
	//%%%FIXME tba
}

void CL_freeCssmPolicyMappings(
	CE_PolicyMappings				*cssmPms,
	Allocator						&alloc)
{
	if(cssmPms == NULL) {
		return;
	}
	//%%%FIXME tba

	memset(cssmPms, 0, sizeof(*cssmPms));
}

#pragma mark --- CE_PolicyConstraints <--> NSS_PolicyConstraints ---

void CL_cssmPolicyConstraintsToNss(
	const CE_PolicyConstraints		*cdsaObj,
	NSS_PolicyConstraints			*nssObj,
	SecNssCoder						&coder)
{
	//%%%FIXME tba
}

void CL_nssPolicyConstraintsToCssm(
	const NSS_PolicyConstraints		*nssObj,
	CE_PolicyConstraints			*cdsaObj,
	SecNssCoder 					&coder,	// for temp decoding
	Allocator						&alloc)
{
	memset(cdsaObj, 0, sizeof(*cdsaObj));
	if(nssObj->requireExplicitPolicy.Data) {
		cdsaObj->requireExplicitPolicyPresent = CSSM_TRUE;
		cdsaObj->inhibitPolicyMapping = clDataToInt(
			nssObj->requireExplicitPolicy, 0);
	}
	if(nssObj->inhibitPolicyMapping.Data) {
		cdsaObj->inhibitPolicyMappingPresent = CSSM_TRUE;
		cdsaObj->inhibitPolicyMapping = clDataToInt(
			nssObj->inhibitPolicyMapping, 0);
	}
}

void CL_freeCssmPolicyConstraints(
	CE_PolicyConstraints			*cssmPcs,
	Allocator						&alloc)
{
	if(cssmPcs == NULL) {
		return;
	}

	memset(cssmPcs, 0, sizeof(*cssmPcs));
}


#pragma mark ----- ECDSA_SigAlgParams support -----

/* 
 * Some implementations use a two-OID mechanism to specify ECDSA signature
 * algorithm with a digest of other than SHA1. This is really not necessary;
 * we use the single-OID method (e.g. CSSMOID_ECDSA_WithSHA512) when 
 * encoding, but we have to accomodate externally generated items with 
 * the two-OID method. This routine decodes the digest OID and infers a 
 * CSSM_ALGORITHMS from it.
 * Throws CSSMERR_CL_UNKNOWN_FORMAT on any error.
 */
CSSM_ALGORITHMS CL_nssDecodeECDSASigAlgParams(
	const CSSM_DATA &encParams,
	SecNssCoder &coder)
{
	CSSM_X509_ALGORITHM_IDENTIFIER algParams;
	memset(&algParams, 0, sizeof(algParams));
	PRErrorCode prtn = coder.decodeItem(encParams, kSecAsn1AlgorithmIDTemplate, &algParams);
	if(prtn) {
		clErrorLog("CL_nssDecodeECDSASigAlgParams: error decoding CSSM_X509_ALGORITHM_IDENTIFIER\n");
		CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
	}
	
	/* get the digest algorithm, convert to ECDSA w/digest OID */
	CSSM_ALGORITHMS digestAlg = CL_oidToAlg(algParams.algorithm);
	switch(digestAlg) {
		case CSSM_ALGID_SHA1:
			return CSSM_ALGID_SHA1WithECDSA;
		case CSSM_ALGID_SHA224:
			return CSSM_ALGID_SHA224WithECDSA;
		case CSSM_ALGID_SHA256:
			return CSSM_ALGID_SHA256WithECDSA;
		case CSSM_ALGID_SHA384:
			return CSSM_ALGID_SHA384WithECDSA;
		case CSSM_ALGID_SHA512:
			return CSSM_ALGID_SHA512WithECDSA;
		default:
			clErrorLog("CL_nssDecodeECDSASigAlgParams: unknown digest algorithm\n");
			CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
	}
}

#pragma mark ----- Top-level Cert/CRL encode and decode -----

/*
 * To ensure a secure means of signing and verifying TBSCert blobs, we
 * provide these functions to encode and decode just the top-level
 * elements of a certificate. Unfortunately there is no guarantee 
 * that when you decode and re-encode a TBSCert blob, you get the 
 * same thing you started with (although with DER rules, as opposed 
 * to BER rules, you should). Thus when signing, we sign the TBSCert
 * and encode the signed cert here without ever decoding the TBSCert (or,
 * at least, without using the decoded version to get the encoded TBS blob).
 */

void CL_certCrlDecodeComponents(
	const CssmData 	&signedItem,		// DER-encoded cert or CRL
	CssmOwnedData	&tbsBlob,			// still DER-encoded
	CssmOwnedData	&algId,				// ditto
	CssmOwnedData	&rawSig)			// raw bits (not an encoded AsnBits)
{
	/* BER-decode into temp memory */
	NSS_SignedCertOrCRL nssObj;
	SecNssCoder coder;
	PRErrorCode prtn;
	
	memset(&nssObj, 0, sizeof(nssObj));
	prtn = coder.decode(signedItem.data(), signedItem.length(),
		kSecAsn1SignedCertOrCRLTemplate, &nssObj);
	if(prtn) {
		CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
	}
	
	/* tbsBlob and algId are raw ASN_ANY including tags, which we pass 
	 * back to caller intact */
	tbsBlob.copy(nssObj.tbsBlob.Data, nssObj.tbsBlob.Length);
	algId.copy(nssObj.signatureAlgorithm.Data, 
		nssObj.signatureAlgorithm.Length);
		
	/* signature is a bit string which we do in fact decode */
	rawSig.copy(nssObj.signature.Data,
		(nssObj.signature.Length + 7) / 8);
}


/*
 * Given pre-DER-encoded blobs, do the final encode step for a signed cert.
 */
void 
CL_certEncodeComponents(
	const CssmData		&TBSCert,		// DER-encoded
	const CssmData		&algId,			// ditto
	const CssmData		&rawSig,		// raw bits, not encoded
	CssmOwnedData 		&signedCert)	// DER-encoded
{
	NSS_SignedCertOrCRL nssObj;
	nssObj.tbsBlob.Data = TBSCert.Data;
	nssObj.tbsBlob.Length = TBSCert.Length;
	nssObj.signatureAlgorithm.Data = algId.Data;
	nssObj.signatureAlgorithm.Length = algId.Length;
	nssObj.signature.Data = rawSig.Data;
	nssObj.signature.Length = rawSig.Length * 8;	// BIT STRING
	
	PRErrorCode prtn;
	
	prtn = SecNssEncodeItemOdata(&nssObj,
		kSecAsn1SignedCertOrCRLTemplate,signedCert);
	if(prtn) {
		CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR);
	}

}