TSFISample.cpp

// tsfisample.cpp : sample application focused on the interfaces that comprise
// the TSFI.
//

#include "stdafx.h"

//Global variables
const char* g_banner = "********************************************************************************";

 
/**
g_mediator contains the mediator/colleague set that will be used by
all commands that require PKI support.
*/

IPKIFMediator*  g_mediator = NULL;

/**
g_settings contains user-specified path settings.
*/

CPKIFPathSettingsPtr g_settings; 

/**
g_path contains the path object used for iterative calls to BuildPath and
ValidatePath and g_pathResults contains the associated results.
*/

CPKIFCertificatePathPtr g_path;
CPKIFPathValidationResultsPtr g_results;

std::map<std::string, Commands> g_map;
typedef pair<string, Commands> CommandMap;

int _tmain(int argc, _TCHAR* argv[])
{

      //Prepare a map containing command strings and enumerated values
      g_map.insert(CommandMap(string("0"), CMD_QUIT));
      g_map.insert(CommandMap(string("1"), CMD_NEW_DEFAULT_MEDIATOR));
      g_map.insert(CommandMap(string("2"), CMD_NEW_MEDIATOR_WITH_OCSP));
      g_map.insert(CommandMap(string("3"), CMD_ADD_LDAP));
      g_map.insert(CommandMap(string("4"), CMD_SPECIFY_DEFAULT_SETTINGS));
      g_map.insert(CommandMap(string("5"), CMD_USE_DEFAULT_SETTINGS));
      g_map.insert(CommandMap(string("6"), CMD_BUILD_PATH));
      g_map.insert(CommandMap(string("7"), CMD_VAL_PATH));
      g_map.insert(CommandMap(string("8"), CMD_BUILD_AND_VAL_PATH));
      g_map.insert(CommandMap(string("9"), CMD_GEN_SIG));
      g_map.insert(CommandMap(string("10"), CMD_VER_SIG));
      g_map.insert(CommandMap(string("11"), CMD_ENC_MSG));
      g_map.insert(CommandMap(string("12"), CMS_DEC_MSG));

      //By default create a new default mediator object and store the value
      //in g_mediator.  Operators are free to replace this via a call to
      //Cmd_NewDefaultMediator or Cmd_NewMediatorWithOCSP and to alter it
      //via a call to Cmd_AddLDAP.

      Cmd_NewDefaultMediator();

      do
      {
            //Show the menu, then...
            DisplayMainMenu();
            //Handle the command and determine if there's more to do

      } while(ProcessCommand());

      cout << "Goodbye!" << endl; 

      return 0;

}


//***************************************************************************
// UTILITY FUNCTIONS
//***************************************************************************

/**
DisplayMainMenu permits the user to perform the tasks defined in the 
Commands enumeration.
*/

void DisplayMainMenu()
{
      cout << g_banner << endl;
      cout << "Select from the following commands.  Enter 0 to quit." <<  endl << endl;
      cout << "Environment definition commands:" << endl;
      cout << "\t1 - Create default mediator/colleague set" << endl
            << "\t2 - Create default mediator/colleage set with a trusted OCSP responder" << endl
            << "\t3 - Add an LDAP directory to previously created mediator/colleague set" << endl
            << "\t4 - Specify path processing settings" << endl
            << "\t5 - Use default path processing settings" << endl << endl;

      cout << "Certification path processing commands:" << endl;
      cout << "\t6 - Build a certification path" << endl
            << "\t7 - Validate a certification path (must follow \"Build a certification path\")" << endl
            << "\t8 - Build and validate a certification path" << endl << endl; 

      cout << "Signature processing commands:" << endl;
      cout << "\t9 - Generate a signed message" << endl
            << "\t10 - Verify a signed message" << endl << endl;

      cout << "PKI encryption processing commands:" << endl;
      cout << "\t11 - Generate an encrypted message" << endl
            << "\t12 - Decrypt an encrypted message" << endl;
      cout << g_banner << endl;

}

 

void CleanGlobals()
{
      //Free the mediator, there is one.  all other globals are smart pointers
      //that will be cleaned up automatically
      if(g_mediator)
            FreeDefaultMediator(g_mediator); 

      g_mediator = NULL;
}

void Pause()
{
      system("pause");
}

 

bool ProcessCommand()
{
      try
      {
            bool bQuit = false;
            char buf[50];
            cin >> buf;
 

            int cmd = -1;
            map <string, Commands> :: const_iterator cm = g_map.find(string(buf));
            if(cm != g_map.end())
                  cmd = cm->second;

            switch(cmd)
            {
            case CMD_QUIT:
                  bQuit = true;
                  Cmd_Quit();
                  break;
            case CMD_NEW_DEFAULT_MEDIATOR:
                  Cmd_NewDefaultMediator();
                  break;
            case CMD_NEW_MEDIATOR_WITH_OCSP:
                  Cmd_NewMediatorWithOCSP();
                  break;
            case CMD_ADD_LDAP:
                  Cmd_AddLDAP();
                  break;
            case CMD_SPECIFY_DEFAULT_SETTINGS:
                  Cmd_SpecifyDefaultSettings();
                  break;
            case CMD_USE_DEFAULT_SETTINGS:
                  Cmd_UseDefaultSettings();
                  break;
            case CMD_BUILD_PATH:
                  Cmd_BuildPath();
                  break;
            case CMD_VAL_PATH:
                  Cmd_ValidatePath();
                  break;
            case CMD_BUILD_AND_VAL_PATH:
                  Cmd_BuildAndValidatePath();
                  break;
            case CMD_GEN_SIG:
                  Cmd_GenerateSignature();
                  break;
            case CMD_VER_SIG:
                  Cmd_VerifySignature();
                  break;
            case CMD_ENC_MSG:
                  Cmd_EncryptMessage();
                  break;
            case CMS_DEC_MSG:
                  Cmd_DecryptMessage();
                  break;
            default:
                  cout << "ERROR: Unrecognized command" << endl;
            }

 

            if(!bQuit)
                  Pause();

            return !bQuit;
      }

      catch(CPKIFException& pe)
      {
            CPKIFStringPtr details = pe.print();
            cout << details->c_str() << endl;
            Pause();
      }
      catch(std::exception& se)
      {
            cout << "Unexpected std::exception" << endl;
            Pause();
      }
      catch(...)
      {
            cout << "Unexpected exception." << endl;
            Pause();
      }
      //never return that it's time to quit following an exception

      return true;
}

bool SolicitBool(const char* prompt)
{
      do
      {
            cout << prompt << "(y or n)?: "; 

            char buf;
            cin >> buf; 

            if('y' == buf || 'Y' == buf)
                  return true;
            else if('n' == buf || 'N' == buf)
                  return false;
            else
                  cout << "ERROR: You entered an invalid character." << endl;
      }
      while(1);
}

bool SolicitInteger(const char* prompt)
{
      do
      {
            cout << prompt << ": ";
            char buf[25];

            cin >> buf;
            return (atoi(buf));

      }
      while(1);

}

 

CPKIFTimePtr GetTimeOfInterest()
{
      if(SolicitBool("Verify paths relative to a time other than the current system time"))
      {
            do
            {
                  cout << "Enter the validation time of interest as GeneralizedTime string (e.g., 20050305124955X): ";
                  char buf[100];
                  cin >> buf;
                  try
                  {
                        CPKIFTimePtr time(new CPKIFTime(buf));
                        return time;
                  }
                  catch(CPKIFException& e)
                  {
                        cout << "ERROR: invalid time value entered" << endl;
                  }
            }
            while(1);
      }
      else
      {
            CPKIFTimePtr time;
            return time;
      }
}

void GetPolicyOIDs(vector<CPKIFPolicyInformationPtr>& oids)
{
      bool addAnother = false;
      char oidBuf[100];
      cout << "Enter OIDs corresponding to certificate policies in the initial policy set." << endl;

      do
      {
            memset(oidBuf, 0, 100); 

            cout << "Enter an OID value corresponding to a certificate policy using dot notation (e.g., 2.5.29.37.0): " << endl;

            cin >> oidBuf;

            try
            {
                  CPKIFOIDPtr oid(new CPKIFOID(new string(oidBuf)));
                  CPKIFPolicyInformationPtr policy(new CPKIFPolicyInformation(oid));
                  oids.push_back(policy); 

                  addAnother = SolicitBool("Would you like to add another policy");
            }
            catch(CPKIFException& e)
            {
                  cout << "ERROR: invalid OID entered" << endl;
            }
      }while(addAnother);
}

CPKIFPathSettingsPtr GetPathSettingsFromUser()
{
      CPKIFPathSettingsPtr settings(new CPKIFPathSettings());
      //builder-focused setting

      settings->SetUseValidatorFilterWhenBuilding(SolicitBool
            ("Should basic validation checks be performed during path building"));

      //revocation-related settings

      settings->SetCheckRevocationStatus(SolicitBool
            ("Should revocation status be checked during path validation"));

      if(settings->GetCheckRevocationStatus())
      {
            settings->SetRequireFreshRevocationData(SolicitBool
                  ("Should fresh revocation data, i.e. nextUpdate < TOI, be required"));

            if(SolicitBool("Should recent revocation data, i.e. thisUpdate > threshold, be required"))
            {
                  settings->SetRequireSufficientlyRecent(true);
                  settings->SetSufficientlyRecent(SolicitInteger
                        ("Enter the maximum age of acceptable revocation status information (in seconds)"));
            }
            else
            {
                  settings->SetRequireSufficientlyRecent(false);
            }
      }
      else
      {
            //these don't matter since we aren't checking revocation status, 
            //but set to false anyway
            settings->SetRequireFreshRevocationData(false);
            settings->SetRequireSufficientlyRecent(false);
      }
 

      //time of interest for validation
      CPKIFTimePtr time = GetTimeOfInterest();
      if(time != (CPKIFTime*)NULL)
      {
            settings->SetValidationTime(time);
      }

       //X.509/RFC3280 path validation inputs
      settings->SetInitialExplicitPolicyIndicator(SolicitBool
            ("Should the initial explicit policy indicator be set to true"));

      settings->SetInitialInhibitAnyPolicyIndicator(SolicitBool
            ("Should the initial inhibit any policy indicator be set to true"));

      settings->SetInitialPolicyMappingInhibitIndicator(SolicitBool
            ("Should the initial policy mapping inhibit indicator be set to true"));

 

      if(SolicitBool("Would you like to specify policies for the initial policy set"))
      {

            vector<CPKIFPolicyInformationPtr> oids;
            GetPolicyOIDs(oids);
 

            CPKIFPolicyInformationListPtr list(new CPKIFPolicyInformationList);
            vector<CPKIFPolicyInformationPtr>::iterator pos;
            vector<CPKIFPolicyInformationPtr>::iterator end = oids.end();
            for(pos = oids.begin(); pos != end; ++pos)
                  list->push_back((*pos));

            settings->SetInitialPolicySet(list);

      }

      return settings;

}
 

long filesize(FILE *stream)
{
      long curpos, length; 

      curpos = ftell(stream);
      fseek(stream, 0L, SEEK_END);
      length = ftell(stream);
      fseek(stream, curpos, SEEK_SET);
      return length;
}

bool WriteToFile(const char* prompt, const unsigned char* pBuf, int nLenOfBuf)
{
      do
      {

            cout << prompt << ": ";
            char buf[MAX_PATH];
            cin >> buf;
 

            FILE* f = fopen(buf, "wb+");
            if(!f)
            {
                  cout << "ERROR: failed to open the specified file for writing" << endl;
            }
            else
            {
                  size_t bytesRead = fwrite(pBuf, 1, nLenOfBuf, f);
                  fclose(f);
 

                  cout << "Wrote " << bytesRead << " bytes to " << buf << endl;
                  return true;
            }
      }while(1);

       //give up (never gets here)
      return false;
}

bool SolicitFile(const char* prompt, unsigned char** ppBuf, int* pLenOfBuf)
{
      do
      {
            cout << prompt << ": ";
 

            char buf[MAX_PATH];
            cin >> buf;
 

            FILE* f = fopen(buf, "rb");
            if(!f)
            {
                  cout << "ERROR: failed to open the specified file for reading" << endl;
            }
            else
            {
                  *pLenOfBuf = filesize(f);
                  *ppBuf = new unsigned char[*pLenOfBuf];
                  size_t bytesRead = fread(*ppBuf, 1, *pLenOfBuf, f);
                  fclose(f); 

                  cout << "Read " << bytesRead << " bytes from " << buf << endl;
                  return true;
            }
      }while(1);
 

      //give up (never gets here)
      return false;
}

 

CPKIFCertificatePtr GetCertificate(const char* prompt)
{
      unsigned char* pTargetCertBuf = NULL;
      int nTargetCertLen = 0;

      if(!SolicitFile(prompt, &pTargetCertBuf, &nTargetCertLen))
      {
            cout << "ERROR: Failed to read target certificate from specified file." << endl;
 

            CPKIFCertificatePtr targetCert;
            return targetCert;
      }

 
      CPKIFCertificatePtr targetCert(new CPKIFCertificate);
      targetCert->Decode(pTargetCertBuf, nTargetCertLen);

 
      //clean up buffer returned from SolicitFile.  A copy has been made by targetCert.
      delete[] pTargetCertBuf; pTargetCertBuf = NULL;

      return targetCert;
}

 
//***************************************************************************
// COMMAND FUNCTIONS
//***************************************************************************
 

/**
Cmd_Quit is called when the user selects the Quit option from the main
menu. 

This function must clean up any memory stored in global variables.
*/

void Cmd_Quit()
{
      CleanGlobals();
}

 

/**
Cmd_NewDefaultMediator is called when the user selects the "Create default mediator/colleague set" 
option from the main menu. 

 This function permits the user create a new default mediator/colleague set.
*/

void Cmd_NewDefaultMediator()
{
      CleanGlobals();
      g_mediator = MakeDefaultMediator();
}

/**
Cmd_NewMediatorWithOCSP is called when the user selects the "Create default
mediator/colleague set with a trusted OCSP responder" option from the main menu.  

This function permits the user create a mediator/colleague set in which the first
revocation status source is a trusted responder.
*/

void Cmd_NewMediatorWithOCSP()
{
      CleanGlobals(); 

      char host[MAX_PATH];
      cout << "Enter the IP address or DNS name of the OCSP responder: " << endl;
      cin >> host;
 

      int port;
      cout << "Enter the port number of the OCSP responder: " << endl;
      cin >> port;
 

      CPKIFOCSPChecker* ocsp = new CPKIFOCSPChecker();
      ocsp->SetHost((const char*)host);
      ocsp->Set_Port(port);
 

      g_mediator = MakeDefaultMediator(false, ocsp);
}
 

/**
Cmd_AddLDAP is called when the user selects the "Add an LDAP directory to previously
created mediator/colleague set" option from the main menu. 
 

This function permits the user to add an LDAP-accessible directory to the mediator/colleague
collection stored in g_mediator.
*/

void Cmd_AddLDAP()
{
      char host[MAX_PATH];
      cout << "Enter the IP address or DNS name of the LDAP directory: " << endl;
      cin >> host; 

      int port;
      cout << "Enter the port number of the LDAP directory: " << endl;
      cin >> port; 

      CPKIFLDAPRepository* ldap = new CPKIFLDAPRepository();
      ldap->SetHost((const char*)host);
      ldap->Set_Port(port);
 

      CPKIFCacheMediator2* pCache = g_mediator->GetMediator<CPKIFCacheMediator2>();
      pCache->AddColleague(ldap);
}

/**
Cmd_SpecifyDefaultSettings is called when the user selects the "Specify path
processing settings" option from the main menu.  

This function must permit the user to specify a the various properties of CPKIFPathSettings.
*/

void Cmd_SpecifyDefaultSettings()
{
      g_settings = GetPathSettingsFromUser();
}

 

/**
Cmd_UseDefaultSettings is called when the user selects the "Use default path processing settings" option from the main menu.  

This function must permit the user to specify a the various properties of CPKIFPathSettings.
*/

void Cmd_UseDefaultSettings()
{
      CPKIFPathSettingsPtr ps(new CPKIFPathSettings);
      g_settings = ps;
}

 

/**
Cmd_BuildPath is called when the user selects the "Build a certification path" option from the main menu.  

This function must permit the user to specify a target certificate, and optionally
path settings, then attempt to build a path using the global mediator.
*/

void Cmd_BuildPath()
{
      bool bReset = false;
      if(g_path != (CPKIFCertificatePath*)NULL)
      {
            bReset = SolicitBool
                  ("Reset state variables and begin a new path building operation");
      } 

      if(bReset || g_path == (CPKIFCertificatePath*)NULL)
      {
            CPKIFCertificatePtr targetCert = GetCertificate(
                  "Enter the full path and file name of the target certificate");
            if(targetCert == (CPKIFCertificate*)NULL)
                  return; 

            //Reset the global variables used by BuildPath and ValidatePath
            CPKIFCertificatePathPtr path(new CPKIFCertificatePath);
            g_path = path;
            CPKIFPathValidationResultsPtr results(new CPKIFPathValidationResults);
            g_results = results; 
            g_path->SetTarget(targetCert);
 

            if(g_settings != (CPKIFPathSettings*)NULL)
                  g_path->SetPathSettings(g_settings);
      }

      IPKIFPathBuild* iPB = g_mediator->GetMediator<IPKIFPathBuild>();
      if(iPB->BuildPath(*g_path))
            cout << "Found a certification path.  Details below..." << endl;
      else
            cout << "Failed to find a certification path.  Details below..." << endl;
 

      CPKIFPathLogger logPath;
      logPath.LogPath(*g_path, "TSFI Sample application", &cout);
}

 

/**
Cmd_ValidatePath is called when the user selects the "Validate a certification path" option from the main menu. 

This function attempts to validate the path returned by the previous call to Build Path.
*/

void Cmd_ValidatePath()
{
      if(g_path == (CPKIFCertificatePath*)NULL)
      {
            cout << "ERROR: no path is available.  You must build a path before \
                        validating a path." << endl;
            return;
      } 

      IPKIFPathValidate* iPV = g_mediator->GetMediator<IPKIFPathValidate>();
 

      bool isSig = false, isEnc= false;
      isSig = SolicitBool("Is the target certificate intended for signature verification purposes");
      isEnc = SolicitBool("Is the target certificate intended for encryption purposes");
 

      CPKIFFuncStoragePtr keyUsageProcessor(new CPKIFFuncStorage(NULL));
      if(isSig)
            keyUsageProcessor->addFunc(keyUsageChecker_Signature);
      if(isEnc)
            keyUsageProcessor->addFunc(keyUsageChecker_Encryption); 

      iPV->ValidatePath(*g_path, *g_results, keyUsageProcessor); 

      CPKIFPathLogger logPath;
      logPath.LogValidationResults(*g_results, *g_path, "TSFI Sample application", &cout);
}

 

/**
Cmd_BuildAndValidatePath is called when the user selects the "Build and validate a certification path" option from the main menu. 

This function must permit the user to specify a target certificate, and optionally
path settings.  It then attempts to build a path and validate a path using the global mediator.
*/

void Cmd_BuildAndValidatePath()
{
      CPKIFCertificatePtr targetCert = GetCertificate(
            "Enter the full path and file name of the target certificate");

      CPKIFCertificatePath path;
      path.SetTarget(targetCert);
 

      if(g_settings != (CPKIFPathSettings*)NULL)
            path.SetPathSettings(g_settings);
 

      CPKIFPathValidationResults pvr;
 

      IPKIFPathBuildAndValidate* iPBAV = g_mediator->GetMediator<IPKIFPathBuildAndValidate>();
      if(iPBAV)
            iPBAV->BuildAndValidatePath(path, pvr);
 

      CPKIFPathLogger logPath;
      logPath.LogValidationResults(pvr, path, "TSFI Sample application", &cout);
}

 

/**
    Cmd_GenerateSignature is called when the user selects the "Generate a signed message" option from the main menu.  

    This function must permit the user to specify content to sign, signer credential and file name to which the signed
    message will be written.
*/

void Cmd_GenerateSignature()
{
      unsigned char* pDataToSign = NULL;
      int nDataToSignLen = 0;

      if(!SolicitFile(
            "Enter the full path and file name of the file containing the data to sign", &pDataToSign, &nDataToSignLen))
      {
            cout << "ERROR: Failed to read data to sign from specified file." << endl;
            return;
      }
 

      CPKIFBufferPtr contentBuffer(new CPKIFBuffer(pDataToSign, nDataToSignLen));
      delete[] pDataToSign; pDataToSign = NULL;
 

      CPKIFSignedData sd;
 

      IPKIFCryptoKeyIDOperations* iKIDO = g_mediator->GetMediator<IPKIFCryptoKeyIDOperations>();
      CPKIFCredentialList creds;
      std::bitset<9> ku = DigitalSignature | NonRepudiation;
      iKIDO->GetKeyList(creds, &ku);
 

      bool addAnother = false;
      do
      {
            cout << "The following " << creds.size() <<
                  " credentials are available for signature generation purposes" << endl;
 

            CPKIFCredentialList::iterator pos = creds.begin();
            CPKIFCredentialList::iterator end = creds.end();
            for(int slot = 0; pos != end; ++pos)
            {
                  cout << slot++ << ": Name - " << (*pos)->Name() <<
                        "  ID: " << (*pos)->ID() << endl;

            } 

            int selection = -1;
            do
            {
                  cout << "Enter the number corresponding to the credential with which you \
                              would like to sign: ";

                  cin >> selection;

                  if(selection > creds.size())
                        cout << "ERROR: invalid selection" << endl;
                  else
                  {
                        CPKIFSignerInfoPtr si(new CPKIFSignerInfo);
                        si->SetCredential(creds[selection]);
                        sd.AddSignerInfo(si);
 

                        if(SolicitBool("Include signer's certificate in message"))
                              sd.AddCertificate(creds[selection]->GetCertificate());

 

                        break;
                  }
            }while(1); 

            addAnother = SolicitBool("Would you like to add another signer");
      }while(addAnother);
 

      sd.AddMediator(g_mediator); 

      CPKIFEncapsulatedContentInfoPtr ecip(new CPKIFEncapsulatedContentInfo);
      ecip->SetContent(contentBuffer);
      sd.SetEncapsulatedContent(ecip);

 

      CPKIFBufferPtr encodedSD = sd.Encode(); 

      CPKIFContentInfo ci;
      ci.SetContentType(g_signedData);
      ci.SetContent(encodedSD);

 

      CPKIFBufferPtr signedMessage = ci.Encode();
 

      WriteToFile("Enter the full path and filename of the location to receive the signed message",
            signedMessage->GetBuffer(), signedMessage->GetLength());

}

 

/**

Cmd_VerifySignature is called when the user selects the "Verify a signed message" option from the main menu.  


This function must permit the user to specify content to verify and file name to which the
message content will be written.
*/

void Cmd_VerifySignature()
{
      unsigned char* pDataToVerify = NULL;
      int nDataToVerifyLen = 0;

      if(!SolicitFile("Enter the full path and file name of the file containing the signed message \
                              to verify", &pDataToVerify, &nDataToVerifyLen))
      {
            cout << "ERROR: Failed to read data to verify from specified file." << endl;
            return;
      }
 

      CPKIFContentInfo ci;
      ci.Decode(pDataToVerify, nDataToVerifyLen);
      delete[] pDataToVerify; pDataToVerify = NULL;
 

      if(*g_signedData != *ci.GetContentType())
      {
            cout << "ERROR: file does not contain a signed message";
            return;
      }
 

      CPKIFSignedData sd;
      sd.AddMediator(g_mediator);
      sd.Decode(ci.GetContent());
 

      CPKIFSignerInfos sis;
      sd.GetSignerInfos(sis);

      bool verifyAnother = false;
      do
      {
            cout << "The following " << sis.size() << " signers are available for signature \
                                                                          verification purposes" << endl;

 

            CPKIFSignerInfos::iterator pos = sis.begin();
            CPKIFSignerInfos::iterator end = sis.end();
            for(int slot = 0; pos != end; ++pos)
            {

                  switch((*pos)->GetSignerIdentifierChoice())
                  {
                  case CPKIFSignerInfo::ISSUERANDSERIAL:
                        {
                              CPKIFIssuerAndSerialNumberPtr iasn =
                                    (*pos)->GetIssuerAndSerialNumber();
                              cout << slot++ << ": Issuer - "
                                    << iasn->GetName()->string() << "  Serial - "
                                    << iasn->GetSerialNumber() << endl;
                              break;
                        }
                  case CPKIFSignerInfo::SKID:
                        {
                              CPKIFBufferPtr skid = (*pos)->GetSKID();
                              char* asciiSKID = new char[(skid->GetLength() * 2)+1];
                              btoa((const char*)skid->GetBuffer(),
                                    asciiSKID, skid->GetLength());
                              cout << slot++ << ": " << asciiSKID << endl;
                        }
                        break;
                  default:
                        cout << "Unrecognized signer identifier type" << endl;
                  }
            }

            int selection = -1;

            do
            {
                  cout << "Enter the number corresponding to the signer whose signature you \
                              would like to verify: ";

                  cin >> selection;
                  if(selection > sis.size())
                        cout << "ERROR: invalid selection" << endl;
                  else
                  {
                        CMSVerificationStatus status = NOT_VERIFIED;
                        sd.Verify(selection, status);
 

                        cout << "Verification status = ";
 

                        switch(status)
                        {
                        case REV_STATUS_INVALID:
                              cout << "REV_STATUS_INVALID" << endl;
                              break;
                        case CERT_PATH_INVALID:
                              cout << "CERT_PATH_INVALID" << endl;
                              break;
                        case CMS_SIGNATURE_INVALID:
                              cout << "CMS_SIGNATURE_INVALID" << endl;
                              break;
                        case NOT_VERIFIED:
                              cout << "NOT_VERIFIED" << endl;
                              break;
                        case CMS_SIGNATURE_VERIFIED:
                              cout << "CMS_SIGNATURE_VERIFIED" << endl;
                              break;
                        case CERT_PATH_VERIFIED:
                              cout << "CERT_PATH_VERIFIED" << endl;
                              break;
                        case REV_STATUS_VERIFIED:
                              cout << "REV_STATUS_VERIFIED" << endl;
                              break;
                        default:
                              cout << "UNRECOGNIZED STATUS" << endl;
                        }

                        CPKIFPathValidationResultsPtr pvr = sd.GetValidationResults();
                        CPKIFCertificatePathPtr path = sd.GetPath();
 

                        CPKIFPathLogger log;
                        log.LogValidationResults(*pvr, *path,
                              "TSFI Sample Application", &cout);
 

                        break;
                  }
            }while(1);

            verifyAnother = SolicitBool("Would you like to verify another signer");
      }while(verifyAnother);
}
 

/**
Cmd_EncryptMessage is called when the user selects the "Generate an encrypted message" option from the main menu.  

This function must permit the user to specify content to encrypt and one or more recipients.
*/

void Cmd_EncryptMessage()
{
      unsigned char* pDataToEncrypt = NULL;
      int nDataToEncryptLen = 0;
      if(!SolicitFile(
            "Enter the full path and file name of the file containing the data to encrypt",
            &pDataToEncrypt, &nDataToEncryptLen))
      {
            cout << "ERROR: Failed to read data to sign from specified file." << endl;
            return;
      }
 

      CPKIFBufferPtr contentBuffer(new CPKIFBuffer(pDataToEncrypt, nDataToEncryptLen));
      delete[] pDataToEncrypt; pDataToEncrypt = NULL;
 

      CPKIFEnvelopedData ed;
      ed.AddMediator(g_mediator);

      bool addAnother = false;

      do
      {
            bool checkStatus = SolicitBool(
                  "Would you like to perform path validation for the recipient's certificate");

            CPKIFCertificatePtr cert = GetCertificate(
                  "Enter the full path and file name of a recipient's DER encoded certificate");

            if(cert == (CPKIFCertificate*)NULL)
            {
                  cout << "ERROR: failed to obtain recipient's certificate from specified file"
                        << endl;
            }
            else
            {
                  CPKIFCertificatePathPtr path;
                  CPKIFPathValidationResultsPtr pvr;
                  try
                  {
                        CMSPathValidationStatus minStatus = PVS_REV_STATUS_VERIFIED;
                        if(!checkStatus)
                              minStatus = PVS_NOT_VALIDATED;
                        ed.AddRecipient(cert, path, pvr, minStatus);
                        cout << "Recipient added to message" << endl;
                  }
                  catch(CPKIFException& e)
                  {
                        if(MSG_INVALID_RECIP != e.GetErrorCode())
                              throw e;

                        cout << "Recipient not added to message" << endl;
                        CPKIFStringPtr details = e.print();
                        cout << details->c_str() << endl;
                  }

 

                  if(pvr != (CPKIFPathValidationResults*)NULL &&
                        path != (CPKIFCertificatePath*)NULL)

                  {
                        CPKIFPathLogger log;
                        log.LogValidationResults(*pvr, *path,
                              "TSFI Sample Application", &cout);
                  }
            }

            addAnother = SolicitBool("Would you like to add another recipient");

      }while(addAnother);
 

      CPKIFEncryptedContentInfoPtr ecip(new CPKIFEncryptedContentInfo);
      ecip->SetContent(contentBuffer);
      ed.SetDataToEncrypt(ecip);
 

      CPKIFBufferPtr encodedED = ed.Encode(); 

      CPKIFContentInfo ci;
      ci.SetContentType(g_envelopedData);
      ci.SetContent(encodedED);
 

      CPKIFBufferPtr encryptedMessage = ci.Encode(); 

      WriteToFile("Enter the full path and filename of the location to receive the \
                        encrypted message", encryptedMessage->GetBuffer(), encryptedMessage->GetLength());

}

/**
Cmd_DecryptMessage is called when the user selects the "Decrypt an encrypted message" option from the main menu. 

This function must permit the user to specify content to decrypt. It could
be extended to permit specification of the credential to use to perform the decryption.
*/

void Cmd_DecryptMessage()
{
      unsigned char* pDataToDecrypt = NULL;
      int nDataToDecryptLen = 0;
      if(!SolicitFile("Enter the full path and file name of the file containing the data to \
                              decrypt", &pDataToDecrypt, &nDataToDecryptLen))
      {
            cout << "ERROR: Failed to read data to decrypt from specified file." << endl;
            return;
      }

      CPKIFContentInfo ci;
      ci.Decode(pDataToDecrypt, nDataToDecryptLen);
      delete[] pDataToDecrypt; pDataToDecrypt = NULL;

      if(*g_envelopedData != *ci.GetContentType())
      {
            cout << "ERROR: file does not contain an encrypted message";
            return;
      }

      CPKIFEnvelopedData ed;
      ed.Decode(ci.GetContent());
      ed.AddMediator(g_mediator);

 

      CPKIFCredentialPtr dummy;
      CPKIFBufferPtr decryptedMessage = ed.Decrypt(dummy);

 

      WriteToFile("Enter the full path and filename of the location to receive the encrypted \
                        message", decryptedMessage->GetBuffer(), decryptedMessage->GetLength());

}