run code in 200+ php & hhvm versions
Bugs & Features
<?php /** * This class helps building secure password-hashes (BCrypt) with PHP, * to store them in the database. It is provided with comments, for * educational purposes. * * @author Martin Stoeckli - www.martinstoeckli.ch/php * @copyright Martin Stoeckli 2013, this code may be freely used in every * type of project, it is provided without warranties of any kind. */ class StoPasswordHash { /** * Generates a bcrypt hash of a password, which can be stored in a database. * @param string $password Password whose hash-value we need. * @param int $cost Controls the number of iterations. Increasing the cost * by 1, doubles the needed calculation time. Must be in the range of 4-31. * @param string $serverSideKey This key acts similar to a pepper, but * can be exchanged when necessary. In certain situations, encrypting * the hash-value can protect weak passwords from a dictionary attack. * @return string Hash-value of the password. A random salt is included. * Without passing a $serverSideKey the result has a length of 60 * characters, with a $serverSideKey the length is 108 characters. */ public static function hashBcrypt($password, $cost=10, $serverSideKey='') { if (!defined('CRYPT_BLOWFISH')) throw new Exception('The CRYPT_BLOWFISH algorithm is required (PHP 5.3).'); if ($cost < 4 || $cost > 31) throw new InvalidArgumentException('The cost factor must be a number between 4 and 31'); if (version_compare(PHP_VERSION, '5.3.7') >= 0) $algorithm = '2y'; // BCrypt, with fixed unicode problem else $algorithm = '2a'; // BCrypt // BCrypt expects nearly the same alphabet as base64_encode returns, // but instead of the '+' characters it accepts '.' characters. // BCrypt alphabet: ./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz $salt = str_replace('+', '.', StoPasswordHash::generateRandomBase64String(22)); // Create crypt parameters: $algorithm$cost$salt $cryptParams = sprintf('$%s$%02d$%s', $algorithm, $cost, $salt); $hash = crypt($password, $cryptParams); // Encrypt hash with the server side key if ($serverSideKey != '') { $encryptedHash = StoPasswordHash::encryptTwofish($hash, $serverSideKey); $hash = base64_encode($encryptedHash); } return $hash; } /** * Checks, if the password matches a given hash value. This is useful when * a user enters his password for login, to check if the password corresponds * to the hash stored in the database. * @param string $password Password to check. * @param string $existingHash Stored hash-value from the database. * @param string $serverSideKey Pass the same key that was used to encrypt * $existingHash, or omit this parameter if no key was used. * @return bool Returns true, if the password matches the hash, * otherwise false. */ public static function verify($password, $existingHash, $serverSideKey='') { if (!defined('CRYPT_BLOWFISH')) throw new Exception('The CRYPT_BLOWFISH algorithm is required (PHP 5.3).'); // Decrypt hash with the server side key if ($serverSideKey != '') { $encryptedHash = base64_decode($existingHash); $existingHash = StoPasswordHash::decryptTwofish($encryptedHash, $serverSideKey); } // The parameters that where used to generate $existingHash, will be // extracted automatically from the first 29 characters of $existingHash. $newHash = crypt($password, $existingHash); return $newHash === $existingHash; } /** * Allows to change the server-side key, or to add/remove the encryption. * @param string $existingHash Encrypted or unencrypted hash-value. * @param string $oldKey Pass the key, that was used to encrypt * $existingHash, or pass '' if the hash is not yet encrypted. * @param string $newKey Pass a new key, to encrypt $existingHash, or * pass '' to remove the encryption. * @return string New encrypted/decrypted hash-value. */ public static function changeServerSideKey($existingHash, $oldKey, $newKey) { // decrypt hash-value $hash = $existingHash; if ($oldKey != '') { $encryptedHash = base64_decode($hash); $hash = StoPasswordHash::decryptTwofish($encryptedHash, $oldKey); } // encrypt hash-value if ($newKey != '') { $encryptedHash = StoPasswordHash::encryptTwofish($hash, $newKey); $hash = base64_encode($encryptedHash); } return $hash; } /** * Generates a random string of a given length, using the random source of * the operating system. The string contains only characters of this * alphabet: +/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz * @param int $length Number of characters the string should have. * @return string A random base64 encoded string. */ protected static function generateRandomBase64String($length) { if (!defined('MCRYPT_DEV_URANDOM')) throw new Exception('The MCRYPT_DEV_URANDOM source is required (PHP 5.3).'); // Generate random bytes, using the operating system's random source. // Since PHP 5.3 this also uses the random source on a Windows server. // Unlike /dev/random, the /dev/urandom does not block the server, if // there is not enough entropy available. $binaryLength = (int)($length * 3 / 4 + 1); $randomBinaryString = mcrypt_create_iv($binaryLength, MCRYPT_DEV_URANDOM); $randomBase64String = base64_encode($randomBinaryString); return substr($randomBase64String, 0, $length); } /** * Encrypts data with the TWOFISH algorithm. The IV vector will be * included in the resulting binary string. * @param string $data Data to encrypt. Trailing \0 characters will get lost. * @param string $key This key will be used to encrypt the data. The key * will be hashed to a binary representation before it is used. * @return string Returns the encrypted data in form of a binary string. */ public static function encryptTwofish($data, $key) { if (!defined('MCRYPT_DEV_URANDOM')) throw new Exception('The MCRYPT_DEV_URANDOM source is required (PHP 5.3).'); if (!defined('MCRYPT_TWOFISH')) throw new Exception('The MCRYPT_TWOFISH algorithm is required (PHP 5.3).'); // The cbc mode is preferable over the ecb mode $td = mcrypt_module_open(MCRYPT_TWOFISH, '', MCRYPT_MODE_CBC, ''); // Twofish accepts a key of 32 bytes. Because usually longer strings // with only readable characters are passed, we build a binary string. $binaryKey = hash('sha256', $key, true); // Create initialization vector of 16 bytes $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_DEV_URANDOM); mcrypt_generic_init($td, $binaryKey, $iv); $encryptedData = mcrypt_generic($td, $data); mcrypt_generic_deinit($td); mcrypt_module_close($td); // Combine iv and encrypted text return $iv . $encryptedData; } /** * Decrypts data, formerly encrypted with @see encryptTwofish. * @param string $encryptedData Binary string with encrypted data. * @param string $key This key will be used to decrypt the data. * @return string Returns the original decrypted data. */ public static function decryptTwofish($encryptedData, $key) { if (!defined('MCRYPT_TWOFISH')) throw new Exception('The MCRYPT_TWOFISH algorithm is required (PHP 5.3).'); $td = mcrypt_module_open(MCRYPT_TWOFISH, '', MCRYPT_MODE_CBC, ''); // Extract initialization vector from encrypted data $ivSize = mcrypt_enc_get_iv_size($td); $iv = substr($encryptedData, 0, $ivSize); $encryptedData = substr($encryptedData, $ivSize); $binaryKey = hash('sha256', $key, true); mcrypt_generic_init($td, $binaryKey, $iv); $decryptedData = mdecrypt_generic($td, $encryptedData); mcrypt_generic_deinit($td); mcrypt_module_close($td); // Original data was padded with 0-characters to block-size return rtrim($decryptedData, "\0"); } protected static function calculateTwofishLength($inputLength) { $td = mcrypt_module_open(MCRYPT_TWOFISH, '', MCRYPT_MODE_CBC, ''); $ivSize = mcrypt_enc_get_iv_size($td); $blockSize = mcrypt_enc_get_block_size($td); mcrypt_module_close($td); return $ivSize + ceil($inputLength / $blockSize) * $blockSize; } } ?>
based on 6DlMO
Output for 5.3.23 - 7.2.0