diff options
author | Sara Golemon <pollita@php.net> | 2019-07-07 21:55:12 -0400 |
---|---|---|
committer | Sara Golemon <pollita@php.net> | 2019-07-07 21:55:12 -0400 |
commit | 17bfac6347431ba7ac8de3eb2be0c4238acea107 (patch) | |
tree | d97749b1d36a285a89ac8aa37942f6f7f617381a /ext | |
parent | 27b6d36f98bc9f9ec7de0b8b5be975b3f4ab4acd (diff) | |
parent | 0ba1db7a4ab035e00dcb3089ef45820054c5a1cf (diff) | |
download | php-git-17bfac6347431ba7ac8de3eb2be0c4238acea107.tar.gz |
Merge branch 'PHP-7.4'
* PHP-7.4:
Provide argon2i(d) password hashing from sodium when needed
Diffstat (limited to 'ext')
-rw-r--r-- | ext/sodium/config.m4 | 2 | ||||
-rw-r--r-- | ext/sodium/config.w32 | 2 | ||||
-rw-r--r-- | ext/sodium/libsodium.c | 19 | ||||
-rw-r--r-- | ext/sodium/php_libsodium.h | 1 | ||||
-rw-r--r-- | ext/sodium/sodium_pwhash.c | 215 | ||||
-rw-r--r-- | ext/sodium/tests/php_password_hash_argon2i.phpt | 113 | ||||
-rw-r--r-- | ext/sodium/tests/php_password_hash_argon2id.phpt | 113 | ||||
-rw-r--r-- | ext/sodium/tests/php_password_verify.phpt | 35 | ||||
-rw-r--r-- | ext/standard/password.c | 2 | ||||
-rw-r--r-- | ext/standard/php_password.h | 11 |
10 files changed, 347 insertions, 166 deletions
diff --git a/ext/sodium/config.m4 b/ext/sodium/config.m4 index bb4f2617f5..906e0ca7f2 100644 --- a/ext/sodium/config.m4 +++ b/ext/sodium/config.m4 @@ -11,6 +11,6 @@ if test "$PHP_SODIUM" != "no"; then AC_DEFINE(HAVE_LIBSODIUMLIB, 1, [ ]) - PHP_NEW_EXTENSION(sodium, libsodium.c, $ext_shared) + PHP_NEW_EXTENSION(sodium, libsodium.c sodium_pwhash.c, $ext_shared) PHP_SUBST(SODIUM_SHARED_LIBADD) fi diff --git a/ext/sodium/config.w32 b/ext/sodium/config.w32 index 5f25aa1ea6..56fc635aa5 100644 --- a/ext/sodium/config.w32 +++ b/ext/sodium/config.w32 @@ -4,7 +4,7 @@ ARG_WITH("sodium", "for libsodium support", "no"); if (PHP_SODIUM != "no") { if (CHECK_LIB("libsodium.lib", "sodium", PHP_SODIUM) && CHECK_HEADER_ADD_INCLUDE("sodium.h", "CFLAGS_SODIUM")) { - EXTENSION("sodium", "libsodium.c"); + EXTENSION("sodium", "libsodium.c sodium_pwhash.c"); AC_DEFINE('HAVE_LIBSODIUMLIB', 1 , 'Have the Sodium library'); AC_DEFINE("HAVE_CRYPTO_AEAD_AES256GCM", 1, ""); PHP_INSTALL_HEADERS("ext/sodium/", "php_libsodium.h"); diff --git a/ext/sodium/libsodium.c b/ext/sodium/libsodium.c index 652663ff08..2159cb52b4 100644 --- a/ext/sodium/libsodium.c +++ b/ext/sodium/libsodium.c @@ -358,8 +358,18 @@ static const zend_function_entry sodium_functions[] = { PHP_FE_END }; +/* Load after the "standard" module in order to give it + * priority in registering argon2i/argon2id password hashers. + */ +static const zend_module_dep sodium_deps[] = { + ZEND_MOD_REQUIRED("standard") + ZEND_MOD_END +}; + zend_module_entry sodium_module_entry = { - STANDARD_MODULE_HEADER, + STANDARD_MODULE_HEADER_EX, + NULL, + sodium_deps, "sodium", sodium_functions, PHP_MINIT(sodium), @@ -632,6 +642,13 @@ PHP_MINIT_FUNCTION(sodium) REGISTER_LONG_CONSTANT("SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING", sodium_base64_VARIANT_URLSAFE_NO_PADDING, CONST_CS | CONST_PERSISTENT); #endif + +#if SODIUM_LIBRARY_VERSION_MAJOR > 9 || (SODIUM_LIBRARY_VERSION_MAJOR == 9 && SODIUM_LIBRARY_VERSION_MINOR >= 6) + if (FAILURE == PHP_MINIT(sodium_password_hash)(INIT_FUNC_ARGS_PASSTHRU)) { + return FAILURE; + } +#endif + return SUCCESS; } diff --git a/ext/sodium/php_libsodium.h b/ext/sodium/php_libsodium.h index 66053f2862..f0ec64ca77 100644 --- a/ext/sodium/php_libsodium.h +++ b/ext/sodium/php_libsodium.h @@ -29,6 +29,7 @@ extern zend_module_entry sodium_module_entry; #endif PHP_MINIT_FUNCTION(sodium); +PHP_MINIT_FUNCTION(sodium_password_hash); PHP_MSHUTDOWN_FUNCTION(sodium); PHP_RINIT_FUNCTION(sodium); PHP_RSHUTDOWN_FUNCTION(sodium); diff --git a/ext/sodium/sodium_pwhash.c b/ext/sodium/sodium_pwhash.c new file mode 100644 index 0000000000..d615a24ab6 --- /dev/null +++ b/ext/sodium/sodium_pwhash.c @@ -0,0 +1,215 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Sara Golemon <pollita@php.net> | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "php_libsodium.h" +#include "ext/standard/php_password.h" + +#include <sodium.h> + +#if SODIUM_LIBRARY_VERSION_MAJOR > 9 || (SODIUM_LIBRARY_VERSION_MAJOR == 9 && SODIUM_LIBRARY_VERSION_MINOR >= 6) + +/** + * OPSLIMIT and MEMLIMIT are taken from libsodium's MODERATE values. + * MEMLIMIT is normalized to KB even though sodium uses Bytes in order to + * present a consistent user-facing API. + * + * Threads are fixed at 1 by libsodium. + * + * When updating these values, synchronize ext/standard/php_password.h values. + */ +#define PHP_SODIUM_PWHASH_MEMLIMIT (256 << 10) +#define PHP_SODIUM_PWHASH_OPSLIMIT 3 +#define PHP_SODIUM_PWHASH_THREADS 1 + +static zend_string *php_sodium_argon2_hash(const zend_string *password, zend_array *options, int alg) { + size_t opslimit = PHP_SODIUM_PWHASH_OPSLIMIT; + size_t memlimit = PHP_SODIUM_PWHASH_MEMLIMIT; + zend_string *ret; + + if ((ZSTR_LEN(password) >= 0xffffffff)) { + php_error_docref(NULL, E_WARNING, "Password is too long"); + return NULL; + } + + if (options) { + zval *opt; + if ((opt = zend_hash_str_find(options, "memory_cost", strlen("memory_cost")))) { + memlimit = zval_get_long(opt); + if ((memlimit < crypto_pwhash_MEMLIMIT_MIN) || (memlimit > crypto_pwhash_MEMLIMIT_MAX)) { + php_error_docref(NULL, E_WARNING, "Memory cost is outside of allowed memory range"); + return NULL; + } + } + if ((opt = zend_hash_str_find(options, "time_cost", strlen("time_cost")))) { + opslimit = zval_get_long(opt); + if ((opslimit < crypto_pwhash_OPSLIMIT_MIN) || (opslimit > crypto_pwhash_OPSLIMIT_MAX)) { + php_error_docref(NULL, E_WARNING, "Time cost is outside of allowed time range"); + return NULL; + } + } + if ((opt = zend_hash_str_find(options, "threads", strlen("threads"))) && (zval_get_long(opt) != 1)) { + php_error_docref(NULL, E_WARNING, "A thread value other than 1 is not supported by this implementation"); + return NULL; + } + } + + ret = zend_string_alloc(crypto_pwhash_STRBYTES - 1, 0); + if (crypto_pwhash_str_alg(ZSTR_VAL(ret), ZSTR_VAL(password), ZSTR_LEN(password), opslimit, memlimit << 10, alg)) { + php_error_docref(NULL, E_WARNING, "Unexpected failure hashing password"); + zend_string_release(ret); + return NULL; + } + + ZSTR_LEN(ret) = strlen(ZSTR_VAL(ret)); + ZSTR_VAL(ret)[ZSTR_LEN(ret)] = 0; + + return ret; +} + +static zend_bool php_sodium_argon2_verify(const zend_string *password, const zend_string *hash) { + if ((ZSTR_LEN(password) >= 0xffffffff) || (ZSTR_LEN(hash) >= 0xffffffff)) { + return 0; + } + return crypto_pwhash_str_verify(ZSTR_VAL(hash), ZSTR_VAL(password), ZSTR_LEN(password)) == 0; +} + +static zend_bool php_sodium_argon2_needs_rehash(const zend_string *hash, zend_array *options) { + size_t opslimit = PHP_SODIUM_PWHASH_OPSLIMIT; + size_t memlimit = PHP_SODIUM_PWHASH_MEMLIMIT; + + if (options) { + zval *opt; + if ((opt = zend_hash_str_find(options, "memory_cost", strlen("memory_cost")))) { + memlimit = zval_get_long(opt); + if ((memlimit < crypto_pwhash_MEMLIMIT_MIN) || (memlimit > crypto_pwhash_MEMLIMIT_MAX)) { + php_error_docref(NULL, E_WARNING, "Memory cost is outside of allowed memory range"); + return 1; + } + } + if ((opt = zend_hash_str_find(options, "time_cost", strlen("time_cost")))) { + opslimit = zval_get_long(opt); + if ((opslimit < crypto_pwhash_OPSLIMIT_MIN) || (opslimit > crypto_pwhash_OPSLIMIT_MAX)) { + php_error_docref(NULL, E_WARNING, "Time cost is outside of allowed time range"); + return 1; + } + } + if ((opt = zend_hash_str_find(options, "threads", strlen("threads"))) && (zval_get_long(opt) != 1)) { + php_error_docref(NULL, E_WARNING, "A thread value other than 1 is not supported by this implementation"); + return 1; + } + } + + return crypto_pwhash_str_needs_rehash(ZSTR_VAL(hash), opslimit, memlimit << 10); +} + +static int php_sodium_argon2_get_info(zval *return_value, const zend_string *hash) { + const char* p = NULL; + zend_long v = 0, threads = 1; + zend_long memory_cost = PHP_SODIUM_PWHASH_MEMLIMIT; + zend_long time_cost = PHP_SODIUM_PWHASH_OPSLIMIT; + + if (!hash || (ZSTR_LEN(hash) < sizeof("$argon2id$"))) { + return FAILURE; + } + + p = ZSTR_VAL(hash); + if (!memcmp(p, "$argon2i$", strlen("$argon2i$"))) { + p += strlen("$argon2i$"); + } else if (!memcmp(p, "$argon2id$", strlen("$argon2id$"))) { + p += strlen("$argon2id$"); + } else { + return FAILURE; + } + + sscanf(p, "v=" ZEND_LONG_FMT "$m=" ZEND_LONG_FMT ",t=" ZEND_LONG_FMT ",p=" ZEND_LONG_FMT, + &v, &memory_cost, &time_cost, &threads); + + add_assoc_long(return_value, "memory_cost", memory_cost); + add_assoc_long(return_value, "time_cost", time_cost); + add_assoc_long(return_value, "threads", threads); + + return SUCCESS; +} + +/* argon2i specific methods */ + +static zend_string *php_sodium_argon2i_hash(const zend_string *password, zend_array *options) { + return php_sodium_argon2_hash(password, options, crypto_pwhash_ALG_ARGON2I13); +} + +static const php_password_algo sodium_algo_argon2i = { + "argon2i", + php_sodium_argon2i_hash, + php_sodium_argon2_verify, + php_sodium_argon2_needs_rehash, + php_sodium_argon2_get_info, + NULL, +}; + +/* argon2id specific methods */ + +static zend_string *php_sodium_argon2id_hash(const zend_string *password, zend_array *options) { + return php_sodium_argon2_hash(password, options, crypto_pwhash_ALG_ARGON2ID13); +} + +static const php_password_algo sodium_algo_argon2id = { + "argon2id", + php_sodium_argon2id_hash, + php_sodium_argon2_verify, + php_sodium_argon2_needs_rehash, + php_sodium_argon2_get_info, + NULL, +}; + +PHP_MINIT_FUNCTION(sodium_password_hash) /* {{{ */ { + zend_string *argon2i = zend_string_init("argon2i", strlen("argon2i"), 1); + + if (php_password_algo_find(argon2i)) { + /* Nothing to do. Core has registered these algorithms for us. */ + zend_string_release(argon2i); + return SUCCESS; + } + zend_string_release(argon2i); + + if (FAILURE == php_password_algo_register("argon2i", &sodium_algo_argon2i)) { + return FAILURE; + } + REGISTER_STRING_CONSTANT("PASSWORD_ARGON2I", "argon2i", CONST_CS | CONST_PERSISTENT); + + if (FAILURE == php_password_algo_register("argon2id", &sodium_algo_argon2id)) { + return FAILURE; + } + REGISTER_STRING_CONSTANT("PASSWORD_ARGON2ID", "argon2id", CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_MEMORY_COST", PHP_SODIUM_PWHASH_MEMLIMIT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_TIME_COST", PHP_SODIUM_PWHASH_OPSLIMIT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_THREADS", PHP_SODIUM_PWHASH_THREADS, CONST_CS | CONST_PERSISTENT); + + REGISTER_STRING_CONSTANT("PASSWORD_ARGON2_PROVIDER", "sodium", CONST_CS | CONST_PERSISTENT); + + return SUCCESS; +} +/* }}} */ + + +#endif /* HAVE_SODIUM_PASSWORD_HASH */ diff --git a/ext/sodium/tests/php_password_hash_argon2i.phpt b/ext/sodium/tests/php_password_hash_argon2i.phpt index b20b6de440..9ce6c7399e 100644 --- a/ext/sodium/tests/php_password_hash_argon2i.phpt +++ b/ext/sodium/tests/php_password_hash_argon2i.phpt @@ -11,102 +11,63 @@ if (!in_array('argon2i', password_algos(), true /* strict */)) { --FILE-- <?php +echo 'Argon2 provider: '; +var_dump(PASSWORD_ARGON2_PROVIDER); + foreach([1, 2, 4] as $mem) { foreach([1, 2, 4] as $time) { - foreach([1, 2, 4] as $threads) { - $opts = [ - 'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST * $mem, - 'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST * $time, - 'threads' => PASSWORD_ARGON2_DEFAULT_THREADS * $threads, - ]; - $password = random_bytes(32); - echo "Using password: "; - var_dump(base64_encode($password)); - $hash = password_hash($password, 'argon2i', $opts); - echo "Hash: "; var_dump($hash); - var_dump(sodium_crypto_pwhash_str_verify($hash, $password)); - } + $opts = [ + 'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST * $mem, + 'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST * $time, + 'threads' => PASSWORD_ARGON2_DEFAULT_THREADS, + ]; + $password = random_bytes(32); + echo "Using password: "; + var_dump(base64_encode($password)); + $hash = password_hash($password, 'argon2i', $opts); + echo "Hash: "; var_dump($hash); + var_dump(sodium_crypto_pwhash_str_verify($hash, $password)); + + // And verify that incorrect passwords fail. + $password[0] = chr(ord($password[0]) ^ 1); + var_dump(sodium_crypto_pwhash_str_verify($hash, $password)); } } --EXPECTF-- +Argon2 provider: string(%d) "%s" Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=1024,t=3,p=2$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=1024,t=3,p=4$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=1024,t=3,p=8$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=1024,t=6,p=2$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=1024,t=6,p=4$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=1024,t=6,p=8$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2i$v=19$m=1024,t=12,p=2$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2i$v=19$m=1024,t=12,p=4$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2i$v=19$m=1024,t=12,p=8$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=2048,t=3,p=2$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=2048,t=3,p=4$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=2048,t=3,p=8$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=2048,t=6,p=2$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=2048,t=6,p=4$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=2048,t=6,p=8$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2i$v=19$m=2048,t=12,p=2$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2i$v=19$m=2048,t=12,p=4$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2i$v=19$m=2048,t=12,p=8$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=4096,t=3,p=2$%s" +Hash: string(97) "$argon2i$v=19$m=262144,t=3,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=4096,t=3,p=4$%s" +Hash: string(97) "$argon2i$v=19$m=262144,t=6,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=4096,t=3,p=8$%s" +Hash: string(98) "$argon2i$v=19$m=262144,t=12,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=4096,t=6,p=2$%s" +Hash: string(97) "$argon2i$v=19$m=524288,t=3,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=4096,t=6,p=4$%s" +Hash: string(97) "$argon2i$v=19$m=524288,t=6,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(95) "$argon2i$v=19$m=4096,t=6,p=8$%s" +Hash: string(98) "$argon2i$v=19$m=524288,t=12,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(96) "$argon2i$v=19$m=4096,t=12,p=2$%s" +Hash: string(98) "$argon2i$v=19$m=1048576,t=3,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(96) "$argon2i$v=19$m=4096,t=12,p=4$%s" +Hash: string(98) "$argon2i$v=19$m=1048576,t=6,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(96) "$argon2i$v=19$m=4096,t=12,p=8$%s" +Hash: string(99) "$argon2i$v=19$m=1048576,t=12,p=1$%s$%s" bool(true) +bool(false) diff --git a/ext/sodium/tests/php_password_hash_argon2id.phpt b/ext/sodium/tests/php_password_hash_argon2id.phpt index 17f23e96b9..e6d4c1ee80 100644 --- a/ext/sodium/tests/php_password_hash_argon2id.phpt +++ b/ext/sodium/tests/php_password_hash_argon2id.phpt @@ -11,102 +11,63 @@ if (!in_array('argon2id', password_algos(), true /* strict */)) { --FILE-- <?php +echo 'Argon2 provider: '; +var_dump(PASSWORD_ARGON2_PROVIDER); + foreach([1, 2, 4] as $mem) { foreach([1, 2, 4] as $time) { - foreach([1, 2, 4] as $threads) { - $opts = [ - 'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST * $mem, - 'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST * $time, - 'threads' => PASSWORD_ARGON2_DEFAULT_THREADS * $threads, - ]; - $password = random_bytes(32); - echo "Using password: "; - var_dump(base64_encode($password)); - $hash = password_hash($password, 'argon2id', $opts); - echo "Hash: "; var_dump($hash); - var_dump(sodium_crypto_pwhash_str_verify($hash, $password)); - } + $opts = [ + 'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST * $mem, + 'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST * $time, + 'threads' => PASSWORD_ARGON2_DEFAULT_THREADS, + ]; + $password = random_bytes(32); + echo "Using password: "; + var_dump(base64_encode($password)); + $hash = password_hash($password, 'argon2id', $opts); + echo "Hash: "; var_dump($hash); + var_dump(sodium_crypto_pwhash_str_verify($hash, $password)); + + // And verify that incorrect passwords fail. + $password[0] = chr(ord($password[0]) ^ 1); + var_dump(sodium_crypto_pwhash_str_verify($hash, $password)); } } --EXPECTF-- +Argon2 provider: string(%d) "%s" Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=1024,t=3,p=2$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=1024,t=3,p=4$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=1024,t=3,p=8$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=1024,t=6,p=2$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=1024,t=6,p=4$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=1024,t=6,p=8$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(97) "$argon2id$v=19$m=1024,t=12,p=2$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(97) "$argon2id$v=19$m=1024,t=12,p=4$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(97) "$argon2id$v=19$m=1024,t=12,p=8$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=2048,t=3,p=2$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=2048,t=3,p=4$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=2048,t=3,p=8$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=2048,t=6,p=2$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=2048,t=6,p=4$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=2048,t=6,p=8$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(97) "$argon2id$v=19$m=2048,t=12,p=2$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(97) "$argon2id$v=19$m=2048,t=12,p=4$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(97) "$argon2id$v=19$m=2048,t=12,p=8$%s" -bool(true) -Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=4096,t=3,p=2$%s" +Hash: string(98) "$argon2id$v=19$m=262144,t=3,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=4096,t=3,p=4$%s" +Hash: string(98) "$argon2id$v=19$m=262144,t=6,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=4096,t=3,p=8$%s" +Hash: string(99) "$argon2id$v=19$m=262144,t=12,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=4096,t=6,p=2$%s" +Hash: string(98) "$argon2id$v=19$m=524288,t=3,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=4096,t=6,p=4$%s" +Hash: string(98) "$argon2id$v=19$m=524288,t=6,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(96) "$argon2id$v=19$m=4096,t=6,p=8$%s" +Hash: string(99) "$argon2id$v=19$m=524288,t=12,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(97) "$argon2id$v=19$m=4096,t=12,p=2$%s" +Hash: string(99) "$argon2id$v=19$m=1048576,t=3,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(97) "$argon2id$v=19$m=4096,t=12,p=4$%s" +Hash: string(99) "$argon2id$v=19$m=1048576,t=6,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(97) "$argon2id$v=19$m=4096,t=12,p=8$%s" +Hash: string(100) "$argon2id$v=19$m=1048576,t=12,p=1$%s$%s" bool(true) +bool(false) diff --git a/ext/sodium/tests/php_password_verify.phpt b/ext/sodium/tests/php_password_verify.phpt index 791db664f5..02c176a869 100644 --- a/ext/sodium/tests/php_password_verify.phpt +++ b/ext/sodium/tests/php_password_verify.phpt @@ -27,6 +27,9 @@ $memSet = [ SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE, ]; +echo 'Argon2 provider: '; +var_dump(PASSWORD_ARGON2_PROVIDER); + foreach($opsSet as $ops) { foreach($memSet as $mem) { $password = random_bytes(32); @@ -35,33 +38,47 @@ foreach($opsSet as $ops) { $hash = sodium_crypto_pwhash_str($password, $ops, $mem); echo "Hash: "; var_dump($hash); var_dump(password_verify($password, $hash)); + + // And verify that incorrect passwords fail. + $password[0] = chr(ord($password[0]) ^ 1); + var_dump(password_verify($password, $hash)); } } --EXPECTF-- +Argon2 provider: string(%d) "%s" Using password: string(44) "%s" -Hash: string(%d) "$argon2i%s" +Hash: string(97) "$argon2id$v=19$m=65536,t=2,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(%d) "$argon2i%s" +Hash: string(98) "$argon2id$v=19$m=262144,t=2,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(%d) "$argon2i%s" +Hash: string(99) "$argon2id$v=19$m=1048576,t=2,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(%d) "$argon2i%s" +Hash: string(97) "$argon2id$v=19$m=65536,t=3,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(%d) "$argon2i%s" +Hash: string(98) "$argon2id$v=19$m=262144,t=3,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(%d) "$argon2i%s" +Hash: string(99) "$argon2id$v=19$m=1048576,t=3,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(%d) "$argon2i%s" +Hash: string(97) "$argon2id$v=19$m=65536,t=4,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(%d) "$argon2i%s" +Hash: string(98) "$argon2id$v=19$m=262144,t=4,p=1$%s$%s" bool(true) +bool(false) Using password: string(44) "%s" -Hash: string(%d) "$argon2i%s" +Hash: string(99) "$argon2id$v=19$m=1048576,t=4,p=1$%s$%s" bool(true) +bool(false) diff --git a/ext/standard/password.c b/ext/standard/password.c index 3c24c7c9c0..7cf1cb68c8 100644 --- a/ext/standard/password.c +++ b/ext/standard/password.c @@ -450,6 +450,8 @@ PHP_MINIT_FUNCTION(password) /* {{{ */ REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_MEMORY_COST", PHP_PASSWORD_ARGON2_MEMORY_COST, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_TIME_COST", PHP_PASSWORD_ARGON2_TIME_COST, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_THREADS", PHP_PASSWORD_ARGON2_THREADS, CONST_CS | CONST_PERSISTENT); + + REGISTER_STRING_CONSTANT("PASSWORD_ARGON2_PROVIDER", "standard", CONST_CS | CONST_PERSISTENT); #endif return SUCCESS; diff --git a/ext/standard/php_password.h b/ext/standard/php_password.h index 46ed4f0368..ce3fdba6bb 100644 --- a/ext/standard/php_password.h +++ b/ext/standard/php_password.h @@ -33,9 +33,16 @@ PHP_MSHUTDOWN_FUNCTION(password); #define PHP_PASSWORD_BCRYPT_COST 10 #if HAVE_ARGON2LIB -#define PHP_PASSWORD_ARGON2_MEMORY_COST 1<<10 +/** + * OPSLIMIT and MEMLIMIT are taken from libsodium's MODERATE values. + * Threads are fixed at 1 by libsodium. + * + * When updating these values, synchronize ext/sodium/sodium_pwhash.c values. + * Note that libargon expresses memlimit in KB, while libsoidum uses bytes. + */ +#define PHP_PASSWORD_ARGON2_MEMORY_COST (256 << 10) #define PHP_PASSWORD_ARGON2_TIME_COST 3 -#define PHP_PASSWORD_ARGON2_THREADS 2 +#define PHP_PASSWORD_ARGON2_THREADS 1 #endif typedef struct _php_password_algo { |