1414 */
1515class Password
1616{
17- const DEFAULT_COST = 12 ;
17+ /**
18+ * Default crypt cost factor.
19+ *
20+ * @var int
21+ */
22+ protected static $ rounds = 12 ;
1823
1924 /**
2025 * Returns the hashing type for a specified password hash.
2126 *
2227 * Automatically detects the hash type: "sha1" (for UserCake legacy accounts), "legacy" (for 0.1.x accounts), and "modern" (used for new accounts).
2328 * @param string $password the hashed password.
29+ * @param array $options
2430 * @return string "sha1"|"legacy"|"modern".
2531 */
26- public static function getHashType ($ password )
32+ public static function getHashType ($ password, array $ options = [] )
2733 {
2834 // If the password in the db is 65 characters long, we have an sha1-hashed password.
2935 if (strlen ($ password ) == 65 ) {
3036 return 'sha1 ' ;
31- } elseif (substr ($ password , 0 , 7 ) == '$2y$ ' . self :: $ _DEFAULT_COST . '$ ' ) {
37+ } elseif (substr ($ password , 0 , 7 ) == '$2y$ ' . static :: cost ( $ options ) . '$ ' ) {
3238 return 'legacy ' ;
3339 }
3440
@@ -39,12 +45,15 @@ public static function getHashType($password)
3945 * Hashes a plaintext password using bcrypt.
4046 *
4147 * @param string $password the plaintext password.
48+ * @param array $options
4249 * @return string the hashed password.
4350 * @throws HashFailedException
4451 */
45- public static function hash ($ password )
52+ public static function hash ($ password, array $ options = [] )
4653 {
47- $ hash = password_hash ($ password , PASSWORD_BCRYPT );
54+ $ hash = password_hash ($ password , PASSWORD_BCRYPT , [
55+ 'cost ' => static ::cost ($ options ),
56+ ]);
4857
4958 if (!$ hash ) {
5059 throw new HashFailedException ();
@@ -60,9 +69,11 @@ public static function hash($password)
6069 * @param string $hash The hash to compare against.
6170 * @return boolean True if the password matches, false otherwise.
6271 */
63- public static function verify ($ password , $ hash )
72+ public static function verify ($ password , $ hash, array $ options = [] )
6473 {
65- if (static ::getHashType ($ hash ) == 'sha1 ' ) {
74+ $ hashType = static ::getHashType ($ hash , $ options );
75+
76+ if ($ hashType == 'sha1 ' ) {
6677 // Legacy UserCake passwords
6778 $ salt = substr ($ hash , 0 , 25 ); // Extract the salt from the hash
6879 $ hashInput = $ salt . sha1 ($ salt . $ password );
@@ -72,11 +83,11 @@ public static function verify($password, $hash)
7283
7384 return false ;
7485
75- } elseif (static :: getHashType ( $ hash ) == 'legacy ' ) {
86+ } elseif ($ hashType == 'legacy ' ) {
7687 // Homegrown implementation (assuming that current install has been using a cost parameter of 12)
7788 // Used for manual implementation of bcrypt.
7889 $ extract = substr ($ hash , 0 , 60 );
79- $ compare = crypt ($ password , '$2y$ ' . self :: $ DEFAULT_COST . '$ ' . substr ($ hash , 60 ));
90+ $ compare = crypt ($ password , '$2y$ ' . static :: cost ( $ options ) . '$ ' . substr ($ hash , 60 ));
8091
8192 if (hash_equals ($ extract , $ compare ) === true ) {
8293 return true ;
@@ -88,4 +99,15 @@ public static function verify($password, $hash)
8899 // Modern implementation
89100 return password_verify ($ password , $ hash );
90101 }
102+
103+ /**
104+ * Extract the cost value from the options array.
105+ *
106+ * @param array $options
107+ * @return int
108+ */
109+ protected static function cost (array $ options = [])
110+ {
111+ return isset ($ options ['rounds ' ]) ? $ options ['rounds ' ] : static ::$ rounds ;
112+ }
91113}
0 commit comments