{Copyright:      Hagen Reddmann  mailto:HaReddmann@AOL.COM
 Author:         Hagen Reddmann
 Remarks:        freeware, but this Copyright must be included
 known Problems: none
 Version:        3.0,  Part I from Delphi Encryption Compendium
                 Delphi 2-4
 Description:    Include a Selection of various Cipher's (Encryption Algo)
                 impl. Algo:
                   Blowfish, Rijndael, Single DES, Triple DES, Cast128,
                   RC2, RC4

 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}

{$IFNDEF CLR}

{$I Dac.inc}

unit CRCipher;
{$ENDIF}

interface
{$A+,Q-,R-,Y-}
uses
{$IFNDEF CLR}
  CLRClasses,
{$ENDIF}
  SysUtils, CRTypes, CRDECUtil, CRSymmetricAlgorithm;

type
  TCipher_Blowfish = class;
  TCipher_Rijndael = class;
  TCipher_1DES     = class;  {Single DES  8 byte Blocksize,  8 byte Keysize  56 bits relevant}
  TCipher_3DES     = class;  {Triple DES  8 byte Blocksize, 24 byte Keysize 168 bits relevant}
  TCipher_Cast128  = class;
  TCipher_RC4 = class;  {Streamcipher}

  TCipher_Blowfish = class(TSymmetricAlgorithm)
  protected
    class procedure GetContext(var BufSize, UserSize: Integer; KeySizes: TKeySizes); override;
    procedure Encode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0); override;
    procedure Decode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0); override;
    procedure Init(const Key: TBytes; const IVector: TIntArray); override;
  end;

  TCipher_Rijndael = class(TSymmetricAlgorithm)
  private
    FRounds: Integer;
  protected
    class procedure GetContext(var BufSize, UserSize: Integer; KeySizes: TKeySizes); override;
    procedure Encode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0); override;
    procedure Decode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0); override;
    procedure Init(const Key: TBytes; const IVector: TIntArray); override;
  end;

  TCipher_1DES = class(TSymmetricAlgorithm)
  protected
    class procedure GetContext(var BufSize, UserSize: Integer; KeySizes: TKeySizes); override;
    procedure Encode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0); override;
    procedure Decode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0); override;
    procedure MakeKey(const Data: array of Byte; const DataOffset: Integer; {$IFDEF CLR}const{$ENDIF} Key: TIntArray; const KeyOffset: Integer; Reverse: Boolean);
    procedure Init(const Key: TBytes; const IVector: TIntArray); override;
    {$IFNDEF CLR}class{$ENDIF} procedure DES_Func({$IFDEF CLR}const{$ENDIF} Data: TIntArray; DataOffset: Integer; {$IFDEF CLR}const{$ENDIF} Key: TIntArray; KeyOffset: Integer);
  end;

  TCipher_3DES = class(TCipher_1DES)
  protected
    class procedure GetContext(var BufSize, UserSize: Integer; KeySizes: TKeySizes); override;
    procedure Encode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0); override;
    procedure Decode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0); override;
    procedure Init(const Key: TBytes; const IVector: TIntArray); override;
  end;

  TCipher_Cast128 = class(TSymmetricAlgorithm) {Carlisle Adams and Stafford Tavares }
  private
    FRounds: Byte;
  protected
    class procedure GetContext(var BufSize, UserSize: Integer; KeySizes: TKeySizes); override;
    procedure Encode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0); override;
    procedure Decode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0); override;
    procedure Init(const Key: TBytes; const IVector: TIntArray); override;
  end;

  TCipher_RC4 = class(TSymmetricAlgorithm)
  private
    FI: Byte;
    FJ: Byte;
    FSI: Byte;
    FSJ: Byte;
  protected
    class procedure GetContext(var BufSize, UserSize: Integer; KeySizes: TKeySizes); override;
    procedure Encode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0); override;
    procedure Decode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0); override;
    procedure Init(const Key: TBytes; const IVector: TIntArray); override;
    procedure Done; override;
  end;

implementation

{$IFNDEF VER6P}
uses
  Windows;
{$ENDIF}

{$I cipher.inc}

{$IFDEF CLR}
var
  SZ_Blowfish_Data: Integer;
  SZ_Blowfish_Key: Integer;
{$ELSE}
const
  FBeginPos = 0;
  FBeginPosData = 0;
  SZ_Blowfish_Data = SizeOf(Blowfish_Data) div SizeOf(Integer);
  SZ_Blowfish_Key = SizeOf(Blowfish_Key) div SizeOf(Integer);
{$ENDIF}

class procedure TCipher_Blowfish.GetContext(var BufSize, UserSize: Integer; KeySizes: TKeySizes);
begin
  BufSize := 8;
  KeySizes.MinSize := 32;
  KeySizes.MaxSize := 448;
  KeySizes.SkipSize := 8;
  UserSize := (SZ_Blowfish_Data + SZ_Blowfish_Key) * SizeOf(Integer);
end;

procedure TCipher_Blowfish.Encode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0);
{$IFDEF UseASM}  // specialy for CPU >= 486
asm
        PUSH   EDI
        PUSH   ESI
        PUSH   EBX
        PUSH   EBP
        SHL    ECX,2
        ADD    EDX,ECX
        PUSH   EDX

        MOV    ESI,[EAX].TCipher_Blowfish.FUser
        MOV    EBX,[EDX]         // A
        MOV    EBP,[EDX + 4]     // B

        BSWAP  EBX               // CPU >= 486
        BSWAP  EBP

        XOR    EDI,EDI
        XOR    EBX,[ESI + 4 * 256 * 4]
//      XOR    ECX,ECX
@@1:

        MOV    EAX,EBX
        SHR    EBX,16
        MOVZX  ECX,BH     // it's faster with AMD Chips,
//        MOV    CL,BH    // it's faster with PII's
        MOV    EDX,[ESI + ECX * 4 + 1024 * 0]
        MOVZX  ECX,BL
//        MOV    CL,BL
        ADD    EDX,[ESI + ECX * 4 + 1024 * 1]

        MOVZX  ECX,AH
//        MOV    CL,AH
        XOR    EDX,[ESI + ECX * 4 + 1024 * 2]
        MOVZX  ECX,AL
//        MOV    CL,AL
        ADD    EDX,[ESI + ECX * 4 + 1024 * 3]
        XOR    EBP,[ESI + 4 * 256 * 4 + 4 + EDI * 4]

        INC    EDI
        XOR    EDX,EBP
        TEST   EDI,010h
        MOV    EBP,EAX
        MOV    EBX,EDX
        JZ     @@1

        POP    EAX
        XOR    EBP,[ESI + 4 * 256 * 4 + 17 * 4]

        BSWAP  EBX
        BSWAP  EBP

        MOV    [EAX],EBP
        MOV    [EAX + 4],EBX

        POP    EBP
        POP    EBX
        POP    ESI
        POP    EDI
end;
{$ELSE}
var
  I,A,B: LongWord;
  P: TIntArray;
begin
  P := User;
{$IFDEF CLR}
  FBeginPos := 0;
  FBeginPosData := 0;
{$ENDIF}
  Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}, SZ_Blowfish_Data);
  Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF}, Offset);

  A := SwapInteger(Data[FBeginPosData]) xor P[FBeginPos]; Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF});
  B := SwapInteger(Data[FBeginPosData + 1]);
  for I := 0 to 7 do
  begin
    B := B xor P[FBeginPos] xor
              (User[          A shr 24        ] +
               User[1 * 256 + A shr 16 and $FF] xor
               User[2 * 256 + A shr  8 and $FF] +
               User[3 * 256 + A        and $FF]);

    A := A xor P[FBeginPos + 1] xor
              (User[          B shr 24        ] +
               User[1 * 256 + B shr 16 and $FF] xor
               User[2 * 256 + B shr  8 and $FF] +
               User[3 * 256 + B        and $FF]);
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}, 2);
  end;
  Data[FBeginPosData] := SwapInteger(B xor P[FBeginPos]);
  Data[FBeginPosData + 1] := SwapInteger(A);
end;
{$ENDIF}

procedure TCipher_Blowfish.Decode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0);
{$IFDEF UseASM}
asm
        PUSH   EDI
        PUSH   ESI
        PUSH   EBX
        PUSH   EBP
        SHL    ECX,2
        ADD    EDX,ECX
        PUSH   EDX

        MOV    ESI,[EAX].TCipher_Blowfish.FUser
        MOV    EBX,[EDX]         // A
        MOV    EBP,[EDX + 4]     // B

        BSWAP  EBX
        BSWAP  EBP

        XOR    EBX,[ESI + 4 * 256 * 4 + 17 * 4]
        MOV    EDI,16
//        XOR    ECX,ECX

@@1:    MOV    EAX,EBX
        SHR    EBX,16

        MOVZX  ECX,BH
//        MOV    CL,BH
        MOV    EDX,[ESI + ECX * 4 + 1024 * 0]
        MOVZX  ECX,BL
//        MOV    CL,BL
        ADD    EDX,[ESI + ECX * 4 + 1024 * 1]

        MOVZX  ECX,AH
//        MOV    CL,AH
        XOR    EDX,[ESI + ECX * 4 + 1024 * 2]
        MOVZX  ECX,AL
//        MOV    CL,AL
        ADD    EDX,[ESI + ECX * 4 + 1024 * 3]
        XOR    EBP,[ESI + 4 * 256 * 4 + EDI * 4]

        XOR    EDX,EBP
        DEC    EDI
        MOV    EBP,EAX
        MOV    EBX,EDX
        JNZ    @@1

        POP    EAX
        XOR    EBP,[ESI + 4 * 256 * 4]

        BSWAP  EBX
        BSWAP  EBP

        MOV    [EAX],EBP
        MOV    [EAX + 4],EBX

        POP    EBP
        POP    EBX
        POP    ESI
        POP    EDI
end;
{$ELSE}
var
  I,A,B: LongWord;
  P: TIntArray;
begin
  P := User;
{$IFDEF CLR}
  FBeginPos := 0;
  FBeginPosData := 0;
{$ENDIF}
  Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}, SZ_Blowfish_Data + SZ_Blowfish_Key - 1);
  Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF}, Offset);

  A := SwapInteger(Data[FBeginPosData]) xor P[FBeginPos];
  B := SwapInteger(Data[FBeginPosData + 1]);
  for I := 0 to 7 do
  begin
    Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}, 2);
    B := B xor P[FBeginPos + 1] xor
              (User[          A shr 24        ] +
               User[1 * 256 + A shr 16 and $FF] xor
               User[2 * 256 + A shr  8 and $FF] +
               User[3 * 256 + A        and $FF]);
    A := A xor P[FBeginPos] xor
              (User[          B shr 24        ] +
               User[1 * 256 + B shr 16 and $FF] xor
               User[2 * 256 + B shr  8 and $FF] +
               User[3 * 256 + B        and $FF]);
  end;
  Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF});
  Data[FBeginPosData] := SwapInteger(B xor P[FBeginPos]);
  Data[FBeginPosData + 1] := SwapInteger(A);
end;
{$ENDIF}

procedure TCipher_Blowfish.Init(const Key: TBytes; const IVector: TIntArray);
var
  I, J: Integer;
  B: array[0..1] of Longword;
  KeyLength: Integer;
begin
  KeyLength := Length(Key);
  InitBegin(KeyLength);
  Buffer.BlockCopy({$IFNDEF CLR}TIntArray(@Blowfish_Data){$ELSE}Blowfish_Data{$ENDIF}, 0, User, 0, SZ_Blowfish_Data * SizeOf(Integer));
  Buffer.BlockCopy(Blowfish_Key, 0, User, SZ_Blowfish_Data * SizeOf(Integer), SZ_Blowfish_Key * SizeOf(Integer));
  J := 0;
  for I := 0 to 17 do
  begin
    User[SZ_Blowfish_Data + I] := User[SZ_Blowfish_Data + I] xor
        (Key[(J + 0) mod KeyLength] shl 24 +
         Key[(J + 1) mod KeyLength] shl 16 +
         Key[(J + 2) mod KeyLength] shl  8 +
         Key[(J + 3) mod KeyLength]);
    J := (J + 4) mod KeyLength;
  end;

{$IFNDEF CLR}
  FillChar(B, SizeOf(B), 0);
{$ELSE}
  &Array.Clear(B, 0, Length(B));
{$ENDIF}

  for I := 0 to 8 do
  begin
    Encode({$IFNDEF CLR}@{$ENDIF}B, 0);
    User[SZ_Blowfish_Data + I * 2]     := SwapInteger(B[0]);
    User[SZ_Blowfish_Data + I * 2 + 1] := SwapInteger(B[1]);
  end;
  for I := 0 to 3 do
    for J := 0 to 127 do
    begin
      Encode({$IFNDEF CLR}@{$ENDIF}B, 0);
      User[I * 256 + J * 2]    := SwapInteger(B[0]);
      User[I * 256 + J * 2 +1] := SwapInteger(B[1]);
    end;

{$IFNDEF CLR}
  FillChar(B, SizeOf(B), 0);
{$ELSE}
  &Array.Clear(B, 0, Length(B));
{$ENDIF}
  InitEnd(IVector);
end;

const
{don't change this}
  Rijndael_Blocks =  4;
  Rijndael_Rounds = 14;

class procedure TCipher_Rijndael.GetContext(var BufSize, UserSize: Integer; KeySizes: TKeySizes);
begin
  BufSize := Rijndael_Blocks * 4;
  KeySizes.MinSize := 128;
  KeySizes.MaxSize := 256;
  KeySizes.SkipSize := 64;
  UserSize := (Rijndael_Rounds + 1) * Rijndael_Blocks * SizeOf(Integer) * 2;
end;

procedure TCipher_Rijndael.Encode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0);
var
  P: TIntArray;
  I,A,B,C,D: Longword;
begin
  P := User;
{$IFDEF CLR}
  FBeginPos := 0;
  FBeginPosData := 0;
{$ENDIF}
  Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF}, Offset);

  for I := 2 to FRounds do
  begin
    A := Data[FBeginPosData] xor P[FBeginPos];
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}); Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
    B := Data[FBeginPosData] xor P[FBeginPos];
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}); Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
    C := Data[FBeginPosData] xor P[FBeginPos];
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}); Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
    D := Data[FBeginPosData] xor P[FBeginPos];
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF});

    Data[FBeginPosData] := Rijndael_T[0, D and $FF]        xor
                      Rijndael_T[1, A shr  8 and $FF] xor
                      Rijndael_T[2, B shr 16 and $FF] xor
                      Rijndael_T[3, C shr 24];
    Dec({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
    Data[FBeginPosData] := Rijndael_T[0, C and $FF]        xor
                      Rijndael_T[1, D shr  8 and $FF] xor
                      Rijndael_T[2, A shr 16 and $FF] xor
                      Rijndael_T[3, B shr 24];
    Dec({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
    Data[FBeginPosData] := Rijndael_T[0, B and $FF]        xor
                      Rijndael_T[1, C shr  8 and $FF] xor
                      Rijndael_T[2, D shr 16 and $FF] xor
                      Rijndael_T[3, A shr 24];
    Dec({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
    Data[FBeginPosData] := Rijndael_T[0, A and $FF]        xor
                      Rijndael_T[1, B shr  8 and $FF] xor
                      Rijndael_T[2, C shr 16 and $FF] xor
                      Rijndael_T[3, D shr 24];
  end;

  A := Data[FBeginPosData] xor P[FBeginPos];
  Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}); Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
  B := Data[FBeginPosData] xor P[FBeginPos];
  Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}); Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
  C := Data[FBeginPosData] xor P[FBeginPos];
  Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}); Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
  D := Data[FBeginPosData] xor P[FBeginPos];
  Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF});

  Data[FBeginPosData] := Rijndael_S[0, D and $FF]               or
                    Rijndael_S[0, A shr  8 and $FF] shl  8 or
                    Rijndael_S[0, B shr 16 and $FF] shl 16 or
                    Rijndael_S[0, C shr 24]         shl 24;
  Dec({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
  Data[FBeginPosData] := Rijndael_S[0, C and $FF]               or
                    Rijndael_S[0, D shr  8 and $FF] shl  8 or
                    Rijndael_S[0, A shr 16 and $FF] shl 16 or
                    Rijndael_S[0, B shr 24]         shl 24;
  Dec({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
  Data[FBeginPosData] := Rijndael_S[0, B and $FF]               or
                    Rijndael_S[0, C shr  8 and $FF] shl  8 or
                    Rijndael_S[0, D shr 16 and $FF] shl 16 or
                    Rijndael_S[0, A shr 24]         shl 24;
  Dec({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
  Data[FBeginPosData] := Rijndael_S[0, A and $FF]               or
                    Rijndael_S[0, B shr  8 and $FF] shl  8 or
                    Rijndael_S[0, C shr 16 and $FF] shl 16 or
                    Rijndael_S[0, D shr 24]         shl 24;

  for I := 1 to Rijndael_Blocks do
  begin
    Data[FBeginPosData] := Data[FBeginPosData] xor P[FBeginPos];
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF});
    Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
  end;
end;

procedure TCipher_Rijndael.Decode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0);
var
  P: TIntArray;
  I,A,B,C,D: Longword;
begin
  P := User;
{$IFDEF CLR}
  FBeginPos := 0;
  FBeginPosData := 0;
{$ENDIF}
  Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}, (UserSize shr 1) div SizeOf(Integer) + FRounds * 4 + 3);
  Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF}, Offset + 3); 

  for I := 2 to FRounds do
  begin
    D := Data[FBeginPosData] xor P[FBeginPos];
    Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}); Dec({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
    C := Data[FBeginPosData] xor P[FBeginPos];
    Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}); Dec({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
    B := Data[FBeginPosData] xor P[FBeginPos];
    Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}); Dec({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
    A := Data[FBeginPosData] xor P[FBeginPos];
    Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF});

    Data[FBeginPosData] := Rijndael_T[4, A and $FF]        xor
                      Rijndael_T[5, D shr  8 and $FF] xor
                      Rijndael_T[6, C shr 16 and $FF] xor
                      Rijndael_T[7, B shr 24];
    Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
    Data[FBeginPosData] := Rijndael_T[4, B and $FF]        xor
                      Rijndael_T[5, A shr  8 and $FF] xor
                      Rijndael_T[6, D shr 16 and $FF] xor
                      Rijndael_T[7, C shr 24];
    Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
    Data[FBeginPosData] := Rijndael_T[4, C and $FF]        xor
                      Rijndael_T[5, B shr  8 and $FF] xor
                      Rijndael_T[6, A shr 16 and $FF] xor
                      Rijndael_T[7, D shr 24];
    Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
    Data[FBeginPosData] := Rijndael_T[4, D and $FF]        xor
                      Rijndael_T[5, C shr  8 and $FF] xor
                      Rijndael_T[6, B shr 16 and $FF] xor
                      Rijndael_T[7, A shr 24];
  end;

  D := Data[FBeginPosData] xor P[FBeginPos];
  Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}); Dec({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
  C := Data[FBeginPosData] xor P[FBeginPos];
  Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}); Dec({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
  B := Data[FBeginPosData] xor P[FBeginPos];
  Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF}); Dec({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
  A := Data[FBeginPosData] xor P[FBeginPos];
  Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF});

  Data[FBeginPosData] := Rijndael_S[1, A and $FF]               or
                    Rijndael_S[1, D shr  8 and $FF] shl  8 or
                    Rijndael_S[1, C shr 16 and $FF] shl 16 or
                    Rijndael_S[1, B shr 24]         shl 24;
  Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
  Data[FBeginPosData] := Rijndael_S[1, B and $FF]               or
                    Rijndael_S[1, A shr  8 and $FF] shl  8 or
                    Rijndael_S[1, D shr 16 and $FF] shl 16 or
                    Rijndael_S[1, C shr 24]         shl 24;
  Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
  Data[FBeginPosData] := Rijndael_S[1, C and $FF]               or
                    Rijndael_S[1, B shr  8 and $FF] shl  8 or
                    Rijndael_S[1, A shr 16 and $FF] shl 16 or
                    Rijndael_S[1, D shr 24]         shl 24;
  Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
  Data[FBeginPosData] := Rijndael_S[1, D and $FF]               or
                    Rijndael_S[1, C shr  8 and $FF] shl  8 or
                    Rijndael_S[1, B shr 16 and $FF] shl 16 or
                    Rijndael_S[1, A shr 24]         shl 24;

  for I := 0 to 3 do
  begin
    Data[FBeginPosData] := Data[FBeginPosData] xor P[FBeginPos];
    Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(P){$ENDIF});
    Dec({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF});
  end;
end;

procedure TCipher_Rijndael.Init(const Key: TBytes; const IVector: TIntArray);
var
  K: array[0..7] of Longword;

  procedure BuildEncodeKey;
  const
    RND_Data: array[0..29] of Byte = (
      $01,$02,$04,$08,$10,$20,$40,$80,$1B,$36,$6C,$D8,$AB,$4D,$9A,
      $2F,$5E,$BC,$63,$C6,$97,$35,$6A,$D4,$B3,$7D,$FA,$EF,$C5,$91);
  var
    T,R: Integer;

    procedure NextRounds;
    var
      J: Integer;
    begin
      J := 0;
      while (J < FRounds-6) and (R <= FRounds) do
      begin
        while (J < FRounds-6) and (T < Rijndael_Blocks) do
        begin
          User[R * Rijndael_Blocks + T] := K[J];
          Inc(J);
          Inc(T);
        end;
        if T = Rijndael_Blocks then
        begin
          T := 0;
          Inc(R);
        end;
      end;
    end;

  var
    RNDPos: Integer;
  {$IFNDEF CLR}
    B: PByte;
  {$ENDIF}
    I: Integer;
  begin
    R := 0;
    T := 0;
    RNDPos := 0;
    NextRounds;
    while R <= FRounds do
    begin
    {$IFNDEF CLR}
      B  := @K;
      B^ := B^ xor Rijndael_S[0, K[FRounds -7] shr  8 and $FF] xor RND_Data[RNDPos]; Inc(B);
      B^ := B^ xor Rijndael_S[0, K[FRounds -7] shr 16 and $FF];                 Inc(B);
      B^ := B^ xor Rijndael_S[0, K[FRounds -7] shr 24];                         Inc(B);
      B^ := B^ xor Rijndael_S[0, K[FRounds -7] and $FF];
    {$ELSE}
      FBeginPos := 0;
      Buffer.SetByte(K, FBeginPos, Buffer.GetByte(K, FBeginPos) xor Rijndael_S[0, K[FRounds -7] shr  8 and $FF] xor RND_Data[RNDPos]);
      Inc(FBeginPos);
      Buffer.SetByte(K, FBeginPos, Buffer.GetByte(K, FBeginPos) xor Rijndael_S[0, K[FRounds -7] shr 16 and $FF]);
      Inc(FBeginPos);
      Buffer.SetByte(K, FBeginPos, Buffer.GetByte(K, FBeginPos) xor Rijndael_S[0, K[FRounds -7] shr 24]);
      Inc(FBeginPos);
      Buffer.SetByte(K, FBeginPos, Buffer.GetByte(K, FBeginPos) xor Rijndael_S[0, K[FRounds -7] and $FF]);
    {$ENDIF}

      Inc(RNDPos);
      if FRounds = 14 then
      begin
        for I := 1 to 3 do K[I] := K[I] xor K[I -1];

      {$IFNDEF CLR}
        B := @K[4];
        B^ := B^ xor Rijndael_S[0, K[3] and $FF];         Inc(B);
        B^ := B^ xor Rijndael_S[0, K[3] shr  8 and $FF];  Inc(B);
        B^ := B^ xor Rijndael_S[0, K[3] shr 16 and $FF];  Inc(B);
        B^ := B^ xor Rijndael_S[0, K[3] shr 24];
      {$ELSE}
        FBeginPos := 4 * SizeOf(Integer);
        Buffer.SetByte(K, FBeginPos, Buffer.GetByte(K, FBeginPos) xor Rijndael_S[0, K[3] and $FF]);
        Inc(FBeginPos);
        Buffer.SetByte(K, FBeginPos, Buffer.GetByte(K, FBeginPos) xor Rijndael_S[0, K[3] shr  8 and $FF]);
        Inc(FBeginPos);
        Buffer.SetByte(K, FBeginPos, Buffer.GetByte(K, FBeginPos) xor Rijndael_S[0, K[3] shr 16 and $FF]);
        Inc(FBeginPos);
        Buffer.SetByte(K, FBeginPos, Buffer.GetByte(K, FBeginPos) xor Rijndael_S[0, K[3] shr 24]);
      {$ENDIF}

        for I := 5 to 7 do K[I] := K[I] xor K[I -1];
      end else
        for I := 1 to FRounds -7 do K[I] := K[I] xor K[I -1];
      NextRounds;
    end;
  end;

  procedure BuildDecodeKey;
  var
    I: Integer;
    D: TIntArray;
    USize: Integer;
  begin
    D := User;
  {$IFDEF CLR}
    FBeginPos := 0;
  {$ENDIF}
    USize := UserSize shr 1;
    Buffer.BlockCopy(User, 0, D, USize, USize);
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(D){$ENDIF}, USize div SizeOf(Integer) + 4);
    for I := 0 to FRounds * 4 - 5 do
    begin
      D[FBeginPos] := Rijndael_Key[D[FBeginPos] and $FF] xor
                 ROL(Rijndael_Key[D[FBeginPos] shr  8 and $FF],  8) xor
                 ROL(Rijndael_Key[D[FBeginPos] shr 16 and $FF], 16) xor
                 ROL(Rijndael_Key[D[FBeginPos] shr 24],         24);
      Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(D){$ENDIF});
    end;
  end;

begin
  InitBegin(Length(Key));
  if Length(Key) <= 16 then FRounds := 10 else
    if Length(Key) <= 24 then FRounds := 12 else FRounds := 14;

{$IFNDEF CLR}
  FillChar(K, SizeOf(K), 0);
{$ELSE}
  &Array.Clear(K, 0, Length(K));
{$ENDIF}

  Assert(Length(Key) <= 32);
  Buffer.BlockCopy(Key, 0, K, 0, Length(Key));
  BuildEncodeKey;
  BuildDecodeKey;

{$IFNDEF CLR}
  FillChar(K, SizeOf(K), 0);
{$ELSE}
  &Array.Clear(K, 0, Length(K));
{$ENDIF}
  InitEnd(IVector);
end;

{DES}
{$IFNDEF CLR}class{$ENDIF} procedure TCipher_1DES.DES_Func({$IFDEF CLR}const{$ENDIF} Data: TIntArray; DataOffset: Integer; {$IFDEF CLR}const{$ENDIF} Key: TIntArray; KeyOffset: Integer);
var
  L,R,X,Y,I: LongWord;
begin
{$IFDEF CLR}
  FBeginPos := 0;
  FBeginPosData := 0;
{$ENDIF}
  Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF}, DataOffset);

  L := SwapInteger(Data[FBeginPosData]);
  R := SwapInteger(Data[FBeginPosData + 1]);

  X := (L shr  4 xor R) and $0F0F0F0F; R := R xor X; L := L xor X shl  4;
  X := (L shr 16 xor R) and $0000FFFF; R := R xor X; L := L xor X shl 16;
  X := (R shr  2 xor L) and $33333333; L := L xor X; R := R xor X shl  2;
  X := (R shr  8 xor L) and $00FF00FF; L := L xor X; R := R xor X shl  8;

  R := R shl 1 or R shr 31;
  X := (L xor R) and $AAAAAAAA;
  R := R xor X;
  L := L xor X;
  L := L shl 1 or L shr 31;

  Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(Key){$ENDIF}, KeyOffset);
  for I := 0 to 7 do
  begin
    X := (R shl 28 or R shr 4) xor Key[FBeginPos]; Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(Key){$ENDIF});
    Y := R xor Key[FBeginPos];                     Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(Key){$ENDIF});
    L := L xor (DES_Data[0, X        and $3F] or DES_Data[1, X shr  8 and $3F] or
                DES_Data[2, X shr 16 and $3F] or DES_Data[3, X shr 24 and $3F] or
                DES_Data[4, Y        and $3F] or DES_Data[5, Y shr  8 and $3F] or
                DES_Data[6, Y shr 16 and $3F] or DES_Data[7, Y shr 24 and $3F]);

    X := (L shl 28 or L shr 4) xor Key[FBeginPos]; Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(Key){$ENDIF});
    Y := L xor Key[FBeginPos];                     Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(Key){$ENDIF});
    R := R xor (DES_Data[0, X        and $3F] or DES_Data[1, X shr  8 and $3F] or
                DES_Data[2, X shr 16 and $3F] or DES_Data[3, X shr 24 and $3F] or
                DES_Data[4, Y        and $3F] or DES_Data[5, Y shr  8 and $3F] or
                DES_Data[6, Y shr 16 and $3F] or DES_Data[7, Y shr 24 and $3F]);
  end;

  R := R shl 31 or R shr 1;
  X := (L xor R) and $AAAAAAAA;
  R := R xor X;
  L := L xor X;
  L := L shl 31 or L shr 1;

  X := (L shr  8 xor R) and $00FF00FF; R := R xor X; L := L xor X shl  8;
  X := (L shr  2 xor R) and $33333333; R := R xor X; L := L xor X shl  2;
  X := (R shr 16 xor L) and $0000FFFF; L := L xor X; R := R xor X shl 16;
  X := (R shr  4 xor L) and $0F0F0F0F; L := L xor X; R := R xor X shl  4;

  Data[FBeginPosData] := SwapInteger(R);
  Data[FBeginPosData + 1] := SwapInteger(L);
end;

class procedure TCipher_1DES.GetContext(var BufSize, UserSize: Integer; KeySizes: TKeySizes);
begin
  BufSize := 8;
  KeySizes.MinSize := 64;
  KeySizes.MaxSize := 64;
  KeySizes.SkipSize := 0;
  UserSize := 32 * 4 * 2;
end;

procedure TCipher_1DES.Encode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0);
begin
  DES_Func(Data, Offset, User, 0);
end;

procedure TCipher_1DES.Decode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0);
begin
  DES_Func(Data, Offset, User, 32);
end;

procedure TCipher_1DES.MakeKey(const Data: array of Byte; const DataOffset: Integer; {$IFDEF CLR}const{$ENDIF} Key: TIntArray; const KeyOffset: Integer; Reverse: Boolean);
const
  ROT: array[0..15] of Byte = (1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28);
var
  I,J,L,M,N: LongWord;
  PC_M, PC_R: array[0..55] of Byte;
  K: array[0..31] of LongWord;
begin
{$IFNDEF CLR}
  FillChar(K, SizeOf(K), 0);
{$ELSE}
  &Array.Clear(K, 0, Length(K));
{$ENDIF}

  for I := 0 to 55 do
    if Data[DataOffset + Integer(DES_PC1[I] shr 3)] and ($80 shr (DES_PC1[I] and $07)) <> 0 then PC_M[I] := 1
      else PC_M[I] := 0;
  for I := 0 to 15 do
  begin
    if Reverse then M := (15 - I) shl 1 else M := I shl 1;
    N := M + 1;
    for J := 0 to 27 do
    begin
      L := J + ROT[I];
      if L < 28 then PC_R[J] := PC_M[L] else PC_R[J] := PC_M[L - 28];
    end;
    for J := 28 to 55 do
    begin
      L := J + ROT[I];
      if L < 56 then PC_R[J] := PC_M[L] else PC_R[J] := PC_M[L - 28];
    end;
    L := $1000000;
    for J := 0 to 23 do
    begin
      L := L shr 1;
      if PC_R[DES_PC2[J     ]] <> 0 then K[M] := K[M] or L;
      if PC_R[DES_PC2[J + 24]] <> 0 then K[N] := K[N] or L;
    end;
  end;

{$IFDEF CLR}
  FBeginPos := 0;
{$ENDIF}
  Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(Key){$ENDIF}, KeyOffset);
  for I := 0 to 15 do
  begin
    M := I shl 1; N := M + 1;
    Key[FBeginPos] := K[M] and $00FC0000 shl  6 or
                     K[M] and $00000FC0 shl 10 or
                     K[N] and $00FC0000 shr 10 or
                     K[N] and $00000FC0 shr  6;
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(Key){$ENDIF});
    Key[FBeginPos] := K[M] and $0003F000 shl 12 or
                     K[M] and $0000003F shl 16 or
                     K[N] and $0003F000 shr  4 or
                     K[N] and $0000003F;
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(Key){$ENDIF});
  end;
end;

procedure TCipher_1DES.Init(const Key: TBytes; const IVector: TIntArray);
begin
  InitBegin(Length(Key));
  Assert(Length(Key) = 8);
  MakeKey(Key, 0, User, 0,  False);
  MakeKey(Key, 0, User, 32, True);
  InitEnd(IVector);
end;

class procedure TCipher_3DES.GetContext(var BufSize, UserSize: Integer; KeySizes: TKeySizes);
begin
  BufSize := 8;
  KeySizes.MinSize := 128;
  KeySizes.MaxSize := 192;
  KeySizes.SkipSize := 64;
  UserSize := 32 * 4 * 2 * 3;
end;

procedure TCipher_3DES.Encode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0);
begin
  DES_Func(Data, Offset, User, 0);
  DES_Func(Data, Offset, User, 32);
  DES_Func(Data, Offset, User, 64);
end;

procedure TCipher_3DES.Decode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0);
begin
  DES_Func(Data, Offset, User, 96);
  DES_Func(Data, Offset, User, 128);
  DES_Func(Data, Offset, User, 160);
end;

procedure TCipher_3DES.Init(const Key: TBytes; const IVector: TIntArray);
var
  Pos: Integer;
begin
  InitBegin(Length(Key));

  Assert((Length(Key) = 16) or (Length(Key) = 24));
  Pos := 0;
  MakeKey(Key, 0, User, Pos, False); Inc(Pos, 32);
  MakeKey(Key, 8, User, Pos, True);  Inc(Pos, 32);
  if Length(Key) = 24 then begin
    MakeKey(Key, 16, User, Pos, False); Inc(Pos, 32);
    MakeKey(Key, 16, User, Pos, True);  Inc(Pos, 32);
  end
  else begin
    MakeKey(Key, 0, User, Pos, False); Inc(Pos, 32);
    MakeKey(Key, 0, User, Pos, True);  Inc(Pos, 32);
  end;
  MakeKey(Key, 8, User, Pos, False); Inc(Pos, 32);
  MakeKey(Key, 0, User, Pos, True);

  InitEnd(IVector);
end;

class procedure TCipher_Cast128.GetContext(var BufSize, UserSize: Integer; KeySizes: TKeySizes);
begin
  BufSize := 8;
  KeySizes.MinSize := 40;
  KeySizes.MaxSize := 128;
  KeySizes.SkipSize := 8;
  UserSize := 128;
end;

procedure TCipher_Cast128.Encode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0);
var
  T,I,A,B: LongWord;
  K: TIntArray;
begin
  K := User;
{$IFDEF CLR}
  FBeginPos := 0;
  FBeginPosData := 0;
{$ENDIF}
  Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF}, Offset);

  A := SwapInteger(Data[FBeginPosData]);
  B := SwapInteger(Data[FBeginPosData + 1]);
  for I := 0 to 2 do
  begin
    T := ROL(K[FBeginPos] + B, K[FBeginPos + 16]);
    A := A xor (Cast128_Data[0, T shr 24] xor
                Cast128_Data[1, T shr 16 and $FF] -
                Cast128_Data[2, T shr  8 and $FF] +
                Cast128_Data[3, T and $FF]);
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(K){$ENDIF});
    T := ROL(K[FBeginPos] xor A, K[FBeginPos + 16]);
    B := B xor (Cast128_Data[0, T shr 24] -
                Cast128_Data[1, T shr 16 and $FF] +
                Cast128_Data[2, T shr  8 and $FF] xor
                Cast128_Data[3, T and $FF]);
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(K){$ENDIF});
    T := ROL(K[FBeginPos] - B, K[FBeginPos + 16]);
    A := A xor (Cast128_Data[0, T shr 24] +
                Cast128_Data[1, T shr 16 and $FF] xor
                Cast128_Data[2, T shr  8 and $FF] -
                Cast128_Data[3, T and $FF]);
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(K){$ENDIF});
    T := ROL(K[FBeginPos] + A, K[FBeginPos + 16]);
    B := B xor (Cast128_Data[0, T shr 24] xor
                Cast128_Data[1, T shr 16 and $FF] -
                Cast128_Data[2, T shr  8 and $FF] +
                Cast128_Data[3, T and $FF]);
    if I = 2 then Break;
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(K){$ENDIF});
    T := ROL(K[FBeginPos] xor B, K[FBeginPos + 16]);
    A := A xor (Cast128_Data[0, T shr 24] -
                Cast128_Data[1, T shr 16 and $FF] +
                Cast128_Data[2, T shr  8 and $FF] xor
                Cast128_Data[3, T and $FF]);
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(K){$ENDIF});
    T := ROL(K[FBeginPos] - A, K[FBeginPos + 16]);
    B := B xor (Cast128_Data[0, T shr 24] +
                Cast128_Data[1, T shr 16 and $FF] xor
                Cast128_Data[2, T shr  8 and $FF] -
                Cast128_Data[3, T and $FF]);
    Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(K){$ENDIF});
    if (I = 1) and (FRounds <= 12) then Break;
  end;
  Data[FBeginPosData + 1] := SwapInteger(A);
  Data[FBeginPosData] := SwapInteger(B);
end;

procedure TCipher_Cast128.Decode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0);
var
  T,I,A,B: LongWord;
  K: TIntArray;
label
  Start;
begin
  K := User;
{$IFDEF CLR}
  FBeginPos := 0;
  FBeginPosData := 0;
{$ENDIF}
  Inc({$IFDEF CLR}FBeginPos{$ELSE}PInteger(K){$ENDIF}, 15);
  Inc({$IFDEF CLR}FBeginPosData{$ELSE}PInteger(Data){$ENDIF}, Offset);

  B := SwapInteger(Data[FBeginPosData]);
  A := SwapInteger(Data[FBeginPosData + 1]);
  I := 2;
  if FRounds <= 12 then Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(K){$ENDIF}, 4) else goto Start;
  while I > 0 do
  begin
    Dec(I);
    T := ROL(K[FBeginPos] - A, K[FBeginPos + 16]);
    B := B xor (Cast128_Data[0, T shr 24] +
                Cast128_Data[1, T shr 16 and $FF] xor
                Cast128_Data[2, T shr  8 and $FF] -
                Cast128_Data[3, T and $FF]);
    Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(K){$ENDIF});
    T := ROL(K[FBeginPos] xor B, K[FBeginPos + 16]);
    A := A xor (Cast128_Data[0, T shr 24] -
                Cast128_Data[1, T shr 16 and $FF] +
                Cast128_Data[2, T shr  8 and $FF] xor
                Cast128_Data[3, T and $FF]);
    Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(K){$ENDIF});
Start:
    T := ROL(K[FBeginPos] + A, K[FBeginPos + 16]);
    B := B xor (Cast128_Data[0, T shr 24] xor
                Cast128_Data[1, T shr 16 and $FF] -
                Cast128_Data[2, T shr  8 and $FF] +
                Cast128_Data[3, T and $FF]);
    Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(K){$ENDIF});
    T := ROL(K[FBeginPos] - B, K[FBeginPos + 16]);
    A := A xor (Cast128_Data[0, T shr 24] +
                Cast128_Data[1, T shr 16 and $FF] xor
                Cast128_Data[2, T shr  8 and $FF] -
                Cast128_Data[3, T and $FF]);
    Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(K){$ENDIF});
    T := ROL(K[FBeginPos] xor A, K[FBeginPos + 16]);
    B := B xor (Cast128_Data[0, T shr 24] -
                Cast128_Data[1, T shr 16 and $FF] +
                Cast128_Data[2, T shr  8 and $FF] xor
                Cast128_Data[3, T and $FF]);
    Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(K){$ENDIF});
    T := ROL(K[FBeginPos] + B, K[FBeginPos + 16]);
    A := A xor (Cast128_Data[0, T shr 24] xor
                Cast128_Data[1, T shr 16 and $FF] -
                Cast128_Data[2, T shr  8 and $FF] +
                Cast128_Data[3, T and $FF]);
    Dec({$IFDEF CLR}FBeginPos{$ELSE}PInteger(K){$ENDIF});
  end;
  Data[FBeginPosData] := SwapInteger(A);
  Data[FBeginPosData + 1] := SwapInteger(B);
end;

procedure TCipher_Cast128.Init(const Key: TBytes; const IVector: TIntArray);
var
  Z, X, T: array[0..3] of LongWord;
  K: TIntArray;
  I: LongWord;
begin
  InitBegin(Length(Key));
  if Length(Key) <= 10 then FRounds := 12 else FRounds := 16;
  K := User;

{$IFNDEF CLR}
  FillChar(X, SizeOf(X), 0);
{$ELSE}
  &Array.Clear(X, 0, Length(X));
{$ENDIF}

  Assert(Length(Key) <= 16);
  Buffer.BlockCopy(Key, 0, X, 0, Length(Key));
  SwapIntegerBuffer({$IFNDEF CLR}@{$ENDIF}X, {$IFNDEF CLR}@{$ENDIF}X, 4);
//  for I := 0 to 3 do X[I] := SwapInteger(X[I]);
  I := 0;
  while I < 32 do
  begin
    if I and 4 = 0 then
    begin
      Z[0] := X[0] xor Cast128_Key[0, X[3] shr 16 and $FF] xor
                       Cast128_Key[1, X[3] and $FF] xor
                       Cast128_Key[2, X[3] shr 24] xor
                       Cast128_Key[3, X[3] shr  8 and $FF] xor
                       Cast128_Key[2, X[2] shr 24];
      T[0] := Z[0];
      Z[1] := X[2] xor Cast128_Key[0, Z[0] shr 24] xor
                       Cast128_Key[1, Z[0] shr  8 and $FF] xor
                       Cast128_Key[2, Z[0] shr 16 and $FF] xor
                       Cast128_Key[3, Z[0] and $FF] xor
                       Cast128_Key[3, X[2] shr  8 and $FF];
      T[1] := Z[1];
      Z[2] := X[3] xor Cast128_Key[0, Z[1] and $FF] xor
                       Cast128_Key[1, Z[1] shr  8 and $FF] xor
                       Cast128_Key[2, Z[1] shr 16 and $FF] xor
                       Cast128_Key[3, Z[1] shr 24] xor
                       Cast128_Key[0, X[2] shr 16 and $FF];
      T[2] := Z[2];
      Z[3] := X[1] xor Cast128_Key[0, Z[2] shr  8 and $FF] xor
                       Cast128_Key[1, Z[2] shr 16 and $FF] xor
                       Cast128_Key[2, Z[2] and $FF] xor
                       Cast128_Key[3, Z[2] shr 24] xor
                       Cast128_Key[1, X[2] and $FF];
      T[3] := Z[3];
    end else
    begin
      X[0] := Z[2] xor Cast128_Key[0, Z[1] shr 16 and $FF] xor
                       Cast128_Key[1, Z[1] and $FF] xor
                       Cast128_Key[2, Z[1] shr 24] xor
                       Cast128_Key[3, Z[1] shr  8 and $FF] xor
                       Cast128_Key[2, Z[0] shr 24];
      T[0] := X[0];
      X[1] := Z[0] xor Cast128_Key[0, X[0] shr 24] xor
                       Cast128_Key[1, X[0] shr  8 and $FF] xor
                       Cast128_Key[2, X[0] shr 16 and $FF] xor
                       Cast128_Key[3, X[0] and $FF] xor
                       Cast128_Key[3, Z[0] shr  8 and $FF];
      T[1] := X[1];
      X[2] := Z[1] xor Cast128_Key[0, X[1] and $FF] xor
                       Cast128_Key[1, X[1] shr  8 and $FF] xor
                       Cast128_Key[2, X[1] shr 16 and $FF] xor
                       Cast128_Key[3, X[1] shr 24] xor
                       Cast128_Key[0, Z[0] shr 16 and $FF];
      T[2] := X[2];
      X[3] := Z[3] xor Cast128_Key[0, X[2] shr  8 and $FF] xor
                       Cast128_Key[1, X[2] shr 16 and $FF] xor
                       Cast128_Key[2, X[2] and $FF] xor
                       Cast128_Key[3, X[2] shr 24] xor
                       Cast128_Key[1, Z[0] and $FF];
      T[3] := X[3];
    end;
    case I and 12 of
      0,12:
        begin
          K[I +0] := Cast128_Key[0, T[2] shr 24] xor
                     Cast128_Key[1, T[2] shr 16 and $FF] xor
                     Cast128_Key[2, T[1] and $FF] xor
                     Cast128_Key[3, T[1] shr  8 and $FF];
          K[I +1] := Cast128_Key[0, T[2] shr  8 and $FF] xor
                     Cast128_Key[1, T[2] and $FF] xor
                     Cast128_Key[2, T[1] shr 16 and $FF] xor
                     Cast128_Key[3, T[1] shr 24];
          K[I +2] := Cast128_Key[0, T[3] shr 24] xor
                     Cast128_Key[1, T[3] shr 16 and $FF] xor
                     Cast128_Key[2, T[0] and $FF] xor
                     Cast128_Key[3, T[0] shr  8 and $FF];
          K[I +3] := Cast128_Key[0, T[3] shr  8 and $FF] xor
                     Cast128_Key[1, T[3] and $FF] xor
                     Cast128_Key[2, T[0] shr 16 and $FF] xor
                     Cast128_Key[3, T[0] shr 24];
        end;
      4,8:
        begin
          K[I +0] := Cast128_Key[0, T[0] and $FF] xor
                     Cast128_Key[1, T[0] shr  8 and $FF] xor
                     Cast128_Key[2, T[3] shr 24] xor
                     Cast128_Key[3, T[3] shr 16 and $FF];
          K[I +1] := Cast128_Key[0, T[0] shr 16 and $FF] xor
                     Cast128_Key[1, T[0] shr 24] xor
                     Cast128_Key[2, T[3] shr  8 and $FF] xor
                     Cast128_Key[3, T[3] and $FF];
          K[I +2] := Cast128_Key[0, T[1] and $FF] xor
                     Cast128_Key[1, T[1] shr  8 and $FF] xor
                     Cast128_Key[2, T[2] shr 24] xor
                     Cast128_Key[3, T[2] shr 16 and $FF];
          K[I +3] := Cast128_Key[0, T[1] shr 16 and $FF] xor
                     Cast128_Key[1, T[1] shr 24] xor
                     Cast128_Key[2, T[2] shr  8 and $FF] xor
                     Cast128_Key[3, T[2] and $FF];
        end;
    end;
    case I and 12 of
      0: begin
           K[I +0] := K[I +0] xor Cast128_Key[0, Z[0] shr  8 and $FF];
           K[I +1] := K[I +1] xor Cast128_Key[1, Z[1] shr  8 and $FF];
           K[I +2] := K[I +2] xor Cast128_Key[2, Z[2] shr 16 and $FF];
           K[I +3] := K[I +3] xor Cast128_Key[3, Z[3] shr 24];
         end;
      4: begin
           K[I +0] := K[I +0] xor Cast128_Key[0, X[2] shr 24];
           K[I +1] := K[I +1] xor Cast128_Key[1, X[3] shr 16 and $FF];
           K[I +2] := K[I +2] xor Cast128_Key[2, X[0] and $FF];
           K[I +3] := K[I +3] xor Cast128_Key[3, X[1] and $FF];
         end;
      8: begin
           K[I +0] := K[I +0] xor Cast128_Key[0, Z[2] shr 16 and $FF];
           K[I +1] := K[I +1] xor Cast128_Key[1, Z[3] shr 24];
           K[I +2] := K[I +2] xor Cast128_Key[2, Z[0] shr  8 and $FF];
           K[I +3] := K[I +3] xor Cast128_Key[3, Z[1] shr  8 and $FF];
         end;
     12: begin
          K[I +0] := K[I +0] xor Cast128_Key[0, X[0] and $FF];
          K[I +1] := K[I +1] xor Cast128_Key[1, X[1] and $FF];
          K[I +2] := K[I +2] xor Cast128_Key[2, X[2] shr 24];
          K[I +3] := K[I +3] xor Cast128_Key[3, X[3] shr 16 and $FF];
        end;
    end;
    if I >= 16 then
    begin
      K[I +0] := K[I +0] and $1F;
      K[I +1] := K[I +1] and $1F;
      K[I +2] := K[I +2] and $1F;
      K[I +3] := K[I +3] and $1F;
    end;
    Inc(I, 4);
  end;

{$IFNDEF CLR}
  FillChar(X, SizeOf(X), 0);
  FillChar(Z, SizeOf(Z), 0);
  FillChar(T, SizeOf(T), 0);
{$ELSE}
  &Array.Clear(X, 0, Length(X));
  &Array.Clear(Z, 0, Length(Z));
  &Array.Clear(T, 0, Length(T));
{$ENDIF}

  InitEnd(IVector);
end;

const
  RC4_BufSize = 32;

class procedure TCipher_RC4.GetContext(var BufSize, UserSize: Integer; KeySizes: TKeySizes);
begin
  BufSize := RC4_BufSize;
  KeySizes.MinSize := 40;
  KeySizes.MaxSize := 2048;
  KeySizes.SkipSize := 8;
  UserSize := 256 * 2;
end;

procedure TCipher_RC4.Encode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0);
var
{$IFNDEF CLR}
  D: PByteArray;
  B: PByte;
{$ENDIF}
  X, S: Byte;
begin
{$IFNDEF CLR}
  D := Pointer(User);
  B := Pointer(Data);
  Inc(PInteger(B), Offset);
{$ELSE}
  FBeginPosData := Offset * SizeOf(Longword);
{$ENDIF}

  if DataSize = 0 then
    DataSize := RC4_BufSize;

  for X := 0 to DataSize - 1 do begin
    Inc(FI);

  {$IFNDEF CLR}
    S := D[FI];
    Inc(FJ, S);
    D[FI] := D[FJ];
    D[FJ] := S;
    B^ := B^ xor D[(D[FI] + S) and $FF];
    Inc(B);
  {$ELSE}
    S := Buffer.GetByte(User, FI);
    Inc(FJ, S);
    Buffer.SetByte(User, FI, Buffer.GetByte(User, FJ));
    Buffer.SetByte(User, FJ, S);
    Buffer.SetByte(Data, FBeginPosData,
      Buffer.GetByte(Data, FBeginPosData) xor
      Buffer.GetByte(User, (Buffer.GetByte(User, FI) + S) and $FF));
    Inc(FBeginPosData);
  {$ENDIF}
  end;
end;

procedure TCipher_RC4.Decode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0);
begin
  Encode(Data, Offset, DataSize);
end;

procedure TCipher_RC4.Init(const Key: TBytes; const IVector: TIntArray);
var
  D: TBytes;
  I, J, S: Integer;
  KeyLength: Integer;
begin
  KeyLength := Length(Key);
  InitBegin(KeyLength);
  FI := 0;
  FJ := 0;
  J := 0;
  SetLength(D, 256);
  for I := 0 to 255 do
    D[I] := I;

  for I := 0 to 255 do begin
    J := (J + D[I] + Key[I mod KeyLength]) and $FF;
    S := D[I];
    D[I] := D[J];
    D[J] := S;
  end;
  InitEnd(IVector);

{Save the Key}
  Buffer.BlockCopy(D, 0, User, 0, 256);
  Buffer.BlockCopy(D, 0, User, 256, 256);
  FSI := FI;
  FSJ := FJ;
end;

procedure TCipher_RC4.Done;
begin
  inherited Done;
  FI := FSI;
  FJ := FSJ;
  Buffer.BlockCopy(User, 256, User, 0, 256);
end;

{$IFDEF CLR}
initialization
  SZ_Blowfish_Data := &Array(Blowfish_Data).Length;
  SZ_Blowfish_Key := &Array(Blowfish_Key).Length;
{$ENDIF}

end.

