わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

Rubyを用いた単一換字暗号の解読

 担当しているセキュリティの科目では,「暗号解読」のレポート課題を,ゴールデンウィーク前に課しています.
 これまで,学内Linux環境で端末を起動し,いくつかのコマンド(sed,echo,cat)を組み合わせれば,効率良く解読できるよと紹介してきましたが,Linux環境がなくなったため,今年度からは別の方法を必要とします.
 この解読のためだけに,BYOD PCにLinuxをインストールするのは,推奨できません.Windows Subsystem for Linux (WSL)も,新規インストールには通信とセットアップで時間がかかります.
 なのですが,ここ数年で「オンライン実行環境」が充実し,ブラウザを使用して,プログラムを書いて実行することが,簡単にできるようになっています.本記事では無料かつ
ユーザ登録なしで利用できるpaiza.IOを用いた解読の試みを紹介します.
 以下の本に合わせて,アルファベット26文字の単一換字暗号を対象とします.小文字(aからzまで)を平文で用いるアルファベット,大文字(AからZまで)を暗号文で用いるアルファベットとします.空白や記号類は出現しないのが慣例です(それらが含まれると解読がより容易になるという事情もあります).暗号文も,この本の記載を使わせてもらいました.

暗号技術入門 第3版

暗号技術入門 第3版

 さっそく,動かしてみましょう.https://paiza.io/ja/にアクセスして,「コード作成を試してみる(無料)」のボタンを押します.左上の「PHP」を「Ruby」に変更してから,以下の内容を貼り付けて,「実行」のボタンを押してみてください.

s = <<EOS
MEYLGVIWAMEYOPINYZGWYEGMZRUUYPZAIXILGVSIZZMPGKKDWOMEPGROEIWGPCEIPAMDKKEYCIUYMGIF
EOS
t = s.tr("YMEPODWING", "ethrginaco")
puts t

 下部に「theLoVanAthegraceZonehotZRUUerZAaXaLoVSaZZtroKKingthroRghanorCharAtiKKheCaUetoaF」と表示されます.
 このプログラムで最も重要な処理を解説します.具体的には,tへの代入の行です.等号の右では,文字列s*1を処理対象として,trという名前のメソッドを呼び出します.文字列の中に"Y"の文字があれば"e"の文字に置き換えます."M"の文字は"t"に,"E"は"h"に,...,"G"は"o"に,置き換えます.複数回,同じ文字が出現すれば,それぞれを置き換えます(だから「単一換字」の暗号なのです).すべての置き換えが終わった状態の文字列が,「s.tr("YMEPODWING", "ethrginaco")」の評価結果となり,変数tに代入される値にもなります.
 単一換字暗号の暗号化・復号・解読は,RubyString#trメソッドで実現できる,というわけです.このメソッドに与える文字列の長さを同じにするのも,大事なところです*2
 結果の文字列について,先頭は「the」ですし,途中には「one」「hot」という単語を見つけることができます.Yをeに,Mをtに,...という解読は,ある程度成功していることが見てとれます.大文字は,trメソッドで置き換えられなかった文字であり,この時点では未解読の文字と言えます.
 今回の暗号文をすべて解読するには,tへの代入の行を,以下のコードに変更します.

t = s.tr("YMEPODWIGCSQRZUALXVNKHJFTB", "ethrginaocwqusmdfyxpljkbvz")

 実行結果は,みなさんお確かめください.空白を入れて読めば,英文になります*3
 レポート課題で無事に解読ができたら,「tr("置換前の文字の並び", "置換後の文字の並び")」が,一般には2行で構成する,換字表と,同等の情報を持つことになります.
 話はここで終わりません.レポート課題で解読の最中というときには,大文字の部分は無視して,小文字にしてみた箇所のみを見て,妥当かどうか検討したいところです.
 大文字を単純に取り除くことも,Rubyで簡単に書けますが,取り除くというより,「未解読の文字がある」という情報に置き換えるべきでしょう.1文字の大文字なら1文字分,連続する大文字はその数だけ,伏せ字にします.
 そうすると,プログラムは以下のようになります.

s = <<EOS
MEYLGVIWAMEYOPINYZGWYEGMZRUUYPZAIXILGVSIZZMPGKKDWOMEPGROEIWGPCEIPAMDKKEYCIUYMGIF
EOS
t = s.tr("YMEPODWING", "ethrginaco")
puts t
puts
u = t.gsub(/[A-Z]/, ".")
puts u

 実行し,出力を見ると,先ほどの大文字小文字混じりと,空行のあとに,「the.o.an.thegrace.onehot....er..a.a.o..a..tro..ingthro.ghanor.har.ti..he.a.etoa.」と表示されます.
 追加したうち「u = t.gsub(/[A-Z]/, ".")」は,文字列tに出現する,文字"A"から文字"Z"までを,すべて"."に置き換えよ,という指示になります.
 残りのいくつかのRubyコードについて,説明しておきます.上のプログラムでは,変数sには「MEYLGVIWAMEYOPINYZGWYEGMZRUUYPZAIXILGVSIZZMPGKKDWOMEPGROEIWGPCEIPAMDKKEYCIUYMGIF」という文字列が代入されるのですが,「s = <<EOS」と「EOS」の間に,引用符なしで指定しています.これはヒアドキュメントと呼ばれます.複数行の文字列の代入に使用するのが一般的で,行ごとに引用符を書かなくて済み,改行で煩わされることもありません.毎年,レポート課題の暗号文は,複数行にしています.解読結果の解答について,1行あたりの字数そしてトータルの行数を,暗号文と同じにしておくと,チェックが(採点も)しやすくなります.
 「puts」も,メソッドです.引数と,終わりに改行を出力します.「puts t」は「puts(t)」と同じで,メソッド呼び出しにカッコは必須でないのです.「puts」だけの行は,空行の出力となります.
 本記事が,単一換字暗号を効率良く解読するための参考になれば幸いです.

*1:これは,「"MEYLGVIWAMEYOPINYZGWYEGMZRUUYPZAIXILGVSIZZMPGKKDWOMEPGROEIWGPCEIPAMDKKEYCIUYMGIF"という文字列を参照する変数s」の簡略表記で,C言語の関数を解説したhttps://linuxjm.osdn.jp/html/LDP_man-pages/man3/strlen.3.htmlでも,同じ使われ方が見られます.

*2:ただし長さが異なっていても,エラーにならず,処理してくれます.

*3:当初の「s.tr("YMEPODWING", "ethrginaco")」には不適切な置き換えがあるのですが,『暗号技術入門』の解読のところで書かれています.