XOR 解密
作者:Andrei Osipov
https://projecteuler.net/problem=59
计算机上的每个字符都被分配了一个唯一的代码,而首选的标准是 ASCII(美国信息交换标准代码)。例如,大写字母 A = 65,星号 (*) = 42,小写字母 k = 107。
一种现代的加密方法是获取一个文本文件,将字节转换为 ASCII,然后将每个字节与一个给定的值进行异或运算,该值取自一个密钥。XOR 函数的优点是,对密文使用相同的加密密钥可以恢复明文;例如,65 XOR 42 = 107,然后 107 XOR 42 = 65。
对于牢不可破的加密,密钥的长度与明文消息的长度相同,并且密钥由随机字节组成。用户会将加密后的消息和加密密钥保存在不同的位置,如果没有这两个“部分”,就不可能解密消息。
不幸的是,这种方法对大多数用户来说都不切实际,因此修改后的方法是使用密码作为密钥。如果密码比消息短(这很可能发生),则密钥会在整个消息中循环重复。这种方法的平衡点在于使用足够长的密码密钥来保证安全性,但又要足够短以便于记忆。
你的任务已经变得很简单了,因为加密密钥由三个小写字母组成。使用 cipher.txt 文件 https://projecteuler.net/project/resources/p059_cipher.txt(一个包含加密 ASCII 代码的文件)以及明文必须包含常见英语单词的知识,解密消息并找到原始文本中 ASCII 值的总和。
use v6;
my constant @common-words = <the was who not did with have does and one that>;
sub infix:<XOR>(@cipher, @password) {
@cipher Z+^ flat (@password xx *);
}
sub as-code(Str $w) {
my @x = $w.comb.map(*.ord)
}
sub as-word(*@s) {
@s.map(*.chr).join
}
sub guess-password(Str $w, @cipher) {
my @word = as-code $w;
my @chunks = @cipher.rotor((@word.elems) => -(@word.elems - 1));
my %tries;
my $offset = 0;
for @chunks -> @chunk {
my @password = @chunk[^3] XOR @word;
my $password = as-word @password;
next unless $password ~~ /^^ <[a..z]> ** 3 $$/ ;
my $decrypted = as-word @cipher[$offset .. *] XOR @password;
my $count = [+] do for @common-words.grep({$_ !~~ $w}) -> $word {
elems $decrypted ~~ m:g:i/$word/
}
%tries{$password} += $count if $count > 0;
return %tries if $count > @common-words.elems;
$offset += 1;
$offset div= $w.chars;
}
%tries;
}
sub MAIN(Bool :$verbose = False,
:$file = $*SPEC.catdir($*PROGRAM-NAME.IO.dirname, 'cipher.txt'),
:$word = @common-words[0],
:$pass is copy,
Bool :$test = False) {
return TEST if $test;
die "'$file' is missing" unless $file.IO.e ;
my @cipher = map *.Int, split /<[,]>/ , slurp $file;
unless $pass {
my %variants = guess-password $word, @cipher;
$pass = %variants.pairs.max(*.value).key;
say "The password is more likely to be '$pass'. " if $verbose;
}
my $decrypted = as-word @cipher XOR as-code($pass);
say "The message: {$decrypted.perl}" if $verbose;
say [+] as-code $decrypted;
say "Done in {now - BEGIN now}" if $verbose;
}
sub TEST {
use Test;
is as-code("abc"), [97,98,99], "as-code works";
is as-word(100,101,102), "def", "as-word works";
is as-word([79,59,12] XOR [103,111,100]), "(Th", "XOR works";
done;
}
Perl 6 示例