mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Implement GH-13514 PASSWORD_ARGON2 from OpenSSL 3.2 (#13635)
* Implement GH-13514 PASSWORD_ARGON2 from OpenSSL 3.2 * simplify init/shutdown * use php_base64_encode_ex * - rename macros - use openssl RAND_bytes - CS * add --with-openssl-argon2 build option * check OSSL_KDF_PARAM_ARGON2_LANES instead of OSSL_set_max_threads * Cleanup and CS * save/restore old threads config + CS * remove unneeded check
This commit is contained in:
parent
64bd828394
commit
32c5ce3451
12 changed files with 705 additions and 5 deletions
|
@ -17,8 +17,15 @@ PHP_ARG_WITH([openssl-legacy-provider],
|
||||||
[no],
|
[no],
|
||||||
[no])
|
[no])
|
||||||
|
|
||||||
|
PHP_ARG_WITH([openssl-argon2],
|
||||||
|
[whether to enable argon2 password hashing (requires OpenSSL >= 3.2)],
|
||||||
|
[AS_HELP_STRING([--with-openssl-argon2],
|
||||||
|
[OPENSSL: Enable argon2 password hashing])],
|
||||||
|
[no],
|
||||||
|
[no])
|
||||||
|
|
||||||
if test "$PHP_OPENSSL" != "no"; then
|
if test "$PHP_OPENSSL" != "no"; then
|
||||||
PHP_NEW_EXTENSION([openssl], [openssl.c xp_ssl.c], [$ext_shared])
|
PHP_NEW_EXTENSION([openssl], [openssl.c openssl_pwhash.c xp_ssl.c], [$ext_shared])
|
||||||
PHP_SUBST([OPENSSL_SHARED_LIBADD])
|
PHP_SUBST([OPENSSL_SHARED_LIBADD])
|
||||||
PHP_SETUP_OPENSSL([OPENSSL_SHARED_LIBADD],
|
PHP_SETUP_OPENSSL([OPENSSL_SHARED_LIBADD],
|
||||||
[AC_DEFINE([HAVE_OPENSSL_EXT], [1],
|
[AC_DEFINE([HAVE_OPENSSL_EXT], [1],
|
||||||
|
@ -38,4 +45,14 @@ if test "$PHP_OPENSSL" != "no"; then
|
||||||
[AC_DEFINE([LOAD_OPENSSL_LEGACY_PROVIDER], [1],
|
[AC_DEFINE([LOAD_OPENSSL_LEGACY_PROVIDER], [1],
|
||||||
[Define to 1 to load the OpenSSL legacy algorithm provider in addition to
|
[Define to 1 to load the OpenSSL legacy algorithm provider in addition to
|
||||||
the default provider.])])
|
the default provider.])])
|
||||||
|
|
||||||
|
if test "$PHP_OPENSSL_ARGON2" != "no"; then
|
||||||
|
if test "$PHP_THREAD_SAFETY" != "no"; then
|
||||||
|
AC_MSG_ERROR([Not supported in ZTS mode for now])
|
||||||
|
fi
|
||||||
|
PHP_CHECK_LIBRARY([crypto], [OSSL_set_max_threads],
|
||||||
|
[AC_DEFINE([HAVE_OPENSSL_ARGON2], [1], [ Define to 1 to enable argon2 password hashing ])],
|
||||||
|
[AC_MSG_ERROR([argon2 hashing requires OpenSSL 3.2])],
|
||||||
|
[$OPENSSL_LIBS])
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -270,9 +270,20 @@ static void php_openssl_pkey_free_obj(zend_object *object)
|
||||||
zend_object_std_dtor(&key_object->std);
|
zend_object_std_dtor(&key_object->std);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
static const zend_module_dep openssl_deps[] = {
|
||||||
|
ZEND_MOD_REQUIRED("standard")
|
||||||
|
ZEND_MOD_END
|
||||||
|
};
|
||||||
|
#endif
|
||||||
/* {{{ openssl_module_entry */
|
/* {{{ openssl_module_entry */
|
||||||
zend_module_entry openssl_module_entry = {
|
zend_module_entry openssl_module_entry = {
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
STANDARD_MODULE_HEADER_EX, NULL,
|
||||||
|
openssl_deps,
|
||||||
|
#else
|
||||||
STANDARD_MODULE_HEADER,
|
STANDARD_MODULE_HEADER,
|
||||||
|
#endif
|
||||||
"openssl",
|
"openssl",
|
||||||
ext_functions,
|
ext_functions,
|
||||||
PHP_MINIT(openssl),
|
PHP_MINIT(openssl),
|
||||||
|
@ -1330,6 +1341,12 @@ PHP_MINIT_FUNCTION(openssl)
|
||||||
|
|
||||||
REGISTER_INI_ENTRIES();
|
REGISTER_INI_ENTRIES();
|
||||||
|
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
if (FAILURE == PHP_MINIT(openssl_pwhash)(INIT_FUNC_ARGS_PASSTHRU)) {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
|
@ -678,3 +678,8 @@ function openssl_spki_export_challenge(string $spki): string|false {}
|
||||||
* @refcount 1
|
* @refcount 1
|
||||||
*/
|
*/
|
||||||
function openssl_get_cert_locations(): array {}
|
function openssl_get_cert_locations(): array {}
|
||||||
|
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
function openssl_password_hash(string $algo, #[\SensitiveParameter] string $password, array $options = []): string {}
|
||||||
|
function openssl_password_verify(string $algo, #[\SensitiveParameter] string $password, string $hash): bool {}
|
||||||
|
#endif
|
||||||
|
|
38
ext/openssl/openssl_arginfo.h
generated
38
ext/openssl/openssl_arginfo.h
generated
|
@ -1,5 +1,5 @@
|
||||||
/* This is a generated file, edit the .stub.php file instead.
|
/* This is a generated file, edit the .stub.php file instead.
|
||||||
* Stub hash: b2af57dc7fedc4305cbae2664331376fe390873f */
|
* Stub hash: 62716ca2f10c7df42529a9140d80bd7cbc2dc481 */
|
||||||
|
|
||||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_x509_export_to_file, 0, 2, _IS_BOOL, 0)
|
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_x509_export_to_file, 0, 2, _IS_BOOL, 0)
|
||||||
ZEND_ARG_OBJ_TYPE_MASK(0, certificate, OpenSSLCertificate, MAY_BE_STRING, NULL)
|
ZEND_ARG_OBJ_TYPE_MASK(0, certificate, OpenSSLCertificate, MAY_BE_STRING, NULL)
|
||||||
|
@ -388,6 +388,22 @@ ZEND_END_ARG_INFO()
|
||||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_get_cert_locations, 0, 0, IS_ARRAY, 0)
|
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_get_cert_locations, 0, 0, IS_ARRAY, 0)
|
||||||
ZEND_END_ARG_INFO()
|
ZEND_END_ARG_INFO()
|
||||||
|
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_password_hash, 0, 2, IS_STRING, 0)
|
||||||
|
ZEND_ARG_TYPE_INFO(0, algo, IS_STRING, 0)
|
||||||
|
ZEND_ARG_TYPE_INFO(0, password, IS_STRING, 0)
|
||||||
|
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
|
||||||
|
ZEND_END_ARG_INFO()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_password_verify, 0, 3, _IS_BOOL, 0)
|
||||||
|
ZEND_ARG_TYPE_INFO(0, algo, IS_STRING, 0)
|
||||||
|
ZEND_ARG_TYPE_INFO(0, password, IS_STRING, 0)
|
||||||
|
ZEND_ARG_TYPE_INFO(0, hash, IS_STRING, 0)
|
||||||
|
ZEND_END_ARG_INFO()
|
||||||
|
#endif
|
||||||
|
|
||||||
ZEND_FUNCTION(openssl_x509_export_to_file);
|
ZEND_FUNCTION(openssl_x509_export_to_file);
|
||||||
ZEND_FUNCTION(openssl_x509_export);
|
ZEND_FUNCTION(openssl_x509_export);
|
||||||
ZEND_FUNCTION(openssl_x509_fingerprint);
|
ZEND_FUNCTION(openssl_x509_fingerprint);
|
||||||
|
@ -451,6 +467,12 @@ ZEND_FUNCTION(openssl_spki_verify);
|
||||||
ZEND_FUNCTION(openssl_spki_export);
|
ZEND_FUNCTION(openssl_spki_export);
|
||||||
ZEND_FUNCTION(openssl_spki_export_challenge);
|
ZEND_FUNCTION(openssl_spki_export_challenge);
|
||||||
ZEND_FUNCTION(openssl_get_cert_locations);
|
ZEND_FUNCTION(openssl_get_cert_locations);
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
ZEND_FUNCTION(openssl_password_hash);
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
ZEND_FUNCTION(openssl_password_verify);
|
||||||
|
#endif
|
||||||
|
|
||||||
static const zend_function_entry ext_functions[] = {
|
static const zend_function_entry ext_functions[] = {
|
||||||
ZEND_FE(openssl_x509_export_to_file, arginfo_openssl_x509_export_to_file)
|
ZEND_FE(openssl_x509_export_to_file, arginfo_openssl_x509_export_to_file)
|
||||||
|
@ -519,6 +541,12 @@ static const zend_function_entry ext_functions[] = {
|
||||||
ZEND_FE(openssl_spki_export, arginfo_openssl_spki_export)
|
ZEND_FE(openssl_spki_export, arginfo_openssl_spki_export)
|
||||||
ZEND_FE(openssl_spki_export_challenge, arginfo_openssl_spki_export_challenge)
|
ZEND_FE(openssl_spki_export_challenge, arginfo_openssl_spki_export_challenge)
|
||||||
ZEND_FE(openssl_get_cert_locations, arginfo_openssl_get_cert_locations)
|
ZEND_FE(openssl_get_cert_locations, arginfo_openssl_get_cert_locations)
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
ZEND_FE(openssl_password_hash, arginfo_openssl_password_hash)
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
ZEND_FE(openssl_password_verify, arginfo_openssl_password_verify)
|
||||||
|
#endif
|
||||||
ZEND_FE_END
|
ZEND_FE_END
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -759,6 +787,14 @@ static void register_openssl_symbols(int module_number)
|
||||||
zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "openssl_pkey_derive", sizeof("openssl_pkey_derive") - 1), 1, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
|
zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "openssl_pkey_derive", sizeof("openssl_pkey_derive") - 1), 1, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
|
||||||
|
|
||||||
zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "openssl_spki_new", sizeof("openssl_spki_new") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
|
zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "openssl_spki_new", sizeof("openssl_spki_new") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
|
||||||
|
zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "openssl_password_hash", sizeof("openssl_password_hash") - 1), 1, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
|
||||||
|
zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "openssl_password_verify", sizeof("openssl_password_verify") - 1), 1, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static zend_class_entry *register_class_OpenSSLCertificate(void)
|
static zend_class_entry *register_class_OpenSSLCertificate(void)
|
||||||
|
|
388
ext/openssl/openssl_pwhash.c
Normal file
388
ext/openssl/openssl_pwhash.c
Normal file
|
@ -0,0 +1,388 @@
|
||||||
|
/*
|
||||||
|
+----------------------------------------------------------------------+
|
||||||
|
| 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: |
|
||||||
|
| https://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: Remi Collet <remi@php.net> |
|
||||||
|
+----------------------------------------------------------------------+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "php.h"
|
||||||
|
#include "ext/standard/php_password.h"
|
||||||
|
#include "php_openssl.h"
|
||||||
|
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
#include "Zend/zend_attributes.h"
|
||||||
|
#include "openssl_pwhash_arginfo.h"
|
||||||
|
#include <ext/standard/base64.h>
|
||||||
|
#include "ext/random/php_random_csprng.h"
|
||||||
|
#include <openssl/params.h>
|
||||||
|
#include <openssl/core_names.h>
|
||||||
|
#include <openssl/kdf.h>
|
||||||
|
#include <openssl/thread.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
|
||||||
|
#define PHP_OPENSSL_MEMLIMIT_MIN 8u
|
||||||
|
#define PHP_OPENSSL_MEMLIMIT_MAX UINT32_MAX
|
||||||
|
#define PHP_OPENSSL_ITERLIMIT_MIN 1u
|
||||||
|
#define PHP_OPENSSL_ITERLIMIT_MAX UINT32_MAX
|
||||||
|
#define PHP_OPENSSL_THREADS_MIN 1u
|
||||||
|
#define PHP_OPENSSL_THREADS_MAX UINT32_MAX
|
||||||
|
|
||||||
|
#define PHP_OPENSSL_ARGON_VERSION 0x13
|
||||||
|
|
||||||
|
#define PHP_OPENSSL_SALT_SIZE 16
|
||||||
|
#define PHP_OPENSSL_HASH_SIZE 32
|
||||||
|
#define PHP_OPENSSL_DIGEST_SIZE 128
|
||||||
|
|
||||||
|
static inline zend_result get_options(zend_array *options, uint32_t *memlimit, uint32_t *iterlimit, uint32_t *threads)
|
||||||
|
{
|
||||||
|
zval *opt;
|
||||||
|
|
||||||
|
*iterlimit = PHP_OPENSSL_PWHASH_ITERLIMIT;
|
||||||
|
*memlimit = PHP_OPENSSL_PWHASH_MEMLIMIT;
|
||||||
|
*threads = PHP_OPENSSL_PWHASH_THREADS;
|
||||||
|
|
||||||
|
if (!options) {
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
if ((opt = zend_hash_str_find(options, "memory_cost", strlen("memory_cost")))) {
|
||||||
|
zend_long smemlimit = zval_get_long(opt);
|
||||||
|
|
||||||
|
if ((smemlimit < 0) || (smemlimit < PHP_OPENSSL_MEMLIMIT_MIN) || (smemlimit > (PHP_OPENSSL_MEMLIMIT_MAX))) {
|
||||||
|
zend_value_error("Memory cost is outside of allowed memory range");
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
*memlimit = smemlimit;
|
||||||
|
}
|
||||||
|
if ((opt = zend_hash_str_find(options, "time_cost", strlen("time_cost")))) {
|
||||||
|
zend_long siterlimit = zval_get_long(opt);
|
||||||
|
if ((siterlimit < PHP_OPENSSL_ITERLIMIT_MIN) || (siterlimit > PHP_OPENSSL_ITERLIMIT_MAX)) {
|
||||||
|
zend_value_error("Time cost is outside of allowed time range");
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
*iterlimit = siterlimit;
|
||||||
|
}
|
||||||
|
if ((opt = zend_hash_str_find(options, "threads", strlen("threads"))) && (zval_get_long(opt) != 1)) {
|
||||||
|
zend_long sthreads = zval_get_long(opt);
|
||||||
|
if ((sthreads < PHP_OPENSSL_THREADS_MIN) || (sthreads > PHP_OPENSSL_THREADS_MAX)) {
|
||||||
|
zend_value_error("Invalid number of threads");
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
*threads = sthreads;
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool php_openssl_argon2_compute_hash(
|
||||||
|
const char *algo,
|
||||||
|
uint32_t version, uint32_t memlimit, uint32_t iterlimit, uint32_t threads,
|
||||||
|
const char *pass, size_t pass_len,
|
||||||
|
const unsigned char *salt, size_t salt_len,
|
||||||
|
unsigned char *hash, size_t hash_len)
|
||||||
|
{
|
||||||
|
OSSL_PARAM params[7], *p = params;
|
||||||
|
EVP_KDF *kdf = NULL;
|
||||||
|
EVP_KDF_CTX *kctx = NULL;
|
||||||
|
uint32_t oldthreads;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
oldthreads = OSSL_get_max_threads(NULL);
|
||||||
|
if (OSSL_set_max_threads(NULL, threads) != 1) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
p = params;
|
||||||
|
*p++ = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_THREADS, &threads);
|
||||||
|
*p++ = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_ARGON2_LANES, &threads);
|
||||||
|
*p++ = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_ITER, &iterlimit);
|
||||||
|
*p++ = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_ARGON2_MEMCOST, &memlimit);
|
||||||
|
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt, salt_len);
|
||||||
|
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASSWORD, (void *)pass, pass_len);
|
||||||
|
*p++ = OSSL_PARAM_construct_end();
|
||||||
|
|
||||||
|
if ((kdf = EVP_KDF_fetch(NULL, algo, NULL)) == NULL) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if ((kctx = EVP_KDF_CTX_new(kdf)) == NULL) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (EVP_KDF_derive(kctx, hash, hash_len, params) != 1) {
|
||||||
|
zend_value_error("Unexpected failure hashing password");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
EVP_KDF_free(kdf);
|
||||||
|
EVP_KDF_CTX_free(kctx);
|
||||||
|
OSSL_set_max_threads(NULL, oldthreads);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static zend_string *php_openssl_argon2_hash(const zend_string *password, zend_array *options, const char *algo)
|
||||||
|
{
|
||||||
|
uint32_t iterlimit, memlimit, threads, version = PHP_OPENSSL_ARGON_VERSION;
|
||||||
|
zend_string *digest = NULL, *salt64 = NULL, *hash64 = NULL;
|
||||||
|
unsigned char hash[PHP_OPENSSL_HASH_SIZE+1], salt[PHP_OPENSSL_SALT_SIZE+1];
|
||||||
|
|
||||||
|
if ((ZSTR_LEN(password) >= UINT32_MAX)) {
|
||||||
|
zend_value_error("Password is too long");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (get_options(options, &memlimit, &iterlimit, &threads) == FAILURE) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (RAND_bytes(salt, PHP_OPENSSL_SALT_SIZE) <= 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!php_openssl_argon2_compute_hash(algo, version, memlimit, iterlimit, threads,
|
||||||
|
ZSTR_VAL(password), ZSTR_LEN(password), salt, PHP_OPENSSL_SALT_SIZE, hash, PHP_OPENSSL_HASH_SIZE)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash64 = php_base64_encode_ex(hash, PHP_OPENSSL_HASH_SIZE, PHP_BASE64_NO_PADDING);
|
||||||
|
|
||||||
|
salt64 = php_base64_encode_ex(salt, PHP_OPENSSL_SALT_SIZE, PHP_BASE64_NO_PADDING);
|
||||||
|
|
||||||
|
digest = zend_string_alloc(PHP_OPENSSL_DIGEST_SIZE, 0);
|
||||||
|
ZSTR_LEN(digest) = snprintf(ZSTR_VAL(digest), ZSTR_LEN(digest), "$%s$v=%d$m=%u,t=%u,p=%u$%s$%s",
|
||||||
|
algo, version, memlimit, iterlimit, threads, ZSTR_VAL(salt64), ZSTR_VAL(hash64));
|
||||||
|
|
||||||
|
zend_string_release(salt64);
|
||||||
|
zend_string_release(hash64);
|
||||||
|
|
||||||
|
return digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int php_openssl_argon2_extract(
|
||||||
|
const zend_string *digest, uint32_t *version, uint32_t *memlimit, uint32_t *iterlimit,
|
||||||
|
uint32_t *threads, zend_string **salt, zend_string **hash)
|
||||||
|
{
|
||||||
|
const char *p;
|
||||||
|
char *hash64, *salt64;
|
||||||
|
|
||||||
|
if (!digest || (ZSTR_LEN(digest) < sizeof("$argon2id$"))) {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
p = ZSTR_VAL(digest);
|
||||||
|
if (!memcmp(p, "$argon2i$", strlen("$argon2i$"))) {
|
||||||
|
p += strlen("$argon2i$");
|
||||||
|
} else if (!memcmp(p, "$argon2id$", strlen("$argon2id$"))) {
|
||||||
|
p += strlen("$argon2id$");
|
||||||
|
} else {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
if (sscanf(p, "v=%" PRIu32 "$m=%" PRIu32 ",t=%" PRIu32 ",p=%" PRIu32,
|
||||||
|
version, memlimit, iterlimit, threads) != 4) {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
if (salt && hash) {
|
||||||
|
/* start of param */
|
||||||
|
p = strchr(p, '$');
|
||||||
|
if (!p) {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
/* start of salt */
|
||||||
|
p = strchr(p+1, '$');
|
||||||
|
if (!p) {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
salt64 = estrdup(p+1);
|
||||||
|
/* start of hash */
|
||||||
|
hash64 = strchr(salt64, '$');
|
||||||
|
if (!hash64) {
|
||||||
|
efree(salt64);
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
*hash64++ = 0;
|
||||||
|
*salt = php_base64_decode((unsigned char *)salt64, strlen(salt64));
|
||||||
|
*hash = php_base64_decode((unsigned char *)hash64, strlen(hash64));
|
||||||
|
efree(salt64);
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool php_openssl_argon2_verify(const zend_string *password, const zend_string *digest, const char *algo)
|
||||||
|
{
|
||||||
|
uint32_t version, iterlimit, memlimit, threads;
|
||||||
|
zend_string *salt, *hash, *new;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if ((ZSTR_LEN(password) >= UINT32_MAX) || (ZSTR_LEN(digest) >= UINT32_MAX)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (FAILURE == php_openssl_argon2_extract(digest, &version, &memlimit, &iterlimit, &threads, &salt, &hash)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
new = zend_string_alloc(ZSTR_LEN(hash), 0);
|
||||||
|
if (php_openssl_argon2_compute_hash(algo, version, memlimit, iterlimit, threads,
|
||||||
|
ZSTR_VAL(password), ZSTR_LEN(password), (unsigned char *)ZSTR_VAL(salt),
|
||||||
|
ZSTR_LEN(salt), (unsigned char *)ZSTR_VAL(new), ZSTR_LEN(new))) {
|
||||||
|
ret = (php_safe_bcmp(hash, new) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
zend_string_release(new);
|
||||||
|
zend_string_release(salt);
|
||||||
|
zend_string_release(hash);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool php_openssl_argon2i_verify(const zend_string *password, const zend_string *digest)
|
||||||
|
{
|
||||||
|
return php_openssl_argon2_verify(password, digest, "argon2i");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool php_openssl_argon2id_verify(const zend_string *password, const zend_string *digest)
|
||||||
|
{
|
||||||
|
return php_openssl_argon2_verify(password, digest, "argon2id");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool php_openssl_argon2_needs_rehash(const zend_string *hash, zend_array *options)
|
||||||
|
{
|
||||||
|
uint32_t version, iterlimit, memlimit, threads;
|
||||||
|
uint32_t new_version = PHP_OPENSSL_ARGON_VERSION, new_iterlimit, new_memlimit, new_threads;
|
||||||
|
|
||||||
|
if (FAILURE == get_options(options, &new_memlimit, &new_iterlimit, &new_threads)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (FAILURE == php_openssl_argon2_extract(hash, &version, &memlimit, &iterlimit, &threads, NULL, NULL)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Algo already checked in pasword_needs_rehash implementation
|
||||||
|
return (version != new_version) ||
|
||||||
|
(iterlimit != new_iterlimit) ||
|
||||||
|
(memlimit != new_memlimit) ||
|
||||||
|
(threads != new_threads);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int php_openssl_argon2_get_info(zval *return_value, const zend_string *hash)
|
||||||
|
{
|
||||||
|
uint32_t v, threads;
|
||||||
|
uint32_t memory_cost;
|
||||||
|
uint32_t time_cost;
|
||||||
|
|
||||||
|
if (FAILURE == php_openssl_argon2_extract(hash, &v, &memory_cost, &time_cost, &threads, NULL, NULL)) {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static zend_string *php_openssl_argon2i_hash(const zend_string *password, zend_array *options)
|
||||||
|
{
|
||||||
|
return php_openssl_argon2_hash(password, options, "argon2i");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const php_password_algo openssl_algo_argon2i = {
|
||||||
|
"argon2i",
|
||||||
|
php_openssl_argon2i_hash,
|
||||||
|
php_openssl_argon2i_verify,
|
||||||
|
php_openssl_argon2_needs_rehash,
|
||||||
|
php_openssl_argon2_get_info,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static zend_string *php_openssl_argon2id_hash(const zend_string *password, zend_array *options)
|
||||||
|
{
|
||||||
|
return php_openssl_argon2_hash(password, options, "argon2id");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const php_password_algo openssl_algo_argon2id = {
|
||||||
|
"argon2id",
|
||||||
|
php_openssl_argon2id_hash,
|
||||||
|
php_openssl_argon2id_verify,
|
||||||
|
php_openssl_argon2_needs_rehash,
|
||||||
|
php_openssl_argon2_get_info,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
PHP_FUNCTION(openssl_password_hash)
|
||||||
|
{
|
||||||
|
zend_string *password, *algo, *digest;
|
||||||
|
zend_array *options = NULL;
|
||||||
|
|
||||||
|
ZEND_PARSE_PARAMETERS_START(2, 3)
|
||||||
|
Z_PARAM_STR(algo)
|
||||||
|
Z_PARAM_STR(password)
|
||||||
|
Z_PARAM_OPTIONAL
|
||||||
|
Z_PARAM_ARRAY_HT(options)
|
||||||
|
ZEND_PARSE_PARAMETERS_END();
|
||||||
|
|
||||||
|
if (strcmp(ZSTR_VAL(algo), "argon2i") && strcmp(ZSTR_VAL(algo), "argon2id")) {
|
||||||
|
zend_argument_value_error(1, "must be a valid password openssl hashing algorithm");
|
||||||
|
RETURN_THROWS();
|
||||||
|
}
|
||||||
|
|
||||||
|
digest = php_openssl_argon2_hash(password, options, ZSTR_VAL(algo));
|
||||||
|
if (!digest) {
|
||||||
|
if (!EG(exception)) {
|
||||||
|
zend_throw_error(NULL, "Password hashing failed for unknown reason");
|
||||||
|
}
|
||||||
|
RETURN_THROWS();
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_NEW_STR(digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
PHP_FUNCTION(openssl_password_verify)
|
||||||
|
{
|
||||||
|
zend_string *password, *algo, *digest;
|
||||||
|
|
||||||
|
ZEND_PARSE_PARAMETERS_START(3, 3)
|
||||||
|
Z_PARAM_STR(algo)
|
||||||
|
Z_PARAM_STR(password)
|
||||||
|
Z_PARAM_STR(digest)
|
||||||
|
ZEND_PARSE_PARAMETERS_END();
|
||||||
|
|
||||||
|
if (strcmp(ZSTR_VAL(algo), "argon2i") && strcmp(ZSTR_VAL(algo), "argon2id")) {
|
||||||
|
zend_argument_value_error(1, "must be a valid password openssl hashing algorithm");
|
||||||
|
RETURN_THROWS();
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_BOOL(php_openssl_argon2_verify(password, digest, ZSTR_VAL(algo)));
|
||||||
|
}
|
||||||
|
|
||||||
|
PHP_MINIT_FUNCTION(openssl_pwhash)
|
||||||
|
{
|
||||||
|
zend_string *argon2i = ZSTR_INIT_LITERAL("argon2i", 1);
|
||||||
|
|
||||||
|
if (php_password_algo_find(argon2i)) {
|
||||||
|
/* Nothing to do. Core or sodium has registered these algorithms for us. */
|
||||||
|
zend_string_release(argon2i);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
zend_string_release(argon2i);
|
||||||
|
|
||||||
|
register_openssl_pwhash_symbols(module_number);
|
||||||
|
|
||||||
|
if (FAILURE == php_password_algo_register("argon2i", &openssl_algo_argon2i)) {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
if (FAILURE == php_password_algo_register("argon2id", &openssl_algo_argon2id)) {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
#endif /* HAVE_OPENSSL_ARGON2 */
|
34
ext/openssl/openssl_pwhash.stub.php
Normal file
34
ext/openssl/openssl_pwhash.stub.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/** @generate-class-entries */
|
||||||
|
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const PASSWORD_ARGON2I = "argon2i";
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const PASSWORD_ARGON2ID = "argon2id";
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
* @cvalue PHP_OPENSSL_PWHASH_MEMLIMIT
|
||||||
|
*/
|
||||||
|
const PASSWORD_ARGON2_DEFAULT_MEMORY_COST = UNKNOWN;
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
* @cvalue PHP_OPENSSL_PWHASH_ITERLIMIT
|
||||||
|
*/
|
||||||
|
const PASSWORD_ARGON2_DEFAULT_TIME_COST = UNKNOWN;
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
* @cvalue PHP_OPENSSL_PWHASH_THREADS
|
||||||
|
*/
|
||||||
|
const PASSWORD_ARGON2_DEFAULT_THREADS = UNKNOWN;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const PASSWORD_ARGON2_PROVIDER = "openssl";
|
||||||
|
#endif
|
||||||
|
|
24
ext/openssl/openssl_pwhash_arginfo.h
generated
Normal file
24
ext/openssl/openssl_pwhash_arginfo.h
generated
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/* This is a generated file, edit the .stub.php file instead.
|
||||||
|
* Stub hash: b6056170a5c8f1a9582c5eef9261a0bd02f7a7e1 */
|
||||||
|
|
||||||
|
static void register_openssl_pwhash_symbols(int module_number)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
REGISTER_STRING_CONSTANT("PASSWORD_ARGON2I", "argon2i", CONST_PERSISTENT);
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
REGISTER_STRING_CONSTANT("PASSWORD_ARGON2ID", "argon2id", CONST_PERSISTENT);
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_MEMORY_COST", PHP_OPENSSL_PWHASH_MEMLIMIT, CONST_PERSISTENT);
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_TIME_COST", PHP_OPENSSL_PWHASH_ITERLIMIT, CONST_PERSISTENT);
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_THREADS", PHP_OPENSSL_PWHASH_THREADS, CONST_PERSISTENT);
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
REGISTER_STRING_CONSTANT("PASSWORD_ARGON2_PROVIDER", "openssl", CONST_PERSISTENT);
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -37,8 +37,10 @@ extern zend_module_entry openssl_module_entry;
|
||||||
/* OpenSSL version check */
|
/* OpenSSL version check */
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||||
#define PHP_OPENSSL_API_VERSION 0x10100
|
#define PHP_OPENSSL_API_VERSION 0x10100
|
||||||
#else
|
#elif OPENSSL_VERSION_NUMBER < 0x30200000L
|
||||||
#define PHP_OPENSSL_API_VERSION 0x30000
|
#define PHP_OPENSSL_API_VERSION 0x30000
|
||||||
|
#else
|
||||||
|
#define PHP_OPENSSL_API_VERSION 0x30200
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -156,11 +158,40 @@ static inline php_openssl_certificate_object *php_openssl_certificate_from_obj(z
|
||||||
|
|
||||||
#define Z_OPENSSL_CERTIFICATE_P(zv) php_openssl_certificate_from_obj(Z_OBJ_P(zv))
|
#define Z_OPENSSL_CERTIFICATE_P(zv) php_openssl_certificate_from_obj(Z_OBJ_P(zv))
|
||||||
|
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MEMLIMIT is normalized to KB even though sodium uses Bytes in order to
|
||||||
|
* present a consistent user-facing API.
|
||||||
|
*
|
||||||
|
* When updating these values, synchronize ext/standard/php_password.h values.
|
||||||
|
*/
|
||||||
|
#if defined(PHP_PASSWORD_ARGON2_MEMORY_COST)
|
||||||
|
#define PHP_OPENSSL_PWHASH_MEMLIMIT PHP_PASSWORD_ARGON2_MEMORY_COST
|
||||||
|
#else
|
||||||
|
#define PHP_OPENSSL_PWHASH_MEMLIMIT (64 << 10)
|
||||||
|
#endif
|
||||||
|
#if defined(PHP_PASSWORD_ARGON2_TIME_COST)
|
||||||
|
#define PHP_OPENSSL_PWHASH_ITERLIMIT PHP_PASSWORD_ARGON2_TIME_COST
|
||||||
|
#else
|
||||||
|
#define PHP_OPENSSL_PWHASH_ITERLIMIT 4
|
||||||
|
#endif
|
||||||
|
#if defined(PHP_PASSWORD_ARGON2_THREADS)
|
||||||
|
#define PHP_OPENSSL_PWHASH_THREADS PHP_PASSWORD_ARGON2_THREADS
|
||||||
|
#else
|
||||||
|
#define PHP_OPENSSL_PWHASH_THREADS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
PHP_MINIT_FUNCTION(openssl);
|
PHP_MINIT_FUNCTION(openssl);
|
||||||
PHP_MSHUTDOWN_FUNCTION(openssl);
|
PHP_MSHUTDOWN_FUNCTION(openssl);
|
||||||
PHP_MINFO_FUNCTION(openssl);
|
PHP_MINFO_FUNCTION(openssl);
|
||||||
PHP_GINIT_FUNCTION(openssl);
|
PHP_GINIT_FUNCTION(openssl);
|
||||||
PHP_GSHUTDOWN_FUNCTION(openssl);
|
PHP_GSHUTDOWN_FUNCTION(openssl);
|
||||||
|
#if defined(HAVE_OPENSSL_ARGON2)
|
||||||
|
PHP_MINIT_FUNCTION(openssl_pwhash);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef PHP_WIN32
|
#ifdef PHP_WIN32
|
||||||
#define PHP_OPENSSL_BIO_MODE_R(flags) (((flags) & PKCS7_BINARY) ? "rb" : "r")
|
#define PHP_OPENSSL_BIO_MODE_R(flags) (((flags) & PKCS7_BINARY) ? "rb" : "r")
|
||||||
|
|
42
ext/openssl/tests/openssl_password.phpt
Normal file
42
ext/openssl/tests/openssl_password.phpt
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
--TEST--
|
||||||
|
Basic features of password_hash
|
||||||
|
--EXTENSIONS--
|
||||||
|
openssl
|
||||||
|
--SKIPIF--
|
||||||
|
<?php
|
||||||
|
if (!function_exists('openssl_password_hash')) {
|
||||||
|
echo "skip - No openssl_password_hash";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
echo 'Argon2 provider: ';
|
||||||
|
var_dump(PASSWORD_ARGON2_PROVIDER);
|
||||||
|
|
||||||
|
foreach([1, 2] as $mem) {
|
||||||
|
foreach([1, 2] as $time) {
|
||||||
|
$opts = [
|
||||||
|
'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST / $mem,
|
||||||
|
'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST / $time,
|
||||||
|
'threads' => PASSWORD_ARGON2_DEFAULT_THREADS,
|
||||||
|
];
|
||||||
|
foreach(['argon2i', 'argon2id'] as $algo) {
|
||||||
|
$pass = "secret$mem$time$algo";
|
||||||
|
$hash = openssl_password_hash($algo, $pass, $opts);
|
||||||
|
var_dump(openssl_password_verify($algo, $pass, $hash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Argon2 provider: string(%d) "%s"
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
|
52
ext/openssl/tests/openssl_password_compat.phpt
Normal file
52
ext/openssl/tests/openssl_password_compat.phpt
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
--TEST--
|
||||||
|
Compatibility of password_hash (libsodium / openssl)
|
||||||
|
--EXTENSIONS--
|
||||||
|
openssl
|
||||||
|
sodium
|
||||||
|
--SKIPIF--
|
||||||
|
<?php
|
||||||
|
if (!function_exists('sodium_crypto_pwhash_str_verify')) {
|
||||||
|
echo "skip - No crypto_pwhash_str_verify";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('openssl_password_hash')) {
|
||||||
|
echo "skip - No crypto_pwhash_str_verify";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
echo 'Argon2 provider: ';
|
||||||
|
var_dump(PASSWORD_ARGON2_PROVIDER);
|
||||||
|
|
||||||
|
foreach([1, 2] as $mem) {
|
||||||
|
foreach([1, 2] as $time) {
|
||||||
|
$opts = [
|
||||||
|
'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST / $mem,
|
||||||
|
'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST / $time,
|
||||||
|
'threads' => PASSWORD_ARGON2_DEFAULT_THREADS,
|
||||||
|
];
|
||||||
|
$algo = 'argon2id';
|
||||||
|
$pass = "secret$mem$time$algo";
|
||||||
|
|
||||||
|
/* hash with libsodium / verify with openssl */
|
||||||
|
$hash = sodium_crypto_pwhash_str($pass, PASSWORD_ARGON2_DEFAULT_TIME_COST / $time, PASSWORD_ARGON2_DEFAULT_MEMORY_COST / $mem);
|
||||||
|
var_dump(openssl_password_verify($algo, $pass, $hash));
|
||||||
|
|
||||||
|
/* hash with openssl / verify with libsodium */
|
||||||
|
$hash = openssl_password_hash($algo, $pass, $opts);
|
||||||
|
var_dump(sodium_crypto_pwhash_str_verify($hash, $pass));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Argon2 provider: string(%d) "%s"
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
|
52
ext/openssl/tests/openssl_password_compat2.phpt
Normal file
52
ext/openssl/tests/openssl_password_compat2.phpt
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
--TEST--
|
||||||
|
Compatibility of password_hash (libargon2 / openssl)
|
||||||
|
--EXTENSIONS--
|
||||||
|
openssl
|
||||||
|
sodium
|
||||||
|
--SKIPIF--
|
||||||
|
<?php
|
||||||
|
if (PASSWORD_ARGON2_PROVIDER != "standard") {
|
||||||
|
echo "skip - libargon2 not available";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('openssl_password_hash')) {
|
||||||
|
echo "skip - No crypto_pwhash_str_verify";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
echo 'Argon2 provider: ';
|
||||||
|
var_dump(PASSWORD_ARGON2_PROVIDER);
|
||||||
|
|
||||||
|
foreach([1, 2] as $mem) {
|
||||||
|
foreach([1, 2] as $time) {
|
||||||
|
$opts = [
|
||||||
|
'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST / $mem,
|
||||||
|
'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST / $time,
|
||||||
|
'threads' => PASSWORD_ARGON2_DEFAULT_THREADS,
|
||||||
|
];
|
||||||
|
$algo = 'argon2id';
|
||||||
|
$pass = "secret$mem$time$algo";
|
||||||
|
|
||||||
|
/* hash with libargon2 / verify with openssl */
|
||||||
|
$hash = password_hash($pass, PASSWORD_ARGON2ID, $opts);
|
||||||
|
var_dump(openssl_password_verify($algo, $pass, $hash));
|
||||||
|
|
||||||
|
/* hash with openssl / verify with libargon2 */
|
||||||
|
$hash = openssl_password_hash($algo, $pass, $opts);
|
||||||
|
var_dump(password_verify($pass, $hash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Argon2 provider: string(8) "standard"
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
|
|
@ -26,8 +26,10 @@ PHP_MSHUTDOWN_FUNCTION(password);
|
||||||
|
|
||||||
#ifdef HAVE_ARGON2LIB
|
#ifdef HAVE_ARGON2LIB
|
||||||
/**
|
/**
|
||||||
* When updating these values, synchronize ext/sodium/sodium_pwhash.c values.
|
* When updating these values, synchronize values in
|
||||||
* Note that libargon expresses memlimit in KB, while libsoidum uses bytes.
|
* ext/sodium/php_libsodium.h
|
||||||
|
* ext/openssl/php_openssl.h
|
||||||
|
* Note that libargon/openssl express memlimit in KB, while libsodium uses bytes.
|
||||||
*/
|
*/
|
||||||
#define PHP_PASSWORD_ARGON2_MEMORY_COST (64 << 10)
|
#define PHP_PASSWORD_ARGON2_MEMORY_COST (64 << 10)
|
||||||
#define PHP_PASSWORD_ARGON2_TIME_COST 4
|
#define PHP_PASSWORD_ARGON2_TIME_COST 4
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue