31
31
#include <openssl/core_names.h>
32
32
#include <openssl/kdf.h>
33
33
#include <openssl/thread.h>
34
+ #include <openssl/rand.h>
34
35
35
- #define MEMLIMIT_MIN 8u
36
- #define MEMLIMIT_MAX UINT32_MAX
37
- #define OPSLIMIT_MIN 1u
38
- #define OPSLIMIT_MAX UINT32_MAX
39
- #define THREADS_MIN 1u
40
- #define THREADS_MAX UINT32_MAX
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
41
42
42
- #define ARGON_VERSION 0x13
43
+ #define PHP_OPENSSL_ARGON_VERSION 0x13
43
44
44
- #define SALT_SIZE 16
45
- #define HASH_SIZE 32
46
- #define DIGEST_SIZE 128
45
+ #define PHP_OPENSSL_SALT_SIZE 16
46
+ #define PHP_OPENSSL_HASH_SIZE 32
47
+ #define PHP_OPENSSL_DIGEST_SIZE 128
47
48
48
- static inline int get_options (zend_array * options , uint32_t * memlimit , uint32_t * opslimit , uint32_t * threads )
49
+ static inline zend_result get_options (zend_array * options , uint32_t * memlimit , uint32_t * iterlimit , uint32_t * threads )
49
50
{
50
51
zval * opt ;
51
52
52
- * opslimit = PHP_OPENSSL_PWHASH_OPSLIMIT ;
53
- * memlimit = PHP_OPENSSL_PWHASH_MEMLIMIT ;
54
- * threads = PHP_OPENSSL_PWHASH_THREADS ;
53
+ * iterlimit = PHP_OPENSSL_PWHASH_ITERLIMIT ;
54
+ * memlimit = PHP_OPENSSL_PWHASH_MEMLIMIT ;
55
+ * threads = PHP_OPENSSL_PWHASH_THREADS ;
55
56
56
57
if (!options ) {
57
58
return SUCCESS ;
58
59
}
59
60
if ((opt = zend_hash_str_find (options , "memory_cost" , strlen ("memory_cost" )))) {
60
61
zend_long smemlimit = zval_get_long (opt );
61
62
62
- if ((smemlimit < 0 ) || (smemlimit < MEMLIMIT_MIN ) || (smemlimit > (MEMLIMIT_MAX ))) {
63
+ if ((smemlimit < 0 ) || (smemlimit < PHP_OPENSSL_MEMLIMIT_MIN ) || (smemlimit > (PHP_OPENSSL_MEMLIMIT_MAX ))) {
63
64
zend_value_error ("Memory cost is outside of allowed memory range" );
64
65
return FAILURE ;
65
66
}
66
67
* memlimit = smemlimit ;
67
68
}
68
69
if ((opt = zend_hash_str_find (options , "time_cost" , strlen ("time_cost" )))) {
69
- zend_long sopslimit = zval_get_long (opt );
70
- if ((sopslimit < OPSLIMIT_MIN ) || (sopslimit > OPSLIMIT_MAX )) {
70
+ zend_long siterlimit = zval_get_long (opt );
71
+ if ((siterlimit < PHP_OPENSSL_ITERLIMIT_MIN ) || (siterlimit > PHP_OPENSSL_ITERLIMIT_MAX )) {
71
72
zend_value_error ("Time cost is outside of allowed time range" );
72
73
return FAILURE ;
73
74
}
74
- * opslimit = sopslimit ;
75
+ * iterlimit = siterlimit ;
75
76
}
76
77
if ((opt = zend_hash_str_find (options , "threads" , strlen ("threads" ))) && (zval_get_long (opt ) != 1 )) {
77
78
zend_long sthreads = zval_get_long (opt );
78
- if ((sthreads < THREADS_MIN ) || (sthreads > THREADS_MAX )) {
79
+ if ((sthreads < PHP_OPENSSL_THREADS_MIN ) || (sthreads > PHP_OPENSSL_THREADS_MAX )) {
79
80
zend_value_error ("Invalid number of threads" );
80
81
return FAILURE ;
81
82
}
@@ -86,19 +87,22 @@ static inline int get_options(zend_array *options, uint32_t *memlimit, uint32_t
86
87
87
88
static bool php_openssl_argon2_compute_hash (
88
89
const char * algo ,
89
- uint32_t version , uint32_t memlimit , uint32_t opslimit , uint32_t threads ,
90
+ uint32_t version , uint32_t memlimit , uint32_t iterlimit , uint32_t threads ,
90
91
const char * pass , size_t pass_len ,
91
92
const unsigned char * salt , size_t salt_len ,
92
93
unsigned char * hash , size_t hash_len )
93
94
{
94
95
OSSL_PARAM params [7 ], * p = params ;
96
+ OSSL_LIB_CTX * ctx = NULL ;
95
97
EVP_KDF * kdf = NULL ;
96
98
EVP_KDF_CTX * kctx = NULL ;
97
99
bool ret = false;
98
100
99
-
100
101
if (threads > 1 ) {
101
- if (OSSL_set_max_threads (NULL , threads ) != 1 ) {
102
+ if ((ctx = OSSL_LIB_CTX_new ()) == NULL ) {
103
+ goto fail ;
104
+ }
105
+ if (OSSL_set_max_threads (ctx , threads ) != 1 ) {
102
106
goto fail ;
103
107
}
104
108
}
@@ -108,76 +112,77 @@ static bool php_openssl_argon2_compute_hash(
108
112
* p ++ = OSSL_PARAM_construct_uint32 (OSSL_KDF_PARAM_ARGON2_LANES ,
109
113
& threads );
110
114
* p ++ = OSSL_PARAM_construct_uint32 (OSSL_KDF_PARAM_ITER ,
111
- & opslimit );
115
+ & iterlimit );
112
116
* p ++ = OSSL_PARAM_construct_uint32 (OSSL_KDF_PARAM_ARGON2_MEMCOST ,
113
117
& memlimit );
114
118
* p ++ = OSSL_PARAM_construct_octet_string (OSSL_KDF_PARAM_SALT ,
115
119
(void * )salt , salt_len );
116
120
* p ++ = OSSL_PARAM_construct_octet_string (OSSL_KDF_PARAM_PASSWORD ,
117
121
(void * )pass , pass_len );
118
- * p ++ = OSSL_PARAM_construct_end ();
122
+ * p ++ = OSSL_PARAM_construct_end ();
119
123
120
- if ((kdf = EVP_KDF_fetch (NULL , algo , NULL )) == NULL ) {
124
+ if ((kdf = EVP_KDF_fetch (ctx , algo , NULL )) == NULL ) {
121
125
goto fail ;
122
126
}
123
127
if ((kctx = EVP_KDF_CTX_new (kdf )) == NULL ) {
124
128
goto fail ;
125
129
}
126
- if (EVP_KDF_derive (kctx , hash , hash_len , params ) != 1 ) {
130
+ if (EVP_KDF_derive (kctx , hash , hash_len , params ) != 1 ) {
127
131
zend_value_error ("Unexpected failure hashing password" );
128
132
goto fail ;
129
133
}
130
134
131
135
ret = true;
132
136
133
137
fail :
134
- EVP_KDF_free (kdf );
135
- EVP_KDF_CTX_free (kctx );
138
+ EVP_KDF_free (kdf );
139
+ EVP_KDF_CTX_free (kctx );
136
140
137
141
if (threads > 1 ) {
138
- OSSL_set_max_threads (NULL , 0 );
142
+ OSSL_set_max_threads (ctx , 0 );
143
+ OSSL_LIB_CTX_free (ctx );
139
144
}
140
145
return ret ;
141
146
}
142
147
143
148
static zend_string * php_openssl_argon2_hash (const zend_string * password , zend_array * options , const char * algo )
144
149
{
145
- uint32_t opslimit , memlimit , threads , version = ARGON_VERSION ;
150
+ uint32_t iterlimit , memlimit , threads , version = PHP_OPENSSL_ARGON_VERSION ;
146
151
zend_string * digest = NULL , * salt64 = NULL , * hash64 = NULL ;
147
- unsigned char hash [HASH_SIZE + 1 ], salt [SALT_SIZE + 1 ];
152
+ unsigned char hash [PHP_OPENSSL_HASH_SIZE + 1 ], salt [PHP_OPENSSL_SALT_SIZE + 1 ];
148
153
149
154
if ((ZSTR_LEN (password ) >= UINT32_MAX )) {
150
155
zend_value_error ("Password is too long" );
151
156
return NULL ;
152
157
}
153
- if (get_options (options , & memlimit , & opslimit , & threads ) == FAILURE ) {
158
+ if (get_options (options , & memlimit , & iterlimit , & threads ) == FAILURE ) {
154
159
return NULL ;
155
160
}
156
- if (FAILURE == php_random_bytes_throw (salt , SALT_SIZE ) ) {
161
+ if (RAND_bytes (salt , PHP_OPENSSL_SALT_SIZE ) <= 0 ) {
157
162
return NULL ;
158
163
}
159
164
160
- if (!php_openssl_argon2_compute_hash (algo , version , memlimit , opslimit , threads ,
161
- ZSTR_VAL (password ), ZSTR_LEN (password ), salt , SALT_SIZE , hash , HASH_SIZE )) {
165
+ if (!php_openssl_argon2_compute_hash (algo , version , memlimit , iterlimit , threads ,
166
+ ZSTR_VAL (password ), ZSTR_LEN (password ), salt , PHP_OPENSSL_SALT_SIZE , hash , PHP_OPENSSL_HASH_SIZE )) {
162
167
return NULL ;
163
168
}
164
169
165
- hash64 = php_base64_encode_ex (hash , HASH_SIZE , PHP_BASE64_NO_PADDING );
170
+ hash64 = php_base64_encode_ex (hash , PHP_OPENSSL_HASH_SIZE , PHP_BASE64_NO_PADDING );
166
171
167
- salt64 = php_base64_encode_ex (salt , SALT_SIZE , PHP_BASE64_NO_PADDING );
172
+ salt64 = php_base64_encode_ex (salt , PHP_OPENSSL_SALT_SIZE , PHP_BASE64_NO_PADDING );
168
173
169
- digest = zend_string_alloc (DIGEST_SIZE , 0 );
174
+ digest = zend_string_alloc (PHP_OPENSSL_DIGEST_SIZE , 0 );
170
175
ZSTR_LEN (digest ) = snprintf (ZSTR_VAL (digest ), ZSTR_LEN (digest ), "$%s$v=%d$m=%u,t=%u,p=%u$%s$%s" ,
171
- algo , version , memlimit , opslimit , threads , ZSTR_VAL (salt64 ), ZSTR_VAL (hash64 ));
176
+ algo , version , memlimit , iterlimit , threads , ZSTR_VAL (salt64 ), ZSTR_VAL (hash64 ));
172
177
173
178
zend_string_release (salt64 );
174
179
zend_string_release (hash64 );
175
180
176
- return digest ;
181
+ return digest ;
177
182
}
178
183
179
184
static int php_openssl_argon2_extract (
180
- const zend_string * digest , uint32_t * version , uint32_t * memlimit , uint32_t * opslimit ,
185
+ const zend_string * digest , uint32_t * version , uint32_t * memlimit , uint32_t * iterlimit ,
181
186
uint32_t * threads , zend_string * * salt , zend_string * * hash )
182
187
{
183
188
const char * p ;
@@ -195,7 +200,7 @@ static int php_openssl_argon2_extract(
195
200
return FAILURE ;
196
201
}
197
202
if (sscanf (p , "v=%" PRIu32 "$m=%" PRIu32 ",t=%" PRIu32 ",p=%" PRIu32 ,
198
- version , memlimit , opslimit , threads ) != 4 ) {
203
+ version , memlimit , iterlimit , threads ) != 4 ) {
199
204
return FAILURE ;
200
205
}
201
206
if (salt && hash ) {
@@ -226,19 +231,19 @@ static int php_openssl_argon2_extract(
226
231
227
232
static bool php_openssl_argon2_verify (const zend_string * password , const zend_string * digest , const char * algo )
228
233
{
229
- uint32_t version , opslimit , memlimit , threads ;
234
+ uint32_t version , iterlimit , memlimit , threads ;
230
235
zend_string * salt , * hash , * new ;
231
236
bool ret = false;
232
237
233
238
if ((ZSTR_LEN (password ) >= UINT32_MAX ) || (ZSTR_LEN (digest ) >= UINT32_MAX )) {
234
239
return false;
235
240
}
236
- if (FAILURE == php_openssl_argon2_extract (digest , & version , & memlimit , & opslimit , & threads , & salt , & hash )) {
241
+ if (FAILURE == php_openssl_argon2_extract (digest , & version , & memlimit , & iterlimit , & threads , & salt , & hash )) {
237
242
return false;
238
243
}
239
244
240
245
new = zend_string_alloc (ZSTR_LEN (hash ), 0 );
241
- if (php_openssl_argon2_compute_hash (algo , version , memlimit , opslimit , threads ,
246
+ if (php_openssl_argon2_compute_hash (algo , version , memlimit , iterlimit , threads ,
242
247
ZSTR_VAL (password ), ZSTR_LEN (password ), (unsigned char * )ZSTR_VAL (salt ),
243
248
ZSTR_LEN (salt ), (unsigned char * )ZSTR_VAL (new ), ZSTR_LEN (new ))) {
244
249
ret = (php_safe_bcmp (hash , new ) == 0 );
@@ -263,19 +268,19 @@ static bool php_openssl_argon2id_verify(const zend_string *password, const zend_
263
268
264
269
static bool php_openssl_argon2_needs_rehash (const zend_string * hash , zend_array * options )
265
270
{
266
- uint32_t version , opslimit , memlimit , threads ;
267
- uint32_t new_version = ARGON_VERSION , new_opslimit , new_memlimit , new_threads ;
271
+ uint32_t version , iterlimit , memlimit , threads ;
272
+ uint32_t new_version = PHP_OPENSSL_ARGON_VERSION , new_iterlimit , new_memlimit , new_threads ;
268
273
269
- if (FAILURE == get_options (options , & new_memlimit , & new_opslimit , & new_threads )) {
274
+ if (FAILURE == get_options (options , & new_memlimit , & new_iterlimit , & new_threads )) {
270
275
return true;
271
276
}
272
- if (FAILURE == php_openssl_argon2_extract (hash , & version , & memlimit , & opslimit , & threads , NULL , NULL )) {
277
+ if (FAILURE == php_openssl_argon2_extract (hash , & version , & memlimit , & iterlimit , & threads , NULL , NULL )) {
273
278
return true;
274
279
}
275
280
276
281
// Algo already checked in pasword_needs_rehash implementation
277
282
return (version != new_version ) ||
278
- (opslimit != new_opslimit ) ||
283
+ (iterlimit != new_iterlimit ) ||
279
284
(memlimit != new_memlimit ) ||
280
285
(threads != new_threads );
281
286
}
0 commit comments