CGI Crypt Analysis
CGI
Download (.zip)
#!perl
# Online Cryptanalysis Tool # By Mike Bobbitt # Mike@Army.ca
# Revision History # # 11 Dec 2000: 1.0 - Initial Release # 12 Deb 2000: 1.1 - Handles quotes properly, shows plaintext guesses in table # 29 Jul 2002: 1.2 - Added digraph table and ability to handle homophones
# To Do # # - Sort character list # - Count number of guesses # - Print message when all letters have guesses # - Handle chars like < and ? properly
# Set debug level: # 0 = None # 1 = Rudimentary # 2 = Detailed # 3 = Overwhelming $debug=0;
# Version $ver="1.2";
# Write output immediately $|=1;
# Print HTML Page Header print "Content-Type: text/html\n\n"; print "<html>";
# Get script name $script_name=$0; # Drop the rest of the path (for UNIX) $script_name=~s/.*\/(.*)/$1/;
# No change? Then this must be a Win32 machine... if ($script_name eq $0) { $script_name=~s/.*\\(.*)/$1/; }
debug("Starting execution, debugging is on...");
# Seed random number generator srand(time^$$);
# English Frequency Table $eng_freq{a}=8.2; $eng_freq{b}=1.5; $eng_freq{c}=2.8; $eng_freq{d}=4.3; $eng_freq{e}=12.7; $eng_freq{f}=2.2; $eng_freq{g}=2.0; $eng_freq{h}=6.1; $eng_freq{i}=7.0; $eng_freq{j}=0.2; $eng_freq{k}=0.8; $eng_freq{l}=4.0; $eng_freq{m}=2.4; $eng_freq{n}=6.7; $eng_freq{o}=7.5; $eng_freq{p}=1.9; $eng_freq{q}=0.1; $eng_freq{r}=6.0; $eng_freq{s}=6.3; $eng_freq{t}=9.1; $eng_freq{u}=2.8; $eng_freq{v}=1.0; $eng_freq{w}=2.4; $eng_freq{x}=0.2; $eng_freq{y}=2.0; $eng_freq{z}=0.1;
# Create the alphabet, for reference @alphabet=("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");
# Read web environment variables
debug("REMOTE_HOST: $ENV{REMOTE_HOST}",2); debug("REMOTE_USER: $ENV{REMOTE_USER}",2); debug("REMOTE_IDENT: $ENV{REMOTE_IDENT}",2); debug("HTTP_USER_AGENT: $ENV{HTTP_USER_AGENT}",2); debug("REQUEST_METHOD: $ENV{REQUEST_METHOD}",2); debug("QUERY_STRING: $ENV{QUERY_STRING}",2); #debug("SSL_CLIENT_CN: $ENV{SSL_CLIENT_CN}",2); #debug("SSL_CLIENT_IO: $ENV{SSL_CLIENT_IO}",2);
# Get the input read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
# Set multi-valued data delimiter $delim="#";
# Read data passed in from the environment, if any if ($ENV{QUERY_STRING}) { if ($buffer) { $buffer="$buffer&"; } $buffer.=$ENV{QUERY_STRING}; }
# Split the name-value pairs @pairs = split(/&/,$buffer);
debug("--- START WEB DATA ---",2);
foreach $pair (@pairs) { ($name,$value)=split(/=/,$pair);
# Un-Webify plus signs and %-encoding $value=~tr/+/ /; $value=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value=~s/<!--(.|\n)*-->//g;
# Remove \r's from submitted data $value=~s/\r//g;
if ($allow_html!=1) { $value=~s/<([^>]|\n)*>//g; }
if (!$FORM{$name}) { $FORM{$name}=$value; } else { $FORM{$name}.=$delim.$value; } debug("{$name}=($value)",2); } debug("--- END WEB DATA ---<p>",2);
# Was a debug value passed in from the web? if (!$debug) { $debug=$FORM{debug}; }
$ciphertext=$FORM{ciphertext}; $plaintext_guess=$FORM{plaintext_guess};
# Explicitly decode "'s $ciphertext=~s/%22/"/g; $plaintext_guess=~s/%22/"/g;
# drop case, if we're told to do so... if ($FORM{case_sense}) { $ciphertext=~tr/A-Z/a-z/; $plaintext_guess=~tr/A-Z/a-z/; debug("Dropping case..."); }
# Build Header $header="<head><link rel=\"stylesheet\" type=\"text/css\" href=\"/CipherLogic.css\">"; $header.="<title>Online Cryptanalysis Tool</title></head>\n"; $header.="<p align=\"center\"><big><big>Cryptanalysis Tool</big></big><br></p>\n";
$header.="<form method=\"POST\" action=\"$script_name\">\n";
print $header;
if ($FORM{showengfreq}) { @freq_keys=keys %eng_freq;
print <<HTML; <center> <table bgcolor="#C0C0C0" border="0"> <tr> <td bgcolor="#808080" align="center">Character</td> <td bgcolor="#808080" align="center">Frequency (%)</td> </tr> HTML
foreach $freq_key (@freq_keys) { print "<tr><td bgcolor=\"#808080\" align=\"center\">$freq_key</td><td bgcolor=\"#808080\" align=\"center\">$eng_freq{$freq_key}</td></tr>\n"; }
print <<HTML; </table> <p> <a href="" OnClick="window.close()">click to close</a> </center> HTML
exit; }
if ($ciphertext) { if (!$plaintext_guess) { $plaintext_guess=$ciphertext; }
$ciphertemp=$ciphertext; $ciphertemp=~s/\n//g;
$realcipherlength=length($ciphertext); $cipherlength=length($ciphertemp); # Were we asked to generate random ciphertext? if ($FORM{encipher}) { debug("We're going to encrypt this."); for $letter (@alphabet) { $gotone=0; while (!$gotone) { # Get random number $num=rand(@alphabet); $randlett=$alphabet[$num]; if (!$taken{$randlett}) { # comment out this if statement to allow a letter to stand for itself if ($randlett ne $letter) { $taken{$randlett}=1; $gotone=1; debug("Using $randlett as replacement for $letter."); $plaintext_guess=replaceChar($plaintext_guess,$ciphertext,$letter,$randlett); } } } } }
# Do we have a character to replace? if ($FORM{replacement_char}) { $plaintext_guess=replaceChar($plaintext_guess,$ciphertext,$FORM{original_char},$FORM{replacement_char}); debug("Plaintext guess is now: $plaintext_guess"); }
printCompare($plaintext_guess,$ciphertext); print "<p>";
if ($showwarning) { print "<font color=red>WARNING</font>: You have already used <font color=yellow>$FORM{replacement_char}</font> as a plaintext guess for $replaced{$FORM{replacement_char}}. (This is OK if you are solving a cipher with homophones.)"; } $char_html=chargraph(); $di_html=digraph();
# Turn quotes to %22 for web submission $ciphertext=~s/"/%22/g; $plaintext_guess=~s/"/%22/g; print <<HTML; <input type="hidden" value="$ciphertext" name="ciphertext"></p> <input type="hidden" value="$plaintext_guess" name="plaintext_guess"></p> <input type="hidden" value="$FORM{spaces}" name="spaces"></p> <input type="hidden" value="$FORM{case_sense}" name="case_sense"></p> <p> Select Character to Replace: <select size="1" name="original_char" tabindex="1"> HTML
foreach $freq_key (@freq_keys) { print "<option>$freq_key</option>"; }
print <<HTML; </select> Replace Occurances With: <input type="text" name="replacement_char" size="1" maxlength="1" tabindex="2"> <p>Or enter crib text here: <input type="text" name="crib" size="15" tabindex="3"> <p><input type="submit" value="Update" name="Update" tabindex="4"></p> <a onclick="window.open('$script_name?showengfreq=1','Frequency','width=220,height=640,resizable'); return false" href="$script_name?showengfreq=1"> >> Show English Character Frequency Table</a><p> <table><tr><td valign="top">$char_html</td><td>$di_html</td></tr></table> HTML
# Are we trying cribs? if ($FORM{crib}) { showCribs(); } } else { print <<HTML; This program can help crack simple monoalphabetic substitutions, such as the Cryptoquotes in many common newspapers. <p> Enter the ciphertext below: <p><textarea rows="10" name="ciphertext" cols="60" tabindex="1"></textarea></p> <p><input type="checkbox" name="spaces" tabindex="2"> Include spaces and punctuation in analysis.<br> <input type="checkbox" name="encipher" tabindex="3"> This is plaintext - generate the ciphertext randomly please.<br> <input type="checkbox" name="case_sense" tabindex="4" checked> Convert all text to lower case. <p><input type="submit" value="Analyze" name="Analyze" tabindex="5"></p> </form> HTML }
# Get file "last modified" time @allinfo=stat($0); $revdate=localtime(@allinfo[9]);
# Print Footer print <<HTML; </p></center></div></form> <hr><small><small><a href="http://Perl.Bobbitt.ca" title="$revdate">$ver</a> © 1999-2003 Cipher Logic Canada Inc.</small></small> <a href="mailto:Mike\@Army.ca">by Mike Bobbitt</a> </body></html> HTML
######################## END OF MAIN ########################
######################## START OF SUBROUTINES ########################
=head3 debug()
debug($debug_message,$debug_level);
$debug_message - String to print if debugging is on $debug_level - Only pring string if current debug level is $debug_level or higher (current debug level is set by $debug)
Prints a message, if $debug has a value.
=cut
sub debug { my $debug_message=shift; my $debug_level=shift;
if (($debug_level le $debug) && $debug) { print "<pre>$debug_message</pre>\n"; } }
##########################################################################
=head3 replaceChar()
$newstring=replaceChar($string,$rostring,$original,$newchar);
$string - String to perform replacements on $rostring - String to use for comparison (usually ciphertext) $original - Character in $string to replace $newchar - Character to replace $original with
Replaces all occurances of $original with $newchar in $string, and returns it.
=cut
sub replaceChar { my $string=shift; my $rostring=shift; my $original=shift; my $newchar=shift;
my $stringlen=length($string); my $marker=0; my $newstring=$string;
$replacements=0;
while ($marker<=$stringlen-1) { debug("Marker: $marker",3); if (substr($rostring,$marker,1) eq $original) { $replacements++; debug("$original found at location $marker. ($newstring[$marker])"); $newstring=substr($newstring,0,$marker).$newchar.substr($newstring,$marker+1,$stringlen); debug("\$newstring is now: [$newstring]"); } $marker++; } return($newstring); }
##########################################################################
=head3 digraph()
$di_html=digraph();
$di_html - HTML for digraph frequency table
Returns the HTML for a digraph frequency table.
=cut sub digraph {
# Print digraph table $di_html=<<HTML; Digraph Frequency Distribution: <p> <table border="0" bgcolor="#C0C0C0"> <tr> <td bgcolor="#808080" align="center">Ciphertext Digraph</td> <td bgcolor="#808080" align="center">Occurrances</td> <td bgcolor="#808080" align="center">Frequency (%)</td> </tr> HTML for ($counter=0;$counter<=$cipherlength-1;$counter++) { $char=substr $ciphertemp,$counter,2;
# Do we count punctuation? if ($FORM{spaces}) { $difrequency{$char}++; $divirtual_length++; } else { if ($char=~/[a-zA-Z]/) { $difrequency{$char}++; $divirtual_length++; } } debug("[$char]"); }
@difreq_keys=keys %difrequency;
foreach $difreq_key (@difreq_keys) { $freq_percent=(int($difrequency{$difreq_key}/$divirtual_length*10000))/100; $di_html.=<<HTML; <tr> <td bgcolor="#808080" align="center">$difreq_key</td> <td bgcolor="#808080" align="center">$difrequency{$difreq_key}</td> <td bgcolor="#808080" align="center">$freq_percent</td> </tr> HTML } $di_html.="</table>";
return($di_html); }
##########################################################################
=head3 chargraph()
$char_html=chargraph();
$char_html - HTML for character frequency table
Returns the HTML for a character frequency table.
=cut sub chargraph { $char_html=<<HTML; Character Frequency Distribution: <p> <table border="0" bgcolor="#C0C0C0"> <tr> <td bgcolor="#808080" align="center">Ciphertext Character</td> <td bgcolor="#808080" align="center">Occurrances</td> <td bgcolor="#808080" align="center">Frequency (%)</td> <td bgcolor="#808080" align="center">Plaintext Guess</td> <td bgcolor="#808080" align="center">Plaintext Guess Frequency (%)</td> <td bgcolor="#808080" align="center">Frequency Difference (%)</td> </tr> HTML for ($counter=0;$counter<=$cipherlength-1;$counter++) { $char=substr $ciphertemp,$counter,1;
# Do we count punctuation? if ($FORM{spaces}) { $frequency{$char}++; $virtual_length++; } else { if ($char=~/[a-zA-Z]/) { $frequency{$char}++; $virtual_length++; } } debug("[$char]"); }
@freq_keys=keys %frequency;
foreach $freq_key (@freq_keys) { $freq_percent=(int($frequency{$freq_key}/$virtual_length*10000))/100; if ($back_replaced{$freq_key}) { $difference=(int(abs($freq_percent-$eng_freq{$back_replaced{$freq_key}})*100))/100; } else { $difference=""; } $char_html.=<<HTML; <tr> <td bgcolor="#808080" align="center">$freq_key</td> <td bgcolor="#808080" align="center">$frequency{$freq_key}</td> <td bgcolor="#808080" align="center">$freq_percent</td> <td bgcolor="#808080" align="center">$back_replaced{$freq_key}</td> <td bgcolor="#808080" align="center">$eng_freq{$back_replaced{$freq_key}}</td> <td bgcolor="#808080" align="center"><font color= HTML if ($difference < 1) { $char_html.="green"; } elsif ($difference > 3) { $char_html.="red"; } else { $char_html.="yellow"; } $char_html.=<<HTML; >$difference</font> </td> </tr> HTML } $char_html.="</table>";
return($char_html); }
##########################################################################
=head3 showCribs()
showCribs();
Shows all possible positions of the provided crib within the working ciphertext
=cut sub showCribs { my $counter,$char,$try,$orig,$original,$newchar,$counter2,$crib_repl; my $crib=$FORM{crib}; my $criblen=length($crib);
for ($counter=0;$counter<=$cipherlength-$criblen;$counter++) { $char=substr $ciphertemp,$counter,$criblen; $try=$ciphertext; $orig=$plaintext_guess; $crib_repl=0;
# Swap out each character of the crib for ($counter2=0;$counter2<=$criblen-1;$counter2++) { # Pull off each character of the crib $newchar=substr $crib,$counter2,1;
# Pull off corresponding char of the ciphertext $original=substr $ciphertemp,$counter+$counter2,1;
# Replace 'em $orig=replaceChar($orig,$try,$original,$newchar); $crib_repl+=$replacements; } print "There were $crib_repl substiturions in crib location #$counter:<br>";
$orig=~s/\n//g; $try=~s/\n//g;
# If the crib wasn't in the resulting plaintext guess, don't show it. if ($orig=~/$crib/) { printCompare($orig,$try,1); } else { print "<br>This crib position is invalid.<p>"; } } }
##########################################################################
=head3 printCompare()
printCompare($plaintext_guess,$ciphertext,$mode);
$plaintext_guess - Plaintext to display $ciphertext - The original ciphertext, used as a comparison $mode - If set to 1, don't show the ciphertext block (optional)
Displays the original ciphertext and the new plaintext guess.
=cut sub printCompare { my $plaintext_guess=shift; my $ciphertext=shift; my $mode=shift;
if (!$mode) { print "Original "; if ($FORM{encipher}) { print "Plain"; } else { print "Cipher"; } print <<HTML; text:<p> <pre> <ul> $ciphertext </ul> </pre> <p> HTML
if ($FORM{encipher}) { print "Generated Ciphertext"; } else { print "Plaintext Guess"; } print <<HTML; : <p> <pre> <ul> HTML }
$showwarning=0; for ($counter=0;$counter<=$realcipherlength-1;$counter++) { $char=substr $ciphertext,$counter,1; $char2=substr $plaintext_guess,$counter,1; if ($char ne $char2) { # Show the newly replaced char as red, any replaced char as yellow if ($char2 eq $FORM{replacement_char}) { print "<font color=red>"; } else { print "<font color=yellow>"; } if ($replaced{$char2} && ($replaced{$char2} ne $char)) { $showwarning=1; } if (!$mode) { $replaced{$char2}=$char; $back_replaced{$char}=$char2; } } print "$char2"; if ($char ne $char2) { print "</font>"; }
} print <<HTML; </ul> </pre> HTML }
######################## END OF SUBROUTINES ########################
######################## END OF FILE ########################
|