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
    {

    }

}