Browse Source

* Add SHA224 hash

Michaël Van Canneyt 1 year ago
parent
commit
487f7bf4ab
1 changed files with 394 additions and 30 deletions
  1. 394 30
      packages/fcl-hash/src/fpsha256.pp

+ 394 - 30
packages/fcl-hash/src/fpsha256.pp

@@ -29,16 +29,34 @@ uses
 {$ENDIF FPC_DOTTEDUNITS}
 
 Type
+  TContextBuffer = array[0..7] of UInt32;
+  PContextBuffer = ^TContextBuffer;
+
+  THashBuffer = array[0..63] of Byte;
+
+  TSHA256Base = record
+    Context: TContextBuffer;
+    HashBuffer : THashBuffer;
+    Index: UInt32;
+    TotalLength: Int64;
+    procedure Compress;
+    procedure Final;
+    procedure Init(Use224 : Boolean = False);
+    procedure Update(PBuf: PByte; Size: UInt32); overload;
+    procedure Update(const Value: TBytes); overload;
+{$IFDEF DEBUGSHA}
+  private
+    procedure DumpBuffer;
+    procedure DumpHash;
+{$ENDIF DEBUGSHA}
+  end;
+
   TSHA256Digest = packed array[0..31] of Byte;
   PSHA256Digest = ^TSHA256Digest;
   PSHA256 = ^TSHA256;
   TSHA256 = record
-    Context: array[0..7] of UInt32;
+    Base : TSHA256Base;
     Digest: TSHA256Digest;
-    HashBuffer: array[0..63] of Byte;
-    Index: UInt32;
-    TotalLength: Int64;
-    procedure Compress;
     procedure Final;
     procedure Init;
     function IsEqual(const ADigest: TSHA256Digest): Boolean;
@@ -70,8 +88,47 @@ Type
     class function HKDF(const Salt, IKM, Info: TBytes; var Output: TBytes; const DesiredLen: Integer): Boolean; static;
   end;
 
+  TSHA224Digest = packed array[0..27] of Byte;
+  PSHA224Digest = ^TSHA224Digest;
+  PSHA224 = ^TSHA224;
+  TSHA224 = record
+    Base : TSHA256Base;
+    Digest: TSHA224Digest;
+    procedure Final;
+    procedure Init;
+    function IsEqual(const ADigest: TSHA224Digest): Boolean;
+    procedure OutputHexa(out Result: AnsiString);
+    procedure Update(PBuf: PByte; Size: UInt32); overload;
+    procedure Update(const Value: TBytes); overload;
+
+    // Calculate SHA224, return digest as bytes.
+    class procedure DigestBytes(const Value: TBytes; out Result: TBytes) ; static;
+    // Calculate  SHA224, return digest as base64(url) string
+    class procedure DigestBase64(const Value: TBytes; const IsURL: Boolean; out Result: AnsiString); static;
+    // Calculate  SHA224, return digest as HEX encoded string
+    class procedure DigestHexa(const Value: TBytes; out Result: AnsiString); static;
+    // HMAC using SHA224 as hash
+    Class function HMAC(Key: PByte; KeySize: UInt32; Data: PByte; DataSize: UInt32; var aDigest: TSHA224Digest): Boolean; overload; static;
+    class function HMAC(Key: PByte; KeySize: UInt32; Data: PByte; DataSize: UInt32; Data2: PByte; DataSize2: UInt32; Data3: PByte; DataSize3: UInt32; var aDigest: TSHA224Digest): Boolean; overload; static;
+    // Calculate HMacSHA224, return digest as hex string.
+    class function HMACHexa(const Key, Data: TBytes; out SignatureHexa: AnsiString): Boolean; overload; static;
+    // Calculate SHA224 from a stream, return digest.
+    class procedure Stream(aStream: TStream; out aDigest: TSHA224Digest); static; overload;
+    class function Stream(aStream: TStream): TSHA224Digest; static; overload;
+    // Digest Stream, result as HexaDecimal string.
+    class procedure StreamHexa(aStream: TStream; out Result: AnsiString); static; overload;
+    class Function StreamHexa(aStream: TStream): AnsiString; static overload;
+    // Digest Stream, result as Base64-encoded string
+    class procedure StreamBase64(aStream: TStream; isURL : Boolean; out Result: AnsiString); static; overload;
+    class Function StreamBase64(aStream: TStream; isURL : Boolean): AnsiString; static; overload;
+    // HKDF : Derive key of desired length from a salt,input key and info  (RF5869, using HMACSHA224) .
+    class function HKDF(const Salt, IKM, Info: TBytes; var Output: TBytes; const DesiredLen: Integer): Boolean; static;
+  end;
+
+
 Const
   SHA256_DIGEST_SIZE = SizeOf(TSHA256Digest); // 32
+  SHA224_DIGEST_SIZE = SizeOf(TSHA224Digest); // 28
 
 implementation
 
@@ -82,25 +139,51 @@ uses fphashutils;
 {$ENDIF FPC_DOTTEDUNITS}
 
 //------------------------------------------------------------------------------
-// SHA256
+// SHA256Base
 //------------------------------------------------------------------------------
 
-procedure TSHA256.Init;
+const
+  Seed256Hash : TContextBuffer = (
+    $6a09e667,
+    $bb67ae85,
+    $3c6ef372,
+    $a54ff53a,
+    $510e527f,
+    $9b05688c,
+    $1f83d9ab,
+    $5be0cd19
+  );
+  Seed224Hash : TContextBuffer = (
+   $c1059ed8,
+   $367cd507,
+   $3070dd17,
+   $f70e5939,
+   $ffc00b31,
+   $68581511,
+   $64f98fa7,
+   $befa4fa4
+  );
+
+procedure TSHA256Base.Init(Use224 : Boolean = False);
+
+var
+  P : PContextBuffer;
+  I : Integer;
+
 begin
   Self.Index := 0;
   Self.TotalLength := 0;
   FillChar(Self.HashBuffer, Sizeof(Self.HashBuffer), 0);
-  Self.Context[0] := $6a09e667;
-  Self.Context[1] := $bb67ae85;
-  Self.Context[2] := $3c6ef372;
-  Self.Context[3] := $a54ff53a;
-  Self.Context[4] := $510e527f;
-  Self.Context[5] := $9b05688c;
-  Self.Context[6] := $1f83d9ab;
-  Self.Context[7] := $5be0cd19;
+  if Use224 then
+    P:=@Seed224Hash
+  else
+    P:=@Seed256Hash;
+  For I:=0 to 7 do
+    Context[i]:=P^[i];
 end;
 
-procedure TSHA256.Compress;
+
+procedure TSHA256Base.Compress;
 // Actual hashing function
 const
   K: array[0..63] of UInt32 = (
@@ -159,7 +242,7 @@ type
       1: (QuadPart: Int64);
   end;
 
-procedure TSHA256.Final;
+procedure TSHA256Base.Final;
 begin
   // 1. append bit '1' after Buffer
   HashBuffer[Self.Index] := $80;
@@ -183,21 +266,10 @@ begin
   Context[5] := SwapEndian(Context[5]);
   Context[6] := SwapEndian(Context[6]);
   Context[7] := SwapEndian(Context[7]);
-  Move(Context, Digest, Sizeof(Context));
 //  Self.Init; // uncomment if you need security protection against memory inspection
 end;
 
-
-function TSHA256.IsEqual(const ADigest: TSHA256Digest): Boolean;
-var
-  Left, Right: TBytes;
-begin
-  Left:=BytesFromVar(@ADigest, SizeOf(ADigest));
-  Right:=BytesFromVar(@Self.Digest, SizeOf(Self.Digest));
-  Result:=CompareMem(Pointer(Left), Pointer(Right),Length(Left));
-end;
-
-procedure TSHA256.Update(PBuf: PByte; Size: UInt32);
+procedure TSHA256Base.Update(PBuf: PByte; Size: UInt32);
 var
   Len: UInt32;
 begin
@@ -221,12 +293,55 @@ begin
   end;
 end;
 
+procedure TSHA256Base.Update(const Value: TBytes);
 
-procedure TSHA256.Update(const Value: TBytes);
 begin
   Update(PByte(Value), System.Length(Value));
 end;
 
+
+//------------------------------------------------------------------------------
+// SHA256
+//------------------------------------------------------------------------------
+
+
+procedure TSHA256.Init;
+begin
+  Base.Init(False);
+end;
+
+
+
+procedure TSHA256.Final;
+
+begin
+  Base.Final;
+  Move(Base.Context,Digest,Sizeof(Digest));
+end;
+
+function TSHA256.IsEqual(const ADigest: TSHA256Digest): Boolean;
+
+var
+  Left, Right: TBytes;
+begin
+  Left:=BytesFromVar(@ADigest, SizeOf(ADigest));
+  Right:=BytesFromVar(@Self.Digest, SizeOf(Self.Digest));
+  Result:=CompareMem(Pointer(Left), Pointer(Right),Length(Left));
+end;
+
+
+procedure TSHA256.Update(PBuf: PByte; Size: UInt32);
+
+begin
+  Base.Update(PBuf,Size);
+end;
+
+
+procedure TSHA256.Update(const Value: TBytes);
+begin
+  Base.Update(Value);
+end;
+
 // @Result[64]
 // 'abc' -> 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'
 procedure TSHA256.OutputHexa(out Result: AnsiString);
@@ -435,5 +550,254 @@ begin
   SetLength(Output,DesiredLen);
 end;
 
+//------------------------------------------------------------------------------
+// SHA224
+//------------------------------------------------------------------------------
+
+
+procedure TSHA224.Init;
+begin
+  Base.Init(True);
+end;
+
+procedure TSHA224.Final;
+
+begin
+  Base.Final;
+  Move(Base.Context,Digest,Sizeof(Digest));
+end;
+
+function TSHA224.IsEqual(const ADigest: TSHA224Digest): Boolean;
+
+var
+  Left, Right: TBytes;
+begin
+  Left:=BytesFromVar(@ADigest, SizeOf(ADigest));
+  Right:=BytesFromVar(@Self.Digest, SizeOf(Self.Digest));
+  Result:=CompareMem(Pointer(Left), Pointer(Right),Length(Left));
+end;
+
+
+procedure TSHA224.Update(PBuf: PByte; Size: UInt32);
+
+begin
+  Base.Update(PBuf,Size);
+end;
+
+
+procedure TSHA224.Update(const Value: TBytes);
+begin
+  Base.Update(Value);
+end;
+
+// @Result[64]
+// 'abc' -> 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'
+procedure TSHA224.OutputHexa(out Result: AnsiString);
+
+begin
+  BytesToHexStr(Result,PByte(@Self.Digest),SizeOf(Self.Digest));
+end;
+
+// @Result[32]
+class procedure TSHA224.DigestBytes(const Value: TBytes; out Result: TBytes);
+var
+  lSHA224: TSHA224;
+begin
+  lSHA224.Init;
+  lSHA224.Update(Value);
+  lSHA224.Final;
+  BytesFromVar(Result, @lSHA224.Digest[0], SizeOf(lSHA224.Digest));
+end;
+
+class procedure TSHA224.DigestBase64(const Value: TBytes; const IsURL: Boolean; out Result: AnsiString);
+var
+  S : TBytes;
+  lSHA224: TSHA224;
+begin
+  lSHA224.Init;
+  lSHA224.Update(Value);
+  lSHA224.Final;
+  BytesFromVar(S, @lSHA224.Digest[0], SizeOf(lSHA224.Digest));
+  BytesEncodeBase64(S, Result, IsURL, False, False);
+end;
+
+// @Result[64]
+Class procedure TSHA224.DigestHexa(const Value: TBytes; out Result: AnsiString);
+var
+  SHA224: TSHA224;
+begin
+  SHA224.Init;
+  SHA224.Update(Value);
+  SHA224.Final;
+  SHA224.OutputHexa(Result);
+end;
+
+class function TSHA224.HMAC(Key: PByte; KeySize: UInt32; Data: PByte; DataSize: UInt32; var aDigest: TSHA224Digest): Boolean;
+begin
+  Result := HMAC(Key, KeySize, Data, DataSize, nil, 0, nil, 0, aDigest);
+end;
+
+{Generate a SHA224 HMAC (Hashed Message Authentication Code) using the Key and Data
+The SHA224 HMAC algorithm is:
+ SHA224(Key xor oPad, SHA224(Key xor iPad, Data))
+ Where iPad is the byte $36 repeated 64 times
+       oPad is the byte $5c repeated 64 times
+ If Key is more than 64 bytes it will be hashed to Key = SHA224(Key) instead
+ If Key is less than 64 bytes it will be padded with zeros }
+ class function TSHA224.HMAC(Key: PByte; KeySize: UInt32; Data: PByte; DataSize: UInt32; Data2: PByte; DataSize2: UInt32; Data3: PByte; DataSize3: UInt32; var aDigest: TSHA224Digest): Boolean;
+
+Type
+  TBuf64 = array[0..63] of Byte;
+
+var
+  Count: UInt32;
+  KeyBuffer, PadBuffer: TBuf64;
+  SHA224, SHA224_: TSHA224;
+begin
+  Result:=False;
+  if Key = nil then
+    Exit;
+  if Data = nil then
+    Exit;
+  KeyBuffer:=Default(TBuf64);
+  SHA224.Init;
+  if KeySize > 64 then
+  begin
+    SHA224.Update(Key, KeySize);
+    SHA224.Final;
+    System.Move(SHA224.Digest[0], KeyBuffer[0], SizeOf(SHA224.Digest));
+  end else
+    System.Move(Key^, KeyBuffer[0], KeySize);
+  // XOR the key buffer with the iPad value
+  for Count := 0 to 63 do
+    PadBuffer[Count] := KeyBuffer[Count] xor $36;
+  SHA224.Init;
+  SHA224.Update(@PadBuffer, SizeOf(PadBuffer));
+  SHA224.Update(Data, DataSize);
+  if Data2 <> nil then
+    SHA224.Update(Data2, DataSize2);
+  if Data3 <> nil then
+    SHA224.Update(Data3, DataSize3);
+  SHA224.Final;
+  // XOR the key buffer with the oPad value
+  for Count := 0 to 63 do
+    PadBuffer[Count] := KeyBuffer[Count] xor $5C;
+  // SHA224 the key buffer and the result of the inner SHA224 (Outer)
+  SHA224_.Init;
+  SHA224_.Update(@PadBuffer, SizeOf(PadBuffer));
+  SHA224_.Update(@SHA224.Digest, SizeOf(SHA224.Digest));
+  SHA224_.Final;
+  System.Move(SHA224_.Digest, aDigest, SizeOf(aDigest));
+  Result:=True;
+end;
+
+// @Result[64]
+class function TSHA224.HMACHexa(const Key, Data: TBytes; out SignatureHexa: AnsiString): Boolean; overload;
+
+var
+  aDigest: TSHA224Digest;
+  S: TBytes;
+begin
+  aDigest:=Default(TSHA224Digest);
+  Result := HMAC(PByte(Key),Length(Key), PByte(Data), Length(Data), aDigest);
+  BytesFromVar(S, @aDigest[0], SizeOf(aDigest));
+  BytesToHexStr(SignatureHexa,S);
+end;
+
+
+class procedure TSHA224.Stream(aStream: TStream; out aDigest: TSHA224Digest);
+
+var
+  aLen : LongInt;
+  Buffer: array[0 .. 64*1024 - 1] of Byte;
+  SHA224: TSHA224;
+
+begin
+  SHA224.Init;
+  repeat
+     aLen:=aStream.Read(Buffer, Length(Buffer));
+     if aLen>0 then
+       SHA224.Update(PByte(Buffer),aLen);
+  until aLen=0;
+  SHA224.Final;
+  aDigest:=SHA224.Digest;
+end;
+
+class function TSHA224.Stream(aStream: TStream): TSHA224Digest;
+
+begin
+  Stream(aStream,Result);
+end;
+
+
+class procedure TSHA224.StreamHexa(aStream: TStream; out Result: AnsiString);
+
+Var
+  B : TBytes;
+  aDigest : TSHA224Digest;
+
+begin
+  Stream(aStream,aDigest);
+  BytesFromVar(B,@aDigest,SizeOf(TSHA224Digest));
+  BytesToHexStr(Result,B);
+end;
+
+class function TSHA224.StreamHexa(aStream: TStream): AnsiString;
+
+begin
+  Result:='';
+  StreamHexa(aStream,Result);
+end;
+
+
+class procedure TSHA224.StreamBase64(aStream: TStream; isURL : Boolean; out Result: AnsiString);
+
+Var
+  B : TBytes;
+  aDigest : TSHA224Digest;
+
+begin
+  Stream(aStream,aDigest);
+  BytesFromVar(B,@aDigest,SizeOf(TSHA224Digest));
+  BytesEncodeBase64(B,Result,isUrl,False,False);
+end;
+
+class Function TSHA224.StreamBase64(aStream: TStream; isURL : Boolean): AnsiString;
+
+begin
+  Result:='';
+  StreamBase64(aStream,isURL,Result);
+end;
+
+class function TSHA224.HKDF(const Salt, IKM, Info: TBytes; var Output: TBytes; const DesiredLen: Integer): Boolean;
+
+var
+  PRK, T: TSHA224Digest;
+  Round: Byte;
+
+begin
+  PRK:=Default(TSHA224Digest);
+  T:=Default(TSHA224Digest);
+  Result := HMAC(PByte(Salt), Length(Salt), PByte(IKM), Length(IKM), PRK);
+  if not Result then
+    Exit;
+  Round := 1;
+  while Length(Output) < DesiredLen do
+  begin
+    if Length(Output) = 0 then
+      Result := HMAC(@PRK, SizeOf(PRK), PByte(Info), Length(Info), @Round, SizeOf(Round), nil, 0, T)
+    else
+      Result := HMAC(@PRK, SizeOf(PRK), @T, SizeOf(T), PByte(Info), Length(Info), @Round, SizeOf(Round), T);
+    if not Result then
+      Exit;
+    Inc(Round);
+    Output:=Concat(OutPut,BytesFromVar(@T,SizeOf(T)));
+    if Length(Output) >= DesiredLen then
+      Break;
+  end;
+  SetLength(Output,DesiredLen);
+end;
+
+
 end.