{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)

 * 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 CRSymmetricAlgorithm;
{$ENDIF}

interface
{$A+,Q-,R-,Y-}
uses
{$IFDEF CLR}
  System.Runtime.InteropServices,
{$ELSE}
  CLRClasses,
{$ENDIF}
  SysUtils, CRDECUtil, CRCryptoTransformIntf, CRTypes;

type
  TKeySizes = class
  public
    MaxSize: Integer;
    MinSize: Integer;
    SkipSize: Integer;
  end;

  TCipherMode = (cmCBC, cmECB);
//  TCipherMode = (cmCTS, cmCBC, cmCFB, cmOFB, cmECB, cmCTSMAC, cmCBCMAC, cmCFBMAC);
{ the Cipher Modes:
  cmCTS     Cipher Text Stealing, a Variant from cmCBC, but relaxes
            the restriction that the DataSize must be a mulitply from BufSize,
            this is the Defaultmode, fast and Bytewise
  cmCBC     Cipher Block Chaining
  cmCFB     K-bit Cipher Feedback, here is K = 8 -> 1 Byte
  cmOFB     K-bit Output Feedback, here is K = 8 -> 1 Byte
  cmECB *   Electronic Codebook, DataSize must be a multiply from BufSize
  cmCTSMAC  Build a Message Authentication Code in cmCTS Mode
  cmCBCMAC  Build a CBC-MAC
  cmCFBMAC  Build a CFB-MAC
}

  TPaddingMode = (pmNone, pmPKCS7, pmZeros);

  TSymmetricAlgorithmClass = class of TSymmetricAlgorithm;

  TSymmetricAlgorithm = class(TProtection)
  private
    FMode: TCipherMode;
    FPadding: TPaddingMode;
    FLegalKeySizes: TKeySizes;

    FIntSize: Integer;
    FBuffer: TIntArray;
    FFeedback: TIntArray;
    FVector: TIntArray;
    FInitialized: Boolean;
  protected
  {$IFDEF CLR}
    FBeginPos: Integer;
    FBeginPosData: Integer;
  {$ENDIF}

    FUser: TIntArray;
    FUserSize: Integer;
    FKeySize: Integer;
    FBufSize: Integer;
    procedure SetKey(const Value: TBytes);
    procedure SetIV(const Value: TBytes);

    {used in method Init()}
    procedure Done; virtual;
    procedure InitBegin(KeySize: Integer);
    procedure InitEnd(const IVector: TIntArray);
    {must override}
    class procedure GetContext(var BufSize, UserSize: Integer; KeySizes: TKeySizes); virtual;
    {override TProtection Methods}
    procedure CodeInit(Action: TPAction); override;
    procedure CodeDone(Action: TPAction); override;
    procedure CodeBuf(const Buffer: TValueArr; const BufferSize: Integer; Action: TPAction); override;
    {the en(de)code function, must override}
    procedure Encode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0); virtual;
    procedure Decode({$IFDEF CLR}const{$ENDIF} Data: TIntArray; Offset: Integer; DataSize: Integer = 0); virtual;

    procedure InternalEncodeBuffer({$IFDEF CLR}const{$ENDIF} Data: TIntArray; DataSize: Integer);
    procedure InternalDecodeBuffer({$IFDEF CLR}const{$ENDIF} Data: TIntArray; DataSize: Integer);

    procedure Init(const Key: TBytes; const IVector: TIntArray); virtual;
    procedure Protect; virtual;

    {the individual Userdata and Buffer}
    property User: TIntArray read FUser;
    property UserSize: Integer read FUserSize;

  public
    constructor Create(AProtection: TProtection = nil); virtual;
    destructor Destroy; override;
    procedure Clear;
    function CreateEncryptor: ICryptoTransform; overload; virtual;
    function CreateDecryptor: ICryptoTransform; overload; virtual;
    function CreateEncryptor(const rgbKey, rgbIV: TBytes): ICryptoTransform; overload; virtual;
    function CreateDecryptor(const rgbKey, rgbIV: TBytes): ICryptoTransform; overload; virtual;

    procedure GenerateIV; virtual;
    procedure GenerateKey; virtual;
    procedure ClearIV;
    procedure SetIVBuf(const Value: IntPtr; const Offset, Count: integer);
    procedure SetIVBytes(const Value: TBytes; const Offset, Count: integer);

    procedure EncodeBuffer(Source, Dest: IntPtr; DataSize: Integer); overload;
    procedure EncodeBuffer(Source: TValueArr; SrcOffset: Integer; Dest: TValueArr; DstOffset: Integer; DataSize: Integer); overload;
    procedure DecodeBuffer(Source, Dest: IntPtr; DataSize: Integer); overload;
    procedure DecodeBuffer(Source: TValueArr; SrcOffset: Integer; Dest: TValueArr; DstOffset: Integer; DataSize: Integer); overload;
    procedure EncryptCBC(const Input: TBytes; InputOffset: Integer; InputLen: Integer; var Output: TBytes; OutputOffset: Integer; const IVValue: TBytes); overload;
    procedure DecryptCBC(const Input: TBytes; InputOffset: Integer; InputLen: Integer; var Output: TBytes; OutputOffset: Integer; const IVValue: TBytes); overload;
    procedure EncryptCBC(const Input: TBytes; InputOffset: Integer; InputLen: Integer; var Output: TBytes; OutputOffset: Integer); overload;
    procedure DecryptCBC(const Input: TBytes; InputOffset: Integer; InputLen: Integer; var Output: TBytes; OutputOffset: Integer); overload;
    procedure EncryptECB(const Input: TBytes; InputOffset: Integer; InputLen: Integer; var Output: TBytes; OutputOffset: Integer); overload;
    procedure DecryptECB(const Input: TBytes; InputOffset: Integer; InputLen: Integer; var Output: TBytes; OutputOffset: Integer); overload;
    function ValidKeySize(BitLength: Integer): Boolean;

    property Initialized: Boolean read FInitialized;
    property Mode: TCipherMode read FMode write FMode;
    property Padding: TPaddingMode read FPadding write FPadding;
    property KeySize: Integer read FKeySize;
    property LegalKeySizes: TKeySizes read FLegalKeySizes;
    property BlockSize: Integer read FBufSize;
    property Key: TBytes write SetKey;
    property IV: TBytes write SetIV;
  end;

//const
//  CheckCipherKeySize: Boolean = False;
{set to True raises Exception when Size of the Key is too large, (Method Init())
 otherwise will truncate the Key, default mode is False}

implementation

uses
{$IFDEF MSWINDOWS}
  Windows,
{$ENDIF}
  DAConsts, CRFunctions;

constructor TSymmetricAlgorithm.Create(AProtection: TProtection = nil);
begin
  inherited Create(AProtection);

  FLegalKeySizes := TKeySizes.Create;
  GetContext(FBufSize, FUserSize, FLegalKeySizes);
  FKeySize := FLegalKeySizes.MaxSize div 8; // bits to bytes
  FIntSize := FBufSize div SizeOf(Longword);

{$IFDEF CLR}
  SetLength(FVector, FIntSize);
  SetLength(FFeedback, FIntSize);
  SetLength(FBuffer, FIntSize);
  SetLength(FUser, FUserSize div SizeOf(Longword));
{$ELSE}
  GetMem(FVector, FBufSize);
  GetMem(FFeedback, FBufSize);
  GetMem(FBuffer, FBufSize);
  GetMem(FUser, FUserSize);
{$ENDIF}

  Protect;
  FMode := cmCBC;
  FPadding := pmNone;
end;

destructor TSymmetricAlgorithm.Destroy;
begin
  Protect;
{$IFDEF CLR}
  SetLength(FVector, 0);
  SetLength(FFeedback, 0);
  SetLength(FBuffer, 0);
  SetLength(FUser, 0);
{$ELSE}
  FreeMem(FVector);
  FreeMem(FFeedback);
  FreeMem(FBuffer);
  FreeMem(FUser);
{$ENDIF}
  FLegalKeySizes.Free;

  inherited;
end;

procedure TSymmetricAlgorithm.Clear;
begin
  Protect;
end;

procedure TSymmetricAlgorithm.InitBegin(KeySize: Integer);
begin
  FInitialized := False;
  Protect;
  if not ValidKeySize(KeySize * 8) then
    raise Exception.CreateFmt(SInvalidKeySize, [ClassName, FLegalKeySizes.MinSize, FLegalKeySizes.MaxSize]);
end;

procedure TSymmetricAlgorithm.InitEnd(const IVector: TIntArray);
begin
  if IVector = nil then Encode(FVector, 0)
  else if IVector <> FVector then
    Buffer.BlockCopy(IVector, 0, FVector, 0, FBufSize);

  Buffer.BlockCopy(FVector, 0, FFeedback, 0, FBufSize);
  FInitialized := True;
end;

class procedure TSymmetricAlgorithm.GetContext(var BufSize, UserSize: Integer; KeySizes: TKeySizes);
begin
  BufSize := 0;
  KeySizes.MinSize := 0;
  KeySizes.MaxSize := 0;
  KeySizes.SkipSize := 0;
  UserSize := 0;
end;

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

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

function TSymmetricAlgorithm.ValidKeySize(BitLength: Integer): Boolean;
var
  Size: Integer;
begin
  Result := False;
  Size := LegalKeySizes.MinSize;
  repeat
    if BitLength < Size then
      Exit;
    if BitLength = Size then begin
      Result := True;
      Exit;
    end;
    Size := Size + LegalKeySizes.SkipSize;
  until Size >= LegalKeySizes.MaxSize + LegalKeySizes.SkipSize;
end;

procedure TSymmetricAlgorithm.SetKey(const Value: TBytes);
begin
  Init(Value, FVector);
  FKeySize := Length(Value);
end;

procedure TSymmetricAlgorithm.SetIV(const Value: TBytes);
begin
  if (Length(Value) = 0) or (Length(Value) > BlockSize) then
    raise Exception.Create(SInvalidIV);

  Buffer.BlockCopy(Value, 0, FVector, 0, Length(Value));
  Buffer.BlockCopy(FVector, 0, FFeedback, 0, FBufSize);
end;

procedure TSymmetricAlgorithm.SetIVBuf(const Value: IntPtr; const Offset, Count: integer);
{$IFDEF CLR}
var
  b: TBytes;
{$ENDIF}
begin
  if (Count = 0) or (Count > BlockSize) then
    raise Exception.Create(SInvalidIV);

{$IFDEF CLR}
  SetLength(b, Count);
  Marshal.Copy(PtrOffset(Value, Offset), b, 0, Count);
  Buffer.BlockCopy(b, 0, FVector, 0, Count);
{$ELSE}
  Move(PtrOffset(Value, Offset)^, FVector[0], Count);
{$ENDIF}
  Buffer.BlockCopy(FVector, 0, FFeedback, 0, FBufSize);
end;

procedure TSymmetricAlgorithm.SetIVBytes(const Value: TBytes; const Offset, Count: integer);
begin
  if (Count = 0) or (Count > BlockSize) then
    raise Exception.Create(SInvalidIV);

  Buffer.BlockCopy(Value, Offset, FVector, 0, Count);
  Buffer.BlockCopy(FVector, 0, FFeedback, 0, FBufSize);
end;

procedure TSymmetricAlgorithm.GenerateIV;
var
  tmp: TBytes;
  i: Integer;
begin
  Randomize;
  SetLength(tmp, BlockSize);
  for i := 0 to BlockSize - 1 do
    tmp[i] := Random(256);

  IV := tmp;
end;

procedure TSymmetricAlgorithm.GenerateKey;
var
  tmp: TBytes;
  i: Integer;
begin
  Randomize;
  SetLength(tmp, KeySize);
  for i := 0 to KeySize - 1 do
    tmp[i] := Random(256);

  Key := tmp;
end;

procedure TSymmetricAlgorithm.Init(const Key: TBytes; const IVector: TIntArray);
begin
end;

procedure TSymmetricAlgorithm.Done;
begin
  if MemCompare(FVector, FFeedback, FBufSize) = 0 then Exit;

  Buffer.BlockCopy(FFeedback, 0, FBuffer, 0, FBufSize);
  Buffer.BlockCopy(FVector, 0, FFeedback, 0, FBufSize);
end;

procedure TSymmetricAlgorithm.Protect;
begin
  FInitialized := False;

{$IFNDEF CLR}
  FillChar(FVector^, FBufSize, $AA);
  FillChar(FFeedback^, FBufSize, $AA);
  FillChar(FBuffer^, FBufSize, $AA);
  FillChar(FUser^, FUserSize, $AA);

  FillChar(FVector^, FBufSize, $55);
  FillChar(FFeedback^, FBufSize, $55);
  FillChar(FBuffer^, FBufSize, $55);
  FillChar(FUser^, FUserSize, $55);

  FillChar(FVector^, FBufSize, $0);
  FillChar(FFeedback^, FBufSize, $0);
  FillChar(FBuffer^, FBufSize, $0);
  FillChar(FUser^, FUserSize, $0);
{$ELSE}
  &Array.Clear(FVector, 0, Length(FVector));
  &Array.Clear(FFeedback, 0, Length(FFeedback));
  &Array.Clear(FBuffer, 0, Length(FBuffer));
  &Array.Clear(FUser, 0, Length(FUser));
{$ENDIF}
end;

procedure TSymmetricAlgorithm.ClearIV;
begin
{$IFNDEF CLR}
  FillChar(FVector^, FBufSize, $0);
  FillChar(FFeedback^, FBufSize, $0);
{$ELSE}
  &Array.Clear(FVector, 0, Length(FVector));
  &Array.Clear(FFeedback, 0, Length(FFeedback));
{$ENDIF}
end;

procedure TSymmetricAlgorithm.CodeInit(Action: TPAction);
begin
  if not Initialized then
    raise Exception.CreateFmt(SNotInitialized, [ClassName]);

  if Action <> paCalc then
    if Action <> paWipe then Done
    else RndXORBufferL(Integer(GetTickCount), FFeedback, FBufSize);

  inherited CodeInit(Action);
end;

procedure TSymmetricAlgorithm.CodeDone(Action: TPAction);
begin
  inherited CodeDone(Action);

  if Action <> paCalc then
    if Action <> paWipe then Done
    else RndXORBufferL(Integer(GetTickCount), FFeedback, FBufSize);
end;

procedure TSymmetricAlgorithm.EncodeBuffer(Source, Dest: IntPtr; DataSize: Integer);
var
{$IFDEF CLR}
  DGC: GCHandle;
{$ENDIF}
  D: TIntArray;
begin
{$IFDEF CLR}
  if (DataSize and $3) > 0 then
    SetLength(D, DataSize div SizeOf(Integer) + 1)
  else
    SetLength(D, DataSize div SizeOf(Integer));

  DGC := GCHandle.Alloc(D, GCHandleType.Pinned);
  try
    CopyBuffer(Source, Marshal.UnsafeAddrOfPinnedArrayElement(D, 0), DataSize);
{$ELSE}
  D := TIntArray(Dest);
  if Source <> Dest then
    Move(Source^, Dest^, DataSize);
{$ENDIF}

  InternalEncodeBuffer(D, DataSize);

{$IFDEF CLR}
    CopyBuffer(Marshal.UnsafeAddrOfPinnedArrayElement(D, 0), Dest, DataSize);
  finally
    DGC.Free;
  end;
{$ENDIF}
end;

procedure TSymmetricAlgorithm.EncodeBuffer(Source: TValueArr; SrcOffset: Integer; Dest: TValueArr; DstOffset: Integer; DataSize: Integer);
var
  D: TIntArray;
begin
{$IFDEF CLR}
  if (DataSize and $3) > 0 then
    SetLength(D, DataSize div SizeOf(Integer) + 1)
  else
    SetLength(D, DataSize div SizeOf(Integer));
  Buffer.BlockCopy(Source, SrcOffset, D, 0, DataSize);
{$ELSE}
  D := TIntArray(PAnsiChar(Dest) + DstOffset);
  if (Source <> Dest) or (SrcOffset <> DstOffset) then
    Move(Source[SrcOffset], Dest[DstOffset], DataSize);
{$ENDIF}

  InternalEncodeBuffer(D, DataSize);

{$IFDEF CLR}
  Buffer.BlockCopy(D, 0, Dest, DstOffset, DataSize);
{$ENDIF}
end;

procedure TSymmetricAlgorithm.InternalEncodeBuffer({$IFDEF CLR}const{$ENDIF} Data: TIntArray; DataSize: Integer);
var
  F: TIntArray;
  Pos: Integer;
begin
  if not Initialized then
    raise Exception.CreateFmt(SNotInitialized, [ClassName]);

  Pos := 0;
  case FMode of
    cmECB:
      begin
        while DataSize >= FBufSize do
        begin
          Encode(Data, Pos);
          Inc(Pos, FIntSize);
          Dec(DataSize, FBufSize);
        end;
        if DataSize > 0 then
        begin
          Buffer.BlockCopy(Data, Pos * SizeOf(Integer), FBuffer, 0, DataSize);
          Encode(FBuffer, 0, DataSize);
          Buffer.BlockCopy(FBuffer, 0, Data, Pos * SizeOf(Integer), DataSize);
        end;
      end;
    cmCBC:
      begin
        F := FFeedback;
        while DataSize >= FBufSize do
        begin
          XORBuffers(Data, Pos, F, FBufSize);
          Encode(Data, Pos);
        {$IFDEF CLR}
          Buffer.BlockCopy(Data, Pos * SizeOf(Integer), F, 0, FBufSize);
        {$ELSE}
          F := Pointer(PAnsiChar(Data) + Pos * SizeOf(Integer));
        {$ENDIF}

          Inc(Pos, FIntSize);
          Dec(DataSize, FBufSize);
        end;
        if F <> FFeedback then Buffer.BlockCopy(F, 0, FFeedback, 0, FBufSize);
        if DataSize > 0 then
        begin
          Buffer.BlockCopy(FFeedback, 0, FBuffer, 0, FBufSize);
          Encode(FBuffer, 0, DataSize);
          XORBuffers(Data, Pos, FBuffer, DataSize);
          XORBuffers(FFeedback, 0, FBuffer, FBufSize);
        end;
      end;
{    cmCTS:
      begin
        while DataSize >= FBufSize do
        begin
          XORBuffers(S, FFeedback, FBufSize, D);
          Encode(D);
          XORBuffers(D, FFeedback, FBufSize, FFeedback);
          Inc(S, FBufSize);
          Inc(D, FBufSize);
          Dec(DataSize, FBufSize);
        end;
        if DataSize > 0 then
        begin
          Move(FFeedback^, FBuffer^, FBufSize);
          Encode(FBuffer);
          XORBuffers(S, FBuffer, DataSize, D);
          XORBuffers(FBuffer, FFeedback, FBufSize, FFeedback);
        end;
      end;
    cmCFB:
      while DataSize > 0 do
      begin
        Move(FFeedback^, FBuffer^, FBufSize);
        Encode(FBuffer);
        D^ := S^ xor PByte(FBuffer)^;
        Move(PByteArray(FFeedback)[1], FFeedback^, FBufSize-1);
        PByteArray(FFeedback)[FBufSize-1] := D^;
        Inc(D);
        Inc(S);
        Dec(DataSize);
      end;
    cmOFB:
      while DataSize > 0 do
      begin
        Move(FFeedback^, FBuffer^, FBufSize);
        Encode(FBuffer);
        D^ := S^ xor PByte(FBuffer)^;
        Move(PByteArray(FFeedback)[1], FFeedback^, FBufSize-1);
        PByteArray(FFeedback)[FBufSize-1] := PByte(FBuffer)^;
        Inc(D);
        Inc(S);
        Dec(DataSize);
      end;
    cmCTSMAC:
      begin
        while DataSize >= FBufSize do
        begin
          XORBuffers(S, FFeedback, FBufSize, FBuffer);
          Encode(FBuffer);
          XORBuffers(FBuffer, FFeedback, FBufSize, FFeedback);
          Inc(S, FBufSize);
          Dec(DataSize, FBufSize);
        end;
        if DataSize > 0 then
        begin
          Move(FFeedback^, FBuffer^, FBufSize);
          Encode(FBuffer);
          XORBuffers(FBuffer, FFeedback, FBufSize, FFeedback);
        end;
      end;
    cmCBCMAC:
      begin
        while DataSize >= FBufSize do
        begin
          XORBuffers(S, FFeedback, FBufSize, FBuffer);
          Encode(FBuffer);
          Move(FBuffer^, FFeedback^, FBufSize);
          Inc(S, FBufSize);
          Dec(DataSize, FBufSize);
        end;
        if DataSize > 0 then
        begin
          Move(FFeedback^, FBuffer^, FBufSize);
          Encode(FBuffer);
          XORBuffers(FBuffer, FFeedback, FBufSize, FFeedback);
        end;
      end;
    cmCFBMAC:
      while DataSize > 0 do
      begin
        Move(FFeedback^, FBuffer^, FBufSize);
        Encode(FBuffer);
        Move(PByteArray(FFeedback)[1], FFeedback^, FBufSize-1);
        PByteArray(FFeedback)[FBufSize-1] := S^ xor PByte(FBuffer)^;
        Inc(S);
        Dec(DataSize);
      end;
}
  end;
end;

procedure TSymmetricAlgorithm.DecodeBuffer(Source, Dest: IntPtr; DataSize: Integer);
var
{$IFDEF CLR}
  DGC: GCHandle;
{$ENDIF}
  D: TIntArray;
begin
{$IFDEF CLR}
  if (DataSize and $3) > 0 then
    SetLength(D, DataSize div SizeOf(Integer) + 1)
  else
    SetLength(D, DataSize div SizeOf(Integer));

  DGC := GCHandle.Alloc(D, GCHandleType.Pinned);
  try
    CopyBuffer(Source, Marshal.UnsafeAddrOfPinnedArrayElement(D, 0), DataSize);
{$ELSE}
  D := TIntArray(Dest);
  if Source <> Dest then
    Move(Source^, Dest^, DataSize);
{$ENDIF}

  InternalDecodeBuffer(D, DataSize);

{$IFDEF CLR}
    CopyBuffer(Marshal.UnsafeAddrOfPinnedArrayElement(D, 0), Dest, DataSize);
  finally
    DGC.Free;
  end;
{$ENDIF}
end;

procedure TSymmetricAlgorithm.DecodeBuffer(Source: TValueArr; SrcOffset: Integer; Dest: TValueArr; DstOffset: Integer; DataSize: Integer);
var
  D: TIntArray;
begin
{$IFDEF CLR}
  if (DataSize and $3) > 0 then
    SetLength(D, DataSize div SizeOf(Integer) + 1)
  else
    SetLength(D, DataSize div SizeOf(Integer));
  Buffer.BlockCopy(Source, SrcOffset, D, 0, DataSize);
{$ELSE}
  D := TIntArray(PAnsiChar(Dest) + DstOffset);
  if (Source <> Dest) or (SrcOffset <> DstOffset) then
    Move(Source[SrcOffset], Dest[DstOffset], DataSize);
{$ENDIF}

  InternalDecodeBuffer(D, DataSize);

{$IFDEF CLR}
  Buffer.BlockCopy(D, 0, Dest, DstOffset, DataSize);
{$ENDIF}
end;

procedure TSymmetricAlgorithm.InternalDecodeBuffer({$IFDEF CLR}const{$ENDIF} Data: TIntArray; DataSize: Integer);
var
  S, F, B: TIntArray;
  Pos: Integer;
begin
  if not Initialized then
    raise Exception.CreateFmt(SNotInitialized, [ClassName]);

  Pos := 0;
  case FMode of
    cmECB:
      begin
        while DataSize >= FBufSize do
        begin
          Decode(Data, Pos);
          Inc(Pos, FIntSize);
          Dec(DataSize, FBufSize);
        end;
        if DataSize > 0 then
        begin
          Buffer.BlockCopy(Data, Pos * SizeOf(Integer), FBuffer, 0, DataSize);
          Encode(FBuffer, 0, DataSize);
          Buffer.BlockCopy(FBuffer, 0, Data, Pos * SizeOf(Integer), DataSize);
        end;
      end;
    cmCBC:
      begin
        F := FFeedback;
        B := FBuffer;
        while DataSize >= FBufSize do
        begin
          Buffer.BlockCopy(Data, Pos * SizeOf(Integer), B, 0, FBufSize);
          Decode(Data, Pos);
          XORBuffers(Data, Pos, F, FBufSize);
          S := B;
          B := F;
          F := S;
          Inc(Pos, FIntSize);
          Dec(DataSize, FBufSize);
        end;
        if F <> FFeedback then Buffer.BlockCopy(F, 0, FFeedback, 0, FBufSize);
        if DataSize > 0 then
        begin
          Buffer.BlockCopy(FFeedback, 0, FBuffer, 0, FBufSize);
          Encode(FBuffer, 0, DataSize);
          XORBuffers(Data, Pos, FBuffer, DataSize);
          XORBuffers(FFeedback, 0, FBuffer, FBufSize);
        end;
      end;
{    cmCTS:
      begin
        if S <> D then Move(S^, D^, DataSize);
        F := FFeedback;
        B := FBuffer;
        while DataSize >= FBufSize do
        begin
          XORBuffers(D, F, FBufSize, B);
          Decode(D);
          XORBuffers(D, F, FBufSize, D);
          S := B;
          B := F;
          F := S;
          Inc(D, FBufSize);
          Dec(DataSize, FBufSize);
        end;
        if F <> FFeedback then Move(F^, FFeedback^, FBufSize);
        if DataSize > 0 then
        begin
          Move(FFeedback^, FBuffer^, FBufSize);
          Encode(FBuffer);
          XORBuffers(FBuffer, D, DataSize, D);
          XORBuffers(FBuffer, FFeedback, FBufSize, FFeedback);
        end;
      end;
    cmCFB:
      while DataSize > 0 do
      begin
        Move(FFeedback^, FBuffer^, FBufSize);
        Encode(FBuffer);
        Move(PByteArray(FFeedback)[1], FFeedback^, FBufSize-1);
        PByteArray(FFeedback)[FBufSize-1] := S^;
        D^ := S^ xor PByte(FBuffer)^;
        Inc(D);
        Inc(S);
        Dec(DataSize);
      end;
    cmOFB:
      while DataSize > 0 do
      begin
        Move(FFeedback^, FBuffer^, FBufSize);
        Encode(FBuffer);
        D^ := S^ xor PByte(FBuffer)^;
        Move(PByteArray(FFeedback)[1], FFeedback^, FBufSize-1);
        PByteArray(FFeedback)[FBufSize-1] := PByte(FBuffer)^;
        Inc(D);
        Inc(S);
        Dec(DataSize);
      end;
    cmCTSMAC, cmCBCMAC, cmCFBMAC:
      begin
        EncodeBuffer(Source, Dest, DataSize);
        Exit;
      end;
}
  end;
end;

procedure TSymmetricAlgorithm.CodeBuf(const Buffer: TValueArr; const BufferSize: Integer; Action: TPAction);
begin
  if Action = paDecode then
  begin
    if Action in Actions then
      DecodeBuffer(Buffer, 0, Buffer, 0, BufferSize);
    inherited CodeBuf(Buffer, BufferSize, Action);
  end
  else begin
    inherited CodeBuf(Buffer, BufferSize, Action);
    if Action in Actions then
      EncodeBuffer(Buffer, 0, Buffer, 0, BufferSize);
  end;
end;

procedure TSymmetricAlgorithm.EncryptCBC(const Input: TBytes; InputOffset: Integer; InputLen: Integer; var Output: TBytes; OutputOffset: Integer; const IVValue: TBytes);
begin
  if Mode <> cmCBC then
    Mode := cmCBC;

  Buffer.BlockCopy(IVValue, 0, FFeedback, 0, BlockSize);
  EncodeBuffer(TValueArr(Input), InputOffset, TValueArr(Output), OutputOffset, InputLen);
end;

procedure TSymmetricAlgorithm.DecryptCBC(const Input: TBytes; InputOffset: Integer; InputLen: Integer; var Output: TBytes; OutputOffset: Integer; const IVValue: TBytes);
begin
  if Mode <> cmCBC then
    Mode := cmCBC;

  Buffer.BlockCopy(IVValue, 0, FFeedback, 0, BlockSize);
  DecodeBuffer(TValueArr(Input), InputOffset, TValueArr(Output), OutputOffset, InputLen);
end;

procedure TSymmetricAlgorithm.EncryptCBC(const Input: TBytes; InputOffset: Integer; InputLen: Integer; var Output: TBytes; OutputOffset: Integer);
begin
  if Mode <> cmCBC then
    Mode := cmCBC;

//  Buffer.BlockCopy(FVector, 0, FFeedback, 0, BlockSize);
  EncodeBuffer(TValueArr(Input), InputOffset, TValueArr(Output),  OutputOffset, InputLen);
end;

procedure TSymmetricAlgorithm.DecryptCBC(const Input: TBytes; InputOffset: Integer; InputLen: Integer; var Output: TBytes; OutputOffset: Integer);
begin
  if Mode <> cmCBC then
    Mode := cmCBC;

//  Buffer.BlockCopy(FVector, 0, FFeedback, 0, BlockSize);
  DecodeBuffer(TValueArr(Input), InputOffset, TValueArr(Output), OutputOffset, InputLen);
end;

procedure TSymmetricAlgorithm.EncryptECB(const Input: TBytes; InputOffset: Integer; InputLen: Integer; var Output: TBytes; OutputOffset: Integer);
begin
  if Mode <> cmECB then
    Mode := cmECB;

//  Buffer.BlockCopy(FVector, 0, FFeedback, 0, BlockSize);
  EncodeBuffer(TValueArr(Input), InputOffset, TValueArr(Output), OutputOffset, InputLen);
end;

procedure TSymmetricAlgorithm.DecryptECB(const Input: TBytes; InputOffset: Integer; InputLen: Integer; var Output: TBytes; OutputOffset: Integer);
begin
  if Mode <> cmECB then
    Mode := cmECB;

//  Buffer.BlockCopy(FVector, 0, FFeedback, 0, BlockSize);
  DecodeBuffer(TValueArr(Input), InputOffset, TValueArr(Output), OutputOffset, InputLen);
end;

function TSymmetricAlgorithm.CreateEncryptor: ICryptoTransform;
begin
  Result := nil;
  Assert(False);
end;

function TSymmetricAlgorithm.CreateDecryptor: ICryptoTransform; 
begin
  Result := nil;
  Assert(False);
end;

function TSymmetricAlgorithm.CreateEncryptor(const rgbKey, rgbIV: TBytes): ICryptoTransform;
begin
  Key := rgbKey;
  IV := rgbIV;

  Result := Self.CreateEncryptor;
end;

function TSymmetricAlgorithm.CreateDecryptor(const rgbKey, rgbIV: TBytes): ICryptoTransform;
begin
  Key := rgbKey;
  IV := rgbIV;

  Result := Self.CreateDecryptor;
end;

end.

