2014/01/02

[プログラミング]暗号の国のアリスと循環アルファベット++

,
 とてもタイトルに困ったんですが…とりあえず大した内容ではないです
 
 『暗号の国のアリス』(著:結城浩)の中でシーザー式暗号というものが取り上げられています。これは、暗号化したい平文のアルファベットをそれぞれ、アルファベット順にn文字前後にずらすといったものです。例えば、"abc"なら3文字ずらしで"def"といった具合です。
 で、この暗号はBrute Force Attackで破ることが出来るのですが、練習問題を解くプログラムを書いてて「どうやって実装すればいいんだろう???」って思ったときのメモです。

実装法1 - テーブルを使う

 どうしようか迷い、最初はテーブルを使うものにしました。つまり、string配列→char配列にし、それぞれの文字のn文字先を、予め用意したテーブルから持ってきます。この場合のキモは、n文字先がテーブルのどの要素に成るかの計算です(と言っても簡単なクイズレベルですけど)。難点としては、コードが長くなるのと、for文の中にforeachが入り二重ループになりそうなことです。

参考: C# でのアルファベットの文字の配列を生成 - http://ja.softuses.com/73951

 この時、大文字(0x41~0x5A)と小文字(0x61~0x7A)を分けるのが面倒だったので、最初の文字列を.ToLower()し、小文字に揃えました(今回はこれでオッケー)。

実装法2 - 拡張メソッドを使う

 で、さっきのページでC#の拡張メソッドを見て、「なんだもっと簡単に書けるじゃん」ということで、結局次のようになりました。
static void Main(string[] args)
{
    var str = "PELCGBTENCUL";  //対象文字列
    foreach(var i in Enumerable.Range(0, 26).ToArray())
    {
        Console.WriteLine("{0} : {1}", i, 
        new string(str.ToLower().Select(c => (char)('a' + ((i + c - 'a') % 26))).ToArray()));
    }
}
 すると、以下のようになり、13文字後ろにずらす(or??文字前にずらす)と、元の平文と思われる"cryptography"が分かります。
総あたり(といっても26回)した時の例
拡張メソッドはお勉強中なのですが、実践できる機会があってよかったです。
 コードとしては、
  • str.ToLower()で、まず小文字に統一します。
  • .Select()で、変数str中の各要素(char)に対して、そのi文字先(ただし、'z' + iなどはASCIIの小文字の範囲を超えるので、i文字先(i + c)から、先頭の文字を引き(- 'a')、更に26で割ることでどのアルファベットか割り出し(% 26)、オフセット0x61のために'a'を足す。という面倒なことをしています。
  • .ToArray()でchar[]にしています。
  • new string(...)は、char[]→stringにするときの常套手段みたいです。でもnewした割りにはその変数すぐ消えるし、なんかちょっと気持ち悪いですね…
拡張メソッドのように、関数の戻り値から、また数珠つなぎのように関数を呼び出し、1行に収めるのはなかなか楽しいですね。

ぎもん

この本では、この後の使い捨て暗号(ワンタイムパッド)が解読不可な理由として、「全ての平文の候補が登場するため、どれが答えかわからない」と説明されています。これには、全てが同じ平文とかが登場するから、と例で示されています。でも、シーザー式暗号のn(0 < n < 26)文字復元をしても、結局どれが平文か分からないような気もする…?(もしかしたら平文は単語ではないのかもしれない)
 結局、元の平文が人間味のある∧既知の単語∧平文候補が1つを覗いて辞書に載ってる単語じゃないとだめなのでは…
 こればかりは、使い捨てパッドが解読不可能である数学的証明を知らないと、真実に辿り着けなさそうですね…

 それと、暗号はやはり戦争に於いても利用されたようですが(ナチスドイツのエニグマetc)、「どうやって鍵を秘密裏に運ぶか」ということに苦心したんですね。前線の兵に秘密鍵を渡す方法というのも気になってきました。

0 コメント to “[プログラミング]暗号の国のアリスと循環アルファベット++”

コメントを投稿