#! /usr/bin/perl # Created By Dimitrios Georgoulas 2003-2004 # Department of Computing, Surrey University # A Perl script for cryptanalysis of the Vigenere Algorithm # Uses automatic and manually keyword selection # Decryption Option Available Only. # Available as an open source. For any modifications/alterations please reference # the author. # All rights reserved Surrey University 2003-2004 # References Algorithm/Structure: # 1) Begginers Guide to Cryptography available at # http://www.murky.org/classical/vigenere.shtml # 2) Vigenere Cipher Applet Demonstration available at # http://islab.oregonstate.edu/koc/ece575/02Project/Mun+Lee/VigenereCipher.html # 3) Cryptanalysis of the Vigenere cipher available at # http://mywebpages.comcast.net/erfarmer201/vigenere/ # 4) JaVi v1.0 - Java Vigenere available at # http://it.geocities.com/teutoburgo/java/javi/JaViManual.html # 5) "Cryptography", by Stinson (p. 31-41) # 6) Breaking the Vigenere cipher available at # http://raphael.math.uic.edu/~jeremy/crypt/The Index of Coincidence.htm ################################################################################ #########################VINGENERE CRYPTANALYSIS################################ ################################################################################ ################################################################################ ##########################START OF THE SUBROUTINES############################## ################################################################################ # Used as part of the decryption part. Responsible for decrypting a single # letter of ciphertext(first letter) given the ciphertext and the keyword. # Perl function used: ord--> This function returns the numeric value (ASCII) of # the first character and is assigned by the value "A" and # chr --> This function returns the character represented by a number and is # opposite of the ord function. sub decrypt_letter{ #Local Variables my ($cipher, $row) = @_; my $plain; $row = ord(($row)) - ord('A');# enable mod 26 $cipher = ord(($cipher)) - ord('A');# enable mod 26 $plain = ($cipher - $row) % 26; $plain = chr($plain + ord('A')); return ($plain); } ################################################################################ # The main subroutine of part 3 of our crypatnalysis that will decrypt the # string of ciphetext given the ciphertext and the keyword. # Returns the desired plaintext. #Perl functions used: length-->The function length will returns the length in #characters of the scalar value and substr-->The function substr will extract a # substring out of a string and returns it. In our case used as a replacement sub decrypt_string{ # Local Variables my ($ciphertext, $Keyword) = @_;# Pass the parameters as a flat list of scalars #as array _@. my $plaintext; $Keyword = $Keyword x (length($ciphertext) / length($Keyword) + 1); for( my $i=0; $i 1; $ave = 0; $a = 0; @separated = separateFile $key, @text; foreach $string (@separated) { if ($string ne "\n") { @stringArray = stringtoarray($string); @tempa = freqPercentage(@stringArray); $tempsum = indexOfCoincidence( \@tempa ); $shift = ($shift * $a + $tempsum) / ++$a; } } $IndexOfCoincidence = ($shift - $prevshift); print "Index Of Coincidence is $IndexOfCoincidence.\n"; print "SHIFT is $shift%.\n"; print "PREVIOUS SHIFT is $prevshift%.\n"; if($shift - $prevshift > .019) #We are close to the probabilistic value {$stop = 1; } #of 0.065 so stop shifts } ############################################################################### print "Key is $key letters long.\n";#We found the keyword length!!!! ################################################################################ ##################################STEP 2######################################## ################################################################################ #FIND THE ACTUAL KEYWORD ################################################################################ #Step 2a) ################################################################################ # using the mutual index of coincidence to find relative shifts # between key char "a" and key char "b" my @multArr = (); foreach $i (1 .. $key-1) { foreach $j ($i+1 .. $key) { @tmp = stringtoarray(@separated[$i-1]); @tempA = freqOfChar( @tmp ); @tmp = stringtoarray(@separated[$j-1]); @tempB = freqOfChar( @tmp ); inner : foreach $distance (0 .. 25) { $temp = MutualIndexOfCoincidence(\@tempA,\@tempB, $distance); if ($temp > .061 ) { push @multArr, [$i, $j,- $distance]; last inner; } } } } ################################################################################ #Step 2b) ################################################################################ # initialize @k for use my @k = (); #To be used to store the relative shifts of the key foreach $i ( 0 .. $key -1) {$k[$i] = -1;} $k[0] = 1; ################################################################################ #Step 3c) ################################################################################ # used to find out if we know all # of the relative shifts in the key # Syntax:: knownShifts @k sub knownShifts { foreach $item ( @_ ) { if ($item == -1) {return 1;} } return 0; } ################################################################################ #Step 3d) ################################################################################ # Apply 26 cyclic shifts according with the keyword length # Hint: Ref 3) top of the page 1 $temp = 0; while ( knownShifts(@k) == 1 ) { $temp = shift @multArr; if ( $k[ $$temp[0] - 1] == -1) { # nothing known... we die. die "Oh no....$temp\n" if ($k[$$temp[1] - 1] == -1); $k[$$temp[0]-1] = $k[$$temp[1]-1] + (- $$temp[2] % 26); } elsif ($k[$temp[1] - 1] == -1) {$k[$$temp[1] - 1] = $k[$$temp[0] - 1] + $$temp[2];} # failed both conditions, so they are both known. } ################################################################################ #Step 3c) ################################################################################ #Obtaine the total of keywords for the 26 cyclic shifts (see above) and select #the one that fits better the requirements # Hint: Ref 5) top of the page 1 $maxfrequency = 0; $maxfrequencyE = 0; foreach $i ( 0 .. 25 ){ @text = (); $Keyword = keyword ($i,\@k); $sum = 0; foreach $j (0..$#text) {if($text[$j] eq "E") {$sum++;}} #Assign the higest frequency to #Letter "E". if ($sum/$#text > $maxfrequency) { $maxfrequency = $sum/$#text; $maxfrequencyE = $i; } print " Available Keys : $Keyword \n" #Print all the available keys } $Keyword = keyword($maxfrequencyE,\@k);###########AUTOMATIC SELECTION########### ################################################################################ print " Automated Key Selection : $Keyword \n";#We found the actual keyword!!!!! ################################################################################ #####################################STEP 3##################################### ################################################################################ #RECREATE THE PLAINTEXT ################################################################################ $ciphertext ='PLEASE ENTER YOUR CIPHER TEXT HERE'; #########################MANUAL SELECTION CURRENT DISABLED###################### #$Keyword = 'DISABLE THE AUTOMATIC SELECTION AND ADD HERE THE KEYWORD YOU WISH" $derived_plaintext; $derived_plaintext=decrypt_string( $ciphertext, $Keyword ); print "Ciphertext :$ciphertext\n";#The original ciphertext ################################################################################ print "Recreated plaintext :$derived_plaintext\n";#WE CRACK VIGENERE CIPHERTEXT! ################################################################################ ##################################END OF PROGRAM################################ ################################################################################