javascriptでSHA-512

javascriptでSHA-512を計算するスクリプトを作成しました。そして2049通りのテストケースを作成し、各ブラウザでテストしました。

これにより、スクリプトによるダイジェスト認証が幾分強固になります。

はじめに

javascriptでMD5を実装し、それを用いてjavascriptとPHPによるダイジェスト認証を実装しました。(参照「javascriptでMD5」、「javascriptのMD5を用いたダイジェスト認証」。)

後者のダイジェスト認証は、HTTPのダイジェスト認証の方式をそのまま実装しています。サーバー側のパスワードファイルは、htdigestで作成したものをそのまま使えます。

しかし、ふと思いました。

「MD5より強力なハッシュ関数にすれば、HTTPのダイジェスト認証より堅牢になるのでは?」

調査

ハッシュ関数

ハッシュ関数についてGoogle先生に尋ねると、「Keccak」(なんて読むの?ケッカク?)というハッシュ関数が、2012年にNISTのSHA-3として選定されたのこと。

このアルゴリズムもWEBで公開されているのですが、サーバー側の一般的なPHPには組み込まれていません。(GitHubにはあるらしいですが。)

ということで、SHA-3はあきらめ、その次に堅牢そうなSHA-512を実装することにしました。

ハッシュ値の堅牢さ

調査の過程で、興味深い記事を見つけました。

dit+さん「最終回:そのパスワードで大丈夫? ~ GPGPUによる高速パスワード解析

「パスワードを、グラフィックボードのGPUでアタックすると、同じ時間でCPUの40倍可能」というシリーズの記事の最終回です。

この記事の「表.ハッシュ解析速度」よると、SHA-512にすることで、同じ時間でのアタック回数はMD5の約40分の1に減らせそうです。(GPUが帳消しですね。)

この記事の後半には、各ハッシュ関数による、パスワード文字数と総解析時間の表が載っており、大変参考になります。但し「ブルートフォースで全パターンを解析するのにかかる時間」とのことですので、実際の解析成功時間は、期待値が総解析時間の1/2で、分布がまっ平らな確率分布になることにご注意下さい。

何はともあれ、MD5からSHA-512にする意義はあるようです。(本当は一足飛びにSHA-3にしたい。。。)

アルゴリズムと実装事例

アルゴリズムは「RFC 6234」にあります。

ざっと眺めると、これもMD5と同様、そう複雑なアルゴリズムではありません。一点、レジスタが64bitで加算があるので、32bitずつ扱うときの桁上りが面倒そうです。

さらには、MD5を作った時には見つけられなかったのですが、Google Codeに「crypto-js」という、暗号化アルゴリズムを実装したjavascript のライブラリがありました。これにはMD5、SHA-2のみならずSHA-3も実装されています。

しかし以前のMD5の実装のフレームに簡単に乗っかりそうだったので、車輪の再発明をする理由を無理やり考えて、作って見ることにしました。

しかし自作する最大の理由「文字をUTF-8で扱う」ことについても、コーディング後に「どうやらcrypto-jsもそうらしい」と気づいたところで後の祭り。

コードの作成

ビット演算について

javascriptでのビット演算に関しては、「javascriptでMD5」の「javascriptでビット演算」の節にも書きました。

しかし今回、64bit加算における、下位32bitの演算による桁上りを実装する際にちょっとハマりました。

桁上りは、単純に下位32bitを足し算してあふれた分を0x100000000で割ってやれば良い、と思っていました。しかし加算中の項の中には、負になるものがあり、うまく行きませんでした。

試しに以下のようなコードを実行してみます。

var n_ent = 0x80000000;
var n_32bit1 = n_ent & 0xffffffff;
var n_32bit2 = n_ent >>> 0;
console.log('and  : 0x ' + n_32bit1.toString(16) + ' (' + n_32bit1.toString() + ')');
console.log('shift: 0x ' + n_32bit2.toString(16) + ' (' + n_32bit2.toString() + ')'); 

結果は以下になります。(chromeにて実行。)

and  : 0x -80000000 (-2147483648)
shift: 0x 80000000 (2147483648) 

32bitに丸めるつもりで「| 0」(OR 0)や「& 0xffffffff」などをすると、MLBが参照されて負の数になるようです。

「>>> 0」の場合は、期待通りに正の数に丸められます。

ということで、下位32bitの加算をする場合は、各項を「>>> 0」で丸めています。上位32bitの場合はビットの値が合えば良いので丸めません。

シフトによる丸めは、crypto-jsのsha512.jsを参考にさせて頂きました。またこのコードで使われている桁上りの方法も一部で使っています。

実装

実装したコードはこちらをご参照下さい。:yjd_sha512.js(文字コードはUTF-8です。)

Uint32Arrayを使っているので古いブラウザ(IE9以前とか)は未対応です。m(_ _)m

yjdmd5.jsをコピペして作ったので構造はほぼ同じですが、MD5と違って、入力をビッグエンディアンで扱っています。

(この記事もMD5の時の稿をコピペして書いています。笑。)

テスト

テストの入力は0文字から1024文字までのランダムなASCIIと漢字の文字列(2049ケース)としました。テストケース(入力と正解)はPHPで書いたスクリプトで生成しました。

テストケースはこちらをご参照下さい。:sha512testcase.js (文字コードはUTF-8です。)

テストドライバはこちらです。:sha512test.html

ボタンを押してもテスト実行時間が表示されるだけなので面白くありませんが、ソースを見るとちゃんとテストを実行していることがお分かり頂けるかと思います。

しかしUTF-8のテストは漢字だけなので、4byteコードの検証はなされていません。

(この記事と同様、テストケースの生成やテストドライバもMD5の時のものをコピペなので楽ちんです。もしかしたらどこかにmd5という文字列が残っているかもしれません。)

テスト結果

Windows 8.1にて、以下のブラウザでテストした結果、OKでした。

  • Chrome 40.0.2214.91 m
  • FireFox Developer Edition 37.0a2 (2015-01-24)
  • IE 11.0.9600.17498
  • Safari 5.1.7 (7534.57.2)
  • Opera 26.0.1656.60

またMD5の時は数10msec-数100msecであったテスト実行時間が、SHA-512では数秒になっています。

ダイジェスト認証スクリプトへの組み込み

前回の「javascriptのMD5を用いたダイジェスト認証」に書きました、スクリプトによるダイジェスト認証にこのSHA-512を組み込むのは難しくありません。

javascript側は、前回のコード中の関数コール「yjd_get_digest(…)」を、今回のyjd_sha512.js中の関数「yjd.sha512.getDigest(…)」に置き換えます。そして「yjd_md5(…)」の代わりに「yjd.sha512.getString(…)」を使います。(さらに「yjd_create_nonce」と同じ関数、「yjd.sha512.createNonce」があります。)

PHP側は、ライブラリ「digest.php」中の関数「AuthDigest」にて、コールしている組み込み関数「md5(…)」を「bin2hex(mhash(MHASH_SHA512, …))」に置き換えるだけです。

まとめ

javascriptでSHA-512を計算するスクリプトを作成し、テストしました。

そして前回レポートしました、javascriptとPHPで行うダイジェスト認証において、SHA-512を組み込む方法を示しました。

スクリプトでダイジェスト認証を行う意味は前回書きましたが、それにまた一つ書き加えられます。

「HTTPのダイジェスト認証よりは幾分堅牢」

最後になりましたが、本稿内で参照しているコード(yjd_sha512.jssha512testcase.jssha512test.html )は、自由に利用、改変、再配布していただいて構いません。(そのうち再利用可能なようにパッケージします。その際のライセンスは未定。)

しかしcrypto-jsの方が、お手軽で信頼性が高いでしょうかね。

まあ、ダイジェスト認証を目的に、ダイジェスト生成やnonceの生成とワンパッケージになっているところに、車輪の再発明の意味があるのかもしれません。

コメントを残す