src/PSPasswordGenerator.psm1
<#
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 } } |