
/* ***************************************************************** *
 * Copyright 1998 International Business Machines Corporation. All   *
 * Rights Reserved.                                                  *
 *                                                                   *
 * Please read this carefully.  Your use of this reference           *
 * implementation of certain of the IETF public-key infrastructure   *
 * specifications ("Software") indicates your acceptance of the      *
 * following.  If you do not agree to the following, do not install  *
 * or use any of the Software.                                       *
 *                                                                   *
 * Permission to use, reproduce, distribute and create derivative    *
 * works from the Software ("Software Derivative Works"), and to     *
 * distribute such Software Derivative Works is hereby granted to    *
 * you by International Business Machines Corporation ("IBM").  This *
 * permission includes a license under the patents of IBM that are   *
 * necessarily infringed by your use of the Software as provided by  *
 * IBM.                                                              *
 *                                                                   *
 * IBM licenses the Software to you on an "AS IS" basis, without     *
 * warranty of any kind.  IBM HEREBY EXPRESSLY DISCLAIMS ALL         *
 * WARRANTIES OR CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING,   *
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS OF       *
 * MERCHANTABILITY, NON INFRINGEMENT AND FITNESS FOR A PARTICULAR    *
 * PURPOSE.  You are solely responsible for determining the          *
 * appropriateness of using this Software and assume all risks       *
 * associated with the use of this Software, including but not       *
 * limited to the risks of program errors, damage to or loss of      *
 * data, programs or equipment, and unavailability or interruption   *
 * of operations.                                                    *
 *                                                                   *
 * IBM WILL NOT BE LIABLE FOR ANY DIRECT DAMAGES OR FOR ANY SPECIAL, *
 * INCIDENTAL, OR  INDIRECT DAMAGES OR FOR ANY ECONOMIC              *
 * CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), EVEN   *
 * IF IBM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  IBM  *
 * will not be liable for the loss of, or damage to, your records or *
 * data, or any damages claimed by you based on a third party claim. *
 *                                                                   *
 * IBM wishes to obtain your feedback to assist in improving the     *
 * Software.  You grant IBM a world-wide, royalty-free right to use, *
 * copy, distribute, sublicense and prepare derivative works based   *
 * upon any feedback, including materials, error corrections,        *
 * Software Derivatives, enhancements, suggestions and the like that *
 * you provide to IBM relating to the Software (this does not        *
 * include products for which you charge a royalty and distribute to *
 * IBM under other terms and conditions).                            *
 *                                                                   *
 * You agree to distribute the Software and any Software Derivatives *
 * under a license agreement that: 1) is sufficient to notify all    *
 * licensees of the Software and Software Derivatives that IBM       *
 * assumes no liability for any claim that may arise regarding the   *
 * Software or Software Derivatives, and 2) that disclaims all       *
 * warranties, both express and implied, from IBM regarding the      *
 * Software and Software Derivatives.  (If you include this          *
 * Agreement with any distribution of the Software or Software       *
 * Derivatives you will have met this requirement.)  You agree that  *
 * you will not delete any copyright notices in the Software.        *
 *                                                                   *
 * This Agreement is the exclusive statement of your rights in the   *
 * Software as provided by IBM.   Except for the rights granted to   *
 * you in the second paragraph above, You are not granted any other  *
 * patent rights, including but not limited to the right to make     *
 * combinations of the Software with products that infringe IBM      *
 * patents. You agree to comply with all applicable laws and         *
 * regulations, including all export and import laws and regulation. *
 * This Agreement is governed by the laws of the State of New York.  *
 * This Agreement supersedes all other communications,               *
 * understandings or agreements we may have had prior to this        *
 * Agreement.                                                        *
 * ***************************************************************** */

//------------------------------------------------------------
// include
//------------------------------------------------------------

#include <assert.h>
#include <iomanip.h>
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef WIN32
#include <crtdbg.h>
#endif
#include "JonahIni.h"
#include "cssm.h"
#include "x509.h"
#include "pkcs.h"
#include "jkl.h"
#include "jdl.h"
#include "ldapdl.h"
#include "jonahtp.h"


//------------------------------------------------------------
// sample extension OIDs
//------------------------------------------------------------

unsigned long AuthorityKeyIdentifier_val[4]     = {2,5,29,35};
unsigned long SubjectKeyIdentifier_val[4]       = {2,5,29,14};
unsigned long KeyUsage_val[4]                   = {2,5,29,15};
unsigned long PrivateKeyUsagePeriod_val[4]      = {2,5,29,16};
unsigned long CertificatePolicies_val[4]        = {2,5,29,32};
unsigned long PolicyMappings_val[4]             = {2,5,29,33};
unsigned long SubjectAlternativeName_val[4]     = {2,5,29,17};
unsigned long IssuerAlternativeName_val[4]      = {2,5,29,18};
unsigned long BasicConstraints_val[4]           = {2,5,29,19};
unsigned long NameConstraints_val[4]            = {2,5,29,30};
unsigned long PolicyConstraints_val[4]          = {2,5,29,36};
unsigned long CRLDistributionPoints_val[4]      = {2,5,29,31};
unsigned long ExtendedKeyUsage_val[4]           = {2,5,29,37};
unsigned long SubjectDirectoryAttributes_val[4] = {2,5,29, 9};
unsigned long AuthorityInfoAccess_val[9]        = {1,3,6,1,5,5,7,1,1};


//------------------------------------------------------------
// X.509/PKIX CRL and CRL entry extensions
//------------------------------------------------------------

unsigned long CRLNumber_val[4]                  = {2,5,29,20};
unsigned long ReasonCode_val[4]                 = {2,5,29,21};
unsigned long InstructionCode_val[4]            = {2,5,29,23};
unsigned long InvalidityDate_val[4]             = {2,5,29,24};
unsigned long DeltaCRLIndicator_val[4]          = {2,5,29,27};
unsigned long IssuingDistributionPoint_val[4]   = {2,5,29,28};
unsigned long CertificateIssuer_val[4]          = {2,5,29,29};


//------------------------------------------------------------
// data structures
//------------------------------------------------------------

typedef struct ncName {
   int type;
   char* value;
} NCNAME, *NCNAME_PTR;

typedef struct ncList {
   int numOfNames;
   NCNAME_PTR nameList;
} NCNAME_LIST, *NCNAME_LIST_PTR;

#define TYPE_rfc822Name                     0
#define TYPE_dNSName                        1
#define TYPE_directoryname                  2
#define TYPE_ediPartyName                   3
#define TYPE_uniformResourceIdentifier      4
#define TYPE_iPAddress                      5
#define TYPE_registeredID                   6

typedef struct certInfo {
   char*                distinguishedName;
   int                  serialNumber;
   asn_x500name         name;
   x509_certificate     certificate;
   CertificateList      crl;
   int                  modulusBits;
   CSSM_KEY             cssmPublicKey;
   CSSM_KEY             cssmPrivateKey;
   bool                 revokeMe;
   ncList               altNamesList;
   ncList               permittedList;
   ncList               excludedList;
} CERTINFO, *CERTINFO_PTR;

typedef struct UnitTestInfo {
   char* dbName;
   char* authName;
   char* authPassword;
   CSSM_ALGORITHMS cssmKeyPairType;
   CSSM_ALGORITHMS cssmSignatureAlgorithmID;
   CSSM_GUID tpVerify;
   CSSM_GUID cspSign;
   CSSM_GUID cspVerify;
   CSSM_GUID dlDirectory;
   int modulusBitsCA;
   int modulusBitsUser;
   int numOfSubjects;
   CERTINFO subjectInfo[8];
   int numOfIssuers;
   CERTINFO issuerInfo[1];
} UNITTESTINFO, *UNITTESTINFO_PTR;





   //------------------------------------------------------------
   // List of CSP GUIDs to run each test case against
   //
   // NOTE: Purpose of two CSP is to allow, for example, Cylink 
   // to generate the certificate and BSAFE to verify it.  The unit 
   // test tries all four combinations.  This provides an important 
   // self-interoperability test.  Try to generate both DSA and RSA 
   // key types and four signature algorithm types.  We expect 
   // failures when Cylink is asked to do RSA operations
   // Critical test is whether Cylink certs can be verified by BSAFE
   // and visa versa.
   //------------------------------------------------------------

//------------------------------------------------------------
// functions
//------------------------------------------------------------

int 
unit_test(UnitTestInfo& unitTestInfo);

uint32 
Test_TP(CSSM_DL_DB_HANDLE dataSource,         
        CSSM_CSP_HANDLE cspVerify,
        CSSM_TP_HANDLE tpVerify,
        CERTINFO& issuer, 
        CERTINFO& subject);

void 
report_unit_test(int rc, int& passed, int& failed, const char* testName);

// create certificate and CRL test functions

void 
CreateIssuerTBSCertificate(asn_x500name& issuer,
                           long serialNumber,
                           ncList& permittedList,
                           ncList& excludedList,
                           ncList& subjectAltNamesList,
                           x509_certificate& certToCreate,
                           CSSM_ALGORITHMS cssmSignatureAlgorithmID,
                           CSSM_KEY& cssmPublicKey,
                           CSSM_HANDLE cspHandle);

void 
CreateSubjectTBSCertificate(asn_x500name& issuer,
                            asn_x500name& subject,
                            long serialNumber,
                            ncList& subjectAltNamesList,
                            ncList& issuerAltNamesList,
                            x509_certificate& certToCreate,
                            x509_certificate& issuerCertificate,
                            CSSM_ALGORITHMS cssmSignatureAlgorithmID,
                            CSSM_KEY& cssmPublicKey,
                            CSSM_HANDLE cspHandle);

void 
CreateIssuerTBSCRL(CertificateList& crl,
                   x509_certificate& issuer);

void 
AddEntryToCRL(CertificateList& crl,
              const x509_certificate& revokee);
         
// TP support test functions

void 
Populate_CSSM_TP_Structures(x509_certificate* certChain[],
                            CSSM_CERTGROUP_PTR& certToBeVerifiedPtr, 
                            uint32 numberofCertsToBeVerified,
                            CSSM_DATA_PTR& anchorCertsPtr, 
                            uint32 numberofAnchorCerts);
                           
void 
Cleanup_CSSM_TP_Structures(CSSM_CERTGROUP_PTR certToBeVerifiedPtr,
                           uint32 numberofCertsToBeVerified,
                           CSSM_DATA_PTR anchorCertsPtr,
                           uint32 numberofAnchorCerts);

uint32 
TestTPCertGroupVerify(CSSM_DL_DB_HANDLE dataSource,
                      CSSM_CSP_HANDLE cspHandle, 
                      CSSM_TP_HANDLE  tpVerify,
                      CSSM_CERTGROUP_PTR certToBeVerifiedPtr, 
                      CSSM_DATA_PTR anchorCerts, 
                      uint32 numberofAnchorCerts);

// Create extensions test functions

void 
addExtension(x509_Extension* child, 
             bool isCritical,
             unsigned long oidArray[],
             int oidArrayLength,
             asn_object& object);

uint32
CreateCRLNumber(x509_Extensions& extensions, int number);

uint32
CreateAuthorityKeyIdentifier(x509_certificate& issuer,
                             x509_Extensions& extensions);

uint32
CreateBasicConstraints(x509_Extensions& extensions,
                       bool isCA,
                       int pathLenConstraint);

uint32
CreateKeyUsage(x509_Extensions& extensions, long mask);

uint32
CreateSubjectKeyIdentifier(x509_Extensions& extensions,
                           asn_object& objectToHash,
                           CSSM_CSP_HANDLE cspHandle);

uint32
CreateIssuerAlternativeNames(x509_Extensions& extensions,
                             ncList& altNamesList);

uint32
CreateSubjectAlternativeNames(x509_Extensions& extensions,
                              ncList& altNamesList);

uint32
PopulateAlternativeNames(XsubjectAltName& alternativeNames,
                         ncList& altNamesList);

uint32
CreateNameConstraints(x509_Extensions& extensions,
                      ncList& permittedList,
                      ncList& excludedList);

uint32
PopulateNameConstraints(XNameConstraints& nameConstraints,
                        ncList& permittedList,
                        ncList& excludedList);

// CSSM memory callbacks

void* 
DefaultMalloc(uint32 size, void* allocRef = 0);

void  
DefaultFree(void* memPtr, void* allocRef = 0);

void* 
DefaultRealloc(void* memPtr, uint32 size, void* allocRef = 0);

void* 
DefaultCalloc(uint32 num, uint32 size, void* allocRef = 0);

// miscellaneous

void 
ThrowCSSMException(uint32 errcode);

void 
SetDateRange(Time& thisUpdate, Time& nextUpdate);

void 
DeallocateMemory(UnitTestInfo& unitTestInfo);

void 
SaveEncodingInfoToFile(CERTINFO_PTR issuerInfo, 
                       int numOfElements, 
                       const char* prefix);

uint32
WriteEncodingToFile(buffer_t& encoding, const char* fileName);

uint32
WriteEncodingToFile(CSSM_DATA& encoding, const char* fileName);

uint32
WriteEncodingToFile(asn_object& reference, const char* fileName);

//------------------------------------------------------------
// function: main
//------------------------------------------------------------

int
main(int argc, char* argv[])
{

#ifdef WIN32
  /* 
   * Set the debug-heap flag to keep freed blocks in the
   * heap's linked list - This will allow us to catch any
   * inadvertent use of freed memory
   */
   int tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
   tmpDbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF;
   tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF;
   _CrtSetDbgFlag(tmpDbgFlag);
#endif

   int rc = EXIT_SUCCESS;
   int i, j, k, l, m, n;
   int passed = 0;
   int failed = 0;

   UnitTestInfo unitTestInfo;

   //------------------------------------------------------------
   // Initialize INI library
   //------------------------------------------------------------

   if (IniInitialize() != true)
      return EXIT_FAILURE;

   //------------------------------------------------------------
   // Initialize CA certificate information
   //------------------------------------------------------------

   long serialNumberCounter = 1;

   struct {
      char* distinguishedName;
      ncName permittedName[6];
      ncName excludedName[2];
      ncName altName[2];
   } issuerInfo[1] = 
   {
      "C=US/O=IBM/OU=Jonah", 
      {{TYPE_rfc822Name, "ibm.com"},{TYPE_directoryname,"C=US/O=IBM"},
       {TYPE_rfc822Name, "iris.com"},{TYPE_directoryname,"C=US/O=Iris"},
       {TYPE_rfc822Name, "lotus.com"},{TYPE_directoryname,"C=US/O=Lotus"}},
      {{TYPE_rfc822Name, "advantis.com"},{TYPE_directoryname,"C=US/O=IBM/OU=Advantis"}},
      {{TYPE_rfc822Name, "bigdaddy.ibm.com"},{TYPE_directoryname,"C=US/O=IBM/CN=Big Daddy"}}
   };

   unitTestInfo.numOfIssuers = 1;
   for (i = 0; i < unitTestInfo.numOfIssuers; i++)
   {
      unitTestInfo.issuerInfo[i].distinguishedName        = issuerInfo[i].distinguishedName;
      unitTestInfo.issuerInfo[i].revokeMe                 = false;
      unitTestInfo.issuerInfo[i].permittedList.numOfNames = 6;
      unitTestInfo.issuerInfo[i].permittedList.nameList   = issuerInfo[i].permittedName;
      unitTestInfo.issuerInfo[i].excludedList.numOfNames  = 2;
      unitTestInfo.issuerInfo[i].excludedList.nameList    = issuerInfo[i].excludedName;
      unitTestInfo.issuerInfo[i].altNamesList.numOfNames  = 2;
      unitTestInfo.issuerInfo[i].altNamesList.nameList    = issuerInfo[i].altName;
      unitTestInfo.issuerInfo[i].serialNumber             = serialNumberCounter++;
      if (unitTestInfo.issuerInfo[i].name.set_value_C(unitTestInfo.issuerInfo[i].distinguishedName) != 0)
         return EXIT_FAILURE;
   }
  
   //------------------------------------------------------------
   // Initialize end-entity certificates information
   //------------------------------------------------------------

   struct {
      bool expectedFailure;  // set if we are forcing a NC violation
      char* distinguishedName;
      ncName altName;
   } subjectInfo[8] = 
   {
      false, "C=US/O=IBM/OU=Jonah/CN=Ian Morrison",     {TYPE_rfc822Name, "codeboy.ibm.com"},
      false, "C=US/O=IBM/OU=Jonah/CN=John Wray",        {TYPE_rfc822Name, "jwray.iris.com"},
      false, "C=US/O=IBM/OU=Jonah/CN=Mary Ellen Zurko", {TYPE_rfc822Name, "zurko.iris.com"},
      false, "C=US/O=IBM/OU=Jonah/CN=Mike Shanzer",     {TYPE_rfc822Name, "shanzer.iris.com"},
      false, "C=US/O=IBM/OU=Jonah/CN=Warren Macek",     {TYPE_rfc822Name, "macek.iris.com"},
      false, "C=US/O=IBM/OU=Jonah/CN=Mike Crane",       {TYPE_rfc822Name, "mcrane.ibm.com"},
      false, "C=US/O=IBM/OU=Jonah/CN=Stacy Powers",     {TYPE_rfc822Name, "powers.ibm.com"},
      false, "C=US/O=IBM/OU=Jonah/CN=Ellen McDermott",  {TYPE_rfc822Name, "mcdermott.lotus.com"}
   };

   bool revokeMe = false;

   unitTestInfo.numOfSubjects = 8;
   for (i = 0; i < unitTestInfo.numOfSubjects; i++)
   {
      unitTestInfo.subjectInfo[i].distinguishedName        = subjectInfo[i].distinguishedName;
      unitTestInfo.subjectInfo[i].revokeMe                 = revokeMe;
      unitTestInfo.subjectInfo[i].permittedList.numOfNames = 0;
      unitTestInfo.subjectInfo[i].excludedList.numOfNames  = 0;
      unitTestInfo.subjectInfo[i].altNamesList.numOfNames  = 1;
      unitTestInfo.subjectInfo[i].altNamesList.nameList    = &(subjectInfo[i].altName);
      unitTestInfo.subjectInfo[i].serialNumber             = serialNumberCounter++;
      if (unitTestInfo.subjectInfo[i].name.set_value_C(unitTestInfo.subjectInfo[i].distinguishedName) != 0)
         return EXIT_FAILURE;
      revokeMe = !revokeMe;
   }

   //------------------------------------------------------------
   // Initialize CSP GUIDs
   //------------------------------------------------------------

   struct csps {
      CSSM_GUID  guid;
      char*       name;
   } cspInfo[2];
   
   cspInfo[0].guid = * JKL_Get_CylinkCsp_GUID();
   cspInfo[0].name = "Cylink CSP";
   cspInfo[1].guid = * JKL_Get_SwCsp_GUID();
   cspInfo[1].name = "BSAFE CSP";

   //------------------------------------------------------------
   // Initialize TP GUID
   //------------------------------------------------------------

   CSSM_GUID tpGuid = * JKL_Get_JonahTp_GUID();

   //------------------------------------------------------------
   // Initialize key sizes
   //------------------------------------------------------------

   int modulusBits[] = {512};

   //------------------------------------------------------------
   // Initialize key pair algorithms 
   //------------------------------------------------------------

   CSSM_ALGORITHMS keyPairType[] = { CSSM_ALGID_DSA, CSSM_ALGID_RSA };

   //------------------------------------------------------------
   // Initialize signature algorithms (# vary by key pair type)
   //------------------------------------------------------------

   int numOfSigAlgs[] = { 1, 3 };

   CSSM_ALGORITHMS sigAlgs[2][3] = 
   { 
      { CSSM_ALGID_SHA1WithDSA, CSSM_ALGID_NONE, CSSM_ALGID_NONE },
      { CSSM_ALGID_MD2WithRSA, CSSM_ALGID_MD5WithRSA, CSSM_ALGID_SHA1WithRSA } 
   };

   char* sigAlgsString[2][3] = 
   { 
      { "CSSM_ALGID_SHA1WithDSA", 0, 0 },
      { "CSSM_ALGID_MD2WithRSA", "CSSM_ALGID_MD5WithRSA", "CSSM_ALGID_SHA1WithRSA" } 
   };
   
   //------------------------------------------------------------
   // iterate CSP used to create certificates
   //------------------------------------------------------------
// for (i = 0; i < 2; i++) 
   for (i = 0; i < 1; i++) 
   {  
      //------------------------------------------------------------
      // iterate CSP used to verify certificate
      //------------------------------------------------------------
//    for (j = 0; j < 2; j++)   
      for (j = 0; j < 1; j++)   
      {  
         //------------------------------------------------------------
         // iterate CA modulus bits
         //------------------------------------------------------------
//       for (k = 0; k < sizeof(modulusBits)/sizeof(int); k++)   
         for (k = 0; k < 1; k++)   
         {  
            //------------------------------------------------------------
            // iterate end-entity modulus bits
            //------------------------------------------------------------
//          for (l = 0; l < sizeof(modulusBits)/sizeof(int); l++)
            for (l = 0; l < 1; l++)
            {  
               //------------------------------------------------------------
               // iterate key pair type
               //------------------------------------------------------------
//             for (m = 0; m < sizeof(keyPairType)/sizeof(CSSM_ALGORITHMS); m++)
               for (m = 0; m < 1; m++)
               { 
                  cout << "\n---------------------------------" << endl;
                  cout << "Signing with:   " << cspInfo[i].name << endl;
                  cout << "Verifying with: " << cspInfo[j].name << endl;
                  cout << "CA cert key size = "     << modulusBits[k];
                  cout << "; user cert key size = " << modulusBits[l];
                  cout << endl;
                  cout << endl;

                  //------------------------------------------------------------
                  // setup unit test information
                  //------------------------------------------------------------

                  unitTestInfo.dbName          = "garner.raleigh.ibm.com:389";
                  unitTestInfo.authName        = "cn=admin, o=IBM, c=US";
                  unitTestInfo.authPassword    = "npss";
                  unitTestInfo.tpVerify        = tpGuid;
                  unitTestInfo.cspSign         = cspInfo[i].guid; 
                  unitTestInfo.cspVerify       = cspInfo[j].guid;
                  unitTestInfo.dlDirectory     = LDAPDL_GUID;
                  unitTestInfo.cssmKeyPairType = keyPairType[m];

                  // define modulus bits for each CA certificate

                  for (n = 0; n < unitTestInfo.numOfIssuers; n++)
                  {
                     unitTestInfo.issuerInfo[n].modulusBits = modulusBits[k];
                  }
               
                  // define modulus bits for each end-entity certificate
               
                  for (n = 0; n < unitTestInfo.numOfSubjects; n++)
                  {
                     unitTestInfo.subjectInfo[n].modulusBits = modulusBits[l];
                  }

                  // iterate over possible signature algorithms for key pair type

                  for (n = 0; n < numOfSigAlgs[m]; n++)
                  {
                     unitTestInfo.cssmSignatureAlgorithmID = sigAlgs[m][n];
                     rc = unit_test(unitTestInfo);
                     report_unit_test(rc, passed, failed, sigAlgsString[m][n]);
                  }
               }
            }
         }
      }
   }

   cout << "Summary: " << endl;
   cout << "Total # Passed: " << passed << endl;
   cout << "Total # Failed: " << failed << endl;

   return rc;
}


//------------------------------------------------------------
// function: report_unit_test
//------------------------------------------------------------

void
report_unit_test(int rc, 
                 int& passed, 
                 int& failed,
                 const char* testName)
{
   if (rc == EXIT_SUCCESS) 
   {
      passed++;
      cout << "PASSED: ";
   }
   else
   {
      failed++;
      cout << "FAILED: ";
   }
   cout << testName << endl << endl;
}


//------------------------------------------------------------
// function: unit_test
//------------------------------------------------------------

int
unit_test(UnitTestInfo& unitTestInfo)
{
   int rc = EXIT_SUCCESS;
   uint32 status = 0;

   int k;

   CSSM_CSP_HANDLE   cspSign;
   CSSM_CSP_HANDLE   cspVerify;
   CSSM_TP_HANDLE    tpVerify;
   CSSM_DL_DB_HANDLE dataSource  = { 0, 0 };
   CSSM_MEMORY_FUNCS memoryFuncs = { DefaultMalloc, DefaultFree, DefaultRealloc, DefaultCalloc, 0 };

   try
   {
      //------------------------------------------------------------
      // Attach to service providers (TP, DL, CSP)
      //------------------------------------------------------------

      cout << "*** Connecting to Cryptographic Service Provider(s)..." << endl << endl;

      if ((status = JKL_InitializeCSSM(memoryFuncs)) != 0)
         return status;
      if ((status = JKL_AttachCSP(unitTestInfo.cspSign, cspSign)) != 0)
         return status;
      if ((status = JKL_AttachCSP(unitTestInfo.cspVerify, cspVerify)) != 0)
         return status;

      cout << "*** Connecting to Trust Policy..." << endl << endl;

      if ((status = JKL_AttachTP(unitTestInfo.tpVerify, tpVerify)) != 0)
         return status;

      cout << "*** Connecting to Directory Services..." << endl << endl;

      if ((status = JDL_Initialize(memoryFuncs)) != 0)
         return status;
      if ((status = JDL_Attach(unitTestInfo.dlDirectory, dataSource.DLHandle)) != 0)
         return status;
      if ((status = JDL_OpenDatabase(unitTestInfo.dbName, 
                                     unitTestInfo.authName, 
                                     unitTestInfo.authPassword, 
                                     dataSource)) != 0)
         return status;

      //------------------------------------------------------------
      // Generate key pairs for issuer and end-entities
      //------------------------------------------------------------
   
      cout << "*** Generating Key Pairs..." << endl;

      cout << "\t" << unitTestInfo.issuerInfo[0].distinguishedName << endl;
      if ((status = JKL_GenerateKeyPair(cspSign,
                                        unitTestInfo.cssmKeyPairType,
                                        unitTestInfo.issuerInfo[0].modulusBits,
                                        unitTestInfo.issuerInfo[0].cssmPublicKey,
                                        unitTestInfo.issuerInfo[0].cssmPrivateKey)) != 0)
         return status;

      for (k = 0; k < unitTestInfo.numOfSubjects; k++)
      {
         cout << "\t" << unitTestInfo.subjectInfo[k].distinguishedName << endl;
         if ((status = JKL_GenerateKeyPair(cspSign,
                                           unitTestInfo.cssmKeyPairType,
                                           unitTestInfo.subjectInfo[k].modulusBits,
                                           unitTestInfo.subjectInfo[k].cssmPublicKey,
                                           unitTestInfo.subjectInfo[k].cssmPrivateKey)) != 0)
            return status;
      }
      cout << endl << endl;

      //------------------------------------------------------------
      // Create sample CA and several end-entity certificates
      //------------------------------------------------------------

      cout << "*** Creating Certificates..." << endl;

      cout << "\t" << unitTestInfo.issuerInfo[0].distinguishedName << endl;
      CreateIssuerTBSCertificate(unitTestInfo.issuerInfo[0].name,
                                 unitTestInfo.issuerInfo[0].serialNumber,
                                 unitTestInfo.issuerInfo[0].permittedList,
                                 unitTestInfo.issuerInfo[0].excludedList,
                                 unitTestInfo.issuerInfo[0].altNamesList,
                                 unitTestInfo.issuerInfo[0].certificate,
                                 unitTestInfo.cssmSignatureAlgorithmID,
                                 unitTestInfo.issuerInfo[0].cssmPublicKey,
                                 cspSign);

      for (k = 0; k < unitTestInfo.numOfSubjects; k++)
      {
         cout << "\t" << unitTestInfo.subjectInfo[k].distinguishedName << endl;
         CreateSubjectTBSCertificate(unitTestInfo.issuerInfo[0].name,
                                     unitTestInfo.subjectInfo[k].name,
                                     unitTestInfo.subjectInfo[k].serialNumber,
                                     unitTestInfo.subjectInfo[k].altNamesList,
                                     unitTestInfo.issuerInfo[0].altNamesList,
                                     unitTestInfo.subjectInfo[k].certificate, 
                                     unitTestInfo.issuerInfo[0].certificate,
                                     unitTestInfo.cssmSignatureAlgorithmID,
                                     unitTestInfo.subjectInfo[k].cssmPublicKey,
                                     cspSign);
      }
      cout << endl << endl;

      //------------------------------------------------------------
      // Sign certificates with issuer's private key
      //------------------------------------------------------------

      cout << "*** Signing Certificates..." << endl;

      cout << "\t" << unitTestInfo.issuerInfo[0].distinguishedName << endl;
      if ((status = JKL_SignCertificate(unitTestInfo.issuerInfo[0].certificate, 
                                        cspSign, 
                                        unitTestInfo.cssmSignatureAlgorithmID,
                                        unitTestInfo.issuerInfo[0].cssmPrivateKey)) != 0)
         ThrowCSSMException(status);

      for (k = 0; k < unitTestInfo.numOfSubjects; k++)
      {
         cout << "\t" << unitTestInfo.subjectInfo[k].distinguishedName << endl;
         if ((status = JKL_SignCertificate(unitTestInfo.subjectInfo[k].certificate, 
                                           cspSign, 
                                           unitTestInfo.cssmSignatureAlgorithmID,
                                           unitTestInfo.issuerInfo[0].cssmPrivateKey)) != 0)
            ThrowCSSMException(status);
      }
      cout << endl << endl;

      //------------------------------------------------------------
      // Create a CRL issued by issuer certificate
      //------------------------------------------------------------

      cout << "*** Create CRL..." << endl;

      CreateIssuerTBSCRL(unitTestInfo.issuerInfo[0].crl, 
                         unitTestInfo.issuerInfo[0].certificate);

      for (k = 0; k < unitTestInfo.numOfSubjects; k++)
      {
         if (unitTestInfo.subjectInfo[k].revokeMe) 
         {
            cout << "\tRevoking: " << unitTestInfo.subjectInfo[k].distinguishedName << endl;
            AddEntryToCRL(unitTestInfo.issuerInfo[0].crl, 
                          unitTestInfo.subjectInfo[k].certificate);
         }
      }
      cout << endl << endl;

      //------------------------------------------------------------
      // Sign CRL with issuer's private key
      //------------------------------------------------------------

      cout << "*** Signing CRL..." << endl;

      if ((status = JKL_SignCRL(unitTestInfo.issuerInfo[0].crl, 
                                cspSign, 
                                unitTestInfo.cssmSignatureAlgorithmID,
                                unitTestInfo.issuerInfo[0].cssmPrivateKey)) != 0)
         ThrowCSSMException(status);
      cout << endl << endl;

      //------------------------------------------------------------
      // Save encodings for later examination
      //------------------------------------------------------------

      SaveEncodingInfoToFile(unitTestInfo.subjectInfo, unitTestInfo.numOfSubjects, "sub");
      SaveEncodingInfoToFile(unitTestInfo.issuerInfo, unitTestInfo.numOfIssuers, "ca");

      //------------------------------------------------------------
      // Publish certificates and CRLs to directory:
      //------------------------------------------------------------

      cout << "*** Publishing Certificates and CRL to Directory..." << endl;

      cout << "\tCRL: " << unitTestInfo.issuerInfo[0].distinguishedName << endl;
      if ((status = JDL_PublishAttribute(dataSource,
                                         unitTestInfo.issuerInfo[0].crl.tbsCertList.issuer, 
                                         unitTestInfo.issuerInfo[0].crl,
                                         JDL_ATTRIBUTE_CRL,
                                         JDL_REPLACE)) != 0)
            ThrowCSSMException(status);

      cout << "\tcACertificate: " << unitTestInfo.issuerInfo[0].distinguishedName << endl;
      if ((status = JDL_PublishAttribute(dataSource,
                                         unitTestInfo.issuerInfo[0].certificate.tbsCertificate.subject, 
                                         unitTestInfo.issuerInfo[0].certificate,
                                         JDL_ATTRIBUTE_CA_CERT,
                                         JDL_REPLACE)) != 0)
            ThrowCSSMException(status);

      for (k = 0; k < unitTestInfo.numOfSubjects; k++)
      {
         cout << "\tuserCertificate: " << unitTestInfo.subjectInfo[k].distinguishedName << endl;
         if ((status = JDL_PublishAttribute(dataSource,
                                            unitTestInfo.subjectInfo[k].certificate.tbsCertificate.subject, 
                                            unitTestInfo.subjectInfo[k].certificate,
                                            JDL_ATTRIBUTE_USER_CERT,
                                            JDL_REPLACE)) != 0)
            ThrowCSSMException(status);
      }
      cout << endl << endl;

      //------------------------------------------------------------
      // Test TP with issuer and each subject in turn
      //------------------------------------------------------------

      cout << "*** Testing TPCertGroupVerify for each chain..." << endl;

      for (int k = 0; k < unitTestInfo.numOfSubjects; k++)
      {
         cout << "Chain: " << endl;
         cout << "Issuer:  " << unitTestInfo.issuerInfo[0].distinguishedName << endl;
         cout << "Subject: " << unitTestInfo.subjectInfo[k].distinguishedName << endl;
         status = Test_TP(dataSource, 
                          cspVerify, 
                          tpVerify,
                          unitTestInfo.issuerInfo[0], 
                          unitTestInfo.subjectInfo[k]);
         if (status != 0)
         {
            if (unitTestInfo.subjectInfo[k].revokeMe == false)
            {
               ThrowCSSMException(status);
            }
            else
            {
               cout << "  Revoked certificate correctly rejected by TP: " << status;
            }
         }
         else
         {
            cout << "  Certificate verified by TP";
         }
         cout << endl << endl;
      }
   }
   catch (const char* msg)
   {
      cerr << "EXCEPTION: " << msg << endl;
      rc = EXIT_FAILURE;
   }
   catch (...)
   {
      cerr << "EXCEPTION: NO_MSG" << endl;
      rc = EXIT_FAILURE;
   }

   //------------------------------------------------------------
   // Cleanup memory allocated by CDSA framework
   //------------------------------------------------------------

   DeallocateMemory(unitTestInfo);

   //------------------------------------------------------------
   // Detach from CDSA framework
   //------------------------------------------------------------

   if ((status = JKL_ModuleDetach(cspSign)) != 0)
      return status;
   if ((status = JKL_ModuleDetach(cspVerify)) != 0)
      return status;
   if ((status = JKL_ModuleDetach(tpVerify)) != 0)
      return status;
   if ((status = JDL_CloseDatabase(dataSource)) != 0)
      return status;
   if ((status = JKL_ModuleDetach(dataSource.DLHandle)) != 0)
      return status;
   
   return rc;
}


//------------------------------------------------------------
// function: CreateIssuerTBSCRL
//------------------------------------------------------------

void 
CreateIssuerTBSCRL(CertificateList& crlToCreate,
                   x509_certificate& issuer)
{
   uint32 status = 0;
   buffer_t encoding;

   // Set CRL version

   if ((status = crlToCreate.tbsCertList.version.set_value(1)) != 0)
      ThrowCSSMException(status);

   // Copy signature field from issuer certificate

   encoding.clear();
   if ((status = issuer.tbsCertificate.signature.write(encoding)) != 0)
      ThrowCSSMException(status);
   if ((status = crlToCreate.tbsCertList.signature.read(encoding)) != 0)
      ThrowCSSMException(status);

   // Copy issuer name from issuer certificate

   encoding.clear();
   if ((status = issuer.tbsCertificate.subject.write(encoding)) != 0)
      ThrowCSSMException(status);
   if ((status = crlToCreate.tbsCertList.issuer.read(encoding)) != 0)
      ThrowCSSMException(status);

   // Fill in thisUpdate/nextUpdate fields

   SetDateRange(crlToCreate.tbsCertList.thisUpdate, 
                crlToCreate.tbsCertList.nextUpdate);

   //------------------------------------------------------------
   // Create CRL extension: authority key identifier
   //------------------------------------------------------------

   if ((status = CreateAuthorityKeyIdentifier(issuer,
                                              crlToCreate.tbsCertList.crlExtensions.value)) != 0)
      ThrowCSSMException(status);

   //------------------------------------------------------------
   // Create CRL extension: CRL Number
   //------------------------------------------------------------

   if ((status = CreateCRLNumber(crlToCreate.tbsCertList.crlExtensions.value, 1)) != 0)
      ThrowCSSMException(status);
}


//------------------------------------------------------------
// function: SetDateRange
//------------------------------------------------------------

void
SetDateRange(Time& thisUpdate, Time& nextUpdate)
{
   uint32 status = 0;

   unsigned year    = 1998;
   unsigned month   = 1;
   unsigned day     = 1;
   unsigned hour    = 1;
   unsigned minute  = 1;
   unsigned second  = 1;
   
   if ((status = thisUpdate.select(0)) != 0)
      ThrowCSSMException(status);
   if ((status = thisUpdate.utcTime.set_value(year-1,month,day,hour,minute,second,0,0)) != 0)
      ThrowCSSMException(status);
   if ((status = nextUpdate.select(0)) != 0)
      ThrowCSSMException(status);
   if ((status = nextUpdate.utcTime.set_value(year+1,month,day,hour,minute,second,0,0)) != 0)
      ThrowCSSMException(status);
}


//------------------------------------------------------------
// function: CreateIssuerTBSCertificate
//------------------------------------------------------------

void 
CreateIssuerTBSCertificate(asn_x500name& issuer,
                           long serialNumber,
                           ncList& permittedList,
                           ncList& excludedList,
                           ncList& subjectAltNamesList,
                           x509_certificate& certToCreate,
                           CSSM_ALGORITHMS cssmSignatureAlgorithmID,
                           CSSM_KEY& cssmPublicKey,
                           CSSM_HANDLE cspHandle)
{
   uint32 status = 0;
   
   //------------------------------------------------------------
   // Fill in algorithm specific TBS fields
   //------------------------------------------------------------

   if ((status = JKL_cssm_to_asn(cssmSignatureAlgorithmID,
                                 certToCreate.tbsCertificate.signature)) != 0)
      ThrowCSSMException(status);

   if ((status = JKL_cssm_to_asn(cssmPublicKey,
                                 certToCreate.tbsCertificate.subjectPublicKeyInfo)) != 0)
      ThrowCSSMException(status);

   //------------------------------------------------------------
   // Fill in remaining TBS fields; caller will sign certificate
   //------------------------------------------------------------

   if ((status = certToCreate.tbsCertificate.version.set_value(X509_V3)) != 0)
      ThrowCSSMException(status);
   if ((status = certToCreate.tbsCertificate.serialNumber.set_value(serialNumber)) != 0)
      ThrowCSSMException(status);

   // extract encoding of issuer name

   buffer_t issuerEncoding;
   if ((status = issuer.write(issuerEncoding)) != 0)
      ThrowCSSMException(status);

   if ((status = certToCreate.tbsCertificate.issuer.read(issuerEncoding)) != 0)
      ThrowCSSMException(status);

   // Fill in thisUpdate/nextUpdate fields

   SetDateRange(certToCreate.tbsCertificate.validity.notBefore, 
                certToCreate.tbsCertificate.validity.notAfter);

   buffer_t issuerEncoding2;
   if ((status = issuer.write(issuerEncoding2)) != 0)
      ThrowCSSMException(status);

   if ((status = certToCreate.tbsCertificate.subject.read(issuerEncoding2)) != 0)
      ThrowCSSMException(status);

   //------------------------------------------------------------
   // Create extension: basic constraints
   //------------------------------------------------------------

   if ((status = CreateBasicConstraints(certToCreate.tbsCertificate.extensions.value, 
                                        true, 
                                        1)) != 0)
      ThrowCSSMException(status);

   //------------------------------------------------------------
   // Create extension: key usage
   //------------------------------------------------------------

   long mask = USAGE_keyCertSign | USAGE_cRLSign;
   if ((status = CreateKeyUsage(certToCreate.tbsCertificate.extensions.value, mask)) != 0)
      ThrowCSSMException(status);

   //------------------------------------------------------------
   // Create extension: subject key identifier
   //------------------------------------------------------------

   if ((status = CreateSubjectKeyIdentifier(certToCreate.tbsCertificate.extensions.value,
                                            certToCreate.tbsCertificate.subjectPublicKeyInfo,
                                            cspHandle)) != 0)
      ThrowCSSMException(status);

   //------------------------------------------------------------
   // Create extension: name constraint
   //------------------------------------------------------------

   if ((status = CreateNameConstraints(certToCreate.tbsCertificate.extensions.value, 
                                       permittedList, 
                                       excludedList)) != 0)
      ThrowCSSMException(status);

   //------------------------------------------------------------
   // Create extension: subject alt name
   //------------------------------------------------------------

   if ((status = CreateSubjectAlternativeNames(certToCreate.tbsCertificate.extensions.value,
                                               subjectAltNamesList)) != 0)
      ThrowCSSMException(status);
}


//------------------------------------------------------------
// function: CreateSubjectTBSCertificate
//------------------------------------------------------------

void 
CreateSubjectTBSCertificate(asn_x500name& issuer,
                            asn_x500name& subject,
                            long serialNumber,
                            ncList& subjectAltNamesList,
                            ncList& issuerAltNamesList,
                            x509_certificate& certToCreate,
                            x509_certificate& issuerCertificate,
                            CSSM_ALGORITHMS cssmSignatureAlgorithmID,
                            CSSM_KEY& cssmPublicKey,
                            CSSM_HANDLE cspHandle)
{ 
   uint32 status = 0;
   buffer_t encoding;
   
   //------------------------------------------------------------
   // Fill in algorithm specific TBS fields
   //------------------------------------------------------------

   if ((status = JKL_cssm_to_asn(cssmSignatureAlgorithmID,
                                 certToCreate.tbsCertificate.signature)) != 0)
      ThrowCSSMException(status);

   if ((status = JKL_cssm_to_asn(cssmPublicKey,
                                 certToCreate.tbsCertificate.subjectPublicKeyInfo)) != 0)
      ThrowCSSMException(status);

   //------------------------------------------------------------
   // Fill in remaining TBS fields; caller will sign certificate
   //------------------------------------------------------------

   if ((status = certToCreate.tbsCertificate.version.set_value(X509_V3)) != 0)
      ThrowCSSMException(status);
   if ((status = certToCreate.tbsCertificate.serialNumber.set_value(serialNumber)) != 0)
      ThrowCSSMException(status);

   // extract encoding of issuer name

   encoding.clear();
   if ((status = issuer.write(encoding)) != 0)
      ThrowCSSMException(status);

   if ((status = certToCreate.tbsCertificate.issuer.read(encoding)) != 0)
      ThrowCSSMException(status);

   // Fill in thisUpdate/nextUpdate fields

   SetDateRange(certToCreate.tbsCertificate.validity.notBefore, 
                certToCreate.tbsCertificate.validity.notAfter);

   // extract encoding of issuer name

   encoding.clear();
   if ((status = subject.write(encoding)) != 0)
      ThrowCSSMException(status);

   if ((status = certToCreate.tbsCertificate.subject.read(encoding)) != 0)
      ThrowCSSMException(status);

   //------------------------------------------------------------
   // Create extension: authority key identifier
   //------------------------------------------------------------

   if ((status = CreateAuthorityKeyIdentifier(issuerCertificate,
                                              certToCreate.tbsCertificate.extensions.value)) != 0)
      ThrowCSSMException(status);

   //------------------------------------------------------------
   // Create extension: subject key identifier
   //------------------------------------------------------------

   if ((status = CreateSubjectKeyIdentifier(certToCreate.tbsCertificate.extensions.value,
                                            certToCreate.tbsCertificate.subjectPublicKeyInfo,
                                            cspHandle)) != 0)
      ThrowCSSMException(status);

   //------------------------------------------------------------
   // Create extension: issuer alt name
   //------------------------------------------------------------

   if ((status = CreateIssuerAlternativeNames(certToCreate.tbsCertificate.extensions.value,
                                              issuerAltNamesList)) != 0)
      ThrowCSSMException(status);

   //------------------------------------------------------------
   // Create extension: subject alt name
   //------------------------------------------------------------

   if ((status = CreateSubjectAlternativeNames(certToCreate.tbsCertificate.extensions.value,
                                               subjectAltNamesList)) != 0)
      ThrowCSSMException(status);
}


//------------------------------------------------------------
// function: CreateAuthorityKeyIdentifier
//------------------------------------------------------------

uint32
CreateAuthorityKeyIdentifier(x509_certificate& issuer,
                             x509_Extensions& extensions)
{
   uint32 status = 0;

   // find issuer's subject key identifier extension:
   
   int children = issuer.tbsCertificate.extensions.value.get_child_count();
   for (int i = 0; i < children ; i++)
   {
      x509_Extension* child = issuer.tbsCertificate.extensions.value[i];
      if (child->extnID.is_equal(SubjectKeyIdentifier_val, 4))
      {
         buffer_t encoding;

         // (1) extract SubjectKeyIdentifier of issuer

         encoding.clear();
         if ((status = child->extnValue.get_value(encoding.data, encoding.data_len)) != 0)
            return status;

         // (2) create matching AuthorityKeyIdentifier

         XAuthorityKeyIdentifier authorityKeyIdentifier;
         if ((status = authorityKeyIdentifier.keyIdentifier.set_value(encoding.data, encoding.data_len)) != 0)
            return status;

         // (3) populate CRL with AKID extension

         child = extensions.add_child();
         addExtension(child, false, AuthorityKeyIdentifier_val, 4, authorityKeyIdentifier);

         break;
      }
   }

   return status;
}


//------------------------------------------------------------
// function: CreateCRLNumber
//------------------------------------------------------------

uint32
CreateCRLNumber(x509_Extensions& extensions, int number)
{
   uint32 status = 0;

   XCRLNumber crlNumber;
   crlNumber.set_value(number);

   addExtension(extensions.add_child(), false, CRLNumber_val, 4, crlNumber);

   return status;
}


//------------------------------------------------------------
// function: CreateBasicConstraints
//------------------------------------------------------------

uint32
CreateBasicConstraints(x509_Extensions& extensions,
                       bool isCA,
                       int pathLenConstraint)
{
   uint32 status = 0;

   XBasicConstraints basicConstraint;

   if (isCA)
   {
      basicConstraint.cA.set_value(true);
      basicConstraint.pathLenConstraints.set_value(pathLenConstraint);
   }
   else
   {
      basicConstraint.cA.set_value(false);
   }

   addExtension(extensions.add_child(), true, BasicConstraints_val, 4, basicConstraint);

   return status;
}


//------------------------------------------------------------
// function: CreateKeyUsage
//------------------------------------------------------------

uint32
CreateKeyUsage(x509_Extensions& extensions,
               long mask)
{
   uint32 status = 0;

   XKeyUsage keyUsage;

   if ((status = keyUsage.set_value(mask)) != 0)
      return status;

   addExtension(extensions.add_child(), true, KeyUsage_val, 4, keyUsage);

   return status;
}


//------------------------------------------------------------
// function: CreateSubjectKeyIdentifier
//------------------------------------------------------------

uint32
CreateSubjectKeyIdentifier(x509_Extensions& extensions,
                           asn_object& objectToHash,
                           CSSM_CSP_HANDLE cspHandle)
{
   uint32 status = 0;

   buffer_t encoding;
   if ((status = objectToHash.write(encoding)) != 0)
      return status;

   XSubjectKeyIdentifier subjectKeyIdentifier;

   CSSM_DATA cssmDataToDigest;
   cssmDataToDigest.Data   = encoding.data;
   cssmDataToDigest.Length = encoding.data_len;

   if ((status = JKL_DigestData(cspHandle,
                                CSSM_ALGID_SHA1,
                                cssmDataToDigest,
                                subjectKeyIdentifier)) != 0)
      return status;

   addExtension(extensions.add_child(), false, SubjectKeyIdentifier_val, 4, subjectKeyIdentifier);

   return status;
}


//------------------------------------------------------------
// function: CreateIssuerAlternativeNames
//------------------------------------------------------------
                        
uint32
CreateIssuerAlternativeNames(x509_Extensions& extensions,
                             ncList& altNamesList)
{
   uint32 status = 0;

   if (altNamesList.numOfNames != 0)
   {
      XIssuerAltName issuerAltName;
   
      if ((status = PopulateAlternativeNames(issuerAltName, altNamesList)) != 0)
         return status;
   
      addExtension(extensions.add_child(), false, IssuerAlternativeName_val, 4, issuerAltName);
   }

   return status;
}


//------------------------------------------------------------
// function: CreateSubjectAlternativeNames
//------------------------------------------------------------
                        
uint32
CreateSubjectAlternativeNames(x509_Extensions& extensions,
                              ncList& altNamesList)
{
   uint32 status = 0;

   if (altNamesList.numOfNames != 0)
   {
      XsubjectAltName subjectAltName;
   
      if ((status = PopulateAlternativeNames(subjectAltName,
                                             altNamesList)) != 0)
         return status;
   
      addExtension(extensions.add_child(), false, SubjectAlternativeName_val, 4, subjectAltName);
   }

   return status;
}


//------------------------------------------------------------
// function: CreateNameConstraints
//------------------------------------------------------------
                        
uint32
CreateNameConstraints(x509_Extensions& extensions,
                      ncList& permittedList,
                      ncList& excludedList)
{
   uint32 status = 0;

   if (permittedList.numOfNames != 0 || excludedList.numOfNames != 0)
   {
      XNameConstraints nameConstraints;
   
      if ((status = PopulateNameConstraints(nameConstraints,
                                            permittedList,
                                            excludedList)) != 0)
         return status;
   
      addExtension(extensions.add_child(), true, NameConstraints_val, 4, nameConstraints);
   }

   return status;
}


//------------------------------------------------------------
// function: PopulateNameConstraints
//------------------------------------------------------------

uint32
PopulateNameConstraints(XNameConstraints& nameConstraints,
                        ncList& permittedList,
                        ncList& excludedList)
{
   uint32 status = 0;

   GeneralSubtree* permitted;
   GeneralSubtree* excluded;

   int i;

   for (i = 0; i < permittedList.numOfNames; i++)
   {
      permitted = nameConstraints.permittedSubtrees.add_child();

      char* value = permittedList.nameList[i].value;
      int type = permittedList.nameList[i].type;

      switch (type)
      {
      case TYPE_rfc822Name:
         if ((status = permitted->base.rfc822Name.set_value_C(value)) != 0)
            return status;
         if ((status = permitted->base.select(type)) != 0)
            return status;
         if ((status = permitted->minimum.set_value(0)) != 0)
            return status;
         break;
      case TYPE_dNSName:
         break;
      case TYPE_directoryname:
         if ((status = permitted->base.directoryname.set_value_C(value)) != 0)
            return status;
         if ((status = permitted->base.select(type)) != 0)
            return status;
         if ((status = permitted->minimum.set_value(0)) != 0)
            return status;
         break;
      case TYPE_ediPartyName:
         break;
      case TYPE_uniformResourceIdentifier:
         break;
      case TYPE_iPAddress:
         break;
      case TYPE_registeredID:
         break;
      default:
         break;
      }
   }

   for (i = 0; i < excludedList.numOfNames; i++)
   {
      excluded = nameConstraints.excludedSubtrees.add_child();

      char* value = excludedList.nameList[i].value;
      int type    = excludedList.nameList[i].type;

      switch (type)
      {
      case TYPE_rfc822Name:
         if ((status = excluded->base.rfc822Name.set_value_C(value)) != 0)
            return status;
         if ((status = excluded->base.select(type)) != 0)
            return status;
         if ((status = excluded->minimum.set_value(0)) != 0)
            return status;
         break;
      case TYPE_dNSName:
         break;
      case TYPE_directoryname:
         if ((status = excluded->base.directoryname.set_value_C(value)) != 0)
            return status;
         if ((status = excluded->base.select(type)) != 0)
            return status;
         if ((status = excluded->minimum.set_value(0)) != 0)
            return status;
         break;
      case TYPE_ediPartyName:
         break;
      case TYPE_uniformResourceIdentifier:
         break;
      case TYPE_iPAddress:
         break;
      case TYPE_registeredID:
         break;
      default:
         break;
      }
   }

   return status;
}


//------------------------------------------------------------
// function: PopulateAlternativeNames
//------------------------------------------------------------

uint32
PopulateAlternativeNames(XsubjectAltName& alternativeNames,
                         ncList& altNamesList)
{
   uint32 status = 0;

   int i;

   for (i = 0; i < altNamesList.numOfNames; i++)
   {
      GeneralName* name = alternativeNames.add_child();

      char* value = altNamesList.nameList[i].value;
      int type = altNamesList.nameList[i].type;

      switch (type)
      {
      case TYPE_rfc822Name:
         if ((status = name->rfc822Name.set_value_C(value)) != 0)
            return status;
         if ((status = name->select(type)) != 0)
            return status;
         break;
      case TYPE_dNSName:
         break;
      case TYPE_directoryname:
         if ((status = name->directoryname.set_value_C(value)) != 0)
            return status;
         if ((status = name->select(type)) != 0)
            return status;
         break;
      case TYPE_ediPartyName:
         break;
      case TYPE_uniformResourceIdentifier:
         break;
      case TYPE_iPAddress:
         break;
      case TYPE_registeredID:
         break;
      default:
         break;
      }
   }

   return status;
}
   

//------------------------------------------------------------
// function: addExtension
//------------------------------------------------------------

void
addExtension(x509_Extension* child, 
             bool isCritical,
             unsigned long oidArray[],
             int oidArrayLength,
             asn_object& object)
{
   uint32 status;

   buffer_t encoding;

   if ((status = object.write(encoding)) != 0)
      ThrowCSSMException(status);
   if ((status = child->extnID.set_value(oidArray, oidArrayLength)) != 0)
      ThrowCSSMException(status);
   if ((status = child->critical.set_value(isCritical)) != 0)
      ThrowCSSMException(status);
   if ((status = child->extnValue.set_value(encoding.data, encoding.data_len)) != 0)
      ThrowCSSMException(status);
}


//------------------------------------------------------------
// function: AddEntryToCRL
//------------------------------------------------------------

void
AddEntryToCRL(CertificateList& crl,
              const x509_certificate& revokee)
{
   uint32 status = 0;

   // create revocation entry

   RevokedCertificate* child = crl.tbsCertList.revokedCertificates.add_child();

   // extract serialNumber from revokee

   buffer_t encoding;
   if ((status = revokee.tbsCertificate.serialNumber.write(encoding)) != 0)
      ThrowCSSMException(status);
   if ((status = child->userCertificate.read(encoding)) != 0)
      ThrowCSSMException(status);

   // hardcode some date as revocation time

   unsigned year    = 1998;
   unsigned month   = 10;
   unsigned day     = 1;
   unsigned hour    = 1;
   unsigned minute  = 1;
   unsigned second  = 1;
   
   if ((status = child->revocationDate.select(0)) != 0)
      ThrowCSSMException(status);
   if ((status = child->revocationDate.utcTime.set_value(year-1,month,day,hour,minute,second,0,0)) != 0)
      ThrowCSSMException(status);
}


//------------------------------------------------------------
// function: SaveEncodingInfoToFile
//------------------------------------------------------------

void
SaveEncodingInfoToFile(CERTINFO_PTR info, 
                       int numOfElements, 
                       const char* prefix)
{
   uint32 status = 0;
   char filename[30];

   for (int k = 0; k < numOfElements; k++)
   {
      PrivateKeyInfo privateKeyInfo;
      SubjectPublicKeyInfo subjectPublicKeyInfo ;

      // (a) CSSM_KEYs

      sprintf(filename, "%s_pub%d.cssm", prefix, k);
      if ((status = WriteEncodingToFile(info[k].cssmPublicKey.KeyData, filename)) != 0)
         ThrowCSSMException(status);

      sprintf(filename, "%s_prv%d.cssm", prefix, k);
      if ((status = WriteEncodingToFile(info[k].cssmPrivateKey.KeyData, filename)) != 0)
         ThrowCSSMException(status);

      // (b) certificates

      sprintf(filename, "%s_cert%d.der", prefix, k);
      if ((status = WriteEncodingToFile(info[k].certificate, filename)) != 0)
         ThrowCSSMException(status);

      // (c) private keys
#if 0
      if ((status = JKL_cssm_to_asn(info[k].cssmPrivateKey, privateKeyInfo)) != 0)
         ThrowCSSMException(status);

      sprintf(filename, "%s_prv%d.der", prefix, k);
      if ((status = WriteEncodingToFile(privateKeyInfo, filename)) != 0)
         ThrowCSSMException(status);

      // (d) public keys

      if ((status = JKL_cssm_to_asn(info[k].cssmPublicKey, subjectPublicKeyInfo)) != 0)
         ThrowCSSMException(status);

      sprintf(filename, "%s_pub%d.der", prefix, k);
      if ((status = WriteEncodingToFile(subjectPublicKeyInfo, filename)) != 0)
         ThrowCSSMException(status);
#endif
      // (e) crl
#if 0
      sprintf(filename, "%s_crl%d.der", k, prefix);
      if ((status = WriteEncodingToFile(info[k].crl, filename)) != 0)
         ThrowCSSMException(status);
#endif
   }
}


//------------------------------------------------------------
// function: DeallocateMemory
//------------------------------------------------------------

void
DeallocateMemory(UnitTestInfo& unitTestInfo)
{
   int k;

   // free CSSM_KEYs

   for (k = 0; k < unitTestInfo.numOfSubjects; k++)
   {
      JKL_FreeData( unitTestInfo.subjectInfo[k].cssmPublicKey  );
      JKL_FreeData( unitTestInfo.subjectInfo[k].cssmPrivateKey );
   }
   for (k = 0; k < unitTestInfo.numOfIssuers; k++)
   {
      JKL_FreeData( unitTestInfo.issuerInfo[k].cssmPublicKey  );
      JKL_FreeData( unitTestInfo.issuerInfo[k].cssmPrivateKey );
   }
}


//------------------------------------------------------------
// function: Test_TP
//------------------------------------------------------------

uint32
Test_TP(CSSM_DL_DB_HANDLE dataSource, 
        CSSM_CSP_HANDLE cspVerify,
        CSSM_TP_HANDLE tpVerify,
        CERTINFO& issuer, 
        CERTINFO& subject)
{
   uint32 status = 0;

   CSSM_CERTGROUP_PTR certToBeVerifiedPtr = 0;
   CSSM_DATA_PTR anchorCertsPtr           = 0;
   uint32 numberofAnchorCerts             = 1;
   uint32 numberofCertsToBeVerified       = 1;

   //------------------------------------------------------------
   // Transmogrify certificate chain into CSSM data structures for TP
   //------------------------------------------------------------

   x509_certificate* certChain[2];
   certChain[0] = &subject.certificate;
   certChain[1] = &issuer.certificate;

   Populate_CSSM_TP_Structures(certChain, 
                               certToBeVerifiedPtr,
                               numberofCertsToBeVerified,
                               anchorCertsPtr,
                               numberofAnchorCerts);

   //------------------------------------------------------------
   // Attempt to validate chain with JonahTP
   //------------------------------------------------------------

   status = TestTPCertGroupVerify(dataSource,
                                  cspVerify,
                                  tpVerify,
                                  certToBeVerifiedPtr,
                                  anchorCertsPtr,
                                  numberofAnchorCerts);

   //------------------------------------------------------------
   // Clean up allocated memory
   //------------------------------------------------------------

   Cleanup_CSSM_TP_Structures(certToBeVerifiedPtr,
                              numberofCertsToBeVerified,
                              anchorCertsPtr,
                              numberofAnchorCerts);

   return status;
}


//------------------------------------------------------------
// function: Populate_CSSM_TP_Structures
//------------------------------------------------------------

void 
Populate_CSSM_TP_Structures(x509_certificate* certChain[],
                            CSSM_CERTGROUP_PTR& certToBeVerifiedPtr, 
                            uint32 numberofCertsToBeVerified,
                            CSSM_DATA_PTR& anchorCertsPtr, 
                            uint32 numberofAnchorCerts)
{
   uint32 status = 0;

   // Create CSSM_CERTGROUP structure with certs to be verified

   certToBeVerifiedPtr    = new CSSM_CERTGROUP;
   CSSM_DATA_PTR CertList = new CSSM_DATA[numberofCertsToBeVerified];

   for (size_t i = 0; i < numberofCertsToBeVerified; i++)
   {
      // extract DER encoding from certificate

      buffer_t certBuffer;
      if ((status = certChain[0]->write(certBuffer)) != 0)
         ThrowCSSMException(status);

      // store DER in CERT_GROUP structure

      CertList[i].Data   = certBuffer.data;
      CertList[i].Length = certBuffer.data_len;
      certBuffer.detach();
   }

   certToBeVerifiedPtr->NumCerts = numberofCertsToBeVerified;
   certToBeVerifiedPtr->CertList = CertList;
   certToBeVerifiedPtr->reserved = 0;

   // Create CSSM_DATA structure with anchors certificate

   anchorCertsPtr = new CSSM_DATA[ numberofAnchorCerts ];
   for (size_t j = 0; j < numberofAnchorCerts; j++)
   {
      buffer_t certBuffer;
      certChain[numberofCertsToBeVerified+j]->write(certBuffer);
      anchorCertsPtr[j].Data   = certBuffer.data;
      anchorCertsPtr[j].Length = certBuffer.data_len;
      certBuffer.detach();
   }
}


//------------------------------------------------------------
// function: TestTPCertGroupVerify
//------------------------------------------------------------

uint32
TestTPCertGroupVerify(CSSM_DL_DB_HANDLE dataSource,
                      CSSM_CSP_HANDLE cspHandle, 
                      CSSM_TP_HANDLE  tpVerify,
                      CSSM_CERTGROUP_PTR certToBeVerifiedPtr, 
                      CSSM_DATA_PTR anchorCertsPtr, 
                      uint32 numberofAnchorCerts)
{
   //------------------------------------
   // Create a list of DL/DB Handles;  
   // will be used by Jonah TP to search CRLs
   //------------------------------------

   CSSM_DL_DB_LIST DBList;
   DBList.NumHandles = 1;
   DBList.DLDBHandle = &dataSource;
   
   // define TP policy to use

   CSSM_TP_STOP_ON verificationAbortOn = CSSM_TP_STOP_ON_POLICY;

   // query Jonah TP for policy identifiers (cheat and use header)
   // better solution is to query TP for supported Polic IDs

   CSSM_FIELD policyIdentifiers;
   uint32 numberofPolicyIdentifiers = 1;
   unsigned char policy[] = { JONAHTP_POLICY_OID_DATA };
   policyIdentifiers.FieldOid.Data   = policy;
   policyIdentifiers.FieldOid.Length = sizeof(policy)/sizeof(unsigned char);

   CSSM_BOOL result;

   result = CSSM_TP_CertGroupVerify(tpVerify,
											   0,
											   &DBList,
											   cspHandle,
											   &policyIdentifiers,
											   numberofPolicyIdentifiers,
											   verificationAbortOn,
											   certToBeVerifiedPtr,
											   anchorCertsPtr,
											   numberofAnchorCerts,
											   (const CSSM_FIELD_PTR)0,
											   0,
											   (CSSM_TP_ACTION)0,
											   (const CSSM_DATA_PTR)0,
											   (CSSM_DATA_PTR*)0,
											   (uint32*)0);
   
   if (CSSM_FALSE == result)
      return CSSM_GetError()->error;

   return 0;
}


//------------------------------------------------------------
// function: Cleanup_CSSM_TP_Structures
//------------------------------------------------------------

void
Cleanup_CSSM_TP_Structures(CSSM_CERTGROUP_PTR certToBeVerifiedPtr,
                           uint32 numberofCertsToBeVerified,
                           CSSM_DATA_PTR anchorCertsPtr,
                           uint32 numberofAnchorCerts)
{
   if (certToBeVerifiedPtr)
   {
      for (size_t j = 0; j < numberofCertsToBeVerified; j++)
      {
         delete certToBeVerifiedPtr->CertList[j].Data;
      }
      delete [] certToBeVerifiedPtr->CertList;
      delete certToBeVerifiedPtr;
   }

   if (anchorCertsPtr)
   {
      for (size_t k = 0; k < numberofAnchorCerts; k++)
      {
         delete anchorCertsPtr[k].Data;
      }
      delete [] anchorCertsPtr;
   }
}


//********************************************************************************
//
// memory management
//
//********************************************************************************


//------------------------------------------------------------
// function: DefaultMalloc
//------------------------------------------------------------

void*
DefaultMalloc(uint32 size,
              void* allocRef)
{
   return malloc(size);
}


//------------------------------------------------------------
// function: DefaultFree
//------------------------------------------------------------

void
DefaultFree(void* memPtr,
            void* allocRef)
{
   free(memPtr);
}


//------------------------------------------------------------
// function: DefaultRealloc
//------------------------------------------------------------

void*
DefaultRealloc(void* memPtr,
               uint32 size,
               void* allocRef)
{
   return realloc(memPtr, size);
}


//------------------------------------------------------------
// function: DefaultCalloc
//------------------------------------------------------------

void*
DefaultCalloc(uint32 num,
              uint32 size,
              void* allocRef)
{
   return calloc(num, size);
}


//------------------------------------------------------------
// function: ThrowCSSMException
//------------------------------------------------------------

void
ThrowCSSMException(uint32 errcode)
{
   static char msg[64];

   sprintf(msg, "CSSM_ERROR_CODE = %lu", errcode);

   throw msg;
}


#include <stdio.h>

//------------------------------------------------------------
// function: writeEncodingToFile
//------------------------------------------------------------

uint32
WriteEncodingToFile(buffer_t& encoding, 
                    const char* fileName)
{
   FILE* fp;

   if ((fp = fopen(fileName, "wb")) != NULL)
   {
      fwrite(encoding.data, 1, encoding.data_len, fp);
      fclose(fp);
   }

   return 0;
}


//------------------------------------------------------------
// function: writeEncodingToFile
//------------------------------------------------------------

uint32
WriteEncodingToFile(CSSM_DATA& encoding, 
                    const char* fileName)
{
   FILE* fp;

   if ((fp = fopen(fileName, "wb")) != NULL)
   {
      fwrite(encoding.Data, 1, encoding.Length, fp);
      fclose(fp);
   }

   return 0;
}


//------------------------------------------------------------
// function: writeEncodingToFile
//------------------------------------------------------------

uint32
WriteEncodingToFile(asn_object& reference, 
                    const char* fileName)
{
   uint32 status = 0;

   FILE* fp;

   if ((fp = fopen(fileName, "wb")) != NULL)
   {
      buffer_t encoding;
      if ((status = reference.write(encoding)) != 0)
         return status;
      fwrite(encoding.data, 1, encoding.data_len, fp);
      fclose(fp);
   }

   return 0;
}
