{******************************************************************************}
{* DCPcrypt v2.0 written by David Barton (crypto@cityinthesky.co.uk) **********}
{******************************************************************************}
{* A binary compatible implementation of Ice and it's variants ****************}
{******************************************************************************}
{* Copyright (c) 1999-2002 David Barton                                       *}
{* Permission is hereby granted, free of charge, to any person obtaining a    *}
{* copy of this software and associated documentation files (the "Software"), *}
{* to deal in the Software without restriction, including without limitation  *}
{* the rights to use, copy, modify, merge, publish, distribute, sublicense,   *}
{* and/or sell copies of the Software, and to permit persons to whom the      *}
{* Software is furnished to do so, subject to the following conditions:       *}
{*                                                                            *}
{* The above copyright notice and this permission notice shall be included in *}
{* all copies or substantial portions of the Software.                        *}
{*                                                                            *}
{* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *}
{* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   *}
{* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    *}
{* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *}
{* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    *}
{* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        *}
{* DEALINGS IN THE SOFTWARE.                                                  *}
{******************************************************************************}
unit DCPice;

{$INCLUDE '..\jedi.inc'}

interface
uses
  Classes, Sysutils, DCPcrypt2, DCPconst, DCPblockciphers;

type
  TDCP_customice= class(TDCP_blockcipher64)
  protected
    rounds: dword;
    ik_keysched: array[0..31,0..2] of dword;
    function f(p, sk: dword): dword;
    procedure key_sched_build(kb: pwordarray; n: dword; keyrot: pdwordarray);
    procedure InitIce(const Key; Size: longword; n: dword);
  public
    procedure Burn; override;
    procedure EncryptECB(const InData; var OutData); override;
    procedure DecryptECB(const InData; var OutData); override;
    constructor Create(AOwner: TComponent); override;
  end;

  TDCP_ice= class(TDCP_customice)
  protected
    procedure InitKey(const Key; Size: longword); override;
  public
    class function GetId: integer; override;
    class function GetAlgorithm: string; override;
    class function GetMaxKeySize: integer; override;
    class function SelfTest: boolean; override;
  end;

  TDCP_thinice= class(TDCP_customice)
  protected
    procedure InitKey(const Key; Size: longword); override;
  public
    class function GetId: integer; override;
    class function GetAlgorithm: string; override;
    class function GetMaxKeySize: integer; override;
    class function SelfTest: boolean; override;
  end;

  TDCP_ice2= class(TDCP_customice)
  protected
    procedure InitKey(const Key; Size: longword); override;
  public
    class function GetId: integer; override;
    class function GetAlgorithm: string; override;
    class function GetMaxKeySize: integer; override;
    class function SelfTest: boolean; override;
  end;

{******************************************************************************}
{******************************************************************************}
implementation
{$R-}{$Q-}

{$IFDEF DELPHIXE2_UP}
  {$POINTERMATH ON}
{$ENDIF}

var
  ice_sbox: array[0..3,0..1023] of dword;
  ice_sboxdone: boolean;

const
  ice_smod: array[0..3,0..3] of dword= (
    (333, 313, 505, 369),
    (379, 375, 319, 391),
    (361, 445, 451, 397),
    (397, 425, 395, 505));
  ice_sxor: array[0..3,0..3] of dword= (
    ($83, $85, $9b, $cd),
    ($cc, $a7, $ad, $41),
    ($4b, $2e, $d4, $33),
    ($ea, $cb, $2e, $04));
  ice_keyrot: array[0..15] of dword= (
     0, 1, 2, 3, 2, 1, 3, 0,
     1, 3, 2, 0, 3, 1, 0, 2);
  ice_pbox: array[0..31] of dword= (
     $00000001,  $00000080,  $00000400,  $00002000,
     $00080000,  $00200000,  $01000000,  $40000000,
     $00000008,  $00000020,  $00000100,  $00004000,
     $00010000,  $00800000,  $04000000,  $20000000,
     $00000004,  $00000010,  $00000200,  $00008000,
     $00020000,  $00400000,  $08000000,  $10000000,
     $00000002,  $00000040,  $00000800,  $00001000,
     $00040000,  $00100000,  $02000000,  $80000000);

function SwapDword(a: dword): dword;
begin
  Result:= ((a and $FF) shl 24) or ((a and $FF00) shl 8) or ((a and $FF0000) shr 8) or ((a and $FF000000) shr 24);
end;

{******************************************************************************}
function gf_mult(a, b, m: dword): dword;
var
  res: dword;
begin
  res:= 0;
  while b<> 0 do
  begin
    if (b and 1)<> 0 then
      res:= res xor a;
    a:= a shl 1;
    b:= b shr 1;
    if a>= 256 then
      a:= a xor m;
  end;
  Result:= res;
end;

function gf_exp7(b, m: dword): dword;
var
  x: dword;
begin
  if b= 0 then
    Result:= 0
  else
  begin
    x:= gf_mult(b,b,m);
    x:= gf_mult(b,x,m);
    x:= gf_mult(x,x,m);
    Result:= gf_mult(b,x,m);
  end;
end;

function ice_perm32(x: dword): dword;
var
  res: dword;
  pbox: pdword;
begin
  res:= 0;
  pbox:= @ice_pbox;
  while x<> 0 do
  begin
    if (x and 1)<> 0 then
      res:= res or pbox^;
    Inc(pbox);
    x:= x shr 1;
  end;
  Result:= res;
end;

procedure ice_sboxes_init;
var
  i, col, row: dword;
  x: dword;
begin
  for i:= 0 to 1023 do
  begin
    col:= (i shr 1) and $FF;
    row:= (i and 1) or ((i and $200) shr 8);
    x:= gf_exp7(col xor ice_sxor[0,row],ice_smod[0,row]) shl 24;
    ice_sbox[0,i]:= ice_perm32(x);
    x:= gf_exp7(col xor ice_sxor[1,row],ice_smod[1,row]) shl 16;
    ice_sbox[1,i]:= ice_perm32(x);
    x:= gf_exp7(col xor ice_sxor[2,row],ice_smod[2,row]) shl  8;
    ice_sbox[2,i]:= ice_perm32(x);
    x:= gf_exp7(col xor ice_sxor[3,row],ice_smod[3,row]);
    ice_sbox[3,i]:= ice_perm32(x);
  end;
end;

function TDCP_customice.f(p, sk: dword): dword;
var
  tl, tr, al, ar: dword;
begin
  tl:= ((p shr 16) and $3ff) or (((p shr 14) or (p shl 18)) and $ffc00);
  tr:= (p and $3ff) or ((p shl 2) and $ffc00);
  al:= ik_keysched[sk,2] and (tl xor tr);
  ar:= al xor tr;
  al:= al xor tl;
  al:= al xor ik_keysched[sk,0];
  ar:= ar xor ik_keysched[sk,1];
  Result:= ice_sbox[0,al shr 10] or ice_sbox[1,al and $3ff] or
           ice_sbox[2,ar shr 10] or ice_sbox[3,ar and $3ff];
end;


procedure TDCP_customice.key_sched_build(kb: pwordarray; n: dword; keyrot: pdwordarray);
var
  i, j, k, kr: dword;
  keys: pdwordarray;
  currentsk: pdword;
  currentkb: pword;
  bit: dword;
begin
  for i:= 0 to 7 do
  begin
    kr:= keyrot^[i];
    keys:= @ik_keysched[n+i];
    for j:= 0 to 2 do
      keys^[j]:= 0;
    for j:= 0 to 14 do
    begin
      currentsk:= @keys^[j mod 3];
      for k:= 0 to 3 do
      begin
        currentkb:= @kb^[(kr + k) and 3];
        bit:= currentkb^ and 1;
        currentsk^:= (currentsk^ shl 1) or bit;
        currentkb^:= (currentkb^ shr 1) or ((bit xor 1) shl 15);
      end;
    end;
  end;
end;

procedure TDCP_customice.InitIce(const Key; Size: longword; n: dword);
var
  i, j: dword;
  kb: array[0..3] of word;
  keyb: array[0..15] of byte;
begin
  FillChar(keyb,Sizeof(keyb),0);
  Move(key,keyb,Size div 8);
  if n> 0 then
    rounds:= 16 * n
  else
    rounds:= 8;

  if rounds= 8 then
  begin
    for i:= 0 to 3 do // Adrien Reboisson - was "for i:= 0 to 4 do"
      kb[3 - i]:= (keyb[i*2] shl 8) or keyb[i*2 + 1];
    key_sched_build(@kb,0,@ice_keyrot);
  end
  else
  begin
    for i:= 0 to (n-1) do
    begin
      for j:= 0 to 3 do
        kb[3-j]:= (keyb[i*8 + j*2] shl 8) or keyb[i*8 + j*2 + 1];
      key_sched_build(@kb,i*8,@ice_keyrot);
      key_sched_build(@kb,rounds - 8 - i*8,@ice_keyrot[8]);
    end;
  end;
end;

procedure TDCP_customice.Burn;
begin
  FillChar(ik_keysched,Sizeof(ik_keysched),0);
  Rounds:= 0;
  inherited Burn;
end;

procedure TDCP_customice.EncryptECB(const InData; var OutData);
var
  i, l, r: dword;
begin
  if not fInitialized then
    raise EDCP_blockcipher.Create('Cipher not initialized');
  l:= SwapDWord(Pdword(@InData)^);
  r:= SwapDWord(Pdword(PointerToInt(@InData)+4)^);
  i:= 0;
  while i< rounds do
  begin
    l:= l xor f(r,i);
    r:= r xor f(l,i+1);
    Inc(i,2);
  end;
  Pdword(@OutData)^:= SwapDWord(r);
  Pdword(PointerToInt(@OutData)+4)^:= SwapDWord(l);
end;

procedure TDCP_customice.DecryptECB(const InData; var OutData);
var
  l, r: dword;
  i: integer;
begin
  if not fInitialized then
    raise EDCP_blockcipher.Create('Cipher not initialized');
  l:= SwapDWord(Pdword(@InData)^);
  r:= SwapDWord(Pdword(PointerToInt(@InData)+4)^);
  i:= rounds-1;
  while i> 0 do
  begin
    l:= l xor f(r,i);
    r:= r xor f(l,i-1);
    Dec(i,2);
  end;
  Pdword(@OutData)^:= SwapDWord(r);
  Pdword(PointerToInt(@OutData)+4)^:= SwapDWord(l);
end;

constructor TDCP_customice.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  if not ice_sboxdone then
  begin
    ice_sboxes_init;
    ice_sboxdone:= true;
  end;
end;

{******************************************************************************}
class function TDCP_ice.GetMaxKeySize: integer;
begin
  Result:= 64;
end;

class function TDCP_ice.GetID: integer;
begin
  Result:= DCP_ice;
end;

class function TDCP_ice.GetAlgorithm: string;
begin
  Result:= 'Ice';
end;

class function TDCP_ice.SelfTest: boolean;
const
  Key1: array[0..7] of byte= ($de,$ad,$be,$ef,$01,$23,$45,$67);
  InData1: array[0..7] of byte= ($fe,$dc,$ba,$98,$76,$54,$32,$10);
  OutData1: array[0..7] of byte= ($7d,$6e,$f1,$ef,$30,$d4,$7a,$96);
var
  Cipher: TDCP_ice;
  Data: array[0..7] of byte;
begin
  FillChar(Data, SizeOf(Data), 0);
  Cipher:= TDCP_ice.Create(nil);
  Cipher.Init(Key1,Sizeof(Key1)*8,nil);
  Cipher.EncryptECB(InData1,Data);
  Result:= boolean(CompareMem(@Data,@OutData1,Sizeof(Data)));
  Cipher.Reset;
  Cipher.DecryptECB(Data,Data);
  Result:= boolean(CompareMem(@Data,@InData1,Sizeof(Data))) and Result;
  Cipher.Burn;
  Cipher.Free;
end;

procedure TDCP_ice.InitKey(const Key; Size: longword);
begin
  InitIce(Key,Size,1);
end;

{******************************************************************************}
class function TDCP_thinice.GetMaxKeySize: integer;
begin
  Result:= 64;
end;

class function TDCP_thinice.GetID: integer;
begin
  Result:= DCP_thinice;
end;

class function TDCP_thinice.GetAlgorithm: string;
begin
  Result:= 'Thin Ice';
end;

class function TDCP_thinice.SelfTest: boolean;
const
  Key1: array[0..7] of byte= ($de,$ad,$be,$ef,$01,$23,$45,$67);
  InData1: array[0..7] of byte= ($fe,$dc,$ba,$98,$76,$54,$32,$10);
  OutData1: array[0..7] of byte= ($de,$24,$0d,$83,$a0,$0a,$9c,$c0);
var
  Cipher: TDCP_thinice;
  Data: array[0..7] of byte;
begin
  FillChar(Data, SizeOf(Data), 0);
  Cipher:= TDCP_thinice.Create(nil);
  Cipher.Init(Key1,Sizeof(Key1)*8,nil);
  Cipher.EncryptECB(InData1,Data);
  Result:= boolean(CompareMem(@Data,@OutData1,Sizeof(Data)));
  Cipher.Reset;
  Cipher.DecryptECB(Data,Data);
  Result:= boolean(CompareMem(@Data,@InData1,Sizeof(Data))) and Result;
  Cipher.Burn;
  Cipher.Free;
end;

procedure TDCP_thinice.InitKey(const Key; Size: longword);
begin
  InitIce(Key,Size,0);
end;

{******************************************************************************}
class function TDCP_ice2.GetMaxKeySize: integer;
begin
  Result:= 128;
end;

class function TDCP_ice2.GetID: integer;
begin
  Result:= DCP_ice2;
end;

class function TDCP_ice2.GetAlgorithm: string;
begin
  Result:= 'Ice2';
end;

class function TDCP_ice2.SelfTest: boolean;
const
  Key1: array[0..15] of byte=
    ($00,$11,$22,$33,$44,$55,$66,$77,$88,$99,$aa,$bb,$cc,$dd,$ee,$ff);
  InData1: array[0..7] of byte= ($fe,$dc,$ba,$98,$76,$54,$32,$10);
  OutData1: array[0..7] of byte= ($f9,$48,$40,$d8,$69,$72,$f2,$1c);
var
  Cipher: TDCP_ice2;
  Data: array[0..7] of byte;
begin
  FillChar(Data, SizeOf(Data), 0);
  Cipher:= TDCP_ice2.Create(nil);
  Cipher.Init(Key1,Sizeof(Key1)*8,nil);
  Cipher.EncryptECB(InData1,Data);
  Result:= boolean(CompareMem(@Data,@OutData1,Sizeof(Data)));
  Cipher.Reset;
  Cipher.DecryptECB(Data,Data);
  Result:= boolean(CompareMem(@Data,@InData1,Sizeof(Data))) and Result;
  Cipher.Burn;
  Cipher.Free;
end;

procedure TDCP_ice2.InitKey(const Key; Size: longword);
begin
  InitIce(Key,Size,2);
end;


initialization
  ice_sboxdone:= false;

end.
