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