3 * The PBKDF based password hashing implementation
5 * Use this class to generate and validate PBKDF hashed passwords.
9 * @see http://httpd.apache.org/docs/2.2/misc/password_encryptions.html
10 * @category PHPCryptLib
12 * @subpackage Implementation
13 * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
14 * @copyright 2011 The Authors
15 * @license http://www.opensource.org/licenses/mit-license.html MIT License
16 * @version Build @@version@@
19 namespace CryptLib\Password\Implementation
;
21 use CryptLib\Key\Factory
as KeyFactory
;
22 use CryptLib\Random\Factory
as RandomFactory
;
23 use CryptLib\Key\Derivation\PBKDF\PBKDF2
as PBKDF2
;
26 * The PBKDF based password hashing implementation
28 * Use this class to generate and validate PBKDF hashed passwords.
32 * @see http://httpd.apache.org/docs/2.2/misc/password_encryptions.html
33 * @category PHPCryptLib
35 * @subpackage Implementation
36 * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
38 class PBKDF
implements \CryptLib\Password\Password
{
41 * @var PBKDF The PBKDF derivation implementation to use for this instance
43 protected $derivation = null;
46 * @var Generator The Random Number Generator to use for making salts
48 protected $generator = null;
51 * @var int The number of iterations to perform on the password
53 protected $iterations = 5000;
56 * @var int The length in bytes of the generated password hash
61 * Determine if the hash was made with this method
63 * @param string $hash The hashed data to check
65 * @return boolean Was the hash created by this method
67 public static function detect($hash) {
68 return strncmp($hash, '$pbkdf$', 7) === 0;
72 * Return the prefix used by this hashing method
74 * @return string The prefix used
76 public static function getPrefix() {
81 * Load an instance of the class based upon the supplied hash
83 * @param string $hash The hash to load from
85 * @return Password the created instance
86 * @throws InvalidArgumentException if the hash wasn't created here
88 public static function loadFromHash($hash) {
89 if (!static::detect($hash)) {
90 throw new \
InvalidArgumentException('Hash Not Created Here');
92 $parts = explode('$', $hash);
93 if (count($parts) != 7) {
94 throw new \
InvalidArgumentException('Hash Not Created Here');
96 $signature = $parts[2];
97 $factory = new KeyFactory();
98 $hash = $factory->getPBKDFFromSignature($signature);
99 $iterations = $parts[3];
101 return new static($hash, $size, $iterations);
105 * Build a new instance of the PBKDF password class
107 * @param PBKDF $derivation The derivation class to use
108 * @param int $size The size of hash to generate
109 * @param int $iterations The number of iterations to perform
110 * @param Generator $generator The Random Generator to use
114 public function __construct(
115 \CryptLib\Key\Derivation\PBKDF
$derivation = null,
118 \CryptLib\Random\Generator
$generator = null
120 if (is_null($derivation)) {
121 $derivation = new PBKDF2();
123 $this->derivation
= $derivation;
124 $this->size
= $size < 40 ?
40 : (int) $size;
125 $this->iterations
= $iterations > 0 ?
(int) $iterations : 1;
126 if (is_null($generator)) {
127 $factory = new RandomFactory
;
128 $generator = $factory->getMediumStrengthGenerator();
130 $this->generator
= $generator;
134 * Create a password hash for a given plain text password
136 * @param string $password The password to hash
138 * @return string The formatted password hash
140 public function create($password) {
141 $size = $this->size
- 8; // remove size of stored bits
142 $saltSize = floor($size / 5); //Use 20% of the size for the salt
143 $hashSize = $size - $saltSize;
144 $salt = $this->generator
->generate($saltSize);
145 return $this->hash($password, $salt, $this->iterations
, $hashSize);
149 * Verify a password hash against a given plain text password
151 * @param string $password The password to hash
152 * @param string $hash The supplied ahsh to validate
154 * @return boolean Does the password validate against the hash
156 public function verify($password, $hash) {
157 if (strlen($hash) <= 16 ||
strpos($hash, '$') === false) {
160 $parts = explode('$', $hash);
161 if (count($parts) != 7) {
163 } elseif ($parts[2] != $this->derivation
->getSignature()) {
166 $iterations = $parts[3];
168 $salt = base64_decode($parts[5]);
169 return $this->hash($password, $salt, $iterations, $size) == $hash;
173 * Perform the hashing of the password
175 * @param string $password The plain text password to hash
176 * @param string $salt The 8 byte salt to use
177 * @param int $iterations The number of iterations to use
179 * @return string The hashed password
181 protected function hash($password, $salt, $iterations, $size) {
182 $bit = $this->derivation
->derive($password, $salt, $iterations, $size);
183 $sig = $this->derivation
->getSignature();
184 $sig = '$pbkdf$' . $sig . '$' . $iterations . '$' . $size;
185 return $sig . '$' . base64_encode($salt) . '$' . base64_encode($bit);