Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 241 lines (219 sloc) 8.748 kb
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
1 <?php
2 /**
3 * Lithium: the most rad php framework
4 *
901a9e4 Nate Abele Fixing coding conventions and updating copyrights.
nateabele authored
5 * @copyright Copyright 2011, Union of RAD (http://union-of-rad.org)
6 * @license http://opensource.org/licenses/bsd-license.php The BSD License
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
7 */
8
9 namespace lithium\security;
10
0e0c649 Nate Abele Can't have `\util` dependencies outside of `\util` and `\core`. Moving `...
nateabele authored
11 use lithium\util\String;
30b38d0 Denis de Bernardy don't extend Crypto
ddebernardy authored
12
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
13 /**
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
14 * `Password` utility class that makes use of PHP's `crypt()` function. Includes a
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
15 * cryptographically strong salt generator, and utility functions to hash and check
16 * passwords.
17 */
30b38d0 Denis de Bernardy don't extend Crypto
ddebernardy authored
18 class Password {
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
19
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
20 /**
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
21 * The default log2 number of iterations for Blowfish encryption.
60c340e Denis de Bernardy simplify base64-related code
ddebernardy authored
22 */
23 const BF = 10;
24
25 /**
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
26 * The default log2 number of iterations for XDES encryption.
60c340e Denis de Bernardy simplify base64-related code
ddebernardy authored
27 */
28 const XDES = 18;
29
30 /**
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
31 * Hashes a password using PHP's `crypt()` and an optional salt. If no
32 * salt is supplied, a cryptographically strong salt will be generated
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
33 * using `lithium\security\Password::salt()`.
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
34 *
35 * Using this function is the proper way to hash a password. Using naive
36 * methods such as sha1 or md5, as is done in many web applications, is
37 * improper due to the lack of a cryptographically strong salt.
38 *
39 * Using `lithium\security\Password::hash()` ensures that:
40 *
41 * - Two identical passwords will never use the same salt, thus never
42 * resulting in the same hash; this prevents a potential attacker from
43 * compromising user accounts by using a database of most commonly used
44 * passwords.
a0930f4 Jeff Loiselle fixed spelling errors
phishy authored
45 * - The salt generator's count iterator can be increased within Lithium
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
46 * or your application as computer hardware becomes faster; this results
47 * in slower hash generation, without invalidating existing passwords.
48 *
49 * Usage:
50 *
51 * {{{
52 * // Hash a password before storing it:
53 * $hashed = Password::hash($password);
54 *
55 * // Check a password by comparing it to its hashed value:
56 * $check = Password::check($password, $hashed);
57 *
58 * // Use a stronger custom salt:
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
59 * $salt = Password::salt('bf', 16); // 2^16 iterations
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
60 * $hashed = Password::hash($password, $salt); // Very slow
61 * $check = Password::check($password, $hashed); // Very slow
62 *
63 * // Forward/backward compatibility
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
64 * $salt1 = Password::salt('bf', 6);
65 * $salt2 = Password::salt('bf', 12);
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
66 * $hashed1 = Password::hash($password, $salt1); // Fast
67 * $hashed2 = Password::hash($password, $salt2); // Slow
68 * $check1 = Password::check($password, $hashed1); // True
69 * $check2 = Password::check($password, $hashed2); // True
70 * }}}
71 *
d92b03c David Persson Reodering see and link tags in docblock.
davidpersson authored
72 * @see lithium\security\Password::check()
73 * @see lithium\security\Password::salt()
601a7e8 David Persson Adding link to crypt function.
davidpersson authored
74 * @link http://php.net/manual/function.crypt.php
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
75 * @param string $password The password to hash.
76 * @param string $salt Optional. The salt string.
77 * @return string The hashed password.
78 * The result's length will be:
79 * - 60 chars long for Blowfish hashes
80 * - 20 chars long for XDES hashes
81 * - 34 chars long for MD5 hashes
1b6b035 Denis de Bernardy tidy up docs for use with li_docs
ddebernardy authored
82 */
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
83 public static function hash($password, $salt = null) {
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
84 return crypt($password, $salt ?: static::salt());
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
85 }
86
87 /**
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
88 * Compares a password and its hashed value using PHP's `crypt()`. Rather than a simple string
89 * comparison, this method uses a constant-time algorithm to defend against
90 * [timing attacks](http://codahale.com/a-lesson-in-timing-attacks/).
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
91 *
d92b03c David Persson Reodering see and link tags in docblock.
davidpersson authored
92 * @see lithium\security\Password::hash()
93 * @see lithium\security\Password::salt()
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
94 * @param string $password The password to check.
95 * @param string $hash The hashed password to compare it to.
7cec108 Jeff Loiselle fixed spelling error
phishy authored
96 * @return boolean Returns a boolean indicating whether the password is correct.
1b6b035 Denis de Bernardy tidy up docs for use with li_docs
ddebernardy authored
97 */
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
98 public static function check($password, $hash) {
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
99 $password = crypt($password, $hash);
100 $result = true;
101
102 if (($length = strlen($password)) != strlen($hash)) {
103 return false;
104 }
105 for ($i = 0; $i < $length; $i++) {
106 $result = $result && ($password[$i] === $hash[$i]);
107 }
108 return $result;
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
109 }
110
111 /**
112 * Generates a cryptographically strong salt, using the best available
113 * method (tries Blowfish, then XDES, and fallbacks to MD5), for use in
114 * `Password::hash()`.
115 *
116 * Blowfish and XDES are adaptive hashing algorithms. MD5 is not. Adaptive
117 * hashing algorithms are designed in such a way that when computers get
118 * faster, you can tune the algorithm to be slower by increasing the number
119 * of hash iterations, without introducing incompatibility with existing
120 * passwords.
121 *
122 * To pick an appropriate iteration count for adaptive algorithms, consider
123 * that the original DES crypt was designed to have the speed of 4 hashes
124 * per second on the hardware of that time. Slower than 4 hashes per second
125 * would probably dampen usability. Faster than 100 hashes per second is
126 * probably too fast. The defaults generate about 10 hashes per second
127 * using a dual-core 2.2GHz CPU.
128 *
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
129 * _Note 1_: this salt generator is different from naive salt implementations
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
130 * (e.g. `md5(microtime())`) in that it uses all of the available bits of
131 * entropy for the supplied salt method.
132 *
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
133 * _Note2_: this method should not be use to generate custom salts. Indeed,
1b6b035 Denis de Bernardy tidy up docs for use with li_docs
ddebernardy authored
134 * the resulting salts are prefixed with information expected by PHP's
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
135 * `crypt()`. To get an arbitrarily long, cryptographically strong salt
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
136 * consisting in random sequences of alpha numeric characters, use
0e0c649 Nate Abele Can't have `\util` dependencies outside of `\util` and `\core`. Moving `...
nateabele authored
137 * `lithium\util\String::random()` instead.
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
138 *
d92b03c David Persson Reodering see and link tags in docblock.
davidpersson authored
139 * @link http://php.net/manual/en/function.crypt.php
140 * @link http://www.postgresql.org/docs/9.0/static/pgcrypto.html
141 * @see lithium\security\Password::hash()
142 * @see lithium\security\Password::check()
143 * @see lithium\util\String::random()
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
144 * @param string $type The hash type. Optional. Defaults to the best
145 * available option. Supported values, along with their maximum
146 * password lengths, include:
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
147 * - `'bf'`: Blowfish (128 salt bits, max 72 chars)
148 * - `'xdes'`: XDES (24 salt bits, max 8 chars)
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
149 * - `'md5'`: MD5 (48 salt bits, unlimited length)
150 * @param integer $count Optional. The base-2 logarithm of the iteration
151 * count, for adaptive algorithms. Defaults to:
152 * - `10` for Blowfish
153 * - `18` for XDES
154 * @return string The salt string.
1b6b035 Denis de Bernardy tidy up docs for use with li_docs
ddebernardy authored
155 */
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
156 public static function salt($type = null, $count = null) {
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
157 switch (true) {
158 case CRYPT_BLOWFISH == 1 && (!$type || $type === 'bf'):
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
159 return static::_genSaltBf($count);
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
160 case CRYPT_EXT_DES == 1 && (!$type || $type === 'xdes'):
161 return static::_genSaltXDES($count);
162 default:
163 return static::_genSaltMD5();
164 }
165 }
166
167 /**
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
168 * Generates a Blowfish salt for use in `lithium\security\Password::hash()`. _Note_: Does not
0e0c649 Nate Abele Can't have `\util` dependencies outside of `\util` and `\core`. Moving `...
nateabele authored
169 * use the `'encode'` option of `String::random()` because it could result in 2 bits less of
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
170 * entropy depending on the last character.
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
171 *
172 * @param integer $count The base-2 logarithm of the iteration count.
173 * Defaults to `10`. Can be `4` to `31`.
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
174 * @return string The Blowfish salt.
1b6b035 Denis de Bernardy tidy up docs for use with li_docs
ddebernardy authored
175 */
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
176 protected static function _genSaltBf($count = 10) {
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
177 $count = (integer) $count;
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
178 $count = ($count < 4 || $count > 31) ? 10 : $count;
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
179
041c866 Denis de Bernardy tests reveal that the last two bits do count for blowfish; revert encodi...
ddebernardy authored
180 $base64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
181 $i = 0;
182
0e0c649 Nate Abele Can't have `\util` dependencies outside of `\util` and `\core`. Moving `...
nateabele authored
183 $input = String::random(16);
041c866 Denis de Bernardy tests reveal that the last two bits do count for blowfish; revert encodi...
ddebernardy authored
184 $output = '';
185
186 do {
187 $c1 = ord($input[$i++]);
188 $output .= $base64[$c1 >> 2];
189 $c1 = ($c1 & 0x03) << 4;
190 if ($i >= 16) {
191 $output .= $base64[$c1];
192 break;
193 }
194
195 $c2 = ord($input[$i++]);
196 $c1 |= $c2 >> 4;
197 $output .= $base64[$c1];
198 $c1 = ($c2 & 0x0f) << 2;
199
200 $c2 = ord($input[$i++]);
201 $c1 |= $c2 >> 6;
202 $output .= $base64[$c1];
203 $output .= $base64[$c2 & 0x3f];
204 } while (1);
205
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
206 return '$2a$' . chr(ord('0') + $count / 10) . chr(ord('0') + $count % 10) . '$' . $output;
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
207 }
208
209 /**
210 * Generates an Extended DES salt for use in `lithium\security\Password::hash()`.
211 *
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
212 * @param integer $count The base-2 logarithm of the iteration count. Defaults to `18`. Can be
213 * `1` to `24`. 1 will be stripped from the non-log value, e.g. 2^18 - 1, to
214 * ensure we don't use a weak DES key.
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
215 * @return string The XDES salt.
216 */
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
217 protected static function _genSaltXDES($count = 18) {
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
218 $count = (integer) $count;
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
219 $count = ($count < 1 || $count > 24) ? 16 : $count;
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
220
221 $count = (1 << $count) - 1;
222 $base64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
223
69d7ebb Nate Abele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
224 $output = '_' . $base64[$count & 0x3f] . $base64[($count >> 6) & 0x3f];
225 $output .= $base64[($count >> 12) & 0x3f] . $base64[($count >> 18) & 0x3f];
0e0c649 Nate Abele Can't have `\util` dependencies outside of `\util` and `\core`. Moving `...
nateabele authored
226 $output .= String::random(3, array('encode' => String::ENCODE_BASE_64));
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
227
228 return $output;
229 }
230
231 /**
232 * Generates an MD5 salt for use in `lithium\security\Password::hash()`.
233 *
234 * @return string The MD5 salt.
1b6b035 Denis de Bernardy tidy up docs for use with li_docs
ddebernardy authored
235 */
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
236 protected static function _genSaltMD5() {
0e0c649 Nate Abele Can't have `\util` dependencies outside of `\util` and `\core`. Moving `...
nateabele authored
237 return '$1$' . String::random(6, array('encode' => String::ENCODE_BASE_64));
3cd874d Denis de Bernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
238 }
239 }
240
241 ?>
Something went wrong with that request. Please try again.