Web プログラミングはガチの素人なので多少のガバはご容赦ください。
TL;DR
こんなん作りました。最後に打った文字 "G" が "O" へ暗号化されているのが見えます。
そしてひらがな対応版も作ってみました。
サイト自体のソースコードはこちらです。
エニグマとは
エニグマは第二次世界大戦期にドイツが使用していた暗号機です。エニグマの詳細についてはすでに素晴らしい記事があるので割愛します。
上記記事でもおススメされていますが、サイモン・シン「暗号解読」はガチで名著です。皆さん読んでください。
技術的詳細
エニグマの実装
今回は JavaScript よりも型安全であるとされる TypeScript を使って実装しました。
ただここで問題になってくるのが、本当に正しく実装できてるのかわからんということです。そこで史実で実際に用いられた暗号文の情報がないか調べてみたのですが、うってつけのものを見つけました。
終戦後も未解読のままだった暗号を分散コンピューティングによって解読していたプロジェクトです。このプロジェクトと一致する解読結果が出てくるか Jest を使ってテストしてみました。たとえば最初に解読された暗号と同じ結果になることを確かめるテストはこんな感じになります。
test('M4 FIRST BROKEN MESSAGE', () => {
expect(
new M4(
new PlugBoard(Alphabet.capitalLatin, ['A', 'T'], ['B', 'L'], ['D', 'F'], ['G', 'J'], ['H', 'M'], ['N', 'W'], ['O', 'P'], ['Q', 'Y'], ['R', 'Z'], ['V', 'X']),
M4.rotorI,
M4.rotorIV,
M4.rotorII,
M4.rotorBeta,
M4.reflectorB,
'VAAA',
'ANJV'
).decrypt(
'NCZWVUSXPNYMINHZXMQXSFWXWLKJAHSHNMCOCCAKUQPMKCSMHKSEINJUSBLKIOSXCKUBHMLLXCSJUSRRDVKOHULXWCCBGVLIYXEOAHXRHKKFVDREWEZLXOBAFGYUJQUKGRTVUKAMEURBVEKSUHHVOYHABCJWMAKLFKLMYFVNRIZRVVRTKOFDANJMOLBGFFLEOPRGTFLVRHOWOPBEKVWMUQFMPWPARMFHAGKXIIBG'
)
).toBe(
'VONVONJLOOKSJHFFTTTEINSEINSDREIZWOYYQNNSNEUNINHALTXXBEIANGRIFFUNTERWASSERGEDRUECKTYWABOSXLETZTERGEGNERSTANDNULACHTDREINULUHRMARQUANTONJOTANEUNACHTSEYHSDREIYZWOZWONULGRADYACHTSMYSTOSSENACHXEKNSVIERMBFAELLTYNNNNNNOOOVIERYSICHTEINSNULL'
);
});
そしてテスト結果がこちらです。
$ npm run test
> reika727.github.io@1.0.0 test
> jest
PASS __test__/enigma.test.ts (11.695 s)
✓ M4 FIRST BROKEN MESSAGE (30 ms)
✓ M4 SECOND BROKEN MESSAGE (12 ms)
✓ M4 FINAL BROKEN MESSAGE (14 ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 12.132 s, estimated 17 s
Ran all test suites.
どうやらちゃんと実装できてそうです👍
UI の作成
一番大変だったのがここです。世の中には Vue とか React とか Angular とか便利なフレームワークがあるようですが、選択肢が多すぎてどれを使えばいいのかわからなかったついでに使い方もよくわからなかったので、生のスクリプトから DOM を直接弄るという昔ながら(?)の方法で動かしました。また、
この、canvas 上にエニグマを描画するところなんか丸とか線とかの部品一個一個を描画しなければならないので、必然的にコードがめっちゃ長くなってしまいます。こういうものをもっと楽に描画できる方法や、もっとスマートに UI を作る方法をご存知の方はぜひご教示ください・・・
TypeScript を用いたサイトの作り方
残念ながら現時点では TypeScript を HTML に埋め込むことはできないので、JavaScript へのトランスパイルが必要になります。これには webpack と ts-loader と HtmlWebpackPlugin というパッケージを組み合わせることで実現できます。まずソースコードがこんな感じの構成になっているとします。
project-root/
└ src/
├ index.html
├ index.ts
└ module.ts
ここで注目すべきなのは、index.html は index.ts を読み込んでいないということです。つまり <script src="index.ts"></script>
とかは書かれていません(先ほども言ったように HTML に TypeScript を埋め込むことはできないので)。
ここで webpack, ts-loader, HtmlWebpackPlugin を使います。webpack は JavaScript を一つにまとめたり CSS を一つにまとめたりすることができます。今回は index.ts と module.ts をまとめて一つのファイルにするのに使います。しかし webpack 単体では JavaScript しか扱えないので、TypeScript を読み込ませるために ts-loader を組み合わせるわけです。このようにして index.ts と module.ts をひとまとめにした main.js が自動生成されます。
project-root/
├ src/
│ ├ index.html
│ ├ index.ts
│ └ module.ts
└ dist/
└ main.js
さらに HtmlWebPackPlugin を組み合わせることで、main.js を読み込む index.html を dist/ 配下に自動生成してくれます。最終的にはこのようになります。
project-root/
├ src/
│ ├ index.html
│ ├ index.ts
│ └ module.ts
└ dist/
├ index.html <- main.js を読み込むコードが自動で追加されている
└ main.js
この dist/ を実際に Web 上に公開すればよいわけです。
ホスティング
作成したページは GitHub Pages で公開しました。また GitHub Actions を使うことで、先ほども出てきた Jest を使ったテストや TypeScript から JavaScript へのトランスパイルなども push のたびに自動で行うようにすることができます。便利ですね~。
今後の課題
- UI フレームワークの選び方と使い方を覚える
- もっとオシャンティーな Web デザインの作り方を勉強する