php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
sodium_pwhash.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Sara Golemon <pollita@php.net> |
14 +----------------------------------------------------------------------+
15*/
16
17#ifdef HAVE_CONFIG_H
18# include "config.h"
19#endif
20
21#include "php.h"
23
24#include <sodium.h>
25
26#include "php_libsodium.h"
28
29#if SODIUM_LIBRARY_VERSION_MAJOR > 9 || (SODIUM_LIBRARY_VERSION_MAJOR == 9 && SODIUM_LIBRARY_VERSION_MINOR >= 6)
30
31static inline int get_options(zend_array *options, size_t *memlimit, size_t *opslimit) {
32 zval *opt;
33
34 *opslimit = PHP_SODIUM_PWHASH_OPSLIMIT;
35 *memlimit = PHP_SODIUM_PWHASH_MEMLIMIT << 10;
36 if (!options) {
37 return SUCCESS;
38 }
39 if ((opt = zend_hash_str_find(options, "memory_cost", strlen("memory_cost")))) {
40 zend_long smemlimit = zval_get_long(opt);
41
42 if ((smemlimit < 0) || (smemlimit < crypto_pwhash_MEMLIMIT_MIN >> 10) || (smemlimit > (crypto_pwhash_MEMLIMIT_MAX >> 10))) {
43 zend_value_error("Memory cost is outside of allowed memory range");
44 return FAILURE;
45 }
46 *memlimit = smemlimit << 10;
47 }
48 if ((opt = zend_hash_str_find(options, "time_cost", strlen("time_cost")))) {
49 *opslimit = zval_get_long(opt);
50 if ((*opslimit < crypto_pwhash_OPSLIMIT_MIN) || (*opslimit > crypto_pwhash_OPSLIMIT_MAX)) {
51 zend_value_error("Time cost is outside of allowed time range");
52 return FAILURE;
53 }
54 }
55 if ((opt = zend_hash_str_find(options, "threads", strlen("threads"))) && (zval_get_long(opt) != 1)) {
56 zend_value_error("A thread value other than 1 is not supported by this implementation");
57 return FAILURE;
58 }
59 return SUCCESS;
60}
61
62static zend_string *php_sodium_argon2_hash(const zend_string *password, zend_array *options, int alg) {
63 size_t opslimit, memlimit;
65
66 if ((ZSTR_LEN(password) >= 0xffffffff)) {
67 zend_value_error("Password is too long");
68 return NULL;
69 }
70
71 if (get_options(options, &memlimit, &opslimit) == FAILURE) {
72 return NULL;
73 }
74
75 ret = zend_string_alloc(crypto_pwhash_STRBYTES - 1, 0);
76 if (crypto_pwhash_str_alg(ZSTR_VAL(ret), ZSTR_VAL(password), ZSTR_LEN(password), opslimit, memlimit, alg)) {
77 zend_value_error("Unexpected failure hashing password");
78 zend_string_release(ret);
79 return NULL;
80 }
81
83 ZSTR_VAL(ret)[ZSTR_LEN(ret)] = 0;
84
85 return ret;
86}
87
88static bool php_sodium_argon2_verify(const zend_string *password, const zend_string *hash) {
89 if ((ZSTR_LEN(password) >= 0xffffffff) || (ZSTR_LEN(hash) >= 0xffffffff)) {
90 return 0;
91 }
92 return crypto_pwhash_str_verify(ZSTR_VAL(hash), ZSTR_VAL(password), ZSTR_LEN(password)) == 0;
93}
94
95static bool php_sodium_argon2_needs_rehash(const zend_string *hash, zend_array *options) {
96 size_t opslimit, memlimit;
97
98 if (get_options(options, &memlimit, &opslimit) == FAILURE) {
99 return 1;
100 }
101 return crypto_pwhash_str_needs_rehash(ZSTR_VAL(hash), opslimit, memlimit);
102}
103
104static int php_sodium_argon2_get_info(zval *return_value, const zend_string *hash) {
105 const char* p = NULL;
106 zend_long v = 0, threads = 1;
107 zend_long memory_cost = PHP_SODIUM_PWHASH_MEMLIMIT;
108 zend_long time_cost = PHP_SODIUM_PWHASH_OPSLIMIT;
109
110 if (!hash || (ZSTR_LEN(hash) < sizeof("$argon2id$"))) {
111 return FAILURE;
112 }
113
114 p = ZSTR_VAL(hash);
115 if (!memcmp(p, "$argon2i$", strlen("$argon2i$"))) {
116 p += strlen("$argon2i$");
117 } else if (!memcmp(p, "$argon2id$", strlen("$argon2id$"))) {
118 p += strlen("$argon2id$");
119 } else {
120 return FAILURE;
121 }
122
124 &v, &memory_cost, &time_cost, &threads);
125
126 add_assoc_long(return_value, "memory_cost", memory_cost);
127 add_assoc_long(return_value, "time_cost", time_cost);
128 add_assoc_long(return_value, "threads", threads);
129
130 return SUCCESS;
131}
132
133/* argon2i specific methods */
134
135static zend_string *php_sodium_argon2i_hash(const zend_string *password, zend_array *options) {
136 return php_sodium_argon2_hash(password, options, crypto_pwhash_ALG_ARGON2I13);
137}
138
139static const php_password_algo sodium_algo_argon2i = {
140 "argon2i",
141 php_sodium_argon2i_hash,
142 php_sodium_argon2_verify,
143 php_sodium_argon2_needs_rehash,
144 php_sodium_argon2_get_info,
145 NULL,
146};
147
148/* argon2id specific methods */
149
150static zend_string *php_sodium_argon2id_hash(const zend_string *password, zend_array *options) {
151 return php_sodium_argon2_hash(password, options, crypto_pwhash_ALG_ARGON2ID13);
152}
153
154static const php_password_algo sodium_algo_argon2id = {
155 "argon2id",
156 php_sodium_argon2id_hash,
157 php_sodium_argon2_verify,
158 php_sodium_argon2_needs_rehash,
159 php_sodium_argon2_get_info,
160 NULL,
161};
162
163PHP_MINIT_FUNCTION(sodium_password_hash) /* {{{ */ {
164 zend_string *argon2i = ZSTR_INIT_LITERAL("argon2i", 1);
165
166 if (php_password_algo_find(argon2i)) {
167 /* Nothing to do. Core has registered these algorithms for us. */
168 zend_string_release(argon2i);
169 return SUCCESS;
170 }
171 zend_string_release(argon2i);
172
173 register_sodium_pwhash_symbols(module_number);
174
175 if (FAILURE == php_password_algo_register("argon2i", &sodium_algo_argon2i)) {
176 return FAILURE;
177 }
178 if (FAILURE == php_password_algo_register("argon2id", &sodium_algo_argon2id)) {
179 return FAILURE;
180 }
181
182 return SUCCESS;
183}
184/* }}} */
185
186
187#endif /* HAVE_SODIUM_PASSWORD_HASH */
sscanf(string $string, string $format, mixed &... $vars)
uint32_t v
Definition cdf.c:1237
#define NULL
Definition gdcache.h:45
hash(string $algo, string $data, bool $binary=false, array $options=[])
Definition hash.stub.php:12
#define SUCCESS
Definition hash_sha3.c:261
#define crypto_pwhash_OPSLIMIT_MIN
Definition libsodium.c:84
const php_password_algo * php_password_algo_find(const zend_string *ident)
Definition password.c:459
int php_password_algo_register(const char *ident, const php_password_algo *algo)
Definition password.c:40
#define PHP_MINIT_FUNCTION
Definition php.h:400
PHP_JSON_API size_t int options
Definition php_json.h:102
struct _php_password_algo php_password_algo
p
Definition session.c:1105
ZEND_API ZEND_COLD void zend_value_error(const char *format,...)
Definition zend.c:1849
struct _zval_struct zval
strlen(string $string)
ZEND_API zval *ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char *str, size_t len)
Definition zend_hash.c:2689
int32_t zend_long
Definition zend_long.h:42
#define ZEND_LONG_FMT
Definition zend_long.h:87
struct _zend_string zend_string
struct _zend_array zend_array
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define ZSTR_INIT_LITERAL(s, persistent)
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
@ FAILURE
Definition zend_types.h:61
zval * return_value
zval * ret