いきなりですが問題です.
シーザー暗号で暗号化された「gzssd」を解読して,英単語を求めなさい.
和歌山県立向陽高等学校へ行き,「古典暗号・現代暗号の解読にチャレンジ」と題して,約40名の生徒に授業を行いました.2コマ連続で,最初のコマ(1コマは70分)では暗号に関する概念・用語といくつかの暗号アルゴリズムを紹介し,あとのコマで5問の暗号解読に取り組んでもらいました.
上記は第2問です.第1問の答え合わせは,PCを使ってこちらで実演しました.時間をおいて「gzssd」については,挙手で大部分の人が解読できたというのを確認してから,「せーの」のあとに言ってもらいました.「sleep」が大勢を占めたところで,「他にありませんか?」と問いかけると一人が挙手し,「bunny」を答えてくれました.
暗号文の字数が短いと,平文の候補が複数,発生し得るという事例です.以前にも記事にしています.
ここからが本題です.高校生向けには,どのようにして答えを求めてもらうのがいいかと,考えました.解き方の強制はしません.思い浮かぶのは「紙と鉛筆」「PC」です---今回の授業,PC教室での実施が前提でした.
「紙と鉛筆」については,「wagahai」という平文から「zdjdkdl」という暗号文ができる,というのを,先のコマの授業資料に入れて,あいだの「xbhbibj」「ycicjck」を手書きしてもらいました.最終的には次のようになります.「wagahai」を所与とし,「wの次はx」「aの次はb」としていくと,2行目以降が作られます.
wagahai xbhbibj ycicjck zdjdkdl
「wagahai」を「zdjdkdl」にする場合,次の文字にする(ずらす)回数が3で,この数値が鍵となります.アルファベット26文字を対象としたシーザー暗号においては,鍵は1から26まで想定でき*1,恒等写像となる26も鍵とみなすのがよいでしょう.「27回ずらす」は「1回ずらす」と同じになるので,27以上は鍵としません.また「gzssd」の解読にあたっては,鍵は未知であり,上記の要領で,ずらすか戻す*2のを26回繰り返すと,5文字で26行の並びの中に,英単語が現れるわけです.
ずらす操作は,文字ごとに独立に行えます.例えばつぎのように書いて,2列目,3列目,…と書き出していくのでも,いいということです*3.
gzssd h i j k l m n o p q r s t u v w x y z a b c d e f
ここからは授業を離れた検討です.「PC」を使用して,効率的に,また「gzssd」以外でも,ずらして単語の候補を求めるには,どうすればいいについても,考えていました.
今回の授業でHTMLファイルを用意し,研究室のサーバに置いて,高校からアクセスしてもらいました.このファイルには5問の問題のほか,それぞれを解くための計算用紙がわりのテキストエリア*4を配置していました.そしてHTMLファイルには,<script>と</script>のあいだに,JavaScriptのコードで,単一換字暗号の置換を効率良く行うため,次のように定義された関数を書いていました.
const tr = (s1, s2, s3) => { const a1 = s1.split(''); const a2 = s2.split(''); const a3 = s3.split(''); let h = {}; for (let i = 0; i < a2.length && i < a3.length; i++) h[a2[i]] = a3[i]; return a1.map(e => h[e] ? h[e] : e).join(''); }
「tr」はLinuxなどで使えるコマンドに由来します.ブラウザ上では,F12キーを押して表示されるDevToolsのConsoleに,tr("wakayama","wkym","kymw")を貼り付けてEnterキーを押すと,'kayamawa'と表示されます."wakayama"という文字列のうちwをkに,kをyに,yをmに,mをwに置き換えます。
ところで数学的には,シーザー暗号は単一換字暗号に真に含まれます.課題と解答例を作成する中で,次のようにすれば,「gzssd」の次から26回分,ずらした結果が出力できることに気づきました.
m="gzssd";for(i=1;i<=26;i++)console.log(m=tr(m,"abcdefghijklmnopqrstuvwxyz","bcdefghijklmnopqrstuvwxyza"))
中のtrの呼び出しは,変数mを対象の文字列として,aをbに,bをcに,...,zをaに,置き換えます.結果をmに代入し,そしてconsole.logに出力します.より丁寧に書くなら,mの代入とconsole.log(m)の2つに分けるべきですが,そうするとforループの処理対象が複数の文となるため,{と}を書く必要も出てきます.
"bcdefghijklmnopqrstuvwxyza"は,そのまま打ち込むと間違えやすいですが,その前の"abcdefghijklmnopqrstuvwxyz"を打ち込んでから,コピーして貼り付けて,aを削除してzの後ろで打ち込むだけです.
「高校からアクセス」について,授業の1週間ほど前に高校を訪れ,動作確認しました.向陽高校の最寄り駅は,貴志川線の日前宮駅でして,自分にとっては通勤路の途中なのでした.
ブラウザから,Yahoo!やWolfram Alphaなどにもアクセスできました.Wolframがあれば,RSAの計算も一通りできますが,分かりやすさを優先して自作のJavaScript関数と解説文をもとに計算してもらいました.「gzssd」の解読を,検索して見つける生徒がいるとまずいので,上でリンクした2つの当ブログ記事は,授業当日には「Temporarily hidden」としました.
前日には,学内の1年生向けプログラミング科目の課題で見られるとまずいので,以下のページを「Temporarily hidden」にしていました.翌日アクセスログを見たところ,課題公開から提出期限までのあいだに,いくらかアクセスがありました.
*1:0から25まで,と考えることもできます.「0回ずらす」が「何もしない」と同等視できるという点で,よさそうに見えましたが,手作業において「0回ずらす」のは分かりにくく,最終的に「1から26まで」を採用しました.
*2:wikipedia:対称群に基づくと,シーザー暗号は巡回置換です.「n回戻す」と同じことを行う「m回ずらす」操作があることを意味します.
*3:横のものを縦に見ることができるという点で,面白かったのですが,この解き方は授業で紹介しませんでした.4×2=2×4を,他の暗号解読のヒントとして問題文に入れていました.
*4:右下をドラッグ&ドロップすればサイズを変えられることも,授業中に案内しました.