This is an old revision of the document!
PHP RFC: Argon2 Password Hash Enhancements
- Version: 0.1
- Date: 2018-01-11
- Author: Charles R. Portwood II firstname.lastname@example.org
- Status: Draft
- First Published at: http://wiki.php.net/rfc/argon2_password_hash_enhancements
This RFC seeks to enhance the functionality initially introduced in http://wiki.php.net/rfc/argon2_password_hash through the addition of Argon2id as a hashing algorithm to supersede Argon2i.
Overview of Argon2 and Argon2id specific algorithm
Argon2 has one primary variant: Argon2id, and two supplementary variants: Argon2d and Argon2i. Argon2d uses data-depending memory access, which makes it suitable for cryptocurrencies and proof-of-work applications with no threats from side-channel timing attacks. Argon2i uses data-independent memory access, which is preferred for password hashing and password-based key derivation. Argon2id works as Argon2i for the first half of the first iteration over the memory, and as Argon2d for the rest, thus providing both side-channel attack protection and brute-force cost savings due to time-memory tradeoffs. Argon2i makes more passes over the memory to protect from tradeoff attacks.
Argon2id is now the recommended Argon2 variant to use in the ITEF draft spec.
The existing password_* functions provided a forward compatible, simplified interface for hashing passwords. This RFC proposes the implementation of Argon2id within the password_* functions for use as a secure alternative to the originally proposed Argon2i.
Proposed PHP Version(s)
PHP NEXT (PHP 7.x ⇒ 7.3)
This change introduces a new hashing algorithm constant:
Changes to password_hash()
The password_hash() function is altered to accept PASSWORD_ARGON2ID as the algorithm.
// Argon2id with default cost factors password_hash('password', PASSWORD_ARGON2ID);
Behaviorally, this implementation will act identical to the Argon2i implementation in that it will accept the same cost variables introduces in the Argon2i RFC.
// Argon2id by name with custom cost factors behaves the same as PASSWORD_ARGON2I password_hash('password', PASSWORD_ARGON2ID, ['memory_cost' => 1<<17, 'time_cost' => 4, 'threads' => 2]);
Functionally, this is simply allow PASSWORD_ARGON2ID to be used. No changes to the API are anticipated.
Changes to password_verify()
The password_verify() function work with Argon2id in addition to Argon2i
Changes to password_get_info()
The password_get_info() function is altered to accept Argon2id hashes, and to return information about a given Argon2 hash.
Changes to password_needs_rehash()
The password_needs_rehash() function is altered to accept Argon2id hashes. If any of the cost factors are changed for an Argon2id hash, this function will return true.
$hash = password_hash('password', PASSWORD_ARGON2ID); password_needs_rehash($hash, PASSWORD_ARGON2ID); // false password_needs_rehash($hash, PASSWORD_ARGON2ID, ['memory_cost' => 1<<17]); // true
Backward Incompatible Changes
Why was Argon2id not included in the original RFC?
The original Argon2i password_hash RFC https://wiki.php.net/rfc/argon2_password_hash was created before Argon2id draft spec was complete or made available. Argon2id was not introduced into the reference library until after the original RFC was voted on, approved, and merged into PHP 7.2 (20161029). To avoid a re-vote and re-implementation of the merge request Argon2id was not included in the original RFC.
That being said, a late addition to the implementation include support for reference library 20161029 since it changed the argon2_encoded() method. This change was made due to uncertainty about what reference library implementation would land in Debian/RHEL, and to ensure forward compatibility with the 20161029 library version if that was the version that would land in Debian/RHEL.
Configuring // Support for Argon2 >= 20161029
Argon2id is only available in reference library >= 20161029.
After the original RFC was merged the reference library version 20161029 was created which had Argon2id, which introduced API incompatibility between the previous version 20160821, specifically with the argon2_encoded() function. Since we didn't know what version would ultimately land in Stretch, the existing m4 scripts check for Argon2id already and use a pre-processor definition to control how this function behaves relative to the Argon2 reference library version.
PHP already knows if Argon2id is available when compiling PHP. As Argon2id is a new algorithm however, we need to decide how –with-password-argon2[=DIR] should behave. Should it include both Argon2i and Argon2id? Should we force a minimum reference library version? Or should we introduce a new configure flag for this new function?
Force lib >= 20161029, --with-password-argon2 is inclusive of both Argon2i and Argon2id
In this scenario we would force the library version to be >= 20161029. From configure, –with-password-argon2[=DIR] m4 would fail if Argon2id wasn't available, and prompt the user to upgrade their library version. The existing implementation already performs a check for the availability of Argon2id due to ABI differences with argon2_encoded() in different library version.
This change would be the easiest, and most forward thinking since Argon2id is the recommended ietf algorithm. Additionally it would ensure that PHP stays up to date with the reference library.
This would require users on Stretch however to manually compile and upgrade to lib >= 20161029. The affect on Windows users is minimal as we're already providing ref/lib's for Windows compilation. Buster (testing) and Sid (unstable) are scheduled with 20161029.
Additionally, it isn't uncommon for PHP to force minimum versions (cite OpenSSL, cURL) of library version.
This is my recommended approach as it forces us to be conscious to changes in the Argon2 reference library.
Allow --with-password-argon2[=DIR] to conditionally enable Argon2id based upon what's available in the library version.
As m4 already knows if Argon2id is available in the lib, the functionality in PHP would be enabled for Argon2id if and only if Argon2id was available in the library.
This approach is the most robust, and provides the greatest amount of backwards comparability with the reference library, but may obscure what feature is actually enabled or not if the user isn't carefully watching the output of configure. In userland code users would have to perform a conditional check for if Argon2id or Argon2i is available, which may introduces confusion if different environments are compiled differently.
I do not recommend this approach for the userland issue mentioned above.
Introduce a new configure argument --with-password-argon2id
This is awkward, but we could explicitly use a new configure flag –with-password-argon2id to determine if Argon2id should be included within PHP. This flag would be in addition to –with-password-argon2. This behavior is award however. As a end user I would expected –with-password-argon2 is inclusive of any valid Argon2 algorithm. Moreover as the –with-password-argon2 check already determines if Argon2id is available, it may introduce more complexity than desired in the implementation.
This option is listed only to illustrate proof of thought and is not recommended.
Proposed Voting Choices
Vote YES to include Argon2id as an alternative to Argon2i within the password_* functions in 7.3. A 50%+1 majority should be sufficient.
Voting will be open for 2 weeks.
The reference implementation assumes –with-password-argon2[=DIR] is inclusive of both Argon2i and Argon2id. At this time this is provided for reference purposes.
- 2018-01-11: 0.1 Initial RFC draft