Elisp で AES 暗号

Emacs には標準では暗号用の pure elisp なパッケージがないみたいだし、暗号プログラムってどんなものか知りたかったので elisp で作ってみた。
openssl コマンドの暗号出力と仕様を統一して、出力を突き合わせてテストしているのでバグってはいないのでしょう。

しかし、一通り動くようになってから*1よくよく検索してみると EmacsWiki に aes.el が上がってた。気付かんかった。
気付いた時点で簡単にベンチマークしてみると、僕のバージョンよりもかなり速い。向こうは base64 もしてるのに。。最新バージョンではほぼ同等のスピードになってる。
ただ、あちらのバージョンは、暗号に短いパスワードを設定してあると、違ったパスワードを渡しても復号できてしまうという致命的なバグがあるようです。詳しくは調べていません

openssl コマンドを利用した実装

まずは openssl コマンドを使った実装を作ってみた。
aes に留まらず、des, blowfish など各種暗号が使える。

https://github.com/mhayashi1120/Emacs-openssl-cipher/

いつもはこれで終わりなんだけど、Salted__ なる文字列が何なのか気になりはじめて、暗号のことを調べ始めたら、つい elisp でやってみたくなっちゃった。

Pure Elisp による AES の実装

https://github.com/mhayashi1120/Emacs-kaesar/

byte-compile しないと遅くて使いものにならない。byte-compile することで 4KB ぐらいの小さなデータなら、おそらく大抵の環境で実用程度に動作すると思われる。
僕の環境では、暗号、復号とも 1 秒もかからなかった。
逆に小さい平文であれば外部コマンドを呼び出すコストがないので、上の openssl を使った実装よりも相当に速い。
パスワード、パスフレーズの暗号化や hex や base64エンコードしてある token などの暗号化ぐらいなら実用的な役にも立つはず。

疑似コード写経するだけなら楽だろうと思っていたのだけど、僕の数学の知識は高一レベルなので、知らない数学用語が出てきてかなり大変だった。
ガロア体とか聞いたことなかったし。

(require 'kaesar)
;; 暗号化
(defvar my-secret nil)

(let ((raw-string "ぼくの秘密"))
  (setq my-secret (base64-encode-string (kaesar-encrypt-string raw-string)))
  (clear-string raw-string)
  my-secret)

=> "U2FsdGVkX1+SwQ8QqoY5vxNhnb35C5SyghtyPJhOSAk="

(base64-decode-string my-secret)

とやると、ちゃんと訳のわからないデータになってる。*2
はてなに記事あげるために base64 encode してる。

;; 復号化
(kaesar-decrypt-string (base64-decode-string my-secret))

=> "ぼくの秘密"

Salted__

冒頭で疑問に思った salt は要するに同じ平文と鍵に大して、異なる暗号出力を得るためのもの。
ネットに転がっているサンプルプログラムでは IV (Initialization Vector) をプログラムにハードコーディングしてるものが散見されるけど、あれは IV の意味がなくなってしまうため NG。IV は平文のままで暗号文と一緒に配布してもよいので毎回生成しなおそう。

Openssl の場合は "Salted__" 文字に後続する 8 byte と password を入力として、鍵と IV を生成している。

Rijndael アルゴリズム

AES の実体は rijndael というアルゴリズム
rijndael のアルゴリズムは入力ストリームに対してルービックキューブのような操作をする。
AES の場合は入力を 4 × 4 の正方形に配置してから以下みたいな感じの操作をする。

  • 列をそれぞれ縦にちょっとずらす。
  • 行をそれぞれ横にちょっとずらす。
  • 色を一定のルールで変える。
  • 鍵をかける。(XOR で)

こういった操作を何回も繰り返す。

復号のときはこの逆をするだけ。

ちなみに最後尾のバイトに対するブロック化 (4 × 4 化) は暗号モードによって異なる。
暗号モードによってはどうやら復号に失敗したことをプログラムで検知できない。

melpa

ちょっと melpa って文言をソースに入れてただけなのに purcell さんにあっという捕捉されて mepla に登録されてしまいました。
ほんとはもうしばらくおいといてからにしようと思ってたのですが、まあいいです。
今の様子では download 数が伸びることはありませんね。先行の aes もそんなに伸びてないですし。

*1:もう 2 年前ぐらいかな?

*2:パスワードは書きません。