25#if defined(HAVE_OPENSSL_ARGON2)
30#include <openssl/params.h>
31#include <openssl/core_names.h>
32#include <openssl/kdf.h>
33#include <openssl/thread.h>
34#include <openssl/rand.h>
36#define PHP_OPENSSL_MEMLIMIT_MIN 8u
37#define PHP_OPENSSL_MEMLIMIT_MAX UINT32_MAX
38#define PHP_OPENSSL_ITERLIMIT_MIN 1u
39#define PHP_OPENSSL_ITERLIMIT_MAX UINT32_MAX
40#define PHP_OPENSSL_THREADS_MIN 1u
41#define PHP_OPENSSL_THREADS_MAX UINT32_MAX
43#define PHP_OPENSSL_ARGON_VERSION 0x13
45#define PHP_OPENSSL_SALT_SIZE 16
46#define PHP_OPENSSL_HASH_SIZE 32
47#define PHP_OPENSSL_DIGEST_SIZE 128
53 *iterlimit = PHP_OPENSSL_PWHASH_ITERLIMIT;
54 *memlimit = PHP_OPENSSL_PWHASH_MEMLIMIT;
55 *threads = PHP_OPENSSL_PWHASH_THREADS;
63 if ((smemlimit < 0) || (smemlimit < PHP_OPENSSL_MEMLIMIT_MIN) || (smemlimit > (PHP_OPENSSL_MEMLIMIT_MAX))) {
67 *memlimit = smemlimit;
70 zend_long siterlimit = zval_get_long(opt);
71 if ((siterlimit < PHP_OPENSSL_ITERLIMIT_MIN) || (siterlimit > PHP_OPENSSL_ITERLIMIT_MAX)) {
75 *iterlimit = siterlimit;
79 if ((sthreads < PHP_OPENSSL_THREADS_MIN) || (sthreads > PHP_OPENSSL_THREADS_MAX)) {
88static bool php_openssl_argon2_compute_hash(
90 uint32_t version, uint32_t memlimit, uint32_t iterlimit, uint32_t threads,
91 const char *
pass,
size_t pass_len,
92 const unsigned char *salt,
size_t salt_len,
93 unsigned char *
hash,
size_t hash_len)
95 OSSL_PARAM params[7], *
p = params;
97 EVP_KDF_CTX *kctx =
NULL;
101 oldthreads = OSSL_get_max_threads(
NULL);
102 if (OSSL_set_max_threads(
NULL, threads) != 1) {
106 *
p++ = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_THREADS, &threads);
107 *
p++ = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_ARGON2_LANES, &threads);
108 *
p++ = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_ITER, &iterlimit);
109 *
p++ = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_ARGON2_MEMCOST, &memlimit);
110 *
p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (
void *)salt, salt_len);
111 *
p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASSWORD, (
void *)
pass, pass_len);
112 *
p++ = OSSL_PARAM_construct_end();
117 if ((kctx = EVP_KDF_CTX_new(kdf)) ==
NULL) {
120 if (EVP_KDF_derive(kctx,
hash, hash_len, params) != 1) {
129 EVP_KDF_CTX_free(kctx);
130 OSSL_set_max_threads(
NULL, oldthreads);
137 uint32_t iterlimit, memlimit, threads, version = PHP_OPENSSL_ARGON_VERSION;
139 unsigned char hash[PHP_OPENSSL_HASH_SIZE+1], salt[PHP_OPENSSL_SALT_SIZE+1];
145 if (get_options(
options, &memlimit, &iterlimit, &threads) ==
FAILURE) {
148 if (RAND_bytes(salt, PHP_OPENSSL_SALT_SIZE) <= 0) {
152 if (!php_openssl_argon2_compute_hash(algo, version, memlimit, iterlimit, threads,
153 ZSTR_VAL(password),
ZSTR_LEN(password), salt, PHP_OPENSSL_SALT_SIZE,
hash, PHP_OPENSSL_HASH_SIZE)) {
161 digest = zend_string_alloc(PHP_OPENSSL_DIGEST_SIZE, 0);
163 algo, version, memlimit, iterlimit, threads,
ZSTR_VAL(salt64),
ZSTR_VAL(hash64));
165 zend_string_release(salt64);
166 zend_string_release(hash64);
171static int php_openssl_argon2_extract(
172 const zend_string *digest, uint32_t *version, uint32_t *memlimit, uint32_t *iterlimit,
176 char *hash64, *salt64;
178 if (!digest || (
ZSTR_LEN(digest) <
sizeof(
"$argon2id$"))) {
182 if (!memcmp(
p,
"$argon2i$",
strlen(
"$argon2i$"))) {
184 }
else if (!memcmp(
p,
"$argon2id$",
strlen(
"$argon2id$"))) {
189 if (
sscanf(
p,
"v=%" PRIu32
"$m=%" PRIu32
",t=%" PRIu32
",p=%" PRIu32,
190 version, memlimit, iterlimit, threads) != 4) {
206 hash64 =
strchr(salt64,
'$');
212 *salt = php_base64_decode((
unsigned char *)salt64,
strlen(salt64));
213 *
hash = php_base64_decode((
unsigned char *)hash64,
strlen(hash64));
219static bool php_openssl_argon2_verify(
const zend_string *password,
const zend_string *digest,
const char *algo)
221 uint32_t version, iterlimit, memlimit, threads;
228 if (
FAILURE == php_openssl_argon2_extract(digest, &version, &memlimit, &iterlimit, &threads, &salt, &
hash)) {
233 if (php_openssl_argon2_compute_hash(algo, version, memlimit, iterlimit, threads,
239 zend_string_release(
new);
240 zend_string_release(salt);
241 zend_string_release(
hash);
248 return php_openssl_argon2_verify(password, digest,
"argon2i");
253 return php_openssl_argon2_verify(password, digest,
"argon2id");
258 uint32_t version, iterlimit, memlimit, threads;
259 uint32_t new_version = PHP_OPENSSL_ARGON_VERSION, new_iterlimit, new_memlimit, new_threads;
261 if (
FAILURE == get_options(
options, &new_memlimit, &new_iterlimit, &new_threads)) {
264 if (
FAILURE == php_openssl_argon2_extract(
hash, &version, &memlimit, &iterlimit, &threads,
NULL,
NULL)) {
269 return (version != new_version) ||
270 (iterlimit != new_iterlimit) ||
271 (memlimit != new_memlimit) ||
272 (threads != new_threads);
278 uint32_t memory_cost;
281 if (
FAILURE == php_openssl_argon2_extract(
hash, &
v, &memory_cost, &time_cost, &threads,
NULL,
NULL)) {
284 add_assoc_long(
return_value,
"memory_cost", memory_cost);
294 return php_openssl_argon2_hash(password,
options,
"argon2i");
299 php_openssl_argon2i_hash,
300 php_openssl_argon2i_verify,
301 php_openssl_argon2_needs_rehash,
302 php_openssl_argon2_get_info,
308 return php_openssl_argon2_hash(password,
options,
"argon2id");
313 php_openssl_argon2id_hash,
314 php_openssl_argon2id_verify,
315 php_openssl_argon2_needs_rehash,
316 php_openssl_argon2_get_info,
372 zend_string_release(argon2i);
375 zend_string_release(argon2i);
377 register_openssl_pwhash_symbols(module_number);
PHPAPI zend_string * php_base64_encode_ex(const unsigned char *str, size_t length, zend_long flags)
#define PHP_BASE64_NO_PADDING
sscanf(string $string, string $format, mixed &... $vars)
strchr(string $haystack, string $needle, bool $before_needle=false)
hash(string $algo, string $data, bool $binary=false, array $options=[])
#define pass(a, b, c, mul)
foreach($dp as $el) foreach( $dp as $el) if( $pass2< 2) echo ""
openssl_password_verify(string $algo, #[\SensitiveParameter] string $password, string $hash)
openssl_password_hash(string $algo, #[\SensitiveParameter] string $password, array $options=[])
const php_password_algo * php_password_algo_find(const zend_string *ident)
int php_password_algo_register(const char *ident, const php_password_algo *algo)
#define PHP_MINIT_FUNCTION
PHPAPI int php_safe_bcmp(const zend_string *a, const zend_string *b)
PHP_JSON_API size_t int options
struct _php_password_algo php_password_algo
ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format,...)
ZEND_API ZEND_COLD void zend_value_error(const char *format,...)
ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format,...)
#define ZEND_PARSE_PARAMETERS_END()
#define Z_PARAM_STR(dest)
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
#define RETURN_NEW_STR(s)
#define Z_PARAM_ARRAY_HT(dest)
strcmp(string $string1, string $string2)
ZEND_API zval *ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char *str, size_t len)
struct _zend_string zend_string
struct _zend_array zend_array
#define ZSTR_INIT_LITERAL(s, persistent)
ZEND_RESULT_CODE zend_result