Category Archives: powershell

Generating Diceware Passphrases with Powershell

Diceware is a method to create a passphrase by rolling dice to select random words from a word list. Diceware passphrases are easy to remember, but hard to crack. Micah Lee has a good article here that explains all the details about what they are and how to use them. The Diceware home page has more technical details on how the system works and has several word lists available for download. The basic idea is that you roll 5 dice to get a 5 digit number and then look that number up in the word list to select each word. Repeat until your passphrase has as many words as you want.

To truly get the benefit of a Diceware passphrase, you need to roll physical dice to get true randomness. However, spend a little time with Google and you will see that there are many computer implementations that generate Diceware passphrases. I decided to write one in Powershell.

The first thing I wanted to do was to come up with a better random number generator than Get-Random. See my blog post here for a discussion on how I came up with Get-RandomInt.

Once I had a decent random number generator, the biggest question I had was on how to store the word list. I decided to use a Powershell hash table. A Powershell hash table is similar to an array, but consists of multiple key-value pairs. Rather than looking up a value by indexing on the element number like you would in an array, you look up values in a has table by indexing on the key. So $hash[$key] will return the value associated with $key. I set up the word list with the words as values and the dice rolls as keys. So it is very easy to look up a word like this: $word = $wordlist[$roll].

I added another feature. Because the words in the word list vary in length from 1 to 6 characters, it is possible that, even with a 7 or 8 word passphrase (which should be very secure), the passphrase generated could be as short as 7 or 8 characters. To prevent this problem, I provided an option to specify a minimum length for the passphrase.

function New-DicewarePassword {
[CmdletBinding()]
[OutputType([String])]
Param (
   # Word list to use
   [Parameter(Position = 0)]
   [ValidateNotNullOrEmpty()]
   [String]$WordList = '.\diceware.wordlist.asc',

   # number of words in the passphrase
   [ValidateRange(3,999)]
   [int]$NumWords = 5,

   # minimum length of the passphrase to generate
   [Int]$MinLength = 15
)

   #read the words file into a hashtable
   if (Test-Path $WordList) {
      $WordsList = @{}
      Get-Content $WordList |
      Where-Object -FilterScript {
         $_ -match '^\d.*\s.*'
      } |
      ForEach-Object -Process {
         $key, $value = -split $_
         $WordsList[$key] = $value
      }
   } else {
      Write-Warning -Message "File $WordList not found"
     Break
   }
   #initialize other variables
   $NumDice = 5 #Diceware word list is based on rolling 5 dice

   #main loop starts here
   Do {
      $PassPhrase = '' #start with a new passphrase each attempt
      for ($Words = 1;$Words -le $NumWords; $Words++) {
         $RollResult = ''
         for ($RollNum = 1;$RollNum -le $NumDice; $RollNum++) {
            $RollResult = $RollResult += [string](Get-RandomInt -min 1 -max 6)
         }
         $PassPhrase += $WordsList[$RollResult]+' '
      }
   } Until ($PassPhrase.trim().Length -ge $MinLength)
   $PassPhrase, '(Length: {0} chars)' -f $PassPhrase.trim().Length
}

Decoding Robocopy Exit Codes

Sometimes, you write a script that uses robocopy to copy or move files around efficiently, and you need to confirm that no errors occurred during robocopy’s run. Unfortunately, robocopy doesn’t use the standard error return mechanism of 0 for no errors and 1 (or some other non-zero number) if an error occurred. Microsoft documents robocopy exit codes in KB 954404, but the information isn’t complete. A better explanation can be found in a Deployment Guys blog post and shows that the exit codes are bitmapped:

Code Meaning
0 No errors occurred and no files were copied.
1 One of more files were copied successfully.
2 Extra files or directories were detected.  Examine the log file for more information.
4 Mismatched files or directories were detected.  Examine the log file for more information.
8 Some files or directories could not be copied and the retry limit was exceeded.
16 Robocopy did not copy any files.  Check the command line parameters and verify that Robocopy has enough rights to write to the destination folder.

These codes are combined to give a complete indication of the results of the run. For example, if the exit code is 3, that indicates that files were copied successfully and that extra files or directories were detected (1 + 2 = 3). The important conclusion is to note that any return code with 0x8 or 0x16 set indicates that some or all of the files were not copied.

To decode these exit codes in Powershell, use the bitwise and operator –band to test for the bits we are interested in. In binary, the important bits are 011000, which converts to decimal 24. So, you can do your robocopy operation like this:

$cmd_args = @($source, $target, $action, $options)
& robocopy.exe @cmd_args
If ($LastExitCode -band 24) {
    Write-Host “Everything is OK”
 } else {
    Write-Host “Errors happened”
}

					

Using the .Net RNGCryptoServiceProvider in Powershell

I was working on a password generator function, and I wanted to use something that was a better random number generator than Powershell’s Get-Random cmdlet. I’m not a crypto expert, so I’ll leave the discussions about how good each random number generator is, but Matt Graeber wrote a nice article that discusses how to test the effectiveness of a random number generator. According to Matt’s testing, the RNGCryptoServiceProvider class is a better random number generator that Powershell’s Get-Random for cryptographic purposes. I liked the way Get-Random worked, so I decided to implement a function that worked in a similar manner. Note, Get-Random works two ways: it either returns a random number or returns a random object from a collection of objects. This implementation only returns a random number.

RNGCryptoServiceProvider implements a GetType method that is passed an array of bytes and returns it filled with random byte values. The trick here is, that I want to return a single unsigned 32 bit integer but GetType returns an array of bytes. I need to use the System.BitConverter .NET class to convert an array of bytes to a single uint32 value. The other challenge I ran into is how to implement minimum and maximum values. I decided to use the mod operator to return a value in the range I need.

Here is the function I came up with:

Function Get-RandomInt {
    [cmdletbinding()]
    Param(
        [uint32]$max=[UInt32]::MaxValue,
        [uint32]$min=[UInt32]::MinValue
    )
    if ($min -lt $max) {
        #initialize everything
        $diff=$max-$min+1
        [Byte[]] $bytes = 1..4  #4 byte array for int32/uint32
        $rng = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
        #generate the number
        $rng.getbytes($bytes)
        $number = [System.BitConverter]::ToUInt32(($bytes),0)
        $number = $number % $diff + $min
        return $number    
    } else {
        Write-Warning 'Min must be less than Max'
        return -1
    }
}

Use it like this:

Get-RandomInt -min 1 -max 100

to get a random number between 1 and 100.

 

…Tim