program knut;

{
Written by David Ireland of DI Management Services Pty Ltd <http://www.di-mgt.com.au>
Use at your own risk. No warranties.
Requires installation of CryptoSys API <http://www.cryptosys.net/>
}

{
Compiles under Free Pascal in Delphi mode

  >fpc knut
  Free Pascal Compiler version 3.0.0 [2015/11/16] for i386
  Copyright (c) 1993-2015 by Florian Klaempfl and others
  Target OS: Win32 for i386
  Compiling knut.pas
  Linking knut.exe
  146 lines compiled, 1.7 sec, 76464 bytes code, 4164 bytes data
  
OUTPUT:

  >knut
  Running knut.exe at 8/03/2016 13:54:18
  API_Version=50100

  Aes128/CBC/pkcs5
  KY=0123456789ABCDEFF0E1D2C3B4A59687
  IV=FEDCBA9876543210FEDCBA9876543210
  PT=[Now is the time for all good men to]
  PT=4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E20746F
  CT=C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E17753C7E8DF5975A
  36677355F5C6584228B
  OK=C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E17753C7E8DF5975A
  36677355F5C6584228B
  P1=4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E20746F
  P1=[Now is the time for all good men to]
  
}


{$APPTYPE CONSOLE}
{$mode Delphi}
{$ASSERTIONS ON}

uses
  SysUtils, diCryptoSys;


const
  CRLF = #13 + #10;

type
  // Define our own dynamic array type for `Array of Byte`
  TByteArr = Array of Byte;

// HELPER FUNCTIONS
  
Function cnvFromHex(strhex : AnsiString) : TByteArr;
var
  nbytes : Integer;
begin
  nbytes := CNV_BytesFromHexStr(NIL, 0, strhex);
  // Dimension the byte array to receive the output
  SetLength(Result, nbytes);
  // Do the conversion
  CNV_BytesFromHexStr(Pointer(Result), nbytes, strhex);
end;

Function cnvToHex(bytes : TByteArr) : AnsiString;
var
  nchars : Integer;
  nbytes : Integer;
begin
  nbytes := Length(bytes);
  nchars := nbytes * 2;
  // Dimension the output
  SetLength(Result, nchars);
  // Do the conversion
  CNV_HexStrFromBytes(Pointer(Result), nchars, Pointer(bytes), nbytes);
end;

Function bytes_from_string(s : AnsiString) : TByteArr;
var
  i : Integer;
begin
  SetLength(Result, Length(s));
  for i := 1 to Length(s) do
    Result[i-1] := Ord(s[i]);
end;

Function bytes_to_string(bytes : TByteArr) : AnsiString;
var
  i : Integer;
begin
  SetLength(Result, Length(bytes));
  for i := 1 to Length(bytes) do
    Result[i] := Chr(bytes[i-1]);
end;

Procedure pr_hexbytes(msg : String; arrbytes : Array of Byte);
// Displays an array of bytes as a hex string
var
  i : Integer;
  nbytes : Integer;
begin
  Write(msg);
  nbytes := Length(arrbytes);
  For i := 0 to (nbytes - 1) do
    Write(IntToHex(arrbytes[i], 2));
  WriteLn;
end;

  

Procedure do_tests;
var
  key : TByteArr;
  iv  : TByteArr;
  pt  : TByteArr;
  ct  : TByteArr;
  p1  : TByteArr;
  correct : TByteArr;
  s   : AnsiString;
  s1  : AnsiString;
  n : LongInt;


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

  // DO QUICK CHECK 
  WriteLn('API_Version='+(IntToStr(API_Version())));
  
  WriteLn(CRLF+'Aes128/CBC/pkcs5');
  
  // CONVERT FROM HEX TO BYTE ARRAYS
  key := cnvFromHex('0123456789ABCDEFF0E1D2C3B4A59687');
  WriteLn('KY=' + cnvToHex(key));
  iv := cnvFromHex('FEDCBA9876543210FEDCBA9876543210');
  WriteLn('IV=' + cnvToHex(iv));
  s := 'Now is the time for all good men to';
  WriteLn('PT=[' + s + ']');
  pt := bytes_from_string(s);
  WriteLn('PT=' + cnvToHex(pt));
  correct := cnvFromHex('C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E17753C7E8DF5975A36677355F5C6584228B');
  
  // ENCRYPT
  // 1. Dimension the output buffer -- IMPORTANT
  n := CIPHER_EncryptBytesPad(NIL, 0, Pointer(pt), Length(pt), Pointer(key), Pointer(iv), 'Aes128/CBC/pkcs5', 0);
  Assert(n > 0);
  SetLength(ct, n);
  // 2. Do the encryption
  n := CIPHER_EncryptBytesPad(Pointer(ct), Length(ct), Pointer(pt), Length(pt), Pointer(key), Pointer(iv), 'Aes128/CBC/pkcs5', 0);
  WriteLn('CT=', cnvToHex(ct));
  WriteLn('OK=', cnvToHex(correct));
  
  // DECRYPT
  // (Output is always the same length as the input or shorter)
  SetLength(p1, Length(ct));
  n := CIPHER_DecryptBytesPad(Pointer(p1), Length(p1), Pointer(ct), Length(ct), Pointer(key), Pointer(iv), 'Aes128/CBC/pkcs5', 0);
  Assert(n > 0);
  //WriteLn('P1=', cnvToHex(p1));
  // Shorten if necessary to remove padding
  SetLength(p1, n);
  WriteLn('P1=', cnvToHex(p1));
  
  // Assuming it's valid ASCII text, convert to a string
  s1 := bytes_to_string(p1);
  WriteLn('P1=[' + s1 + ']');

  WriteLn(CRLF+'ALL DONE.');

end;

// MAIN PROCEDURE
begin
  try
    do_tests;

   except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
   end;
 end.