Public/New-MemorablePassword.ps1
|
function New-MemorablePassword { <# .SYNOPSIS Generates an easy-to-remember password. .DESCRIPTION Builds a memorable password from two words, one symbol, and one number. When a total length is specified, the command attempts to fit both words to that size. By default, the result is copied to the clipboard and written to the PassGen log. .PARAMETER TotalLength Specifies the total length of the generated password. Valid values are 12 through 18. .PARAMETER SkipClipboard Prevents the generated password from being copied to the clipboard. .PARAMETER PassThru Returns the generated password to the pipeline. .EXAMPLE New-MemorablePassword .EXAMPLE New-MemorablePassword -TotalLength 16 #> [CmdletBinding()] param( [Parameter(Position = 0)] [ValidateRange(12, 18)] [int]$TotalLength, [Parameter()] [switch]$SkipClipboard, [Parameter()] [switch]$PassThru ) $symbols = '@','!','#','$','%','^','&','*','-','_','=','+',';',':','<','>','.','?','/','~' $fixedOverhead = 2 $maxAttempts = 50 $textInfo = (Get-Culture).TextInfo $wordBuckets = @{} foreach ($word in Get-PassGenWordList) { if ($word.Length -lt 4 -or $word.Length -gt 12) { continue } if (-not $wordBuckets.ContainsKey($word.Length)) { $wordBuckets[$word.Length] = New-Object System.Collections.Generic.List[string] } $null = $wordBuckets[$word.Length].Add($word) } $availableLengths = @($wordBuckets.Keys) for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { $firstWordLength = $null $secondWordLength = $null if ($PSBoundParameters.ContainsKey('TotalLength')) { $targetContentLength = $TotalLength - $fixedOverhead $validFirstLengths = @( $availableLengths | Where-Object { $wordBuckets.ContainsKey($targetContentLength - $_) } ) if (-not $validFirstLengths) { throw "No word combinations fit length $TotalLength." } $firstWordLength = $validFirstLengths | Get-Random $secondWordLength = $targetContentLength - $firstWordLength } else { $firstWordLength = $availableLengths | Get-Random $secondWordLength = $availableLengths | Get-Random } $firstWord = $textInfo.ToTitleCase(($wordBuckets[$firstWordLength] | Get-Random)) $secondWord = $textInfo.ToTitleCase(($wordBuckets[$secondWordLength] | Get-Random)) $number = Get-Random -Minimum 1 -Maximum 10 $symbol = $symbols | Get-Random $middle = @($number, $symbol) | Get-Random -Count 2 $password = "$firstWord$($middle[0])$secondWord$($middle[1])" if ($PSBoundParameters.ContainsKey('TotalLength') -and $password.Length -ne $TotalLength) { continue } return (Complete-PassGenResult -Value $password -DisplaySegment @( [pscustomobject]@{ Text = $firstWord; Color = 'Red' } [pscustomobject]@{ Text = [string]$middle[0]; Color = 'White' } [pscustomobject]@{ Text = $secondWord; Color = 'Yellow' } [pscustomobject]@{ Text = [string]$middle[1]; Color = 'Green' } ) -SkipClipboard:$SkipClipboard.IsPresent -PassThru:$PassThru.IsPresent) } throw 'Unable to generate a memorable password that satisfied the requested constraints.' } Set-Alias -Name pge -Value New-MemorablePassword |