Problem 59 (Project Euler) [原文]
暗号化鍵は3文字の小文字である. cipher1.txtは暗号化されたASCIIのコードを含んでいる. また, 平文はよく用いられる英単語を含んでいる. この暗号文を復号し, 平文のASCIIでの値の和を求めよ.
ちょっとしたハッカー気分でどうぞ。
要は、英小文字3文字からなるパスワードを用意し、先頭バイトからXOR演算を繰り返し行って、文字に復号してみろ…ということです。
まず先頭の何文字か分だけを復号し、「ありふれた英単語」が含まれるか否を確認してパスワードを決定します。全てを復号して答えを求めるのはその後がよさそうです。
最初に何バイトぐらいを復号するか、また「ありふれた英単語」として何を選ぶのかの2点で少し試行錯誤が必要でした。わかってしまえばなんでもないのですけど…。
復号後の"cipher1.txt"はちょっと意地悪な書き出しになっています。
begin_time = Time.now
########################################
# 試行錯誤が必要かも…
#---------------------------------------
# ありふれた英単語もしくはその断片
reg = /\s?[T|t]he\s/
# 最初に取得するサンプル文字列のバイト数
test_bytes = 40
########################################
$cipher = Array.new(0)
open("cipher1.txt", "r") do |f|
$cipher = f.read.split(/,/).map!{|v| v.to_i}
end
# 暗号を復号した文字列を返す
def decode(i, j, k, limit = 0) # limit 復号バイト数,0なら全文
str = ""
$cipher.each_with_index do |n, m|
break if limit > 0 && m > limit
case m % 3
when 0
ascii = n ^ k;
when 1
ascii = n ^ j
when 2
ascii = n ^ i
end
str += ascii.chr
end
str
end
str = String.new
pwd_range = ("a"[0].."z"[0])
pwd_range.each do |i|
pwd_range.each do |j|
pwd_range.each do |k|
if reg =~ decode(i, j, k, test_bytes)
str = decode(i, j, k)
ans = 0
(0...str.length).each{|v|ans += str[v]}
puts str
puts "answer : #{ans}"
puts "Pwd : #{k.chr}#{j.chr}#{i.chr}"
puts " -- #{Time.now - begin_time}sec"
exit
end
end
end
end