0

The code below creates a hash table containing a reversed cipher. I am trying to preform a lookup on the hash table, and return the results. I am trying to decrypt the content inside of an XML file provided to me, with the script i've created below in powershell.

$translation = [ordered]@{}
$alpha = 'A'..'Z'
For($i=0; $i -lt 26; $i++)
{
 $translation[$alpha[((((-$i + $offset) % 26) + 26) % 26)]] = $alpha[$i]
}

The XML file contains:

<?xml version="1.0" encoding="UTF-8"?>
<translate>
  <caesar_cipher>
    <offset>-7</offset>
    <cipher>
RFGNCTAZITALFGB FG BZRRPBBOZIIV RFHEIPALGN AMP TQYTGRPQ EFXPCBMPII 
TBBLNGHPGA
    </cipher>
  </caesar_cipher>
</translate>

Any idea how to preform a lookup in the hash table to decrypt the message within the XML file, outputting to powershell?

mklement0
  • 382,024
  • 64
  • 607
  • 775
bear443
  • 13
  • 3
  • Duplicate of [the question here](https://stackoverflow.com/questions/28999417/how-to-search-hash-table-keys-that-contain-a-specific-value-powershell) – Maximilian Burszley Nov 21 '18 at 21:47
  • 4
    Please don't make more work for people by vandalizing your posts. By posting on the Stack Exchange (SE) network, you've granted a non-revocable right, under the [CC BY-SA 3.0 license](//creativecommons.org/licenses/by-sa/3.0), for SE to distribute that content (i.e. regardless of your future choices). By SE policy, the non-vandalized version of the post is the one which is distributed. Thus, any vandalism will be reverted. – K.Dᴀᴠɪs Nov 21 '18 at 22:17

3 Answers3

3
# The input XML.
$xml = @'
<?xml version="1.0" encoding="UTF-8"?>
<translate>
  <caesar_cipher>
    <offset>-7</offset>
    <cipher>
RFGNCTAZITALFGB FG BZRRPBBOZIIV RFHEIPALGN AMP TQYTGRPQ EFXPCBMPII 
TBBLNGHPGA
    </cipher>
  </caesar_cipher>
</translate>
'@

# Read the XML text into an XML DOM (XmlDocument).
$doc = [xml] $xml

# Extract the offset needed to build the translation hashtable.
$offset = $doc.translate.caesar_cipher.offset

# Build the translation hashtable, which maps a given [char] instance
# to a different [char] instance.
# (The hashtable doesn't strictly need to be *ordered*.)
$translation = [ordered] @{}
# NOTE: 'A'..'Z' only works in PowerShell *Core*.
#       In *Windows PowerShell* you must use construct the array via *code points*, 
#       as shown in the next statement.
$alpha = [char[]] (65..90)
for($i=0; $i -lt 26; $i++) {
  $translation[$alpha[((((-$i + $offset) % 26) + 26) % 26)]] = $alpha[$i]
}
# Add an entry to pass spaces through as-is.
$translation[[char] ' '] = [char] ' '

# Extract the text to be deciphered.
$cipherText = $doc.translate.caesar_cipher.cipher

# Translate the individual characters and reassemble them into a string.
-join $translation[[char[]] $cipherText]

The above yields:

CONGRATULATIONS ON SUCCESSFULLY COMPLETING THE ADVANCED POWERSHELL ASSIGNMENT    

And congratulations on successfully making us do your homework for you.


Note:

  • PowerShell automatically provides convenient dot notation (.) to drill down into XML documents.

  • Pitfall: PowerShell often automatically treats [char] instances as [string] instances as needed, but not in the case of hashtable lookups: hashtable lookups must use the same data type as the key's data type, which is why $cipherText is cast to [char[]] to look up [char] instances and why adding the entry for spaces above uses an explicit [char] cast to define the key.

  • Windows PowerShell only supports numerical endpoints for .., the range operator, whereas PowerShell Core also supports characters.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    Nice, didn't realize you could `[char[]]$string` – Maximilian Burszley Nov 21 '18 at 21:48
  • @bear443: Sounds like you're using _Windows PowerShell_, not PowerShell _Core_. Only the latter supports using _characters_ with `..` to create ranges of characters. See my update for how to do it in Windows PowerShell. The updated code should now run on Windows PowerShell and PowerShell Core alike. – mklement0 Nov 21 '18 at 22:04
  • @TheIncorrigible1: Yes, it's effectively the same as `$string.ToCharArray()`, but I prefer `[char[]]`, because it is more PowerShell-idiomatic. Performance-wise, there's virtually no difference. – mklement0 Nov 21 '18 at 22:52
1

You have a couple issues with your script. First, your generation of the cipher's math is wrong. You want to pull the offset from the xml. You also want to catch the space character in your hash or it'll return $null when the lookup is performed. This is true of any other non-alpha characters unless you have them defined. Once this is addressed, all you need to do is perform the lookup and join the characters back together. You can perform multiple lookups in a dictionary by passing an array in PowerShell exemplified below.

# initialize alpha and hash lookups
$alpha = 'A'..'Z'
$decipher = @{ ' ' = ' ' }

# load prerequisite variables
$xml = [xml]@'
<?xml version="1.0" encoding="UTF-8"?>
<translate>
  <caesar_cipher>
    <offset>-7</offset>
    <cipher>
RFGNCTAZITALFGB FG BZRRPBBOZIIV RFHEIPALGN AMP TQYTGRPQ EFXPCBMPII TBBLNGHPGA
    </cipher>
  </caesar_cipher>
</translate>
'@
$offset = [int]$xml.translate.caesar_cipher.offset
$cipher = $xml.translate.caesar_cipher.cipher

# generate decipher table
0..($alpha.Length - 1) | % {$decipher["$($alpha[(-$_ + $offset) % $alpha.Length])"] = $alpha[$_]}

# perform lookups
-join $decipher[$cipher -split '']
Maximilian Burszley
  • 18,243
  • 4
  • 34
  • 63
0

As TheIncorrigible1 suggests, you can use the XPath expression //cipher/text() to select the desired text nodes.

$xml = [xml](Get-Content path\to\file.xml)

$Ciphers = $xml.SelectNodes('//cipher/text()').InnerText
foreach($Cipher in $Ciphers){
  Write-Host "Cipher text:`n$Cipher" -ForegroundColor Magenta
  Write-Host "Decrypted text:`n$(-join ($Cipher.ToCharArray() |ForEach-Object {
    # if it's in the set A..Z, translate
    if($_ -in $alpha){
      $_ = $translation[$_]
    }
    $_
  }))" -ForegroundColor Green
}
mklement0
  • 382,024
  • 64
  • 607
  • 775
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206