2
\$\begingroup\$

I wanted to make some simple functions that would be used in the encryption and decryption using a Vigenere square.

# Define the alphabet used in the cipher once so as to not define in memory for every encrypt/decrypt action
$global:alphabet = "abcdefghijklmnopqrstuvwxyz"

function Encrypt-VigenereCipherCharacter{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [char]$Character,
        [parameter(Mandatory=$true)]
        [char]$Key
    )

    # This function will encrypt an alphabetic character using a Vigenere Cipher and the key passed. 

    # Set the characters to lower case for baseline calculation. 
    $CharacterIndex = $global:alphabet.IndexOf(([string]$Character).ToLower())
    $KeyIndex = $global:alphabet.IndexOf(([string]$Key).ToLower())

    Write-Verbose "The character/index to $($PSCmdlet.ParameterSetName.ToLower()) is :'$Character/$CharacterIndex' and we are using the key/index: '$Key/$KeyIndex'"

    # Get the index of the encrypted character to return.
    $ShiftedCharacterIndex = $CharacterIndex + $KeyIndex
    # If this shift goes past Z we need to roll back the index to "wrap around" the alphabet to the be.
    if($ShiftedCharacterIndex -ge $global:alphabet.Length){$ShiftedCharacterIndex -= $global:alphabet.Length}


    # Return the shifted character
    return $global:alphabet[$ShiftedCharacterIndex]    
}

function Decrypt-VigenereCipherCharacter{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [char]$Character,
        [parameter(Mandatory=$true)]
        [char]$Key
    )

    # This function will decrypt an alphabetic character using a Vigenere Cipher and the key passed. 

    # Set the characters to lower case for baseline calculation. 
    $CharacterIndex = $global:alphabet.IndexOf(([string]$Character).ToLower())
    $KeyIndex = $global:alphabet.IndexOf(([string]$Key).ToLower())

    Write-Verbose "The character/index to decrypt is :'$Character/$CharacterIndex' and we are using the key/index: '$Key/$KeyIndex'"

    # Decrypt the character based on the positional difference of the character and key
    if($KeyIndex -le $CharacterIndex){
        $ShiftedCharacterIndex =  $CharacterIndex - $KeyIndex
    } else {
        $ShiftedCharacterIndex = $global:alphabet.Length - $KeyIndex + $CharacterIndex
    }

    # Return the shifted character
    return $global:alphabet[$ShiftedCharacterIndex]    
}

function Encrypt-PlainTextString{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [alias("Text")]
        [string]$PlainText,
        [parameter(Mandatory=$true)]
        [string]$Key
    )

    # This function will encrypt a scrubbed character string using a Vigenere Cipher and the key passed.

    # Scrub all the repeated characters from the key so that it is just all unique characters in their original sequence
    Write-Verbose "The key requested is : $key"
    $Key = -join([char[]]$Key | Select-Object -Unique)
    Write-Verbose "The key being used is: $key"

    # Remove all alphabetic character from the plain text string.
    Write-Verbose "The text requested to encrypt is: $Plaintext"
    $PlainText = $PlainText -replace "\W"
    Write-Verbose "The text that will be encrypted is: $Plaintext"

    # Build the key stream to be the exact length of the text being encrypted. 
    $keyStream = ($key * ([math]::Ceiling($PlainText.Length / $key.Length))).Substring(0,$PlainText.Length)
    Write-Verbose "The text that follows will be the plain text string and its keystream below it."
    Write-Verbose $PlainText
    Write-Verbose $keyStream

    # Encrypt the text
    return -join (0..($PlainText.length - 1) | ForEach-Object{
        Get-VigenereCipherCharacter -Character $PlainText[$_] -Key $keyStream[$_] -Encrypt
    })
}

function Decrypt-CipherText{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [alias("Text")]
        [string]$CipherText,
        [parameter(Mandatory=$true)]
        [string]$Key
    )

    # This function will decrypt a scrubbed character string using a Vigenere Cipher and the key passed.

    # Scrub all the repeated characters from the key so that it is just all unique characters in their original sequence
    Write-Verbose "The key requested is : $key"
    $Key = -join([char[]]$Key | Select-Object -Unique)
    Write-Verbose "The key being used is: $key"

    # Remove all alphabetic character from the plain text string.
    Write-Verbose "The text requested to decrypt is: $CipherText"
    $CipherText = $CipherText -replace "\W"
    Write-Verbose "The text that will be decrypted is: $CipherText"

    # Build the key stream to be the exact length of the text being decrypted. 
    $keyStream = ($key * ([math]::Ceiling($CipherText.Length / $key.Length))).Substring(0,$CipherText.Length)
    Write-Verbose "The text that follows will be the plain text string and its keystream below it."
    Write-Verbose $CipherText
    Write-Verbose $keyStream

    # Decrypt the text
    return -join (0..($CipherText.length - 1) | ForEach-Object{
        Get-VigenereCipherCharacter -Character $CipherText[$_] -Key $keyStream[$_] -Decrypt
    })

}

function Distribute-Spaces{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [string]$Text,
        [parameter(Mandatory=$false)]
        [alias("Spaces")]
        [int]$NumberOfSpaces = 0
    )

    # This function will randomly insert a number of spaces into the text passed.

    # If the number of space is not provided assume that we place about 1 every 5 characters. 
    if($NumberOfSpaces -le 0){$NumberOfSpaces = $Text.Length / 9}

    # Get positional values for these new spaces and ensure they are not at the beginning or end of the new string.
    $spacePositions = 1..$NumberOfSpaces | ForEach-Object{Get-Random -Minimum 1 -Maximum ($text.Length - 2)}

    # Rebuild the string with the inserted spaces.
    -join (0..($text.Length - 1) | ForEach-Object{
        if($spacePositions -contains $_){
            $text[$_] + " "
        } else {
            $text[$_]
        }
    })
}

The functions exists in two pairs. Encrypt-PlainTextString will take a string and key and encrypt each character with Encrypt-VigenereCipherCharacter using a Vigenere square. There are foil decryption functions as well. There is also a spaces distribution function called Distribute-Spaces which is meant to insert spaces into the encrypted text to "obfuscate" words.

I originally had the encryption and decryption functions as the same function and used switches to differentiate between the two actions. It made sense since they shared several lines. The do however to different things and names the functions with appropriate meaningful names was difficult. Therefore I split them up into their own functions.

Some example usage would be:

$plaintext = "The red fox trots quietly at midnight."
$key = "jamesbond"
$ciphertext = Distribute-Spaces -Text(Encrypt-PlainTextString -PlainText $plaintext -Key $key)
$ciphertext

Decrypt-CipherText -CipherText $ciphertext -Key $key

Which would generate the following output.

chqvwe tb acraxkrivhclkeln wqqrgtx
theredfoxtrotsquietlyatmidnight
\$\endgroup\$
2
\$\begingroup\$

I originally had the encryption and decryption functions as the same function

I am guessing that function was named Get-VigenereCipherCharacter? You are still referencing it instead of Encrypt/Decrypt-VigenereCipherCharacter.


As you said, if you can find meaningful function names, combining functions can make sense. As well the as character rotation functions for encrypt and decrypt, you can combine everything. Ultimately all the functions are similar:

Function Use-VigenereEncryption {
   [CmdletBinding()]
   Param (
      [Parameter(ParameterSetName=’Decrypt’)][Switch]$Decrypt,
      [Parameter(ParameterSetName=’Encrypt’)][Switch]$Encrypt,
      [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Text,
      [parameter(Mandatory=$true)][string]$Key
   )

   $alphabet = "abcdefghijklmnopqrstuvwxyz"
   # Remove all alphabetic character from the plain text string.
   Write-Verbose "The text requested to be encrypted/decrypted: $Text"
   $Text = ($Text -replace "\W").ToLower()
   Write-Verbose "The text that will be encrypted/decrypted: $Text"
   # Scrub all the repeated characters from the key so that it is just all unique characters in their original sequence
   Write-Verbose "The key provided: $key"
   $Key = -join([char[]]$Key | Select-Object -Unique)
   Write-Verbose "The key being used: $key"
   # Build the key stream to be the exact length of the text being decrypted. 
   $keyStream = ($key * ([math]::Ceiling($Text.Length / $key.Length))).Substring(0,$Text.Length)
   Write-Verbose "The resulting encryption/decryption string: $keyStream"

   -join (0..($Text.length - 1) | % {
      $CharacterIndex = $alphabet.IndexOf($Text[$_])
      $KeyIndex = $alphabet.IndexOf($keyStream[$_])

      Write-Verbose "The character/index to encrypt/decrypt is :'$($Text[$_])/$CharacterIndex' and we are using the key/index: '$Key/$KeyIndex'"

      Switch ($PsCmdlet.ParameterSetName) {
         "Decrypt" {
            If ($KeyIndex -le $CharacterIndex) {
               $alphabet[$CharacterIndex - $KeyIndex]
            } Else {
               $alphabet[$alphabet.Length - $KeyIndex + $CharacterIndex]
            }
         }

         "Encrypt" {
            If ($CharacterIndex + $KeyIndex -ge $alphabet.Length) {
               $alphabet[$CharacterIndex + $KeyIndex - $alphabet.Length]
            } Else {
               $alphabet[$CharacterIndex + $KeyIndex]
            }
            #randomly insert spaces:
            If ((Get-Random -Minimum 1 -Maximum ($Text.Length - 2)) -le ($Text.Length / 9)) {
               " "
            }
         }
      }
   })
}


$plaintext = "The red fox trots quietly at midnight."
$key = "jamesbond"
$ciphertext = Use-VigenereEncryption -Encrypt -Text $plaintext -Key $key
$ciphertext

$ciphertext | Use-VigenereEncryption -Decrypt -Key $key -Verbose
\$\endgroup\$
  • \$\begingroup\$ Yes. I didn't update the function names as they were still working in my ISE session so I didn't catch it. This is like one of my original drafts of this and you bring it together better than I did. Thanks. Only thing I am going to change on this is make the verbose text dependent on the parameter set so it always says the correct text. \$\endgroup\$ – Matt Jul 5 '16 at 14:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.