This is an old revision of the document!

PHP RFC: Your Title Here


The password_*() functions introduced a means to support standardized password hashing mechanisms for use by applications. Support for bcrypt was included in the initial launch, with argon2i and argon2id being added later (if and only if libargon was available on the system at the time of compilation.

This qualification about the availability of libargon is a complicating factor for linux distributions where the core of php-src is often kept intentionally lean, with additional extensions being provided in separate packages to be loaded only by those installations who want the additional functionality. This means that some percentage of distro package users either have a library dependency they didn't want, or lack functionality they did want.

This proposal seeks to remedy this by introducing a password hashing registry mechanism similar to the Hash extension's php_hash_register_algo() API.


Internal API

  struct php_password_algo {
    const char* name; // Symbolic name of the algorithm, e.g. "argon2id"
    zend_string* (*hash)(const zend_string* password, zend_array* options);
    zend_bool (*verify)(const zend_string* password, const zend_string* hash);
    zend_bool (*needs_rehash)(const zend_string* hash, zend_array *options);
    int (*get_info)(zval *return_value, const zend_string* hash);
    zend_bool (*valid)(const zend_string* hash);
  PHPAPI zend_long php_password_algo_register(const php_password_algo* algo);
  PHPAPI const php_password_algo* php_password_algo_default(zend_long *pid)
  PHPAPI const php_password_algo* php_password_algo_find(zend_long algo_id);
  PHPAPI const php_password_algo* php_password_algo_identify(const zend_string* hash, zend_long *pid);
  PHPAPI void php_password_algo_unregister(zend_long algo_id);

Extensions wishing to provide an algorithm implementation will setup a (typically global const) structure to contain the four method pointers and call php_password_algo_register() during MINIT to hook in. The integer value returned by this function will be a process unique integer value which the extension may assign to a constant or leave to discovery via password_algos().

The hash, verify, and needs_rehash method pointers function exactly as their PHP userspace functions describe, but don't require an algo ID, as this has already been determined by the exported functions in looking up the algorithm.

The get_info method pointer allows adding entries to an array return value for the password_get_info() userspace command. This function must return SUCCESS or FAILURE.

The valid method pointer is the mechanism used for determining what algorithm handler is appropriate for a given hash string. For example, only the bcrypt handler should return true for a hash string beginning with “$2y$”.

In order to fail-closed, if more than one algorithm returns true for valid(), then ALL implementations must pass their corresponding verify() test in order for the password to be considered valid. This is considered an undesirable use-case, and implementations attempting to hijack a password hashing mechanism must explicitly make the decision to disable the previous handler in order to override behavior.

Userspace API

An additional function, password_algos(), will be added to return a complete list of all registered password hashing algorithms as a numerically indexed array. The key of this array will be the process-unique algorithm identifier, while the value element will be the human readable name for the algo. For example:

  > print_r(password_algos());
  Array (
      [1] => "bcrypt"
      [2] => "argon2i"
      [3] => "argon2id"

Backward Incompatible Changes

Algorithm numeric identifiers are no longer fixed values which can't change between installations. Applications MUST use the constant or symbolic name when specifying algorithms rather than hard code the numeric value for the algo.

Extension Changes

ext/standard will continue to register the bcrypt algo, as well as argon2i and argon2id if the library is available at compile time in order to maintain compatibility with older builds. The ext/sodium extension will be extended to make “standard” an explicit dependency, then will check to see if argon2i/argon2id were registered already. If not, they will be registered by ext/sodium to produce compatible results available via dynamic load.

Proposed PHP Version(s)


Open Questions

  • Should the registry support password hashing mechanisms defined in script code? (I don't think so, but feel free to disagree)

Future Scope

Review ext/sodium to see if there are additional password hashing algorithms it may be appropriate to enable.

Proposed Voting Choices

Simple 50% +1, make the password hashing system extensible via internal-only registry.

Patches and Tests

rfc/password_registry.1539693638.txt.gz · Last modified: 2018/10/16 12:40 by pollita