1<?php
2/**
3 * Portable PHP password hashing framework.
4 * @package phpass
5 * @since 2.5.0
6 * @version 0.5 / WordPress
7 * @link https://www.openwall.com/phpass/
8 */
9
10#
11# Portable PHP password hashing framework.
12#
13# Version 0.5.4 / WordPress.
14#
15# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
16# the public domain. Revised in subsequent years, still public domain.
17#
18# There's absolutely no warranty.
19#
20# The homepage URL for this framework is:
21#
22# http://www.openwall.com/phpass/
23#
24# Please be sure to update the Version line if you edit this file in any way.
25# It is suggested that you leave the main version number intact, but indicate
26# your project name (after the slash) and add your own revision information.
27#
28# Please do not change the "private" password hashing method implemented in
29# here, thereby making your hashes incompatible. However, if you must, please
30# change the hash type identifier (the "$P$") to something different.
31#
32# Obviously, since this code is in the public domain, the above are not
33# requirements (there can be none), but merely suggestions.
34#
35
36/**
37 * Portable PHP password hashing framework.
38 *
39 * @package phpass
40 * @version 0.5 / WordPress
41 * @link https://www.openwall.com/phpass/
42 * @since 2.5.0
43 */
44class PasswordHash {
45 var $itoa64;
46 var $iteration_count_log2;
47 var $portable_hashes;
48 var $random_state;
49
50 function __construct($iteration_count_log2, $portable_hashes)
51 {
52 $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
53
54 if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) {
55 $iteration_count_log2 = 8;
56 }
57 $this->iteration_count_log2 = $iteration_count_log2;
58
59 $this->portable_hashes = $portable_hashes;
60
61 $this->random_state = microtime();
62 if (function_exists('getmypid')) {
63 $this->random_state .= getmypid();
64 }
65 }
66
67 function PasswordHash($iteration_count_log2, $portable_hashes)
68 {
69 self::__construct($iteration_count_log2, $portable_hashes);
70 }
71
72 function get_random_bytes($count)
73 {
74 $output = '';
75 if (@is_readable('/dev/urandom') &&
76 ($fh = @fopen('/dev/urandom', 'rb'))) {
77 $output = fread($fh, $count);
78 fclose($fh);
79 }
80
81 if (strlen($output) < $count) {
82 $output = '';
83 for ($i = 0; $i < $count; $i += 16) {
84 $this->random_state =
85 md5(microtime() . $this->random_state);
86 $output .= md5($this->random_state, TRUE);
87 }
88 $output = substr($output, 0, $count);
89 }
90
91 return $output;
92 }
93
94 function encode64($input, $count)
95 {
96 $output = '';
97 $i = 0;
98 do {
99 $value = ord($input[$i++]);
100 $output .= $this->itoa64[$value & 0x3f];
101 if ($i < $count) {
102 $value |= ord($input[$i]) << 8;
103 }
104 $output .= $this->itoa64[($value >> 6) & 0x3f];
105 if ($i++ >= $count) {
106 break;
107 }
108 if ($i < $count) {
109 $value |= ord($input[$i]) << 16;
110 }
111 $output .= $this->itoa64[($value >> 12) & 0x3f];
112 if ($i++ >= $count) {
113 break;
114 }
115 $output .= $this->itoa64[($value >> 18) & 0x3f];
116 } while ($i < $count);
117
118 return $output;
119 }
120
121 function gensalt_private($input)
122 {
123 $output = '$P$';
124 $output .= $this->itoa64[min($this->iteration_count_log2 + 5,
125 30)];
126 $output .= $this->encode64($input, 6);
127
128 return $output;
129 }
130
131 function crypt_private($password, $setting)
132 {
133 $output = '*0';
134 if (substr($setting, 0, 2) === $output) {
135 $output = '*1';
136 }
137
138 $id = substr($setting, 0, 3);
139 # We use "$P$", phpBB3 uses "$H$" for the same thing
140 if ($id !== '$P$' && $id !== '$H$') {
141 return $output;
142 }
143
144 $count_log2 = strpos($this->itoa64, $setting[3]);
145 if ($count_log2 < 7 || $count_log2 > 30) {
146 return $output;
147 }
148
149 $count = 1 << $count_log2;
150
151 $salt = substr($setting, 4, 8);
152 if (strlen($salt) !== 8) {
153 return $output;
154 }
155
156 # We were kind of forced to use MD5 here since it's the only
157 # cryptographic primitive that was available in all versions
158 # of PHP in use. To implement our own low-level crypto in PHP
159 # would have resulted in much worse performance and
160 # consequently in lower iteration counts and hashes that are
161 # quicker to crack (by non-PHP code).
162 $hash = md5($salt . $password, TRUE);
163 do {
164 $hash = md5($hash . $password, TRUE);
165 } while (--$count);
166
167 $output = substr($setting, 0, 12);
168 $output .= $this->encode64($hash, 16);
169
170 return $output;
171 }
172
173 function gensalt_blowfish($input)
174 {
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';
184
185 $output = '$2a$';
186 $output .= chr((int)(ord('0') + $this->iteration_count_log2 / 10));
187 $output .= chr(ord('0') + $this->iteration_count_log2 % 10);
188 $output .= '$';
189
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;
198 }
199
200 $c2 = ord($input[$i++]);
201 $c1 |= $c2 >> 4;
202 $output .= $itoa64[$c1];
203 $c1 = ($c2 & 0x0f) << 2;
204
205 $c2 = ord($input[$i++]);
206 $c1 |= $c2 >> 6;
207 $output .= $itoa64[$c1];
208 $output .= $itoa64[$c2 & 0x3f];
209 } while (1);
210
211 return $output;
212 }
213
214 function HashPassword($password)
215 {
216 if ( strlen( $password ) > 4096 ) {
217 return '*';
218 }
219
220 $random = '';
221
222 if (CRYPT_BLOWFISH === 1 && !$this->portable_hashes) {
223 $random = $this->get_random_bytes(16);
224 $hash =
225 crypt($password, $this->gensalt_blowfish($random));
226 if (strlen($hash) === 60) {
227 return $hash;
228 }
229 }
230
231 if (strlen($random) < 6) {
232 $random = $this->get_random_bytes(6);
233 }
234 $hash =
235 $this->crypt_private($password,
236 $this->gensalt_private($random));
237 if (strlen($hash) === 34) {
238 return $hash;
239 }
240
241 # Returning '*' on error is safe here, but would _not_ be safe
242 # in a crypt(3)-like function used _both_ for generating new
243 # hashes and for validating passwords against existing hashes.
244 return '*';
245 }
246
247 function CheckPassword($password, $stored_hash)
248 {
249 if ( strlen( $password ) > 4096 ) {
250 return false;
251 }
252
253 $hash = $this->crypt_private($password, $stored_hash);
254 if ($hash[0] === '*') {
255 $hash = crypt($password, $stored_hash);
256 }
257
258 # This is not constant-time. In order to keep the code simple,
259 # for timing safety we currently rely on the salts being
260 # unpredictable, which they are at least in the non-fallback
261 # cases (that is, when we use /dev/urandom and bcrypt).
262 return $hash === $stored_hash;
263 }
264}
265