xref: /PHP-5.4/ext/hash/hash.c (revision 4435b914)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 The PHP Group                                |
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: Sara Golemon <pollita@php.net>                               |
16   |         Scott MacVicar <scottmac@php.net>                            |
17   +----------------------------------------------------------------------+
18 */
19 
20 /* $Id$ */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "php_hash.h"
27 #include "ext/standard/info.h"
28 #include "ext/standard/file.h"
29 
30 static int php_hash_le_hash;
31 HashTable php_hash_hashtable;
32 
33 #if (PHP_MAJOR_VERSION >= 5)
34 # define DEFAULT_CONTEXT FG(default_context)
35 #else
36 # define DEFAULT_CONTEXT NULL
37 #endif
38 
39 #ifdef PHP_MHASH_BC
40 struct mhash_bc_entry {
41 	char *mhash_name;
42 	char *hash_name;
43 	int value;
44 };
45 
46 #define MHASH_NUM_ALGOS 34
47 
48 static struct mhash_bc_entry mhash_to_hash[MHASH_NUM_ALGOS] = {
49 	{"CRC32", "crc32", 0},
50 	{"MD5", "md5", 1},
51 	{"SHA1", "sha1", 2},
52 	{"HAVAL256", "haval256,3", 3},
53 	{NULL, NULL, 4},
54 	{"RIPEMD160", "ripemd160", 5},
55 	{NULL, NULL, 6},
56 	{"TIGER", "tiger192,3", 7},
57 	{"GOST", "gost", 8},
58 	{"CRC32B", "crc32b", 9},
59 	{"HAVAL224", "haval224,3", 10},
60 	{"HAVAL192", "haval192,3", 11},
61 	{"HAVAL160", "haval160,3", 12},
62 	{"HAVAL128", "haval128,3", 13},
63 	{"TIGER128", "tiger128,3", 14},
64 	{"TIGER160", "tiger160,3", 15},
65 	{"MD4", "md4", 16},
66 	{"SHA256", "sha256", 17},
67 	{"ADLER32", "adler32", 18},
68 	{"SHA224", "sha224", 19},
69 	{"SHA512", "sha512", 20},
70 	{"SHA384", "sha384", 21},
71 	{"WHIRLPOOL", "whirlpool", 22},
72 	{"RIPEMD128", "ripemd128", 23},
73 	{"RIPEMD256", "ripemd256", 24},
74 	{"RIPEMD320", "ripemd320", 25},
75 	{NULL, NULL, 26}, /* support needs to be added for snefru 128 */
76 	{"SNEFRU256", "snefru256", 27},
77 	{"MD2", "md2", 28},
78 	{"FNV132", "fnv132", 29},
79 	{"FNV1A32", "fnv1a32", 30},
80 	{"FNV164", "fnv164", 31},
81 	{"FNV1A64", "fnv1a64", 32},
82 	{"JOAAT", "joaat", 33},
83 };
84 #endif
85 
86 /* Hash Registry Access */
87 
php_hash_fetch_ops(const char * algo,int algo_len)88 PHP_HASH_API const php_hash_ops *php_hash_fetch_ops(const char *algo, int algo_len) /* {{{ */
89 {
90 	php_hash_ops *ops;
91 	char *lower = estrndup(algo, algo_len);
92 
93 	zend_str_tolower(lower, algo_len);
94 	if (SUCCESS != zend_hash_find(&php_hash_hashtable, lower, algo_len + 1, (void*)&ops)) {
95 		ops = NULL;
96 	}
97 	efree(lower);
98 
99 	return ops;
100 }
101 /* }}} */
102 
php_hash_register_algo(const char * algo,const php_hash_ops * ops)103 PHP_HASH_API void php_hash_register_algo(const char *algo, const php_hash_ops *ops) /* {{{ */
104 {
105 	int algo_len = strlen(algo);
106 	char *lower = estrndup(algo, algo_len);
107 
108 	zend_str_tolower(lower, algo_len);
109 	zend_hash_add(&php_hash_hashtable, lower, algo_len + 1, (void*)ops, sizeof(php_hash_ops), NULL);
110 	efree(lower);
111 }
112 /* }}} */
113 
php_hash_copy(const void * ops,void * orig_context,void * dest_context)114 PHP_HASH_API int php_hash_copy(const void *ops, void *orig_context, void *dest_context) /* {{{ */
115 {
116 	php_hash_ops *hash_ops = (php_hash_ops *)ops;
117 
118 	memcpy(dest_context, orig_context, hash_ops->context_size);
119 	return SUCCESS;
120 }
121 /* }}} */
122 
123 /* Userspace */
124 
php_hash_do_hash(INTERNAL_FUNCTION_PARAMETERS,int isfilename,zend_bool raw_output_default)125 static void php_hash_do_hash(INTERNAL_FUNCTION_PARAMETERS, int isfilename, zend_bool raw_output_default) /* {{{ */
126 {
127 	char *algo, *data, *digest;
128 	int algo_len, data_len;
129 	zend_bool raw_output = raw_output_default;
130 	const php_hash_ops *ops;
131 	void *context;
132 	php_stream *stream = NULL;
133 
134 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", &algo, &algo_len, &data, &data_len, &raw_output) == FAILURE) {
135 		return;
136 	}
137 
138 	ops = php_hash_fetch_ops(algo, algo_len);
139 	if (!ops) {
140 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown hashing algorithm: %s", algo);
141 		RETURN_FALSE;
142 	}
143 	if (isfilename) {
144 		if (CHECK_NULL_PATH(data, data_len)) {
145 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid path");
146 			RETURN_FALSE;
147 		}
148 		stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, DEFAULT_CONTEXT);
149 		if (!stream) {
150 			/* Stream will report errors opening file */
151 			RETURN_FALSE;
152 		}
153 	}
154 
155 	context = emalloc(ops->context_size);
156 	ops->hash_init(context);
157 
158 	if (isfilename) {
159 		char buf[1024];
160 		int n;
161 
162 		while ((n = php_stream_read(stream, buf, sizeof(buf))) > 0) {
163 			ops->hash_update(context, (unsigned char *) buf, n);
164 		}
165 		php_stream_close(stream);
166 	} else {
167 		ops->hash_update(context, (unsigned char *) data, data_len);
168 	}
169 
170 	digest = emalloc(ops->digest_size + 1);
171 	ops->hash_final((unsigned char *) digest, context);
172 	efree(context);
173 
174 	if (raw_output) {
175 		digest[ops->digest_size] = 0;
176 		RETURN_STRINGL(digest, ops->digest_size, 0);
177 	} else {
178 		char *hex_digest = safe_emalloc(ops->digest_size, 2, 1);
179 
180 		php_hash_bin2hex(hex_digest, (unsigned char *) digest, ops->digest_size);
181 		hex_digest[2 * ops->digest_size] = 0;
182 		efree(digest);
183 		RETURN_STRINGL(hex_digest, 2 * ops->digest_size, 0);
184 	}
185 }
186 /* }}} */
187 
188 /* {{{ proto string hash(string algo, string data[, bool raw_output = false])
189 Generate a hash of a given input string
190 Returns lowercase hexits by default */
PHP_FUNCTION(hash)191 PHP_FUNCTION(hash)
192 {
193 	php_hash_do_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
194 }
195 /* }}} */
196 
197 /* {{{ proto string hash_file(string algo, string filename[, bool raw_output = false])
198 Generate a hash of a given file
199 Returns lowercase hexits by default */
PHP_FUNCTION(hash_file)200 PHP_FUNCTION(hash_file)
201 {
202 	php_hash_do_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
203 }
204 /* }}} */
205 
php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAMETERS,int isfilename,zend_bool raw_output_default)206 static void php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAMETERS, int isfilename, zend_bool raw_output_default) /* {{{ */
207 {
208 	char *algo, *data, *digest, *key, *K;
209 	int algo_len, data_len, key_len, i;
210 	zend_bool raw_output = raw_output_default;
211 	const php_hash_ops *ops;
212 	void *context;
213 	php_stream *stream = NULL;
214 
215 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|b", &algo, &algo_len, &data, &data_len,
216 																  &key, &key_len, &raw_output) == FAILURE) {
217 		return;
218 	}
219 
220 	ops = php_hash_fetch_ops(algo, algo_len);
221 	if (!ops) {
222 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown hashing algorithm: %s", algo);
223 		RETURN_FALSE;
224 	}
225 	if (isfilename) {
226 		if (CHECK_NULL_PATH(data, data_len)) {
227 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid path");
228 			RETURN_FALSE;
229 		}
230 		stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, DEFAULT_CONTEXT);
231 		if (!stream) {
232 			/* Stream will report errors opening file */
233 			RETURN_FALSE;
234 		}
235 	}
236 
237 	context = emalloc(ops->context_size);
238 	ops->hash_init(context);
239 
240 	K = emalloc(ops->block_size);
241 	memset(K, 0, ops->block_size);
242 
243 	if (key_len > ops->block_size) {
244 		/* Reduce the key first */
245 		ops->hash_update(context, (unsigned char *) key, key_len);
246 		ops->hash_final((unsigned char *) K, context);
247 		/* Make the context ready to start over */
248 		ops->hash_init(context);
249 	} else {
250 		memcpy(K, key, key_len);
251 	}
252 
253 	/* XOR ipad */
254 	for(i=0; i < ops->block_size; i++) {
255 		K[i] ^= 0x36;
256 	}
257 	ops->hash_update(context, (unsigned char *) K, ops->block_size);
258 
259 	if (isfilename) {
260 		char buf[1024];
261 		int n;
262 
263 		while ((n = php_stream_read(stream, buf, sizeof(buf))) > 0) {
264 			ops->hash_update(context, (unsigned char *) buf, n);
265 		}
266 		php_stream_close(stream);
267 	} else {
268 		ops->hash_update(context, (unsigned char *) data, data_len);
269 	}
270 
271 	digest = emalloc(ops->digest_size + 1);
272 	ops->hash_final((unsigned char *) digest, context);
273 
274 	/* Convert K to opad -- 0x6A = 0x36 ^ 0x5C */
275 	for(i=0; i < ops->block_size; i++) {
276 		K[i] ^= 0x6A;
277 	}
278 
279 	/* Feed this result into the outter hash */
280 	ops->hash_init(context);
281 	ops->hash_update(context, (unsigned char *) K, ops->block_size);
282 	ops->hash_update(context, (unsigned char *) digest, ops->digest_size);
283 	ops->hash_final((unsigned char *) digest, context);
284 
285 	/* Zero the key */
286 	memset(K, 0, ops->block_size);
287 	efree(K);
288 	efree(context);
289 
290 	if (raw_output) {
291 		digest[ops->digest_size] = 0;
292 		RETURN_STRINGL(digest, ops->digest_size, 0);
293 	} else {
294 		char *hex_digest = safe_emalloc(ops->digest_size, 2, 1);
295 
296 		php_hash_bin2hex(hex_digest, (unsigned char *) digest, ops->digest_size);
297 		hex_digest[2 * ops->digest_size] = 0;
298 		efree(digest);
299 		RETURN_STRINGL(hex_digest, 2 * ops->digest_size, 0);
300 	}
301 }
302 /* }}} */
303 
304 /* {{{ proto string hash_hmac(string algo, string data, string key[, bool raw_output = false])
305 Generate a hash of a given input string with a key using HMAC
306 Returns lowercase hexits by default */
PHP_FUNCTION(hash_hmac)307 PHP_FUNCTION(hash_hmac)
308 {
309 	php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
310 }
311 /* }}} */
312 
313 /* {{{ proto string hash_hmac_file(string algo, string filename, string key[, bool raw_output = false])
314 Generate a hash of a given file with a key using HMAC
315 Returns lowercase hexits by default */
PHP_FUNCTION(hash_hmac_file)316 PHP_FUNCTION(hash_hmac_file)
317 {
318 	php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
319 }
320 /* }}} */
321 
322 
323 /* {{{ proto resource hash_init(string algo[, int options, string key])
324 Initialize a hashing context */
PHP_FUNCTION(hash_init)325 PHP_FUNCTION(hash_init)
326 {
327 	char *algo, *key = NULL;
328 	int algo_len, key_len = 0, argc = ZEND_NUM_ARGS();
329 	long options = 0;
330 	void *context;
331 	const php_hash_ops *ops;
332 	php_hash_data *hash;
333 
334 	if (zend_parse_parameters(argc TSRMLS_CC, "s|ls", &algo, &algo_len, &options, &key, &key_len) == FAILURE) {
335 		return;
336 	}
337 
338 	ops = php_hash_fetch_ops(algo, algo_len);
339 	if (!ops) {
340 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown hashing algorithm: %s", algo);
341 		RETURN_FALSE;
342 	}
343 
344 	if (options & PHP_HASH_HMAC &&
345 		key_len <= 0) {
346 		/* Note: a zero length key is no key at all */
347 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "HMAC requested without a key");
348 		RETURN_FALSE;
349 	}
350 
351 	context = emalloc(ops->context_size);
352 	ops->hash_init(context);
353 
354 	hash = emalloc(sizeof(php_hash_data));
355 	hash->ops = ops;
356 	hash->context = context;
357 	hash->options = options;
358 	hash->key = NULL;
359 
360 	if (options & PHP_HASH_HMAC) {
361 		char *K = emalloc(ops->block_size);
362 		int i;
363 
364 		memset(K, 0, ops->block_size);
365 
366 		if (key_len > ops->block_size) {
367 			/* Reduce the key first */
368 			ops->hash_update(context, (unsigned char *) key, key_len);
369 			ops->hash_final((unsigned char *) K, context);
370 			/* Make the context ready to start over */
371 			ops->hash_init(context);
372 		} else {
373 			memcpy(K, key, key_len);
374 		}
375 
376 		/* XOR ipad */
377 		for(i=0; i < ops->block_size; i++) {
378 			K[i] ^= 0x36;
379 		}
380 		ops->hash_update(context, (unsigned char *) K, ops->block_size);
381 		hash->key = (unsigned char *) K;
382 	}
383 
384 	ZEND_REGISTER_RESOURCE(return_value, hash, php_hash_le_hash);
385 }
386 /* }}} */
387 
388 /* {{{ proto bool hash_update(resource context, string data)
389 Pump data into the hashing algorithm */
PHP_FUNCTION(hash_update)390 PHP_FUNCTION(hash_update)
391 {
392 	zval *zhash;
393 	php_hash_data *hash;
394 	char *data;
395 	int data_len;
396 
397 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zhash, &data, &data_len) == FAILURE) {
398 		return;
399 	}
400 
401 	ZEND_FETCH_RESOURCE(hash, php_hash_data*, &zhash, -1, PHP_HASH_RESNAME, php_hash_le_hash);
402 
403 	hash->ops->hash_update(hash->context, (unsigned char *) data, data_len);
404 
405 	RETURN_TRUE;
406 }
407 /* }}} */
408 
409 /* {{{ proto int hash_update_stream(resource context, resource handle[, integer length])
410 Pump data into the hashing algorithm from an open stream */
PHP_FUNCTION(hash_update_stream)411 PHP_FUNCTION(hash_update_stream)
412 {
413 	zval *zhash, *zstream;
414 	php_hash_data *hash;
415 	php_stream *stream = NULL;
416 	long length = -1, didread = 0;
417 
418 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr|l", &zhash, &zstream, &length) == FAILURE) {
419 		return;
420 	}
421 
422 	ZEND_FETCH_RESOURCE(hash, php_hash_data*, &zhash, -1, PHP_HASH_RESNAME, php_hash_le_hash);
423 	php_stream_from_zval(stream, &zstream);
424 
425 	while (length) {
426 		char buf[1024];
427 		long n, toread = 1024;
428 
429 		if (length > 0 && toread > length) {
430 			toread = length;
431 		}
432 
433 		if ((n = php_stream_read(stream, buf, toread)) <= 0) {
434 			/* Nada mas */
435 			RETURN_LONG(didread);
436 		}
437 		hash->ops->hash_update(hash->context, (unsigned char *) buf, n);
438 		length -= n;
439 		didread += n;
440 	}
441 
442 	RETURN_LONG(didread);
443 }
444 /* }}} */
445 
446 /* {{{ proto bool hash_update_file(resource context, string filename[, resource context])
447 Pump data into the hashing algorithm from a file */
PHP_FUNCTION(hash_update_file)448 PHP_FUNCTION(hash_update_file)
449 {
450 	zval *zhash, *zcontext = NULL;
451 	php_hash_data *hash;
452 	php_stream_context *context;
453 	php_stream *stream;
454 	char *filename, buf[1024];
455 	int filename_len, n;
456 
457 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rp|r", &zhash, &filename, &filename_len, &zcontext) == FAILURE) {
458 		return;
459 	}
460 
461 	ZEND_FETCH_RESOURCE(hash, php_hash_data*, &zhash, -1, PHP_HASH_RESNAME, php_hash_le_hash);
462 	context = php_stream_context_from_zval(zcontext, 0);
463 
464 	stream = php_stream_open_wrapper_ex(filename, "rb", REPORT_ERRORS, NULL, context);
465 	if (!stream) {
466 		/* Stream will report errors opening file */
467 		RETURN_FALSE;
468 	}
469 
470 	while ((n = php_stream_read(stream, buf, sizeof(buf))) > 0) {
471 		hash->ops->hash_update(hash->context, (unsigned char *) buf, n);
472 	}
473 	php_stream_close(stream);
474 
475 	RETURN_TRUE;
476 }
477 /* }}} */
478 
479 /* {{{ proto string hash_final(resource context[, bool raw_output=false])
480 Output resulting digest */
PHP_FUNCTION(hash_final)481 PHP_FUNCTION(hash_final)
482 {
483 	zval *zhash;
484 	php_hash_data *hash;
485 	zend_bool raw_output = 0;
486 	zend_rsrc_list_entry *le;
487 	char *digest;
488 	int digest_len;
489 
490 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|b", &zhash, &raw_output) == FAILURE) {
491 		return;
492 	}
493 
494 	ZEND_FETCH_RESOURCE(hash, php_hash_data*, &zhash, -1, PHP_HASH_RESNAME, php_hash_le_hash);
495 
496 	digest_len = hash->ops->digest_size;
497 	digest = emalloc(digest_len + 1);
498 	hash->ops->hash_final((unsigned char *) digest, hash->context);
499 	if (hash->options & PHP_HASH_HMAC) {
500 		int i;
501 
502 		/* Convert K to opad -- 0x6A = 0x36 ^ 0x5C */
503 		for(i=0; i < hash->ops->block_size; i++) {
504 			hash->key[i] ^= 0x6A;
505 		}
506 
507 		/* Feed this result into the outter hash */
508 		hash->ops->hash_init(hash->context);
509 		hash->ops->hash_update(hash->context, (unsigned char *) hash->key, hash->ops->block_size);
510 		hash->ops->hash_update(hash->context, (unsigned char *) digest, hash->ops->digest_size);
511 		hash->ops->hash_final((unsigned char *) digest, hash->context);
512 
513 		/* Zero the key */
514 		memset(hash->key, 0, hash->ops->block_size);
515 		efree(hash->key);
516 		hash->key = NULL;
517 	}
518 	digest[digest_len] = 0;
519 	efree(hash->context);
520 	hash->context = NULL;
521 
522 	/* zend_list_REAL_delete() */
523 	if (zend_hash_index_find(&EG(regular_list), Z_RESVAL_P(zhash), (void *) &le)==SUCCESS) {
524 		/* This is a hack to avoid letting the resource hide elsewhere (like in separated vars)
525 			FETCH_RESOURCE is intelligent enough to handle dealing with any issues this causes */
526 		le->refcount = 1;
527 	} /* FAILURE is not an option */
528 	zend_list_delete(Z_RESVAL_P(zhash));
529 
530 	if (raw_output) {
531 		RETURN_STRINGL(digest, digest_len, 0);
532 	} else {
533 		char *hex_digest = safe_emalloc(digest_len,2,1);
534 
535 		php_hash_bin2hex(hex_digest, (unsigned char *) digest, digest_len);
536 		hex_digest[2 * digest_len] = 0;
537 		efree(digest);
538 		RETURN_STRINGL(hex_digest, 2 * digest_len, 0);
539 	}
540 }
541 /* }}} */
542 
543 /* {{{ proto resource hash_copy(resource context)
544 Copy hash resource */
PHP_FUNCTION(hash_copy)545 PHP_FUNCTION(hash_copy)
546 {
547 	zval *zhash;
548 	php_hash_data *hash, *copy_hash;
549 	void *context;
550 	int res;
551 
552 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zhash) == FAILURE) {
553 		return;
554 	}
555 
556 	ZEND_FETCH_RESOURCE(hash, php_hash_data*, &zhash, -1, PHP_HASH_RESNAME, php_hash_le_hash);
557 
558 
559 	context = emalloc(hash->ops->context_size);
560 	hash->ops->hash_init(context);
561 
562 	res = hash->ops->hash_copy(hash->ops, hash->context, context);
563 	if (res != SUCCESS) {
564 		efree(context);
565 		RETURN_FALSE;
566 	}
567 
568 	copy_hash = emalloc(sizeof(php_hash_data));
569 	copy_hash->ops = hash->ops;
570 	copy_hash->context = context;
571 	copy_hash->options = hash->options;
572 	copy_hash->key = ecalloc(1, hash->ops->block_size);
573 	if (hash->key) {
574 		memcpy(copy_hash->key, hash->key, hash->ops->block_size);
575 	}
576 	ZEND_REGISTER_RESOURCE(return_value, copy_hash, php_hash_le_hash);
577 }
578 /* }}} */
579 
580 /* {{{ proto array hash_algos(void)
581 Return a list of registered hashing algorithms */
PHP_FUNCTION(hash_algos)582 PHP_FUNCTION(hash_algos)
583 {
584 	HashPosition pos;
585 	char *str;
586 	uint str_len;
587 	long type;
588 	ulong idx;
589 
590 	array_init(return_value);
591 	for(zend_hash_internal_pointer_reset_ex(&php_hash_hashtable, &pos);
592 		(type = zend_hash_get_current_key_ex(&php_hash_hashtable, &str, &str_len, &idx, 0, &pos)) != HASH_KEY_NON_EXISTANT;
593 		zend_hash_move_forward_ex(&php_hash_hashtable, &pos)) {
594 		add_next_index_stringl(return_value, str, str_len-1, 1);
595 	}
596 }
597 /* }}} */
598 
599 /* Module Housekeeping */
600 
php_hash_dtor(zend_rsrc_list_entry * rsrc TSRMLS_DC)601 static void php_hash_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
602 {
603 	php_hash_data *hash = (php_hash_data*)rsrc->ptr;
604 
605 	/* Just in case the algo has internally allocated resources */
606 	if (hash->context) {
607 		unsigned char *dummy = emalloc(hash->ops->digest_size);
608 		hash->ops->hash_final(dummy, hash->context);
609 		efree(dummy);
610 		efree(hash->context);
611 	}
612 
613 	if (hash->key) {
614 		memset(hash->key, 0, hash->ops->block_size);
615 		efree(hash->key);
616 	}
617 	efree(hash);
618 }
619 /* }}} */
620 
621 #define PHP_HASH_HAVAL_REGISTER(p,b)	php_hash_register_algo("haval" #b "," #p , &php_hash_##p##haval##b##_ops);
622 
623 #ifdef PHP_MHASH_BC
624 
PHP_MINFO_FUNCTION(mhash)625 PHP_MINFO_FUNCTION(mhash)
626 {
627 	php_info_print_table_start();
628 	php_info_print_table_row(2, "MHASH support", "Enabled");
629 	php_info_print_table_row(2, "MHASH API Version", "Emulated Support");
630 	php_info_print_table_end();
631 }
632 
633 zend_module_entry mhash_module_entry = {
634 	STANDARD_MODULE_HEADER,
635 	"mhash",
636 	NULL,
637 	NULL,
638 	NULL,
639 	NULL,
640 	NULL,
641 	PHP_MINFO(mhash),
642 	NO_VERSION_YET,
643 	STANDARD_MODULE_PROPERTIES,
644 };
645 
mhash_init(INIT_FUNC_ARGS)646 static void mhash_init(INIT_FUNC_ARGS)
647 {
648 	char buf[128];
649 	int len;
650 	int algo_number = 0;
651 
652 	for (algo_number = 0; algo_number < MHASH_NUM_ALGOS; algo_number++) {
653 		struct mhash_bc_entry algorithm = mhash_to_hash[algo_number];
654 		if (algorithm.mhash_name == NULL) {
655 			continue;
656 		}
657 
658 		len = slprintf(buf, 127, "MHASH_%s", algorithm.mhash_name, strlen(algorithm.mhash_name));
659 		zend_register_long_constant(buf, len + 1, algorithm.value, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
660 	}
661 	zend_register_internal_module(&mhash_module_entry TSRMLS_CC);
662 }
663 
664 /* {{{ proto string mhash(int hash, string data [, string key])
665    Hash data with hash */
PHP_FUNCTION(mhash)666 PHP_FUNCTION(mhash)
667 {
668 	zval **z_algorithm;
669 	long algorithm;
670 
671 	if (zend_parse_parameters(1 TSRMLS_CC, "Z", &z_algorithm) == FAILURE) {
672 		return;
673 	}
674 
675 	SEPARATE_ZVAL(z_algorithm);
676 	convert_to_long_ex(z_algorithm);
677 	algorithm = Z_LVAL_PP(z_algorithm);
678 
679 	/* need to convert the first parameter from int constant to string algorithm name */
680 	if (algorithm >= 0 && algorithm < MHASH_NUM_ALGOS) {
681 		struct mhash_bc_entry algorithm_lookup = mhash_to_hash[algorithm];
682 		if (algorithm_lookup.hash_name) {
683 			ZVAL_STRING(*z_algorithm, algorithm_lookup.hash_name, 1);
684 		}
685 	}
686 
687 	if (ZEND_NUM_ARGS() == 3) {
688 		php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
689 	} else if (ZEND_NUM_ARGS() == 2) {
690 		php_hash_do_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
691 	} else {
692 		WRONG_PARAM_COUNT;
693 	}
694 }
695 /* }}} */
696 
697 /* {{{ proto string mhash_get_hash_name(int hash)
698    Gets the name of hash */
PHP_FUNCTION(mhash_get_hash_name)699 PHP_FUNCTION(mhash_get_hash_name)
700 {
701 	long algorithm;
702 
703 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &algorithm) == FAILURE) {
704 		return;
705 	}
706 
707 	if (algorithm >= 0 && algorithm  < MHASH_NUM_ALGOS) {
708 		struct mhash_bc_entry algorithm_lookup = mhash_to_hash[algorithm];
709 		if (algorithm_lookup.mhash_name) {
710 			RETURN_STRING(algorithm_lookup.mhash_name, 1);
711 		}
712 	}
713 	RETURN_FALSE;
714 }
715 /* }}} */
716 
717 /* {{{ proto int mhash_count(void)
718    Gets the number of available hashes */
PHP_FUNCTION(mhash_count)719 PHP_FUNCTION(mhash_count)
720 {
721 	if (zend_parse_parameters_none() == FAILURE) {
722 		return;
723 	}
724 	RETURN_LONG(MHASH_NUM_ALGOS - 1);
725 }
726 /* }}} */
727 
728 /* {{{ proto int mhash_get_block_size(int hash)
729    Gets the block size of hash */
PHP_FUNCTION(mhash_get_block_size)730 PHP_FUNCTION(mhash_get_block_size)
731 {
732 	long algorithm;
733 
734 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &algorithm) == FAILURE) {
735 		return;
736 	}
737 	RETVAL_FALSE;
738 
739 	if (algorithm >= 0 && algorithm  < MHASH_NUM_ALGOS) {
740 		struct mhash_bc_entry algorithm_lookup = mhash_to_hash[algorithm];
741 		if (algorithm_lookup.mhash_name) {
742 			const php_hash_ops *ops = php_hash_fetch_ops(algorithm_lookup.hash_name, strlen(algorithm_lookup.hash_name));
743 			if (ops) {
744 				RETVAL_LONG(ops->digest_size);
745 			}
746 		}
747 	}
748 }
749 /* }}} */
750 
751 #define SALT_SIZE 8
752 
753 /* {{{ proto string mhash_keygen_s2k(int hash, string input_password, string salt, int bytes)
754    Generates a key using hash functions */
PHP_FUNCTION(mhash_keygen_s2k)755 PHP_FUNCTION(mhash_keygen_s2k)
756 {
757 	long algorithm, l_bytes;
758 	int bytes;
759 	char *password, *salt;
760 	int password_len, salt_len;
761 	char padded_salt[SALT_SIZE];
762 
763 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lssl", &algorithm, &password, &password_len, &salt, &salt_len, &l_bytes) == FAILURE) {
764 		return;
765 	}
766 
767 	bytes = (int)l_bytes;
768 	if (bytes <= 0){
769 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "the byte parameter must be greater than 0");
770 		RETURN_FALSE;
771 	}
772 
773 	salt_len = MIN(salt_len, SALT_SIZE);
774 
775 	memcpy(padded_salt, salt, salt_len);
776 	if (salt_len < SALT_SIZE) {
777 		memset(padded_salt + salt_len, 0, SALT_SIZE - salt_len);
778 	}
779 	salt_len = SALT_SIZE;
780 
781 	RETVAL_FALSE;
782 	if (algorithm >= 0 && algorithm < MHASH_NUM_ALGOS) {
783 		struct mhash_bc_entry algorithm_lookup = mhash_to_hash[algorithm];
784 		if (algorithm_lookup.mhash_name) {
785 			const php_hash_ops *ops = php_hash_fetch_ops(algorithm_lookup.hash_name, strlen(algorithm_lookup.hash_name));
786 			if (ops) {
787 				unsigned char null = '\0';
788 				void *context;
789 				char *key, *digest;
790 				int i = 0, j = 0;
791 				int block_size = ops->digest_size;
792 				int times = bytes / block_size;
793 				if (bytes % block_size  != 0) times++;
794 
795 				context = emalloc(ops->context_size);
796 				ops->hash_init(context);
797 
798 				key = ecalloc(1, times * block_size);
799 				digest = emalloc(ops->digest_size + 1);
800 
801 				for (i = 0; i < times; i++) {
802 					ops->hash_init(context);
803 
804 					for (j=0;j<i;j++) {
805 						ops->hash_update(context, &null, 1);
806 					}
807 					ops->hash_update(context, (unsigned char *)padded_salt, salt_len);
808 					ops->hash_update(context, (unsigned char *)password, password_len);
809 					ops->hash_final((unsigned char *)digest, context);
810 					memcpy( &key[i*block_size], digest, block_size);
811 				}
812 
813 				RETVAL_STRINGL(key, bytes, 1);
814 				memset(key, 0, bytes);
815 				efree(digest);
816 				efree(context);
817 				efree(key);
818 			}
819 		}
820 	}
821 }
822 /* }}} */
823 
824 #endif
825 
826 /* {{{ PHP_MINIT_FUNCTION
827  */
PHP_MINIT_FUNCTION(hash)828 PHP_MINIT_FUNCTION(hash)
829 {
830 	php_hash_le_hash = zend_register_list_destructors_ex(php_hash_dtor, NULL, PHP_HASH_RESNAME, module_number);
831 
832 	zend_hash_init(&php_hash_hashtable, 35, NULL, NULL, 1);
833 
834 	php_hash_register_algo("md2",			&php_hash_md2_ops);
835 	php_hash_register_algo("md4",			&php_hash_md4_ops);
836 	php_hash_register_algo("md5",			&php_hash_md5_ops);
837 	php_hash_register_algo("sha1",			&php_hash_sha1_ops);
838 	php_hash_register_algo("sha224",		&php_hash_sha224_ops);
839 	php_hash_register_algo("sha256",		&php_hash_sha256_ops);
840 	php_hash_register_algo("sha384",		&php_hash_sha384_ops);
841 	php_hash_register_algo("sha512",		&php_hash_sha512_ops);
842 	php_hash_register_algo("ripemd128",		&php_hash_ripemd128_ops);
843 	php_hash_register_algo("ripemd160",		&php_hash_ripemd160_ops);
844 	php_hash_register_algo("ripemd256",		&php_hash_ripemd256_ops);
845 	php_hash_register_algo("ripemd320",		&php_hash_ripemd320_ops);
846 	php_hash_register_algo("whirlpool",		&php_hash_whirlpool_ops);
847 	php_hash_register_algo("tiger128,3",	&php_hash_3tiger128_ops);
848 	php_hash_register_algo("tiger160,3",	&php_hash_3tiger160_ops);
849 	php_hash_register_algo("tiger192,3",	&php_hash_3tiger192_ops);
850 	php_hash_register_algo("tiger128,4",	&php_hash_4tiger128_ops);
851 	php_hash_register_algo("tiger160,4",	&php_hash_4tiger160_ops);
852 	php_hash_register_algo("tiger192,4",	&php_hash_4tiger192_ops);
853 	php_hash_register_algo("snefru",		&php_hash_snefru_ops);
854 	php_hash_register_algo("snefru256",		&php_hash_snefru_ops);
855 	php_hash_register_algo("gost",			&php_hash_gost_ops);
856 	php_hash_register_algo("adler32",		&php_hash_adler32_ops);
857 	php_hash_register_algo("crc32",			&php_hash_crc32_ops);
858 	php_hash_register_algo("crc32b",		&php_hash_crc32b_ops);
859 	php_hash_register_algo("fnv132",		&php_hash_fnv132_ops);
860 	php_hash_register_algo("fnv164",		&php_hash_fnv164_ops);
861 	php_hash_register_algo("joaat",			&php_hash_joaat_ops);
862 
863 	PHP_HASH_HAVAL_REGISTER(3,128);
864 	PHP_HASH_HAVAL_REGISTER(3,160);
865 	PHP_HASH_HAVAL_REGISTER(3,192);
866 	PHP_HASH_HAVAL_REGISTER(3,224);
867 	PHP_HASH_HAVAL_REGISTER(3,256);
868 
869 	PHP_HASH_HAVAL_REGISTER(4,128);
870 	PHP_HASH_HAVAL_REGISTER(4,160);
871 	PHP_HASH_HAVAL_REGISTER(4,192);
872 	PHP_HASH_HAVAL_REGISTER(4,224);
873 	PHP_HASH_HAVAL_REGISTER(4,256);
874 
875 	PHP_HASH_HAVAL_REGISTER(5,128);
876 	PHP_HASH_HAVAL_REGISTER(5,160);
877 	PHP_HASH_HAVAL_REGISTER(5,192);
878 	PHP_HASH_HAVAL_REGISTER(5,224);
879 	PHP_HASH_HAVAL_REGISTER(5,256);
880 
881 	REGISTER_LONG_CONSTANT("HASH_HMAC",		PHP_HASH_HMAC,	CONST_CS | CONST_PERSISTENT);
882 
883 #ifdef PHP_MHASH_BC
884 	mhash_init(INIT_FUNC_ARGS_PASSTHRU);
885 #endif
886 
887 	return SUCCESS;
888 }
889 /* }}} */
890 
891 /* {{{ PHP_MSHUTDOWN_FUNCTION
892  */
PHP_MSHUTDOWN_FUNCTION(hash)893 PHP_MSHUTDOWN_FUNCTION(hash)
894 {
895 	zend_hash_destroy(&php_hash_hashtable);
896 
897 	return SUCCESS;
898 }
899 /* }}} */
900 
901 /* {{{ PHP_MINFO_FUNCTION
902  */
PHP_MINFO_FUNCTION(hash)903 PHP_MINFO_FUNCTION(hash)
904 {
905 	HashPosition pos;
906 	char buffer[2048];
907 	char *s = buffer, *e = s + sizeof(buffer), *str;
908 	ulong idx;
909 	long type;
910 
911 	for(zend_hash_internal_pointer_reset_ex(&php_hash_hashtable, &pos);
912 		(type = zend_hash_get_current_key_ex(&php_hash_hashtable, &str, NULL, &idx, 0, &pos)) != HASH_KEY_NON_EXISTANT;
913 		zend_hash_move_forward_ex(&php_hash_hashtable, &pos)) {
914 		s += slprintf(s, e - s, "%s ", str);
915 	}
916 	*s = 0;
917 
918 	php_info_print_table_start();
919 	php_info_print_table_row(2, "hash support", "enabled");
920 	php_info_print_table_row(2, "Hashing Engines", buffer);
921 	php_info_print_table_end();
922 }
923 /* }}} */
924 
925 /* {{{ arginfo */
926 #ifdef PHP_HASH_MD5_NOT_IN_CORE
927 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_md5, 0, 0, 1)
928 	ZEND_ARG_INFO(0, str)
929 	ZEND_ARG_INFO(0, raw_output)
930 ZEND_END_ARG_INFO()
931 
932 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_md5_file, 0, 0, 1)
933 	ZEND_ARG_INFO(0, filename)
934 	ZEND_ARG_INFO(0, raw_output)
935 ZEND_END_ARG_INFO()
936 #endif
937 
938 #ifdef PHP_HASH_SHA1_NOT_IN_CORE
939 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_sha1, 0, 0, 1)
940 	ZEND_ARG_INFO(0, str)
941 	ZEND_ARG_INFO(0, raw_output)
942 ZEND_END_ARG_INFO()
943 
944 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_sha1_file, 0, 0, 1)
945 	ZEND_ARG_INFO(0, filename)
946 	ZEND_ARG_INFO(0, raw_output)
947 ZEND_END_ARG_INFO()
948 #endif
949 
950 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash, 0, 0, 2)
951 	ZEND_ARG_INFO(0, algo)
952 	ZEND_ARG_INFO(0, data)
953 	ZEND_ARG_INFO(0, raw_output)
954 ZEND_END_ARG_INFO()
955 
956 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_file, 0, 0, 2)
957 	ZEND_ARG_INFO(0, algo)
958 	ZEND_ARG_INFO(0, filename)
959 	ZEND_ARG_INFO(0, raw_output)
960 ZEND_END_ARG_INFO()
961 
962 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_hmac, 0, 0, 3)
963 	ZEND_ARG_INFO(0, algo)
964 	ZEND_ARG_INFO(0, data)
965 	ZEND_ARG_INFO(0, key)
966 	ZEND_ARG_INFO(0, raw_output)
967 ZEND_END_ARG_INFO()
968 
969 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_hmac_file, 0, 0, 3)
970 	ZEND_ARG_INFO(0, algo)
971 	ZEND_ARG_INFO(0, filename)
972 	ZEND_ARG_INFO(0, key)
973 	ZEND_ARG_INFO(0, raw_output)
974 ZEND_END_ARG_INFO()
975 
976 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_init, 0, 0, 1)
977 	ZEND_ARG_INFO(0, algo)
978 	ZEND_ARG_INFO(0, options)
979 	ZEND_ARG_INFO(0, key)
980 ZEND_END_ARG_INFO()
981 
982 ZEND_BEGIN_ARG_INFO(arginfo_hash_update, 0)
983 	ZEND_ARG_INFO(0, context)
984 	ZEND_ARG_INFO(0, data)
985 ZEND_END_ARG_INFO()
986 
987 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_update_stream, 0, 0, 2)
988 	ZEND_ARG_INFO(0, context)
989 	ZEND_ARG_INFO(0, handle)
990 	ZEND_ARG_INFO(0, length)
991 ZEND_END_ARG_INFO()
992 
993 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_update_file, 0, 0, 2)
994 	ZEND_ARG_INFO(0, context)
995 	ZEND_ARG_INFO(0, filename)
996 	ZEND_ARG_INFO(0, context)
997 ZEND_END_ARG_INFO()
998 
999 ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_final, 0, 0, 1)
1000 	ZEND_ARG_INFO(0, context)
1001 	ZEND_ARG_INFO(0, raw_output)
1002 ZEND_END_ARG_INFO()
1003 
1004 ZEND_BEGIN_ARG_INFO(arginfo_hash_copy, 0)
1005 	ZEND_ARG_INFO(0, context)
1006 ZEND_END_ARG_INFO()
1007 
1008 ZEND_BEGIN_ARG_INFO(arginfo_hash_algos, 0)
1009 ZEND_END_ARG_INFO()
1010 
1011 /* BC Land */
1012 #ifdef PHP_MHASH_BC
1013 ZEND_BEGIN_ARG_INFO(arginfo_mhash_get_block_size, 0)
1014 	ZEND_ARG_INFO(0, hash)
1015 ZEND_END_ARG_INFO()
1016 
1017 ZEND_BEGIN_ARG_INFO(arginfo_mhash_get_hash_name, 0)
1018 	ZEND_ARG_INFO(0, hash)
1019 ZEND_END_ARG_INFO()
1020 
1021 ZEND_BEGIN_ARG_INFO(arginfo_mhash_keygen_s2k, 0)
1022 	ZEND_ARG_INFO(0, hash)
1023 	ZEND_ARG_INFO(0, input_password)
1024 	ZEND_ARG_INFO(0, salt)
1025 	ZEND_ARG_INFO(0, bytes)
1026 ZEND_END_ARG_INFO()
1027 
1028 ZEND_BEGIN_ARG_INFO(arginfo_mhash_count, 0)
1029 ZEND_END_ARG_INFO()
1030 
1031 ZEND_BEGIN_ARG_INFO_EX(arginfo_mhash, 0, 0, 2)
1032 	ZEND_ARG_INFO(0, hash)
1033 	ZEND_ARG_INFO(0, data)
1034 	ZEND_ARG_INFO(0, key)
1035 ZEND_END_ARG_INFO()
1036 #endif
1037 
1038 /* }}} */
1039 
1040 /* {{{ hash_functions[]
1041  */
1042 const zend_function_entry hash_functions[] = {
1043 	PHP_FE(hash,									arginfo_hash)
1044 	PHP_FE(hash_file,								arginfo_hash_file)
1045 
1046 	PHP_FE(hash_hmac,								arginfo_hash_hmac)
1047 	PHP_FE(hash_hmac_file,							arginfo_hash_hmac_file)
1048 
1049 	PHP_FE(hash_init,								arginfo_hash_init)
1050 	PHP_FE(hash_update,								arginfo_hash_update)
1051 	PHP_FE(hash_update_stream,						arginfo_hash_update_stream)
1052 	PHP_FE(hash_update_file,						arginfo_hash_update_file)
1053 	PHP_FE(hash_final,								arginfo_hash_final)
1054 	PHP_FE(hash_copy,								arginfo_hash_copy)
1055 
1056 	PHP_FE(hash_algos,								arginfo_hash_algos)
1057 
1058 	/* BC Land */
1059 #ifdef PHP_HASH_MD5_NOT_IN_CORE
1060 	PHP_NAMED_FE(md5, php_if_md5,					arginfo_hash_md5)
1061 	PHP_NAMED_FE(md5_file, php_if_md5_file,			arginfo_hash_md5_file)
1062 #endif /* PHP_HASH_MD5_NOT_IN_CORE */
1063 
1064 #ifdef PHP_HASH_SHA1_NOT_IN_CORE
1065 	PHP_NAMED_FE(sha1, php_if_sha1,					arginfo_hash_sha1)
1066 	PHP_NAMED_FE(sha1_file, php_if_sha1_file,		arginfo_hash_sha1_file)
1067 #endif /* PHP_HASH_SHA1_NOT_IN_CORE */
1068 
1069 #ifdef PHP_MHASH_BC
1070 	PHP_FE(mhash_keygen_s2k, arginfo_mhash_keygen_s2k)
1071 	PHP_FE(mhash_get_block_size, arginfo_mhash_get_block_size)
1072 	PHP_FE(mhash_get_hash_name, arginfo_mhash_get_hash_name)
1073 	PHP_FE(mhash_count, arginfo_mhash_count)
1074 	PHP_FE(mhash, arginfo_mhash)
1075 #endif
1076 
1077 	PHP_FE_END
1078 };
1079 /* }}} */
1080 
1081 /* {{{ hash_module_entry
1082  */
1083 zend_module_entry hash_module_entry = {
1084 #if ZEND_MODULE_API_NO >= 20010901
1085 	STANDARD_MODULE_HEADER,
1086 #endif
1087 	PHP_HASH_EXTNAME,
1088 	hash_functions,
1089 	PHP_MINIT(hash),
1090 	PHP_MSHUTDOWN(hash),
1091 	NULL, /* RINIT */
1092 	NULL, /* RSHUTDOWN */
1093 	PHP_MINFO(hash),
1094 #if ZEND_MODULE_API_NO >= 20010901
1095 	PHP_HASH_EXTVER, /* Replace with version number for your extension */
1096 #endif
1097 	STANDARD_MODULE_PROPERTIES
1098 };
1099 /* }}} */
1100 
1101 #ifdef COMPILE_DL_HASH
1102 ZEND_GET_MODULE(hash)
1103 #endif
1104 
1105 /*
1106  * Local variables:
1107  * tab-width: 4
1108  * c-basic-offset: 4
1109  * End:
1110  * vim600: noet sw=4 ts=4 fdm=marker
1111  * vim<600: noet sw=4 ts=4
1112  */
1113