3 * Portable PHP password hashing framework.
5 * Version 0.1 / genuine.
7 * Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
10 * There's absolutely no warranty.
12 * The homepage URL for this framework is:
14 * http://www.openwall.com/phpass/
16 * Please be sure to update the Version line if you edit this file in any way.
17 * It is suggested that you leave the main version number intact, but indicate
18 * your project name (after the slash) and add your own revision information.
20 * Please do not change the "private" password hashing method implemented in
21 * here, thereby making your hashes incompatible. However, if you must, please
22 * change the hash type identifier (the "$P$") to something different.
24 * Obviously, since this code is in the public domain, the above are not
25 * requirements (there can be none), but merely suggestions.
27 * @author Solar Designer <solar at openwall.com
28 * @license none - Public domain
35 * Phpass - create strong password hash
40 var $iteration_count_log2;
44 function PasswordHash($iteration_count_log2, $portable_hashes)
46 $this->itoa64
= './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
48 if ($iteration_count_log2 < 4 ||
$iteration_count_log2 > 31)
49 $iteration_count_log2 = 8;
50 $this->iteration_count_log2
= $iteration_count_log2;
52 $this->portable_hashes
= $portable_hashes;
54 $this->random_state
= microtime() . getmypid();
57 function get_random_bytes($count)
60 if (($fh = @fopen
('/dev/urandom', 'rb'))) {
61 $output = fread($fh, $count);
65 if (strlen($output) < $count) {
67 for ($i = 0; $i < $count; $i +
= 16) {
69 md5(microtime() . $this->random_state
);
71 pack('H*', md5($this->random_state
));
73 $output = substr($output, 0, $count);
79 function encode64($input, $count)
84 $value = ord($input[$i++
]);
85 $output .= $this->itoa64
[$value & 0x3f];
87 $value |
= ord($input[$i]) << 8;
88 $output .= $this->itoa64
[($value >> 6) & 0x3f];
92 $value |
= ord($input[$i]) << 16;
93 $output .= $this->itoa64
[($value >> 12) & 0x3f];
96 $output .= $this->itoa64
[($value >> 18) & 0x3f];
97 } while ($i < $count);
102 function gensalt_private($input)
105 $output .= $this->itoa64
[min($this->iteration_count_log2 +
106 ((PHP_VERSION
>= '5') ?
5 : 3), 30)];
107 $output .= $this->encode64($input, 6);
112 function crypt_private($password, $setting)
115 if (substr($setting, 0, 2) == $output)
118 if (substr($setting, 0, 3) != '$P$')
121 $count_log2 = strpos($this->itoa64
, $setting[3]);
122 if ($count_log2 < 7 ||
$count_log2 > 30)
125 $count = 1 << $count_log2;
127 $salt = substr($setting, 4, 8);
128 if (strlen($salt) != 8)
131 // We're kind of forced to use MD5 here since it's the only
132 // cryptographic primitive available in all versions of PHP
133 // currently in use. To implement our own low-level crypto
134 // in PHP would result in much worse performance and
135 // consequently in lower iteration counts and hashes that are
136 // quicker to crack (by non-PHP code).
137 if (PHP_VERSION
>= '5') {
138 $hash = md5($salt . $password, TRUE);
140 $hash = md5($hash . $password, TRUE);
143 $hash = pack('H*', md5($salt . $password));
145 $hash = pack('H*', md5($hash . $password));
149 $output = substr($setting, 0, 12);
150 $output .= $this->encode64($hash, 16);
155 function gensalt_extended($input)
157 $count_log2 = min($this->iteration_count_log2 +
8, 24);
158 // This should be odd to not reveal weak DES keys, and the
159 // maximum valid value is (2**24 - 1) which is odd anyway.
160 $count = (1 << $count_log2) - 1;
163 $output .= $this->itoa64
[$count & 0x3f];
164 $output .= $this->itoa64
[($count >> 6) & 0x3f];
165 $output .= $this->itoa64
[($count >> 12) & 0x3f];
166 $output .= $this->itoa64
[($count >> 18) & 0x3f];
168 $output .= $this->encode64($input, 3);
173 function gensalt_blowfish($input)
175 // This one needs to use a different order of characters and a
176 // different encoding scheme from the one in encode64() above.
177 // We care because the last character in our encoded string will
178 // only represent 2 bits. While two known implementations of
179 // bcrypt will happily accept and correct a salt string which
180 // has the 4 unused bits set to non-zero, we do not want to take
181 // chances and we also do not want to waste an additional byte
183 $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
186 $output .= chr(ord('0') +
$this->iteration_count_log2
/ 10);
187 $output .= chr(ord('0') +
$this->iteration_count_log2 %
10);
192 $c1 = ord($input[$i++
]);
193 $output .= $itoa64[$c1 >> 2];
194 $c1 = ($c1 & 0x03) << 4;
196 $output .= $itoa64[$c1];
200 $c2 = ord($input[$i++
]);
202 $output .= $itoa64[$c1];
203 $c1 = ($c2 & 0x0f) << 2;
205 $c2 = ord($input[$i++
]);
207 $output .= $itoa64[$c1];
208 $output .= $itoa64[$c2 & 0x3f];
214 function HashPassword($password)
218 if (CRYPT_BLOWFISH
== 1 && !$this->portable_hashes
) {
219 $random = $this->get_random_bytes(16);
221 crypt($password, $this->gensalt_blowfish($random));
222 if (strlen($hash) == 60)
226 if (CRYPT_EXT_DES
== 1 && !$this->portable_hashes
) {
227 if (strlen($random) < 3)
228 $random = $this->get_random_bytes(3);
230 crypt($password, $this->gensalt_extended($random));
231 if (strlen($hash) == 20)
235 if (strlen($random) < 6)
236 $random = $this->get_random_bytes(6);
238 $this->crypt_private($password,
239 $this->gensalt_private($random));
240 if (strlen($hash) == 34)
243 // Returning '*' on error is safe here, but would _not_ be safe
244 // in a crypt(3)-like function used _both_ for generating new
245 // hashes and for validating passwords against existing hashes.
249 function CheckPassword($password, $stored_hash)
251 $hash = $this->crypt_private($password, $stored_hash);
253 $hash = crypt($password, $stored_hash);
255 return $hash == $stored_hash;
260 * Create stronger password hash
261 * but still allow to check the password against old password storage has
265 class PeoplePasswordHash
extends PasswordHash
{
272 * Chech password against stored password
274 * The stored password can be plain, a md5 hash or a phpass hash
277 * @param string $Password
280 function CheckPassword($User, $Password, $RegenerateHash=1) {
281 if ($User->Password
[0] === '_' ||
$User->Password
[0] === '$') {
282 return parent
::CheckPassword($Password, $User->Password
);
283 } else if ($Password && $User->Password
!== '*'
284 && ($Password === $User->Password ||
md5($Password) === $User->Password
)
286 if ($RegenerateHash) {
287 $this->SetNewPassword($User, $Password);
295 * Regenerate password
298 * @param string $Password
300 function SetNewPassword($User, $Password) {
301 $UserManager = $this->Context
->ObjectFactory
->NewContextObject(
302 $this->Context
, 'UserManager');
303 $User->Password
= parent
::HashPassword($Password);
304 return $UserManager->SaveUserCredentials($User);
310 * @param Context $Context
311 * @return PeoplePasswordHash
313 function PeoplePasswordHash(&$Context) {
314 $this->Context
=& $Context;
315 parent
::PasswordHash(
316 $Context->Configuration
['PASSWORD_HASH_ITERATION'],
317 $Context->Configuration
['PASSWORD_HASH_PORTABLE']);