program TestFirmaSat;
{
  Some tests for the Delphi/FreePascal interface to FirmaSAT
  $Id: TestFirmaSat.pas $
  Copyright (C) 2010-16 David Ireland, DI Management Services Pty Limited.
  All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
  Provided as is with no warranties. Use at your own risk.
  Last updated:
    $Date: 2016-01-27 04:41 $
    $Revision: 7.2.0 $
   Remember to unzip the test files from FirmaSAT-testfiles.zip into TEST_DIR.
}
 
{$APPTYPE CONSOLE}
{$mode Delphi}

uses
  SysUtils, diFirmaSat;

const
  { WARNING: this is where we expect to find the test files.
    CHANGE THIS TO SUIT }
  TEST_DIR = 'C:\Test\Muestra';

  CRLF = #13 + #10;

Procedure disp_error(nErrCode : Integer; showLast : Boolean = True);
const
  MAX_CHARS = SAT_MAX_ERROR_CHARS;
var
  msg : Array[0..MAX_CHARS-1] of AnsiChar;
  nchars : Integer;
begin
  Write('Error code ' + IntToStr(nErrCode));

  nchars := SAT_ErrorLookup(msg, MAX_CHARS, nErrCode);
  If (nchars > 0) then
    Write(': ' +  msg);

  if showLast then
  begin
    nchars := SAT_LastError(msg, MAX_CHARS);
    if (nchars > 0) then
      Write(': ' +  msg);
  end;
  WriteLn('');
end;

Function RequiredFilesExist : Boolean;
begin
  Result := True; // Innocent until proven guilty
  if Not FileExists('emisor.cer') then Result := False;
  if Not FileExists('emisor.key') then Result := False;
  if Not FileExists('detallista_base2015.xml') then Result := False;
  if Not FileExists('V3_2_BadCurp.xml') then Result := False;
  if Not FileExists('ejemplo_v32-base2015.xml') then Result := False;
  if Not FileExists('ejemplo_v32-signed2015.xml') then Result := False;
  if Not FileExists('AC4_SAT.cer') then Result := False;
  if Not FileExists('hehf7712015z2.cer') then Result := False;
  
end;

// DO THE TESTS TO CALL FIRMASAT FUNCTIONS
Procedure do_tests;
var
  ret : Integer;
  nchars : Integer;
  buf : AnsiString;
  buf1 : AnsiString;
  ch  : Char;
  fname : AnsiString;
  newname : AnsiString;
  keyfile : AnsiString;
  certfile : AnsiString;
  password : AnsiString;
  elementName : AnsiString;
  attributeName : AnsiString;
  query : AnsiString;
  digest : Array[0..SAT_MAX_HASH_CHARS-1] of AnsiChar;
  numstr : Array[Byte] of AnsiChar;

begin
  WriteLn('Running ' + ExtractFileName(ParamStr(0)) + ' at ' + DateTimeToStr(Now));

  // Set current working directory to find test files
  ChDir(TEST_DIR);
  if Not RequiredFilesExist then
  begin
    WriteLn('Cannot find required files! Please make sure test files are copied to TEST_DIR.');
    ReadLn;
    Exit;
  end;

  WriteLn(CRLF + 'GENERAL:');
  WriteLn('Interrogate the core diFirmaSat DLL:');
  WriteLn('SAT_Version='+(IntToStr(SAT_Version)));

  nchars := SAT_CompileTime(NIL, 0);
  WriteLn('SAT_CompileTime returns nchars=' + IntToStr(nchars));
  buf := AnsiString(StringOfChar(#0,nchars));
  nchars := SAT_CompileTime(Pointer(buf), nchars);
  WriteLn('SAT_CompileTime=' + buf);
  Assert (nchars > 0);

  nchars := SAT_ModuleName(NIL, 0, 0);
  WriteLn('SAT_ModuleName returns nchars=' + IntToStr(nchars));
  buf := AnsiString(StringOfChar(#0,nchars));
  nchars := SAT_ModuleName(Pointer(buf), nchars, 0);
  WriteLn('SAT_ModuleName=' + buf);
  Assert (nchars > 0);

  ch := Chr(SAT_LicenceType());
  WriteLn('SAT_LicenceType='+ ch);


  WriteLn(CRLF + 'TRY VALIDATING XML FILES:');
  WriteLn('1. A valid one:');
  fname := 'ejemplo_v32-signed2015.xml';
  ret := SAT_ValidateXml(fname, 0);
  WriteLn('SAT_ValidateXml(' + string(fname) + ') returns ' + IntToStr(ret) + ' (0=>success)');
  WriteLn('2. An invalid one:');
  fname := 'V3_2_BadCurp.xml';
  ret := SAT_ValidateXml(fname, 0);
  WriteLn('SAT_ValidateXml(' + string(fname) + ') returns ' + IntToStr(ret) + ' (expected error)');
  If (ret <> 0) then disp_error(ret, False);

  WriteLn(CRLF + 'FORM THE PIPESTRING FROM AN XML FILE:');
  fname := 'ejemplo_v32-signed2015.xml';
  nchars := SAT_MakePipeStringFromXml(NIL, 0, fname, 0);
  WriteLn('SAT_MakePipeStringFromXml returns nchars=' + IntToStr(nchars));
  { This may be larger than required }
  buf := AnsiString(StringOfChar(#0,nchars));
  nchars := SAT_MakePipeStringFromXml(Pointer(buf), nchars, fname, 0);
  { Make sure we trim any trailing null characters }
  buf := Copy(buf, 1, nchars);
  WriteLn('SAT_MakePipeStringFromXml=' + CRLF + buf);
  Assert(nchars > 0);

  WriteLn(CRLF + 'SIGN AN XML FILE:');
  fname := 'ejemplo_v32-base2015.xml';
  newname := 'ejemplo_v32_signed_new.xml';
  keyfile := 'emisor.key';
  password := '12345678a';   // CAUTION: DO NOT HARD-CODE REAL PASSWORDS!
  certfile := 'emisor.cer';
  ret := SAT_SignXml(newname, fname, keyfile, password, certfile, 0);
  WriteLn('SAT_SignXml("' + string(fname) + '"-->"' + string(newname) + '") returns ' + IntToStr(ret));
  If (ret <> 0) then disp_error(ret);
  Assert (0 = ret);

  // Did we make a valid XML file?
  ret := SAT_ValidateXml(newname, 0);
  WriteLn('SAT_ValidateXml(' + string(newname) + ') returns ' + IntToStr(ret) + ' (0=>success)');
  Assert(0 = ret);

  WriteLn(CRLF + 'VERIFY A SIGNATURE IN AN XML FILE:');
  WriteLn('1. One we know is good:');
  fname := 'ejemplo_v32-signed2015.xml';
  ret := SAT_VerifySignature(fname, '', 0);
  WriteLn('SAT_VerifySignature(' + string(fname) + ') returns ' + IntToStr(ret) + ' (0=>success)');
  Assert(0 = ret);

  WriteLn('2. One we just made, so it should be good:');
  fname := newname;
  ret := SAT_VerifySignature(fname, '', 0);
  WriteLn('SAT_VerifySignature(' + string(fname) + ') returns ' + IntToStr(ret) + ' (0=>success)');
  Assert(0 = ret);

  WriteLn(CRLF + 'FORM THE DIGEST OF THE PIPESTRING IN AN XML FILE:');
  fname := 'ejemplo_v32-signed2015.xml';
  nchars := SAT_MakeDigestFromXml(digest, Length(digest), fname, 0);
  WriteLn('SAT_MakeDigestFromXml returns nchars=' + IntToStr(nchars));
  if (nchars < 0) then disp_error(nchars);
  WriteLn('Digest=' + digest);
  Assert(nchars > 0);

  WriteLn(CRLF + 'EXTRACT THE DIGEST FROM THE SIGNATURE IN AN XML FILE:');
  fname := 'ejemplo_v32-signed2015.xml';
  nchars := SAT_ExtractDigestFromSignature(digest, Length(digest), fname, '', 0);
  WriteLn('SAT_ExtractDigestFromSignature returns nchars=' + IntToStr(nchars));
  if (nchars < 0) then disp_error(nchars);
  WriteLn('Digest=' + digest);
  Assert(nchars > 0);

  WriteLn(CRLF + 'EXTRACT AN ATTRIBUTE FROM AN XML FILE:');
  fname := 'ejemplo_v32-signed2015.xml';
  elementName := 'Comprobante';
  attributeName := 'sello';
  nchars := SAT_GetXmlAttribute(NIL, 0, fname, attributeName, elementName);
  if (nchars >= 0) then
    begin
      buf := AnsiString(StringOfChar(#0,nchars));
      nchars := SAT_GetXmlAttribute(Pointer(buf), nchars, fname, attributeName, elementName);
      WriteLn('SAT_GetXmlAttribute(' + string(fname) + ', ' + string(attributeName) + ', ' + string(elementName) + ')=' + CRLF + string(buf));
    end
  else
    disp_error(nchars);
  Assert(nchars > 0);

  WriteLn(CRLF + 'GET DETAILS OF X.509 CERTIFICATE:');
  WriteLn('1. From embedded `certificado` in XML');
  fname := 'ejemplo_v32-signed2015.xml';
  WriteLn('Input file: ' + fname);
  nchars := SAT_GetCertNumber(numstr, Length(numstr), fname, 0);
  WriteLn('SAT_GetCertNumber returns nchars=' + IntToStr(nchars));
  if (nchars < 0) then disp_error(nchars);
  WriteLn('serialNumber=' + numstr);
  nchars := SAT_GetCertExpiry(numstr, Length(numstr), fname, 0);
  WriteLn('SAT_GetCertExpiry returns nchars=' + IntToStr(nchars));
  if (nchars < 0) then disp_error(nchars);
  WriteLn('notAfter=' + numstr);
  Assert(nchars > 0);

  WriteLn('2. From X.509 file');
  fname := 'emisor.cer';
  WriteLn('Input file: ' + fname);
  nchars := SAT_GetCertNumber(numstr, Length(numstr), fname, 0);
  WriteLn('SAT_GetCertNumber returns nchars=' + IntToStr(nchars));
  if (nchars < 0) then disp_error(nchars);
  WriteLn('serialNumber=' + numstr);
  nchars := SAT_GetCertExpiry(numstr, Length(numstr), fname, 0);
  WriteLn('SAT_GetCertExpiry returns nchars=' + IntToStr(nchars));
  if (nchars < 0) then disp_error(nchars);
  WriteLn('notAfter=' + numstr);
  Assert(nchars > 0);

  WriteLn(CRLF + 'GET CERTIFICATE AS A BASE64 STRING:');
  fname := 'emisor.cer';
  WriteLn('For file ' + string(fname) + '...');
  nchars := SAT_GetCertAsString(NIL, 0, fname, 0);
  if (nchars >= 0) then
    begin
      buf := AnsiString(StringOfChar(#0,nchars));
      nchars := SAT_GetCertAsString(Pointer(buf), nchars, fname, 0);
      WriteLn('SAT_GetCertAsString(' + string(fname) + ')=' + CRLF + string(buf));
      WriteLn('Length of cert string=' + IntToStr(nchars));
    end
  else
    disp_error(nchars);
  Assert(nchars > 0);

  // Compare against string from XML file
  fname := 'ejemplo_v32-signed2015.xml';
  WriteLn('For file ' + string(fname) + '...');
  nchars := SAT_GetCertAsString(NIL, 0, fname, 0);
  if (nchars >= 0) then
    begin
      buf1 := AnsiString(StringOfChar(#0,nchars));
      nchars := SAT_GetCertAsString(Pointer(buf1), nchars, fname, 0);
      WriteLn('Length of cert string=' + IntToStr(nchars));
    end
  else
    disp_error(nchars);
  Assert(nchars > 0);

  If buf <> buf1 then
    WriteLn('ERROR: cert strings do not match!')
  else
    WriteLn('OK, cert strings match.');

  WriteLn(CRLF + 'MAKE A SIGNATURE FROM A BASE XML FILE:');
  fname := 'ejemplo_v32-base2015.xml';
  keyfile := 'emisor.key';
  password := '12345678a';   // CAUTION: DO NOT HARD-CODE REAL PASSWORDS!
  nchars := SAT_MakeSignatureFromXml(NIL, 0, fname, keyfile, password);
  if (nchars >= 0) then
    begin
      buf := AnsiString(StringOfChar(#0,nchars));
      nchars := SAT_MakeSignatureFromXml(Pointer(buf), nchars, fname, keyfile, password);
      WriteLn('SAT_MakeSignatureFromXml(' + string(fname) + ', ' + string(keyfile) + ')=' + CRLF + string(buf));
    end
  else
    disp_error(nchars);
  Assert(nchars > 0);

  WriteLn(CRLF + 'SIGN A DETALLISTA XML FILE:');
  fname := 'detallista_base2015.xml';
  newname := 'detallista-new-signed.xml';
  keyfile := 'emisor.key';
  password := '12345678a';   // CAUTION: DO NOT HARD-CODE REAL PASSWORDS!
  certfile := 'emisor.cer';
  ret := SAT_SignXml(newname, fname, keyfile, password, certfile, 0);
  WriteLn('SAT_SignXml("' + string(fname) + '"-->"' + string(newname) + '") returns ' + IntToStr(ret));
  If (ret <> 0) then disp_error(ret);
  Assert(0 = ret);

    // Did we make a valid XML file?
  fname := newname;
  ret := SAT_ValidateXml(fname, 0);
  WriteLn('SAT_ValidateXml(' + string(fname) + ') returns ' + IntToStr(ret) + ' (0=>success)');
  If (ret <> 0) then disp_error(ret);
  Assert(0 = ret);
  // Is the signature valid?
  ret := SAT_VerifySignature(fname, '', 0);
  WriteLn('SAT_VerifySignature(' + string(fname) + ') returns ' + IntToStr(ret) + ' (0=>success)');
  If (ret <> 0) then disp_error(ret);
  Assert(0 = ret);

  WriteLn(CRLF + 'EXTRACT AN ATTRIBUTE FROM A DETALLISTA XML FILE:');
  fname := 'detallista-new-signed.xml';
  elementName := 'detallista:detallista';
  attributeName := 'documentStructureVersion';
  nchars := SAT_GetXmlAttribute(NIL, 0, fname, attributeName, elementName);
  if (nchars >= 0) then
    begin
      buf := AnsiString(StringOfChar(#0,nchars));
      nchars := SAT_GetXmlAttribute(Pointer(buf), nchars, fname, attributeName, elementName);
      WriteLn('SAT_GetXmlAttribute(' + string(fname) + ', ' + string(attributeName) + ', ' + string(elementName) + ')="' + string(buf) + '"');
    end
  else
    disp_error(nchars);
  Assert(nchars > 0);
  // Check against the correct answer
  if AnsiCompareStr(string(buf), 'AMC8.1')= 0 then
    WriteLn('OK, attribute value is correct.')
  else
    WriteLN('ERROR: attribute value is wrong!');
  Assert(AnsiCompareStr(string(buf), 'AMC8.1')= 0);

  WriteLn(CRLF + 'QUERY KEY SIZE OF CERTIFICATE:');
  fname := 'AC4_SAT.cer';
  query := 'keySize';
  nchars := SAT_QueryCert(NIL, 0, fname, query, 0);
  if (nchars >= 0) then
    begin
      buf := AnsiString(StringOfChar(#0,nchars));
      nchars := SAT_QueryCert(Pointer(buf), nchars, fname, query, 0);
      WriteLn('SAT_QueryCert(' + string(fname) + ', ' + string(query) + ')=[' + string(buf) + ']');
    end
  else
    disp_error(nchars);
  Assert(nchars > 0);

  WriteLn(CRLF + 'QUERY SIGNATURE ALGORITHM IN CERTIFICATES:');
  fname := 'emisor.cer';
  query := 'sigAlg';
  nchars := SAT_QueryCert(NIL, 0, fname, query, 0);
  if (nchars >= 0) then
    begin
      buf := AnsiString(StringOfChar(#0,nchars));
      nchars := SAT_QueryCert(Pointer(buf), nchars, fname, query, 0);
      WriteLn('SAT_QueryCert(' + string(fname) + ', ' + string(query) + ')=[' + string(buf) + ']');
    end
  else
    disp_error(nchars);
  Assert(nchars > 0);
  fname := 'hehf7712015z2.cer';
  query := 'sigAlg';
  nchars := SAT_QueryCert(NIL, 0, fname, query, 0);
  if (nchars >= 0) then
    begin
      buf := AnsiString(StringOfChar(#0,nchars));
      nchars := SAT_QueryCert(Pointer(buf), nchars, fname, query, 0);
      WriteLn('SAT_QueryCert(' + string(fname) + ', ' + string(query) + ')=[' + string(buf) + ']');
    end
  else
    disp_error(nchars);
  Assert(nchars > 0);

  WriteLn(CRLF+'ALL DONE.');
end;

// MAIN PROCEDURE
begin
  try
    do_tests;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.