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; }