Fixed theme Blogger
[vanilla-miry.git] / library / People / People.Class.PasswordHash.php
blob840c687ee8c636177ac5a59400aecf2372760578
1 <?php
2 /**
3 * Portable PHP password hashing framework.
4 *
5 * Version 0.1 / genuine.
6 *
7 * Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
8 * the public domain.
9 *
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
29 * @package People
30 * @version 1.1.8
34 /**
35 * Phpass - create strong password hash
36 * @package People
38 class PasswordHash {
39 var $itoa64;
40 var $iteration_count_log2;
41 var $portable_hashes;
42 var $random_state;
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)
59 $output = '';
60 if (($fh = @fopen('/dev/urandom', 'rb'))) {
61 $output = fread($fh, $count);
62 fclose($fh);
65 if (strlen($output) < $count) {
66 $output = '';
67 for ($i = 0; $i < $count; $i += 16) {
68 $this->random_state =
69 md5(microtime() . $this->random_state);
70 $output .=
71 pack('H*', md5($this->random_state));
73 $output = substr($output, 0, $count);
76 return $output;
79 function encode64($input, $count)
81 $output = '';
82 $i = 0;
83 do {
84 $value = ord($input[$i++]);
85 $output .= $this->itoa64[$value & 0x3f];
86 if ($i < $count)
87 $value |= ord($input[$i]) << 8;
88 $output .= $this->itoa64[($value >> 6) & 0x3f];
89 if ($i++ >= $count)
90 break;
91 if ($i < $count)
92 $value |= ord($input[$i]) << 16;
93 $output .= $this->itoa64[($value >> 12) & 0x3f];
94 if ($i++ >= $count)
95 break;
96 $output .= $this->itoa64[($value >> 18) & 0x3f];
97 } while ($i < $count);
99 return $output;
102 function gensalt_private($input)
104 $output = '$P$';
105 $output .= $this->itoa64[min($this->iteration_count_log2 +
106 ((PHP_VERSION >= '5') ? 5 : 3), 30)];
107 $output .= $this->encode64($input, 6);
109 return $output;
112 function crypt_private($password, $setting)
114 $output = '*0';
115 if (substr($setting, 0, 2) == $output)
116 $output = '*1';
118 if (substr($setting, 0, 3) != '$P$')
119 return $output;
121 $count_log2 = strpos($this->itoa64, $setting[3]);
122 if ($count_log2 < 7 || $count_log2 > 30)
123 return $output;
125 $count = 1 << $count_log2;
127 $salt = substr($setting, 4, 8);
128 if (strlen($salt) != 8)
129 return $output;
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);
139 do {
140 $hash = md5($hash . $password, TRUE);
141 } while (--$count);
142 } else {
143 $hash = pack('H*', md5($salt . $password));
144 do {
145 $hash = pack('H*', md5($hash . $password));
146 } while (--$count);
149 $output = substr($setting, 0, 12);
150 $output .= $this->encode64($hash, 16);
152 return $output;
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;
162 $output = '_';
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);
170 return $output;
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
182 // of entropy.
183 $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
185 $output = '$2a$';
186 $output .= chr(ord('0') + $this->iteration_count_log2 / 10);
187 $output .= chr(ord('0') + $this->iteration_count_log2 % 10);
188 $output .= '$';
190 $i = 0;
191 do {
192 $c1 = ord($input[$i++]);
193 $output .= $itoa64[$c1 >> 2];
194 $c1 = ($c1 & 0x03) << 4;
195 if ($i >= 16) {
196 $output .= $itoa64[$c1];
197 break;
200 $c2 = ord($input[$i++]);
201 $c1 |= $c2 >> 4;
202 $output .= $itoa64[$c1];
203 $c1 = ($c2 & 0x0f) << 2;
205 $c2 = ord($input[$i++]);
206 $c1 |= $c2 >> 6;
207 $output .= $itoa64[$c1];
208 $output .= $itoa64[$c2 & 0x3f];
209 } while (1);
211 return $output;
214 function HashPassword($password)
216 $random = '';
218 if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
219 $random = $this->get_random_bytes(16);
220 $hash =
221 crypt($password, $this->gensalt_blowfish($random));
222 if (strlen($hash) == 60)
223 return $hash;
226 if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
227 if (strlen($random) < 3)
228 $random = $this->get_random_bytes(3);
229 $hash =
230 crypt($password, $this->gensalt_extended($random));
231 if (strlen($hash) == 20)
232 return $hash;
235 if (strlen($random) < 6)
236 $random = $this->get_random_bytes(6);
237 $hash =
238 $this->crypt_private($password,
239 $this->gensalt_private($random));
240 if (strlen($hash) == 34)
241 return $hash;
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.
246 return '*';
249 function CheckPassword($password, $stored_hash)
251 $hash = $this->crypt_private($password, $stored_hash);
252 if ($hash[0] == '*')
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
263 * @package People
265 class PeoplePasswordHash extends PasswordHash {
267 * @var Context;
269 var $Context;
272 * Chech password against stored password
274 * The stored password can be plain, a md5 hash or a phpass hash
276 * @param User $User
277 * @param string $Password
278 * @return boolean
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);
289 return true;
291 return false;
295 * Regenerate password
297 * @param User $User
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);
308 * Constructor
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']);