diff options
| -rw-r--r-- | UPGRADING | 12 | ||||
| -rw-r--r-- | ext/standard/config.m4 | 11 | ||||
| -rw-r--r-- | ext/standard/config.w32 | 6 | ||||
| -rw-r--r-- | ext/standard/password.c | 60 | ||||
| -rw-r--r-- | ext/standard/php_password.h | 1 | ||||
| -rw-r--r-- | ext/standard/tests/password/password_get_info_argon2.phpt | 19 | ||||
| -rw-r--r-- | ext/standard/tests/password/password_hash_argon2.phpt | 7 | ||||
| -rw-r--r-- | ext/standard/tests/password/password_hash_error_argon2.phpt | 15 | ||||
| -rw-r--r-- | ext/standard/tests/password/password_needs_rehash_argon2.phpt | 15 | ||||
| -rw-r--r-- | ext/standard/tests/password/password_verify_argon2.phpt | 8 |
10 files changed, 125 insertions, 29 deletions
@@ -150,6 +150,15 @@ readline: options has been added to readline_info(). These options are only available if PHP is linked against libreadline (rather than libedit). +Standard: + . The -–with-password-argon2[=dir] configure argument now provides support for + both Argon2i and Argon2id hashes in the password_hash(), password_verify(), + password_get_info(), and password_needs_rehash() functions. Passwords may be + hashed and verified using the PASSWORD_ARGON2ID constant. + Support for both Argon2i and Argon2id in the password_* functions now requires + PHP be linked against libargon2 reference library >= 20161029. + (RFC: https://wiki.php.net/rfc/argon2_password_hash_enhancements). + ======================================== 3. Changes in SAPI modules ======================================== @@ -311,6 +320,9 @@ PGSQL: . Requires Postgres 9.6 - PGSQL_DIAG_SEVERITY_NONLOCALIZED +Standard: + . PASSWORD_ARGON2ID + ======================================== 11. Changes to INI File Handling ======================================== diff --git a/ext/standard/config.m4 b/ext/standard/config.m4 index e4146aa2fb..3794b01218 100644 --- a/ext/standard/config.m4 +++ b/ext/standard/config.m4 @@ -434,18 +434,11 @@ if test "$PHP_PASSWORD_ARGON2" != "no"; then PHP_ADD_LIBRARY_WITH_PATH(argon2, $ARGON2_DIR/$PHP_LIBDIR) PHP_ADD_INCLUDE($ARGON2_DIR/include) - AC_CHECK_LIB(argon2, argon2_hash, [ - LIBS="$LIBS -largon2" - AC_DEFINE(HAVE_ARGON2LIB, 1, [ Define to 1 if you have the <argon2.h> header file ]) - ], [ - AC_MSG_ERROR([Problem with libargon2.(a|so). Please verify that Argon2 header and libaries are installed]) - ]) - AC_CHECK_LIB(argon2, argon2id_hash_raw, [ LIBS="$LIBS -largon2" - AC_DEFINE(HAVE_ARGON2ID, 1, [ Define to 1 if Argon2 library has support for Argon2ID]) + AC_DEFINE(HAVE_ARGON2LIB, 1, [ Define to 1 if you have the <argon2.h> header file ]) ], [ - AC_MSG_RESULT([not found]) + AC_MSG_ERROR([Problem with libargon2.(a|so). Please verify that Argon2 header and libaries >= 20161029 are installed]) ]) fi diff --git a/ext/standard/config.w32 b/ext/standard/config.w32 index e2fd7a28f1..f934610054 100644 --- a/ext/standard/config.w32 +++ b/ext/standard/config.w32 @@ -6,10 +6,10 @@ ARG_WITH("password-argon2", "Argon2 support", "no"); if (PHP_PASSWORD_ARGON2 != "no") { if (CHECK_LIB("argon2_a.lib;argon2.lib", null, PHP_PASSWORD_ARGON2) && CHECK_HEADER_ADD_INCLUDE("argon2.h", "CFLAGS")) { - AC_DEFINE('HAVE_ARGON2LIB', 1); - if (CHECK_FUNC_IN_HEADER("argon2.h", "argon2id_hash_raw", PHP_PHP_BUILD + "\\include", "CFLAGS")) { - AC_DEFINE('HAVE_ARGON2ID', 1); + if (!CHECK_FUNC_IN_HEADER("argon2.h", "argon2id_hash_raw", PHP_PHP_BUILD + "\\include", "CFLAGS")) { + ERROR("Please verify that Argon2 header and libaries >= 20161029 are installed"); } + AC_DEFINE('HAVE_ARGON2LIB', 1); } else { WARNING("Argon2 not enabled; libaries and headers not found"); } diff --git a/ext/standard/password.c b/ext/standard/password.c index d5bbd18fbd..70906f3799 100644 --- a/ext/standard/password.c +++ b/ext/standard/password.c @@ -45,6 +45,7 @@ PHP_MINIT_FUNCTION(password) /* {{{ */ REGISTER_LONG_CONSTANT("PASSWORD_BCRYPT", PHP_PASSWORD_BCRYPT, CONST_CS | CONST_PERSISTENT); #if HAVE_ARGON2LIB REGISTER_LONG_CONSTANT("PASSWORD_ARGON2I", PHP_PASSWORD_ARGON2I, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PASSWORD_ARGON2ID", PHP_PASSWORD_ARGON2ID, CONST_CS | CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("PASSWORD_BCRYPT_DEFAULT_COST", PHP_PASSWORD_BCRYPT_COST, CONST_CS | CONST_PERSISTENT); @@ -66,6 +67,8 @@ static zend_string* php_password_get_algo_name(const php_password_algo algo) #if HAVE_ARGON2LIB case PHP_PASSWORD_ARGON2I: return zend_string_init("argon2i", sizeof("argon2i") - 1, 0); + case PHP_PASSWORD_ARGON2ID: + return zend_string_init("argon2id", sizeof("argon2id") - 1, 0); #endif case PHP_PASSWORD_UNKNOWN: default: @@ -81,6 +84,10 @@ static php_password_algo php_password_determine_algo(const zend_string *hash) return PHP_PASSWORD_BCRYPT; } #if HAVE_ARGON2LIB + if (len >= sizeof("$argon2id$")-1 && !memcmp(h, "$argon2id$", sizeof("$argon2id$")-1)) { + return PHP_PASSWORD_ARGON2ID; + } + if (len >= sizeof("$argon2i$")-1 && !memcmp(h, "$argon2i$", sizeof("$argon2i$")-1)) { return PHP_PASSWORD_ARGON2I; } @@ -159,6 +166,21 @@ static zend_string* php_password_make_salt(size_t length) /* {{{ */ } /* }}} */ +#if HAVE_ARGON2LIB +static void extract_argon2_parameters(const php_password_algo algo, const zend_string *hash, + zend_long *v, zend_long *memory_cost, + zend_long *time_cost, zend_long *threads) /* {{{ */ +{ + if (algo == PHP_PASSWORD_ARGON2ID) { + sscanf(ZSTR_VAL(hash), "$%*[argon2id]$v=" ZEND_LONG_FMT "$m=" ZEND_LONG_FMT ",t=" ZEND_LONG_FMT ",p=" ZEND_LONG_FMT, v, memory_cost, time_cost, threads); + } else if (algo == PHP_PASSWORD_ARGON2I) { + sscanf(ZSTR_VAL(hash), "$%*[argon2i]$v=" ZEND_LONG_FMT "$m=" ZEND_LONG_FMT ",t=" ZEND_LONG_FMT ",p=" ZEND_LONG_FMT, v, memory_cost, time_cost, threads); + } + + return; +} +#endif + /* {{{ proto array password_get_info(string $hash) Retrieves information about a given hash */ PHP_FUNCTION(password_get_info) @@ -186,13 +208,15 @@ PHP_FUNCTION(password_get_info) break; #if HAVE_ARGON2LIB case PHP_PASSWORD_ARGON2I: + case PHP_PASSWORD_ARGON2ID: { zend_long v = 0; zend_long memory_cost = PHP_PASSWORD_ARGON2_MEMORY_COST; zend_long time_cost = PHP_PASSWORD_ARGON2_TIME_COST; zend_long threads = PHP_PASSWORD_ARGON2_THREADS; - sscanf(ZSTR_VAL(hash), "$%*[argon2i]$v=" ZEND_LONG_FMT "$m=" ZEND_LONG_FMT ",t=" ZEND_LONG_FMT ",p=" ZEND_LONG_FMT, &v, &memory_cost, &time_cost, &threads); + extract_argon2_parameters(algo, hash, &v, &memory_cost, &time_cost, &threads); + add_assoc_long(&options, "memory_cost", memory_cost); add_assoc_long(&options, "time_cost", time_cost); add_assoc_long(&options, "threads", threads); @@ -252,6 +276,7 @@ PHP_FUNCTION(password_needs_rehash) break; #if HAVE_ARGON2LIB case PHP_PASSWORD_ARGON2I: + case PHP_PASSWORD_ARGON2ID: { zend_long v = 0; zend_long new_memory_cost = PHP_PASSWORD_ARGON2_MEMORY_COST, memory_cost = 0; @@ -270,7 +295,7 @@ PHP_FUNCTION(password_needs_rehash) new_threads = zval_get_long(option_buffer); } - sscanf(ZSTR_VAL(hash), "$%*[argon2i]$v=" ZEND_LONG_FMT "$m=" ZEND_LONG_FMT ",t=" ZEND_LONG_FMT ",p=" ZEND_LONG_FMT, &v, &memory_cost, &time_cost, &threads); + extract_argon2_parameters(algo, hash, &v, &memory_cost, &time_cost, &threads); if (new_time_cost != time_cost || new_memory_cost != memory_cost || new_threads != threads) { RETURN_TRUE; @@ -303,7 +328,16 @@ PHP_FUNCTION(password_verify) switch(algo) { #if HAVE_ARGON2LIB case PHP_PASSWORD_ARGON2I: - RETURN_BOOL(ARGON2_OK == argon2_verify(ZSTR_VAL(hash), ZSTR_VAL(password), ZSTR_LEN(password), Argon2_i)); + case PHP_PASSWORD_ARGON2ID: + { + argon2_type type; + if (algo == PHP_PASSWORD_ARGON2ID) { + type = Argon2_id; + } else if (algo == PHP_PASSWORD_ARGON2I) { + type = Argon2_i; + } + RETURN_BOOL(ARGON2_OK == argon2_verify(ZSTR_VAL(hash), ZSTR_VAL(password), ZSTR_LEN(password), type)); + } break; #endif case PHP_PASSWORD_BCRYPT: @@ -470,13 +504,19 @@ PHP_FUNCTION(password_hash) break; #if HAVE_ARGON2LIB case PHP_PASSWORD_ARGON2I: + case PHP_PASSWORD_ARGON2ID: { zval *option_buffer; zend_string *salt, *out, *encoded; size_t time_cost = PHP_PASSWORD_ARGON2_TIME_COST; size_t memory_cost = PHP_PASSWORD_ARGON2_MEMORY_COST; size_t threads = PHP_PASSWORD_ARGON2_THREADS; - argon2_type type = Argon2_i; + argon2_type type; + if (algo == PHP_PASSWORD_ARGON2ID) { + type = Argon2_id; + } else if (algo == PHP_PASSWORD_ARGON2I) { + type = Argon2_i; + } size_t encoded_len; int status = 0; @@ -485,7 +525,7 @@ PHP_FUNCTION(password_hash) } if (memory_cost > ARGON2_MAX_MEMORY || memory_cost < ARGON2_MIN_MEMORY) { - php_error_docref(NULL, E_WARNING, "Memory cost is outside of allowed memory range", memory_cost); + php_error_docref(NULL, E_WARNING, "Memory cost is outside of allowed memory range"); RETURN_NULL(); } @@ -494,7 +534,7 @@ PHP_FUNCTION(password_hash) } if (time_cost > ARGON2_MAX_TIME || time_cost < ARGON2_MIN_TIME) { - php_error_docref(NULL, E_WARNING, "Time cost is outside of allowed time range", time_cost); + php_error_docref(NULL, E_WARNING, "Time cost is outside of allowed time range"); RETURN_NULL(); } @@ -503,7 +543,7 @@ PHP_FUNCTION(password_hash) } if (threads > ARGON2_MAX_LANES || threads == 0) { - php_error_docref(NULL, E_WARNING, "Invalid number of threads", threads); + php_error_docref(NULL, E_WARNING, "Invalid number of threads"); RETURN_NULL(); } @@ -517,10 +557,8 @@ PHP_FUNCTION(password_hash) memory_cost, threads, (uint32_t)ZSTR_LEN(salt), - ZSTR_LEN(out) -#if HAVE_ARGON2ID - , type -#endif + ZSTR_LEN(out), + type ); encoded = zend_string_alloc(encoded_len - 1, 0); diff --git a/ext/standard/php_password.h b/ext/standard/php_password.h index c7dca73839..c5e00f0fb8 100644 --- a/ext/standard/php_password.h +++ b/ext/standard/php_password.h @@ -43,6 +43,7 @@ typedef enum { PHP_PASSWORD_BCRYPT, #if HAVE_ARGON2LIB PHP_PASSWORD_ARGON2I, + PHP_PASSWORD_ARGON2ID, #endif } php_password_algo; diff --git a/ext/standard/tests/password/password_get_info_argon2.phpt b/ext/standard/tests/password/password_get_info_argon2.phpt index 0c9080f0a9..7caf6e6c69 100644 --- a/ext/standard/tests/password/password_get_info_argon2.phpt +++ b/ext/standard/tests/password/password_get_info_argon2.phpt @@ -1,13 +1,15 @@ --TEST-- -Test normal operation of password_get_info() with Argon2 +Test normal operation of password_get_info() with Argon2i and Argon2id --SKIPIF-- <?php if (!defined('PASSWORD_ARGON2I')) die('skip password_get_info not built with Argon2'); +if (!defined('PASSWORD_ARGON2ID')) die('skip password_get_info not built with Argon2'); ?> --FILE-- <?php var_dump(password_get_info('$argon2i$v=19$m=65536,t=3,p=1$SWhIcG5MT21Pc01PbWdVZw$WagZELICsz7jlqOR2YzoEVTWb2oOX1tYdnhZYXxptbU')); +var_dump(password_get_info('$argon2id$v=19$m=1024,t=2,p=2$Zng1U1RHS0h1aUJjbGhPdA$ajQnG5s01Ws1ad8xv+1qGfXF8mYxxWdyul5rBpomuZQ')); echo "OK!"; ?> --EXPECT-- @@ -26,4 +28,19 @@ array(3) { int(1) } } +array(3) { + ["algo"]=> + int(3) + ["algoName"]=> + string(8) "argon2id" + ["options"]=> + array(3) { + ["memory_cost"]=> + int(1024) + ["time_cost"]=> + int(2) + ["threads"]=> + int(2) + } +} OK!
\ No newline at end of file diff --git a/ext/standard/tests/password/password_hash_argon2.phpt b/ext/standard/tests/password/password_hash_argon2.phpt index 29f7f28a95..a2cbdfacbd 100644 --- a/ext/standard/tests/password/password_hash_argon2.phpt +++ b/ext/standard/tests/password/password_hash_argon2.phpt @@ -1,8 +1,9 @@ --TEST-- -Test normal operation of password_hash() with argon2 +Test normal operation of password_hash() with Argon2i and Argon2id --SKIPIF-- <?php if (!defined('PASSWORD_ARGON2I')) die('skip password_hash not built with Argon2'); +if (!defined('PASSWORD_ARGON2ID')) die('skip password_hash not built with Argon2'); --FILE-- <?php @@ -11,8 +12,12 @@ $password = "the password for testing 12345!"; $hash = password_hash($password, PASSWORD_ARGON2I); var_dump(password_verify($password, $hash)); +$hash = password_hash($password, PASSWORD_ARGON2ID); +var_dump(password_verify($password, $hash)); + echo "OK!"; ?> --EXPECT-- bool(true) +bool(true) OK!
\ No newline at end of file diff --git a/ext/standard/tests/password/password_hash_error_argon2.phpt b/ext/standard/tests/password/password_hash_error_argon2.phpt index 92c71e064b..c1968b2971 100644 --- a/ext/standard/tests/password/password_hash_error_argon2.phpt +++ b/ext/standard/tests/password/password_hash_error_argon2.phpt @@ -1,14 +1,18 @@ --TEST-- -Test error operation of password_hash() with argon2 +Test error operation of password_hash() with Argon2i and Argon2id --SKIPIF-- <?php if (!defined('PASSWORD_ARGON2I')) die('skip password_hash not built with Argon2'); +if (!defined('PASSWORD_ARGON2ID')) die('skip password_hash not built with Argon2'); ?> --FILE-- <?php var_dump(password_hash('test', PASSWORD_ARGON2I, ['memory_cost' => 0])); var_dump(password_hash('test', PASSWORD_ARGON2I, ['time_cost' => 0])); var_dump(password_hash('test', PASSWORD_ARGON2I, ['threads' => 0])); +var_dump(password_hash('test', PASSWORD_ARGON2ID, ['memory_cost' => 0])); +var_dump(password_hash('test', PASSWORD_ARGON2ID, ['time_cost' => 0])); +var_dump(password_hash('test', PASSWORD_ARGON2ID, ['threads' => 0])); ?> --EXPECTF-- Warning: password_hash(): Memory cost is outside of allowed memory range in %s on line %d @@ -18,4 +22,13 @@ Warning: password_hash(): Time cost is outside of allowed time range in %s on li NULL Warning: password_hash(): Invalid number of threads in %s on line %d +NULL + +Warning: password_hash(): Memory cost is outside of allowed memory range in %s on line %d +NULL + +Warning: password_hash(): Time cost is outside of allowed time range in %s on line %d +NULL + +Warning: password_hash(): Invalid number of threads in %s on line %d NULL
\ No newline at end of file diff --git a/ext/standard/tests/password/password_needs_rehash_argon2.phpt b/ext/standard/tests/password/password_needs_rehash_argon2.phpt index 0b5fede1e3..de85a1d27b 100644 --- a/ext/standard/tests/password/password_needs_rehash_argon2.phpt +++ b/ext/standard/tests/password/password_needs_rehash_argon2.phpt @@ -1,8 +1,9 @@ --TEST-- -Test normal operation of password_needs_rehash() with argon2 +Test normal operation of password_needs_rehash() with Argon2i and Argon2id --SKIPIF-- <?php if (!defined('PASSWORD_ARGON2I')) die('skip password_needs_rehash not built with Argon2'); +if (!defined('PASSWORD_ARGON2ID')) die('skip password_hash not built with Argon2'); ?> --FILE-- <?php @@ -12,6 +13,12 @@ var_dump(password_needs_rehash($hash, PASSWORD_ARGON2I)); var_dump(password_needs_rehash($hash, PASSWORD_ARGON2I, ['memory_cost' => 1<<17])); var_dump(password_needs_rehash($hash, PASSWORD_ARGON2I, ['time_cost' => 4])); var_dump(password_needs_rehash($hash, PASSWORD_ARGON2I, ['threads' => 4])); + +$hash = password_hash('test', PASSWORD_ARGON2ID); +var_dump(password_needs_rehash($hash, PASSWORD_ARGON2ID)); +var_dump(password_needs_rehash($hash, PASSWORD_ARGON2ID, ['memory_cost' => 1<<17])); +var_dump(password_needs_rehash($hash, PASSWORD_ARGON2ID, ['time_cost' => 4])); +var_dump(password_needs_rehash($hash, PASSWORD_ARGON2ID, ['threads' => 4])); echo "OK!"; ?> --EXPECT-- @@ -19,4 +26,8 @@ bool(false) bool(true) bool(true) bool(true) -OK! +bool(false) +bool(true) +bool(true) +bool(true) +OK!
\ No newline at end of file diff --git a/ext/standard/tests/password/password_verify_argon2.phpt b/ext/standard/tests/password/password_verify_argon2.phpt index 986f5e7005..f84df97286 100644 --- a/ext/standard/tests/password/password_verify_argon2.phpt +++ b/ext/standard/tests/password/password_verify_argon2.phpt @@ -1,8 +1,9 @@ --TEST-- -Test normal operation of password_verify() with argon2 +Test normal operation of password_verify() with Argon2i and Argon2id --SKIPIF-- <?php if (!defined('PASSWORD_ARGON2I')) die('skip password_verify not built with Argon2'); +if (!defined('PASSWORD_ARGON2ID')) die('skip password_hash not built with Argon2'); ?> --FILE-- <?php @@ -10,9 +11,14 @@ if (!defined('PASSWORD_ARGON2I')) die('skip password_verify not built with Argon var_dump(password_verify('test', '$argon2i$v=19$m=65536,t=3,p=1$OEVjWWs2Z3YvWlNZQ0ZmNw$JKin7ahjmh8JYvMyFcXri0Ss/Uvd3uYpD7MG6C/5Cy0')); var_dump(password_verify('argon2', '$argon2i$v=19$m=65536,t=3,p=1$OEVjWWs2Z3YvWlNZQ0ZmNw$JKin7ahjmh8JYvMyFcXri0Ss/Uvd3uYpD7MG6C/5Cy0')); + +var_dump(password_verify('test', '$argon2id$v=19$m=1024,t=2,p=2$WS90MHJhd3AwSC5xTDJpZg$8tn2DaIJR2/UX4Cjcy2t3EZaLDL/qh+NbLQAOvTmdAg')); +var_dump(password_verify('argon2id', '$argon2id$v=19$m=1024,t=2,p=2$WS90MHJhd3AwSC5xTDJpZg$8tn2DaIJR2/UX4Cjcy2t3EZaLDL/qh+NbLQAOvTmdAg')); echo "OK!"; ?> --EXPECT-- bool(true) bool(false) +bool(true) +bool(false) OK!
\ No newline at end of file |
