src/PSPasswordGenerator.psm1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
<#
PSPasswordGenerator.psm1, code for the PSPasswordGenerator module Copyright (C) 2016-2022 Colin Cogle <colin@colincogle.name> Online at <https://github.com/rhymeswithmogul/PSPasswordGenerator> This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. #> #Requires -Version 3.0 # .ExternalHelp PSPasswordGenerator-help.xml Function Get-RandomPassword { [CmdletBinding(DefaultParameterSetName='RandomSecurely')] [OutputType([String], ParameterSetName='RandomInsecurely')] [OutputType([String], ParameterSetName='WordsInsecurely')] [OutputType([Security.SecureString], ParameterSetName='RandomSecurely')] [OutputType([Security.SecureString], ParameterSetName='WordsSecurely')] [Alias('New-RandomPassword')] Param( [Parameter(ParameterSetName='RandomInsecurely')] [Parameter(ParameterSetName='RandomSecurely')] [ValidateRange(1, [UInt32]::MaxValue)] [Alias('Count', 'MinLength', 'Size')] [UInt32] $Length = 16, [Parameter(ParameterSetName='RandomInsecurely')] [Parameter(ParameterSetName='RandomSecurely')] [Switch] $StartWithLetter, [Parameter(ParameterSetName='WordsInsecurely')] [Parameter(ParameterSetName='WordsSecurely')] [ValidateRange(1, [UInt32]::MaxValue)] [UInt32] $Words = 3, [Parameter(ParameterSetName='WordsInsecurely', Mandatory)] [Parameter(ParameterSetName='WordsSecurely', Mandatory)] [ValidateNotNullOrEmpty()] [IO.FileInfo] $WordList, [Parameter(ParameterSetName='RandomInsecurely', Mandatory)] [Parameter(ParameterSetName='WordsInsecurely', Mandatory)] [Switch] $AsPlainText, [Switch] $NoSymbols, [Switch] $UseAmbiguousCharacters, [Switch] $UseExtendedAscii ) # Warn the user if they've specified mutually-exclusive options. If ($NoSymbols -and $UseExtendedAscii) { Write-Warning 'The -NoSymbols parameter was also specified. No extended ASCII characters will be used.' } $ret = "" If ($PSCmdlet.ParameterSetName -Like 'Random*') { For ($i = 0; $i -lt $Length; $i++) { Do { Do { Do { Do { $x = Get-Random -Minimum 33 -Maximum 254 Write-Debug "Considering character: $([char]$x)" } While ($x -eq 127 -Or (-Not $UseExtendedAscii -And $x -gt 127)) # The above Do..While loop does this: # 1. Don't allow ASCII 127 (delete). # 2. Don't allow extended ASCII, unless the user wants it. } While (-Not $UseAmbiguousCharacters -And ($x -In @(49,73,108,124,48,79))) # The above loop disallows 1 (ASCII 49), I (73), l (108), # | (124), 0 (48) or O (79) -- unless the user wants those. } While ($NoSymbols -And ($x -lt 48 -Or ($x -gt 57 -And $x -lt 65) -Or ($x -gt 90 -And $x -lt 97) -Or $x -gt 122)) # If the -NoSymbols parameter was specified, this loop will ensure # that the character is neither a symbol nor in the extended ASCII # character set. } While ($i -eq 0 -And $StartWithLetter -And -Not (($x -ge 65 -And $x -le 90) -Or ($x -ge 97 -And $x -le 122))) # If the -StartWithLetter parameter was specified, this loop will make # sure that the first character is an upper- or lower-case letter. Write-Debug "SUCCESS: Adding character: $([char]$x)" $ret += [char]$x } } # If we're generating random words: Else { # There is DEFINITELY room for improvement here. Loading an entire # wordlist into memory can be quite cumbersome. $allWords = Get-Content -LiteralPath $WordList -ErrorAction Stop $culture = (Get-Culture).TextInfo $ret = '' For ($i = 0; $i -lt $Words; $i++) { # Pick a random word from the list. $word = Get-Random $allWords # Randomly capitalize the first letter of the word. If ((Get-Random) % 2) { $word = $culture.ToTitleCase($word) } # Stick something in between the words. # Letters are 65-90 (caps) and 97-122 (lower) $separator = 0 Do { $ch = (Get-RandomPassword -Length 1 -NoSymbols:$NoSymbols -AsPlainText -UseExtendedAscii:$UseExtendedAscii) Write-Debug "Trying separator $ch." $separator = [Convert]::ToByte([Char]$ch) } While ( ($separator -ge 65 -and $separator -le 90) <# No uppercase letters #> ` -or ($separator -ge 97 -and $separator -le 122) <# No lowercase letters #> ` -or ($separator -ge 128 -and $separator -le 165) <# No accented letters #> ` -or ($separator -gt 165 -and -Not $UseExtendedAscii) <# Unwanted extended ASCII #> ` ) Write-Debug "WORD=`"$word`", SEP=`"$([Char]$separator)`"" $ret += $word $ret += [Char]$separator } # Chop off the final separator. $ret = $ret.Substring(0, $ret.Length - 1) } If ($AsPlainText) { Return $ret } Else { $ss = ConvertTo-SecureString -AsPlainText -Force -String $ret Remove-Variable -Name 'ret' -ErrorAction SilentlyContinue Return $ss } } |