[Piet]カエサル暗号プログラム

今回はカエサル暗号を作るPietプログラムを作ってみました。

カエサル暗号とは

以下、英語(アルファベット)の文を暗号化する前提で説明します。

カエサル暗号とは、平文(暗号化前の文章)の各文字を、アルファベット順に一定の文字数だけずらして暗号文を作る手法です。

1文字ずらす場合は、平文に出てくる"A"を"B"に、"B"を"C"に、…、"Z"を"A"に、それぞれ変換します。

2文字ずらす場合は、平文に出てくる"A"を"C"に、"B"を"D"に、…、"Y"を"A"に、"Z"を"B"に、それぞれ変換します。

たとえば、"HELLO FOX"という平文を、カエサル暗号で暗号化することを考えます。暗号化の際にアルファベット順に5文字ずらすとします。5文字の場合は、"H"は"M"に、"E"は"J"に、…と変換されるので、暗号文は、"MJQQT KTC"となります。

カエサル暗号はわかりやすいですが、簡単に解読されやすい暗号でもあります。 アルファベットは26文字しかないため、例えば、1文字ずらす場合と27文字ずらす場合では同じ暗号化結果が出ます。 従って暗号化のパターンは、ずらす文字数が1文字、2文字、…、25文字の25通りしかありません。 (26文字ずらすと、ずらした結果が平文と一致するので、暗号になりません。) 何文字ずらしたかわからなくても、最悪25通り全ての可能性を調べ上げれば解読できるのです。

カエサル暗号を実装する

暗号化のフローは次のようになります。

  1. 平文を入力する(ピリオドが入力されたら終了)
  2. 暗号化のときにずらす文字数を入力する
  3. 暗号化ループ開始
  4. 全ての平文が暗号化されていたら終了
  5. 未暗号の平文のうち、先頭1文字を取り出す
  6. 英字なら暗号化して出力、そうでないならそのまま出力
  7. 暗号化ループ先頭に戻る

この処理をPietソースコード生成用のコードで書くと次のようになります。

push1 dup sub

:CaesarCipher_input_loop
inc dup push3 push2 roll push1 add push2 push1 roll goto:CaesarCipher_input_isperiod

:CaesarCipher_input_isperiod
dup push2 dup dup dup dup push1 add mul mul mul push1 sub mul sub not if:CaesarCipher_input_key:
pop goto:CaesarCipher_input_loop

:CaesarCipher_input_key
pop inn

:CaesarCipher_check_key
dup push1 dup sub gt if:CaesarCipher_encription:
push2 dup dup dup push3 mul mul mul add add goto:CaesarCipher_check_key

:CaesarCipher_encription
dup outn push2 push1 roll

:CaesarCipher_encription_loop
dup if::CaesarCipher_end
dup push3 push1 roll push2 add push1 push2 sub roll
dup push3 push2 roll dup push4 push3 roll goto:isalpha

:CaesarCipher_encription_isalpha
dup if:CaesarCipher_encription_case_alpha:
pop outc pop push2 push1 roll pop
goto:CaesarCipher_encription_next

:CaesarCipher_encription_case_alpha
push2 dup mul dup mul mul push3
dup mul dup mul push2 push1 roll sub dup
push4 push2 roll add push2 push1 roll sub
push2 dup dup dup push3 mul mul mul add mod add
outc push2 push1 roll pop goto:CaesarCipher_encription_next

:CaesarCipher_encription_next
push2 push1 roll push1 sub goto:CaesarCipher_encription_loop

:CaesarCipher_end
pop pop end

#-------
:isalpha

:isalpha_upper
dup dup push2 push3 push4 add mul
dup push1 sub push2 push3 add mul push1 sub
push2 push1 roll push1 add push2 push3 mul mul push1 add
push4 push1 roll

:isalpha_upper_judge
gt push3 push1 roll gt add push2 sub if:isalpha_lower:isalpha_UC

:isalpha_lower
dup dup push2 dup dup dup dup mul mul mul mul dup push3 mul
push2 push1 roll push2 dup mul mul push5 sub push4 push1 roll

:isalpha_lower_judge
gt push3 push1 roll gt add push2 sub if:isalpha_not_alpha:isalpha_LC

:isalpha_not_alpha
push1 push1 sub goto:isalpha_end

:isalpha_UC
push2 push1 sub goto:isalpha_end

:isalpha_LC
push1 push2 sub goto:isalpha_end

:isalpha_end
goto:CaesarCipher_encription_isalpha

ちょっと長いですが解説します。 処理は大まかに「ラベル」ごとに分割できます。「ラベル」とはコロン「:」で始まる文字列のことで、goto命令やif命令のジャンプ先になります。 例えば、先頭付近にある「:CaesarCipher_input_loop」や「:CaesarCipher_input_isperiod」はラベルです。

各ラベルの処理内容は次のとおりです。No.には先ほどの処理フローの対応する項目番号を記入しています。

No. ラベル名 説明
:1 開始地点。スタック初期化
1 :CaesarCipher_input_loop 平文入力ループ開始。入力された文字を受け取る
:CaesarCipher_input_isperiod 入力された平文の文字がピリオドか判定する(※1)
2 :CaesarCipher_input_key 暗号化時にずらす文字数を入力する
:CaesarCipher_check_key ずらす文字数が負の数なら、正の数になるまで26を足す
3 :CaesarCipher_encription 暗号化開始準備
3~5 :CaesarCipher_encription_loop 暗号化ループ開始。未暗号化の平文があるか判定(※2)
6 :isalphaで始まるラベルたち 次に暗号化する文字が英字か判定する
:CaesarCipher_encription_isalpha 処理分岐(※3)
:CaesarCipher_encription_case_alpha 暗号化して出力し「:CaesarCipher_encription_next」に進む
7 :CaesarCipher_encription_next 暗号化ループ終わり。スタックを調整し「:CaesarCipher_encription_loop」に戻る
:CaesarCipher_end プログラム終了

分岐詳細:

  • ※1:
    • ピリオド以外なら平文入力を継続(「:CaesarCipher_input_loop」に戻る)
    • ピリオドなら次の処理へ(「:CaesarCipher_input_key」に進む)
  • ※2:
    • なければ終了(「:CaesarCipher_end」に進む)
    • あれば次に暗号化する文字を準備して「:isalpha」に進む
  • ※3:
    • 英字なら「:CaesarCipher_encription_case_alpha」に進む
    • 英字でないなら、そのまま出力して「:CaesarCipher_encription_next」に戻る

また、ラベル間の処理の遷移を図にすると次のようになります。

カエサル暗号処理のラベル間の遷移図

いくつかのラベルは内部に条件分岐やジャンプ命令を含んでおり、内部でもループが発生しうるため、 薄い灰色の波線ブロックでその様子を表現しています。先ほどの説明と合わせると、プログラムがどのような順に処理をしているのか、なんとなくイメージがつくのではないかと思います。

出力結果

このプログラムをGridPietGeneratorに入力してPietソースコードを自動生成すると次のようになります。

ソースコード(原寸大)

画像サイズは223 × 206でした。

npietで動作確認してみます。

./npiet -tpic CaesarCipher_raw.png
? AbCDefG:HijklMn/OpqrStu vWXyZ.3
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 3DeFGhiJ:KlmnoPq/RstuVwx yZAbC.

私の環境だとこのような結果になりました。 なぜか暗号化後の文章の先頭にずらす文字数の3が出ています (そういう仕様だったかもしれませんが…)が、それ以外を比較すると

AbCDefG:HijklMn/OpqrStu vWXyZ.
DeFGhiJ:KlmnoPq/RstuVwx yZAbC.

となります。英字部分のみ3文字ずれて暗号化されていることがわかります。 大文字・小文字の対応が取れていることや、英字以外の文字がそのまま出力されていることもわかります。

これで暗号化し放題です。

遊んでみる

「hello, world!」を8文字ずらして暗号化してみます。

./npiet -tpic CaesarCipher_raw.png
? hello, world!.8
? ? ? ? ? ? ? ? ? ? ? ? ? ? 8pmttw, ewztl!.

「pmttw, ewztl!.」と暗号化されました。

この暗号文を受け取った人は、8文字逆方向にずらせば元の平文を復元できます。 何文字ずらすかあらかじめ取り決めておく必要があります。

逆方向にずらす場合はずらす文字列として-8を入力すればOKです。

./npiet -tpic CaesarCipher_raw.png
? pmttw, ewztl!.-8
? ? ? ? ? ? ? ? ? ? ? ? ? ? 18hello, world!.

無事平文が出てきました。やったね!

終わりに

今回はカエサル暗号をPietにしてみました。 今までより格段に複雑なので、雰囲気を感じ取っていただければ良いと思います。

次回はさらに複雑なビジュネル暗号を扱ってみます。

それでは、eqq kag msmuz!.