classes/Passphrase.Class.ps1

class Passphrase {
    [ValidateCount(
        0,
        3065)]
    [System.Collections.Generic.List[string]]$Words = @()

    [ValidateRange(
        0,
        9)]
    [System.Collections.Generic.List[int]]$Numbers = @()

    [ValidatePattern(
        '[!"#$%&()*+,./:;<=>?@\^_{|}]')]
    [System.Collections.Generic.List[char]]$Specials = @()

    [ValidateNotNullOrEmpty()]
    [char]$Separator

    [bool]$IncludeUppercase = $false

    [ValidateSet(
        'Weak',
        'Reasonable',
        'Strong',
        'Very strong',
        'Overkill')]
    [string]$Strength

    [ValidateRange(
        0,
        50128)]
    [double]$Points

    [int]$Length

    Passphrase(
        [string[]]$Words) {
        $Words | Get-Random -Count 3 | ForEach-Object {
            $this.Words.Add($_)
        }

        $PassphraseStrength = $this.GetStrength($true)
        $this.Points = $PassphraseStrength[0]
        $this.Strength = $PassphraseStrength[1]
        $this.Length = $this.ToString().Length
    }

    Passphrase(
        [string]$Passphrase,
        [char]$Separator) {
        [string[]]$WordsArray = $Passphrase.Split($Separator)

        [string[]]$WordsWithNumbers = $WordsArray -match '\d'
        [int[]]$NumbersFound = $WordsWithNumbers -replace '\D',''
        $NumbersFound | ForEach-Object {
            [int[]](($_ -split '') -ne '') | ForEach-Object {
                $this.Numbers.Add($_)
            }
        }
        $WordsWithNumbers | ForEach-Object {
            $WordsArray = $WordsArray.Replace($_, ($_ -replace '\d',''))
        }

        [string[]]$WordsWithSpecials = $WordsArray -match '[^A-Za-z0-9]+'
        [string[]]$SpecialsFound = $WordsWithSpecials -replace '[A-Za-z0-9]+'
        $SpecialsFound | ForEach-Object {
            [char[]](($_ -split '') -ne '') | ForEach-Object {
                $this.Specials.Add($_)
            }
        }
        $WordsWithSpecials | ForEach-Object {
            $WordsArray = $WordsArray.Replace($_, ($_ -replace '[^A-Za-z0-9]+',''))
        }

        [string[]]$WordsUppercase = $WordsArray -cmatch '[A-Z]+'
        $WordsUppercase | ForEach-Object {
            $WordsArray = $WordsArray.Replace($_, $_.ToLower())
        }
        if ($WordsUppercase) {
            $this.IncludeUppercase = $true
        }

        $this.Words = $WordsArray

        $PassphraseStrength = $this.GetStrength($true)
        $this.Points = $PassphraseStrength[0]
        $this.Strength = $PassphraseStrength[1]
        $this.Length = $this.ToString().Length
    }

    Passphrase(
        [string[]]$Words,
        [int]$AmountOfWords,
        [char]$Separator) {
        $Words | Get-Random -Count $AmountOfWords | ForEach-Object {
            $this.Words.Add($_)
        }

        $this.Separator = $Separator

        $PassphraseStrength = $this.GetStrength($true)
        $this.Points = $PassphraseStrength[0]
        $this.Strength = $PassphraseStrength[1]
        $this.Length = $this.ToString().Length
    }

    Passphrase(
        [string[]]$Words,
        [int]$AmountOfWords,
        [char]$Separator,
        [int]$AmountOfNumbers,
        [int]$AmountOfSpecials,
        [bool]$IncludeUppercase) {
        $Words | Get-Random -Count $AmountOfWords | ForEach-Object {
            $this.Words.Add($_)
        }

        $this.Separator = $Separator

        $this.AddNumber($AmountOfNumbers)

        $this.AddSpecial($AmountOfSpecials)

        if ($IncludeUppercase) {
            $this.AddUppercase()
        }

        $PassphraseStrength = $this.GetStrength($true)
        $this.Points = $PassphraseStrength[0]
        $this.Strength = $PassphraseStrength[1]
        $this.Length = $this.ToString().Length
    }

    [void]AddWord(
        [string[]]$Words) {
        $Words | ForEach-Object {
            $this.Words.Add($_)
        }

        $PassphraseStrength = $this.GetStrength($true)
        $this.Points = $PassphraseStrength[0]
        $this.Strength = $PassphraseStrength[1]
        $this.Length = $this.ToString().Length
    }

    [void]AddNumber(
        [int]$AmountOfNumbers) {
        for ($i = 1; $i -le $AmountOfNumbers; $i++) {
            [int]$Number = (0..9) | Get-Random
            $this.Numbers.Add($Number)
        }

        $PassphraseStrength = $this.GetStrength($true)
        $this.Points = $PassphraseStrength[0]
        $this.Strength = $PassphraseStrength[1]
        $this.Length = $this.ToString().Length
    }
    
    [void]AddSpecial(
        [int]$AmountOfSpecials) {
        [char[]]$SpecialCharacters = '!"#$%&()*+,./:;<=>?@\^_{|}'.ToCharArray()

        for ($i = 1; $i -le $AmountOfSpecials; $i++) {
            $Special = $SpecialCharacters | Get-Random
            $this.Specials.Add($Special)
        }

        $PassphraseStrength = $this.GetStrength($true)
        $this.Points = $PassphraseStrength[0]
        $this.Strength = $PassphraseStrength[1]
        $this.Length = $this.ToString().Length
    }

    [void]AddUppercase() {
        $this.IncludeUppercase = $true

        $PassphraseStrength = $this.GetStrength($true)
        $this.Points = $PassphraseStrength[0]
        $this.Strength = $PassphraseStrength[1]
        $this.Length = $this.ToString().Length
    }

    [void]RemoveWord(
        [string[]]$Words) {
        $Words | ForEach-Object {
            $this.Words.Remove($_)
        }

        $PassphraseStrength = $this.GetStrength($true)
        $this.Points = $PassphraseStrength[0]
        $this.Strength = $PassphraseStrength[1]
        $this.Length = $this.ToString().Length
    }

    [void]RemoveNumber(
        [int[]]$Numbers) {
        $Numbers | ForEach-Object {
            $this.Numbers.Remove($_)
        }

        $PassphraseStrength = $this.GetStrength($true)
        $this.Points = $PassphraseStrength[0]
        $this.Strength = $PassphraseStrength[1]
        $this.Length = $this.ToString().Length
    }

    [void]RemoveSpecial(
        [char[]]$Specials) {
        $Specials | ForEach-Object {
            $this.Specials.Remove($_)
        }

        $PassphraseStrength = $this.GetStrength($true)
        $this.Points = $PassphraseStrength[0]
        $this.Strength = $PassphraseStrength[1]
        $this.Length = $this.ToString().Length
    }

    [void]RemoveUppercase() {
        $this.IncludeUppercase = $false

        $PassphraseStrength = $this.GetStrength($true)
        $this.Points = $PassphraseStrength[0]
        $this.Strength = $PassphraseStrength[1]
        $this.Length = $this.ToString().Length
    }

    [string]GetStrength() {
        [string]$String = $this.ToString()
        [double]$Score = 0
        [int]$CharacterSets = 0
        [int]$MultipleCharactersInSets = 0

        if ($String.Length -ge 1) {
            $Score = $Score + 4
        }

        if ($String.Lenth -ge 8) {
            $String.Substring(1,7).ToCharArray() | ForEach-Object {
                $Score = $Score + 2
            }
        } elseif ($String.Length -lt 8 -and $String.Length -gt 1) {
            $String.Substring(1,($String.Length - 1)).ToCharArray() | ForEach-Object {
                $Score = $Score + 2
            }
        }

        if ($String.Lenth -ge 20) {
            $String.Substring(8,19).ToCharArray() | ForEach-Object {
                $Score = $Score + 1.5
            }
        } elseif ($String.Length -lt 20 -and $String.Length -gt 8) {
            $String.Substring(8,($String.Length - 8)).ToCharArray() | ForEach-Object {
                $Score = $Score + 1.5
            }
        }

        if ($String.Length -gt 20) {
            $String.ToCharArray() | ForEach-Object {
                $Score = $Score + 1
            }
        }

        switch -Regex -CaseSensitive ($String) {
            ([regex]::new('[a-z]')) {
                $CharacterSets++
            }
            ([regex]::new('[A-Z]')) {
                $CharacterSets++
            }
            ([regex]::new('\d')) {
                $CharacterSets++
            }
            ([regex]::new('[!"#$%&()*+,-./:;<=>?@\^_{|}]')) {
                $CharacterSets++
            }
            ([regex]::new('[a-z].*[a-z]')) {
                $MultipleCharactersInSets++
            }
            ([regex]::new('[A-Z].*[A-Z]')) {
                $MultipleCharactersInSets++
            }
            ([regex]::new('\d.*\d')) {
                $MultipleCharactersInSets++
            }
            ([regex]::new('[!"#$%&()*+,-./:;<=>?@\^_{|}].*[!"#$%&()*+,-./:;<=>?@\^_{|}]')) {
                $MultipleCharactersInSets++
            }
        }

        if ($MultipleCharactersInSets -ge 3) {
            $Score = $Score + 8
        } elseif ($CharacterSets -ge 3) {
            $Score = $Score + 6
        }

        $PassphraseStrength = switch ([int]$Score) {
            {0..27 -contains $_} {
                'Weak'
            }
            {28..35 -contains $_} {
                'Reasonable'
            }
            {36..59 -contains $_} {
                'Strong'
            }
            {60..127 -contains $_} {
                'Very strong'
            }
            {128..50128 -contains $_} {
                'Overkill'
            }
        }

        return $PassphraseStrength
    }

    [array]GetStrength(
        [bool]$IncludePoints) {
        [string]$String = $this.ToString()
        [double]$Score = 0
        [int]$CharacterSets = 0
        [int]$MultipleCharactersInSets = 0

        if ($String.Length -ge 1) {
            $Score = $Score + 4
        }

        if ($String.Lenth -ge 8) {
            $String.Substring(1,7).ToCharArray() | ForEach-Object {
                $Score = $Score + 2
            }
        } elseif ($String.Length -lt 8 -and $String.Length -gt 1) {
            $String.Substring(1,($String.Length - 1)).ToCharArray() | ForEach-Object {
                $Score = $Score + 2
            }
        }

        if ($String.Lenth -ge 20) {
            $String.Substring(8,19).ToCharArray() | ForEach-Object {
                $Score = $Score + 1.5
            }
        } elseif ($String.Length -lt 20 -and $String.Length -gt 8) {
            $String.Substring(8,($String.Length - 8)).ToCharArray() | ForEach-Object {
                $Score = $Score + 1.5
            }
        }

        if ($String.Length -gt 20) {
            $String.ToCharArray() | ForEach-Object {
                $Score = $Score + 1
            }
        }

        switch -Regex -CaseSensitive ($String) {
            ([regex]::new('[a-z]')) {
                $CharacterSets++
            }
            ([regex]::new('[A-Z]')) {
                $CharacterSets++
            }
            ([regex]::new('\d')) {
                $CharacterSets++
            }
            ([regex]::new('[!"#$%&()*+,-./:;<=>?@\^_{|}]')) {
                $CharacterSets++
            }
            ([regex]::new('[a-z].*[a-z]')) {
                $MultipleCharactersInSets++
            }
            ([regex]::new('[A-Z].*[A-Z]')) {
                $MultipleCharactersInSets++
            }
            ([regex]::new('\d.*\d')) {
                $MultipleCharactersInSets++
            }
            ([regex]::new('[!"#$%&()*+,-./:;<=>?@\^_{|}].*[!"#$%&()*+,-./:;<=>?@\^_{|}]')) {
                $MultipleCharactersInSets++
            }
        }

        if ($MultipleCharactersInSets -ge 3) {
            $Score = $Score + 8
        } elseif ($CharacterSets -ge 3) {
            $Score = $Score + 6
        }

        $PassphraseStrength = switch ([int]$Score) {
            {0..27 -contains $_} {
                'Weak'
            }
            {28..35 -contains $_} {
                'Reasonable'
            }
            {36..59 -contains $_} {
                'Strong'
            }
            {60..127 -contains $_} {
                'Very strong'
            }
            {128..50128 -contains $_} {
                'Overkill'
            }
        }

        if ($IncludePoints) {
            return $Score, $PassphraseStrength
        } else {
            return $PassphraseStrength
        }
    }

    [string]ToString() {
        [string[]]$WordsArray = $this.Words | Sort-Object {Get-Random}

        if ($this.IncludeUppercase) {
            [string]$Word = $WordsArray | Get-Random
            $WordsArray = $WordsArray.Replace($Word, $Word.ToUpper())
        }
        
        foreach ($Num in $this.Numbers) {
            [string]$Word = $WordsArray | Get-Random
            [int]$Placement = @(
                0,
                $Word.Length) | Get-Random
            [string]$WordWithNumber = $Word.Insert($Placement, $Num)
            $WordsArray = $WordsArray.Replace($Word, $WordWithNumber)
        }

        foreach ($Char in $this.Specials) {
            [string]$Word = $WordsArray | Get-Random
            [int]$Placement = @(
                0,
                $Word.Length) | Get-Random
            [string]$WordWithSpecial = $Word.Insert($Placement, $Char)
            $WordsArray = $WordsArray.Replace($Word, $WordWithSpecial)
        }

        return $WordsArray -join $this.Separator
    }
}
# SIG # Begin signature block
# MIIM+AYJKoZIhvcNAQcCoIIM6TCCDOUCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUs7FCy1ML2quVAYQEF/XcRK+s
# qSygggo/MIIFGTCCBAGgAwIBAgIQDbnGEbOq/3RgewIGXryqxTANBgkqhkiG9w0B
# AQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk
# IElEIFJvb3QgQ0EwHhcNMTQxMTE4MTIwMDAwWhcNMjQxMTE4MTIwMDAwWjBtMQsw
# CQYDVQQGEwJOTDEWMBQGA1UECBMNTm9vcmQtSG9sbGFuZDESMBAGA1UEBxMJQW1z
# dGVyZGFtMQ8wDQYDVQQKEwZURVJFTkExITAfBgNVBAMTGFRFUkVOQSBDb2RlIFNp
# Z25pbmcgQ0EgMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKri5yAv
# rBCV+s0k5fig3/WirZ+8s8nh+B/EPuSWQW275wPwDBRxvaY4UbdQOac59kJt4lzE
# nv+reNW9ZwMh6W4EzEbfxYcklJ/91iwFYYOTsvXhd2QqutVQ87bab9CLvH8+awDu
# XLM0v1DA+MjwfVd+dApIr21ITItvil4jvnbLXYR4VjuIZ5vRiGCiCEQHiImmw/Lc
# KuBzbMKbhCb3FD6LSqhpCPSTiegfaeu0KnUyCxmPfLMMuFrkRrRka8fQUJvwgLRP
# NXGfIH9ZyFRm7M0zE98JMoUQAmFoPLSSJGC6oNK8tccHvfxQ6jRgCB8CoY8ftyz9
# WqZJgLk5+llJ+RkCAwEAAaOCAbswggG3MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYD
# VR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHkGCCsGAQUFBwEBBG0w
# azAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUF
# BzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVk
# SURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqgOKA2hjRodHRw
# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3Js
# MD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5k
# aWdpY2VydC5jb20vQ1BTMB0GA1UdDgQWBBQyCsEMwWg+V6gt+Xki5Y6c6USOMjAf
# BgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkqhkiG9w0BAQsFAAOC
# AQEARK1QChmvA+HzpJ7KM8s0RJW06t34uUi15WqZw7cBjq7VxHMefVoI/Rm/IEzF
# AbvIstsQydkrDd3OcTzcW665Z0vvvbBEmLhhKdyoenOyFL10PzBcw3nADPRW3LP+
# 4Yl4RaWH6Fkoj0SLbQY/sTbEMO50bFTLxAPXb3ga42xDdhVGniJJWZdNON4bTNJ8
# lhv8utfpehgwFyzVhoku0JoZPjXyxiu+UUlnSR1lIa9CIk4NTQ8aAumbgnbn/Iqw
# e3VWTeo/kA+KJwRVMBN6U6H+9l6i9kk5VF8DyYtqNc4wqALgQBXtFZUQHQZj7++N
# o5rhwVpgmjGEl7nwi5AqasvHIjCCBR4wggQGoAMCAQICEAc7i58aZsyuhHuR4DLA
# /D4wDQYJKoZIhvcNAQELBQAwbTELMAkGA1UEBhMCTkwxFjAUBgNVBAgTDU5vb3Jk
# LUhvbGxhbmQxEjAQBgNVBAcTCUFtc3RlcmRhbTEPMA0GA1UEChMGVEVSRU5BMSEw
# HwYDVQQDExhURVJFTkEgQ29kZSBTaWduaW5nIENBIDMwHhcNMTgxMjA1MDAwMDAw
# WhcNMjExMjA4MTIwMDAwWjBrMQswCQYDVQQGEwJOTzENMAsGA1UEBxMET3NsbzEf
# MB0GA1UECgwWTm9yZ2VzIG11c2lra2jDuGdza29sZTELMAkGA1UECxMCSVQxHzAd
# BgNVBAMMFk5vcmdlcyBtdXNpa2tow7hnc2tvbGUwggEiMA0GCSqGSIb3DQEBAQUA
# A4IBDwAwggEKAoIBAQDP5Goq8/HycbDA/W4fivn4cqPm6xrWw/w+u+Bu7ZUoQDNK
# 3DyFB10P0IXF2w94c4Kr9R6LykFfVxnKYHiCU9hdApEqp9oyqmw9zSwZrI2YIih4
# BOSXop2eN0Rk0hS1+2dPEZ739Y2A4aKtVmrCdKVPrn5BENVUl+vU06f7LsKn6a8m
# F3xGdWY7iWwaNrRfWwkyjkADa3WEAb9gn4w9N4Cas0LdiT8FTmAgwcD7lwtMtcQR
# QnsKmK+w7li1Y8aUs/66eVS7s+vo2UybwL9eX32RJDS7/xB6tvlzHL6+jBkZ43s7
# CMpSIIryzuUvJbfxi5iYZhVYmt38bfPe6IQ12aqJAgMBAAGjggG6MIIBtjAfBgNV
# HSMEGDAWgBQyCsEMwWg+V6gt+Xki5Y6c6USOMjAdBgNVHQ4EFgQUSNYRh/NjMM2c
# P60D3bXEYy6ETkowDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMD
# MHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9URVJF
# TkFDb2RlU2lnbmluZ0NBMy5jcmwwN6A1oDOGMWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0
# LmNvbS9URVJFTkFDb2RlU2lnbmluZ0NBMy5jcmwwTAYDVR0gBEUwQzA3BglghkgB
# hv1sAwEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQ
# UzAIBgZngQwBBAEwdgYIKwYBBQUHAQEEajBoMCQGCCsGAQUFBzABhhhodHRwOi8v
# b2NzcC5kaWdpY2VydC5jb20wQAYIKwYBBQUHMAKGNGh0dHA6Ly9jYWNlcnRzLmRp
# Z2ljZXJ0LmNvbS9URVJFTkFDb2RlU2lnbmluZ0NBMy5jcnQwDAYDVR0TAQH/BAIw
# ADANBgkqhkiG9w0BAQsFAAOCAQEAYXdI/OeJHNVlDYrNsvn8hpQ9dVSJxBUKlU9X
# 9HHOfflqz34I0zQYS0hohfwFPFrTNvECo5RsfRWXNe0lxM2lWXwwDuZLPVQOtd9R
# mjW/6ZSGHP7LVxuWfhIxhRHNsaALd7UCU7xQeYF9Yf/nWBOb+k9TF3F1poraNm8r
# fCD8ZwIZuquzyVNY/XHRiXNlVIplYT0njx/NgM1DG2heT12GNPqFZO5OIArNGvrF
# s2En9d48g48TDpk7wwne6dOHtlV3JqJ6Dd5z9zF9Y3H0qWUWo9tUAu0ew2mAUY7S
# XUW7hG9ErJvXx4a2ySiJuz0jxka/DmIMJJtnw2QIvMqM4YqSSjGCAiMwggIfAgEB
# MIGBMG0xCzAJBgNVBAYTAk5MMRYwFAYDVQQIEw1Ob29yZC1Ib2xsYW5kMRIwEAYD
# VQQHEwlBbXN0ZXJkYW0xDzANBgNVBAoTBlRFUkVOQTEhMB8GA1UEAxMYVEVSRU5B
# IENvZGUgU2lnbmluZyBDQSAzAhAHO4ufGmbMroR7keAywPw+MAkGBSsOAwIaBQCg
# eDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEE
# AYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJ
# BDEWBBR0wd1oFuqgKJH19I2Mji0hthTIkTANBgkqhkiG9w0BAQEFAASCAQBYf55a
# JD2wRjEbNgaXA8+kvuQYdGisXYN44ip5Bv6hgWnGKBe36YHe61NyfCkYumewFo9x
# sdS3FtuANTiLDVA5JBwIUzecptnIkILEO7DmSgkqpOXJQ+hT5WuRdT9qeu9nWBuQ
# YrT4eTHGpj+ubhjRDDMGDrw8rXeMjmVyjndpGLJnDZB+oq9ZO5y8byPp2KDDZZRO
# Axr/zMEA7fPLNTkYYWxHM+V50zR3wU43UkuuihBDvWlGzYgoOp8pqIQkYNgXYCS1
# jd6e1vXET4SF+NxyTE+XmyKcFKJPS3g7zGObC/+mg5koFuEM2oeIuL0ceoYIrmnP
# FpDuM51zbToHglYa
# SIG # End signature block