uebung4.plsrc
#!/usr/local/bin/perl -w
use strict;
my %p = (a => .082, b => .015, c => .028, d => .043, e => .127,
f => .022, g => .020, h => .061, i => .070, j => .002,
k => .008, l => .040, m => .024, n => .067, o => .075,
p => .019, q => .001, r => .060, s => .063, t => .091,
u => .028, v => .010, w => .023, x => .001, y => .020,
z => .001);
my @digrams = qw(th he in er an re ed on es st en at to nt ha nd
ou ea ng as or ti is et it ar te se hi of);
my @trigrams = qw(the ing and her ere ent tha nth was eth for dth);
my %i = (1=>1, 3=>9, 5=>21, 7=>15, 11=>19, 17=>23, 25=>25);
%i = (%i, reverse %i);
my @pi = (23, 13, 24, 0, 7, 15, 14, 6, 25, 16, 22, 1, 19,
18, 5, 11, 17, 2, 21, 12, 20, 4, 10, 9, 3, 8);
sub reversepi {
my @a = splice @_,0,@_;
$_[$a[$_]] = $_ for 0..@a-1;
@_
}
sub e_affin {
my ($a, $b) = (shift, shift);
@_ = map { ord ($_) - 97 } split //, lc shift;
$_[$_] = ($a*$_[$_]+$b)%26 for 0..@_-1;
uc join '', map { chr ($_+97) } @_
}
sub d_affin {
my ($a, $b) = (shift, shift);
@_ = map { ord ($_) - 97 } split //, lc shift;
$_[$_] = ($i{$a} * ($_[$_]-$b))%26 for 0..@_-1;
join '', map { chr ($_+97) } @_
}
sub d_enigma {
my $k = ord (lc shift)-97;
@_ = map { ord ($_) - 97 } split //, lc shift;
my @z;
$z[$_-1] = ($k + $_ - 1) % 26 for 1..@_;
$_[$_] = ($_[$_] - $z[$_]) % 26 for 0..@_-1;
$_[$_] = [reversepi (@pi)]->[$_[$_]] for 0..@_-1;
join '', map { chr ($_ + 97) } @_;
}
sub d_vigenere {
my @k = map { ord ($_) - 97 } split //, lc shift();
@_ = map { ord ($_) - 97 } split //, lc shift();
$_[$_] = ($_[$_] - $k[$_ % @k]) % 26 for 0..@_-1;
join '', map { chr ($_ + 97) } @_;
}
sub bktest {
my $s = shift; my %o; my $r;
$o{substr ($s, $_, 3)}++ for 0..length($s)-4;
my @c = reverse sort { $o{$a} <=> $o{$b} }
map { $o{$_} > 1 ? $_ : () } keys %o;
for my $t (@c) {
my $i = 0;
push @_, ($i = index $s, $t, $i)++ for 1..$o{$t};
@_ = map { $_-$_[0] } @_;
$i = euclid ($i, shift @_) for 1..@_;
print $t, '(',$o{$t},'):', $i, "\n";
$r = defined $r ? $r : $i;
}
$r
}
sub buckets {
my ($m, $s) = (shift, shift);
length ($s) % $m and substr $s, length($s)-length($s)%$m-1, length($s)%$m, '';
while (my $t = substr $s, 0, $m, '') {
$_[$_] .= substr ($t, $_, 1) for 0..$m-1;
}
@_
}
sub cindex {
my ($s, $m) = @_; my $r = -1;
for my $m (1..$m) {
@_ = ();
for my $t (buckets ($m, $s)) {
my $total;
my %f = frequency ($t);
$total += $f{$_} * ($f{$_}-1) for 'a'..'z';
$total /= length ($t) * (length ($t)-1);
push @_, $total;
}
my $total; $total += $_ for @_; $total /= @_;
$r = abs ($total-0.065) < 0.01 && ($r < 0 or $m%$r) ?
$m : $r;
print $m, ': ', sprintf ('%.3f', $total),
($m==$r) ? ' !' : '', "\n";
}
$r
}
sub vbreaker {
my ($s, $m) = @_; my $r;
for my $t (buckets ($m, $s)) {
my %f = frequency ($t);
$_ = 0;
for my $g (0..25) {
my $total;
$total += $p{$_} * $f{chr((ord($_)-97+$g)%26+97)} for 'a'..'z';
$total /= length $t;
abs ($total-0.065) < 0.01 &&
abs ($_-0.065) > abs ($total-0.065) and $_ = $g;
}
$r .= chr ($_+65);
}
$r
}
sub euclid {
$_[1] or return $_[0];
euclid ($_[1], $_[0] % $_[1])
}
sub frequency {
my $a = lc shift;
$_{$_} = 0 for 'a'..'z';
$_{$_}++ for split //, lc $a;
%_
}
sub relfrequency {
my $a = lc shift; %_ = frequency ($a);
$_{$_} /= length($a) for keys %_; %_
}
sub likely {
my $s = lc shift; my ($r, $c);
$c = @digrams; $r += ($s =~ /$_/g) * $c-- for @digrams;
$c = @trigrams; $r += ($s =~ /$_/g) * $c-- for @trigrams;
$r
}
sub solve {
my ($d1, $d2, $d3, $d4) = @_;
for my $a (1..25) { for my $b (1..25) {
return $a, $b
if (($d1*$a+$b)%26 == $d2 && ($d3*$a+$b)%26 == $d4);
}}
undef
}
sub affinbreakerOnOrangejuice {
my $in = shift; my $tries = shift;
for my $a (keys %i) {
for my $b (0..25) {
my $d = d_affin ($a, $b, $in);
push @_, "a : $a, b : $b, l : ".likely($d).", p : ".$d;
}
}
print join "\n", splice @{[reverse sort
{ likely ($a) <=> likely ($b) } @_]}, 0, $tries;
}
sub affinbreakerOnSteroids {
my $in = shift; my $tries = shift;
my %f = frequency ($in);
my @nd = map {ord ($_) - 97} reverse sort {$p{$a} <=> $p{$b}} keys %p;
my @fl = map {ord ($_) - 97} reverse sort {$f{$a} <=> $f{$b}} keys %f;
my $br = grep {$_ >= (0.5 * $f{chr($fl[0]+97)}); } values %f;
for my $i (0..$br-1) {
for (my $j = ($i+1) % $br; $j!=$i; $j= ($j+1) % $br) {
my ($a, $b) = solve ($nd[0], $fl[$i], $nd[1], $fl[$j]);
next if ! defined $a or euclid ($a, 26) > 1;
push @_, "a: $a, b: $b, p: ". d_affin ($a, $b, $in);
}
}
for my $i (0..25) {
for (my $j = $i < $br ? $br : ($i+1)%26;
($j != 0 && $i < $br) || ($j!=$i && $i >= $br);
$j = ($j+1)%26){
my ($a, $b) = solve $nd[0], $fl[$i], $nd[1], $fl[$j];
next if ! defined $a or euclid ($a, 26) > 1;
push @_, "a: $a, b: $b, p: ". d_affin ($a, $b, $in);
}
}
print join "\n", splice @_,0,$tries;
}
my $in = join '', map { chomp; $_ } <STDIN>;
if ($ARGV[0] eq 'affin') {
affinbreakerOnSteroids ($in, 5);
print "\n";
affinbreakerOnOrangejuice ($in, 5);
}
if ($ARGV[0] eq 'vigenere') {
if ((my $m = cindex ($in, 20)) == bktest ($in)) {
my $k = vbreaker ($in, $m);
print "$k : ", d_vigenere ($k, $in);
}
}
if ($ARGV[0] eq 'enigma') {
push @_, $_ . " : " . d_enigma($_, $in) for 'A'..'Z';
print join ("\n", reverse sort { likely ($a) <=> likely ($b) } @_);
}