mirror of
https://github.com/php/php-src.git
synced 2025-08-19 17:04:47 +02:00
Merge branch 'pull-request/1997'
This commit is contained in:
commit
1305fdaa3b
9 changed files with 411 additions and 58 deletions
|
@ -550,6 +550,38 @@ dnl Check for getrandom on newer Linux kernels
|
||||||
dnl
|
dnl
|
||||||
AC_CHECK_DECLS([getrandom])
|
AC_CHECK_DECLS([getrandom])
|
||||||
|
|
||||||
|
dnl
|
||||||
|
dnl Check for argon2
|
||||||
|
dnl
|
||||||
|
PHP_ARG_WITH(password-argon2, for Argon2 support,
|
||||||
|
[ --with-password-argon2[=DIR] Include Argon2 support in password_*. DIR is the Argon2 shared library path]])
|
||||||
|
|
||||||
|
if test "$PHP_PASSWORD_ARGON2" != "no"; then
|
||||||
|
AC_MSG_CHECKING([for Argon2 library])
|
||||||
|
for i in $PHP_PASSWORD_ARGON2 /usr /usr/local ; do
|
||||||
|
if test -r $i/include/argon2.h; then
|
||||||
|
ARGON2_DIR=$i;
|
||||||
|
AC_MSG_RESULT(found in $i)
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if test -z "$ARGON2_DIR"; then
|
||||||
|
AC_MSG_RESULT([not found])
|
||||||
|
AC_MSG_ERROR([Please ensure the argon2 header and library are installed])
|
||||||
|
fi
|
||||||
|
|
||||||
|
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])
|
||||||
|
])
|
||||||
|
fi
|
||||||
|
|
||||||
dnl
|
dnl
|
||||||
dnl Setup extension sources
|
dnl Setup extension sources
|
||||||
dnl
|
dnl
|
||||||
|
|
|
@ -1,6 +1,17 @@
|
||||||
// vim:ft=javascript
|
// vim:ft=javascript
|
||||||
// $Id$
|
// $Id$
|
||||||
|
|
||||||
|
ARG_WITH("password-argon2", "Argon2 support", "no");
|
||||||
|
|
||||||
|
if (PHP_PASSWORD_ARGON2 != "no") {
|
||||||
|
if (CHECK_LIB("Argon2Ref.lib", null, PHP_PASSWORD_ARGON2)
|
||||||
|
&& CHECK_HEADER_ADD_INCLUDE("argon2.h", "CFLAGS")) {
|
||||||
|
AC_DEFINE('HAVE_ARGON2LIB', 1);
|
||||||
|
} else {
|
||||||
|
WARNING("Argon2 not enabled; libaries and headers not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ARG_WITH("config-file-scan-dir", "Dir to check for additional php ini files", "");
|
ARG_WITH("config-file-scan-dir", "Dir to check for additional php ini files", "");
|
||||||
|
|
||||||
AC_DEFINE("PHP_CONFIG_FILE_SCAN_DIR", PHP_CONFIG_FILE_SCAN_DIR);
|
AC_DEFINE("PHP_CONFIG_FILE_SCAN_DIR", PHP_CONFIG_FILE_SCAN_DIR);
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
| license@php.net so we can mail you a copy immediately. |
|
| license@php.net so we can mail you a copy immediately. |
|
||||||
+----------------------------------------------------------------------+
|
+----------------------------------------------------------------------+
|
||||||
| Authors: Anthony Ferrara <ircmaxell@php.net> |
|
| Authors: Anthony Ferrara <ircmaxell@php.net> |
|
||||||
|
| Charles R. Portwood II <charlesportwoodii@erianna.com> |
|
||||||
+----------------------------------------------------------------------+
|
+----------------------------------------------------------------------+
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -30,6 +31,9 @@
|
||||||
#include "zend_interfaces.h"
|
#include "zend_interfaces.h"
|
||||||
#include "info.h"
|
#include "info.h"
|
||||||
#include "php_random.h"
|
#include "php_random.h"
|
||||||
|
#if HAVE_ARGON2LIB
|
||||||
|
#include "argon2.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if PHP_WIN32
|
#if PHP_WIN32
|
||||||
#include "win32/winutil.h"
|
#include "win32/winutil.h"
|
||||||
|
@ -39,8 +43,16 @@ PHP_MINIT_FUNCTION(password) /* {{{ */
|
||||||
{
|
{
|
||||||
REGISTER_LONG_CONSTANT("PASSWORD_DEFAULT", PHP_PASSWORD_DEFAULT, CONST_CS | CONST_PERSISTENT);
|
REGISTER_LONG_CONSTANT("PASSWORD_DEFAULT", PHP_PASSWORD_DEFAULT, CONST_CS | CONST_PERSISTENT);
|
||||||
REGISTER_LONG_CONSTANT("PASSWORD_BCRYPT", PHP_PASSWORD_BCRYPT, CONST_CS | CONST_PERSISTENT);
|
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);
|
||||||
|
#endif
|
||||||
|
|
||||||
REGISTER_LONG_CONSTANT("PASSWORD_BCRYPT_DEFAULT_COST", PHP_PASSWORD_BCRYPT_COST, CONST_CS | CONST_PERSISTENT);
|
REGISTER_LONG_CONSTANT("PASSWORD_BCRYPT_DEFAULT_COST", PHP_PASSWORD_BCRYPT_COST, CONST_CS | CONST_PERSISTENT);
|
||||||
|
#if HAVE_ARGON2LIB
|
||||||
|
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);
|
||||||
|
#endif
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -51,6 +63,10 @@ static char* php_password_get_algo_name(const php_password_algo algo)
|
||||||
switch (algo) {
|
switch (algo) {
|
||||||
case PHP_PASSWORD_BCRYPT:
|
case PHP_PASSWORD_BCRYPT:
|
||||||
return "bcrypt";
|
return "bcrypt";
|
||||||
|
#if HAVE_ARGON2LIB
|
||||||
|
case PHP_PASSWORD_ARGON2I:
|
||||||
|
return "argon2i";
|
||||||
|
#endif
|
||||||
case PHP_PASSWORD_UNKNOWN:
|
case PHP_PASSWORD_UNKNOWN:
|
||||||
default:
|
default:
|
||||||
return "unknown";
|
return "unknown";
|
||||||
|
@ -61,7 +77,12 @@ static php_password_algo php_password_determine_algo(const char *hash, const siz
|
||||||
{
|
{
|
||||||
if (len > 3 && hash[0] == '$' && hash[1] == '2' && hash[2] == 'y' && len == 60) {
|
if (len > 3 && hash[0] == '$' && hash[1] == '2' && hash[2] == 'y' && len == 60) {
|
||||||
return PHP_PASSWORD_BCRYPT;
|
return PHP_PASSWORD_BCRYPT;
|
||||||
|
}
|
||||||
|
#if HAVE_ARGON2LIB
|
||||||
|
if (len >= sizeof("$argon2i$")-1 && !memcmp(hash, "$argon2i$", sizeof("$argon2i$")-1)) {
|
||||||
|
return PHP_PASSWORD_ARGON2I;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return PHP_PASSWORD_UNKNOWN;
|
return PHP_PASSWORD_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
@ -143,6 +164,8 @@ static int php_password_make_salt(size_t length, char *ret) /* {{{ */
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ proto array password_get_info(string $hash)
|
||||||
|
Retrieves information about a given hash */
|
||||||
PHP_FUNCTION(password_get_info)
|
PHP_FUNCTION(password_get_info)
|
||||||
{
|
{
|
||||||
php_password_algo algo;
|
php_password_algo algo;
|
||||||
|
@ -167,6 +190,21 @@ PHP_FUNCTION(password_get_info)
|
||||||
add_assoc_long(&options, "cost", cost);
|
add_assoc_long(&options, "cost", cost);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#if HAVE_ARGON2LIB
|
||||||
|
case PHP_PASSWORD_ARGON2I:
|
||||||
|
{
|
||||||
|
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(hash, "$%*[argon2i]$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(&options, "memory_cost", memory_cost);
|
||||||
|
add_assoc_long(&options, "time_cost", time_cost);
|
||||||
|
add_assoc_long(&options, "threads", threads);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
case PHP_PASSWORD_UNKNOWN:
|
case PHP_PASSWORD_UNKNOWN:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -178,7 +216,10 @@ PHP_FUNCTION(password_get_info)
|
||||||
add_assoc_string(return_value, "algoName", algo_name);
|
add_assoc_string(return_value, "algoName", algo_name);
|
||||||
add_assoc_zval(return_value, "options", &options);
|
add_assoc_zval(return_value, "options", &options);
|
||||||
}
|
}
|
||||||
|
/** }}} */
|
||||||
|
|
||||||
|
/* {{{ proto boolean password_needs_rehash(string $hash, integer $algo[, array $options])
|
||||||
|
Determines if a given hash requires re-hashing based upon parameters */
|
||||||
PHP_FUNCTION(password_needs_rehash)
|
PHP_FUNCTION(password_needs_rehash)
|
||||||
{
|
{
|
||||||
zend_long new_algo = 0;
|
zend_long new_algo = 0;
|
||||||
|
@ -213,14 +254,43 @@ PHP_FUNCTION(password_needs_rehash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#if HAVE_ARGON2LIB
|
||||||
|
case PHP_PASSWORD_ARGON2I:
|
||||||
|
{
|
||||||
|
zend_long v = 0;
|
||||||
|
zend_long new_memory_cost = PHP_PASSWORD_ARGON2_MEMORY_COST, memory_cost = 0;
|
||||||
|
zend_long new_time_cost = PHP_PASSWORD_ARGON2_TIME_COST, time_cost = 0;
|
||||||
|
zend_long new_threads = PHP_PASSWORD_ARGON2_THREADS, threads = 0;
|
||||||
|
|
||||||
|
if (options && (option_buffer = zend_hash_str_find(options, "memory_cost", sizeof("memory_cost")-1)) != NULL) {
|
||||||
|
new_memory_cost = zval_get_long(option_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options && (option_buffer = zend_hash_str_find(options, "time_cost", sizeof("time_cost")-1)) != NULL) {
|
||||||
|
new_time_cost = zval_get_long(option_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options && (option_buffer = zend_hash_str_find(options, "threads", sizeof("threads")-1)) != NULL) {
|
||||||
|
new_threads = zval_get_long(option_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
sscanf(hash, "$%*[argon2i]$v=" ZEND_LONG_FMT "$m=" ZEND_LONG_FMT ",t=" ZEND_LONG_FMT ",p=" ZEND_LONG_FMT, &v, &memory_cost, &time_cost, &threads);
|
||||||
|
|
||||||
|
if (new_time_cost != time_cost || new_memory_cost != memory_cost || new_threads != threads) {
|
||||||
|
RETURN_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
case PHP_PASSWORD_UNKNOWN:
|
case PHP_PASSWORD_UNKNOWN:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
RETURN_FALSE;
|
RETURN_FALSE;
|
||||||
}
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
/* {{{ proto boolean password_make_salt(string password, string hash)
|
/* {{{ proto boolean password_verify(string password, string hash)
|
||||||
Verify a hash created using crypt() or password_hash() */
|
Verify a hash created using crypt() or password_hash() */
|
||||||
PHP_FUNCTION(password_verify)
|
PHP_FUNCTION(password_verify)
|
||||||
{
|
{
|
||||||
|
@ -228,35 +298,62 @@ PHP_FUNCTION(password_verify)
|
||||||
size_t i, password_len, hash_len;
|
size_t i, password_len, hash_len;
|
||||||
char *password, *hash;
|
char *password, *hash;
|
||||||
zend_string *ret;
|
zend_string *ret;
|
||||||
|
php_password_algo algo;
|
||||||
|
|
||||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &password, &password_len, &hash, &hash_len) == FAILURE) {
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &password, &password_len, &hash, &hash_len) == FAILURE) {
|
||||||
RETURN_FALSE;
|
RETURN_FALSE;
|
||||||
}
|
}
|
||||||
if ((ret = php_crypt(password, (int)password_len, hash, (int)hash_len, 1)) == NULL) {
|
|
||||||
RETURN_FALSE;
|
algo = php_password_determine_algo(hash, (size_t) hash_len);
|
||||||
|
|
||||||
|
switch(algo) {
|
||||||
|
#if HAVE_ARGON2LIB
|
||||||
|
case PHP_PASSWORD_ARGON2I:
|
||||||
|
{
|
||||||
|
argon2_type type = Argon2_i;
|
||||||
|
|
||||||
|
status = argon2_verify(hash, password, password_len, type);
|
||||||
|
|
||||||
|
if (status == ARGON2_OK) {
|
||||||
|
RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_FALSE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case PHP_PASSWORD_BCRYPT:
|
||||||
|
case PHP_PASSWORD_UNKNOWN:
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
if ((ret = php_crypt(password, (int)password_len, hash, (int)hash_len, 1)) == NULL) {
|
||||||
|
RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ZSTR_LEN(ret) != hash_len || hash_len < 13) {
|
||||||
|
zend_string_free(ret);
|
||||||
|
RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're using this method instead of == in order to provide
|
||||||
|
* resistance towards timing attacks. This is a constant time
|
||||||
|
* equality check that will always check every byte of both
|
||||||
|
* values. */
|
||||||
|
for (i = 0; i < hash_len; i++) {
|
||||||
|
status |= (ZSTR_VAL(ret)[i] ^ hash[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
zend_string_free(ret);
|
||||||
|
|
||||||
|
RETURN_BOOL(status == 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ZSTR_LEN(ret) != hash_len || hash_len < 13) {
|
RETURN_FALSE;
|
||||||
zend_string_free(ret);
|
|
||||||
RETURN_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We're using this method instead of == in order to provide
|
|
||||||
* resistance towards timing attacks. This is a constant time
|
|
||||||
* equality check that will always check every byte of both
|
|
||||||
* values. */
|
|
||||||
for (i = 0; i < hash_len; i++) {
|
|
||||||
status |= (ZSTR_VAL(ret)[i] ^ hash[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
zend_string_free(ret);
|
|
||||||
|
|
||||||
RETURN_BOOL(status == 0);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
/* {{{ proto string password_hash(string password, int algo, array options = array())
|
/* {{{ proto string password_hash(string password, int algo[, array options = array()])
|
||||||
Hash a password */
|
Hash a password */
|
||||||
PHP_FUNCTION(password_hash)
|
PHP_FUNCTION(password_hash)
|
||||||
{
|
{
|
||||||
|
@ -267,7 +364,13 @@ PHP_FUNCTION(password_hash)
|
||||||
size_t salt_len = 0, required_salt_len = 0, hash_format_len;
|
size_t salt_len = 0, required_salt_len = 0, hash_format_len;
|
||||||
HashTable *options = 0;
|
HashTable *options = 0;
|
||||||
zval *option_buffer;
|
zval *option_buffer;
|
||||||
zend_string *result;
|
|
||||||
|
#if HAVE_ARGON2LIB
|
||||||
|
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;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|H", &password, &password_len, &algo, &options) == FAILURE) {
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|H", &password, &password_len, &algo, &options) == FAILURE) {
|
||||||
return;
|
return;
|
||||||
|
@ -275,23 +378,57 @@ PHP_FUNCTION(password_hash)
|
||||||
|
|
||||||
switch (algo) {
|
switch (algo) {
|
||||||
case PHP_PASSWORD_BCRYPT:
|
case PHP_PASSWORD_BCRYPT:
|
||||||
{
|
{
|
||||||
zend_long cost = PHP_PASSWORD_BCRYPT_COST;
|
zend_long cost = PHP_PASSWORD_BCRYPT_COST;
|
||||||
|
|
||||||
if (options && (option_buffer = zend_hash_str_find(options, "cost", sizeof("cost")-1)) != NULL) {
|
if (options && (option_buffer = zend_hash_str_find(options, "cost", sizeof("cost")-1)) != NULL) {
|
||||||
cost = zval_get_long(option_buffer);
|
cost = zval_get_long(option_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cost < 4 || cost > 31) {
|
||||||
|
php_error_docref(NULL, E_WARNING, "Invalid bcrypt cost parameter specified: " ZEND_LONG_FMT, cost);
|
||||||
|
RETURN_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
required_salt_len = 22;
|
||||||
|
sprintf(hash_format, "$2y$%02ld$", (long) cost);
|
||||||
|
hash_format_len = 7;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
#if HAVE_ARGON2LIB
|
||||||
|
case PHP_PASSWORD_ARGON2I:
|
||||||
|
{
|
||||||
|
if (options && (option_buffer = zend_hash_str_find(options, "memory_cost", sizeof("memory_cost")-1)) != NULL) {
|
||||||
|
memory_cost = zval_get_long(option_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
if (cost < 4 || cost > 31) {
|
if (memory_cost > ARGON2_MAX_MEMORY || memory_cost < ARGON2_MIN_MEMORY) {
|
||||||
php_error_docref(NULL, E_WARNING, "Invalid bcrypt cost parameter specified: " ZEND_LONG_FMT, cost);
|
php_error_docref(NULL, E_WARNING, "Memory cost is outside of allowed memory range", memory_cost);
|
||||||
RETURN_NULL();
|
RETURN_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options && (option_buffer = zend_hash_str_find(options, "time_cost", sizeof("time_cost")-1)) != NULL) {
|
||||||
|
time_cost = zval_get_long(option_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
RETURN_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options && (option_buffer = zend_hash_str_find(options, "threads", sizeof("threads")-1)) != NULL) {
|
||||||
|
threads = zval_get_long(option_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (threads > ARGON2_MAX_LANES || threads == 0) {
|
||||||
|
php_error_docref(NULL, E_WARNING, "Invalid number of threads", threads);
|
||||||
|
RETURN_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
required_salt_len = 16;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
required_salt_len = 22;
|
#endif
|
||||||
sprintf(hash_format, "$2y$%02ld$", (long) cost);
|
|
||||||
hash_format_len = 7;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PHP_PASSWORD_UNKNOWN:
|
case PHP_PASSWORD_UNKNOWN:
|
||||||
default:
|
default:
|
||||||
php_error_docref(NULL, E_WARNING, "Unknown password hashing algorithm: " ZEND_LONG_FMT, algo);
|
php_error_docref(NULL, E_WARNING, "Unknown password hashing algorithm: " ZEND_LONG_FMT, algo);
|
||||||
|
@ -356,30 +493,86 @@ PHP_FUNCTION(password_hash)
|
||||||
salt_len = required_salt_len;
|
salt_len = required_salt_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
salt[salt_len] = 0;
|
switch (algo) {
|
||||||
|
case PHP_PASSWORD_BCRYPT:
|
||||||
|
{
|
||||||
|
zend_string *result;
|
||||||
|
salt[salt_len] = 0;
|
||||||
|
|
||||||
hash = safe_emalloc(salt_len + hash_format_len, 1, 1);
|
hash = safe_emalloc(salt_len + hash_format_len, 1, 1);
|
||||||
sprintf(hash, "%s%s", hash_format, salt);
|
sprintf(hash, "%s%s", hash_format, salt);
|
||||||
hash[hash_format_len + salt_len] = 0;
|
hash[hash_format_len + salt_len] = 0;
|
||||||
|
|
||||||
efree(salt);
|
efree(salt);
|
||||||
|
|
||||||
/* This cast is safe, since both values are defined here in code and cannot overflow */
|
/* This cast is safe, since both values are defined here in code and cannot overflow */
|
||||||
hash_len = (int) (hash_format_len + salt_len);
|
hash_len = (int) (hash_format_len + salt_len);
|
||||||
|
|
||||||
if ((result = php_crypt(password, (int)password_len, hash, hash_len, 1)) == NULL) {
|
if ((result = php_crypt(password, (int)password_len, hash, hash_len, 1)) == NULL) {
|
||||||
efree(hash);
|
efree(hash);
|
||||||
RETURN_FALSE;
|
RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
efree(hash);
|
||||||
|
|
||||||
|
if (ZSTR_LEN(result) < 13) {
|
||||||
|
zend_string_free(result);
|
||||||
|
RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_STR(result);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#if HAVE_ARGON2LIB
|
||||||
|
case PHP_PASSWORD_ARGON2I:
|
||||||
|
{
|
||||||
|
size_t out_len = 32;
|
||||||
|
size_t encoded_len;
|
||||||
|
int status = 0;
|
||||||
|
|
||||||
|
encoded_len = argon2_encodedlen(
|
||||||
|
time_cost,
|
||||||
|
memory_cost,
|
||||||
|
threads,
|
||||||
|
(uint32_t)salt_len,
|
||||||
|
out_len
|
||||||
|
);
|
||||||
|
|
||||||
|
zend_string *out = zend_string_alloc(out_len, 0);
|
||||||
|
zend_string *encoded = zend_string_alloc(encoded_len, 0);
|
||||||
|
|
||||||
|
status = argon2_hash(
|
||||||
|
time_cost,
|
||||||
|
memory_cost,
|
||||||
|
threads,
|
||||||
|
password,
|
||||||
|
password_len,
|
||||||
|
salt,
|
||||||
|
salt_len,
|
||||||
|
out->val,
|
||||||
|
out_len,
|
||||||
|
encoded->val,
|
||||||
|
encoded_len,
|
||||||
|
type,
|
||||||
|
ARGON2_VERSION_NUMBER
|
||||||
|
);
|
||||||
|
|
||||||
|
efree(out);
|
||||||
|
efree(salt);
|
||||||
|
|
||||||
|
if (status != ARGON2_OK) {
|
||||||
|
efree(encoded);
|
||||||
|
php_error_docref(NULL, E_WARNING, argon2_error_message(status));
|
||||||
|
RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_STR(encoded);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
RETURN_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
efree(hash);
|
|
||||||
|
|
||||||
if (ZSTR_LEN(result) < 13) {
|
|
||||||
zend_string_free(result);
|
|
||||||
RETURN_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_STR(result);
|
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
| license@php.net so we can mail you a copy immediately. |
|
| license@php.net so we can mail you a copy immediately. |
|
||||||
+----------------------------------------------------------------------+
|
+----------------------------------------------------------------------+
|
||||||
| Authors: Anthony Ferrara <ircmaxell@php.net> |
|
| Authors: Anthony Ferrara <ircmaxell@php.net> |
|
||||||
|
| Charles R. Portwood II <charlesportwoodii@erianna.com> |
|
||||||
+----------------------------------------------------------------------+
|
+----------------------------------------------------------------------+
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -28,13 +29,21 @@ PHP_FUNCTION(password_get_info);
|
||||||
|
|
||||||
PHP_MINIT_FUNCTION(password);
|
PHP_MINIT_FUNCTION(password);
|
||||||
|
|
||||||
#define PHP_PASSWORD_DEFAULT PHP_PASSWORD_BCRYPT
|
#define PHP_PASSWORD_DEFAULT PHP_PASSWORD_BCRYPT
|
||||||
|
|
||||||
#define PHP_PASSWORD_BCRYPT_COST 10
|
#define PHP_PASSWORD_BCRYPT_COST 10
|
||||||
|
|
||||||
|
#if HAVE_ARGON2LIB
|
||||||
|
#define PHP_PASSWORD_ARGON2_MEMORY_COST 1<<10
|
||||||
|
#define PHP_PASSWORD_ARGON2_TIME_COST 2
|
||||||
|
#define PHP_PASSWORD_ARGON2_THREADS 2
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PHP_PASSWORD_UNKNOWN,
|
PHP_PASSWORD_UNKNOWN,
|
||||||
PHP_PASSWORD_BCRYPT
|
PHP_PASSWORD_BCRYPT,
|
||||||
|
#if HAVE_ARGON2LIB
|
||||||
|
PHP_PASSWORD_ARGON2I,
|
||||||
|
#endif
|
||||||
} php_password_algo;
|
} php_password_algo;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
29
ext/standard/tests/password/password_get_info_argon2.phpt
Normal file
29
ext/standard/tests/password/password_get_info_argon2.phpt
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
--TEST--
|
||||||
|
Test normal operation of password_get_info() with Argon2
|
||||||
|
--SKIPIF--
|
||||||
|
<?php
|
||||||
|
if (!defined('PASSWORD_ARGON2I')) die('Skipped: 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'));
|
||||||
|
echo "OK!";
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
array(3) {
|
||||||
|
["algo"]=>
|
||||||
|
int(2)
|
||||||
|
["algoName"]=>
|
||||||
|
string(7) "argon2i"
|
||||||
|
["options"]=>
|
||||||
|
array(3) {
|
||||||
|
["memory_cost"]=>
|
||||||
|
int(65536)
|
||||||
|
["time_cost"]=>
|
||||||
|
int(3)
|
||||||
|
["threads"]=>
|
||||||
|
int(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OK!
|
18
ext/standard/tests/password/password_hash_argon2.phpt
Normal file
18
ext/standard/tests/password/password_hash_argon2.phpt
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
--TEST--
|
||||||
|
Test normal operation of password_hash() with argon2
|
||||||
|
--SKIPIF--
|
||||||
|
<?php
|
||||||
|
if (!defined('PASSWORD_ARGON2I')) die('Skipped: password_hash not built with Argon2');
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$password = "the password for testing 12345!";
|
||||||
|
|
||||||
|
$hash = password_hash($password, PASSWORD_ARGON2I);
|
||||||
|
var_dump(password_verify($password, $hash));
|
||||||
|
|
||||||
|
echo "OK!";
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
bool(true)
|
||||||
|
OK!
|
21
ext/standard/tests/password/password_hash_error_argon2.phpt
Normal file
21
ext/standard/tests/password/password_hash_error_argon2.phpt
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
--TEST--
|
||||||
|
Test error operation of password_hash() with argon2
|
||||||
|
--SKIPIF--
|
||||||
|
<?php
|
||||||
|
if (!defined('PASSWORD_ARGON2I')) die('Skipped: 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]));
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
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
|
|
@ -0,0 +1,22 @@
|
||||||
|
--TEST--
|
||||||
|
Test normal operation of password_needs_rehash() with argon2
|
||||||
|
--SKIPIF--
|
||||||
|
<?php
|
||||||
|
if (!defined('PASSWORD_ARGON2I')) die('Skipped: password_needs_rehash not built with Argon2');
|
||||||
|
?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$hash = '$argon2i$v=19$m=65536,t=3,p=1$YkprUktYN0lHQTd2bWRFeA$79aA+6IvgclpDAJVoezProlqzIPy7do/P0sBDXS9Nn0';
|
||||||
|
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' => 2]));
|
||||||
|
var_dump(password_needs_rehash($hash, PASSWORD_ARGON2I, ['threads' => 2]));
|
||||||
|
echo "OK!";
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
bool(false)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
OK!
|
18
ext/standard/tests/password/password_verify_argon2.phpt
Normal file
18
ext/standard/tests/password/password_verify_argon2.phpt
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
--TEST--
|
||||||
|
Test normal operation of password_verify() with argon2
|
||||||
|
--SKIPIF--
|
||||||
|
<?php
|
||||||
|
if (!defined('PASSWORD_ARGON2I')) die('Skipped: password_verify not built with Argon2');
|
||||||
|
?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
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'));
|
||||||
|
echo "OK!";
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
bool(true)
|
||||||
|
bool(false)
|
||||||
|
OK!
|
Loading…
Add table
Add a link
Reference in a new issue