]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_apple_x509_cl/lib/clNssUtils.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_apple_x509_cl / lib / clNssUtils.cpp
diff --git a/libsecurity_apple_x509_cl/lib/clNssUtils.cpp b/libsecurity_apple_x509_cl/lib/clNssUtils.cpp
new file mode 100644 (file)
index 0000000..d4a9ddd
--- /dev/null
@@ -0,0 +1,1386 @@
+/*
+ * 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);
+       }
+
+}