0; $i -= 16) { $context .= substr($binary, 0, min(16, $i)); } for ($i = $max; $i > 0; $i >>= 1) { $context .= ($i & 1) ? chr(0) : $mdp[0]; } $binary = pack('H32', md5($context)); for ($i = 0; $i < 1000; ++$i) { $new = ($i & 1) ? $mdp : $binary; if ($i % 3) { $new .= $salt; } if ($i % 7) { $new .= $mdp; } $new .= ($i & 1) ? $binary : $mdp; $binary = pack('H32', md5($new)); } $hash = ''; for ($i = 0; $i < 5; ++$i) { $k = $i + 6; $j = $i + 12; if (16 == $j) { $j = 5; } $hash = $binary[$i].$binary[$k].$binary[$j].$hash; } $hash = chr(0).chr(0).$binary[11].$hash; $hash = strtr( strrev(substr(base64_encode($hash), 2)), self::BASE64_ALPHABET, self::APRMD5_ALPHABET ); return '$apr1$'.$salt.'$'.$hash; } // 8 character salts are the best. Don't encourage anything but the best. public static function salt() { $alphabet = self::APRMD5_ALPHABET; $salt = ''; for ($i = 0; $i < 8; ++$i) { $offset = hexdec(bin2hex(openssl_random_pseudo_bytes(1))) % 64; $salt .= $alphabet[$offset]; } return $salt; } public static function check($plain, $hash) { $parts = explode('$', $hash); return self::hash($plain, $parts[2]) === $hash; } }