/* $Id: PKI_Examples.c $ */

/*
This source code carries out a series of tests on the functions in the
CryptoSys(tm) PKI Toolkit <www.cryptosys.net>

Copyright (C) 2004-12 DI Management Services Pty Limited. All rights reserved.

Last updated:
  $Date: 2012-09-08 21:36:00 $
  $Revision: 3.9.0 $

Use in conjunction with diCrPKI.lib and diCrPKI.dll (Version 3.9 or later) or
diCrPKI.a in Linux. 

This is not meant to be representative of good security coding.  The RSA keys
are deliberately made smaller than you should use in practice to save time in
testing. Do not use 512-bit keys in practice.

There is minimal error checking here - we use assert as a blunt instrument - and
we make little or no effort to clean up passwords etc afterwards.

In particular, we save and display unencrypted private keys here to demonstrate 
certain debugging techniques. This should *never* be done in a real production
envrionment.  ALWAYS use the encrypted form and keep the private key string and 
password secret, then wipe them securely immediately after use. (It is not
advised to prompt the user with the actual password, either.)

*/

#if _MSC_VER >= 1100
  /* Detect memory leaks in MSVC++ */ 
  #define _CRTDBG_MAP_ALLOC
  #include <stdlib.h>
  #include <crtdbg.h>
#else
  #include <stdlib.h>
#endif

#include "diCrPKI.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#ifdef NDEBUG
/* Make sure assertion testing is turned on */
  #undef NDEBUG
#endif
#include <assert.h>

/* In-line instructions to link to the library. 
 * This works for us in MSVC and Borland C
 * -- you may need to do otherwise.
 */
#if _MSC_VER >= 1400 
/* This should work in MS VS2005 and above */
#if _WIN64
  #if _DEBUG
  #define MYLIBDIR "../x64/Debug/"
  #else
  #define MYLIBDIR "../x64/Release/"
  #endif
#else
  #if _DEBUG
  #define MYLIBDIR "../Debug/"
  #else
  #define MYLIBDIR "../Release/"
  #endif
#endif
#pragma comment(lib, MYLIBDIR "diCrPKI.lib")
#elif defined(_WIN32) || defined(WIN32) || defined(__BORLANDC__)
/* Link to library in same dir as this source.
 * This works in old MSVC++ and Borland.
 */
#pragma comment(lib, ".\\diCrPKI.lib")
#endif
#if defined(__BORLANDC__)
#pragma warn -8004     /* "'foo' is assigned a value that is never used" */
#endif


/* SYSTEM-SPECIFIC DIR FNS:
 * Used in `make_new_test_dir` and `remove_test_dir` only.
 */
#ifdef unix
  #define DELCMD "rm"
#else
  #define DELCMD "DEL"
#endif
#ifdef _MSC_VER
  /* MSVC functions */
  #include <direct.h>
  #define MKDIR(d) _mkdir(d)
  #define CHDIR(d) _chdir(d)
  #define RMDIR(d) _rmdir(d)
  #define GETCWD(od, n) _getcwd(od, n)
#elif __BORLANDC__
  /* Borland functions */
  #include <dir.h>
  #define MKDIR(d) mkdir(d)
  #define CHDIR(d) chdir(d)
  #define RMDIR(d) rmdir(d)
  #define GETCWD(od, n) getcwd(od, n)
#elif unix
  /* Linux functions */
  #include <unistd.h>
  #include <sys/stat.h>
  #include <sys/types.h>
  #define MKDIR(d) mkdir(d, 0777)
  #define CHDIR(d) chdir(d)
  #define RMDIR(d) rmdir(d)
  #define GETCWD(od, n) getcwd(od, n)
#else
  /* Take a punt the compiler has inbuilt fns or die!
     -- replace with your own here if necessary */
  #define MKDIR(d) mkdir(d)
  #define CHDIR(d) chdir(d)
  #define RMDIR(d) rmdir(d)
  #define GETCWD(od, n) getcwd(od, n)
#endif


/* Global variables */
char testdir[FILENAME_MAX];
char old_cwd[FILENAME_MAX];
char new_cwd[FILENAME_MAX];

int no_prompt = 0;

/*********************************/
/* SETUP AND UTILITIES FOR TESTS */
/*********************************/

void check_user_options(int argc, char *argv[])
{
  /* Deal with command-line options:
  [no-prompt]
  (we use this when debugging)
  */
  while (argc > 1)
  {
    if (strcmp(argv[1], "no-prompt") == 0)
      no_prompt = 1;
    else
      fprintf(stderr, "Invalid option '%s'\n", argv[1]);

    argc--;
    argv++;
  }
}

int make_new_test_dir(void)
{
  /* 
  (1) Try and create a sub-dir in current working dir
  (2) If that fails, use _tempnam
  */

  unsigned char rbytes[4];
  char hex[9];

  /* Use PKI to generate a random address */
  RNG_Bytes(rbytes, sizeof(rbytes), NULL, 0);
  HASH_HexFromBytes(hex, sizeof(hex)-1, rbytes, sizeof(rbytes), 0);

  /* Create a test directory */
  sprintf(testdir, "pkitest.%s", hex);
  printf("Trying to create test directory '%s'...\n", testdir);
  
  /* Use system-specific fns to create and set as default dir */
  if (MKDIR(testdir) != 0)
#ifdef _MSC_VER
  { /* Check added in version 2.2 in case we run this where we don't have permission */
    char *tempname;
    printf("Unable to create test directory '%s'. Trying with _tempnam...\n", testdir);
    /* Now try using _tempnam (making a copy) */
    tempname = _tempnam("\\", "pki");
    strncpy(testdir, tempname, FILENAME_MAX-1);
    free(tempname); /* _tempnam uses malloc */
    if (MKDIR(testdir) != 0)
    {
      fprintf(stderr, "ERROR: unable to create a temp directory.");
      exit(EXIT_FAILURE);
    }
  }
#else /* If not MSVC, we have just failed */
  {
    fprintf(stderr, "ERROR: unable to create a temp directory.");
    exit(EXIT_FAILURE);
  }
#endif

  printf("Created test directory '%s' OK.\n", testdir);

  /* Remember current working directory */
  GETCWD(old_cwd, FILENAME_MAX-1);

  /* Change CWD to our new temp dir */
  CHDIR(testdir);

  /* And check */
  GETCWD(new_cwd, FILENAME_MAX-1);
  printf("Current dir is '%s'\n", new_cwd);

  return 0;
}

void remove_test_dir(char *dirname, char *old_cwd)
{
  /* Use system commands to do the business 
     --see DELCMD macro above (NB strings concatenate) */
  /* CAUTION: Use this carefully */
  int res;

  CHDIR(dirname);
  system (DELCMD" *.txt");
  system (DELCMD" *.cer");
  system (DELCMD" *.bin");
  system (DELCMD" *.pfx");
  system (DELCMD" *.p10");
  system (DELCMD" *.p7*");
  system (DELCMD" *.xml");
  system (DELCMD" *.dat");
  system (DELCMD" *.crl");
  /* Go back to the CWD we stored at the start */
  if (!old_cwd)
    CHDIR("..");
  else
    CHDIR(old_cwd);
  res = RMDIR(dirname);
  if (res == 0)
    printf("Removed test directory OK.\n");
  else
    printf("ERROR: (%d) failed to remove test directory.\n", res);
}

void create_test_files(void)
/* Create the test files we expect to find */
{
  FILE *fp;
  unsigned char buf[PKI_RNG_SEED_BYTES];

  /* 1. SafeToDelete.txt */
  fp = fopen("!ThisFolderIsSafeToDelete.txt", "wb");
  assert(fp != NULL);
  fprintf(fp, "This folder has been created by the CryptoSys PKI test program.\nIt can be safely deleted at any time.");
  fclose(fp);

  /* 2. Excontent.txt */
  fp = fopen("excontent.txt", "wb");
  assert(fp != NULL);
  fprintf(fp, "This is some sample content.");
  fclose(fp);

  /* 3. seed.dat
  -- this is a fudge to avoid the prompt! 
  You should use RNG_MakeSeedFile to create a proper RNG seed file */
  RNG_Bytes(buf, sizeof(buf), NULL, 0);
  fp = fopen("seed.dat", "wb");
  assert(fp != NULL);
  fwrite(buf, sizeof(buf), 1, fp);
  fclose(fp);
}

char *lookup_error(int errcode)
/* Looks up description of error msg and returns
   ptr to static string
*/
{
  static char errmsg[128];

  errmsg[0] = '\0';
  PKI_ErrorLookup(errmsg, sizeof(errmsg), errcode);

  return errmsg;
}

static void disp_error(long result)
{
  char szErrMsg[512];
  long errcode;

  PKI_LastError(szErrMsg, sizeof(szErrMsg)-1);
  errcode = PKI_ErrorCode();
  printf("ERROR Returned=%ld/ErrorCode=%ld: %s; %s\n", result, PKI_ErrorCode(),
    lookup_error(errcode), szErrMsg);
}

static void pr_bytes(unsigned char *bytes, long nbytes)
{
  long i;

  for (i = 0; i < nbytes; i++)
  {
    if (i && (i % 32) == 0)
      printf("\n");
    else if (i && (i % 4) == 0)
      printf(" ");
    printf("%02x", *bytes++);
  }
  printf("\n");
}

static void pr_byteshex(const char *pre, const void *bytes, size_t nbytes, const char *post)
{
  size_t i;
  const int b_perword = 4;
  const int b_perline = 32;
  const unsigned char *pb = (const unsigned char *)bytes;
  if (pre) printf("%s", pre);
  for (i = 0; i < nbytes; i++)
  {
    if (i && (i % b_perline) == 0)
      printf("\n");
    else if (i && (i % b_perword) == 0)
      printf(" ");
    printf("%02x", *pb++);
  }
  if (post) printf("%s", post);
}

static void pr_textfile(const char *fname)
/* Print the contents of a text file */
{
  FILE *fp;
  int c;
  fp = fopen(fname, "rb");
  if (!fp) return;
  while ((c = fgetc(fp)) != EOF)
  {
    printf("%c", c);
  }
  fclose(fp);
}

static void dump_and_display(const char *certname)
{
  /* Dump the details of the certificate to a text file then display it */
  long result = X509_TextDump("dumpfile.txt", certname, 0);
  if (result == 0)
    pr_textfile("dumpfile.txt");
  else
    printf("ERROR: canot do X.509 text dump for file '%s'\n", certname);
}

static int cmp_files(char *file1, char *file2)
/* Compares two binary files: returns 0 if identical 
   or 1 if not identical or -1 if file error */
{
  FILE *fp1, *fp2;
  int c1, c2;
  long len1, len2;
  int result = 0; /* Innocent until proven guilty */

  fp1 = fopen(file1, "rb");
  if (fp1 == NULL) 
    return -1;
  fp2 = fopen(file2, "rb");
  if (fp2 == NULL)
  {
    fclose(fp1);
    return -1;
  }

  /* Check lengths are equal */
  if (fseek(fp1, 0, SEEK_END) || fseek(fp2, 0, SEEK_END))
  {
    fclose(fp1);
    fclose(fp2);
    return -1;
  }
  len1 = ftell(fp1);
  len2 = ftell(fp2);
  if (len1 < 0 || len2 < 0) result = -1;
  if (len1 != len2) result = 1;
  if (result != 0)
  {
    fclose(fp1);
    fclose(fp2);
    return result;
  }
  rewind(fp1);
  rewind(fp2);

  while ((c1 = fgetc(fp1)) != EOF)
  {
    c2 = fgetc(fp2);
    if (c2 == EOF)
    { /* File 2 is shorter than file 1 */
      result = 1;
      break;
    }
    if (c1 != c2)
    { /* Found a mis-match */
      result = 1;
      break;
    }
  }

  if (feof(fp1))
  { /* Make sure file2 is same length */
    if ((c2 = fgetc(fp2)) != EOF)
      result = 1;
  }

  fclose(fp1);
  fclose(fp2);

  return result;
}

/****************/
/* TESTS PROPER */
/****************/

void show_version(void)
{
  long result;
  char timestamp[256];
  char platform[16];
  char dllname[FILENAME_MAX];
  
  printf("\nVersion information, etc...\n");
  result = PKI_Version(0, 0);
  assert(result > 0);
  printf("PKI_Version=%ld\n", result);
  
  result = PKI_LicenceType(0);
  assert(result > 0);
  printf("PKI_LicenceType: %c\n", (char)result);

  result = PKI_ModuleName(dllname, sizeof(dllname)-1, 0);
  assert(result > 0);
  printf("Core DLL = [%s]\n", dllname);

  result = PKI_CompileTime(timestamp, sizeof(timestamp)-1);
  assert(result > 0);
  printf("Toolkit last compiled [%s]\n", timestamp);

  result = PKI_LicenceType(PKI_GEN_PLATFORM);
  printf("IsWin64=%ld\n", result);

  result = PKI_ModuleName(platform, sizeof(platform)-1, PKI_GEN_PLATFORM);
  assert(result > 0);
  printf("Platform='%s'\n", platform);
}

void init_rng(void)
{
/* Assumes a seed file has been created earlier (using RNG_MakeSeedFile) */
  long result;
  const char *fname = "seed.dat";

  printf("\nInitialize the random number generator from a seed file...\n");

  result = RNG_Initialize(fname, 0);
  printf("RNG_Initialize('%s') returns %ld (expected 0)\n", fname, result);
  assert(result == 0);
}

void carol_creates_keys(void)
/* Carol creates a 1024-bit RSA key pair with
   the private key encrypted with password 'password'
*/
{
  char *pubfile = "carol_pub.bin";
  char *epkfile = "carol_epk.bin";
  char *szPassword = "password";
  long result;
  time_t start, finish;
  double duration;

  printf("\nCarol creates a 1024-bit RSA key pair...\n");

  start = clock();
  result = RSA_MakeKeys(pubfile, epkfile, 1024, PKI_RSAEXP_EQ_65537, 50, 
    1000, szPassword, NULL, 0, PKI_KEYGEN_INDICATE);
  finish = clock();
  assert(result == 0);

  duration = (double)(finish - start) / CLOCKS_PER_SEC;
  printf("Time=%.3lf secs\n", duration);
  printf("...created key files '%s' and '%s'\n", pubfile, epkfile);
}

void carol_creates_ca_cert(void)
{
  char *certfile = "carol.cer";
  char *epkfile = "carol_epk.bin";
  char *password = "password";
  char *distname = "C=AU;O=Example Corp;CN=Carol";
  char *extns = "subjectKeyIdentifier=deadbeef;rfc822Name=carol@example.com";
  long result;

  printf("\nCarol creates a self-signed CA certificate...\n");

  /* Make an self-signed cert No. 1 valid for 10 years */
  result = X509_MakeCertSelf(certfile, epkfile, 
    1, 10, distname, extns, 0, password, PKI_X509_CA_TRUE);
  assert(result == 0);
  printf("Created self-signed X.509 certificate '%s'\n", certfile);

  /* Use X509_TextDump to dump details to a file then display them */
  dump_and_display(certfile);
}

void ann_and_ben_create_keys(void)
/* Ann and Ben both create 512-bit key pairs 
(NB we use 512 for speed in this demo)
both using 'password' as the password
(but they don't tell each other or Carol this fact!)
Ben's private key is secured using "pkcs5PBES2"/"des-EDE3-CBC" 
instead of the default "pbeWithSHAAnd3-KeyTripleDES-CBC".
*/
{
  char *annpubfile = "ann_pub.bin";
  char *annepkfile = "ann_epk.bin";
  char *benpubfile = "ben_pub.bin";
  char *benepkfile = "ben_epk.bin";
  char *szPassword = "password";
  long keybits = 512;
  long result;
  time_t start, finish;
  double duration;

  printf("\nCreating 512-bit key pair for Ann...\n");
  start = clock();
  result = RSA_MakeKeys(annpubfile, annepkfile, keybits, PKI_RSAEXP_EQ_5, 50, 
    1000, szPassword, NULL, 0, 0);
  finish = clock();
  assert(result == 0);
  duration = (double)(finish - start) / CLOCKS_PER_SEC;
  printf("Time=%.3lf secs\n", duration);
  printf("...created key files '%s' and '%s'\n", annpubfile, annepkfile);

  printf("\nCreating 512-bit key pair for Ben...\n");
  start = clock();
  result = RSA_MakeKeys(benpubfile, benepkfile, keybits, PKI_RSAEXP_EQ_3, 50, 
    1000, szPassword, NULL, 0, PKI_PBES2_3DES);
  finish = clock();
  assert(result == 0);
  duration = (double)(finish - start) / CLOCKS_PER_SEC;
  printf("Time=%.3lf secs\n", duration);
  printf("...created key files '%s' and '%s'\n", benpubfile, benepkfile);
}

void carol_makes_certs_for_ann_and_ben(void)
{
  long result;
  char *acertfile = "ann.cer";
  char *bcertfile = "ben.cer";
  char *issuercert = "carol.cer";
  char *epkfile = "carol_epk.bin";
  char *szPassword = "password";

  printf("\nCarol makes X.509 certificates for Ann and Ben...\n");

  /* Make two end-user certs valid for 2 years, signed by Carol */
  result = X509_MakeCert(acertfile, issuercert, "ann_pub.bin", epkfile, 
    0x101, 2, "C=AU;O=Example Corp;CN=Ann", NULL, 0, szPassword, 0);
  assert(result == 0);
  printf("Created end-user X.509 certificate '%s'\n", acertfile);

  result = X509_MakeCert(bcertfile, issuercert, "ben_pub.bin", epkfile, 
    0x102, 2, "C=AU;O=Example Corp;CN=Ben", "ben@example.com", 0, szPassword, 
    PKI_SIG_MD5RSA);
  assert(result == 0);
  printf("Created end-user X.509 certificate '%s'\n", bcertfile);
}

void ben_tests_his_keys_in_the_raw(void)
/* Ben uses the "raw" RSA functions to encrypt and sign some random data */
{
  long result;
  char *pubfile = "ben_pub.bin";
  char *epkfile = "ben_epk.bin";
  char *szPassword = "password";
  long datalen;
  unsigned char *data, *copy;
  char *pubkeystr, *prikeystr;
  long klen;

  printf("\nBen tests his keys using the 'raw' RSA functions...\n");

  /* Read in the keys to strings */
  klen = RSA_ReadPublicKey(NULL, 0, pubfile, 0);
  assert(klen > 0);
  pubkeystr = malloc(klen+1); /* NB one extra */
  assert(pubkeystr != NULL);
  result = RSA_ReadPublicKey(pubkeystr, klen, pubfile, 0);
  assert(result == klen);
  klen = RSA_ReadEncPrivateKey(NULL, 0, epkfile, szPassword, 0);
  assert(klen > 0);
  prikeystr = malloc(klen+1); /* NB one extra */
  assert(prikeystr != NULL);
  result = RSA_ReadEncPrivateKey(prikeystr, klen, epkfile, szPassword, 0);
  assert(result == klen);

  /* Generate some random data, m, to test it with */
  datalen = RSA_KeyBytes(pubkeystr);
  data = malloc(datalen);
  assert(data != NULL);
  result = RNG_Bytes(data, datalen, "", 0);
  assert (result == 0);
  /* But make sure m < n */
  data[0] = 0x0;
  printf("Random data (%ld bytes)=\n", datalen);
  pr_bytes(data, datalen);
  /* Keep a copy for testing */
  copy = malloc(datalen);
  assert(copy != NULL);
  memcpy(copy, data, datalen);

  /* Encrypt with the public key */
  result = RSA_RawPublic(data, datalen, pubkeystr, 0);
  assert (result == 0);
  printf("Encrypted data=\n");
  pr_bytes(data, datalen);
  /* Decrypt with the private key */
  result = RSA_RawPrivate(data, datalen, prikeystr, 0);
  /* check */
  if (memcmp(copy, data, datalen) == 0)
    printf("Encrypt/decrypt worked OK\n");
  else
    printf("Encrypt/decrypt FAILED!\n");
  
  /* Sign: Encrypt with the private key */
  result = RSA_RawPrivate(data, datalen, prikeystr, 0);
  assert (result == 0);
  printf("Signed data=\n");
  pr_bytes(data, datalen);
  /* Verify: Decrypt with the public key */
  result = RSA_RawPublic(data, datalen, pubkeystr, 0);
  /* check */
  if (memcmp(copy, data, datalen) == 0)
    printf("Sign/verify worked OK\n");
  else
    printf("Sign/verify FAILED!\n");

  /* Make sure we wipe the private key string */
  WIPE_Data(prikeystr, (long)strlen(prikeystr));

  free(copy);
  free(data);
  free(pubkeystr);
  free(prikeystr);
}

void ann_sends_enc_msg_to_ben(void)
{
  long result;
  char *cmsfile = "cmsforben.p7m";
  char *datafile = "excontent.txt";
  char *bcertfile = "ben.cer";
  
  printf("\nAnn sends an encrypted message to Ben...\n");

  result = CMS_MakeEnvData(cmsfile, datafile, bcertfile, NULL, 0, 0);
  /* NB expecting # recipients == 1 to be returned */
  assert(result == 1);
  printf("Created CMS enveloped-data file '%s'\n", cmsfile);
}

void ben_decrypts_msg_from_ann(void)
{
  long result;
  char *cmsfile = "cmsforben.p7m";
  char *outfile = "textfromann.txt";
  char *chkfile = "excontent.txt";
  char *epkfile = "ben_epk.bin";
  char *szPassword = "password";
  char *prikeystr = NULL;
  long klen;
  
  printf("\nBen decrypts the message received from Ann...\n");

  /* Get Ben's private key */
  klen = RSA_ReadEncPrivateKey(NULL, 0, epkfile, szPassword, 0);
  assert(klen > 0);
  prikeystr = malloc(klen+1); /* NB one extra */
  assert(prikeystr != NULL);
  result = RSA_ReadEncPrivateKey(prikeystr, klen, epkfile, szPassword, 0);
  assert(result == klen);
  
  /* Decrypt the file */
  result = CMS_ReadEnvData(outfile, cmsfile, NULL, prikeystr, 0);
  if (!(result == 0)) disp_error(result);
  assert(result == 0);
  printf("Decrypted CMS enveloped-data to output file '%s'\n", outfile);

  free(prikeystr);

  /* Check we got what we started with */
  if (cmp_files(outfile, chkfile) == 0)
    printf("Decryption OK - files match.\n");
  else
    printf("Decryption failed - not what we started with!\n");
}


char *get_rsa_private_key(char *keyfile, char *password, long *pklen)
/* One-step wrapper function to get private key string from EPK file */
/* NB allocates memory - must be freed later by caller */
{
  long klen;
  char *key;

  klen = RSA_ReadEncPrivateKey(NULL, 0, keyfile, password, 0);
  if (klen <= 0) return NULL;
  key = malloc(klen+1); /* NB one extra */
  assert(key != NULL);
  klen = RSA_ReadEncPrivateKey(key, klen, keyfile, password, 0);
  
  /* If provided, set key length */
  if (pklen) *pklen = klen;
  return key;
}

void ann_sends_encmsg_ben_reads(void)
{
  long result;
  char *cmsfile = "forcarolandben.p7m";
  char *bcertfile = "ben.cer";
  char *certlist = "carol.cer;ben.cer";
  char *msgtosend = "Be in bar at 7 pm wearing a red rose";
  char *prikeystr = NULL;
  char *msgrecvd;
  long mlen;
  
  printf("\nAnn sends an encrypted message and Ben reads it...\n");

  /* Ann creates a CMS message file, this time from a string 
     that could be read by either Carol or Ben (see certlist) */
  result = CMS_MakeEnvDataFromString(cmsfile, msgtosend, certlist, NULL, 0, 0);
  /* NB expecting # recipients == 2 to be returned */
  assert(result == 2);
  printf("Created CMS enveloped-data file '%s'\n", cmsfile);

  /* Ben reads it directly into a string, using his private key */
  prikeystr = get_rsa_private_key("ben_epk.bin", "password", NULL);
  assert(prikeystr != NULL);
  /* How long is it? */
  mlen = CMS_ReadEnvDataToString(NULL, 0, cmsfile, bcertfile, prikeystr, 0);
  printf("CMS_ReadEnvDataIntoString returns %ld\n", mlen);
  assert(mlen > 0);
  msgrecvd = malloc(mlen+1);  /* NB one extra */
  mlen = CMS_ReadEnvDataToString(msgrecvd, mlen, cmsfile, bcertfile, prikeystr, 0);
  assert(mlen > 0);
  printf("Message is '%s'\n", msgrecvd);
  /* Check they are the same */
  result = strcmp(msgrecvd, msgtosend);
  if (result == 0)
    printf("SUCCESS: messages match.\n");
  else
    printf("ERROR: messages do not match!\n");
  assert(result == 0);

  /* clean up */
  free(prikeystr);
  free(msgrecvd);

}

void ann_sends_encr_base64_ben_reads(void)
{
  long result;
  char *cmsfile = "forbenp7m.txt";
  char *bcertfile = "ben.cer";
  char *msgtosend = "Be in bar at 7 pm wearing a red rose";
  char *prikeystr = NULL;
  char *msgrecvd;
  long mlen;
  
  printf("\nAnn sends an encrypted message in base64 format...\n");

  /* Ann creates a CMS message file in base64 format */
  result = CMS_MakeEnvDataFromString(cmsfile, msgtosend, bcertfile, NULL, 0, PKI_CMS_FORMAT_BASE64);
  /* NB expecting # recipients == 1 to be returned */
  assert(result == 1);
  printf("Created CMS enveloped-data file '%s'\n", cmsfile);

  /* Ben reads it directly into a string */
  prikeystr = get_rsa_private_key("ben_epk.bin", "password", NULL);
  assert(prikeystr != NULL);
  /* How long is it? */
  mlen = CMS_ReadEnvDataToString(NULL, 0, cmsfile, bcertfile, prikeystr, PKI_CMS_FORMAT_BASE64);
  printf("CMS_ReadEnvDataIntoString returns %ld\n", mlen);
  if (mlen <= 0) disp_error(mlen);
  assert(mlen > 0);
  msgrecvd = malloc(mlen+1);  /* NB one extra */
  mlen = CMS_ReadEnvDataToString(msgrecvd, mlen, cmsfile, bcertfile, prikeystr, PKI_CMS_FORMAT_BASE64);
  if (mlen <= 0) disp_error(mlen);
  assert(mlen > 0);
  printf("Message is '%s'\n", msgrecvd);
  /* Check they are the same */
  result = strcmp(msgrecvd, msgtosend);
  if (result == 0)
    printf("SUCCESS: messages match.\n");
  else
    printf("ERROR: messages do not match!\n");
  assert(result == 0);

  /* clean up */
  free(prikeystr);
  free(msgrecvd);

}

void ann_signs_msg(void)
{
  long result;
  char *sigfile = "signedbyann.p7m";
  char *infile = "excontent.txt";
  char *epkfile = "ann_epk.bin";
  char *certfile = "ann.cer";
  char *szPassword = "password";
  char *prikeystr = NULL;
  long klen;

  printf("\nAnn signs a message...\n");

  prikeystr = get_rsa_private_key(epkfile, szPassword, &klen);
  assert(prikeystr != NULL);
  result = CMS_MakeSigData(sigfile, infile, certfile, prikeystr, 0);
  if (result) disp_error(result);
  assert(result == 0);
  printf("Created CMS signed-data file '%s'\n", sigfile);

  free(prikeystr);
}

void ben_verifies_msg_from_ann(void)
/* Actually could be verified by anybody who has Ann's cert */
{
  char *sigfile = "signedbyann.p7m";
  char *data = NULL;
  long datalen;
  char hexdigest[41];
  char hashcontent[41];
  long hashlen;
  long hashalg;

  printf("\nBen verifies the signed message received from Ann...\n");

  /* 1. Get the content data out of the signed-data file */
  datalen = CMS_ReadSigDataToString(NULL, 0, sigfile, 0);
  printf("CMS_ReadSigDataToString returns %ld\n", datalen);
  assert(datalen > 0);
  data = malloc(datalen+1);
  assert(data != NULL);
  datalen = CMS_ReadSigDataToString(data, datalen, sigfile, 0);
  printf("Signed content is '%s'\n", data);

  /* 2. Get the message digest hash from signed-data */
  hashalg = CMS_GetSigDataDigest(hexdigest, sizeof(hexdigest)-1, sigfile, NULL, 0);
  printf("CMS_ReadSigDataToString returns %ld\n", datalen);
  assert(hashalg >= 0);
  printf("Signed digest  = %s\n", hexdigest);

  /* 3. Make an independent hash of content */
  hashlen = HASH_HexFromBytes(hashcontent, sizeof(hashcontent)-1, data, datalen, hashalg);
  printf("Content digest = %s\n", hashcontent);
  
  /* 4. Compare the hash digests */
  if (strncmp(hashcontent, hexdigest, hashlen) == 0)
    printf("SUCCESS: Message digests match\n");
  else
    printf("FAILURE: digests do not match!\n");

  /* Clean up */
  free(data);
}

void ben_reads_signed_data(void)
/* Ben reads the signed-data from Ann, saving as a file */
{
  char *sigfile = "signedbyann.p7m";
  char *outfile = "signedfromann.txt";
  char *origfile = "excontent.txt";
  long result;

  printf("\nBen reads the signed-data received from Ann...\n");

  result = CMS_ReadSigData(outfile, sigfile, 0);
  printf("CMS_ReadSigData returns %ld\n", result);

  /* Compare this file with original */
  result = cmp_files(outfile, origfile);
  if (result == 0)
    printf("SUCCESS: signed data matches original file\n");
  else
    printf("FAILURE: signed data does NOT match original file\n");
  assert(result == 0);
}
  

void ann_signs_base64_ben_verifies(void)
/* Ann creates a signed-data object in base64 format; Ben verifies it. */
{
  long result;
  char *szContent = "This is some sample content.";
  char *sigfile = "signedbyannp7m.txt";
  char *epkfile = "ann_epk.bin";
  char *certfile = "ann.cer";
  char *szPassword = "password";
  char *prikeystr = NULL;
  long klen;
  char *data = NULL;
  long datalen;
  char hexdigest[41];
  char hashcontent[41];
  long hashlen;
  long hashalg;

  printf("\nAnn creates a signed-data object in base64 format and Ben verifies it...\n");

  prikeystr = get_rsa_private_key(epkfile, szPassword, &klen);
  assert(prikeystr != NULL);
  result = CMS_MakeSigDataFromString(sigfile, szContent, certfile, prikeystr, PKI_CMS_FORMAT_BASE64);
  if (result) disp_error(result);
  assert(result == 0);
  printf("Created base64 CMS signed-data file '%s' from string\n", sigfile);

  free(prikeystr);

  /* 1. Get the content data out of the signed-data file */
  datalen = CMS_ReadSigDataToString(NULL, 0, sigfile, PKI_CMS_FORMAT_BASE64);
  printf("CMS_ReadSigDataToString returns %ld\n", datalen);
  if (datalen <= 0) disp_error(datalen);
  assert(datalen > 0);
  data = malloc(datalen+1);
  assert(data != NULL);
  datalen = CMS_ReadSigDataToString(data, datalen, sigfile, PKI_CMS_FORMAT_BASE64);
  if (datalen <= 0) disp_error(datalen);
  assert(datalen > 0);
  printf("Signed content is '%s'\n", data);

  /* 2. Get the message digest hash from signed-data */
  hashalg = CMS_GetSigDataDigest(hexdigest, sizeof(hexdigest)-1, sigfile, NULL, PKI_CMS_FORMAT_BASE64);
  printf("CMS_GetSigDataDigest returns %ld\n", datalen);
  if (hashalg < 0) disp_error(hashalg);
  assert(hashalg >= 0);
  printf("Signed digest  = %s\n", hexdigest);

  /* 3. Make an independent hash of content */
  hashlen = HASH_HexFromBytes(hashcontent, sizeof(hashcontent)-1, data, datalen, hashalg);
  printf("Content digest = %s\n", hashcontent);
  
  /* 4. Compare the hash digests */
  if (strncmp(hashcontent, hexdigest, hashlen) == 0)
    printf("SUCCESS: Message digests match\n");
  else
    printf("FAILURE: digests do not match!\n");

  /* Alternatively, just verify (new in version 2.8) */
  result = CMS_VerifySigData(sigfile, "", "", PKI_CMS_FORMAT_BASE64);
  printf("CMS_VerifySigData returns %ld (expecting 0 => verified signature and content OK)\n", result);
  if (result != 0) disp_error(result);
  assert(result == 0);

  /* And while we're here, we'll query the signed data file (new v2.8) */
  datalen = CMS_QuerySigData(NULL, 0, sigfile, "signatureAlgorithm", PKI_CMS_FORMAT_BASE64);
  printf("CMS_QuerySigData returns %ld\n", datalen);
  assert(datalen > 0);
  data = realloc(data, datalen+1);
  datalen = CMS_QuerySigData(data, datalen, sigfile, "signatureAlgorithm", PKI_CMS_FORMAT_BASE64);
  printf("signatureAlgorithm='%s'\n", data);

  /* Clean up */
  free(data);
}


#define RAND_DATA_LEN 32

void ben_signs_det_msg(void)
{
  char *detsigfile = "detsigbyben.p7s";
  char *epkfile = "ben_epk.bin";
  char *certfile = "ben.cer";
  char *datafile = "datafromben.txt";
  char data[RAND_DATA_LEN+1] = { 0 };
  const int datalen = RAND_DATA_LEN;
  char hexdigest[41];
  char *prikey;
  long klen;
  long result;

  int i, cr;
  FILE *fp;

  printf("\nBen signs a detached message...\n");

  /* Ben creates some random text */
  srand((unsigned)time(NULL));
  for (i = 0; i < datalen; i++)
  {
    /* Random printable characters between 32 and 127 */
    cr = (rand() % 95) + ' ';
    data[i] = cr;
  }
  data[i] = 0;
  printf("Ben's random text is '%s'\n", data);

  /* Save to a file */
  fp = fopen(datafile, "w");
  assert(fp != NULL);
  fwrite(data, 1, RAND_DATA_LEN, fp);
  fclose(fp);

  /* Compute hash digest using MD5 */
  HASH_HexFromBytes(hexdigest, sizeof(hexdigest)-1, (unsigned char*)data, datalen, PKI_HASH_MD5);
  printf("MD5(data)=%s\n", hexdigest);

  /* Get private key */
  prikey = get_rsa_private_key(epkfile, "password", &klen);
  assert(prikey != NULL);
  /* Make a detached signature file using MD5 */
  result = CMS_MakeDetachedSig(detsigfile, hexdigest, certfile, prikey, PKI_HASH_MD5);
  if (result != 0) disp_error(result);
  printf("Created detached signature file '%s'\n", detsigfile);
  
  free(prikey);
}

void ann_verifies_det_msg_from_ben(void)
/* Ben must send both the original content and the detached signature to ann */
{
  char *datafile = "datafromben.txt";
  char *detsigfile = "detsigbyben.p7s";
  char *data = NULL;
  long datalen;
  char hexdigest[41];
  char hashcontent[41];
  long hashlen;
  long hashalg;
  FILE *fp;

  printf("\nAnn verifies the detached signature received from Ben...\n");

  /* 1. Get the content data out of the separate file */
  fp = fopen(datafile, "r");
  assert(fp != NULL);
  for (datalen = 0; fgetc(fp) != EOF; datalen++)
    ;
  rewind(fp);
  data = malloc(datalen);
  assert(data != NULL);
  fread(data, 1, datalen, fp);
  fclose(fp);

  /* 2. Get the message digest hash from signed-data */
  hashalg = CMS_GetSigDataDigest(hexdigest, sizeof(hexdigest)-1, detsigfile, NULL, 0);
  printf("CMS_ReadSigDataToString returns %ld\n", datalen);
  assert(hashalg >= 0);
  printf("Signed digest  = %s\n", hexdigest);

  /* 3. Make an independent hash of content */
  hashlen = HASH_HexFromBytes(hashcontent, sizeof(hashcontent)-1, data, datalen, hashalg);
  printf("Content digest = %s\n", hashcontent);
  
  /* 4. Compare the hash digests */
  if (strncmp(hashcontent, hexdigest, hashlen) == 0)
    printf("SUCCESS: Message digests match\n");
  else
    printf("FAILURE: digests do not match!\n");

  /* Clean up */
  free(data);

}

void ben_makes_crs(void)
/* Ben prepares a certificate signing request in both PEM and binary formats */
{
  char *reqfile = "ben_crs.txt";
  char *reqfilebin = "ben_crs.bin";
  char *epkfile = "ben_epk.bin";
  char *password = "password";
  char *dn = "CN=ben;O=Example Corp;OU=Bens Unit;L=Bensville;S=California;C=US";
  long result;

  printf("\nBen prepares a certificate signing request (CRS)...\n");

  result =  X509_CertRequest(reqfile, epkfile, dn, "", password, 0);
  assert(result == 0);
  printf("Created certificate request '%s'\n", reqfile);

  result =  X509_CertRequest(reqfilebin, epkfile, dn, "", password, PKI_X509_FORMAT_BIN);
  assert(result == 0);
  printf("Created binary-format certificate request '%s'\n", reqfilebin);
}

void ann_verifies_her_cert(void)
{
  char *certfile = "ann.cer";
  char *issuercert = "carol.cer";
  char *benscert = "ben.cer";
  char thumb[41];
  char digest[41];
  long diglen;
  unsigned char *data;
  long datalen;
  FILE *fp;
  int c;
  long result;
  char szDate[32];
  time_t ltime;
  struct tm *tmnow;

  printf("\nAnn verifies her certificate...\n");
  
  /* Check dates */
  result = X509_CertIssuedOn(certfile, szDate, sizeof(szDate)-1, 0);
  printf("X509_CertIssuedOn returns %ld: ", result);
  if (result > 0)
    printf("%s\n", szDate);
  else
    printf("FAILURE: Unable to find ValidFrom date in %s.\n", certfile);
  assert(result > 0);

  result = X509_CertExpiresOn(certfile, szDate, sizeof(szDate)-1, 0);
  printf("X509_CertExpiresOn returns %ld: ", result);
  if (result > 0)
    printf("%s\n", szDate);
  else
    printf("FAILURE: Unable to find ValidTo date in %s.\n", certfile);
  assert(result > 0);

  /* Check system clock */
  time(&ltime);
  tmnow = gmtime(&ltime);
  printf("Time now is %04d-%02d-%02dT%02d:%02d:%02dZ\n", 
    tmnow->tm_year+1900, tmnow->tm_mon+1, tmnow->tm_mday,
    tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec);

  /* Catch cert created in the first minute of the hour (which will not yet be valid) */
  if (0 == tmnow->tm_min)
    printf("Skipping cert validity test as we are in first minute of the hour.\n");
  else
  {
    /* Is Ann's cert valid? */
    result = X509_CertIsValidNow(certfile, 0);
    printf("X509_CertIsValidNow returns %ld: ", result);
    if (result == 0)
      printf("Certificate '%s' is valid now.\n", certfile);
    else
      printf("FAILURE: Certificate '%s' is NOT valid now.\n", certfile);
    assert(result == 0);
  }

  /* Was it issued by Carol? */
  result = X509_VerifyCert(certfile, issuercert, 0);
  printf("X509_VerifyCert returns %ld: ", result);
  if (result == 0)
    printf("Certificate '%s' was issued by '%s'.\n", certfile, issuercert);
  else
    printf("FAILURE: Certificate '%s' was NOT issued by '%s'.\n", certfile, issuercert);
  assert(result == 0);

  /* Was it issued by Ben? */
  result = X509_VerifyCert(certfile, benscert, 0);
  printf("X509_VerifyCert returns %ld: ", result);
  if (result == 0)
    printf("Certificate '%s' was issued by '%s'.\n", certfile, benscert);
  else
    printf("Certificate '%s' was NOT issued by '%s'.\n", certfile, benscert);
  assert(result == -1);

  /* What is the thumbprint? */
  result = X509_CertThumb(certfile, thumb, sizeof(thumb)-1, 0);
  assert(result > 0);
  printf("Ann's certificate has thumbprint: %s\n", thumb);

  /* Check it the long way... */
  /* (i) Read in the file to memory */
  fp = fopen(certfile, "rb");
  assert(fp != NULL);
  for (datalen = 0; (c = fgetc(fp)) != EOF; datalen++)
    ;
  data = malloc(datalen);
  assert(data != NULL);
  rewind(fp);
  fread(data, 1, datalen, fp);
  fclose(fp);
  /* (ii) Make a hash digest of the data */
  diglen = HASH_HexFromBytes(digest, sizeof(digest)-1, data, datalen, PKI_HASH_SHA1);
  assert(diglen > 0);
  printf("Hash digest of file data is     : %s\n", digest);
  free(data);
  /* (iii) Compare */
  result = strncmp(digest, thumb, diglen);
  if (result == 0)
    printf("SUCCESS: digest matches the 'thumbprint'\n");
  else
    printf("FAILURE: digest does not match thumbprint\n");
  assert(result == 0);
}

void ben_resaves_his_public_key(void)
/* How to save the public key in different formats.
   Note we don't need any security for operatons involving the PUBLIC key.
*/
{
  char *pbkfile = "ben_pub.bin";
  char *newpubk = "ben_pubssl.txt";
  char *xmlpubfile = "ben_pub.xml";
  long result;
  char *pubkeystr, *chkkeystr;
  char *xmlstr;
  long klen, nbits, xlen;
  FILE *fp;

  printf("\nBen reads and saves his RSA public key in different formats...\n");

  /* Read and re-save the public key */
  klen = RSA_ReadPublicKey(NULL, 0, pbkfile, 0);
  assert(klen >= 0);
  pubkeystr = malloc(klen+1); /* NB always one extra */
  assert(pubkeystr != NULL);
  klen = RSA_ReadPublicKey(pubkeystr, klen, pbkfile, 0);
  assert(klen >= 0);
  printf("Ben's public key string (in 'internal' format)=%s\n", pubkeystr);

  /* How long is the key? */
  nbits = RSA_KeyBits(pubkeystr);
  printf("Ben's public key is %ld bits long\n", nbits);

  /* Save again in OpenSSL-compatible format */
  result = RSA_SavePublicKey(newpubk, pubkeystr, PKI_KEY_FORMAT_SSL);
  assert(result == 0);
  printf("Saved as '%s'\n", newpubk);
  /* This is a text file in PEM-style format, so we can look at it */
  pr_textfile(newpubk);

  /* Read in the one we just made */
  klen = RSA_ReadPublicKey(NULL, 0, newpubk, 0);
  assert(klen >= 0);
  chkkeystr = malloc(klen+1);
  assert(chkkeystr != 0);
  klen = RSA_ReadPublicKey(chkkeystr, klen, newpubk, 0);
  assert(klen >= 0);
  /* Check against original */
  /* [v3.0] You can no longer just compare the internal strings as these are
  now encrypted as of version 3.0. Use RSA_KeyHashCode instead.
  */
  printf("RSA_KeyHashCode(original key)=%08lX\n", RSA_KeyHashCode(pubkeystr));
  printf("RSA_KeyHashCode(check key)   =%08lX\n", RSA_KeyHashCode(chkkeystr));
  result = (RSA_KeyHashCode(chkkeystr) == RSA_KeyHashCode(pubkeystr));
  if (result)
    printf("Private keys match OK\n");
  else
    printf("FAILURE: private keys do NOT match\n");
  assert(result);

  /* Convert public key string to XML format */
  xlen = RSA_ToXMLString(NULL, 0, pubkeystr, 0);
  assert(xlen > 0);
  xmlstr = malloc(xlen+1);  /* NB one extra for a string */
  assert(xmlstr != NULL);
  xlen = RSA_ToXMLString(xmlstr, xlen, pubkeystr, 0);
  assert(xlen > 0);
  printf("Public key in XML format=\n%s\n", xmlstr);
  /* Save as an XML file */
  fp = fopen(xmlpubfile, "w");
  assert(fp != NULL);
  fprintf(fp, "<?xml version=\"1.0\"?>\n");
  fprintf(fp, "%s\n", xmlstr);
  assert(fclose(fp) == 0);
  printf("Created file '%s'\n", xmlpubfile);

  free(pubkeystr);
  free(chkkeystr);
  free(xmlstr);
}

void ben_examines_his_private_key(void)
/* How to save your private key in different formats
   CAUTION: saving the private key in unencrypted "PrivateKeyInfo" format
   or in XML format should NEVER be done with a production key.
*/
{
  char *epkfile = "ben_epk.bin";
  char *prifile = "ben_pri.txt";
  char *xmlfile = "ben_pri.xml";
  char *certfile = "ben.cer";
  long result;
  char *prikeystr, *pubkeystr, *chkkeystr;
  char *xmlstr;
  long klen, nbits, nbits1, xlen;
  char password[256];
  long pwdlen, i;
  FILE *fp;

  printf("\nBen reads, checks and saves his RSA keys in different formats...\n");

  /* Prompt for private key password */
  printf("Prompt the user for the private-key password...\n");
  printf("Hint: the password is 'password'\n");
  pwdlen = PWD_Prompt(password, sizeof(password)-1, "Hint: Password is 'password'");
  
  /* Fail gently if wrong password entered */
  if (strcmp(password, "password"))
  {
    printf("Woops! That was the wrong password.\n");
    return;
  }

  /* Read encrypted private key into internal format */
  klen = RSA_ReadEncPrivateKey(NULL, 0, epkfile, password, 0);
  assert(klen >= 0);
  prikeystr = malloc(klen+1); /* NB always one extra */
  assert(prikeystr != NULL);
  klen = RSA_ReadEncPrivateKey(prikeystr, klen, epkfile, password, 0);
  assert(klen >= 0);
  /* CAUTION: you should never show this string in practice! */
  printf("Ben's private key string (in 'internal' format)=%s\n", prikeystr);

  /* How long is the key? */
  nbits = RSA_KeyBits(prikeystr);
  printf("Ben's private key is %ld bits long\n", nbits);
  assert(nbits > 0);

  /* Read in the public key from ben's certificate */
  klen = RSA_GetPublicKeyFromCert(NULL, 0, certfile, 0);
  assert(klen >= 0);
  pubkeystr = malloc(klen+1); /* NB always one extra */
  assert(pubkeystr != NULL);
  klen = RSA_GetPublicKeyFromCert(pubkeystr, klen, certfile, 0);
  assert(klen >= 0);
  /* This will be different every time you run this */
  printf("Ben's public key string (in 'internal' format)=%s\n", pubkeystr);

  /* How long is the key? */
  nbits1 = RSA_KeyBits(prikeystr);
  printf("Ben's public key is %ld bits long\n", nbits1);
  assert(nbits == nbits1);

  /* Check these two internal key strings match */
  result = RSA_KeyMatch(prikeystr, pubkeystr);
  printf("RSA_KeyMatch returns %ld (expected 0)\n", result);
  assert(result == 0);

  /* Save private key as unencrypted PrivatekeyInfo in text format compatible with OpenSSL */
  /* CAUTION: don't do this with a production key */
  result = RSA_SavePrivateKeyInfo(prifile, prikeystr, PKI_KEY_FORMAT_SSL);
  assert(result == 0);
  printf("Saved as '%s'\n", prifile);
  /* This is a text file in PEM-style format, so we can look at it */
  pr_textfile(prifile);

  /* Read in unencrypted private key */
  klen = RSA_ReadPrivateKeyInfo(NULL, 0, prifile, 0);
  assert(klen >= 0);
  chkkeystr = malloc(klen+1);
  assert(chkkeystr != 0);
  klen = RSA_ReadPrivateKeyInfo(chkkeystr, klen, prifile, 0);
  assert(klen >= 0);
  /* Check against original */
  printf("RSA_KeyHashCode(original key)=%08lX\n", RSA_KeyHashCode(prikeystr));
  printf("RSA_KeyHashCode(check key)   =%08lX\n", RSA_KeyHashCode(chkkeystr));
  result = (RSA_KeyHashCode(chkkeystr) == RSA_KeyHashCode(prikeystr));
  if (result)
    printf("Private keys match OK\n");
  else
    printf("FAILURE: private keys do NOT match\n");
  assert(result);

  /* Save private key in (unencrypted) XML format using non-standard hex format */
  xlen = RSA_ToXMLString(NULL, 0, prikeystr, PKI_XML_HEXBINARY);
  assert(xlen > 0);
  xmlstr = malloc(xlen+1);  /* NB one extra for a string */
  assert(xmlstr != NULL);
  xlen = RSA_ToXMLString(xmlstr, xlen, prikeystr, PKI_XML_HEXBINARY);
  assert(xlen > 0);
  printf("Private key in XML format=\n%s\n", xmlstr);
  /* Save as an XML file */
  fp = fopen(xmlfile, "w");
  assert(fp != NULL);
  fprintf(fp, "<?xml version=\"1.0\"?>\n");
  fprintf(fp, "%s\n", xmlstr);
  assert(fclose(fp) == 0);
  printf("Created file '%s'\n", xmlfile);

  /* Clear the password */
  result = WIPE_Data(password, pwdlen);
  assert(result == 0);
  /* Check all zeroised */
  for (i = 0; i < pwdlen; i++)
    assert(password[i] == 0);

  /* Wipe the unecrypted key file */
  result = WIPE_File(prifile, 0);
  assert(result == 0);
  printf("%s has been securely wiped\n", prifile);

  /* Wipe the private key string */
  result = WIPE_Data(prikeystr, (long)strlen(prikeystr));
  assert(result == 0);

  free(prikeystr);
  free(pubkeystr);
  free(chkkeystr);
  free(xmlstr);
}

void carol_reads_public_key_from_cert(void)
/* Example showing how we can read the public key from both an X.509 certificate
   and from the public key file made using RSA_MakeKeys. 
*/
{
  char *pbkfile  = "carol_pub.bin";
  char *certfile = "carol.cer";
  long result;
  char *pubkeystr, *pubkeystr1;
  long klen, nbits, nbits1;

  printf("\nCarol extracts her public key from her X.509 certificate...\n");

  /* Read in public key from X.509 certificate */
  klen = RSA_GetPublicKeyFromCert(NULL, 0, certfile, 0);
  assert(klen >= 0);
  pubkeystr = malloc(klen+1);
  assert(pubkeystr != NULL);
  klen = RSA_GetPublicKeyFromCert(pubkeystr, klen, certfile, 0);
  assert(klen >= 0);
  nbits = RSA_KeyBits(pubkeystr);
  printf("Carol's key is %ld bits long\n", nbits);

  /* And from public key file */
  klen = RSA_ReadPublicKey(NULL, 0, pbkfile, 0);
  assert(klen >= 0);
  pubkeystr1 = malloc(klen+1);
  assert(pubkeystr1 != NULL);
  klen = RSA_ReadPublicKey(pubkeystr1, klen, pbkfile, 0);
  assert(klen >= 0);
  nbits1 = RSA_KeyBits(pubkeystr1);
  assert(nbits1 == nbits);

  /* [v3.0] You can no longer just compare the internal strings as these are
  now encrypted as of version 3.0. Use RSA_KeyHashCode instead.
  */
  printf("RSA_KeyHashCode(original key)=%08lX\n", RSA_KeyHashCode(pubkeystr));
  printf("RSA_KeyHashCode(check key)   =%08lX\n", RSA_KeyHashCode(pubkeystr1));
  result = (RSA_KeyHashCode(pubkeystr) == RSA_KeyHashCode(pubkeystr1));
  if (result)
    printf("Public keys match OK\n");
  else
    printf("FAILURE: public keys do NOT match\n");
  assert(result);

  free(pubkeystr);
  free(pubkeystr1);
}

void ann_makes_cert_chain(void)
/* Ann creates a PKCS#7 certificate chain with her cert and the issuer's (carol's)
   then she extracts the certificates from it */
{
  long result;
  char *certlist;
  char *p7file = "ann.p7c";
  char certname[FILENAME_MAX], distname[256];
  int ncerts, index;

  printf("\nAnn makes a PKCS7 certificate chain...\n");

  /* Create list of certs in chain separated by semicolons */
  certlist = "carol.cer;ann.cer";

  /* Make a certs-only signed data file */
  result = CMS_MakeSigData(p7file, "", certlist, "", PKI_CMS_CERTS_ONLY);
  printf("CMS_MakeSigData returns %ld (expecting 0)\n", result);
  assert(result == 0);
  printf("Created PKCS#7 cert chain file '%s'\n", p7file);

  /* Now extract all the certificates from it */
  index = 0;  /* How many certs? */
  ncerts = X509_GetCertFromP7Chain(NULL, p7file, index, 0);
  printf("X509_GetCertFromP7Chain(0) returns %d (= no of certs)\n", ncerts);
  assert(ncerts > 0);
  for (index = 1; index <= ncerts; index++)
  {
    sprintf(certname, "cert%d.cer", index);
    result = X509_GetCertFromP7Chain(certname, p7file, index, 0);
    printf("X509_GetCertFromP7Chain(%d) returns %ld (= file size)\n", index, result);
    assert(result > 0);
    /* Query the certificate */
    result = X509_CertSubjectName(certname, distname, sizeof(distname)-1, "", 0);
    assert(result > 0);
    printf("%s has subject name '%s'\n", certname, distname);
  }
}

void ben_makes_pfx(void)
/* Ben creates a PKCS#12 PFX file with his encrypted private key and certificate
   then he verifies the signature and extracts his key and certificate from it */
{
  char *pfxfile =  "ben.pfx";
  char *certfile = "ben.cer";
  char *epkfile =  "ben_epk.bin";
  char *password = "password";
  char *certcopy = "ben_copy.cer";
  char *epkcopy =  "ben_epk_copy.bin";
  long result;
  long nchars;
  char *prikeybuf;
  char *pubkeybuf;
  long ncode1, ncode2, nret;

  printf("\nBen makes a PKCS12 PFX file...\n");

  /* Create the PFX file */
  result = PFX_MakeFile(pfxfile, certfile, epkfile, password, "Ben's Friendly ID", 0);
  printf("PFX_MakeFile returns %ld (expecting 0)\n", result);
  assert(result == 0);
  printf("Created PKCS#12 file '%s'\n", pfxfile);

  /* Verify the signature */
  result = PFX_VerifySig(pfxfile, password, 0);
  printf("PFX_VerifySig returns %ld (expecting 0 => verified OK)\n", result);
  assert(result == 0);

  /* Extract the private key as a file (still encrypted) */
  result = RSA_GetPrivateKeyFromPFX(epkcopy, pfxfile, 0);
  printf("RSA_GetPrivateKeyFromPFX returns %ld (= file size)\n", result);
  assert(result > 0);
  printf("Extracted encrypted private key file '%s'\n", epkcopy);
  /* can we read the key? */
  result = RSA_ReadEncPrivateKey(NULL, 0, epkcopy, password, 0);
  printf("RSA_ReadEncPrivateKey returns %ld (= internal key size)\n", result);
  assert(result > 0);

  /* Extract the certificate */
  // NB As of [v3.8] the cert in a PFX is encrypted by default, so we need the password
  result = X509_GetCertFromPFX(certcopy, pfxfile, password, 0);
  printf("X509_GetCertFromPFX returns %ld (= file size)\n", result);
  assert(result > 0);
  printf("Extracted certificate file '%s'\n", certcopy);
  /* compare with original */
  result = cmp_files(certcopy, certfile);
  printf("%s to original file\n", (result == 0 ? "EQUAL" : "NOT EQUAL!!"));
  assert(result == 0);

  /* Read the private key from the PFX file into an internal string [new in v3.8] */
  nchars = RSA_ReadPrivateKeyFromPFX(NULL, 0, pfxfile, password, 0);
  printf("RSA_ReadPrivateKeyFromPFX returns %ld (expecting > 0)\n", nchars);
  assert(nchars > 0);
  prikeybuf = malloc(nchars+1);
  assert(prikeybuf);
  nchars = RSA_ReadPrivateKeyFromPFX(prikeybuf, nchars, pfxfile, password, 0);
  /* display some info about the key */
  printf("Private key length = %ld\n", RSA_KeyBits(prikeybuf));
  ncode1 = RSA_KeyHashCode(prikeybuf);
  printf("KeyHashCode=%lX\n", ncode1);
  nret = RSA_CheckKey(prikeybuf, 0);
  printf("RSA_CheckKey returns %ld: (PKI_VALID_PRIVATEKEY=%d)\n", nret, PKI_VALID_PRIVATEKEY);
  assert(nret == PKI_VALID_PRIVATEKEY);

  /* Convert the internal private key string into a public one */
  nchars = RSA_PublicKeyFromPrivate(NULL, 0, prikeybuf, 0);
  printf("RSA_PublicKeyFromPrivate returns %ld (expecting > 0)\n", nchars);
  assert(nchars > 0);
  pubkeybuf = malloc(nchars+1);
  assert(pubkeybuf);
  nchars = RSA_PublicKeyFromPrivate(pubkeybuf, nchars, prikeybuf, 0);
  /* display some info about the key */
  printf("Public key length = %ld\n", RSA_KeyBits(pubkeybuf));
  ncode2 = RSA_KeyHashCode(pubkeybuf);
  printf("KeyHashCode=%lX\n", ncode2);
  nret = RSA_CheckKey(pubkeybuf, 0);
  printf("RSA_CheckKey returns %ld: (PKI_VALID_PUBLICKEY=%d)\n", nret, PKI_VALID_PUBLICKEY);
  assert(nret == PKI_VALID_PUBLICKEY);
  /* we should have got the same KeyHashCode for both */
  printf("KeyHashCodes are %s\n", (ncode1 == ncode2 ? "EQUAL" : "NOT EQUAL!!"));
  assert(ncode1 == ncode2);

  free(prikeybuf);
  free(pubkeybuf);

}

void ben_encrypts_raw_key(void)
/* Ben has a 192-bit session key (Content Encryption Key, CEK) he wants to send encrypted to ann */
/* INPUT: CEK; CEK length; Ann's public key file.
   OUTPUT: encrypted block.
*/
{
  long result;
  char *pubfile = "ann_pub.bin";
  unsigned char cek[24] = 
  { /* 0xffeeddccbbaa99887766554433221100fedcba9876543210 */
    0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 
    0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, 
    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 
  };
  unsigned char *block;
  char *pubkeystr;
  long klen, blocklen, ceklen;
  char *fname = "raw_enc_key_for_ann.bin";
  FILE *fp;

  printf("\nBen uses 'raw' RSA to encrypt a secret for Ann...\n");

  /* Read in Ann's public key to internal string */
  klen = RSA_ReadPublicKey(NULL, 0, pubfile, 0);
  assert(klen > 0);
  pubkeystr = malloc(klen+1); /* NB one extra */
  assert(pubkeystr != NULL);
  result = RSA_ReadPublicKey(pubkeystr, klen, pubfile, 0);
  assert(result == klen);

  /* Create a block equal in length to the RSA key modulus */
  blocklen = RSA_KeyBytes(pubkeystr);
  assert(blocklen > 0);
  block = malloc(blocklen);
  assert(block != NULL);

  /* Encode the message (i.e. the CEK) for encryption */
  ceklen = sizeof(cek);
  result = RSA_EncodeMsg(block, blocklen, cek, ceklen, PKI_EME_PKCSV1_5);
  assert(result == 0);

  printf("CEK=\n");
  pr_bytes(cek, ceklen);
  printf("Block before encryption=\n");
  pr_bytes(block, blocklen);

  /* Encrypt the block using the RSA public key */
  result = RSA_RawPublic(block, blocklen, pubkeystr, 0);
  assert(result == 0);
  printf("Encrypted block=\n");
  pr_bytes(block, blocklen);

  /* Save as a binary file (and send to Ann) */
  fp = fopen(fname, "wb");
  assert(fp != NULL);
  fwrite(block, 1, blocklen, fp);
  fclose(fp);

  printf("Saved encrypted key as file '%s'\n", fname);

  free(pubkeystr);
  free(block);

}

void ann_decrypts_raw_key(void)
/* Ann decrypts the session key (CEK) from Ben using her RSA private key */
/* INPUT: encrypted block; Ann's private key file; password.
   OUTPUT: CEK.
*/
{
  long result;
  char *epkfile = "ann_epk.bin";
  char *szPassword = "password";
  unsigned char *block, *cek;
  char *prikeystr;
  long klen, blocklen, ceklen;
  char *fname = "raw_enc_key_for_ann.bin";
  FILE *fp;

  printf("\nAnn decrypts the 'raw' data from Ben...\n");

  /* Read in Ann's private key to an internal string */
  klen = RSA_ReadEncPrivateKey(NULL, 0, epkfile, szPassword, 0);
  assert(klen > 0);
  prikeystr = malloc(klen+1); /* NB one extra */
  assert(prikeystr != NULL);
  result = RSA_ReadEncPrivateKey(prikeystr, klen, epkfile, szPassword, 0);
  assert(result == klen);

  /* Create a block equal in length to the RSA key modulus */
  blocklen = RSA_KeyBytes(prikeystr);
  assert(blocklen > 0);
  block = malloc(blocklen);
  assert(block != NULL);

  /* Read in the encrypted data from the binary file 
     -- it MUST be the same length as the key */
  fp = fopen(fname, "rb");
  assert(fp != NULL);
  result = (long)fread(block, 1, blocklen, fp);
  fclose(fp);
  assert (result == blocklen);
  printf("Encrypted block=\n");
  pr_bytes(block, blocklen);

  /* Decrypt the block using the RSA private key */
  result = RSA_RawPrivate(block, blocklen, prikeystr, 0);
  assert(result == 0);
  printf("Decrypted block=\n");
  pr_bytes(block, blocklen);

  /* Decode the block to extract the message (the CEK) */
  /* how long is it? */
  ceklen = RSA_DecodeMsg(NULL, 0, block, blocklen, PKI_EME_PKCSV1_5);
  printf("RSA_DecodeMsg returns %ld\n", ceklen);
  assert(ceklen > 0);
  /* allocate memory and extract */
  cek = malloc(ceklen);
  result = RSA_DecodeMsg(cek, ceklen, block, blocklen, PKI_EME_PKCSV1_5);
  assert(result == ceklen);

  printf("CEK=\n");
  pr_bytes(cek, ceklen);

  /* Now use the CEK ... */

  /* Make sure we wipe the private key string and CEK */
  WIPE_Data(prikeystr, (long)strlen(prikeystr));
  WIPE_Data(cek, ceklen);

  free(prikeystr);
  free(block);
  free(cek);

}

void ann_signs_raw_msg(void)
/* Ann uses raw RSA to sign a message (actually its message digest) */
/* INPUT: Message-to-be-signed; hash algorithm to use (SHA-1); Ann's private key file; password.
   OUTPUT: signature value.
*/
{
  long result;
  char *epkfile = "ann_epk.bin";
  char *szPassword = "password";
  char *msg = "This is some sample content."; 
  unsigned char hash[PKI_SHA1_BYTES];
  unsigned char *block;
  char *prikeystr;
  long klen, blocklen, hashlen;
  char *fname = "raw_sig_by_ann.bin";
  FILE *fp;

  printf("\nAnn uses raw RSA to sign a message...\n");

  /* Ann reads in her private key */
  klen = RSA_ReadEncPrivateKey(NULL, 0, epkfile, szPassword, 0);
  assert(klen > 0);
  prikeystr = malloc(klen+1); /* NB one extra */
  assert(prikeystr != NULL);
  result = RSA_ReadEncPrivateKey(prikeystr, klen, epkfile, szPassword, 0);
  assert(result == klen);

  /* Create a block equal in length to the RSA key modulus */
  blocklen = RSA_KeyBytes(prikeystr);
  assert(blocklen > 0);
  block = malloc(blocklen);
  assert(block != NULL);

  /* Compute the SHA-1 message digest of the message-to-be-signed */
  hashlen = sizeof(hash);
  result = HASH_Bytes(hash, hashlen, (unsigned char *)msg, (long)strlen(msg), PKI_HASH_SHA1);
  printf("SHA-1('%s')=\n", msg);
  pr_bytes(hash, hashlen);

  /* Encode the message digest for signature (EMSA) */
  result = RSA_EncodeMsg(block, blocklen, hash, hashlen, 
    PKI_EMSIG_PKCSV1_5 + PKI_HASH_SHA1 + PKI_EMSIG_DIGESTONLY);
  assert(result == 0);
  printf("Block before signing=\n");
  pr_bytes(block, blocklen);

  /* (NOTE: we could have omitted the digest computation above and encoded the message
     directly without using the PKI_EMSIG_DIGESTONLY option.) */

  /* Sign the block using the private key */
  result = RSA_RawPrivate(block, blocklen, prikeystr, 0);
  assert(result == 0);
  printf("Signature block=\n");
  pr_bytes(block, blocklen);

  /* Save as a binary file (and send to Ben) */
  fp = fopen(fname, "wb");
  assert(fp != NULL);
  fwrite(block, 1, blocklen, fp);
  fclose(fp);
  printf("Saved signature value as file '%s'\n", fname);

  /* Make sure we wipe the private key string */
  WIPE_Data(prikeystr, (long)strlen(prikeystr));
  free(prikeystr);
  free(block);

}

void ben_verifies_raw_msg(void)
/* Ben verifies the raw RSA signature received from Ann */
/* INPUT: signature value; Ann's public key file; HASH(message-to-be-verified); hash algorithm used (SHA-1).
   OUTPUT: "Valid signature" or "Invalid signature".
*/
{
  long result;
  char *pubfile = "ann_pub.bin";
  unsigned char cfhash[PKI_SHA1_BYTES] = 
  { /* 406aec085279ba6e16022d9e0629c0229687dd48 */
    0x40, 0x6A, 0xEC, 0x08, 0x52, 0x79, 0xBA, 0x6E, 
    0x16, 0x02, 0x2D, 0x9E, 0x06, 0x29, 0xC0, 0x22, 
    0x96, 0x87, 0xDD, 0x48 };
  unsigned char *block, *hash;
  char *pubkeystr;
  long klen, blocklen, hashlen;
  char *fname = "raw_sig_by_ann.bin";
  FILE *fp;

  printf("\nBen verifies the raw RSA signature received from Ann...\n");

  /* Ann reads in her private key */
  klen = RSA_ReadPublicKey(NULL, 0, pubfile, 0);
  assert(klen > 0);
  pubkeystr = malloc(klen+1); /* NB one extra */
  assert(pubkeystr != NULL);
  result = RSA_ReadPublicKey(pubkeystr, klen, pubfile, 0);
  assert(result == klen);

  /* Create a block equal in length to the RSA key modulus */
  blocklen = RSA_KeyBytes(pubkeystr);
  assert(blocklen > 0);
  block = malloc(blocklen);
  assert(block != NULL);

  /* Read in the signature value from the binary file 
     -- it MUST be the same length as the key */
  fp = fopen(fname, "rb");
  assert(fp != NULL);
  result = (long)fread(block, 1, blocklen, fp);
  fclose(fp);
  assert (result == blocklen);
  printf("Signature value=\n");
  pr_bytes(block, blocklen);

  /* Verify the block by decrypting using the RSA public key */
  result = RSA_RawPublic(block, blocklen, pubkeystr, 0);
  assert(result == 0);
  printf("Decrypted block=\n");
  pr_bytes(block, blocklen);

  /* Decode the block to extract the message digest */
  /* how long is it? */
  hashlen = RSA_DecodeMsg(NULL, 0, block, blocklen, PKI_EMSIG_PKCSV1_5);
  printf("RSA_DecodeMsg returns %ld\n", hashlen);
  assert(hashlen > 0);
  /* allocate memory and extract */
  hash = malloc(hashlen);
  result = RSA_DecodeMsg(hash, hashlen, block, blocklen, PKI_EMSIG_PKCSV1_5);
  assert(result == hashlen);

  printf("HASH= ");
  pr_bytes(hash, hashlen);

  /* Ben has already independently computed the message digest,
     so he compares the two digest values */
  printf("HASH'=");
  pr_bytes(cfhash, sizeof(cfhash));
  result = memcmp(cfhash, hash, hashlen);
  printf("%s\n", (result == 0 ? "Valid signature" : "INVALID SIGNATURE"));
  assert (result == 0);


  /* Clean up */
  free(pubkeystr);
  free(block);
  free(hash);
}

void ben_creates_cjk_ca_cert(void)
/* Ben creates a self-signed certificate with his name in Chinese */
{
  char *certfile = "ben_china.cer";
  char *epkfile = "ben_epk.bin";
  char *password = "password";
  char *distname;
  long keyusage;
  long result;
  char *query;
  char soutput[64];

  printf("\nBen creates a self-signed certificate with his Chinese name...\n");

  /* Set Key Usage flags */
  keyusage = PKI_X509_KEYUSAGE_DIGITALSIGNATURE 
    + PKI_X509_KEYUSAGE_KEYCERTSIGN + PKI_X509_KEYUSAGE_CRLSIGN;

  /* Set name using UTF-8-encoded chinese characters:
  CN=ben (U+672C)
  C= zhong guo (U+4E2D, U+570B)
  OU=zong ju (U+7E3D, U+5C40)
  */
  distname = "CN=#xe69cac;C=#xe4b8ade59c8b;OU=#xe7b8bde5b180";

  /*  Make an self-signed cert No. 0x888 valid for 8 years 
    using sha256WithRSAEncryption for signature */
  result = X509_MakeCertSelf(certfile, epkfile, 
    0x888, 8, distname, "ben@ho.com.cn", keyusage, password, 
    PKI_X509_UTF8|PKI_SIG_SHA256RSA);
  assert(result == 0);
  printf("Created self-signed X.509 certificate '%s'\n", certfile);

  /* Now query the certificate */
  query = "signatureAlgorithm";
  result = X509_QueryCert(soutput, sizeof(soutput)-1, certfile, query, 0);
  printf("%s='%s'\n", query, soutput);
}

void damien_creates_csr(void)
/* Damien creates a certificate signing request (CSR) after making himself 
   a pair of RSA keys */
{
  char *pubfile = "damien_pub.bin";
  char *epkfile = "damien_epk.bin";
  char *csrfile = "damien_csr.p10";
  char *szPassword = "password";
  long keybits = 512;
  long result;
  time_t start, finish;
  double duration;
  char *dn;

  printf("\nCreating 512-bit key pair for Damien...\n");
  start = clock();
  result = RSA_MakeKeys(pubfile, epkfile, keybits, PKI_RSAEXP_EQ_65537, 50, 
    1000, szPassword, NULL, 0, 0);
  finish = clock();
  assert(result == 0);
  duration = (double)(finish - start) / CLOCKS_PER_SEC;
  printf("Time=%.3lf secs\n", duration);
  printf("...created key files '%s' and '%s'\n", pubfile, epkfile);

  printf("\nDamien creates a certificate signing request...\n");
  dn="CN=Damien;O=Beelzebub, Inc.";
  result =  X509_CertRequest(csrfile, epkfile, dn, NULL, szPassword, 0);
  assert(result == 0);
  printf("Created certificate request '%s'\n", csrfile);

  /* Dump the details of the CSR to a text file then display it */
  result = X509_TextDump("dump.txt", csrfile, 0);
  assert(result == 0);
  pr_textfile("dump.txt");

}

void carol_makes_cert_from_damiens_csr(void)
{
  long result;
  char *certfile = "damien.cer";
  char *csrfile = "damien_csr.p10";
  char *issuercert = "carol.cer";
  char *epkfile = "carol_epk.bin";
  char *szPassword = "password";
  char *extns;
  char *p7file;
  char *certlist;

  printf("\nCarol makes an X.509 certificate for Damien using his Certificate Signing Request...\n");

  /* Carol makes and signs an end-user certificate for Damien valid for 5 years
     starting on 1 January 2010, adding her KeyIdentifier.
     Note we pass Damien's CSR filename instead of the public key file 
     and leave the DN empty (this is important) */
  extns = "notBefore=2010-01-01T12:00";
  result = X509_MakeCert(certfile, issuercert, csrfile, epkfile, 
    0x666, 5, "", extns, 0, szPassword, PKI_X509_AUTHKEYID);
  assert(result == 0);
  printf("Created end-user X.509 certificate '%s'\n", certfile);

  /* Dump the details of the certificate to a text file then display it */
  dump_and_display(certfile);

  /* Carol now creates a PKCS7 certificate chain for Damien containing her
     certificate and Damien's new one. This is made in PEM format and so we use
     the .p7b extension, which MS seems to prefer over .p7c for PEM files */
  p7file = "damien.p7b";
  certlist = "carol.cer;damien.cer";
  result = CMS_MakeSigData(p7file, "", certlist, "", PKI_CMS_CERTS_ONLY | PKI_CMS_FORMAT_BASE64);
  assert(result == 0);
  printf("Created PKCS7 certs-only chain file '%s'\n", p7file);
  pr_textfile(p7file);

}

void damien_reads_new_p7_chain_file(void)
{
  long result;
  char *p7file = "damien.p7b";
  long index, ncerts;
  char certfile[FILENAME_MAX];
  char subjectname[256];
  char serialnumber[64];

  printf("\nDamien validates the P7 cert chain file from Carol and extracts all the certificates...\n");

  /* Validate the certificate chain file */
  result = X509_ValidatePath(p7file, "", 0);
  printf("Cert chain '%s' is %s\n", p7file, (result == 0 ? "VALID" : "NOT VALID: ERROR"));
  assert(result == 0);
  
  /* Extract all the certificates from it */
  index = 0;  /* How many certs? */
  ncerts = X509_GetCertFromP7Chain(NULL, p7file, index, 0);
  printf("X509_GetCertFromP7Chain(0) returns %ld (= no of certs)\n", ncerts);
  assert(ncerts > 0);
  for (index = 1; index <= ncerts; index++)
  {
    sprintf(certfile, "cert-%ld.cer", index);
    result = X509_GetCertFromP7Chain(certfile, p7file, index, 0);
    printf("X509_GetCertFromP7Chain(%ld) returns %ld (= file size)\n", index, result);
    assert(result > 0);
    /* Query the certificate */
    printf("%s:\n", certfile);
    result = X509_QueryCert(serialnumber, sizeof(serialnumber)-1, certfile, "serialNumber", 0);
    assert(result > 0);
    printf("  serialNumber: %s\n", serialnumber);
    result = X509_QueryCert(subjectname, sizeof(subjectname)-1, certfile, "subjectName", 0);
    assert(result > 0);
    printf("  subjectName: %s\n", subjectname);
  }
}

void carol_revokes_damiens_cert(void)
{
  long result;
  char *crlfile = "carol.crl";
  char *issuercert = "carol.cer";
  char *epkfile = "carol_epk.bin";
  char *password = "password";
  char *extns, *revokedcerts;

  printf("\nCarol revokes Damien's certificate...\n");

  /* Carol is issuing this CRL on 1 April 2010 */
  extns = "thisUpdate=2010-04-01T12:00";
  /* Damien's certificate serial number was 0x666 - it was revoked on 31 March 2010 */
  revokedcerts = "#x666,2010-03-31T12:01:59";

  /* Carol makes and signs a certificate revocation list in PEM format */
  result = X509_MakeCRL(crlfile, issuercert, epkfile, password, revokedcerts, extns, PKI_X509_FORMAT_PEM);
  assert(result == 0);
  printf("Created certificate revocation list (CRL) '%s'\n", crlfile);
  dump_and_display(crlfile);
  pr_textfile(crlfile);
}

void we_check_damiens_cert_in_crl(void)
{
  long result;
  char *crlfile = "carol.crl";
  char *issuercert = "carol.cer";
  char *certfile = "damien.cer";
  char *date;

  printf("\nWe check whether or not Damien's cert has been revoked...\n");

  result = X509_CheckCertInCRL(certfile, crlfile, issuercert, "", 0);
  if (result == PKI_X509_REVOKED)
    printf("Cert '%s' HAS BEEN REVOKED\n", certfile);
  else if (result == 0)
    printf("Cert '%s' has NOT been revoked\n", certfile);
  assert(result >= 0);

  /* But it was still valid on the first of March 2010 */
  date = "2010-03-01";
  result = X509_CheckCertInCRL(certfile, crlfile, "", date, 0);
  if (result == PKI_X509_REVOKED)
    printf("Cert '%s' WAS REVOKED as at %s\n", certfile, date);
  else if (result == 0)
    printf("Cert '%s' had NOT been revoked as at %s\n", certfile, date);
  assert(result >= 0);
}

void test_byte_encoding(void)
{
  long ulen, llen;
  unsigned char *utf8, *newlatin;
  /*
   Set up a byte array with the following 4 characters encoded in Latin-1
    U+0061 LATIN SMALL LETTER A
    U+00E9 LATIN SMALL LETTER E WITH ACUTE
    U+00F1 LATIN SMALL LETTER N WITH TILDE
    U+0062 LATIN SMALL LETTER B
  */
  unsigned char latin1[] = { 0x61, 0xe9, 0xf1, 0x62 };

  printf("\nWe change the byte encoding of a string from Latin-1 to UTF-8 and back again...\n");
  llen = sizeof(latin1);
  pr_byteshex("Latin-1=(0x)", latin1, llen, " ");
  printf("(%ld bytes)\n", llen);
  /* Convert to UTF-8 */
  ulen = CNV_ByteEncoding(NULL, 0, latin1, llen, PKI_CNV_UTF8_FROM_LATIN1);
  assert(ulen > 0);
  utf8 = malloc(ulen);
  ulen = CNV_ByteEncoding(utf8, ulen, latin1, llen, PKI_CNV_UTF8_FROM_LATIN1);
  pr_byteshex("UTF-8  =(0x)", utf8, ulen, " ");
  printf("(%ld bytes)\n", ulen);
  /* and convert back to Latin-1 */
  llen = CNV_ByteEncoding(NULL, 0, utf8, ulen, PKI_CNV_LATIN1_FROM_UTF8);
  assert(llen > 0);
  newlatin = malloc(llen);
  llen = CNV_ByteEncoding(newlatin, llen, utf8, ulen, PKI_CNV_LATIN1_FROM_UTF8);
  pr_byteshex("Latin-1=(0x)", newlatin, llen, " ");
  printf("(%ld bytes)\n", llen);

  free(utf8);
  free(newlatin);
}

void test_utf8bytes_from_latin1(void)
{
  long utflen, latinlen, status;
  unsigned char *cpu;

  const char *latin1data = "Ação é rápida"; // This contains 3 non-ASCII chars
  printf("\nWe change a Latin-1 string into UTF-8-encoded bytes...\n");
  latinlen = (long)strlen(latin1data);
  printf("Latin-1=\"%s\" (%ld characters)\n", latin1data, latinlen);
  utflen = CNV_UTF8BytesFromLatin1(NULL, 0, latin1data);
  printf("CNV_UTF8BytesFromLatin1 returns %ld\n", utflen);
  assert(utflen >= 0);
  cpu = malloc(utflen+1);
  assert(cpu != NULL);
  utflen = CNV_UTF8BytesFromLatin1(cpu, utflen, latin1data);
  pr_byteshex("UTF-8=(0x)", cpu, utflen, " ");
  printf("(%ld bytes)\n", utflen);
  
  printf("Now check we have valid UTF-8-encoded bytes...\n");
  status = CNV_CheckUTF8Bytes(cpu, utflen);
  printf("CNV_CheckUTF8Bytes returns %ld (expected 2)\n", status);
  assert(status > 0);
  free(cpu);
}


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

/* MSVC memory leak checking stuff */
#if _MSC_VER >= 1100
  _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
  _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT );
  _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
  _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT );
  _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
  _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT );
#endif

  /* Setup... */
  check_user_options(argc, argv);
  make_new_test_dir();
  create_test_files();

  /* Do the tests...*/
  show_version();
  init_rng();
  carol_creates_keys();
  carol_creates_ca_cert();
  ann_and_ben_create_keys();
  carol_makes_certs_for_ann_and_ben();
  ben_tests_his_keys_in_the_raw();
  ann_sends_enc_msg_to_ben();
  ben_decrypts_msg_from_ann();
  ann_sends_encmsg_ben_reads();
  ann_sends_encr_base64_ben_reads();
  ann_signs_msg();
  ben_verifies_msg_from_ann();
  ben_reads_signed_data();
  ben_signs_det_msg();
  ann_verifies_det_msg_from_ben();
  ann_signs_base64_ben_verifies();
  ben_makes_crs();
  ann_verifies_her_cert();
  ben_resaves_his_public_key();
  if (!no_prompt) ben_examines_his_private_key();
  carol_reads_public_key_from_cert();
  ann_makes_cert_chain();
  ben_makes_pfx();
  ben_encrypts_raw_key();
  ann_decrypts_raw_key();
  ann_signs_raw_msg();
  ben_verifies_raw_msg();
  ben_creates_cjk_ca_cert();
  /* Added in [v3.5] */
  damien_creates_csr();
  carol_makes_cert_from_damiens_csr();
  damien_reads_new_p7_chain_file();
  carol_revokes_damiens_cert();
  we_check_damiens_cert_in_crl();
  /* Added in [v.36] */
  test_byte_encoding();
  test_utf8bytes_from_latin1();

  printf("\nCore PKI DLL version = %ld\n", PKI_Version(0,0));
  
  printf("\nALL DONE.\n");

  /* Option to clean up - default is Yes */
  printf("The directory '%s' has been created by this test program.\n"
    "Do you want to remove this directory? [Y]/N: ", testdir);
  c = getchar();
  if (c != 'N' && c != 'n')
    remove_test_dir(testdir, old_cwd);

  return 0;
}