Vigenère Encryption Cipher in Perl

By Jari Perkiömäki

The following implementation of the Vigenère Cipher in Perl is adjusted to the Finnish alphabet, using the programming ideas from my earlier implementation of the Affine Cipher. This Vigenère implementation also incorporates a simple columnar transposition function, making the ciphertext just slightly more difficult to crack. However, this implementation uses direct standard alphabets in a linear manner and its security is therefore inherently weak.

The summary

As we know, any monoalphabetic cipher (e.g. Affine Cipher) can be solved by means of letter frequency analysis, repetition patterns and the information on the way letters combine with each other. These are of course very language-dependent characteristics. The Vigenère cipher is based on an encipherment square (see below) where successive rows consist of the normal alphabet shifted by 1 position. Here any particular cipherletter may represent different plaintext letters, depending on its position in the message. Systems like these are called polyalphabetic ciphers.

The Vigenère Square

Plain A B C D E F G H I J K L M N O P Q R S T U V W X Y Z Å Ä Ö

C     A B C D E F G H I J K L M N O P Q R S T U V W X Y Z Å Ä Ö
i     B C D E F G H I J K L M N O P Q R S T U V W X Y Z Å Ä Ö A
p     C D E F G H I J K L M N O P Q R S T U V W X Y Z Å Ä Ö A B
h     D E F G H I J K L M N O P Q R S T U V W X Y Z Å Ä Ö A B C
e     E F G H I J K L M N O P Q R S T U V W X Y Z Å Ä Ö A B C D
r     F G H I J K L M N O P Q R S T U V W X Y Z Å Ä Ö A B C D E
      G H I J K L M N O P Q R S T U V W X Y Z Å Ä Ö A B C D E F
      H I J K L M N O P Q R S T U V W X Y Z Å Ä Ö A B C D E F G
      I J K L M N O P Q R S T U V W X Y Z Å Ä Ö A B C D E F G H
      J K L M N O P Q R S T U V W X Y Z Å Ä Ö A B C D E F G H I
      K L M N O P Q R S T U V W X Y Z Å Ä Ö A B C D E F G H I J
      L M N O P Q R S T U V W X Y Z Å Ä Ö A B C D E F G H I J K
      M N O P Q R S T U V W X Y Z Å Ä Ö A B C D E F G H I J K L
      N O P Q R S T U V W X Y Z Å Ä Ö A B C D E F G H I J K L M
      O P Q R S T U V W X Y Z Å Ä Ö A B C D E F G H I J K L M N
      P Q R S T U V W X Y Z Å Ä Ö A B C D E F G H I J K L M N O
      Q R S T U V W X Y Z Å Ä Ö A B C D E F G H I J K L M N O P
      R S T U V W X Y Z Å Ä Ö A B C D E F G H I J K L M N O P Q
      S T U V W X Y Z Å Ä Ö A B C D E F G H I J K L M N O P Q R
      T U V W X Y Z Å Ä Ö A B C D E F G H I J K L M N O P Q R S
      U V W X Y Z Å Ä Ö A B C D E F G H I J K L M N O P Q R S T
      V W X Y Z Å Ä Ö A B C D E F G H I J K L M N O P Q R S T U
      W X Y Z Å Ä Ö A B C D E F G H I J K L M N O P Q R S T U V
      X Y Z Å Ä Ö A B C D E F G H I J K L M N O P Q R S T U V W
      Y Z Å Ä Ö A B C D E F G H I J K L M N O P Q R S T U V W X
      Z Å Ä Ö A B C D E F G H I J K L M N O P Q R S T U V W X Y
      Å Ä Ö A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
      Ä Ö A B C D E F G H I J K L M N O P Q R S T U V W X Y Z Å
      Ö A B C D E F G H I J K L M N O P Q R S T U V W X Y Z Å Ä

To encipher a message, the user chooses a keyword, say PARIS. Then he chooses the corresponding cipher rows from the encipherment square above as follows (of course, to decipher a message you only need the same rows):

Plain A B C D E F G H I J K L M N O P Q R S T U V W X Y Z Å Ä Ö

      P Q R S T U V W X Y Z Å Ä Ö A B C D E F G H I J K L M N O
      A B C D E F G H I J K L M N O P Q R S T U V W X Y Z Å Ä Ö
      R S T U V W X Y Z Å Ä Ö A B C D E F G H I J K L M N O P Q
      I J K L M N O P Q R S T U V W X Y Z Å Ä Ö A B C D E F G H
      S T U V W X Y Z Å Ä Ö A B C D E F G H I J K L M N O P Q R

Let us assume our message to be enciphered is:

ON MONTA HYVÄÄ SYYTÄ JÄÄDÄ SATEELLA KOTIIN

(translation: There are many good reasons to stay home when it rains.)

The enciphering process starts by finding the first letter O of the message in the cipher row starting with P. We see the cipherletter is A. Then the plainletter N is found the cipher row starting with A, thus N is -- N. The plainletter M becomes A (on the cipher row R), O becomes W, and N becomes C. When we have used the cipher row S (the last letter in the keyword), we start again from the cipher row P.

Finally, after all substitutions, we will get:

Key:    PA RISPA RISPA RISPA RISPA RISPARIS PARISP
Plain:  ON MONTA HYVÄÄ SYYTÄ JÄÄDÄ SATEELLA KOTIIN
Cipher: AN AWCFA YDKNÄ GDNFÄ ÅGQSÄ GIITEÖTS ZOHQÅÖ

Standard mode

To achieve the result above with our Perl script, you will say:

echo "on monta hyvää syytä jäädä sateella kotiin" |./vigenere.pl -v -k paris

Plain:  onmontahyvääsyytäjäädäsateellakotiin
Cipher: anawcfaydknägdnfäågqsägiiteötszohqåö

Columnar transposition

The standard mode is pretty vulnerable to attack if the keyword is extremely weak. Consider the following:

echo "on monta hyvää syytä jäädä sateella kotiin" | ./vigenere.pl -v -k aaaaa

Plain:  onmontahyvääsyytäjäädäsateellakotiin
Cipher: onmontahyvääsyytäjäädäsateellakotiin

The keyword AAAAA is absolutely useless because it exposes the entire plaintext! However, we can make the ciphertext slightly more obscure by using columnar transposition (the command option -t) as follows:

echo "on monta hyvää syytä jäädä sateella kotiin" | ./vigenere.pl -v -t -k aaaaa

Plain:  onmontahyvääsyytäjäädäsateellakotiin
Cipher: onmontahyvääsyytäjäädäsateellakotiin
Transp: otätdeknnaäääeomhsjsltoyyäalinvyätai

The transposed ciphertext is constructed in the following way:

Key A A A A A
    - - - - -
    O N M O N
    T A H Y V
    Ä Ä S Y Y
    T Ä J Ä Ä
    D Ä S A T
    E E L L A
    K O T I I
    N

We take the keyword and place the ciphertext underneath row by row. Then we take the first column, the second column and so on, and join them together into one row.

Finally, to apply this technique to our original example with the keyword PARIS:

echo "on monta hyvää syytä jäädä sateella kotiin" | ./vigenere.pl -v -t -k paris

Plain:  onmontahyvääsyytäjäädäsateellakotiin
Cipher: anawcfaydknägdnfäågqsägiiteötszohqåö
Transp: afnfstzönaäääeoaygågöhwddgitqcknqiså

See the columnar presentation of the ciphertext before transposition:

Key P A R I S
    - - - - -
    A N A W C
    F A Y D K
    N Ä G D N
    F Ä Å G Q
    S Ä G I I
    T E Ö T S
    Z O H Q Å
    Ö

... and just to make it look neat and polished, use the group Perl utility as follows:

echo "on monta hyvää syytä jäädä sateella kotiin" | ./vigenere.pl -t -k paris | ./group -u -g

AFNFS TZÖNA ÄÄÄEO AYGÅG ÖHWDD GITQC KNQIS Å

The syntax

Vigenère Cipher: polyalphabetic substitution
Options:
-k WORD  Keyword for enciphering
-t       Do columnar transposition by keyword
-v       Verbose: show the plaintext and the ciphertext(s).
-h       Show this help.

Files: Multiple files are accepted. No files means using STDIN.

The code

#!/usr/bin/perl
# 27 Mar 06: Initial Code.
# 28 Mar 06: Added simple columnar transposition.
# Author: Jari Perkiömäki, jpe@uwasa.fi
#

$kw = $byte = $plain = $cipher = $tcipher = $ct = "";
$help = $i = $ki = $j = $le = $trans = $verbose = 0;

use Getopt::Long;
GetOptions ('h'   => \$help,     # help
            'k=s' => \$kw,       # keyword
            't'   => \$trans,    # columnar transposition by keyword
            'v'   => \$verbose); # show plain/ciphertext

help() if ($help || $kw eq "");
chomp $kw;

foreach ('a' .. 'z', 'å', 'ä', 'ö') {
    $abc{$_} = $i++;
}

@keyword = split(//, $kw);
$le = @keyword;

%cba = reverse %abc;

undef $/;

while(<>) {

    tr/A-ZÅÄÖ/a-zåäö/;
    tr|/#+%&=,;:!\?\.\"\'\-<>\(\)\[\]@\\_| |;
    s/\s+//g;
    $plain = $_;

    foreach $byte (split //) {
        $j = 0 if ($j == $le);
        $i = $abc{$byte};

        if ($j < $le) {
            $ki = $abc{$keyword[$j]};
            $ct = $cba{(($i + $ki) % 29)};
            $s[$j] .= $ct;
            $cipher .= $ct;
            $j++;
        }
    }

    for ($t = 0; $t < $le; $t++) {
        $tcipher .= $s[$t];
    }

}

if ($verbose) {
    $trans ? print "Plain:  $plain\nCipher: $cipher\nTransp: $tcipher\n" : print "Plain:  $plain\nCipher: $cipher\n";
} else {
    $trans ? print "$tcipher\n" : print "$cipher\n" ;
}

sub help {
    print <<EOF;
Usage: $0 [options] [files]
Vigenère Cipher: polyalphabetic substitution
Options:
-k WORD  Keyword for enciphering
-t       Do columnar transposition by keyword
-v       Verbose: show the plaintext and the ciphertext.
-h       Show this help.

Files: Multiple files are accepted. No files means using STDIN.
EOF
exit(1);
}