]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTests/clxutils/caVerify/caVerify.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTests / clxutils / caVerify / caVerify.cpp
diff --git a/SecurityTests/clxutils/caVerify/caVerify.cpp b/SecurityTests/clxutils/caVerify/caVerify.cpp
new file mode 100644 (file)
index 0000000..e19d330
--- /dev/null
@@ -0,0 +1,648 @@
+/* Copyright (c) 1998,2003-2006,2008 Apple Inc.
+ *
+ * caVerify.cpp
+ *
+ * Verify proper detection of basicConstraints.cA
+ */
+
+#include <utilLib/common.h>
+#include <utilLib/cspwrap.h>
+#include <security_cdsa_utils/cuFileIo.h>
+#include <clAppUtils/CertBuilderApp.h>
+#include <clAppUtils/clutils.h>
+#include <clAppUtils/timeStr.h>
+#include <clAppUtils/tpUtils.h>
+#include <security_cdsa_utils/cuPrintCert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.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>
+#include <string.h>
+
+/* define nonzero to build on Puma */
+#define PUMA_BUILD                     0
+
+/* default key and signature algorithm */
+#define SIG_ALG_DEFAULT                CSSM_ALGID_SHA1WithRSA
+#define KEY_ALG_DEFAULT                CSSM_ALGID_RSA
+
+#define NUM_CERTS_DEF          5               /* default is random from 2 to this */
+#define NUM_LOOPS_DEF          100
+
+#if            PUMA_BUILD
+extern "C" {
+       void cssmPerror(const char *how, CSSM_RETURN error);
+}
+#endif /* PUMA_BUILD */
+
+static void usage(char **argv)
+{
+       printf("Usage: %s [options]\n", argv[0]);
+       printf("Options:\n");
+       printf("   n=numCerts (default=random from 2 to %d)\n", NUM_CERTS_DEF);
+       printf("   a=alg  where alg is s(RSA/SHA1), 5(RSA/MD5), f(FEE/MD5), "
+                       "F(FEE/SHA1), e(ECDSA), E(ANSI/ECDSA), 6(ECDSA/SHA256)\n");
+       printf("   k=keySizeInBits\n");
+       printf("   c (dump certs on error)\n");
+       printf("   l=numLoops (default = %d)\n", NUM_LOOPS_DEF);
+       exit(1);
+}
+
+void showCerts(
+       const CSSM_DATA *certs,
+       unsigned numCerts)
+{
+       unsigned i;
+       
+       for(i=0; i<numCerts; i++) {
+               printf("======== cert %d ========\n", i);
+               printCert(certs[i].Data, certs[i].Length, CSSM_FALSE);
+               printf("\n\n");
+       }
+}
+
+void writeCerts(
+       const CSSM_DATA *certs,
+       unsigned numCerts)
+{
+       unsigned i;
+       char fileName[80];
+       
+       for(i=0; i<numCerts; i++) {
+               sprintf(fileName, "caVerifyCert%u.cer", i);
+               writeFile(fileName, certs[i].Data, certs[i].Length);
+               printf("....wrote %lu bytes to %s\n", certs[i].Length, fileName);
+       }
+}
+
+/* 
+ * Generate a cert chain using specified key pairs and extensions.
+ * The last cert in the chain (certs[numCerts-1]) is a root cert, 
+ * self-signed. 
+ */
+static CSSM_RETURN tpGenCertsExten(
+       CSSM_CSP_HANDLE         cspHand,
+       CSSM_CL_HANDLE          clHand,
+       CSSM_ALGORITHMS         sigAlg,                 /* CSSM_ALGID_SHA1WithRSA, etc. */
+       unsigned                        numCerts,
+       /* per-cert arrays */
+       const char                      *nameBase,              /* C string */
+       CSSM_KEY_PTR            pubKeys,                /* array of public keys */
+       CSSM_KEY_PTR            privKeys,               /* array of private keys */
+       CSSM_X509_EXTENSION     **extensions,
+       unsigned                        *numExtensions,
+       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;
+       
+       nameOid.oid = &CSSMOID_OrganizationName;        // const
+       nameOid.string = nameStr;
+       
+       /* 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,
+                       0x12345 + dex,  // serial number
+                       issuerName,
+                       subjectName,
+                       notBefore,
+                       notAfter,
+                       &pubKeys[dex],
+                       sigAlg,
+                       NULL,                                           // subj unique ID
+                       NULL,                                           // issuer unique ID
+                       extensions[dex],                        // extensions
+                       numExtensions[dex]);            // 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. 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;
+}
+
+static int doTest(
+       CSSM_CSP_HANDLE cspHand,                // CSP handle
+       CSSM_CL_HANDLE  clHand,                 // CL handle
+       CSSM_TP_HANDLE  tpHand,                 // TP handle
+       unsigned                numCerts,               // >= 2
+       CSSM_KEY_PTR    pubKeys,
+       CSSM_KEY_PTR    privKeys,
+       CSSM_ALGORITHMS sigAlg,
+       CSSM_BOOL               expectFail,
+       CSSM_BOOL               dumpCerts,
+       CSSM_BOOL               quiet)
+{
+       CSSM_DATA_PTR           certs;
+       CSSM_X509_EXTENSION     **extens;
+       unsigned                        *numExtens;
+       char                            *notBeforeStr = genTimeAtNowPlus(0);
+       char                            *notAfterStr = genTimeAtNowPlus(10000);
+       unsigned                        certDex;
+       CE_BasicConstraints     *bc;
+       CSSM_X509_EXTENSION *thisExten;
+       CE_BasicConstraints *thisBc;
+       const char                      *failMode = "not set - internal error";
+       CSSM_RETURN                     crtn;
+       unsigned                        badCertDex = 0;
+       
+       certs = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA) * numCerts);
+       memset(certs, 0, sizeof(CSSM_DATA) * numCerts);
+       
+       /*
+        * For now just zero or one extension per cert - basicConstraints. 
+        * Eventually we'll want to test keyUsage as well.
+        */
+       extens = (CSSM_X509_EXTENSION **)malloc(sizeof(CSSM_X509_EXTENSION *) * numCerts);
+       memset(extens, 0, sizeof(CSSM_X509_EXTENSION *) * numCerts);
+       numExtens = (unsigned *)malloc(sizeof(unsigned) * numCerts);
+       bc = (CE_BasicConstraints *)malloc(sizeof(CE_BasicConstraints) * numCerts);
+       
+       /*
+        * Set up all extensions for success
+        */
+       for(certDex=0; certDex<numCerts; certDex++) {
+               extens[certDex] = (CSSM_X509_EXTENSION *)malloc(sizeof(CSSM_X509_EXTENSION));
+               numExtens[certDex] = 1;
+               thisExten = extens[certDex];
+               thisBc = &bc[certDex];
+               
+               thisExten->extnId = CSSMOID_BasicConstraints;
+               thisExten->critical = CSSM_TRUE;
+               thisExten->format = CSSM_X509_DATAFORMAT_PARSED;
+               thisExten->value.parsedValue = thisBc;
+               thisExten->BERvalue.Data = NULL;
+               thisExten->BERvalue.Length = 0;
+
+               if(certDex == 0) {
+                       /* leaf - flip coin to determine presence of basicConstraints */
+                       int coin = genRand(1,2);
+                       if(coin == 1) {
+                               /* basicConstraints, !cA */
+                               thisBc->cA = CSSM_FALSE;
+                               thisBc->pathLenConstraintPresent = CSSM_FALSE;
+                               thisBc->pathLenConstraint = 0;
+                       }
+                       else {
+                               /* !basicConstraints, !cA by default */
+                               numExtens[certDex] = 0;
+                       }
+               }
+               else if(certDex == (numCerts-1)) {
+                       /* root - flip coin to determine presence of basicConstraints */
+                       int coin = genRand(1,2);
+                       if(coin == 1) {
+                               /* basicConstraints, cA */
+                               thisBc->cA = CSSM_TRUE;
+                               /* flip coin to determine present of pathLenConstraint */
+                               coin = genRand(1,2);
+                               if(coin == 1) {
+                                       thisBc->pathLenConstraintPresent = CSSM_FALSE;
+                                       thisBc->pathLenConstraint = 0;
+                               }
+                               else {
+                                       thisBc->pathLenConstraintPresent = CSSM_TRUE;
+                                       thisBc->pathLenConstraint = genRand(certDex-1, numCerts+1);
+                               }
+                       }
+                       else {
+                               /* !basicConstraints, cA by default */
+                               numExtens[certDex] = 0;
+                       }
+               }
+               else {
+                       /* intermediate = cA required */
+                       thisBc->cA = CSSM_TRUE;
+                       /* flip coin to determine presence of pathLenConstraint */
+                       int coin = genRand(1,2);
+                       if(coin == 1) {
+                               thisBc->pathLenConstraintPresent = CSSM_FALSE;
+                               thisBc->pathLenConstraint = 0;
+                       }
+                       else {
+                               thisBc->pathLenConstraintPresent = CSSM_TRUE;
+                               thisBc->pathLenConstraint = genRand(certDex-1, numCerts+1);
+                       }
+               }
+       }
+       
+       if(expectFail) {
+               /* introduce a failure */
+               if(numCerts == 2) {
+                       /* only possible failure is explicit !cA in root */
+                       /* don't assume presence of BC exten */
+                       badCertDex = 1;
+                       thisExten = extens[badCertDex];
+                       thisBc = &bc[badCertDex];
+                       thisBc->cA = CSSM_FALSE;
+                       thisBc->pathLenConstraintPresent = CSSM_FALSE;
+                       bc->pathLenConstraint = 0;
+                       numExtens[badCertDex] = 1;
+                       failMode = "Explicit !cA in root";
+               }
+               else {
+                       /* roll the dice to select an intermediate cert */
+                       badCertDex = genRand(1, numCerts-2);
+                       thisExten = extens[badCertDex];
+                       if((thisExten == NULL) || (numExtens[badCertDex] == 0)) {
+                               printf("***INTERNAL SCREWUP\n");
+                               exit(1);
+                       }
+                       thisBc = &bc[badCertDex];
+                       
+                       /* 
+                        * roll die: fail by 
+                        *   -- no BasicConstraints
+                        *   -- !cA
+                        *   -- bad pathLenConstraint 
+                        */
+                       int die = genRand(1,3);
+                       if((die == 1) &&
+                          (badCertDex != 1)) {         // last cA doesn't need pathLenConstraint 
+                               thisBc->pathLenConstraintPresent = CSSM_TRUE;
+                               thisBc->pathLenConstraint = badCertDex - 2;     // one short
+                               failMode = "Short pathLenConstraint";
+                       }
+                       else if(die == 2) {
+                               thisBc->cA = CSSM_FALSE;
+                               failMode = "Explicit !cA in intermediate";
+                       }
+                       else {
+                               /* no extension */
+                               numExtens[badCertDex] = 0;
+                               failMode = "No BasicConstraints in intermediate";
+                       }
+               }
+       }
+       if(!quiet && expectFail) {
+               printf("   ...bad cert at index %d: %s\n", badCertDex, failMode);
+       }
+       
+       /* here we go - create cert chain */
+       crtn = tpGenCertsExten(cspHand,
+               clHand,
+               sigAlg,
+               numCerts,
+               "caVerify",             // nameBase
+               pubKeys,
+               privKeys,
+               extens,
+               numExtens,
+               certs,
+               notBeforeStr,
+               notAfterStr);
+       if(crtn) {
+               printError("tpGenCertsExten", crtn);
+               return crtn;    // and leak like crazy 
+       }
+       
+       CSSM_CERTGROUP  cgrp;
+       memset(&cgrp, 0, sizeof(CSSM_CERTGROUP));
+       cgrp.NumCerts = numCerts;
+       #if             PUMA_BUILD
+       cgrp.CertGroupType = CSSM_CERTGROUP_ENCODED_CERT;
+       #else
+       /* Jaguar */
+       cgrp.CertGroupType = CSSM_CERTGROUP_DATA;
+       #endif  /* PUMA_BUILD */
+       cgrp.CertType = CSSM_CERT_X_509v3;
+       cgrp.CertEncoding = CSSM_CERT_ENCODING_DER; 
+       cgrp.GroupList.CertList = certs;
+       
+       #if             PUMA_BUILD
+       crtn = tpCertGroupVerify(tpHand,
+               clHand,
+               cspHand,
+               NULL,                                           // DlDbList
+               &CSSMOID_APPLE_X509_BASIC,      // SSL requires built-in root match 
+               &cgrp,
+               /* pass in OUR ROOT as anchors */
+               (CSSM_DATA_PTR)&certs[numCerts-1],              // anchorCerts
+               1,
+               CSSM_TP_STOP_ON_POLICY, 
+               CSSM_FALSE,                                     // allowExpired
+               NULL);                                          // vfyResult
+       #else
+       /* Jaguar */
+       crtn = tpCertGroupVerify(tpHand,
+               clHand,
+               cspHand,
+               NULL,                                           // DlDbList
+               &CSSMOID_APPLE_TP_SSL,          // may want to parameterize this
+               NULL,                                           // fieldOpts for server name
+               NULL,                                           // actionDataPtr for allow expired
+               NULL,                                           // policyOpts
+               &cgrp,
+               /* pass in OUR ROOT as anchors */
+               (CSSM_DATA_PTR)&certs[numCerts-1],              // anchorCerts
+               1,
+               CSSM_TP_STOP_ON_POLICY, 
+               NULL,                                           // cssmTimeStr
+               NULL);                                          // vfyResult
+       #endif  /* PUMA_BUILD */
+       if(expectFail) {
+               if(crtn != CSSMERR_TP_VERIFY_ACTION_FAILED) {
+                       cssmPerror("***Expected error TP_VERIFY_ACTION_FAILED; got ", crtn);
+                       printf("   Expected failure due to %s\n", failMode);
+                       if(dumpCerts) {
+                               showCerts(certs, numCerts);
+                               writeCerts(certs, numCerts);
+                       }
+                       return testError(quiet);
+               }
+       }
+       else if(crtn) {
+               cssmPerror("Unexpected failure on tpCertGroupVerify", crtn);
+               if(dumpCerts) {
+                       showCerts(certs, numCerts);
+               }
+               return testError(quiet);
+       }
+               
+       /* clean up */
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       CSSM_CL_HANDLE  clHand;                 // CL handle
+       CSSM_CSP_HANDLE cspHand;                // CSP handle
+       CSSM_TP_HANDLE  tpHand;
+       CSSM_KEY_PTR    pubKeys;
+       CSSM_KEY_PTR    privKeys;
+       CSSM_RETURN             crtn;
+       int                             arg;
+       unsigned                certDex;
+       unsigned                loopNum;
+       unsigned                maxCerts;
+       
+       /* user-spec'd variables */
+       CSSM_ALGORITHMS keyAlg = KEY_ALG_DEFAULT;
+       CSSM_ALGORITHMS sigAlg = SIG_ALG_DEFAULT;
+       uint32                  keySizeInBits = CSP_KEY_SIZE_DEFAULT;
+       unsigned                numCerts = 0;                   // means random per loop
+       unsigned                numLoops = NUM_LOOPS_DEF;
+       CSSM_BOOL               quiet = CSSM_FALSE;
+       CSSM_BOOL               dumpCerts = CSSM_FALSE;
+       
+       for(arg=1; arg<argc; arg++) {
+               switch(argv[arg][0]) {
+                       case 'a':
+                               if((argv[arg][1] == '\0') || (argv[arg][2] == '\0')) {
+                                       usage(argv);
+                               }
+                               switch(argv[arg][2]) {
+                                       case 's':
+                                               keyAlg = CSSM_ALGID_RSA;
+                                               sigAlg = CSSM_ALGID_SHA1WithRSA;
+                                               break;
+                                       case '5':
+                                               keyAlg = CSSM_ALGID_RSA;
+                                               sigAlg = CSSM_ALGID_MD5WithRSA;
+                                               break;
+                                       case 'f':
+                                               keyAlg = CSSM_ALGID_FEE;
+                                               sigAlg = CSSM_ALGID_FEE_MD5;
+                                               break;
+                                       case 'F':
+                                               keyAlg = CSSM_ALGID_FEE;
+                                               sigAlg = CSSM_ALGID_FEE_SHA1;
+                                               break;
+                                       case 'e':
+                                               keyAlg = CSSM_ALGID_FEE;
+                                               sigAlg = CSSM_ALGID_SHA1WithECDSA;
+                                               break;
+                                       case 'E':
+                                               keyAlg = CSSM_ALGID_ECDSA;
+                                               sigAlg = CSSM_ALGID_SHA1WithECDSA;
+                                               break;
+                                       case '6':
+                                               keyAlg = CSSM_ALGID_ECDSA;
+                                               sigAlg = CSSM_ALGID_SHA256WithECDSA;
+                                               break;
+                                       default:
+                                               usage(argv);
+                               }
+                               break;
+                   case 'k':
+                               keySizeInBits = atoi(&argv[arg][2]);
+                               break;
+                   case 'l':
+                               numLoops = atoi(&argv[arg][2]);
+                               break;
+                   case 'n':
+                               numCerts = atoi(&argv[arg][2]);
+                               break;
+                   case 'c':
+                               dumpCerts = CSSM_TRUE;
+                               break;
+                   case 'q':
+                               quiet = CSSM_TRUE;
+                               break;
+                       default:
+                               usage(argv);
+               }
+       }
+       
+       /* connect to CL, TP, and CSP */
+       clHand = clStartup();
+       if(clHand == 0) {
+               return 0;
+       }
+       tpHand = tpStartup();
+       if(tpHand == 0) {
+               return 0;
+       }
+       cspHand = cspStartup();
+       if(cspHand == 0) {
+               return 0;
+       }
+
+       if(numCerts == 0) {
+               maxCerts = NUM_CERTS_DEF;       // random, this is the max $
+       }
+       else {
+               maxCerts = numCerts;            // user-specd
+       }
+       
+       printf("Starting caVerify; args: ");
+       for(unsigned i=1; i<(unsigned)argc; i++) {
+               printf("%s ", argv[i]);
+       }
+       printf("\n");
+
+       /* cook up maxCerts key pairs */
+       if(!quiet) {
+               printf("generating key pairs...\n");
+       }
+       pubKeys  = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY) * maxCerts);
+       privKeys = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY) * maxCerts);
+       for(certDex=0; certDex<maxCerts; certDex++) {
+               crtn = cspGenKeyPair(cspHand,
+                       keyAlg,
+                       "a key pair",
+                       9,
+                       keySizeInBits,
+                       &pubKeys[certDex],
+                       CSSM_FALSE,                     // pubIsRef - should work both ways, but not yet
+                       CSSM_KEYUSE_VERIFY,
+                       CSSM_KEYBLOB_RAW_FORMAT_NONE,
+                       &privKeys[certDex],
+                       CSSM_TRUE,                      // privIsRef - doesn't matter
+                       CSSM_KEYUSE_SIGN,
+                       CSSM_KEYBLOB_RAW_FORMAT_NONE,
+                       CSSM_FALSE);
+               if(crtn) {
+                       printError("cspGenKeyPair", crtn);
+                       printf("***error generating key pair. Aborting.\n");
+                       exit(1);
+               }
+       }
+
+       for(loopNum=0; loopNum<numLoops; loopNum++) {
+               unsigned thisNumCerts;
+               
+               /* random: num certs and whether this loop is to test a failure */
+               if(numCerts) {
+                       thisNumCerts = numCerts;
+               }
+               else {
+                       thisNumCerts = genRand(2, NUM_CERTS_DEF);
+               }
+               int coin = genRand(1,2);
+               CSSM_BOOL expectFail = (coin == 1) ? CSSM_TRUE : CSSM_FALSE;
+               if(!quiet) {
+                       printf("...loop %d numCerts %u expectFail %s\n", loopNum,
+                               thisNumCerts, expectFail ? "TRUE" : "FALSE");
+               }
+               if(doTest(cspHand,
+                       clHand,
+                       tpHand,
+                       thisNumCerts,
+                       pubKeys,
+                       privKeys,
+                       sigAlg,
+                       expectFail,
+                       dumpCerts,
+                       quiet)) {
+                               break;
+               }
+       }
+       /* clean up */
+       return 0;
+}
+
+