|
@@ -0,0 +1,448 @@
|
|
|
|
+{
|
|
|
|
+ This file is part of the Free Component Library.
|
|
|
|
+ Copyright (c) 2021 by the Free Pascal team.
|
|
|
|
+
|
|
|
|
+ SHA256 and HMACSha256 routines.
|
|
|
|
+
|
|
|
|
+ See the file COPYING.FPC, included in this distribution,
|
|
|
|
+ for details about the copyright.
|
|
|
|
+
|
|
|
|
+ This program is distributed in the hope that it will be useful,
|
|
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
+}
|
|
|
|
+unit sha256;
|
|
|
|
+
|
|
|
|
+{$mode ObjFPC}{$H+}
|
|
|
|
+{$MODESWITCH advancedrecords}
|
|
|
|
+
|
|
|
|
+interface
|
|
|
|
+
|
|
|
|
+uses
|
|
|
|
+ Classes, SysUtils;
|
|
|
|
+
|
|
|
|
+Type
|
|
|
|
+ TSHA256Digest = packed array[0..31] of Byte;
|
|
|
|
+ PSHA256Digest = ^TSHA256Digest;
|
|
|
|
+ PSHA256 = ^TSHA256;
|
|
|
|
+ TSHA256 = record
|
|
|
|
+ Context: array[0..7] of UInt32;
|
|
|
|
+ Digest: TSHA256Digest;
|
|
|
|
+ HashBuffer: array[0..63] of Byte;
|
|
|
|
+ Index: UInt32;
|
|
|
|
+ Length: Int64;
|
|
|
|
+ procedure Compress;
|
|
|
|
+ procedure Final;
|
|
|
|
+ procedure Init;
|
|
|
|
+ function IsEqual(const ADigest: TSHA256Digest): Boolean;
|
|
|
|
+ procedure OutputHexa(out Result: AnsiString);
|
|
|
|
+ procedure Update(PBuf: PByte; Size: UInt32); overload;
|
|
|
|
+ procedure Update(const Value: TBytes); overload;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+Const
|
|
|
|
+ SHA256_DIGEST_SIZE = SizeOf(TSHA256Digest); // 32
|
|
|
|
+
|
|
|
|
+// Calculate SHA256, return digest as bytes.
|
|
|
|
+procedure SHA256(const Value: TBytes; out Result: TBytes);
|
|
|
|
+// Calculate SHA256, return digest as base64(url) string
|
|
|
|
+procedure SHA256Base64(const Value: TBytes; const IsURL: Boolean; out Result: AnsiString);
|
|
|
|
+// Calculate SHA256, return digest as HEX encoded string
|
|
|
|
+procedure SHA256Hexa(const Value: TBytes; out Result: AnsiString);
|
|
|
|
+
|
|
|
|
+// Calculate HMacSHA256, return digest as bytes.
|
|
|
|
+function HMACSHA256(Key: PByte; KeySize: UInt32; Data: PByte; DataSize: UInt32; var Digest: TSHA256Digest): Boolean; overload;
|
|
|
|
+function HMACSHA256(Key: PByte; KeySize: UInt32; Data: PByte; DataSize: UInt32; Data2: PByte; DataSize2: UInt32; Data3: PByte; DataSize3: UInt32; var Digest: TSHA256Digest): Boolean; overload;
|
|
|
|
+// Calculate HMacSHA256, return digest as hex string.
|
|
|
|
+function HMACSHA256Hexa(const Key, Data: TBytes; out SignatureHexa: AnsiString): Boolean; overload;
|
|
|
|
+
|
|
|
|
+// Calculate HMacSHA256 from a stream, return digest.
|
|
|
|
+procedure StreamSHA256(aStream: TStream; out Digest: TSHA256Digest);
|
|
|
|
+function StreamSHA256(aStream: TStream): TSHA256Digest;
|
|
|
|
+
|
|
|
|
+// Calculate HMacSHA256 from a stream, return digest as HEX-Encoded string.
|
|
|
|
+procedure StreamSHA256Hexa(aStream: TStream; out Result: AnsiString);
|
|
|
|
+Function StreamSHA256Hexa(aStream: TStream): AnsiString;
|
|
|
|
+
|
|
|
|
+// Calculate HMacSHA256 from a stream, return digest as base64-encoded string.
|
|
|
|
+procedure StreamSHA256Base64(aStream: TStream; isURL : Boolean; out Result: AnsiString);
|
|
|
|
+Function StreamSHA256Base64(aStream: TStream; isURL : Boolean): AnsiString;
|
|
|
|
+
|
|
|
|
+// HKDF : Derive key of desired length from a salt,input key and info (RF5869, using HMACSHA256) .
|
|
|
|
+function HKDF_SHA256(const Salt, IKM, Info: TBytes; var Output: TBytes; const DesiredLen: Integer): Boolean;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+implementation
|
|
|
|
+
|
|
|
|
+uses hashutils;
|
|
|
|
+
|
|
|
|
+//------------------------------------------------------------------------------
|
|
|
|
+// SHA256
|
|
|
|
+//------------------------------------------------------------------------------
|
|
|
|
+
|
|
|
|
+procedure TSHA256.Init;
|
|
|
|
+begin
|
|
|
|
+ Self.Index := 0;
|
|
|
|
+ Self.Length := 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;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TSHA256.Compress;
|
|
|
|
+// Actual hashing function
|
|
|
|
+const
|
|
|
|
+ K: array[0..63] of UInt32 = (
|
|
|
|
+ $428a2f98, $71374491, $b5c0fbcf, $e9b5dba5, $3956c25b, $59f111f1,
|
|
|
|
+ $923f82a4, $ab1c5ed5, $d807aa98, $12835b01, $243185be, $550c7dc3,
|
|
|
|
+ $72be5d74, $80deb1fe, $9bdc06a7, $c19bf174, $e49b69c1, $efbe4786,
|
|
|
|
+ $0fc19dc6, $240ca1cc, $2de92c6f, $4a7484aa, $5cb0a9dc, $76f988da,
|
|
|
|
+ $983e5152, $a831c66d, $b00327c8, $bf597fc7, $c6e00bf3, $d5a79147,
|
|
|
|
+ $06ca6351, $14292967, $27b70a85, $2e1b2138, $4d2c6dfc, $53380d13,
|
|
|
|
+ $650a7354, $766a0abb, $81c2c92e, $92722c85, $a2bfe8a1, $a81a664b,
|
|
|
|
+ $c24b8b70, $c76c51a3, $d192e819, $d6990624, $f40e3585, $106aa070,
|
|
|
|
+ $19a4c116, $1e376c08, $2748774c, $34b0bcb5, $391c0cb3, $4ed8aa4a,
|
|
|
|
+ $5b9cca4f, $682e6ff3, $748f82ee, $78a5636f, $84c87814, $8cc70208,
|
|
|
|
+ $90befffa, $a4506ceb, $bef9a3f7, $c67178f2);
|
|
|
|
+Type
|
|
|
|
+ TBuf64 = array[0..63] of UInt32;
|
|
|
|
+var
|
|
|
|
+ A, B, C, D, E, F, G, H: UInt32;
|
|
|
|
+ W: TBuf64;
|
|
|
|
+ I: UInt32;
|
|
|
|
+ t1, t2: UInt32;
|
|
|
|
+begin
|
|
|
|
+ w:=Default(TBuf64);
|
|
|
|
+ // Calculate "expanded message blocks"
|
|
|
|
+ Move(HashBuffer, W, Sizeof(HashBuffer));
|
|
|
|
+ for I := 0 to 15 do
|
|
|
|
+ W[I] := SwapEndian(W[I]);
|
|
|
|
+ for I := 16 to 63 do
|
|
|
|
+ W[I] := (((W[I-2] shr 17) or(W[I-2] shl 15)) xor ((W[I-2] shr 19) or (W[I-2] shl 13))
|
|
|
|
+ xor (W[I-2] shr 10))+W[I-7]+(((W[I-15] shr 7) or (W[I-15] shl 25))
|
|
|
|
+ xor ((W[I-15] shr 18) or (W[I-15] shl 14)) xor (W[I-15] shr 3))+W[I-16];
|
|
|
|
+ A := Context[0]; B := Context[1]; C := Context[2]; D := Context[3]; E := Context[4]; F := Context[5]; G := Context[6]; H := Context[7];
|
|
|
|
+
|
|
|
|
+ for I := 0 to High(W) do
|
|
|
|
+ begin
|
|
|
|
+ t1 := H+(((E shr 6) or (E shl 26)) xor ((E shr 11) or (E shl 21)) xor ((E shr 25) or (E shl 7)))+((E and F) xor (not E and G))+K[I]+W[I];
|
|
|
|
+ t2 := (((A shr 2) or (A shl 30)) xor ((A shr 13) or (A shl 19)) xor ((A shr 22) xor (A shl 10)))+((A and B) xor (A and C) xor (B and C));
|
|
|
|
+ H := G; G := F; F := E; E := D+t1;
|
|
|
|
+ D := C; C := B; B := A; A := t1+t2;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ Inc(Context[0], A);
|
|
|
|
+ Inc(Context[1], B);
|
|
|
|
+ Inc(Context[2], C);
|
|
|
|
+ Inc(Context[3], D);
|
|
|
|
+ Inc(Context[4], E);
|
|
|
|
+ Inc(Context[5], F);
|
|
|
|
+ Inc(Context[6], G);
|
|
|
|
+ Inc(Context[7], H);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+type
|
|
|
|
+ TInt64Rec = packed record
|
|
|
|
+ case Integer of
|
|
|
|
+ 0: (Lo, Hi: UInt32);
|
|
|
|
+ 1: (QuadPart: Int64);
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+procedure TSHA256.Final;
|
|
|
|
+begin
|
|
|
|
+ // 1. append bit '1' after Buffer
|
|
|
|
+ HashBuffer[Self.Index] := $80;
|
|
|
|
+ FillChar(HashBuffer[Self.Index+1], SizeOf(HashBuffer)-Self.Index-1, 0);
|
|
|
|
+ // 2. Compress if more than 448 bits, (no room for 64 bit length)
|
|
|
|
+ if Self.Index >= 56 then
|
|
|
|
+ begin
|
|
|
|
+ Compress;
|
|
|
|
+ FillChar(HashBuffer, SizeOf(HashBuffer), 0);
|
|
|
|
+ end;
|
|
|
|
+ // Write 64 bit Buffer length into the last bits of the last block
|
|
|
|
+ // (in big endian format) and do a final compress
|
|
|
|
+ PUInt32(@HashBuffer[56])^ := SwapEndian(TInt64Rec(Self.Length).Hi);
|
|
|
|
+ PUInt32(@HashBuffer[60])^ := SwapEndian(TInt64Rec(Self.Length).Lo);
|
|
|
|
+ Compress;
|
|
|
|
+ Context[0] := SwapEndian(Context[0]);
|
|
|
|
+ Context[1] := SwapEndian(Context[1]);
|
|
|
|
+ Context[2] := SwapEndian(Context[2]);
|
|
|
|
+ Context[3] := SwapEndian(Context[3]);
|
|
|
|
+ Context[4] := SwapEndian(Context[4]);
|
|
|
|
+ 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),System.Length(Left));
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TSHA256.Update(PBuf: PByte; Size: UInt32);
|
|
|
|
+var
|
|
|
|
+ Len: UInt32;
|
|
|
|
+begin
|
|
|
|
+ Inc(Self.Length, Int64(UInt32(Size)) shl 3);
|
|
|
|
+ while Size > 0 do
|
|
|
|
+ begin
|
|
|
|
+ if (Sizeof(HashBuffer)-Self.Index) <= UInt32(Size) then
|
|
|
|
+ begin
|
|
|
|
+ Len := Sizeof(HashBuffer)-Self.Index;
|
|
|
|
+ Move(PBuf^, HashBuffer[Self.Index], Len);
|
|
|
|
+ Dec(Size, Len);
|
|
|
|
+ Inc(PBuf, Len);
|
|
|
|
+ Compress;
|
|
|
|
+ Self.Index := 0;
|
|
|
|
+ end else
|
|
|
|
+ begin
|
|
|
|
+ Move(PBuf^, HashBuffer[Self.Index], Size);
|
|
|
|
+ Inc(Self.Index, Size);
|
|
|
|
+ Size := 0;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+(*
|
|
|
|
+procedure TSHA256.Update(const Buffer: TXBuffer);
|
|
|
|
+begin
|
|
|
|
+ Update(PByte(Buffer.Buf), Buffer.Size);
|
|
|
|
+end;
|
|
|
|
+*)
|
|
|
|
+
|
|
|
|
+procedure TSHA256.Update(const Value: TBytes);
|
|
|
|
+begin
|
|
|
|
+ Update(PByte(Value), System.Length(Value));
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+// @Result[64]
|
|
|
|
+// 'abc' -> 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'
|
|
|
|
+procedure TSHA256.OutputHexa(out Result: AnsiString);
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ BytesToHexStr(Result,PByte(@Self.Digest),SizeOf(Self.Digest));
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+// @Result[32]
|
|
|
|
+procedure SHA256(const Value: TBytes; out Result: TBytes);
|
|
|
|
+var
|
|
|
|
+ lSHA256: TSHA256;
|
|
|
|
+begin
|
|
|
|
+ lSHA256.Init;
|
|
|
|
+ lSHA256.Update(Value);
|
|
|
|
+ lSHA256.Final;
|
|
|
|
+ BytesFromVar(Result, @lSHA256.Digest[0], SizeOf(lSHA256.Digest));
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure SHA256Base64(const Value: TBytes; const IsURL: Boolean; out Result: AnsiString);
|
|
|
|
+var
|
|
|
|
+ S : TBytes;
|
|
|
|
+ lSHA256: TSHA256;
|
|
|
|
+begin
|
|
|
|
+ lSHA256.Init;
|
|
|
|
+ lSHA256.Update(Value);
|
|
|
|
+ lSHA256.Final;
|
|
|
|
+ BytesFromVar(S, @lSHA256.Digest[0], SizeOf(lSHA256.Digest));
|
|
|
|
+ BytesEncodeBase64(S, Result, IsURL, False, False);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+// @Result[64]
|
|
|
|
+procedure SHA256Hexa(const Value: TBytes; out Result: AnsiString);
|
|
|
|
+var
|
|
|
|
+ SHA256: TSHA256;
|
|
|
|
+begin
|
|
|
|
+ SHA256.Init;
|
|
|
|
+ SHA256.Update(Value);
|
|
|
|
+ SHA256.Final;
|
|
|
|
+ SHA256.OutputHexa(Result);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function HMACSHA256(Key: PByte; KeySize: UInt32; Data: PByte; DataSize: UInt32; var Digest: TSHA256Digest): Boolean;
|
|
|
|
+begin
|
|
|
|
+ Result := HMACSHA256(Key, KeySize, Data, DataSize, nil, 0, nil, 0, Digest);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+{Generate a SHA256 HMAC (Hashed Message Authentication Code) using the Key and Data
|
|
|
|
+The SHA256 HMAC algorithm is:
|
|
|
|
+ SHA256(Key xor oPad, SHA256(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 = SHA256(Key) instead
|
|
|
|
+ If Key is less than 64 bytes it will be padded with zeros }
|
|
|
|
+function HMACSHA256(Key: PByte; KeySize: UInt32; Data: PByte; DataSize: UInt32; Data2: PByte; DataSize2: UInt32; Data3: PByte; DataSize3: UInt32; var Digest: TSHA256Digest): Boolean;
|
|
|
|
+
|
|
|
|
+Type
|
|
|
|
+ TBuf64 = array[0..63] of Byte;
|
|
|
|
+
|
|
|
|
+var
|
|
|
|
+ Count: UInt32;
|
|
|
|
+ KeyBuffer, PadBuffer: TBuf64;
|
|
|
|
+ SHA256, SHA256_: TSHA256;
|
|
|
|
+begin
|
|
|
|
+ Result:=False;
|
|
|
|
+ if Key = nil then
|
|
|
|
+ Exit;
|
|
|
|
+ if Data = nil then
|
|
|
|
+ Exit;
|
|
|
|
+ KeyBuffer:=Default(TBuf64);
|
|
|
|
+ SHA256.Init;
|
|
|
|
+ if KeySize > 64 then
|
|
|
|
+ begin
|
|
|
|
+ SHA256.Update(Key, KeySize);
|
|
|
|
+ SHA256.Final;
|
|
|
|
+ System.Move(SHA256.Digest[0], KeyBuffer[0], SizeOf(SHA256.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;
|
|
|
|
+ SHA256.Init;
|
|
|
|
+ SHA256.Update(@PadBuffer, SizeOf(PadBuffer));
|
|
|
|
+ SHA256.Update(Data, DataSize);
|
|
|
|
+ if Data2 <> nil then
|
|
|
|
+ SHA256.Update(Data2, DataSize2);
|
|
|
|
+ if Data3 <> nil then
|
|
|
|
+ SHA256.Update(Data3, DataSize3);
|
|
|
|
+ SHA256.Final;
|
|
|
|
+ // XOR the key buffer with the oPad value
|
|
|
|
+ for Count := 0 to 63 do
|
|
|
|
+ PadBuffer[Count] := KeyBuffer[Count] xor $5C;
|
|
|
|
+ // SHA256 the key buffer and the result of the inner SHA256 (Outer)
|
|
|
|
+ SHA256_.Init;
|
|
|
|
+ SHA256_.Update(@PadBuffer, SizeOf(PadBuffer));
|
|
|
|
+ SHA256_.Update(@SHA256.Digest, SizeOf(SHA256.Digest));
|
|
|
|
+ SHA256_.Final;
|
|
|
|
+ System.Move(SHA256_.Digest, Digest, SizeOf(Digest));
|
|
|
|
+// FillChar(KeyDigest, SizeOf(TSHA1Digest),0);
|
|
|
|
+// FillChar(KeyBuffer, SizeOf(TSHA1ByteBuffer),0);
|
|
|
|
+// FillChar(PadBuffer, SizeOf(TSHA1ByteBuffer),0);
|
|
|
|
+ Result:=True;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+// @Result[64]
|
|
|
|
+function HMACSHA256Hexa(const Key, Data: TBytes; out SignatureHexa: AnsiString): Boolean; overload;
|
|
|
|
+var
|
|
|
|
+ Digest: TSHA256Digest;
|
|
|
|
+ S: TBytes;
|
|
|
|
+begin
|
|
|
|
+ Digest:=Default(TSHA256Digest);
|
|
|
|
+ Result := HMACSHA256(PByte(Key),Length(Key), PByte(Data), Length(Data), Digest);
|
|
|
|
+ BytesFromVar(S, @Digest[0], SizeOf(Digest));
|
|
|
|
+ BytesToHexStr(SignatureHexa,S);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+procedure StreamSHA256(aStream: TStream; out Digest: TSHA256Digest);
|
|
|
|
+
|
|
|
|
+const
|
|
|
|
+ BUFFER_SIZE = 64*1024;
|
|
|
|
+
|
|
|
|
+var
|
|
|
|
+ aLen : LongInt;
|
|
|
|
+ Buffer: TBytes;
|
|
|
|
+ SHA256: TSHA256;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ Buffer:=Nil;
|
|
|
|
+ SHA256.Init;
|
|
|
|
+ SetLength(Buffer,BUFFER_SIZE);
|
|
|
|
+ repeat
|
|
|
|
+ aLen:=aStream.Read(Buffer, BUFFER_SIZE);
|
|
|
|
+ if aLen = 0 then
|
|
|
|
+ Break;
|
|
|
|
+ SHA256.Update(PByte(Buffer),aLen);
|
|
|
|
+ until aLen=0;
|
|
|
|
+ SHA256.Final;
|
|
|
|
+ Digest:=SHA256.Digest;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function StreamSHA256(aStream: TStream): TSHA256Digest;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ StreamSHA256(aStream,Result);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+procedure StreamSHA256Hexa(aStream: TStream; out Result: AnsiString);
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ B : TBytes;
|
|
|
|
+ Digest : TSHA256Digest;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ StreamSHA256(aStream,Digest);
|
|
|
|
+ BytesFromVar(B,@Digest,SizeOf(TSHA256Digest));
|
|
|
|
+ BytesToHexStr(Result,B);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function StreamSHA256Hexa(aStream: TStream): AnsiString;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ Result:='';
|
|
|
|
+ StreamSHA256Hexa(aStream,Result);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+procedure StreamSHA256Base64(aStream: TStream; isURL : Boolean; out Result: AnsiString);
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ B : TBytes;
|
|
|
|
+ Digest : TSHA256Digest;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ StreamSHA256(aStream,Digest);
|
|
|
|
+ BytesFromVar(B,@Digest,SizeOf(TSHA256Digest));
|
|
|
|
+ BytesEncodeBase64(B,Result,isUrl,False,False);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+Function StreamSHA256Base64(aStream: TStream; isURL : Boolean): AnsiString;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ Result:='';
|
|
|
|
+ StreamSHA256Base64(aStream,isURL,Result);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function HKDF_SHA256(const Salt, IKM, Info: TBytes; var Output: TBytes; const DesiredLen: Integer): Boolean;
|
|
|
|
+
|
|
|
|
+var
|
|
|
|
+ PRK, T: TSHA256Digest;
|
|
|
|
+ Round: Byte;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ PRK:=Default(TSHA256Digest);
|
|
|
|
+ T:=Default(TSHA256Digest);
|
|
|
|
+ Result := HMACSHA256(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 := HMACSHA256(@PRK, SizeOf(PRK), PByte(Info), Length(Info), @Round, SizeOf(Round), nil, 0, T)
|
|
|
|
+ else
|
|
|
|
+ Result := HMACSHA256(@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.
|
|
|
|
+
|