wp/wp-includes/class-wp-application-passwords.php
changeset 22 8c2e4d02f4ef
parent 21 48c4eec2b7e6
--- a/wp/wp-includes/class-wp-application-passwords.php	Fri Sep 05 18:40:08 2025 +0200
+++ b/wp/wp-includes/class-wp-application-passwords.php	Fri Sep 05 18:52:52 2025 +0200
@@ -60,6 +60,7 @@
 	 *
 	 * @since 5.6.0
 	 * @since 5.7.0 Returns WP_Error if application name already exists.
+	 * @since 6.8.0 The hashed password value now uses wp_fast_hash() instead of phpass.
 	 *
 	 * @param int   $user_id  User ID.
 	 * @param array $args     {
@@ -71,7 +72,7 @@
 	 * @return array|WP_Error {
 	 *     Application password details, or a WP_Error instance if an error occurs.
 	 *
-	 *     @type string $0 The unhashed generated application password.
+	 *     @type string $0 The generated application password in plain text.
 	 *     @type array  $1 {
 	 *         The details about the created password.
 	 *
@@ -94,12 +95,8 @@
 			return new WP_Error( 'application_password_empty_name', __( 'An application name is required to create an application password.' ), array( 'status' => 400 ) );
 		}
 
-		if ( self::application_name_exists_for_user( $user_id, $args['name'] ) ) {
-			return new WP_Error( 'application_password_duplicate_name', __( 'Each application name should be unique.' ), array( 'status' => 409 ) );
-		}
-
 		$new_password    = wp_generate_password( static::PW_LENGTH, false );
-		$hashed_password = wp_hash_password( $new_password );
+		$hashed_password = self::hash_password( $new_password );
 
 		$new_item = array(
 			'uuid'      => wp_generate_uuid4(),
@@ -128,6 +125,7 @@
 		 * Fires when an application password is created.
 		 *
 		 * @since 5.6.0
+		 * @since 6.8.0 The hashed password value now uses wp_fast_hash() instead of phpass.
 		 *
 		 * @param int    $user_id      The user ID.
 		 * @param array  $new_item     {
@@ -141,7 +139,7 @@
 		 *     @type null   $last_used Null.
 		 *     @type null   $last_ip   Null.
 		 * }
-		 * @param string $new_password The unhashed generated application password.
+		 * @param string $new_password The generated application password in plain text.
 		 * @param array  $args         {
 		 *     Arguments used to create the application password.
 		 *
@@ -161,7 +159,7 @@
 	 *
 	 * @param int $user_id User ID.
 	 * @return array {
-	 *     The list of app passwords.
+	 *     The list of application passwords.
 	 *
 	 *     @type array ...$0 {
 	 *         @type string      $uuid      The unique identifier for the application password.
@@ -204,7 +202,17 @@
 	 *
 	 * @param int    $user_id User ID.
 	 * @param string $uuid    The password's UUID.
-	 * @return array|null The application password if found, null otherwise.
+	 * @return array|null {
+	 *     The application password if found, null otherwise.
+	 *
+	 *     @type string      $uuid      The unique identifier for the application password.
+	 *     @type string      $app_id    A UUID provided by the application to uniquely identify it.
+	 *     @type string      $name      The name of the application password.
+	 *     @type string      $password  A one-way hash of the password.
+	 *     @type int         $created   Unix timestamp of when the password was created.
+	 *     @type int|null    $last_used The Unix timestamp of the GMT date the application password was last used.
+	 *     @type string|null $last_ip   The IP address the application password was last used by.
+	 * }
 	 */
 	public static function get_user_application_password( $user_id, $uuid ) {
 		$passwords = static::get_user_application_passwords( $user_id );
@@ -243,10 +251,21 @@
 	 * Updates an application password.
 	 *
 	 * @since 5.6.0
+	 * @since 6.8.0 The actual password should now be hashed using wp_fast_hash().
 	 *
 	 * @param int    $user_id User ID.
 	 * @param string $uuid    The password's UUID.
-	 * @param array  $update  Information about the application password to update.
+	 * @param array  $update  {
+	 *     Information about the application password to update.
+	 *
+	 *     @type string      $uuid      The unique identifier for the application password.
+	 *     @type string      $app_id    A UUID provided by the application to uniquely identify it.
+	 *     @type string      $name      The name of the application password.
+	 *     @type string      $password  A one-way hash of the password.
+	 *     @type int         $created   Unix timestamp of when the password was created.
+	 *     @type int|null    $last_used The Unix timestamp of the GMT date the application password was last used.
+	 *     @type string|null $last_ip   The IP address the application password was last used by.
+	 * }
 	 * @return true|WP_Error True if successful, otherwise a WP_Error instance is returned on error.
 	 */
 	public static function update_application_password( $user_id, $uuid, $update = array() ) {
@@ -280,9 +299,21 @@
 			 * Fires when an application password is updated.
 			 *
 			 * @since 5.6.0
+			 * @since 6.8.0 The password is now hashed using wp_fast_hash() instead of phpass.
+			 *              Existing passwords may still be hashed using phpass.
 			 *
 			 * @param int   $user_id The user ID.
-			 * @param array $item    The updated app password details.
+			 * @param array $item    {
+			 *     The updated application password details.
+			 *
+			 *     @type string      $uuid      The unique identifier for the application password.
+			 *     @type string      $app_id    A UUID provided by the application to uniquely identify it.
+			 *     @type string      $name      The name of the application password.
+			 *     @type string      $password  A one-way hash of the password.
+			 *     @type int         $created   Unix timestamp of when the password was created.
+			 *     @type int|null    $last_used The Unix timestamp of the GMT date the application password was last used.
+			 *     @type string|null $last_ip   The IP address the application password was last used by.
+			 * }
 			 * @param array $update  The information to update.
 			 */
 			do_action( 'wp_update_application_password', $user_id, $item, $update );
@@ -404,9 +435,22 @@
 	 * @since 5.6.0
 	 *
 	 * @param int   $user_id   User ID.
-	 * @param array $passwords Application passwords.
+	 * @param array $passwords {
+	 *     The list of application passwords.
 	 *
-	 * @return bool
+	 *     @type array ...$0 {
+	 *         @type string      $uuid      The unique identifier for the application password.
+	 *         @type string      $app_id    A UUID provided by the application to uniquely identify it.
+	 *         @type string      $name      The name of the application password.
+	 *         @type string      $password  A one-way hash of the password.
+	 *         @type int         $created   Unix timestamp of when the password was created.
+	 *         @type int|null    $last_used The Unix timestamp of the GMT date the application password was last used.
+	 *         @type string|null $last_ip   The IP address the application password was last used by.
+	 *     }
+	 * }
+	 * @return int|bool User meta ID if the key didn't exist (ie. this is the first time that an application password
+	 *                  has been saved for the user), true on successful update, false on failure or if the value passed
+	 *                  is the same as the one that is already in the database.
 	 */
 	protected static function set_user_application_passwords( $user_id, $passwords ) {
 		return update_user_meta( $user_id, static::USERMETA_KEY_APPLICATION_PASSWORDS, $passwords );
@@ -420,9 +464,52 @@
 	 * @param string $raw_password The raw application password.
 	 * @return string The chunked password.
 	 */
-	public static function chunk_password( $raw_password ) {
+	public static function chunk_password(
+		#[\SensitiveParameter]
+		$raw_password
+	) {
 		$raw_password = preg_replace( '/[^a-z\d]/i', '', $raw_password );
 
 		return trim( chunk_split( $raw_password, 4, ' ' ) );
 	}
+
+	/**
+	 * Hashes a plaintext application password.
+	 *
+	 * @since 6.8.0
+	 *
+	 * @param string $password Plaintext password.
+	 * @return string Hashed password.
+	 */
+	public static function hash_password(
+		#[\SensitiveParameter]
+		string $password
+	): string {
+		return wp_fast_hash( $password );
+	}
+
+	/**
+	 * Checks a plaintext application password against a hashed password.
+	 *
+	 * @since 6.8.0
+	 *
+	 * @param string $password Plaintext password.
+	 * @param string $hash     Hash of the password to check against.
+	 * @return bool Whether the password matches the hashed password.
+	 */
+	public static function check_password(
+		#[\SensitiveParameter]
+		string $password,
+		string $hash
+	): bool {
+		if ( ! str_starts_with( $hash, '$generic$' ) ) {
+			/*
+			 * If the hash doesn't start with `$generic$`, it is a hash created with `wp_hash_password()`.
+			 * This is the case for application passwords created before 6.8.0.
+			 */
+			return wp_check_password( $password, $hash );
+		}
+
+		return wp_verify_fast_hash( $password, $hash );
+	}
 }