Summary: Chris Campbell explains his resolution process for the the Iron Scripter competition at PowerShell Saturday 007.

Microsoft Scripting Guy, Ed Wilson, is here. Today we welcome back guest blogger, Chris Campbell.

It has been just over a month since I got back from PowerShell Saturday 007 in Charlotte, and it was a blast! The talks were excellent; and overall, it was just a lot of fun. Besides having the opportunity to speak, the highlight for me was the Iron Scripter competition run by Jim Christopher. The rules were simple. Whoever finishes first wins, you can use any resource, and no complaining.

The mission, if we chose to accept it, was to create a function that decrypted a message and another function that could recreate the following message:

vfjm wxmmdhx jm vqu mxnkxv. jv fdm sqvfjsh vq mdy kxdtty, eov mvjtt jv jm xsnkyuvxa. ji yqo nds
axnkyuv vfjm wxmmdhx exiqkx dsyqsx xtmx, yqo gjtt ex nkqgsxa jkqs mnkjuvxk iqk uqgxkmfxtt mdvokady!

As everyone else dug into the console, I hunted for a quiet place to work. My first guess was that it was a substitution cipher, which can be solved using frequency analysis.  I downloaded the English Letter Frequency Table and compared that to letters in the cipher text:

$CipherText = 'vfjm wxmmdhx jm vqu mxnkxv. jv fdm sqvfjsh vq mdy kxdtty, eov mvjtt jv jm xsnkyuvxa. ji yqo nds
axnkyuv vfjm wxmmdhx exiqkx dsyqsx xtmx, yqo gjtt ex nkqgsxa jkqs mnkjuvxk iqk uqgxkmfxtt mdvokady!'

$CipherArray = $CipherText.ToCharArray()

$LetterCounts = @{}

    switch -regex ($CipherArray) {

        "[a-z]" {

            $Letter = $Matches[0]

            $LetterCounts[$Letter] = [int]$LetterCounts[$Letter] + 1

        }

    }

First, I put the message into a string variable and converted it to a character array. Next I used a regex to pull out each letter and ignore the punctuation characters while adding the letters and count to a hash table.

Now we can display the results from the hash table to compare to the distribution:

$LetterCounts.GetEnumerator() | Sort-Object -Property Value -Descending

Image of command output

With the limited number of characters, I decided to try a different route. I took a guess that the word PowerShell would be in the cipher text. Next I created a hash table with uppercase letters to test my theory:

#uqgxkmfxtt = powershell

$CipherTable =@{'u'='P';

                'q'='O';

                'g'='W';

                'x'='E';

                'k'='R';

                'm'='S';

                'f'='H';

                't'='L'}

Now we can replace the characters to see if the theory is correct:

  function Test {

    $PlainText = ''

    $CipherArray | ForEach-Object {

        if ($CipherTable.contains("$_")) {

            $PlainText += $CipherTable.item("$_")

        }

        else {

            $PlainText += $_

        }

    }

    $PlainText

}

Image of command output

Next we can solve a few other obvious words and add to our hash table:

Image of command output

Almost there:

Image of command output

Now we can update our hash table with lowercase characters and build a function to decrypt the whole message. Unfortunately, not every character is used, so we will have to deal with that situation. I chose to insert “[UNKNOWN]”.  We also need to pass any punctuation through the function.

$CipherTable =@{'u'='p';'q'='o';

                'g'='w';'x'='e';

                'k'='r';'m'='s';

                'f'='h';'t'='l';

                'v'='t';'w'='m';

                'd'='a';'h'='g';

                'j'='i';'n'='c';

                's'='n';'y'='y';

                'a'='d';'i'='f';

                'o'='u';'e'='b'}

#missing from cipher z, b, c, l, p, r

function DecryptCipher {

    Param (

    [string] $CipherText

    )

    $CipherArray = $CipherText.ToCharArray()

    $UnknownChar = 'z','b','c','l','p','r'

    $PlainText = ''

    $CipherArray | ForEach-Object {

        if ($CipherTable.ContainsKey("$_")) {

            $Plaintext += $CipherTable.Item("$_")

        }

        elseif ($UnknownChar.Contains("$_")) {

            $PlainText += '[UNKNOWN]'

        }

        else {

            $PlainText += $_

        }

    }

    Return $PlainText

}

The second half of the challenge is to create a function to encrypt plaintext by using the same algorithm. We can do that with the same hash table.

#missing from plain z, j, q, x, k, v

function EncryptPlainText {

    Param (

    [string] $PlainText

    )

    $PlainArray = $PlainText.ToCharArray()

    $UnknownChar = 'z','j','q','x','k','v'

    $CipherText = ''

    $PlainArray | ForEach-Object {

        $CurrentLetter = $_

        if ($UnknownChar.Contains("$CurrentLetter")) {

            $CipherText += '[UNKNOWN]'

        }

        elseif ($CipherTable.ContainsValue("$CurrentLetter")) {

            $FoundLetter = ($CipherTable.GetEnumerator() | Where-Object {

                 $_.Value -eq $CurrentLetter}).Name

                            $CipherText += $FoundLetter

        }

        else {

            $CipherText += $CurrentLetter

        }

    }

    Return $CipherText

}

The mission is complete, but we are missing one key step. We need to test the functions with a test case.

Image of command output

The challenge took me just over 30 minutes to complete, but there were several people including the winner, Stephen Owen (https://twitter.com/SRed13), who had already finished. Congratulations to him and to everyone else who enjoyed the awesome challenge.  Special thanks to Jim Christopher (https://twitter.com/beefarino) for planning such a great event!

~Chris

Thank you once again, Chris, for coming to Charlotte to speak at PowerShell Saturday 007, and for accepting the mission of the Iron Scripter.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy