IchigoJamで換字式暗号

[こどもパソコン IchigoJam](https://ichigojam.net/) で換字式暗号の暗号化と復号をするプログラムを作ってみました。
※IchigoJamはjig.jpの登録商標です。

# どんな暗号か？

[換字式暗号](https://ja.wikipedia.org/wiki/%E6%8F%9B%E5%AD%97%E5%BC%8F%E6%9A%97%E5%8F%B7)とは、平文の文字を別の文字に置き換える暗号である。
今回は、英語のアルファベット大文字26字を、それぞれ別のアルファベット(たまたま同じになることもある)に置き換える。

# 準備：疑似乱数(Xorshift)

今回は、アルファベットの置き換え方を決めるために疑似乱数を用いる。
疑似乱数とは、コンピュータによる計算で求められる適当な数の並びのことであり、
最初の状態を決めると出てくる数の並びが決まる。
今回は、[Xorshift](https://ja.wikipedia.org/wiki/Xorshift)を用いる。

まず、[Wikipediaに載っているXorshiftの実装](https://ja.wikipedia.org/w/index.php?title=Xorshift&oldid=79799391)

```c
uint32_t xor128(void) { 
  static uint32_t x = 123456789;
  static uint32_t y = 362436069;
  static uint32_t z = 521288629;
  static uint32_t w = 88675123; 
  uint32_t t;
 
  t = x ^ (x << 11);
  x = y; y = z; z = w;
  return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); 
}
```

をIchigoJamのマシン語に移植した。
Xorshiftの状態(`x,y,z,w`)は、配列の最初の8要素(`[0]`～`[7]`)を用いて保存する。
また、IchigoJam BASICで使いやすいよう、返り値は0～32767とする。

```
R3 = PC + #40
' t = x ^ (x << 11)
R0 = [R3 + 0]L
R1 = R0 << 11
R0 ^= R1
' x = y
R1 = [R3 + 1]L
[R3 + 0]L = R1
' y = z
R1 = [R3 + 2]L
[R3 + 1]L = R1
' z = w
R1 = [R3 + 3]L
[R3 + 2]L = R1
' w = (w ^ (w >> 19))
R1 = [R3 + 3]L
R2 = R1 >> 19
R1 ^= R2
' w = w ^ t ^ (t >> 8)
R2 = R0 >> 8
R0 ^= R1
R0 ^= R2
[R3 + 3]L = R0
' return w & 0x7fff
R1 = #7F
R1 = R1 << 8
R1 += #FF
R0 &= R1
RET
```

以下は、これを単独で動かすプログラムである。

```
10 'Xorshift
20 POKE#700,#3F,#A3,#18,#68,#C1,#02,#48,#40
30 POKE#708,#59,#68,#19,#60,#99,#68,#59,#60
40 POKE#710,#D9,#68,#99,#60,#D9,#68,#CA,#0C
50 POKE#718,#51,#40,#02,#0A,#48,#40,#50,#40
60 POKE#720,#D8,#60,#7F,#21,#09,#02,#FF,#31
70 POKE#728,#08,#40,#70,#47
80 POKE#800,#15,#CD,#5B,#07,#E5,#55,#9A,#15
90 POKE#808,#B5,#3B,#12,#1F,#33,#13,#49,#05
100 FOR I=1 TO 20
110 PRINT USR(#700,0)
120 NEXT
```

うまくいけば、以下のように出力されるはずである。

```
17898
5862
18858
176
606
16710
5187
3884
11608
25961
12756
28197
21232
24297
22900
31957
19508
26134
16716
27526
```

なお、執筆時点において、[IchigoJam web by jig.jp](https://fukuno.jig.jp/app/IchigoJam/) ではうまく動かず、RUNするたびに違う数値列が出力される。

# 換字式暗号の実装

結論から言うと、これが今回実装したプログラムである。

```
10 ' ｶﾝｼﾞｼｷ ｱﾝｺﾞｳ
20 POKE#700,78,163,24,104,193,2,72,64,89,104,25,96,153,104,89,96,217,104,153,96,217,104,202,12,81,64,2,10,72,64,80,64,216,96,127,33,9,2,255,49,8,64,112,71:GOTO 70
30 L=0:CLK
40 K=INKEY():IF K=8 IF L=0 GOTO 40 ELSE ?CHR$(8);:L=L-1:GOTO 40
50 IF K=10 IF L=0 GOTO 40 ELSE ?CHR$(10);:RETURN
60 IF K<D OR U<K OR L=M GOTO 40 ELSE ?CHR$(K);:[40+L]=K:L=L+1:GOTO 40
70 ?"ｶﾝｼﾞｼｷ ｱﾝｺﾞｳ"
80 ?"ｱｲｺﾄﾊﾞ (16ﾓｼﾞ ﾏﾃﾞ):"
90 D=#21:U=#7E:M=16:GOSUB 30
100 IF L<16 FOR I=L TO 15:[40+I]=0:NEXT
110 FOR I=0 TO 7:[30+I]=[40+I*2]|[40+I*2+1]<<8:NEXT
120 FOR I=1 TO 16:X=USR(#700,0):NEXT
130 FOR I=0 TO 25:[I]=I:NEXT
140 FOR I=0 TO 25:P=USR(#700,0)%(26-I):T=[I]:[I]=[I+P]:[I+P]=T:NEXT
150 ?"ｱﾝｺﾞｳｶ(0)? ﾌｸｺﾞｳ(1)?"
160 X=INKEY():IF X<#30 OR #31<X GOTO 160
170 ?X-#30:IF X=#30 GOTO 200
180 FOR I=0 TO 25:[40+I]=[I]:NEXT
190 FOR I=0 TO 25:[[40+I]]=I:NEXT
200 IF X=#30 ?"ﾋﾗﾌﾞﾝ"; ELSE ?"ｱﾝｺﾞｳﾌﾞﾝ";
210 ?" (31ﾓｼﾞ ﾏﾃﾞ):"
220 D=#41:U=#5A:M=31:GOSUB 30
230 IF X=#30 ?"ｱﾝｺﾞｳﾌﾞﾝ:" ELSE ?"ﾋﾗﾌﾞﾝ:"
240 FOR I=0 TO L-1:?CHR$(#41+[[40+I]-#41]);:NEXT:?""
```

10行目：`FILES`用のタイトル
20行目：マシン語によるXorshiftの実装 (状態は`[30]`～`[37]`に保存する)
30行目～60行目：文字コードが`D`～`U`の文字のみからなる1文字以上`M`文字以内の文字列を読み込み、配列に格納する
70行目：タイトルを表示する
80行目：プロンプトを表示する
90行目：サブルーチンを用いて合言葉の入力を受け付ける
100行目～110行目：入力された合言葉をXorshiftの状態に変換する
120行目：初期状態の影響が強くならないよう、Xorshiftを空打ちする
130行目：0～25の数列を配列に格納する
140行目：数列をシャッフルする (アルファベットの置き換え方を決める)
150行目：プロンプトを表示する
160行目～170行目：暗号化か復号かの選択の入力を受け付け、分岐する
180行目～190行目：暗号化用の置き換え方から復号用の置き換え方を求める
200行目～210行目：プロンプトを表示する
220行目：サブルーチンを用いて暗号化/復号する文字列の入力を受け付ける
230行目：処理結果のタイトルを表示する
240行目：生成した置き換え方に従い、文字を置き換えて出力する

合言葉は1文字以上16文字以下であり、文字コード`#21`～`#7E`の文字の入力が許される。
アルファベットの大文字と小文字は区別される。
Xorshiftの状態が16バイトなので、16文字以下としている。

暗号化/復号する文字列は1文字以上31文字以下であり、英語のアルファベット大文字のみの入力が許される。
「31文字以下」というのは画面表示を考えた制限であり、理論上は何文字でも大丈夫である。

以下に動画およびテキストで実行例を示す。
暗号化時と同じ合言葉を入れれば正しく複号でき、1文字でも違う合言葉を入れると正しく複号できない様子がわかる。

YouTube動画 (1:13)
[![IchigoJamで換字式暗号](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/b2910c76-3903-a396-56c1-c77b57f2bd28.png)
](https://www.youtube.com/watch?v=xNFHiRGXWcM)

```
IchigoJam BASIC 1.0.0 by jig.jp
OK
Loaded 1024byte
OK
ｶﾝｼﾞｼｷ ｱﾝｺﾞｳ
ｱｲｺﾄﾊﾞ (16ﾓｼﾞ ﾏﾃﾞ):
MATUTOUYAYUMI
ｱﾝｺﾞｳｶ(0)? ﾌｸｺﾞｳ(1)?
0
ﾋﾗﾌﾞﾝ (31ﾓｼﾞ ﾏﾃﾞ):
ASUMIKANA
ｱﾝｺﾞｳﾌﾞﾝ:
HISKTRHLH
OK
ｶﾝｼﾞｼｷ ｱﾝｺﾞｳ
ｱｲｺﾄﾊﾞ (16ﾓｼﾞ ﾏﾃﾞ):
MATUTOUYAYUMI
ｱﾝｺﾞｳｶ(0)? ﾌｸｺﾞｳ(1)?
1
ｱﾝｺﾞｳﾌﾞﾝ (31ﾓｼﾞ ﾏﾃﾞ):
HISKTRHLH
ﾋﾗﾌﾞﾝ:
ASUMIKANA
OK
ｶﾝｼﾞｼｷ ｱﾝｺﾞｳ
ｱｲｺﾄﾊﾞ (16ﾓｼﾞ ﾏﾃﾞ):
ARAIYUMI
ｱﾝｺﾞｳｶ(0)? ﾌｸｺﾞｳ(1)?
1
ｱﾝｺﾞｳﾌﾞﾝ (31ﾓｼﾞ ﾏﾃﾞ):
HISKTRHLH
ﾋﾗﾌﾞﾝ:
LKJSIXLCL
OK
ｶﾝｼﾞｼｷ ｱﾝｺﾞｳ
ｱｲｺﾄﾊﾞ (16ﾓｼﾞ ﾏﾃﾞ):
MATUTOUYAYUMU
ｱﾝｺﾞｳｶ(0)? ﾌｸｺﾞｳ(1)?
1
ｱﾝｺﾞｳﾌﾞﾝ (31ﾓｼﾞ ﾏﾃﾞ):
HISKTRHLH
ﾋﾗﾌﾞﾝ:
WKUENBWQW
OK
ｶﾝｼﾞｼｷ ｱﾝｺﾞｳ
ｱｲｺﾄﾊﾞ (16ﾓｼﾞ ﾏﾃﾞ):
MATUTOUYAYUMI
ｱﾝｺﾞｳｶ(0)? ﾌｸｺﾞｳ(1)?
1
ｱﾝｺﾞｳﾌﾞﾝ (31ﾓｼﾞ ﾏﾃﾞ):
HISKTRHLH
ﾋﾗﾌﾞﾝ:
ASUMIKANA
OK
```

# 結論

IchigoJamで有名な疑似乱数生成アルゴリズムのXorshiftを実装し、
それを用いた数列のシャッフルを用いて換字式暗号を実装することができた。