Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 236 lines (214 sloc) 8.601 kb
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
1 <?php
2 /**
3 * Lithium: the most rad php framework
4 *
f99a12f @mehlah Happy 2013!
mehlah authored
5 * @copyright Copyright 2013, Union of RAD (http://union-of-rad.org)
901a9e4 @nateabele Fixing coding conventions and updating copyrights.
nateabele authored
6 * @license http://opensource.org/licenses/bsd-license.php The BSD License
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
7 */
8
9 namespace lithium\security;
10
0e0c649 @nateabele Can't have `\util` dependencies outside of `\util` and `\core`. Moving `...
nateabele authored
11 use lithium\util\String;
30b38d0 @ddebernardy don't extend Crypto
ddebernardy authored
12
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
13 /**
69d7ebb @nateabele 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 @ddebernardy 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 @ddebernardy don't extend Crypto
ddebernardy authored
18 class Password {
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
19
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
20 /**
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
21 * The default log2 number of iterations for Blowfish encryption.
60c340e @ddebernardy simplify base64-related code
ddebernardy authored
22 */
23 const BF = 10;
24
25 /**
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
26 * The default log2 number of iterations for XDES encryption.
60c340e @ddebernardy simplify base64-related code
ddebernardy authored
27 */
28 const XDES = 18;
29
30 /**
3cd874d @ddebernardy 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 @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
33 * using `lithium\security\Password::salt()`.
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
34 *
3f88293 @jperras Fixed typo in `lithium\security\Password` doc block.
jperras authored
35 * Using this function is the proper way to hash a password. Using naïve
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
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 @phishy fixed spelling errors
phishy authored
45 * - The salt generator's count iterator can be increased within Lithium
3cd874d @ddebernardy 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 @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
59 * $salt = Password::salt('bf', 16); // 2^16 iterations
3cd874d @ddebernardy 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 @nateabele 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 @ddebernardy 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 @davidpersson Reodering see and link tags in docblock.
davidpersson authored
72 * @see lithium\security\Password::check()
73 * @see lithium\security\Password::salt()
601a7e8 @davidpersson Adding link to crypt function.
davidpersson authored
74 * @link http://php.net/manual/function.crypt.php
3cd874d @ddebernardy 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 @ddebernardy tidy up docs for use with li_docs
ddebernardy authored
82 */
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
83 public static function hash($password, $salt = null) {
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
84 return crypt($password, $salt ?: static::salt());
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
85 }
86
87 /**
69d7ebb @nateabele 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
3b376e6 @daschl Security: Refactoring timing attack prevention into String class and imp...
daschl authored
89 * comparison, this method uses a constant-time algorithm to defend against timing attacks.
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
90 *
d92b03c @davidpersson Reodering see and link tags in docblock.
davidpersson authored
91 * @see lithium\security\Password::hash()
92 * @see lithium\security\Password::salt()
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
93 * @param string $password The password to check.
94 * @param string $hash The hashed password to compare it to.
7cec108 @phishy fixed spelling error
phishy authored
95 * @return boolean Returns a boolean indicating whether the password is correct.
1b6b035 @ddebernardy tidy up docs for use with li_docs
ddebernardy authored
96 */
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
97 public static function check($password, $hash) {
0ac200f @daschl Security: Renaming compareConstant to compare
daschl authored
98 return String::compare(crypt($password, $hash), $hash);
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
99 }
100
101 /**
102 * Generates a cryptographically strong salt, using the best available
103 * method (tries Blowfish, then XDES, and fallbacks to MD5), for use in
104 * `Password::hash()`.
105 *
106 * Blowfish and XDES are adaptive hashing algorithms. MD5 is not. Adaptive
107 * hashing algorithms are designed in such a way that when computers get
108 * faster, you can tune the algorithm to be slower by increasing the number
109 * of hash iterations, without introducing incompatibility with existing
110 * passwords.
111 *
112 * To pick an appropriate iteration count for adaptive algorithms, consider
113 * that the original DES crypt was designed to have the speed of 4 hashes
114 * per second on the hardware of that time. Slower than 4 hashes per second
115 * would probably dampen usability. Faster than 100 hashes per second is
116 * probably too fast. The defaults generate about 10 hashes per second
117 * using a dual-core 2.2GHz CPU.
118 *
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
119 * _Note 1_: this salt generator is different from naive salt implementations
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
120 * (e.g. `md5(microtime())`) in that it uses all of the available bits of
121 * entropy for the supplied salt method.
122 *
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
123 * _Note2_: this method should not be use to generate custom salts. Indeed,
1b6b035 @ddebernardy tidy up docs for use with li_docs
ddebernardy authored
124 * the resulting salts are prefixed with information expected by PHP's
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
125 * `crypt()`. To get an arbitrarily long, cryptographically strong salt
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
126 * consisting in random sequences of alpha numeric characters, use
0e0c649 @nateabele Can't have `\util` dependencies outside of `\util` and `\core`. Moving `...
nateabele authored
127 * `lithium\util\String::random()` instead.
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
128 *
d92b03c @davidpersson Reodering see and link tags in docblock.
davidpersson authored
129 * @link http://php.net/manual/en/function.crypt.php
130 * @link http://www.postgresql.org/docs/9.0/static/pgcrypto.html
131 * @see lithium\security\Password::hash()
132 * @see lithium\security\Password::check()
133 * @see lithium\util\String::random()
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
134 * @param string $type The hash type. Optional. Defaults to the best
135 * available option. Supported values, along with their maximum
136 * password lengths, include:
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
137 * - `'bf'`: Blowfish (128 salt bits, max 72 chars)
138 * - `'xdes'`: XDES (24 salt bits, max 8 chars)
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
139 * - `'md5'`: MD5 (48 salt bits, unlimited length)
140 * @param integer $count Optional. The base-2 logarithm of the iteration
141 * count, for adaptive algorithms. Defaults to:
142 * - `10` for Blowfish
143 * - `18` for XDES
144 * @return string The salt string.
1b6b035 @ddebernardy tidy up docs for use with li_docs
ddebernardy authored
145 */
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
146 public static function salt($type = null, $count = null) {
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
147 switch (true) {
fa4fe6b @nateabele Cleaning up coding standards warnings
nateabele authored
148 case CRYPT_BLOWFISH === 1 && (!$type || $type === 'bf'):
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
149 return static::_genSaltBf($count);
fa4fe6b @nateabele Cleaning up coding standards warnings
nateabele authored
150 case CRYPT_EXT_DES === 1 && (!$type || $type === 'xdes'):
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
151 return static::_genSaltXDES($count);
152 default:
153 return static::_genSaltMD5();
154 }
155 }
156
157 /**
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
158 * Generates a Blowfish salt for use in `lithium\security\Password::hash()`. _Note_: Does not
0e0c649 @nateabele Can't have `\util` dependencies outside of `\util` and `\core`. Moving `...
nateabele authored
159 * use the `'encode'` option of `String::random()` because it could result in 2 bits less of
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
160 * entropy depending on the last character.
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
161 *
162 * @param integer $count The base-2 logarithm of the iteration count.
163 * Defaults to `10`. Can be `4` to `31`.
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
164 * @return string The Blowfish salt.
1b6b035 @ddebernardy tidy up docs for use with li_docs
ddebernardy authored
165 */
7078e8b @mehlah Minor update to be explicit about default value of a variable
mehlah authored
166 protected static function _genSaltBf($count = null) {
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
167 $count = (integer) $count;
c729b73 @mehlah Update to make use of defined BF and XDES iterations numbers constants
mehlah authored
168 $count = ($count < 4 || $count > 31) ? static::BF : $count;
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
169
041c866 @ddebernardy tests reveal that the last two bits do count for blowfish; revert encodi...
ddebernardy authored
170 $base64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
171 $i = 0;
172
0e0c649 @nateabele Can't have `\util` dependencies outside of `\util` and `\core`. Moving `...
nateabele authored
173 $input = String::random(16);
041c866 @ddebernardy tests reveal that the last two bits do count for blowfish; revert encodi...
ddebernardy authored
174 $output = '';
175
176 do {
177 $c1 = ord($input[$i++]);
178 $output .= $base64[$c1 >> 2];
179 $c1 = ($c1 & 0x03) << 4;
180 if ($i >= 16) {
181 $output .= $base64[$c1];
182 break;
183 }
184
185 $c2 = ord($input[$i++]);
186 $c1 |= $c2 >> 4;
187 $output .= $base64[$c1];
188 $c1 = ($c2 & 0x0f) << 2;
189
190 $c2 = ord($input[$i++]);
191 $c1 |= $c2 >> 6;
192 $output .= $base64[$c1];
193 $output .= $base64[$c2 & 0x3f];
194 } while (1);
195
acb4261 @m4rcsch fixed whitespace issues, docblock errors and line length errors
m4rcsch authored
196 $result = '$2a$';
197 $result .= chr(ord('0') + $count / static::BF);
198 $result .= chr(ord('0') + $count % static::BF);
199 $result .= '$' . $output;
200
201 return $result;
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
202 }
203
204 /**
205 * Generates an Extended DES salt for use in `lithium\security\Password::hash()`.
206 *
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
207 * @param integer $count The base-2 logarithm of the iteration count. Defaults to `18`. Can be
208 * `1` to `24`. 1 will be stripped from the non-log value, e.g. 2^18 - 1, to
209 * ensure we don't use a weak DES key.
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
210 * @return string The XDES salt.
211 */
7078e8b @mehlah Minor update to be explicit about default value of a variable
mehlah authored
212 protected static function _genSaltXDES($count = null) {
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
213 $count = (integer) $count;
c729b73 @mehlah Update to make use of defined BF and XDES iterations numbers constants
mehlah authored
214 $count = ($count < 1 || $count > 24) ? static::XDES : $count;
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
215
216 $count = (1 << $count) - 1;
217 $base64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
218
69d7ebb @nateabele Tightening up APIs, docs, and code formatting in `\security\Crypto` and ...
nateabele authored
219 $output = '_' . $base64[$count & 0x3f] . $base64[($count >> 6) & 0x3f];
220 $output .= $base64[($count >> 12) & 0x3f] . $base64[($count >> 18) & 0x3f];
0e0c649 @nateabele Can't have `\util` dependencies outside of `\util` and `\core`. Moving `...
nateabele authored
221 $output .= String::random(3, array('encode' => String::ENCODE_BASE_64));
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
222
223 return $output;
224 }
225
226 /**
227 * Generates an MD5 salt for use in `lithium\security\Password::hash()`.
228 *
229 * @return string The MD5 salt.
1b6b035 @ddebernardy tidy up docs for use with li_docs
ddebernardy authored
230 */
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
231 protected static function _genSaltMD5() {
0e0c649 @nateabele Can't have `\util` dependencies outside of `\util` and `\core`. Moving `...
nateabele authored
232 return '$1$' . String::random(6, array('encode' => String::ENCODE_BASE_64));
3cd874d @ddebernardy introduce \lithium\security\Crypto and \lithium\security\Password
ddebernardy authored
233 }
234 }
235
236 ?>
Something went wrong with that request. Please try again.