Public/Get-StringEntropy.ps1
function Get-StringEntropy { <# .SYNOPSIS Calculate the entropy (in bits) of the provided string .DESCRIPTION Based primarily upon discussion here, https://technet.microsoft.com/en-us/library/cc512609.aspx this function will calculate the entropy of the provided string, returning an integer result indicating bits of entropy. Numerous assumptions MUST be made in the calculation of this number. This function takes the easiest approach, which you can also read as "lazy" at best or misleading at worst. We need to figure out the "size" of the space from which the symbols in the string are drawn - after all the value we're calculating is not absolute in any way, it's relative to some max/min values. We make the following assumptions in this function: --if there is a lower case letter in the provided string, assume it's possible any lower case letter could have been used. Assume the same for upper, numeric, and special chars. --by "special characters" we mean the following: ~`!@#$%^&*()_-+={}[]|\:;"'<,>.?/ --by "letters", we mean just the letters on a U.S. keyboard. --no rules regarding which symbols can appear where, e.g. can't start with a number. --no rules disallowing runs, e.g. sequential numbers, sequential characters, etc. --no rules considering non-normal distribution of symbols, e.g. "e" just as likley to appear as "#" The net impact of these assumptions is we are over-calculating the entropy so the best use of this function is probably for comparison between strings, not as some arbiter of absolute entropy. .PARAMETER InputString The string for which to calculate entropy. .EXAMPLE get-StringEntropy -s "JBSWY3DPEHPK3PXP" .NOTES FileName: get-StringEntropy Author: nelsondev1 #> [CmdletBinding(SupportsShouldProcess = $False, ConfirmImpact = 'Low')] Param( [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] [String[]]$InputString ) begin { $specialChars = @" ~ `!@#$%^&*()_-+={}[]|\:;"'<,>.?/ "@ } process { $symbolCount = 0 # running count of our symbol space foreach ($s in $InputString) { if ($s -cmatch "[a-z]+") { $symbolCount += 26 Write-Verbose "$s contains at least one lower case character. Symbol space now $symbolCount" } if ($s -cmatch "[A-Z]+") { $symbolCount += 26 Write-Verbose "$s contains at least one upper case character. Symbol space now $symbolCount" } if ($s -cmatch "[0-9]+") { $symbolCount += 10 Write-Verbose "$s contains at least one numeric character. Symbol space now $symbolCount" } # In the particular use, I found trying to regex/match...challenging. Instead, just going # to iterate and look for containment. $hasSpecialChars = $false foreach ($c in $specialChars.ToCharArray()) { if ($s.Contains($c)) { $hasSpecialChars = $true } } if ($hasSpecialChars) { $symbolCount += $specialChars.Length Write-Verbose "$s contains at least one special character. Symbol space now $symbolCount" } # in a batch mode, we might want to pre-calculate the possible values since log is slow-ish. # there wouldn't be many unique options (eg 26, 26+26, 26+10, 26+16, 26+26+10, etc.) # ...though in comparison to performing the above regex matches it may not be a big deal. # anyway... # Entropy-per-symbol is the base 2 log of the symbol space size $entroyPerSymbol = [Math]::Log($symbolCount) / [Math]::Log(2) Write-Verbose "Bits of entropy per symbol calculated to be $entroyPerSymbol" $passwordEntropy = $entroyPerSymbol * $s.Length Write-Verbose "Returning value of $passwordEntropy" Write-Output $passwordEntropy # this is the bits of entropy in the starting string } } end { } } |