/* TestFirmaSat.c */

// Some tests using the FirmaSAT C/C++ interface.
// This uses plain-old ANSI C.
// Requires certain files to exist in the current working directory.

/* 
 **************************** COPYRIGHT NOTICE ****************************
 * Copyright (C) 2010-11 DI Management Services Pty Limited. 
 * All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
 *   $Id: TestFirmaSat.c $
 *   Last updated:
 *   $Date: 2011-12-29 11:12:00 $
 *   $Version: 5.0.0 $
 ************************* END OF COPYRIGHT NOTICE ************************
 */

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

/* Link to the DLL library */
#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 "diFirmaSat2.lib")

void display_all_errors(void);

static void disp_error(long nErrCode)
{
  char msg[1024];
  long nchars;

  printf("Error code %ld", nErrCode);

  nchars = SAT_ErrorLookup(msg, sizeof(msg)-1, nErrCode);
  if (nchars > 0)
    printf(": %s", msg);

  nchars = SAT_LastError(msg, sizeof(msg)-1);
  if (nchars > 0)
    printf(" : %s", msg);
  putchar('\n');
}

int file_exists(const char *fname)
{
  FILE *fp;

  fp = fopen(fname, "rb");
  if (fp == NULL)
    return 0;

  fclose(fp);
  return 1;
}

int required_files_exist(void)
// Check for required files in current working directory
{
  const char *arrFiles[] = {
        "aaa010101aaa_CSD_01.cer", 
        "aaa010101aaa_CSD_01.key",
    "aaa010101aaa_CSD_02.cer",
    "aaa010101aaa_CSD_02.key",
        "detallista_base2011.xml", 
        "Muestra_v2_bad.xml", 
        "Muestra_v2_base2011.xml", 
        "Muestra_v2_signed2011.xml",
    "ejemplo_v3_base2011.xml",
    "ejemplo_v3_tfd.xml",
    "ejemplo_v3_noprefix.xml",
    "V2_2-base.xml",
    "V3_2-base.xml",
    "V3_2_BadCurp.xml",
    "Muestra_v22-base.xml",
    "ejemplo_v32-base.xml",
    NULL
  };
  const char *fname;
  int i;
  for (i = 0, fname = arrFiles[i]; fname; fname = arrFiles[++i])
  {
    if (!file_exists(fname))
    {
      printf("**ERROR: File %s is missing.\n", fname);
      return 0;
    }
  }
  return 1;
}

/* DO THE BUSINESS... */
int main(void)
{
  long ret, nchars, n;
  char ch;
  char *buf, *buf1;
  char *fname;
  char *newname, *keyfile, *password, *certfile;
  char *attributeName, *elementName;
  char digest[SAT_MAX_HASH_CHARS+1];  /* Digest hex length plus one. [NB changed in v2.1] */
  char digest1[SAT_MAX_HASH_CHARS+1];
  char numstr[21];
  char cdate[64];
  int i;
  char eName[64];

  /* Check if all required files exist in the CWD */
  if (!required_files_exist())
  {
    printf("Required file cannot be found in current working directory.\n");
    exit(1);
  }

    printf("INTERROGATE THE CORE diFirmaSAT2 DLL:\n");
    ret = SAT_Version();
    printf("Version=%ld\n", ret);
  if (ret < 400)
  {
    printf("Require FirmaSAT version 4.0 or greater.\n");
    exit(1);
  }

    ch = (char)SAT_LicenceType();
    printf("LicenceType=%c\n", ch);

  nchars = SAT_ModuleName(NULL, 0, 0);
  if (nchars > 0)
  {
    buf = malloc(nchars+1);
    nchars = SAT_ModuleName(buf, nchars, 0);
    printf("Module=%s\n", buf);
    free(buf);
  }
  assert(nchars > 0);
  /* Detect platform [new in v2.0.1] */
  nchars = SAT_ModuleName(NULL, 0, SAT_GEN_PLATFORM);
  if (nchars > 0)
  {
    buf = malloc(nchars+1);
    nchars = SAT_ModuleName(buf, nchars, 64);
    printf("Platform=%s\n", buf);
    free(buf);
  }
  assert(nchars > 0);

  nchars = SAT_CompileTime(NULL, 0);
  if (nchars > 0)
  {
    buf = malloc(nchars+1);
    nchars = SAT_CompileTime(buf, nchars);
    printf("Compiled=%s\n", buf);
    free(buf);
  }
  assert(nchars > 0);

  printf("\nFORM THE PIPESTRING FROM AN XML FILE: \n");
    fname = "Muestra_v2_signed2011.xml";
  nchars = SAT_MakePipeStringFromXml(NULL, 0, fname, SAT_ENCODE_UTF8);
  printf("SAT_MakePipeStringFromXml(UTF8) returns %ld\n", nchars);
  if (nchars >= 0)
  {
    buf = malloc(nchars+1);
    nchars = SAT_MakePipeStringFromXml(buf, nchars, fname, SAT_ENCODE_UTF8);
    printf("%s\n", buf);
    printf("Actual length=%ld\n", nchars);
    free(buf);
  }
  else
    disp_error(nchars);
  assert(nchars > 0);

    printf("\nSIGN AN XML FILE: \n");
    fname = "Muestra_v2_base2011.xml";
    newname = "Muestra_v2_signed2011-new.xml";
    keyfile = "aaa010101aaa_CSD_01.key";
    password = "a0123456789";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
    certfile = "aaa010101aaa_CSD_01.cer";
    ret = SAT_SignXml(newname, fname, keyfile, password, certfile, 0);
    printf("Sat.SignXml('%s'-->'%s') returns %ld\n", fname, newname, ret);
  assert (ret == 0);
    // Did we make a valid XML file?
  ret = SAT_ValidateXml(newname, 0);
  printf("SAT_ValidateXml(%s) returns %ld (0=>success)\n", newname, ret);
  assert (ret == 0);

    printf("\nVERIFY A SIGNATURE IN AN XML FILE: \n");
    printf("1. One we know is good:\n");
    fname = "Muestra_v2_signed2011.xml";
    ret = SAT_VerifySignature(fname, NULL, 0);
    printf("Sat.VerifySignature('%s') returns %ld\n", fname, ret);
  assert (ret == 0);

    printf("2. One we just made, so it should be good:\n");
    fname = newname;
    ret = SAT_VerifySignature(fname, NULL, 0);
    printf("Sat.VerifySignature('%s') returns %ld\n", fname, ret);
  assert (ret == 0);

    printf("\nFORM THE DIGEST OF THE PIPESTRING IN AN XML FILE: \n");
    fname = "Muestra_v2_signed2011.xml";
  ret = SAT_MakeDigestFromXml(digest, sizeof(digest)-1, fname, 0);
  printf("SAT_MakeDigestFromXml returns %ld\n", ret);
  if (ret < 0) disp_error(ret);
  printf("Digest=%s\n", digest);
  assert (ret > 0);

    printf("\nEXTRACT THE DIGEST FROM THE SIGNATURE IN AN XML FILE: \n");
    fname = "Muestra_v2_signed2011.xml";
  ret = SAT_ExtractDigestFromSignature(digest1, sizeof(digest1)-1, fname, NULL, 0);
  printf("SAT_ExtractDigestFromSignature returns %ld\n", ret);
  if (ret < 0) disp_error(ret);
  printf("Digest=%s\n", digest1);
  assert (ret > 0);
  /* check that the two digests match (caution: upper- vs lower-case) */
  assert (_stricmp(digest, digest1) == 0);

  printf("\nTRY VALIDATING XML FILES:\n");
  printf("1. A valid one:\n");
  fname = "Muestra_v2_signed2011.xml";
  ret = SAT_ValidateXml(fname, 0);
  printf("SAT_ValidateXml(%s) returns %ld (0=>success)\n", fname, ret);
  assert (ret == 0);

  printf("2. An invalid one (missing version):\n");
    fname = "Muestra_v2_bad.xml";
  ret = SAT_ValidateXml(fname, 0);
  printf("SAT_ValidateXml(%s) returns %ld (expected error)\n", fname, ret);
  assert (ret < 0);
  disp_error(ret);

  printf("3. An invalid one (empty noCertificado):\n");
    fname = "ejemplo_v32-base.xml";
  ret = SAT_ValidateXml(fname, 0);
  printf("SAT_ValidateXml(%s) returns %ld (expected error)\n", fname, ret);
  assert (ret < 0);
  disp_error(ret);

    printf("\nEXTRACT AN ATTRIBUTE FROM AN XML FILE: \n");
    fname = "Muestra_v2_signed2011.xml";
    elementName = "Comprobante";
    attributeName = "sello";
  nchars = SAT_GetXmlAttribute(NULL, 0, fname, attributeName, elementName);
  if (nchars >= 0)
  {
    buf = malloc(nchars+1);
    nchars = SAT_GetXmlAttribute(buf, nchars, fname, attributeName, elementName);
    printf("SAT_GetXmlAttribute('%s','%s','%s')=\n%s\n", fname, attributeName, elementName, buf);
    free(buf);
  }
  else
    disp_error(nchars);
  assert(nchars > 0);

    printf("\nGET DETAILS OF X.509 CERTIFICATE:\n");
    printf("1. From embedded `certificado` in XML\n");
    fname = "Muestra_v2_signed2011.xml";
  printf("Input file: %s\n", fname);
  ret = SAT_GetCertNumber(numstr, sizeof(numstr)-1, fname, 0);
  printf("SAT_GetCertNumber returns %ld\n", ret);
  if (ret < 0) disp_error(ret);
  printf("serialNumber=%s\n", numstr);
  ret = SAT_GetCertExpiry(numstr, sizeof(numstr)-1, fname, 0);
  printf("SAT_GetCertExpiry returns %ld\n", ret);
  if (ret < 0) disp_error(ret);
  printf("notAfter=%s\n", numstr);
  // [v3.0] added this...
  ret = SAT_GetCertExpiry(numstr, sizeof(numstr)-1, fname, SAT_DATE_NOTBEFORE);
  printf("SAT_GetCertExpiry(DATE_NOTBEFORE) returns %ld\n", ret);
  if (ret < 0) disp_error(ret);
  printf("notBefore=%s\n", numstr);

  printf("2. From X.509 file\n");
  fname = "aaa010101aaa_CSD_01.cer";
  printf("Input file: %s\n", fname);
  ret = SAT_GetCertNumber(numstr, sizeof(numstr)-1, fname, 0);
  printf("SAT_GetCertNumber returns %ld\n", ret);
  if (ret < 0) disp_error(ret);
  printf("serialNumber=%s\n", numstr);
  ret = SAT_GetCertExpiry(numstr, sizeof(numstr)-1, fname, 0);
  printf("SAT_GetCertExpiry returns %ld\n", ret);
  if (ret < 0) disp_error(ret);
  printf("notAfter=%s\n", numstr);
  // [v3.0] added this...
  ret = SAT_GetCertExpiry(numstr, sizeof(numstr)-1, fname, SAT_DATE_NOTBEFORE);
  printf("SAT_GetCertExpiry(DATE_NOTBEFORE) returns %ld\n", ret);
  if (ret < 0) disp_error(ret);
  printf("notBefore=%s\n", numstr);

    printf("\nGET CERTIFICATE AS A BASE64 STRING:\n");
    fname = "aaa010101aaa_CSD_01.cer";
  printf("For file %s...\n", fname);
  nchars = SAT_GetCertAsString(NULL, 0, fname, 0);
  printf("SAT_GetCertAsString(%s) returns %ld\n", fname, nchars);
  if (nchars < 0) disp_error(ret);
  assert(nchars > 0);
  if (nchars >= 0)
  {
    buf = malloc(nchars+1);
    nchars = SAT_GetCertAsString(buf, nchars, fname, 0);
    printf("%s\n", buf);
    printf("Actual length=%ld\n", nchars);
    //free(buf); // Keep string for later...
  }
  else
    disp_error(nchars);
  assert(nchars > 0);

    // Compare against string from XML file
    fname = "Muestra_v2_signed2011.xml";
  printf("For file %s...\n", fname);
  nchars = SAT_GetCertAsString(NULL, 0, fname, 0);
  if (nchars < 0) disp_error(ret);
  assert(nchars > 0);
  if (nchars >= 0)
  {
    buf1 = malloc(nchars+1);
    nchars = SAT_GetCertAsString(buf1, nchars, fname, 0);
    printf("Length of cert string=%ld\n", nchars);
  }
  else
    disp_error(nchars);
  assert(nchars > 0);
  // Compare strings
  if (strcmp(buf, buf1) == 0)
    printf("OK, cert strings are equal.\n");
  else
    printf("ERROR: cert strings do not match!\n");
  assert(strcmp(buf, buf1) == 0);
  free(buf);
  free(buf1);

    printf("\nMAKE A SIGNATURE FROM A BASE XML FILE: \n");
    fname = "Muestra_v2_base2011.xml";
    keyfile = "aaa010101aaa_CSD_01.key";
    password = "a0123456789";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
  nchars = SAT_MakeSignatureFromXml(NULL, 0, fname, keyfile, password);
  if (nchars >= 0)
  {
    buf = malloc(nchars+1);
    nchars = SAT_MakeSignatureFromXml(buf, nchars, fname, keyfile, password);
    printf("SAT_MakeSignatureFromXml('%s','%s')=\n%s\n", fname, keyfile, buf);
    free(buf);
  }
  else
    disp_error(nchars);
  assert(nchars > 0);

    printf("\nSIGN A DETALLISTA XML FILE: \n");
    fname = "detallista_base2011.xml";
    newname = "detallista-new-signed.xml";
    keyfile = "aaa010101aaa_CSD_01.key";
    password = "a0123456789";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
    certfile = "aaa010101aaa_CSD_01.cer";
  ret = SAT_SignXml(newname, fname, keyfile, password, certfile, 0);
  printf("SAT_SignXml(%s) returns %ld (0=>success)\n", fname, ret);
  if (ret != 0) disp_error(ret);
  assert(0 == ret);

    // Did we make a valid XML file?
  fname = newname;
  ret = SAT_ValidateXml(fname, 0);
  printf("SAT_ValidateXml(%s) returns %ld (0=>success)\n", fname, ret);
  if (ret != 0) disp_error(ret);
  assert(0 == ret);
  // Is the signature valid?
  ret = SAT_VerifySignature(fname, NULL, 0);
  printf("SAT_VerifySignature(%s) returns %ld (0=>success)\n", fname, ret);
  if (ret != 0) disp_error(ret);
  assert(0 == ret);

    printf("\nEXTRACT AN ATTRIBUTE FROM A DETALLISTA XML FILE: \n");
    fname = "detallista-new-signed.xml";
    elementName = "detallista:detallista";
    attributeName = "documentStructureVersion";
  nchars = SAT_GetXmlAttribute(NULL, 0, fname, attributeName, elementName);
  if (nchars >= 0)
  {
    buf = malloc(nchars+1);
    nchars = SAT_GetXmlAttribute(buf, nchars, fname, attributeName, elementName);
    printf("SAT_GetXmlAttribute('%s','%s','%s')='%s'\n", fname, attributeName, elementName, buf);
    //free(buf); // Save string for later
  }
  else
    disp_error(nchars);
  assert(nchars > 0);
  // Check against the correct answer
  assert(strcmp(buf, "AMC8.1") == 0);
  free(buf);

    printf("\nSIGN CFDI VERSION 3.0 XML FILE: \n");
    fname = "ejemplo_v3_base2011.xml";
    newname = "ejemplo_v3_signed-new.xml";
    keyfile = "aaa010101aaa_CSD_01.key";
    password = "a0123456789";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
    certfile = "aaa010101aaa_CSD_01.cer";
    ret = SAT_SignXml(newname, fname, keyfile, password, certfile, 0);
    printf("SAT_SignXml('%s'-->'%s') returns %ld\n", fname, newname, ret);
  assert (ret == 0);
    // Did we make a valid XML file?
  ret = SAT_ValidateXml(newname, 0);
  printf("SAT_ValidateXml(%s) returns %ld (0=>success)\n", newname, ret);
  assert (ret == 0);

    printf("\nVERIFY SIGNATURE IN AN XML FILE: \n");
    fname = "ejemplo_v3_signed-new.xml";
    ret = SAT_VerifySignature(fname, NULL, 0);
    printf("SAT_VerifySignature('%s') returns %ld\n", fname, ret);
  assert (ret == 0);

    printf("\nEXTRACT THE DIGEST FROM THE SIGNATURE IN AN XML FILE: \n");
    fname = "ejemplo_v3_signed-new.xml";
  ret = SAT_ExtractDigestFromSignature(digest, sizeof(digest)-1, fname, NULL, 0);
  printf("SAT_ExtractDigestFromSignature returns %ld\n", ret);
  if (ret < 0) disp_error(ret);
  printf("Digest=%s\n", digest);
  assert (ret > 0);

    printf("\nFORM THE SHA-1 DIGEST OF THE PIPESTRING IN AN XML FILE: \n");
    fname = "ejemplo_v3_signed-new.xml";
  ret = SAT_MakeDigestFromXml(digest, sizeof(digest)-1, fname, SAT_HASH_SHA1);
  printf("SAT_MakeDigestFromXml(SHA1) returns %ld\n", ret);
  if (ret < 0) disp_error(ret);
  printf("Digest=%s\n", digest);
  assert (ret > 0);

  // [v3.0] NEW FUNCTIONS ADDED...

  printf("\nCHECK PRIVATE KEY MATCHES PUBLIC KEY IN CERTIFICATE: \n");
  certfile = "aaa010101aaa_CSD_01.cer";
  keyfile = "aaa010101aaa_CSD_01.key";
  password = "a0123456789";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
  ret = SAT_CheckKeyAndCert(keyfile, password, certfile, 0);
  printf("SAT_CheckKeyAndCert(%s,%s) returns %ld\n", keyfile, certfile, ret);
  if (ret != 0) disp_error(ret);
  assert(ret == 0);

  certfile = "aaa010101aaa_CSD_02.cer";
  keyfile = "aaa010101aaa_CSD_02.key";
  password = "a0123456789";
  ret = SAT_CheckKeyAndCert(keyfile, password, certfile, 0);
  printf("SAT_CheckKeyAndCert(%s,%s) returns %ld\n", keyfile, certfile, ret);
  if (ret != 0) disp_error(ret);
  assert(ret == 0);

  /* Get embedded certificate from XML doc */
  certfile = "Muestra_v2_signed2011.xml";
  keyfile = "aaa010101aaa_CSD_01.key";
  password = "a0123456789";
  ret = SAT_CheckKeyAndCert(keyfile, password, certfile, 0);
  printf("SAT_CheckKeyAndCert(%s,%s) returns %ld\n", keyfile, certfile, ret);
  if (ret != 0) disp_error(ret);
  assert(ret == 0);

  /* 
  We can pass key file and certificate as "PEM" strings.
  The "BEGIN/END" encapsulation is optional for a certificate,
  but is required for the encrypted private key.
  */
  certfile =
    "-----BEGIN CERTIFICATE-----"
    "MIIE/TCCA+WgAwIBAgIUMzAwMDEwMDAwMDAxMDAwMDA4MDAwDQYJKoZIhvcNAQEF"
    "BQAwggFvMRgwFgYDVQQDDA9BLkMuIGRlIHBydWViYXMxLzAtBgNVBAoMJlNlcnZp"
    "Y2lvIGRlIEFkbWluaXN0cmFjacOzbiBUcmlidXRhcmlhMTgwNgYDVQQLDC9BZG1p"
    "bmlzdHJhY2nDs24gZGUgU2VndXJpZGFkIGRlIGxhIEluZm9ybWFjacOzbjEpMCcG"
    "CSqGSIb3DQEJARYaYXNpc25ldEBwcnVlYmFzLnNhdC5nb2IubXgxJjAkBgNVBAkM"
    "HUF2LiBIaWRhbGdvIDc3LCBDb2wuIEd1ZXJyZXJvMQ4wDAYDVQQRDAUwNjMwMDEL"
    "MAkGA1UEBhMCTVgxGTAXBgNVBAgMEERpc3RyaXRvIEZlZGVyYWwxEjAQBgNVBAcM"
    "CUNveW9hY8OhbjEVMBMGA1UELRMMU0FUOTcwNzAxTk4zMTIwMAYJKoZIhvcNAQkC"
    "DCNSZXNwb25zYWJsZTogSMOpY3RvciBPcm5lbGFzIEFyY2lnYTAeFw0xMDA3MzAx"
    "NjU4NDBaFw0xMjA3MjkxNjU4NDBaMIGWMRIwEAYDVQQDDAlNYXRyaXogU0ExEjAQ"
    "BgNVBCkMCU1hdHJpeiBTQTESMBAGA1UECgwJTWF0cml6IFNBMSUwIwYDVQQtExxB"
    "QUEwMTAxMDFBQUEgLyBBQUFBMDEwMTAxQUFBMR4wHAYDVQQFExUgLyBBQUFBMDEw"
    "MTAxSERGUlhYMDExETAPBgNVBAsMCFVuaWRhZCAxMIGfMA0GCSqGSIb3DQEBAQUA"
    "A4GNADCBiQKBgQDD0ltQNthUNUfzq0t1GpIyapjzOn1W5fGM5G/pQyMluCzP9YlV"
    "AgBjGgzwYp9Z0J9gadg3y2ZrYDwvv8b72goyRnhnv3bkjVRKlus6LDc00K7Jl23U"
    "YzNGlXn5+i0HxxuWonc2GYKFGsN4rFWKVy3Fnpv8Z2D7dNqsVyT5HapEqwIDAQAB"
    "o4HqMIHnMAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgbAMB0GA1UdDgQWBBSYodSw"
    "Rczzj5H7mcO3+mAyXz+y0DAuBgNVHR8EJzAlMCOgIaAfhh1odHRwOi8vcGtpLnNh"
    "dC5nb2IubXgvc2F0LmNybDAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0"
    "dHA6Ly9vY3NwLnNhdC5nb2IubXgvMB8GA1UdIwQYMBaAFOtZfQQimlONnnEaoFiW"
    "KfU54KDFMBAGA1UdIAQJMAcwBQYDKgMEMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0G"
    "CSqGSIb3DQEBBQUAA4IBAQArHQEorApwqumSn5EqDOAjbezi8fLco1cYES/PD+LQ"
    "RM1Vb1g7VLE3hR4S5NNBv0bMwwWAr0WfL9lRRj0PMKLorO8y4TJjRU8MiYXfzSuK"
    "YL5Z16kW8zlVHw7CtmjhfjoIMwjQo3prifWxFv7VpfIBstKKShU0qB6KzUUNwg2O"
    "la4t4gg2JJcBmyIAIInHSGoeinR2V1tQ10aRqJdXkGin4WZ75yMbQH4L0NfotqY6"
    "bpF2CqIY3aogQyJGhUJji4gYnS2DvHcyoICwgawshjSaX8Y0Xlwnuh6EusqhqlhT"
    "gwPNAPrKIXCmOWtqjlDhho/lhkHJMzuTn8AoVapbBUnj"
    "-----END CERTIFICATE-----";
  keyfile = 
    "-----BEGIN ENCRYPTED PRIVATE KEY-----"
    "MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQInuASOYqnkMoCAggA"
    "MBQGCCqGSIb3DQMHBAhXp9z9DSW2PgSCAoAQVlA5ii7OMeprchNs4wwgE2ReNZrw"
    "yVvFISJpnCN4Y906w/MYvpoc5J8q5eIZi9R5Y+FPBaCkprN0w54ypMYQX9oPuSlk"
    "KW5C7LCq5d1mx4ZeGDmqCNXesehi/kY/0Mt/m8CB90x8dwKVE3bLJVwi+IgbgLf5"
    "zStnIXs/LTiA3a9B0Yo8Oe6J4q+Kt0Dlg0+JfSekP76BmImpScc6lUClOX54pCCr"
    "gOOZpdJK7G9u/UpxfVdAQeL2GaLE3tfxlEHuFtHmkXMy51OuSv7er+9FuNMxCYGa"
    "rLiUybDuFlvcV7++9aVql/Qao5gGgKZ1oeCJNb1DZ4xdKJedtx0Kzrp3JmTeSNmG"
    "yzkbczFEM1m2GhPQGD6uadqMCguIwlHHm46s/jWBdEjhuQaedbwcQUiOn20dsfh5"
    "F1Ld3aMDqAXaJdqhqsXrehMXDdHoTV8c6x66cDXAqiJGng6jySDe/sNMtlX1fHiw"
    "6mjq12Wu5E8bmlzHXy8BO4zJS9qgYec34UK30sFk3ZhBXM8ALq2cn1JkGxDDkqjv"
    "dOOocARME+dxNHxw6rw0UESi0i5GtwYRL8eLHuPPJN7zcQG8FytGI8CsTMYSe72L"
    "v0EFacZv7UOQde0vLww8A4EieDLxMu25mZAZ9DebFQwSHtiS4NK5PHFTk4Tm2vEr"
    "X/HZfMQmDQbqG8v/cNNKGX/OGcIVu9eLY7T55OG1n1O2bofVkjZ0gzgAo7ZCWMqX"
    "f/17dljEIQ/b9iHESwri/Lw5g9cQBBAx23pNPP2SteGhvQg50ZyrxGA5/an1ti2Z"
    "u5nQ+7YpPyrKxjERRjk8B8i2qLxDrJuaDHfAOWTvdVoGX9/s7M2p68B6"
    "-----END ENCRYPTED PRIVATE KEY-----";
  password = "a0123456789";
  ret = SAT_CheckKeyAndCert(keyfile, password, certfile, 0);
  printf("SAT_CheckKeyAndCert(STRINGS) returns %ld\n", ret);
  if (ret != 0) disp_error(ret);
  assert(ret == 0);

    printf("\nGET RECEIPT (COMPROBANTE) VERSION NUMBER FROM XML FILE: \n");
    fname = "Muestra_v2_base2011.xml";
  ret = SAT_XmlReceiptVersion(fname, 0);
  printf("SAT_XmlReceiptVersion(%s)=%ld\n", fname, ret);
  if (ret <= 0) disp_error(ret);
  assert (2 == ret);
    fname = "ejemplo_v3_base2011.xml";
  ret = SAT_XmlReceiptVersion(fname, 0);
  printf("SAT_XmlReceiptVersion(%s)=%ld\n", fname, ret);
  if (ret <= 0) disp_error(ret);
  assert (3 == ret);
  // New in [v5.0]...
  fname = "Muestra_v22-base.xml";
  ret = SAT_XmlReceiptVersion(fname, 0);
  printf("SAT_XmlReceiptVersion(%s)=%ld\n", fname, ret);
  if (ret <= 0) disp_error(ret);
  assert (22 == ret);
  fname = "ejemplo_v32-base.xml";
  ret = SAT_XmlReceiptVersion(fname, 0);
  printf("SAT_XmlReceiptVersion(%s)=%ld\n", fname, ret);
  if (ret <= 0) disp_error(ret);
  assert (32 == ret);


  // NEW IN [v4.0] ...

  // Show we can now cope with CFDi XML with no prefixes
    fname = "ejemplo_v3_noprefix.xml";
  ret = SAT_XmlReceiptVersion(fname, 0);
  printf("SAT_XmlReceiptVersion(%s)=%ld\n", fname, ret);
  if (ret <= 0) disp_error(ret);
  assert (3 == ret);

  printf("\nCREATE CADENA ORIGINAL DEL TIMBRE FISCAL DIGITAL (PIPESTRING FOR TFD): \n");
    fname = "ejemplo_v3_tfd.xml";
  nchars = SAT_MakePipeStringFromXml(NULL, 0, fname, SAT_TFD);
  printf("SAT_MakePipeStringFromXml(%s, SAT_TFD) returns %ld\n", fname, nchars);
  if (nchars >= 0)
  {
    buf = malloc(nchars+1);
    nchars = SAT_MakePipeStringFromXml(buf, nchars, fname, SAT_TFD);
    printf("%s\n", buf);
    free(buf);
  }
  else
    disp_error(nchars);
  assert(nchars > 0);

    printf("\nFORM DIGEST OF PIPESTRING FOR TFD: \n");
    fname = "ejemplo_v3_tfd.xml";
  ret = SAT_MakeDigestFromXml(digest, sizeof(digest)-1, fname, SAT_TFD);
  printf("SAT_MakeDigestFromXml returns %ld\n", ret);
  if (ret < 0) disp_error(ret);
  printf("SAT_MakeDigestFromXml(%s, SAT_TFD)=\n%s\n", fname, digest);
  assert (ret > 0);

    printf("\nEXTRACT DIGEST FROM TFD SELLOSAT: \n");
    fname = "ejemplo_v3_tfd.xml";
  certfile = "aaa010101aaa_CSD_02.cer";
  // NB certificate is required for TFD option
  ret = SAT_ExtractDigestFromSignature(digest1, sizeof(digest1)-1, fname, certfile, SAT_TFD);
  printf("SAT_ExtractDigestFromSignature returns %ld\n", ret);
  if (ret < 0) disp_error(ret);
  printf("SAT_ExtractDigestFromSignature(%s, SAT_TFD)=\n%s\n", fname, digest1);
  assert (ret > 0);
  /* check that the two digests match (CAUTION: upper- vs lower-case) */
  assert (_stricmp(digest, digest1) == 0);

    printf("\nPRETEND WE ARE A PAC WITH A KEY ALLOWED TO SIGN THE TFD: \n");
    fname = "ejemplo_v3_tfd.xml";
    keyfile = "aaa010101aaa_CSD_02.key";
    password = "a0123456789";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
  nchars = SAT_MakeSignatureFromXmlEx(NULL, 0, fname, keyfile, password, SAT_TFD);
  if (nchars >= 0)
  {
    buf = malloc(nchars+1);
    nchars = SAT_MakeSignatureFromXmlEx(buf, nchars, fname, keyfile, password, SAT_TFD);
    printf("SAT_MakeSignatureFromXmlEx(%s,%s,SAT_TFD)=\n%s\n", fname, keyfile, buf);
    // free(buf); // Save for later...
  }
  else
    disp_error(nchars);
  assert(nchars > 0);

    // Extract the correct signature field from the XML...
    fname = "ejemplo_v3_tfd.xml";
  elementName = "tfd:TimbreFiscalDigital";
    attributeName = "selloSAT";
  nchars = SAT_GetXmlAttribute(NULL, 0, fname, attributeName, elementName);
  if (nchars >= 0)
  {
    buf1 = malloc(nchars+1);
    nchars = SAT_GetXmlAttribute(buf1, nchars, fname, attributeName, elementName);
    printf("SAT_GetXmlAttribute(%s,%s,%s)=\n%s\n", fname, attributeName, elementName, buf1);
    // free(buf); // Save for later...
  }
  else
    disp_error(nchars);
  assert(nchars > 0);
  // Check against the correct answer
  assert(strcmp(buf, buf1) == 0);
  free(buf);
  free(buf1);

    printf("\nVERIFY SIGNATURE IN TFD SELLOSAT: \n");
    fname = "ejemplo_v3_tfd.xml";
  certfile = "aaa010101aaa_CSD_02.cer";
    ret = SAT_VerifySignature(fname, certfile, SAT_TFD);
    printf("SAT_VerifySignature(%s,%s,SAT_TFD')=%ld \n(expected 0)\n", fname, certfile, ret);
  assert (ret == 0);

  // New in [v4.1]
  printf("\nADD UTF-8 BOM TO EXISTING FILE:\n");
  fname = "Muestra_v2_signed2011.xml";
  newname = "Muestra_v2_with_BOM.xml";
  n = SAT_FixBOM(newname, fname, 0);
  printf("SAT_FixBOM(%s->%s})=%ld (expected 0)\n", fname, newname, n);
  assert(n == 0);

  // NEW IN [v5.0]... 

  printf("\nEXTRACT ATTRIBUTES FROM CONSECUTIVE ELEMENTS:\n");
  fname = "ejemplo_v3_base2011.xml";
  attributeName = "descripcion";
  elementName = "cfdi:Concepto";
  printf("For file '%s'\n", fname);
  for (i = 1; i <= 100; i++)
  {
    char s[64];
    sprintf(eName, "%s[%d]", elementName, i);
    SAT_GetXmlAttribute(s, sizeof(s)-1, fname, attributeName, eName);
    printf("SAT_GetXmlAttribute(%s,%s)=%s\n", attributeName, eName, s);
    if (strlen(s) == 0)
    {
      break;
    }
  }

  printf("\nDETECT VERSION 2.2 OR 3.2 AUTOMATICALLY AND SIGN:\n");
  certfile = "aaa010101aaa_CSD_01.cer";
  keyfile = "aaa010101aaa_CSD_01.key";
  password = "a0123456789";

  fname = "Muestra_v22-base.xml";
  newname = "Muestra_v22-new_signed.xml";
  n = SAT_SignXml(newname, fname, keyfile, password, certfile, 0);
  printf("SAT_SignXml('%s'-->'%s') returns %ld\n", fname, newname, n);
  assert(n == 0);
  // Did we make a valid XML file?
  n = SAT_ValidateXml(newname, 0);
  printf("Sat.SAT_ValidateXml('%s') returns %ld\n", newname, n);
  assert(n == 0);

  fname = "V2_2-base.xml";
  newname = "V2_2-new_signed.xml";
  n = SAT_SignXml(newname, fname, keyfile, password, certfile, 0);
  printf("SAT_SignXml('%s'-->'%s') returns %ld\n", fname, newname, n);
  assert(n == 0);
  // Did we make a valid XML file?
  n = SAT_ValidateXml(newname, 0);
  printf("Sat.SAT_ValidateXml('%s') returns %ld\n", newname, n);
  assert(n == 0);

  fname = "ejemplo_v32-base.xml";
  newname = "ejemplo_v32-new_signed.xml";
  n = SAT_SignXml(newname, fname, keyfile, password, certfile, 0);
  printf("SAT_SignXml('%s'-->'%s') returns %ld\n", fname, newname, n);
  assert(n == 0);
  // Did we make a valid XML file?
  n = SAT_ValidateXml(newname, 0);
  printf("Sat.SAT_ValidateXml('%s') returns %ld\n", newname, n);
  assert(n == 0);

  fname = "V3_2-base.xml";
  newname = "V3_2-new_signed.xml";
  n = SAT_SignXml(newname, fname, keyfile, password, certfile, 0);
  printf("SAT_SignXml('%s'-->'%s') returns %ld\n", fname, newname, n);
  assert(n == 0);
  // Did we make a valid XML file?
  n = SAT_ValidateXml(newname, 0);
  printf("Sat.SAT_ValidateXml('%s') returns %ld\n", newname, n);
  assert(n == 0);

  printf("\nVALIDATE XML WITH STRICT AND LOOSE OPTIONS:\n");
  printf("Default strict behaviour (badly formed CURP attribute):\n");
  fname = "V3_2_BadCurp.xml";
  n = SAT_ValidateXml(fname, 0);
  printf("SAT_ValidateXml('%s') returns %ld\n", fname, n);
  disp_error(n);
  assert(n != 0);

  printf("\nUsing XmlOption.Loose:\n");
  n = SAT_ValidateXml(fname, SAT_XML_LOOSE);
  printf("Sat.SAT_ValidateXml('%s', LOOSE) returns %ld\n", fname, n);
  assert(n == 0);

  printf("\nGET PRIVATE KEY AS BASE64:\n");
  fname = "aaa010101aaa_CSD_01.key";
  password = "a0123456789";
  nchars = SAT_GetKeyAsString(NULL, 0, fname, password, 0);
  assert(nchars > 0);
  buf = malloc(nchars+1);
  nchars = SAT_GetKeyAsString(buf, nchars, fname, password, 0);
  printf("SAT_GetKeyAsString(%s)=\n%s\n", fname, buf);
  printf("SAT_GetKeyAsString(%s) length=%d\n", fname, nchars);


  // Finally, display current DLL version
  SAT_CompileTime(cdate, sizeof(cdate)-1);
  printf("\nFirmaSAT version=%ld [%s]\n", SAT_Version(), cdate);

// Change "#if 0" to "#if 1" to activate this
#if 0
  printf("\nDISPLAY ALL POSSIBLE ERROR MESSAGES:\n");
  display_all_errors();
#endif

  printf("\nALL DONE.\n");

  return 0;
}

void display_all_errors(void)
{
  long i, nchars;
  char errbuf[256];
  for (i = 0; i < 10000; i++)
  {
    nchars = SAT_ErrorLookup(errbuf, sizeof(errbuf)-1, i);
    if (nchars > 0)
      printf("%ld=>%s\n", i, errbuf);
  }
}