PHPで、SHA512を計算するスクリプト
2010年10月10日
Jeans CMSは、ハッシュ値の計算にSHA512を用いていて、これでパスワードの認証などを行っている。Jeansの実行にはPHP5.2が必要で、PHP5.2のデフォルトではhash()関数を用いてSHA512を利用することができる。
ところが、サーバーの設定によっては、hash()関数が使えないらしい。そこで、そういった状況でも対応できるよう、PHPを用いてSHA512を実装することを試みた。
CでSHA512を計算するコードとして、PolarSSLというものを見つけた。ライセンスはGPLなので、Jeansにもそのまま取り込める。Cで動くことを確認して、PHPに移植した。64ビット整数の演算が必要で、PHP5.2ではこれが実装されていないので、この部分もエミュレートするなどした。結果として、ちゃんと計算することができたので、ここにメモしておく。
以下のコードでは、hash2()関数として実装している。色々な長さのランダムな文字列についてSHA512を計算するコードも含まれている。
ところが、サーバーの設定によっては、hash()関数が使えないらしい。そこで、そういった状況でも対応できるよう、PHPを用いてSHA512を実装することを試みた。
CでSHA512を計算するコードとして、PolarSSLというものを見つけた。ライセンスはGPLなので、Jeansにもそのまま取り込める。Cで動くことを確認して、PHPに移植した。64ビット整数の演算が必要で、PHP5.2ではこれが実装されていないので、この部分もエミュレートするなどした。結果として、ちゃんと計算することができたので、ここにメモしておく。
以下のコードでは、hash2()関数として実装している。色々な長さのランダムな文字列についてSHA512を計算するコードも含まれている。
<?php /* * The licence of following code is GPL (v2.0). */ for($i=1;$i<1024*1024;$i*=2){ echo "$i\n"; $str=''; for($j=0;$j<$i;$j++) $str.=chr(rand(0,255)); $h1=hash('SHA512',$str); $h2=hash2('SHA512',$str); if ($h1!=$h2) exit('ERROR!'); echo $h1."\n"; } function hash2($mode,$str,$raw_output=false){ $mode=strtolower($mode); switch($mode){ case 'md5': return md5($str,$raw_output); case 'sha1': return sha1($str,$raw_output); case 'sha512': case 'sha384': $result=false; misc_sha512::sha4($str,strlen($str),$result,$mode=='sha384'); if ($raw_output) return $result; $ret=''; for($i=0;$i<64;$i++){ $byte=ord(substr($result,$i,1)); if ($byte<16) $ret.='0'; $ret.=dechex($byte); } return $ret; default: exit(__FILE__.__LINE__); } } class misc_sha512 { /** * SHA-512/384 calculation routine. * This code originally came from PolarSSL (http://www.polarssl.org) * but was modified by Jeans CMS team. */ /* * SHA-512 context structure */ public $total,$state,$buffer,$is384; public function __construct($is384){ $this->total=array(str_repeat("\x00",8),str_repeat("\x00",8)); $this->buffer=str_repeat("\x00",128); $this->is384=$is384; $this->state=array(); if( $is384 ) { /* SHA-384 */ $this->state[0] = "\xCB\xBB\x9D\x5D\xC1\x05\x9E\xD8"; $this->state[1] = "\x62\x9A\x29\x2A\x36\x7C\xD5\x07"; $this->state[2] = "\x91\x59\x01\x5A\x30\x70\xDD\x17"; $this->state[3] = "\x15\x2F\xEC\xD8\xF7\x0E\x59\x39"; $this->state[4] = "\x67\x33\x26\x67\xFF\xC0\x0B\x31"; $this->state[5] = "\x8E\xB4\x4A\x87\x68\x58\x15\x11"; $this->state[6] = "\xDB\x0C\x2E\x0D\x64\xF9\x8F\xA7"; $this->state[7] = "\x47\xB5\x48\x1D\xBE\xFA\x4F\xA4"; } else { /* SHA-512 */ $this->state[0] = "\x6A\x09\xE6\x67\xF3\xBC\xC9\x08"; $this->state[1] = "\xBB\x67\xAE\x85\x84\xCA\xA7\x3B"; $this->state[2] = "\x3C\x6E\xF3\x72\xFE\x94\xF8\x2B"; $this->state[3] = "\xA5\x4F\xF5\x3A\x5F\x1D\x36\xF1"; $this->state[4] = "\x51\x0E\x52\x7F\xAD\xE6\x82\xD1"; $this->state[5] = "\x9B\x05\x68\x8C\x2B\x3E\x6C\x1F"; $this->state[6] = "\x1F\x83\xD9\xAB\xFB\x41\xBD\x6B"; $this->state[7] = "\x5B\xE0\xCD\x19\x13\x7E\x21\x79"; } } /* * There is only a static public function below. */ static public function sha4(&$input,$ilen,&$output,$is384){ $ctx=false; self::sha4_starts( $ctx, $is384 ); self::sha4_update( $ctx, $input, $ilen ); self::sha4_finish( $ctx, $output ); } /* * Private functions follow. */ /* * 64 bit calculation routines. */ static private function int_to_64($int){ $result=''; for($i=0;$i<8;$i++){ $result=chr($int & 0xff).$result; $int=$int>>8; } return $result; } static private function int_from_64($i64str){ $result=0; for($i=0;$i<8;$i++){ $result*=256; $result+=ord(substr($i64str,$i,1)); } return $result; } static private function bitc($a,$mode,$b){ if (is_int($a)) $a=self::int_to_64($a); if (is_int($b)) $b=self::int_to_64($b); $result=''; $c=0; for($i=7;0<=$i;$i--){ $aa=ord(substr($a,$i,1)); $bb=ord(substr($b,$i,1)); switch($mode){ case '&': $byte=$aa & $bb; break; case '|': $byte=$aa | $bb; break; case '^': $byte=$aa ^ $bb; break; case '+': $byte=$aa + $bb +$c; $c=$byte>>8; $byte=$byte & 0xff; break; default: exit(__CLASS__.__LINE__); } $result=chr($byte).$result; } return $result; } /* * Shift and rotate routines. */ static private function shr($x,$n){ $n8=$n>>3; if (0<$n8) { $x=substr(str_repeat("\x00",$n8).$x,0,8); } $n=$n & 7; if (0<$n) { $result=''; $c=0; for($i=0;$i<8;$i++){ $byte=ord(substr($x,$i,1)); $result.=chr(($byte>>$n) | $c); $c=($byte<<(8-$n)) & 0xff; } $x=$result; } return $x; } static private function ror($x,$n){ $n8=$n>>3; if (0<$n8) { $x=substr($x.$x,8-$n8,8); } $n=$n & 7; if (0<$n) { $result=''; $byte=ord(substr($x,-1)); $byte=$byte<<(8-$n); $c=$byte & 0xff; for($i=0;$i<8;$i++){ $byte=ord(substr($x,$i,1)); $result.=chr(($byte>>$n) | $c); $c=($byte<<(8-$n)) & 0xff; } $x=$result; } return $x; } static private function shl($x,$n){ $n8=$n>>3; if (0<$n8) { $x=substr($x.str_repeat("\x00",$n8),-8); } $n=$n & 7; if (0<$n) { $result=''; $c=0; for($i=7;0<=$i;$i--){ $byte=ord(substr($x,$i,1))<<$n; $result=chr(($byte & 0xff) | $c).$result; $c=$byte>>8; } $x=$result; } return $x; } /* * Get/put 64bit data from/into buffer. */ static private function get_uint64_be(&$n,$b,$i){ $n=substr($b,$i,8); } static private function put_uint64_be($n,&$b,$i){ $b=substr($b,0,$i).$n.substr($b,$i+8); } /* * Round constants */ static private $k=array( "\x42\x8A\x2F\x98\xD7\x28\xAE\x22","\x71\x37\x44\x91\x23\xEF\x65\xCD", "\xB5\xC0\xFB\xCF\xEC\x4D\x3B\x2F","\xE9\xB5\xDB\xA5\x81\x89\xDB\xBC", "\x39\x56\xC2\x5B\xF3\x48\xB5\x38","\x59\xF1\x11\xF1\xB6\x05\xD0\x19", "\x92\x3F\x82\xA4\xAF\x19\x4F\x9B","\xAB\x1C\x5E\xD5\xDA\x6D\x81\x18", "\xD8\x07\xAA\x98\xA3\x03\x02\x42","\x12\x83\x5B\x01\x45\x70\x6F\xBE", "\x24\x31\x85\xBE\x4E\xE4\xB2\x8C","\x55\x0C\x7D\xC3\xD5\xFF\xB4\xE2", "\x72\xBE\x5D\x74\xF2\x7B\x89\x6F","\x80\xDE\xB1\xFE\x3B\x16\x96\xB1", "\x9B\xDC\x06\xA7\x25\xC7\x12\x35","\xC1\x9B\xF1\x74\xCF\x69\x26\x94", "\xE4\x9B\x69\xC1\x9E\xF1\x4A\xD2","\xEF\xBE\x47\x86\x38\x4F\x25\xE3", "\x0F\xC1\x9D\xC6\x8B\x8C\xD5\xB5","\x24\x0C\xA1\xCC\x77\xAC\x9C\x65", "\x2D\xE9\x2C\x6F\x59\x2B\x02\x75","\x4A\x74\x84\xAA\x6E\xA6\xE4\x83", "\x5C\xB0\xA9\xDC\xBD\x41\xFB\xD4","\x76\xF9\x88\xDA\x83\x11\x53\xB5", "\x98\x3E\x51\x52\xEE\x66\xDF\xAB","\xA8\x31\xC6\x6D\x2D\xB4\x32\x10", "\xB0\x03\x27\xC8\x98\xFB\x21\x3F","\xBF\x59\x7F\xC7\xBE\xEF\x0E\xE4", "\xC6\xE0\x0B\xF3\x3D\xA8\x8F\xC2","\xD5\xA7\x91\x47\x93\x0A\xA7\x25", "\x06\xCA\x63\x51\xE0\x03\x82\x6F","\x14\x29\x29\x67\x0A\x0E\x6E\x70", "\x27\xB7\x0A\x85\x46\xD2\x2F\xFC","\x2E\x1B\x21\x38\x5C\x26\xC9\x26", "\x4D\x2C\x6D\xFC\x5A\xC4\x2A\xED","\x53\x38\x0D\x13\x9D\x95\xB3\xDF", "\x65\x0A\x73\x54\x8B\xAF\x63\xDE","\x76\x6A\x0A\xBB\x3C\x77\xB2\xA8", "\x81\xC2\xC9\x2E\x47\xED\xAE\xE6","\x92\x72\x2C\x85\x14\x82\x35\x3B", "\xA2\xBF\xE8\xA1\x4C\xF1\x03\x64","\xA8\x1A\x66\x4B\xBC\x42\x30\x01", "\xC2\x4B\x8B\x70\xD0\xF8\x97\x91","\xC7\x6C\x51\xA3\x06\x54\xBE\x30", "\xD1\x92\xE8\x19\xD6\xEF\x52\x18","\xD6\x99\x06\x24\x55\x65\xA9\x10", "\xF4\x0E\x35\x85\x57\x71\x20\x2A","\x10\x6A\xA0\x70\x32\xBB\xD1\xB8", "\x19\xA4\xC1\x16\xB8\xD2\xD0\xC8","\x1E\x37\x6C\x08\x51\x41\xAB\x53", "\x27\x48\x77\x4C\xDF\x8E\xEB\x99","\x34\xB0\xBC\xB5\xE1\x9B\x48\xA8", "\x39\x1C\x0C\xB3\xC5\xC9\x5A\x63","\x4E\xD8\xAA\x4A\xE3\x41\x8A\xCB", "\x5B\x9C\xCA\x4F\x77\x63\xE3\x73","\x68\x2E\x6F\xF3\xD6\xB2\xB8\xA3", "\x74\x8F\x82\xEE\x5D\xEF\xB2\xFC","\x78\xA5\x63\x6F\x43\x17\x2F\x60", "\x84\xC8\x78\x14\xA1\xF0\xAB\x72","\x8C\xC7\x02\x08\x1A\x64\x39\xEC", "\x90\xBE\xFF\xFA\x23\x63\x1E\x28","\xA4\x50\x6C\xEB\xDE\x82\xBD\xE9", "\xBE\xF9\xA3\xF7\xB2\xC6\x79\x15","\xC6\x71\x78\xF2\xE3\x72\x53\x2B", "\xCA\x27\x3E\xCE\xEA\x26\x61\x9C","\xD1\x86\xB8\xC7\x21\xC0\xC2\x07", "\xEA\xDA\x7D\xD6\xCD\xE0\xEB\x1E","\xF5\x7D\x4F\x7F\xEE\x6E\xD1\x78", "\x06\xF0\x67\xAA\x72\x17\x6F\xBA","\x0A\x63\x7D\xC5\xA2\xC8\x98\xA6", "\x11\x3F\x98\x04\xBE\xF9\x0D\xAE","\x1B\x71\x0B\x35\x13\x1C\x47\x1B", "\x28\xDB\x77\xF5\x23\x04\x7D\x84","\x32\xCA\xAB\x7B\x40\xC7\x24\x93", "\x3C\x9E\xBE\x0A\x15\xC9\xBE\xBC","\x43\x1D\x67\xC4\x9C\x10\x0D\x4C", "\x4C\xC5\xD4\xBE\xCB\x3E\x42\xB6","\x59\x7F\x29\x9C\xFC\x65\x7E\x2A", "\x5F\xCB\x6F\xAB\x3A\xD6\xFA\xEC","\x6C\x44\x19\x8C\x4A\x47\x58\x17" ); /* * SHA-512 context setup * See also constructor. */ static private $sha4_padding; static private function sha4_starts( &$ctx, $is384 ){ $ctx=new self($is384); if (!isset(self::$sha4_padding)) { self::$sha4_padding="\x80".str_repeat("\x00",127); } } /* * Bit calculation routines follow */ static private function s0($x){ $result=self::ror($x,1); $result=self::bitc($result,'^',self::ror($x,8)); $result=self::bitc($result,'^',self::shr($x,7)); return $result; } static private function s1($x){ $result=self::ror($x,19); $result=self::bitc($result,'^',self::ror($x,61)); $result=self::bitc($result,'^',self::shr($x,6)); return $result; } static private function s2($x){ $result=self::ror($x,28); $result=self::bitc($result,'^',self::ror($x,34)); $result=self::bitc($result,'^',self::ror($x,39)); return $result; } static private function s3($x){ $result=self::ror($x,14); $result=self::bitc($result,'^',self::ror($x,18)); $result=self::bitc($result,'^',self::ror($x,41)); return $result; } static private function f0($x,$y,$z){ $v1=self::bitc($x,'&',$y); $v2=self::bitc($x,'|',$y); $v2=self::bitc($z,'&',$v2); return self::bitc($v1,'|',$v2); } static private function f1($x,$y,$z){ $v1=self::bitc($y,'^',$z); $v1=self::bitc($x,'&',$v1); return self::bitc($z,'^',$v1); } static private function p0($a,$b,$c,&$d,$e,$f,$g,&$h,$x,$k){ $temp1=self::bitc($h,'+',self::s3($e)); $temp1=self::bitc($temp1,'+',self::f1($e,$f,$g)); $temp1=self::bitc($temp1,'+',$k); $temp1=self::bitc($temp1,'+',$x); $temp2=self::bitc(self::s2($a),'+',self::f0($a,$b,$c)); $d=self::bitc($d,'+',$temp1); $h=self::bitc($temp1,'+',$temp2); } /* * SHA512/SHA384 process */ static private function sha4_process(&$ctx, $data){ $W=array_fill(0,80,false); for( $i = 0; $i < 16; $i++ ){ self::get_uint64_be( $W[$i], $data, $i << 3 ); } for( ; $i < 80; $i++ ){ $temp=self::bitc(self::s1($W[$i-2]),'+',$W[$i-7]); $temp=self::bitc($temp,'+',self::s0($W[$i-15])); $W[$i]=self::bitc($temp,'+',$W[$i - 16]); } $A = $ctx->state[0]; $B = $ctx->state[1]; $C = $ctx->state[2]; $D = $ctx->state[3]; $E = $ctx->state[4]; $F = $ctx->state[5]; $G = $ctx->state[6]; $H = $ctx->state[7]; $i = 0; do{ self::p0( $A, $B, $C, $D, $E, $F, $G, $H, $W[$i], self::$k[$i] ); $i++; self::p0( $H, $A, $B, $C, $D, $E, $F, $G, $W[$i], self::$k[$i] ); $i++; self::p0( $G, $H, $A, $B, $C, $D, $E, $F, $W[$i], self::$k[$i] ); $i++; self::p0( $F, $G, $H, $A, $B, $C, $D, $E, $W[$i], self::$k[$i] ); $i++; self::p0( $E, $F, $G, $H, $A, $B, $C, $D, $W[$i], self::$k[$i] ); $i++; self::p0( $D, $E, $F, $G, $H, $A, $B, $C, $W[$i], self::$k[$i] ); $i++; self::p0( $C, $D, $E, $F, $G, $H, $A, $B, $W[$i], self::$k[$i] ); $i++; self::p0( $B, $C, $D, $E, $F, $G, $H, $A, $W[$i], self::$k[$i] ); $i++; } while( $i < 80 ); $ctx->state[0] = self::bitc($ctx->state[0],'+', $A); $ctx->state[1] = self::bitc($ctx->state[1],'+', $B); $ctx->state[2] = self::bitc($ctx->state[2],'+', $C); $ctx->state[3] = self::bitc($ctx->state[3],'+', $D); $ctx->state[4] = self::bitc($ctx->state[4],'+', $E); $ctx->state[5] = self::bitc($ctx->state[5],'+', $F); $ctx->state[6] = self::bitc($ctx->state[6],'+', $G); $ctx->state[7] = self::bitc($ctx->state[7],'+', $H); } /* * SHA-512 process buffer */ static private function memcpy(&$to,$to_pos,&$from,$from_pos,$len){ $to=substr($to,0,$to_pos). substr($from,$from_pos,$len). substr($to,$to_pos+$len); } static private function sha4_update( &$ctx, &$input, $ilen ){ if( $ilen <= 0 ) return; $input_pos=0; $temp=array_fill(0,128,0); $left = ord(substr($ctx->total[0],-1)) & 0x7F; $fill = (int)( 128 - $left ); $ctx->total[0]=self::bitc($ctx->total[0],'+',(int)$ilen); if( self::int_from_64($ctx->total[0]) < $ilen ) $ctx->total[1]=self::bitc($ctx->total[1],'+',1); if( $left && $ilen >= $fill ) { self::memcpy($ctx->buffer,$left,$input,$input_pos,$fill); self::sha4_process( $ctx, $ctx->buffer ); $input_pos+=$fill; $ilen -= $fill; $left = 0; } while( $ilen >= 128 ) { $temp=''; self::memcpy($temp,0,$input,$input_pos,128); self::sha4_process( $ctx, $temp ); $input_pos +=128; $ilen -= 128; } if( $ilen > 0 ) { self::memcpy($ctx->buffer,$left,$input,$input_pos,$ilen); } } /* * SHA-512 final digest */ static private function sha4_finish( &$ctx, &$output){ $msglen=str_repeat("\x00",16); $high=self::bitc(self::shr($ctx->total[0],61),'|',self::shl($ctx->total[1],3)); $low=self::shl($ctx->total[0],3); self::put_uint64_be( $high, $msglen, 0 ); self::put_uint64_be( $low, $msglen, 8 ); $last = ord(substr($ctx->total[0],-1)) & 0x7F; $padn = ( $last < 112 ) ? ( 112 - $last ) : ( 240 - $last ); self::sha4_update( $ctx, self::$sha4_padding, $padn ); self::sha4_update( $ctx, $msglen, 16 ); self::put_uint64_be( $ctx->state[0], $output, 0 ); self::put_uint64_be( $ctx->state[1], $output, 8 ); self::put_uint64_be( $ctx->state[2], $output, 16 ); self::put_uint64_be( $ctx->state[3], $output, 24 ); self::put_uint64_be( $ctx->state[4], $output, 32 ); self::put_uint64_be( $ctx->state[5], $output, 40 ); if( !$ctx->is384 ) { self::put_uint64_be( $ctx->state[6], $output, 48 ); self::put_uint64_be( $ctx->state[7], $output, 56 ); } } }