{*******************************************************} { } { Tocsg.Hash } { } { Copyright (C) 2020 kku } { } {*******************************************************} unit Tocsg.Hash; interface uses // EM.SHA1, // 11.1 에서 제대로 동작 하지 않음 22_0419 08:29:50 kku Windows, SysUtils, System.Classes, EM.MD5, EM.Tocsg.Sha1, EM.Tocsg.hash, Tocsg.Obj; type THashStreamBase = class(TTgObject) protected bInit_: Boolean; public Constructor Create(bInit: Boolean = true); procedure WorkInit; virtual; abstract; procedure SetBuffer(buf: Pointer; nCount: Integer); virtual; abstract; function WorkFinalToStr: String; virtual; abstract; end; TSHA1Hash = class(THashStreamBase) private ctx_: TTgHashContext; // TSHA1Context; public procedure WorkInit; override; procedure SetBuffer(buf: Pointer; nCount: Integer); override; function WorkFinalToStr: String; override; function WorkFinalToDigest(var SHA1Digest: TSHA1Digest): Boolean; end; TSHA256Hash = class(THashStreamBase) private ctx_: TTgHashContext; // TSHA1Context; public procedure WorkInit; override; procedure SetBuffer(buf: Pointer; nCount: Integer); override; function WorkFinalToStr: String; override; function WorkFinalToDigest(var SHA256Digest: TSHA256Digest): Boolean; end; TMD5Hash = class(THashStreamBase) private ctx_: TMD5Context; public procedure WorkInit; override; procedure SetBuffer(buf: Pointer; nCount: Integer); override; function WorkFinalToStr: String; override; end; TTgHashProgress = reference to function(s: TStream; nPercent: Integer): Boolean; function GetStreamToSha1Str(aStream: TStream; aProgress: TTgHashProgress = nil): String; function GetFileToSha1Str(const sPath: String; aProgress: TTgHashProgress = nil): String; function GetStreamToHash(aStream: TStream; var sSHA1, sMD5, sSHA256: AnsiString; aProgress: TTgHashProgress = nil; llUntilSize: LONGLONG = 0): Boolean; function GetFileToHash(const sPath: String; var sSHA1, sMD5, sSHA256: AnsiString; aProgress: TTgHashProgress = nil; llUntilSize: LONGLONG = 0): Boolean; overload; function GetStreamToSha256(aStream: TStream; aProgress: TTgHashProgress = nil; llUntilSize: LONGLONG = 0): AnsiString; function GetFileToSha256(const sPath: String; aProgress: TTgHashProgress = nil; llUntilSize: LONGLONG = 0): AnsiString; function ConvStrToSha1A(str: AnsiString): String; function ConvStrToSha1A_Bin(str: AnsiString): TBytes; function ConvStrToSha1W(str: WideString): String; function ConvStrToSha256A_Bin(str: AnsiString): TBytes; function ConvStrToSha256A(str: AnsiString): String; function ConvStrToSha256W(str: WideString): String; function ConvStrToHash(str: String): DWORD; // By .. https://helloacm.com/simple-and-fast-hash-functions-in-delphi/ type HashFunction = function(aData: Pointer; aDataLength: Integer): DWORD; function Hash_djb2(aData: Pointer; aDataLength: Integer): NativeUInt; function Hash_djb2a(aData: Pointer; aDataLength: Integer): NativeUInt; function Hash_fnv(aData: Pointer; aDataLength: Integer): NativeUInt; function Hash_fnv1a(aData: Pointer; aDataLength: Integer): NativeUInt; function Hash_sdbm(aData: Pointer; aDataLength: Integer): NativeUInt; function Hash_jenkis(aData: Pointer; aDataLength: Integer): NativeUInt; implementation uses EM.CRC32, EM.Tocsg.sha256, Tocsg.Binary, Tocsg.Safe, Tocsg.Exception; const // BUF_SIZE = 65536; // 이거 쓰면 Range 오류남 25_0911 17:38:13 kku BUF_SIZE = 32768; function ConvStrToSha1A(str: AnsiString): String; var nRead : Integer; ctx: TTgHashContext; //TSHA1Context; sd: TSHA1Digest; begin try SHA1Init(ctx); nRead := Length(str); SHA1Update(ctx, @str[1], nRead); SHA1Final(ctx, sd); Result := ConvBytesToHexStr(PByte(@sd), SizeOf(sd)); finally end; end; function ConvStrToSha1A_Bin(str: AnsiString): TBytes; var nRead : Integer; ctx: TTgHashContext; //TSHA1Context; sd: TSHA1Digest; begin try SHA1Init(ctx); nRead := Length(str); SHA1Update(ctx, @str[1], nRead); SHA1Final(ctx, sd); SetLength(Result, Length(sd)); CopyMemory(Result, @sd[0], SizeOf(sd)); finally end; end; function ConvStrToSha1W(str: WideString): String; var nRead: Integer; ctx: TTgHashContext; //TSHA1Context; sd: TSHA1Digest; begin try if str = '' then begin Result := ''; exit; end; SHA1Init(ctx); nRead := Length(str); SHA1Update(ctx, @str[1], nRead*2); SHA1Final(ctx, sd); Result := ConvBytesToHexStr(PByte(@sd), SizeOf(sd)); except on E: Exception do ETgException.TraceException(E, 'Fail .. ConvStrToSha1W()'); end; end; function ConvStrToSha256A_Bin(str: AnsiString): TBytes; var nRead : Integer; ctx: TTgHashContext; sd: TSHA256Digest; begin try SHA256Init(ctx); nRead := Length(str); SHA256Update(ctx, @str[1], nRead); SHA256Final(ctx, sd); SetLength(Result, Length(sd)); CopyMemory(Result, @sd[0], SizeOf(sd)); finally end; end; function ConvStrToSha256A(str: AnsiString): String; var nRead: Integer; ctx: TTgHashContext; sd: TSHA256Digest; begin Result := ''; try if str = '' then exit; SHA256Init(ctx); nRead := Length(str); SHA256Update(ctx, @str[1], nRead); SHA256Final(ctx, sd); Result := ConvBytesToHexStr(PByte(@sd), SizeOf(sd)); except on E: Exception do ETgException.TraceException(E, 'Fail .. ConvStrToSha256A()'); end; end; function ConvStrToSha256W(str: WideString): String; var nRead: Integer; ctx: TTgHashContext; sd: TSHA256Digest; begin Result := ''; try if str = '' then exit; SHA256Init(ctx); nRead := Length(str); SHA256Update(ctx, @str[1], nRead*2); SHA256Final(ctx, sd); Result := ConvBytesToHexStr(PByte(@sd), SizeOf(sd)); except on E: Exception do ETgException.TraceException(E, 'Fail .. ConvStrToSha256W()'); end; end; function GetStreamToSha1Str(aStream: TStream; aProgress: TTgHashProgress = nil): String; var nRead: Integer; pBuf: array [0..BUF_SIZE] of AnsiChar; ctx: TTgHashContext; //TSHA1Context; sd: TSHA1Digest; begin Result := ''; SHA1Init(ctx); Repeat nRead := aStream.Read(pBuf, BUF_SIZE); SHA1Update(ctx, @pBuf, nRead); if Assigned(aProgress) then if not aProgress(aStream, (aStream.Position * 100) div aStream.Size) then exit; Until nRead <> BUF_SIZE; SHA1Final(ctx, sd); Result := ConvBytesToHexStr(PByte(@sd), SizeOf(sd)); end; function GetFileToSha1Str(const sPath: String; aProgress: TTgHashProgress = nil): String; var fs: TFileStream; begin Result := ''; try if FileExists(sPath) then begin Guard(fs, TFileStream.Create(sPath, fmOpenRead or fmShareDenyNone)); Result := GetStreamToSha1Str(fs, aProgress); end; except exit; end; end; function GetFileToHash(const sPath: String; var sSHA1, sMD5, sSHA256: AnsiString; aProgress: TTgHashProgress = nil; llUntilSize: LONGLONG = 0): Boolean; var fs: TFileStream; begin try Guard(fs, TFileStream.Create(sPath, fmOpenRead or fmShareDenyNone)); Result := GetStreamToHash(fs, sSHA1, sMD5, sSHA256, aProgress, llUntilSize); except Result := false; exit; end; end; function GetStreamToHash(aStream: TStream; var sSHA1, sMD5, sSHA256: AnsiString; aProgress: TTgHashProgress = nil; llUntilSize: LONGLONG = 0): Boolean; var nRead: Integer; pBuf: array [0..BUF_SIZE-1] of AnsiChar; SHA1Ctx: TTgHashContext; //TSHA1Context; MD5Ctx: TMD5Context; SHA256Ctx: TTgHashContext; sd: TSHA1Digest; md: EM.MD5.TMD5Digest; s256d: TSHA256Digest; llTotalRead: LONGLONG; begin Result := false; if aStream.Size > 0 then begin llTotalRead := 0; sSHA1 := ''; sMD5 := ''; sSHA256 := ''; MD5Init(MD5Ctx); SHA1Init(SHA1Ctx); SHA256Init(SHA256Ctx); Repeat nRead := aStream.Read(pBuf, BUF_SIZE); MD5Update(MD5Ctx, @pBuf, nRead); SHA1Update(SHA1Ctx, @pBuf, nRead); SHA256Update(SHA256Ctx, @pBuf, nRead); if Assigned(aProgress) then if not aProgress(aStream, (aStream.Position * 100) div aStream.Size) then exit; Inc(llTotalRead, nRead); if (llUntilSize > 0) and (llUntilSize <= llTotalRead) then // 추가 15_0921 11:43:31 kku break; Until (nRead <> BUF_SIZE) or (aStream.Size <= llTotalRead); MD5Final(MD5Ctx, md); SHA1Final(SHA1Ctx, sd); SHA256Final(SHA256Ctx, s256d); sMD5 := ConvBytesToHexStr(PByte(@md), SizeOf(md)); sSHA1 := ConvBytesToHexStr(PByte(@sd), SizeOf(sd)); sSHA256 := ConvBytesToHexStr(PByte(@s256d), SizeOf(s256d)); Result := true; end; end; function GetStreamToSha256(aStream: TStream; aProgress: TTgHashProgress = nil; llUntilSize: LONGLONG = 0): AnsiString; //const // BUF_SIZE2 = 32768; // BUF_SIZE(65536) 사용하면 Range 오류난다 25_0728 18:20:37 kku var nRead: Integer; pBuf: array [0..BUF_SIZE-1] of AnsiChar; SHA256Ctx: TTgHashContext; s256d: TSHA256Digest; llTotalRead: LONGLONG; begin Result := ''; if aStream.Size > 0 then begin llTotalRead := 0; SHA256Init(SHA256Ctx); Repeat nRead := aStream.Read(pBuf, BUF_SIZE); SHA256Update(SHA256Ctx, @pBuf, nRead); if Assigned(aProgress) then if not aProgress(aStream, (aStream.Position * 100) div aStream.Size) then exit; Inc(llTotalRead, nRead); if (llUntilSize > 0) and (llUntilSize <= llTotalRead) then // 추가 15_0921 11:43:31 kku break; Until (nRead <> BUF_SIZE) or (aStream.Size <= llTotalRead); SHA256Final(SHA256Ctx, s256d); Result := ConvBytesToHexStr(PByte(@s256d), SizeOf(s256d)); end; end; function GetFileToSha256(const sPath: String; aProgress: TTgHashProgress = nil; llUntilSize: LONGLONG = 0): AnsiString; var fs: TFileStream; begin try Guard(fs, TFileStream.Create(sPath, fmOpenRead or fmShareDenyNone)); Result := GetStreamToSha256(fs, aProgress, llUntilSize); except Result := ''; exit; end; end; // IniFiles.pas 에서 TStringHash.HashOf 가져옴 2010-11-04 kku function ConvStrToHash(str: String): DWORD; var i: Integer; begin Result := 0; for i := 1 to Length(str) do Result := ((Result shl 2) or (Result shr (SizeOf(Result) * 8 - 2))) xor Ord(str[i]); end; { THashStreamBase } Constructor THashStreamBase.Create(bInit: Boolean = true); begin Inherited Create; bInit_ := false; if bInit then WorkInit; end; { TSHA1Hash } procedure TSHA1Hash.WorkInit; begin SHA1Init(ctx_); bInit_ := true; end; procedure TSHA1Hash.SetBuffer(buf: Pointer; nCount: Integer); begin if bInit_ then SHA1Update(ctx_, buf, nCount); end; function TSHA1Hash.WorkFinalToStr: String; var sd : TSHA1Digest; begin Result := ''; if bInit_ then begin SHA1Final(ctx_, sd); Result := ConvBytesToHexStr(PByte(@sd), SizeOf(sd)); bInit_ := false; end; end; function TSHA1Hash.WorkFinalToDigest(var SHA1Digest: TSHA1Digest): Boolean; begin Result := false; ZeroMemory(@SHA1Digest, SizeOf(SHA1Digest)); if bInit_ then begin SHA1Final(ctx_, SHA1Digest); bInit_ := false; Result := true; end; end; { TSHA256Hash } procedure TSHA256Hash.WorkInit; begin SHA256Init(ctx_); bInit_ := true; end; procedure TSHA256Hash.SetBuffer(buf: Pointer; nCount: Integer); begin if bInit_ then SHA256Update(ctx_, buf, nCount); end; function TSHA256Hash.WorkFinalToStr: String; var sd : TSHA256Digest; begin Result := ''; if bInit_ then begin SHA256Final(ctx_, sd); Result := ConvBytesToHexStr(PByte(@sd), SizeOf(sd)); bInit_ := false; end; end; function TSHA256Hash.WorkFinalToDigest(var SHA256Digest: TSHA256Digest): Boolean; begin Result := false; ZeroMemory(@SHA256Digest, SizeOf(SHA256Digest)); if bInit_ then begin SHA256Final(ctx_, SHA256Digest); bInit_ := false; Result := true; end; end; { TMD5Hash } procedure TMD5Hash.SetBuffer(buf: Pointer; nCount: Integer); begin if bInit_ then MD5Update(ctx_, buf, nCount); end; function TMD5Hash.WorkFinalToStr: String; var sd : EM.MD5.TMD5Digest; begin Result := ''; if bInit_ then begin MD5Final(ctx_, sd); Result := ConvBytesToHexStr(PByte(@sd), SizeOf(sd)); bInit_ := false; end; end; procedure TMD5Hash.WorkInit; begin MD5Init(ctx_); bInit_ := true; end; // By .. https://helloacm.com/simple-and-fast-hash-functions-in-delphi/ // HASH_DJB2 function Hash_djb2(aData: Pointer; aDataLength: Integer): NativeUInt; var i: integer; begin Result := 5381; for i := 1 to aDataLength do begin Result := ((Result shl 5) + Result) + PByte(aData)^; aData := Pointer(NativeUInt(aData) + 1); end; end; // HASH_DJB2A // A Slight variation of Hash_djb2 function Hash_djb2a(aData: Pointer; aDataLength: Integer): NativeUInt; var i: integer; begin Result := 5381; for i := 1 to aDataLength do begin Result := ((Result shl 5) xor Result) xor PByte(aData)^; aData := Pointer(NativeUInt(aData) + 1); end; end; // HASH_FNV function Hash_fnv(aData: Pointer; aDataLength: Integer): NativeUInt; var i: integer; begin Result := 2166136261; for i := 1 to aDataLength do begin Result := (Result * 16777619) xor PByte(aData)^; aData := Pointer(NativeUInt(aData) + 1); end; end; // HASH_FNV1A // Slight variation of Hash_fnv. function Hash_fnv1a(aData: Pointer; aDataLength: Integer): NativeUInt; var i: integer; begin Result := 2166136261; for i := 1 to aDataLength do begin Result := (Result xor PByte(aData)^) * 16777619; aData := Pointer(NativeUInt(aData) + 1); end; end; // HASH_SDBM function Hash_sdbm(aData: Pointer; aDataLength: Integer): NativeUInt; var i: integer; begin Result := 0; for i := 1 to aDataLength do begin Result := PByte(aData)^ + (Result shl 6) + (Result shl 16) - Result; aData := Pointer(NativeUInt(aData) + 1); end; end; // HASH_JENKIS function Hash_jenkis(aData: Pointer; aDataLength: Integer): NativeUInt; var i: integer; begin Result := 0; for i := 1 to aDataLength do begin Inc(Result, PByte(aData)^); Inc(Result, Result shl 10); Result := Result xor (Result shr 6); aData := Pointer(NativeUInt(aData) + 1); end; Inc(Result, Result shl 3); Result := Result xor (Result shr 11); Inc(Result, Result shl 15); end; function StrToHash_FNV1A(sSrc: String): NativeUInt; var nLen: Integer; begin nLen := Length(sSrc); if nLen = 0 then begin Result := 0; exit; end; Result := Hash_fnv1a(@sSrc[1], nLen); end; end.