summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--UPGRADING12
-rw-r--r--ext/standard/config.m411
-rw-r--r--ext/standard/config.w326
-rw-r--r--ext/standard/password.c60
-rw-r--r--ext/standard/php_password.h1
-rw-r--r--ext/standard/tests/password/password_get_info_argon2.phpt19
-rw-r--r--ext/standard/tests/password/password_hash_argon2.phpt7
-rw-r--r--ext/standard/tests/password/password_hash_error_argon2.phpt15
-rw-r--r--ext/standard/tests/password/password_needs_rehash_argon2.phpt15
-rw-r--r--ext/standard/tests/password/password_verify_argon2.phpt8
10 files changed, 125 insertions, 29 deletions
diff --git a/UPGRADING b/UPGRADING
index ce0d4a852f..ddda3d4d66 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -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