unit IdHashIntf; interface {$i IdCompilerDefines.inc} uses Classes, IdFIPS, IdGlobal, IdHash, {$IFDEF DOTNET} System.Security.Cryptography, IdException {$ELSE} IdStreamVCL {$ENDIF} ; type TIdHashInt = class(TIdHash) protected function HashToHex(const AHash: TIdBytes): String; override; function GetHashInst : TIdHashInst; virtual; abstract; function InitHash : TIdHashIntCtx; virtual; procedure UpdateHash(ACtx : TIdHashIntCtx; const AIn : TIdBytes); function FinalHash(ACtx : TIdHashIntCtx) : TIdBytes; function GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override; {$IFNDEF DOTNET} public class function IsAvailable : Boolean; override; {$ENDIF} end; TIdHashSHA224 = class(TIdHashInt) protected function GetHashInst : TIdHashInst; override; {$IFNDEF DOTNET} public class function IsAvailable : Boolean; override; {$ENDIF} end; TIdHashSHA256 = class(TIdHashInt) protected function GetHashInst : TIdHashInst; override; {$IFNDEF DOTNET} public class function IsAvailable : Boolean; override; {$ENDIF} end; TIdHashSHA386 = class(TIdHashInt) protected function GetHashInst : TIdHashInst; override; {$IFNDEF DOTNET} public class function IsAvailable : Boolean; override; {$ENDIF} end; TIdHashSHA512 = class(TIdHashInt) protected function GetHashInst : TIdHashInst; override; {$IFNDEF DOTNET} public class function IsAvailable : Boolean; override; {$ENDIF} end; {$IFDEF DOTNET} EIdSecurityAPIException = class(EIdException); EIdSHA224NotSupported = class(EIdSecurityAPIException); {$ELSE} EIdDigestError = class(EIdOpenSSLAPICryptoError); EIdDigestFinalEx = class(EIdDigestError); EIdDigestInitEx = class(EIdDigestError); EIdDigestUpdate = class(EIdDigestError); {$ENDIF} implementation {$IFNDEF DOTNET} uses IdCTypes; {$ENDIF} { TIdHashInt } function TIdHashInt.FinalHash(ACtx: TIdHashIntCtx): TIdBytes; var {$IFDEF DOTNET} LDummy : TIdBytes; {$ELSE} LLen, LRet : TIdC_UInt; {$ENDIF} begin {$IFDEF DOTNET} //This is a funny way of coding. I have to pass a dummy value to //TransformFinalBlock so that things can work similarly to the OpenSSL //Crypto API. You can't pass nul to TransformFinalBlock without an exception. SetLength(LDummy,0); ACtx.TransformFinalBlock(LDummy,0,0); Result := ACtx.Hash; {$ELSE} SetLength(Result,OPENSSL_EVP_MAX_MD_SIZE); LRet := IdSslEvpDigestFinalEx(@ACtx,@Result[0],LLen); if LRet <> 1 then begin EIdDigestFinalEx.RaiseException('EVP_DigestFinal_ex error'); end; SetLength(Result,LLen); IdSslEvpMDCtxCleanup(@ACtx); {$ENDIF} end; function TIdHashInt.GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; var LBuf : TIdBytes; LSize : Int64; LCtx : TIdHashIntCtx; begin LCtx := InitHash; try SetLength(LBuf,2048); repeat LSize := ReadTIdBytesFromStream(AStream,LBuf,2048); if LSize = 0 then begin break; end; if LSize < 2048 then begin SetLength(LBuf,LSize); UpdateHash(LCtx,LBuf); break; end else begin UpdateHash(LCtx,LBuf); end; until False; finally Result := FinalHash(LCtx); end; end; function TIdHashInt.HashToHex(const AHash: TIdBytes): String; begin Result := ToHex(AHash); end; function TIdHashInt.InitHash: TIdHashIntCtx; {$IFNDEF DOTNET} var LHash : TIdHashInst; LRet : TIdC_Int; {$ENDIF} begin {$IFDEF DOTNET} Result := GetHashInst; {$ELSE} LHash := GetHashInst; IdSslEvpMDCtxInit(@Result); LRet := IdSslEvpDigestInitEx(@Result, LHash, nil); if LRet <> 1 then begin EIdDigestInitEx.RaiseException('EVP_DigestInit_ex error'); end; {$ENDIF} end; {$IFNDEF DOTNET} class function TIdHashInt.IsAvailable: Boolean; begin Result := Assigned(IdSslEvpDigestInitEx) and Assigned(IdSslEvpDigestUpdate) and Assigned(IdSslEvpDigestFinalEx); end; {$ENDIF} procedure TIdHashInt.UpdateHash(ACtx: TIdHashIntCtx; const AIn: TIdBytes); {$IFNDEF DOTNET} var LRet : TIdC_Int; {$ENDIF} begin {$IFDEF DOTNET} ACtx.TransformBlock(AIn,0,Length(AIn),AIn,0); {$ELSE} LRet := IdSslEvpDigestUpdate(@ACtx,@Ain[0],Length(AIn)); if LRet <> 1 then begin EIdDigestInitEx.RaiseException('EVP_DigestUpdate error'); end; {$ENDIF} end; { TIdHashSHA224 } function TIdHashSHA224.GetHashInst: TIdHashInst; begin {$IFDEF DOTNET} Result := nil; Raise EIdSHA224NotSupported.Create('SHA224 not supported.'); {$ELSE} Result := IdSslEvpSHA224; {$ENDIF} end; {$IFNDEF DOTNET} class function TIdHashSHA224.IsAvailable: Boolean; begin Result := Assigned(IdSslEvpSHA224) and inherited IsAvailable; end; {$ENDIF} { TIdHashSHA256 } function TIdHashSHA256.GetHashInst: TIdHashInst; begin {$IFDEF DOTNET} Result := System.Security.Cryptography.SHA256Managed.Create; {$ELSE} Result := IdSslEvpSHA256; {$ENDIF} end; {$IFNDEF DOTNET} class function TIdHashSHA256.IsAvailable: Boolean; begin Result := Assigned(IdSslEvpSHA256) and inherited IsAvailable; end; {$ENDIF} { TIdHashSHA386 } function TIdHashSHA386.GetHashInst: TIdHashInst; begin {$IFDEF DOTNET} Result := System.Security.Cryptography.SHA384Managed.Create; {$ELSE} Result := IdSslEvpSHA384; {$ENDIF} end; {$IFNDEF DOTNET} class function TIdHashSHA386.IsAvailable: Boolean; begin Result := Assigned(IdSslEvpSHA384) and inherited IsAvailable; end; {$ENDIF} { TIdHashSHA512 } function TIdHashSHA512.GetHashInst: TIdHashInst; begin {$IFDEF DOTNET} Result := System.Security.Cryptography.SHA512Managed.Create; {$ELSE} Result := IdSslEvpSHA512; {$ENDIF} end; {$IFNDEF DOTNET} class function TIdHashSHA512.IsAvailable: Boolean; begin Result := Assigned(IdSslEvpSHA512) and inherited IsAvailable; end; {$ENDIF} end.