2020年4月18日土曜日

HMACと暗号の実装

ハッシュを作ったので次はHMACなど作ってみた。
HMACはハッシュを使ってパスワード認証的なものもつけているので改変されていないかなどで有利なのかどうなのか。JavaではMacという分類のあれこれ。
ハッシュごとのハッシュ長は取得できてもブロック長やら何やらを取得するのがJavaの標準APIにはなさそうなので自作ハッシュではそのあたりも拡張しておく。
HMACに適当なハッシュを載せられるので、MD2とか対応してなさそうなのをあわせてみてもいいし、SHA-3ではべつのアルゴリズムKMACを使うようだけどもHMACでも問題なく対応しているし、仕様にもあるはず。 JDKにはまだHMAC-SHA3とかなかったかも。
HMAC-MD5とか、HMAC-SHA-256とかいろいろできるので広がる。
ただ、暗号という位置づけなのか、何もしないとHMACなどMac系のアルゴリズムはJavaに登録できない感じだったので、署名などつけないといけないのかもしれず、暗号だけのライブラリには分けてみたけど署名を試すのはまた次回。
登録しなくても、自作すればどうにでも使えるので動作は問題ない。
他にもMac系のものがあるようなので、KMacとか作ってみる予定は未定。SHA-3と別の資料なのでまた読むのが大変。

暗号

作る予定ではなかったかもしれないけど、ハッシュをいろいろ作ってみた勢いで暗号にも手を出してしまった。
詳細ではなくどんなのがあるかくらいのメモでいいかな?

ブロック暗号、ストリーム暗号


暗号の基本、大まかにはブロック単位の暗号と、バイト単位でデータと(疑似)乱数をXORで暗号を作るストリーム暗号とがある。他のもあるが、大量のデータを暗号化するにはこのふたつかな。よく使われるのはブロック暗号なのかどうか。ブロック暗号も組み合わせ方によってはストリーム暗号として使うこともできる。逆にストリーム暗号の長さを固定してブロック暗号として使えるのか、というと使える、がビット反転では弱くないのか謎。暗号強度に差が出る場合があるのでそれぞれのアルゴリズムで判断した方がいいかもしれない。
ブロック暗号もストリーム暗号もJavaのストリームとして使えるようにしておくと便利なのかもしれないので基本の部分は共通の設計にしておこう。
ストリーム暗号ではXORするだけなので、最後は必要な長さで切り取って終わらせられるのだけど、ブロック暗号ではデータの長さとブロックの長さとで端数が出てしまうので、PKCS#5などのPaddingの埋め方もいろいろあるよというところぐらいまで。
PKCS#5のPaddingでは、最後に長さの数値を繰り返し埋め込んで、複合化したときにその長さを読んで切り捨てる、という方式がPKCS5Paddingとかいう簡単な方式。

DSAは実装も暗号?


暗号はDSAからいってみよう、と思ったら大変。バイト単位で計算するんではなくてビット単位で並び替えてみたり、それなりに難しい箇所がたくさん。
DSAはもう廃止されているアルゴリズムなので、いろいろ参考にできるものが少なくて大変。まずテストパターンが見つからないので合っているのかどうかがわからない。 内部状態は紙の資料をコピーしたようなのが1つ見つかったので試してみることはできたけども。
というわけでDSAは、要所要所でどれが合ってるのか探るのに苦労した。
乱数表は、わかりやすく展開できるのにいつまであの形なんだろかという疑問もあり。コードを書きにくいのは、何の得にもならないと思うのだけど。そういう点が何カ所かあった気がする。暗号の練習には最適かどうか謎。

暗号モード

で、とりあえず動いたところでブロック暗号には暗号アルゴリズムとは別にモードというものもあったりなかったり。ECBとかCBCとか。単純に暗号コードを潜らせただけでは1つのブロックを暗号にはできてもつづくブロックを同じように暗号化していればバレてしまう。難しい暗号になるわけではないのでなるべく均一に解読されやすそうにならないように、いろいろと前後混ぜていく処理など。
それもHMACのように暗号とは別に作っておくことで、どの暗号とも組み合わせが効くので便利。ECBというのは何もしないモードかな。暗号自体にもNULL暗号という何もしない暗号があるので間違って使わないようTLSなどでは無効化してあったりなかったり。
暗号モードによってはブロック暗号をストリーム暗号にすることもできる。これはまた便利な場面も出てくるかも。
CBC,ECB,CFB,OFB,CTR,PCBCぐらいのものを作ってみた気がする。

ブロック暗号とストリーム暗号、のモード


ブロック暗号を使ってストリーム暗号の乱数を作っているのがCFBとかOFBとかのモード。これでブロック暗号でもバイト単位での暗号が可能。ビット単位で反転させる攻撃には弱そうだけども。CFB,OFBをとりあえずストリームモードにも対応してみた。CTRも対応できそうか。
ストリーム暗号専用の暗号はまだ実装してみていないのでまたあとで。KDDIの KCipher2作ってみようかと思う。

速度が出ない?

データをファイルなどから読んで、ハッシュや暗号にブロックサイズ単位で投げつける処理をまとめて作っていたのだけど、なかなか処理速度が上がらなかった。出力先のOutputStreamにもブロックサイズでしか出力していなかったからで、ある程度まとめてから吐き出す処理に変えると速くなった。ハッシュのときは出力先がなかったのでいろいろと改良しておいた。
モードによって連続して暗号化できるわけでもないので暗号を単純にまとめて速度アップもやりにくい。いろいろと難しい。

ストリームに暗号をはさめばよい

という考えでFilterOutputStreamっぽいものを書いたら速度が出なかったので、次はwriteした単位でまとめてから次に渡すようにしてみる。
パディングに引っかかりそうなデータを残して暗号化にかけ、送り出す、というのを暗号、復号、InputStream、OutputStreamの組み合わせで作ってみるといろいろ楽になる。
JavaのCipherにラップして放り込んでみたら復号側の処理単位が途中データを出してくれなくてあれでだめだった。暗号と復号をひっくり返すModeっぽいのも作っておくと楽しい?

AESは数学なあれ

AESは難しい。まず資料だけ見て書けるものではなかった。ガロア体とか有限体とかいうのが謎だったので調べまわり、ようやく動くものができた。行列の計算ぐらいは基礎だけどもAESでは桁が繰り上がらないとかガロア体特有でいろいろ難解。
動くようになれば最適化しがいのあるガロア体でもあり。
2ビットや4ビットの数と掛け合わされているので減らせる計算もある。
説明どおりに作れば鍵長128bit,192bit,256bitには対応できる。鍵は初期時に全パターン生成しておく。それは簡単。byte単位でもintサイズでもいいが、最初はバイト単位で作ってみたり。
次にsboxの生成方法を調べてみたがこれはほとんど正解なし。書籍でもお茶を濁したようなものばかり。正解らしきものは見つけたので使わせてもらう。
AESの中の処理はガロア体で掛け算、ガロア体で変換、シフト、鍵で混ぜの4種類くらい。

AES(バイト単位で暗号化するよう書いてみた)でJDKのAESと速度比較してみたら、完敗だったのでintで処理するように書き直して互角ぐらいの速度まで持ってきた。
KCipher2ではMixColumnsがテーブル化されてしまっていたのでAESでも真似てみるとたぶん最速の状態になった。

KCipher-2

九州大学とKDDI研究所とが作ったというストリーム暗号、AESと部分的に似ているが微妙に異なる処理なので、合っているのかいないのがも微妙にわかりずらいので難航。いつできるかな…。元の資料よりRFC 7008見た方がいいのかも?

0 件のコメント:

コメントを投稿

Java API for JSON Processing,JSONBと比較してみた

ABNFパーサを作ったおまけでてきとーにRFCだけ見てなんとなく作ったJSON実装(SoftLibJSON)がどれくらいJava標準(Java API for JSON Processing と JSONB)に近いのかなと思い、比較してみた。 結果、一応元のRFCがあるのでほぼそ...