{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 (DEC Part I)
                 Delphi 2-4, BCB 3-4

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

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

type
  THashAlgorithmClass = class of THashAlgorithm;

 {the Base-Class of all Hashs}
  THashAlgorithm = class(TProtection, ICryptoTransform)
  protected
    function Get_CanReuseTransform: Boolean;
    function Get_CanTransformMultipleBlocks: Boolean;
    function Get_InputBlockSize: Integer;
    function Get_OutputBlockSize: Integer;

  protected
    HashBlock: TBytes;
    State: Integer;

    procedure CodeInit(Action: TPAction); override; {TProtection Methods, You can use a Hash to En/Decode}
    procedure CodeDone(Action: TPAction); override; {TProtection Methods}
    procedure Protect(IsInit: Boolean); {calls any installed Protection}
    procedure HashCore(rgb: IntPtr; cbSize: Integer); overload; virtual;
    procedure HashCore(const rgb: TBytes; ibStart, cbSize: Integer); overload; virtual; abstract;
    function HashFinal: TBytes; virtual; abstract;
    function GetHashSize: Integer; virtual;

  public
    constructor Create; virtual;
    procedure Init; virtual;
    procedure Calc(Data: IntPtr; DataSize: Integer); overload; virtual; {must override}
    procedure Calc(const Data: TBytes; Offset: Integer; DataSize: Integer); overload; virtual; {must override}
    procedure Done; virtual;
    function DigestKey: TIntArray; virtual; {must override}
    class function DigestKeySize: Integer; virtual; {must override}
    procedure Initialize; virtual; abstract;
    procedure TransformBlock(InputBuffer: IntPtr; InputCount: Integer); overload; virtual;
    procedure TransformBlock(const InputBuffer: TBytes; InputOffset, InputCount: Integer); overload; virtual;
    function TransformBlock(const InputBuffer: TBytes; InputOffset, InputCount: Integer; var OutputBuffer: TBytes; OutputOffset: Integer): Integer; overload; virtual;
    procedure TransformFinalBlock(const InputBuffer: TBytes; InputOffset, InputCount: Integer); virtual;
    function ComputeHash(const Buffer: TBytes; offset, count: Integer): TBytes; overload;
    function ComputeHash(const Buffer: TBytes): TBytes; overload;

    property HashSize: Integer read GetHashSize;
    property Hash: TBytes read HashBlock;
  end;

implementation

uses
{$IFDEF MSWINDOWS}
  Windows;
{$ELSE}
  CRFunctions;
{$ENDIF}

constructor THashAlgorithm.Create;
begin
  inherited Create(nil);
end;

procedure THashAlgorithm.Init;
begin
  Protect(True);
end;

procedure THashAlgorithm.Calc(Data: IntPtr; DataSize: Integer);
begin
  Assert(False);
end;

procedure THashAlgorithm.Calc(const Data: TBytes; Offset: Integer; DataSize: Integer);
begin
end;

procedure THashAlgorithm.Protect(IsInit: Boolean);
var
  buf: TValueArr;
begin
  if Protection <> nil then
    if not IsInit then begin
    {$IFDEF CLR}
      SetLength(buf, DigestKeySize);
      Buffer.BlockCopy(DigestKey, 0, buf, 0, DigestKeySize);
    {$ELSE}
      buf := Pointer(DigestKey);
    {$ENDIF}

      Protection.CodeBuffer(buf, DigestKeySize, paScramble);

    {$IFDEF CLR}
      Buffer.BlockCopy(buf, 0, DigestKey, 0, DigestKeySize);
    {$ENDIF}
    end;
end;

procedure THashAlgorithm.Done;
begin
  Protect(False);
end;

function THashAlgorithm.DigestKey: TIntArray;
begin
  Result := nil;
end;

class function THashAlgorithm.DigestKeySize: Integer;
begin
  Result := 0;
end;

procedure THashAlgorithm.CodeInit(Action: TPAction);
begin
  Init;
  if Action = paWipe then
    RndXORBufferL(Integer(GetTickCount), DigestKey, DigestKeySize);
  inherited CodeInit(Action);
end;

procedure THashAlgorithm.CodeDone(Action: TPAction);
begin
  inherited CodeDone(Action);
  if (Action <> paCalc) then Init else Done;
end;

procedure THashAlgorithm.TransformBlock(InputBuffer: IntPtr; InputCount: Integer);
begin
  State := 1;
  HashCore(InputBuffer, InputCount);
end;

procedure THashAlgorithm.HashCore(rgb: IntPtr; cbSize: Integer);
begin
  Assert(False);
end;

procedure THashAlgorithm.TransformBlock(const InputBuffer: TBytes; InputOffset, InputCount: Integer);
begin
  State := 1;
  HashCore(InputBuffer, InputOffset, InputCount);
end;

function THashAlgorithm.TransformBlock(const InputBuffer: TBytes; InputOffset, InputCount: Integer; var OutputBuffer: TBytes; OutputOffset: Integer): Integer;
begin
  State := 1;
  HashCore(InputBuffer, InputOffset, InputCount);
  if (InputBuffer <> OutputBuffer) or (InputOffset <> OutputOffset) then
    Buffer.BlockCopy(InputBuffer, InputOffset, OutputBuffer, OutputOffset, InputCount);
  Result := InputCount;
end;

procedure THashAlgorithm.TransformFinalBlock(const InputBuffer: TBytes; InputOffset, InputCount: Integer);
begin
  HashCore(InputBuffer, InputOffset, InputCount);
  HashBlock := HashFinal;
//  SetLength(Result, InputCount);
//  Buffer.BlockCopy(InputBuffer, InputOffset, Result, 0, InputCount);
  State := 0;
end;

function THashAlgorithm.ComputeHash(const Buffer: TBytes): TBytes;
begin
  HashCore(Buffer, 0, Length(Buffer));
  HashBlock := HashFinal;
  Initialize;
  Result := HashBlock;
end;

function THashAlgorithm.ComputeHash(const Buffer: TBytes; offset, count: Integer): TBytes;
begin
  HashCore(Buffer, offset, count);
  HashBlock := HashFinal;
  Initialize;
  Result := HashBlock;
end;

function THashAlgorithm.Get_CanReuseTransform: Boolean;
begin
  Result := True;
end;

function THashAlgorithm.Get_CanTransformMultipleBlocks: Boolean;
begin
  Result := True;
end;

function THashAlgorithm.Get_InputBlockSize: Integer;
begin
  Result := 1;
end;

function THashAlgorithm.Get_OutputBlockSize: Integer;
begin
  Result := 1;
end;

function THashAlgorithm.GetHashSize: Integer;
begin
  Result := DigestKeySize * 8;
end;

end.

