Commits

Snake Doctor committed 2d017e6

Added test implementation for Keccak in Delphi. DOESN'T WORK!

Comments (0)

Files changed (2)

+program CryptoKeccak;
+
+{$APPTYPE CONSOLE}
+
+uses
+  Windows,
+  SysUtils,
+  unitKeccak in 'unitKeccak.pas';
+
+const
+  HexTable: array[0..15] of AnsiChar = '0123456789ABCDEF';
+
+procedure PrintData( title: string; val: PByte; size: integer );
+var
+  i, j: UInt32;
+  data: PUInt8Array absolute val;
+begin
+  write( title );
+
+  if size > 0 then
+    for i := 0 to size-1 do
+    begin
+      Write( HexTable[ (Ord(data[i]) AND $F0) SHR 4 ], HexTable[ Ord(data[i]) AND $F ], ' ' );
+
+      if (i <> 0) AND ((1+i) mod 16 = 0) then
+      begin
+        writeln;
+        for j := 1 to Length(title) do
+          write( ' ' );
+      end;
+    end;
+  writeln;
+end;
+
+procedure HashString_224A( val: AnsiString );
+var
+  ctx: TSpongeState;
+  HashVal: array[0..27] of byte;
+begin
+  ZeroMemory( @ctx, sizeof(ctx) );
+  Assert( Keccak_Init( @ctx, 224 ) = SUCCESS );
+  if length(val) > 0 then
+  begin
+    Assert( Keccak_Update( @ctx, @val[1], length(val) ) = SUCCESS );
+  end;
+  Assert( Keccak_Final( @ctx, @HashVal ) = SUCCESS );
+
+  if Length(val) > 0 then
+    PrintData( 'Input:  ', @val[1], length(val) )
+  else
+    PrintData( 'Input:  ', nil, 0 );
+  PrintData( 'Hash:   ', @HashVal, SizeOf(HashVal) );
+  writeln; writeln; writeln;
+
+  readln;
+end;
+
+procedure HashString_256A( val: AnsiString );
+var
+  ctx: TSpongeState;
+  HashVal: array[0..31] of byte;
+begin
+  ZeroMemory( @ctx, sizeof(ctx) );
+  Assert( Keccak_Init( @ctx, 256 ) = SUCCESS );
+  if length(val) > 0 then
+  begin
+    Assert( Keccak_Update( @ctx, @val[1], length(val) ) = SUCCESS );
+  end;
+  Assert( Keccak_Final( @ctx, @HashVal ) = SUCCESS );
+
+  if Length(val) > 0 then
+    PrintData( 'Input:  ', @val[1], length(val) )
+  else
+    PrintData( 'Input:  ', nil, 0 );
+  PrintData( 'Hash:   ', @HashVal, SizeOf(HashVal) );
+  writeln; writeln; writeln;
+
+  readln;
+end;
+
+procedure HashString_384A( val: AnsiString );
+var
+  ctx: TSpongeState;
+  HashVal: array[0..47] of byte;
+begin
+  ZeroMemory( @ctx, sizeof(ctx) );
+  Assert( Keccak_Init( @ctx, 384 ) = SUCCESS );
+  if length(val) > 0 then
+  begin
+    Assert( Keccak_Update( @ctx, @val[1], length(val) ) = SUCCESS );
+  end;
+  Assert( Keccak_Final( @ctx, @HashVal ) = SUCCESS );
+
+  if Length(val) > 0 then
+    PrintData( 'Input:  ', @val[1], length(val) )
+  else
+    PrintData( 'Input:  ', nil, 0 );
+  PrintData( 'Hash:   ', @HashVal, SizeOf(HashVal) );
+  writeln; writeln; writeln;
+
+  readln;
+end;
+
+procedure HashString_512A( val: AnsiString );
+var
+  ctx: TSpongeState;
+  HashVal: array[0..63] of byte;
+begin
+  ZeroMemory( @ctx, sizeof(ctx) );
+  Assert( Keccak_Init( @ctx, 512 ) = SUCCESS );
+  if length(val) > 0 then
+  begin
+    Assert( Keccak_Update( @ctx, @val[1], length(val) ) = SUCCESS );
+  end;
+  Assert( Keccak_Final( @ctx, @HashVal ) = SUCCESS );
+
+  if Length(val) > 0 then
+    PrintData( 'Input:  ', @val[1], length(val) )
+  else
+    PrintData( 'Input:  ', nil, 0 );
+  PrintData( 'Hash:   ', @HashVal, SizeOf(HashVal) );
+  writeln; writeln; writeln;
+
+  readln;
+end;
+
+
+var
+  vals: array[0..3] of AnsiString = (
+  	   '',
+       #$C0,
+       #$CC,
+       #$4A#$4F#$20#$24#$84#$51#$25#$26
+    );
+{  vals: array[0..3] of AnsiString = (
+       #$FF,
+       #$FF#$FE#$FD#$FC#$FB#$FA#$F9#$F8#$F7#$F6#$F5#$F4#$F3#$F2#$F1#$F0#$EF#$EE#$ED#$EC#$EB#$EA#$E9#$E8#$E7#$E6#$E5#$E4#$E3#$E2#$E1#$E0,
+       #$FF#$FE#$FD#$FC#$FB#$FA#$F9#$F8#$F7#$F6#$F5#$F4#$F3#$F2#$F1#$F0#$EF#$EE#$ED#$EC#$EB#$EA#$E9#$E8#$E7#$E6#$E5#$E4#$E3#$E2#$E1#$E0#$DF#$DE#$DD#$DC#$DB#$DA#$D9#$D8#$D7#$D6#$D5#$D4#$D3#$D2#$D1#$D0#$CF#$CE#$CD#$CC#$CB#$CA#$C9#$C8#$C7#$C6#$C5#$C4#$C3#$C2#$C1#$C0,
+       ''
+    );
+//}
+
+
+//  fncs: array[0..1] of procedure( val: AnsiString ) = ( HashString_256A, HashString_256B{, HashString_512} );
+  fncs: array[0..0] of procedure( val: AnsiString ) = ( HashString_256A );
+
+var
+  i, j: UInt32;
+
+begin
+  for i := low(fncs) to high(fncs) do
+  begin
+    for j := low(vals) to high(vals) do
+    begin
+      fncs[i]( vals[j] );
+    end;
+
+    writeln;
+    writeln('----------');
+    writeln; writeln;
+
+  end;
+
+   readln;
+end.
+unit unitKeccak;
+interface
+uses
+  windows, sysutils, classes;
+
+{$REGION 'Generic Types'}
+type
+  size_t = NativeUInt;
+
+  PInt8  = ^Int8;
+  PInt16 = ^Int16;
+  PInt32 = ^Int32;
+  PInt64 = ^Int64;
+
+  PUInt8  = ^UInt8;
+  PUInt16 = ^UInt16;
+  PUInt32 = ^UInt32;
+  PUInt64 = ^UInt64;
+
+  TUInt8Array  = array[0..(MAXDWORD div 4 )-1] of UInt8;
+  TUInt16Array = array[0..(MAXDWORD div 8 )-1] of UInt16;
+  TUInt32Array = array[0..(MAXDWORD div 16 )-1] of UInt32;
+  TUInt64Array = array[0..(MAXDWORD div 32)-1] of UInt64;
+
+  TDynUInt8 = record
+  	case Integer of
+		0: (Val: UInt8);
+        1: (Arr: TUInt8Array);
+  end;
+
+  TDynUInt16 = record
+  	case Integer of
+		0: (Val: UInt16);
+        1: (Arr: TUInt16Array);
+  end;
+
+  TDynUInt32 = record
+  	case Integer of
+		0: (Val: UInt32);
+        1: (Arr: TUInt32Array);
+  end;
+
+  TDynUInt64 = record
+  	case Integer of
+		0: (Val: UInt64);
+        1: (ArrUInt64: TUInt64Array);
+  end;
+
+  TDynUInt64_ = record
+  	case Integer of
+        0: (ValUInt8:  UInt8);
+        1: (ValUInt16: UInt16);
+        2: (ValUInt32: UInt32);
+        3: (ValUInt64: UInt64);
+        4: (ArrUInt8:  TUInt8Array);
+        5: (ArrUInt16: TUInt16Array);
+        6: (ArrUInt32: TUInt32Array);
+        7: (ArrUInt64: TUInt64Array);
+  end;
+
+  PUInt8Array  = ^TUInt8Array;
+  PUInt16Array = ^TUInt16Array;
+  PUInt32Array = ^TUInt32Array;
+  PUInt64Array = ^TUInt64Array;
+
+  PDynUInt8  = ^TDynUInt8;
+  PDynUInt16 = ^TDynUInt16;
+  PDynUInt32 = ^TDynUInt32;
+  PDynUInt64 = ^TDynUInt64;
+{$ENDREGION}
+
+{$REGION 'Keccak Types'}
+const
+	KeccakPermutationSize = 1600;
+	KeccakPermutationSizeInBytes = (KeccakPermutationSize div 8);
+	KeccakMaximumRate = 1536;
+	KeccakMaximumRateInBytes = (KeccakMaximumRate div 8);
+	nrRounds = 24;
+    nrLanes = 25;
+
+type
+	EHashReturn = (SUCCESS = 0, FAIL = 1, BAD_HASHLEN = 2);
+
+	TSpongeState = record
+        state: array[0..KeccakPermutationSizeInBytes-1] of UInt8;
+        dataQueue: array[0..KeccakMaximumRateInBytes-1] of UInt8;
+        rate: UInt32;
+        capacity: UInt32;
+        bitsInQueue: UInt32;
+        fixedOutputLength: UInt32;
+        squeezing: UInt8;
+        bitsAvailableForSqueezing: UInt32;
+	end;
+    PSpongeState = ^TSpongeState;
+{$ENDREGION}
+{$REGION 'Keccak Internal Variables'}
+var
+	KeccakRoundConstants: array[0..nrRounds-1] of UInt64;
+	KeccakRhoOffsets: array[0..nrLanes-1] of UInt32;
+{$ENDREGION}
+
+function InitSponge(ctx: PSpongeState; rate: UInt32; capacity: UInt32): integer;
+procedure AbsorbQueue(ctx: PSpongeState);
+function Absorb(ctx: PSpongeState; const data: PUInt8Array; databitlen: UInt32): integer;
+procedure PadAndSwitchToSqueezingPhase(ctx: PSpongeState);
+function Squeeze(ctx: PSpongeState; output: PUInt8Array; outputLength: UInt32): integer;
+
+procedure KeccakPermutation( state: PUInt8Array );
+procedure KeccakPermutationAfterXor( state: PUInt8Array; const data: PUInt8Array; dataLengthInBytes: UInt32 );
+procedure KeccakPermutationOnWords( state: PUInt64Array );
+procedure KeccakInitializeRoundConstants;
+procedure KeccakInitializeRhoOffsets;
+procedure KeccakInitialize;
+procedure KeccakInitializeState(state: PUInt8Array);
+procedure KeccakInitializeDataQueue(state: PUInt8Array);
+procedure KeccakAbsorb(state: PUInt8Array; const data: PUInt8Array; laneCount: UInt32);
+procedure KeccakExtract(state: PUInt8Array; data: PUInt8Array; laneCount: UInt32);
+
+function Keccak_Init(state: PSpongeState; hashbitlen: integer): EHashReturn;
+function Keccak_Update(state: PSpongeState; const data: Pointer; databitlen: UInt64): EHashReturn;
+function Keccak_Final(state: PSpongeState; hashval: Pointer): EHashReturn;
+
+implementation
+
+function index(x, y: UInt32): UInt32; inline;
+begin
+    Result := (x mod 5) + 5*(y mod 5);
+end;
+
+function ROL64(a: UInt64; offset: Byte): UInt64; inline;
+begin
+	Result := a;
+	if offset <> 0 then
+    begin
+		Result := (a SHL offset) XOR (a SHR (64-offset));
+    end;
+end;
+
+
+{$REGION 'Sponge Functions'}
+function InitSponge;
+begin
+    if (rate+capacity) <> 1600 then
+    begin
+        Result := 1;
+        exit;
+    end;
+
+    if (rate <= 0) OR (rate >= 1600) OR ((rate mod 64) <> 0) then
+    begin
+        Result := 1;
+        exit;
+    end;
+
+    KeccakInitialize();
+	ctx.rate := rate;
+    ctx.capacity := capacity;
+    ctx.fixedOutputLength := 0;
+    KeccakInitializeState( @ctx.state[0] );
+    KeccakInitializeDataQueue( @ctx.dataQueue[0] );
+    ctx.bitsInQueue := 0;
+    ctx.squeezing := 0;
+    ctx.bitsAvailableForSqueezing := 0;
+
+    Result := 0;
+end;
+
+procedure AbsorbQueue;
+begin
+    // state->bitsInQueue is assumed to be equal to state->rate
+    KeccakAbsorb( PUInt8Array(@ctx.state[0]), PUInt8Array(@ctx.dataQueue[0]), ctx.rate div 64);
+    ctx.bitsInQueue := 0;
+end;
+
+function Absorb;
+var
+	i, j, wholeBlocks: UInt64;
+    partialBlock, partialByte: UInt32;
+    curData: PUint8Array;
+    mask: UInt8;
+begin
+    Result := 0;
+    if (ctx.bitsInQueue mod 8) <> 0 then
+    begin
+        Result := 1; // Only the last call may contain a partial byte
+        exit;
+    end;
+    if ctx.squeezing <> 0 then
+    begin
+        Result := 1; // Too late for additional input
+        exit;
+    end;
+
+    i := 0;
+    while(i < databitlen) do
+    begin
+        if ((ctx.bitsInQueue = 0) AND (databitlen >= ctx.rate) AND (i <= (databitlen - ctx.rate))) then
+        begin
+            wholeBlocks := (databitlen-i) div ctx.rate;
+            curData := @data[i div 8];
+
+            //for j := 0 to wholeBlocks-1 do
+            j := 0;
+            while j < wholeBlocks do
+            begin
+            	KeccakAbsorb(@ctx.state[0], curData, ctx.rate div 64);
+
+                j := j + 1;
+                curData := @curData[ctx.rate div 8];
+            end;
+
+            i := i + (wholeBlocks * ctx.rate);
+        end else
+        begin
+            partialBlock := databitlen - i;
+            if (partialBlock + ctx.bitsInQueue) > ctx.rate then
+                partialBlock := ctx.rate - ctx.bitsInQueue;
+
+            partialByte := partialBlock mod 8;
+            partialBlock := partialBlock - partialByte;
+
+            //memcpy(ctx.dataQueue + (ctx.bitsInQueue div 8), data+ (i div 8), partialBlock div 8);
+            CopyMemory( @ctx.dataQueue[ctx.bitsInQueue div 8], @data[i div 8], partialBlock div 8 );
+
+            ctx.bitsInQueue := ctx.bitsInQueue + partialBlock;
+            i := i + partialBlock;
+            if ctx.bitsInQueue = ctx.rate then
+                AbsorbQueue(ctx);
+            if partialByte > 0 then
+            begin
+                mask := (1 SHL partialByte)-1;
+                ctx.dataQueue[ctx.bitsInQueue div 8] := data[i div 8] AND mask;
+                ctx.bitsInQueue := ctx.bitsInQueue + partialByte;
+                i := i + partialByte;
+            end;
+        end;
+    end;
+end;
+
+procedure PadAndSwitchToSqueezingPhase;
+begin
+    // Note: the bits are numbered from 0=LSB to 7=MSB
+    if ctx.bitsInQueue + 1 = ctx.rate then
+    begin
+        ctx.dataQueue[ctx.bitsInQueue div 8] := ctx.dataQueue[ctx.bitsInQueue div 8] OR (1 SHL (ctx.bitsInQueue mod 8));
+        AbsorbQueue( ctx );
+        //memset(state->dataQueue, 0, state->rate/8);
+        FillMemory( @ctx.dataQueue[0], ctx.rate div 8, 0 );
+    end else
+    begin
+        //memset(state->dataQueue + (state->bitsInQueue+7)/8, 0, state->rate/8 - (state->bitsInQueue+7)/8);
+        FillMemory( @ctx.dataQueue[(ctx.bitsInQueue+7) div 8], ctx.rate div 8 - (ctx.bitsInQueue+7) div 8, 0 );
+
+        ctx.dataQueue[ctx.bitsInQueue div 8] := ctx.dataQueue[ctx.bitsInQueue div 8] OR (1 SHL (ctx.bitsInQueue mod 8));
+    end;
+
+    ctx.dataQueue[(ctx.rate-1) div 8] := ctx.dataQueue[(ctx.rate-1) div 8] OR (1 SHL ((ctx.rate-1) mod 8));
+    AbsorbQueue( ctx );
+
+    KeccakExtract( @ctx.state[0], @ctx.dataQueue[0], ctx.rate div 64);
+    ctx.bitsAvailableForSqueezing := ctx.rate;
+
+    ctx.squeezing := 1;
+end;
+
+function Squeeze;
+var
+	i: UInt64;
+	partialBlock: UInt32;
+begin
+    if ctx.squeezing = 0 then
+        PadAndSwitchToSqueezingPhase( ctx );
+
+    if (outputLength mod 8) <> 0 then
+    begin
+        Result := 1; // Only multiple of 8 bits are allowed, truncation can be done at user level
+        exit;
+    end;
+
+    i := 0;
+    while i < outputLength do
+    begin
+        if ctx.bitsAvailableForSqueezing = 0 then
+        begin
+            KeccakPermutation(@ctx.state[0]);
+            KeccakExtract(@ctx.state[0], @ctx.dataQueue[0], ctx.rate div 64);
+            ctx.bitsAvailableForSqueezing := ctx.rate;
+        end;
+        partialBlock := ctx.bitsAvailableForSqueezing;
+        if partialBlock > outputLength - i then
+            partialBlock := outputLength - i;
+
+        //memcpy(output+i/8, state->dataQueue+(state->rate-state->bitsAvailableForSqueezing)/8, partialBlock/8);
+        CopyMemory( @output[i div 8], @ctx.dataQueue[(ctx.rate-ctx.bitsAvailableForSqueezing) div 8], partialBlock div 8);
+        ctx.bitsAvailableForSqueezing := ctx.bitsAvailableForSqueezing - partialBlock;
+        i := i + partialBlock;
+    end;
+    Result := 0;
+end;
+{$ENDREGION}
+
+{$REGION 'KeccakF-1600 Functions'}
+procedure KeccakPermutation;
+begin
+{#if (PLATFORM_BYTE_ORDER != IS_LITTLE_ENDIAN)
+    UINT64 stateAsWords[KeccakPermutationSize/64];
+#endif
+
+#if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN)}
+
+    KeccakPermutationOnWords( PUInt64Array(state) );
+
+{#else
+    fromBytesToWords(stateAsWords, state);
+    KeccakPermutationOnWords(stateAsWords);
+    fromWordsToBytes(state, stateAsWords);
+#endif }
+end;
+
+procedure KeccakPermutationAfterXor;
+var
+	i: UInt32;
+begin
+    for i := 0 to dataLengthInBytes-1 do
+    begin
+        state[i] := state[i] xor data[i];
+    end;
+    KeccakPermutation(state);
+end;
+
+procedure theta(A: PUInt64Array);
+var
+	x,y: UInt32;
+    C, D: array[0..4] of UInt64;
+begin
+    for x := 0 to 4 do
+    begin
+        C[x] := 0;
+        for y := 0 to 4 do
+            C[x] := C[x] xor A[index(x, y)];
+    end;
+
+    for x := 0 to 4 do
+        D[x] := ROL64(C[(x+1) mod 5], 1) xor C[(x+4) mod 5];
+
+    for x := 0 to 4 do
+        for y := 0 to 4 do
+            A[index(x, y)] := A[index(x, y)] xor D[x];
+end;
+
+procedure rho(A: PUInt64Array);
+var
+	x,y: UInt32;
+begin
+    for x := 0 to 4 do
+    	for y := 0 to 4 do
+        	A[index(x, y)] := ROL64(A[index(x, y)], KeccakRhoOffsets[index(x, y)]);
+end;
+
+procedure pi(A: PUInt64Array);
+var
+	x,y: UInt32;
+    tempA: array[0..24] of UInt64;
+begin
+    for x := 0 to 4 do
+    	for y :=0 to 4 do
+        	tempA[index(x, y)] := A[index(x, y)];
+
+    for x := 0 to 4 do
+    	for y :=0 to 4 do
+        	A[index(0*x+1*y, 2*x+3*y)] := tempA[index(x, y)];
+end;
+
+procedure chi(A: PUInt64Array);
+var
+	x,y: UInt32;
+    C: array[0..4] of UInt64;
+begin
+    for y := 0 to 4 do
+    begin
+        for x := 0 to 4 do
+            C[x] := A[index(x, y)] xor ((NOT A[index(x+1, y)]) AND A[index(x+2, y)]);
+
+        for x := 0 to 4 do
+            A[index(x, y)] := C[x];
+    end;
+end;
+
+procedure iota(A: PUInt64Array; indexRound: UInt32);
+begin
+    A[index(0, 0)] := A[index(0, 0)] xor KeccakRoundConstants[indexRound];
+end;
+
+
+procedure KeccakPermutationOnWords;
+var
+	i: UInt32;
+begin
+    for i := 0 to nrRounds-1 do
+    begin
+        theta(state);
+        rho(state);
+        pi(state);
+        chi(state);
+        iota(state, i);
+    end;
+end;
+
+function LFSR86540(LFSR: PDynUInt8): boolean;
+begin
+    Result := (LFSR.Val AND 1) <> 0;
+    if ((LFSR.Val AND $80) <> 0) then
+    begin
+        // Primitive polynomial over GF(2): x^8+x^6+x^5+x^4+1
+        LFSR.Val := (LFSR.Val SHL 1) xor $71;
+    end else
+    begin
+        LFSR.Val := LFSR.Val SHL 1;
+    end;
+end;
+
+procedure KeccakInitializeRoundConstants;
+var
+	LFSRstate: UInt8;
+    i, j, bitPosition: UInt32;
+begin
+    LFSRstate := $01;
+
+    for i := 0 to nrRounds-1 do
+    begin
+        KeccakRoundConstants[i] := 0;
+        for j := 0 to 7-1 do
+		begin
+            bitPosition := (1 SHL j)-1; //2^j-1
+            if LFSR86540(@LFSRstate) then
+            begin
+                KeccakRoundConstants[i] := KeccakRoundConstants[i] XOR (1 SHL bitPosition);
+            end;
+        end;
+	end;
+end;
+
+procedure KeccakInitializeRhoOffsets;
+var
+	x, y, t, newX, newY: UInt32;
+begin
+	KeccakRhoOffsets[index(0, 0)] := 0;
+    x := 1;
+	y := 0;
+	for t :=0 to 24-1 do
+    begin
+        KeccakRhoOffsets[index(x, y)] := ((t+1)*(t+2) div 2) mod 64;
+        newX := (0*x+1*y) mod 5;
+		newY := (2*x+3*y) mod 5;
+        x := newX;
+		y := newY;
+    end;
+end;
+
+procedure KeccakInitialize;
+begin
+	KeccakInitializeRoundConstants;
+	KeccakInitializeRhoOffsets;
+end;
+
+procedure KeccakInitializeState;
+begin
+    FillMemory(state, KeccakPermutationSizeInBytes, 0);
+end;
+
+procedure KeccakInitializeDataQueue;
+begin
+    FillMemory(state, KeccakMaximumRateInBytes, 0);
+end;
+
+procedure KeccakAbsorb;
+begin
+    KeccakPermutationAfterXor(state, data, laneCount*8);
+end;
+
+procedure KeccakExtract;
+begin
+    CopyMemory( @data[0], @state[0], laneCount*8 );
+end;
+{$ENDREGION}
+
+{$REGION 'NIST Interface Wrappers'}
+function Keccak_Init;
+begin
+	Result := SUCCESS;
+    case hashbitlen of
+        0: begin // Default parameters, arbitrary length output
+            InitSponge(state, 1024, 576);
+        end;
+
+        224: begin
+            InitSponge(state, 1152, 448);
+        end;
+
+        256: begin
+            InitSponge(state, 1088, 512);
+        end;
+
+        384: begin
+            InitSponge(state, 832, 768);
+        end;
+
+        512: begin
+			InitSponge(state, 576, 1024);
+        end
+
+        else
+            Result := BAD_HASHLEN;
+    end;
+    state.fixedOutputLength := hashbitlen;
+end;
+
+function Keccak_Update;
+var
+	lastByte: UInt8;
+begin
+    if (databitlen mod 8) = 0 then
+    begin
+        Result := EHashReturn( Absorb(state, data, databitlen) );
+    end else
+    begin
+        Result := EHashReturn( Absorb(state, data, databitlen - (databitlen mod 8)) );
+        if Result = SUCCESS then
+        begin
+            // Align the last partial byte to the least significant bits
+            lastByte := PUInt8Array(data)[databitlen div 8] SHR (8 - (databitlen mod 8));
+            Result := EHashReturn( Absorb(state, @lastByte, databitlen mod 8) );
+        end;
+    end;
+end;
+
+function Keccak_Final;
+begin
+    Result := EHashReturn( Squeeze(state, hashval, state.fixedOutputLength) );
+end;
+{$ENDREGION}
+
+initialization
+  //..
+finalization
+  //..
+end.