{-Test prog for AES CTR Seek, (c) we July 2010}

program T_AES_CS;

{$i STD.INC}

{$ifdef APPCONS}
  {$apptype console}
{$endif}

{$ifdef BIT16}
{$N+,F+}
{$endif}


uses
  {$ifdef WINCRT}
     wincrt,
  {$endif}
  HRTimer,
  {$ifdef USEDLL}
    {$ifdef VirtualPascal}
      AES_Intv;
    {$else}
      AES_Intf;
    {$endif}
  {$else}
    aes_type, aes_ctr;
  {$endif}

{USE_INT64: if Int64 and errout available}

{$ifdef FPC}
  {$ifdef FPC2Plus}
    {$define USE_INT64}
  {$endif}
{$endif}
{$ifdef CONDITIONALEXPRESSIONS}  {D6+}
  {$define USE_INT64}
{$endif}


{---------------------------------------------------------------------------}
procedure My_IncMSBFull(var CTR: TAESBlock);
{$ifdef USEDLL} stdcall; {$endif}
  {-Increment CTR[15]..CTR[0]}
var
  j: integer;
begin
  {This is the same as the standard pre-defined function, but it cannot be }
  {recognized by its @address and therefore the seek loop will be performed}
  for j:=15 downto 0 do begin
    if CTR[j]=$FF then CTR[j] := 0
    else begin
      inc(CTR[j]);
      exit;
    end;
  end;
end;


var
  HR: THRTimer;

var
  ctx1, ctx2: TAESContext;
  Err : integer;

{$ifdef USE_INT64}
const
  BSIZE=$8000;
{$else}
const
  BSIZE=8192;
{$endif}

var
  pbuf, cbuf1, cbuf2: array[0..BSIZE-1] of byte;

{---------------------------------------------------------------------------}
procedure CheckError;
begin
  if Err<>0 then begin
    writeln('Error ',Err);
    halt;
  end;
end;


{---------------------------------------------------------------------------}
procedure randomtest(userdef: boolean);
const
  key128 : array[0..15] of byte = ($2b,$7e,$15,$16,$28,$ae,$d2,$a6,
                                   $ab,$f7,$15,$88,$09,$cf,$4f,$3c);

  key192 : array[0..23] of byte = ($8e,$73,$b0,$f7,$da,$0e,$64,$52,
                                   $c8,$10,$f3,$2b,$80,$90,$79,$e5,
                                   $62,$f8,$ea,$d2,$52,$2c,$6b,$7b);

  key256 : array[0..31] of byte = ($60,$3d,$eb,$10,$15,$ca,$71,$be,
                                   $2b,$73,$ae,$f0,$85,$7d,$77,$81,
                                   $1f,$35,$2c,$07,$3b,$61,$08,$d7,
                                   $2d,$98,$10,$a3,$09,$14,$df,$f4);

     CTR : TAESBlock =            ($f0,$f1,$f2,$f3,$f4,$f5,$f6,$f7,
                                   $f8,$f9,$fa,$fb,$fc,$fd,$fe,$ff);

  plain  : array[0..63] of byte = ($6b,$c1,$be,$e2,$2e,$40,$9f,$96,
                                   $e9,$3d,$7e,$11,$73,$93,$17,$2a,
                                   $ae,$2d,$8a,$57,$1e,$03,$ac,$9c,
                                   $9e,$b7,$6f,$ac,$45,$af,$8e,$51,
                                   $30,$c8,$1c,$46,$a3,$5c,$e4,$11,
                                   $e5,$fb,$c1,$19,$1a,$0a,$52,$ef,
                                   $f6,$9f,$24,$45,$df,$4f,$9b,$17,
                                   $ad,$2b,$41,$7b,$e6,$6c,$37,$10);

     ct1 : array[0..63] of byte = ($87,$4d,$61,$91,$b6,$20,$e3,$26,
                                   $1b,$ef,$68,$64,$99,$0d,$b6,$ce,
                                   $98,$06,$f6,$6b,$79,$70,$fd,$ff,
                                   $86,$17,$18,$7b,$b9,$ff,$fd,$ff,
                                   $5a,$e4,$df,$3e,$db,$d5,$d3,$5e,
                                   $5b,$4f,$09,$02,$0d,$b0,$3e,$ab,
                                   $1e,$03,$1d,$da,$2f,$be,$03,$d1,
                                   $79,$21,$70,$a0,$f3,$00,$9c,$ee);

     ct2 : array[0..63] of byte = ($1a,$bc,$93,$24,$17,$52,$1c,$a2,
                                   $4f,$2b,$04,$59,$fe,$7e,$6e,$0b,
                                   $09,$03,$39,$ec,$0a,$a6,$fa,$ef,
                                   $d5,$cc,$c2,$c6,$f4,$ce,$8e,$94,
                                   $1e,$36,$b2,$6b,$d1,$eb,$c6,$70,
                                   $d1,$bd,$1d,$66,$56,$20,$ab,$f7,
                                   $4f,$78,$a7,$f6,$d2,$98,$09,$58,
                                   $5a,$97,$da,$ec,$58,$c6,$b0,$50);

     ct3 : array[0..63] of byte = ($60,$1e,$c3,$13,$77,$57,$89,$a5,
                                   $b7,$a7,$f5,$04,$bb,$f3,$d2,$28,
                                   $f4,$43,$e3,$ca,$4d,$62,$b5,$9a,
                                   $ca,$84,$e9,$90,$ca,$ca,$f5,$c5,
                                   $2b,$09,$30,$da,$a2,$3d,$e9,$4c,
                                   $e8,$70,$17,$ba,$2d,$84,$98,$8d,
                                   $df,$c9,$c5,$8d,$b6,$7a,$ad,$a6,
                                   $13,$c2,$dd,$08,$45,$79,$41,$a6);

var
  ct: array[0..255] of byte;
  SO: integer;
begin

  writeln('NIST vector test: 128 bit key');
  Err := AES_CTR_Init(key128, 128, CTR, ctx2);
  CheckError;
  if userdef then begin
    Err := AES_SetIncProc({$ifdef FPC_ProcVar}@{$endif}My_IncMSBFull, ctx2);
    CheckError;
  end;
  for SO:=0 to 63 do begin
    write('.');
    Err := AES_CTR_Seek(CTR, SO, 0, ctx2);
    CheckError;
    Err := AES_CTR_Encrypt(@plain[SO], @ct[SO], 1, ctx2);
    if ct[SO]<>ct1[SO] then begin
      writeln('Diff:  SO=',SO:2,'  ct1[SO]=',ct1[SO]:3,'  ct[SO]=',ct[SO]:3);
    end;
  end;
  writeln(' done');

  writeln('NIST vector test: 192 bit key');
  Err := AES_CTR_Init(key192, 192, CTR, ctx2);
  CheckError;
  for SO:=0 to 63 do begin
    write('.');
    {$ifdef USE_INT64}
      Err := AES_CTR_Seek64(CTR, SO, ctx2);
    {$else}
      Err := AES_CTR_Seek(CTR, SO, 0, ctx2);
    {$endif}
    CheckError;
    Err := AES_CTR_Encrypt(@plain[SO], @ct[SO], 1, ctx2);
    if ct[SO]<>ct2[SO] then begin
      writeln('Diff:  SO=',SO:2,'  ct2[SO]=',ct2[SO]:3,'  ct[SO]=',ct[SO]:3);
    end;
  end;
  writeln(' done');

  writeln('NIST vector test: 256 bit key');
  Err := AES_CTR_Init(key256, 256, CTR, ctx2);
  CheckError;
  for SO:=63 downto 0 do begin
    write('.');
    Err := AES_CTR_Seek(CTR, SO, 0, ctx2);
    CheckError;
    Err := AES_CTR_Encrypt(@plain[SO], @ct[SO], 1, ctx2);
    if ct[SO]<>ct3[SO] then begin
      writeln('Diff:  SO=',SO:2,'  ct3[SO]=',ct2[SO]:3,'  ct[SO]=',ct[SO]:3);
    end;
  end;
  writeln(' done');
end;


{---------------------------------------------------------------------------}
procedure bigtest(n: integer);
const
  key128 : array[0..15] of byte = ($2b,$7e,$15,$16,$28,$ae,$d2,$a6,
                                   $ab,$f7,$15,$88,$09,$cf,$4f,$3c);
     CTR : TAESBlock =            ($ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,
                                   $ff,$ff,$ff,$ff,$fd,$fc,$fb,$fa);

{$ifdef USE_INT64}
var
  ofs: int64;
const
  oma = int64($3FFFFFFF)*$100;  {avoid braindamaged D2 error}
{$else}
var
  ofs: longint;
const
  oma = $6FFFFFFF;
{$endif}
var
  i: integer;
begin
  for i:=0 to BSIZE-1 do pbuf[i] := random(256);
  Err := AES_CTR_Init(key128, 128, CTR, ctx1);
  CheckError;
  case n of
    1: begin
         writeln('IncProc = AES_IncMSBFull,   max. offset = ',oma);
         {$ifdef USE_INT64}
           writeln(erroutput, 'IncProc = AES_IncMSBFull,   max. offset = ',oma);
         {$endif}
         {$ifdef FPC_ProcVar}
           err := AES_SetIncProc(@AES_IncMSBFull, ctx1);
         {$else}
           err := AES_SetIncProc(AES_IncMSBFull, ctx1);
         {$endif}
       end;
    2: begin
         writeln('IncProc = AES_IncLSBFull,   max. offset = ',oma);
         {$ifdef USE_INT64}
           writeln(erroutput, 'IncProc = AES_IncLSBFull,   max. offset = ',oma);
         {$endif}
         {$ifdef FPC_ProcVar}
           err := AES_SetIncProc(@AES_IncLSBFull, ctx1);
         {$else}
           err := AES_SetIncProc(AES_IncLSBFull, ctx1);
         {$endif}
       end;

    3: begin
         writeln('IncProc = AES_IncMSBPart,   max. offset = ',oma);
         {$ifdef USE_INT64}
           writeln(erroutput, 'IncProc = AES_IncMSBPart,   max. offset = ',oma);
         {$endif}
         {$ifdef FPC_ProcVar}
           err := AES_SetIncProc(@AES_IncMSBPart, ctx1);
         {$else}
           err := AES_SetIncProc(AES_IncMSBPart, ctx1);
         {$endif}
       end;

    4: begin
         writeln('IncProc = AES_IncLSBPart,   max. offset = ',oma);
         {$ifdef USE_INT64}
           writeln(erroutput, 'IncProc = AES_IncLSBPart,   max. offset = ',oma);
         {$endif}
         {$ifdef FPC_ProcVar}
           err := AES_SetIncProc(@AES_IncLSBPart, ctx1);
         {$else}
           err := AES_SetIncProc(AES_IncLSBPart, ctx1);
         {$endif}
       end;
  end;

  CheckError;
  ofs := 0;
  ReStartTimer(HR);
  repeat
    for i:=1 to 99 do begin
      Err := AES_CTR_Encrypt(@pbuf, @cbuf1, BSIZE, ctx1);
      ofs := ofs + BSIZE;
    end;
    {$ifdef USE_INT64}
      write(erroutput, 100.0*ofs/oma:1:3,'%'#13);
    {$else}
      write(100.0*ofs/oma:1:3,'%'#13);
    {$endif}
    Err := AES_CTR_Encrypt(@pbuf, @cbuf1, BSIZE, ctx1);
    CheckError;
    i := random(BSIZE);
    Err := AES_CTR_Init(key128, 128, CTR, ctx2);
    CheckError;
    case n of
      1: begin
          (*
           {$ifdef FPC_ProcVar}
             err := AES_SetIncProc(@AES_IncMSBFull, ctx2);
           {$else}
             err := AES_SetIncProc(AES_IncMSBFull, ctx2);
           {$endif}
          *)
         end;
      2: begin
           {$ifdef FPC_ProcVar}
             err := AES_SetIncProc(@AES_IncLSBFull, ctx2);
           {$else}
             err := AES_SetIncProc(AES_IncLSBFull, ctx2);
           {$endif}
         end;

      3: begin
           {$ifdef FPC_ProcVar}
             err := AES_SetIncProc(@AES_IncMSBPart, ctx2);
           {$else}
             err := AES_SetIncProc(AES_IncMSBPart, ctx2);
           {$endif}
         end;

      4: begin
           {$ifdef FPC_ProcVar}
             err := AES_SetIncProc(@AES_IncLSBPart, ctx2);
           {$else}
             err := AES_SetIncProc(AES_IncLSBPart, ctx2);
           {$endif}
         end;
      else begin
             writeln('Invalid n');
             halt;
           end;
    end;
    CheckError;
    {$ifdef USE_INT64}
      Err := AES_CTR_Seek64(CTR, ofs+i, ctx2);
    {$else}
      Err := AES_CTR_Seek(CTR, ofs+i, 0, ctx2);
    {$endif}
    CheckError;
    Err := AES_CTR_Encrypt(@pbuf[i], @cbuf2[i], 1, ctx2);
    CheckError;
    if cbuf1[i]<>cbuf2[i] then begin
      writeln('Diff:  Offset=',ofs+i,'  cbuf1[]=',cbuf1[i]:3,'  cbuf2[]=',cbuf2[i]:3);
      halt;
    end;
    ofs := ofs + BSIZE;
  until ofs>oma;
  writeln('Done - no differences.');
  writeln('Time [s]: ', ReadSeconds(HR):1:3);
end;

var
  {$ifdef D12Plus}
    s: string;
  {$else}
    s: string[10];
  {$endif}

begin
  writeln('Test program "AES CTR Seek"    (C) 2010-2017  W.Ehrhardt');
  {$ifdef USEDLL}
    writeln('DLL Version: ',AES_DLL_Version);
  {$endif}
  writeln;
  writeln('Test using standard AES_IncMSBFull');
  randomtest(false);
  writeln;
  writeln('Test using user-defines My_IncMSBFull');
  randomtest(true);
  writeln;
  StartTimer(HR);
  s := paramstr(1);
  if s='big' then begin
    bigtest(1);
    bigtest(2);
    bigtest(3);
    bigtest(4);
  end;
end.
