1 | <?php
|
---|
2 | /**
|
---|
3 | * Portable PHP password hashing framework.
|
---|
4 | * @package phpass
|
---|
5 | * @since 2.5
|
---|
6 | * @version 0.1
|
---|
7 | * @link http://www.openwall.com/phpass/
|
---|
8 | */
|
---|
9 |
|
---|
10 | #
|
---|
11 | # Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
|
---|
12 | # the public domain.
|
---|
13 | #
|
---|
14 | # There's absolutely no warranty.
|
---|
15 | #
|
---|
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.
|
---|
19 | #
|
---|
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.
|
---|
23 | #
|
---|
24 | # Obviously, since this code is in the public domain, the above are not
|
---|
25 | # requirements (there can be none), but merely suggestions.
|
---|
26 | #
|
---|
27 |
|
---|
28 | /**
|
---|
29 | * Portable PHP password hashing framework.
|
---|
30 | *
|
---|
31 | * @package phpass
|
---|
32 | * @version 0.1 / genuine
|
---|
33 | * @link http://www.openwall.com/phpass/
|
---|
34 | * @since 2.5
|
---|
35 | */
|
---|
36 | class PasswordHash {
|
---|
37 | var $itoa64;
|
---|
38 | var $iteration_count_log2;
|
---|
39 | var $portable_hashes;
|
---|
40 | var $random_state;
|
---|
41 |
|
---|
42 | function PasswordHash($iteration_count_log2, $portable_hashes)
|
---|
43 | {
|
---|
44 | $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
---|
45 |
|
---|
46 | if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
|
---|
47 | $iteration_count_log2 = 8;
|
---|
48 | $this->iteration_count_log2 = $iteration_count_log2;
|
---|
49 |
|
---|
50 | $this->portable_hashes = $portable_hashes;
|
---|
51 |
|
---|
52 | $this->random_state = microtime() . (function_exists('getmypid') ? getmypid() : '') . uniqid(rand(), TRUE);
|
---|
53 |
|
---|
54 | }
|
---|
55 |
|
---|
56 | function get_random_bytes($count)
|
---|
57 | {
|
---|
58 | $output = '';
|
---|
59 | if (($fh = @fopen('/dev/urandom', 'rb'))) {
|
---|
60 | $output = fread($fh, $count);
|
---|
61 | fclose($fh);
|
---|
62 | }
|
---|
63 |
|
---|
64 | if (strlen($output) < $count) {
|
---|
65 | $output = '';
|
---|
66 | for ($i = 0; $i < $count; $i += 16) {
|
---|
67 | $this->random_state =
|
---|
68 | md5(microtime() . $this->random_state);
|
---|
69 | $output .=
|
---|
70 | pack('H*', md5($this->random_state));
|
---|
71 | }
|
---|
72 | $output = substr($output, 0, $count);
|
---|
73 | }
|
---|
74 |
|
---|
75 | return $output;
|
---|
76 | }
|
---|
77 |
|
---|
78 | function encode64($input, $count)
|
---|
79 | {
|
---|
80 | $output = '';
|
---|
81 | $i = 0;
|
---|
82 | do {
|
---|
83 | $value = ord($input[$i++]);
|
---|
84 | $output .= $this->itoa64[$value & 0x3f];
|
---|
85 | if ($i < $count)
|
---|
86 | $value |= ord($input[$i]) << 8;
|
---|
87 | $output .= $this->itoa64[($value >> 6) & 0x3f];
|
---|
88 | if ($i++ >= $count)
|
---|
89 | break;
|
---|
90 | if ($i < $count)
|
---|
91 | $value |= ord($input[$i]) << 16;
|
---|
92 | $output .= $this->itoa64[($value >> 12) & 0x3f];
|
---|
93 | if ($i++ >= $count)
|
---|
94 | break;
|
---|
95 | $output .= $this->itoa64[($value >> 18) & 0x3f];
|
---|
96 | } while ($i < $count);
|
---|
97 |
|
---|
98 | return $output;
|
---|
99 | }
|
---|
100 |
|
---|
101 | function gensalt_private($input)
|
---|
102 | {
|
---|
103 | $output = '$P$';
|
---|
104 | $output .= $this->itoa64[min($this->iteration_count_log2 +
|
---|
105 | ((PHP_VERSION >= '5') ? 5 : 3), 30)];
|
---|
106 | $output .= $this->encode64($input, 6);
|
---|
107 |
|
---|
108 | return $output;
|
---|
109 | }
|
---|
110 |
|
---|
111 | function crypt_private($password, $setting)
|
---|
112 | {
|
---|
113 | $output = '*0';
|
---|
114 | if (substr($setting, 0, 2) == $output)
|
---|
115 | $output = '*1';
|
---|
116 |
|
---|
117 | if (substr($setting, 0, 3) != '$P$')
|
---|
118 | return $output;
|
---|
119 |
|
---|
120 | $count_log2 = strpos($this->itoa64, $setting[3]);
|
---|
121 | if ($count_log2 < 7 || $count_log2 > 30)
|
---|
122 | return $output;
|
---|
123 |
|
---|
124 | $count = 1 << $count_log2;
|
---|
125 |
|
---|
126 | $salt = substr($setting, 4, 8);
|
---|
127 | if (strlen($salt) != 8)
|
---|
128 | return $output;
|
---|
129 |
|
---|
130 | # We're kind of forced to use MD5 here since it's the only
|
---|
131 | # cryptographic primitive available in all versions of PHP
|
---|
132 | # currently in use. To implement our own low-level crypto
|
---|
133 | # in PHP would result in much worse performance and
|
---|
134 | # consequently in lower iteration counts and hashes that are
|
---|
135 | # quicker to crack (by non-PHP code).
|
---|
136 | if (PHP_VERSION >= '5') {
|
---|
137 | $hash = md5($salt . $password, TRUE);
|
---|
138 | do {
|
---|
139 | $hash = md5($hash . $password, TRUE);
|
---|
140 | } while (--$count);
|
---|
141 | } else {
|
---|
142 | $hash = pack('H*', md5($salt . $password));
|
---|
143 | do {
|
---|
144 | $hash = pack('H*', md5($hash . $password));
|
---|
145 | } while (--$count);
|
---|
146 | }
|
---|
147 |
|
---|
148 | $output = substr($setting, 0, 12);
|
---|
149 | $output .= $this->encode64($hash, 16);
|
---|
150 |
|
---|
151 | return $output;
|
---|
152 | }
|
---|
153 |
|
---|
154 | function gensalt_extended($input)
|
---|
155 | {
|
---|
156 | $count_log2 = min($this->iteration_count_log2 + 8, 24);
|
---|
157 | # This should be odd to not reveal weak DES keys, and the
|
---|
158 | # maximum valid value is (2**24 - 1) which is odd anyway.
|
---|
159 | $count = (1 << $count_log2) - 1;
|
---|
160 |
|
---|
161 | $output = '_';
|
---|
162 | $output .= $this->itoa64[$count & 0x3f];
|
---|
163 | $output .= $this->itoa64[($count >> 6) & 0x3f];
|
---|
164 | $output .= $this->itoa64[($count >> 12) & 0x3f];
|
---|
165 | $output .= $this->itoa64[($count >> 18) & 0x3f];
|
---|
166 |
|
---|
167 | $output .= $this->encode64($input, 3);
|
---|
168 |
|
---|
169 | return $output;
|
---|
170 | }
|
---|
171 |
|
---|
172 | function gensalt_blowfish($input)
|
---|
173 | {
|
---|
174 | # This one needs to use a different order of characters and a
|
---|
175 | # different encoding scheme from the one in encode64() above.
|
---|
176 | # We care because the last character in our encoded string will
|
---|
177 | # only represent 2 bits. While two known implementations of
|
---|
178 | # bcrypt will happily accept and correct a salt string which
|
---|
179 | # has the 4 unused bits set to non-zero, we do not want to take
|
---|
180 | # chances and we also do not want to waste an additional byte
|
---|
181 | # of entropy.
|
---|
182 | $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
---|
183 |
|
---|
184 | $output = '$2a$';
|
---|
185 | $output .= chr(ord('0') + $this->iteration_count_log2 / 10);
|
---|
186 | $output .= chr(ord('0') + $this->iteration_count_log2 % 10);
|
---|
187 | $output .= '$';
|
---|
188 |
|
---|
189 | $i = 0;
|
---|
190 | do {
|
---|
191 | $c1 = ord($input[$i++]);
|
---|
192 | $output .= $itoa64[$c1 >> 2];
|
---|
193 | $c1 = ($c1 & 0x03) << 4;
|
---|
194 | if ($i >= 16) {
|
---|
195 | $output .= $itoa64[$c1];
|
---|
196 | break;
|
---|
197 | }
|
---|
198 |
|
---|
199 | $c2 = ord($input[$i++]);
|
---|
200 | $c1 |= $c2 >> 4;
|
---|
201 | $output .= $itoa64[$c1];
|
---|
202 | $c1 = ($c2 & 0x0f) << 2;
|
---|
203 |
|
---|
204 | $c2 = ord($input[$i++]);
|
---|
205 | $c1 |= $c2 >> 6;
|
---|
206 | $output .= $itoa64[$c1];
|
---|
207 | $output .= $itoa64[$c2 & 0x3f];
|
---|
208 | } while (1);
|
---|
209 |
|
---|
210 | return $output;
|
---|
211 | }
|
---|
212 |
|
---|
213 | function HashPassword($password)
|
---|
214 | {
|
---|
215 | $random = '';
|
---|
216 |
|
---|
217 | if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
|
---|
218 | $random = $this->get_random_bytes(16);
|
---|
219 | $hash =
|
---|
220 | crypt($password, $this->gensalt_blowfish($random));
|
---|
221 | if (strlen($hash) == 60)
|
---|
222 | return $hash;
|
---|
223 | }
|
---|
224 |
|
---|
225 | if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
|
---|
226 | if (strlen($random) < 3)
|
---|
227 | $random = $this->get_random_bytes(3);
|
---|
228 | $hash =
|
---|
229 | crypt($password, $this->gensalt_extended($random));
|
---|
230 | if (strlen($hash) == 20)
|
---|
231 | return $hash;
|
---|
232 | }
|
---|
233 |
|
---|
234 | if (strlen($random) < 6)
|
---|
235 | $random = $this->get_random_bytes(6);
|
---|
236 | $hash =
|
---|
237 | $this->crypt_private($password,
|
---|
238 | $this->gensalt_private($random));
|
---|
239 | if (strlen($hash) == 34)
|
---|
240 | return $hash;
|
---|
241 |
|
---|
242 | # Returning '*' on error is safe here, but would _not_ be safe
|
---|
243 | # in a crypt(3)-like function used _both_ for generating new
|
---|
244 | # hashes and for validating passwords against existing hashes.
|
---|
245 | return '*';
|
---|
246 | }
|
---|
247 |
|
---|
248 | function CheckPassword($password, $stored_hash)
|
---|
249 | {
|
---|
250 | $hash = $this->crypt_private($password, $stored_hash);
|
---|
251 | if ($hash[0] == '*')
|
---|
252 | $hash = crypt($password, $stored_hash);
|
---|
253 |
|
---|
254 | return $hash == $stored_hash;
|
---|
255 | }
|
---|
256 | }
|
---|
257 |
|
---|
258 | ?>
|
---|