]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTests/clxutils/extenTestTp/extenTestTp.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTests / clxutils / extenTestTp / extenTestTp.cpp
diff --git a/SecurityTests/clxutils/extenTestTp/extenTestTp.cpp b/SecurityTests/clxutils/extenTestTp/extenTestTp.cpp
new file mode 100644 (file)
index 0000000..73908b3
--- /dev/null
@@ -0,0 +1,1430 @@
+/*
+ * extenTestTp - verify encoding and decoding of extensions using 
+ *                        CSSM_TP_SubmitCredRequest() to create the certs. 
+ */
+#include <security_cdsa_utils/cuFileIo.h>
+#include <clAppUtils/CertBuilderApp.h>
+#include <utilLib/common.h>
+#include <utilLib/cspwrap.h>
+#include <clAppUtils/clutils.h>
+#include <security_cdsa_utils/cuPrintCert.h>
+#include <security_cdsa_utils/cuOidParser.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <Security/cssm.h>
+#include <Security/x509defs.h>
+#include <Security/oidsattr.h>
+#include <Security/oidscert.h>
+#include <Security/oidsalg.h>
+#include <Security/certextensions.h>
+#include <Security/cssmapple.h>
+
+#define KEY_ALG                        CSSM_ALGID_RSA
+#define SIG_ALG                        CSSM_ALGID_SHA1WithRSA
+#define SIG_OID                        CSSMOID_SHA1WithRSA
+#define KEY_SIZE_BITS  CSP_RSA_KEY_SIZE_DEFAULT
+#define SUBJ_KEY_LABEL "subjectKey"
+
+#define LOOPS_DEF      10
+
+static void usage(char **argv)
+{
+       printf("Usage: %s [options]\n", argv[0]);
+       printf("Options:\n");
+       printf("   e=extenSpec (default = all)\n");
+       printf("      k  keyUsage\n");
+       printf("      b  basicConstraints\n");
+       printf("      x  extendedKeyUsage\n");
+       printf("      s  subjectKeyId\n");
+       printf("      a  authorityKeyId\n");
+       printf("      t  SubjectAltName\n");
+       printf("      i  IssuerAltName\n");
+       printf("      c  certPolicies\n");
+       printf("      n  netscapeCertType\n");
+       printf("      p  CRLDistributionPoints\n");
+       printf("      A  AuthorityInfoAccess\n");
+       printf("      S  SubjectInfoAccess\n");
+       printf("   w(rite blobs)\n");
+       printf("   f=fileName (default is extension-specific file name)\n");
+       printf("   d(isplay certs)\n");
+       printf("   l=loops (default = %d)\n", LOOPS_DEF);
+       printf("   p(ause on each loop)\n");
+       printf("   P(ause on each cert)\n");
+       exit(1);
+}
+
+/* subject and issuer - we aren't testing this */
+CSSM_APPLE_TP_NAME_OID dummyRdn[] = 
+{
+       { "Apple Computer",                                     &CSSMOID_OrganizationName },
+       { "Doug Mitchell",                                      &CSSMOID_CommonName }
+};
+#define NUM_DUMMY_NAMES        (sizeof(dummyRdn) / sizeof(CB_NameOid))
+
+/*
+ * Static components we reuse for each encode/decode. 
+ */
+static CSSM_KEY                        subjPrivKey;    
+static CSSM_KEY                        subjPubKey;             
+
+static CSSM_BOOL randBool()
+{
+       unsigned r = genRand(1, 0x10000000);
+       return (r & 0x1) ? CSSM_TRUE : CSSM_FALSE;
+}
+
+/* Fill a CSSM_DATA with random data. Its referent is allocd with malloc. */
+static void randData(  
+       CSSM_DATA_PTR   data,
+       uint8                   maxLen)
+{
+       data->Data = (uint8 *)malloc(maxLen);
+       simpleGenData(data, 1, maxLen);
+}
+
+/*
+ * Various compare tests
+ */
+int compBool(
+       CSSM_BOOL pre,
+       CSSM_BOOL post,
+       const char *desc)
+{
+       if(pre == post) {
+               return 0;
+       }
+       printf("***Boolean miscompare on %s\n", desc);
+       /* in case a CSSM_TRUE isn't exactly right... */
+       switch(post) {
+               case CSSM_FALSE:
+               case CSSM_TRUE:
+                       break;
+               default:
+                       printf("*** post value is %d expected %d\n",
+                               (int)post, (int)pre);
+                       break;
+       }
+       return 1;
+}
+
+static int compCssmData(
+       CSSM_DATA &d1,
+       CSSM_DATA &d2,
+       const char *desc)
+{
+       if(appCompareCssmData(&d1, &d2)) {
+               return 0;
+       }
+       printf("CSSM_DATA miscompare on %s\n", desc);
+       return 1;
+}      
+
+#pragma mark ----- individual extension tests -----
+
+#pragma mark --- CE_KeyUsage ---
+static void kuCreate(void *arg)
+{
+       CE_KeyUsage *ku = (CE_KeyUsage *)arg;
+       
+       /* set two random valid bits */
+       *ku = 0;
+       *ku |= 1 << genRand(7, 15);
+       *ku |= 1 << genRand(7, 15);
+}
+
+static unsigned kuCompare(const void *pre, const void *post)
+{
+       const CE_KeyUsage *kuPre = (CE_KeyUsage *)pre;
+       const CE_KeyUsage *kuPost = (CE_KeyUsage *)post;
+       if(*kuPre != *kuPost) {
+               printf("***Miscompare in CE_KeyUsage\n");
+               return 1;
+       }
+       return 0;
+}
+
+#pragma mark --- CE_BasicConstraints ---
+static void bcCreate(void *arg)
+{
+       CE_BasicConstraints *bc = (CE_BasicConstraints *)arg;
+       bc->cA = randBool();
+       bc->pathLenConstraintPresent = randBool();
+       if(bc->pathLenConstraintPresent) {
+               bc->pathLenConstraint = genRand(1,10);
+       }
+}
+
+static unsigned bcCompare(const void *pre, const void *post)
+{
+       const CE_BasicConstraints *bcpre = (CE_BasicConstraints *)pre;
+       const CE_BasicConstraints *bcpost = (CE_BasicConstraints *)post;
+       unsigned rtn = 0;
+       
+       rtn += compBool(bcpre->cA, bcpost->cA, "BasicConstraints.cA");
+       rtn += compBool(bcpre->pathLenConstraintPresent, 
+               bcpost->pathLenConstraintPresent, 
+               "BasicConstraints.pathLenConstraintPresent");
+       if(bcpre->pathLenConstraint != bcpost->pathLenConstraint) {
+               printf("BasicConstraints.pathLenConstraint mismatch\n");
+               rtn++;
+       }
+       return rtn;
+}
+
+#pragma mark --- CE_SubjectKeyID ---
+static void skidCreate(void *arg)
+{
+       CSSM_DATA_PTR skid = (CSSM_DATA_PTR)arg;
+       randData(skid, 16);
+}
+
+static unsigned skidCompare(const void *pre, const void *post)
+{
+       CSSM_DATA_PTR spre = (CSSM_DATA_PTR)pre;
+       CSSM_DATA_PTR spost = (CSSM_DATA_PTR)post;
+       return compCssmData(*spre, *spost, "SubjectKeyID");
+}
+
+static void skidFree(void *arg)
+{
+       CSSM_DATA_PTR skid = (CSSM_DATA_PTR)arg;
+       free(skid->Data);
+}
+
+#pragma mark --- CE_NetscapeCertType ---
+static void nctCreate(void *arg)
+{
+       CE_NetscapeCertType *nct = (CE_NetscapeCertType *)arg;
+       
+       /* set two random valid bits */
+       *nct = 0;
+       *nct |= 1 << genRand(8, 15);
+       *nct |= 1 << genRand(8, 15);
+}
+
+static unsigned nctCompare(const void *pre, const void *post)
+{
+       const CE_NetscapeCertType *nPre = (CE_NetscapeCertType *)pre;
+       const CE_NetscapeCertType *nPost = (CE_NetscapeCertType *)post;
+       if(*nPre != *nPost) {
+               printf("***Miscompare in CE_NetscapeCertType\n");
+               return 1;
+       }
+       return 0;
+}
+
+#pragma mark --- CE_ExtendedKeyUsage ---
+
+/* a static array of meaningless OIDs, use 1.. NUM_SKU_OIDS */
+CSSM_OID ekuOids[] = {
+       CSSMOID_CrlNumber,
+       CSSMOID_CrlReason,
+       CSSMOID_HoldInstructionCode,
+       CSSMOID_InvalidityDate
+};
+#define NUM_SKU_OIDS 4
+
+static void ekuCreate(void *arg)
+{
+       CE_ExtendedKeyUsage *eku = (CE_ExtendedKeyUsage *)arg;
+       eku->numPurposes = genRand(1, NUM_SKU_OIDS);
+       eku->purposes = ekuOids;
+}
+
+static unsigned ekuCompare(const void *pre, const void *post)
+{
+       CE_ExtendedKeyUsage *ekupre = (CE_ExtendedKeyUsage *)pre;
+       CE_ExtendedKeyUsage *ekupost = (CE_ExtendedKeyUsage *)post;
+       
+       if(ekupre->numPurposes != ekupost->numPurposes) {
+               printf("CE_ExtendedKeyUsage.numPurposes miscompare\n");
+               return 1;
+       }
+       unsigned rtn = 0;
+       for(unsigned dex=0; dex<ekupre->numPurposes; dex++) {
+               rtn += compCssmData(ekupre->purposes[dex],
+                       ekupost->purposes[dex], "CE_ExtendedKeyUsage.purposes");
+       }
+       return rtn;
+}
+
+
+#pragma mark --- general purpose X509 name generator ---
+
+/* Attr/Value pairs, pick one of NUM_ATTR_STRINGS */
+static char *attrStrings[] = {
+       (char *)"thisName",
+       (char *)"anotherName",
+       (char *)"someOtherName"
+};
+#define NUM_ATTR_STRINGS       3
+
+/* A/V type, pick one of NUM_ATTR_TYPES */
+static CSSM_OID attrTypes[] = {
+       CSSMOID_Surname,
+       CSSMOID_CountryName,
+       CSSMOID_OrganizationName,
+       CSSMOID_Description
+};
+#define NUM_ATTR_TYPES 4
+
+/* A/V tag, pick one of NUM_ATTR_TAGS */
+static char attrTags[] = {
+       BER_TAG_PRINTABLE_STRING,
+       BER_TAG_IA5_STRING,
+       BER_TAG_T61_STRING
+};
+#define NUM_ATTR_TAGS  3
+
+static void rdnCreate(
+       CSSM_X509_RDN_PTR rdn)
+{
+       unsigned numPairs = genRand(1,4);
+       rdn->numberOfPairs = numPairs;
+       unsigned len = numPairs * sizeof(CSSM_X509_TYPE_VALUE_PAIR);
+       rdn->AttributeTypeAndValue = 
+               (CSSM_X509_TYPE_VALUE_PAIR_PTR)malloc(len);
+       memset(rdn->AttributeTypeAndValue, 0, len);
+       
+       for(unsigned atvDex=0; atvDex<numPairs; atvDex++) {
+               CSSM_X509_TYPE_VALUE_PAIR &pair = 
+                       rdn->AttributeTypeAndValue[atvDex];
+               unsigned die = genRand(1, NUM_ATTR_TYPES);
+               pair.type = attrTypes[die - 1];
+               die = genRand(1, NUM_ATTR_STRINGS);
+               char *str = attrStrings[die - 1];
+               pair.value.Data = (uint8 *)str;
+               pair.value.Length = strlen(str);
+               die = genRand(1, NUM_ATTR_TAGS);
+               pair.valueType = attrTags[die - 1];
+       }
+}
+
+static unsigned rdnCompare(
+       CSSM_X509_RDN_PTR rdn1,
+       CSSM_X509_RDN_PTR rdn2)
+{
+       if(rdn1->numberOfPairs != rdn2->numberOfPairs) {
+               printf("***Mismatch in numberOfPairs\n");
+               return 1;
+       }
+       unsigned rtn = 0;
+       for(unsigned atvDex=0; atvDex<rdn1->numberOfPairs; atvDex++) {
+               CSSM_X509_TYPE_VALUE_PAIR &p1 = 
+                       rdn1->AttributeTypeAndValue[atvDex];
+               CSSM_X509_TYPE_VALUE_PAIR &p2 = 
+                       rdn2->AttributeTypeAndValue[atvDex];
+               if(p1.valueType != p2.valueType) {
+                       printf("***valueType miscompare\n");
+                       rtn++;
+               }
+               if(compCssmData(p1.type, p2.type, "ATV.type")) {
+                       rtn++;
+               }
+               if(compCssmData(p1.value, p2.value, "ATV.value")) {
+                       rtn++;
+               }
+       }
+       return rtn;
+}
+
+static void rdnFree(
+       CSSM_X509_RDN_PTR rdn)
+{
+       free(rdn->AttributeTypeAndValue);
+}
+
+static void x509NameCreate(
+       CSSM_X509_NAME_PTR x509Name)
+{
+       memset(x509Name, 0, sizeof(*x509Name));
+       unsigned numRdns = genRand(1,4);
+       x509Name->numberOfRDNs = numRdns;
+       unsigned len = numRdns * sizeof(CSSM_X509_RDN);
+       x509Name->RelativeDistinguishedName = (CSSM_X509_RDN_PTR)malloc(len);
+       memset(x509Name->RelativeDistinguishedName, 0, len);
+       
+       for(unsigned rdnDex=0; rdnDex<numRdns; rdnDex++) {
+               CSSM_X509_RDN &rdn = x509Name->RelativeDistinguishedName[rdnDex];
+               rdnCreate(&rdn);
+       }
+}
+
+static unsigned x509NameCompare(
+       const CSSM_X509_NAME_PTR n1,
+       const CSSM_X509_NAME_PTR n2)
+{
+       if(n1->numberOfRDNs != n2->numberOfRDNs) {
+               printf("***Mismatch in numberOfRDNs\n");
+               return 1;
+       }
+       unsigned rtn = 0;
+       for(unsigned rdnDex=0; rdnDex<n1->numberOfRDNs; rdnDex++) {
+               CSSM_X509_RDN &rdn1 = n1->RelativeDistinguishedName[rdnDex];
+               CSSM_X509_RDN &rdn2 = n2->RelativeDistinguishedName[rdnDex];
+               rtn += rdnCompare(&rdn1, &rdn2);
+       }
+       return rtn;
+}
+
+static void x509NameFree(
+       CSSM_X509_NAME_PTR n)
+{
+       for(unsigned rdnDex=0; rdnDex<n->numberOfRDNs; rdnDex++) {
+               CSSM_X509_RDN &rdn = n->RelativeDistinguishedName[rdnDex];
+               rdnFree(&rdn);
+       }
+       free(n->RelativeDistinguishedName);
+}
+
+#pragma mark --- general purpose GeneralNames generator ---
+
+#define SOME_URL_1     (char *)"http://foo.bar.com"
+#define SOME_URL_2     (char *)"http://bar.foo.com"
+#define SOME_DNS_1     (char *)"Some DNS"
+#define SOME_DNS_2     (char *)"Another DNS"
+unsigned char  someIpAdr_1[] = {208, 161, 124, 209 };
+unsigned char  someIpAdr_2[] = {10, 0, 61, 5};
+
+static void genNameCreate(CE_GeneralName *name)
+{
+       unsigned type = genRand(1, 5);
+       const char *src;
+       unsigned char *usrc;
+       switch(type) {
+               case 1:
+                       name->nameType = GNT_URI;
+                       name->berEncoded = CSSM_FALSE;
+                       src = randBool() ? SOME_URL_1 : SOME_URL_2;
+                       appCopyData(src, strlen(src), &name->name);
+                       break;
+
+               case 2:
+                       name->nameType = GNT_RegisteredID;
+                       name->berEncoded = CSSM_FALSE;
+                       appCopyData(CSSMOID_SubjectDirectoryAttributes.Data,
+                               CSSMOID_SubjectDirectoryAttributes.Length,
+                               &name->name);
+                       break;
+                       
+               case 3:
+                       name->nameType = GNT_DNSName;
+                       name->berEncoded = CSSM_FALSE;
+                       src = randBool() ? SOME_DNS_1 : SOME_DNS_2;
+                       appCopyData(src, strlen(src), &name->name);
+                       break;
+                       
+               case 4:
+                       name->nameType = GNT_IPAddress;
+                       name->berEncoded = CSSM_FALSE;
+                       usrc = randBool() ? someIpAdr_1 : someIpAdr_2;
+                       appCopyData(usrc, 4, &name->name);
+                       break;
+                       
+               case 5:
+               {
+                       /* X509_NAME, the hard one */
+                       name->nameType = GNT_DirectoryName;
+                       name->berEncoded = CSSM_FALSE;
+                       appSetupCssmData(&name->name, sizeof(CSSM_X509_NAME));
+                       x509NameCreate((CSSM_X509_NAME_PTR)name->name.Data);
+               }
+       }
+}
+
+static void genNamesCreate(void *arg)
+{
+       CE_GeneralNames *names = (CE_GeneralNames *)arg;
+       names->numNames = genRand(1, 3);
+       // one at a time
+       //names->numNames = 1;
+       names->generalName = (CE_GeneralName *)malloc(names->numNames * 
+               sizeof(CE_GeneralName));
+       memset(names->generalName, 0, names->numNames * sizeof(CE_GeneralName));
+
+       for(unsigned i=0; i<names->numNames; i++) {
+               CE_GeneralName *name = &names->generalName[i];
+               genNameCreate(name);
+       }
+}
+
+static unsigned genNameCompare(
+       CE_GeneralName *npre,
+       CE_GeneralName *npost)
+{
+       unsigned rtn = 0;
+       if(npre->nameType != npost->nameType) {
+               printf("***CE_GeneralName.nameType miscompare\n");
+               rtn++;
+       }
+       if(compBool(npre->berEncoded, npost->berEncoded, 
+                       "CE_GeneralName.berEncoded")) {
+               rtn++;
+       }
+       
+       /* nameType-specific compare */
+       switch(npre->nameType) {
+               case GNT_RFC822Name:
+                       rtn += compCssmData(npre->name, npost->name,
+                               "CE_GeneralName.RFC822Name");
+                       break;
+               case GNT_DNSName:
+                       rtn += compCssmData(npre->name, npost->name,
+                               "CE_GeneralName.DNSName");
+                       break;
+               case GNT_URI:
+                       rtn += compCssmData(npre->name, npost->name,
+                               "CE_GeneralName.URI");
+                       break;
+               case GNT_IPAddress:
+                       rtn += compCssmData(npre->name, npost->name,
+                               "CE_GeneralName.RFIPAddressC822Name");
+                       break;
+               case GNT_RegisteredID:
+                       rtn += compCssmData(npre->name, npost->name,
+                               "CE_GeneralName.RegisteredID");
+                       break;
+               case GNT_DirectoryName:
+                       rtn += x509NameCompare((CSSM_X509_NAME_PTR)npre->name.Data,
+                               (CSSM_X509_NAME_PTR)npost->name.Data);
+                       break;
+               default:
+                       printf("****BRRZAP! genNamesCompare needs work\n");
+                       rtn++;
+       }
+       return rtn;
+}
+
+static unsigned genNamesCompare(const void *pre, const void *post)
+{
+       const CE_GeneralNames *gnPre = (CE_GeneralNames *)pre;
+       const CE_GeneralNames *gnPost = (CE_GeneralNames *)post;
+       unsigned rtn = 0;
+       
+       if((gnPre == NULL) || (gnPost == NULL)) {
+               printf("***Bad GenNames pointer\n");
+               return 1;
+       }
+       if(gnPre->numNames != gnPost->numNames) {
+               printf("***CE_GeneralNames.numNames miscompare\n");
+               return 1;
+       }
+       for(unsigned dex=0; dex<gnPre->numNames; dex++) {
+               CE_GeneralName *npre  = &gnPre->generalName[dex];
+               CE_GeneralName *npost = &gnPost->generalName[dex];
+               rtn += genNameCompare(npre, npost);
+       }
+       return rtn;
+}
+
+
+static void genNameFree(CE_GeneralName *n)
+{
+       switch(n->nameType) {
+               case GNT_DirectoryName:
+                       x509NameFree((CSSM_X509_NAME_PTR)n->name.Data);
+                       CSSM_FREE(n->name.Data);
+                       break;
+               default:
+                       CSSM_FREE(n->name.Data);
+                       break;
+       }
+}
+
+
+static void genNamesFree(void *arg)
+{
+       const CE_GeneralNames *gn = (CE_GeneralNames *)arg;
+       for(unsigned dex=0; dex<gn->numNames; dex++) {
+               CE_GeneralName *n = (CE_GeneralName *)&gn->generalName[dex];
+               genNameFree(n);
+       }
+       free(gn->generalName);
+}
+
+#pragma mark --- CE_CRLDistPointsSyntax ---
+static void cdpCreate(void *arg)
+{
+       CE_CRLDistPointsSyntax *cdp = (CE_CRLDistPointsSyntax *)arg;
+       //cdp->numDistPoints = genRand(1,3);
+       // one at a time
+       cdp->numDistPoints = 1;
+       unsigned len = sizeof(CE_CRLDistributionPoint) * cdp->numDistPoints;
+       cdp->distPoints = (CE_CRLDistributionPoint *)malloc(len);
+       memset(cdp->distPoints, 0, len);
+       
+       for(unsigned dex=0; dex<cdp->numDistPoints; dex++) {
+               CE_CRLDistributionPoint *pt = &cdp->distPoints[dex];
+               
+               /* all fields optional */
+               if(randBool()) {
+                       CE_DistributionPointName *dpn = pt->distPointName =
+                               (CE_DistributionPointName *)malloc(
+                                       sizeof(CE_DistributionPointName));
+                       memset(dpn, 0, sizeof(CE_DistributionPointName));
+                       
+                       /* CE_DistributionPointName has two flavors */
+                       if(randBool()) {
+                               dpn->nameType = CE_CDNT_FullName;
+                               dpn->dpn.fullName = (CE_GeneralNames *)malloc(
+                                       sizeof(CE_GeneralNames));
+                               memset(dpn->dpn.fullName, 0, sizeof(CE_GeneralNames));
+                               genNamesCreate(dpn->dpn.fullName);
+                       }
+                       else {
+                               dpn->nameType = CE_CDNT_NameRelativeToCrlIssuer;
+                               dpn->dpn.rdn = (CSSM_X509_RDN_PTR)malloc(
+                                       sizeof(CSSM_X509_RDN));
+                               memset(dpn->dpn.rdn, 0, sizeof(CSSM_X509_RDN));
+                               rdnCreate(dpn->dpn.rdn);
+                       }
+               }       /* creating CE_DistributionPointName */
+               
+               pt->reasonsPresent = randBool();
+               if(pt->reasonsPresent) {
+                       CE_CrlDistReasonFlags *cdr = &pt->reasons;
+                       /* set two random valid bits */
+                       *cdr = 0;
+                       *cdr |= 1 << genRand(0,7);
+                       *cdr |= 1 << genRand(0,7);
+               }
+               
+               /* make sure at least one present */
+               if((!pt->distPointName && !pt->reasonsPresent) || randBool()) {
+                       pt->crlIssuer = (CE_GeneralNames *)malloc(sizeof(CE_GeneralNames));
+                       memset(pt->crlIssuer, 0, sizeof(CE_GeneralNames));
+                       genNamesCreate(pt->crlIssuer);
+               }
+       }
+}
+
+static unsigned cdpCompare(const void *pre, const void *post)
+{
+       CE_CRLDistPointsSyntax *cpre = (CE_CRLDistPointsSyntax *)pre;
+       CE_CRLDistPointsSyntax *cpost = (CE_CRLDistPointsSyntax *)post;
+       
+       if(cpre->numDistPoints != cpost->numDistPoints) {
+               printf("***CE_CRLDistPointsSyntax.numDistPoints miscompare\n");
+               return 1;
+       }
+       unsigned rtn = 0;
+       for(unsigned dex=0; dex<cpre->numDistPoints; dex++) {
+               CE_CRLDistributionPoint *ptpre  = &cpre->distPoints[dex];
+               CE_CRLDistributionPoint *ptpost = &cpost->distPoints[dex];
+               
+               if(ptpre->distPointName) {
+                       if(ptpost->distPointName == NULL) {
+                               printf("***NULL distPointName post decode\n");
+                               rtn++;
+                               goto checkReason;
+                       }
+                       CE_DistributionPointName *dpnpre = ptpre->distPointName;
+                       CE_DistributionPointName *dpnpost = ptpost->distPointName;
+                       if(dpnpre->nameType != dpnpost->nameType) {
+                               printf("***CE_DistributionPointName.nameType miscompare\n");
+                               rtn++;
+                               goto checkReason;
+                       }
+                       if(dpnpre->nameType == CE_CDNT_FullName) {
+                               rtn += genNamesCompare(dpnpre->dpn.fullName, dpnpost->dpn.fullName);
+                       }
+                       else {
+                               rtn += rdnCompare(dpnpre->dpn.rdn, dpnpost->dpn.rdn);
+                       }
+                               
+               }
+               else if(ptpost->distPointName != NULL) {
+                       printf("***NON NULL distPointName post decode\n");
+                       rtn++;
+               }
+               
+       checkReason:
+               if(ptpre->reasons != ptpost->reasons) {
+                       printf("***CE_CRLDistributionPoint.reasons miscompare\n");
+                       rtn++;
+               }
+               
+               if(ptpre->crlIssuer) {
+                       if(ptpost->crlIssuer == NULL) {
+                               printf("***NULL crlIssuer post decode\n");
+                               rtn++;
+                               continue;
+                       }
+                       CE_GeneralNames *gnpre = ptpre->crlIssuer;
+                       CE_GeneralNames *gnpost = ptpost->crlIssuer;
+                       rtn += genNamesCompare(gnpre, gnpost);
+               }
+               else if(ptpost->crlIssuer != NULL) {
+                       printf("***NON NULL crlIssuer post decode\n");
+                       rtn++;
+               }
+       }
+       return rtn;
+}
+
+static void cdpFree(void *arg)
+{
+       CE_CRLDistPointsSyntax *cdp = (CE_CRLDistPointsSyntax *)arg;
+       for(unsigned dex=0; dex<cdp->numDistPoints; dex++) {
+               CE_CRLDistributionPoint *pt = &cdp->distPoints[dex];
+               if(pt->distPointName) {
+                       CE_DistributionPointName *dpn = pt->distPointName;
+                       if(dpn->nameType == CE_CDNT_FullName) {
+                               genNamesFree(dpn->dpn.fullName);
+                               free(dpn->dpn.fullName);
+                       }
+                       else {
+                               rdnFree(dpn->dpn.rdn);
+                               free(dpn->dpn.rdn);
+                       }
+                       free(dpn);
+               }
+               
+               if(pt->crlIssuer) {
+                       genNamesFree(pt->crlIssuer);
+                       free(pt->crlIssuer);
+               }
+       }       
+       free(cdp->distPoints);
+}
+
+#pragma mark --- CE_AuthorityKeyID ---
+static void authKeyIdCreate(void *arg)
+{
+       CE_AuthorityKeyID *akid = (CE_AuthorityKeyID *)arg;
+       
+       /* all three fields optional */
+       
+       akid->keyIdentifierPresent = randBool();
+       if(akid->keyIdentifierPresent) {
+               randData(&akid->keyIdentifier, 16);
+       }
+       
+       akid->generalNamesPresent = randBool();
+       if(akid->generalNamesPresent) {
+               akid->generalNames = 
+                       (CE_GeneralNames *)malloc(sizeof(CE_GeneralNames));
+               memset(akid->generalNames, 0, sizeof(CE_GeneralNames));
+               genNamesCreate(akid->generalNames);
+       }
+       
+       if(!akid->keyIdentifierPresent & !akid->generalNamesPresent) {
+               /* force at least one to be present */
+               akid->serialNumberPresent = CSSM_TRUE;
+       }
+       else  {
+               akid->serialNumberPresent = randBool();
+       }
+       if(akid->serialNumberPresent) {
+               randData(&akid->serialNumber, 16);
+       }
+
+}
+
+static unsigned authKeyIdCompare(const void *pre, const void *post)
+{
+       CE_AuthorityKeyID *akpre = (CE_AuthorityKeyID *)pre;
+       CE_AuthorityKeyID *akpost = (CE_AuthorityKeyID *)post;
+       unsigned rtn = 0;
+       
+       if(compBool(akpre->keyIdentifierPresent, akpost->keyIdentifierPresent,
+                       "CE_AuthorityKeyID.keyIdentifierPresent")) {
+               rtn++;
+       }
+       else if(akpre->keyIdentifierPresent) {
+               rtn += compCssmData(akpre->keyIdentifier,
+                       akpost->keyIdentifier, "CE_AuthorityKeyID.keyIdentifier");
+       }
+       
+       if(compBool(akpre->generalNamesPresent, akpost->generalNamesPresent,
+                       "CE_AuthorityKeyID.generalNamesPresent")) {
+               rtn++;
+       }
+       else if(akpre->generalNamesPresent) {
+               rtn += genNamesCompare(akpre->generalNames,
+                       akpost->generalNames);
+       }
+
+       if(compBool(akpre->serialNumberPresent, akpost->serialNumberPresent,
+                       "CE_AuthorityKeyID.serialNumberPresent")) {
+               rtn++;
+       }
+       else if(akpre->serialNumberPresent) {
+               rtn += compCssmData(akpre->serialNumber,
+                       akpost->serialNumber, "CE_AuthorityKeyID.serialNumber");
+       }
+       return rtn;
+}
+
+static void authKeyIdFree(void *arg)
+{
+       CE_AuthorityKeyID *akid = (CE_AuthorityKeyID *)arg;
+
+       if(akid->keyIdentifier.Data) {
+               free(akid->keyIdentifier.Data);
+       }
+       if(akid->generalNames) {
+               genNamesFree(akid->generalNames);               // genNamesCreate mallocd
+               free(akid->generalNames);                               // we mallocd
+       }
+       if(akid->serialNumber.Data) {
+               free(akid->serialNumber.Data);
+       }
+}
+
+#pragma mark --- CE_CertPolicies ---
+
+/* random OIDs, pick 1..NUM_CP_OIDS */
+static CSSM_OID cpOids[] = 
+{
+       CSSMOID_EmailAddress,
+       CSSMOID_UnstructuredName,
+       CSSMOID_ContentType,
+       CSSMOID_MessageDigest
+};
+#define NUM_CP_OIDS 4
+
+/* CPS strings, pick one of NUM_CPS_STR */
+static char *someCPSs[] = 
+{
+       (char *)"http://www.apple.com",
+       (char *)"https://cdnow.com",
+       (char *)"ftp:backwards.com"
+};
+#define NUM_CPS_STR            3
+
+/* make these looks like real sequences */
+static uint8 someUnotice[] = {0x30, 0x03, BER_TAG_BOOLEAN, 1, 0xff};
+static uint8 someOtherData[] = {0x30, 0x02, BER_TAG_NULL, 0};
+
+static void cpCreate(void *arg)
+{
+       CE_CertPolicies *cp = (CE_CertPolicies *)arg;
+       cp->numPolicies = genRand(1,3);
+       //cp->numPolicies = 1;
+       unsigned len = sizeof(CE_PolicyInformation) * cp->numPolicies;
+       cp->policies = (CE_PolicyInformation *)malloc(len);
+       memset(cp->policies, 0, len);
+       
+       for(unsigned polDex=0; polDex<cp->numPolicies; polDex++) {
+               CE_PolicyInformation *pi = &cp->policies[polDex];
+               unsigned die = genRand(1, NUM_CP_OIDS);
+               pi->certPolicyId = cpOids[die - 1];
+               unsigned numQual = genRand(1,3);
+               pi->numPolicyQualifiers = numQual;
+               len = sizeof(CE_PolicyQualifierInfo) * numQual;
+               pi->policyQualifiers = (CE_PolicyQualifierInfo *)
+                       malloc(len);
+               memset(pi->policyQualifiers, 0, len);
+               for(unsigned cpiDex=0; cpiDex<numQual; cpiDex++) {
+                       CE_PolicyQualifierInfo *qi = 
+                               &pi->policyQualifiers[cpiDex];
+                       if(randBool()) {
+                               qi->policyQualifierId = CSSMOID_QT_CPS;
+                               die = genRand(1, NUM_CPS_STR);
+                               qi->qualifier.Data = (uint8 *)someCPSs[die - 1];
+                               qi->qualifier.Length = strlen((char *)qi->qualifier.Data);
+                       }
+                       else {
+                               qi->policyQualifierId = CSSMOID_QT_UNOTICE;
+                               if(randBool()) {
+                                       qi->qualifier.Data = someUnotice;
+                                       qi->qualifier.Length = 5;
+                               }
+                               else {
+                                       qi->qualifier.Data = someOtherData;
+                                       qi->qualifier.Length = 4;
+                               }
+                       }
+               }
+       }
+}
+
+static unsigned cpCompare(const void *pre, const void *post)
+{
+       CE_CertPolicies *cppre  = (CE_CertPolicies *)pre;
+       CE_CertPolicies *cppost = (CE_CertPolicies *)post;
+       
+       if(cppre->numPolicies != cppost->numPolicies) {
+               printf("CE_CertPolicies.numPolicies mismatch\n");
+               return 1;
+       }
+       unsigned rtn = 0;
+       for(unsigned polDex=0; polDex<cppre->numPolicies; polDex++) {
+               CE_PolicyInformation *pipre  = &cppre->policies[polDex];
+               CE_PolicyInformation *pipost = &cppost->policies[polDex];
+               rtn += compCssmData(pipre->certPolicyId, pipost->certPolicyId,
+                       "CE_PolicyInformation.certPolicyId");
+               if(pipre->numPolicyQualifiers != pipost->numPolicyQualifiers) {
+                       printf("CE_PolicyInformation.CE_PolicyInformation mismatch\n");
+                       rtn++;
+                       continue;
+               }
+               
+               for(unsigned qiDex=0; qiDex<pipre->numPolicyQualifiers; qiDex++) {
+                       CE_PolicyQualifierInfo *qipre  = &pipre->policyQualifiers[qiDex];
+                       CE_PolicyQualifierInfo *qipost = &pipost->policyQualifiers[qiDex];
+                       rtn += compCssmData(qipre->policyQualifierId,
+                               qipost->policyQualifierId,
+                               "CE_PolicyQualifierInfo.policyQualifierId");
+                       rtn += compCssmData(qipre->qualifier,
+                               qipost->qualifier,
+                               "CE_PolicyQualifierInfo.qualifier");
+               }
+       }
+       return rtn;
+}
+
+static void cpFree(void *arg)
+{
+       CE_CertPolicies *cp = (CE_CertPolicies *)arg;
+       for(unsigned polDex=0; polDex<cp->numPolicies; polDex++) {
+               CE_PolicyInformation *pi = &cp->policies[polDex];
+               free(pi->policyQualifiers);
+       }
+       free(cp->policies);
+}
+
+#pragma mark --- CE_AuthorityInfoAccess ---
+
+/* random OIDs, pick 1..NUM_AI_OIDS */
+static CSSM_OID aiOids[] = 
+{
+       CSSMOID_AD_OCSP,
+       CSSMOID_AD_CA_ISSUERS,
+       CSSMOID_AD_TIME_STAMPING,
+       CSSMOID_AD_CA_REPOSITORY
+};
+#define NUM_AI_OIDS 4
+
+static void aiaCreate(void *arg)
+{
+       CE_AuthorityInfoAccess *aia = (CE_AuthorityInfoAccess *)arg;
+       aia->numAccessDescriptions = genRand(1,3);
+       unsigned len = aia->numAccessDescriptions * sizeof(CE_AccessDescription);
+       aia->accessDescriptions = (CE_AccessDescription *)malloc(len);
+       memset(aia->accessDescriptions, 0, len);
+       
+       for(unsigned dex=0; dex<aia->numAccessDescriptions; dex++) {
+               CE_AccessDescription *ad = &aia->accessDescriptions[dex];
+               int die = genRand(1, NUM_AI_OIDS);
+               ad->accessMethod = aiOids[die - 1];
+               genNameCreate(&ad->accessLocation);
+       }
+}
+
+static unsigned aiaCompare(const void *pre, const void *post)
+{
+       CE_AuthorityInfoAccess *apre = (CE_AuthorityInfoAccess *)pre;
+       CE_AuthorityInfoAccess *apost = (CE_AuthorityInfoAccess *)post;
+       unsigned rtn = 0;
+       
+       if(apre->numAccessDescriptions != apost->numAccessDescriptions) {
+               printf("***CE_AuthorityInfoAccess.numAccessDescriptions miscompare\n");
+               return 1;
+       }
+       for(unsigned dex=0; dex<apre->numAccessDescriptions; dex++) {
+               CE_AccessDescription *adPre  = &apre->accessDescriptions[dex];
+               CE_AccessDescription *adPost = &apost->accessDescriptions[dex];
+               if(compCssmData(adPre->accessMethod, adPost->accessMethod, 
+                               "CE_AccessDescription.accessMethod")) {
+                       rtn++;
+               }
+               rtn += genNameCompare(&adPre->accessLocation, &adPost->accessLocation);
+       }
+       return rtn;
+}
+
+static void aiaFree(void *arg)
+{
+       CE_AuthorityInfoAccess *aia = (CE_AuthorityInfoAccess *)arg;
+       for(unsigned dex=0; dex<aia->numAccessDescriptions; dex++) {
+               CE_AccessDescription *ad = &aia->accessDescriptions[dex];
+               genNameFree(&ad->accessLocation);
+       }
+       free(aia->accessDescriptions);
+}
+
+#pragma mark --- test definitions ---
+
+/*
+ * Define one extension test.
+ */
+
+/* 
+ * Cook up this extension with random, reasonable values. 
+ * Incoming pointer refers to extension-specific C struct, mallocd
+ * and zeroed by main test routine.
+ */
+typedef void (*extenCreateFcn)(void *arg);
+
+/*
+ * Compare two instances of this extension. Return number of 
+ * compare errors.
+ */
+typedef unsigned (*extenCompareFcn)(
+       const void *preEncode, 
+       const void *postEncode);
+       
+/*
+ * Free struct components mallocd in extenCreateFcn. Do not free
+ * the outer struct.
+ */
+typedef void (*extenFreeFcn)(void *arg);
+
+typedef struct {
+       /* three extension-specific functions */
+       extenCreateFcn          createFcn;
+       extenCompareFcn         compareFcn;
+       extenFreeFcn            freeFcn;
+       
+       /* size of C struct passed to all three functions */
+       unsigned                        extenSize;
+       
+       /* the CE_DataType for this extension, used on encode */
+       CE_DataType                     extenType;
+       
+       /* the OID for this extension, tested on decode */
+       CSSM_OID                        extenOid;
+       
+       /* description for error logging and blob writing */
+       const char                      *extenDescr;
+       
+       /* command-line letter for this one */
+       char                            extenLetter;
+       
+} ExtenTest;
+
+/* empty freeFcn means no extension-specific resources to free */
+#define NO_FREE                NULL
+
+static ExtenTest extenTests[] = {
+       { kuCreate, kuCompare, NO_FREE, 
+         sizeof(CE_KeyUsage), 
+         DT_KeyUsage, CSSMOID_KeyUsage, 
+         "KeyUsage", 'k' },
+       { bcCreate, bcCompare, NO_FREE,
+         sizeof(CE_BasicConstraints), 
+         DT_BasicConstraints, CSSMOID_BasicConstraints,
+         "BasicConstraints", 'b' },
+       { ekuCreate, ekuCompare, NO_FREE,
+         sizeof(CE_ExtendedKeyUsage), 
+         DT_ExtendedKeyUsage, CSSMOID_ExtendedKeyUsage,
+         "ExtendedKeyUsage", 'x' },
+       { skidCreate, skidCompare, skidFree,
+         sizeof(CSSM_DATA), 
+         DT_SubjectKeyID, CSSMOID_SubjectKeyIdentifier,
+         "SubjectKeyID", 's' },
+       { authKeyIdCreate, authKeyIdCompare, authKeyIdFree,
+         sizeof(CE_AuthorityKeyID), 
+         DT_AuthorityKeyID, CSSMOID_AuthorityKeyIdentifier, 
+         "AuthorityKeyID", 'a' },
+       { genNamesCreate, genNamesCompare, genNamesFree, 
+         sizeof(CE_GeneralNames), 
+         DT_SubjectAltName, CSSMOID_SubjectAltName,
+         "SubjectAltName", 't' },
+       { genNamesCreate, genNamesCompare, genNamesFree, 
+         sizeof(CE_GeneralNames), 
+         DT_IssuerAltName, CSSMOID_IssuerAltName,
+         "IssuerAltName", 'i' },
+       { nctCreate, nctCompare, NO_FREE, 
+         sizeof(CE_NetscapeCertType), 
+         DT_NetscapeCertType, CSSMOID_NetscapeCertType, 
+         "NetscapeCertType", 'n' },
+       { cdpCreate, cdpCompare, cdpFree,
+         sizeof(CE_CRLDistPointsSyntax),
+         DT_CrlDistributionPoints, CSSMOID_CrlDistributionPoints,
+         "CRLDistPoints", 'p' },
+       { cpCreate, cpCompare, cpFree,
+         sizeof(CE_CertPolicies), 
+         DT_CertPolicies, CSSMOID_CertificatePolicies,
+         "CertPolicies", 'c' },
+       { aiaCreate, aiaCompare, aiaFree,
+         sizeof(CE_AuthorityInfoAccess), 
+         DT_AuthorityInfoAccess, CSSMOID_AuthorityInfoAccess,
+         "AuthorityInfoAccess", 'A' },
+       #if 0
+       /* TP doesn't have this, neither does certextensions.h! */
+       { aiaCreate, aiaCompare, aiaFree,
+         sizeof(CE_AuthorityInfoAccess), CSSMOID_SubjectInfoAccess,
+         "SubjectInfoAccess", 'S' },
+       #endif
+};
+
+#define NUM_EXTEN_TESTS                (sizeof(extenTests) / sizeof(ExtenTest))
+
+static void printExtenPre(
+       CE_DataAndType          *extenData,
+       CSSM_OID                        *extenOid,
+       OidParser                       &parser)
+{
+       CSSM_FIELD field;
+       CSSM_X509_EXTENSION     extn;
+
+       /* 
+        * Convert pre-digested into CSSM_X509_EXTENSION that 
+        * printCertField() can understand 
+        */
+       extn.extnId = *extenOid;
+       extn.critical = extenData->critical;
+       extn.format = CSSM_X509_DATAFORMAT_PARSED;
+       extn.value.parsedValue = &extenData->extension;
+       field.FieldOid = *extenOid;
+       field.FieldValue.Data = (uint8 *)&extn;
+       field.FieldValue.Length = sizeof(CSSM_X509_EXTENSION);
+       printf("=== PRE-ENCODE:\n");
+       printCertField(field, parser, CSSM_TRUE);
+}
+
+
+static void printExten(
+       CSSM_X509_EXTENSION     &extn,
+       OidParser                       &parser)
+{
+       CSSM_FIELD field;
+       field.FieldOid = extn.extnId;
+       field.FieldValue.Data = (uint8 *)&extn;
+       field.FieldValue.Length = sizeof(CSSM_X509_EXTENSION);
+       printf("=== POST-DECODE:\n");
+       printCertField(field, parser, CSSM_TRUE);
+}
+
+static int doTest(
+       CSSM_CL_HANDLE  clHand,
+       CSSM_TP_HANDLE  tpHand,
+       CSSM_CSP_HANDLE cspHand,
+       ExtenTest               &extenTest,
+       bool                    writeBlobs,
+       const char              *constFileName, // all blobs to this file if non-NULL
+       bool                    displayExtens,
+       OidParser               &parser)
+{
+       CSSM_APPLE_TP_CERT_REQUEST      certReq;
+       CSSM_TP_REQUEST_SET                     reqSet;
+       sint32                                          estTime;
+       CSSM_BOOL                                       confirmRequired;
+       CSSM_TP_RESULT_SET_PTR          resultSet;
+       CSSM_ENCODED_CERT                       *encCert;
+       CSSM_TP_CALLERAUTH_CONTEXT      CallerAuthContext;
+       CSSM_FIELD                                      policyId;
+       CE_DataAndType                          extPre;
+       CSSM_RETURN                                     crtn;
+       CSSM_DATA                                       refId;  // mallocd by CSSM_TP_SubmitCredRequest
+       CSSM_DATA                                       signedCert = {0, NULL};
+       
+       memset(&certReq, 0, sizeof(certReq));
+       memset(&extPre, 0, sizeof(extPre));
+       
+       /*
+        * Cook up a random and reasonable instance of the C struct 
+        * associated with this extension.
+        */
+       extenTest.createFcn(&extPre.extension);
+
+       /*
+        * Cook up the associated CSSM_X509_EXTENSION.
+        */
+       extPre.type = extenTest.extenType;
+       extPre.critical = randBool();
+       
+       /*
+        * Now the cert request proper.
+        */
+       certReq.cspHand = cspHand;
+       certReq.clHand = clHand;
+       certReq.serialNumber = 0x8765;
+       certReq.numSubjectNames = NUM_DUMMY_NAMES;
+       certReq.subjectNames = dummyRdn;
+       certReq.numIssuerNames = NUM_DUMMY_NAMES;
+       certReq.issuerNames = dummyRdn;
+       certReq.certPublicKey = &subjPubKey;
+       certReq.issuerPrivateKey = &subjPrivKey;
+       certReq.signatureAlg = CSSM_ALGID_SHA1WithRSA;
+       certReq.signatureOid = SIG_OID;
+       certReq.notBefore = 0;                  // now
+       certReq.notAfter = 10000;               // seconds from now
+       certReq.numExtensions = 1;
+       certReq.extensions = &extPre;
+       
+       reqSet.NumberOfRequests = 1;
+       reqSet.Requests = &certReq;
+
+       /* a big CSSM_TP_CALLERAUTH_CONTEXT just to specify an OID */
+       memset(&CallerAuthContext, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT));
+       memset(&policyId, 0, sizeof(CSSM_FIELD));
+       policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN;
+       CallerAuthContext.Policy.NumberOfPolicyIds = 1;
+       CallerAuthContext.Policy.PolicyIds = &policyId;
+       
+       /* GO */
+       crtn = CSSM_TP_SubmitCredRequest(tpHand,
+               NULL,                           // PreferredAuthority
+               CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,
+               &reqSet,
+               &CallerAuthContext,
+               &estTime,
+               &refId);
+       if(crtn) {
+               printError("CSSM_TP_SubmitCredRequest", crtn);
+               /* show what we tried to encode and decode */
+               printExtenPre(&extPre, &extenTest.extenOid, parser);
+               return 1;
+       }
+       crtn = CSSM_TP_RetrieveCredResult(tpHand,
+               &refId,
+               NULL,                           // CallerAuthCredentials
+               &estTime,
+               &confirmRequired,
+               &resultSet);            // leaks.....
+       if(crtn) {
+               printError("CSSM_TP_RetrieveCredResult", crtn);
+               /* show what we tried to encode and decode */
+               printExtenPre(&extPre, &extenTest.extenOid, parser);
+               return 1;
+       }
+       if(resultSet == NULL) {
+               printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n");
+               /* show what we tried to encode and decode */
+               printExtenPre(&extPre, &extenTest.extenOid, parser);
+               return 1;
+       }
+       encCert = (CSSM_ENCODED_CERT *)resultSet->Results;
+       signedCert = encCert->CertBlob;
+
+       if(writeBlobs) {
+               char fileName[200];
+               if(constFileName) {
+                       strcpy(fileName, constFileName);
+               }
+               else {
+                       sprintf(fileName, "%scert.der", extenTest.extenDescr);
+               }
+               writeFile(fileName, signedCert.Data, signedCert.Length);
+               printf("...wrote %lu bytes to %s\n", signedCert.Length, fileName);
+       }
+       
+       /* snag the same extension from the encoded cert */
+       CSSM_DATA_PTR postField;
+       CSSM_HANDLE resultHand;
+       uint32 numFields;
+       
+       crtn = CSSM_CL_CertGetFirstFieldValue(clHand,
+               &signedCert,
+               &extenTest.extenOid,
+               &resultHand,
+               &numFields,
+               &postField);
+       if(crtn) {
+               printf("****Extension field not found on decode for %s\n",
+                       extenTest.extenDescr);
+               printError("CSSM_CL_CertGetFirstFieldValue", crtn);
+               
+               /* show what we tried to encode and decode */
+               printExtenPre(&extPre, &extenTest.extenOid, parser);
+               return 1;
+       }
+       
+       if(numFields != 1) {
+               printf("****GetFirstFieldValue: expect 1 value, got %u\n",
+                       (unsigned)numFields);
+               return 1;
+       }
+       CSSM_CL_CertAbortQuery(clHand, resultHand);
+       
+       /* verify the fields we generated */
+       CSSM_X509_EXTENSION *extnPost = (CSSM_X509_EXTENSION *)postField->Data;
+       if((extnPost == NULL) || 
+          (postField->Length != sizeof(CSSM_X509_EXTENSION))) {
+               printf("***Malformed CSSM_X509_EXTENSION (1) after decode\n");
+               return 1;
+       }
+       int rtn = 0;
+       rtn += compBool(extPre.critical, extnPost->critical, 
+               "CSSM_X509_EXTENSION.critical");
+       rtn += compCssmData(extenTest.extenOid, extnPost->extnId, 
+               "CSSM_X509_EXTENSION.extnId");
+               
+       if(extnPost->format != CSSM_X509_DATAFORMAT_PARSED) {
+               printf("***Expected CSSM_X509_DATAFORMAT_PARSED (%x(x), got %x(x)\n",
+                       CSSM_X509_DATAFORMAT_PARSED, extnPost->format);
+       }
+       if(extnPost->value.parsedValue == NULL) {
+               printf("***no parsedValue pointer!\n");
+               return 1;
+       }
+       
+       /* down to extension-specific compare */
+       rtn += extenTest.compareFcn(&extPre.extension, extnPost->value.parsedValue);
+       
+       if(rtn) {
+               /* print preencode only on error */
+               printExtenPre(&extPre, &extenTest.extenOid, parser);
+       }
+       if(displayExtens || rtn) {
+               printExten(*extnPost, parser);
+       }
+       
+       /* free the allocated data */
+       if(extenTest.freeFcn) {
+               extenTest.freeFcn(&extPre.extension);
+       }
+       CSSM_CL_FreeFieldValue(clHand, &extenTest.extenOid, postField);
+       CSSM_FREE(signedCert.Data);
+       CSSM_FREE(encCert);
+       CSSM_FREE(resultSet);
+       return rtn;
+}
+
+static void doPause(bool pause)
+{
+       if(!pause) {
+               return;
+       }
+       fpurge(stdin);
+       printf("CR to continue ");
+       getchar();
+}
+
+int main(int argc, char **argv)
+{
+       CSSM_CL_HANDLE          clHand;
+       CSSM_TP_HANDLE          tpHand;
+       CSSM_CSP_HANDLE         cspHand;
+       CSSM_RETURN                     crtn;
+       int                                     arg;
+       int                                     rtn;
+       OidParser                       parser;
+       char                            *argp;
+       unsigned                        i;
+       
+       /* user-specificied params */
+       unsigned                        minExtenNum = 0;
+       unsigned                        maxExtenNum = NUM_EXTEN_TESTS-1;
+       bool                            writeBlobs = false;
+       bool                            displayExtens = false;
+       bool                            quiet = false;
+       unsigned                        loops = LOOPS_DEF;
+       bool                            pauseLoop = false;
+       bool                            pauseCert = false;
+       const char                      *constFileName = NULL;
+
+       for(arg=1; arg<argc; arg++) {
+               argp = argv[arg];
+               switch(argp[0]) {
+                       case 'w':
+                               writeBlobs = true;
+                               break;
+                       case 'd':
+                               displayExtens = true;
+                               break;
+                       case 'q':
+                               quiet = true;
+                               break;
+                       case 'p':
+                               pauseLoop = true;
+                               break;
+                       case 'P':
+                               pauseCert = true;
+                               break;
+                       case 'l':
+                               loops = atoi(&argp[2]);
+                               break;
+                       case 'f':
+                               constFileName = &argp[2];
+                               break;
+                       case 'e':
+                               if(argp[1] != '=') {
+                                       usage(argv);
+                               }
+                               /* scan thru test array looking for epecified extension */
+                               for(i=0; i<NUM_EXTEN_TESTS; i++) {
+                                       if(extenTests[i].extenLetter == argp[2]) {
+                                               minExtenNum = maxExtenNum = i;
+                                               break;
+                                       }
+                               }
+                               if(i == NUM_EXTEN_TESTS) {
+                                       usage(argv);
+                               }
+                               break;
+                       default:
+                               usage(argv);
+               }
+       }
+       
+       
+       /* common setup */
+       clHand = clStartup();
+       if(clHand == 0) {
+               return 0;
+       }
+       tpHand = tpStartup();
+       if(tpHand == 0) {
+               return 0;
+       }
+       cspHand = cspStartup();
+       if(cspHand == 0) {
+               return 0;
+       }
+
+       printf("Starting extenTestTp; args: ");
+       for(i=1; i<(unsigned)argc; i++) {
+               printf("%s ", argv[i]);
+       }
+       printf("\n");
+
+       /* one common key pair - we're definitely not testing this */
+       crtn = cspGenKeyPair(cspHand,
+               KEY_ALG,
+               SUBJ_KEY_LABEL,
+               strlen(SUBJ_KEY_LABEL),
+               KEY_SIZE_BITS,
+               &subjPubKey,
+               CSSM_FALSE,                     // pubIsRef - should work both ways, but not yet
+               CSSM_KEYUSE_VERIFY,
+               CSSM_KEYBLOB_RAW_FORMAT_NONE,
+               &subjPrivKey,
+               CSSM_TRUE,                      // privIsRef - doesn't matter
+               CSSM_KEYUSE_SIGN,
+               CSSM_KEYBLOB_RAW_FORMAT_NONE,
+               CSSM_FALSE);
+       if(crtn) {
+               exit(1);
+       }
+       
+       for(unsigned loop=0; loop<loops; loop++) {
+               if(!quiet) {
+                       printf("...loop %u\n", loop);
+               }
+               for(unsigned extenDex=minExtenNum; extenDex<=maxExtenNum; extenDex++) {
+                       rtn = doTest(clHand, tpHand, cspHand, extenTests[extenDex],
+                               writeBlobs, constFileName, displayExtens, parser);
+                       if(rtn) {
+                               break;
+                       }
+                       doPause(pauseCert);
+               }
+               if(rtn) {
+                       break;
+               }
+               doPause(pauseLoop);
+       }
+       
+       if(rtn) {
+               printf("***%s FAILED\n", argv[0]);
+       }
+       else if(!quiet) {
+               printf("...%s passed\n", argv[0]);
+       }
+       
+       
+       /* cleanup */
+       CSSM_ModuleDetach(cspHand);
+       CSSM_ModuleDetach(clHand);
+       CSSM_ModuleDetach(tpHand);
+       return 0;
+}
+