49 |
49 |
50 function __construct($iteration_count_log2, $portable_hashes) |
50 function __construct($iteration_count_log2, $portable_hashes) |
51 { |
51 { |
52 $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; |
52 $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; |
53 |
53 |
54 if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) |
54 if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) { |
55 $iteration_count_log2 = 8; |
55 $iteration_count_log2 = 8; |
|
56 } |
56 $this->iteration_count_log2 = $iteration_count_log2; |
57 $this->iteration_count_log2 = $iteration_count_log2; |
57 |
58 |
58 $this->portable_hashes = $portable_hashes; |
59 $this->portable_hashes = $portable_hashes; |
59 |
60 |
60 $this->random_state = microtime(); |
61 $this->random_state = microtime(); |
61 if (function_exists('getmypid')) |
62 if (function_exists('getmypid')) { |
62 $this->random_state .= getmypid(); |
63 $this->random_state .= getmypid(); |
|
64 } |
63 } |
65 } |
64 |
66 |
65 function PasswordHash($iteration_count_log2, $portable_hashes) |
67 function PasswordHash($iteration_count_log2, $portable_hashes) |
66 { |
68 { |
67 self::__construct($iteration_count_log2, $portable_hashes); |
69 self::__construct($iteration_count_log2, $portable_hashes); |
94 $output = ''; |
96 $output = ''; |
95 $i = 0; |
97 $i = 0; |
96 do { |
98 do { |
97 $value = ord($input[$i++]); |
99 $value = ord($input[$i++]); |
98 $output .= $this->itoa64[$value & 0x3f]; |
100 $output .= $this->itoa64[$value & 0x3f]; |
99 if ($i < $count) |
101 if ($i < $count) { |
100 $value |= ord($input[$i]) << 8; |
102 $value |= ord($input[$i]) << 8; |
|
103 } |
101 $output .= $this->itoa64[($value >> 6) & 0x3f]; |
104 $output .= $this->itoa64[($value >> 6) & 0x3f]; |
102 if ($i++ >= $count) |
105 if ($i++ >= $count) { |
103 break; |
106 break; |
104 if ($i < $count) |
107 } |
|
108 if ($i < $count) { |
105 $value |= ord($input[$i]) << 16; |
109 $value |= ord($input[$i]) << 16; |
|
110 } |
106 $output .= $this->itoa64[($value >> 12) & 0x3f]; |
111 $output .= $this->itoa64[($value >> 12) & 0x3f]; |
107 if ($i++ >= $count) |
112 if ($i++ >= $count) { |
108 break; |
113 break; |
|
114 } |
109 $output .= $this->itoa64[($value >> 18) & 0x3f]; |
115 $output .= $this->itoa64[($value >> 18) & 0x3f]; |
110 } while ($i < $count); |
116 } while ($i < $count); |
111 |
117 |
112 return $output; |
118 return $output; |
113 } |
119 } |
114 |
120 |
115 function gensalt_private($input) |
121 function gensalt_private($input) |
116 { |
122 { |
117 $output = '$P$'; |
123 $output = '$P$'; |
118 $output .= $this->itoa64[min($this->iteration_count_log2 + |
124 $output .= $this->itoa64[min($this->iteration_count_log2 + 5, |
119 ((PHP_VERSION >= '5') ? 5 : 3), 30)]; |
125 30)]; |
120 $output .= $this->encode64($input, 6); |
126 $output .= $this->encode64($input, 6); |
121 |
127 |
122 return $output; |
128 return $output; |
123 } |
129 } |
124 |
130 |
125 function crypt_private($password, $setting) |
131 function crypt_private($password, $setting) |
126 { |
132 { |
127 $output = '*0'; |
133 $output = '*0'; |
128 if (substr($setting, 0, 2) === $output) |
134 if (substr($setting, 0, 2) === $output) { |
129 $output = '*1'; |
135 $output = '*1'; |
|
136 } |
130 |
137 |
131 $id = substr($setting, 0, 3); |
138 $id = substr($setting, 0, 3); |
132 # We use "$P$", phpBB3 uses "$H$" for the same thing |
139 # We use "$P$", phpBB3 uses "$H$" for the same thing |
133 if ($id !== '$P$' && $id !== '$H$') |
140 if ($id !== '$P$' && $id !== '$H$') { |
134 return $output; |
141 return $output; |
|
142 } |
135 |
143 |
136 $count_log2 = strpos($this->itoa64, $setting[3]); |
144 $count_log2 = strpos($this->itoa64, $setting[3]); |
137 if ($count_log2 < 7 || $count_log2 > 30) |
145 if ($count_log2 < 7 || $count_log2 > 30) { |
138 return $output; |
146 return $output; |
|
147 } |
139 |
148 |
140 $count = 1 << $count_log2; |
149 $count = 1 << $count_log2; |
141 |
150 |
142 $salt = substr($setting, 4, 8); |
151 $salt = substr($setting, 4, 8); |
143 if (strlen($salt) !== 8) |
152 if (strlen($salt) !== 8) { |
144 return $output; |
153 return $output; |
|
154 } |
145 |
155 |
146 # We were kind of forced to use MD5 here since it's the only |
156 # We were kind of forced to use MD5 here since it's the only |
147 # cryptographic primitive that was available in all versions |
157 # cryptographic primitive that was available in all versions |
148 # of PHP in use. To implement our own low-level crypto in PHP |
158 # of PHP in use. To implement our own low-level crypto in PHP |
149 # would have resulted in much worse performance and |
159 # would have resulted in much worse performance and |
211 |
221 |
212 if (CRYPT_BLOWFISH === 1 && !$this->portable_hashes) { |
222 if (CRYPT_BLOWFISH === 1 && !$this->portable_hashes) { |
213 $random = $this->get_random_bytes(16); |
223 $random = $this->get_random_bytes(16); |
214 $hash = |
224 $hash = |
215 crypt($password, $this->gensalt_blowfish($random)); |
225 crypt($password, $this->gensalt_blowfish($random)); |
216 if (strlen($hash) === 60) |
226 if (strlen($hash) === 60) { |
217 return $hash; |
227 return $hash; |
218 } |
228 } |
219 |
229 } |
220 if (strlen($random) < 6) |
230 |
|
231 if (strlen($random) < 6) { |
221 $random = $this->get_random_bytes(6); |
232 $random = $this->get_random_bytes(6); |
|
233 } |
222 $hash = |
234 $hash = |
223 $this->crypt_private($password, |
235 $this->crypt_private($password, |
224 $this->gensalt_private($random)); |
236 $this->gensalt_private($random)); |
225 if (strlen($hash) === 34) |
237 if (strlen($hash) === 34) { |
226 return $hash; |
238 return $hash; |
|
239 } |
227 |
240 |
228 # Returning '*' on error is safe here, but would _not_ be safe |
241 # Returning '*' on error is safe here, but would _not_ be safe |
229 # in a crypt(3)-like function used _both_ for generating new |
242 # in a crypt(3)-like function used _both_ for generating new |
230 # hashes and for validating passwords against existing hashes. |
243 # hashes and for validating passwords against existing hashes. |
231 return '*'; |
244 return '*'; |
236 if ( strlen( $password ) > 4096 ) { |
249 if ( strlen( $password ) > 4096 ) { |
237 return false; |
250 return false; |
238 } |
251 } |
239 |
252 |
240 $hash = $this->crypt_private($password, $stored_hash); |
253 $hash = $this->crypt_private($password, $stored_hash); |
241 if ($hash[0] === '*') |
254 if ($hash[0] === '*') { |
242 $hash = crypt($password, $stored_hash); |
255 $hash = crypt($password, $stored_hash); |
|
256 } |
243 |
257 |
244 # This is not constant-time. In order to keep the code simple, |
258 # This is not constant-time. In order to keep the code simple, |
245 # for timing safety we currently rely on the salts being |
259 # for timing safety we currently rely on the salts being |
246 # unpredictable, which they are at least in the non-fallback |
260 # unpredictable, which they are at least in the non-fallback |
247 # cases (that is, when we use /dev/urandom and bcrypt). |
261 # cases (that is, when we use /dev/urandom and bcrypt). |