35 |
35 |
36 /** |
36 /** |
37 * Creates a recovery mode key. |
37 * Creates a recovery mode key. |
38 * |
38 * |
39 * @since 5.2.0 |
39 * @since 5.2.0 |
40 * |
40 * @since 6.8.0 The stored key is now hashed using wp_fast_hash() instead of phpass. |
41 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance. |
|
42 * |
41 * |
43 * @param string $token A token generated by {@see generate_recovery_mode_token()}. |
42 * @param string $token A token generated by {@see generate_recovery_mode_token()}. |
44 * @return string Recovery mode key. |
43 * @return string Recovery mode key. |
45 */ |
44 */ |
46 public function generate_and_store_recovery_mode_key( $token ) { |
45 public function generate_and_store_recovery_mode_key( $token ) { |
47 |
|
48 global $wp_hasher; |
|
49 |
|
50 $key = wp_generate_password( 22, false ); |
46 $key = wp_generate_password( 22, false ); |
51 |
|
52 if ( empty( $wp_hasher ) ) { |
|
53 require_once ABSPATH . WPINC . '/class-phpass.php'; |
|
54 $wp_hasher = new PasswordHash( 8, true ); |
|
55 } |
|
56 |
|
57 $hashed = $wp_hasher->HashPassword( $key ); |
|
58 |
47 |
59 $records = $this->get_keys(); |
48 $records = $this->get_keys(); |
60 |
49 |
61 $records[ $token ] = array( |
50 $records[ $token ] = array( |
62 'hashed_key' => $hashed, |
51 'hashed_key' => wp_fast_hash( $key ), |
63 'created_at' => time(), |
52 'created_at' => time(), |
64 ); |
53 ); |
65 |
54 |
66 $this->update_keys( $records ); |
55 $this->update_keys( $records ); |
67 |
56 |
83 * |
72 * |
84 * Recovery mode keys can only be used once; the key will be consumed in the process. |
73 * Recovery mode keys can only be used once; the key will be consumed in the process. |
85 * |
74 * |
86 * @since 5.2.0 |
75 * @since 5.2.0 |
87 * |
76 * |
88 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance. |
|
89 * |
|
90 * @param string $token The token used when generating the given key. |
77 * @param string $token The token used when generating the given key. |
91 * @param string $key The unhashed key. |
78 * @param string $key The plain text key. |
92 * @param int $ttl Time in seconds for the key to be valid for. |
79 * @param int $ttl Time in seconds for the key to be valid for. |
93 * @return true|WP_Error True on success, error object on failure. |
80 * @return true|WP_Error True on success, error object on failure. |
94 */ |
81 */ |
95 public function validate_recovery_mode_key( $token, $key, $ttl ) { |
82 public function validate_recovery_mode_key( $token, $key, $ttl ) { |
96 global $wp_hasher; |
|
97 |
|
98 $records = $this->get_keys(); |
83 $records = $this->get_keys(); |
99 |
84 |
100 if ( ! isset( $records[ $token ] ) ) { |
85 if ( ! isset( $records[ $token ] ) ) { |
101 return new WP_Error( 'token_not_found', __( 'Recovery Mode not initialized.' ) ); |
86 return new WP_Error( 'token_not_found', __( 'Recovery Mode not initialized.' ) ); |
102 } |
87 } |
107 |
92 |
108 if ( ! is_array( $record ) || ! isset( $record['hashed_key'], $record['created_at'] ) ) { |
93 if ( ! is_array( $record ) || ! isset( $record['hashed_key'], $record['created_at'] ) ) { |
109 return new WP_Error( 'invalid_recovery_key_format', __( 'Invalid recovery key format.' ) ); |
94 return new WP_Error( 'invalid_recovery_key_format', __( 'Invalid recovery key format.' ) ); |
110 } |
95 } |
111 |
96 |
112 if ( empty( $wp_hasher ) ) { |
97 if ( ! wp_verify_fast_hash( $key, $record['hashed_key'] ) ) { |
113 require_once ABSPATH . WPINC . '/class-phpass.php'; |
|
114 $wp_hasher = new PasswordHash( 8, true ); |
|
115 } |
|
116 |
|
117 if ( ! $wp_hasher->CheckPassword( $key, $record['hashed_key'] ) ) { |
|
118 return new WP_Error( 'hash_mismatch', __( 'Invalid recovery key.' ) ); |
98 return new WP_Error( 'hash_mismatch', __( 'Invalid recovery key.' ) ); |
119 } |
99 } |
120 |
100 |
121 if ( time() > $record['created_at'] + $ttl ) { |
101 if ( time() > $record['created_at'] + $ttl ) { |
122 return new WP_Error( 'key_expired', __( 'Recovery key expired.' ) ); |
102 return new WP_Error( 'key_expired', __( 'Recovery key expired.' ) ); |
167 |
147 |
168 /** |
148 /** |
169 * Gets the recovery key records. |
149 * Gets the recovery key records. |
170 * |
150 * |
171 * @since 5.2.0 |
151 * @since 5.2.0 |
|
152 * @since 6.8.0 Each key is now hashed using wp_fast_hash() instead of phpass. |
|
153 * Existing keys may still be hashed using phpass. |
172 * |
154 * |
173 * @return array Associative array of $token => $data pairs, where $data has keys 'hashed_key' |
155 * @return array { |
174 * and 'created_at'. |
156 * Associative array of token => data pairs, where the data is an associative |
|
157 * array of information about the key. |
|
158 * |
|
159 * @type array ...$0 { |
|
160 * Information about the key. |
|
161 * |
|
162 * @type string $hashed_key The hashed value of the key. |
|
163 * @type int $created_at The timestamp when the key was created. |
|
164 * } |
|
165 * } |
175 */ |
166 */ |
176 private function get_keys() { |
167 private function get_keys() { |
177 return (array) get_option( $this->option_name, array() ); |
168 return (array) get_option( $this->option_name, array() ); |
178 } |
169 } |
179 |
170 |
180 /** |
171 /** |
181 * Updates the recovery key records. |
172 * Updates the recovery key records. |
182 * |
173 * |
183 * @since 5.2.0 |
174 * @since 5.2.0 |
|
175 * @since 6.8.0 Each key should now be hashed using wp_fast_hash() instead of phpass. |
184 * |
176 * |
185 * @param array $keys Associative array of $token => $data pairs, where $data has keys 'hashed_key' |
177 * @param array $keys { |
186 * and 'created_at'. |
178 * Associative array of token => data pairs, where the data is an associative |
|
179 * array of information about the key. |
|
180 * |
|
181 * @type array ...$0 { |
|
182 * Information about the key. |
|
183 * |
|
184 * @type string $hashed_key The hashed value of the key. |
|
185 * @type int $created_at The timestamp when the key was created. |
|
186 * } |
|
187 * } |
187 * @return bool True on success, false on failure. |
188 * @return bool True on success, false on failure. |
188 */ |
189 */ |
189 private function update_keys( array $keys ) { |
190 private function update_keys( array $keys ) { |
190 return update_option( $this->option_name, $keys ); |
191 return update_option( $this->option_name, $keys, false ); |
191 } |
192 } |
192 } |
193 } |