PHP RFC: Provide argon2i(d) implementations for password_hash() from ext/sodium


If PHP core was built without libargon, then password_hash() and family will not support the argon2i and argon2id hashing mechanisms. Since Password Hash Registry (implemented in 7.4) allows dynamically loaded extensions to register additional algorithms at load time, and since libsodium implements the argon2i and argon2id algorithms, it makes sense to provide backfill implementations of these algorithms if core lacks them.

For anyone who builds PHP themselves, this is mostly a non-issue as they can install libargon before compiling PHP. For distributions, however, this allows the core PHP package to be leaner with fewer external dependencies, while still providing improved algorithm support for those who need it.


Wrap crypto_pwhash_str_alg() and crypto_pwhash_str_verify() from libsodium to provide argon2i and argonid implementations to the password_hash() family of functions if core has not already registered these algorithms.

Additional Changes

PHP's default tuning for the libargon based Argon2i(d) hashing mechanisms hasn't been updated in awhile and is a bit low for best practices. Because of this, PHP's defaults for libargon usage will be updated to match libsodium's OPSLIMIT_MODERATE and MEMLIMIT_MODERATE current values. Defaults for sodium implementation of argon2i(d) will also use these hardcoded values rather than constants in order to ensure consistency between builds of the same PHP version.

  • mem_cost default: 256 * 1024 KB (libargon takes values in KB, so the user-facing API is normalized to KB. For libsodium, we transparently multiply by 1024 since it expects bytes)
  • time_cost default: 3
  • threads default: 1 (libsodium has a max threads count of 1)

New Constants

Just in case an application wants to know where their argon2 support is coming from, I'd propose a new constant to be declared by whichever module is providing the support.

PASSWORD_ARGON2_PROVIDER == 'standard' || 'sodium'

Backward Incompatible Changes

  • Incompatibilities between libargon and libsodium: Sodium's argon2 implementation enforces a minimum time_cost value of 3. Prior to PHP 7.4, the default value for time_cost was 2. This means that argon2 password hashes produced using password_hash() on earlier versions of PHP (and using the default cost value) will not be verifiable by the ext/sodium implementation of these algorithms. Indeed, any argon2 password hash produced using an explicit time_cost of 2 or less will fail to verify with the libsodium implementation.
    • Note that for installations which explicitly provide libargon support, this presents no change in behavior, as the libargon implementation in ext/standard supersedes the sodium implementation.
    • The above turned out to be incorrect. While libsodium will refuse to generate hashes with a time_cost < 3, it will happily verify them.
  • libsodium support for explicitly choosing algorithm: libsodium >= 1.0.15 provides an API for explicitly choosing which argon2 algorithm to use. Older versions of libsodium make this decision for you. For example, my build using libsodium 1.0.13 always produces argon2i hashes, not argon2id. Options:
    • Make libsodium >= 1.0.15 a requirement for building
    • Make libsodium >= 1.0.15 a requirement for including password_hash() support, but still building other features (preferred option)
    • Simply accept not having all algorithms available. (Worst option IMO)
      • This RFC will be moving forward with the second option above: Only export argon2i/argon2id password hashing functions which libsodium >= 1.0.15 is available
  • libsodium with threads > 1: libsodium's pwhash function does not accept a threads parameter and uses a hard-coded value of 1. PHP's password_hash() wrapping of this implementation will fail closed with an error if a value greater than one is specified.

Proposed PHP Version(s)



Started 23rd June 2019. Ends 7th July 2019

Provide argon2i(d) from ext/sodium when argon not present
Real name Yes No
ashnazg (ashnazg)  
carusogabriel (carusogabriel)  
cpriest (cpriest)  
daverandom (daverandom)  
derick (derick)  
duncan3dc (duncan3dc)  
emir (emir)  
galvao (galvao)  
gasolwu (gasolwu)  
girgias (girgias)  
jasny (jasny)  
jedisct1 (jedisct1)  
jhdxr (jhdxr)  
jpauli (jpauli)  
kelunik (kelunik)  
kguest (kguest)  
mariano (mariano)  
mike (mike)  
narf (narf)  
nikic (nikic)  
ocramius (ocramius)  
petk (petk)  
pollita (pollita)  
ramsey (ramsey)  
remi (remi)  
reywob (reywob)  
rjhdby (rjhdby)  
salathe (salathe)  
sergey (sergey)  
stas (stas)  
Final result: 30 0
This poll has been closed.

Proposed Patch

* https://github.com/php/php-src/pull/4012 This implementation only registers the algos for password_hash() *IF* the libsodium library is recent enough. (preferred option umder BC Implications)

rfc/sodium.argon.hash.txt · Last modified: 2020/08/01 23:55 by carusogabriel