ログ日記

作業ログと日記とメモ

ハッシュ関数がよくわからない

cryptはdesがどうのこうのでダメだという話がよくあるが、cryptとmd5sha1やhashの違いがよくわからない。

<?php
$str = 'foobarbaz';
echo md5($str) . "\n";
echo hash('md5', $str) . "\n";
echo crypt($str) . "\n";

echo sha1($str) . "\n";
echo hash('sha1', $str) . "\n";
php hash.php
6df23dc03f9b54cc38a0fc1483df6e21
6df23dc03f9b54cc38a0fc1483df6e21
$1$2vFnj56C$KGzeZFN3T4qTKvH8Wl5hE/
5f5513f8822fdbe5145af33b64d8d970dcf95c6e
5f5513f8822fdbe5145af33b64d8d970dcf95c6e

うーん。どれを使えばいいのやら。
cryptだけ何らかの暗号化方式を使って更に加工しているみたい。


基本的にはMD5アルゴリズムを元に実行されます。


0x00 パスワードを pw, 塩を salt, "$1$"を magic と定義します。
0x01 [pw, salt, pw]という文字列を作ります。これを S1 とします。
0x02 S1 を MD5で暗号化します。出力結果を M1 とします。
0x03 [pw, magic, salt]という文字列を作ります。これを S2 とします。
0x04 S2 に M1 を、ある条件(仮に条件を F1 とする) F1 に従って追加します。
0x05 S2 に さらにある条件 F2 を使って、文字列を追加します。
0x06 S2 を MD5で暗号化します。出力結果を M2 とします。
0x07 pw, M2, salt をある条件 F3 を使用して組み合わせて文字列にし暗号化します。これを M_0001 とします。
0x08 0x07 を M_0001 〜 M_1000 まで 1000回くり返します。M2 はそのつど M_xxxx に変わります。
0x09 M_1000 を M3 とします。
0x0a M3 を ある条件 F4 を使用して 22bytes の文字列に変換します。これが暗号文です。仮に Z と定義します。
0x0b 最終的に[magic, salt, "$", Z]という文字列を出力します。


条件 F1 〜 F4 はソースコード見た方が良いと思います。
なかなか複雑な処理を行っています。

http://ruffnex.oc.to/kenji/xrea/md5crypt.txt

うーん。さっぱり。


Crypt::SaltedHash が生成する値は、LDAP のパスワードを扱う方法を定義した RFC-3112 に準拠した形式になるというメリットがあります (ただし SHA-1, MD5 を使用した場合のみ)。


ある日突然、ユーザアカウントの管理を LDAP に移行したい! ということになっても、パスワードカラムの値をそのまま使えるのです。素晴らしいですね。

パスワード保存のお供に Crypt::SaltedHash - JPerl Advent Calendar 2009

これが主な違い?




あとsqueezeや http://php53.dotdeb.orgPHPならcryptでもsha256とかが使えるらしい。

crypt() で $5$ や $6$ から始まる salt を指定することで、SHA-256 や SHA-512 が使用できるようになりました。

<?php
echo 'SHA-256' . crypt( 'test', '$5$rounds=5000$salt$' ) . "\n";
echo 'SHA-512' . crypt( 'test', '$6$rounds=5000$salt$' ) . "\n";

結果は以下のようになります。

SHA-256: $5$rounds=5000$salt$lNwM/2EW94.5e484ZZwetUClB7.Z/Z3buPmQvXdPEj4
SHA-512: $6$rounds=5000$salt$xdLuw21n.5WciQUUpHTTPfR6QwS..Z1Q/4xGfiyYa51WSQktzSXYXSk2zBp.Is5r9WiXrGqRmHpEG0iG0HaSk.
PHP 5.3.2 での修正点や機能追加について - t_komuraの日記


で、結局パスワードやセッションIDを生成するのは何がいいんだろうね。
cryptでSHA-512を使いつつsaltを自動生成とかできないのかな。



こういうのもあった。

<?php
function mkPasswd_SSHA($clearPass, $salt=null)if(!isset($salt))mt_srand((double)microtime()*1000000);
                $salt = substr(md5(mt_rand()), 4, 8);
        }
        if(function_exists('sha1'))$hash = pack("H*", sha1($clearPass . $salt));
        }else if(function_exists('mHash'))$hash = mHash(MHASH_SHA1, $clearPass . $salt);
        }elsedie("Error: Can not use Function:(SHA or SSHA). Use PHP Ver. 4.3.0 over.");
        }
        return base64_encode($hash . $salt);
}
http://www.developlus.jp/topics/password.html

うーむ。


saltを自動生成する部分だけ自作してcryptを使うのがいいのかな。どうなんだろ。



PHPのcryptをsha256変換方式で使う場合の情報って全然ないなーと思ったら本家にあった。

  • CRYPT_SHA256 - SHA-256 hash with a sixteen character salt prefixed with $5$. If the salt string starts with 'rounds=$', the numeric value of N is used to indicate how many times the hashing loop should be executed, much like the cost parameter on Blowfish. The default number of rounds is 5000, there is a minimum of 1000 and a maximum of 999,999,999. Any selection of N outside this range will be truncated to the nearest limit.
  • CRYPT_SHA512 - SHA-512 hash with a sixteen character salt prefixed with $6$. If the salt string starts with 'rounds=$', the numeric value of N is used to indicate how many times the hashing loop should be executed, much like the cost parameter on Blowfish. The default number of rounds is 5000, there is a minimum of 1000 and a maximum of 999,999,999. Any selection of N outside this range will be truncated to the nearest limit.
PHP: crypt - Manual

日本語訳されてなかっただけだった…。


実装ネタ元のリンクもあった。
http://people.redhat.com/drepper/SHA-crypt.txt
長い…。
しかしさすがPHPのマニュアルだ。説明がないと思ったけどちゃんと書いてた。
続きはまた後でやろう。