;; 
;;=====================================================================================----- 
;; 
;;FUNCTION	CheckSum() 
;; 
;;AUTHOR	Glenn Barnas 
;; 
;;ACTION	Generates MD5, SHA-1, SHA-384, SHA-256 & SHA-512 hashes from string or file 
;;              Uses either Dot-Net, CertUtil, CmdHashGen 
;; 
;;VERSION	2.0 - 2019-03-25 
;; 
;;HISTORY	1.0 - 2016-08-28 - Initial Release 
;;              1.1 - 2016-10-10 - update to support either CertUtil or CmdHashGen 
;;              1.2 - 2019-01-21 - Eliminate use of WSHPipe - causes screen pop-up with KF GUI 
;;              2.0 - 2019-03-25 - Rewrite to try multiple methods, starting with built-in, then 
;;                                 standard CertUtil, then add-on CmdHashGen utility 
;; 
;;SYNTAX:	CheckSum(SrcData [, Type] [, InputType] [, CPath]) 
;; 
;;PARAMETERS:	SrcData		(Required)	The filespec or string to be hashed 
;;		Type		(Optional)	The type of Hash you want to generate (defaults to MD5) 
;;		InputType	(Optional)	True forces a string hash instead of a file hash 
;;              CPath    	(Optional)	Path to CmdHashGen, default is found by PATH variable. 
;; 
;;REMARKS       Incorporates CheckSumK() logic by Arend Pronk but falls back to alternate utilities if DotNET 3.5 
;;              is not installed. 
;; 
;;RETURNS:	The hash generated by the specified type. If error, return will be empty and error set to: 
;;               Error 1  - failed to generate a checksum, returns zero 
;;               Error 2  - Source file not found 
;;               Error 87 - Invalid argument, either file or hash type 
;; 
;;DEPENDENCIES:	The SHA utility must exist in the system PATH. CertUtil.exe is the default. 
;; 
;;EXAMPLES: 
;; 
;;		'   MD5: ' + CheckSum('C:\Kix32.exe') ? 
;; 		'   MD5: ' + CheckSum('Hello', , 1) ? 
;; 		'  SHA1: ' + CheckSum('C:\Kix32.exe', 'SHA1') ? 
;; 		'  SHA1: ' + CheckSum('Hello', 'SHA1', 1) ? 
;; 		'SHA256: ' + CheckSum('C:\Kix32.exe', 'SHA256') ? 
;; 		'SHA256: ' + CheckSum('Hello', 'SHA256', 1) ? 
;; 		'SHA384: ' + CheckSum('C:\Kix32.exe', 'SHA384') ? 
;; 		'SHA384: ' + CheckSum('Hello', 'SHA384', 1) ? 
;; 		'SHA512: ' + CheckSum('C:\Kix32.exe', 'SHA512') ? 
;; 		'SHA512: ' + CheckSum('Hello', 'SHA512', 1') ? 
;;              ; 
;;              ; Special form - hash of a file path and not file content! 
;; 		'SHA256: ' + CheckSum('C:\Kix32.exe', 'SHA256', 'String') ? 
; 
; 
Function CheckSum($_Input, OPTIONAL $_HashType, OPTIONAL $_InputType, OPTIONAL $_CPath)
 
  Dim $_Rc							; Return Code Catcher 
  Dim $_oXML							;  
  Dim $_oXEL							;  
  Dim $_oHash							; object reference 
  Dim $_oStream							; file datastream object 
  Dim $_BinaryFile						; incremental read 
  Dim $_FinalBlock						; File to checksum 
  Dim $_I							; index pointer 
  Dim $_							; Temp Var 
  Dim $_Fh							; File Handle 
  Dim $_FData							; File Data 
  Dim $_TFile, $_WFile						; temp files 
  Dim $_Exit							; exit status 
  Dim $_Cmd							; command and args 
 
  $_Exit = 0
 
  If NOT Exist($_Input) And Not $_InputType			; Check for the source file if not testing for a string 
    $CheckSum = 0
    Exit 2 							; The system cannot find the file specified. 
  EndIf
 
  If $_InputType						; String Check - write it to the temp file 
    $_TFile = '%TEMP%\HashIt_' + @PID + '_' + Right('0000' + @MSECS, 3) + '.tmp'
    $_WFile = $_TFile + '.out'					; working file name 
    $_ = RedirectOutput($_TFile, 1)				; write string to source file name 
    $_Input
    $_ = RedirectOutput('')
  Else
    $_TFile = $_Input						; Source file name 
    $_WFile = $_TFile + '.out'					; working file name 
  EndIf
 
  If Not $_HashType $_HashType = 'MD5' EndIf			; Set default hash type to MD5 if not specified 
 
  ; verify the defined hash type is valid 
  If Not InStr('~MD5~SHA1~SHA256~SHA384~SHA512~', '~' + $_HashType + '~')
    $CheckSum = 0
    Exit 87							; exit if not a valid type 
  EndIf
 
  If $_CPath							; if CPath is provided 
    If Right($_CPath, 1) <> '\'
      $_CPath = $_CPath + '\'					; make sure it ends with "\" 
    EndIf
  EndIf
 
  ; ================================================================================ 
  ; Start with the built-in method - only if DotNET 3.5 is installed 
  ; ================================================================================ 
 
  If KeyExist('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5')
 
    ; set the system security descriptor string 
    If $_HashType = 'MD5' Or $_HashType = 'SHA1'
      $_ = 'System.Security.Cryptography.' + $_HashType + 'CryptoServiceProvider'
    Else
      $_ = 'System.Security.Cryptography.' + $_HashType + 'Managed'
    EndIf
 
    $_oHash = CreateObject($_)					; instantiate the object 
    If Not @ERROR						; proceed if successful 
      $_oHash.Initialize
 
      $_oStream = CreateObject('ADODB.Stream')			; open the source 
      $_oStream.Type = 1
      $_oStream.Open
      $_oStream.LoadFromFile($_TFile)
 
      If Not @ERROR						; loaded from the source file - proceed 
 
        If $_oStream.Size > 209715200				; handle large files in blocks 
          For $_I = 0 to $_oStream.Size Step 31744
            $_BinaryFile = $_oStream.Read(31744)
            $_Rc = $_oHash.TransformBlock($_BinaryFile, 0, 31744, $_BinaryFile, 0)
            If ($_oStream.Size -$_I) <= 31744
              $_FinalBlock = ($_oStream.Size -$_I)
              $_Rc = $_oHash.TransformFinalBlock($_BinaryFile, 0, $_FinalBlock)
            EndIf
          Next
        Else
          $_BinaryFile = $_oStream.Read
          $_Rc = $_oHash.ComputeHash_2($_BinaryFile)
        EndIf
        $_oStream.Close						; close and terminate the stream object 
        $_oStream = ''
 
        $_oXML = CreateObject('MSXML2.DOMDocument')		; process the checksum data 
        $_oXEL = $_oXML.CreateElement('tmp')
        $_oXEL.DataType = 'bin.hex'
        $_oXEL.NodeTypedValue = $_oHash.Hash
 
        $CheckSum = $_oXEL.Text
 
        If $_InputType
          Del $_TFile						; Delete the source file if string processing 
        EndIf
        Del $_WFile						; delete the temp work file 
        Exit 0							; return success 
 
      EndIf
    EndIf
  EndIf
 
  ; ================================================================================ 
  ; Use the CertUtil.exe method 
  ; ================================================================================ 
 
  $_Cmd = '%COMSPEC% /c CertUtil.exe -hashfile ' + $_TFile + ' ' + $_HashType + ' > ' + $_Wfile
 
  ; run the command and get the result 
  Shell $_Cmd
  If Not @ERROR
 
    $_Fh = FreeFileHandle()
    $_ = Open($_Fh, $_WFile)
    If Not @ERROR
      ; hash string is first part of second line, remove spaces 
      $_FData = ReadLine($_Fh)					; get first line and discard 
      $_FData = ReadLine($_Fh)					; get the second line 
      $_ = Join(Split($_FData, ' '), '')			; remove spaces 
      If InStr($_, 'toomanyarg')				; check for error 
        $_Exit = 1						; set error status to prevent file cleanup 
      Else
        $CheckSum = $_						; prepare to return the checksum 
      EndIf
      $_ = Close($_Fh)
 
      If $_InputType And Not $_Exit				; Cleanup temp files if not error state 
        Del $_TFile
      EndIf
      Del $_WFile                                               ; delete the working file 
 
      If Not $_Exit
        Exit 0							; exit success status 
      EndIf
 
    EndIf
  EndIf
 
 
  ; ================================================================================ 
  ; Use the CmdHashGen.exe method 
  ; ================================================================================ 
 
  $_Cmd = '%COMSPEC% /c ' + $_CPath + 'CmdHashGen.exe ' + $_TFile + '  /b /' + $_HashType + ' > ' + $_Wfile
 
  ; run the command and get the result 
  Shell $_Cmd							; run the command 
  If Not @ERROR							; if successful 
 
    $_Fh = FreeFileHandle()
    $_ = Open($_Fh, $_WFile)					; read the work-file content 
    If Not @ERROR						; if successful 
      ; Hash is first value in first line 
      $CheckSum = Split($_FData, ' ')[0]			; extract the hash value 
      $_ = Close($_Fh)
 
      If $_InputType						; Cleanup temp files 
        Del $_TFile
      EndIf
      Del $_WFile
 
      Exit 0							; exit success status 
 
    EndIf
  EndIf
 
  ; should have exited by now if any method worked - clean up and exit with error status 
  If $_InputType						; Cleanup temp files 
    Del $_TFile
  EndIf
  Del $_WFile
 
  $CheckSum = 0							; return zero 
  Exit 1							; exit error status 
 
EndFunction