1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2013-2016 Jakub Zelenka |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Jakub Zelenka <bukka@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "php.h"
20 #include "php_crypto.h"
21 #include "php_crypto_hash.h"
22 #include "php_crypto_cipher.h"
23 #include "php_crypto_object.h"
24 #include "zend_exceptions.h"
25 #include "ext/standard/php_string.h"
26
27 #include <openssl/evp.h>
28 #include <openssl/hmac.h>
29
30 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
31 #define PHP_CRYPTO_HMAC_DO(_rc, _method) \
32 _rc = _method
33 #else
34 #define PHP_CRYPTO_HMAC_DO(_rc, _method) \
35 _rc = 1; _method
36
HMAC_CTX_copy(HMAC_CTX * dctx,HMAC_CTX * sctx)37 int HMAC_CTX_copy(HMAC_CTX *dctx, HMAC_CTX *sctx)
38 {
39 if (!EVP_MD_CTX_copy(&dctx->i_ctx, &sctx->i_ctx))
40 goto err;
41 if (!EVP_MD_CTX_copy(&dctx->o_ctx, &sctx->o_ctx))
42 goto err;
43 if (!EVP_MD_CTX_copy(&dctx->md_ctx, &sctx->md_ctx))
44 goto err;
45 memcpy(dctx->key, sctx->key, HMAC_MAX_MD_CBLOCK);
46 dctx->key_length = sctx->key_length;
47 dctx->md = sctx->md;
48 return 1;
49 err:
50 return 0;
51 }
52
53 #endif
54
55 #if OPENSSL_VERSION_NUMBER < 0x10100000L
56
HMAC_CTX_new()57 static HMAC_CTX *HMAC_CTX_new()
58 {
59 HMAC_CTX *ctx = OPENSSL_malloc(sizeof(HMAC_CTX));
60 if (ctx) {
61 HMAC_CTX_init(ctx);
62 }
63
64 return ctx;
65 }
66
HMAC_CTX_free(HMAC_CTX * ctx)67 static inline void HMAC_CTX_free(HMAC_CTX *ctx)
68 {
69 OPENSSL_free(ctx);
70 }
71
72 #endif
73
74 PHP_CRYPTO_EXCEPTION_DEFINE(Hash)
75 PHP_CRYPTO_ERROR_INFO_BEGIN(Hash)
76 PHP_CRYPTO_ERROR_INFO_ENTRY(
77 HASH_ALGORITHM_NOT_FOUND,
78 "Hash algorithm '%s' not found"
79 )
80 PHP_CRYPTO_ERROR_INFO_ENTRY(
81 STATIC_METHOD_NOT_FOUND,
82 "Hash static method '%s' not found"
83 )
84 PHP_CRYPTO_ERROR_INFO_ENTRY(
85 STATIC_METHOD_TOO_MANY_ARGS,
86 "Hash static method %s can accept max one argument"
87 )
88 PHP_CRYPTO_ERROR_INFO_ENTRY(
89 INIT_FAILED,
90 "Initialization of hash failed"
91 )
92 PHP_CRYPTO_ERROR_INFO_ENTRY(
93 UPDATE_FAILED,
94 "Updating of hash context failed"
95 )
96 PHP_CRYPTO_ERROR_INFO_ENTRY(
97 DIGEST_FAILED,
98 "Creating of hash digest failed"
99 )
100 PHP_CRYPTO_ERROR_INFO_ENTRY(
101 INPUT_DATA_LENGTH_HIGH,
102 "Input data length can't exceed max integer length"
103 )
104 PHP_CRYPTO_ERROR_INFO_END()
105
106
107 ZEND_BEGIN_ARG_INFO(arginfo_crypto_hash_algorithm, 0)
108 ZEND_ARG_INFO(0, algorithm)
109 ZEND_END_ARG_INFO()
110
111 ZEND_BEGIN_ARG_INFO(arginfo_crypto_hash_data, 0)
112 ZEND_ARG_INFO(0, data)
113 ZEND_END_ARG_INFO()
114
115 ZEND_BEGIN_ARG_INFO_EX(arginfo_crypto_hash_list, 0, 0, 0)
116 ZEND_ARG_INFO(0, aliases)
117 ZEND_ARG_INFO(0, prefix)
118 ZEND_END_ARG_INFO()
119
120 ZEND_BEGIN_ARG_INFO(arginfo_crypto_hash_static, 0)
121 ZEND_ARG_INFO(0, name)
122 ZEND_ARG_INFO(0, arguments)
123 ZEND_END_ARG_INFO()
124
125 static const zend_function_entry php_crypto_hash_object_methods[] = {
126 PHP_CRYPTO_ME(
127 Hash, getAlgorithms,
128 arginfo_crypto_hash_list,
129 ZEND_ACC_STATIC|ZEND_ACC_PUBLIC
130 )
131 PHP_CRYPTO_ME(
132 Hash, hasAlgorithm,
133 arginfo_crypto_hash_algorithm,
134 ZEND_ACC_STATIC|ZEND_ACC_PUBLIC
135 )
136 PHP_CRYPTO_ME(
137 Hash, __callStatic,
138 arginfo_crypto_hash_static,
139 ZEND_ACC_STATIC|ZEND_ACC_PUBLIC
140 )
141 PHP_CRYPTO_ME(
142 Hash, __construct,
143 arginfo_crypto_hash_algorithm,
144 ZEND_ACC_CTOR|ZEND_ACC_PUBLIC
145 )
146 PHP_CRYPTO_ME(
147 Hash, update,
148 arginfo_crypto_hash_data,
149 ZEND_ACC_PUBLIC
150 )
151 PHP_CRYPTO_ME(
152 Hash, getAlgorithmName,
153 NULL,
154 ZEND_ACC_PUBLIC
155 )
156 PHP_CRYPTO_ME(
157 Hash, digest,
158 NULL,
159 ZEND_ACC_PUBLIC
160 )
161 PHP_CRYPTO_ME(
162 Hash, hexdigest,
163 NULL,
164 ZEND_ACC_PUBLIC
165 )
166 PHP_CRYPTO_ME(
167 Hash, getSize,
168 NULL,
169 ZEND_ACC_PUBLIC
170 )
171 PHP_CRYPTO_ME(
172 Hash, getBlockSize,
173 NULL,
174 ZEND_ACC_PUBLIC
175 )
176 PHPC_FE_END
177 };
178
179 PHP_CRYPTO_EXCEPTION_DEFINE(MAC)
180 PHP_CRYPTO_ERROR_INFO_BEGIN(MAC)
181 PHP_CRYPTO_ERROR_INFO_ENTRY(
182 MAC_ALGORITHM_NOT_FOUND,
183 "MAC algorithm '%s' not found"
184 )
185 PHP_CRYPTO_ERROR_INFO_ENTRY(
186 KEY_LENGTH_INVALID,
187 "The key length for MAC is invalid"
188 )
189 PHP_CRYPTO_ERROR_INFO_END()
190
191 ZEND_BEGIN_ARG_INFO(arginfo_crypto_mac_construct, 0)
192 ZEND_ARG_INFO(0, algorithm)
193 ZEND_ARG_INFO(0, key)
194 ZEND_END_ARG_INFO()
195
196 static const zend_function_entry php_crypto_mac_object_methods[] = {
197 PHP_CRYPTO_ME(
198 MAC, __construct,
199 arginfo_crypto_mac_construct,
200 ZEND_ACC_PUBLIC
201 )
202 PHPC_FE_END
203 };
204
205 /* class entries */
206 PHP_CRYPTO_API zend_class_entry *php_crypto_hash_ce;
207 PHP_CRYPTO_API zend_class_entry *php_crypto_mac_ce;
208 PHP_CRYPTO_API zend_class_entry *php_crypto_hmac_ce;
209 #ifdef PHP_CRYPTO_HAS_CMAC
210 PHP_CRYPTO_API zend_class_entry *php_crypto_cmac_ce;
211 #endif
212
213 /* object handler */
214 PHPC_OBJ_DEFINE_HANDLER_VAR(crypto_hash);
215
216 /* algorithm name getter macros */
217 #define PHP_CRYPTO_HASH_GET_ALGORITHM_NAME_EX(this_object) \
218 PHPC_READ_PROPERTY(php_crypto_hash_ce, this_object, \
219 "algorithm", sizeof("algorithm")-1, 1)
220
221 #define PHP_CRYPTO_HASH_GET_ALGORITHM_NAME(this_object) \
222 Z_STRVAL_P(PHP_CRYPTO_HASH_GET_ALGORITHM_NAME_EX(this_object))
223
224 /* {{{ crypto_hash free object handler */
PHPC_OBJ_HANDLER_FREE(crypto_hash)225 PHPC_OBJ_HANDLER_FREE(crypto_hash)
226 {
227 PHPC_OBJ_HANDLER_FREE_INIT(crypto_hash);
228
229 if (PHPC_THIS->type == PHP_CRYPTO_HASH_TYPE_MD) {
230 EVP_MD_CTX_destroy(PHP_CRYPTO_HASH_CTX(PHPC_THIS));
231 } else if (PHPC_THIS->type == PHP_CRYPTO_HASH_TYPE_HMAC) {
232 HMAC_CTX_free(PHP_CRYPTO_HMAC_CTX(PHPC_THIS));
233 }
234 #ifdef PHP_CRYPTO_HAS_CMAC
235 else if (PHPC_THIS->type == PHP_CRYPTO_HASH_TYPE_CMAC) {
236 CMAC_CTX_free(PHP_CRYPTO_CMAC_CTX(PHPC_THIS));
237 }
238 #endif
239
240 if (PHPC_THIS->key) {
241 efree(PHPC_THIS->key);
242 }
243
244 PHPC_OBJ_HANDLER_FREE_DESTROY();
245 }
246 /* }}} */
247
248 /* {{{ crypto_hash create_ex object helper */
PHPC_OBJ_HANDLER_CREATE_EX(crypto_hash)249 PHPC_OBJ_HANDLER_CREATE_EX(crypto_hash)
250 {
251 PHPC_OBJ_HANDLER_CREATE_EX_INIT(crypto_hash);
252
253 if (PHPC_CLASS_TYPE == php_crypto_hash_ce) {
254 PHPC_THIS->type = PHP_CRYPTO_HASH_TYPE_MD;
255 PHP_CRYPTO_HASH_CTX(PHPC_THIS) = EVP_MD_CTX_create();
256 } else if (PHPC_CLASS_TYPE == php_crypto_hmac_ce) {
257 PHPC_THIS->type = PHP_CRYPTO_HASH_TYPE_HMAC;
258 PHP_CRYPTO_HMAC_CTX(PHPC_THIS) = HMAC_CTX_new();
259 }
260 #ifdef PHP_CRYPTO_HAS_CMAC
261 else if (PHPC_CLASS_TYPE == php_crypto_cmac_ce) {
262 PHPC_THIS->type = PHP_CRYPTO_HASH_TYPE_CMAC;
263 PHP_CRYPTO_CMAC_CTX(PHPC_THIS) = CMAC_CTX_new();
264 }
265 #endif
266 else {
267 PHPC_THIS->type = PHP_CRYPTO_HASH_TYPE_NONE;
268 }
269
270 PHPC_THIS->key = NULL;
271 PHPC_THIS->key_len = 0;
272
273 PHPC_OBJ_HANDLER_CREATE_EX_RETURN(crypto_hash);
274 }
275 /* }}} */
276
277 /* {{{ crypto_hash create object handler */
PHPC_OBJ_HANDLER_CREATE(crypto_hash)278 PHPC_OBJ_HANDLER_CREATE(crypto_hash)
279 {
280 PHPC_OBJ_HANDLER_CREATE_RETURN(crypto_hash);
281 }
282 /* }}} */
283
284 /* {{{ crypto_hash clone object handler */
PHPC_OBJ_HANDLER_CLONE(crypto_hash)285 PHPC_OBJ_HANDLER_CLONE(crypto_hash)
286 {
287 zend_bool copy_success;
288 PHPC_OBJ_HANDLER_CLONE_INIT(crypto_hash);
289
290 PHPC_THAT->status = PHPC_THIS->status;
291 PHPC_THAT->type = PHPC_THIS->type;
292 if (PHPC_THIS->key) {
293 PHPC_THAT->key = emalloc(PHPC_THIS->key_len + 1);
294 memcpy(PHPC_THAT->key, PHPC_THIS->key, PHPC_THIS->key_len + 1);
295 PHPC_THAT->key_len = PHPC_THIS->key_len;
296 }
297
298 if (PHPC_THAT->type == PHP_CRYPTO_HASH_TYPE_MD) {
299 copy_success = EVP_MD_CTX_copy(
300 PHP_CRYPTO_HASH_CTX(PHPC_THAT), PHP_CRYPTO_HASH_CTX(PHPC_THIS));
301 PHP_CRYPTO_HASH_ALG(PHPC_THAT) = EVP_MD_CTX_md(PHP_CRYPTO_HASH_CTX(PHPC_THIS));
302 } else if (PHPC_THAT->type == PHP_CRYPTO_HASH_TYPE_HMAC) {
303 copy_success = HMAC_CTX_copy(
304 PHP_CRYPTO_HMAC_CTX(PHPC_THAT), PHP_CRYPTO_HMAC_CTX(PHPC_THIS));
305 }
306 #ifdef PHP_CRYPTO_HAS_CMAC
307 else if (PHPC_THAT->type == PHP_CRYPTO_HASH_TYPE_CMAC) {
308 copy_success = CMAC_CTX_copy(
309 PHP_CRYPTO_CMAC_CTX(PHPC_THAT), PHP_CRYPTO_CMAC_CTX(PHPC_THIS));
310 }
311 #endif
312 else {
313 copy_success = 0;
314 }
315
316 if (!copy_success) {
317 php_error(E_ERROR, "Cloning of Hash object failed");
318 }
319
320 PHPC_OBJ_HANDLER_CLONE_RETURN();
321 }
322 /* }}} */
323
324 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(crypto_hash)325 PHP_MINIT_FUNCTION(crypto_hash)
326 {
327 zend_class_entry ce;
328
329 /* Hash class */
330 INIT_CLASS_ENTRY(ce, PHP_CRYPTO_CLASS_NAME(Hash), php_crypto_hash_object_methods);
331 PHPC_CLASS_SET_HANDLER_CREATE(ce, crypto_hash);
332 php_crypto_hash_ce = PHPC_CLASS_REGISTER(ce);
333 PHPC_OBJ_INIT_HANDLERS(crypto_hash);
334 PHPC_OBJ_SET_HANDLER_OFFSET(crypto_hash);
335 PHPC_OBJ_SET_HANDLER_FREE(crypto_hash);
336 PHPC_OBJ_SET_HANDLER_CLONE(crypto_hash);
337 zend_declare_property_null(php_crypto_hash_ce,
338 "algorithm", sizeof("algorithm")-1, ZEND_ACC_PROTECTED TSRMLS_CC);
339
340 /* HashException registration */
341 PHP_CRYPTO_EXCEPTION_REGISTER(ce, Hash);
342 PHP_CRYPTO_ERROR_INFO_REGISTER(Hash);
343
344 /* MAC class */
345 INIT_CLASS_ENTRY(ce, PHP_CRYPTO_CLASS_NAME(MAC), php_crypto_mac_object_methods);
346 php_crypto_mac_ce = PHPC_CLASS_REGISTER_EX(ce, php_crypto_hash_ce, NULL);
347 php_crypto_mac_ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
348
349 /* MACException registration */
350 PHP_CRYPTO_EXCEPTION_REGISTER_EX(ce, MAC, Hash);
351 PHP_CRYPTO_ERROR_INFO_REGISTER(MAC);
352
353 /* HMAC class */
354 INIT_CLASS_ENTRY(ce, PHP_CRYPTO_CLASS_NAME(HMAC), NULL);
355 php_crypto_hmac_ce = PHPC_CLASS_REGISTER_EX(ce, php_crypto_mac_ce, NULL);
356
357 #ifdef PHP_CRYPTO_HAS_CMAC
358 /* CMAC class */
359 INIT_CLASS_ENTRY(ce, PHP_CRYPTO_CLASS_NAME(CMAC), NULL);
360 php_crypto_cmac_ce = PHPC_CLASS_REGISTER_EX(ce, php_crypto_mac_ce, NULL);
361 #endif
362
363 return SUCCESS;
364 }
365 /* }}} */
366
367 /* METHODS */
368
369 /* {{{ php_crypto_hash_set_algorithm_name */
php_crypto_hash_set_algorithm_name(zval * object,char * algorithm,phpc_str_size_t algorithm_len TSRMLS_DC)370 static inline void php_crypto_hash_set_algorithm_name(zval *object,
371 char *algorithm, phpc_str_size_t algorithm_len TSRMLS_DC)
372 {
373 php_strtoupper(algorithm, algorithm_len);
374 zend_update_property_stringl(php_crypto_hash_ce, object,
375 "algorithm", sizeof("algorithm")-1, algorithm, algorithm_len TSRMLS_CC);
376 }
377 /* }}} */
378
379 /* {{{ php_crypto_hash_bin2hex */
php_crypto_hash_bin2hex(char * out,const unsigned char * in,unsigned in_len)380 PHP_CRYPTO_API void php_crypto_hash_bin2hex(char *out, const unsigned char *in, unsigned in_len)
381 {
382 static const char hexits[17] = "0123456789abcdef";
383 unsigned i;
384 for (i = 0; i < in_len; i++) {
385 out[i * 2] = hexits[in[i] >> 4];
386 out[(i * 2) + 1] = hexits[in[i] & 0x0F];
387 }
388 out[i * 2] = 0;
389 }
390 /* }}} */
391
392 /* {{{ php_crypto_hash_init */
php_crypto_hash_init(PHPC_THIS_DECLARE (crypto_hash)TSRMLS_DC)393 static inline int php_crypto_hash_init(PHPC_THIS_DECLARE(crypto_hash) TSRMLS_DC)
394 {
395 int rc;
396
397 if (PHPC_THIS->type == PHP_CRYPTO_HASH_TYPE_MD) {
398 rc = EVP_DigestInit_ex(PHP_CRYPTO_HASH_CTX(PHPC_THIS),
399 PHP_CRYPTO_HASH_ALG(PHPC_THIS), NULL);
400 } else {
401 /* It is a MAC instance and the key is required */
402 if (!PHPC_THIS->key) {
403 php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Hash, INIT_FAILED));
404 return FAILURE;
405 }
406
407 /* update hash context */
408 switch (PHPC_THIS->type) {
409 case PHP_CRYPTO_HASH_TYPE_HMAC:
410 PHP_CRYPTO_HMAC_DO(rc, HMAC_Init_ex)(
411 PHP_CRYPTO_HMAC_CTX(PHPC_THIS),
412 PHPC_THIS->key, PHPC_THIS->key_len,
413 PHP_CRYPTO_HMAC_ALG(PHPC_THIS), NULL);
414
415 break;
416 #ifdef PHP_CRYPTO_HAS_CMAC
417 case PHP_CRYPTO_HASH_TYPE_CMAC:
418 rc = CMAC_Init(PHP_CRYPTO_CMAC_CTX(PHPC_THIS),
419 PHPC_THIS->key, PHPC_THIS->key_len,
420 PHP_CRYPTO_CMAC_ALG(PHPC_THIS), NULL);
421 break;
422 #endif
423 default:
424 rc = 0;
425 }
426 }
427
428 /* initialize hash */
429 if (!rc) {
430 php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Hash, INIT_FAILED));
431 return FAILURE;
432 }
433 PHPC_THIS->status = PHP_CRYPTO_HASH_STATUS_HASH;
434 return SUCCESS;
435 }
436 /* }}} */
437
438 /* {{{ php_crypto_hash_update */
php_crypto_hash_update(PHPC_THIS_DECLARE (crypto_hash),char * data,phpc_str_size_t data_len TSRMLS_DC)439 static inline int php_crypto_hash_update(PHPC_THIS_DECLARE(crypto_hash),
440 char *data, phpc_str_size_t data_len TSRMLS_DC)
441 {
442 int rc;
443
444 /* check if hash is initialized and if it's not, then try to initialize */
445 if (PHPC_THIS->status != PHP_CRYPTO_HASH_STATUS_HASH &&
446 php_crypto_hash_init(PHPC_THIS TSRMLS_CC) == FAILURE) {
447 return FAILURE;
448 }
449
450 /* update hash context */
451 switch (PHPC_THIS->type) {
452 case PHP_CRYPTO_HASH_TYPE_MD:
453 rc = EVP_DigestUpdate(PHP_CRYPTO_HASH_CTX(PHPC_THIS), data, data_len);
454 break;
455 case PHP_CRYPTO_HASH_TYPE_HMAC:
456 PHP_CRYPTO_HMAC_DO(rc, HMAC_Update)(
457 PHP_CRYPTO_HMAC_CTX(PHPC_THIS),
458 (unsigned char *) data, data_len);
459 break;
460 #ifdef PHP_CRYPTO_HAS_CMAC
461 case PHP_CRYPTO_HASH_TYPE_CMAC:
462 rc = CMAC_Update(PHP_CRYPTO_CMAC_CTX(PHPC_THIS), data, data_len);
463 break;
464 #endif
465 default:
466 rc = 0;
467 }
468
469 if (!rc) {
470 php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Hash, UPDATE_FAILED));
471 return FAILURE;
472 }
473
474 return SUCCESS;
475 }
476 /* }}} */
477
478 /* {{{ php_crypto_hash_digest */
php_crypto_hash_digest(INTERNAL_FUNCTION_PARAMETERS,int encode_to_hex)479 static inline void php_crypto_hash_digest(INTERNAL_FUNCTION_PARAMETERS, int encode_to_hex)
480 {
481 PHPC_THIS_DECLARE(crypto_hash);
482 PHPC_STR_DECLARE(hash);
483 unsigned char hash_value[EVP_MAX_MD_SIZE + 1];
484 unsigned int hash_len;
485 size_t hash_len_size;
486 int rc;
487
488 if (zend_parse_parameters_none() == FAILURE) {
489 return;
490 }
491
492 PHPC_THIS_FETCH(crypto_hash);
493
494 /* check if hash is initialized and if it's not, then try to initialize */
495 if (PHPC_THIS->status != PHP_CRYPTO_HASH_STATUS_HASH &&
496 php_crypto_hash_init(PHPC_THIS TSRMLS_CC) == FAILURE) {
497 RETURN_FALSE;
498 }
499
500 /* finalize hash context */
501 switch (PHPC_THIS->type) {
502 case PHP_CRYPTO_HASH_TYPE_MD:
503 rc = EVP_DigestFinal(PHP_CRYPTO_HASH_CTX(PHPC_THIS), hash_value, &hash_len);
504 break;
505 case PHP_CRYPTO_HASH_TYPE_HMAC:
506 PHP_CRYPTO_HMAC_DO(rc, HMAC_Final)(
507 PHP_CRYPTO_HMAC_CTX(PHPC_THIS), hash_value, &hash_len);
508 break;
509 #ifdef PHP_CRYPTO_HAS_CMAC
510 case PHP_CRYPTO_HASH_TYPE_CMAC:
511 rc = CMAC_Final(PHP_CRYPTO_CMAC_CTX(PHPC_THIS), hash_value, &hash_len_size);
512 /* this is safe because the hash_len_size is always really small */
513 hash_len = hash_len_size;
514 break;
515 #endif
516 default:
517 rc = 0;
518 }
519
520 if (!rc) {
521 php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Hash, DIGEST_FAILED));
522 RETURN_FALSE;
523 }
524 hash_value[hash_len] = 0;
525 PHPC_THIS->status = PHP_CRYPTO_HASH_STATUS_CLEAR;
526
527 if (encode_to_hex) {
528 unsigned int hash_hex_len = hash_len * 2;
529 PHPC_STR_ALLOC(hash, hash_hex_len);
530 php_crypto_hash_bin2hex(PHPC_STR_VAL(hash), hash_value, hash_len);
531 } else {
532 PHPC_STR_INIT(hash, (char *) hash_value, hash_len);
533 }
534
535 PHPC_STR_RETURN(hash);
536 }
537 /* }}} */
538
539 /* {{{ proto static string Crypto\Hash::getAlgorithms(bool $aliases = false,
540 string $prefix = null)
541 Returns hash algorithms */
PHP_CRYPTO_METHOD(Hash,getAlgorithms)542 PHP_CRYPTO_METHOD(Hash, getAlgorithms)
543 {
544 php_crypto_object_fn_get_names(INTERNAL_FUNCTION_PARAM_PASSTHRU, OBJ_NAME_TYPE_MD_METH);
545 }
546 /* }}} */
547
548 /* {{{ proto static bool Crypto\Hash::hasAlgorithm(string $algorithm)
549 Finds out whether algorithm exists */
PHP_CRYPTO_METHOD(Hash,hasAlgorithm)550 PHP_CRYPTO_METHOD(Hash, hasAlgorithm)
551 {
552 char *algorithm;
553 phpc_str_size_t algorithm_len;
554
555 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
556 &algorithm, &algorithm_len) == FAILURE) {
557 return;
558 }
559
560 if (EVP_get_digestbyname(algorithm)) {
561 RETURN_TRUE;
562 } else {
563 RETURN_FALSE;
564 }
565 }
566 /* }}} */
567
568 /* {{{ proto static Crypto\Hash::__callStatic(string $name, array $arguments)
569 Hash magic method for calling static methods */
PHP_CRYPTO_METHOD(Hash,__callStatic)570 PHP_CRYPTO_METHOD(Hash, __callStatic)
571 {
572 char *algorithm;
573 phpc_str_size_t algorithm_len;
574 int argc;
575 zval *args, *pz_arg;
576 phpc_val *ppv_arg;
577 const EVP_MD *digest;
578 PHPC_THIS_DECLARE(crypto_hash);
579
580 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa",
581 &algorithm, &algorithm_len, &args) == FAILURE) {
582 return;
583 }
584
585 argc = PHPC_HASH_NUM_ELEMENTS(Z_ARRVAL_P(args));
586 if (argc > 1) {
587 php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(Hash, STATIC_METHOD_TOO_MANY_ARGS), algorithm);
588 RETURN_FALSE;
589 }
590
591 digest = EVP_get_digestbyname(algorithm);
592 if (!digest) {
593 php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(Hash, STATIC_METHOD_NOT_FOUND), algorithm);
594 RETURN_FALSE;
595 }
596
597 object_init_ex(return_value, php_crypto_hash_ce);
598 php_crypto_hash_set_algorithm_name(return_value, algorithm, algorithm_len TSRMLS_CC);
599 PHPC_THIS_FETCH_FROM_ZVAL(crypto_hash, return_value);
600 PHP_CRYPTO_HASH_ALG(PHPC_THIS) = digest;
601
602 if (argc == 1) {
603 PHPC_HASH_INTERNAL_POINTER_RESET(Z_ARRVAL_P(args));
604 PHPC_HASH_GET_CURRENT_DATA(Z_ARRVAL_P(args), ppv_arg);
605 convert_to_string_ex(ppv_arg);
606 PHPC_PVAL_TO_PZVAL(ppv_arg, pz_arg);
607 if (php_crypto_hash_update(PHPC_THIS,
608 Z_STRVAL_P(pz_arg), Z_STRLEN_P(pz_arg) TSRMLS_CC) == FAILURE) {
609 RETURN_NULL();
610 }
611 }
612 }
613 /* }}} */
614
615 /* {{{ proto Crypto\Hash::__construct(string $algorithm)
616 Hash constructor */
PHP_CRYPTO_METHOD(Hash,__construct)617 PHP_CRYPTO_METHOD(Hash, __construct)
618 {
619 PHPC_THIS_DECLARE(crypto_hash);
620 char *algorithm, *algorithm_uc;
621 phpc_str_size_t algorithm_len;
622 const EVP_MD *digest;
623
624 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
625 &algorithm, &algorithm_len) == FAILURE) {
626 return;
627 }
628
629 algorithm_uc = estrdup(algorithm);
630 php_crypto_hash_set_algorithm_name(getThis(), algorithm_uc, strlen(algorithm_uc) TSRMLS_CC);
631 PHPC_THIS_FETCH(crypto_hash);
632
633 digest = EVP_get_digestbyname(algorithm);
634 if (!digest) {
635 php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(Hash, HASH_ALGORITHM_NOT_FOUND), algorithm);
636 } else {
637 PHP_CRYPTO_HASH_ALG(PHPC_THIS) = digest;
638 }
639
640 efree(algorithm_uc);
641 }
642 /* }}} */
643
644 /* {{{ proto string Crypto\Hash::getAlgorithmName()
645 Returns hash algorithm string */
PHP_CRYPTO_METHOD(Hash,getAlgorithmName)646 PHP_CRYPTO_METHOD(Hash, getAlgorithmName)
647 {
648 zval *algorithm;
649 PHPC_READ_PROPERTY_RV_DECLARE;
650
651 algorithm = PHP_CRYPTO_HASH_GET_ALGORITHM_NAME_EX(getThis());
652 RETURN_ZVAL(algorithm, 1, 0);
653 }
654 /* }}} */
655
656 /* {{{ proto void Crypto\Hash::update(string $data)
657 Updates hash */
PHP_CRYPTO_METHOD(Hash,update)658 PHP_CRYPTO_METHOD(Hash, update)
659 {
660 PHPC_THIS_DECLARE(crypto_hash);
661 char *data;
662 phpc_str_size_t data_len;
663
664 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len) == FAILURE) {
665 return;
666 }
667
668 PHPC_THIS_FETCH(crypto_hash);
669 php_crypto_hash_update(PHPC_THIS, data, data_len TSRMLS_CC);
670 ZVAL_ZVAL(return_value, getThis(), 1, 0);
671 }
672 /* }}} */
673
674 /* {{{ proto string Crypto\Hash::digest()
675 Return hash digest in raw foramt */
PHP_CRYPTO_METHOD(Hash,digest)676 PHP_CRYPTO_METHOD(Hash, digest)
677 {
678 php_crypto_hash_digest(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
679 }
680 /* }}} */
681
682 /* {{{ proto string Crypto\Hash::hexdigest()
683 Return hash digest in hex format */
PHP_CRYPTO_METHOD(Hash,hexdigest)684 PHP_CRYPTO_METHOD(Hash, hexdigest)
685 {
686 php_crypto_hash_digest(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
687 }
688 /* }}} */
689
690 /* {{{ proto int Crypto\Hash::getBlockSize()
691 Returns hash block size */
PHP_CRYPTO_METHOD(Hash,getBlockSize)692 PHP_CRYPTO_METHOD(Hash, getBlockSize)
693 {
694 int block_size;
695 PHPC_THIS_DECLARE(crypto_hash);
696
697 if (zend_parse_parameters_none() == FAILURE) {
698 return;
699 }
700
701 PHPC_THIS_FETCH(crypto_hash);
702
703 /* find out block size */
704 switch (PHPC_THIS->type) {
705 case PHP_CRYPTO_HASH_TYPE_MD:
706 block_size = EVP_MD_block_size(PHP_CRYPTO_HASH_ALG(PHPC_THIS));
707 break;
708 case PHP_CRYPTO_HASH_TYPE_HMAC:
709 block_size = EVP_MD_block_size(PHP_CRYPTO_HMAC_ALG(PHPC_THIS));
710 break;
711 #ifdef PHP_CRYPTO_HAS_CMAC
712 case PHP_CRYPTO_HASH_TYPE_CMAC:
713 block_size = EVP_CIPHER_block_size(PHP_CRYPTO_CMAC_ALG(PHPC_THIS));
714 break;
715 #endif
716 default:
717 block_size = 0;
718 }
719
720 RETURN_LONG(block_size);
721 }
722
723 /* {{{ proto int Crypto\Hash::getSize()
724 Returns hash size */
PHP_CRYPTO_METHOD(Hash,getSize)725 PHP_CRYPTO_METHOD(Hash, getSize)
726 {
727 int hash_size;
728 PHPC_THIS_DECLARE(crypto_hash);
729
730 if (zend_parse_parameters_none() == FAILURE) {
731 return;
732 }
733
734 PHPC_THIS_FETCH(crypto_hash);
735
736 /* find out block size */
737 switch (PHPC_THIS->type) {
738 case PHP_CRYPTO_HASH_TYPE_MD:
739 hash_size = EVP_MD_size(PHP_CRYPTO_HASH_ALG(PHPC_THIS));
740 break;
741 case PHP_CRYPTO_HASH_TYPE_HMAC:
742 hash_size = EVP_MD_size(PHP_CRYPTO_HMAC_ALG(PHPC_THIS));
743 break;
744 #ifdef PHP_CRYPTO_HAS_CMAC
745 case PHP_CRYPTO_HASH_TYPE_CMAC:
746 hash_size = EVP_CIPHER_block_size(PHP_CRYPTO_CMAC_ALG(PHPC_THIS));
747 break;
748 #endif
749 default:
750 hash_size = 0;
751 }
752
753 RETURN_LONG(hash_size);
754 }
755
756 /* {{{ proto Crypto\MAC::__construct(string $algorithm, string $key)
757 Create a MAC (used by MAC subclasses - HMAC and CMAC) */
PHP_CRYPTO_METHOD(MAC,__construct)758 PHP_CRYPTO_METHOD(MAC, __construct)
759 {
760 PHPC_THIS_DECLARE(crypto_hash);
761 char *algorithm, *algorithm_uc, *key;
762 phpc_str_size_t algorithm_len, key_len;
763 int key_len_int;
764
765 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
766 &key, &key_len, &algorithm, &algorithm_len) == FAILURE) {
767 return;
768 }
769
770 algorithm_uc = estrdup(algorithm);
771 algorithm_len = strlen(algorithm_uc);
772 php_crypto_hash_set_algorithm_name(getThis(), algorithm_uc, algorithm_len TSRMLS_CC);
773 PHPC_THIS_FETCH(crypto_hash);
774
775 if (PHPC_THIS->type == PHP_CRYPTO_HASH_TYPE_HMAC) {
776 const EVP_MD *digest = EVP_get_digestbyname(algorithm_uc);
777 if (!digest) {
778 goto php_crypto_mac_alg_not_found;
779 }
780 PHP_CRYPTO_HMAC_ALG(PHPC_THIS) = digest;
781 }
782 #ifdef PHP_CRYPTO_HAS_CMAC
783 /* CMAC algorithm uses a cipher algorithm */
784 else if (PHPC_THIS->type == PHP_CRYPTO_HASH_TYPE_CMAC) {
785 const EVP_CIPHER *cipher = php_crypto_get_cipher_algorithm(algorithm_uc, algorithm_len);
786 if (!cipher) {
787 goto php_crypto_mac_alg_not_found;
788 }
789 if (key_len != EVP_CIPHER_block_size(cipher)) {
790 php_crypto_error(PHP_CRYPTO_ERROR_ARGS(MAC, KEY_LENGTH_INVALID));
791 efree(algorithm_uc);
792 return;
793 }
794 PHP_CRYPTO_CMAC_ALG(PHPC_THIS) = cipher;
795 }
796 #endif
797
798 efree(algorithm_uc);
799
800 /* check key length overflow */
801 if (php_crypto_str_size_to_int(key_len, &key_len_int) == FAILURE) {
802 php_crypto_error(PHP_CRYPTO_ERROR_ARGS(MAC, KEY_LENGTH_INVALID));
803 return;
804 }
805
806 PHPC_THIS->key = emalloc(key_len + 1);
807 memcpy(PHPC_THIS->key, key, key_len);
808 PHPC_THIS->key[key_len] = '\0';
809 PHPC_THIS->key_len = key_len_int;
810 return;
811
812 php_crypto_mac_alg_not_found:
813 php_crypto_error_ex(PHP_CRYPTO_ERROR_ARGS(MAC, MAC_ALGORITHM_NOT_FOUND), algorithm);
814 efree(algorithm_uc);
815 }
816