]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTests/clxutils/clAppUtils/tpUtils.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTests / clxutils / clAppUtils / tpUtils.cpp
diff --git a/SecurityTests/clxutils/clAppUtils/tpUtils.cpp b/SecurityTests/clxutils/clAppUtils/tpUtils.cpp
new file mode 100644 (file)
index 0000000..1e5e83d
--- /dev/null
@@ -0,0 +1,1215 @@
+/*
+ * tpUtils.cpp - TP and cert group test support
+ */
+
+#include <Security/cssmtype.h>
+#include <clAppUtils/tpUtils.h>
+#include <clAppUtils/clutils.h>
+#include <utilLib/common.h>
+#include <utilLib/cspwrap.h>
+#include <clAppUtils/CertBuilderApp.h>
+#include <Security/oidsattr.h>
+#include <Security/oidscert.h>
+#include <Security/SecTrustSettingsPriv.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <Security/SecKeychainItem.h>
+#include <Security/SecKeychain.h>
+#include <Security/SecCertificate.h>
+#include <security_cdsa_utils/cuFileIo.h>
+
+/*
+ * Currently, DBs created with SecKeychainCreateNew() do not contain 
+ * the schema for CSSM_DL_DB_RECORD_X509_CERTIFICATE records. Keychain 
+ * code (Certificate::add()) does this on the fly, I don't know why.
+ * To avoid dependencies on KC - other than SecKeychainCreateNew - we'll
+ * emulate that "add this schema on the fly" logic here. 
+ *
+ * Turn this option off if and when Radar 2927378 is approved and 
+ * integrated into Security TOT.
+ */
+#define FAKE_ADD_CERT_SCHEMA   1
+#if    FAKE_ADD_CERT_SCHEMA
+
+/* defined in SecKeychainAPIPriv.h */
+// static const int kSecAlias = 'alis';
+
+/* Macro to declare a CSSM_DB_SCHEMA_ATTRIBUTE_INFO */
+#define SCHEMA_ATTR_INFO(id, name, type)       \
+       { id, (char *)name, {0, NULL},  CSSM_DB_ATTRIBUTE_FORMAT_ ## type }
+       
+/* Too bad we can't get this from inside of the Security framework. */
+static CSSM_DB_SCHEMA_ATTRIBUTE_INFO certSchemaAttrInfo[] = 
+{
+       SCHEMA_ATTR_INFO(kSecCertTypeItemAttr, "CertType", UINT32),
+       SCHEMA_ATTR_INFO(kSecCertEncodingItemAttr, "CertEncoding", UINT32),
+       SCHEMA_ATTR_INFO(kSecLabelItemAttr, "PrintName", BLOB),
+       SCHEMA_ATTR_INFO(kSecAlias, "Alias", BLOB),
+       SCHEMA_ATTR_INFO(kSecSubjectItemAttr, "Subject", BLOB),
+       SCHEMA_ATTR_INFO(kSecIssuerItemAttr, "Issuer", BLOB),
+       SCHEMA_ATTR_INFO(kSecSerialNumberItemAttr, "SerialNumber", BLOB),
+       SCHEMA_ATTR_INFO(kSecSubjectKeyIdentifierItemAttr, "SubjectKeyIdentifier", BLOB),
+       SCHEMA_ATTR_INFO(kSecPublicKeyHashItemAttr, "PublicKeyHash", BLOB)
+};
+#define NUM_CERT_SCHEMA_ATTRS  \
+       (sizeof(certSchemaAttrInfo) / sizeof(CSSM_DB_SCHEMA_ATTRIBUTE_INFO))
+
+/* Macro to declare a CSSM_DB_SCHEMA_INDEX_INFO */
+#define SCHEMA_INDEX_INFO(id, indexNum, indexType)     \
+       { id, CSSM_DB_INDEX_ ## indexType,  CSSM_DB_INDEX_ON_ATTRIBUTE }
+       
+
+static CSSM_DB_SCHEMA_INDEX_INFO certSchemaIndices[] = 
+{
+       SCHEMA_INDEX_INFO(kSecCertTypeItemAttr, 0, UNIQUE),
+       SCHEMA_INDEX_INFO(kSecIssuerItemAttr, 0, UNIQUE),
+       SCHEMA_INDEX_INFO(kSecSerialNumberItemAttr, 0, UNIQUE),
+       SCHEMA_INDEX_INFO(kSecCertTypeItemAttr, 1, NONUNIQUE),
+       SCHEMA_INDEX_INFO(kSecSubjectItemAttr, 2, NONUNIQUE),
+       SCHEMA_INDEX_INFO(kSecIssuerItemAttr, 3, NONUNIQUE),
+       SCHEMA_INDEX_INFO(kSecSerialNumberItemAttr, 4, NONUNIQUE),
+       SCHEMA_INDEX_INFO(kSecSubjectKeyIdentifierItemAttr, 5, NONUNIQUE),
+       SCHEMA_INDEX_INFO(kSecPublicKeyHashItemAttr, 6, NONUNIQUE)
+};
+#define NUM_CERT_INDICES       \
+       (sizeof(certSchemaIndices) / sizeof(CSSM_DB_SCHEMA_INDEX_INFO))
+
+
+CSSM_RETURN tpAddCertSchema(
+       CSSM_DL_DB_HANDLE       dlDbHand)
+{
+       return CSSM_DL_CreateRelation(dlDbHand,
+               CSSM_DL_DB_RECORD_X509_CERTIFICATE,
+               "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
+               NUM_CERT_SCHEMA_ATTRS,
+               certSchemaAttrInfo,
+               NUM_CERT_INDICES,
+               certSchemaIndices);             
+}
+#endif /* FAKE_ADD_CERT_SCHEMA */
+
+/*
+ * Given a raw cert, extract DER-encoded normalized subject and issuer names.
+ */
+static CSSM_DATA_PTR tpGetNormSubject(
+       CSSM_CL_HANDLE                  clHand,
+       const CSSM_DATA                 *rawCert)
+{
+       CSSM_RETURN crtn;
+       CSSM_HANDLE searchHand = CSSM_INVALID_HANDLE;
+       uint32 numFields;
+       CSSM_DATA_PTR fieldValue;
+
+       crtn = CSSM_CL_CertGetFirstFieldValue(clHand,
+               rawCert,
+               &CSSMOID_X509V1SubjectName,
+               &searchHand,
+               &numFields, 
+               &fieldValue);
+       if(crtn) {
+               printError("CSSM_CL_CertGetFirstFieldValue", crtn);
+               return NULL;
+       }
+       CSSM_CL_CertAbortQuery(clHand, searchHand);
+       return fieldValue;
+}
+
+static CSSM_DATA_PTR tpGetNormIssuer(
+       CSSM_CL_HANDLE                  clHand,
+       const CSSM_DATA                 *rawCert)
+{
+       CSSM_RETURN crtn;
+       CSSM_HANDLE searchHand = CSSM_INVALID_HANDLE;
+       uint32 numFields;
+       CSSM_DATA_PTR fieldValue;
+
+       crtn = CSSM_CL_CertGetFirstFieldValue(clHand,
+               rawCert,
+               &CSSMOID_X509V1IssuerName,
+               &searchHand,
+               &numFields, 
+               &fieldValue);
+       if(crtn) {
+               printError("CSSM_CL_CertGetFirstFieldValue", crtn);
+               return NULL;
+       }
+       CSSM_CL_CertAbortQuery(clHand, searchHand);
+       return fieldValue;
+}
+
+
+#define SERIAL_NUMBER_BASE     0x33445566
+
+/*
+ * Given an array of certs and an uninitialized CSSM_CERTGROUP, place the
+ * certs into the certgroup and optionally into one of a list of DBs in 
+ * random order. Optionally the first cert in the array is placed in the 
+ * first element of certgroup. Only error is memory error. It's legal to 
+ * pass in an empty cert array. 
+ */
+CSSM_RETURN tpMakeRandCertGroup(
+       CSSM_CL_HANDLE                  clHand,
+       CSSM_DL_DB_LIST_PTR             dbList,
+       const CSSM_DATA_PTR             certs,
+       unsigned                                numCerts,
+       CSSM_CERTGROUP_PTR              certGroup,
+       CSSM_BOOL                               firstCertIsSubject,     // true: certs[0] goes to head 
+                                                                                               //   of certGroup
+       CSSM_BOOL                               verbose,
+       CSSM_BOOL                               allInDbs,                       // all certs go to DBs
+       CSSM_BOOL                               skipFirstDb)            // no certs go to db[0]
+{
+       unsigned                startDex = 0;           // where to start processing
+       unsigned                certDex;                        // into certs and certGroup
+       unsigned                die;
+       CSSM_RETURN     crtn;
+       
+       #if     TP_DB_ENABLE
+       if((dbList == NULL) && (allInDbs | skipFirstDb)) {
+               printf("need dbList for allInDbs or skipFirstDb\n");
+               return CSSM_ERRCODE_INTERNAL_ERROR;
+       }
+       if(skipFirstDb && (dbList->NumHandles == 1)) {
+               printf("Need more than one DB for skipFirstDb\n");
+               return CSSM_ERRCODE_INTERNAL_ERROR;
+       }
+       #else 
+       if(dbList != NULL) { 
+               printf("TP/DB not supported yet\n");
+               return CSSMERR_CSSM_INTERNAL_ERROR;
+       }
+       #endif
+       
+       certGroup->NumCerts = 0;
+       certGroup->CertGroupType = CSSM_CERTGROUP_DATA;
+       certGroup->CertType = CSSM_CERT_X_509v3;
+       certGroup->CertEncoding = CSSM_CERT_ENCODING_DER; 
+       if(numCerts == 0) {
+               /* legal */
+               certGroup->GroupList.CertList = NULL;
+               return CSSM_OK;
+       }
+       
+       /* make CertList big enough for all certs */
+       certGroup->GroupList.CertList = (CSSM_DATA_PTR)CSSM_CALLOC(numCerts, sizeof(CSSM_DATA));
+       if(certGroup->GroupList.CertList == NULL) {
+               printf("Memory error!\n");
+               return CSSMERR_CSSM_MEMORY_ERROR;
+       }
+       if(firstCertIsSubject) {
+               certGroup->GroupList.CertList[0] = certs[0];
+               certGroup->NumCerts = 1;
+               startDex = 1;
+       }
+       for(certDex=startDex; certDex<numCerts; certDex++) {
+               /* flip a coin, half of the certs go into a DB */
+               die = genRand(1, 2);                    // one random bit 
+               if( ( (dbList != NULL) && (dbList->NumHandles != 0) ) &&
+                   ( (die == 1) || allInDbs) ) {
+                       /* put this cert in one of the DBs */
+                       if(skipFirstDb) {
+                               die = genRand(1, dbList->NumHandles-1);
+                       }
+                       else {
+                               die = genRand(0, dbList->NumHandles-1);
+                       }
+                       if(verbose) {
+                               printf("   ...cert %d to DB[%d]\n", certDex, die);
+                       }
+                       crtn = tpStoreRawCert(dbList->DLDBHandle[die],
+                                       clHand,
+                                       &certs[certDex]);
+                       if(crtn) {
+                               return crtn;
+                       }                               
+               } 
+               else {
+                       /* find a random unused place in certGroupFrag */
+                       CSSM_DATA_PTR   certData;
+                       
+                       while(1) {
+                               die = genRand(0, numCerts-1);
+                               certData = &certGroup->GroupList.CertList[die];
+                               if(certData->Data == NULL) {
+                                       *certData = certs[certDex];
+                                       certGroup->NumCerts++;
+                                       if(verbose) {
+                                               printf("   ...cert %d to frag[%d]\n", 
+                                                       certDex, die);
+                                       }
+                                       break;
+                               }
+                               /* else try again and hope we don't spin forever */
+                       }
+               }       /* random place in certGroup */
+       }               /* main loop */
+       
+       if(dbList != NULL) {
+               /* 
+                * Since we put some of the certs in dlDb rather than in certGroup,
+                * compact the contents of certGroup. Its NumCerts is correct, 
+                * but some of the entries in CertList are empty.
+                */
+               unsigned i;
+               
+               for(certDex=0; certDex<numCerts; certDex++) {
+                       if(certGroup->GroupList.CertList[certDex].Data == NULL) {
+                               /* find next non-NULL cert */
+                               for(i=certDex+1; i<numCerts; i++) {
+                                       if(certGroup->GroupList.CertList[i].Data != NULL) {
+                                               if(verbose) {
+                                                       printf("   ...frag[%d] to frag[%d]\n", 
+                                                               i, certDex);
+                                               }
+                                               certGroup->GroupList.CertList[certDex] = 
+                                                       certGroup->GroupList.CertList[i];
+                                               certGroup->GroupList.CertList[i].Data = NULL;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       }
+       return CSSM_OK;
+}
+
+/*
+ * Store a cert in specified DL/DB. All attributes are optional except
+ * as noted (right?). 
+ */
+CSSM_RETURN tpStoreCert(
+       CSSM_DL_DB_HANDLE               dlDb,
+       const CSSM_DATA_PTR             cert,
+       /* REQUIRED fields */
+       CSSM_CERT_TYPE                  certType,               // e.g. CSSM_CERT_X_509v3
+       uint32                                  serialNum,      
+       const CSSM_DATA                 *issuer,                // (shouldn't this be subject?)
+                                                                                       // normalized & encoded
+       /* OPTIONAL fields */
+       CSSM_CERT_ENCODING              certEncoding,   // e.g. CSSM_CERT_ENCODING_DER
+       const CSSM_DATA                 *printName,
+       const CSSM_DATA                 *subject)               // normalized & encoded
+{
+       CSSM_DB_ATTRIBUTE_DATA                  attrs[6];
+       CSSM_DB_RECORD_ATTRIBUTE_DATA   recordAttrs;
+       CSSM_DB_ATTRIBUTE_DATA_PTR              attr = &attrs[0];
+       CSSM_DB_UNIQUE_RECORD_PTR               recordPtr = NULL;
+       CSSM_DATA                                               certTypeData;
+       CSSM_DATA                                               certEncData;
+       CSSM_DATA_PTR                                   serialNumData;
+       uint32                                                  numAttributes;
+       
+       if(issuer == NULL) {
+               printf("***For now, must specify cert issuer when storing\n");
+               return CSSM_ERRCODE_INTERNAL_ERROR;
+       }
+       
+       /* how many attributes are we storing? */
+       numAttributes = 4;              // certType, serialNum, issuer, certEncoding
+       if(printName != NULL) {
+               numAttributes++;
+       }
+       if(subject != NULL) {
+               numAttributes++;
+       }
+       
+       /* cook up CSSM_DB_RECORD_ATTRIBUTE_DATA */
+       recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE;
+       recordAttrs.SemanticInformation = 0;
+       recordAttrs.NumberOfAttributes = numAttributes;
+       recordAttrs.AttributeData = attrs;
+       
+       /* grind thru the attributes - first the required ones plus certEncoding */
+       certTypeData.Data = (uint8 *)&certType;
+       certTypeData.Length = sizeof(CSSM_CERT_TYPE);
+       certEncData.Data = (uint8 *)&certEncoding;
+       certEncData.Length = sizeof(CSSM_CERT_ENCODING);
+       serialNumData = intToDER(serialNum);
+       
+       attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
+       attr->Info.Label.AttributeName = (char *)"CertType";
+       attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
+       attr->NumberOfValues = 1;
+       attr->Value = &certTypeData;
+       attr++;
+       
+       attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
+       attr->Info.Label.AttributeName = (char *)"CertEncoding";
+       attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
+       attr->NumberOfValues = 1;
+       attr->Value = &certEncData;
+       attr++;
+       
+       attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
+       attr->Info.Label.AttributeName = (char *)"SerialNumber";
+       attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
+       attr->NumberOfValues = 1;
+       attr->Value = serialNumData;
+       attr++;
+
+       attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
+       attr->Info.Label.AttributeName = (char *)"Issuer";
+       attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
+       attr->NumberOfValues = 1;
+       attr->Value = (CSSM_DATA_PTR)issuer;
+       attr++;
+
+       /* now the options */
+       if(printName != NULL) {
+               attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
+               attr->Info.Label.AttributeName = (char *)"PrintName";
+               attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
+               attr->NumberOfValues = 1;
+               attr->Value = (CSSM_DATA_PTR)printName;
+               attr++;
+       }
+       if(subject != NULL) {
+               attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; 
+               attr->Info.Label.AttributeName = (char *)"Subject";
+               attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
+               attr->NumberOfValues = 1;
+               attr->Value = (CSSM_DATA_PTR)subject;
+               attr++;
+       }
+
+       /* Okay, here we go */
+       CSSM_RETURN crtn = CSSM_DL_DataInsert(dlDb,
+                       CSSM_DL_DB_RECORD_X509_CERTIFICATE,
+                       &recordAttrs,
+                       cert,
+                       &recordPtr);
+       #if     FAKE_ADD_CERT_SCHEMA
+       if(crtn == CSSMERR_DL_INVALID_RECORDTYPE) {
+               /* gross hack of inserting this "new" schema that Keychain didn't specify */
+               crtn = tpAddCertSchema(dlDb);
+               if(crtn == CSSM_OK) {
+                       /* Retry with a fully capable DLDB */
+                       crtn = CSSM_DL_DataInsert(dlDb,
+                               CSSM_DL_DB_RECORD_X509_CERTIFICATE,
+                               &recordAttrs,
+                               cert,
+                               &recordPtr);
+               }
+       }
+       #endif  /* FAKE_ADD_CERT_SCHEMA */
+       
+       /* free resources allocated to get this far */
+       appFreeCssmData(serialNumData, CSSM_TRUE);
+       if(recordPtr != NULL) {
+               CSSM_DL_FreeUniqueRecord(dlDb, recordPtr);
+       }
+       if(crtn) {
+               printError("CSSM_DL_DataInsert", crtn);
+       }
+       return crtn;
+}
+
+/*
+ * Store a cert when we don't already know the required fields. We'll 
+ * extract them or make them up.
+ */
+CSSM_RETURN tpStoreRawCert(
+       CSSM_DL_DB_HANDLE               dlDb,
+       CSSM_CL_HANDLE                  clHand,
+       const CSSM_DATA_PTR             cert)
+{
+       CSSM_DATA_PTR           normSubj;
+       CSSM_DATA_PTR           normIssuer;
+       CSSM_DATA                       printName;
+       CSSM_RETURN                     crtn;
+       static uint32           fakeSerialNum = 0;
+       
+       normSubj   = tpGetNormSubject(clHand, cert);
+       normIssuer = tpGetNormIssuer(clHand, cert);
+       if((normSubj == NULL) || (normIssuer == NULL)) {
+               return CSSM_ERRCODE_INTERNAL_ERROR;
+       }
+       printName.Data = (uint8 *)"Some Printable Name";
+       printName.Length = strlen((char *)printName.Data);
+       crtn = tpStoreCert(dlDb,
+               cert,
+               CSSM_CERT_X_509v3,
+               fakeSerialNum++,
+               normIssuer,
+               CSSM_CERT_ENCODING_DER,
+               &printName,
+               normSubj);
+       appFreeCssmData(normSubj, CSSM_TRUE);
+       appFreeCssmData(normIssuer, CSSM_TRUE);
+       return crtn;
+}
+
+/* 
+ * Generate numKeyPairs key pairs of specified algorithm and size.
+ * Key labels will be 'keyLabelBase' concatenated with a 4-digit
+ * decimal number.
+ */
+CSSM_RETURN tpGenKeys(
+       CSSM_CSP_HANDLE cspHand,
+       CSSM_DL_DB_HANDLE dbHand,                       /* keys go here */
+       unsigned                numKeyPairs,
+       uint32                  keyGenAlg,              /* CSSM_ALGID_RSA, etc. */
+       uint32                  keySizeInBits,                  
+       const char              *keyLabelBase,  /* C string */
+       CSSM_KEY_PTR    pubKeys,                /* array of keys RETURNED here */
+       CSSM_KEY_PTR    privKeys,               /* array of keys RETURNED here */
+       CSSM_DATA_PTR   paramData)              /* optional DSA params */
+{
+       CSSM_RETURN             crtn;
+       unsigned                i;
+       char                    label[80];
+       unsigned                labelLen = strlen(keyLabelBase);
+       
+       memset(pubKeys, 0, numKeyPairs * sizeof(CSSM_KEY));
+       memset(privKeys, 0, numKeyPairs * sizeof(CSSM_KEY));
+       memmove(label, keyLabelBase, labelLen);
+       
+       for(i=0; i<numKeyPairs; i++) {
+               /* unique label */
+               sprintf(label+labelLen, "%04d", i);     
+               if(keyGenAlg == CSSM_ALGID_DSA) {
+                       crtn = cspGenDSAKeyPair(cspHand,
+                               label,
+                               labelLen + 4,
+                               keySizeInBits,
+                               &pubKeys[i],
+                               CSSM_FALSE,                     // pubIsRef
+                               CSSM_KEYUSE_VERIFY,     // pubKeyUsage
+                               CSSM_KEYBLOB_RAW_FORMAT_NONE,
+                               &privKeys[i],
+                               CSSM_TRUE,                      // privIsRef
+                               CSSM_KEYUSE_SIGN,
+                               CSSM_KEYBLOB_RAW_FORMAT_NONE,
+                               CSSM_FALSE,                     // genParams
+                               paramData);
+               }
+               else {
+                       crtn = cspGenKeyPair(cspHand,
+                               keyGenAlg,
+                               // not used in X, yet dbHand,
+                               label,
+                               labelLen + 4,
+                               keySizeInBits,
+                               &pubKeys[i],
+                               CSSM_FALSE,                     // pubIsRef
+                               CSSM_KEYUSE_VERIFY,     // pubKeyUsage
+                               CSSM_KEYBLOB_RAW_FORMAT_NONE,
+                               &privKeys[i],
+                               CSSM_TRUE,                      /// privIsRef
+                               CSSM_KEYUSE_SIGN,
+                               CSSM_KEYBLOB_RAW_FORMAT_NONE,
+                               CSSM_FALSE);
+               }
+               if(crtn) {
+                       return crtn;
+               }
+       }
+       
+       /* verify they are all different keys */
+       for(i=0; i<numKeyPairs; i++) {
+               CSSM_DATA_PTR k1 = &pubKeys[i].KeyData;
+               for(unsigned j=i+1; j<numKeyPairs; j++) {
+                       CSSM_DATA_PTR k2 = &pubKeys[j].KeyData;
+                       if(appCompareCssmData(k1, k2)) {
+                               printf("***HEY! public keys %d and %d are indentical!\n", i, j);
+                       }
+               }
+       }
+       return crtn;
+}
+
+/* 
+ * Generate a cert chain using specified key pairs. The last cert in the
+ * chain (certs[numCerts-1]) is a root cert, self-signed. 
+ */
+CSSM_RETURN tpGenCerts(
+       CSSM_CSP_HANDLE cspHand,
+       CSSM_CL_HANDLE  clHand,
+       unsigned                numCerts,
+       uint32                  sigAlg,                 /* CSSM_ALGID_SHA1WithRSA, etc. */
+       const char              *nameBase,              /* C string */
+       CSSM_KEY_PTR    pubKeys,                /* array of public keys */
+       CSSM_KEY_PTR    privKeys,               /* array of private keys */
+       CSSM_DATA_PTR   certs,                  /* array of certs RETURNED here */
+       const char              *notBeforeStr,  /* from genTimeAtNowPlus() */
+       const char              *notAfterStr)   /* from genTimeAtNowPlus() */
+{
+       return tpGenCertsStore(cspHand,
+               clHand,
+               numCerts,
+               sigAlg,
+               nameBase,
+               pubKeys,
+               privKeys,
+               NULL,           // storeArray
+               certs,
+               notBeforeStr,
+               notAfterStr);
+}
+
+/* 
+ * Generate a cert chain using specified key pairs. The last cert in the
+ * chain (certs[numCerts-1]) is a root cert, self-signed. Store
+ * the certs indicated by corresponding element on storeArray. If 
+ * storeArray[n].DLHandle == 0, the cert is not stored. 
+ */
+CSSM_RETURN tpGenCertsStore(
+       CSSM_CSP_HANDLE         cspHand,
+       CSSM_CL_HANDLE          clHand,
+       unsigned                        numCerts,
+       uint32                          sigAlg,                 /* CSSM_ALGID_SHA1WithRSA, etc. */
+       const char                      *nameBase,              /* C string */
+       CSSM_KEY_PTR            pubKeys,                /* array of public keys */
+       CSSM_KEY_PTR            privKeys,               /* array of private keys */
+       CSSM_DL_DB_HANDLE       *storeArray,    /* array of certs stored here  */
+       CSSM_DATA_PTR           certs,                  /* array of certs RETURNED here */
+       const char                      *notBeforeStr,  /* from genTimeAtNowPlus() */
+       const char                      *notAfterStr)   /* from genTimeAtNowPlus() */
+
+{
+       int                             dex;
+       CSSM_RETURN                     crtn;
+       CSSM_X509_NAME          *issuerName = NULL;
+       CSSM_X509_NAME          *subjectName = NULL;
+       CSSM_X509_TIME          *notBefore;                     // UTC-style "not before" time
+       CSSM_X509_TIME          *notAfter;                      // UTC-style "not after" time
+       CSSM_DATA_PTR           rawCert = NULL;         // from CSSM_CL_CertCreateTemplate
+       CSSM_DATA                       signedCert;                     // from CSSM_CL_CertSign        
+       uint32                          rtn;
+       CSSM_KEY_PTR            signerKey;                      // signs the cert
+       CSSM_CC_HANDLE          signContext;
+       char                            nameStr[100];
+       CSSM_DATA_PTR           thisCert;                       // ptr into certs[]
+       CB_NameOid                      nameOid;
+       CE_BasicConstraints     bc;
+       CSSM_X509_EXTENSION exten;
+       
+       nameOid.oid = &CSSMOID_OrganizationName;        // const
+       nameOid.string = nameStr;
+       
+       /* one extension for nonleaf indicating cA */
+       exten.extnId = CSSMOID_BasicConstraints;
+       exten.critical = CSSM_TRUE;
+       exten.format = CSSM_X509_DATAFORMAT_PARSED;
+       exten.value.parsedValue = &bc;
+       exten.BERvalue.Data = NULL;
+       exten.BERvalue.Length = 0;
+       bc.cA = CSSM_TRUE;
+       bc.pathLenConstraintPresent = CSSM_FALSE;
+       bc.pathLenConstraint = 0;
+
+       /* main loop - once per keypair/cert - starting at end/root */
+       for(dex=numCerts-1; dex>=0; dex--) {
+               thisCert = &certs[dex];
+               
+               thisCert->Data = NULL;
+               thisCert->Length = 0;
+               
+               sprintf(nameStr, "%s%04d", nameBase, dex);
+               if(issuerName == NULL) {
+                       /* last (root) cert - subject same as issuer */
+                       issuerName = CB_BuildX509Name(&nameOid, 1); 
+                       /* self-signed */
+                       signerKey = &privKeys[dex];
+               }
+               else {
+                       /* previous subject becomes current issuer */
+                       CB_FreeX509Name(issuerName);
+                       issuerName = subjectName;
+                       signerKey = &privKeys[dex+1];
+               }
+               subjectName = CB_BuildX509Name(&nameOid, 1);
+               if((subjectName == NULL) || (issuerName == NULL)) {
+                       printf("Error creating X509Names\n");
+                       crtn = CSSMERR_CSSM_MEMORY_ERROR;
+                       break;
+               }
+               
+               /* 
+                * not before/after in Y2k-compliant generalized time format.
+                * These come preformatted from our caller. 
+                */
+               notBefore = CB_BuildX509Time(0, notBeforeStr);
+               notAfter  = CB_BuildX509Time(0, notAfterStr);
+
+               /* 
+                * Cook up cert template 
+                * Note serial number would be app-specified in real world
+                */
+               rawCert = CB_MakeCertTemplate(clHand,
+                       SERIAL_NUMBER_BASE + dex,                       // serial number
+                       issuerName,
+                       subjectName,
+                       notBefore,
+                       notAfter,
+                       &pubKeys[dex],
+                       sigAlg,
+                       NULL,                   // subj unique ID
+                       NULL,                   // issuer unique ID
+                       &exten,                 // extensions
+                       (dex == 0) ? 0 : 1);// numExtensions
+       
+               if(rawCert == NULL) {
+                       crtn = CSSM_ERRCODE_INTERNAL_ERROR;
+                       break;
+               }
+
+               /* Free the stuff we allocd to get here */
+               CB_FreeX509Time(notBefore);
+               CB_FreeX509Time(notAfter);
+
+               /**** sign the cert ****/
+               /* 1. get a signing context */
+               crtn = CSSM_CSP_CreateSignatureContext(cspHand,
+                               sigAlg,
+                               NULL,                   // no passphrase for now
+                               signerKey,
+                               &signContext);
+               if(crtn) {
+                       printError("CreateSignatureContext", crtn);
+                       break;
+               }
+               
+               /* 2. use CL to sign the cert */ 
+               signedCert.Data = NULL;
+               signedCert.Length = 0;
+               crtn = CSSM_CL_CertSign(clHand,
+                       signContext,
+                       rawCert,                        // CertToBeSigned
+                       NULL,                           // SignScope per spec
+                       0,                                      // ScopeSize per spec
+                       &signedCert);
+               if(crtn) {
+                       printError("CSSM_CL_CertSign", crtn);
+                       break;
+               }
+               
+               /* 3. Optionally store the cert in DL */
+               if((storeArray != NULL) && storeArray[dex].DBHandle != 0) {
+                       crtn = tpStoreRawCert(storeArray[dex],
+                               clHand,
+                               &signedCert);
+                       if(crtn) {
+                               break;
+                       }
+               }
+               
+               /* 4. delete signing context */
+               crtn = CSSM_DeleteContext(signContext);
+               if(crtn) {
+                       printError("CSSM_DeleteContext", crtn);
+                       break;
+               }
+
+               /* 
+                * CSSM_CL_CertSign() returned us a mallocd CSSM_DATA. Copy
+                * its fields to caller's cert. 
+                */
+               certs[dex] = signedCert;
+               
+               /* and the raw unsigned cert as well */
+               appFreeCssmData(rawCert, CSSM_TRUE);
+               rtn = 0;
+       }
+       
+       /* free resources */
+       if(issuerName != NULL) {
+               CB_FreeX509Name(issuerName);
+       }
+       if(subjectName != NULL) {
+               CB_FreeX509Name(subjectName);
+       }
+       return crtn;
+}
+
+/* compare two CSSM_CERTGROUPs, returns CSSM_TRUE on success */
+CSSM_BOOL tpCompareCertGroups(
+       const CSSM_CERTGROUP    *grp1,
+       const CSSM_CERTGROUP    *grp2)
+{
+       unsigned i;
+       CSSM_DATA_PTR   d1;
+       CSSM_DATA_PTR   d2;
+       
+       if(grp1->NumCerts != grp2->NumCerts) {
+               return CSSM_FALSE;
+       }
+       for(i=0; i<grp1->NumCerts; i++) {
+               d1 = &grp1->GroupList.CertList[i];
+               d2 = &grp2->GroupList.CertList[i];
+               
+               /* these are all errors */
+               if((d1->Data == NULL) ||
+                  (d1->Length == 0)  ||
+                  (d2->Data == NULL) ||
+                  (d2->Length == 0)) {
+                       printf("compareCertGroups: bad cert group!\n");
+                       return CSSM_FALSE;
+               }
+               if(d1->Length != d2->Length) {
+                       return CSSM_FALSE;
+               }
+               if(memcmp(d1->Data, d2->Data, d1->Length)) {
+                       return CSSM_FALSE;
+               }
+       }
+       return CSSM_TRUE;
+}
+
+/* free a CSSM_CERT_GROUP */ 
+void tpFreeCertGroup(
+       CSSM_CERTGROUP_PTR      certGroup,
+       CSSM_BOOL                       freeCertData,           // free individual CertList.Data 
+       CSSM_BOOL                       freeStruct)                     // free the overall CSSM_CERTGROUP
+{
+       unsigned dex;
+       
+       if(certGroup == NULL) {
+               return; 
+       }
+       
+       if(freeCertData) {
+               /* free the individual cert Data fields */
+               for(dex=0; dex<certGroup->NumCerts; dex++) {
+                       appFreeCssmData(&certGroup->GroupList.CertList[dex], CSSM_FALSE);
+               }
+       }
+
+       /* and the array of CSSM_DATAs */
+       if(certGroup->GroupList.CertList) {
+               CSSM_FREE(certGroup->GroupList.CertList);
+       }
+       
+       if(freeStruct) {
+               CSSM_FREE(certGroup);
+       }
+}
+
+CSSM_RETURN clDeleteAllCerts(CSSM_DL_DB_HANDLE dlDb)
+{
+       CSSM_QUERY                                              query;
+       CSSM_DB_UNIQUE_RECORD_PTR               record = NULL;
+       CSSM_RETURN                                             crtn;
+       CSSM_HANDLE                                             resultHand;
+       CSSM_DB_RECORD_ATTRIBUTE_DATA   recordAttrs;
+       
+       recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE;
+       recordAttrs.NumberOfAttributes = 0;
+       recordAttrs.AttributeData = NULL;
+       
+       /* just search by recordType, no predicates */
+       query.RecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE;
+       query.Conjunctive = CSSM_DB_NONE;
+       query.NumSelectionPredicates = 0;
+       query.SelectionPredicate = NULL;
+       query.QueryLimits.TimeLimit = 0;                        // FIXME - meaningful?
+       query.QueryLimits.SizeLimit = 1;                        // FIXME - meaningful?
+       query.QueryFlags = 0;           // CSSM_QUERY_RETURN_DATA...FIXME - used?
+
+       crtn = CSSM_DL_DataGetFirst(dlDb,
+               &query,
+               &resultHand,
+               &recordAttrs,
+               NULL,                   // No data
+               &record);
+       switch(crtn) {
+               case CSSM_OK:
+                       break;          // proceed
+               case CSSMERR_DL_ENDOFDATA:
+                       /* OK, no certs */
+                       return CSSM_OK;
+               default:
+                       printError("DataGetFirst", crtn);
+                       return crtn;
+       }
+
+       crtn = CSSM_DL_DataDelete(dlDb, record);
+       if(crtn) {
+               printError("CSSM_DL_DataDelete", crtn);
+               return crtn;
+       }
+       CSSM_DL_FreeUniqueRecord(dlDb, record);
+       
+       /* now the rest of them */
+       for(;;) {
+               crtn = CSSM_DL_DataGetNext(dlDb,
+                       resultHand, 
+                       &recordAttrs,
+                       NULL,
+                       &record);
+               switch(crtn) {
+                       case CSSM_OK:
+                               crtn = CSSM_DL_DataDelete(dlDb, record);
+                               if(crtn) {
+                                       printError("CSSM_DL_DataDelete", crtn);
+                                       return crtn;
+                               }
+                               CSSM_DL_FreeUniqueRecord(dlDb, record);
+                               break;          // and go again 
+                       case CSSMERR_DL_ENDOFDATA:
+                               /* normal termination */
+                               break;
+                       default:
+                               printError("DataGetNext", crtn);
+                               return crtn;
+               }
+               if(crtn != CSSM_OK) {
+                       break;
+               }
+       }
+       CSSM_DL_DataAbortQuery(dlDb, resultHand);
+       return CSSM_OK;
+}
+
+/*
+ * Wrapper for CSSM_TP_CertGroupVerify. What an ugly API.
+ */
+CSSM_RETURN tpCertGroupVerify(
+       CSSM_TP_HANDLE                                          tpHand,
+       CSSM_CL_HANDLE                                          clHand,
+       CSSM_CSP_HANDLE                                         cspHand,
+       CSSM_DL_DB_LIST_PTR                                     dbListPtr,
+       const CSSM_OID                                          *policy,                // optional
+       const CSSM_DATA                                         *fieldOpts,             // optional
+       const CSSM_DATA                                         *actionData,    // optional
+       void                                                            *policyOpts,
+       const CSSM_CERTGROUP                            *certGroup,
+       CSSM_DATA_PTR                                           anchorCerts,
+       unsigned                                                        numAnchorCerts,
+       CSSM_TP_STOP_ON                                         stopOn,         // CSSM_TP_STOP_ON_POLICY, etc.
+       CSSM_TIMESTRING                                         cssmTimeStr,// optional
+       CSSM_TP_VERIFY_CONTEXT_RESULT_PTR       result)         // optional, RETURNED
+{
+       /* main job is building a CSSM_TP_VERIFY_CONTEXT and its components */
+       CSSM_TP_VERIFY_CONTEXT          vfyCtx;
+       CSSM_TP_CALLERAUTH_CONTEXT      authCtx;
+       
+       memset(&vfyCtx, 0, sizeof(CSSM_TP_VERIFY_CONTEXT));
+       vfyCtx.Action = CSSM_TP_ACTION_DEFAULT;
+       if(actionData) {
+               vfyCtx.ActionData = *actionData;
+       }
+       else {
+               vfyCtx.ActionData.Data = NULL;
+               vfyCtx.ActionData.Length = 0;
+       }
+       vfyCtx.Cred = &authCtx;
+       
+       /* CSSM_TP_CALLERAUTH_CONTEXT components */
+       /* 
+               typedef struct cssm_tp_callerauth_context {
+                       CSSM_TP_POLICYINFO Policy;
+                       CSSM_TIMESTRING VerifyTime;
+                       CSSM_TP_STOP_ON VerificationAbortOn;
+                       CSSM_TP_VERIFICATION_RESULTS_CALLBACK CallbackWithVerifiedCert;
+                       uint32 NumberOfAnchorCerts;
+                       CSSM_DATA_PTR AnchorCerts;
+                       CSSM_DL_DB_LIST_PTR DBList;
+                       CSSM_ACCESS_CREDENTIALS_PTR CallerCredentials;
+               } CSSM_TP_CALLERAUTH_CONTEXT, *CSSM_TP_CALLERAUTH_CONTEXT_PTR;
+       */
+       /* zero or one policy here */
+       CSSM_FIELD      policyId;
+       if(policy != NULL) {
+               policyId.FieldOid = (CSSM_OID)*policy;
+               authCtx.Policy.NumberOfPolicyIds = 1;
+               authCtx.Policy.PolicyIds = &policyId;
+               if(fieldOpts != NULL) {
+                       policyId.FieldValue = *fieldOpts;
+               }
+               else {
+                       policyId.FieldValue.Data = NULL;
+                       policyId.FieldValue.Length = 0;
+               }
+       }
+       else {
+               authCtx.Policy.NumberOfPolicyIds = 0;
+               authCtx.Policy.PolicyIds = NULL;
+       }
+       authCtx.Policy.PolicyControl = policyOpts;
+       authCtx.VerifyTime = cssmTimeStr;                       // may be NULL
+       authCtx.VerificationAbortOn = stopOn;
+       authCtx.CallbackWithVerifiedCert = NULL;
+       authCtx.NumberOfAnchorCerts = numAnchorCerts;
+       authCtx.AnchorCerts = anchorCerts;
+       authCtx.DBList = dbListPtr;
+       authCtx.CallerCredentials = NULL;
+       
+       return CSSM_TP_CertGroupVerify(tpHand,
+               clHand,
+               cspHand,
+               certGroup,
+               &vfyCtx,
+               result);
+}
+
+/*
+ * Open, optionally create, KC-style DLDB. 
+ */
+#define KC_DB_PATH             "Library/Keychains"             /* relative to home */
+
+CSSM_RETURN tpKcOpen(
+       CSSM_DL_HANDLE          dlHand,
+       const char                      *kcName,
+       const char                      *pwd,                           // optional to avoid UI 
+       CSSM_BOOL                       doCreate,
+       CSSM_DB_HANDLE          *dbHand)                        // RETURNED
+{
+       char kcPath[300];
+       const char *kcFileName = kcName;
+       char *userHome = getenv("HOME");
+       
+       if(userHome == NULL) {
+               /* well, this is probably not going to work */
+               userHome = (char *)"";
+       }
+       sprintf(kcPath, "%s/%s/%s", userHome, KC_DB_PATH, kcFileName);
+       return dbCreateOpen(dlHand, kcPath,
+               doCreate, CSSM_FALSE, pwd, dbHand);
+}
+
+/*
+ * Free the contents of a CSSM_TP_VERIFY_CONTEXT_RESULT returned from
+ * CSSM_TP_CertGroupVerify().
+ */
+CSSM_RETURN freeVfyResult(
+       CSSM_TP_VERIFY_CONTEXT_RESULT *ctx)
+{
+       int numCerts = -1;
+       CSSM_RETURN crtn = CSSM_OK;
+       
+       for(unsigned i=0; i<ctx->NumberOfEvidences; i++) {
+               CSSM_EVIDENCE_PTR evp = &ctx->Evidence[i];
+               switch(evp->EvidenceForm) {
+                       case CSSM_EVIDENCE_FORM_APPLE_HEADER:
+                               /* Evidence = (CSSM_TP_APPLE_EVIDENCE_HEADER *) */
+                               appFree(evp->Evidence, NULL);
+                               evp->Evidence = NULL;
+                               break;
+                       case CSSM_EVIDENCE_FORM_APPLE_CERTGROUP:
+                       {
+                               /* Evidence = CSSM_CERTGROUP_PTR */
+                               CSSM_CERTGROUP_PTR cgp = (CSSM_CERTGROUP_PTR)evp->Evidence;
+                               numCerts = cgp->NumCerts;       
+                               tpFreeCertGroup(cgp, CSSM_TRUE, CSSM_TRUE);
+                               evp->Evidence = NULL;
+                               break;
+                       }
+                       case CSSM_EVIDENCE_FORM_APPLE_CERT_INFO:
+                       {
+                               /* Evidence = array of CSSM_TP_APPLE_EVIDENCE_INFO */
+                               if(numCerts < 0) {
+                                       /* Haven't gotten a CSSM_CERTGROUP_PTR! */
+                                       printf("***Malformed VerifyContextResult (2)\n");
+                                       crtn = CSSMERR_TP_INTERNAL_ERROR;
+                                       break;
+                               }
+                               CSSM_TP_APPLE_EVIDENCE_INFO *evInfo = 
+                                       (CSSM_TP_APPLE_EVIDENCE_INFO *)evp->Evidence;
+                               for(unsigned k=0; k<(unsigned)numCerts; k++) {
+                                       /* Dispose of StatusCodes, UniqueRecord */
+                                       CSSM_TP_APPLE_EVIDENCE_INFO *thisEvInfo = 
+                                               &evInfo[k];
+                                       if(thisEvInfo->StatusCodes) {
+                                               appFree(thisEvInfo->StatusCodes, NULL);
+                                       }
+                                       if(thisEvInfo->UniqueRecord) {
+                                               CSSM_RETURN crtn = 
+                                                       CSSM_DL_FreeUniqueRecord(thisEvInfo->DlDbHandle,
+                                                               thisEvInfo->UniqueRecord);
+                                               if(crtn) {
+                                                       printError("CSSM_DL_FreeUniqueRecord", crtn);
+                                                       printf("   Record %p\n", thisEvInfo->UniqueRecord);
+                                                       break;
+                                               }
+                                               thisEvInfo->UniqueRecord = NULL;
+                                       }
+                               }       /* for each cert info */
+                               appFree(evp->Evidence, NULL);
+                               evp->Evidence = NULL;
+                               break;
+                       }       /* CSSM_EVIDENCE_FORM_APPLE_CERT_INFO */
+               }               /* switch(evp->EvidenceForm) */
+       }                       /* for each evidence */
+       if(ctx->Evidence) {
+               appFree(ctx->Evidence, NULL);
+               ctx->Evidence = NULL;
+       }
+       return crtn;
+}
+
+/* Display verify results */
+static void statusBitTest(
+       CSSM_TP_APPLE_CERT_STATUS certStatus, 
+       uint32 bit,
+       const char *str)
+{
+       if(certStatus & bit) {
+               printf("%s  ", str);
+       }
+}
+
+void printCertInfo(
+       unsigned numCerts,                                                      // from CertGroup
+       const CSSM_TP_APPLE_EVIDENCE_INFO *info)
+{
+       CSSM_TP_APPLE_CERT_STATUS cs;
+       
+       for(unsigned i=0; i<numCerts; i++) {
+               const CSSM_TP_APPLE_EVIDENCE_INFO *thisInfo = &info[i];
+               cs = thisInfo->StatusBits;
+               printf("   cert %u:\n", i);
+               printf("      StatusBits     : 0x%x", (unsigned)cs);
+               if(cs) {
+                       printf(" ( ");
+                       statusBitTest(cs, CSSM_CERT_STATUS_EXPIRED, "EXPIRED");
+                       statusBitTest(cs, CSSM_CERT_STATUS_NOT_VALID_YET, 
+                               "NOT_VALID_YET");
+                       statusBitTest(cs, CSSM_CERT_STATUS_IS_IN_INPUT_CERTS, 
+                               "IS_IN_INPUT_CERTS");
+                       statusBitTest(cs, CSSM_CERT_STATUS_IS_IN_ANCHORS, 
+                               "IS_IN_ANCHORS");
+                       statusBitTest(cs, CSSM_CERT_STATUS_IS_ROOT, "IS_ROOT");
+                       statusBitTest(cs, CSSM_CERT_STATUS_IS_FROM_NET, "IS_FROM_NET");
+                       statusBitTest(cs, CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_USER, 
+                               "TRUST_SETTINGS_FOUND_USER");
+                       statusBitTest(cs, CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_ADMIN, 
+                               "TRUST_SETTINGS_FOUND_ADMIN");
+                       statusBitTest(cs, CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_SYSTEM, 
+                               "TRUST_SETTINGS_FOUND_SYSTEM");
+                       statusBitTest(cs, CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST, 
+                               "TRUST_SETTINGS_TRUST");
+                       statusBitTest(cs, CSSM_CERT_STATUS_TRUST_SETTINGS_DENY, 
+                               "TRUST_SETTINGS_DENY");
+                       statusBitTest(cs, CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR, 
+                               "TRUST_SETTINGS_IGNORED_ERROR");
+                       printf(")\n");
+               }
+               else {
+                       printf("\n");
+               }
+               printf("      NumStatusCodes : %u ",
+                       (unsigned)thisInfo->NumStatusCodes);
+               for(unsigned j=0; j<thisInfo->NumStatusCodes; j++) {
+                       printf("%s  ", 
+                               cssmErrToStr(thisInfo->StatusCodes[j]));
+               }
+               printf("\n");
+               printf("      Index: %u\n", (unsigned)thisInfo->Index);
+       }
+       return;
+}
+
+/* we really only need CSSM_EVIDENCE_FORM_APPLE_CERT_INFO */
+#define SHOW_ALL_VFY_RESULTS           0
+
+void dumpVfyResult(
+       const CSSM_TP_VERIFY_CONTEXT_RESULT *vfyResult)
+{
+       unsigned numEvidences = vfyResult->NumberOfEvidences;
+       unsigned numCerts = 0;
+       printf("Returned evidence:\n");
+       for(unsigned dex=0; dex<numEvidences; dex++) {
+               CSSM_EVIDENCE_PTR ev = &vfyResult->Evidence[dex];
+               #if SHOW_ALL_VFY_RESULTS
+               printf("   Evidence %u:\n", dex);
+               #endif
+               switch(ev->EvidenceForm) {
+                       case CSSM_EVIDENCE_FORM_APPLE_HEADER:
+                       {
+                               #if SHOW_ALL_VFY_RESULTS
+                               const CSSM_TP_APPLE_EVIDENCE_HEADER *hdr = 
+                                       (const CSSM_TP_APPLE_EVIDENCE_HEADER *)(ev->Evidence);
+                               printf("      Form = HEADER; Version = %u\n", hdr->Version);
+                               #endif
+                               break;
+                       }
+                       case CSSM_EVIDENCE_FORM_APPLE_CERTGROUP:
+                       {
+                               const CSSM_CERTGROUP *grp = 
+                                       (const CSSM_CERTGROUP *)ev->Evidence;
+                               numCerts = grp->NumCerts;
+                               #if SHOW_ALL_VFY_RESULTS
+                               /* parse the rest of this eventually */
+                               /* Note we depend on this coming before the CERT_INFO */
+                               printf("      Form = CERTGROUP; numCerts = %u\n", numCerts);
+                               #endif
+                               break;
+                       }
+                       case CSSM_EVIDENCE_FORM_APPLE_CERT_INFO:        
+                       {
+                               const CSSM_TP_APPLE_EVIDENCE_INFO *info = 
+                                       (const CSSM_TP_APPLE_EVIDENCE_INFO *)ev->Evidence;
+                               printCertInfo(numCerts, info);
+                               break;
+                       }
+                       default:
+                               printf("***UNKNOWN Evidence form (%u)\n", 
+                                       (unsigned)ev->EvidenceForm);
+                               break;
+               }
+       }
+}
+
+/* 
+ * Obtain system anchors in CF and in CSSM_DATA form.
+ * Caller must CFRelease the returned rootArray and 
+ * free() the returned CSSM_DATA array, but not its
+ * contents - SecCertificates themselves own that.
+ */
+OSStatus getSystemAnchors(
+       CFArrayRef *rootArray,  /* RETURNED */
+       CSSM_DATA **anchors,    /* RETURNED */
+       unsigned *numAnchors)   /* RETURNED */
+{
+       OSStatus ortn;
+       CFArrayRef cfAnchors;
+       CSSM_DATA *cssmAnchors;
+
+       ortn = SecTrustSettingsCopyUnrestrictedRoots(false, false, true,
+               &cfAnchors);
+       if(ortn) {
+               cssmPerror("SecTrustSettingsCopyUnrestrictedRoots", ortn);
+               return ortn;
+       }
+       unsigned _numAnchors = CFArrayGetCount(cfAnchors);
+       cssmAnchors = (CSSM_DATA *)malloc(sizeof(CSSM_DATA) * _numAnchors);
+       unsigned dex;
+       for(dex=0; dex<_numAnchors; dex++) {
+               SecCertificateRef root = (SecCertificateRef)CFArrayGetValueAtIndex(
+                       cfAnchors, dex);
+               ortn = SecCertificateGetData(root, &cssmAnchors[dex]);
+               if(ortn) {
+                       cssmPerror("SecCertificateGetData", ortn);
+                       return ortn;
+               }
+       }
+       *rootArray = cfAnchors;
+       *anchors = cssmAnchors;
+       *numAnchors = _numAnchors;
+       return noErr;
+}
+
+/* get a SecCertificateRef from a file */
+SecCertificateRef certFromFile(
+       const char *fileName)
+{
+       unsigned char *cp = NULL;
+       unsigned len = 0;
+       if(readFile(fileName, &cp, &len)) {
+               printf("***Error reading file %s\n", fileName);
+               return NULL;
+       }
+       SecCertificateRef certRef;
+       CSSM_DATA certData = {len, cp};
+       OSStatus ortn = SecCertificateCreateFromData(&certData, 
+                       CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &certRef);
+       if(ortn) {
+               cssmPerror("SecCertificateCreateFromData", ortn);
+               return NULL;
+       }
+       free(cp);
+       return certRef;
+}
+