PoshCode Archive  Artifact Content

Artifact 420f5d6913d6013983afd159fb45b826517cb93889d59b0ab4ddad8c26f6fef7:

  • File Test-Hash.ps1 — part of check-in [469b45aa4e] at 2018-06-10 13:25:19 on branch trunk — Test md5/sha1/etc file hashes. (user: Joel Bennett size: 4918)

# encoding: ascii
# api: powershell
# title: Test-Hash
# description: Test md5/sha1/etc file hashes.
# version: 0.1
# type: function
# author: Joel Bennett
# license: CC0
# function: Test-Hash
# x-poshcode-id: 3414
# x-archived: 2012-05-16T21:39:23
# x-published: 2012-05-14T13:01:00
#
# I’ve enhanced this script to make it easy to store the results in a csv file and then test them later.  See Example 1.
# (This one fixes a missing “m” in the previous scrip)
#
function Test-Hash { 
#.Synopsis
#   Test the HMAC hash(es) of a file
#.Description
#   Takes the HMAC hash of a file using specified algorithm, and optionally, compare it to a baseline hash
#.Example
#   C:\PS>ls .\Bin\Release -recurse | Test-Hash -BasePath .\Bin\Release -Type SHA256 | Export-CSV ~\Hashes.csv
#   C:\Program Files\MyProduct>Import-CSV ~\Hashes.csv | Test-Hash
#
#   This example shows how to take the hash of a collection of files and store them in a csv file, and then later verify that the files in a secondary location match the originals exactly.
#
#.Example
#   Test-Hash npp.5.3.1.Installer.exe -HashFile npp.5.3.1.release.md5
# 
#   Searches the provided hash file for a line matching the "npp.5.3.1.Installer.exe" file name
#   and take the hash of the file (using the extension of the HashFile as the Type of Hash).
#
#.Example
#   Test-Hash npp.5.3.1.Installer.exe 360293affe09ffc229a3f75411ffa9a1 MD5
#
#   Takes the MD5 hash and compares it to the provided hash
#
#.Example
#   Test-Hash npp.5.3.1.Installer.exe 5e6c2045f4ddffd459e6282f3ff1bd32b7f67517 
#
#   Tests all of the hashes against the provided (Sha1) hash
[CmdletBinding(DefaultParameterSetName="NoExpectation")]
PARAM(
   # The path to the file to hash
   [Parameter(Position=0,Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
   [Alias("PSPath")]
   [string]$Path,
   
   # When hashing many files in folders, use this to convert to relative paths (so you can compare again in a different location)
   [Parameter(Position=2,Mandatory=$false,ParameterSetName="NoExpectation")]
   [string]$BasePath,
   
   # Supports hashing against a .md5 or .sha1 file such as frequently found on open source servers or torrent sites
   [Parameter(Position=1,Mandatory=$true,ParameterSetName="FromHashFile")]
   [string]$HashFileName,
   
   # Supports passing in the expected hash (particularly useful when piping input from a previous run)
   [Parameter(Position=2,Mandatory=$true,ParameterSetName="ManualHash", ValueFromPipelineByPropertyName=$true)]
   [Parameter(Position=2,Mandatory=$false,ParameterSetName="FromHashFile")]
   [ALias("Hash")]
   [string]$ExpectedHash = $(if($HashFileName){  ((Get-Content $HashFileName) -match $Path)[0].split(" ")[0]  }),
   
   # The algorithm to use when hashing
   [Parameter(Position=1,Mandatory=$true,ParameterSetName="ManualHash", ValueFromPipelineByPropertyName=$true)]
   [Parameter(Position=1,Mandatory=$false,ParameterSetName="NoExpectation")]
   [ValidateSet("MD5","SHA1","SHA256","SHA384","SHA512","RIPEMD160")]
   [string[]]$Algorithm = $(if($HashFileName){ [IO.Path]::GetExtension((Convert-Path $HashFileName)).Substring(1) } else { "SHA256" })
)

begin {
   $ofs=""
   if($BasePath) {
      Push-Location $BasePath
   }
}  

process {
   if($BasePath) {
      $Path = Resolve-Path $Path -Relative
   }
   if(Test-Path $Path -Type Container) {
      # I'd like to support recursing all the files, but for now, just skip
      Write-Warning "Cannot calculate hash for directories: '$Path'"
      return
   }

   $Hashes = @(
      foreach($Type in $Algorithm) {
         # Take the Hash without storing the bytes (ouch)
         [string]$hash = [Security.Cryptography.HashAlgorithm]::Create( $Type ).ComputeHash( [IO.File]::ReadAllBytes( (Convert-Path $Path) ) ) | ForEach { "{0:x2}" -f $_ }
         # Output an object with the hash, algorithm and path
         New-Object PSObject -Property @{ Path = $Path; Algorithm = $Type; Hash = $Hash }
      }
   )

   if($ExpectedHash) {
      # Check all the hashes to see if any of them match
      if( $Match = $Hashes | Where { $_.Hash -eq $ExpectedHash } ) {
         Write-Verbose "Matched hash:`n$($Match | Out-String)"
         # Output an object with the hash, algorithm and path
         New-Object PSObject -Property @{ Path = $Match.Path; Algorithm = $Match.Algorithm; Hash = $Match.Hash; Matches = $True }
      } else {
         Write-Verbose "Failed to match hash:`n$($PSBoundParameters | Out-String)"
         # Output an object with the first hash, algorithm and path
         New-Object PSObject -Property @{ Path = $Hashes[0].Path; Algorithm = $Hashes[0].Algorithm; Hash = $Hashes[0].Hash; Matches = $False }
      }         
   } else {
      Write-Output $Hashes  
   }
}
end {  
   if($BasePath) {
      Pop-Location
   }
}
}